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 {
10771077 .exe => return step.fail("cannot link with an executable build artifact", .{}),
10781078 .@"test" => return step.fail("cannot link with a test", .{}),
10791079 .obj => {
1080 const included_in_lib = !my_responsibility and
1081 compile.kind == .lib and other.kind == .obj;
1082 if (!already_linked and !included_in_lib) {
1080 const included_in_lib_or_obj = !my_responsibility and (compile.kind == .lib or compile.kind == .obj);
1081 if (!already_linked and !included_in_lib_or_obj) {
10831082 try zig_args.append(other.getEmittedBin().getPath(b));
10841083 total_linker_objects += 1;
10851084 }
src/link/Elf.zig+56
......@@ -1302,6 +1302,11 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)
13021302 }
13031303 }
13041304
1305 self.checkDuplicates() catch |err| switch (err) {
1306 error.HasDuplicates => return error.FlushFailure,
1307 else => |e| return e,
1308 };
1309
13051310 try self.initOutputSections();
13061311 try self.addLinkerDefinedSymbols();
13071312 self.claimUnresolved();
......@@ -3428,6 +3433,27 @@ fn allocateLinkerDefinedSymbols(self: *Elf) void {
34283433 }
34293434}
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
34313457fn initOutputSections(self: *Elf) !void {
34323458 for (self.objects.items) |index| {
34333459 try self.file(index).?.object.initOutputSections(self);
......@@ -6101,6 +6127,36 @@ fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void {
61016127 }
61026128}
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
61046160fn reportMissingLibraryError(
61056161 self: *Elf,
61066162 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 {
581581 }
582582}
583583
584pub fn checkDuplicates(self: *Object, elf_file: *Elf) void {
584pub fn checkDuplicates(self: *Object, dupes: anytype, elf_file: *Elf) error{OutOfMemory}!void {
585585 const first_global = self.first_global orelse return;
586586 for (self.globals(), 0..) |index, i| {
587 const sym_idx = @as(u32, @intCast(first_global + i));
588 const this_sym = self.symtab.items[sym_idx];
587 const sym_idx = first_global + i;
588 const sym = self.symtab.items[sym_idx];
589589 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() or
593 this_sym.st_shndx == elf.SHN_UNDEF or
594 this_sym.st_bind() == elf.STB_WEAK or
595 this_sym.st_shndx == elf.SHN_COMMON) continue;
592 if (self.index == global_file.index() or
593 sym.st_shndx == elf.SHN_UNDEF or
594 sym.st_bind() == elf.STB_WEAK or
595 sym.st_shndx == elf.SHN_COMMON) continue;
596596
597 if (this_sym.st_shndx != elf.SHN_ABS) {
598 const atom_index = self.atoms.items[this_sym.st_shndx];
597 if (sym.st_shndx != elf.SHN_ABS) {
598 const atom_index = self.atoms.items[sym.st_shndx];
599599 const atom = elf_file.atom(atom_index) orelse continue;
600600 if (!atom.flags.alive) continue;
601601 }
602602
603 elf_file.base.fatal("multiple definition: {}: {}: {s}", .{
604 self.fmtPath(),
605 global_file.fmtPath(),
606 global.getName(elf_file),
607 });
603 const gop = try dupes.getOrPut(index);
604 if (!gop.found_existing) {
605 gop.value_ptr.* = .{};
606 }
607 try gop.value_ptr.append(elf_file.base.comp.gpa, self.index);
608608 }
609609}
610610
src/link/Elf/ZigObject.zig+26
......@@ -451,6 +451,32 @@ pub fn markLive(self: *ZigObject, elf_file: *Elf) void {
451451 }
452452}
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
454480/// This is just a temporary helper function that allows us to re-read what we wrote to file into a buffer.
455481/// We need this so that we can write to an archive.
456482/// 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 {
695695fn testEmitRelocatable(b: *Build, opts: Options) *Step {
696696 const test_step = addTestStep(b, "emit-relocatable", opts);
697697
698 const obj1 = addObject(b, opts, .{
699 .name = "obj1",
700 .zig_source_bytes =
701 \\const std = @import("std");
702 \\extern var bar: i32;
703 \\export fn foo() i32 {
704 \\ return bar;
705 \\}
706 \\export fn printFoo() void {
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 ,
698 const a_o = addObject(b, opts, .{ .name = "a", .zig_source_bytes =
699 \\const std = @import("std");
700 \\extern var bar: i32;
701 \\export fn foo() i32 {
702 \\ return bar;
703 \\}
704 \\export fn printFoo() void {
705 \\ std.debug.print("foo={d}\n", .{foo()});
706 \\}
717707 });
718 obj1.linkLibC();
708 a_o.linkLibC();
719709
720 const exe = addExecutable(b, opts, .{
721 .name = "test",
722 .zig_source_bytes =
723 \\const std = @import("std");
724 \\extern fn printFoo() void;
725 \\extern fn printBar() void;
726 \\pub fn main() void {
727 \\ printFoo();
728 \\ printBar();
729 \\}
730 ,
710 const b_o = addObject(b, opts, .{ .name = "b", .c_source_bytes =
711 \\#include <stdio.h>
712 \\int bar = 42;
713 \\void printBar() {
714 \\ fprintf(stderr, "bar=%d\n", bar);
715 \\}
731716 });
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);
733733 exe.linkLibC();
734734
735735 const run = addRunArtifact(exe);