authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-01-15 07:37:09+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-01-15 07:37:09+01:00
log3dddb881bfefbcd64aae7775ed72c42e37503674
treed3a8523750581848381b796a3de59fc05b66c64f
parent852e7e24b5f15b489463bdabb0039e2a424e5ee6
parentb1ffc2b8b353f64362c27df2ecc44436db234a29
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #18560 from ziglang/elf-report-dupes

elf: report duplicate symbol definitions

5 files changed, 131 insertions(+), 50 deletions(-)

lib/std/Build/Step/Compile.zig+2-3
...@@ -1077,9 +1077,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -1077,9 +1077,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
1077 .exe => return step.fail("cannot link with an executable build artifact", .{}),1077 .exe => return step.fail("cannot link with an executable build artifact", .{}),
1078 .@"test" => return step.fail("cannot link with a test", .{}),1078 .@"test" => return step.fail("cannot link with a test", .{}),
1079 .obj => {1079 .obj => {
1080 const included_in_lib = !my_responsibility and1080 const included_in_lib_or_obj = !my_responsibility and (compile.kind == .lib or compile.kind == .obj);
1081 compile.kind == .lib and other.kind == .obj;1081 if (!already_linked and !included_in_lib_or_obj) {
1082 if (!already_linked and !included_in_lib) {
1083 try zig_args.append(other.getEmittedBin().getPath(b));1082 try zig_args.append(other.getEmittedBin().getPath(b));
1084 total_linker_objects += 1;1083 total_linker_objects += 1;
1085 }1084 }
src/link/Elf.zig+56
...@@ -1302,6 +1302,11 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)...@@ -1302,6 +1302,11 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)
1302 }1302 }
1303 }1303 }
13041304
1305 self.checkDuplicates() catch |err| switch (err) {
1306 error.HasDuplicates => return error.FlushFailure,
1307 else => |e| return e,
1308 };
1309
1305 try self.initOutputSections();1310 try self.initOutputSections();
1306 try self.addLinkerDefinedSymbols();1311 try self.addLinkerDefinedSymbols();
1307 self.claimUnresolved();1312 self.claimUnresolved();
...@@ -3428,6 +3433,27 @@ fn allocateLinkerDefinedSymbols(self: *Elf) void {...@@ -3428,6 +3433,27 @@ fn allocateLinkerDefinedSymbols(self: *Elf) void {
3428 }3433 }
3429}3434}
34303435
3436fn checkDuplicates(self: *Elf) !void {
3437 const gpa = self.base.comp.gpa;
3438
3439 var dupes = std.AutoArrayHashMap(Symbol.Index, std.ArrayListUnmanaged(File.Index)).init(gpa);
3440 defer {
3441 for (dupes.values()) |*list| {
3442 list.deinit(gpa);
3443 }
3444 dupes.deinit();
3445 }
3446
3447 if (self.zigObjectPtr()) |zig_object| {
3448 try zig_object.checkDuplicates(&dupes, self);
3449 }
3450 for (self.objects.items) |index| {
3451 try self.file(index).?.object.checkDuplicates(&dupes, self);
3452 }
3453
3454 try self.reportDuplicates(dupes);
3455}
3456
3431fn initOutputSections(self: *Elf) !void {3457fn initOutputSections(self: *Elf) !void {
3432 for (self.objects.items) |index| {3458 for (self.objects.items) |index| {
3433 try self.file(index).?.object.initOutputSections(self);3459 try self.file(index).?.object.initOutputSections(self);
...@@ -6101,6 +6127,36 @@ fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void {...@@ -6101,6 +6127,36 @@ fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void {
6101 }6127 }
6102}6128}
61036129
6130fn reportDuplicates(self: *Elf, dupes: anytype) error{ HasDuplicates, OutOfMemory }!void {
6131 const max_notes = 3;
6132 var has_dupes = false;
6133 var it = dupes.iterator();
6134 while (it.next()) |entry| {
6135 const sym = self.symbol(entry.key_ptr.*);
6136 const notes = entry.value_ptr.*;
6137 const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);
6138
6139 var err = try self.addErrorWithNotes(nnotes + 1);
6140 try err.addMsg(self, "duplicate symbol definition: {s}", .{sym.name(self)});
6141 try err.addNote(self, "defined by {}", .{sym.file(self).?.fmtPath()});
6142
6143 var inote: usize = 0;
6144 while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {
6145 const file_ptr = self.file(notes.items[inote]).?;
6146 try err.addNote(self, "defined by {}", .{file_ptr.fmtPath()});
6147 }
6148
6149 if (notes.items.len > max_notes) {
6150 const remaining = notes.items.len - max_notes;
6151 try err.addNote(self, "defined {d} more times", .{remaining});
6152 }
6153
6154 has_dupes = true;
6155 }
6156
6157 if (has_dupes) return error.HasDuplicates;
6158}
6159
6104fn reportMissingLibraryError(6160fn reportMissingLibraryError(
6105 self: *Elf,6161 self: *Elf,
6106 checked_paths: []const []const u8,6162 checked_paths: []const []const u8,
src/link/Elf/Object.zig+15-15
...@@ -581,30 +581,30 @@ pub fn markEhFrameAtomsDead(self: Object, elf_file: *Elf) void {...@@ -581,30 +581,30 @@ pub fn markEhFrameAtomsDead(self: Object, elf_file: *Elf) void {
581 }581 }
582}582}
583583
584pub fn checkDuplicates(self: *Object, elf_file: *Elf) void {584pub fn checkDuplicates(self: *Object, dupes: anytype, elf_file: *Elf) error{OutOfMemory}!void {
585 const first_global = self.first_global orelse return;585 const first_global = self.first_global orelse return;
586 for (self.globals(), 0..) |index, i| {586 for (self.globals(), 0..) |index, i| {
587 const sym_idx = @as(u32, @intCast(first_global + i));587 const sym_idx = first_global + i;
588 const this_sym = self.symtab.items[sym_idx];588 const sym = self.symtab.items[sym_idx];
589 const global = elf_file.symbol(index);589 const global = elf_file.symbol(index);
590 const global_file = global.getFile(elf_file) orelse continue;590 const global_file = global.file(elf_file) orelse continue;
591591
592 if (self.index == global_file.getIndex() or592 if (self.index == global_file.index() or
593 this_sym.st_shndx == elf.SHN_UNDEF or593 sym.st_shndx == elf.SHN_UNDEF or
594 this_sym.st_bind() == elf.STB_WEAK or594 sym.st_bind() == elf.STB_WEAK or
595 this_sym.st_shndx == elf.SHN_COMMON) continue;595 sym.st_shndx == elf.SHN_COMMON) continue;
596596
597 if (this_sym.st_shndx != elf.SHN_ABS) {597 if (sym.st_shndx != elf.SHN_ABS) {
598 const atom_index = self.atoms.items[this_sym.st_shndx];598 const atom_index = self.atoms.items[sym.st_shndx];
599 const atom = elf_file.atom(atom_index) orelse continue;599 const atom = elf_file.atom(atom_index) orelse continue;
600 if (!atom.flags.alive) continue;600 if (!atom.flags.alive) continue;
601 }601 }
602602
603 elf_file.base.fatal("multiple definition: {}: {}: {s}", .{603 const gop = try dupes.getOrPut(index);
604 self.fmtPath(),604 if (!gop.found_existing) {
605 global_file.fmtPath(),605 gop.value_ptr.* = .{};
606 global.getName(elf_file),606 }
607 });607 try gop.value_ptr.append(elf_file.base.comp.gpa, self.index);
608 }608 }
609}609}
610610
src/link/Elf/ZigObject.zig+26
...@@ -451,6 +451,32 @@ pub fn markLive(self: *ZigObject, elf_file: *Elf) void {...@@ -451,6 +451,32 @@ pub fn markLive(self: *ZigObject, elf_file: *Elf) void {
451 }451 }
452}452}
453453
454pub fn checkDuplicates(self: *ZigObject, dupes: anytype, elf_file: *Elf) error{OutOfMemory}!void {
455 for (self.globals(), 0..) |index, i| {
456 const esym = self.global_esyms.items(.elf_sym)[i];
457 const shndx = self.global_esyms.items(.shndx)[i];
458 const global = elf_file.symbol(index);
459 const global_file = global.file(elf_file) orelse continue;
460
461 if (self.index == global_file.index() or
462 esym.st_shndx == elf.SHN_UNDEF or
463 esym.st_bind() == elf.STB_WEAK or
464 esym.st_shndx == elf.SHN_COMMON) continue;
465
466 if (esym.st_shndx == SHN_ATOM) {
467 const atom_index = self.atoms.items[shndx];
468 const atom = elf_file.atom(atom_index) orelse continue;
469 if (!atom.flags.alive) continue;
470 }
471
472 const gop = try dupes.getOrPut(index);
473 if (!gop.found_existing) {
474 gop.value_ptr.* = .{};
475 }
476 try gop.value_ptr.append(elf_file.base.comp.gpa, self.index);
477 }
478}
479
454/// This is just a temporary helper function that allows us to re-read what we wrote to file into a buffer.480/// This is just a temporary helper function that allows us to re-read what we wrote to file into a buffer.
455/// We need this so that we can write to an archive.481/// We need this so that we can write to an archive.
456/// TODO implement writing ZigObject data directly to a buffer instead.482/// TODO implement writing ZigObject data directly to a buffer instead.
test/link/elf.zig+32-32
...@@ -695,41 +695,41 @@ fn testDsoUndef(b: *Build, opts: Options) *Step {...@@ -695,41 +695,41 @@ fn testDsoUndef(b: *Build, opts: Options) *Step {
695fn testEmitRelocatable(b: *Build, opts: Options) *Step {695fn testEmitRelocatable(b: *Build, opts: Options) *Step {
696 const test_step = addTestStep(b, "emit-relocatable", opts);696 const test_step = addTestStep(b, "emit-relocatable", opts);
697697
698 const obj1 = addObject(b, opts, .{698 const a_o = addObject(b, opts, .{ .name = "a", .zig_source_bytes =
699 .name = "obj1",699 \\const std = @import("std");
700 .zig_source_bytes =700 \\extern var bar: i32;
701 \\const std = @import("std");701 \\export fn foo() i32 {
702 \\extern var bar: i32;702 \\ return bar;
703 \\export fn foo() i32 {703 \\}
704 \\ return bar;704 \\export fn printFoo() void {
705 \\}705 \\ std.debug.print("foo={d}\n", .{foo()});
706 \\export fn printFoo() void {706 \\}
707 \\ std.debug.print("foo={d}\n", .{foo()});
708 \\}
709 ,
710 .c_source_bytes =
711 \\#include <stdio.h>
712 \\int bar = 42;
713 \\void printBar() {
714 \\ fprintf(stderr, "bar=%d\n", bar);
715 \\}
716 ,
717 });707 });
718 obj1.linkLibC();708 a_o.linkLibC();
719709
720 const exe = addExecutable(b, opts, .{710 const b_o = addObject(b, opts, .{ .name = "b", .c_source_bytes =
721 .name = "test",711 \\#include <stdio.h>
722 .zig_source_bytes =712 \\int bar = 42;
723 \\const std = @import("std");713 \\void printBar() {
724 \\extern fn printFoo() void;714 \\ fprintf(stderr, "bar=%d\n", bar);
725 \\extern fn printBar() void;715 \\}
726 \\pub fn main() void {
727 \\ printFoo();
728 \\ printBar();
729 \\}
730 ,
731 });716 });
732 exe.addObject(obj1);717 b_o.linkLibC();
718
719 const c_o = addObject(b, opts, .{ .name = "c" });
720 c_o.addObject(a_o);
721 c_o.addObject(b_o);
722
723 const exe = addExecutable(b, opts, .{ .name = "test", .zig_source_bytes =
724 \\const std = @import("std");
725 \\extern fn printFoo() void;
726 \\extern fn printBar() void;
727 \\pub fn main() void {
728 \\ printFoo();
729 \\ printBar();
730 \\}
731 });
732 exe.addObject(c_o);
733 exe.linkLibC();733 exe.linkLibC();
734734
735 const run = addRunArtifact(exe);735 const run = addRunArtifact(exe);