From c2a0a882842d8cc9d0ad29781fe1e13c1a5880cd Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Fri, 12 Jan 2024 10:24:02 +0100 Subject: [PATCH] macho: report duplicate symbols --- src/link/MachO.zig | 120 ++++++++++++++++++-------------------- src/link/MachO/Object.zig | 18 ++++++ 2 files changed, 76 insertions(+), 62 deletions(-) diff --git a/src/link/MachO.zig b/src/link/MachO.zig index af5d165665914398517e28d30b90daec58a1d9a2..291183213286a80c313db3a4277588682ce2bbfb 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -509,6 +509,14 @@ pub fn flushModule(self: *MachO, arena: Allocator, prog_node: *std.Progress.Node try dead_strip.gcAtoms(self); } + self.checkDuplicates() catch |err| switch (err) { + error.HasDuplicates => return error.FlushFailure, + else => |e| { + try self.reportUnexpectedError("unexpected error while checking for duplicate symbol definitions", .{}); + return e; + }, + }; + self.markImportsAndExports(); self.deadStripDylibs(); @@ -1414,6 +1422,24 @@ fn claimUnresolved(self: *MachO) error{OutOfMemory}!void { } } +fn checkDuplicates(self: *MachO) !void { + const gpa = self.base.comp.gpa; + + var dupes = std.AutoArrayHashMap(Symbol.Index, std.ArrayListUnmanaged(File.Index)).init(gpa); + defer { + for (dupes.values()) |*list| { + list.deinit(gpa); + } + dupes.deinit(); + } + + for (self.objects.items) |index| { + try self.getFile(index).?.object.checkDuplicates(&dupes, self); + } + + try self.reportDuplicates(dupes); +} + fn markImportsAndExports(self: *MachO) void { for (self.objects.items) |index| { for (self.getFile(index).?.getSymbols()) |sym_index| { @@ -3468,68 +3494,38 @@ fn reportUnexpectedError(self: *MachO, comptime format: []const u8, args: anytyp try err.addNote(self, "please report this as a linker bug on https://github.com/ziglang/zig/issues/new/choose", .{}); } -// fn reportSymbolCollision( -// self: *MachO, -// first: SymbolWithLoc, -// other: SymbolWithLoc, -// ) error{OutOfMemory}!void { -// const comp = self.base.comp; -// const gpa = comp.gpa; -// try comp.link_errors.ensureUnusedCapacity(gpa, 1); - -// var notes = try std.ArrayList(File.ErrorMsg).initCapacity(gpa, 2); -// defer notes.deinit(); - -// if (first.getFile()) |file| { -// const note = try std.fmt.allocPrint(gpa, "first definition in {s}", .{ -// self.objects.items[file].name, -// }); -// notes.appendAssumeCapacity(.{ .msg = note }); -// } -// if (other.getFile()) |file| { -// const note = try std.fmt.allocPrint(gpa, "next definition in {s}", .{ -// self.objects.items[file].name, -// }); -// notes.appendAssumeCapacity(.{ .msg = note }); -// } - -// var err_msg = File.ErrorMsg{ .msg = try std.fmt.allocPrint(gpa, "symbol {s} defined multiple times", .{ -// self.getSymbolName(first), -// }) }; -// err_msg.notes = try notes.toOwnedSlice(); - -// comp.link_errors.appendAssumeCapacity(err_msg); -// } - -// fn reportUnhandledSymbolType(self: *MachO, sym_with_loc: SymbolWithLoc) error{OutOfMemory}!void { -// const comp = self.base.comp; -// const gpa = comp.gpa; -// try comp.link_errors.ensureUnusedCapacity(gpa, 1); - -// const notes = try gpa.alloc(File.ErrorMsg, 1); -// errdefer gpa.free(notes); - -// const file = sym_with_loc.getFile().?; -// notes[0] = .{ .msg = try std.fmt.allocPrint(gpa, "defined in {s}", .{self.objects.items[file].name}) }; - -// const sym = self.getSymbol(sym_with_loc); -// const sym_type = if (sym.stab()) -// "stab" -// else if (sym.indr()) -// "indirect" -// else if (sym.abs()) -// "absolute" -// else -// unreachable; - -// comp.link_errors.appendAssumeCapacity(.{ -// .msg = try std.fmt.allocPrint(gpa, "unhandled symbol type: '{s}' has type {s}", .{ -// self.getSymbolName(sym_with_loc), -// sym_type, -// }), -// .notes = notes, -// }); -// } +fn reportDuplicates(self: *MachO, dupes: anytype) error{ HasDuplicates, OutOfMemory }!void { + const tracy = trace(@src()); + defer tracy.end(); + + const max_notes = 4; + + var has_dupes = false; + var it = dupes.iterator(); + while (it.next()) |entry| { + const sym = self.getSymbol(entry.key_ptr.*); + const notes = entry.value_ptr.*; + const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes); + + var err = try self.addErrorWithNotes(nnotes); + try err.addMsg(self, "duplicate symbol definition: {s}", .{sym.getName(self)}); + + var inote: usize = 0; + while (inote < @min(notes.items.len, max_notes)) : (inote += 1) { + const file = self.getFile(notes.items[inote]).?; + try err.addNote(self, "defined by {}", .{file.fmtPath()}); + } + + if (notes.items.len > max_notes) { + const remaining = notes.items.len - max_notes; + try err.addNote(self, "defined {d} more times", .{remaining}); + } + + has_dupes = true; + } + + if (has_dupes) return error.HasDuplicates; +} pub fn getDebugSymbols(self: *MachO) ?*DebugSymbols { if (self.d_sym) |*ds| { diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index a89e75d533e0d459253e2264505e64de62bdaf39..136b39d61746abfeb9ad79b5856c30482b7e960f 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -1070,6 +1070,24 @@ pub fn markLive(self: *Object, macho_file: *MachO) void { } } +pub fn checkDuplicates(self: *Object, dupes: anytype, macho_file: *MachO) error{OutOfMemory}!void { + for (self.symbols.items, 0..) |index, nlist_idx| { + const sym = macho_file.getSymbol(index); + if (sym.visibility != .global) continue; + const file = sym.getFile(macho_file) orelse continue; + if (file.getIndex() == self.index) continue; + + const nlist = self.symtab.items(.nlist)[nlist_idx]; + if (!nlist.undf() and !nlist.tentative() and !(nlist.weakDef() or nlist.pext())) { + const gop = try dupes.getOrPut(index); + if (!gop.found_existing) { + gop.value_ptr.* = .{}; + } + try gop.value_ptr.append(macho_file.base.comp.gpa, self.index); + } + } +} + pub fn scanRelocs(self: Object, macho_file: *MachO) !void { const tracy = trace(@src()); defer tracy.end(); -- 2.54.0