From da25ed95fce32449f70942ea77aa5e00e75dbbdd Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 3 Dec 2024 17:35:47 -0800 Subject: [PATCH] macho linker conforms to explicit error sets, again --- src/link.zig | 19 ++++++++++++++++--- src/link/Dwarf.zig | 6 ++---- src/link/Elf.zig | 15 +++++++++++++-- src/link/Elf/ZigObject.zig | 24 ++++++++++++------------ src/link/Elf/relocatable.zig | 6 +++--- src/link/MachO.zig | 8 ++++---- src/link/MachO/ZigObject.zig | 35 +++++++++++++++++++++++------------ 7 files changed, 73 insertions(+), 40 deletions(-) diff --git a/src/link.zig b/src/link.zig index adcad570d4..ce82aa708f 100644 --- a/src/link.zig +++ b/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"); diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 99d95de49d..bcf2f9955a 100644 --- a/src/link/Dwarf.zig +++ b/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; diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 543cd80f6a..be9ab135a8 100644 --- a/src/link/Elf.zig +++ b/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(); diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index a76bcb83ac..179cc72ff1 100644 --- a/src/link/Elf/ZigObject.zig +++ b/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) { diff --git a/src/link/Elf/relocatable.zig b/src/link/Elf/relocatable.zig index 83bfe6d1f7..e8f9414da6 100644 --- a/src/link/Elf/relocatable.zig +++ b/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; diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 6090f6381a..d93867695d 100644 --- a/src/link/MachO.zig +++ b/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); diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index f63e1cd973..3d99baad06 100644 --- a/src/link/MachO/ZigObject.zig +++ b/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(