Commit 7a96907b92

Jakub Konka <kubkon@jakubkonka.com>
2024-01-14 19:20:11
elf: check for and report duplicate symbol definitions
1 parent 89d4ac6
Changed files (3)
src/link/Elf/Object.zig
@@ -581,30 +581,30 @@ pub fn markEhFrameAtomsDead(self: Object, elf_file: *Elf) void {
     }
 }
 
-pub fn checkDuplicates(self: *Object, elf_file: *Elf) void {
+pub fn checkDuplicates(self: *Object, dupes: anytype, elf_file: *Elf) error{OutOfMemory}!void {
     const first_global = self.first_global orelse return;
     for (self.globals(), 0..) |index, i| {
-        const sym_idx = @as(u32, @intCast(first_global + i));
-        const this_sym = self.symtab.items[sym_idx];
+        const sym_idx = first_global + i;
+        const sym = self.symtab.items[sym_idx];
         const global = elf_file.symbol(index);
-        const global_file = global.getFile(elf_file) orelse continue;
+        const global_file = global.file(elf_file) orelse continue;
 
-        if (self.index == global_file.getIndex() or
-            this_sym.st_shndx == elf.SHN_UNDEF or
-            this_sym.st_bind() == elf.STB_WEAK or
-            this_sym.st_shndx == elf.SHN_COMMON) continue;
+        if (self.index == global_file.index() or
+            sym.st_shndx == elf.SHN_UNDEF or
+            sym.st_bind() == elf.STB_WEAK or
+            sym.st_shndx == elf.SHN_COMMON) continue;
 
-        if (this_sym.st_shndx != elf.SHN_ABS) {
-            const atom_index = self.atoms.items[this_sym.st_shndx];
+        if (sym.st_shndx != elf.SHN_ABS) {
+            const atom_index = self.atoms.items[sym.st_shndx];
             const atom = elf_file.atom(atom_index) orelse continue;
             if (!atom.flags.alive) continue;
         }
 
-        elf_file.base.fatal("multiple definition: {}: {}: {s}", .{
-            self.fmtPath(),
-            global_file.fmtPath(),
-            global.getName(elf_file),
-        });
+        const gop = try dupes.getOrPut(index);
+        if (!gop.found_existing) {
+            gop.value_ptr.* = .{};
+        }
+        try gop.value_ptr.append(elf_file.base.comp.gpa, self.index);
     }
 }
 
src/link/Elf/ZigObject.zig
@@ -451,6 +451,32 @@ pub fn markLive(self: *ZigObject, elf_file: *Elf) void {
     }
 }
 
+pub fn checkDuplicates(self: *ZigObject, dupes: anytype, elf_file: *Elf) error{OutOfMemory}!void {
+    for (self.globals(), 0..) |index, i| {
+        const esym = self.global_esyms.items(.elf_sym)[i];
+        const shndx = self.global_esyms.items(.shndx)[i];
+        const global = elf_file.symbol(index);
+        const global_file = global.file(elf_file) orelse continue;
+
+        if (self.index == global_file.index() or
+            esym.st_shndx == elf.SHN_UNDEF or
+            esym.st_bind() == elf.STB_WEAK or
+            esym.st_shndx == elf.SHN_COMMON) continue;
+
+        if (esym.st_shndx == SHN_ATOM) {
+            const atom_index = self.atoms.items[shndx];
+            const atom = elf_file.atom(atom_index) orelse continue;
+            if (!atom.flags.alive) continue;
+        }
+
+        const gop = try dupes.getOrPut(index);
+        if (!gop.found_existing) {
+            gop.value_ptr.* = .{};
+        }
+        try gop.value_ptr.append(elf_file.base.comp.gpa, self.index);
+    }
+}
+
 /// This is just a temporary helper function that allows us to re-read what we wrote to file into a buffer.
 /// We need this so that we can write to an archive.
 /// TODO implement writing ZigObject data directly to a buffer instead.
src/link/Elf.zig
@@ -1323,6 +1323,11 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)
         }
     }
 
+    self.checkDuplicates() catch |err| switch (err) {
+        error.HasDuplicates => return error.FlushFailure,
+        else => |e| return e,
+    };
+
     try self.initOutputSections();
     try self.addLinkerDefinedSymbols();
     self.claimUnresolved();
@@ -3491,6 +3496,27 @@ fn allocateLinkerDefinedSymbols(self: *Elf) void {
     }
 }
 
+fn checkDuplicates(self: *Elf) !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();
+    }
+
+    if (self.zigObjectPtr()) |zig_object| {
+        try zig_object.checkDuplicates(&dupes, self);
+    }
+    for (self.objects.items) |index| {
+        try self.file(index).?.object.checkDuplicates(&dupes, self);
+    }
+
+    try self.reportDuplicates(dupes);
+}
+
 fn initOutputSections(self: *Elf) !void {
     for (self.objects.items) |index| {
         try self.file(index).?.object.initOutputSections(self);
@@ -6164,6 +6190,36 @@ fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void {
     }
 }
 
+fn reportDuplicates(self: *Elf, dupes: anytype) error{ HasDuplicates, OutOfMemory }!void {
+    const max_notes = 3;
+    var has_dupes = false;
+    var it = dupes.iterator();
+    while (it.next()) |entry| {
+        const sym = self.symbol(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 + 1);
+        try err.addMsg(self, "duplicate symbol definition: {s}", .{sym.name(self)});
+        try err.addNote(self, "defined by {}", .{sym.file(self).?.fmtPath()});
+
+        var inote: usize = 0;
+        while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {
+            const file_ptr = self.file(notes.items[inote]).?;
+            try err.addNote(self, "defined by {}", .{file_ptr.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;
+}
+
 fn reportMissingLibraryError(
     self: *Elf,
     checked_paths: []const []const u8,