Commit da25ed95fc

Andrew Kelley <andrew@ziglang.org>
2024-12-04 02:35:47
macho linker conforms to explicit error sets, again
1 parent 6235cc3
src/link/Elf/relocatable.zig
@@ -1,4 +1,4 @@
-pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation) link.File.FlushError!void {
+pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation) !void {
     const gpa = comp.gpa;
     const diags = &comp.link_diags;
 
@@ -130,7 +130,7 @@ pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation) link.File.FlushError!v
     if (diags.hasErrors()) return error.LinkFailure;
 }
 
-pub fn flushObject(elf_file: *Elf, comp: *Compilation) link.File.FlushError!void {
+pub fn flushObject(elf_file: *Elf, comp: *Compilation) !void {
     const diags = &comp.link_diags;
 
     if (diags.hasErrors()) return error.LinkFailure;
@@ -259,7 +259,7 @@ fn initComdatGroups(elf_file: *Elf) !void {
     }
 }
 
-fn updateSectionSizes(elf_file: *Elf) link.File.FlushError!void {
+fn updateSectionSizes(elf_file: *Elf) !void {
     const slice = elf_file.sections.slice();
     for (slice.items(.atom_list_2)) |*atom_list| {
         if (atom_list.atoms.keys().len == 0) continue;
src/link/Elf/ZigObject.zig
@@ -264,7 +264,7 @@ pub fn deinit(self: *ZigObject, allocator: Allocator) void {
     }
 }
 
-pub fn flush(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) link.File.FlushError!void {
+pub fn flush(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !void {
     // Handle any lazy symbols that were emitted by incremental compilation.
     if (self.lazy_syms.getPtr(.anyerror_type)) |metadata| {
         const pt: Zcu.PerThread = .activate(elf_file.base.comp.zcu.?, tid);
@@ -279,7 +279,7 @@ pub fn flush(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) link.File.
             metadata.text_symbol_index,
         ) catch |err| return switch (err) {
             error.CodegenFail => error.LinkFailure,
-            else => |e| e,
+            else => |e| return e,
         };
         if (metadata.rodata_state != .unused) self.updateLazySymbol(
             elf_file,
@@ -288,7 +288,7 @@ pub fn flush(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) link.File.
             metadata.rodata_symbol_index,
         ) catch |err| return switch (err) {
             error.CodegenFail => error.LinkFailure,
-            else => |e| e,
+            else => |e| return e,
         };
     }
     for (self.lazy_syms.values()) |*metadata| {
@@ -1263,7 +1263,7 @@ fn updateNavCode(
     shdr_index: u32,
     code: []const u8,
     stt_bits: u8,
-) !void {
+) link.File.UpdateNavError!void {
     const zcu = pt.zcu;
     const gpa = zcu.gpa;
     const ip = &zcu.intern_pool;
@@ -1342,7 +1342,7 @@ fn updateNavCode(
     const shdr = elf_file.sections.items(.shdr)[shdr_index];
     if (shdr.sh_type != elf.SHT_NOBITS) {
         const file_offset = atom_ptr.offset(elf_file);
-        try elf_file.base.file.?.pwriteAll(code, file_offset);
+        try elf_file.pwriteAll(code, file_offset);
         log.debug("writing {} from 0x{x} to 0x{x}", .{ nav.fqn.fmt(ip), file_offset, file_offset + code.len });
     }
 }
@@ -1355,7 +1355,7 @@ fn updateTlv(
     sym_index: Symbol.Index,
     shndx: u32,
     code: []const u8,
-) !void {
+) link.File.UpdateNavError!void {
     const zcu = pt.zcu;
     const ip = &zcu.intern_pool;
     const gpa = zcu.gpa;
@@ -1394,7 +1394,7 @@ fn updateTlv(
     const shdr = elf_file.sections.items(.shdr)[shndx];
     if (shdr.sh_type != elf.SHT_NOBITS) {
         const file_offset = atom_ptr.offset(elf_file);
-        try elf_file.base.file.?.pwriteAll(code, file_offset);
+        try elf_file.pwriteAll(code, file_offset);
         log.debug("writing TLV {s} from 0x{x} to 0x{x}", .{
             atom_ptr.name(elf_file),
             file_offset,
@@ -1617,7 +1617,7 @@ fn updateLazySymbol(
     pt: Zcu.PerThread,
     sym: link.File.LazySymbol,
     symbol_index: Symbol.Index,
-) link.File.FlushError!void {
+) !void {
     const zcu = pt.zcu;
     const gpa = zcu.gpa;
 
@@ -1698,7 +1698,7 @@ fn updateLazySymbol(
     local_sym.value = 0;
     local_esym.st_value = 0;
 
-    try elf_file.base.file.?.pwriteAll(code, atom_ptr.offset(elf_file));
+    try elf_file.pwriteAll(code, atom_ptr.offset(elf_file));
 }
 
 const LowerConstResult = union(enum) {
@@ -1750,7 +1750,7 @@ fn lowerConst(
     try self.allocateAtom(atom_ptr, true, elf_file);
     errdefer self.freeNavMetadata(elf_file, sym_index);
 
-    try elf_file.base.file.?.pwriteAll(code, atom_ptr.offset(elf_file));
+    try elf_file.pwriteAll(code, atom_ptr.offset(elf_file));
 
     return .{ .ok = sym_index };
 }
@@ -1898,7 +1898,7 @@ fn trampolineSize(cpu_arch: std.Target.Cpu.Arch) u64 {
     return len;
 }
 
-fn writeTrampoline(tr_sym: Symbol, target: Symbol, elf_file: *Elf) !void {
+fn writeTrampoline(tr_sym: Symbol, target: Symbol, elf_file: *Elf) link.File.UpdateNavError!void {
     const atom_ptr = tr_sym.atom(elf_file).?;
     const fileoff = atom_ptr.offset(elf_file);
     const source_addr = tr_sym.address(.{}, elf_file);
@@ -1908,7 +1908,7 @@ fn writeTrampoline(tr_sym: Symbol, target: Symbol, elf_file: *Elf) !void {
         .x86_64 => try x86_64.writeTrampolineCode(source_addr, target_addr, &buf),
         else => @panic("TODO implement write trampoline for this CPU arch"),
     };
-    try elf_file.base.file.?.pwriteAll(out, fileoff);
+    try elf_file.pwriteAll(out, fileoff);
 
     if (elf_file.base.child_pid) |pid| {
         switch (builtin.os.tag) {
src/link/MachO/ZigObject.zig
@@ -559,18 +559,26 @@ pub fn flushModule(self: *ZigObject, macho_file: *MachO, tid: Zcu.PerThread.Id)
 
         // Most lazy symbols can be updated on first use, but
         // anyerror needs to wait for everything to be flushed.
-        if (metadata.text_state != .unused) try self.updateLazySymbol(
+        if (metadata.text_state != .unused) self.updateLazySymbol(
             macho_file,
             pt,
             .{ .kind = .code, .ty = .anyerror_type },
             metadata.text_symbol_index,
-        );
-        if (metadata.const_state != .unused) try self.updateLazySymbol(
+        ) catch |err| switch (err) {
+            error.OutOfMemory => return error.OutOfMemory,
+            error.LinkFailure => return error.LinkFailure,
+            else => |e| return diags.fail("failed to update lazy symbol: {s}", .{@errorName(e)}),
+        };
+        if (metadata.const_state != .unused) self.updateLazySymbol(
             macho_file,
             pt,
             .{ .kind = .const_data, .ty = .anyerror_type },
             metadata.const_symbol_index,
-        );
+        ) catch |err| switch (err) {
+            error.OutOfMemory => return error.OutOfMemory,
+            error.LinkFailure => return error.LinkFailure,
+            else => |e| return diags.fail("failed to update lazy symbol: {s}", .{@errorName(e)}),
+        };
     }
     for (self.lazy_syms.values()) |*metadata| {
         if (metadata.text_state != .unused) metadata.text_state = .flushed;
@@ -803,7 +811,7 @@ pub fn updateFunc(
         .ok => code_buffer.items,
         .fail => |em| {
             try zcu.failed_codegen.put(gpa, func.owner_nav, em);
-            return;
+            return error.CodegenFail;
         },
     };
 
@@ -855,7 +863,8 @@ pub fn updateFunc(
         }
         const target_sym = self.symbols.items[sym_index];
         const source_sym = self.symbols.items[target_sym.getExtra(macho_file).trampoline];
-        try writeTrampoline(source_sym, target_sym, macho_file);
+        writeTrampoline(source_sym, target_sym, macho_file) catch |err|
+            return macho_file.base.cgFail(func.owner_nav, "failed to write trampoline: {s}", .{@errorName(err)});
     }
 }
 
@@ -955,7 +964,6 @@ fn updateNavCode(
         else => |a| a.maxStrict(target_util.minFunctionAlignment(target)),
     };
 
-    const diags = &macho_file.base.comp.link_diags;
     const sect = &macho_file.sections.items(.header)[sect_index];
     const sym = &self.symbols.items[sym_index];
     const nlist = &self.symtab.items(.nlist)[sym.nlist_idx];
@@ -984,7 +992,8 @@ fn updateNavCode(
         const need_realloc = code.len > capacity or !required_alignment.check(atom.value);
 
         if (need_realloc) {
-            atom.grow(macho_file) catch |err| return diags.fail("failed to grow atom: {s}", .{@errorName(err)});
+            atom.grow(macho_file) catch |err|
+                return macho_file.base.cgFail(nav_index, "failed to grow atom: {s}", .{@errorName(err)});
             log.debug("growing {} from 0x{x} to 0x{x}", .{ nav.fqn.fmt(ip), old_vaddr, atom.value });
             if (old_vaddr != atom.value) {
                 sym.value = 0;
@@ -997,7 +1006,8 @@ fn updateNavCode(
             sect.size = needed_size;
         }
     } else {
-        try atom.allocate(macho_file);
+        atom.allocate(macho_file) catch |err|
+            return macho_file.base.cgFail(nav_index, "failed to allocate atom: {s}", .{@errorName(err)});
         errdefer self.freeNavMetadata(macho_file, sym_index);
 
         sym.value = 0;
@@ -1006,7 +1016,8 @@ fn updateNavCode(
 
     if (!sect.isZerofill()) {
         const file_offset = sect.offset + atom.value;
-        try macho_file.pwriteAll(code, file_offset);
+        macho_file.base.file.?.pwriteAll(code, file_offset) catch |err|
+            return macho_file.base.cgFail(nav_index, "failed to write output file: {s}", .{@errorName(err)});
     }
 }
 
@@ -1353,7 +1364,7 @@ fn updateLazySymbol(
     pt: Zcu.PerThread,
     lazy_sym: link.File.LazySymbol,
     symbol_index: Symbol.Index,
-) error{ OutOfMemory, LinkFailure }!void {
+) !void {
     const zcu = pt.zcu;
     const gpa = zcu.gpa;
     const diags = &macho_file.base.comp.link_diags;
@@ -1494,7 +1505,7 @@ fn writeTrampoline(tr_sym: Symbol, target: Symbol, macho_file: *MachO) !void {
         .x86_64 => try x86_64.writeTrampolineCode(source_addr, target_addr, &buf),
         else => @panic("TODO implement write trampoline for this CPU arch"),
     };
-    try macho_file.pwriteAll(out, fileoff);
+    try macho_file.base.file.?.pwriteAll(out, fileoff);
 }
 
 pub fn getOrCreateMetadataForNav(
src/link/Dwarf.zig
@@ -26,9 +26,7 @@ pub const UpdateError = error{
     OutOfMemory,
 };
 
-pub const FlushError =
-    UpdateError ||
-    std.process.GetCwdError;
+pub const FlushError = UpdateError || std.process.GetCwdError;
 
 pub const RelocError =
     std.fs.File.PWriteError;
@@ -4312,7 +4310,7 @@ fn refAbbrevCode(dwarf: *Dwarf, abbrev_code: AbbrevCode) UpdateError!@typeInfo(A
     return @intFromEnum(abbrev_code);
 }
 
-pub fn flushModule(dwarf: *Dwarf, pt: Zcu.PerThread) FlushError!void {
+pub fn flushModule(dwarf: *Dwarf, pt: Zcu.PerThread) !void {
     const zcu = pt.zcu;
     const ip = &zcu.intern_pool;
 
src/link/Elf.zig
@@ -807,7 +807,6 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
     defer tracy.end();
 
     const comp = self.base.comp;
-    const gpa = comp.gpa;
     const diags = &comp.link_diags;
 
     if (self.llvm_object) |llvm_object| {
@@ -821,6 +820,18 @@ pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_nod
     const sub_prog_node = prog_node.start("ELF Flush", 0);
     defer sub_prog_node.end();
 
+    return flushModuleInner(self, arena, tid) catch |err| switch (err) {
+        error.OutOfMemory => return error.OutOfMemory,
+        error.LinkFailure => return error.LinkFailure,
+        else => |e| return diags.fail("ELF flush failed: {s}", .{@errorName(e)}),
+    };
+}
+
+fn flushModuleInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void {
+    const comp = self.base.comp;
+    const gpa = comp.gpa;
+    const diags = &comp.link_diags;
+
     const module_obj_path: ?Path = if (self.base.zcu_object_sub_path) |path| .{
         .root_dir = self.base.emit.root_dir,
         .sub_path = if (fs.path.dirname(self.base.emit.sub_path)) |dirname|
@@ -2432,7 +2443,7 @@ pub fn addCommentString(self: *Elf) !void {
     self.comment_merge_section_index = msec_index;
 }
 
-pub fn resolveMergeSections(self: *Elf) link.File.FlushError!void {
+pub fn resolveMergeSections(self: *Elf) !void {
     const tracy = trace(@src());
     defer tracy.end();
 
src/link/MachO.zig
@@ -3423,7 +3423,7 @@ fn initMetadata(self: *MachO, options: InitMetadataOptions) !void {
     };
 }
 
-pub fn growSection(self: *MachO, sect_index: u8, needed_size: u64) error{ OutOfMemory, LinkFailure }!void {
+pub fn growSection(self: *MachO, sect_index: u8, needed_size: u64) !void {
     if (self.base.isRelocatable()) {
         try self.growSectionRelocatable(sect_index, needed_size);
     } else {
@@ -3431,7 +3431,7 @@ pub fn growSection(self: *MachO, sect_index: u8, needed_size: u64) error{ OutOfM
     }
 }
 
-fn growSectionNonRelocatable(self: *MachO, sect_index: u8, needed_size: u64) error{ OutOfMemory, LinkFailure }!void {
+fn growSectionNonRelocatable(self: *MachO, sect_index: u8, needed_size: u64) !void {
     const diags = &self.base.comp.link_diags;
     const sect = &self.sections.items(.header)[sect_index];
 
@@ -3480,7 +3480,7 @@ fn growSectionNonRelocatable(self: *MachO, sect_index: u8, needed_size: u64) err
     seg.vmsize = needed_size;
 }
 
-fn growSectionRelocatable(self: *MachO, sect_index: u8, needed_size: u64) error{ OutOfMemory, LinkFailure }!void {
+fn growSectionRelocatable(self: *MachO, sect_index: u8, needed_size: u64) !void {
     const sect = &self.sections.items(.header)[sect_index];
 
     if (!sect.isZerofill()) {
@@ -3490,7 +3490,7 @@ fn growSectionRelocatable(self: *MachO, sect_index: u8, needed_size: u64) error{
             sect.size = 0;
 
             // Must move the entire section.
-            const alignment = try self.alignPow(sect.@"align");
+            const alignment = try math.powi(u32, 2, sect.@"align");
             const new_offset = try self.findFreeSpace(needed_size, alignment);
             const new_addr = self.findFreeSpaceVirtual(needed_size, alignment);
 
src/link.zig
@@ -632,15 +632,14 @@ pub const File = struct {
     pub const UpdateDebugInfoError = Dwarf.UpdateError;
     pub const FlushDebugInfoError = Dwarf.FlushError;
 
+    /// Note that `LinkFailure` is not a member of this error set because the error message
+    /// must be attached to `Zcu.failed_codegen` rather than `Compilation.link_diags`.
     pub const UpdateNavError = error{
         Overflow,
         OutOfMemory,
         /// Indicates the error is already reported and stored in
         /// `failed_codegen` on the Zcu.
         CodegenFail,
-        /// Indicates the error is already reported and stored in `link_diags`
-        /// on the Compilation.
-        LinkFailure,
     };
 
     /// Called from within CodeGen to retrieve the symbol index of a global symbol.
@@ -1284,6 +1283,20 @@ pub const File = struct {
         }, llvm_object, prog_node);
     }
 
+    pub fn cgFail(
+        base: *File,
+        nav_index: InternPool.Nav.Index,
+        comptime format: []const u8,
+        args: anytype,
+    ) error{ CodegenFail, OutOfMemory } {
+        @branchHint(.cold);
+        const zcu = base.comp.zcu.?;
+        const gpa = zcu.gpa;
+        try zcu.failed_codegen.ensureUnusedCapacity(gpa, 1);
+        const msg = try Zcu.ErrorMsg.create(gpa, zcu.navSrcLoc(nav_index), format, args);
+        zcu.failed_codegen.putAssumeCapacityNoClobber(gpa, nav_index, msg);
+    }
+
     pub const C = @import("link/C.zig");
     pub const Coff = @import("link/Coff.zig");
     pub const Plan9 = @import("link/Plan9.zig");