authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-05-29 14:01:38+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-05-30 14:13:17+02:00
log7cfe6c7c13e9931c162edc88d7e0c79fe17752f2
tree90db2a8a1991bb2989a83ca7d4ce4892130d9287
parentf66a9be7cc4e63ed9328d3488df7e7e7784dce49

Elf2: implement copy relocations

These are a way of avoiding text relocations in dynamic executables, but are also necessary for correctness in some cases due to PC32 relocations overflowing with rtld's default mmap behavior. See implementation for more details. This fixes the "Symbol 'x' causes overflow in R_X86_64_PC32 relocation" errors which were previously emitted quite often by rtld on dynamic executables linked with Elf2. Notably, this means the Zig compiler can now be linked against LLVM, statically or dynamically, with no errors and no workarounds needed. Also, fix a possible (although rare-ish) memory corruption, caused by a node slice being accessed after the slice was potentially invalidated by the addition of a new node. It seems that this was occasionally manifesting as a corrupted symbol table entry leading to an integer overflow when applying a relocation.

1 files changed, 489 insertions(+), 225 deletions(-)

src/link/Elf2.zig+489-225
......@@ -49,6 +49,20 @@ globals: struct {
4949 strong_undef: std.array_hash_map.Auto(String(.strtab), Symbol.Global),
5050 weak_undef: std.array_hash_map.Auto(String(.strtab), Symbol.Global),
5151},
52/// Key is the name of an undef global for which we have created a "copy relocation" (`R_*_COPY`).
53copied_globals: std.array_hash_map.Auto(String(.strtab), struct {
54 node: MappedFile.Node.Index,
55 /// The index of this global's runtime relocation in `.rela.dyn`.
56 rela_index: Section.RelaIndex,
57}),
58/// Key is the name of an undef global for which we would *like* to create a copy relocation
59/// (`R_*_COPY`), but cannot because we have not seen an appropriate definition in a linked DSO yet.
60///
61/// Therefore, if, when scanning a DSO input, we discover a definition for one of these symbols, we
62/// will remove it from this map and call `
63///
64/// which have not created
65want_copied_globals: std.array_hash_map.Auto(String(.strtab), void),
5266/// Key is a node which is a valid `Symbol.node` value, value is the name of the first global symbol
5367/// in that node. That symbol is the head of a linked list: see `Symbol.Global.next_in_node`.
5468///
......@@ -57,14 +71,29 @@ globals: struct {
5771/// We use a separate hash map for this data rather than storing it in `navs` etc to save memory,
5872/// because the vast majority of nodes which can export global symbols actually will not.
5973node_global_symbols: std.array_hash_map.Auto(MappedFile.Node.Index, String(.strtab)),
60/// Contains all globals symbols defined in any needed DSO. This map serves two purposes:
74/// Contains all globals symbols defined in any needed DSO. This map serves three purposes:
75///
76/// * If we discover an undefined reference to one of these symbols, we know whether the symbol has
77/// type `STT_FUNC`, in which case we will create a PLT entry.
6178///
62/// * If we discover an undefined reference to one of these symbols, we will know the associated
63/// symbol type, which is important because it may cause us to create a PLT entry.
79/// * If we discover a direct relocation (i.e. no GOT or PLT indirection) targeting one of these
80/// symbols, we know whether the symbol has type `STT_OBJECT` and we know its size and alignment,
81/// so we can emit a copy relocation for that symbol instead of using a text relocation.
6482///
6583/// * When emitting a dynamic executable, we can detect which undefined references are resolved by a
6684/// linked DSO, so can emit "undefined global symbol" errors for any other undefined references.
67dso_globals: std.array_hash_map.Auto(String(.strtab), std.elf.STT),
85dso_globals: std.array_hash_map.Auto(String(.strtab), struct {
86 type: std.elf.STT,
87 size: u64,
88 /// This is usually unnecessary, but if a symbol is given a copy relocation (`R_*_COPY`) and so
89 /// becomes a part of the executable's address space despite being defined by a different DSO,
90 /// we need to know its alignment requirement so that we don't break other code. This isn't
91 /// actually stored on the symbol---instead we compute a maximum alignment from the alignment of
92 /// the section containing the symbol, and the symbol's offset within the section. I know this
93 /// sounds like a terrible hack, but it is *genuinely* how you're supposed to do this. Copy
94 /// relocations suck.
95 alignment: std.mem.Alignment,
96}),
6897shstrtab: StringTable,
6998strtab: StringTable,
7099dynstr: StringTable,
......@@ -136,22 +165,24 @@ synth_prog_node: std.Progress.Node,
136165input_prog_node: std.Progress.Node,
137166
138167const Node = union(enum) {
139 /// Cannot contain relocations.
140168 file,
141 /// Cannot contain relocations.
142169 ehdr,
143 /// Cannot contain relocations.
144170 shdr,
145 /// Cannot contain relocations.
146171 segment: u32,
147172 /// The section '.plt' may contain relocations via `elf.plt_first_symbol_reloc`.
148173 ///
149174 /// The section '.dynamic' may contain relocations via `elf.dynamic_first_symbol_reloc`.
150 ///
151 /// Otherwise, cannot contain relocations.
152175 section: Section.Index,
153176 /// May contain relocations.
154177 input_section: InputSection.Index,
178 /// Value is the name of a global which has an entry in `elf.copied_globals`, so, a global for
179 /// which we have emitted a copy relocation.
180 ///
181 /// TODO it would be better to emit these into `.bss` or `.bss.rel.ro`, once we support those.
182 ///
183 /// TODO: currently, the `elf.copied_globals` entry may not be there---this case exists because
184 /// `MappedFile` does not (yet?) support deleting nodes. See logic in `setGlobalSymbolValue`.
185 copied_global: String(.strtab),
155186 /// May contain relocations.
156187 nav: NavMapIndex,
157188 /// May contain relocations.
......@@ -746,6 +777,7 @@ const GotReloc = struct {
746777 .ehdr => unreachable,
747778 .shdr => unreachable,
748779 .segment => unreachable,
780 .copied_global => unreachable,
749781 .section => |shndx| shndx.vaddr(elf),
750782 .input_section => |isi| isi.ptrConst(elf).vaddr,
751783 inline .nav,
......@@ -808,6 +840,15 @@ pub const MachineRelocType = union {
808840 .X86_64 => .{ .X86_64 = .NONE },
809841 };
810842 }
843 pub fn copy(elf: *Elf) MachineRelocType {
844 return switch (elf.ehdrField(.machine)) {
845 else => unreachable,
846 .AARCH64 => .{ .AARCH64 = .COPY },
847 .PPC64 => .{ .PPC64 = .COPY },
848 .RISCV => .{ .RISCV = .COPY },
849 .X86_64 => .{ .X86_64 = .COPY },
850 };
851 }
811852 pub fn jumpSlot(elf: *Elf) MachineRelocType {
812853 return switch (elf.ehdrField(.machine)) {
813854 else => unreachable,
......@@ -973,6 +1014,7 @@ const SymbolReloc = struct {
9731014 .ehdr => unreachable,
9741015 .shdr => unreachable,
9751016 .segment => unreachable,
1017 .copied_global => unreachable,
9761018 .section => |shndx| shndx.vaddr(elf),
9771019 .input_section => |isi| isi.ptrConst(elf).vaddr,
9781020 inline .nav,
......@@ -984,11 +1026,9 @@ const SymbolReloc = struct {
9841026 const dest_vaddr = node_vaddr + reloc.offset;
9851027 const dest_slice = reloc.node.slice(&elf.mf)[@intCast(reloc.offset)..];
9861028 const target_endian = elf.targetEndian();
987 const sym_value: u64, const sym_size: u64 = switch (elf.symPtr(reloc.target.index(elf))) {
988 inline else => |target_sym| .{
989 elf.targetLoad(&target_sym.value),
990 elf.targetLoad(&target_sym.size),
991 },
1029 const sym_value: u64 = reloc.target.value(elf);
1030 const sym_size: u64 = switch (elf.symPtr(reloc.target.index(elf))) {
1031 inline else => |target_sym| elf.targetLoad(&target_sym.size),
9921032 };
9931033 const target_value = sym_value +% @as(u64, @bitCast(reloc.addend));
9941034 type: switch (reloc.type) {
......@@ -1114,6 +1154,12 @@ const SymbolReloc = struct {
11141154
11151155 fn delete(reloc: *SymbolReloc, elf: *Elf, index: SymbolReloc.Index) void {
11161156 assert(index.get(elf) == reloc);
1157
1158 reloc.deleteOutputRel(elf);
1159 if (reloc.type.dependsOnTlsSize()) {
1160 assert(elf.tls_size_symbol_relocs.swapRemove(index));
1161 }
1162
11171163 switch (reloc.prev) {
11181164 .none => {
11191165 const target_ptr = reloc.target.index(elf).ptr(elf);
......@@ -1126,18 +1172,25 @@ const SymbolReloc = struct {
11261172 .none => {},
11271173 else => |next| next.get(elf).prev = reloc.prev,
11281174 }
1129 if (reloc.rela_index.unwrap()) |rela_index| {
1130 reloc.relaSection(elf).relaDeleteOne(elf, rela_index);
1131 switch (elf.nodeWantsDsoRelocation(reloc.node)) {
1175
1176 reloc.* = undefined;
1177 }
1178
1179 /// If `reloc.rela_index` is populated, reset it to `.none` and delete the relocation, updating
1180 /// `elf.textrel_count` if necessary.
1181 fn deleteOutputRel(reloc: *SymbolReloc, elf: *Elf) void {
1182 const rela_index = reloc.rela_index.unwrap() orelse return;
1183 reloc.relaSection(elf).relaDeleteOne(elf, rela_index);
1184 switch (elf.ehdrField(.type)) {
1185 .NONE, .CORE, _ => unreachable,
1186 .REL => {},
1187 .EXEC, .DYN => switch (elf.nodeWantsDsoRelocation(reloc.node)) {
11321188 .no => unreachable, // there *was* a dynamic relocation!
11331189 .yes => {},
11341190 .yes_textrel => elf.textrel_count -= 1,
1135 }
1136 }
1137 if (reloc.type.dependsOnTlsSize()) {
1138 assert(elf.tls_size_symbol_relocs.swapRemove(index));
1191 },
11391192 }
1140 reloc.* = undefined;
1193 reloc.rela_index = .none;
11411194 }
11421195};
11431196
......@@ -1468,7 +1521,9 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
14681521 };
14691522
14701523 const @"type": std.elf.STT = switch (opts.type) {
1471 .NOTYPE => elf.dso_globals.get(opts.name.strtab) orelse .NOTYPE,
1524 .NOTYPE => if (elf.dso_globals.get(opts.name.strtab)) |dso_global| t: {
1525 break :t dso_global.type;
1526 } else .NOTYPE,
14721527 else => |t| t,
14731528 };
14741529
......@@ -1605,6 +1660,23 @@ fn setGlobalSymbolValue(
16051660 assert(global_ptr.prev_in_node == .empty);
16061661 }
16071662
1663 if (elf.copied_globals.fetchSwapRemove(global_name)) |copied_global_kv| {
1664 // This is a quite rare case: there was a definition for this symbol in a shared library
1665 // input, and we ended up emitting a copy relocation for it, but we've now got our *own*
1666 // definition which replaces it. We know that our definition cannot be preempted because we
1667 // are the executable (only executables can have copy relocations!), so we definitely do not
1668 // need the copy relocation.
1669
1670 // All we actually need to do is remove the entry from `copied_globals` (already done), and
1671 // delete the actual `R_*_COPY` relocation. Of course, we also need to re-apply relocations
1672 // targeting this symbol, but we were going to do that at the end of this function anyway.
1673 elf.shndx.rela_dyn.relaDeleteOne(elf, copied_global_kv.value.rela_index);
1674 // TODO: once `MappedFile` has a way to delete a node (so it can re-use the space), we
1675 // should delete `copied_global_kv.value.node`, which is an "orphaned" `copied_global` node.
1676 } else {
1677 _ = elf.want_copied_globals.swapRemove(global_name);
1678 }
1679
16081680 global_ptr.symtab_index.ptr(elf).node = new.node;
16091681
16101682 const old_head: String(.strtab) = old_head: {
......@@ -1668,21 +1740,7 @@ fn setGlobalSymbolValue(
16681740 // If this symbol was previously undefined, relocations targeting it may have been lowered to
16691741 // runtime relocations which we have now discovered we do not need, so delete those.
16701742 if (elf.shndx.dynamic != .UNDEF) {
1671 var ri = global_ptr.symtab_index.ptr(elf).first_target_reloc;
1672 while (ri != .none) {
1673 const reloc = ri.get(elf);
1674 assert(reloc.target == Symbol.Id.global(global_name));
1675 if (reloc.rela_index.unwrap()) |rela_index| {
1676 reloc.relaSection(elf).relaDeleteOne(elf, rela_index);
1677 switch (elf.nodeWantsDsoRelocation(reloc.node)) {
1678 .no => unreachable, // there *was* a dynamic relocation!
1679 .yes => {},
1680 .yes_textrel => elf.textrel_count -= 1,
1681 }
1682 reloc.rela_index = .none;
1683 }
1684 ri = reloc.next;
1685 }
1743 Symbol.Id.global(global_name).deleteDynamicTargetRelocs(elf);
16861744 }
16871745
16881746 // Finally, update the symbol value, re-applying target relocations. Also note that because we
......@@ -2029,6 +2087,9 @@ const Symbol = struct {
20292087 };
20302088 }
20312089
2090 /// Returns the value of this symbol, or 0 if it is undefined. If the symbol is an undefined
2091 /// global for which we have emitted a copy relocation, returns the virtual address of that
2092 /// copy relocation, which the symbol is guaranteed to resolve to at runtime.
20322093 fn value(s: Symbol.Id, elf: *Elf) u64 {
20332094 return switch (elf.symPtr(s.index(elf))) {
20342095 inline else => |sym| elf.targetLoad(&sym.value),
......@@ -2084,12 +2145,36 @@ const Symbol = struct {
20842145 }
20852146 }
20862147
2148 /// Scans through all relocations targeting `sym_id` and deletes each one's dynamic
2149 /// relocation entry, if it has one.
2150 ///
2151 /// Asserts we are creating a DSO.
2152 fn deleteDynamicTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void {
2153 assert(elf.ehdrField(.type) != .REL);
2154 assert(elf.shndx.dynamic != .UNDEF);
2155 var ri = sym_id.index(elf).ptr(elf).first_target_reloc;
2156 while (ri != .none) {
2157 const reloc = ri.get(elf);
2158 assert(reloc.target == sym_id);
2159 reloc.deleteOutputRel(elf);
2160 ri = reloc.next;
2161 }
2162 }
2163
20872164 /// Returns `true` if the target of `s` has moved, meaning the symbol's value will change at
20882165 /// some point due to a call to `flushMoved`.
20892166 fn hasMoved(s: Symbol.Id, elf: *Elf) bool {
20902167 const node = s.index(elf).ptr(elf).node;
2091 if (node == .none) return false;
2092 return node.hasMoved(&elf.mf);
2168 if (node != .none) {
2169 return node.hasMoved(&elf.mf);
2170 }
2171 switch (s.unwrap()) {
2172 .local => {},
2173 .global => |name| if (elf.copied_globals.getPtr(name)) |copied_global| {
2174 return copied_global.node.hasMoved(&elf.mf);
2175 },
2176 }
2177 return false;
20932178 }
20942179 };
20952180};
......@@ -2110,6 +2195,7 @@ pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId {
21102195 .segment,
21112196 .section,
21122197 .input_section,
2198 .copied_global,
21132199 => unreachable,
21142200
21152201 inline .nav,
......@@ -2204,7 +2290,7 @@ pub fn addReloc(
22042290) !void {
22052291 const node: MappedFile.Node.Index = Node.fromAtom(atom);
22062292 try elf.ensureUnusedRelocCapacity(node, 1);
2207 elf.addRelocAssumeCapacity(node, offset, .fromTypeErased(target), addend, @"type");
2293 try elf.addRelocAssumeCapacity(node, offset, .fromTypeErased(target), addend, @"type");
22082294}
22092295pub fn navSymbol(elf: *Elf, nav_index: InternPool.Nav.Index) !link.File.SymbolId {
22102296 const zcu = elf.base.comp.zcu.?;
......@@ -2252,7 +2338,7 @@ pub fn getVAddr(elf: *Elf, reloc_info: link.File.RelocInfo, target: link.File.Sy
22522338 const node: MappedFile.Node.Index = Node.fromAtom(reloc_info.parent.atom_index);
22532339 const target_sym: Symbol.Id = .fromTypeErased(target);
22542340 try elf.ensureUnusedRelocCapacity(node, 1);
2255 elf.addRelocAssumeCapacity(
2341 try elf.addRelocAssumeCapacity(
22562342 node,
22572343 reloc_info.offset,
22582344 target_sym,
......@@ -2511,6 +2597,8 @@ fn create(
25112597 .strong_undef = .empty,
25122598 .weak_undef = .empty,
25132599 },
2600 .copied_globals = .empty,
2601 .want_copied_globals = .empty,
25142602 .node_global_symbols = .empty,
25152603 .dso_globals = .empty,
25162604 .shstrtab = .{ .map = .empty },
......@@ -2558,6 +2646,8 @@ pub fn deinit(elf: *Elf) void {
25582646 elf.globals.weak_def.deinit(gpa);
25592647 elf.globals.strong_undef.deinit(gpa);
25602648 elf.globals.weak_undef.deinit(gpa);
2649 elf.copied_globals.deinit(gpa);
2650 elf.want_copied_globals.deinit(gpa);
25612651 elf.node_global_symbols.deinit(gpa);
25622652 elf.dso_globals.deinit(gpa);
25632653 elf.shstrtab.map.deinit(gpa);
......@@ -3117,14 +3207,14 @@ fn initHeaders(
31173207 });
31183208 elf.plt_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len);
31193209 try elf.ensureUnusedRelocCapacity(plt_ni, 2);
3120 elf.addRelocAssumeCapacity(
3210 try elf.addRelocAssumeCapacity(
31213211 plt_ni,
31223212 2,
31233213 got_plt_sym,
31243214 8 * 1 - 4,
31253215 .{ .X86_64 = .PC32 },
31263216 );
3127 elf.addRelocAssumeCapacity(
3217 try elf.addRelocAssumeCapacity(
31283218 plt_ni,
31293219 8,
31303220 got_plt_sym,
......@@ -3267,7 +3357,7 @@ pub fn endProgress(elf: *Elf) void {
32673357fn getNode(elf: *const Elf, ni: MappedFile.Node.Index) Node {
32683358 return elf.nodes.get(@intFromEnum(ni));
32693359}
3270/// Asserts that `ni` is a section, input section, NAV, UAV, or lazy code/data.
3360/// Asserts that `ni` is a section, input section, copied global, NAV, UAV, or lazy code/data.
32713361fn getNodeShndx(elf: *const Elf, ni: MappedFile.Node.Index) Section.Index {
32723362 return switch (elf.getNode(ni)) {
32733363 .file => unreachable,
......@@ -3278,6 +3368,7 @@ fn getNodeShndx(elf: *const Elf, ni: MappedFile.Node.Index) Section.Index {
32783368 .section => |shndx| shndx,
32793369
32803370 .input_section,
3371 .copied_global,
32813372 .nav,
32823373 .uav,
32833374 .lazy_code,
......@@ -3294,6 +3385,7 @@ fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
32943385 },
32953386 .section => |shndx| if (shndx == elf.shndx.tdata) 0 else shndx.vaddr(elf),
32963387 .input_section => unreachable,
3388 .copied_global => unreachable,
32973389 inline .nav, .uav, .lazy_code, .lazy_const_data => |i| Symbol.Id.local(i.symbol(elf)).value(elf),
32983390 };
32993391 const offset, _ = ni.location(&elf.mf).resolve(&elf.mf);
......@@ -3312,6 +3404,7 @@ fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void {
33123404 .shdr => unreachable, // cannot contain relocs
33133405 .segment => unreachable, // cannot contain relocs
33143406 .section => unreachable, // cannot contain relocs (.plt and .dynamic unsupported)
3407 .copied_global => unreachable, // cannot contain relocs
33153408 .input_section => |isi| .{
33163409 &elf.input_sections.items[@intFromEnum(isi)].first_symbol_reloc,
33173410 &elf.input_sections.items[@intFromEnum(isi)].first_got_reloc,
......@@ -4270,7 +4363,7 @@ fn loadObject(
42704363 .{rel.info.sym},
42714364 );
42724365 }
4273 elf.addRelocAssumeCapacity(
4366 try elf.addRelocAssumeCapacity(
42744367 loc_node,
42754368 rel.offset - loc_sec.shdr.addr,
42764369 target,
......@@ -4303,12 +4396,19 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {
43034396 if (ehdr.machine != elf.ehdrField(.machine))
43044397 return diags.failParse(path, "bad machine", .{});
43054398 if (ehdr.shnum > 0) try fr.seekTo(ehdr.shoff);
4399 // We're going to need to know the alignment of every section later.
4400 const section_aligns = try gpa.alloc(std.mem.Alignment, ehdr.shnum);
4401 defer gpa.free(section_aligns);
43064402 const dynamic_sh: ElfN.Shdr, const dynsym_sh: ElfN.Shdr = sh: {
43074403 var dynamic_sh: ?ElfN.Shdr = null;
43084404 var dynsym_sh: ?ElfN.Shdr = null;
4309 for (0..ehdr.shnum) |_| {
4405 for (section_aligns) |*section_align| {
43104406 const sh = try r.peekStruct(ElfN.Shdr, target_endian);
43114407 try r.discardAll(ehdr.shentsize);
4408 section_align.* = .fromByteUnits(std.math.ceilPowerOfTwoAssert(
4409 usize,
4410 @intCast(@max(sh.addralign, 1)),
4411 ));
43124412 switch (sh.type) {
43134413 else => {},
43144414 .DYNAMIC => dynamic_sh = sh,
......@@ -4395,17 +4495,51 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {
43954495 }
43964496
43974497 if (sym.shndx == std.elf.SHN_UNDEF) continue;
4498 if (sym.shndx >= ehdr.shnum) continue;
43984499
43994500 if (sym.name >= dynstr.len) {
44004501 return diags.failParse(path, "bad symbol name string", .{});
44014502 }
44024503
4504 // We need to guess the worst-case alignment of the symbol. Yes, I know this seems
4505 // insane---refer to the doc comment on `alignment` in `Elf.dso_globals`.
4506 const sym_align: std.mem.Alignment = switch (sym.value) {
4507 0 => section_aligns[sym.shndx],
4508 else => section_aligns[sym.shndx].min(@enumFromInt(@ctz(sym.value))),
4509 };
4510
44034511 const name = try elf.string(.strtab, std.mem.sliceTo(dynstr[sym.name..], 0));
44044512 const gop = elf.dso_globals.getOrPutAssumeCapacity(name);
4405 if (!gop.found_existing or gop.value_ptr.* == .NOTYPE) {
4406 gop.value_ptr.* = sym.info.type;
4513
4514 if (gop.found_existing and gop.value_ptr.type != .NOTYPE) {
4515 if (sym.size > gop.value_ptr.size or
4516 sym_align.compare(.gt, gop.value_ptr.alignment))
4517 {
4518 gop.value_ptr.size = @max(gop.value_ptr.size, sym.size);
4519 gop.value_ptr.alignment = gop.value_ptr.alignment.max(sym_align);
4520 if (elf.copied_globals.get(name)) |copied_global| {
4521 // We have a copy relocation for this global, but the amount of space we
4522 // reserved for it could be too small or underaligned!
4523 try copied_global.node.resize(&elf.mf, gpa, gop.value_ptr.size);
4524 try copied_global.node.realign(&elf.mf, gpa, gop.value_ptr.alignment);
4525 const global_ptr = elf.globalByName(name).?;
4526 switch (elf.symPtr(global_ptr.symtab_index)) {
4527 inline else => |sym_ptr| elf.targetStore(&sym_ptr.size, @intCast(gop.value_ptr.size)),
4528 }
4529 switch (elf.dynsymPtr(global_ptr.dynsym_index)) {
4530 inline else => |dynsym_ptr| elf.targetStore(&dynsym_ptr.size, @intCast(gop.value_ptr.size)),
4531 }
4532 }
4533 }
4534 continue;
44074535 }
44084536
4537 gop.value_ptr.* = .{
4538 .type = sym.info.type,
4539 .size = sym.size,
4540 .alignment = sym_align,
4541 };
4542
44094543 // If there's already an undefined symbol by this name of type STT_NOTYPE, populate
44104544 // its type now.
44114545 const global_ptr = elf.globals.strong_undef.getPtr(name) orelse
......@@ -4414,14 +4548,20 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {
44144548
44154549 if (global_ptr.dynsym_index == 0) continue;
44164550
4551 if (elf.want_copied_globals.swapRemove(name)) {
4552 // We just found a DSO definition of a symbol for which we wanted a copy
4553 // relocation, so add one if we can!
4554 _ = try elf.maybeAddCopyRelocation(name);
4555 }
4556
44174557 const sym_ptr = @field(elf.symPtr(global_ptr.symtab_index), @tagName(class));
4558 errdefer comptime unreachable; // messing with the output file could invalidate `sym_ptr`
4559
44184560 switch (elf.targetLoad(&sym_ptr.other).visibility) {
44194561 .HIDDEN, .INTERNAL, .PROTECTED => continue,
44204562 .DEFAULT => {},
44214563 }
44224564
4423 if (elf.targetLoad(&sym_ptr.shndx) != std.elf.SHN_UNDEF) continue;
4424
44254565 const cur_info = elf.targetLoad(&sym_ptr.info);
44264566 if (cur_info.type == .NOTYPE) {
44274567 const new_type: std.elf.STT = switch (sym.info.type) {
......@@ -4440,9 +4580,8 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {
44404580 .type = new_type,
44414581 });
44424582
4443 // If we just turned this into an STT_FUNC symbol, then we have determined
4444 // that it needs a PLT entry.
44454583 if (new_type == .FUNC) {
4584 // We turned STT_NOTYPE into STT_FUNC, so we now need a PLT entry...
44464585 elf.addPltEntry(name, global_ptr.dynsym_index);
44474586 // ...and therefore, we need to re-apply that symbol's relocations, as
44484587 // some might be targeting its PLT entry.
......@@ -4630,6 +4769,9 @@ fn prelinkInner(elf: *Elf) !void {
46304769 }
46314770 break :rpath try elf.string(.dynstr, buf.items);
46324771 };
4772 const soname: ?String(.dynstr) = if (elf.options.soname) |soname_slice| str: {
4773 break :str try elf.string(.dynstr, soname_slice);
4774 } else null;
46334775 const needed_len = elf.needed.count();
46344776 const dynamic_len = needed_len + @intFromBool(elf.options.soname != null) +
46354777 @intFromBool(rpath != .empty) +
......@@ -4644,141 +4786,155 @@ fn prelinkInner(elf: *Elf) !void {
46444786 switch (elf.shdrPtr(elf.shndx.dynamic)) {
46454787 inline else => |shdr| elf.targetStore(&shdr.size, dynamic_size),
46464788 }
4647 const sec_dynamic = dynamic_ni.slice(&elf.mf);
4648 const dynamic_entries: [][2]ElfN.Addr = @ptrCast(@alignCast(sec_dynamic));
4649 var dynamic_index: usize = 0;
4650 for (
4651 dynamic_entries[dynamic_index..][0..needed_len],
4652 elf.needed.keys(),
4653 ) |*dynamic_entry, needed| dynamic_entry.* = .{ std.elf.DT_NEEDED, @intFromEnum(needed) };
4654 dynamic_index += needed_len;
4655 if (elf.options.soname) |soname| {
4656 dynamic_entries[dynamic_index] = .{ std.elf.DT_SONAME, @intFromEnum(try elf.string(.dynstr, soname)) };
4657 dynamic_index += 1;
4658 }
4659 if (rpath != .empty) {
4660 dynamic_entries[dynamic_index] = .{ std.elf.DT_RUNPATH, @intFromEnum(rpath) };
4661 dynamic_index += 1;
4662 }
4663 if (flags != 0) {
4664 dynamic_entries[dynamic_index] = .{ std.elf.DT_FLAGS, flags };
4665 dynamic_index += 1;
4666 }
4667 if (flags_1 != 0) {
4668 dynamic_entries[dynamic_index] = .{ std.elf.DT_FLAGS_1, flags_1 };
4669 dynamic_index += 1;
4670 }
4671 if (comp.config.output_mode == .Exe) {
4672 dynamic_entries[dynamic_index] = .{ std.elf.DT_DEBUG, 0 };
4673 dynamic_index += 1;
4674 }
4675 if (elf.shndx.init_array != .UNDEF) {
4676 dynamic_entries[dynamic_index..][0..2].* = .{
4677 .{ std.elf.DT_INIT_ARRAY, @intCast(elf.shndx.init_array.vaddr(elf)) },
4678 .{ std.elf.DT_INIT_ARRAYSZ, elf.targetLoad(
4679 &@field(elf.shdrPtr(elf.shndx.init_array), @tagName(ct_class)).size,
4789
4790 const dynamic_indices: struct {
4791 init_array: ?usize,
4792 fini_array: ?usize,
4793 preinit_array: ?usize,
4794 } = indices: {
4795 const sec_dynamic = dynamic_ni.slice(&elf.mf);
4796 const dynamic_entries: [][2]ElfN.Addr = @ptrCast(@alignCast(sec_dynamic));
4797 errdefer comptime unreachable; // don't invalidate `dynamic_entries`
4798 var dynamic_index: usize = 0;
4799 for (
4800 dynamic_entries[dynamic_index..][0..needed_len],
4801 elf.needed.keys(),
4802 ) |*dynamic_entry, needed| dynamic_entry.* = .{ std.elf.DT_NEEDED, @intFromEnum(needed) };
4803 dynamic_index += needed_len;
4804 if (soname) |soname_dynstr| {
4805 dynamic_entries[dynamic_index] = .{ std.elf.DT_SONAME, @intFromEnum(soname_dynstr) };
4806 dynamic_index += 1;
4807 }
4808 if (rpath != .empty) {
4809 dynamic_entries[dynamic_index] = .{ std.elf.DT_RUNPATH, @intFromEnum(rpath) };
4810 dynamic_index += 1;
4811 }
4812 if (flags != 0) {
4813 dynamic_entries[dynamic_index] = .{ std.elf.DT_FLAGS, flags };
4814 dynamic_index += 1;
4815 }
4816 if (flags_1 != 0) {
4817 dynamic_entries[dynamic_index] = .{ std.elf.DT_FLAGS_1, flags_1 };
4818 dynamic_index += 1;
4819 }
4820 if (comp.config.output_mode == .Exe) {
4821 dynamic_entries[dynamic_index] = .{ std.elf.DT_DEBUG, 0 };
4822 dynamic_index += 1;
4823 }
4824 const init_array_index: ?usize = if (elf.shndx.init_array != .UNDEF) i: {
4825 dynamic_entries[dynamic_index..][0..2].* = .{
4826 .{ std.elf.DT_INIT_ARRAY, @intCast(elf.shndx.init_array.vaddr(elf)) },
4827 .{ std.elf.DT_INIT_ARRAYSZ, elf.targetLoad(
4828 &@field(elf.shdrPtr(elf.shndx.init_array), @tagName(ct_class)).size,
4829 ) },
4830 };
4831 defer dynamic_index += 2;
4832 break :i dynamic_index;
4833 } else null;
4834 const fini_array_index: ?usize = if (elf.shndx.fini_array != .UNDEF) i: {
4835 dynamic_entries[dynamic_index..][0..2].* = .{
4836 .{ std.elf.DT_FINI_ARRAY, @intCast(elf.shndx.fini_array.vaddr(elf)) },
4837 .{ std.elf.DT_FINI_ARRAYSZ, elf.targetLoad(
4838 &@field(elf.shdrPtr(elf.shndx.fini_array), @tagName(ct_class)).size,
4839 ) },
4840 };
4841 defer dynamic_index += 2;
4842 break :i dynamic_index;
4843 } else null;
4844 const preinit_array_index: ?usize = if (elf.shndx.preinit_array != .UNDEF) i: {
4845 dynamic_entries[dynamic_index..][0..2].* = .{
4846 .{ std.elf.DT_PREINIT_ARRAY, @intCast(elf.shndx.preinit_array.vaddr(elf)) },
4847 .{ std.elf.DT_PREINIT_ARRAYSZ, elf.targetLoad(
4848 &@field(elf.shdrPtr(elf.shndx.preinit_array), @tagName(ct_class)).size,
4849 ) },
4850 };
4851 defer dynamic_index += 2;
4852 break :i dynamic_index;
4853 } else null;
4854 dynamic_entries[dynamic_index..][0..12].* = .{
4855 .{ std.elf.DT_RELA, @intCast(elf.shndx.rela_dyn.vaddr(elf)) },
4856 .{ std.elf.DT_RELASZ, elf.targetLoad(
4857 &@field(elf.shdrPtr(elf.shndx.rela_dyn), @tagName(ct_class)).size,
46804858 ) },
4681 };
4682 try elf.ensureUnusedRelocCapacity(dynamic_ni, 1);
4683 elf.addRelocAssumeCapacity(
4684 dynamic_ni,
4685 @sizeOf(ElfN.Addr) * (2 * dynamic_index + 1),
4686 .local(elf.shndx.init_array.get(elf).lsi),
4687 0,
4688 .absAddr(elf),
4689 );
4690 dynamic_index += 2;
4691 }
4692 if (elf.shndx.fini_array != .UNDEF) {
4693 dynamic_entries[dynamic_index..][0..2].* = .{
4694 .{ std.elf.DT_FINI_ARRAY, @intCast(elf.shndx.fini_array.vaddr(elf)) },
4695 .{ std.elf.DT_FINI_ARRAYSZ, elf.targetLoad(
4696 &@field(elf.shdrPtr(elf.shndx.fini_array), @tagName(ct_class)).size,
4859 .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) },
4860 .{ std.elf.DT_JMPREL, @intCast(elf.shndx.rela_plt.vaddr(elf)) },
4861 .{ std.elf.DT_PLTRELSZ, elf.targetLoad(
4862 &@field(elf.shdrPtr(elf.shndx.rela_plt), @tagName(ct_class)).size,
46974863 ) },
4698 };
4699 try elf.ensureUnusedRelocCapacity(dynamic_ni, 1);
4700 elf.addRelocAssumeCapacity(
4701 dynamic_ni,
4702 @sizeOf(ElfN.Addr) * (2 * dynamic_index + 1),
4703 .local(elf.shndx.fini_array.get(elf).lsi),
4704 0,
4705 .absAddr(elf),
4706 );
4707 dynamic_index += 2;
4708 }
4709 if (elf.shndx.preinit_array != .UNDEF) {
4710 dynamic_entries[dynamic_index..][0..2].* = .{
4711 .{ std.elf.DT_PREINIT_ARRAY, @intCast(elf.shndx.preinit_array.vaddr(elf)) },
4712 .{ std.elf.DT_PREINIT_ARRAYSZ, elf.targetLoad(
4713 &@field(elf.shdrPtr(elf.shndx.preinit_array), @tagName(ct_class)).size,
4864 .{ std.elf.DT_PLTGOT, @intCast(elf.shndx.got_plt.vaddr(elf)) },
4865 .{ std.elf.DT_PLTREL, std.elf.DT_RELA },
4866 .{ std.elf.DT_SYMTAB, @intCast(elf.shndx.dynsym.vaddr(elf)) },
4867 .{ std.elf.DT_SYMENT, @sizeOf(ElfN.Sym) },
4868 .{ std.elf.DT_STRTAB, @intCast(elf.shndx.dynstr.vaddr(elf)) },
4869 .{ std.elf.DT_STRSZ, elf.targetLoad(
4870 &@field(elf.shdrPtr(elf.shndx.dynstr), @tagName(ct_class)).size,
47144871 ) },
4872 .{ std.elf.DT_NULL, 0 },
4873 };
4874 dynamic_index += 12;
4875 assert(dynamic_index == dynamic_len);
4876 if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry|
4877 std.mem.byteSwapAllFields(@TypeOf(dynamic_entry.*), dynamic_entry);
4878
4879 break :indices .{
4880 .init_array = init_array_index,
4881 .fini_array = fini_array_index,
4882 .preinit_array = preinit_array_index,
47154883 };
4716 try elf.ensureUnusedRelocCapacity(dynamic_ni, 1);
4717 elf.addRelocAssumeCapacity(
4718 dynamic_ni,
4719 @sizeOf(ElfN.Addr) * (2 * dynamic_index + 1),
4720 .local(elf.shndx.preinit_array.get(elf).lsi),
4721 0,
4722 .absAddr(elf),
4723 );
4724 dynamic_index += 2;
4725 }
4726 dynamic_entries[dynamic_index..][0..12].* = .{
4727 .{ std.elf.DT_RELA, @intCast(elf.shndx.rela_dyn.vaddr(elf)) },
4728 .{ std.elf.DT_RELASZ, elf.targetLoad(
4729 &@field(elf.shdrPtr(elf.shndx.rela_dyn), @tagName(ct_class)).size,
4730 ) },
4731 .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) },
4732 .{ std.elf.DT_JMPREL, @intCast(elf.shndx.rela_plt.vaddr(elf)) },
4733 .{ std.elf.DT_PLTRELSZ, elf.targetLoad(
4734 &@field(elf.shdrPtr(elf.shndx.rela_plt), @tagName(ct_class)).size,
4735 ) },
4736 .{ std.elf.DT_PLTGOT, @intCast(elf.shndx.got_plt.vaddr(elf)) },
4737 .{ std.elf.DT_PLTREL, std.elf.DT_RELA },
4738 .{ std.elf.DT_SYMTAB, @intCast(elf.shndx.dynsym.vaddr(elf)) },
4739 .{ std.elf.DT_SYMENT, @sizeOf(ElfN.Sym) },
4740 .{ std.elf.DT_STRTAB, @intCast(elf.shndx.dynstr.vaddr(elf)) },
4741 .{ std.elf.DT_STRSZ, elf.targetLoad(
4742 &@field(elf.shdrPtr(elf.shndx.dynstr), @tagName(ct_class)).size,
4743 ) },
4744 .{ std.elf.DT_NULL, 0 },
47454884 };
4746 dynamic_index += 12;
4747 assert(dynamic_index == dynamic_len);
4748 if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry|
4749 std.mem.byteSwapAllFields(@TypeOf(dynamic_entry.*), dynamic_entry);
47504885
47514886 elf.dynamic_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len);
4752 try elf.ensureUnusedRelocCapacity(dynamic_ni, 5);
4753 elf.addRelocAssumeCapacity(
4887 try elf.ensureUnusedRelocCapacity(dynamic_ni, 8);
4888 if (dynamic_indices.init_array) |index| try elf.addRelocAssumeCapacity(
4889 dynamic_ni,
4890 @sizeOf(ElfN.Addr) * (2 * index + 1),
4891 .local(elf.shndx.init_array.get(elf).lsi),
4892 0,
4893 .absAddr(elf),
4894 );
4895 if (dynamic_indices.fini_array) |index| try elf.addRelocAssumeCapacity(
4896 dynamic_ni,
4897 @sizeOf(ElfN.Addr) * (2 * index + 1),
4898 .local(elf.shndx.fini_array.get(elf).lsi),
4899 0,
4900 .absAddr(elf),
4901 );
4902 if (dynamic_indices.preinit_array) |index| try elf.addRelocAssumeCapacity(
4903 dynamic_ni,
4904 @sizeOf(ElfN.Addr) * (2 * index + 1),
4905 .local(elf.shndx.preinit_array.get(elf).lsi),
4906 0,
4907 .absAddr(elf),
4908 );
4909 try elf.addRelocAssumeCapacity(
47544910 dynamic_ni,
47554911 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 12) + 1),
47564912 .local(elf.shndx.rela_dyn.get(elf).lsi),
47574913 0,
47584914 .absAddr(elf),
47594915 );
4760 elf.addRelocAssumeCapacity(
4916 try elf.addRelocAssumeCapacity(
47614917 dynamic_ni,
47624918 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 9) + 1),
47634919 .local(elf.shndx.rela_plt.get(elf).lsi),
47644920 0,
47654921 .absAddr(elf),
47664922 );
4767 elf.addRelocAssumeCapacity(
4923 try elf.addRelocAssumeCapacity(
47684924 dynamic_ni,
47694925 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 7) + 1),
47704926 .local(elf.shndx.got_plt.get(elf).lsi),
47714927 0,
47724928 .absAddr(elf),
47734929 );
4774 elf.addRelocAssumeCapacity(
4930 try elf.addRelocAssumeCapacity(
47754931 dynamic_ni,
47764932 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 5) + 1),
47774933 .local(elf.shndx.dynsym.get(elf).lsi),
47784934 0,
47794935 .absAddr(elf),
47804936 );
4781 elf.addRelocAssumeCapacity(
4937 try elf.addRelocAssumeCapacity(
47824938 dynamic_ni,
47834939 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 3) + 1),
47844940 .local(elf.shndx.dynstr.get(elf).lsi),
......@@ -4951,6 +5107,8 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize)
49515107 },
49525108 }
49535109}
5110/// Although this function requires a preceding call to `ensureUnusedRelocCapacity`, it is still
5111/// fallible, because there are some rare cases for which we cannot reserve capacity upfront.
49545112fn addRelocAssumeCapacity(
49555113 elf: *Elf,
49565114 node: MappedFile.Node.Index,
......@@ -4958,7 +5116,7 @@ fn addRelocAssumeCapacity(
49585116 target: Symbol.Id,
49595117 addend: i64,
49605118 @"type": MachineRelocType,
4961) void {
5119) !void {
49625120 assert(node != .none);
49635121 switch (elf.ehdrField(.type)) {
49645122 .NONE, .CORE, _ => unreachable,
......@@ -5021,25 +5179,25 @@ fn addRelocAssumeCapacity(
50215179 .TLSDESC => @panic("TODO: R_X86_64_TLSDESC"),
50225180
50235181 // Relocations targeting a symbol
5024 .@"64" => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs64),
5025 .@"32" => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32),
5026 .@"32S" => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32s),
5027 .PC64 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel64),
5028 .PC32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel32),
5029 .PLT32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .pltrel32),
5030 .SIZE64 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .size64),
5031 .SIZE32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .size32),
5032 .DTPOFF64 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .dtpoff64),
5033 .DTPOFF32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .dtpoff32),
5034 .TPOFF64 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff64),
5035 .TPOFF32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff32),
5182 .@"64" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs64),
5183 .@"32" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32),
5184 .@"32S" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32s),
5185 .PC64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel64),
5186 .PC32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel32),
5187 .PLT32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .pltrel32),
5188 .SIZE64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .size64),
5189 .SIZE32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .size32),
5190 .DTPOFF64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .dtpoff64),
5191 .DTPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .dtpoff32),
5192 .TPOFF64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff64),
5193 .TPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff32),
50365194 .GOTPC64 => {
50375195 const got_sym: Symbol.Id = .local(elf.shndx.got.get(elf).lsi);
5038 return elf.addSymbolRelocAssumeCapacity(node, offset, got_sym, addend, .rel64);
5196 try elf.addSymbolRelocAssumeCapacity(node, offset, got_sym, addend, .rel64);
50395197 },
50405198 .GOTPC32 => {
50415199 const got_sym: Symbol.Id = .local(elf.shndx.got.get(elf).lsi);
5042 return elf.addSymbolRelocAssumeCapacity(node, offset, got_sym, addend, .rel32);
5200 try elf.addSymbolRelocAssumeCapacity(node, offset, got_sym, addend, .rel32);
50435201 },
50445202
50455203 // TODO: these are the address of an arbitrary symbol (or PLT entry) relative to the
......@@ -5077,11 +5235,16 @@ fn addSymbolRelocAssumeCapacity(
50775235 target: Symbol.Id,
50785236 addend: i64,
50795237 @"type": SymbolReloc.Type,
5080) void {
5238) !void {
50815239 assert(elf.ehdrField(.type) != .REL);
50825240
50835241 const rela_index: Section.RelaIndex.Optional = r: {
50845242 if (elf.shndx.dynamic == .UNDEF) break :r .none;
5243 const global_name = switch (target.unwrap()) {
5244 .local => break :r .none,
5245 .global => |name| name,
5246 };
5247
50855248 const rela_type: MachineRelocType = switch (elf.ehdrField(.machine)) {
50865249 else => |machine| @panic(@tagName(machine)),
50875250 .X86_64 => .{ .X86_64 = switch (@"type") {
......@@ -5101,21 +5264,31 @@ fn addSymbolRelocAssumeCapacity(
51015264 .size32 => .SIZE32,
51025265 } },
51035266 };
5104 const dynsym_index: u32 = switch (target.unwrap()) {
5105 .local => break :r .none,
5106 // TODO: even if the symbol is locally defined, preemption/interposition is a
5107 // possibility, which this condition does not currently consider!
5108 .global => |name| if (elf.globals.strong_def.contains(name) or
5109 elf.globals.weak_def.contains(name))
5110 {
5111 break :r .none;
5112 } else elf.globalByName(name).?.dynsym_index,
5113 };
5267 // TODO: even if the symbol is locally defined, preemption/interposition is a
5268 // possibility, which this condition does not currently consider!
5269 if (elf.globals.strong_def.contains(global_name) or
5270 elf.globals.weak_def.contains(global_name))
5271 {
5272 break :r .none;
5273 }
5274
5275 const dynsym_index = elf.globalByName(global_name).?.dynsym_index;
5276 if (dynsym_index == 0) break :r .none;
51145277
51155278 switch (elf.nodeWantsDsoRelocation(node)) {
51165279 .no => break :r .none,
51175280 .yes => {},
5118 .yes_textrel => elf.textrel_count += 1,
5281 .yes_textrel => if (try elf.maybeAddCopyRelocation(global_name)) {
5282 // We were able to use a copy relocation on this symbol to avoid a text relocation,
5283 // which is apparently considered a good thing despite copy relocations being an
5284 // abomination. (This is necessary for correctness in some cases, because e.g. a
5285 // 32-bit runtime relocation on a 64-bit target will often cause rtld errors due to
5286 // the DSOs being loaded too far apart.)
5287 break :r .none;
5288 } else {
5289 // At least for now, our only choice is a text relocation.
5290 elf.textrel_count += 1;
5291 },
51195292 }
51205293
51215294 // It currently looks like we need a runtime relocation for this.
......@@ -5175,6 +5348,7 @@ fn addGotRelocAssumeCapacity(
51755348 .ehdr => unreachable, // cannot contain relocs
51765349 .shdr => unreachable, // cannot contain relocs
51775350 .segment => unreachable, // cannot contain relocs
5351 .copied_global => unreachable, // cannot contain relocs
51785352 }
51795353
51805354 const gop = elf.got.getOrPutAssumeCapacity(target);
......@@ -5275,29 +5449,37 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
52755449 .global => |name| name,
52765450 };
52775451 // If the symbol is *defined* in this module, we might be able to avoid the relocation.
5278 if (elf.globals.strong_def.getPtr(name) orelse
5279 elf.globals.weak_def.getPtr(name)) |global|
5280 {
5452 const need_reloc: bool = need_reloc: {
5453 const global = g: {
5454 if (elf.globals.strong_def.getPtr(name)) |g| break :g g;
5455 if (elf.globals.weak_def.getPtr(name)) |g| break :g g;
5456 // The global is undefined, which probably means we need a relocation---unless
5457 // we have created a copy relocation for it, in which case we own the canonical
5458 // address of this symbol in this DSO!
5459 break :need_reloc !elf.copied_globals.contains(name);
5460 };
5461
52815462 // We have a definition, but it might be interposable (aka preemptible). There
52825463 // are two cases where it is not and so we can (and, in fact, must) elide the
52835464 // runtime relocation:
52845465 // * We are the executable. Symbols from executables cannot be interposed.
52855466 // * The symbol's visibility disallows interposition.
52865467 if (elf.base.comp.config.output_mode == .Exe) {
5287 // No relocation needed.
5288 break :val .{ .unsigned = sym_id.value(elf) };
5468 break :need_reloc false;
52895469 }
52905470 const visibility: std.elf.STV = switch (elf.symPtr(global.symtab_index)) {
52915471 inline else => |sym| elf.targetLoad(&sym.other).visibility,
52925472 };
5293 switch (visibility) {
5294 .DEFAULT => {},
5295 .INTERNAL, .HIDDEN, .PROTECTED => {
5296 // No relocation needed.
5297 break :val .{ .unsigned = sym_id.value(elf) };
5298 },
5299 }
5473 break :need_reloc switch (visibility) {
5474 .DEFAULT => true,
5475 .INTERNAL, .HIDDEN, .PROTECTED => false,
5476 };
5477 };
5478
5479 if (!need_reloc) {
5480 break :val .{ .unsigned = sym_id.value(elf) };
53005481 }
5482
53015483 break :val .{ .reloc = .{
53025484 .type = if (tag == .symbol) .globDat(elf) else .dtpOffAddr(elf),
53035485 .dynsym_index = elf.globalByName(name).?.dynsym_index,
......@@ -5403,6 +5585,80 @@ fn nodeWantsDsoRelocation(elf: *Elf, node: MappedFile.Node.Index) enum { yes, ye
54035585 return .yes;
54045586}
54055587
5588/// If the given undefined global could have a copy relocation, creates that relocation if it does
5589/// not already exist, and returns `true`.
5590///
5591/// Returns `false` iff a copy relocation cannot currently be created for the global. If it may be
5592/// possible in future, the symbol is added to `elf.want_copied_globals` so that the copy relocation
5593/// will be created if and when we discover a suitable definition in an input DSO.
5594///
5595/// If this function creates a new copy relocation, it will also update relocations targeting the
5596/// global where needed---the caller does not need to do this.
5597///
5598/// Asserts that `elf.shndx.dynamic != .UNDEF` and that `global_name` refers to an *undefined* global.
5599fn maybeAddCopyRelocation(elf: *Elf, global_name: String(.strtab)) !bool {
5600 assert(elf.shndx.dynamic != .UNDEF);
5601
5602 const gpa = elf.base.comp.gpa;
5603
5604 const global_ptr = elf.globals.strong_undef.getPtr(global_name) orelse
5605 elf.globals.weak_undef.getPtr(global_name).?;
5606
5607 assert(global_ptr.dynsym_index != 0);
5608
5609 // Only dynamic executables may contain `R_*_COPY` relocations.
5610 if (elf.shndx.dynamic == .UNDEF) return false;
5611 if (elf.base.comp.config.output_mode != .Exe) return false;
5612
5613 const dso_global = elf.dso_globals.get(global_name) orelse {
5614 // We do not have a definition to provide the correct size for the symbol. If a definition
5615 // is discovered in a later DSO, we may at that point be able to add a copy relocation.
5616 try elf.want_copied_globals.put(gpa, global_name, {});
5617 return false;
5618 };
5619
5620 if (dso_global.type != .OBJECT) return false;
5621
5622 const gop = try elf.copied_globals.getOrPut(gpa, global_name);
5623 if (gop.found_existing) return true;
5624 errdefer assert(elf.copied_globals.pop().?.key == global_name);
5625
5626 try elf.nodes.ensureUnusedCapacity(gpa, 1);
5627 const node = try elf.mf.addLastChildNode(gpa, Section.Index.data.get(elf).ni, .{
5628 .size = dso_global.size,
5629 .alignment = dso_global.alignment,
5630 });
5631 errdefer comptime unreachable;
5632
5633 const vaddr = elf.computeNodeVAddr(node);
5634 elf.nodes.appendAssumeCapacity(.{ .copied_global = global_name });
5635 const rela_index = elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
5636 .type = .copy(elf),
5637 .offset = vaddr,
5638 .raw_sym_index = global_ptr.dynsym_index,
5639 .addend = 0,
5640 });
5641 gop.value_ptr.* = .{
5642 .node = node,
5643 .rela_index = rela_index,
5644 };
5645
5646 switch (elf.symPtr(global_ptr.symtab_index)) {
5647 inline else => |sym| elf.targetStore(&sym.size, @intCast(dso_global.size)),
5648 }
5649 switch (elf.dynsymPtr(global_ptr.dynsym_index)) {
5650 inline else => |dynsym| elf.targetStore(&dynsym.size, @intCast(dso_global.size)),
5651 }
5652
5653 // Because we now have a copy relocation, any dynamic relocations which target this symbol are
5654 // now incorrect, since we now own the canonical address of the symbol. So delete those relocs
5655 // and then update the symbol's address (and re-apply relocations targeting it of course).
5656 Symbol.Id.global(global_name).deleteDynamicTargetRelocs(elf);
5657 Symbol.Id.global(global_name).flushMoved(elf, vaddr);
5658
5659 return true;
5660}
5661
54065662pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {
54075663 elf.updateNavInner(pt, nav_index) catch |err| switch (err) {
54085664 error.OutOfMemory,
......@@ -5561,10 +5817,8 @@ pub fn flush(
55615817 error.Canceled => |e| return e,
55625818 else => |e| return comp.link_diags.fail("flush write failed: {t}", .{e}),
55635819 };
5564 const global = elf.globalByName(sym_name_strtab) orelse break :entry 0;
5565 switch (elf.symPtr(global.symtab_index)) {
5566 inline else => |sym| break :entry elf.targetLoad(&sym.value),
5567 }
5820 if (elf.globalByName(sym_name_strtab) == null) break :entry 0;
5821 break :entry Symbol.Id.global(sym_name_strtab).value(elf);
55685822 };
55695823 switch (elf.ehdrPtr()) {
55705824 inline else => |ehdr| elf.targetStore(&ehdr.entry, @intCast(entry_addr)),
......@@ -5923,15 +6177,12 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {
59236177 assert(first_name != .empty);
59246178 var name = first_name;
59256179 while (name != .empty) {
5926 const global = elf.globalByName(name).?;
5927 const old_sym_addr: u64 = switch (elf.symPtr(global.symtab_index)) {
5928 inline else => |sym| elf.targetLoad(&sym.value),
5929 };
6180 const old_sym_addr = Symbol.Id.global(name).value(elf);
59306181 Symbol.Id.global(name).flushMoved(
59316182 elf,
59326183 old_sym_addr - old_addr + addr,
59336184 );
5934 name = global.next_in_node;
6185 name = elf.globalByName(name).?.next_in_node;
59356186 }
59366187 }
59376188
......@@ -5968,18 +6219,20 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {
59686219 var lsi, const end_lsi = ii.localSymbolRange(elf);
59696220 while (lsi != end_lsi) : (lsi = @enumFromInt(@intFromEnum(lsi) + 1)) {
59706221 if (lsi.index().ptr(elf).node != ni) continue;
5971 const old_sym_addr: u64 = switch (elf.symPtr(lsi.index())) {
5972 inline else => |sym| switch (elf.targetLoad(&sym.other).visibility) {
5973 .HIDDEN, .INTERNAL => {
5974 // This is actually a global symbol which got demoted to STB_LOCAL due
5975 // to its visibility. It will be handled in the global symbols pass
5976 // below; don't touch it now.
5977 continue;
5978 },
5979 .PROTECTED => unreachable, // not allowed for an STB_LOCAL symbol
5980 .DEFAULT => elf.targetLoad(&sym.value),
5981 },
6222 const visibility: std.elf.STV = switch (elf.symPtr(lsi.index())) {
6223 inline else => |sym| elf.targetLoad(&sym.other).visibility,
59826224 };
6225 switch (visibility) {
6226 .HIDDEN, .INTERNAL => {
6227 // This is actually a global symbol which got demoted to STB_LOCAL due
6228 // to its visibility. It will be handled in the global symbols pass
6229 // below; don't touch it now.
6230 continue;
6231 },
6232 .PROTECTED => unreachable, // not allowed for an STB_LOCAL symbol
6233 .DEFAULT => {},
6234 }
6235 const old_sym_addr = Symbol.Id.local(lsi).value(elf);
59836236 Symbol.Id.local(lsi).flushMoved(
59846237 elf,
59856238 old_sym_addr - old_section_addr + new_section_addr,
......@@ -5991,15 +6244,12 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {
59916244 assert(first_name != .empty);
59926245 var name = first_name;
59936246 while (name != .empty) {
5994 const global = elf.globalByName(name).?;
5995 const old_sym_addr: u64 = switch (elf.symPtr(global.symtab_index)) {
5996 inline else => |sym| elf.targetLoad(&sym.value),
5997 };
6247 const old_sym_addr = Symbol.Id.global(name).value(elf);
59986248 Symbol.Id.global(name).flushMoved(
59996249 elf,
60006250 old_sym_addr - old_section_addr + new_section_addr,
60016251 );
6002 name = global.next_in_node;
6252 name = elf.globalByName(name).?.next_in_node;
60036253 }
60046254 }
60056255
......@@ -6010,6 +6260,19 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {
60106260 isi.ptrConst(elf).first_got_reloc,
60116261 );
60126262 },
6263 .copied_global => |global_name| {
6264 const copied_global = elf.copied_globals.getPtr(global_name) orelse {
6265 // TODO: this node is orphaned, which is possible because `MappedFile` does not yet
6266 // support deleting nodes. See logic in `setGlobalSymbolValue`.
6267 return;
6268 };
6269 assert(copied_global.node == ni);
6270
6271 const new_addr = elf.computeNodeVAddr(ni);
6272 elf.shndx.rela_dyn.relaSetOffset(elf, copied_global.rela_index, new_addr);
6273
6274 Symbol.Id.global(global_name).flushMoved(elf, new_addr);
6275 },
60136276 inline .nav, .uav, .lazy_code, .lazy_const_data => |mi| {
60146277 const new_addr = elf.computeNodeVAddr(ni);
60156278 Symbol.Id.local(mi.symbol(elf)).flushMoved(elf, new_addr);
......@@ -6137,7 +6400,7 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void {
61376400 }
61386401 },
61396402 },
6140 .input_section, .nav, .uav, .lazy_code, .lazy_const_data => {},
6403 .copied_global, .input_section, .nav, .uav, .lazy_code, .lazy_const_data => {},
61416404 }
61426405}
61436406fn updateDynamicEntry(elf: *Elf, key: u32, new_val: u64) void {
......@@ -6262,9 +6525,9 @@ fn updateExportsInner(
62626525 .uav => |uav| .{ (try elf.uavMapIndex(uav, .none)).symbol(elf), .OBJECT },
62636526 };
62646527 while (try elf.idle(pt.tid)) {}
6265 const value: u64, const size: u64, const shndx: Section.Index = switch (elf.symPtr(exported_lsi.index())) {
6528 const value: u64 = Symbol.Id.local(exported_lsi).value(elf);
6529 const size: u64, const shndx: Section.Index = switch (elf.symPtr(exported_lsi.index())) {
62666530 inline else => |exported_sym| .{
6267 elf.targetLoad(&exported_sym.value),
62686531 elf.targetLoad(&exported_sym.size),
62696532 .fromSection(elf.targetLoad(&exported_sym.shndx)),
62706533 },
......@@ -6368,6 +6631,7 @@ pub fn printNode(
63686631 elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf).slice(elf),
63696632 });
63706633 },
6634 .copied_global => |name| try w.print("(copy:{s})", .{name}),
63716635 .nav => |nmi| {
63726636 const zcu = elf.base.comp.zcu.?;
63736637 const ip = &zcu.intern_pool;