authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-09-12 00:10:54+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-09-12 00:10:54+02:00
log00787885f4f99a92d85b496ff9e03993d69b1cf6
treeff86e3d84ec68c39a8db90bb2388b613a570534d
parent67d458370dfc87753e9938e7af94cb86e3bc98cc

elf: report undefined symbols in objects


4 files changed, 88 insertions(+), 52 deletions(-)

src/link/Elf.zig+76-40
......@@ -843,6 +843,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
843843 sym.name_offset = name_off;
844844 esym.st_name = name_off;
845845 esym.st_info |= elf.STT_FILE;
846 esym.st_shndx = elf.SHN_ABS;
846847 }
847848 }
848849}
......@@ -1299,22 +1300,19 @@ fn parseObject(self: *Elf, in_file: std.fs.File, path: []const u8, ctx: *ParseEr
12991300
13001301 const gpa = self.base.allocator;
13011302 const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32));
1302 const index = @as(File.Index, @intCast(self.files.slice().len));
1303
1304 var object = Object{
1303 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
1304 self.files.set(index, .{ .object = .{
13051305 .path = path,
13061306 .data = data,
13071307 .index = index,
1308 };
1309 errdefer object.deinit(gpa);
1308 } });
1309 try self.objects.append(gpa, index);
1310
1311 const object = self.file(index).?.object;
13101312 try object.parse(self);
13111313
13121314 ctx.detected_cpu_arch = object.header.?.e_machine.toTargetCpuArch().?;
13131315 if (ctx.detected_cpu_arch != self.base.options.target.cpu.arch) return error.InvalidCpuArch;
1314
1315 _ = try self.files.addOne(gpa);
1316 self.files.set(index, .{ .object = object });
1317 try self.objects.append(gpa, index);
13181316}
13191317
13201318fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void {
......@@ -2690,12 +2688,14 @@ pub fn updateDeclExports(
26902688 break :blk sym_index;
26912689 };
26922690 const sym = self.symbol(sym_index);
2691 sym.flags.@"export" = true;
26932692 sym.value = decl_sym.value;
26942693 sym.atom_index = decl_sym.atom_index;
26952694 sym.output_section_index = decl_sym.output_section_index;
26962695 const esym = zig_module.sourceSymbol(sym_index, self);
26972696 esym.* = decl_esym;
26982697 esym.st_info = (stb_bits << 4) | stt_bits;
2698 _ = self.unresolved.swapRemove(sym_index);
26992699 }
27002700}
27012701
......@@ -3468,13 +3468,8 @@ pub fn globalByName(self: *Elf, name: []const u8) ?Symbol.Index {
34683468
34693469pub fn getGlobalSymbol(self: *Elf, name: []const u8, lib_name: ?[]const u8) !u32 {
34703470 _ = lib_name;
3471 const gpa = self.base.allocator;
3472 const name_off = try self.strtab.insert(gpa, name);
3473 const gop = try self.getOrPutGlobal(name_off);
3474 if (!gop.found_existing) {
3475 try self.unresolved.putNoClobber(gpa, gop.index, {});
3476 }
3477 return gop.index;
3471 const zig_module = self.file(self.zig_module_index.?).?.zig_module;
3472 return zig_module.addGlobal(name, self);
34783473}
34793474
34803475const GetOrCreateComdatGroupOwnerResult = struct {
......@@ -3519,42 +3514,83 @@ fn reportUndefined(self: *Elf) !void {
35193514
35203515 try self.misc_errors.ensureUnusedCapacity(gpa, self.unresolved.keys().len);
35213516
3522 for (self.unresolved.keys()) |sym_index| {
3523 const undef_sym = self.symbol(sym_index);
3517 const CollectStruct = struct {
3518 notes: [max_notes]link.File.ErrorMsg = [_]link.File.ErrorMsg{.{ .msg = undefined }} ** max_notes,
3519 notes_len: u3 = 0,
3520 notes_count: usize = 0,
3521 };
35243522
3525 var all_notes: usize = 0;
3526 var notes = try std.ArrayList(link.File.ErrorMsg).initCapacity(gpa, max_notes + 1);
3527 defer notes.deinit();
3523 const collect: []CollectStruct = try gpa.alloc(CollectStruct, self.unresolved.keys().len);
3524 defer gpa.free(collect);
3525 @memset(collect, .{});
35283526
3529 // Collect all references across all input files
3530 if (self.zig_module_index) |index| {
3531 const zig_module = self.file(index).?.zig_module;
3532 for (zig_module.atoms.keys()) |atom_index| {
3533 const atom_ptr = self.atom(atom_index).?;
3534 if (!atom_ptr.alive) continue;
3535
3536 for (atom_ptr.relocs(self)) |rel| {
3537 if (sym_index == rel.r_sym()) {
3538 const note = try std.fmt.allocPrint(gpa, "referenced by {s}:{s}", .{
3539 zig_module.path,
3540 atom_ptr.name(self),
3541 });
3542 notes.appendAssumeCapacity(.{ .msg = note });
3543 all_notes += 1;
3544 break;
3527 // Collect all references across all input files
3528 if (self.zig_module_index) |index| {
3529 const zig_module = self.file(index).?.zig_module;
3530 for (zig_module.atoms.keys()) |atom_index| {
3531 const atom_ptr = self.atom(atom_index).?;
3532 if (!atom_ptr.alive) continue;
3533
3534 for (atom_ptr.relocs(self)) |rel| {
3535 if (self.unresolved.getIndex(rel.r_sym())) |bin_index| {
3536 const note = try std.fmt.allocPrint(gpa, "referenced by {s}:{s}", .{
3537 zig_module.path,
3538 atom_ptr.name(self),
3539 });
3540 const bin = &collect[bin_index];
3541 if (bin.notes_len < max_notes) {
3542 bin.notes[bin.notes_len] = .{ .msg = note };
3543 bin.notes_len += 1;
35453544 }
3545 bin.notes_count += 1;
35463546 }
35473547 }
35483548 }
3549 }
3550
3551 for (self.objects.items) |index| {
3552 const object = self.file(index).?.object;
3553 for (object.atoms.items) |atom_index| {
3554 const atom_ptr = self.atom(atom_index) orelse continue;
3555 if (!atom_ptr.alive) continue;
3556
3557 for (atom_ptr.relocs(self)) |rel| {
3558 const sym_index = object.symbols.items[rel.r_sym()];
3559 if (self.unresolved.getIndex(sym_index)) |bin_index| {
3560 const note = try std.fmt.allocPrint(gpa, "referenced by {}:{s}", .{
3561 object.fmtPath(),
3562 atom_ptr.name(self),
3563 });
3564 const bin = &collect[bin_index];
3565 if (bin.notes_len < max_notes) {
3566 bin.notes[bin.notes_len] = .{ .msg = note };
3567 bin.notes_len += 1;
3568 }
3569 bin.notes_count += 1;
3570 }
3571 }
3572 }
3573 }
3574
3575 // Generate error notes
3576 for (self.unresolved.keys(), 0..) |sym_index, bin_index| {
3577 const collected = &collect[bin_index];
3578
3579 var notes = try std.ArrayList(link.File.ErrorMsg).initCapacity(gpa, max_notes + 1);
3580 defer notes.deinit();
3581
3582 for (collected.notes[0..collected.notes_len]) |note| {
3583 notes.appendAssumeCapacity(note);
3584 }
35493585
3550 if (all_notes > max_notes) {
3551 const remaining = all_notes - max_notes;
3586 if (collected.notes_count > max_notes) {
3587 const remaining = collected.notes_count - max_notes;
35523588 const note = try std.fmt.allocPrint(gpa, "referenced {d} more times", .{remaining});
35533589 notes.appendAssumeCapacity(.{ .msg = note });
35543590 }
35553591
35563592 var err_msg = link.File.ErrorMsg{
3557 .msg = try std.fmt.allocPrint(gpa, "undefined symbol: {s}", .{undef_sym.name(self)}),
3593 .msg = try std.fmt.allocPrint(gpa, "undefined symbol: {s}", .{self.symbol(sym_index).name(self)}),
35583594 };
35593595 err_msg.notes = try notes.toOwnedSlice();
35603596
src/link/Elf/LinkerDefined.zig-9
......@@ -46,15 +46,6 @@ pub fn resolveSymbols(self: *LinkerDefined, elf_file: *Elf) void {
4646 }
4747}
4848
49// pub fn resetGlobals(self: *LinkerDefined, elf_file: *Elf) void {
50// for (self.symbols.items) |index| {
51// const global = elf_file.getSymbol(index);
52// const name = global.name;
53// global.* = .{};
54// global.name = name;
55// }
56// }
57
5849pub fn updateSymtabSize(self: *LinkerDefined, elf_file: *Elf) void {
5950 for (self.globals()) |global_index| {
6051 const global = elf_file.symbol(global_index);
src/link/Elf/Object.zig+8-1
......@@ -187,6 +187,7 @@ fn addAtom(self: *Object, shdr: elf.Elf64_Shdr, shndx: u16, name: [:0]const u8,
187187 atom.name_offset = try elf_file.strtab.insert(elf_file.base.allocator, name);
188188 atom.file_index = self.index;
189189 atom.input_section_index = shndx;
190 atom.alive = true;
190191 self.atoms.items[shndx] = atom_index;
191192
192193 if (shdr.sh_flags & elf.SHF_COMPRESSED != 0) {
......@@ -244,6 +245,10 @@ fn initSymtab(self: *Object, elf_file: *Elf) !void {
244245 const off = try elf_file.strtab.insert(gpa, name);
245246 const gop = try elf_file.getOrPutGlobal(off);
246247 self.symbols.addOneAssumeCapacity().* = gop.index;
248
249 if (sym.st_shndx == elf.SHN_UNDEF) {
250 try elf_file.unresolved.put(gpa, gop.index, {});
251 }
247252 }
248253}
249254
......@@ -394,6 +399,8 @@ pub fn resolveSymbols(self: *Object, elf_file: *Elf) void {
394399 if (!atom.alive) continue;
395400 }
396401
402 _ = elf_file.unresolved.swapRemove(index);
403
397404 const global = elf_file.symbol(index);
398405 if (self.asFile().symbolRank(this_sym, !self.alive) < global.symbolRank(elf_file)) {
399406 const atom = switch (this_sym.st_shndx) {
......@@ -598,7 +605,7 @@ pub fn globals(self: *Object) []const u32 {
598605 return self.symbols.items[start..];
599606}
600607
601pub inline fn shdrContents(self: *Object, index: u32) []const u8 {
608pub fn shdrContents(self: *Object, index: u32) []const u8 {
602609 assert(index < self.shdrs.items.len);
603610 const shdr = self.shdrs.items[index];
604611 return self.data[shdr.sh_offset..][0..shdr.sh_size];
src/link/Elf/ZigModule.zig+4-2
......@@ -58,7 +58,7 @@ pub fn addLocal(self: *ZigModule, elf_file: *Elf) !Symbol.Index {
5858 return symbol_index;
5959}
6060
61pub fn addGlobal(self: *ZigModule, name: [:0]const u8, elf_file: *Elf) !Symbol.Index {
61pub fn addGlobal(self: *ZigModule, name: []const u8, elf_file: *Elf) !Symbol.Index {
6262 const gpa = elf_file.base.allocator;
6363 try self.elf_global_symbols.ensureUnusedCapacity(gpa, 1);
6464 try self.global_symbols.ensureUnusedCapacity(gpa, 1);
......@@ -69,10 +69,12 @@ pub fn addGlobal(self: *ZigModule, name: [:0]const u8, elf_file: *Elf) !Symbol.I
6969 esym.st_name = off;
7070 esym.st_info = elf.STB_GLOBAL << 4;
7171 const gop = try elf_file.getOrPutGlobal(off);
72 if (!gop.found_existing) {
73 try elf_file.unresolved.putNoClobber(gpa, gop.index, {});
74 }
7275 const sym = elf_file.symbol(gop.index);
7376 sym.file_index = self.index;
7477 sym.esym_index = esym_index;
75 sym.flags.@"export" = true;
7678 self.global_symbols.putAssumeCapacityNoClobber(gop.index, {});
7779 return gop.index;
7880}