Commit 3f42ed3ca2

Jakub Konka <kubkon@jakubkonka.com>
2023-12-05 13:49:55
elf: do not write ELF header if there were errors
1 parent af8621d
Changed files (2)
src
src/link/Elf/Object.zig
@@ -54,6 +54,15 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {
 
     self.header = try reader.readStruct(elf.Elf64_Ehdr);
 
+    if (elf_file.base.options.target.cpu.arch != self.header.?.e_machine.toTargetCpuArch().?) {
+        try elf_file.reportParseError2(
+            self.index,
+            "invalid cpu architecture: {s}",
+            .{@tagName(self.header.?.e_machine.toTargetCpuArch().?)},
+        );
+        return error.InvalidCpuArch;
+    }
+
     if (self.header.?.e_shnum == 0) return;
 
     const gpa = elf_file.base.allocator;
src/link/Elf.zig
@@ -1041,9 +1041,14 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
     }
 
     for (positionals.items) |obj| {
-        var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined };
-        self.parsePositional(obj.path, obj.must_link, &parse_ctx) catch |err|
-            try self.handleAndReportParseError(obj.path, err, &parse_ctx);
+        self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
+            error.LinkFail, error.InvalidCpuArch => {}, // already reported
+            else => |e| try self.reportParseError(
+                obj.path,
+                "unexpected error: parsing input file failed with error {s}",
+                .{@errorName(e)},
+            ),
+        };
     }
 
     var system_libs = std.ArrayList(SystemLib).init(arena);
@@ -1122,9 +1127,14 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
     }
 
     for (system_libs.items) |lib| {
-        var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined };
-        self.parseLibrary(lib, false, &parse_ctx) catch |err|
-            try self.handleAndReportParseError(lib.path, err, &parse_ctx);
+        self.parseLibrary(lib, false) catch |err| switch (err) {
+            error.LinkFail, error.InvalidCpuArch => {}, // already reported
+            else => |e| try self.reportParseError(
+                lib.path,
+                "unexpected error: parsing library failed with error {s}",
+                .{@errorName(e)},
+            ),
+        };
     }
 
     // Finally, as the last input objects we add compiler_rt and CSU postlude (if any).
@@ -1140,9 +1150,14 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
     if (csu.crtn) |v| try positionals.append(.{ .path = v });
 
     for (positionals.items) |obj| {
-        var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined };
-        self.parsePositional(obj.path, obj.must_link, &parse_ctx) catch |err|
-            try self.handleAndReportParseError(obj.path, err, &parse_ctx);
+        self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
+            error.LinkFail, error.InvalidCpuArch => {}, // already reported
+            else => |e| try self.reportParseError(
+                obj.path,
+                "unexpected error: parsing input file failed with error {s}",
+                .{@errorName(e)},
+            ),
+        };
     }
 
     // Init all objects
@@ -1300,9 +1315,14 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const
     if (module_obj_path) |path| try positionals.append(.{ .path = path });
 
     for (positionals.items) |obj| {
-        var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined };
-        self.parsePositional(obj.path, obj.must_link, &parse_ctx) catch |err|
-            try self.handleAndReportParseError(obj.path, err, &parse_ctx);
+        self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
+            error.LinkFail, error.InvalidCpuArch => {}, // already reported
+            else => |e| try self.reportParseError(
+                obj.path,
+                "unexpected error: parsing input file failed with error {s}",
+                .{@errorName(e)},
+            ),
+        };
     }
 
     // First, we flush relocatable object file generated with our backends.
@@ -1432,9 +1452,14 @@ pub fn flushObject(self: *Elf, comp: *Compilation, module_obj_path: ?[]const u8)
     if (module_obj_path) |path| try positionals.append(.{ .path = path });
 
     for (positionals.items) |obj| {
-        var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined };
-        self.parsePositional(obj.path, obj.must_link, &parse_ctx) catch |err|
-            try self.handleAndReportParseError(obj.path, err, &parse_ctx);
+        self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
+            error.LinkFail, error.InvalidCpuArch => {}, // already reported
+            else => |e| try self.reportParseError(
+                obj.path,
+                "unexpected error: parsing input file failed with error {s}",
+                .{@errorName(e)},
+            ),
+        };
     }
 
     // Init all objects
@@ -1770,37 +1795,36 @@ const ParseError = error{
     FileSystem,
     NotSupported,
     InvalidCharacter,
-    MalformedObject,
 } || LdScript.Error || std.os.AccessError || std.os.SeekError || std.fs.File.OpenError || std.fs.File.ReadError;
 
