authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-09-12 23:27:11+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-09-12 23:27:14+02:00
log1a6d12ea92eb9033aed686c88b53ff406ec2fbe7
tree00700d4647d1551641a3694e315bd9423d3f3687
parent9719fa7412371243aaac6f307f80d783c7ac50cb

elf: clean up and unify symbol ref handling in relocs

Also, this lets us re-enable proper undefined symbols tracking.

6 files changed, 146 insertions(+), 107 deletions(-)

src/link/Elf.zig+79-91
...@@ -6,6 +6,9 @@ ptr_width: PtrWidth,...@@ -6,6 +6,9 @@ ptr_width: PtrWidth,
6/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.6/// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
7llvm_object: ?*LlvmObject = null,7llvm_object: ?*LlvmObject = null,
88
9/// A list of all input files.
10/// Index of each input file also encodes the priority or precedence of one input file
11/// over another.
9files: std.MultiArrayList(File.Entry) = .{},12files: std.MultiArrayList(File.Entry) = .{},
10zig_module_index: ?File.Index = null,13zig_module_index: ?File.Index = null,
11linker_defined_index: ?File.Index = null,14linker_defined_index: ?File.Index = null,
...@@ -47,6 +50,7 @@ shstrtab: StringTable(.strtab) = .{},...@@ -47,6 +50,7 @@ shstrtab: StringTable(.strtab) = .{},
47/// .strtab buffer50/// .strtab buffer
48strtab: StringTable(.strtab) = .{},51strtab: StringTable(.strtab) = .{},
4952
53/// Representation of the GOT table as committed to the file.
50got: GotSection = .{},54got: GotSection = .{},
5155
52text_section_index: ?u16 = null,56text_section_index: ?u16 = null,
...@@ -86,10 +90,10 @@ rela_iplt_start_index: ?Symbol.Index = null,...@@ -86,10 +90,10 @@ rela_iplt_start_index: ?Symbol.Index = null,
86rela_iplt_end_index: ?Symbol.Index = null,90rela_iplt_end_index: ?Symbol.Index = null,
87start_stop_indexes: std.ArrayListUnmanaged(u32) = .{},91start_stop_indexes: std.ArrayListUnmanaged(u32) = .{},
8892
93/// An array of symbols parsed across all input files.
89symbols: std.ArrayListUnmanaged(Symbol) = .{},94symbols: std.ArrayListUnmanaged(Symbol) = .{},
90symbols_extra: std.ArrayListUnmanaged(u32) = .{},95symbols_extra: std.ArrayListUnmanaged(u32) = .{},
91resolver: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index) = .{},96resolver: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index) = .{},
92unresolved: std.AutoArrayHashMapUnmanaged(Symbol.Index, void) = .{},
93symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .{},97symbols_free_list: std.ArrayListUnmanaged(Symbol.Index) = .{},
9498
95phdr_table_dirty: bool = false,99phdr_table_dirty: bool = false,
...@@ -271,7 +275,6 @@ pub fn deinit(self: *Elf) void {...@@ -271,7 +275,6 @@ pub fn deinit(self: *Elf) void {
271 self.symbols_free_list.deinit(gpa);275 self.symbols_free_list.deinit(gpa);
272 self.got.deinit(gpa);276 self.got.deinit(gpa);
273 self.resolver.deinit(gpa);277 self.resolver.deinit(gpa);
274 self.unresolved.deinit(gpa);
275 self.start_stop_indexes.deinit(gpa);278 self.start_stop_indexes.deinit(gpa);
276279
277 {280 {
...@@ -316,7 +319,7 @@ pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: link....@@ -316,7 +319,7 @@ pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: link.
316 const parent_atom = self.symbol(reloc_info.parent_atom_index).atom(self).?;319 const parent_atom = self.symbol(reloc_info.parent_atom_index).atom(self).?;
317 try parent_atom.addReloc(self, .{320 try parent_atom.addReloc(self, .{
318 .r_offset = reloc_info.offset,321 .r_offset = reloc_info.offset,
319 .r_info = (@as(u64, @intCast(this_sym_index)) << 32) | elf.R_X86_64_64,322 .r_info = (@as(u64, @intCast(this_sym.esym_index)) << 32) | elf.R_X86_64_64,
320 .r_addend = reloc_info.addend,323 .r_addend = reloc_info.addend,
321 });324 });
322325
...@@ -997,12 +1000,16 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -997,12 +1000,16 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
997 };1000 };
998 _ = compiler_rt_path;1001 _ = compiler_rt_path;
9991002
1000 // Parse input files1003 // Here we will parse input positional and library files (if referenced).
1004 // This will roughly match in any linker backend we support.
1001 var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);1005 var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);
1002 defer positionals.deinit();1006 defer positionals.deinit();
1003 try positionals.ensureUnusedCapacity(self.base.options.objects.len);1007 try positionals.ensureUnusedCapacity(self.base.options.objects.len);
1004 positionals.appendSliceAssumeCapacity(self.base.options.objects);1008 positionals.appendSliceAssumeCapacity(self.base.options.objects);
10051009
1010 // This is a set of object files emitted by clang in a single `build-exe` invocation.
1011 // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up
1012 // in this set.
1006 for (comp.c_object_table.keys()) |key| {1013 for (comp.c_object_table.keys()) |key| {
1007 try positionals.append(.{ .path = key.status.success.object_path });1014 try positionals.append(.{ .path = key.status.success.object_path });
1008 }1015 }
...@@ -1016,6 +1023,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1016,6 +1023,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1016 try self.handleAndReportParseError(obj.path, err, &parse_ctx);1023 try self.handleAndReportParseError(obj.path, err, &parse_ctx);
1017 }1024 }
10181025
1026 // Handle any lazy symbols that were emitted by incremental compilation.
1019 if (self.lazy_syms.getPtr(.none)) |metadata| {1027 if (self.lazy_syms.getPtr(.none)) |metadata| {
1020 // Most lazy symbols can be updated on first use, but1028 // Most lazy symbols can be updated on first use, but
1021 // anyerror needs to wait for everything to be flushed.1029 // anyerror needs to wait for everything to be flushed.
...@@ -1046,27 +1054,34 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1046,27 +1054,34 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1046 try dw.flushModule(module);1054 try dw.flushModule(module);
1047 }1055 }
10481056
1057 // If we haven't already, create a linker-generated input file comprising of
1058 // linker-defined synthetic symbols only such as `_DYNAMIC`, etc.
1049 if (self.linker_defined_index == null) {1059 if (self.linker_defined_index == null) {
1050 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));1060 const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
1051 self.files.set(index, .{ .linker_defined = .{ .index = index } });1061 self.files.set(index, .{ .linker_defined = .{ .index = index } });
1052 self.linker_defined_index = index;1062 self.linker_defined_index = index;
1053 }1063 }
1054
1055 // Symbol resolution happens here
1056 try self.addLinkerDefinedSymbols();1064 try self.addLinkerDefinedSymbols();
1065
1066 // Now, we are ready to resolve the symbols across all input files.
1067 // We will first resolve the files in the ZigModule, next in the parsed
1068 // input Object files.
1069 // Any qualifing unresolved symbol will be upgraded to an absolute, weak
1070 // symbol for potential resolution at load-time.
1057 self.resolveSymbols();1071 self.resolveSymbols();
1058 self.markImportsExports();1072 self.markImportsExports();
1059 self.claimUnresolved();1073 self.claimUnresolved();
10601074
1061 // Scan and create missing synthetic entries such as GOT indirection1075 // Scan and create missing synthetic entries such as GOT indirection.
1062 try self.scanRelocs();1076 try self.scanRelocs();
10631077
1064 // Allocate atoms parsed from input object files1078 // Allocate atoms parsed from input object files, followed by allocating
1079 // linker-defined synthetic symbols.
1065 try self.allocateObjects();1080 try self.allocateObjects();
1066 self.allocateLinkerDefinedSymbols();1081 self.allocateLinkerDefinedSymbols();
10671082
1068 // Beyond this point, everything has been allocated a virtual address and we can resolve1083 // Beyond this point, everything has been allocated a virtual address and we can resolve
1069 // the relocations.1084 // the relocations, and commit objects to file.
1070 if (self.zig_module_index) |index| {1085 if (self.zig_module_index) |index| {
1071 for (self.file(index).?.zig_module.atoms.keys()) |atom_index| {1086 for (self.file(index).?.zig_module.atoms.keys()) |atom_index| {
1072 const atom_ptr = self.atom(atom_index).?;1087 const atom_ptr = self.atom(atom_index).?;
...@@ -1083,9 +1098,12 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1083,9 +1098,12 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1083 }1098 }
1084 try self.writeObjects();1099 try self.writeObjects();
10851100
1101 // Generate and emit the symbol table.
1086 try self.updateSymtabSize();1102 try self.updateSymtabSize();
1087 try self.writeSymtab();1103 try self.writeSymtab();
10881104
1105 // Dump the state for easy debugging.
1106 // State can be dumped via `--debug-log link_state`.
1089 if (build_options.enable_logging) {1107 if (build_options.enable_logging) {
1090 state_log.debug("{}", .{self.dumpState()});1108 state_log.debug("{}", .{self.dumpState()});
1091 }1109 }
...@@ -1393,17 +1411,32 @@ fn claimUnresolved(self: *Elf) void {...@@ -1393,17 +1411,32 @@ fn claimUnresolved(self: *Elf) void {
1393 }1411 }
1394}1412}
13951413
1414/// In scanRelocs we will go over all live atoms and scan their relocs.
1415/// This will help us work out what synthetics to emit, GOT indirection, etc.
1416/// This is also the point where we will report undefined symbols for any
1417/// alloc sections.
1396fn scanRelocs(self: *Elf) !void {1418fn scanRelocs(self: *Elf) !void {
1419 const gpa = self.base.allocator;
1420
1421 var undefs = std.AutoHashMap(Symbol.Index, std.ArrayList(Atom.Index)).init(gpa);
1422 defer {
1423 var it = undefs.iterator();
1424 while (it.next()) |entry| {
1425 entry.value_ptr.deinit();
1426 }
1427 undefs.deinit();
1428 }
1429
1397 if (self.zig_module_index) |index| {1430 if (self.zig_module_index) |index| {
1398 const zig_module = self.file(index).?.zig_module;1431 const zig_module = self.file(index).?.zig_module;
1399 try zig_module.scanRelocs(self);1432 try zig_module.scanRelocs(self, &undefs);
1400 }1433 }
1401 for (self.objects.items) |index| {1434 for (self.objects.items) |index| {
1402 const object = self.file(index).?.object;1435 const object = self.file(index).?.object;
1403 try object.scanRelocs(self);1436 try object.scanRelocs(self, &undefs);
1404 }1437 }
14051438
1406 // try self.reportUndefined();1439 try self.reportUndefined(&undefs);
14071440
1408 for (self.symbols.items) |*sym| {1441 for (self.symbols.items) |*sym| {
1409 if (sym.flags.needs_got) {1442 if (sym.flags.needs_got) {
...@@ -1433,8 +1466,10 @@ fn allocateObjects(self: *Elf) !void {...@@ -1433,8 +1466,10 @@ fn allocateObjects(self: *Elf) !void {
14331466
1434 for (object.globals()) |global_index| {1467 for (object.globals()) |global_index| {
1435 const global = self.symbol(global_index);1468 const global = self.symbol(global_index);
1469 const atom_ptr = global.atom(self) orelse continue;
1470 if (!atom_ptr.alive) continue;
1436 if (global.file_index == index) {1471 if (global.file_index == index) {
1437 global.value = global.atom(self).?.value;1472 global.value = atom_ptr.value;
1438 }1473 }
1439 }1474 }
1440 }1475 }
...@@ -2829,21 +2864,23 @@ pub fn updateDeclExports(...@@ -2829,21 +2864,23 @@ pub fn updateDeclExports(
2829 };2864 };
2830 const stt_bits: u8 = @as(u4, @truncate(decl_esym.st_info));2865 const stt_bits: u8 = @as(u4, @truncate(decl_esym.st_info));
28312866
2867 const name_off = try self.strtab.insert(gpa, exp_name);
2832 const sym_index = if (decl_metadata.@"export"(self, exp_name)) |exp_index| exp_index.* else blk: {2868 const sym_index = if (decl_metadata.@"export"(self, exp_name)) |exp_index| exp_index.* else blk: {
2833 const sym_index = try zig_module.addGlobalEsym(gpa);2869 const sym_index = try zig_module.addGlobalEsym(gpa);
2834 _ = try zig_module.global_symbols.addOne(gpa);2870 const lookup_gop = try zig_module.globals_lookup.getOrPut(gpa, name_off);
2871 const esym = zig_module.elfSym(sym_index);
2872 esym.st_name = name_off;
2873 lookup_gop.value_ptr.* = sym_index;
2835 try decl_metadata.exports.append(gpa, sym_index);2874 try decl_metadata.exports.append(gpa, sym_index);
2875 const gop = try self.getOrPutGlobal(name_off);
2876 try zig_module.global_symbols.append(gpa, gop.index);
2836 break :blk sym_index;2877 break :blk sym_index;
2837 };2878 };
2838 const name_off = try self.strtab.insert(gpa, exp_name);2879 const esym = &zig_module.global_esyms.items[sym_index & 0x0fffffff];
2839 const esym = &zig_module.global_esyms.items[sym_index];
2840 esym.st_value = decl_sym.value;2880 esym.st_value = decl_sym.value;
2841 esym.st_shndx = decl_sym.atom_index;2881 esym.st_shndx = decl_sym.atom_index;
2842 esym.st_info = (stb_bits << 4) | stt_bits;2882 esym.st_info = (stb_bits << 4) | stt_bits;
2843 esym.st_name = name_off;2883 esym.st_name = name_off;
2844
2845 const gop = try self.getOrPutGlobal(name_off);
2846 zig_module.global_symbols.items[sym_index] = gop.index;
2847 }2884 }
2848}2885}
28492886
...@@ -3636,16 +3673,17 @@ pub fn getGlobalSymbol(self: *Elf, name: []const u8, lib_name: ?[]const u8) !u32...@@ -3636,16 +3673,17 @@ pub fn getGlobalSymbol(self: *Elf, name: []const u8, lib_name: ?[]const u8) !u32
3636 _ = lib_name;3673 _ = lib_name;
3637 const gpa = self.base.allocator;3674 const gpa = self.base.allocator;
3638 const off = try self.strtab.insert(gpa, name);3675 const off = try self.strtab.insert(gpa, name);
3639 const gop = try self.getOrPutGlobal(off);
3640 const zig_module = self.file(self.zig_module_index.?).?.zig_module;3676 const zig_module = self.file(self.zig_module_index.?).?.zig_module;
3641 const lookup_gop = try zig_module.globals_lookup.getOrPut(gpa, off);3677 const lookup_gop = try zig_module.globals_lookup.getOrPut(gpa, off);
3642 if (!lookup_gop.found_existing) {3678 if (!lookup_gop.found_existing) {
3643 const esym_index = try zig_module.addGlobalEsym(gpa);3679 const esym_index = try zig_module.addGlobalEsym(gpa);
3644 const esym = &zig_module.global_esyms.items[esym_index];3680 const esym = zig_module.elfSym(esym_index);
3645 esym.st_name = off;3681 esym.st_name = off;
3646 lookup_gop.value_ptr.* = esym_index;3682 lookup_gop.value_ptr.* = esym_index;
3683 const gop = try self.getOrPutGlobal(off);
3684 try zig_module.global_symbols.append(gpa, gop.index);
3647 }3685 }
3648 return gop.index;3686 return lookup_gop.value_ptr.*;
3649}3687}
36503688
3651const GetOrCreateComdatGroupOwnerResult = struct {3689const GetOrCreateComdatGroupOwnerResult = struct {
...@@ -3684,89 +3722,39 @@ pub fn comdatGroupOwner(self: *Elf, index: ComdatGroupOwner.Index) *ComdatGroupO...@@ -3684,89 +3722,39 @@ pub fn comdatGroupOwner(self: *Elf, index: ComdatGroupOwner.Index) *ComdatGroupO
3684 return &self.comdat_groups_owners.items[index];3722 return &self.comdat_groups_owners.items[index];
3685}3723}
36863724
3687fn reportUndefined(self: *Elf) !void {3725fn reportUndefined(self: *Elf, undefs: anytype) !void {
3688 const gpa = self.base.allocator;3726 const gpa = self.base.allocator;
3689 const max_notes = 4;3727 const max_notes = 4;
36903728
3691 try self.misc_errors.ensureUnusedCapacity(gpa, self.unresolved.keys().len);3729 try self.misc_errors.ensureUnusedCapacity(gpa, undefs.count());
3692
3693 const CollectStruct = struct {
3694 notes: [max_notes]link.File.ErrorMsg = [_]link.File.ErrorMsg{.{ .msg = undefined }} ** max_notes,
3695 notes_len: u3 = 0,
3696 notes_count: usize = 0,
3697 };
3698
3699 const collect: []CollectStruct = try gpa.alloc(CollectStruct, self.unresolved.keys().len);
3700 defer gpa.free(collect);
3701 @memset(collect, .{});
3702
3703 // Collect all references across all input files
3704 if (self.zig_module_index) |index| {
3705 const zig_module = self.file(index).?.zig_module;
3706 for (zig_module.atoms.keys()) |atom_index| {
3707 const atom_ptr = self.atom(atom_index).?;
3708 if (!atom_ptr.alive) continue;
3709
3710 for (atom_ptr.relocs(self)) |rel| {
3711 if (self.unresolved.getIndex(rel.r_sym())) |bin_index| {
3712 const note = try std.fmt.allocPrint(gpa, "referenced by {s}:{s}", .{
3713 zig_module.path,
3714 atom_ptr.name(self),
3715 });
3716 const bin = &collect[bin_index];
3717 if (bin.notes_len < max_notes) {
3718 bin.notes[bin.notes_len] = .{ .msg = note };
3719 bin.notes_len += 1;
3720 }
3721 bin.notes_count += 1;
3722 }
3723 }
3724 }
3725 }
3726
3727 for (self.objects.items) |index| {
3728 const object = self.file(index).?.object;
3729 for (object.atoms.items) |atom_index| {
3730 const atom_ptr = self.atom(atom_index) orelse continue;
3731 if (!atom_ptr.alive) continue;
37323730
3733 for (atom_ptr.relocs(self)) |rel| {3731 var it = undefs.iterator();
3734 const sym_index = object.symbols.items[rel.r_sym()];3732 while (it.next()) |entry| {
3735 if (self.unresolved.getIndex(sym_index)) |bin_index| {3733 const undef_index = entry.key_ptr.*;
3736 const note = try std.fmt.allocPrint(gpa, "referenced by {}:{s}", .{3734 const atoms = entry.value_ptr.*.items;
3737 object.fmtPath(),3735 const nnotes = @min(atoms.len, max_notes);
3738 atom_ptr.name(self),
3739 });
3740 const bin = &collect[bin_index];
3741 if (bin.notes_len < max_notes) {
3742 bin.notes[bin.notes_len] = .{ .msg = note };
3743 bin.notes_len += 1;
3744 }
3745 bin.notes_count += 1;
3746 }
3747 }
3748 }
3749 }
3750
3751 // Generate error notes
3752 for (self.unresolved.keys(), 0..) |sym_index, bin_index| {
3753 const collected = &collect[bin_index];
37543736
3755 var notes = try std.ArrayList(link.File.ErrorMsg).initCapacity(gpa, max_notes + 1);3737 var notes = try std.ArrayList(link.File.ErrorMsg).initCapacity(gpa, max_notes + 1);
3756 defer notes.deinit();3738 defer notes.deinit();
37573739
3758 for (collected.notes[0..collected.notes_len]) |note| {3740 for (atoms[0..nnotes]) |atom_index| {
3759 notes.appendAssumeCapacity(note);3741 const atom_ptr = self.atom(atom_index).?;
3742 const file_ptr = self.file(atom_ptr.file_index).?;
3743 const note = try std.fmt.allocPrint(gpa, "referenced by {s}:{s}", .{
3744 file_ptr.fmtPath(),
3745 atom_ptr.name(self),
3746 });
3747 notes.appendAssumeCapacity(.{ .msg = note });
3760 }3748 }
37613749
3762 if (collected.notes_count > max_notes) {3750 if (atoms.len > max_notes) {
3763 const remaining = collected.notes_count - max_notes;3751 const remaining = atoms.len - max_notes;
3764 const note = try std.fmt.allocPrint(gpa, "referenced {d} more times", .{remaining});3752 const note = try std.fmt.allocPrint(gpa, "referenced {d} more times", .{remaining});
3765 notes.appendAssumeCapacity(.{ .msg = note });3753 notes.appendAssumeCapacity(.{ .msg = note });
3766 }3754 }
37673755
3768 var err_msg = link.File.ErrorMsg{3756 var err_msg = link.File.ErrorMsg{
3769 .msg = try std.fmt.allocPrint(gpa, "undefined symbol: {s}", .{self.symbol(sym_index).name(self)}),3757 .msg = try std.fmt.allocPrint(gpa, "undefined symbol: {s}", .{self.symbol(undef_index).name(self)}),
3770 };3758 };
3771 err_msg.notes = try notes.toOwnedSlice();3759 err_msg.notes = try notes.toOwnedSlice();
37723760
...@@ -3931,7 +3919,7 @@ const DeclMetadata = struct {...@@ -3931,7 +3919,7 @@ const DeclMetadata = struct {
3931 fn @"export"(m: DeclMetadata, elf_file: *Elf, name: []const u8) ?*u32 {3919 fn @"export"(m: DeclMetadata, elf_file: *Elf, name: []const u8) ?*u32 {
3932 const zig_module = elf_file.file(elf_file.zig_module_index.?).?.zig_module;3920 const zig_module = elf_file.file(elf_file.zig_module_index.?).?.zig_module;
3933 for (m.exports.items) |*exp| {3921 for (m.exports.items) |*exp| {
3934 const exp_name = elf_file.strtab.getAssumeExists(zig_module.global_esyms.items[exp.*].st_name);3922 const exp_name = elf_file.strtab.getAssumeExists(zig_module.elfSym(exp.*).st_name);
3935 if (mem.eql(u8, name, exp_name)) return exp;3923 if (mem.eql(u8, name, exp_name)) return exp;
3936 }3924 }
3937 return null;3925 return null;
src/link/Elf/Atom.zig+40-3
...@@ -312,7 +312,7 @@ pub fn freeRelocs(self: Atom, elf_file: *Elf) void {...@@ -312,7 +312,7 @@ pub fn freeRelocs(self: Atom, elf_file: *Elf) void {
312 zig_module.relocs.items[self.relocs_section_index].clearRetainingCapacity();312 zig_module.relocs.items[self.relocs_section_index].clearRetainingCapacity();
313}313}
314314
315pub fn scanRelocs(self: Atom, elf_file: *Elf) !void {315pub fn scanRelocs(self: Atom, elf_file: *Elf, undefs: anytype) !void {
316 const file_ptr = elf_file.file(self.file_index).?;316 const file_ptr = elf_file.file(self.file_index).?;
317 const rels = self.relocs(elf_file);317 const rels = self.relocs(elf_file);
318 var i: usize = 0;318 var i: usize = 0;
...@@ -322,11 +322,25 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf) !void {...@@ -322,11 +322,25 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf) !void {
322 if (rel.r_type() == elf.R_X86_64_NONE) continue;322 if (rel.r_type() == elf.R_X86_64_NONE) continue;
323323
324 const symbol = switch (file_ptr) {324 const symbol = switch (file_ptr) {
325 .zig_module => elf_file.symbol(rel.r_sym()),325 .zig_module => |x| elf_file.symbol(x.symbol(rel.r_sym())),
326 .object => |x| elf_file.symbol(x.symbols.items[rel.r_sym()]),326 .object => |x| elf_file.symbol(x.symbols.items[rel.r_sym()]),
327 else => unreachable,327 else => unreachable,
328 };328 };
329329
330 // Check for violation of One Definition Rule for COMDATs.
331 if (symbol.file(elf_file) == null) {
332 // TODO convert into an error
333 log.debug("{}: {s}: {s} refers to a discarded COMDAT section", .{
334 file_ptr.fmtPath(),
335 self.name(elf_file),
336 symbol.name(elf_file),
337 });
338 continue;
339 }
340
341 // Report an undefined symbol.
342 try self.reportUndefined(elf_file, symbol, rel, undefs);
343
330 // While traversing relocations, mark symbols that require special handling such as344 // While traversing relocations, mark symbols that require special handling such as
331 // pointer indirection via GOT, or a stub trampoline via PLT.345 // pointer indirection via GOT, or a stub trampoline via PLT.
332 switch (rel.r_type()) {346 switch (rel.r_type()) {
...@@ -363,6 +377,28 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf) !void {...@@ -363,6 +377,28 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf) !void {
363 }377 }
364}378}
365379
380// This function will report any undefined non-weak symbols that are not imports.
381fn reportUndefined(self: Atom, elf_file: *Elf, sym: *const Symbol, rel: elf.Elf64_Rela, undefs: anytype) !void {
382 const rel_esym = switch (elf_file.file(self.file_index).?) {
383 .zig_module => |x| x.elfSym(rel.r_sym()).*,
384 .object => |x| x.symtab[rel.r_sym()],
385 else => unreachable,
386 };
387 const esym = sym.elfSym(elf_file);
388 if (rel_esym.st_shndx == elf.SHN_UNDEF and
389 rel_esym.st_bind() == elf.STB_GLOBAL and
390 sym.esym_index > 0 and
391 !sym.flags.import and
392 esym.st_shndx == elf.SHN_UNDEF)
393 {
394 const gop = try undefs.getOrPut(sym.index);
395 if (!gop.found_existing) {
396 gop.value_ptr.* = std.ArrayList(Atom.Index).init(elf_file.base.allocator);
397 }
398 try gop.value_ptr.append(self.atom_index);
399 }
400}
401
366/// TODO mark relocs dirty402/// TODO mark relocs dirty
367pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void {403pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void {
368 relocs_log.debug("0x{x}: {s}", .{ self.value, self.name(elf_file) });404 relocs_log.debug("0x{x}: {s}", .{ self.value, self.name(elf_file) });
...@@ -376,7 +412,7 @@ pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void {...@@ -376,7 +412,7 @@ pub fn resolveRelocs(self: Atom, elf_file: *Elf, code: []u8) !void {
376 if (r_type == elf.R_X86_64_NONE) continue;412 if (r_type == elf.R_X86_64_NONE) continue;
377413
378 const target = switch (file_ptr) {414 const target = switch (file_ptr) {
379 .zig_module => elf_file.symbol(rel.r_sym()),415 .zig_module => |x| elf_file.symbol(x.symbol(rel.r_sym())),
380 .object => |x| elf_file.symbol(x.symbols.items[rel.r_sym()]),416 .object => |x| elf_file.symbol(x.symbols.items[rel.r_sym()]),
381 else => unreachable,417 else => unreachable,
382 };418 };
...@@ -564,3 +600,4 @@ const Allocator = std.mem.Allocator;...@@ -564,3 +600,4 @@ const Allocator = std.mem.Allocator;
564const Atom = @This();600const Atom = @This();
565const Elf = @import("../Elf.zig");601const Elf = @import("../Elf.zig");
566const File = @import("file.zig").File;602const File = @import("file.zig").File;
603const Symbol = @import("Symbol.zig");
src/link/Elf/Object.zig+2-2
...@@ -392,14 +392,14 @@ fn filterRelocs(...@@ -392,14 +392,14 @@ fn filterRelocs(
392 return .{ .start = f_start, .len = f_len };392 return .{ .start = f_start, .len = f_len };
393}393}
394394
395pub fn scanRelocs(self: *Object, elf_file: *Elf) !void {395pub fn scanRelocs(self: *Object, elf_file: *Elf, undefs: anytype) !void {
396 for (self.atoms.items) |atom_index| {396 for (self.atoms.items) |atom_index| {
397 const atom = elf_file.atom(atom_index) orelse continue;397 const atom = elf_file.atom(atom_index) orelse continue;
398 if (!atom.alive) continue;398 if (!atom.alive) continue;
399 const shdr = atom.inputShdr(elf_file);399 const shdr = atom.inputShdr(elf_file);
400 if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue;400 if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue;
401 if (shdr.sh_type == elf.SHT_NOBITS) continue;401 if (shdr.sh_type == elf.SHT_NOBITS) continue;
402 try atom.scanRelocs(elf_file);402 try atom.scanRelocs(elf_file, undefs);
403 }403 }
404404
405 for (self.cies.items) |cie| {405 for (self.cies.items) |cie| {
src/link/Elf/Symbol.zig+2-7
...@@ -43,7 +43,7 @@ pub fn isLocal(symbol: Symbol) bool {...@@ -43,7 +43,7 @@ pub fn isLocal(symbol: Symbol) bool {
43 return !(symbol.flags.import or symbol.flags.@"export");43 return !(symbol.flags.import or symbol.flags.@"export");
44}44}
4545
46pub inline fn isIFunc(symbol: Symbol, elf_file: *Elf) bool {46pub fn isIFunc(symbol: Symbol, elf_file: *Elf) bool {
47 return symbol.type(elf_file) == elf.STT_GNU_IFUNC;47 return symbol.type(elf_file) == elf.STT_GNU_IFUNC;
48}48}
4949
...@@ -69,12 +69,7 @@ pub fn file(symbol: Symbol, elf_file: *Elf) ?File {...@@ -69,12 +69,7 @@ pub fn file(symbol: Symbol, elf_file: *Elf) ?File {
69pub fn elfSym(symbol: Symbol, elf_file: *Elf) elf.Elf64_Sym {69pub fn elfSym(symbol: Symbol, elf_file: *Elf) elf.Elf64_Sym {
70 const file_ptr = symbol.file(elf_file).?;70 const file_ptr = symbol.file(elf_file).?;
71 switch (file_ptr) {71 switch (file_ptr) {
72 .zig_module => |x| {72 .zig_module => |x| return x.elfSym(symbol.esym_index).*,
73 const is_global = symbol.esym_index & 0x10000000 != 0;
74 const esym_index = symbol.esym_index & 0x0fffffff;
75 if (is_global) return x.global_esyms.items[esym_index];
76 return x.local_esyms.items[esym_index];
77 },
78 .linker_defined => |x| return x.symtab.items[symbol.esym_index],73 .linker_defined => |x| return x.symtab.items[symbol.esym_index],
79 .object => |x| return x.symtab[symbol.esym_index],74 .object => |x| return x.symtab[symbol.esym_index],
80 }75 }
src/link/Elf/ZigModule.zig+22-3
...@@ -1,3 +1,8 @@...@@ -1,3 +1,8 @@
1//! ZigModule encapsulates the state of the incrementally compiled Zig module.
2//! It stores the associated input local and global symbols, allocated atoms,
3//! and any relocations that may have been emitted.
4//! Think about this as fake in-memory Object file for the Zig module.
5
1/// Path is owned by Module and lives as long as *Module.6/// Path is owned by Module and lives as long as *Module.
2path: []const u8,7path: []const u8,
3index: File.Index,8index: File.Index,
...@@ -41,7 +46,7 @@ pub fn addGlobalEsym(self: *ZigModule, allocator: Allocator) !Symbol.Index {...@@ -41,7 +46,7 @@ pub fn addGlobalEsym(self: *ZigModule, allocator: Allocator) !Symbol.Index {
41 const esym = self.global_esyms.addOneAssumeCapacity();46 const esym = self.global_esyms.addOneAssumeCapacity();
42 esym.* = Elf.null_sym;47 esym.* = Elf.null_sym;
43 esym.st_info = elf.STB_GLOBAL << 4;48 esym.st_info = elf.STB_GLOBAL << 4;
44 return index;49 return index | 0x10000000;
45}50}
4651
47pub fn addAtom(self: *ZigModule, output_section_index: u16, elf_file: *Elf) !Symbol.Index {52pub fn addAtom(self: *ZigModule, output_section_index: u16, elf_file: *Elf) !Symbol.Index {
...@@ -135,11 +140,11 @@ pub fn claimUnresolved(self: *ZigModule, elf_file: *Elf) void {...@@ -135,11 +140,11 @@ pub fn claimUnresolved(self: *ZigModule, elf_file: *Elf) void {
135 }140 }
136}141}
137142
138pub fn scanRelocs(self: *ZigModule, elf_file: *Elf) !void {143pub fn scanRelocs(self: *ZigModule, elf_file: *Elf, undefs: anytype) !void {
139 for (self.atoms.keys()) |atom_index| {144 for (self.atoms.keys()) |atom_index| {
140 const atom = elf_file.atom(atom_index) orelse continue;145 const atom = elf_file.atom(atom_index) orelse continue;
141 if (!atom.alive) continue;146 if (!atom.alive) continue;
142 try atom.scanRelocs(elf_file);147 try atom.scanRelocs(elf_file, undefs);
143 }148 }
144}149}
145150
...@@ -197,6 +202,20 @@ pub fn writeSymtab(self: *ZigModule, elf_file: *Elf, ctx: anytype) void {...@@ -197,6 +202,20 @@ pub fn writeSymtab(self: *ZigModule, elf_file: *Elf, ctx: anytype) void {
197 }202 }
198}203}
199204
205pub fn symbol(self: *ZigModule, index: Symbol.Index) Symbol.Index {
206 const is_global = index & 0x10000000 != 0;
207 const actual_index = index & 0x0fffffff;
208 if (is_global) return self.global_symbols.items[actual_index];
209 return self.local_symbols.items[actual_index];
210}
211
212pub fn elfSym(self: *ZigModule, index: Symbol.Index) *elf.Elf64_Sym {
213 const is_global = index & 0x10000000 != 0;
214 const actual_index = index & 0x0fffffff;
215 if (is_global) return &self.global_esyms.items[actual_index];
216 return &self.local_esyms.items[actual_index];
217}
218
200pub fn locals(self: *ZigModule) []const Symbol.Index {219pub fn locals(self: *ZigModule) []const Symbol.Index {
201 return self.local_symbols.items;220 return self.local_symbols.items;
202}221}
src/link/Elf/file.zig+1-1
...@@ -23,7 +23,7 @@ pub const File = union(enum) {...@@ -23,7 +23,7 @@ pub const File = union(enum) {
23 _ = unused_fmt_string;23 _ = unused_fmt_string;
24 _ = options;24 _ = options;
25 switch (file) {25 switch (file) {
26 .zig_module => try writer.writeAll("(zig module)"),26 .zig_module => |x| try writer.print("{s}", .{x.path}),
27 .linker_defined => try writer.writeAll("(linker defined)"),27 .linker_defined => try writer.writeAll("(linker defined)"),
28 .object => |x| try writer.print("{}", .{x.fmtPath()}),28 .object => |x| try writer.print("{}", .{x.fmtPath()}),
29 // .shared_object => |x| try writer.writeAll(x.path),29 // .shared_object => |x| try writer.writeAll(x.path),