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 {...@@ -49,6 +49,20 @@ globals: struct {
49 strong_undef: std.array_hash_map.Auto(String(.strtab), Symbol.Global),49 strong_undef: std.array_hash_map.Auto(String(.strtab), Symbol.Global),
50 weak_undef: std.array_hash_map.Auto(String(.strtab), Symbol.Global),50 weak_undef: std.array_hash_map.Auto(String(.strtab), Symbol.Global),
51},51},
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),
52/// Key is a node which is a valid `Symbol.node` value, value is the name of the first global symbol66/// Key is a node which is a valid `Symbol.node` value, value is the name of the first global symbol
53/// in that node. That symbol is the head of a linked list: see `Symbol.Global.next_in_node`.67/// in that node. That symbol is the head of a linked list: see `Symbol.Global.next_in_node`.
54///68///
...@@ -57,14 +71,29 @@ globals: struct {...@@ -57,14 +71,29 @@ globals: struct {
57/// We use a separate hash map for this data rather than storing it in `navs` etc to save memory,71/// We use a separate hash map for this data rather than storing it in `navs` etc to save memory,
58/// because the vast majority of nodes which can export global symbols actually will not.72/// because the vast majority of nodes which can export global symbols actually will not.
59node_global_symbols: std.array_hash_map.Auto(MappedFile.Node.Index, String(.strtab)),73node_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.
61///78///
62/// * If we discover an undefined reference to one of these symbols, we will know the associated79/// * If we discover a direct relocation (i.e. no GOT or PLT indirection) targeting one of these
63/// symbol type, which is important because it may cause us to create a PLT entry.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.
64///82///
65/// * When emitting a dynamic executable, we can detect which undefined references are resolved by a83/// * When emitting a dynamic executable, we can detect which undefined references are resolved by a
66/// linked DSO, so can emit "undefined global symbol" errors for any other undefined references.84/// 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}),
68shstrtab: StringTable,97shstrtab: StringTable,
69strtab: StringTable,98strtab: StringTable,
70dynstr: StringTable,99dynstr: StringTable,
...@@ -136,22 +165,24 @@ synth_prog_node: std.Progress.Node,...@@ -136,22 +165,24 @@ synth_prog_node: std.Progress.Node,
136input_prog_node: std.Progress.Node,165input_prog_node: std.Progress.Node,
137166
138const Node = union(enum) {167const Node = union(enum) {
139 /// Cannot contain relocations.
140 file,168 file,
141 /// Cannot contain relocations.
142 ehdr,169 ehdr,
143 /// Cannot contain relocations.
144 shdr,170 shdr,
145 /// Cannot contain relocations.
146 segment: u32,171 segment: u32,
147 /// The section '.plt' may contain relocations via `elf.plt_first_symbol_reloc`.172 /// The section '.plt' may contain relocations via `elf.plt_first_symbol_reloc`.
148 ///173 ///
149 /// The section '.dynamic' may contain relocations via `elf.dynamic_first_symbol_reloc`.174 /// The section '.dynamic' may contain relocations via `elf.dynamic_first_symbol_reloc`.
150 ///
151 /// Otherwise, cannot contain relocations.
152 section: Section.Index,175 section: Section.Index,
153 /// May contain relocations.176 /// May contain relocations.
154 input_section: InputSection.Index,177 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),
155 /// May contain relocations.186 /// May contain relocations.
156 nav: NavMapIndex,187 nav: NavMapIndex,
157 /// May contain relocations.188 /// May contain relocations.
...@@ -746,6 +777,7 @@ const GotReloc = struct {...@@ -746,6 +777,7 @@ const GotReloc = struct {
746 .ehdr => unreachable,777 .ehdr => unreachable,
747 .shdr => unreachable,778 .shdr => unreachable,
748 .segment => unreachable,779 .segment => unreachable,
780 .copied_global => unreachable,
749 .section => |shndx| shndx.vaddr(elf),781 .section => |shndx| shndx.vaddr(elf),
750 .input_section => |isi| isi.ptrConst(elf).vaddr,782 .input_section => |isi| isi.ptrConst(elf).vaddr,
751 inline .nav,783 inline .nav,
...@@ -808,6 +840,15 @@ pub const MachineRelocType = union {...@@ -808,6 +840,15 @@ pub const MachineRelocType = union {
808 .X86_64 => .{ .X86_64 = .NONE },840 .X86_64 => .{ .X86_64 = .NONE },
809 };841 };
810 }842 }
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 }
811 pub fn jumpSlot(elf: *Elf) MachineRelocType {852 pub fn jumpSlot(elf: *Elf) MachineRelocType {
812 return switch (elf.ehdrField(.machine)) {853 return switch (elf.ehdrField(.machine)) {
813 else => unreachable,854 else => unreachable,
...@@ -973,6 +1014,7 @@ const SymbolReloc = struct {...@@ -973,6 +1014,7 @@ const SymbolReloc = struct {
973 .ehdr => unreachable,1014 .ehdr => unreachable,
974 .shdr => unreachable,1015 .shdr => unreachable,
975 .segment => unreachable,1016 .segment => unreachable,
1017 .copied_global => unreachable,
976 .section => |shndx| shndx.vaddr(elf),1018 .section => |shndx| shndx.vaddr(elf),
977 .input_section => |isi| isi.ptrConst(elf).vaddr,1019 .input_section => |isi| isi.ptrConst(elf).vaddr,
978 inline .nav,1020 inline .nav,
...@@ -984,11 +1026,9 @@ const SymbolReloc = struct {...@@ -984,11 +1026,9 @@ const SymbolReloc = struct {
984 const dest_vaddr = node_vaddr + reloc.offset;1026 const dest_vaddr = node_vaddr + reloc.offset;
985 const dest_slice = reloc.node.slice(&elf.mf)[@intCast(reloc.offset)..];1027 const dest_slice = reloc.node.slice(&elf.mf)[@intCast(reloc.offset)..];
986 const target_endian = elf.targetEndian();1028 const target_endian = elf.targetEndian();
987 const sym_value: u64, const sym_size: u64 = switch (elf.symPtr(reloc.target.index(elf))) {1029 const sym_value: u64 = reloc.target.value(elf);
988 inline else => |target_sym| .{1030 const sym_size: u64 = switch (elf.symPtr(reloc.target.index(elf))) {
989 elf.targetLoad(&target_sym.value),1031 inline else => |target_sym| elf.targetLoad(&target_sym.size),
990 elf.targetLoad(&target_sym.size),
991 },
992 };1032 };
993 const target_value = sym_value +% @as(u64, @bitCast(reloc.addend));1033 const target_value = sym_value +% @as(u64, @bitCast(reloc.addend));
994 type: switch (reloc.type) {1034 type: switch (reloc.type) {
...@@ -1114,6 +1154,12 @@ const SymbolReloc = struct {...@@ -1114,6 +1154,12 @@ const SymbolReloc = struct {
11141154
1115 fn delete(reloc: *SymbolReloc, elf: *Elf, index: SymbolReloc.Index) void {1155 fn delete(reloc: *SymbolReloc, elf: *Elf, index: SymbolReloc.Index) void {
1116 assert(index.get(elf) == reloc);1156 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
1117 switch (reloc.prev) {1163 switch (reloc.prev) {
1118 .none => {1164 .none => {
1119 const target_ptr = reloc.target.index(elf).ptr(elf);1165 const target_ptr = reloc.target.index(elf).ptr(elf);
...@@ -1126,18 +1172,25 @@ const SymbolReloc = struct {...@@ -1126,18 +1172,25 @@ const SymbolReloc = struct {
1126 .none => {},1172 .none => {},
1127 else => |next| next.get(elf).prev = reloc.prev,1173 else => |next| next.get(elf).prev = reloc.prev,
1128 }1174 }
1129 if (reloc.rela_index.unwrap()) |rela_index| {1175
1130 reloc.relaSection(elf).relaDeleteOne(elf, rela_index);1176 reloc.* = undefined;
1131 switch (elf.nodeWantsDsoRelocation(reloc.node)) {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)) {
1132 .no => unreachable, // there *was* a dynamic relocation!1188 .no => unreachable, // there *was* a dynamic relocation!
1133 .yes => {},1189 .yes => {},
1134 .yes_textrel => elf.textrel_count -= 1,1190 .yes_textrel => elf.textrel_count -= 1,
1135 }1191 },
1136 }
1137 if (reloc.type.dependsOnTlsSize()) {
1138 assert(elf.tls_size_symbol_relocs.swapRemove(index));
1139 }1192 }
1140 reloc.* = undefined;1193 reloc.rela_index = .none;
1141 }1194 }
1142};1195};
11431196
...@@ -1468,7 +1521,9 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{...@@ -1468,7 +1521,9 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
1468 };1521 };
14691522
1470 const @"type": std.elf.STT = switch (opts.type) {1523 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,
1472 else => |t| t,1527 else => |t| t,
1473 };1528 };
14741529
...@@ -1605,6 +1660,23 @@ fn setGlobalSymbolValue(...@@ -1605,6 +1660,23 @@ fn setGlobalSymbolValue(
1605 assert(global_ptr.prev_in_node == .empty);1660 assert(global_ptr.prev_in_node == .empty);
1606 }1661 }
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
1608 global_ptr.symtab_index.ptr(elf).node = new.node;1680 global_ptr.symtab_index.ptr(elf).node = new.node;
16091681
1610 const old_head: String(.strtab) = old_head: {1682 const old_head: String(.strtab) = old_head: {
...@@ -1668,21 +1740,7 @@ fn setGlobalSymbolValue(...@@ -1668,21 +1740,7 @@ fn setGlobalSymbolValue(
1668 // If this symbol was previously undefined, relocations targeting it may have been lowered to1740 // If this symbol was previously undefined, relocations targeting it may have been lowered to
1669 // runtime relocations which we have now discovered we do not need, so delete those.1741 // runtime relocations which we have now discovered we do not need, so delete those.
1670 if (elf.shndx.dynamic != .UNDEF) {1742 if (elf.shndx.dynamic != .UNDEF) {
1671 var ri = global_ptr.symtab_index.ptr(elf).first_target_reloc;1743 Symbol.Id.global(global_name).deleteDynamicTargetRelocs(elf);
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 }
1686 }1744 }
16871745
1688 // Finally, update the symbol value, re-applying target relocations. Also note that because we1746 // Finally, update the symbol value, re-applying target relocations. Also note that because we
...@@ -2029,6 +2087,9 @@ const Symbol = struct {...@@ -2029,6 +2087,9 @@ const Symbol = struct {
2029 };2087 };
2030 }2088 }
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.
2032 fn value(s: Symbol.Id, elf: *Elf) u64 {2093 fn value(s: Symbol.Id, elf: *Elf) u64 {
2033 return switch (elf.symPtr(s.index(elf))) {2094 return switch (elf.symPtr(s.index(elf))) {
2034 inline else => |sym| elf.targetLoad(&sym.value),2095 inline else => |sym| elf.targetLoad(&sym.value),
...@@ -2084,12 +2145,36 @@ const Symbol = struct {...@@ -2084,12 +2145,36 @@ const Symbol = struct {
2084 }2145 }
2085 }2146 }
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
2087 /// Returns `true` if the target of `s` has moved, meaning the symbol's value will change at2164 /// Returns `true` if the target of `s` has moved, meaning the symbol's value will change at
2088 /// some point due to a call to `flushMoved`.2165 /// some point due to a call to `flushMoved`.
2089 fn hasMoved(s: Symbol.Id, elf: *Elf) bool {2166 fn hasMoved(s: Symbol.Id, elf: *Elf) bool {
2090 const node = s.index(elf).ptr(elf).node;2167 const node = s.index(elf).ptr(elf).node;
2091 if (node == .none) return false;2168 if (node != .none) {
2092 return node.hasMoved(&elf.mf);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;
2093 }2178 }
2094 };2179 };
2095};2180};
...@@ -2110,6 +2195,7 @@ pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId {...@@ -2110,6 +2195,7 @@ pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId {
2110 .segment,2195 .segment,
2111 .section,2196 .section,
2112 .input_section,2197 .input_section,
2198 .copied_global,
2113 => unreachable,2199 => unreachable,
21142200
2115 inline .nav,2201 inline .nav,
...@@ -2204,7 +2290,7 @@ pub fn addReloc(...@@ -2204,7 +2290,7 @@ pub fn addReloc(
2204) !void {2290) !void {
2205 const node: MappedFile.Node.Index = Node.fromAtom(atom);2291 const node: MappedFile.Node.Index = Node.fromAtom(atom);
2206 try elf.ensureUnusedRelocCapacity(node, 1);2292 try elf.ensureUnusedRelocCapacity(node, 1);
2207 elf.addRelocAssumeCapacity(node, offset, .fromTypeErased(target), addend, @"type");2293 try elf.addRelocAssumeCapacity(node, offset, .fromTypeErased(target), addend, @"type");
2208}2294}
2209pub fn navSymbol(elf: *Elf, nav_index: InternPool.Nav.Index) !link.File.SymbolId {2295pub fn navSymbol(elf: *Elf, nav_index: InternPool.Nav.Index) !link.File.SymbolId {
2210 const zcu = elf.base.comp.zcu.?;2296 const zcu = elf.base.comp.zcu.?;
...@@ -2252,7 +2338,7 @@ pub fn getVAddr(elf: *Elf, reloc_info: link.File.RelocInfo, target: link.File.Sy...@@ -2252,7 +2338,7 @@ pub fn getVAddr(elf: *Elf, reloc_info: link.File.RelocInfo, target: link.File.Sy
2252 const node: MappedFile.Node.Index = Node.fromAtom(reloc_info.parent.atom_index);2338 const node: MappedFile.Node.Index = Node.fromAtom(reloc_info.parent.atom_index);
2253 const target_sym: Symbol.Id = .fromTypeErased(target);2339 const target_sym: Symbol.Id = .fromTypeErased(target);
2254 try elf.ensureUnusedRelocCapacity(node, 1);2340 try elf.ensureUnusedRelocCapacity(node, 1);
2255 elf.addRelocAssumeCapacity(2341 try elf.addRelocAssumeCapacity(
2256 node,2342 node,
2257 reloc_info.offset,2343 reloc_info.offset,
2258 target_sym,2344 target_sym,
...@@ -2511,6 +2597,8 @@ fn create(...@@ -2511,6 +2597,8 @@ fn create(
2511 .strong_undef = .empty,2597 .strong_undef = .empty,
2512 .weak_undef = .empty,2598 .weak_undef = .empty,
2513 },2599 },
2600 .copied_globals = .empty,
2601 .want_copied_globals = .empty,
2514 .node_global_symbols = .empty,2602 .node_global_symbols = .empty,
2515 .dso_globals = .empty,2603 .dso_globals = .empty,
2516 .shstrtab = .{ .map = .empty },2604 .shstrtab = .{ .map = .empty },
...@@ -2558,6 +2646,8 @@ pub fn deinit(elf: *Elf) void {...@@ -2558,6 +2646,8 @@ pub fn deinit(elf: *Elf) void {
2558 elf.globals.weak_def.deinit(gpa);2646 elf.globals.weak_def.deinit(gpa);
2559 elf.globals.strong_undef.deinit(gpa);2647 elf.globals.strong_undef.deinit(gpa);
2560 elf.globals.weak_undef.deinit(gpa);2648 elf.globals.weak_undef.deinit(gpa);
2649 elf.copied_globals.deinit(gpa);
2650 elf.want_copied_globals.deinit(gpa);
2561 elf.node_global_symbols.deinit(gpa);2651 elf.node_global_symbols.deinit(gpa);
2562 elf.dso_globals.deinit(gpa);2652 elf.dso_globals.deinit(gpa);
2563 elf.shstrtab.map.deinit(gpa);2653 elf.shstrtab.map.deinit(gpa);
...@@ -3117,14 +3207,14 @@ fn initHeaders(...@@ -3117,14 +3207,14 @@ fn initHeaders(
3117 });3207 });
3118 elf.plt_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len);3208 elf.plt_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len);
3119 try elf.ensureUnusedRelocCapacity(plt_ni, 2);3209 try elf.ensureUnusedRelocCapacity(plt_ni, 2);
3120 elf.addRelocAssumeCapacity(3210 try elf.addRelocAssumeCapacity(
3121 plt_ni,3211 plt_ni,
3122 2,3212 2,
3123 got_plt_sym,3213 got_plt_sym,
3124 8 * 1 - 4,3214 8 * 1 - 4,
3125 .{ .X86_64 = .PC32 },3215 .{ .X86_64 = .PC32 },
3126 );3216 );
3127 elf.addRelocAssumeCapacity(3217 try elf.addRelocAssumeCapacity(
3128 plt_ni,3218 plt_ni,
3129 8,3219 8,
3130 got_plt_sym,3220 got_plt_sym,
...@@ -3267,7 +3357,7 @@ pub fn endProgress(elf: *Elf) void {...@@ -3267,7 +3357,7 @@ pub fn endProgress(elf: *Elf) void {
3267fn getNode(elf: *const Elf, ni: MappedFile.Node.Index) Node {3357fn getNode(elf: *const Elf, ni: MappedFile.Node.Index) Node {
3268 return elf.nodes.get(@intFromEnum(ni));3358 return elf.nodes.get(@intFromEnum(ni));
3269}3359}
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.
3271fn getNodeShndx(elf: *const Elf, ni: MappedFile.Node.Index) Section.Index {3361fn getNodeShndx(elf: *const Elf, ni: MappedFile.Node.Index) Section.Index {
3272 return switch (elf.getNode(ni)) {3362 return switch (elf.getNode(ni)) {
3273 .file => unreachable,3363 .file => unreachable,
...@@ -3278,6 +3368,7 @@ fn getNodeShndx(elf: *const Elf, ni: MappedFile.Node.Index) Section.Index {...@@ -3278,6 +3368,7 @@ fn getNodeShndx(elf: *const Elf, ni: MappedFile.Node.Index) Section.Index {
3278 .section => |shndx| shndx,3368 .section => |shndx| shndx,
32793369
3280 .input_section,3370 .input_section,
3371 .copied_global,
3281 .nav,3372 .nav,
3282 .uav,3373 .uav,
3283 .lazy_code,3374 .lazy_code,
...@@ -3294,6 +3385,7 @@ fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {...@@ -3294,6 +3385,7 @@ fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
3294 },3385 },
3295 .section => |shndx| if (shndx == elf.shndx.tdata) 0 else shndx.vaddr(elf),3386 .section => |shndx| if (shndx == elf.shndx.tdata) 0 else shndx.vaddr(elf),
3296 .input_section => unreachable,3387 .input_section => unreachable,
3388 .copied_global => unreachable,
3297 inline .nav, .uav, .lazy_code, .lazy_const_data => |i| Symbol.Id.local(i.symbol(elf)).value(elf),3389 inline .nav, .uav, .lazy_code, .lazy_const_data => |i| Symbol.Id.local(i.symbol(elf)).value(elf),
3298 };3390 };
3299 const offset, _ = ni.location(&elf.mf).resolve(&elf.mf);3391 const offset, _ = ni.location(&elf.mf).resolve(&elf.mf);
...@@ -3312,6 +3404,7 @@ fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void {...@@ -3312,6 +3404,7 @@ fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void {
3312 .shdr => unreachable, // cannot contain relocs3404 .shdr => unreachable, // cannot contain relocs
3313 .segment => unreachable, // cannot contain relocs3405 .segment => unreachable, // cannot contain relocs
3314 .section => unreachable, // cannot contain relocs (.plt and .dynamic unsupported)3406 .section => unreachable, // cannot contain relocs (.plt and .dynamic unsupported)
3407 .copied_global => unreachable, // cannot contain relocs
3315 .input_section => |isi| .{3408 .input_section => |isi| .{
3316 &elf.input_sections.items[@intFromEnum(isi)].first_symbol_reloc,3409 &elf.input_sections.items[@intFromEnum(isi)].first_symbol_reloc,
3317 &elf.input_sections.items[@intFromEnum(isi)].first_got_reloc,3410 &elf.input_sections.items[@intFromEnum(isi)].first_got_reloc,
...@@ -4270,7 +4363,7 @@ fn loadObject(...@@ -4270,7 +4363,7 @@ fn loadObject(
4270 .{rel.info.sym},4363 .{rel.info.sym},
4271 );4364 );
4272 }4365 }
4273 elf.addRelocAssumeCapacity(4366 try elf.addRelocAssumeCapacity(
4274 loc_node,4367 loc_node,
4275 rel.offset - loc_sec.shdr.addr,4368 rel.offset - loc_sec.shdr.addr,
4276 target,4369 target,
...@@ -4303,12 +4396,19 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {...@@ -4303,12 +4396,19 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {
4303 if (ehdr.machine != elf.ehdrField(.machine))4396 if (ehdr.machine != elf.ehdrField(.machine))
4304 return diags.failParse(path, "bad machine", .{});4397 return diags.failParse(path, "bad machine", .{});
4305 if (ehdr.shnum > 0) try fr.seekTo(ehdr.shoff);4398 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);
4306 const dynamic_sh: ElfN.Shdr, const dynsym_sh: ElfN.Shdr = sh: {4402 const dynamic_sh: ElfN.Shdr, const dynsym_sh: ElfN.Shdr = sh: {
4307 var dynamic_sh: ?ElfN.Shdr = null;4403 var dynamic_sh: ?ElfN.Shdr = null;
4308 var dynsym_sh: ?ElfN.Shdr = null;4404 var dynsym_sh: ?ElfN.Shdr = null;
4309 for (0..ehdr.shnum) |_| {4405 for (section_aligns) |*section_align| {
4310 const sh = try r.peekStruct(ElfN.Shdr, target_endian);4406 const sh = try r.peekStruct(ElfN.Shdr, target_endian);
4311 try r.discardAll(ehdr.shentsize);4407 try r.discardAll(ehdr.shentsize);
4408 section_align.* = .fromByteUnits(std.math.ceilPowerOfTwoAssert(
4409 usize,
4410 @intCast(@max(sh.addralign, 1)),
4411 ));
4312 switch (sh.type) {4412 switch (sh.type) {
4313 else => {},4413 else => {},
4314 .DYNAMIC => dynamic_sh = sh,4414 .DYNAMIC => dynamic_sh = sh,
...@@ -4395,17 +4495,51 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {...@@ -4395,17 +4495,51 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {
4395 }4495 }
43964496
4397 if (sym.shndx == std.elf.SHN_UNDEF) continue;4497 if (sym.shndx == std.elf.SHN_UNDEF) continue;
4498 if (sym.shndx >= ehdr.shnum) continue;
43984499
4399 if (sym.name >= dynstr.len) {4500 if (sym.name >= dynstr.len) {
4400 return diags.failParse(path, "bad symbol name string", .{});4501 return diags.failParse(path, "bad symbol name string", .{});
4401 }4502 }
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
4403 const name = try elf.string(.strtab, std.mem.sliceTo(dynstr[sym.name..], 0));4511 const name = try elf.string(.strtab, std.mem.sliceTo(dynstr[sym.name..], 0));
4404 const gop = elf.dso_globals.getOrPutAssumeCapacity(name);4512 const gop = elf.dso_globals.getOrPutAssumeCapacity(name);
4405 if (!gop.found_existing or gop.value_ptr.* == .NOTYPE) {4513
4406 gop.value_ptr.* = sym.info.type;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;
4407 }4535 }
44084536
4537 gop.value_ptr.* = .{
4538 .type = sym.info.type,
4539 .size = sym.size,
4540 .alignment = sym_align,
4541 };
4542
4409 // If there's already an undefined symbol by this name of type STT_NOTYPE, populate4543 // If there's already an undefined symbol by this name of type STT_NOTYPE, populate
4410 // its type now.4544 // its type now.
4411 const global_ptr = elf.globals.strong_undef.getPtr(name) orelse4545 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 {...@@ -4414,14 +4548,20 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {
44144548
4415 if (global_ptr.dynsym_index == 0) continue;4549 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
4417 const sym_ptr = @field(elf.symPtr(global_ptr.symtab_index), @tagName(class));4557 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
4418 switch (elf.targetLoad(&sym_ptr.other).visibility) {4560 switch (elf.targetLoad(&sym_ptr.other).visibility) {
4419 .HIDDEN, .INTERNAL, .PROTECTED => continue,4561 .HIDDEN, .INTERNAL, .PROTECTED => continue,
4420 .DEFAULT => {},4562 .DEFAULT => {},
4421 }4563 }
44224564
4423 if (elf.targetLoad(&sym_ptr.shndx) != std.elf.SHN_UNDEF) continue;
4424
4425 const cur_info = elf.targetLoad(&sym_ptr.info);4565 const cur_info = elf.targetLoad(&sym_ptr.info);
4426 if (cur_info.type == .NOTYPE) {4566 if (cur_info.type == .NOTYPE) {
4427 const new_type: std.elf.STT = switch (sym.info.type) {4567 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 {...@@ -4440,9 +4580,8 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {
4440 .type = new_type,4580 .type = new_type,
4441 });4581 });
44424582
4443 // If we just turned this into an STT_FUNC symbol, then we have determined
4444 // that it needs a PLT entry.
4445 if (new_type == .FUNC) {4583 if (new_type == .FUNC) {
4584 // We turned STT_NOTYPE into STT_FUNC, so we now need a PLT entry...
4446 elf.addPltEntry(name, global_ptr.dynsym_index);4585 elf.addPltEntry(name, global_ptr.dynsym_index);
4447 // ...and therefore, we need to re-apply that symbol's relocations, as4586 // ...and therefore, we need to re-apply that symbol's relocations, as
4448 // some might be targeting its PLT entry.4587 // some might be targeting its PLT entry.
...@@ -4630,6 +4769,9 @@ fn prelinkInner(elf: *Elf) !void {...@@ -4630,6 +4769,9 @@ fn prelinkInner(elf: *Elf) !void {
4630 }4769 }
4631 break :rpath try elf.string(.dynstr, buf.items);4770 break :rpath try elf.string(.dynstr, buf.items);
4632 };4771 };
4772 const soname: ?String(.dynstr) = if (elf.options.soname) |soname_slice| str: {
4773 break :str try elf.string(.dynstr, soname_slice);
4774 } else null;
4633 const needed_len = elf.needed.count();4775 const needed_len = elf.needed.count();
4634 const dynamic_len = needed_len + @intFromBool(elf.options.soname != null) +4776 const dynamic_len = needed_len + @intFromBool(elf.options.soname != null) +
4635 @intFromBool(rpath != .empty) +4777 @intFromBool(rpath != .empty) +
...@@ -4644,141 +4786,155 @@ fn prelinkInner(elf: *Elf) !void {...@@ -4644,141 +4786,155 @@ fn prelinkInner(elf: *Elf) !void {
4644 switch (elf.shdrPtr(elf.shndx.dynamic)) {4786 switch (elf.shdrPtr(elf.shndx.dynamic)) {
4645 inline else => |shdr| elf.targetStore(&shdr.size, dynamic_size),4787 inline else => |shdr| elf.targetStore(&shdr.size, dynamic_size),
4646 }4788 }
4647 const sec_dynamic = dynamic_ni.slice(&elf.mf);4789
4648 const dynamic_entries: [][2]ElfN.Addr = @ptrCast(@alignCast(sec_dynamic));4790 const dynamic_indices: struct {
4649 var dynamic_index: usize = 0;4791 init_array: ?usize,
4650 for (4792 fini_array: ?usize,
4651 dynamic_entries[dynamic_index..][0..needed_len],4793 preinit_array: ?usize,
4652 elf.needed.keys(),4794 } = indices: {
4653 ) |*dynamic_entry, needed| dynamic_entry.* = .{ std.elf.DT_NEEDED, @intFromEnum(needed) };4795 const sec_dynamic = dynamic_ni.slice(&elf.mf);
4654 dynamic_index += needed_len;4796 const dynamic_entries: [][2]ElfN.Addr = @ptrCast(@alignCast(sec_dynamic));
4655 if (elf.options.soname) |soname| {4797 errdefer comptime unreachable; // don't invalidate `dynamic_entries`
4656 dynamic_entries[dynamic_index] = .{ std.elf.DT_SONAME, @intFromEnum(try elf.string(.dynstr, soname)) };4798 var dynamic_index: usize = 0;
4657 dynamic_index += 1;4799 for (
4658 }4800 dynamic_entries[dynamic_index..][0..needed_len],
4659 if (rpath != .empty) {4801 elf.needed.keys(),
4660 dynamic_entries[dynamic_index] = .{ std.elf.DT_RUNPATH, @intFromEnum(rpath) };4802 ) |*dynamic_entry, needed| dynamic_entry.* = .{ std.elf.DT_NEEDED, @intFromEnum(needed) };
4661 dynamic_index += 1;4803 dynamic_index += needed_len;
4662 }4804 if (soname) |soname_dynstr| {
4663 if (flags != 0) {4805 dynamic_entries[dynamic_index] = .{ std.elf.DT_SONAME, @intFromEnum(soname_dynstr) };
4664 dynamic_entries[dynamic_index] = .{ std.elf.DT_FLAGS, flags };4806 dynamic_index += 1;
4665 dynamic_index += 1;4807 }
4666 }4808 if (rpath != .empty) {
4667 if (flags_1 != 0) {4809 dynamic_entries[dynamic_index] = .{ std.elf.DT_RUNPATH, @intFromEnum(rpath) };
4668 dynamic_entries[dynamic_index] = .{ std.elf.DT_FLAGS_1, flags_1 };4810 dynamic_index += 1;
4669 dynamic_index += 1;4811 }
4670 }4812 if (flags != 0) {
4671 if (comp.config.output_mode == .Exe) {4813 dynamic_entries[dynamic_index] = .{ std.elf.DT_FLAGS, flags };
4672 dynamic_entries[dynamic_index] = .{ std.elf.DT_DEBUG, 0 };4814 dynamic_index += 1;
4673 dynamic_index += 1;4815 }
4674 }4816 if (flags_1 != 0) {
4675 if (elf.shndx.init_array != .UNDEF) {4817 dynamic_entries[dynamic_index] = .{ std.elf.DT_FLAGS_1, flags_1 };
4676 dynamic_entries[dynamic_index..][0..2].* = .{4818 dynamic_index += 1;
4677 .{ std.elf.DT_INIT_ARRAY, @intCast(elf.shndx.init_array.vaddr(elf)) },4819 }
4678 .{ std.elf.DT_INIT_ARRAYSZ, elf.targetLoad(4820 if (comp.config.output_mode == .Exe) {
4679 &@field(elf.shdrPtr(elf.shndx.init_array), @tagName(ct_class)).size,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,
4680 ) },4858 ) },
4681 };4859 .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) },
4682 try elf.ensureUnusedRelocCapacity(dynamic_ni, 1);4860 .{ std.elf.DT_JMPREL, @intCast(elf.shndx.rela_plt.vaddr(elf)) },
4683 elf.addRelocAssumeCapacity(4861 .{ std.elf.DT_PLTRELSZ, elf.targetLoad(
4684 dynamic_ni,4862 &@field(elf.shdrPtr(elf.shndx.rela_plt), @tagName(ct_class)).size,
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,
4697 ) },4863 ) },
4698 };4864 .{ std.elf.DT_PLTGOT, @intCast(elf.shndx.got_plt.vaddr(elf)) },
4699 try elf.ensureUnusedRelocCapacity(dynamic_ni, 1);4865 .{ std.elf.DT_PLTREL, std.elf.DT_RELA },
4700 elf.addRelocAssumeCapacity(4866 .{ std.elf.DT_SYMTAB, @intCast(elf.shndx.dynsym.vaddr(elf)) },
4701 dynamic_ni,4867 .{ std.elf.DT_SYMENT, @sizeOf(ElfN.Sym) },
4702 @sizeOf(ElfN.Addr) * (2 * dynamic_index + 1),4868 .{ std.elf.DT_STRTAB, @intCast(elf.shndx.dynstr.vaddr(elf)) },
4703 .local(elf.shndx.fini_array.get(elf).lsi),4869 .{ std.elf.DT_STRSZ, elf.targetLoad(
4704 0,4870 &@field(elf.shdrPtr(elf.shndx.dynstr), @tagName(ct_class)).size,
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,
4714 ) },4871 ) },
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,
4715 };4883 };
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 },
4745 };4884 };
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
4751 elf.dynamic_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len);4886 elf.dynamic_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len);
4752 try elf.ensureUnusedRelocCapacity(dynamic_ni, 5);4887 try elf.ensureUnusedRelocCapacity(dynamic_ni, 8);
4753 elf.addRelocAssumeCapacity(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(
4754 dynamic_ni,4910 dynamic_ni,
4755 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 12) + 1),4911 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 12) + 1),
4756 .local(elf.shndx.rela_dyn.get(elf).lsi),4912 .local(elf.shndx.rela_dyn.get(elf).lsi),
4757 0,4913 0,
4758 .absAddr(elf),4914 .absAddr(elf),
4759 );4915 );
4760 elf.addRelocAssumeCapacity(4916 try elf.addRelocAssumeCapacity(
4761 dynamic_ni,4917 dynamic_ni,
4762 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 9) + 1),4918 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 9) + 1),
4763 .local(elf.shndx.rela_plt.get(elf).lsi),4919 .local(elf.shndx.rela_plt.get(elf).lsi),
4764 0,4920 0,
4765 .absAddr(elf),4921 .absAddr(elf),
4766 );4922 );
4767 elf.addRelocAssumeCapacity(4923 try elf.addRelocAssumeCapacity(
4768 dynamic_ni,4924 dynamic_ni,
4769 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 7) + 1),4925 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 7) + 1),
4770 .local(elf.shndx.got_plt.get(elf).lsi),4926 .local(elf.shndx.got_plt.get(elf).lsi),
4771 0,4927 0,
4772 .absAddr(elf),4928 .absAddr(elf),
4773 );4929 );
4774 elf.addRelocAssumeCapacity(4930 try elf.addRelocAssumeCapacity(
4775 dynamic_ni,4931 dynamic_ni,
4776 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 5) + 1),4932 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 5) + 1),
4777 .local(elf.shndx.dynsym.get(elf).lsi),4933 .local(elf.shndx.dynsym.get(elf).lsi),
4778 0,4934 0,
4779 .absAddr(elf),4935 .absAddr(elf),
4780 );4936 );
4781 elf.addRelocAssumeCapacity(4937 try elf.addRelocAssumeCapacity(
4782 dynamic_ni,4938 dynamic_ni,
4783 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 3) + 1),4939 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 3) + 1),
4784 .local(elf.shndx.dynstr.get(elf).lsi),4940 .local(elf.shndx.dynstr.get(elf).lsi),
...@@ -4951,6 +5107,8 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize)...@@ -4951,6 +5107,8 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize)
4951 },5107 },
4952 }5108 }
4953}5109}
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.
4954fn addRelocAssumeCapacity(5112fn addRelocAssumeCapacity(
4955 elf: *Elf,5113 elf: *Elf,
4956 node: MappedFile.Node.Index,5114 node: MappedFile.Node.Index,
...@@ -4958,7 +5116,7 @@ fn addRelocAssumeCapacity(...@@ -4958,7 +5116,7 @@ fn addRelocAssumeCapacity(
4958 target: Symbol.Id,5116 target: Symbol.Id,
4959 addend: i64,5117 addend: i64,
4960 @"type": MachineRelocType,5118 @"type": MachineRelocType,
4961) void {5119) !void {
4962 assert(node != .none);5120 assert(node != .none);
4963 switch (elf.ehdrField(.type)) {5121 switch (elf.ehdrField(.type)) {
4964 .NONE, .CORE, _ => unreachable,5122 .NONE, .CORE, _ => unreachable,
...@@ -5021,25 +5179,25 @@ fn addRelocAssumeCapacity(...@@ -5021,25 +5179,25 @@ fn addRelocAssumeCapacity(
5021 .TLSDESC => @panic("TODO: R_X86_64_TLSDESC"),5179 .TLSDESC => @panic("TODO: R_X86_64_TLSDESC"),
50225180
5023 // Relocations targeting a symbol5181 // Relocations targeting a symbol
5024 .@"64" => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs64),5182 .@"64" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs64),
5025 .@"32" => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32),5183 .@"32" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32),
5026 .@"32S" => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32s),5184 .@"32S" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32s),
5027 .PC64 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel64),5185 .PC64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel64),
5028 .PC32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel32),5186 .PC32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel32),
5029 .PLT32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .pltrel32),5187 .PLT32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .pltrel32),
5030 .SIZE64 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .size64),5188 .SIZE64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .size64),
5031 .SIZE32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .size32),5189 .SIZE32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .size32),
5032 .DTPOFF64 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .dtpoff64),5190 .DTPOFF64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .dtpoff64),
5033 .DTPOFF32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .dtpoff32),5191 .DTPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .dtpoff32),
5034 .TPOFF64 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff64),5192 .TPOFF64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff64),
5035 .TPOFF32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff32),5193 .TPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff32),
5036 .GOTPC64 => {5194 .GOTPC64 => {
5037 const got_sym: Symbol.Id = .local(elf.shndx.got.get(elf).lsi);5195 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);
5039 },5197 },
5040 .GOTPC32 => {5198 .GOTPC32 => {
5041 const got_sym: Symbol.Id = .local(elf.shndx.got.get(elf).lsi);5199 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);
5043 },5201 },
50445202
5045 // TODO: these are the address of an arbitrary symbol (or PLT entry) relative to the5203 // TODO: these are the address of an arbitrary symbol (or PLT entry) relative to the
...@@ -5077,11 +5235,16 @@ fn addSymbolRelocAssumeCapacity(...@@ -5077,11 +5235,16 @@ fn addSymbolRelocAssumeCapacity(
5077 target: Symbol.Id,5235 target: Symbol.Id,
5078 addend: i64,5236 addend: i64,
5079 @"type": SymbolReloc.Type,5237 @"type": SymbolReloc.Type,
5080) void {5238) !void {
5081 assert(elf.ehdrField(.type) != .REL);5239 assert(elf.ehdrField(.type) != .REL);
50825240
5083 const rela_index: Section.RelaIndex.Optional = r: {5241 const rela_index: Section.RelaIndex.Optional = r: {
5084 if (elf.shndx.dynamic == .UNDEF) break :r .none;5242 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
5085 const rela_type: MachineRelocType = switch (elf.ehdrField(.machine)) {5248 const rela_type: MachineRelocType = switch (elf.ehdrField(.machine)) {
5086 else => |machine| @panic(@tagName(machine)),5249 else => |machine| @panic(@tagName(machine)),
5087 .X86_64 => .{ .X86_64 = switch (@"type") {5250 .X86_64 => .{ .X86_64 = switch (@"type") {
...@@ -5101,21 +5264,31 @@ fn addSymbolRelocAssumeCapacity(...@@ -5101,21 +5264,31 @@ fn addSymbolRelocAssumeCapacity(
5101 .size32 => .SIZE32,5264 .size32 => .SIZE32,
5102 } },5265 } },
5103 };5266 };
5104 const dynsym_index: u32 = switch (target.unwrap()) {5267 // TODO: even if the symbol is locally defined, preemption/interposition is a
5105 .local => break :r .none,5268 // possibility, which this condition does not currently consider!
5106 // TODO: even if the symbol is locally defined, preemption/interposition is a5269 if (elf.globals.strong_def.contains(global_name) or
5107 // possibility, which this condition does not currently consider!5270 elf.globals.weak_def.contains(global_name))
5108 .global => |name| if (elf.globals.strong_def.contains(name) or5271 {
5109 elf.globals.weak_def.contains(name))5272 break :r .none;
5110 {5273 }
5111 break :r .none;5274
5112 } else elf.globalByName(name).?.dynsym_index,5275 const dynsym_index = elf.globalByName(global_name).?.dynsym_index;
5113 };5276 if (dynsym_index == 0) break :r .none;
51145277
5115 switch (elf.nodeWantsDsoRelocation(node)) {5278 switch (elf.nodeWantsDsoRelocation(node)) {
5116 .no => break :r .none,5279 .no => break :r .none,
5117 .yes => {},5280 .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 },
5119 }5292 }
51205293
5121 // It currently looks like we need a runtime relocation for this.5294 // It currently looks like we need a runtime relocation for this.
...@@ -5175,6 +5348,7 @@ fn addGotRelocAssumeCapacity(...@@ -5175,6 +5348,7 @@ fn addGotRelocAssumeCapacity(
5175 .ehdr => unreachable, // cannot contain relocs5348 .ehdr => unreachable, // cannot contain relocs
5176 .shdr => unreachable, // cannot contain relocs5349 .shdr => unreachable, // cannot contain relocs
5177 .segment => unreachable, // cannot contain relocs5350 .segment => unreachable, // cannot contain relocs
5351 .copied_global => unreachable, // cannot contain relocs
5178 }5352 }
51795353
5180 const gop = elf.got.getOrPutAssumeCapacity(target);5354 const gop = elf.got.getOrPutAssumeCapacity(target);
...@@ -5275,29 +5449,37 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {...@@ -5275,29 +5449,37 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
5275 .global => |name| name,5449 .global => |name| name,
5276 };5450 };
5277 // If the symbol is *defined* in this module, we might be able to avoid the relocation.5451 // If the symbol is *defined* in this module, we might be able to avoid the relocation.
5278 if (elf.globals.strong_def.getPtr(name) orelse5452 const need_reloc: bool = need_reloc: {
5279 elf.globals.weak_def.getPtr(name)) |global|5453 const global = g: {
5280 {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
5281 // We have a definition, but it might be interposable (aka preemptible). There5462 // We have a definition, but it might be interposable (aka preemptible). There
5282 // are two cases where it is not and so we can (and, in fact, must) elide the5463 // are two cases where it is not and so we can (and, in fact, must) elide the
5283 // runtime relocation:5464 // runtime relocation:
5284 // * We are the executable. Symbols from executables cannot be interposed.5465 // * We are the executable. Symbols from executables cannot be interposed.
5285 // * The symbol's visibility disallows interposition.5466 // * The symbol's visibility disallows interposition.
5286 if (elf.base.comp.config.output_mode == .Exe) {5467 if (elf.base.comp.config.output_mode == .Exe) {
5287 // No relocation needed.5468 break :need_reloc false;
5288 break :val .{ .unsigned = sym_id.value(elf) };
5289 }5469 }
5290 const visibility: std.elf.STV = switch (elf.symPtr(global.symtab_index)) {5470 const visibility: std.elf.STV = switch (elf.symPtr(global.symtab_index)) {
5291 inline else => |sym| elf.targetLoad(&sym.other).visibility,5471 inline else => |sym| elf.targetLoad(&sym.other).visibility,
5292 };5472 };
5293 switch (visibility) {5473 break :need_reloc switch (visibility) {
5294 .DEFAULT => {},5474 .DEFAULT => true,
5295 .INTERNAL, .HIDDEN, .PROTECTED => {5475 .INTERNAL, .HIDDEN, .PROTECTED => false,
5296 // No relocation needed.5476 };
5297 break :val .{ .unsigned = sym_id.value(elf) };5477 };
5298 },5478
5299 }5479 if (!need_reloc) {
5480 break :val .{ .unsigned = sym_id.value(elf) };
5300 }5481 }
5482
5301 break :val .{ .reloc = .{5483 break :val .{ .reloc = .{
5302 .type = if (tag == .symbol) .globDat(elf) else .dtpOffAddr(elf),5484 .type = if (tag == .symbol) .globDat(elf) else .dtpOffAddr(elf),
5303 .dynsym_index = elf.globalByName(name).?.dynsym_index,5485 .dynsym_index = elf.globalByName(name).?.dynsym_index,
...@@ -5403,6 +5585,80 @@ fn nodeWantsDsoRelocation(elf: *Elf, node: MappedFile.Node.Index) enum { yes, ye...@@ -5403,6 +5585,80 @@ fn nodeWantsDsoRelocation(elf: *Elf, node: MappedFile.Node.Index) enum { yes, ye
5403 return .yes;5585 return .yes;
5404}5586}
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
5406pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {5662pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {
5407 elf.updateNavInner(pt, nav_index) catch |err| switch (err) {5663 elf.updateNavInner(pt, nav_index) catch |err| switch (err) {
5408 error.OutOfMemory,5664 error.OutOfMemory,
...@@ -5561,10 +5817,8 @@ pub fn flush(...@@ -5561,10 +5817,8 @@ pub fn flush(
5561 error.Canceled => |e| return e,5817 error.Canceled => |e| return e,
5562 else => |e| return comp.link_diags.fail("flush write failed: {t}", .{e}),5818 else => |e| return comp.link_diags.fail("flush write failed: {t}", .{e}),
5563 };5819 };
5564 const global = elf.globalByName(sym_name_strtab) orelse break :entry 0;5820 if (elf.globalByName(sym_name_strtab) == null) break :entry 0;
5565 switch (elf.symPtr(global.symtab_index)) {5821 break :entry Symbol.Id.global(sym_name_strtab).value(elf);
5566 inline else => |sym| break :entry elf.targetLoad(&sym.value),
5567 }
5568 };5822 };
5569 switch (elf.ehdrPtr()) {5823 switch (elf.ehdrPtr()) {
5570 inline else => |ehdr| elf.targetStore(&ehdr.entry, @intCast(entry_addr)),5824 inline else => |ehdr| elf.targetStore(&ehdr.entry, @intCast(entry_addr)),
...@@ -5923,15 +6177,12 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {...@@ -5923,15 +6177,12 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {
5923 assert(first_name != .empty);6177 assert(first_name != .empty);
5924 var name = first_name;6178 var name = first_name;
5925 while (name != .empty) {6179 while (name != .empty) {
5926 const global = elf.globalByName(name).?;6180 const old_sym_addr = Symbol.Id.global(name).value(elf);
5927 const old_sym_addr: u64 = switch (elf.symPtr(global.symtab_index)) {
5928 inline else => |sym| elf.targetLoad(&sym.value),
5929 };
5930 Symbol.Id.global(name).flushMoved(6181 Symbol.Id.global(name).flushMoved(
5931 elf,6182 elf,
5932 old_sym_addr - old_addr + addr,6183 old_sym_addr - old_addr + addr,
5933 );6184 );
5934 name = global.next_in_node;6185 name = elf.globalByName(name).?.next_in_node;
5935 }6186 }
5936 }6187 }
59376188
...@@ -5968,18 +6219,20 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {...@@ -5968,18 +6219,20 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {
5968 var lsi, const end_lsi = ii.localSymbolRange(elf);6219 var lsi, const end_lsi = ii.localSymbolRange(elf);
5969 while (lsi != end_lsi) : (lsi = @enumFromInt(@intFromEnum(lsi) + 1)) {6220 while (lsi != end_lsi) : (lsi = @enumFromInt(@intFromEnum(lsi) + 1)) {
5970 if (lsi.index().ptr(elf).node != ni) continue;6221 if (lsi.index().ptr(elf).node != ni) continue;
5971 const old_sym_addr: u64 = switch (elf.symPtr(lsi.index())) {6222 const visibility: std.elf.STV = switch (elf.symPtr(lsi.index())) {
5972 inline else => |sym| switch (elf.targetLoad(&sym.other).visibility) {6223 inline else => |sym| 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 },
5982 };6224 };
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);
5983 Symbol.Id.local(lsi).flushMoved(6236 Symbol.Id.local(lsi).flushMoved(
5984 elf,6237 elf,
5985 old_sym_addr - old_section_addr + new_section_addr,6238 old_sym_addr - old_section_addr + new_section_addr,
...@@ -5991,15 +6244,12 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {...@@ -5991,15 +6244,12 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {
5991 assert(first_name != .empty);6244 assert(first_name != .empty);
5992 var name = first_name;6245 var name = first_name;
5993 while (name != .empty) {6246 while (name != .empty) {
5994 const global = elf.globalByName(name).?;6247 const old_sym_addr = Symbol.Id.global(name).value(elf);
5995 const old_sym_addr: u64 = switch (elf.symPtr(global.symtab_index)) {
5996 inline else => |sym| elf.targetLoad(&sym.value),
5997 };
5998 Symbol.Id.global(name).flushMoved(6248 Symbol.Id.global(name).flushMoved(
5999 elf,6249 elf,
6000 old_sym_addr - old_section_addr + new_section_addr,6250 old_sym_addr - old_section_addr + new_section_addr,
6001 );6251 );
6002 name = global.next_in_node;6252 name = elf.globalByName(name).?.next_in_node;
6003 }6253 }
6004 }6254 }
60056255
...@@ -6010,6 +6260,19 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {...@@ -6010,6 +6260,19 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {
6010 isi.ptrConst(elf).first_got_reloc,6260 isi.ptrConst(elf).first_got_reloc,
6011 );6261 );
6012 },6262 },
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 },
6013 inline .nav, .uav, .lazy_code, .lazy_const_data => |mi| {6276 inline .nav, .uav, .lazy_code, .lazy_const_data => |mi| {
6014 const new_addr = elf.computeNodeVAddr(ni);6277 const new_addr = elf.computeNodeVAddr(ni);
6015 Symbol.Id.local(mi.symbol(elf)).flushMoved(elf, new_addr);6278 Symbol.Id.local(mi.symbol(elf)).flushMoved(elf, new_addr);
...@@ -6137,7 +6400,7 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void {...@@ -6137,7 +6400,7 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void {
6137 }6400 }
6138 },6401 },
6139 },6402 },
6140 .input_section, .nav, .uav, .lazy_code, .lazy_const_data => {},6403 .copied_global, .input_section, .nav, .uav, .lazy_code, .lazy_const_data => {},
6141 }6404 }
6142}6405}
6143fn updateDynamicEntry(elf: *Elf, key: u32, new_val: u64) void {6406fn updateDynamicEntry(elf: *Elf, key: u32, new_val: u64) void {
...@@ -6262,9 +6525,9 @@ fn updateExportsInner(...@@ -6262,9 +6525,9 @@ fn updateExportsInner(
6262 .uav => |uav| .{ (try elf.uavMapIndex(uav, .none)).symbol(elf), .OBJECT },6525 .uav => |uav| .{ (try elf.uavMapIndex(uav, .none)).symbol(elf), .OBJECT },
6263 };6526 };
6264 while (try elf.idle(pt.tid)) {}6527 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())) {
6266 inline else => |exported_sym| .{6530 inline else => |exported_sym| .{
6267 elf.targetLoad(&exported_sym.value),
6268 elf.targetLoad(&exported_sym.size),6531 elf.targetLoad(&exported_sym.size),
6269 .fromSection(elf.targetLoad(&exported_sym.shndx)),6532 .fromSection(elf.targetLoad(&exported_sym.shndx)),
6270 },6533 },
...@@ -6368,6 +6631,7 @@ pub fn printNode(...@@ -6368,6 +6631,7 @@ pub fn printNode(
6368 elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf).slice(elf),6631 elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf).slice(elf),
6369 });6632 });
6370 },6633 },
6634 .copied_global => |name| try w.print("(copy:{s})", .{name}),
6371 .nav => |nmi| {6635 .nav => |nmi| {
6372 const zcu = elf.base.comp.zcu.?;6636 const zcu = elf.base.comp.zcu.?;
6373 const ip = &zcu.intern_pool;6637 const ip = &zcu.intern_pool;