diff --git a/src/Compilation.zig b/src/Compilation.zig index 069cc8a2c7..a30d97f1da 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -2609,6 +2609,9 @@ pub fn totalErrorCount(self: *Compilation) u32 { } total += @intFromBool(self.link_error_flags.missing_libc); + // Misc linker errors + total += self.bin_file.miscErrors().len; + // Compile log errors only count if there are no other errors. if (total == 0) { if (self.bin_file.options.module) |module| { @@ -2759,6 +2762,19 @@ pub fn getAllErrorsAlloc(self: *Compilation) !ErrorBundle { })); } + for (self.bin_file.miscErrors()) |link_err| { + try bundle.addRootErrorMessage(.{ + .msg = try bundle.addString(link_err.msg), + .notes_len = @intCast(link_err.notes.len), + }); + const notes_start = try bundle.reserveNotes(@intCast(link_err.notes.len)); + for (link_err.notes, 0..) |note, i| { + bundle.extra.items[notes_start + i] = @intFromEnum(try bundle.addErrorMessage(.{ + .msg = try bundle.addString(note.msg), + })); + } + } + if (self.bin_file.options.module) |module| { if (bundle.root_list.items.len == 0 and module.compile_log_decls.count() != 0) { const keys = module.compile_log_decls.keys(); diff --git a/src/link.zig b/src/link.zig index 48ad78364c..724c1500f4 100644 --- a/src/link.zig +++ b/src/link.zig @@ -866,6 +866,13 @@ pub const File = struct { } } + pub fn miscErrors(base: *File) []const ErrorMsg { + switch (base.tag) { + .macho => return @fieldParentPtr(MachO, "base", base).misc_errors.items, + else => return &.{}, + } + } + pub const UpdateDeclExportsError = error{ OutOfMemory, AnalysisFail, @@ -1129,6 +1136,19 @@ pub const File = struct { missing_libc: bool = false, }; + pub const ErrorMsg = struct { + msg: []const u8, + notes: []ErrorMsg = &.{}, + + pub fn deinit(self: *ErrorMsg, gpa: Allocator) void { + for (self.notes) |*note| { + note.deinit(gpa); + } + gpa.free(self.notes); + gpa.free(self.msg); + } + }; + pub const LazySymbol = struct { pub const Kind = enum { code, const_data }; diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 440c26260a..2b765ab6b9 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -52,8 +52,8 @@ const Value = @import("../value.zig").Value; pub const DebugSymbols = @import("MachO/DebugSymbols.zig"); -const Bind = @import("MachO/dyld_info/bind.zig").Bind(*const MachO, MachO.SymbolWithLoc); -const LazyBind = @import("MachO/dyld_info/bind.zig").LazyBind(*const MachO, MachO.SymbolWithLoc); +const Bind = @import("MachO/dyld_info/bind.zig").Bind(*const MachO, SymbolWithLoc); +const LazyBind = @import("MachO/dyld_info/bind.zig").LazyBind(*const MachO, SymbolWithLoc); const Rebase = @import("MachO/dyld_info/Rebase.zig"); pub const base_tag: File.Tag = File.Tag.macho; @@ -154,6 +154,7 @@ got_table: TableSection(SymbolWithLoc) = .{}, stub_table: TableSection(SymbolWithLoc) = .{}, error_flags: File.ErrorFlags = File.ErrorFlags{}, +misc_errors: std.ArrayListUnmanaged(File.ErrorMsg) = .{}, segment_table_dirty: bool = false, got_table_count_dirty: bool = false, @@ -295,6 +296,12 @@ pub const SymbolWithLoc = extern struct { } }; +pub const SymbolResolver = struct { + arena: Allocator, + table: std.StringHashMap(u32), + unresolved: std.AutoArrayHashMap(u32, void), +}; + const HotUpdateState = struct { mach_task: ?std.os.darwin.MachTask = null, }; @@ -1856,6 +1863,11 @@ pub fn deinit(self: *MachO) void { bindings.deinit(gpa); } self.bindings.deinit(gpa); + + for (self.misc_errors.items) |*err| { + err.deinit(gpa); + } + self.misc_errors.deinit(gpa); } fn freeAtom(self: *MachO, atom_index: Atom.Index) void { @@ -4021,6 +4033,38 @@ pub inline fn getPageSize(cpu_arch: std.Target.Cpu.Arch) u16 { }; } +pub fn reportUndefined(self: *MachO, ctx: anytype, resolver: *const SymbolResolver) !void { + const count = resolver.unresolved.count(); + if (count == 0) return; + + const gpa = self.base.allocator; + + try self.misc_errors.ensureUnusedCapacity(gpa, count); + + for (resolver.unresolved.keys()) |global_index| { + const global = ctx.globals.items[global_index]; + const sym_name = ctx.getSymbolName(global); + + const nnotes: usize = if (global.getFile() == null) @as(usize, 0) else 1; + var notes = try std.ArrayList(File.ErrorMsg).initCapacity(gpa, nnotes); + defer notes.deinit(); + + if (global.getFile()) |file| { + const note = try std.fmt.allocPrint(gpa, "referenced in {s}", .{ctx.objects.items[file].name}); + notes.appendAssumeCapacity(.{ .msg = note }); + } + + var err_msg = File.ErrorMsg{ + .msg = try std.fmt.allocPrint(gpa, "undefined reference to symbol {s}", .{sym_name}), + }; + err_msg.notes = try notes.toOwnedSlice(); + + self.misc_errors.appendAssumeCapacity(err_msg); + } + + return error.FlushFailure; +} + /// Binary search pub fn bsearch(comptime T: type, haystack: []align(1) const T, predicate: anytype) usize { if (!@hasDecl(@TypeOf(predicate), "predicate")) diff --git a/src/link/MachO/dead_strip.zig b/src/link/MachO/dead_strip.zig index 42d1604a0d..282e3a21c7 100644 --- a/src/link/MachO/dead_strip.zig +++ b/src/link/MachO/dead_strip.zig @@ -13,7 +13,7 @@ const AtomIndex = @import("zld.zig").AtomIndex; const Atom = @import("ZldAtom.zig"); const MachO = @import("../MachO.zig"); const SymbolWithLoc = MachO.SymbolWithLoc; -const SymbolResolver = @import("zld.zig").SymbolResolver; +const SymbolResolver = MachO.SymbolResolver; const UnwindInfo = @import("UnwindInfo.zig"); const Zld = @import("zld.zig").Zld; diff --git a/src/link/MachO/zld.zig b/src/link/MachO/zld.zig index 5307a3ac02..9e2271bccf 100644 --- a/src/link/MachO/zld.zig +++ b/src/link/MachO/zld.zig @@ -33,6 +33,7 @@ const LibStub = @import("../tapi.zig").LibStub; const Object = @import("Object.zig"); const StringTable = @import("../strtab.zig").StringTable; const SymbolWithLoc = MachO.SymbolWithLoc; +const SymbolResolver = MachO.SymbolResolver; const Trie = @import("Trie.zig"); const UnwindInfo = @import("UnwindInfo.zig"); @@ -788,7 +789,6 @@ pub const Zld = struct { const global_index = resolver.unresolved.keys()[next_sym]; const global = self.globals.items[global_index]; const sym = self.getSymbolPtr(global); - const sym_name = self.getSymbolName(global); if (sym.discarded()) { sym.* = .{ @@ -811,11 +811,6 @@ pub const Zld = struct { continue; } - log.err("undefined reference to symbol '{s}'", .{sym_name}); - if (global.getFile()) |file| { - log.err(" first referenced in '{s}'", .{self.objects.items[file].name}); - } - next_sym += 1; } } @@ -3022,12 +3017,6 @@ const IndirectPointer = struct { } }; -pub const SymbolResolver = struct { - arena: Allocator, - table: std.StringHashMap(u32), - unresolved: std.AutoArrayHashMap(u32, void), -}; - pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void { const tracy = trace(@src()); defer tracy.end(); @@ -3419,10 +3408,7 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr .unresolved = std.AutoArrayHashMap(u32, void).init(arena), }; try zld.resolveSymbols(&resolver); - - if (resolver.unresolved.count() > 0) { - return error.UndefinedSymbolReference; - } + try macho_file.reportUndefined(&zld, &resolver); if (options.output_mode == .Exe) { const entry_name = options.entry orelse load_commands.default_entry_point;