-fn parsePositional(self: *Elf, path: []const u8, must_link: bool, ctx: *ParseErrorCtx) ParseError!void {
+fn parsePositional(self: *Elf, path: []const u8, must_link: bool) ParseError!void {
     const tracy = trace(@src());
     defer tracy.end();
     if (try Object.isObject(path)) {
-        try self.parseObject(path, ctx);
+        try self.parseObject(path);
     } else {
-        try self.parseLibrary(.{ .path = path }, must_link, ctx);
+        try self.parseLibrary(.{ .path = path }, must_link);
     }
 }
 
-fn parseLibrary(self: *Elf, lib: SystemLib, must_link: bool, ctx: *ParseErrorCtx) ParseError!void {
+fn parseLibrary(self: *Elf, lib: SystemLib, must_link: bool) ParseError!void {
     const tracy = trace(@src());
     defer tracy.end();
 
     if (try Archive.isArchive(lib.path)) {
-        try self.parseArchive(lib.path, must_link, ctx);
+        try self.parseArchive(lib.path, must_link);
     } else if (try SharedObject.isSharedObject(lib.path)) {
-        try self.parseSharedObject(lib, ctx);
+        try self.parseSharedObject(lib);
     } else {
         // TODO if the script has a top-level comment identifying it as GNU ld script,
         // then report parse errors. Otherwise return UnknownFileType.
-        self.parseLdScript(lib, ctx) catch |err| switch (err) {
+        self.parseLdScript(lib) catch |err| switch (err) {
             else => return error.UnknownFileType,
         };
     }
 }
 
-fn parseObject(self: *Elf, path: []const u8, ctx: *ParseErrorCtx) ParseError!void {
+fn parseObject(self: *Elf, path: []const u8) ParseError!void {
     const tracy = trace(@src());
     defer tracy.end();
 
@@ -1818,12 +1842,9 @@ fn parseObject(self: *Elf, path: []const u8, ctx: *ParseErrorCtx) ParseError!voi
 
     const object = self.file(index).?.object;
     try object.parse(self);
-
-    ctx.detected_cpu_arch = object.header.?.e_machine.toTargetCpuArch().?;
-    if (ctx.detected_cpu_arch != self.base.options.target.cpu.arch) return error.InvalidCpuArch;
 }
 
-fn parseArchive(self: *Elf, path: []const u8, must_link: bool, ctx: *ParseErrorCtx) ParseError!void {
+fn parseArchive(self: *Elf, path: []const u8, must_link: bool) ParseError!void {
     const tracy = trace(@src());
     defer tracy.end();
 
@@ -1846,13 +1867,10 @@ fn parseArchive(self: *Elf, path: []const u8, must_link: bool, ctx: *ParseErrorC
         object.alive = must_link;
         try object.parse(self);
         try self.objects.append(gpa, index);
-
-        ctx.detected_cpu_arch = object.header.?.e_machine.toTargetCpuArch().?;
-        if (ctx.detected_cpu_arch != self.base.options.target.cpu.arch) return error.InvalidCpuArch;
     }
 }
 
-fn parseSharedObject(self: *Elf, lib: SystemLib, ctx: *ParseErrorCtx) ParseError!void {
+fn parseSharedObject(self: *Elf, lib: SystemLib) ParseError!void {
     const tracy = trace(@src());
     defer tracy.end();
 
@@ -1872,12 +1890,9 @@ fn parseSharedObject(self: *Elf, lib: SystemLib, ctx: *ParseErrorCtx) ParseError
 
     const shared_object = self.file(index).?.shared_object;
     try shared_object.parse(self);
-
-    ctx.detected_cpu_arch = shared_object.header.?.e_machine.toTargetCpuArch().?;
-    if (ctx.detected_cpu_arch != self.base.options.target.cpu.arch) return error.InvalidCpuArch;
 }
 
-fn parseLdScript(self: *Elf, lib: SystemLib, ctx: *ParseErrorCtx) ParseError!void {
+fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void {
     const tracy = trace(@src());
     defer tracy.end();
 
@@ -1891,11 +1906,6 @@ fn parseLdScript(self: *Elf, lib: SystemLib, ctx: *ParseErrorCtx) ParseError!voi
     defer script.deinit(gpa);
     try script.parse(data, self);
 
-    if (script.cpu_arch) |cpu_arch| {
-        ctx.detected_cpu_arch = cpu_arch;
-        if (ctx.detected_cpu_arch != self.base.options.target.cpu.arch) return error.InvalidCpuArch;
-    }
-
     const lib_dirs = self.base.options.lib_dirs;
 
     var arena_allocator = std.heap.ArenaAllocator.init(gpa);
@@ -1949,11 +1959,17 @@ fn parseLdScript(self: *Elf, lib: SystemLib, ctx: *ParseErrorCtx) ParseError!voi
         }
 
         const full_path = test_path.items;
-        var scr_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined };
         self.parseLibrary(.{
             .needed = scr_obj.needed,
             .path = full_path,
-        }, false, &scr_ctx) catch |err| try self.handleAndReportParseError(full_path, err, &scr_ctx);
+        }, false) catch |err| switch (err) {
+            error.LinkFail, error.InvalidCpuArch => {}, // already reported
+            else => |e| try self.reportParseError(
+                full_path,
+                "unexpected error: parsing library failed with error {s}",
+                .{@errorName(e)},
+            ),
+        };
     }
 }
 
@@ -3009,6 +3025,8 @@ fn writePhdrTable(self: *Elf) !void {
 }
 
 fn writeElfHeader(self: *Elf) !void {
+    if (self.misc_errors.items.len > 0) return; // We had errors, so skip flushing to render the output unusable
+
     var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
 
     var index: usize = 0;
@@ -6047,33 +6065,6 @@ fn reportMissingLibraryError(
     }
 }
 
-const ParseErrorCtx = struct {
-    detected_cpu_arch: std.Target.Cpu.Arch,
-};
-
-fn handleAndReportParseError(
-    self: *Elf,
-    path: []const u8,
-    err: ParseError,
-    ctx: *const ParseErrorCtx,
-) error{OutOfMemory}!void {
-    const cpu_arch = self.base.options.target.cpu.arch;
-    switch (err) {
-        error.LinkFail => {}, // already reported
-        error.UnknownFileType => try self.reportParseError(path, "unknown file type", .{}),
-        error.InvalidCpuArch => try self.reportParseError(
-            path,
-            "invalid cpu architecture: expected '{s}', but found '{s}'",
-            .{ @tagName(cpu_arch), @tagName(ctx.detected_cpu_arch) },
-        ),
-        else => |e| try self.reportParseError(
-            path,
-            "unexpected error: parsing object failed with error {s}",
-            .{@errorName(e)},
-        ),
-    }
-}
-
 fn reportParseError(
     self: *Elf,
     path: []const u8,