authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-05-26 13:13:55+02:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-05-26 13:13:55+02:00
log8086ae1769178222bade30100d11bb6106d7974b
treebce3f41e856866603991e72c8b2a3758cafe2831
parent11bb8ab9b3cf832a5e391ac8c73cbc33221db19d
parentad06fe07c531b76f99d655680b240916561e21e1

Merge pull request 'Elf2: more enhancements' (#35447) from elf2 into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/35447

4 files changed, 2655 insertions(+), 961 deletions(-)

cmake/Findclang.cmake+1-3
...@@ -56,11 +56,8 @@ else()...@@ -56,11 +56,8 @@ else()
56 FIND_AND_ADD_CLANG_LIB(clangAnalysisLifetimeSafety)56 FIND_AND_ADD_CLANG_LIB(clangAnalysisLifetimeSafety)
57 FIND_AND_ADD_CLANG_LIB(clangAnalysis)57 FIND_AND_ADD_CLANG_LIB(clangAnalysis)
58 FIND_AND_ADD_CLANG_LIB(clangASTMatchers)58 FIND_AND_ADD_CLANG_LIB(clangASTMatchers)
59 FIND_AND_ADD_CLANG_LIB(clangAST)
60 FIND_AND_ADD_CLANG_LIB(clangParse)59 FIND_AND_ADD_CLANG_LIB(clangParse)
61 FIND_AND_ADD_CLANG_LIB(clangSema)
62 FIND_AND_ADD_CLANG_LIB(clangAPINotes)60 FIND_AND_ADD_CLANG_LIB(clangAPINotes)
63 FIND_AND_ADD_CLANG_LIB(clangBasic)
64 FIND_AND_ADD_CLANG_LIB(clangEdit)61 FIND_AND_ADD_CLANG_LIB(clangEdit)
65 FIND_AND_ADD_CLANG_LIB(clangLex)62 FIND_AND_ADD_CLANG_LIB(clangLex)
66 FIND_AND_ADD_CLANG_LIB(clangRewriteFrontend)63 FIND_AND_ADD_CLANG_LIB(clangRewriteFrontend)
...@@ -73,6 +70,7 @@ else()...@@ -73,6 +70,7 @@ else()
73 FIND_AND_ADD_CLANG_LIB(clangSupport)70 FIND_AND_ADD_CLANG_LIB(clangSupport)
74 FIND_AND_ADD_CLANG_LIB(clangInstallAPI)71 FIND_AND_ADD_CLANG_LIB(clangInstallAPI)
75 FIND_AND_ADD_CLANG_LIB(clangAST)72 FIND_AND_ADD_CLANG_LIB(clangAST)
73 FIND_AND_ADD_CLANG_LIB(clangBasic)
76endif()74endif()
7775
78if (MSVC)76if (MSVC)
src/codegen/x86_64/Emit.zig+6-2
...@@ -850,8 +850,12 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI...@@ -850,8 +850,12 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI
850 emit.atom_id,850 emit.atom_id,
851 end_offset - 4,851 end_offset - 4,
852 target.symbol,852 target.symbol,
853 reloc.off,853 if (emit.pic) reloc.off - 4 else reloc.off,
854 .{ .X86_64 = .@"32S" },854 .{ .X86_64 = rt: {
855 if (!emit.pic) break :rt .@"32S";
856 if (target.is_extern and !target.force_pcrel_direct) break :rt .GOTPCREL;
857 break :rt .PC32;
858 } },
855 ) else if (emit.bin_file.cast(.coff2)) |coff| try coff.addReloc(859 ) else if (emit.bin_file.cast(.coff2)) |coff| try coff.addReloc(
856 @enumFromInt(@intFromEnum(emit.atom_id)),860 @enumFromInt(@intFromEnum(emit.atom_id)),
857 end_offset - 4,861 end_offset - 4,
src/link/Elf2.zig+2558-950
...@@ -34,6 +34,12 @@ shndx: struct {...@@ -34,6 +34,12 @@ shndx: struct {
34 dynstr: Section.Index,34 dynstr: Section.Index,
35 dynamic: Section.Index,35 dynamic: Section.Index,
36 tdata: Section.Index,36 tdata: Section.Index,
37 rela_dyn: Section.Index,
38 rela_plt: Section.Index,
39 // These sections are created only as needed, and are initially `.UNDEF`.
40 init_array: Section.Index,
41 fini_array: Section.Index,
42 preinit_array: Section.Index,
37},43},
38symtab: std.ArrayList(Symbol),44symtab: std.ArrayList(Symbol),
39globals: struct {45globals: struct {
...@@ -50,16 +56,34 @@ globals: struct {...@@ -50,16 +56,34 @@ globals: struct {
50/// We use a separate hash map for this data rather than storing it in `navs` etc to save memory,56/// We use a separate hash map for this data rather than storing it in `navs` etc to save memory,
51/// because the vast majority of nodes which can export global symbols actually will not.57/// because the vast majority of nodes which can export global symbols actually will not.
52node_global_symbols: std.array_hash_map.Auto(MappedFile.Node.Index, String(.strtab)),58node_global_symbols: std.array_hash_map.Auto(MappedFile.Node.Index, String(.strtab)),
59/// Contains all globals symbols defined in any needed DSO. This map serves two purposes:
60///
61/// * If we discover an undefined reference to one of these symbols, we will know the associated
62/// symbol type, which is important because it may cause us to create a PLT entry.
63///
64/// * When emitting a dynamic executable, we can detect which undefined references are resolved by a
65/// linked DSO, so can emit "undefined global symbol" errors for any other undefined references.
66dso_globals: std.array_hash_map.Auto(String(.strtab), std.elf.STT),
53shstrtab: StringTable,67shstrtab: StringTable,
54strtab: StringTable,68strtab: StringTable,
55dynstr: StringTable,69dynstr: StringTable,
56got: struct {70
57 len: u32,71/// Indices map 1--1 to indices into the actual `.got` section.
58 tlsld: GotIndex,72///
59 plt: std.AutoArrayHashMapUnmanaged(Symbol.Id, void),73/// Value is the output relocation in `.rela.dyn` for the GOT entry.
60},74got: std.array_hash_map.Auto(GotKey, Section.RelaIndex.Optional),
61first_plt_reloc: Reloc.Index,75/// Indices map 1--1 to indices into the actual `.got.plt` section. These also equal indices into
62first_dynamic_reloc: Reloc.Index,76/// the relocations in `.rela.plt`, because every PLT entry has one output relocation (if a runtime
77/// relocation is no longer necessary, then neither is the corresponding PLT entry!).
78///
79/// PLT entries in this map may be "dead", meaning the PLT entry has been deemed unnecessary so is
80/// available for reuse---see `Elf.pltEntryIsDead`. Such entries must not be targeted by relocs.
81plt: std.array_hash_map.Auto(Symbol.Id, void),
82/// The `.plt` section contains zero or more symbol relocations starting at this index.
83plt_first_symbol_reloc: SymbolReloc.Index,
84/// The `.dynamic` section contains zero or more symbol relocations starting at this index.
85dynamic_first_symbol_reloc: SymbolReloc.Index,
86
63needed: std.AutoArrayHashMapUnmanaged(String(.dynstr), void),87needed: std.AutoArrayHashMapUnmanaged(String(.dynstr), void),
64inputs: std.ArrayList(struct {88inputs: std.ArrayList(struct {
65 path: std.Build.Cache.Path,89 path: std.Build.Cache.Path,
...@@ -69,29 +93,42 @@ inputs: std.ArrayList(struct {...@@ -69,29 +93,42 @@ inputs: std.ArrayList(struct {
69input_sections: std.ArrayList(InputSection),93input_sections: std.ArrayList(InputSection),
70input_section_pending_index: u32,94input_section_pending_index: u32,
71navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, struct {95navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, struct {
72 /// The start index of the contiguous sequence of relocations in this NAV.
73 first_reloc: Reloc.Index,
74 lsi: Symbol.LocalIndex,96 lsi: Symbol.LocalIndex,
97 /// The start index of the contiguous sequence of symbol relocations in this NAV.
98 first_symbol_reloc: SymbolReloc.Index,
99 /// The start index of the contiguous sequence of GOT relocations in this NAV.
100 first_got_reloc: GotReloc.Index,
75}),101}),
76uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, struct {102uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, struct {
77 /// The start index of the contiguous sequence of relocations in this UAV.
78 first_reloc: Reloc.Index,
79 lsi: Symbol.LocalIndex,103 lsi: Symbol.LocalIndex,
104 /// The start index of the contiguous sequence of symbol relocations in this UAV.
105 first_symbol_reloc: SymbolReloc.Index,
106 // No `first_got_reloc` field because a UAV never contains GOT relocations.
80}),107}),
81lazy: std.EnumArray(link.File.LazySymbol.Kind, struct {108lazy: std.EnumArray(link.File.LazySymbol.Kind, struct {
82 map: std.AutoArrayHashMapUnmanaged(InternPool.Index, struct {109 map: std.AutoArrayHashMapUnmanaged(InternPool.Index, struct {
83 /// The start index of the contiguous sequence of relocations in this lazy code/data.
84 first_reloc: Reloc.Index,
85 lsi: Symbol.LocalIndex,110 lsi: Symbol.LocalIndex,
111 /// The start index of the contiguous sequence of symbol relocations in this lazy code/data.
112 first_symbol_reloc: SymbolReloc.Index,
113 /// The start index of the contiguous sequence of GOT relocations in this lazy code/data.
114 first_got_reloc: GotReloc.Index,
86 }),115 }),
87 pending_index: u32,116 pending_index: u32,
88}),117}),
89pending_uavs: std.ArrayList(Node.UavMapIndex),118pending_uavs: std.ArrayList(Node.UavMapIndex),
90relocs: std.ArrayList(Reloc),119symbol_relocs: std.ArrayList(SymbolReloc),
91120got_relocs: std.ArrayList(GotReloc),
121/// Set of relocations which must be re-applied if the size of the TLS segment changes.
122tls_size_symbol_relocs: std.array_hash_map.Auto(SymbolReloc.Index, void),
123/// Index matches the index into `shdrs`.
124section_by_name: std.array_hash_map.Auto(String(.shstrtab), void),
92/// Key is the name of a global symbol which has been moved to a new symtab index. Any relocation125/// Key is the name of a global symbol which has been moved to a new symtab index. Any relocation
93/// entries which target that symbol must be updated to reference the correct symbol index.126/// entries which target that symbol must be updated to reference the correct symbol index.
94changed_symtab_index: std.array_hash_map.Auto(String(.strtab), void),127changed_symtab_index: std.array_hash_map.Auto(String(.strtab), void),
128/// Counts how many relocations are currently in `.rela.dyn` which would require a `DT_TEXTREL`
129/// entry in the `.dynamic` section. This allows adding `DT_TEXTREL` to the output `.dynamic`
130/// section in `flush` only when it is actually necessary. See also `nodeRequiresTextrel`.
131textrel_count: u32,
95132
96const_prog_node: std.Progress.Node,133const_prog_node: std.Progress.Node,
97synth_prog_node: std.Progress.Node,134synth_prog_node: std.Progress.Node,
...@@ -106,21 +143,21 @@ const Node = union(enum) {...@@ -106,21 +143,21 @@ const Node = union(enum) {
106 shdr,143 shdr,
107 /// Cannot contain relocations.144 /// Cannot contain relocations.
108 segment: u32,145 segment: u32,
109 /// The section '.plt' may contain relocations via `elf.first_plt_reloc`.146 /// The section '.plt' may contain relocations via `elf.plt_first_symbol_reloc`.
110 ///147 ///
111 /// The section '.dynamic' may contain relocations via `elf.first_dynamic_reloc`.148 /// The section '.dynamic' may contain relocations via `elf.dynamic_first_symbol_reloc`.
112 ///149 ///
113 /// Otherwise, cannot contain relocations.150 /// Otherwise, cannot contain relocations.
114 section: Section.Index,151 section: Section.Index,
115 /// May contain relocations through the `first_reloc` field in `elf.input_sections`.152 /// May contain relocations.
116 input_section: InputSection.Index,153 input_section: InputSection.Index,
117 /// May contain relocations through the `first_reloc` field in `elf.navs`.154 /// May contain relocations.
118 nav: NavMapIndex,155 nav: NavMapIndex,
119 /// May contain relocations through the `first_reloc` field in `elf.uavs`.156 /// May contain relocations.
120 uav: UavMapIndex,157 uav: UavMapIndex,
121 /// May contain relocations through the `first_reloc` field in `elf.lazy.map`.158 /// May contain relocations.
122 lazy_code: LazyMapRef.Index(.code),159 lazy_code: LazyMapRef.Index(.code),
123 /// May contain relocations through the `first_reloc` field in `elf.lazy.map`.160 /// May contain relocations.
124 lazy_const_data: LazyMapRef.Index(.const_data),161 lazy_const_data: LazyMapRef.Index(.const_data),
125162
126 pub const InputIndex = enum(u32) {163 pub const InputIndex = enum(u32) {
...@@ -162,8 +199,11 @@ const Node = union(enum) {...@@ -162,8 +199,11 @@ const Node = union(enum) {
162 return elf.navs.values()[@intFromEnum(nmi)].lsi;199 return elf.navs.values()[@intFromEnum(nmi)].lsi;
163 }200 }
164201
165 fn firstReloc(nmi: NavMapIndex, elf: *const Elf) Reloc.Index {202 fn firstSymbolReloc(nmi: NavMapIndex, elf: *const Elf) SymbolReloc.Index {
166 return elf.navs.values()[@intFromEnum(nmi)].first_reloc;203 return elf.navs.values()[@intFromEnum(nmi)].first_symbol_reloc;
204 }
205 fn firstGotReloc(nmi: NavMapIndex, elf: *const Elf) GotReloc.Index {
206 return elf.navs.values()[@intFromEnum(nmi)].first_got_reloc;
167 }207 }
168 };208 };
169209
...@@ -178,8 +218,13 @@ const Node = union(enum) {...@@ -178,8 +218,13 @@ const Node = union(enum) {
178 return elf.uavs.values()[@intFromEnum(umi)].lsi;218 return elf.uavs.values()[@intFromEnum(umi)].lsi;
179 }219 }
180220
181 fn firstReloc(umi: UavMapIndex, elf: *const Elf) Reloc.Index {221 fn firstSymbolReloc(umi: UavMapIndex, elf: *const Elf) SymbolReloc.Index {
182 return elf.uavs.values()[@intFromEnum(umi)].first_reloc;222 return elf.uavs.values()[@intFromEnum(umi)].first_symbol_reloc;
223 }
224 fn firstGotReloc(umi: UavMapIndex, elf: *const Elf) GotReloc.Index {
225 _ = umi;
226 _ = elf;
227 return .none;
183 }228 }
184 };229 };
185230
...@@ -203,8 +248,11 @@ const Node = union(enum) {...@@ -203,8 +248,11 @@ const Node = union(enum) {
203 return lmi.ref().symbol(elf);248 return lmi.ref().symbol(elf);
204 }249 }
205250
206 fn firstReloc(lmi: @This(), elf: *const Elf) Reloc.Index {251 fn firstSymbolReloc(lmi: @This(), elf: *const Elf) SymbolReloc.Index {
207 return lmi.ref().firstReloc(elf);252 return elf.lazy.getPtrConst(kind).map.values()[@intFromEnum(lmi)].first_symbol_reloc;
253 }
254 fn firstGotReloc(lmi: @This(), elf: *const Elf) GotReloc.Index {
255 return elf.lazy.getPtrConst(kind).map.values()[@intFromEnum(lmi)].first_got_reloc;
208 }256 }
209 };257 };
210 }258 }
...@@ -216,10 +264,6 @@ const Node = union(enum) {...@@ -216,10 +264,6 @@ const Node = union(enum) {
216 pub fn symbol(lmr: LazyMapRef, elf: *const Elf) Symbol.LocalIndex {264 pub fn symbol(lmr: LazyMapRef, elf: *const Elf) Symbol.LocalIndex {
217 return elf.lazy.getPtrConst(lmr.kind).map.values()[lmr.index].lsi;265 return elf.lazy.getPtrConst(lmr.kind).map.values()[lmr.index].lsi;
218 }266 }
219
220 fn firstReloc(lmr: LazyMapRef, elf: *const Elf) Reloc.Index {
221 return elf.lazy.getPtrConst(lmr.kind).map.values()[lmr.index].first_reloc;
222 }
223 };267 };
224268
225 pub const Known = struct {269 pub const Known = struct {
...@@ -255,8 +299,10 @@ const InputSection = struct {...@@ -255,8 +299,10 @@ const InputSection = struct {
255 vaddr: u64,299 vaddr: u64,
256 /// The node corresponding to this input section.300 /// The node corresponding to this input section.
257 node: MappedFile.Node.Index,301 node: MappedFile.Node.Index,
258 /// The start index of the contiguous sequence of relocations in this input section.302 /// The start index of the contiguous sequence of symbol relocations in this input section.
259 first_reloc: Reloc.Index,303 first_symbol_reloc: SymbolReloc.Index,
304 /// The start index of the contiguous sequence of GOT relocations in this input section.
305 first_got_reloc: GotReloc.Index,
260306
261 const Index = enum(u32) {307 const Index = enum(u32) {
262 _,308 _,
...@@ -290,21 +336,53 @@ const Section = struct {...@@ -290,21 +336,53 @@ const Section = struct {
290 ///336 ///
291 /// If the section does not have flag `std.elf.SHF.ALLOC`, this is `.null`.337 /// If the section does not have flag `std.elf.SHF.ALLOC`, this is `.null`.
292 lsi: Symbol.LocalIndex,338 lsi: Symbol.LocalIndex,
293 rela_shndx: Section.Index,339 rela: union {
294 rela_free: RelIndex,340 /// This field is active if and only if this section is *not* a `SHT_RELA` section.
341 ///
342 /// This field's value refers to this section's corresponding relocation section, if it
343 /// currently has one. If this section does not currently have a relocation section, the
344 /// value is `.UNDEF`.
345 ///
346 /// This field is only ever non-`.UNDEF` when emitting a relocatable (`ET_REL`). While there
347 /// are also output relocations in DSOs, they are all placed in the `.rela.dyn`
348 /// (`elf.shdnx.rela_dyn`) and `.rela.plt` (`elf.shndx.rela_plt`) sections, rather than
349 /// having separate relocation sections for each section.
350 shndx: Section.Index,
351
352 /// This field is active if and only if this section *is* a `SHT_RELA` section.
353 ///
354 /// This is the head of a single-linked list of free `ElfN.Rela` entries in this section.
355 /// Entries in this list have `info.type` set to `R_*_NONE`, have `info.sym` set to 0, and
356 /// have `offset` set to `@enumFromInt(next)` where `next` is `RelaIndex.Optional`. Also,
357 /// `addend` is set to the length of the list starting from this point; so the last node in
358 /// the list has `addend = 1`, the one before it has `addend = 2`, etc. This is so that the
359 /// head node always contains the current length of the list.
360 ///
361 /// It would be okay to store these values (in the `offset` and `addend` fields) in the
362 /// compiler's host endianness, because they will never be read by other tooling. However,
363 /// we nonetheless use target endianness, because using host endianness would introduce an
364 /// unnecessary dependency of the output binary on the compiler's host architecture.
365 free_head: RelaIndex.Optional,
366 },
295367
296 pub const RelIndex = enum(u32) {368 const RelaIndex = enum(u32) {
297 none,369 none,
298 _,370 _,
299371
300 pub fn wrap(i: ?u32) RelIndex {372 const Optional = enum(u32) {
301 return @enumFromInt((i orelse return .none) + 1);373 none = std.math.maxInt(u32),
302 }374 _,
303 pub fn unwrap(ri: RelIndex) ?u32 {375
304 return switch (ri) {376 fn unwrap(opt: RelaIndex.Optional) ?RelaIndex {
305 .none => null,377 return switch (opt) {
306 _ => @intFromEnum(ri) - 1,378 .none => null,
307 };379 _ => @enumFromInt(@intFromEnum(opt)),
380 };
381 }
382 };
383
384 fn toOptional(i: RelaIndex) RelaIndex.Optional {
385 return @enumFromInt(@intFromEnum(i));
308 }386 }
309 };387 };
310388
...@@ -357,11 +435,10 @@ const Section = struct {...@@ -357,11 +435,10 @@ const Section = struct {
357 return &elf.shdrs.items[@intFromEnum(s)];435 return &elf.shdrs.items[@intFromEnum(s)];
358 }436 }
359437
360 fn name(s: Index, elf: *Elf) [:0]const u8 {438 fn name(s: Index, elf: *Elf) String(.shstrtab) {
361 const str: String(.shstrtab) = switch (elf.shdrPtr(s)) {439 return switch (elf.shdrPtr(s)) {
362 inline else => |shdr| @enumFromInt(elf.targetLoad(&shdr.name)),440 inline else => |shdr| @enumFromInt(elf.targetLoad(&shdr.name)),
363 };441 };
364 return str.slice(elf);
365 }442 }
366443
367 fn vaddr(s: Index, elf: *Elf) u64 {444 fn vaddr(s: Index, elf: *Elf) u64 {
...@@ -377,9 +454,690 @@ const Section = struct {...@@ -377,9 +454,690 @@ const Section = struct {
377 inline else => |shdr| elf.targetStore(&shdr.name, @intFromEnum(shstrtab_entry)),454 inline else => |shdr| elf.targetStore(&shdr.name, @intFromEnum(shstrtab_entry)),
378 }455 }
379 }456 }
457
458 /// Asserts that `shndx` is a `SHT_RELA` section and ensures that its node has enough unused
459 /// space to hold `n` additional `ElfN.Rela` entries.
460 fn relaEnsureAdditionalCapacity(rela_shndx: Index, elf: *Elf, n: usize) !void {
461 const node = rela_shndx.get(elf).ni;
462 const need_size: u64 = switch (elf.shdrPtr(rela_shndx)) {
463 inline else => |shdr, class| need_size: {
464 assert(elf.targetLoad(&shdr.type) == .RELA);
465 const cur_size = elf.targetLoad(&shdr.size);
466 const ent_size = @sizeOf(class.ElfN().Rela);
467 assert(elf.targetLoad(&shdr.entsize) == ent_size);
468 const free_len: u32 = free_len: {
469 const opt_free_head = rela_shndx.get(elf).rela.free_head;
470 const free_head = opt_free_head.unwrap() orelse break :free_len 0;
471 const relas: []const class.ElfN().Rela = @ptrCast(@alignCast(
472 node.slice(&elf.mf)[0..@intCast(cur_size)],
473 ));
474 const free_len = elf.targetLoad(&relas[@intFromEnum(free_head)].addend);
475 assert(free_len > 0);
476 break :free_len @intCast(free_len);
477 };
478 const need_additional = n -| free_len;
479 break :need_size cur_size + need_additional * ent_size;
480 },
481 };
482 _, const cur_node_size = node.location(&elf.mf).resolve(&elf.mf);
483 if (need_size > cur_node_size) {
484 const gpa = elf.base.comp.gpa;
485 try node.resize(&elf.mf, gpa, need_size +| need_size / MappedFile.growth_factor);
486 }
487 }
488
489 /// Asserts that `shndx` is a `SHT_RELA` section and deletes the `ElfN.Rela` entry at the
490 /// given `index` in it. The entry is added to the free-list for reuse later. Asserts that
491 /// the relocation entry at `index` is not already free.
492 fn relaDeleteOne(rela_shndx: Index, elf: *Elf, index: RelaIndex) void {
493 switch (elf.shdrPtr(rela_shndx)) {
494 inline else => |shdr, class| {
495 assert(elf.targetLoad(&shdr.type) == .RELA);
496 assert(elf.targetLoad(&shdr.entsize) == @sizeOf(class.ElfN().Rela));
497 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
498 rela_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(elf.targetLoad(&shdr.size))],
499 ));
500 const opt_free_head = rela_shndx.get(elf).rela.free_head;
501 const old_free_len: u32 = free_len: {
502 const free_head = opt_free_head.unwrap() orelse break :free_len 0;
503 const free_len = elf.targetLoad(&relas[@intFromEnum(free_head)].addend);
504 assert(free_len > 0);
505 break :free_len @intCast(free_len);
506 };
507 const none_reloc_type = MachineRelocType.none(elf).unwrap(elf);
508 {
509 const old_type = elf.targetLoad(&relas[@intFromEnum(index)].info).type;
510 assert(old_type != none_reloc_type); // bug: `index` is already in the free-list
511 }
512 relas[@intFromEnum(index)] = .{
513 .offset = @intFromEnum(opt_free_head), // next
514 .info = .{
515 .type = @intCast(none_reloc_type),
516 .sym = 0,
517 },
518 .addend = @intCast(old_free_len + 1), // list length
519 };
520 if (elf.targetEndian() != native_endian) {
521 std.mem.byteSwapAllFields(class.ElfN().Rela, &relas[@intFromEnum(index)]);
522 }
523 },
524 }
525 rela_shndx.get(elf).rela.free_head = index.toOptional();
526 }
527
528 /// Asserts that `shndx` is a `SHT_RELA` section and adds a new `ElfN.Rela` entry to it with
529 /// the given field values. Returns the index of the populated entry. Asserts that capacity
530 /// for this operation was already guaranteed using `relaEnsureAdditionalCapacity`.
531 fn relaAddOneAssumeCapacity(rela_shndx: Index, elf: *Elf, opts: struct {
532 type: MachineRelocType,
533 offset: u64,
534 /// This is a raw `u32` because whether this is an index into `.symtab` (`Symbol.Index`)
535 /// or an index into `.dynsym` is contextual.
536 raw_sym_index: u32,
537 addend: i64,
538 }) RelaIndex {
539 switch (elf.shdrPtr(rela_shndx)) {
540 inline else => |shdr, class| {
541 assert(elf.targetLoad(&shdr.type) == .RELA);
542 const ent_size = @sizeOf(class.ElfN().Rela);
543 assert(elf.targetLoad(&shdr.entsize) == ent_size);
544 const new_index: RelaIndex = if (rela_shndx.get(elf).rela.free_head.unwrap()) |free_head| new_index: {
545 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
546 rela_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(elf.targetLoad(&shdr.size))],
547 ));
548 const next: RelaIndex.Optional = @enumFromInt(elf.targetLoad(
549 &relas[@intFromEnum(free_head)].offset,
550 ));
551 rela_shndx.get(elf).rela.free_head = next;
552
553 const old_free_len: u32 = @intCast(
554 elf.targetLoad(&relas[@intFromEnum(free_head)].addend),
555 );
556 const new_free_len: u32 = if (next.unwrap()) |i| @intCast(
557 elf.targetLoad(&relas[@intFromEnum(i)].addend),
558 ) else 0;
559 assert(new_free_len == old_free_len - 1);
560
561 break :new_index free_head;
562 } else new_index: {
563 const old_size = elf.targetLoad(&shdr.size);
564 const new_size = old_size + ent_size;
565 elf.targetStore(&shdr.size, new_size);
566 if (rela_shndx == elf.shndx.rela_dyn) {
567 elf.updateDynamicEntry(std.elf.DT_RELASZ, new_size);
568 } else if (rela_shndx == elf.shndx.rela_plt) {
569 elf.updateDynamicEntry(std.elf.DT_PLTRELSZ, new_size);
570 }
571 break :new_index @enumFromInt(@divExact(old_size, ent_size));
572 };
573 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
574 rela_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(elf.targetLoad(&shdr.size))],
575 ));
576 relas[@intFromEnum(new_index)] = .{
577 .offset = @intCast(opts.offset),
578 .info = .{
579 .type = @intCast(opts.type.unwrap(elf)),
580 .sym = @intCast(opts.raw_sym_index),
581 },
582 .addend = @intCast(opts.addend),
583 };
584 if (elf.targetEndian() != native_endian) {
585 std.mem.byteSwapAllFields(class.ElfN().Rela, &relas[@intFromEnum(new_index)]);
586 }
587 return new_index;
588 },
589 }
590 }
591
592 /// Asserts that `shndx` is a `SHT_RELA` section and updates the `info.sym` field of the
593 /// `ElfN.Rela` entry at the given index. As with `relaAddOneAssumeCapacity`, the symbol
594 /// index is a raw `u32`, because it may be an index into `.symtab` or an index into
595 /// `.dynsym`. Asserts that `index` is not in the free-list (i.e. is not deleted).
596 fn relaUpdateSym(rela_shndx: Index, elf: *Elf, index: RelaIndex, raw_sym_index: u32) void {
597 switch (elf.shdrPtr(rela_shndx)) {
598 inline else => |shdr, class| {
599 assert(elf.targetLoad(&shdr.type) == .RELA);
600 assert(elf.targetLoad(&shdr.entsize) == @sizeOf(class.ElfN().Rela));
601 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
602 rela_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(elf.targetLoad(&shdr.size))],
603 ));
604 const rela_info = elf.targetLoad(&relas[@intFromEnum(index)].info);
605 {
606 const none_reloc_type = MachineRelocType.none(elf).unwrap(elf);
607 assert(rela_info.type != none_reloc_type); // bug: `index` is in the free-list
608 }
609 elf.targetStore(&relas[@intFromEnum(index)].info, .{
610 .type = rela_info.type,
611 .sym = @intCast(raw_sym_index),
612 });
613 },
614 }
615 }
616
617 /// Asserts that `shndx` is a `SHT_RELA` section and updates the `offset` field of the
618 /// `ElfN.Rela` entry at the given index. Asserts that `index` is not in the free-list (i.e.
619 /// it is not deleted).
620 fn relaSetOffset(rela_shndx: Index, elf: *Elf, index: RelaIndex, new_offset: u64) void {
621 switch (elf.shdrPtr(rela_shndx)) {
622 inline else => |shdr, class| {
623 assert(elf.targetLoad(&shdr.type) == .RELA);
624 assert(elf.targetLoad(&shdr.entsize) == @sizeOf(class.ElfN().Rela));
625 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
626 rela_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(elf.targetLoad(&shdr.size))],
627 ));
628 {
629 const rela_info = elf.targetLoad(&relas[@intFromEnum(index)].info);
630 const none_reloc_type = MachineRelocType.none(elf).unwrap(elf);
631 assert(rela_info.type != none_reloc_type); // bug: `index` is in the free-list
632 }
633 elf.targetStore(&relas[@intFromEnum(index)].offset, @intCast(new_offset));
634 },
635 }
636 }
637
638 /// Asserts that `shndx` is a `SHT_RELA` section and updates the `offset` field of the
639 /// `ElfN.Rela` entry at the given index, by subtracting `old_base` and adding `new_base`.
640 /// Asserts that `index` is not in the free-list (i.e. it is not deleted).
641 fn relaAdjustOffset(rela_shndx: Index, elf: *Elf, index: RelaIndex, old_base: u64, new_base: u64) void {
642 switch (elf.shdrPtr(rela_shndx)) {
643 inline else => |shdr, class| {
644 assert(elf.targetLoad(&shdr.type) == .RELA);
645 assert(elf.targetLoad(&shdr.entsize) == @sizeOf(class.ElfN().Rela));
646 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
647 rela_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(elf.targetLoad(&shdr.size))],
648 ));
649 {
650 const rela_info = elf.targetLoad(&relas[@intFromEnum(index)].info);
651 const none_reloc_type = MachineRelocType.none(elf).unwrap(elf);
652 assert(rela_info.type != none_reloc_type); // bug: `index` is in the free-list
653 }
654 const old_offset = elf.targetLoad(&relas[@intFromEnum(index)].offset);
655 elf.targetStore(&relas[@intFromEnum(index)].offset, @intCast(
656 old_offset - old_base + new_base,
657 ));
658 },
659 }
660 }
380 };661 };
381};662};
382663
664/// Identifies a single entry in the GOT.
665const GotKey = union(enum) {
666 /// The entry is a reserved word, initialized to zero. `initHeaders` will add as many of these
667 /// as the target machine ABI requires.
668 ///
669 /// This `u32` value exists to allow reserving multiple words with distinct keys.
670 reserved: u32,
671
672 /// Value is the address of the given symbol.
673 symbol: Symbol.Id,
674
675 /// Value is the signed offset of the given symbol from the TLS pointer.
676 tpoff: Symbol.Id,
677
678 /// Value is the TLS module ID of the DSO we are creating.
679 ///
680 /// Used for the first of the two GOT entries generated by a TLSLD relocation.
681 tlsld0,
682 /// Value is always 0.
683 ///
684 /// Used for the second of the two GOT entries generated by a TLSLD relocation.
685 tlsld1,
686
687 /// Value is the TLS module ID for the given STT_TLS symbol.
688 ///
689 /// Used for the first of the two GOT entries generated by a TLSGD relocation.
690 tlsgd0: Symbol.Id,
691 /// Value is the offset of the given STT_TLS symbol from the base of the per-module TLS area.
692 ///
693 /// Used for the second of the two GOT entries generated by a TLSGD relocation.
694 tlsgd1: Symbol.Id,
695};
696
697/// A relocation targeting a particular GOT entry.
698const GotReloc = struct {
699 /// The node containing this relocation. Possible values are:
700 /// * An input section
701 /// * A section
702 /// * A NAV, UAV, or lazy code/data
703 /// * `.none`, if this relocation was deleted (in which case it should be ignored)
704 node: MappedFile.Node.Index,
705 /// The offset of the relocation inside of `node`.
706 offset: u64,
707 target: GotKey,
708 addend: i64,
709 type: GotReloc.Type,
710
711 const deleted: GotReloc = .{
712 .node = .none,
713 .offset = undefined,
714 .target = undefined,
715 .addend = undefined,
716 .type = undefined,
717 };
718
719 const Type = enum(u8) {
720 offset64,
721 offset32,
722 rel64,
723 rel32,
724 };
725
726 const Index = enum(u32) {
727 none = std.math.maxInt(u32),
728 _,
729
730 fn get(index: GotReloc.Index, elf: *Elf) *GotReloc {
731 return &elf.got_relocs.items[@intFromEnum(index)];
732 }
733 };
734
735 fn apply(reloc: *const GotReloc, elf: *Elf) void {
736 assert(elf.ehdrField(.type) != .REL);
737 if (reloc.node == .none) return; // deleted
738 if (reloc.node.hasMoved(&elf.mf) or elf.shndx.got.get(elf).ni.hasMoved(&elf.mf)) {
739 // There's no point applying the relocation now, because it will be re-applied by
740 // `flushMoved` at some point anyway.
741 return;
742 }
743 const node_vaddr: u64 = switch (elf.getNode(reloc.node)) {
744 .file => unreachable,
745 .ehdr => unreachable,
746 .shdr => unreachable,
747 .segment => unreachable,
748 .section => |shndx| shndx.vaddr(elf),
749 .input_section => |isi| isi.ptrConst(elf).vaddr,
750 inline .nav,
751 .uav,
752 .lazy_code,
753 .lazy_const_data,
754 => |i| Symbol.Id.local(i.symbol(elf)).value(elf),
755 };
756 const dest_vaddr = node_vaddr + reloc.offset;
757 const dest_slice = reloc.node.slice(&elf.mf)[@intCast(reloc.offset)..];
758 const target_endian = elf.targetEndian();
759 const got_vaddr = elf.shndx.got.vaddr(elf);
760 const got_index: u64 = elf.got.getIndex(reloc.target).?;
761 const got_offset: u64 = switch (elf.identClass()) {
762 .NONE, _ => unreachable,
763 inline else => |class| @sizeOf(class.ElfN().Addr) * got_index,
764 };
765 const addend: u64 = @bitCast(reloc.addend);
766 switch (reloc.type) {
767 .offset64 => std.mem.writeInt(
768 u64,
769 dest_slice[0..8],
770 got_offset +% addend,
771 target_endian,
772 ),
773 .offset32 => std.mem.writeInt(
774 u32,
775 dest_slice[0..4],
776 @intCast(got_offset +% addend),
777 target_endian,
778 ),
779 .rel64 => std.mem.writeInt(
780 i64,
781 dest_slice[0..8],
782 @bitCast(got_vaddr +% got_offset +% addend -% dest_vaddr),
783 target_endian,
784 ),
785 .rel32 => std.mem.writeInt(
786 i32,
787 dest_slice[0..4],
788 @intCast(@as(i64, @bitCast(got_vaddr +% got_offset +% addend -% dest_vaddr))),
789 target_endian,
790 ),
791 }
792 }
793};
794
795pub const MachineRelocType = union {
796 X86_64: std.elf.R_X86_64,
797 AARCH64: std.elf.R_AARCH64,
798 RISCV: std.elf.R_RISCV,
799 PPC64: std.elf.R_PPC64,
800
801 pub fn none(elf: *Elf) MachineRelocType {
802 return switch (elf.ehdrField(.machine)) {
803 else => unreachable,
804 .AARCH64 => .{ .AARCH64 = .NONE },
805 .PPC64 => .{ .PPC64 = .NONE },
806 .RISCV => .{ .RISCV = .NONE },
807 .X86_64 => .{ .X86_64 = .NONE },
808 };
809 }
810 pub fn jumpSlot(elf: *Elf) MachineRelocType {
811 return switch (elf.ehdrField(.machine)) {
812 else => unreachable,
813 .X86_64 => .{ .X86_64 = .JUMP_SLOT },
814 };
815 }
816 pub fn globDat(elf: *Elf) MachineRelocType {
817 return switch (elf.ehdrField(.machine)) {
818 else => unreachable,
819 .X86_64 => .{ .X86_64 = .GLOB_DAT },
820 };
821 }
822 pub fn dtpOffAddr(elf: *Elf) MachineRelocType {
823 return switch (elf.ehdrField(.machine)) {
824 else => unreachable,
825 .X86_64 => .{ .X86_64 = .DTPOFF64 },
826 };
827 }
828 pub fn absAddr(elf: *Elf) MachineRelocType {
829 return switch (elf.ehdrField(.machine)) {
830 else => unreachable,
831 .AARCH64 => .{ .AARCH64 = .ABS64 },
832 .PPC64 => .{ .PPC64 = .ADDR64 },
833 .RISCV => .{ .RISCV = .@"64" },
834 .X86_64 => .{ .X86_64 = .@"64" },
835 };
836 }
837 pub fn sizeAddr(elf: *Elf) MachineRelocType {
838 return switch (elf.ehdrField(.machine)) {
839 else => unreachable,
840 .X86_64 => .{ .X86_64 = .SIZE64 },
841 };
842 }
843
844 pub fn wrap(int: u32, elf: *Elf) MachineRelocType {
845 return switch (elf.ehdrField(.machine)) {
846 else => unreachable,
847 inline .AARCH64,
848 .PPC64,
849 .RISCV,
850 .X86_64,
851 => |machine| @unionInit(MachineRelocType, @tagName(machine), @enumFromInt(int)),
852 };
853 }
854 pub fn unwrap(rt: MachineRelocType, elf: *Elf) u32 {
855 return switch (elf.ehdrField(.machine)) {
856 else => unreachable,
857 inline .AARCH64,
858 .PPC64,
859 .RISCV,
860 .X86_64,
861 => |machine| @intFromEnum(@field(rt, @tagName(machine))),
862 };
863 }
864};
865
866/// A relocation targeting an arbitrary symbol with a fixed addend.
867const SymbolReloc = struct {
868 /// The node containing this relocation. Possible values are:
869 /// * An input section
870 /// * A section
871 /// * A NAV, UAV, or lazy code/data
872 node: MappedFile.Node.Index,
873 /// The offset of the relocation inside of `node`.
874 offset: u64,
875 /// A symbol used to compute the relocated value. Precise meaning depends on `@"type"`.
876 target: Symbol.Id,
877 /// A signed constant used to compute the relocated value. Precise meaning depends on `@"type"`.
878 addend: i64,
879 /// Specifies how to apply the relocation.
880 type: SymbolReloc.Type,
881 /// Forms a linked list of all symbol relocations with the same `target`. This list exists so
882 /// that all relocations targeting a particular symbol can be re-applied if that symbol moves.
883 /// Doubly-linked so that relocations can be removed.
884 next: SymbolReloc.Index,
885 /// Back-reference in a doubly-linked list---see `next`.
886 prev: SymbolReloc.Index,
887 /// If this relocation has a corresponding output relocation, this is its index within the
888 /// appropriate SHT_RELA section (see `relaSection`). If there is no output relocation
889 /// corresponding to this relocation, this is `.none`.
890 ///
891 /// If we are producing a relocatable, this field is always populated, because all relocations
892 /// are emitted as output relocations.
893 ///
894 /// If we are producing a DSO, this field is populated if this relocation requires a runtime
895 /// relocation entry. The entry will be removed if we discover a definition which allows us to
896 /// statically resolve the relocation.
897 rela_index: Section.RelaIndex.Optional,
898
899 /// Determines the section in which this relocation will be placed if it is outstanding.
900 ///
901 /// When producing a relocatable (ET_REL), the relocation section is `Section.rela.shndx` for
902 /// the section of `node`, and this function asserts that the aforementioned `rela.shndx` field
903 /// is populated.
904 ///
905 /// When producing a DSO, the relocation section is always `.rela.dyn`. It is not `.rela.plt`
906 /// because relocations in the GOTPLT are handled specially, without `SymbolReloc` entries.
907 fn relaSection(sr: *const SymbolReloc, elf: *Elf) Section.Index {
908 const shndx = switch (elf.ehdrField(.type)) {
909 .NONE, .CORE, _ => unreachable,
910 .REL => elf.getNodeShndx(sr.node).get(elf).rela.shndx,
911 .EXEC, .DYN => elf.shndx.rela_dyn,
912 };
913 assert(shndx != .UNDEF);
914 return shndx;
915 }
916
917 const Index = enum(u32) {
918 none = std.math.maxInt(u32),
919 _,
920
921 fn get(index: SymbolReloc.Index, elf: *Elf) *SymbolReloc {
922 return &elf.symbol_relocs.items[@intFromEnum(index)];
923 }
924 };
925
926 const Type = enum {
927 /// This input relocation is being directly forwarded to an `ElfN.Rela` entry in the output
928 /// file. `rela_index` is guaranteed to be populated. The ELF relocation type is available
929 /// in the `ElfN.Rela` entry.
930 ///
931 /// If we are emitting a relocatable (`ET_REL`), all symbol relocs use this type (since we
932 /// do not apply any relocations ourselves). Otherwise, no symbol relocs use this type.
933 write_rela,
934
935 abs64,
936 abs32,
937 abs32s,
938 rel64,
939 rel32,
940 pltrel64,
941 pltrel32,
942 dtpoff64,
943 dtpoff32,
944 tpoff64,
945 tpoff32,
946 size64,
947 size32,
948
949 fn dependsOnTlsSize(t: SymbolReloc.Type) bool {
950 return switch (t) {
951 .tpoff32, .tpoff64 => true,
952 else => false,
953 };
954 }
955 };
956
957 fn apply(reloc: *const SymbolReloc, elf: *Elf) void {
958 assert(elf.ehdrField(.type) != .REL);
959 assert(reloc.node != .none);
960 if (reloc.node.hasMoved(&elf.mf) or reloc.target.hasMoved(elf)) {
961 // There's no point applying the relocation now, because it will be re-applied by
962 // `flushMoved` at some point anyway.
963 return;
964 }
965 if (reloc.rela_index != .none) {
966 // This relocation has been lowered to a runtime relocation. Until that changes, it is
967 // not our job to apply it.
968 return;
969 }
970 const node_vaddr: u64 = switch (elf.getNode(reloc.node)) {
971 .file => unreachable,
972 .ehdr => unreachable,
973 .shdr => unreachable,
974 .segment => unreachable,
975 .section => |shndx| shndx.vaddr(elf),
976 .input_section => |isi| isi.ptrConst(elf).vaddr,
977 inline .nav,
978 .uav,
979 .lazy_code,
980 .lazy_const_data,
981 => |i| Symbol.Id.local(i.symbol(elf)).value(elf),
982 };
983 const dest_vaddr = node_vaddr + reloc.offset;
984 const dest_slice = reloc.node.slice(&elf.mf)[@intCast(reloc.offset)..];
985 const target_endian = elf.targetEndian();
986 const sym_value: u64, const sym_size: u64 = switch (elf.symPtr(reloc.target.index(elf))) {
987 inline else => |target_sym| .{
988 elf.targetLoad(&target_sym.value),
989 elf.targetLoad(&target_sym.size),
990 },
991 };
992 const target_value = sym_value +% @as(u64, @bitCast(reloc.addend));
993 type: switch (reloc.type) {
994 .write_rela => unreachable,
995 .abs64 => std.mem.writeInt(
996 u64,
997 dest_slice[0..8],
998 target_value,
999 target_endian,
1000 ),
1001 .abs32 => std.mem.writeInt(
1002 u32,
1003 dest_slice[0..4],
1004 @intCast(target_value),
1005 target_endian,
1006 ),
1007 .abs32s => std.mem.writeInt(
1008 i32,
1009 dest_slice[0..4],
1010 @intCast(@as(i64, @bitCast(target_value))),
1011 target_endian,
1012 ),
1013 .rel64 => std.mem.writeInt(
1014 i64,
1015 dest_slice[0..8],
1016 @bitCast(target_value -% dest_vaddr),
1017 target_endian,
1018 ),
1019 .rel32 => std.mem.writeInt(
1020 i32,
1021 dest_slice[0..4],
1022 @intCast(@as(i64, @bitCast(target_value -% dest_vaddr))),
1023 target_endian,
1024 ),
1025 .pltrel64 => {
1026 const plt_index = elf.plt.getIndex(reloc.target) orelse continue :type .rel64;
1027 if (elf.pltEntryIsDead(plt_index)) continue :type .rel64;
1028 const plt_shndx: Section.Index, const plt_entry_size: u64 = switch (elf.ehdrField(.machine)) {
1029 else => |machine| @panic(@tagName(machine)),
1030 .X86_64 => .{ elf.shndx.plt_sec, 16 },
1031 };
1032 const plt_entry = plt_shndx.vaddr(elf) +% plt_index * plt_entry_size;
1033 std.mem.writeInt(
1034 i64,
1035 dest_slice[0..8],
1036 @bitCast(plt_entry +% @as(u64, @bitCast(reloc.addend)) -% dest_vaddr),
1037 target_endian,
1038 );
1039 },
1040 .pltrel32 => {
1041 const plt_index = elf.plt.getIndex(reloc.target) orelse continue :type .rel32;
1042 if (elf.pltEntryIsDead(plt_index)) continue :type .rel32;
1043 const plt_shndx: Section.Index, const plt_entry_size: u64 = switch (elf.ehdrField(.machine)) {
1044 else => |machine| @panic(@tagName(machine)),
1045 .X86_64 => .{ elf.shndx.plt_sec, 16 },
1046 };
1047 const plt_entry = plt_shndx.vaddr(elf) +% plt_index * plt_entry_size;
1048 std.mem.writeInt(
1049 i32,
1050 dest_slice[0..4],
1051 @intCast(@as(i64, @bitCast(
1052 plt_entry +% @as(u64, @bitCast(reloc.addend)) -% dest_vaddr,
1053 ))),
1054 target_endian,
1055 );
1056 },
1057 .size64 => std.mem.writeInt(
1058 u64,
1059 dest_slice[0..8],
1060 sym_size +% @as(u64, @bitCast(reloc.addend)),
1061 target_endian,
1062 ),
1063 .size32 => std.mem.writeInt(
1064 u32,
1065 dest_slice[0..4],
1066 @intCast(sym_size +% @as(u64, @bitCast(reloc.addend))),
1067 target_endian,
1068 ),
1069 .dtpoff64 => std.mem.writeInt(
1070 i64,
1071 dest_slice[0..8],
1072 @bitCast(target_value),
1073 target_endian,
1074 ),
1075 .dtpoff32 => std.mem.writeInt(
1076 i32,
1077 dest_slice[0..4],
1078 @intCast(@as(i64, @bitCast(target_value))),
1079 target_endian,
1080 ),
1081 .tpoff64 => {
1082 const tls_phndx = elf.getNode(elf.ni.tls).segment;
1083 const tls_size: u64 = switch (elf.phdrSlice()) {
1084 inline else => |phdr| tls_size: {
1085 assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS);
1086 break :tls_size elf.targetLoad(&phdr[tls_phndx].memsz);
1087 },
1088 };
1089 std.mem.writeInt(
1090 i64,
1091 dest_slice[0..8],
1092 @bitCast(target_value -% tls_size),
1093 target_endian,
1094 );
1095 },
1096 .tpoff32 => {
1097 const tls_phndx = elf.getNode(elf.ni.tls).segment;
1098 const tls_size: u64 = switch (elf.phdrSlice()) {
1099 inline else => |phdr| tls_size: {
1100 assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS);
1101 break :tls_size elf.targetLoad(&phdr[tls_phndx].memsz);
1102 },
1103 };
1104 std.mem.writeInt(
1105 i32,
1106 dest_slice[0..4],
1107 @intCast(@as(i64, @bitCast(target_value -% tls_size))),
1108 target_endian,
1109 );
1110 },
1111 }
1112 }
1113
1114 fn delete(reloc: *SymbolReloc, elf: *Elf, index: SymbolReloc.Index) void {
1115 assert(index.get(elf) == reloc);
1116 switch (reloc.prev) {
1117 .none => {
1118 const target_ptr = reloc.target.index(elf).ptr(elf);
1119 assert(target_ptr.first_target_reloc == index);
1120 target_ptr.first_target_reloc = reloc.next;
1121 },
1122 else => |prev| prev.get(elf).next = reloc.next,
1123 }
1124 switch (reloc.next) {
1125 .none => {},
1126 else => |next| next.get(elf).prev = reloc.prev,
1127 }
1128 if (reloc.rela_index.unwrap()) |rela_index| {
1129 reloc.relaSection(elf).relaDeleteOne(elf, rela_index);
1130 if (elf.nodeRequiresTextrel(reloc.node)) {
1131 elf.textrel_count -= 1;
1132 }
1133 }
1134 if (reloc.type.dependsOnTlsSize()) {
1135 assert(elf.tls_size_symbol_relocs.swapRemove(index));
1136 }
1137 reloc.* = undefined;
1138 }
1139};
1140
383fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe_global }) !void {1141fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe_global }) !void {
384 const gpa = elf.base.comp.gpa;1142 const gpa = elf.base.comp.gpa;
3851143
...@@ -426,59 +1184,70 @@ fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe...@@ -426,59 +1184,70 @@ fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe
426 try elf.shndx.dynsym.get(elf).ni.resize(&elf.mf, gpa, new_size);1184 try elf.shndx.dynsym.get(elf).ni.resize(&elf.mf, gpa, new_size);
427 }1185 }
4281186
429 try elf.got.plt.ensureUnusedCapacity(gpa, len);1187 try elf.ensureUnusedPltCapacity(len);
430 const need_plt_capacity = elf.got.plt.count() + len;1188 }
1189 },
1190 }
1191}
1192fn ensureUnusedPltCapacity(elf: *Elf, len: u32) !void {
1193 const gpa = elf.base.comp.gpa;
4311194
432 switch (elf.ehdrField(.machine)) {1195 try elf.shndx.rela_plt.relaEnsureAdditionalCapacity(elf, len);
433 else => |machine| @panic(@tagName(machine)),
434 .X86_64 => {
435 // Ensure the `.plt` section's node is big enough
436 const plt_need_size: usize = 16 * (1 + need_plt_capacity);
437 _, const plt_cur_size = elf.shndx.plt.get(elf).ni.location(&elf.mf).resolve(&elf.mf);
438 if (plt_cur_size < plt_need_size) {
439 const new_size = plt_need_size +| plt_need_size / MappedFile.growth_factor;
440 try elf.shndx.plt.get(elf).ni.resize(&elf.mf, gpa, new_size);
441 }
4421196
443 // Ensure the `.got.plt` section's node is big enough1197 try elf.plt.ensureUnusedCapacity(gpa, len);
444 const got_plt_need_size: usize = switch (elf.identClass()) {1198 const need_plt_capacity = elf.plt.count() + len;
445 .NONE, _ => unreachable,
446 inline else => |class| @sizeOf(class.ElfN().Addr) * (3 + need_plt_capacity),
447 };
448 _, const got_plt_cur_size = elf.shndx.got_plt.get(elf).ni.location(&elf.mf).resolve(&elf.mf);
449 if (got_plt_cur_size < got_plt_need_size) {
450 const new_size = got_plt_need_size +| got_plt_need_size / MappedFile.growth_factor;
451 try elf.shndx.got_plt.get(elf).ni.resize(&elf.mf, gpa, new_size);
452 }
4531199
454 // Ensure the `.plt.sec` section's node is big enough1200 switch (elf.ehdrField(.machine)) {
455 const plt_sec_need_size: usize = 16 * need_plt_capacity;1201 else => |machine| @panic(@tagName(machine)),
456 _, const plt_sec_cur_size = elf.shndx.plt_sec.get(elf).ni.location(&elf.mf).resolve(&elf.mf);1202 .X86_64 => {
457 if (plt_sec_cur_size < plt_sec_need_size) {1203 // Ensure the `.plt` section's node is big enough
458 const new_size = plt_sec_need_size +| plt_sec_need_size / MappedFile.growth_factor;1204 const plt_need_size: usize = 16 * (1 + need_plt_capacity);
459 try elf.shndx.plt_sec.get(elf).ni.resize(&elf.mf, gpa, new_size);1205 _, const plt_cur_size = elf.shndx.plt.get(elf).ni.location(&elf.mf).resolve(&elf.mf);
460 }1206 if (plt_cur_size < plt_need_size) {
1207 const new_size = plt_need_size +| plt_need_size / MappedFile.growth_factor;
1208 try elf.shndx.plt.get(elf).ni.resize(&elf.mf, gpa, new_size);
1209 }
4611210
462 // Ensure the `.rela.plt` section's node is big enough1211 // Ensure the `.got.plt` section's node is big enough
463 const rela_plt_shndx = elf.shndx.got_plt.get(elf).rela_shndx;1212 const got_plt_need_size: usize = switch (elf.identClass()) {
464 const rela_plt_need_size: usize = switch (elf.shdrPtr(rela_plt_shndx)) {1213 .NONE, _ => unreachable,
465 inline else => |shdr| @intCast(elf.targetLoad(&shdr.entsize) * need_plt_capacity),1214 inline else => |class| @sizeOf(class.ElfN().Addr) * (3 + need_plt_capacity),
466 };1215 };
467 _, const rela_plt_cur_size = rela_plt_shndx.get(elf).ni.location(&elf.mf).resolve(&elf.mf);1216 _, const got_plt_cur_size = elf.shndx.got_plt.get(elf).ni.location(&elf.mf).resolve(&elf.mf);
468 if (rela_plt_cur_size < rela_plt_need_size) {1217 if (got_plt_cur_size < got_plt_need_size) {
469 const new_size = rela_plt_need_size +| rela_plt_need_size / MappedFile.growth_factor;1218 const new_size = got_plt_need_size +| got_plt_need_size / MappedFile.growth_factor;
470 try rela_plt_shndx.get(elf).ni.resize(&elf.mf, gpa, new_size);1219 try elf.shndx.got_plt.get(elf).ni.resize(&elf.mf, gpa, new_size);
471 } else {1220 }
472 // Still mark `.rela.plt` as resized so that the DT_PLTRELSZ entry can1221
473 // be updated if we do indeed add a PLT entry.1222 // Ensure the `.plt.sec` section's node is big enough
474 try rela_plt_shndx.get(elf).ni.resized(gpa, &elf.mf);1223 const plt_sec_need_size: usize = 16 * need_plt_capacity;
475 }1224 _, const plt_sec_cur_size = elf.shndx.plt_sec.get(elf).ni.location(&elf.mf).resolve(&elf.mf);
476 },1225 if (plt_sec_cur_size < plt_sec_need_size) {
477 }1226 const new_size = plt_sec_need_size +| plt_sec_need_size / MappedFile.growth_factor;
1227 try elf.shndx.plt_sec.get(elf).ni.resize(&elf.mf, gpa, new_size);
478 }1228 }
479 },1229 },
480 }1230 }
481}1231}
1232/// Given an index into the PLT, returns whether that PLT entry is dead, meaning it may be reused at
1233/// any time and must not be targeted by relocations. See also the doc comment on `Elf.plt`.
1234fn pltEntryIsDead(elf: *Elf, plt_index: usize) bool {
1235 assert(elf.shndx.plt != .UNDEF);
1236 assert(plt_index <= elf.plt.count());
1237 // We track which PLT entries are alive based on the relocation entries, since there is a 1-1
1238 // mapping between PLT entries and `.rela.plt` entries and the relocation entries already have
1239 // a free-list mechanism.
1240 switch (elf.shdrPtr(elf.shndx.rela_plt)) {
1241 inline else => |rela_shdr, class| {
1242 const size = elf.targetLoad(&rela_shdr.size);
1243 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
1244 elf.shndx.rela_plt.get(elf).ni.slice(&elf.mf)[0..@intCast(size)],
1245 ));
1246 const rel_type = elf.targetLoad(&relas[plt_index].info).type;
1247 return rel_type == MachineRelocType.none(elf).unwrap(elf);
1248 },
1249 }
1250}
4821251
483const AddLocalSymbolOptions = struct {1252const AddLocalSymbolOptions = struct {
484 node: MappedFile.Node.Index,1253 node: MappedFile.Node.Index,
...@@ -695,6 +1464,11 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{...@@ -695,6 +1464,11 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
695 .weak => .WEAK,1464 .weak => .WEAK,
696 };1465 };
6971466
1467 const @"type": std.elf.STT = switch (opts.type) {
1468 .NOTYPE => elf.dso_globals.get(opts.name.strtab) orelse .NOTYPE,
1469 else => |t| t,
1470 };
1471
698 const sym_index: Symbol.Index = @enumFromInt(elf.symtab.items.len);1472 const sym_index: Symbol.Index = @enumFromInt(elf.symtab.items.len);
699 elf.symtab.appendAssumeCapacity(.{1473 elf.symtab.appendAssumeCapacity(.{
700 .node = opts.node,1474 .node = opts.node,
...@@ -713,7 +1487,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{...@@ -713,7 +1487,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
713 .name = @intFromEnum(opts.name.strtab),1487 .name = @intFromEnum(opts.name.strtab),
714 .value = @intCast(opts.value),1488 .value = @intCast(opts.value),
715 .size = @intCast(opts.size),1489 .size = @intCast(opts.size),
716 .info = .{ .type = opts.type, .bind = bind },1490 .info = .{ .type = @"type", .bind = bind },
717 .other = .{ .visibility = opts.visibility },1491 .other = .{ .visibility = opts.visibility },
718 .shndx = opts.shndx.toSection().?,1492 .shndx = opts.shndx.toSection().?,
719 };1493 };
...@@ -749,7 +1523,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{...@@ -749,7 +1523,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
749 .name = @intFromEnum(opts.name.dynstr),1523 .name = @intFromEnum(opts.name.dynstr),
750 .value = @intCast(opts.value),1524 .value = @intCast(opts.value),
751 .size = @intCast(opts.size),1525 .size = @intCast(opts.size),
752 .info = .{ .type = opts.type, .bind = bind },1526 .info = .{ .type = @"type", .bind = bind },
753 .other = .{ .visibility = opts.visibility },1527 .other = .{ .visibility = opts.visibility },
754 .shndx = opts.shndx.toSection().?,1528 .shndx = opts.shndx.toSection().?,
755 };1529 };
...@@ -778,12 +1552,13 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{...@@ -778,12 +1552,13 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
778 if (new_global_ptr.dynsym_index != 0 and1552 if (new_global_ptr.dynsym_index != 0 and
779 opts.visibility == .DEFAULT and1553 opts.visibility == .DEFAULT and
780 opts.shndx == .UNDEF and1554 opts.shndx == .UNDEF and
781 opts.type == .FUNC)1555 (@"type" == .FUNC or @"type" == std.elf.STT.GNU_IFUNC))
782 {1556 {
783 // We're adding an undefined global STT_FUNC symbol which could be resolved by another DSO.1557 // We're adding an undefined global STT_FUNC symbol which could be resolved by another DSO.
784 // We therefore might need a PLT entry, so let's add one now. TODO: it'd be good to remove1558 // We therefore might need a PLT entry, so let's add one now.
785 // the PLT entry if we later discover a link inpu which resolves this reference.
786 elf.addPltEntry(opts.name.strtab, new_global_ptr.dynsym_index);1559 elf.addPltEntry(opts.name.strtab, new_global_ptr.dynsym_index);
1560 // TODO: we also need to emit a PLT entry if the symbol could be preempted/interposed! By
1561 // not doing that we're basically implementing the behavior of `-Bsymbolic-functions`.
787 }1562 }
7881563
789 return .global(opts.name.strtab);1564 return .global(opts.name.strtab);
...@@ -800,6 +1575,7 @@ fn setGlobalSymbolValue(...@@ -800,6 +1575,7 @@ fn setGlobalSymbolValue(
800 shndx: Section.Index,1575 shndx: Section.Index,
801 },1576 },
802) void {1577) void {
1578 assert(new.shndx != .UNDEF);
803 const old_node = global_ptr.symtab_index.ptr(elf).node;1579 const old_node = global_ptr.symtab_index.ptr(elf).node;
804 if (old_node != .none) {1580 if (old_node != .none) {
805 if (global_ptr.next_in_node != .empty) {1581 if (global_ptr.next_in_node != .empty) {
...@@ -874,7 +1650,40 @@ fn setGlobalSymbolValue(...@@ -874,7 +1650,40 @@ fn setGlobalSymbolValue(
874 },1650 },
875 };1651 };
8761652
877 global_ptr.flushMoved(elf, new.value);1653 // If this symbol was previously undefined, it may have had a PLT entry. If so, we now need to
1654 // delete its newly-unnecessary runtime relocation to avoid a runtime dynamic linker error.
1655 // This also allows the PLT entry to be reused---see `pltEntryIsDead`.
1656 if (elf.plt.getIndex(.global(global_name))) |plt_index| {
1657 // TODO: we might still need the PLT entry if the symbol could be preempted/interposed! See
1658 // matching comment at the end of `addGlobalSymbolAssumeCapacity`.
1659 if (!elf.pltEntryIsDead(plt_index)) {
1660 elf.shndx.rela_plt.relaDeleteOne(elf, @enumFromInt(plt_index));
1661 assert(elf.pltEntryIsDead(plt_index));
1662 }
1663 }
1664
1665 // If this symbol was previously undefined, relocations targeting it may have been lowered to
1666 // runtime relocations which we have now discovered we do not need, so delete those.
1667 if (elf.shndx.dynamic != .UNDEF) {
1668 var ri = global_ptr.symtab_index.ptr(elf).first_target_reloc;
1669 while (ri != .none) {
1670 const reloc = ri.get(elf);
1671 assert(reloc.target == Symbol.Id.global(global_name));
1672 if (reloc.rela_index.unwrap()) |rela_index| {
1673 reloc.relaSection(elf).relaDeleteOne(elf, rela_index);
1674 if (elf.nodeRequiresTextrel(reloc.node)) {
1675 elf.textrel_count -= 1;
1676 }
1677 reloc.rela_index = .none;
1678 }
1679 ri = reloc.next;
1680 }
1681 }
1682
1683 // Finally, update the symbol value, re-applying target relocations. Also note that because we
1684 // possibly removed the PLT entry above, some relocations which were previously targeting the
1685 // PLT will now instead target the symbol itself.
1686 Symbol.Id.global(global_name).flushMoved(elf, new.value);
878}1687}
879/// When the same global symbol appears in two inputs---even if one symbol is defined and the other1688/// When the same global symbol appears in two inputs---even if one symbol is defined and the other
880/// undefined---their visibility values are combined to determine the resulting visibility, which1689/// undefined---their visibility values are combined to determine the resulting visibility, which
...@@ -1009,14 +1818,45 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {...@@ -1009,14 +1818,45 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {
1009 if (elf.targetEndian() != native_endian) {1818 if (elf.targetEndian() != native_endian) {
1010 std.mem.byteSwapAllFields(class.ElfN().Sym, dynsym);1819 std.mem.byteSwapAllFields(class.ElfN().Sym, dynsym);
1011 }1820 }
1821 global_ptr.dynsym_index = 0;
1012 }1822 }
1013 },1823 },
1014 }1824 }
1015}1825}
1016fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void {1826fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void {
1017 const target_endian = elf.targetEndian();1827 const target_endian = elf.targetEndian();
1018 const plt_index: u32 = @intCast(elf.got.plt.count());1828
1019 elf.got.plt.putAssumeCapacityNoClobber(.global(global_name), {});1829 // We use the existing free-list tracking of the `.rela.plt` section to also behave as a
1830 // free-list for the PLT itself---see `pltEntryIsDead` for details.
1831 const plt_index: u32 = @intFromEnum(elf.shndx.rela_plt.relaAddOneAssumeCapacity(elf, .{
1832 .type = .jumpSlot(elf),
1833 .offset = 0, // populated later
1834 .raw_sym_index = dynsym_index,
1835 .addend = 0,
1836 }));
1837
1838 // Now that we know the index, we can set the relocation's offset.
1839 const got_plt_addr = switch (elf.shdrPtr(elf.shndx.got_plt)) {
1840 inline else => |shdr, class| got_plt_addr: {
1841 const ent_size = @sizeOf(class.ElfN().Addr);
1842 assert(elf.targetLoad(&shdr.entsize) == ent_size);
1843 const offset = ent_size * @as(u64, 3 + plt_index);
1844 assert(offset <= elf.targetLoad(&shdr.size));
1845 break :got_plt_addr elf.targetLoad(&shdr.addr) + offset;
1846 },
1847 };
1848 elf.shndx.rela_plt.relaSetOffset(elf, @enumFromInt(plt_index), got_plt_addr);
1849
1850 if (plt_index < elf.plt.count()) {
1851 // We reused a free entry, so we're already done!
1852 elf.plt.setKey(plt_index, .global(global_name));
1853 return;
1854 }
1855
1856 // We added a new entry, so we now need to extend the PLT sections.
1857 assert(plt_index == elf.plt.count());
1858 elf.plt.putAssumeCapacityNoClobber(.global(global_name), {});
1859
1020 switch (elf.ehdrField(.machine)) {1860 switch (elf.ehdrField(.machine)) {
1021 else => |machine| @panic(@tagName(machine)),1861 else => |machine| @panic(@tagName(machine)),
1022 .X86_64 => {1862 .X86_64 => {
...@@ -1044,12 +1884,12 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void...@@ -1044,12 +1884,12 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void
1044 },1884 },
1045 };1885 };
10461886
1047 const got_plt_shndx = elf.shndx.got_plt;
1048 const got_plt_ni = elf.shndx.got_plt.get(elf).ni;1887 const got_plt_ni = elf.shndx.got_plt.get(elf).ni;
1049 const got_plt_addr = got_plt_addr: switch (elf.shdrPtr(got_plt_shndx)) {1888 switch (elf.shdrPtr(elf.shndx.got_plt)) {
1050 inline else => |shdr, class| {1889 inline else => |shdr, class| {
1051 const ent_size = @sizeOf(class.ElfN().Addr);1890 const ent_size = @sizeOf(class.ElfN().Addr);
1052 const old_size = ent_size * (3 + plt_index);1891 const old_size = ent_size * (3 + plt_index);
1892 assert(elf.targetLoad(&shdr.size) == old_size);
1053 elf.targetStore(&shdr.size, old_size + ent_size);1893 elf.targetStore(&shdr.size, old_size + ent_size);
1054 std.mem.writeInt(1894 std.mem.writeInt(
1055 class.ElfN().Addr,1895 class.ElfN().Addr,
...@@ -1057,9 +1897,8 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void...@@ -1057,9 +1897,8 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void
1057 @intCast(plt_addr),1897 @intCast(plt_addr),
1058 target_endian,1898 target_endian,
1059 );1899 );
1060 break :got_plt_addr elf.targetLoad(&shdr.addr) + old_size;
1061 },1900 },
1062 };1901 }
10631902
1064 const plt_sec_ni = elf.shndx.plt_sec.get(elf).ni;1903 const plt_sec_ni = elf.shndx.plt_sec.get(elf).ni;
1065 switch (elf.shdrPtr(elf.shndx.plt_sec)) {1904 switch (elf.shdrPtr(elf.shndx.plt_sec)) {
...@@ -1082,30 +1921,6 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void...@@ -1082,30 +1921,6 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void
1082 );1921 );
1083 },1922 },
1084 }1923 }
1085
1086 const rela_plt_shndx = got_plt_shndx.get(elf).rela_shndx;
1087 const rela_plt_ni = rela_plt_shndx.get(elf).ni;
1088 switch (elf.shdrPtr(rela_plt_shndx)) {
1089 inline else => |shdr, class| {
1090 const Rela = class.ElfN().Rela;
1091 const rela_size = elf.targetLoad(&shdr.entsize);
1092 const old_size = rela_size * plt_index;
1093 const new_size = old_size + rela_size;
1094 elf.targetStore(&shdr.size, new_size);
1095 const rela: *Rela = @ptrCast(@alignCast(
1096 rela_plt_ni.slice(&elf.mf)[@intCast(old_size)..@intCast(new_size)],
1097 ));
1098 rela.* = .{
1099 .offset = @intCast(got_plt_addr),
1100 .info = .{
1101 .type = @intFromEnum(std.elf.R_X86_64.JUMP_SLOT),
1102 .sym = @intCast(dynsym_index),
1103 },
1104 .addend = 0,
1105 };
1106 if (target_endian != native_endian) std.mem.byteSwapAllFields(Rela, rela);
1107 },
1108 }
1109 },1924 },
1110 }1925 }
1111}1926}
...@@ -1113,13 +1928,13 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void...@@ -1113,13 +1928,13 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void
1113const Symbol = struct {1928const Symbol = struct {
1114 /// The node which this symbol's value is defined relative to. Possible values are:1929 /// The node which this symbol's value is defined relative to. Possible values are:
1115 /// * `.none` for a SHN_ABS or SHN_UNDEF symbol1930 /// * `.none` for a SHN_ABS or SHN_UNDEF symbol
1116 /// * A section (the symbol's value is that section's vaddr)1931 /// * A section (the symbol's value is some vaddr in that section)
1117 /// * An input section (the symbol's value is some vaddr in that input section)1932 /// * An input section (the symbol's value is some vaddr in that input section)
1118 /// * A NAV, UAV, or lazy code/data (the symbol's value is exactly the vaddr of that node)1933 /// * A NAV, UAV, or lazy code/data (the symbol's value is exactly the vaddr of that node)
1119 node: MappedFile.Node.Index,1934 node: MappedFile.Node.Index,
11201935
1121 /// The head of a linked list of relocations targeting this symbol.1936 /// The head of a linked list of relocations targeting this symbol.
1122 first_target_reloc: Reloc.Index,1937 first_target_reloc: SymbolReloc.Index,
11231938
1124 const Global = struct {1939 const Global = struct {
1125 /// The current index of the symtab entry for this global symbol.1940 /// The current index of the symtab entry for this global symbol.
...@@ -1136,16 +1951,6 @@ const Symbol = struct {...@@ -1136,16 +1951,6 @@ const Symbol = struct {
1136 ///1951 ///
1137 /// If `node` is `.none`, this is `.empty`.1952 /// If `node` is `.none`, this is `.empty`.
1138 prev_in_node: String(.strtab),1953 prev_in_node: String(.strtab),
1139
1140 /// Like `Symbol.Index.flushMoved`, but also updates the dynamic symbol table if necessary.
1141 fn flushMoved(g: *const Global, elf: *Elf, value: u64) void {
1142 g.symtab_index.flushMoved(elf, value);
1143 if (g.dynsym_index != 0) {
1144 switch (elf.dynsymPtr(g.dynsym_index)) {
1145 inline else => |sym| elf.targetStore(&sym.value, @intCast(value)),
1146 }
1147 }
1148 }
1149 };1954 };
11501955
1151 /// An index directly into the symtab. These values are not stable (global symbols are sometimes1956 /// An index directly into the symtab. These values are not stable (global symbols are sometimes
...@@ -1158,23 +1963,20 @@ const Symbol = struct {...@@ -1158,23 +1963,20 @@ const Symbol = struct {
1158 null = 0,1963 null = 0,
1159 _,1964 _,
11601965
1161 fn flushMoved(si: Symbol.Index, elf: *Elf, value: u64) void {
1162 switch (elf.symPtr(si)) {
1163 inline else => |sym| elf.targetStore(&sym.value, @intCast(value)),
1164 }
1165 if (elf.ehdrField(.type) != .REL) {
1166 var ri = si.ptr(elf).first_target_reloc;
1167 while (ri != .none) {
1168 const reloc = ri.get(elf);
1169 assert(reloc.target.index(elf) == si);
1170 reloc.apply(elf);
1171 ri = reloc.next;
1172 }
1173 }
1174 }
1175 fn ptr(si: Symbol.Index, elf: *Elf) *Symbol {1966 fn ptr(si: Symbol.Index, elf: *Elf) *Symbol {
1176 return &elf.symtab.items[@intFromEnum(si)];1967 return &elf.symtab.items[@intFromEnum(si)];
1177 }1968 }
1969
1970 fn applyTargetRelocs(si: Symbol.Index, elf: *Elf) void {
1971 assert(elf.ehdrField(.type) != .REL);
1972 var ri = si.ptr(elf).first_target_reloc;
1973 while (ri != .none) {
1974 const reloc = ri.get(elf);
1975 assert(reloc.target.index(elf) == si);
1976 reloc.apply(elf);
1977 ri = reloc.next;
1978 }
1979 }
1178 };1980 };
11791981
1180 /// A `LocalIndex` is a raw index into the symtab like `Index`, but it guarantees that the1982 /// A `LocalIndex` is a raw index into the symtab like `Index`, but it guarantees that the
...@@ -1239,6 +2041,44 @@ const Symbol = struct {...@@ -1239,6 +2041,44 @@ const Symbol = struct {
1239 };2041 };
1240 }2042 }
12412043
2044 fn flushMoved(sym_id: Symbol.Id, elf: *Elf, new_value: u64) void {
2045 // Update the symbol value in `.symtab`
2046 const sym_index = sym_id.index(elf);
2047 switch (elf.symPtr(sym_index)) {
2048 inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)),
2049 }
2050
2051 // Update the symbol value in `.dynsym` if applicable
2052 switch (sym_id.unwrap()) {
2053 .local => {},
2054 .global => |name| {
2055 const g = elf.globalByName(name).?;
2056 if (g.dynsym_index != 0) {
2057 switch (elf.dynsymPtr(g.dynsym_index)) {
2058 inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)),
2059 }
2060 }
2061 },
2062 }
2063
2064 // Re-apply relocations targeting this symbol
2065 if (elf.ehdrField(.type) != .REL) {
2066 sym_index.applyTargetRelocs(elf);
2067 }
2068
2069 // Update GOT entries targeting this symbol
2070 if (elf.got.getIndex(.{ .symbol = sym_id })) |got_index| {
2071 elf.updateGotEntry(got_index);
2072 }
2073 if (elf.got.getIndex(.{ .tpoff = sym_id })) |got_index| {
2074 elf.updateGotEntry(got_index);
2075 }
2076 if (elf.got.getIndex(.{ .tlsgd0 = sym_id })) |got_index| {
2077 elf.updateGotEntry(got_index);
2078 elf.updateGotEntry(got_index + 1); // tlsgd1
2079 }
2080 }
2081
1242 /// Returns `true` if the target of `s` has moved, meaning the symbol's value will change at2082 /// Returns `true` if the target of `s` has moved, meaning the symbol's value will change at
1243 /// some point due to a call to `flushMoved`.2083 /// some point due to a call to `flushMoved`.
1244 fn hasMoved(s: Symbol.Id, elf: *Elf) bool {2084 fn hasMoved(s: Symbol.Id, elf: *Elf) bool {
...@@ -1305,7 +2145,8 @@ pub fn lazySymbol(elf: *Elf, lazy: link.File.LazySymbol) !link.File.SymbolId {...@@ -1305,7 +2145,8 @@ pub fn lazySymbol(elf: *Elf, lazy: link.File.LazySymbol) !link.File.SymbolId {
1305 .type = sym_type,2145 .type = sym_type,
1306 .shndx = shndx,2146 .shndx = shndx,
1307 }),2147 }),
1308 .first_reloc = .none,2148 .first_symbol_reloc = .none,
2149 .first_got_reloc = .none,
1309 };2150 };
1310 elf.nodes.appendAssumeCapacity(switch (lazy.kind) {2151 elf.nodes.appendAssumeCapacity(switch (lazy.kind) {
1311 .code => .{ .lazy_code = @enumFromInt(gop.index) },2152 .code => .{ .lazy_code = @enumFromInt(gop.index) },
...@@ -1354,7 +2195,7 @@ pub fn addReloc(...@@ -1354,7 +2195,7 @@ pub fn addReloc(
1354 offset: u64,2195 offset: u64,
1355 target: link.File.SymbolId,2196 target: link.File.SymbolId,
1356 addend: i64,2197 addend: i64,
1357 @"type": Reloc.Type,2198 @"type": MachineRelocType,
1358) !void {2199) !void {
1359 const node: MappedFile.Node.Index = Node.fromAtom(atom);2200 const node: MappedFile.Node.Index = Node.fromAtom(atom);
1360 try elf.ensureUnusedRelocCapacity(node, 1);2201 try elf.ensureUnusedRelocCapacity(node, 1);
...@@ -1368,7 +2209,7 @@ pub fn navSymbol(elf: *Elf, nav_index: InternPool.Nav.Index) !link.File.SymbolId...@@ -1368,7 +2209,7 @@ pub fn navSymbol(elf: *Elf, nav_index: InternPool.Nav.Index) !link.File.SymbolId
1368 return elf.externSymbol(.{2209 return elf.externSymbol(.{
1369 .name = @"extern".name.toSlice(ip),2210 .name = @"extern".name.toSlice(ip),
1370 .lib_name = @"extern".lib_name.toSlice(ip),2211 .lib_name = @"extern".lib_name.toSlice(ip),
1371 .type = navType(ip, nav.resolved.?, elf.base.comp.config.any_non_single_threaded),2212 .type = elf.navType(nav.resolved.?),
1372 .linkage = @"extern".linkage,2213 .linkage = @"extern".linkage,
1373 .visibility = @"extern".visibility,2214 .visibility = @"extern".visibility,
1374 });2215 });
...@@ -1507,310 +2348,44 @@ const StringTable = struct {...@@ -1507,310 +2348,44 @@ const StringTable = struct {
1507 key,2348 key,
1508 StringTable.Adapter{ .slice = slice_const },2349 StringTable.Adapter{ .slice = slice_const },
1509 .{ .slice = slice_const },2350 .{ .slice = slice_const },
1510 );2351 );
1511 if (gop.found_existing) return gop.key_ptr.*;2352 if (gop.found_existing) return gop.key_ptr.*;
1512 try ni.resized(gpa, &elf.mf);2353 const old_size, const new_size = size: switch (elf.shdrPtr(shndx)) {
1513 const old_size, const new_size = size: switch (elf.shdrPtr(shndx)) {2354 inline else => |shdr| {
1514 inline else => |shdr| {2355 const old_size: u32 = @intCast(elf.targetLoad(&shdr.size));
1515 const old_size: u32 = @intCast(elf.targetLoad(&shdr.size));2356 const new_size: u32 = @intCast(old_size + key.len + 1);
1516 const new_size: u32 = @intCast(old_size + key.len + 1);2357 elf.targetStore(&shdr.size, new_size);
1517 elf.targetStore(&shdr.size, new_size);2358 break :size .{ old_size, new_size };
1518 break :size .{ old_size, new_size };
1519 },
1520 };
1521 _, const node_size = ni.location(&elf.mf).resolve(&elf.mf);
1522 if (new_size > node_size)
1523 try ni.resize(&elf.mf, gpa, new_size +| new_size / MappedFile.growth_factor);
1524 const slice = ni.slice(&elf.mf)[old_size..];
1525 @memcpy(slice[0..key.len], key);
1526 slice[key.len] = 0;
1527 gop.key_ptr.* = old_size;
1528 return old_size;
1529 }
1530};
1531
1532const GotIndex = enum(u32) {
1533 none = std.math.maxInt(u32),
1534 _,
1535
1536 pub fn wrap(i: ?u32) GotIndex {
1537 const gi: GotIndex = @enumFromInt(i orelse return .none);
1538 assert(gi != .none);
1539 return gi;
1540 }
1541 pub fn unwrap(gi: GotIndex) ?u32 {
1542 return switch (gi) {
1543 _ => @intFromEnum(gi),
1544 .none => null,
1545 };
1546 }
1547};
1548
1549const Reloc = extern struct {
1550 type: Reloc.Type,
1551 prev: Reloc.Index,
1552 next: Reloc.Index,
1553 node: MappedFile.Node.Index,
1554 target: Symbol.Id,
1555 index: Section.RelIndex,
1556 offset: u64,
1557 addend: i64,
1558
1559 pub const Type = extern union {
1560 X86_64: std.elf.R_X86_64,
1561 AARCH64: std.elf.R_AARCH64,
1562 RISCV: std.elf.R_RISCV,
1563 PPC64: std.elf.R_PPC64,
1564
1565 pub fn none(elf: *Elf) Reloc.Type {
1566 return switch (elf.ehdrField(.machine)) {
1567 else => unreachable,
1568 .AARCH64 => .{ .AARCH64 = .NONE },
1569 .PPC64 => .{ .PPC64 = .NONE },
1570 .RISCV => .{ .RISCV = .NONE },
1571 .X86_64 => .{ .X86_64 = .NONE },
1572 };
1573 }
1574 pub fn absAddr(elf: *Elf) Reloc.Type {
1575 return switch (elf.ehdrField(.machine)) {
1576 else => unreachable,
1577 .AARCH64 => .{ .AARCH64 = .ABS64 },
1578 .PPC64 => .{ .PPC64 = .ADDR64 },
1579 .RISCV => .{ .RISCV = .@"64" },
1580 .X86_64 => .{ .X86_64 = .@"64" },
1581 };
1582 }
1583 pub fn sizeAddr(elf: *Elf) Reloc.Type {
1584 return switch (elf.ehdrField(.machine)) {
1585 else => unreachable,
1586 .X86_64 => .{ .X86_64 = .SIZE64 },
1587 };
1588 }
1589
1590 pub fn wrap(int: u32, elf: *Elf) Reloc.Type {
1591 return switch (elf.ehdrField(.machine)) {
1592 else => unreachable,
1593 inline .AARCH64,
1594 .PPC64,
1595 .RISCV,
1596 .X86_64,
1597 => |machine| @unionInit(Reloc.Type, @tagName(machine), @enumFromInt(int)),
1598 };
1599 }
1600 pub fn unwrap(rt: Reloc.Type, elf: *Elf) u32 {
1601 return switch (elf.ehdrField(.machine)) {
1602 else => unreachable,
1603 inline .AARCH64,
1604 .PPC64,
1605 .RISCV,
1606 .X86_64,
1607 => |machine| @intFromEnum(@field(rt, @tagName(machine))),
1608 };
1609 }
1610 };
1611
1612 pub const Index = enum(u32) {
1613 none = std.math.maxInt(u32),
1614 _,
1615
1616 pub fn get(si: Reloc.Index, elf: *Elf) *Reloc {
1617 return &elf.relocs.items[@intFromEnum(si)];
1618 }
1619 };
1620
1621 pub fn apply(reloc: *const Reloc, elf: *Elf) void {
1622 assert(elf.ehdrField(.type) != .REL);
1623 assert(reloc.node != .none);
1624 if (reloc.node.hasMoved(&elf.mf) or reloc.target.hasMoved(elf)) {
1625 // There's no point applying the relocation now, because it will be re-applied by
1626 // `flushMoved` at some point anyway.
1627 return;
1628 }
1629 const node_vaddr: u64 = switch (elf.getNode(reloc.node)) {
1630 .file => unreachable,
1631 .ehdr => unreachable,
1632 .shdr => unreachable,
1633 .segment => unreachable,
1634 .section => |shndx| shndx.vaddr(elf),
1635 .input_section => |isi| isi.ptrConst(elf).vaddr,
1636 inline .nav,
1637 .uav,
1638 .lazy_code,
1639 .lazy_const_data,
1640 => |i| Symbol.Id.local(i.symbol(elf)).value(elf),
1641 };
1642 const dest_vaddr = node_vaddr + reloc.offset;
1643 const dest_slice = reloc.node.slice(&elf.mf)[@intCast(reloc.offset)..];
1644 const target_endian = elf.targetEndian();
1645 switch (elf.symPtr(reloc.target.index(elf))) {
1646 inline else => |target_sym, class| {
1647 const target_value = elf.targetLoad(&target_sym.value) +% @as(u64, @bitCast(reloc.addend));
1648 switch (elf.ehdrField(.machine)) {
1649 else => |machine| @panic(@tagName(machine)),
1650 .X86_64 => switch (reloc.type.X86_64) {
1651 else => |kind| @panic(@tagName(kind)),
1652 .@"64" => std.mem.writeInt(
1653 u64,
1654 dest_slice[0..8],
1655 target_value,
1656 target_endian,
1657 ),
1658 .PC32 => std.mem.writeInt(
1659 i32,
1660 dest_slice[0..4],
1661 @intCast(@as(i64, @bitCast(target_value -% dest_vaddr))),
1662 target_endian,
1663 ),
1664 .PLT32 => std.mem.writeInt(
1665 i32,
1666 dest_slice[0..4],
1667 @intCast(@as(i64, @bitCast(if (elf.got.plt.getIndex(reloc.target)) |plt_index|
1668 elf.targetLoad(&@field(
1669 elf.shdrPtr(elf.shndx.plt_sec),
1670 @tagName(class),
1671 ).addr) +% 16 * plt_index +%
1672 @as(u64, @bitCast(reloc.addend)) -% dest_vaddr
1673 else
1674 target_value -% dest_vaddr))),
1675 target_endian,
1676 ),
1677 .@"32" => std.mem.writeInt(
1678 u32,
1679 dest_slice[0..4],
1680 @intCast(target_value),
1681 target_endian,
1682 ),
1683 .@"32S" => std.mem.writeInt(
1684 i32,
1685 dest_slice[0..4],
1686 @intCast(@as(i64, @bitCast(target_value))),
1687 target_endian,
1688 ),
1689 .TLSLD => std.mem.writeInt(
1690 i32,
1691 dest_slice[0..4],
1692 @intCast(@as(i64, @bitCast(
1693 elf.shndx.got.vaddr(elf) +%
1694 @as(u64, @bitCast(reloc.addend)) +%
1695 @as(u64, 8) * elf.got.tlsld.unwrap().? -%
1696 dest_vaddr,
1697 ))),
1698 target_endian,
1699 ),
1700 .DTPOFF32 => std.mem.writeInt(
1701 i32,
1702 dest_slice[0..4],
1703 @intCast(@as(i64, @bitCast(target_value))),
1704 target_endian,
1705 ),
1706 .TPOFF32 => {
1707 const phdr = @field(elf.phdrSlice(), @tagName(class));
1708 const ph = &phdr[elf.getNode(elf.ni.tls).segment];
1709 assert(elf.targetLoad(&ph.type) == .TLS);
1710 std.mem.writeInt(
1711 i32,
1712 dest_slice[0..4],
1713 @intCast(@as(i64, @bitCast(target_value -% elf.targetLoad(&ph.memsz)))),
1714 target_endian,
1715 );
1716 },
1717 .SIZE32 => std.mem.writeInt(
1718 u32,
1719 dest_slice[0..4],
1720 @intCast(
1721 elf.targetLoad(&target_sym.size) +% @as(u64, @bitCast(reloc.addend)),
1722 ),
1723 target_endian,
1724 ),
1725 .SIZE64 => std.mem.writeInt(
1726 u64,
1727 dest_slice[0..8],
1728 elf.targetLoad(&target_sym.size) +% @as(u64, @bitCast(reloc.addend)),
1729 target_endian,
1730 ),
1731 },
1732 }
1733 },
1734 }
1735 }
1736
1737 pub fn delete(reloc: *Reloc, elf: *Elf) void {
1738 switch (reloc.prev) {
1739 .none => {
1740 const target_ptr = reloc.target.index(elf).ptr(elf);
1741 assert(target_ptr.first_target_reloc.get(elf) == reloc);
1742 target_ptr.first_target_reloc = reloc.next;
1743 },
1744 else => |prev| prev.get(elf).next = reloc.next,
1745 }
1746 switch (reloc.next) {
1747 .none => {},
1748 else => |next| next.get(elf).prev = reloc.prev,
1749 }
1750 switch (elf.ehdrField(.type)) {
1751 .NONE, .CORE, _ => unreachable,
1752 .REL => {
1753 const sh = elf.getNodeShndx(reloc.node).get(elf);
1754 switch (elf.shdrPtr(sh.rela_shndx)) {
1755 inline else => |shdr, class| {
1756 const Rela = class.ElfN().Rela;
1757 const ent_size = elf.targetLoad(&shdr.entsize);
1758 const start = ent_size * reloc.index.unwrap().?;
1759 const rela_slice = sh.rela_shndx.get(elf).ni.slice(&elf.mf);
1760 const rela: *Rela = @ptrCast(@alignCast(
1761 rela_slice[@intCast(start)..][0..@intCast(ent_size)],
1762 ));
1763 rela.* = .{
1764 .offset = @intFromEnum(sh.rela_free),
1765 .info = .{
1766 .type = @intCast(Reloc.Type.none(elf).unwrap(elf)),
1767 .sym = 0,
1768 },
1769 .addend = 0,
1770 };
1771 },
1772 }
1773 sh.rela_free = reloc.index;
1774 },2359 },
1775 .EXEC, .DYN => assert(reloc.index == .none),2360 };
2361 if (shndx == elf.shndx.dynstr) {
2362 elf.updateDynamicEntry(std.elf.DT_STRSZ, new_size);
1776 }2363 }
1777 reloc.* = undefined;2364 _, const node_size = ni.location(&elf.mf).resolve(&elf.mf);
2365 if (new_size > node_size)
2366 try ni.resize(&elf.mf, gpa, new_size +| new_size / MappedFile.growth_factor);
2367 const slice = ni.slice(&elf.mf)[old_size..];
2368 @memcpy(slice[0..key.len], key);
2369 slice[key.len] = 0;
2370 gop.key_ptr.* = old_size;
2371 return old_size;
1778 }2372 }
2373};
17792374
1780 fn updateTargetIndex(reloc: *const Reloc, elf: *Elf) void {2375const GotIndex = enum(u32) {
1781 assert(elf.ehdrField(.type) == .REL);2376 none = std.math.maxInt(u32),
1782 const sh = elf.getNodeShndx(reloc.node).get(elf);2377 _,
1783 switch (elf.shdrPtr(sh.rela_shndx)) {
1784 inline else => |shdr, class| {
1785 assert(elf.targetLoad(&shdr.entsize) == @sizeOf(class.ElfN().Rela));
1786 const size = elf.targetLoad(&shdr.size);
1787 const raw_rela_slice = sh.rela_shndx.get(elf).ni.slice(&elf.mf);
1788 const rela_slice: []class.ElfN().Rela = @ptrCast(@alignCast(raw_rela_slice[0..@intCast(size)]));
1789 elf.targetStore(&rela_slice[reloc.index.unwrap().?].info, .{
1790 .type = @intCast(reloc.type.unwrap(elf)),
1791 .sym = @intCast(@intFromEnum(reloc.target.index(elf))),
1792 });
1793 },
1794 }
1795 }
17962378
1797 fn updateNodeOffset(reloc: *const Reloc, elf: *Elf, node_offset: u64) void {2379 pub fn wrap(i: ?u32) GotIndex {
1798 assert(elf.ehdrField(.type) == .REL);2380 const gi: GotIndex = @enumFromInt(i orelse return .none);
1799 const total_offset = node_offset + reloc.offset;2381 assert(gi != .none);
1800 const sh = elf.getNodeShndx(reloc.node).get(elf);2382 return gi;
1801 switch (elf.shdrPtr(sh.rela_shndx)) {
1802 inline else => |shdr, class| {
1803 assert(elf.targetLoad(&shdr.entsize) == @sizeOf(class.ElfN().Rela));
1804 const size = elf.targetLoad(&shdr.size);
1805 const raw_rela_slice = sh.rela_shndx.get(elf).ni.slice(&elf.mf);
1806 const rela_slice: []class.ElfN().Rela = @ptrCast(@alignCast(raw_rela_slice[0..@intCast(size)]));
1807 elf.targetStore(&rela_slice[reloc.index.unwrap().?].offset, @intCast(total_offset));
1808 },
1809 }
1810 }2383 }
18112384 pub fn unwrap(gi: GotIndex) ?u32 {
1812 comptime {2385 return switch (gi) {
1813 if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Reloc) == 40);2386 _ => @intFromEnum(gi),
2387 .none => null,
2388 };
1814 }2389 }
1815};2390};
18162391
...@@ -1918,6 +2493,11 @@ fn create(...@@ -1918,6 +2493,11 @@ fn create(
1918 .dynstr = .UNDEF,2493 .dynstr = .UNDEF,
1919 .dynamic = .UNDEF,2494 .dynamic = .UNDEF,
1920 .tdata = .UNDEF,2495 .tdata = .UNDEF,
2496 .rela_dyn = .UNDEF,
2497 .rela_plt = .UNDEF,
2498 .init_array = .UNDEF,
2499 .fini_array = .UNDEF,
2500 .preinit_array = .UNDEF,
1921 },2501 },
1922 .symtab = .empty,2502 .symtab = .empty,
1923 .globals = .{2503 .globals = .{
...@@ -1927,16 +2507,14 @@ fn create(...@@ -1927,16 +2507,14 @@ fn create(
1927 .weak_undef = .empty,2507 .weak_undef = .empty,
1928 },2508 },
1929 .node_global_symbols = .empty,2509 .node_global_symbols = .empty,
2510 .dso_globals = .empty,
1930 .shstrtab = .{ .map = .empty },2511 .shstrtab = .{ .map = .empty },
1931 .strtab = .{ .map = .empty },2512 .strtab = .{ .map = .empty },
1932 .dynstr = .{ .map = .empty },2513 .dynstr = .{ .map = .empty },
1933 .got = .{2514 .got = .empty,
1934 .len = 0,2515 .plt = .empty,
1935 .tlsld = .none,2516 .plt_first_symbol_reloc = .none,
1936 .plt = .empty,2517 .dynamic_first_symbol_reloc = .none,
1937 },
1938 .first_plt_reloc = .none,
1939 .first_dynamic_reloc = .none,
1940 .needed = .empty,2518 .needed = .empty,
1941 .inputs = .empty,2519 .inputs = .empty,
1942 .input_sections = .empty,2520 .input_sections = .empty,
...@@ -1948,11 +2526,15 @@ fn create(...@@ -1948,11 +2526,15 @@ fn create(
1948 .pending_index = 0,2526 .pending_index = 0,
1949 }),2527 }),
1950 .pending_uavs = .empty,2528 .pending_uavs = .empty,
1951 .relocs = .empty,2529 .symbol_relocs = .empty,
2530 .got_relocs = .empty,
2531 .tls_size_symbol_relocs = .empty,
2532 .section_by_name = .empty,
1952 .changed_symtab_index = .empty,2533 .changed_symtab_index = .empty,
1953 .const_prog_node = .none,2534 .const_prog_node = .none,
1954 .synth_prog_node = .none,2535 .synth_prog_node = .none,
1955 .input_prog_node = .none,2536 .input_prog_node = .none,
2537 .textrel_count = 0,
1956 };2538 };
1957 errdefer elf.deinit();2539 errdefer elf.deinit();
19582540
...@@ -1972,10 +2554,12 @@ pub fn deinit(elf: *Elf) void {...@@ -1972,10 +2554,12 @@ pub fn deinit(elf: *Elf) void {
1972 elf.globals.strong_undef.deinit(gpa);2554 elf.globals.strong_undef.deinit(gpa);
1973 elf.globals.weak_undef.deinit(gpa);2555 elf.globals.weak_undef.deinit(gpa);
1974 elf.node_global_symbols.deinit(gpa);2556 elf.node_global_symbols.deinit(gpa);
2557 elf.dso_globals.deinit(gpa);
1975 elf.shstrtab.map.deinit(gpa);2558 elf.shstrtab.map.deinit(gpa);
1976 elf.strtab.map.deinit(gpa);2559 elf.strtab.map.deinit(gpa);
1977 elf.dynstr.map.deinit(gpa);2560 elf.dynstr.map.deinit(gpa);
1978 elf.got.plt.deinit(gpa);2561 elf.got.deinit(gpa);
2562 elf.plt.deinit(gpa);
1979 elf.needed.deinit(gpa);2563 elf.needed.deinit(gpa);
1980 for (elf.inputs.items) |input| if (input.member) |m| gpa.free(m);2564 for (elf.inputs.items) |input| if (input.member) |m| gpa.free(m);
1981 elf.inputs.deinit(gpa);2565 elf.inputs.deinit(gpa);
...@@ -1984,7 +2568,10 @@ pub fn deinit(elf: *Elf) void {...@@ -1984,7 +2568,10 @@ pub fn deinit(elf: *Elf) void {
1984 elf.uavs.deinit(gpa);2568 elf.uavs.deinit(gpa);
1985 for (&elf.lazy.values) |*lazy| lazy.map.deinit(gpa);2569 for (&elf.lazy.values) |*lazy| lazy.map.deinit(gpa);
1986 elf.pending_uavs.deinit(gpa);2570 elf.pending_uavs.deinit(gpa);
1987 elf.relocs.deinit(gpa);2571 elf.symbol_relocs.deinit(gpa);
2572 elf.got_relocs.deinit(gpa);
2573 elf.tls_size_symbol_relocs.deinit(gpa);
2574 elf.section_by_name.deinit(gpa);
1988 elf.changed_symtab_index.deinit(gpa);2575 elf.changed_symtab_index.deinit(gpa);
1989 elf.* = undefined;2576 elf.* = undefined;
1990}2577}
...@@ -2293,7 +2880,7 @@ fn initHeaders(...@@ -2293,7 +2880,7 @@ fn initHeaders(
2293 .entsize = 0,2880 .entsize = 0,
2294 };2881 };
2295 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Shdr, sh_undef);2882 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Shdr, sh_undef);
2296 elf.shdrs.appendAssumeCapacity(.{ .lsi = .null, .ni = .none, .rela_shndx = .UNDEF, .rela_free = .none });2883 elf.shdrs.appendAssumeCapacity(.{ .lsi = .null, .ni = .none, .rela = .{ .shndx = .UNDEF } });
22972884
2298 elf.symtab.addOneAssumeCapacity().* = .{2885 elf.symtab.addOneAssumeCapacity().* = .{
2299 .node = .none,2886 .node = .none,
...@@ -2368,8 +2955,15 @@ fn initHeaders(...@@ -2368,8 +2955,15 @@ fn initHeaders(
2368 if (@"type" != .REL) {2955 if (@"type" != .REL) {
2369 elf.shndx.got = try elf.addSection(elf.ni.data_rel_ro, .{2956 elf.shndx.got = try elf.addSection(elf.ni.data_rel_ro, .{
2370 .name = ".got",2957 .name = ".got",
2958 .type = .PROGBITS,
2959 // Reserve space for the reserved words, populated later.
2960 .size = switch (machine) {
2961 else => @panic(@tagName(machine)),
2962 .X86_64 => 3 * 8,
2963 },
2371 .flags = .{ .WRITE = true, .ALLOC = true },2964 .flags = .{ .WRITE = true, .ALLOC = true },
2372 .addralign = addr_align,2965 .addralign = addr_align,
2966 .entsize = @intCast(addr_align.toByteUnits()),
2373 });2967 });
2374 elf.shndx.got_plt = try elf.addSection(2968 elf.shndx.got_plt = try elf.addSection(
2375 if (elf.options.z_now) elf.ni.data_rel_ro else elf.ni.data,2969 if (elf.options.z_now) elf.ni.data_rel_ro else elf.ni.data,
...@@ -2383,6 +2977,7 @@ fn initHeaders(...@@ -2383,6 +2977,7 @@ fn initHeaders(
2383 .X86_64 => 3 * 8,2977 .X86_64 => 3 * 8,
2384 },2978 },
2385 .addralign = addr_align,2979 .addralign = addr_align,
2980 .entsize = @intCast(addr_align.toByteUnits()),
2386 },2981 },
2387 );2982 );
2388 const plt_size: std.elf.Xword, const plt_align: std.mem.Alignment, const plt_sec =2983 const plt_size: std.elf.Xword, const plt_align: std.mem.Alignment, const plt_sec =
...@@ -2478,7 +3073,7 @@ fn initHeaders(...@@ -2478,7 +3073,7 @@ fn initHeaders(
2478 .NONE, _ => unreachable,3073 .NONE, _ => unreachable,
2479 inline else => |ct_class| @sizeOf(ct_class.ElfN().Rela),3074 inline else => |ct_class| @sizeOf(ct_class.ElfN().Rela),
2480 };3075 };
2481 elf.shndx.got.get(elf).rela_shndx = try elf.addSection(elf.ni.rodata, .{3076 elf.shndx.rela_dyn = try elf.addSection(elf.ni.rodata, .{
2482 .name = ".rela.dyn",3077 .name = ".rela.dyn",
2483 .type = .RELA,3078 .type = .RELA,
2484 .flags = .{ .ALLOC = true },3079 .flags = .{ .ALLOC = true },
...@@ -2487,13 +3082,12 @@ fn initHeaders(...@@ -2487,13 +3082,12 @@ fn initHeaders(
2487 .entsize = rela_size,3082 .entsize = rela_size,
2488 .node_align = elf.mf.flags.block_size,3083 .node_align = elf.mf.flags.block_size,
2489 });3084 });
2490 const got_plt_shndx = elf.shndx.got_plt;3085 elf.shndx.rela_plt = try elf.addSection(elf.ni.rodata, .{
2491 got_plt_shndx.get(elf).rela_shndx = try elf.addSection(elf.ni.rodata, .{
2492 .name = ".rela.plt",3086 .name = ".rela.plt",
2493 .type = .RELA,3087 .type = .RELA,
2494 .flags = .{ .ALLOC = true, .INFO_LINK = true },3088 .flags = .{ .ALLOC = true, .INFO_LINK = true },
2495 .link = elf.shndx.dynsym.toSection().?,3089 .link = elf.shndx.dynsym.toSection().?,
2496 .info = got_plt_shndx.toSection().?,3090 .info = elf.shndx.got_plt.toSection().?,
2497 .addralign = addr_align,3091 .addralign = addr_align,
2498 .entsize = rela_size,3092 .entsize = rela_size,
2499 .node_align = elf.mf.flags.block_size,3093 .node_align = elf.mf.flags.block_size,
...@@ -2516,7 +3110,7 @@ fn initHeaders(...@@ -2516,7 +3110,7 @@ fn initHeaders(
2516 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *0x0(%rip)3110 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *0x0(%rip)
2517 0x0f, 0x1f, 0x40, 0x00, // nopl 0x0(%rax)3111 0x0f, 0x1f, 0x40, 0x00, // nopl 0x0(%rax)
2518 });3112 });
2519 elf.first_plt_reloc = @enumFromInt(elf.relocs.items.len);3113 elf.plt_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len);
2520 try elf.ensureUnusedRelocCapacity(plt_ni, 2);3114 try elf.ensureUnusedRelocCapacity(plt_ni, 2);
2521 elf.addRelocAssumeCapacity(3115 elf.addRelocAssumeCapacity(
2522 plt_ni,3116 plt_ni,
...@@ -2544,6 +3138,83 @@ fn initHeaders(...@@ -2544,6 +3138,83 @@ fn initHeaders(
2544 elf.nodes.appendAssumeCapacity(.{ .segment = tls_phndx });3138 elf.nodes.appendAssumeCapacity(.{ .segment = tls_phndx });
2545 elf.phdrs.items[tls_phndx] = elf.ni.tls;3139 elf.phdrs.items[tls_phndx] = elf.ni.tls;
2546 }3140 }
3141
3142 // Populate reserved GOT words.
3143 switch (machine) {
3144 else => @panic(@tagName(machine)),
3145 .X86_64 => {
3146 try elf.got.ensureUnusedCapacity(gpa, 3);
3147 elf.got.putAssumeCapacityNoClobber(switch (have_dynamic_section) {
3148 true => .{ .symbol = .local(elf.shndx.dynamic.get(elf).lsi) },
3149 false => .{ .reserved = 0 },
3150 }, .none);
3151 elf.got.putAssumeCapacityNoClobber(.{ .reserved = 1 }, .none);
3152 elf.got.putAssumeCapacityNoClobber(.{ .reserved = 2 }, .none);
3153 },
3154 }
3155 switch (elf.shdrPtr(elf.shndx.got)) {
3156 inline else => |shdr, ct_class| {
3157 const Addr = ct_class.ElfN().Addr;
3158 assert(elf.targetLoad(&shdr.size) == elf.got.count() * @sizeOf(Addr));
3159 },
3160 }
3161
3162 // Create any always-provided linker-defined symbols. The symbols marking the `INIT_ARRAY`/
3163 // `FINI_ARRAY`/`PREINIT_ARRAY` sections are instead created by `createInitFiniArraySection`
3164 // when needed (it seems to be legal to leave those undefined if the section doesn't exist).
3165
3166 try elf.ensureUnusedSymbolCapacity(4, .maybe_global);
3167 // Despite the name, `__dso_handle` is necessary even in static binaries.
3168 _ = elf.addGlobalSymbolAssumeCapacity(.{
3169 .node = Section.Index.text.get(elf).ni,
3170 .name = try .string(elf, "__dso_handle"),
3171 .value = Section.Index.text.vaddr(elf),
3172 .size = 0,
3173 .type = .NOTYPE,
3174 .bind = .strong,
3175 .visibility = .HIDDEN,
3176 .shndx = .text,
3177 }) catch |err| switch (err) {
3178 error.MultipleDefinitions => unreachable, // no inputs are processed yet
3179 };
3180 _ = elf.addGlobalSymbolAssumeCapacity(.{
3181 .node = elf.shndx.plt.get(elf).ni,
3182 .name = try .string(elf, "_PROCEDURE_LINKAGE_TABLE_"),
3183 .value = elf.shndx.plt.vaddr(elf),
3184 .size = 0,
3185 .type = .NOTYPE,
3186 .bind = .strong,
3187 .visibility = .HIDDEN,
3188 .shndx = elf.shndx.plt,
3189 }) catch |err| switch (err) {
3190 error.MultipleDefinitions => unreachable, // no inputs are processed yet
3191 };
3192 _ = elf.addGlobalSymbolAssumeCapacity(.{
3193 .node = elf.shndx.got.get(elf).ni,
3194 .name = try .string(elf, "_GLOBAL_OFFSET_TABLE_"),
3195 .value = elf.shndx.got.vaddr(elf),
3196 .size = 0,
3197 .type = .NOTYPE,
3198 .bind = .strong,
3199 .visibility = .HIDDEN,
3200 .shndx = elf.shndx.got,
3201 }) catch |err| switch (err) {
3202 error.MultipleDefinitions => unreachable, // no inputs are processed yet
3203 };
3204 if (have_dynamic_section) {
3205 _ = elf.addGlobalSymbolAssumeCapacity(.{
3206 .node = elf.shndx.dynamic.get(elf).ni,
3207 .name = try .string(elf, "_DYNAMIC"),
3208 .value = elf.shndx.dynamic.vaddr(elf),
3209 .size = 0,
3210 .type = .NOTYPE,
3211 .bind = .strong,
3212 .visibility = .HIDDEN,
3213 .shndx = elf.shndx.dynamic,
3214 }) catch |err| switch (err) {
3215 error.MultipleDefinitions => unreachable, // no inputs are processed yet
3216 };
3217 }
2547 } else {3218 } else {
2548 assert(maybe_interp == null);3219 assert(maybe_interp == null);
2549 assert(!have_dynamic_section);3220 assert(!have_dynamic_section);
...@@ -2554,6 +3225,12 @@ fn initHeaders(...@@ -2554,6 +3225,12 @@ fn initHeaders(
2554 .addralign = elf.mf.flags.block_size,3225 .addralign = elf.mf.flags.block_size,
2555 });3226 });
2556 assert(elf.nodes.len == expected_nodes_len);3227 assert(elf.nodes.len == expected_nodes_len);
3228
3229 try elf.section_by_name.ensureUnusedCapacity(gpa, elf.shdrs.items.len);
3230 for (0..elf.shdrs.items.len) |shndx_raw| {
3231 const shndx: Section.Index = @enumFromInt(shndx_raw);
3232 elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {});
3233 }
2557}3234}
25583235
2559pub fn startProgress(elf: *Elf, prog_node: std.Progress.Node) void {3236pub fn startProgress(elf: *Elf, prog_node: std.Progress.Node) void {
...@@ -2586,7 +3263,7 @@ fn getNode(elf: *const Elf, ni: MappedFile.Node.Index) Node {...@@ -2586,7 +3263,7 @@ fn getNode(elf: *const Elf, ni: MappedFile.Node.Index) Node {
2586 return elf.nodes.get(@intFromEnum(ni));3263 return elf.nodes.get(@intFromEnum(ni));
2587}3264}
2588/// Asserts that `ni` is a section, input section, NAV, UAV, or lazy code/data.3265/// Asserts that `ni` is a section, input section, NAV, UAV, or lazy code/data.
2589fn getNodeShndx(elf: *Elf, ni: MappedFile.Node.Index) Section.Index {3266fn getNodeShndx(elf: *const Elf, ni: MappedFile.Node.Index) Section.Index {
2590 return switch (elf.getNode(ni)) {3267 return switch (elf.getNode(ni)) {
2591 .file => unreachable,3268 .file => unreachable,
2592 .ehdr => unreachable,3269 .ehdr => unreachable,
...@@ -2624,55 +3301,80 @@ fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {...@@ -2624,55 +3301,80 @@ fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
2624/// Asserts that `ni` must be a node which supports relocations (see `Elf.Node`). Does not support3301/// Asserts that `ni` must be a node which supports relocations (see `Elf.Node`). Does not support
2625/// the special-case sections '.plt' and '.dynamic'.3302/// the special-case sections '.plt' and '.dynamic'.
2626fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void {3303fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void {
2627 const first_reloc_ptr: *Reloc.Index = switch (elf.getNode(ni)) {3304 const symbol_relocs: *SymbolReloc.Index, const got_relocs: ?*GotReloc.Index = switch (elf.getNode(ni)) {
2628 .file => unreachable, // cannot contain relocs3305 .file => unreachable, // cannot contain relocs
2629 .ehdr => unreachable, // cannot contain relocs3306 .ehdr => unreachable, // cannot contain relocs
2630 .shdr => unreachable, // cannot contain relocs3307 .shdr => unreachable, // cannot contain relocs
2631 .segment => unreachable, // cannot contain relocs3308 .segment => unreachable, // cannot contain relocs
2632 .section => unreachable, // cannot contain relocs (.plt and .dynamic unsupported)3309 .section => unreachable, // cannot contain relocs (.plt and .dynamic unsupported)
2633 .input_section => |isi| &elf.input_sections.items[@intFromEnum(isi)].first_reloc,3310 .input_section => |isi| .{
2634 .nav => |nmi| &elf.navs.values()[@intFromEnum(nmi)].first_reloc,3311 &elf.input_sections.items[@intFromEnum(isi)].first_symbol_reloc,
2635 .uav => |umi| &elf.uavs.values()[@intFromEnum(umi)].first_reloc,3312 &elf.input_sections.items[@intFromEnum(isi)].first_got_reloc,
2636 inline .lazy_code, .lazy_const_data => |lmi| &elf.lazy.getPtr(lmi.ref().kind).map.values()[lmi.ref().index].first_reloc,3313 },
3314 .nav => |nmi| .{
3315 &elf.navs.values()[@intFromEnum(nmi)].first_symbol_reloc,
3316 &elf.navs.values()[@intFromEnum(nmi)].first_got_reloc,
3317 },
3318 .uav => |umi| .{
3319 &elf.uavs.values()[@intFromEnum(umi)].first_symbol_reloc,
3320 null,
3321 },
3322 inline .lazy_code, .lazy_const_data => |lmi| .{
3323 &elf.lazy.getPtr(lmi.ref().kind).map.values()[lmi.ref().index].first_symbol_reloc,
3324 &elf.lazy.getPtr(lmi.ref().kind).map.values()[lmi.ref().index].first_got_reloc,
3325 },
2637 };3326 };
2638 if (first_reloc_ptr.* != .none) {3327
2639 for (elf.relocs.items[@intFromEnum(first_reloc_ptr.*)..]) |*reloc| {3328 if (symbol_relocs.* != .none) {
3329 for (
3330 elf.symbol_relocs.items[@intFromEnum(symbol_relocs.*)..],
3331 @intFromEnum(symbol_relocs.*)..,
3332 ) |*reloc, index| {
2640 if (reloc.node != ni) break;3333 if (reloc.node != ni) break;
2641 reloc.delete(elf);3334 reloc.delete(elf, @enumFromInt(index));
3335 }
3336 }
3337 symbol_relocs.* = @enumFromInt(elf.symbol_relocs.items.len);
3338
3339 if (got_relocs) |ptr| {
3340 if (ptr.* != .none) {
3341 for (elf.got_relocs.items[@intFromEnum(ptr.*)..]) |*reloc| {
3342 if (reloc.node != ni) break;
3343 reloc.* = .deleted;
3344 }
2642 }3345 }
3346 ptr.* = @enumFromInt(elf.got_relocs.items.len);
2643 }3347 }
2644 first_reloc_ptr.* = @enumFromInt(elf.relocs.items.len);
2645}3348}
26463349
2647/// Given that `node` has moved, updates all relocations in `node` (starting from `first_reloc`) as3350/// Given that `node` has moved, updates all relocations in `node` as needed. In relocatables, this
2648/// needed. In relocatables, this means updating the offsets of those relocations. In ELF modules,3351/// means updating the relocations' offsets. In ELF modules, this means applying the relocations.
2649/// this means applying the relocations.
2650fn flushMovedNodeRelocs(3352fn flushMovedNodeRelocs(
2651 elf: *Elf,3353 elf: *Elf,
2652 node: MappedFile.Node.Index,3354 node: MappedFile.Node.Index,
2653 node_vaddr: u64,3355 node_vaddr: u64,
2654 first_reloc: Reloc.Index,3356 first_symbol_reloc: SymbolReloc.Index,
3357 first_got_reloc: GotReloc.Index,
2655) void {3358) void {
2656 if (first_reloc == .none) return;3359 if (first_symbol_reloc != .none) {
2657 switch (elf.ehdrField(.type)) {3360 for (elf.symbol_relocs.items[@intFromEnum(first_symbol_reloc)..]) |*reloc| {
2658 .NONE, .CORE, _ => unreachable,3361 if (reloc.node != node) break;
2659 .REL => {3362 if (reloc.rela_index.unwrap()) |rela_index| {
2660 // In a relocatable, we're not actually applying any relocations ourselves, but we need3363 // Update the offsets of any `ElfN.Rela` entry we've emitted, since the node they're
2661 // to update the offsets of the relocation entries since the node they're in has moved.3364 // in has moved, so their offset within the section might also have moved.
2662 for (elf.relocs.items[@intFromEnum(first_reloc)..]) |*reloc| {3365 reloc.relaSection(elf).relaSetOffset(elf, rela_index, node_vaddr + reloc.offset);
2663 if (reloc.node != node) break;3366 } else {
2664 reloc.updateNodeOffset(elf, node_vaddr);3367 // We've applied this relocation ourselves! Just re-apply it now.
2665 }
2666 },
2667 .EXEC, .DYN => {
2668 // For an ELF module, we just need to apply relocations.
2669 for (elf.relocs.items[@intFromEnum(first_reloc)..]) |*reloc| {
2670 if (reloc.node != node) break;
2671 reloc.apply(elf);3368 reloc.apply(elf);
2672 }3369 }
2673 // TODO: once we're emitting runtime relocation entries, we need to update their offsets3370 }
2674 // too, like the logic for relocatables above.3371 }
2675 },3372
3373 if (first_got_reloc != .none) {
3374 for (elf.got_relocs.items[@intFromEnum(first_got_reloc)..]) |*reloc| {
3375 if (reloc.node != node) break;
3376 reloc.apply(elf);
3377 }
2676 }3378 }
2677}3379}
26783380
...@@ -2803,28 +3505,137 @@ fn dynsymPtr(elf: *Elf, index: u32) SymPtr {...@@ -2803,28 +3505,137 @@ fn dynsymPtr(elf: *Elf, index: u32) SymPtr {
2803 }3505 }
2804}3506}
28053507
2806fn navType(3508fn navType(elf: *const Elf, nav_resolved: InternPool.Nav.Resolved) std.elf.STT {
2807 ip: *const InternPool,3509 const any_non_single_threaded = elf.base.comp.config.any_non_single_threaded;
2808 nav_resolved: @typeInfo(@FieldType(InternPool.Nav, "resolved")).optional.child,
2809 any_non_single_threaded: bool,
2810) std.elf.STT {
2811 return if (any_non_single_threaded and nav_resolved.@"threadlocal")3510 return if (any_non_single_threaded and nav_resolved.@"threadlocal")
2812 .TLS3511 .TLS
2813 else if (ip.isFunctionType(nav_resolved.type))3512 else if (elf.base.comp.zcu.?.intern_pool.isFunctionType(nav_resolved.type))
2814 .FUNC3513 .FUNC
2815 else3514 else
2816 .OBJECT;3515 .OBJECT;
2817}3516}
2818fn namedSection(elf: *const Elf, name: []const u8) ?Section.Index {3517fn mapInputSection(elf: *Elf, opts: struct {
2819 if (std.mem.eql(u8, name, ".rodata") or3518 name: []const u8,
2820 std.mem.startsWith(u8, name, ".rodata.")) return .rodata;3519 flags: std.elf.SHF,
2821 if (std.mem.eql(u8, name, ".text") or3520 addralign: std.elf.Xword,
2822 std.mem.startsWith(u8, name, ".text.")) return .text;3521 entsize: std.elf.Xword,
2823 if (std.mem.eql(u8, name, ".data") or3522}) !Section.Index {
2824 std.mem.startsWith(u8, name, ".data.")) return .data;3523 const gpa = elf.base.comp.gpa;
2825 if (std.mem.eql(u8, name, ".tdata") or3524 if (opts.flags.INFO_LINK or
2826 std.mem.startsWith(u8, name, ".tdata.")) return elf.shndx.tdata;3525 opts.flags.LINK_ORDER or
2827 return null;3526 opts.flags.OS_NONCONFORMING or
3527 (opts.flags.EXECINSTR and opts.flags.WRITE) or
3528 (opts.flags.EXECINSTR and opts.flags.TLS))
3529 {
3530 return error.UnsupportedSectionFlags;
3531 }
3532 if (opts.flags.TLS and elf.ni.tls == .none) {
3533 assert(!elf.base.comp.config.any_non_single_threaded);
3534 return error.TlsSectionUnavailable;
3535 }
3536
3537 if (elf.base.comp.config.debug_format == .strip and
3538 std.mem.startsWith(u8, opts.name, ".debug_") and
3539 !opts.flags.ALLOC)
3540 {
3541 return error.StripSection;
3542 }
3543
3544 const name: []const u8 = switch (elf.ehdrField(.type)) {
3545 .NONE, .CORE, _ => unreachable,
3546 .REL => opts.name,
3547 .EXEC, .DYN => name: {
3548 if (std.mem.startsWith(u8, opts.name, ".text.")) break :name ".text";
3549 if (std.mem.startsWith(u8, opts.name, ".rodata.")) break :name ".rodata";
3550 if (std.mem.startsWith(u8, opts.name, ".data.")) break :name ".data";
3551 if (std.mem.startsWith(u8, opts.name, ".data.rel.ro.")) break :name ".data.rel.ro";
3552 if (std.mem.startsWith(u8, opts.name, ".tdata.")) break :name ".tdata";
3553 if (std.mem.startsWith(u8, opts.name, ".gcc_except_table.")) break :name ".gcc_except_table";
3554 // TODO: actually generate a bss section!
3555 if (std.mem.eql(u8, opts.name, ".bss")) break :name ".data";
3556 if (std.mem.startsWith(u8, opts.name, ".bss.")) break :name ".data";
3557 // TODO: actually generate a tbss section!
3558 if (std.mem.eql(u8, opts.name, ".tbss")) break :name ".tdata";
3559 if (std.mem.startsWith(u8, opts.name, ".tbss.")) break :name ".tdata";
3560 break :name opts.name;
3561 },
3562 };
3563 const existing_shndx: Section.Index = existing: {
3564 const name_shstrtab = try elf.string(.shstrtab, name);
3565 const gop = try elf.section_by_name.getOrPut(gpa, name_shstrtab);
3566 if (gop.found_existing) {
3567 break :existing @enumFromInt(gop.index);
3568 }
3569 errdefer assert(elf.section_by_name.pop().?.key == name_shstrtab);
3570 const parent_node: MappedFile.Node.Index = parent: {
3571 if (!opts.flags.ALLOC) break :parent elf.ni.file;
3572 if (opts.flags.EXECINSTR) break :parent elf.ni.text;
3573 if (opts.flags.TLS) break :parent elf.ni.tls;
3574 if (opts.flags.WRITE) break :parent elf.ni.data;
3575 break :parent elf.ni.rodata;
3576 };
3577 assert(gop.index == elf.shdrs.items.len);
3578 return elf.addSection(parent_node, .{
3579 .name = name,
3580 .type = .NULL, // because initial size is 0
3581 .flags = flags: {
3582 // We need to decompress the section for linking.
3583 var flags = opts.flags;
3584 flags.COMPRESSED = false;
3585 break :flags flags;
3586 },
3587 .node_align = .fromByteUnits(std.math.ceilPowerOfTwoAssert(
3588 usize,
3589 @intCast(@max(opts.addralign, 1)),
3590 )),
3591 .entsize = std.math.lossyCast(u32, opts.entsize),
3592 });
3593 };
3594 // Validate that the input is compatible with this section...
3595 switch (elf.shdrPtr(existing_shndx)) {
3596 inline else => |shdr| {
3597 const cur_flags = elf.targetLoad(&shdr.flags).shf;
3598 if (cur_flags.EXECINSTR != opts.flags.EXECINSTR or
3599 cur_flags.WRITE != opts.flags.WRITE or
3600 cur_flags.TLS != opts.flags.TLS)
3601 {
3602 return error.SectionFlagsConflict;
3603 }
3604
3605 switch (elf.targetLoad(&shdr.type)) {
3606 .NULL, .PROGBITS => {},
3607 else => return error.SectionTypeConflict,
3608 }
3609 },
3610 }
3611 // ...then realign the section's node if necessary...
3612 if (opts.addralign > existing_shndx.get(elf).ni.alignment(&elf.mf).toByteUnits()) {
3613 const new_alignment: std.mem.Alignment = .fromByteUnits(
3614 std.math.ceilPowerOfTwoAssert(usize, @intCast(opts.addralign)),
3615 );
3616 try existing_shndx.get(elf).ni.realign(&elf.mf, gpa, new_alignment);
3617 }
3618 // ...and update the shdr as needed.
3619 switch (elf.shdrPtr(existing_shndx)) {
3620 inline else => |shdr| {
3621 // Combine the section flags.
3622 const cur_flags = elf.targetLoad(&shdr.flags).shf;
3623 elf.targetStore(&shdr.flags, .{ .shf = .{
3624 .EXECINSTR = cur_flags.EXECINSTR,
3625 .WRITE = cur_flags.WRITE,
3626 .TLS = cur_flags.TLS,
3627 .ALLOC = cur_flags.ALLOC or opts.flags.ALLOC,
3628 .STRINGS = cur_flags.STRINGS and opts.flags.STRINGS,
3629 .MERGE = cur_flags.MERGE and opts.flags.MERGE,
3630 } });
3631 // Increase addralign to the maximum of the current value and the new value---the node
3632 // alignment was already increased above.
3633 if (opts.addralign > elf.targetLoad(&shdr.addralign)) {
3634 elf.targetStore(&shdr.addralign, @intCast(opts.addralign));
3635 }
3636 },
3637 }
3638 return existing_shndx;
2828}3639}
2829fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavMapIndex {3640fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavMapIndex {
2830 const gpa = zcu.gpa;3641 const gpa = zcu.gpa;
...@@ -2838,17 +3649,41 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavM...@@ -2838,17 +3649,41 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavM
2838 const nav_gop = elf.navs.getOrPutAssumeCapacity(nav_index);3649 const nav_gop = elf.navs.getOrPutAssumeCapacity(nav_index);
2839 const nmi: Node.NavMapIndex = @enumFromInt(nav_gop.index);3650 const nmi: Node.NavMapIndex = @enumFromInt(nav_gop.index);
2840 if (!nav_gop.found_existing) {3651 if (!nav_gop.found_existing) {
2841 const sym_type = navType(ip, nav.resolved.?, elf.base.comp.config.any_non_single_threaded);
2842 const shndx: Section.Index = section: {3652 const shndx: Section.Index = section: {
2843 if (nav.resolved.?.@"linksection".toSlice(ip)) |@"linksection"| {3653 if (nav.resolved.?.@"linksection".toSlice(ip)) |@"linksection"| {
2844 if (elf.namedSection(@"linksection")) |shndx| break :section shndx;3654 if (elf.mapInputSection(.{
3655 .name = @"linksection",
3656 .flags = .{
3657 .ALLOC = true,
3658 .EXECINSTR = ip.isFunctionType(nav.resolved.?.type),
3659 .WRITE = !nav.resolved.?.@"const",
3660 .TLS = elf.base.comp.config.any_non_single_threaded and
3661 nav.resolved.?.@"threadlocal",
3662 },
3663 .addralign = 1,
3664 .entsize = 0,
3665 })) |shndx| {
3666 break :section shndx;
3667 } else |err| switch (err) {
3668 error.StripSection,
3669 error.TlsSectionUnavailable,
3670 error.UnsupportedSectionFlags,
3671 error.SectionTypeConflict,
3672 error.SectionFlagsConflict,
3673 => {}, // fall back to default behavior below
3674
3675 else => |e| return e,
3676 }
3677 }
3678 if (elf.base.comp.config.any_non_single_threaded and nav.resolved.?.@"threadlocal") {
3679 break :section elf.shndx.tdata;
3680 } else if (!nav.resolved.?.@"const") {
3681 break :section .data;
3682 } else if (ip.isFunctionType(nav.resolved.?.type)) {
3683 break :section .text;
3684 } else {
3685 break :section .rodata;
2845 }3686 }
2846 break :section switch (sym_type) {
2847 else => unreachable,
2848 .FUNC => .text,
2849 .OBJECT => .data,
2850 .TLS => elf.shndx.tdata,
2851 };
2852 };3687 };
2853 const alignment: InternPool.Alignment = switch (Type.fromInterned(nav.resolved.?.type).zigTypeTag(zcu)) {3688 const alignment: InternPool.Alignment = switch (Type.fromInterned(nav.resolved.?.type).zigTypeTag(zcu)) {
2854 .@"fn" => a: {3689 .@"fn" => a: {
...@@ -2880,10 +3715,11 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavM...@@ -2880,10 +3715,11 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavM
2880 .name = try elf.string(.strtab, nav.fqn.toSlice(ip)),3715 .name = try elf.string(.strtab, nav.fqn.toSlice(ip)),
2881 .value = 0,3716 .value = 0,
2882 .size = 0,3717 .size = 0,
2883 .type = sym_type,3718 .type = elf.navType(nav.resolved.?),
2884 .shndx = shndx,3719 .shndx = shndx,
2885 }),3720 }),
2886 .first_reloc = .none,3721 .first_symbol_reloc = .none,
3722 .first_got_reloc = .none,
2887 };3723 };
2888 elf.nodes.appendAssumeCapacity(.{ .nav = nmi });3724 elf.nodes.appendAssumeCapacity(.{ .nav = nmi });
2889 }3725 }
...@@ -2932,7 +3768,7 @@ fn uavMapIndex(...@@ -2932,7 +3768,7 @@ fn uavMapIndex(
2932 .type = .OBJECT,3768 .type = .OBJECT,
2933 .shndx = shndx,3769 .shndx = shndx,
2934 }),3770 }),
2935 .first_reloc = .none,3771 .first_symbol_reloc = .none,
2936 };3772 };
2937 elf.nodes.appendAssumeCapacity(.{ .uav = umi });3773 elf.nodes.appendAssumeCapacity(.{ .uav = umi });
2938 elf.const_prog_node.increaseEstimatedTotalItems(1);3774 elf.const_prog_node.increaseEstimatedTotalItems(1);
...@@ -2940,7 +3776,7 @@ fn uavMapIndex(...@@ -2940,7 +3776,7 @@ fn uavMapIndex(
2940 } else {3776 } else {
2941 const node = uav_gop.value_ptr.lsi.index().ptr(elf).node;3777 const node = uav_gop.value_ptr.lsi.index().ptr(elf).node;
2942 if (resolved_align.toStdMem().order(node.alignment(&elf.mf)).compare(.gt)) {3778 if (resolved_align.toStdMem().order(node.alignment(&elf.mf)).compare(.gt)) {
2943 node.realign(&elf.mf, resolved_align.toStdMem());3779 try node.realign(&elf.mf, gpa, resolved_align.toStdMem());
2944 }3780 }
2945 }3781 }
2946 return umi;3782 return umi;
...@@ -2977,7 +3813,14 @@ pub fn loadInput(elf: *Elf, input: link.Input) (Io.File.Reader.SizeError ||...@@ -2977,7 +3813,14 @@ pub fn loadInput(elf: *Elf, input: link.Input) (Io.File.Reader.SizeError ||
2977 else => |e| return e,3813 else => |e| return e,
2978 };3814 };
2979 },3815 },
2980 .dso_exact => |dso_exact| try elf.loadDsoExact(dso_exact.name),3816 .dso_exact => |dso_exact| {
3817 log.debug("load dso_exact '{f}'", .{std.zig.fmtString(dso_exact.name)});
3818 if (elf.shndx.dynamic != .UNDEF) {
3819 try elf.needed.put(elf.base.comp.gpa, try elf.string(.dynstr, dso_exact.name), {});
3820 }
3821 // TODO: we need to get a resolved file path from the frontend, because we need to read
3822 // the shared object to discover symbol types.
3823 },
2981 }3824 }
2982}3825}
2983fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {3826fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {
...@@ -3006,6 +3849,13 @@ fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void...@@ -3006,6 +3849,13 @@ fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void
3006 continue;3849 continue;
3007 }3850 }
3008 load_object: {3851 load_object: {
3852 if (std.mem.eql(u8, &header.ar_name, std.elf.SYMNAME) or
3853 std.mem.eql(u8, &header.ar_name, std.elf.SYM64NAME) or
3854 std.mem.eql(u8, &header.ar_name, std.elf.SYMDEFNAME) or
3855 std.mem.eql(u8, &header.ar_name, std.elf.SYMDEFSORTEDNAME))
3856 {
3857 break :load_object;
3858 }
3009 const member = header.name() orelse member: {3859 const member = header.name() orelse member: {
3010 const strtab_offset = header.nameOffset() catch |err| switch (err) {3860 const strtab_offset = header.nameOffset() catch |err| switch (err) {
3011 error.Overflow => break :member error.Overflow,3861 error.Overflow => break :member error.Overflow,
...@@ -3021,7 +3871,6 @@ fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void...@@ -3021,7 +3871,6 @@ fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void
3021 } catch |err| switch (err) {3871 } catch |err| switch (err) {
3022 error.Overflow => return diags.failParse(path, "bad member name offset", .{}),3872 error.Overflow => return diags.failParse(path, "bad member name offset", .{}),
3023 };3873 };
3024 if (!std.mem.endsWith(u8, member, ".o")) break :load_object;
3025 try elf.loadObject(path, member, fr, .{ .offset = offset, .size = size });3874 try elf.loadObject(path, member, fr, .{ .offset = offset, .size = size });
3026 }3875 }
3027 try fr.seekTo(std.mem.alignForward(u64, offset + size, 2));3876 try fr.seekTo(std.mem.alignForward(u64, offset + size, 2));
...@@ -3109,47 +3958,146 @@ fn loadObject(...@@ -3109,47 +3958,146 @@ fn loadObject(
3109 defer gpa.free(shstrtab);3958 defer gpa.free(shstrtab);
3110 try elf.nodes.ensureUnusedCapacity(gpa, ehdr.shnum - 1);3959 try elf.nodes.ensureUnusedCapacity(gpa, ehdr.shnum - 1);
3111 try elf.input_sections.ensureUnusedCapacity(gpa, ehdr.shnum - 1);3960 try elf.input_sections.ensureUnusedCapacity(gpa, ehdr.shnum - 1);
3112 for (sections[1..]) |*section| switch (section.shdr.type) {3961 for (sections[1..]) |*section| {
3113 else => {},3962 if (section.shdr.name >= shstrtab.len) continue;
3114 .PROGBITS, .NOBITS => {3963 const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0);
3115 if (section.shdr.name >= shstrtab.len) continue;3964 const opts: struct {
3116 const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0);3965 shndx: Section.Index,
3117 const shndx: Section.Index = elf.namedSection(name) orelse shndx: {3966 has_file_bits: bool,
3118 // TODO: actually generate a .bss section. For now, just throw it into `.data`.3967 node_fixed: bool,
3119 if (std.mem.eql(u8, name, ".bss") or3968 } = switch (section.shdr.type) {
3120 std.mem.startsWith(u8, name, ".bss.")) break :shndx .data;3969 else => continue,
3121 if (std.mem.eql(u8, name, ".tbss") or3970 .PROGBITS, .NOBITS => opts: {
3122 std.mem.startsWith(u8, name, ".tbss.")) break :shndx elf.shndx.tdata;3971 const shndx = elf.mapInputSection(.{
3123 break :shndx .UNDEF;3972 .name = name,
3124 };3973 .flags = section.shdr.flags.shf,
3125 if (shndx == .UNDEF) continue;3974 .addralign = section.shdr.addralign,
3126 const ni = try elf.mf.addLastChildNode(gpa, shndx.get(elf).ni, .{3975 .entsize = section.shdr.entsize,
3127 .size = section.shdr.size,3976 }) catch |err| switch (err) {
3128 .alignment = .fromByteUnits(std.math.ceilPowerOfTwoAssert(3977 error.StripSection => continue,
3129 usize,3978 error.TlsSectionUnavailable => return diags.failParse(
3130 @intCast(@max(section.shdr.addralign, 1)),3979 path,
3131 )),3980 "thread-local storage section '{s}' is incompatible with '-fsingle-threaded'",
3132 .moved = true, // see assert at end of `flushInputSection`3981 .{name},
3133 });3982 ),
3134 elf.nodes.appendAssumeCapacity(.{3983 error.UnsupportedSectionFlags => if (!section.shdr.flags.shf.ALLOC) {
3135 .input_section = @enumFromInt(elf.input_sections.items.len),3984 // It probably doesn't matter, just skip this section.
3136 });3985 continue;
3137 section.isi = @enumFromInt(elf.input_sections.items.len);3986 } else return diags.failParse(
3138 elf.input_sections.addOneAssumeCapacity().* = .{3987 path,
3139 .input = input_index,3988 "unsupported flags for section '{s}'",
3140 .file_location = .{3989 .{name},
3141 .offset = fl.offset + section.shdr.offset,3990 ),
3142 .size = if (section.shdr.type == .NOBITS) 0 else section.shdr.size,3991 error.SectionTypeConflict => if (!section.shdr.flags.shf.ALLOC) {
3992 // It probably doesn't matter, just skip this section.
3993 continue;
3994 } else return diags.failParse(
3995 path,
3996 "type of section '{s}' conflicts with other inputs",
3997 .{name},
3998 ),
3999 error.SectionFlagsConflict => if (!section.shdr.flags.shf.ALLOC) {
4000 // It probably doesn't matter, just skip this section.
4001 continue;
4002 } else return diags.failParse(
4003 path,
4004 "flags of section '{s}' conflict with other inputs",
4005 .{name},
4006 ),
4007 else => |e| return e,
4008 };
4009 if (section.shdr.flags.shf.COMPRESSED) {
4010 // SHF_COMPRESSED is only allowed on non-alloc sections.
4011 if (section.shdr.flags.shf.ALLOC) return diags.failParse(
4012 path,
4013 "section '{s}' has conflicting flags SHF_ALLOC and SHF_COMPRESSED",
4014 .{name},
4015 );
4016 // TODO: handle compressed input sections. We'll need to set a flag to
4017 // indicate that `flushInputSection` needs to decompress the section.
4018 // But because this section isn't SHF_ALLOC, it's probably okay to just
4019 // skip it for now.
4020 continue;
4021 }
4022 break :opts .{
4023 .shndx = shndx,
4024 .has_file_bits = section.shdr.type == .PROGBITS,
4025 // For well-known sections, we know that it's fine to have e.g. random
4026 // padding, so there's no need to make the sections fixed. For custom
4027 // sections, however, we do want fixed nodes to avoid padding.
4028 .node_fixed = shndx != .text and
4029 shndx != .rodata and
4030 shndx != .data and
4031 shndx != .data_rel_ro and
4032 shndx != elf.shndx.tdata,
4033 };
4034 },
4035 inline .INIT_ARRAY, .FINI_ARRAY, .PREINIT_ARRAY => |@"type"| .{
4036 .shndx = shndx: {
4037 // TODO: the input section name may include a "priority" value between 1
4038 // and 65535 which should affect the order we assemble input sections in
4039 const init_fini_section_name: []const u8 = switch (@"type") {
4040 .INIT_ARRAY => "init_array",
4041 .FINI_ARRAY => "fini_array",
4042 .PREINIT_ARRAY => "preinit_array",
4043 else => comptime unreachable,
4044 };
4045 const shndx: *Section.Index = &@field(elf.shndx, init_fini_section_name);
4046 const need_addralign: u8 = switch (class) {
4047 .NONE, _ => unreachable,
4048 .@"32" => 4,
4049 .@"64" => 8,
4050 };
4051 if (section.shdr.addralign != need_addralign) {
4052 return diags.failParse(path, "bad addralign on {t} shdr", .{@"type"});
4053 }
4054 if (shndx.* == .UNDEF) {
4055 try elf.createInitFiniArraySection(shndx, init_fini_section_name, @"type");
4056 }
4057 switch (elf.shdrPtr(shndx.*)) {
4058 inline else => |shdr| {
4059 const old_size = elf.targetLoad(&shdr.size);
4060 const new_size = old_size + section.shdr.size;
4061 elf.targetStore(&shdr.size, @intCast(new_size));
4062 elf.updateInitFiniArraySectionSize(shndx.*, init_fini_section_name, @"type", new_size);
4063 },
4064 }
4065 break :shndx shndx.*;
3143 },4066 },
3144 // The section vaddr is initially 0, because the symbol addresses are4067 .has_file_bits = true,
3145 // zero-based. This will eventually be updated by `flushMoved`.4068 // This node must be fixed to prevent padding from being added between different
3146 .vaddr = 0,4069 // INIT_ARRAY/FINI_ARRAY/PREINIT_ARRAY input sections.
3147 .node = ni,4070 .node_fixed = true,
3148 .first_reloc = .none,4071 },
3149 };4072 };
3150 elf.synth_prog_node.increaseEstimatedTotalItems(1);4073 const ni = try elf.mf.addLastChildNode(gpa, opts.shndx.get(elf).ni, .{
3151 },4074 .size = section.shdr.size,
3152 };4075 .alignment = .fromByteUnits(std.math.ceilPowerOfTwoAssert(
4076 usize,
4077 @intCast(@max(section.shdr.addralign, 1)),
4078 )),
4079 .moved = true, // see assert at end of `flushInputSection`
4080 .fixed = opts.node_fixed,
4081 });
4082 elf.nodes.appendAssumeCapacity(.{
4083 .input_section = @enumFromInt(elf.input_sections.items.len),
4084 });
4085 section.isi = @enumFromInt(elf.input_sections.items.len);
4086 elf.input_sections.addOneAssumeCapacity().* = .{
4087 .input = input_index,
4088 .file_location = .{
4089 .offset = fl.offset + section.shdr.offset,
4090 .size = if (opts.has_file_bits) section.shdr.size else 0,
4091 },
4092 // The section vaddr is initially 0, because the symbol addresses are
4093 // zero-based. This will eventually be updated by `flushMoved`.
4094 .vaddr = 0,
4095 .node = ni,
4096 .first_symbol_reloc = .none,
4097 .first_got_reloc = .none,
4098 };
4099 elf.synth_prog_node.increaseEstimatedTotalItems(1);
4100 }
3153 var symmap: std.ArrayList(Symbol.Id) = .empty;4101 var symmap: std.ArrayList(Symbol.Id) = .empty;
3154 defer symmap.deinit(gpa);4102 defer symmap.deinit(gpa);
3155 for (sections[1..], 1..) |*symtab, symtab_shndx| switch (symtab.shdr.type) {4103 for (sections[1..], 1..) |*symtab, symtab_shndx| switch (symtab.shdr.type) {
...@@ -3198,20 +4146,24 @@ fn loadObject(...@@ -3198,20 +4146,24 @@ fn loadObject(
3198 };4146 };
31994147
3200 if (input_sym.shndx == std.elf.SHN_UNDEF) switch (input_sym.info.bind) {4148 if (input_sym.shndx == std.elf.SHN_UNDEF) switch (input_sym.info.bind) {
3201 _ => |bind| return diags.failParse(4149 else => |bind| return diags.failParse(
3202 path,4150 path,
3203 "symbol '{s}' has unsupported binding (0x{x})",4151 "symbol '{s}' has unsupported binding (0x{x})",
3204 .{ name, bind },4152 .{ name, bind },
3205 ),4153 ),
3206 .LOCAL => continue,4154 .LOCAL => continue,
3207 .GLOBAL, .WEAK => |bind| {4155 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {
3208 si.* = elf.addGlobalSymbolAssumeCapacity(.{4156 si.* = elf.addGlobalSymbolAssumeCapacity(.{
3209 .node = .none,4157 .node = .none,
3210 .name = try .string(elf, name),4158 .name = try .string(elf, name),
3211 .value = input_sym.value,4159 .value = input_sym.value,
3212 .size = input_sym.size,4160 .size = input_sym.size,
3213 .type = sym_type,4161 .type = sym_type,
3214 .bind = if (bind == .WEAK) .weak else .strong,4162 .bind = switch (bind) {
4163 .WEAK, .GNU_UNIQUE => .weak,
4164 .GLOBAL => .strong,
4165 else => unreachable,
4166 },
3215 .visibility = input_sym.other.visibility,4167 .visibility = input_sym.other.visibility,
3216 .shndx = .UNDEF,4168 .shndx = .UNDEF,
3217 }) catch |err| switch (err) {4169 }) catch |err| switch (err) {
...@@ -3224,7 +4176,7 @@ fn loadObject(...@@ -3224,7 +4176,7 @@ fn loadObject(
3224 const input_section_node = (sections[input_sym.shndx].isi orelse continue).node(elf);4176 const input_section_node = (sections[input_sym.shndx].isi orelse continue).node(elf);
32254177
3226 switch (input_sym.info.bind) {4178 switch (input_sym.info.bind) {
3227 _ => |bind| return diags.failParse(4179 else => |bind| return diags.failParse(
3228 path,4180 path,
3229 "symbol '{s}' has unsupported binding (0x{x})",4181 "symbol '{s}' has unsupported binding (0x{x})",
3230 .{ name, bind },4182 .{ name, bind },
...@@ -3240,14 +4192,18 @@ fn loadObject(...@@ -3240,14 +4192,18 @@ fn loadObject(
3240 });4192 });
3241 si.* = .local(lsi);4193 si.* = .local(lsi);
3242 },4194 },
3243 .GLOBAL, .WEAK => |bind| {4195 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {
3244 si.* = elf.addGlobalSymbolAssumeCapacity(.{4196 si.* = elf.addGlobalSymbolAssumeCapacity(.{
3245 .node = input_section_node,4197 .node = input_section_node,
3246 .name = try .string(elf, name),4198 .name = try .string(elf, name),
3247 .value = input_sym.value,4199 .value = input_sym.value,
3248 .size = input_sym.size,4200 .size = input_sym.size,
3249 .type = sym_type,4201 .type = sym_type,
3250 .bind = if (bind == .WEAK) .weak else .strong,4202 .bind = switch (bind) {
4203 .WEAK, .GNU_UNIQUE => .weak,
4204 .GLOBAL => .strong,
4205 else => unreachable,
4206 },
3251 .visibility = input_sym.other.visibility,4207 .visibility = input_sym.other.visibility,
3252 .shndx = elf.getNodeShndx(input_section_node),4208 .shndx = elf.getNodeShndx(input_section_node),
3253 }) catch |err| switch (err) {4209 }) catch |err| switch (err) {
...@@ -3298,11 +4254,17 @@ fn loadObject(...@@ -3298,11 +4254,17 @@ fn loadObject(
3298 .{rel.info.sym},4254 .{rel.info.sym},
3299 );4255 );
3300 const target = symmap.items[rel.info.sym - 1];4256 const target = symmap.items[rel.info.sym - 1];
3301 if (target == Symbol.Id.null) return diags.failParse(4257 if (target == Symbol.Id.null) {
3302 path,4258 // If this is not an SHF_ALLOC section, then let's let this
3303 "unsupported symbol at index {d} required for relocation",4259 // slide for now, because it probably doesn't affect the final
3304 .{rel.info.sym},4260 // binary's functionality for this section to be a bit broken.
3305 );4261 if (!loc_sec.shdr.flags.shf.ALLOC) continue;
4262 return diags.failParse(
4263 path,
4264 "unsupported symbol at index {d} required for relocation",
4265 .{rel.info.sym},
4266 );
4267 }
3306 elf.addRelocAssumeCapacity(4268 elf.addRelocAssumeCapacity(
3307 loc_node,4269 loc_node,
3308 rel.offset - loc_sec.shdr.addr,4270 rel.offset - loc_sec.shdr.addr,
...@@ -3320,6 +4282,7 @@ fn loadObject(...@@ -3320,6 +4282,7 @@ fn loadObject(
3320}4282}
3321fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {4283fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {
3322 const comp = elf.base.comp;4284 const comp = elf.base.comp;
4285 const gpa = comp.gpa;
3323 const diags = &comp.link_diags;4286 const diags = &comp.link_diags;
3324 const r = &fr.interface;4287 const r = &fr.interface;
33254288
...@@ -3334,68 +4297,157 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {...@@ -3334,68 +4297,157 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {
3334 if (ehdr.type != .DYN) return diags.failParse(path, "unsupported dso type", .{});4297 if (ehdr.type != .DYN) return diags.failParse(path, "unsupported dso type", .{});
3335 if (ehdr.machine != elf.ehdrField(.machine))4298 if (ehdr.machine != elf.ehdrField(.machine))
3336 return diags.failParse(path, "bad machine", .{});4299 return diags.failParse(path, "bad machine", .{});
3337 if (ehdr.phoff == 0 or ehdr.phnum <= 1)4300 if (ehdr.shnum > 0) try fr.seekTo(ehdr.shoff);
3338 return diags.failParse(path, "no program headers", .{});4301 const dynamic_sh: ElfN.Shdr, const dynsym_sh: ElfN.Shdr = sh: {
3339 try fr.seekTo(ehdr.phoff);4302 var dynamic_sh: ?ElfN.Shdr = null;
3340 const dynamic_ph = for (0..ehdr.phnum) |_| {4303 var dynsym_sh: ?ElfN.Shdr = null;
3341 const ph = try r.peekStruct(ElfN.Phdr, target_endian);4304 for (0..ehdr.shnum) |_| {
3342 try r.discardAll(ehdr.phentsize);4305 const sh = try r.peekStruct(ElfN.Shdr, target_endian);
3343 switch (ph.type) {4306 try r.discardAll(ehdr.shentsize);
3344 else => {},4307 switch (sh.type) {
3345 .DYNAMIC => break ph,4308 else => {},
4309 .DYNAMIC => dynamic_sh = sh,
4310 .DYNSYM => dynsym_sh = sh,
4311 }
4312 }
4313 break :sh .{
4314 dynamic_sh orelse return diags.failParse(path, "missing SHT_DYNAMIC section", .{}),
4315 dynsym_sh orelse return diags.failParse(path, "missing SHT_DYNSYM section", .{}),
4316 };
4317 };
4318 const dynstr_sh: ElfN.Shdr = sh: {
4319 if (dynsym_sh.link >= ehdr.shnum) {
4320 return diags.failParse(path, "bad dynamic string table section index", .{});
3346 }4321 }
3347 } else return diags.failParse(path, "no dynamic segment", .{});4322 try fr.seekTo(ehdr.shoff + dynsym_sh.link * ehdr.shentsize);
4323 break :sh try r.peekStruct(ElfN.Shdr, target_endian);
4324 };
4325
4326 if (dynamic_sh.entsize != @sizeOf(ElfN.Addr) * 2) {
4327 return diags.failParse(path, "bad dynamic section entsize", .{});
4328 }
3348 const dynnum = std.math.divExact(4329 const dynnum = std.math.divExact(
3349 u32,4330 u32,
3350 @intCast(dynamic_ph.filesz),4331 @intCast(dynamic_sh.size),
3351 @sizeOf(ElfN.Addr) * 2,4332 @sizeOf(ElfN.Addr) * 2,
3352 ) catch return diags.failParse(4333 ) catch return diags.failParse(
3353 path,4334 path,
3354 "dynamic segment filesz (0x{x}) is not a multiple of entsize (0x{x})",4335 "dynamic section size (0x{x}) is not a multiple of entsize (0x{x})",
3355 .{ dynamic_ph.filesz, @sizeOf(ElfN.Addr) * 2 },4336 .{ dynamic_sh.size, @sizeOf(ElfN.Addr) * 2 },
3356 );4337 );
3357 var strtab: ?ElfN.Addr = null;4338
3358 var strsz: ?ElfN.Addr = null;4339 if (dynsym_sh.entsize < @sizeOf(ElfN.Sym)) {
3359 var soname: ?ElfN.Addr = null;4340 return diags.failParse(path, "bad dynsym entsize", .{});
3360 try fr.seekTo(dynamic_ph.offset);4341 }
3361 for (0..dynnum) |_| {4342 const symnum = std.math.divExact(
4343 u32,
4344 @intCast(dynsym_sh.size),
4345 @intCast(dynsym_sh.entsize),
4346 ) catch return diags.failParse(
4347 path,
4348 "dynsym size (0x{x}) is not a multiple of entsize (0x{x})",
4349 .{ dynsym_sh.size, dynsym_sh.entsize },
4350 );
4351
4352 const dynstr = try gpa.alloc(u8, @intCast(dynstr_sh.size));
4353 defer gpa.free(dynstr);
4354 try fr.seekTo(dynstr_sh.offset);
4355 try r.readSliceAll(dynstr);
4356
4357 // Find the DT_SONAME dynamic entry so that it can become our DT_NEEDED entry.
4358 try fr.seekTo(dynamic_sh.offset);
4359 const soname: []const u8 = for (0..dynnum) |_| {
3362 const tag = try r.takeInt(ElfN.Addr, target_endian);4360 const tag = try r.takeInt(ElfN.Addr, target_endian);
3363 const val = try r.takeInt(ElfN.Addr, target_endian);4361 const val = try r.takeInt(ElfN.Addr, target_endian);
3364 switch (tag) {4362 if (tag == std.elf.DT_SONAME) {
3365 else => {},4363 // val is a dynstr index
3366 std.elf.DT_STRTAB => strtab = val,4364 if (val >= dynstr.len) {
3367 std.elf.DT_STRSZ => strsz = val,4365 return diags.failParse(path, "bad soname string", .{});
3368 std.elf.DT_SONAME => soname = val,4366 }
4367 break std.mem.sliceTo(dynstr[@intCast(val)..], 0);
3369 }4368 }
3370 }4369 } else std.fs.path.basename(path.sub_path);
3371 if (strtab == null or soname == null)4370 try elf.needed.put(gpa, try elf.string(.dynstr, soname), {});
3372 return elf.loadDsoExact(std.fs.path.basename(path.sub_path));4371
3373 if (strsz) |size| if (soname.? >= size)4372 // Scan the symbol table and populate `elf.dso_globals`.
3374 return diags.failParse(path, "bad soname string", .{});4373 const first_global = @min(dynsym_sh.info, symnum);
3375 try fr.seekTo(ehdr.phoff);4374 try elf.dso_globals.ensureUnusedCapacity(gpa, symnum - first_global);
3376 const ph = for (0..ehdr.phnum) |_| {4375 try elf.ensureUnusedPltCapacity(symnum - first_global);
3377 const ph = try r.peekStruct(ElfN.Phdr, target_endian);4376 try fr.seekTo(dynsym_sh.offset + first_global * dynsym_sh.entsize);
3378 try r.discardAll(ehdr.phentsize);4377 for (first_global..symnum) |_| {
3379 switch (ph.type) {4378 const sym = try r.peekStruct(ElfN.Sym, target_endian);
3380 else => {},4379 try r.discardAll(@intCast(dynsym_sh.entsize));
3381 .LOAD => if (strtab.? >= ph.vaddr and4380
3382 strtab.? + (strsz orelse 0) <= ph.vaddr + ph.filesz) break ph,4381 switch (sym.info.bind) {
4382 else => continue,
4383 .GLOBAL, .WEAK, .GNU_UNIQUE => {},
3383 }4384 }
3384 } else return diags.failParse(path, "strtab not part of a loaded segment", .{});4385 // STV_HIDDEN/STV_INTERNAL symbols should be marked as STB_LOCAL and hence skipped
3385 try fr.seekTo(strtab.? + soname.? - ph.vaddr + ph.offset);4386 // above, but we might as well double-check.
3386 return elf.loadDsoExact(r.peekSentinel(0) catch |err| switch (err) {4387 switch (sym.other.visibility) {
3387 error.StreamTooLong => return diags.failParse(path, "soname too lang", .{}),4388 .HIDDEN, .INTERNAL => continue,
3388 else => |e| return e,4389 .DEFAULT, .PROTECTED => {},
3389 });4390 }
4391
4392 if (sym.shndx == std.elf.SHN_UNDEF) continue;
4393
4394 if (sym.name >= dynstr.len) {
4395 return diags.failParse(path, "bad symbol name string", .{});
4396 }
4397
4398 const name = try elf.string(.strtab, std.mem.sliceTo(dynstr[sym.name..], 0));
4399 const gop = elf.dso_globals.getOrPutAssumeCapacity(name);
4400 if (!gop.found_existing or gop.value_ptr.* == .NOTYPE) {
4401 gop.value_ptr.* = sym.info.type;
4402 }
4403
4404 // If there's already an undefined symbol by this name of type STT_NOTYPE, populate
4405 // its type now.
4406 const global_ptr = elf.globals.strong_undef.getPtr(name) orelse
4407 elf.globals.weak_undef.getPtr(name) orelse
4408 continue;
4409
4410 if (global_ptr.dynsym_index == 0) continue;
4411
4412 const sym_ptr = @field(elf.symPtr(global_ptr.symtab_index), @tagName(class));
4413 switch (elf.targetLoad(&sym_ptr.other).visibility) {
4414 .HIDDEN, .INTERNAL, .PROTECTED => continue,
4415 .DEFAULT => {},
4416 }
4417
4418 if (elf.targetLoad(&sym_ptr.shndx) != std.elf.SHN_UNDEF) continue;
4419
4420 const cur_info = elf.targetLoad(&sym_ptr.info);
4421 if (cur_info.type == .NOTYPE) {
4422 const new_type: std.elf.STT = switch (sym.info.type) {
4423 .GNU_IFUNC => .FUNC,
4424 else => |t| t,
4425 };
4426
4427 elf.targetStore(&sym_ptr.info, .{
4428 .bind = cur_info.bind,
4429 .type = new_type,
4430 });
4431
4432 const dynsym_ptr = @field(elf.dynsymPtr(global_ptr.dynsym_index), @tagName(class));
4433 elf.targetStore(&dynsym_ptr.info, .{
4434 .bind = elf.targetLoad(&dynsym_ptr.info).bind,
4435 .type = new_type,
4436 });
4437
4438 // If we just turned this into an STT_FUNC symbol, then we have determined
4439 // that it needs a PLT entry.
4440 if (new_type == .FUNC) {
4441 elf.addPltEntry(name, global_ptr.dynsym_index);
4442 // ...and therefore, we need to re-apply that symbol's relocations, as
4443 // some might be targeting its PLT entry.
4444 global_ptr.symtab_index.applyTargetRelocs(elf);
4445 }
4446 }
4447 }
3390 },4448 },
3391 }4449 }
3392}4450}
3393fn loadDsoExact(elf: *Elf, name: []const u8) !void {
3394 log.debug("loadDsoExact({f})", .{std.zig.fmtString(name)});
3395 if (elf.shndx.dynamic != .UNDEF) {
3396 try elf.needed.put(elf.base.comp.gpa, try elf.string(.dynstr, name), {});
3397 }
3398}
33994451
3400/// Validates that the `std.elf.Ident` present at the start of `r` is a compatible link input.4452/// Validates that the `std.elf.Ident` present at the start of `r` is a compatible link input.
3401///4453///
...@@ -3432,22 +4484,100 @@ fn checkInputIdent(...@@ -3432,22 +4484,100 @@ fn checkInputIdent(
3432 .{ident.version},4484 .{ident.version},
3433 );4485 );
34344486
3435 // OSABI is a bit more complex. On Linux, `.NONE` and `.GNU` are both valid and both common.4487 // OSABI is a bit more complex. On Linux, `.NONE` and `.GNU` are both valid and both common.
3436 // It sounds reasonable to allow the value we chose *and* allow `.NONE`.4488 // It sounds reasonable to allow the value we chose *and* allow `.NONE`.
3437 const expect_abiversion: u8 = abiver: {4489 const expect_abiversion: u8 = abiver: {
3438 if (ident.osabi == .NONE) break :abiver 0;4490 if (ident.osabi == .NONE) break :abiver 0;
3439 if (ident.osabi == target.osabi) break :abiver target.abiversion;4491 if (ident.osabi == target.osabi) break :abiver target.abiversion;
3440 return diags.failParse(4492 return diags.failParse(
3441 path,4493 path,
3442 "bad ELF OS/ABI ({?s})",4494 "bad ELF OS/ABI ({?s})",
3443 .{std.enums.tagName(std.elf.OSABI, ident.osabi)},4495 .{std.enums.tagName(std.elf.OSABI, ident.osabi)},
3444 );4496 );
4497 };
4498 if (ident.abiversion != expect_abiversion) return diags.failParse(
4499 path,
4500 "bad ELF ABI version ({d})",
4501 .{ident.abiversion},
4502 );
4503}
4504
4505fn createInitFiniArraySection(
4506 elf: *Elf,
4507 shndx: *Section.Index,
4508 comptime name: []const u8,
4509 @"type": std.elf.SHT,
4510) !void {
4511 assert(shndx.* == .UNDEF);
4512 const gpa = elf.base.comp.gpa;
4513 const addr_align: std.mem.Alignment = switch (elf.identClass()) {
4514 .NONE, _ => unreachable,
4515 .@"32" => .@"4",
4516 .@"64" => .@"8",
4517 };
4518 assert(elf.section_by_name.count() == elf.shdrs.items.len);
4519 try elf.section_by_name.ensureUnusedCapacity(gpa, 1);
4520 shndx.* = try elf.addSection(elf.ni.data_rel_ro, .{
4521 .name = "." ++ name,
4522 .type = @"type",
4523 .flags = .{ .WRITE = true, .ALLOC = true },
4524 .node_align = addr_align,
4525 });
4526 elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {});
4527 try elf.ensureUnusedSymbolCapacity(2, .maybe_global);
4528 _ = elf.addGlobalSymbolAssumeCapacity(.{
4529 .node = shndx.get(elf).ni,
4530 .name = try .string(elf, "__" ++ name ++ "_start"),
4531 .value = shndx.vaddr(elf),
4532 .size = 0,
4533 .type = .NOTYPE,
4534 .bind = .strong,
4535 .visibility = .HIDDEN,
4536 .shndx = shndx.*,
4537 }) catch |err| switch (err) {
4538 error.MultipleDefinitions => return elf.base.comp.link_diags.fail(
4539 "multiple definitions of '{s}'",
4540 .{"__" ++ name ++ "_start"},
4541 ),
4542 };
4543 _ = elf.addGlobalSymbolAssumeCapacity(.{
4544 .node = shndx.get(elf).ni,
4545 .name = try .string(elf, "__" ++ name ++ "_end"),
4546 .value = shndx.vaddr(elf),
4547 .size = 0,
4548 .type = .NOTYPE,
4549 .bind = .strong,
4550 .visibility = .HIDDEN,
4551 .shndx = shndx.*,
4552 }) catch |err| switch (err) {
4553 error.MultipleDefinitions => return elf.base.comp.link_diags.fail(
4554 "multiple definitions of '{s}'",
4555 .{"__" ++ name ++ "_end"},
4556 ),
4557 };
4558}
4559fn updateInitFiniArraySectionSize(
4560 elf: *Elf,
4561 shndx: Section.Index,
4562 comptime name: []const u8,
4563 @"type": std.elf.SHT,
4564 new_size: u64,
4565) void {
4566 if (elf.shndx.dynamic != .UNDEF) {
4567 const arraysz_dyn_key: u32 = switch (@"type") {
4568 .INIT_ARRAY => std.elf.DT_INIT_ARRAYSZ,
4569 .FINI_ARRAY => std.elf.DT_FINI_ARRAYSZ,
4570 .PREINIT_ARRAY => std.elf.DT_PREINIT_ARRAYSZ,
4571 else => unreachable,
4572 };
4573 elf.updateDynamicEntry(arraysz_dyn_key, new_size);
4574 }
4575
4576 const end_vaddr: u64 = switch (elf.shdrPtr(shndx)) {
4577 inline else => |shdr| shndx.vaddr(elf) + elf.targetLoad(&shdr.size),
3445 };4578 };
3446 if (ident.abiversion != expect_abiversion) return diags.failParse(4579 const end_sym_name = elf.string(.strtab, "__" ++ name ++ "_end") catch unreachable; // string definitely already exists
3447 path,4580 Symbol.Id.global(end_sym_name).flushMoved(elf, end_vaddr);
3448 "bad ELF ABI version ({d})",
3449 .{ident.abiversion},
3450 );
3451}4581}
34524582
3453pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) !void {4583pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) !void {
...@@ -3486,9 +4616,22 @@ fn prelinkInner(elf: *Elf) !void {...@@ -3486,9 +4616,22 @@ fn prelinkInner(elf: *Elf) !void {
3486 const ElfN = ct_class.ElfN();4616 const ElfN = ct_class.ElfN();
3487 const flags: ElfN.Addr = if (elf.options.z_now) std.elf.DF_BIND_NOW else 0;4617 const flags: ElfN.Addr = if (elf.options.z_now) std.elf.DF_BIND_NOW else 0;
3488 const flags_1: ElfN.Addr = if (elf.options.z_now) std.elf.DF_1_NOW else 0;4618 const flags_1: ElfN.Addr = if (elf.options.z_now) std.elf.DF_1_NOW else 0;
4619 const rpath: String(.dynstr) = rpath: {
4620 var buf: std.ArrayList(u8) = .empty;
4621 defer buf.deinit(gpa);
4622 for (elf.options.rpath_list, 0..) |path, i| {
4623 if (i > 0) try buf.append(gpa, ':');
4624 try buf.appendSlice(gpa, path);
4625 }
4626 break :rpath try elf.string(.dynstr, buf.items);
4627 };
3489 const needed_len = elf.needed.count();4628 const needed_len = elf.needed.count();
3490 const dynamic_len = needed_len + @intFromBool(elf.options.soname != null) +4629 const dynamic_len = needed_len + @intFromBool(elf.options.soname != null) +
4630 @intFromBool(rpath != .empty) +
3491 @intFromBool(flags != 0) + @intFromBool(flags_1 != 0) +4631 @intFromBool(flags != 0) + @intFromBool(flags_1 != 0) +
4632 @as(usize, @intFromBool(elf.shndx.init_array != .UNDEF)) * 2 +
4633 @as(usize, @intFromBool(elf.shndx.fini_array != .UNDEF)) * 2 +
4634 @as(usize, @intFromBool(elf.shndx.preinit_array != .UNDEF)) * 2 +
3492 @intFromBool(comp.config.output_mode == .Exe) + 12;4635 @intFromBool(comp.config.output_mode == .Exe) + 12;
3493 const dynamic_size: u32 = @intCast(@sizeOf(ElfN.Addr) * 2 * dynamic_len);4636 const dynamic_size: u32 = @intCast(@sizeOf(ElfN.Addr) * 2 * dynamic_len);
3494 const dynamic_ni = elf.shndx.dynamic.get(elf).ni;4637 const dynamic_ni = elf.shndx.dynamic.get(elf).ni;
...@@ -3508,6 +4651,10 @@ fn prelinkInner(elf: *Elf) !void {...@@ -3508,6 +4651,10 @@ fn prelinkInner(elf: *Elf) !void {
3508 dynamic_entries[dynamic_index] = .{ std.elf.DT_SONAME, @intFromEnum(try elf.string(.dynstr, soname)) };4651 dynamic_entries[dynamic_index] = .{ std.elf.DT_SONAME, @intFromEnum(try elf.string(.dynstr, soname)) };
3509 dynamic_index += 1;4652 dynamic_index += 1;
3510 }4653 }
4654 if (rpath != .empty) {
4655 dynamic_entries[dynamic_index] = .{ std.elf.DT_RUNPATH, @intFromEnum(rpath) };
4656 dynamic_index += 1;
4657 }
3511 if (flags != 0) {4658 if (flags != 0) {
3512 dynamic_entries[dynamic_index] = .{ std.elf.DT_FLAGS, flags };4659 dynamic_entries[dynamic_index] = .{ std.elf.DT_FLAGS, flags };
3513 dynamic_index += 1;4660 dynamic_index += 1;
...@@ -3520,23 +4667,72 @@ fn prelinkInner(elf: *Elf) !void {...@@ -3520,23 +4667,72 @@ fn prelinkInner(elf: *Elf) !void {
3520 dynamic_entries[dynamic_index] = .{ std.elf.DT_DEBUG, 0 };4667 dynamic_entries[dynamic_index] = .{ std.elf.DT_DEBUG, 0 };
3521 dynamic_index += 1;4668 dynamic_index += 1;
3522 }4669 }
3523 const rela_dyn_shndx = elf.shndx.got.get(elf).rela_shndx;4670 if (elf.shndx.init_array != .UNDEF) {
3524 const rela_plt_shndx = elf.shndx.got_plt.get(elf).rela_shndx;4671 dynamic_entries[dynamic_index..][0..2].* = .{
4672 .{ std.elf.DT_INIT_ARRAY, @intCast(elf.shndx.init_array.vaddr(elf)) },
4673 .{ std.elf.DT_INIT_ARRAYSZ, elf.targetLoad(
4674 &@field(elf.shdrPtr(elf.shndx.init_array), @tagName(ct_class)).size,
4675 ) },
4676 };
4677 try elf.ensureUnusedRelocCapacity(dynamic_ni, 1);
4678 elf.addRelocAssumeCapacity(
4679 dynamic_ni,
4680 @sizeOf(ElfN.Addr) * (2 * dynamic_index + 1),
4681 .local(elf.shndx.init_array.get(elf).lsi),
4682 0,
4683 .absAddr(elf),
4684 );
4685 dynamic_index += 2;
4686 }
4687 if (elf.shndx.fini_array != .UNDEF) {
4688 dynamic_entries[dynamic_index..][0..2].* = .{
4689 .{ std.elf.DT_FINI_ARRAY, @intCast(elf.shndx.fini_array.vaddr(elf)) },
4690 .{ std.elf.DT_FINI_ARRAYSZ, elf.targetLoad(
4691 &@field(elf.shdrPtr(elf.shndx.fini_array), @tagName(ct_class)).size,
4692 ) },
4693 };
4694 try elf.ensureUnusedRelocCapacity(dynamic_ni, 1);
4695 elf.addRelocAssumeCapacity(
4696 dynamic_ni,
4697 @sizeOf(ElfN.Addr) * (2 * dynamic_index + 1),
4698 .local(elf.shndx.fini_array.get(elf).lsi),
4699 0,
4700 .absAddr(elf),
4701 );
4702 dynamic_index += 2;
4703 }
4704 if (elf.shndx.preinit_array != .UNDEF) {
4705 dynamic_entries[dynamic_index..][0..2].* = .{
4706 .{ std.elf.DT_PREINIT_ARRAY, @intCast(elf.shndx.preinit_array.vaddr(elf)) },
4707 .{ std.elf.DT_PREINIT_ARRAYSZ, elf.targetLoad(
4708 &@field(elf.shdrPtr(elf.shndx.preinit_array), @tagName(ct_class)).size,
4709 ) },
4710 };
4711 try elf.ensureUnusedRelocCapacity(dynamic_ni, 1);
4712 elf.addRelocAssumeCapacity(
4713 dynamic_ni,
4714 @sizeOf(ElfN.Addr) * (2 * dynamic_index + 1),
4715 .local(elf.shndx.preinit_array.get(elf).lsi),
4716 0,
4717 .absAddr(elf),
4718 );
4719 dynamic_index += 2;
4720 }
3525 dynamic_entries[dynamic_index..][0..12].* = .{4721 dynamic_entries[dynamic_index..][0..12].* = .{
3526 .{ std.elf.DT_RELA, @intCast(elf.computeNodeVAddr(rela_dyn_shndx.get(elf).ni)) },4722 .{ std.elf.DT_RELA, @intCast(elf.shndx.rela_dyn.vaddr(elf)) },
3527 .{ std.elf.DT_RELASZ, elf.targetLoad(4723 .{ std.elf.DT_RELASZ, elf.targetLoad(
3528 &@field(elf.shdrPtr(rela_dyn_shndx), @tagName(ct_class)).size,4724 &@field(elf.shdrPtr(elf.shndx.rela_dyn), @tagName(ct_class)).size,
3529 ) },4725 ) },
3530 .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) },4726 .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) },
3531 .{ std.elf.DT_JMPREL, @intCast(elf.computeNodeVAddr(rela_plt_shndx.get(elf).ni)) },4727 .{ std.elf.DT_JMPREL, @intCast(elf.shndx.rela_plt.vaddr(elf)) },
3532 .{ std.elf.DT_PLTRELSZ, elf.targetLoad(4728 .{ std.elf.DT_PLTRELSZ, elf.targetLoad(
3533 &@field(elf.shdrPtr(rela_plt_shndx), @tagName(ct_class)).size,4729 &@field(elf.shdrPtr(elf.shndx.rela_plt), @tagName(ct_class)).size,
3534 ) },4730 ) },
3535 .{ std.elf.DT_PLTGOT, @intCast(elf.computeNodeVAddr(elf.shndx.got_plt.get(elf).ni)) },4731 .{ std.elf.DT_PLTGOT, @intCast(elf.shndx.got_plt.vaddr(elf)) },
3536 .{ std.elf.DT_PLTREL, std.elf.DT_RELA },4732 .{ std.elf.DT_PLTREL, std.elf.DT_RELA },
3537 .{ std.elf.DT_SYMTAB, @intCast(elf.computeNodeVAddr(elf.shndx.dynsym.get(elf).ni)) },4733 .{ std.elf.DT_SYMTAB, @intCast(elf.shndx.dynsym.vaddr(elf)) },
3538 .{ std.elf.DT_SYMENT, @sizeOf(ElfN.Sym) },4734 .{ std.elf.DT_SYMENT, @sizeOf(ElfN.Sym) },
3539 .{ std.elf.DT_STRTAB, @intCast(elf.computeNodeVAddr(elf.shndx.dynstr.get(elf).ni)) },4735 .{ std.elf.DT_STRTAB, @intCast(elf.shndx.dynstr.vaddr(elf)) },
3540 .{ std.elf.DT_STRSZ, elf.targetLoad(4736 .{ std.elf.DT_STRSZ, elf.targetLoad(
3541 &@field(elf.shdrPtr(elf.shndx.dynstr), @tagName(ct_class)).size,4737 &@field(elf.shdrPtr(elf.shndx.dynstr), @tagName(ct_class)).size,
3542 ) },4738 ) },
...@@ -3547,19 +4743,19 @@ fn prelinkInner(elf: *Elf) !void {...@@ -3547,19 +4743,19 @@ fn prelinkInner(elf: *Elf) !void {
3547 if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry|4743 if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry|
3548 std.mem.byteSwapAllFields(@TypeOf(dynamic_entry.*), dynamic_entry);4744 std.mem.byteSwapAllFields(@TypeOf(dynamic_entry.*), dynamic_entry);
35494745
3550 elf.first_dynamic_reloc = @enumFromInt(elf.relocs.items.len);4746 elf.dynamic_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len);
3551 try elf.ensureUnusedRelocCapacity(dynamic_ni, 5);4747 try elf.ensureUnusedRelocCapacity(dynamic_ni, 5);
3552 elf.addRelocAssumeCapacity(4748 elf.addRelocAssumeCapacity(
3553 dynamic_ni,4749 dynamic_ni,
3554 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 12) + 1),4750 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 12) + 1),
3555 .local(rela_dyn_shndx.get(elf).lsi),4751 .local(elf.shndx.rela_dyn.get(elf).lsi),
3556 0,4752 0,
3557 .absAddr(elf),4753 .absAddr(elf),
3558 );4754 );
3559 elf.addRelocAssumeCapacity(4755 elf.addRelocAssumeCapacity(
3560 dynamic_ni,4756 dynamic_ni,
3561 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 9) + 1),4757 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 9) + 1),
3562 .local(rela_plt_shndx.get(elf).lsi),4758 .local(elf.shndx.rela_plt.get(elf).lsi),
3563 0,4759 0,
3564 .absAddr(elf),4760 .absAddr(elf),
3565 );4761 );
...@@ -3663,7 +4859,11 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {...@@ -3663,7 +4859,11 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
3663 .type = .SECTION,4859 .type = .SECTION,
3664 .shndx = shndx,4860 .shndx = shndx,
3665 }) else .null;4861 }) else .null;
3666 elf.shdrs.appendAssumeCapacity(.{ .lsi = lsi, .ni = ni, .rela_shndx = .UNDEF, .rela_free = .none });4862 elf.shdrs.appendAssumeCapacity(.{ .lsi = lsi, .ni = ni, .rela = switch (opts.type) {
4863 .REL => unreachable,
4864 .RELA => .{ .free_head = .none },
4865 else => .{ .shndx = .UNDEF },
4866 } });
3667 elf.nodes.appendAssumeCapacity(.{ .section = shndx });4867 elf.nodes.appendAssumeCapacity(.{ .section = shndx });
3668 const offset = ni.fileLocation(&elf.mf, false).offset;4868 const offset = ni.fileLocation(&elf.mf, false).offset;
3669 switch (elf.shdrPtr(shndx)) {4869 switch (elf.shdrPtr(shndx)) {
...@@ -3689,20 +4889,23 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {...@@ -3689,20 +4889,23 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
3689fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize) !void {4889fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize) !void {
3690 if (len == 0) return;4890 if (len == 0) return;
3691 const gpa = elf.base.comp.gpa;4891 const gpa = elf.base.comp.gpa;
3692 try elf.relocs.ensureUnusedCapacity(gpa, len);4892 try elf.symbol_relocs.ensureUnusedCapacity(gpa, len);
4893 try elf.got_relocs.ensureUnusedCapacity(gpa, len);
3693 const class = elf.identClass();4894 const class = elf.identClass();
3694 const rela_shndx, const rela_len = rela: switch (elf.ehdrField(.type)) {4895 switch (elf.ehdrField(.type)) {
3695 .NONE, .CORE, _ => unreachable,4896 .NONE, .CORE, _ => unreachable,
3696 .REL => {4897 .REL => {
3697 const shndx = elf.getNodeShndx(node);4898 const shndx = elf.getNodeShndx(node);
3698 if (shndx.get(elf).rela_shndx == .UNDEF) {4899 if (shndx.get(elf).rela.shndx == .UNDEF) {
3699 var bfa_buf: [32]u8 = undefined;4900 var bfa_buf: [32]u8 = undefined;
3700 var bfa: std.heap.BufferFirstAllocator = .init(&bfa_buf, gpa);4901 var bfa: std.heap.BufferFirstAllocator = .init(&bfa_buf, gpa);
3701 const allocator = bfa.allocator();4902 const allocator = bfa.allocator();
37024903
3703 const rela_name = try std.fmt.allocPrint(allocator, ".rela{s}", .{shndx.name(elf)});4904 const rela_name = try std.fmt.allocPrint(allocator, ".rela{s}", .{shndx.name(elf).slice(elf)});
3704 defer allocator.free(rela_name);4905 defer allocator.free(rela_name);
37054906
4907 assert(elf.section_by_name.count() == elf.shdrs.items.len);
4908 try elf.section_by_name.ensureUnusedCapacity(gpa, 1);
3706 const rela_shndx = try elf.addSection(.none, .{4909 const rela_shndx = try elf.addSection(.none, .{
3707 .name = rela_name,4910 .name = rela_name,
3708 .type = .RELA,4911 .type = .RELA,
...@@ -3719,33 +4922,29 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize)...@@ -3719,33 +4922,29 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize)
3719 },4922 },
3720 .node_align = elf.mf.flags.block_size,4923 .node_align = elf.mf.flags.block_size,
3721 });4924 });
3722 shndx.get(elf).rela_shndx = rela_shndx;4925 elf.section_by_name.putAssumeCapacityNoClobber(rela_shndx.name(elf), {});
4926 shndx.get(elf).rela.shndx = rela_shndx;
3723 }4927 }
3724 break :rela .{ shndx.get(elf).rela_shndx, len };4928 try shndx.get(elf).rela.shndx.relaEnsureAdditionalCapacity(elf, len);
3725 },4929 },
3726 .EXEC, .DYN => switch (elf.got.tlsld) {4930 .EXEC, .DYN => {
3727 _ => return,4931 try elf.tls_size_symbol_relocs.ensureUnusedCapacity(gpa, len);
3728 .none => if (elf.shndx.dynamic != .UNDEF) {4932 const new_got_entries = len * 2; // at worst, every reloc is a new TLSGD
3729 try elf.mf.updates.ensureUnusedCapacity(gpa, 1);4933 try elf.got.ensureUnusedCapacity(gpa, new_got_entries);
3730 const got_ni = elf.shndx.got.get(elf).ni;4934 const got_ni = elf.shndx.got.get(elf).ni;
3731 _, const got_node_size = got_ni.location(&elf.mf).resolve(&elf.mf);4935 _, const got_node_size = got_ni.location(&elf.mf).resolve(&elf.mf);
3732 const got_size = switch (class) {4936 const need_got_size = switch (class) {
3733 .NONE, _ => unreachable,4937 .NONE, _ => unreachable,
3734 inline else => |ct_class| (elf.got.len + 2) * @sizeOf(ct_class.ElfN().Addr),4938 inline else => |ct_class| (elf.got.count() + new_got_entries) * @sizeOf(ct_class.ElfN().Addr),
3735 };4939 };
3736 if (got_size > got_node_size)4940 if (need_got_size > got_node_size)
3737 try got_ni.resize(&elf.mf, gpa, got_size +| got_size / MappedFile.growth_factor);4941 try got_ni.resize(&elf.mf, gpa, need_got_size +| need_got_size / MappedFile.growth_factor);
3738 break :rela .{ elf.shndx.got.get(elf).rela_shndx, 1 };4942
3739 } else return,4943 if (elf.shndx.dynamic != .UNDEF) {
4944 try elf.shndx.rela_dyn.relaEnsureAdditionalCapacity(elf, new_got_entries);
4945 }
3740 },4946 },
3741 };4947 }
3742 const rela_ni = rela_shndx.get(elf).ni;
3743 _, const rela_node_size = rela_ni.location(&elf.mf).resolve(&elf.mf);
3744 const rela_size = switch (elf.shdrPtr(rela_shndx)) {
3745 inline else => |shdr| elf.targetLoad(&shdr.size) + elf.targetLoad(&shdr.entsize) * rela_len,
3746 };
3747 if (rela_size > rela_node_size)
3748 try rela_ni.resize(&elf.mf, gpa, rela_size +| rela_size / MappedFile.growth_factor);
3749}4948}
3750fn addRelocAssumeCapacity(4949fn addRelocAssumeCapacity(
3751 elf: *Elf,4950 elf: *Elf,
...@@ -3753,123 +4952,443 @@ fn addRelocAssumeCapacity(...@@ -3753,123 +4952,443 @@ fn addRelocAssumeCapacity(
3753 offset: u64,4952 offset: u64,
3754 target: Symbol.Id,4953 target: Symbol.Id,
3755 addend: i64,4954 addend: i64,
3756 @"type": Reloc.Type,4955 @"type": MachineRelocType,
3757) void {4956) void {
3758 assert(node != .none);4957 assert(node != .none);
3759 const ri: Reloc.Index = @enumFromInt(elf.relocs.items.len);4958 switch (elf.ehdrField(.type)) {
3760 const next: Reloc.Index = next: {4959 .NONE, .CORE, _ => unreachable,
3761 const target_ptr = target.index(elf).ptr(elf);4960 .REL => {
3762 const next = target_ptr.first_target_reloc;4961 const rela_shndx = elf.getNodeShndx(node).get(elf).rela.shndx;
3763 target_ptr.first_target_reloc = ri;4962 const rela_index = rela_shndx.relaAddOneAssumeCapacity(elf, .{
3764 break :next next;4963 .type = @"type",
4964 // This field needs to equal the offset into the section, which is *not* necessarily
4965 // the same thing as our `offset`, which is the offset into `node`. We could compute
4966 // the section offset now, but there's no point, because `flushMovedNodeRelocs` will
4967 // eventually do it for us anyway, so just init to 0.
4968 .offset = 0,
4969 .raw_sym_index = @intFromEnum(target.index(elf)),
4970 .addend = addend,
4971 });
4972 const ri: SymbolReloc.Index = @enumFromInt(elf.symbol_relocs.items.len);
4973 const next: SymbolReloc.Index = next: {
4974 const target_ptr = target.index(elf).ptr(elf);
4975 const next = target_ptr.first_target_reloc;
4976 target_ptr.first_target_reloc = ri;
4977 break :next next;
4978 };
4979 if (next != .none) {
4980 next.get(elf).prev = ri;
4981 }
4982 elf.symbol_relocs.appendAssumeCapacity(.{
4983 .node = node,
4984 .offset = offset,
4985 .type = .write_rela,
4986 .target = target,
4987 .addend = addend,
4988 .next = next,
4989 .prev = .none,
4990 .rela_index = rela_index.toOptional(),
4991 });
4992 },
4993
4994 .DYN, .EXEC => switch (elf.ehdrField(.machine)) {
4995 else => |machine| @panic(@tagName(machine)),
4996 .X86_64 => switch (@"type".X86_64) {
4997 _,
4998 .NONE,
4999 .COPY,
5000 .GLOB_DAT,
5001 .JUMP_SLOT,
5002 .RELATIVE64,
5003 .RELATIVE,
5004 .IRELATIVE,
5005 .@"16",
5006 .PC16,
5007 .@"8",
5008 .PC8,
5009 .DTPMOD64,
5010 .GOTPLT64,
5011 => @panic("TODO: error for illegal or unsupported input relocation"),
5012
5013 // TODO: the psABI links to https://www.fsfla.org/~lxoliva/writeups/TLS/RFC-TLSDESC-x86.txt
5014 .GOTPC32_TLSDESC => @panic("TODO: R_X86_64_GOTPC32_TLSDESC"),
5015 .TLSDESC_CALL => @panic("TODO: R_X86_64_TLSDESC_CALL"),
5016 .TLSDESC => @panic("TODO: R_X86_64_TLSDESC"),
5017
5018 // Relocations targeting a symbol
5019 .@"64" => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs64),
5020 .@"32" => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32),
5021 .@"32S" => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32s),
5022 .PC64 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel64),
5023 .PC32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel32),
5024 .PLT32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .pltrel32),
5025 .SIZE64 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .size64),
5026 .SIZE32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .size32),
5027 .DTPOFF64 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .dtpoff64),
5028 .DTPOFF32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .dtpoff32),
5029 .TPOFF64 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff64),
5030 .TPOFF32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff32),
5031 .GOTPC64 => {
5032 const got_sym: Symbol.Id = .local(elf.shndx.got.get(elf).lsi);
5033 return elf.addSymbolRelocAssumeCapacity(node, offset, got_sym, addend, .rel64);
5034 },
5035 .GOTPC32 => {
5036 const got_sym: Symbol.Id = .local(elf.shndx.got.get(elf).lsi);
5037 return elf.addSymbolRelocAssumeCapacity(node, offset, got_sym, addend, .rel32);
5038 },
5039
5040 // TODO: these are the address of an arbitrary symbol (or PLT entry) relative to the
5041 // base of the GOT, which is quite annoying. Luckily, they seem to be rare, so I'm
5042 // probably just going to introduce a set (ArrayHashMap) of SymbolReloc.Index which
5043 // need to be re-applied whenever the GOT moves.
5044 .GOTOFF64 => @panic("TODO: R_X86_64_GOTOFF64"), // offset of symbol from GOT base
5045 .PLTOFF64 => @panic("TODO: R_X86_64_PLTOFF64"), // offset of PLT entry from GOT base (yes, I know, the name is stupid)
5046
5047 // Relocations targeting a GOT entry
5048 .GOT64 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .offset64),
5049 .GOT32 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .offset32),
5050 .GOTPCREL64 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .rel64),
5051 .GOTPCREL => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .rel32),
5052 // TODO: the next two are relaxable to non-GOT relocations, but I haven't figured
5053 // out how to represent relaxations yet. If we want to remove a `GotReloc` and add a
5054 // `SymbolReloc` at some point, we can't do that in `GotReloc.apply`, because that
5055 // function must be idempotent to ensure reproducible binaries. I think we would
5056 // need to do that as soon as the operation is known to be relaxable (e.g. because
5057 // we found a defininition for a non-preemptible symbol).
5058 .GOTPCRELX => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .rel32),
5059 .REX_GOTPCRELX => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .rel32),
5060
5061 .TLSGD => elf.addGotRelocAssumeCapacity(node, offset, .{ .tlsgd0 = target }, addend, .rel32),
5062 .TLSLD => elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .rel32),
5063 .GOTTPOFF => elf.addGotRelocAssumeCapacity(node, offset, .{ .tpoff = target }, addend, .rel32),
5064 },
5065 },
5066 }
5067}
5068fn addSymbolRelocAssumeCapacity(
5069 elf: *Elf,
5070 node: MappedFile.Node.Index,
5071 offset: u64,
5072 target: Symbol.Id,
5073 addend: i64,
5074 @"type": SymbolReloc.Type,
5075) void {
5076 assert(elf.ehdrField(.type) != .REL);
5077
5078 const rela_index: Section.RelaIndex.Optional = r: {
5079 if (elf.shndx.dynamic == .UNDEF) break :r .none;
5080 const rela_type: MachineRelocType = switch (elf.ehdrField(.machine)) {
5081 else => |machine| @panic(@tagName(machine)),
5082 .X86_64 => .{ .X86_64 = switch (@"type") {
5083 .write_rela => unreachable,
5084 .abs64 => .@"64",
5085 .abs32 => .@"32",
5086 .abs32s => .@"32S",
5087 .rel64 => .PC64,
5088 .rel32 => .PC32,
5089 .pltrel64 => break :r .none,
5090 .pltrel32 => break :r .none,
5091 .dtpoff64 => .DTPOFF64,
5092 .dtpoff32 => .DTPOFF32,
5093 .tpoff64 => .TPOFF64,
5094 .tpoff32 => .TPOFF32,
5095 .size64 => .SIZE64,
5096 .size32 => .SIZE32,
5097 } },
5098 };
5099 const dynsym_index: u32 = switch (target.unwrap()) {
5100 .local => break :r .none,
5101 // TODO: even if the symbol is locally defined, preemption/interposition is a
5102 // possibility, which this condition does not currently consider!
5103 .global => |name| if (elf.globals.strong_def.contains(name) or
5104 elf.globals.weak_def.contains(name))
5105 {
5106 break :r .none;
5107 } else elf.globalByName(name).?.dynsym_index,
5108 };
5109
5110 if (elf.nodeRequiresTextrel(node)) {
5111 elf.textrel_count += 1;
5112 }
5113
5114 // It currently looks like we need a runtime relocation for this.
5115 break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
5116 .type = rela_type,
5117 // This field needs to equal the offset into the section, which is *not* necessarily
5118 // the same thing as our `offset`, which is the offset into `node`. We could compute
5119 // the section offset now, but there's no point, because `flushMovedNodeRelocs` will
5120 // eventually do it for us anyway, so just init to 0.
5121 .offset = 0,
5122 .raw_sym_index = dynsym_index,
5123 .addend = addend,
5124 }).toOptional();
3765 };5125 };
5126
5127 const ri: SymbolReloc.Index = @enumFromInt(elf.symbol_relocs.items.len);
5128 const target_ptr = target.index(elf).ptr(elf);
5129 const next = target_ptr.first_target_reloc;
5130 target_ptr.first_target_reloc = ri;
3766 if (next != .none) {5131 if (next != .none) {
3767 next.get(elf).prev = ri;5132 next.get(elf).prev = ri;
3768 }5133 }
3769 elf.relocs.addOneAssumeCapacity().* = .{5134 elf.symbol_relocs.appendAssumeCapacity(.{
5135 .node = node,
5136 .offset = offset,
5137 .target = target,
5138 .addend = addend,
3770 .type = @"type",5139 .type = @"type",
3771 .prev = .none,
3772 .next = next,5140 .next = next,
5141 .prev = .none,
5142 .rela_index = rela_index,
5143 });
5144 if (@"type".dependsOnTlsSize()) {
5145 elf.tls_size_symbol_relocs.putAssumeCapacityNoClobber(ri, {});
5146 }
5147}
5148fn addGotRelocAssumeCapacity(
5149 elf: *Elf,
5150 node: MappedFile.Node.Index,
5151 offset: u64,
5152 target: GotKey,
5153 addend: i64,
5154 @"type": GotReloc.Type,
5155) void {
5156 assert(elf.ehdrField(.type) != .REL);
5157 switch (elf.getNode(node)) {
5158 .input_section,
5159 .nav,
5160 .lazy_code,
5161 .lazy_const_data,
5162 => {},
5163
5164 .section => unreachable, // cannot contain GOT relocs
5165 .uav => unreachable, // cannot contain GOT relocs
5166
5167 .file => unreachable, // cannot contain relocs
5168 .ehdr => unreachable, // cannot contain relocs
5169 .shdr => unreachable, // cannot contain relocs
5170 .segment => unreachable, // cannot contain relocs
5171 }
5172
5173 const gop = elf.got.getOrPutAssumeCapacity(target);
5174 if (!gop.found_existing) {
5175 gop.value_ptr.* = .none;
5176 const maybe_next_key: ?GotKey = switch (target) {
5177 .reserved => null,
5178 .tpoff => null,
5179 .symbol => null,
5180 .tlsld0 => .tlsld1,
5181 .tlsgd0 => |sym| .{ .tlsgd1 = sym },
5182 .tlsld1 => unreachable,
5183 .tlsgd1 => unreachable,
5184 };
5185 switch (elf.shdrPtr(elf.shndx.got)) {
5186 inline else => |got_shdr, class| {
5187 const Addr = class.ElfN().Addr;
5188 const old_size = elf.targetLoad(&got_shdr.size);
5189 const new_entry_count = @as(u32, 1) + @intFromBool(maybe_next_key != null);
5190 elf.targetStore(&got_shdr.size, @intCast(old_size + @sizeOf(Addr) * new_entry_count));
5191 },
5192 }
5193 if (maybe_next_key) |next_key| {
5194 elf.got.putAssumeCapacityNoClobber(next_key, .none);
5195 elf.updateGotEntry(gop.index);
5196 elf.updateGotEntry(gop.index + 1);
5197 } else {
5198 elf.updateGotEntry(gop.index);
5199 }
5200 }
5201
5202 elf.got_relocs.appendAssumeCapacity(.{
3773 .node = node,5203 .node = node,
5204 .offset = offset,
3774 .target = target,5205 .target = target,
3775 .index = index: switch (elf.ehdrField(.type)) {5206 .addend = addend,
3776 .NONE, .CORE, _ => unreachable,5207 .type = @"type",
3777 .REL => {5208 });
3778 const sh = elf.getNodeShndx(node).get(elf);5209}
3779 switch (elf.shdrPtr(sh.rela_shndx)) {5210fn updateGotEntry(elf: *Elf, got_index: usize) void {
3780 inline else => |shdr, class| {5211 const entry_value: union(enum) {
3781 const Rela = class.ElfN().Rela;5212 unsigned: u64,
3782 const ent_size = elf.targetLoad(&shdr.entsize);5213 signed: i64,
3783 const rela_slice = sh.rela_shndx.get(elf).ni.slice(&elf.mf);5214 reloc: struct {
3784 const index: u32 = if (sh.rela_free.unwrap()) |index| alloc_index: {5215 type: MachineRelocType,
3785 const rela: *Rela = @ptrCast(@alignCast(5216 dynsym_index: u32,
3786 rela_slice[@intCast(ent_size * index)..][0..@intCast(ent_size)],5217 },
3787 ));5218 } = switch (elf.got.keys()[got_index]) {
3788 sh.rela_free = @enumFromInt(rela.offset);5219 .reserved => .{ .unsigned = 0 },
3789 break :alloc_index index;5220 .tpoff => |sym_id| val: {
3790 } else alloc_index: {5221 // We will break from this block if we require a relocation.
3791 const old_size = elf.targetLoad(&shdr.size);5222 known: {
3792 const new_size = old_size + ent_size;5223 if (elf.base.comp.config.output_mode != .Exe) {
3793 elf.targetStore(&shdr.size, @intCast(new_size));5224 // Only the executable's per-module TLS block is at a known offset from the
3794 break :alloc_index @intCast(@divExact(old_size, ent_size));5225 // general TLS pointer.
3795 };5226 break :known;
3796 const rela: *Rela = @ptrCast(@alignCast(5227 }
3797 rela_slice[@intCast(ent_size * index)..][0..@intCast(ent_size)],5228 switch (sym_id.unwrap()) {
3798 ));5229 .local => {},
3799 // The `offset` field here needs to equal the offset into the section, which5230 .global => |name| if (elf.globals.strong_undef.contains(name) or
3800 // is *not* the same as our `offset` which is the offset into `node`. We5231 elf.globals.weak_undef.contains(name))
3801 // could calculate it now, but there's no point since `flushMovedNodeRelocs`5232 {
3802 // will eventually do that for us anyway. So for now, just set offset to 0.5233 // This is an external TLS symbol, so we don't know its offset.
3803 rela.* = .{5234 break :known;
3804 .offset = 0,
3805 .info = .{
3806 .type = @intCast(@"type".unwrap(elf)),
3807 .sym = @intCast(@intFromEnum(target.index(elf))),
3808 },
3809 .addend = @intCast(addend),
3810 };
3811 if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(Rela, rela);
3812 break :index .wrap(index);
3813 },5235 },
3814 }5236 }
3815 },5237 // It's a symbol which we define, the symbol is not interposable because we're the
3816 .EXEC, .DYN => {5238 // executable, and we know our per-module TLS block's offset because we're the
3817 switch (elf.ehdrField(.machine)) {5239 // executable. We therefore know this value!
3818 else => |machine| @panic(@tagName(machine)),5240 const tls_phndx = elf.getNode(elf.ni.tls).segment;
3819 .AARCH64, .PPC64, .RISCV => {},5241 const tls_size: u64 = switch (elf.phdrSlice()) {
3820 .X86_64 => switch (@"type".X86_64) {5242 inline else => |phdr| tls_size: {
3821 else => {},5243 assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS);
3822 .TLSLD => switch (elf.got.tlsld) {5244 break :tls_size elf.targetLoad(&phdr[tls_phndx].memsz);
3823 _ => {},5245 },
3824 .none => if (elf.shndx.dynamic != .UNDEF) {5246 };
3825 const tlsld_index = elf.got.len;5247 const sym_value = sym_id.value(elf);
3826 elf.got.tlsld = .wrap(tlsld_index);5248 break :val .{ .signed = @bitCast(sym_value -% tls_size) };
3827 elf.got.len = tlsld_index + 2;5249 }
3828 const got_addr = got_addr: switch (elf.shdrPtr(elf.shndx.got)) {5250 break :val .{
3829 inline else => |shdr, class| {5251 .reloc = .{
3830 const addr_size = @sizeOf(class.ElfN().Addr);5252 .type = switch (elf.ehdrField(.machine)) {
3831 const old_size = addr_size * tlsld_index;5253 else => |machine| @panic(@tagName(machine)),
3832 const new_size = old_size + addr_size * 2;5254 .X86_64 => .{ .X86_64 = .TPOFF64 },
3833 @memset(5255 },
3834 elf.shndx.got.get(elf).ni.slice(&elf.mf)[old_size..new_size],5256 .dynsym_index = switch (sym_id.unwrap()) {
3835 0,5257 .global => |name| elf.globalByName(name).?.dynsym_index,
3836 );5258 // TODO: I have no idea if compilers are even allowed to emit this, but if they
3837 break :got_addr elf.targetLoad(&shdr.addr) + old_size;5259 // are then I guess we need to add this local symbol to `.dynsym`?
3838 },5260 .local => @panic("TODO(Elf2): GOT tpoff entry referencing local symbol"),
5261 },
5262 },
5263 };
5264 },
5265 .symbol, .tlsgd1 => |sym_id, tag| val: {
5266 const name = switch (sym_id.unwrap()) {
5267 .local => break :val .{ .unsigned = sym_id.value(elf) },
5268 .global => |name| name,
5269 };
5270 // If the symbol is *defined* in this module, we might be able to avoid the relocation.
5271 if (elf.globals.strong_def.getPtr(name) orelse
5272 elf.globals.weak_def.getPtr(name)) |global|
5273 {
5274 // We have a definition, but it might be interposable (aka preemptible). There
5275 // are two cases where it is not and so we can (and, in fact, must) elide the
5276 // runtime relocation:
5277 // * We are the executable. Symbols from executables cannot be interposed.
5278 // * The symbol's visibility disallows interposition.
5279 if (elf.base.comp.config.output_mode == .Exe) {
5280 // No relocation needed.
5281 break :val .{ .unsigned = sym_id.value(elf) };
5282 }
5283 const visibility: std.elf.STV = switch (elf.symPtr(global.symtab_index)) {
5284 inline else => |sym| elf.targetLoad(&sym.other).visibility,
5285 };
5286 switch (visibility) {
5287 .DEFAULT => {},
5288 .INTERNAL, .HIDDEN, .PROTECTED => {
5289 // No relocation needed.
5290 break :val .{ .unsigned = sym_id.value(elf) };
5291 },
5292 }
5293 }
5294 break :val .{ .reloc = .{
5295 .type = if (tag == .symbol) .globDat(elf) else .dtpOffAddr(elf),
5296 .dynsym_index = elf.globalByName(name).?.dynsym_index,
5297 } };
5298 },
5299 .tlsgd0 => |sym| switch (elf.shndx.dynamic) {
5300 .UNDEF => .{ .unsigned = 1 }, // TLS module ID for exexcutable
5301 else => .{
5302 .reloc = .{
5303 .type = .{ .X86_64 = .DTPMOD64 },
5304 .dynsym_index = switch (sym.unwrap()) {
5305 .local => 0,
5306 .global => |name| dsi: {
5307 // Like in the `.tlsgd1` case, we need to check for a non-interposable definition.
5308 if (elf.globals.strong_def.getPtr(name) orelse
5309 elf.globals.weak_def.getPtr(name)) |global|
5310 {
5311 if (elf.base.comp.config.output_mode == .Exe) {
5312 break :dsi 0; // non-interposable definition
5313 }
5314 const visibility: std.elf.STV = switch (elf.symPtr(global.symtab_index)) {
5315 inline else => |sym_ptr| elf.targetLoad(&sym_ptr.other).visibility,
3839 };5316 };
3840 const rela_dyn_shndx = elf.shndx.got.get(elf).rela_shndx;5317 switch (visibility) {
3841 const rela_dyn_ni = rela_dyn_shndx.get(elf).ni;5318 .DEFAULT => {},
3842 switch (elf.shdrPtr(rela_dyn_shndx)) {5319 .INTERNAL, .HIDDEN, .PROTECTED => {
3843 inline else => |shdr, class| {5320 break :dsi 0; // non-interposable definition
3844 const Rela = class.ElfN().Rela;
3845 const old_size = elf.targetLoad(&shdr.size);
3846 const new_size = old_size + elf.targetLoad(&shdr.entsize);
3847 elf.targetStore(&shdr.size, new_size);
3848 const rela: *Rela = @ptrCast(@alignCast(rela_dyn_ni
3849 .slice(&elf.mf)[@intCast(old_size)..@intCast(new_size)]));
3850 rela.* = .{
3851 .offset = @intCast(got_addr),
3852 .info = .{
3853 .type = @intFromEnum(std.elf.R_X86_64.DTPMOD64),
3854 .sym = 0,
3855 },
3856 .addend = 0,
3857 };
3858 if (elf.targetEndian() != native_endian)
3859 std.mem.byteSwapAllFields(Rela, rela);
3860 },5321 },
3861 }5322 }
3862 rela_dyn_ni.resizedAssumeCapacity(&elf.mf);5323 }
3863 },5324 // `sym` is either undefined or an interposable definition, so use its
5325 // actual dynsym index.
5326 break :dsi elf.globalByName(name).?.dynsym_index;
3864 },5327 },
3865 },5328 },
3866 }5329 },
3867 break :index .none;
3868 },5330 },
3869 },5331 },
3870 .offset = offset,5332 .tlsld0 => switch (elf.shndx.dynamic) {
3871 .addend = addend,5333 .UNDEF => .{ .unsigned = 1 }, // TLS module ID for exexcutable
5334 else => .{ .reloc = .{
5335 .type = .{ .X86_64 = .DTPMOD64 },
5336 .dynsym_index = 0,
5337 } },
5338 },
5339 .tlsld1 => .{ .unsigned = 0 },
5340 };
5341
5342 // First, write to the GOT itself. If we're planning to use a relocation, we'll just write zeroes.
5343 const got_entry_addr: u64 = switch (elf.shdrPtr(elf.shndx.got)) {
5344 inline else => |got_shdr, class| got_entry_addr: {
5345 const addr_size = @sizeOf(class.ElfN().Addr);
5346 const offset = got_index * addr_size;
5347 const entry_ptr: *class.ElfN().Addr = @ptrCast(@alignCast(
5348 elf.shndx.got.get(elf).ni.slice(&elf.mf)[offset..][0..addr_size],
5349 ));
5350 entry_ptr.* = switch (entry_value) {
5351 .unsigned => |x| @intCast(x),
5352 .signed => |x| switch (class) {
5353 .NONE, _ => comptime unreachable,
5354 .@"32" => @bitCast(@as(i32, @intCast(x))),
5355 .@"64" => @bitCast(x),
5356 },
5357 .reloc => 0,
5358 };
5359 break :got_entry_addr elf.targetLoad(&got_shdr.addr) + offset;
5360 },
5361 };
5362
5363 // Then, add or remove the relocation entry if needed.
5364 if (elf.shndx.dynamic == .UNDEF) {
5365 // There are no relocations in the output file, so there's no reloc to delete and we can't
5366 // add a reloc in any case. (If we *are* requesting a reloc, it'll be because the value of
5367 // this GOT entry is not yet known, e.g. because a symbol is currently undefined.)
5368 return;
5369 }
5370 if (elf.got.values()[got_index].unwrap()) |rela_index| {
5371 // Clear the old relocation entry (although we might immediately re-use it below).
5372 elf.shndx.rela_dyn.relaDeleteOne(elf, rela_index);
5373 }
5374 elf.got.values()[got_index] = switch (entry_value) {
5375 .unsigned, .signed => .none, // no relocation needed
5376 .reloc => |reloc| elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
5377 .type = reloc.type,
5378 .offset = got_entry_addr,
5379 .raw_sym_index = reloc.dynsym_index,
5380 .addend = 0,
5381 }).toOptional(),
5382 };
5383}
5384
5385/// Returns whether a `DT_TEXTREL` dynamic entry is needed to have a runtime relocation in `node`.
5386fn nodeRequiresTextrel(elf: *Elf, node: MappedFile.Node.Index) bool {
5387 const shndx = elf.getNodeShndx(node);
5388 const shf: std.elf.SHF = switch (elf.shdrPtr(shndx)) {
5389 inline else => |shdr| elf.targetLoad(&shdr.flags).shf,
3872 };5390 };
5391 return shf.ALLOC and !shf.WRITE;
3873}5392}
38745393
3875pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {5394pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {
...@@ -3999,16 +5518,21 @@ pub fn flush(...@@ -3999,16 +5518,21 @@ pub fn flush(
3999 _ = arena;5518 _ = arena;
4000 _ = prog_node;5519 _ = prog_node;
40015520
4002 if (elf.ehdrField(.type) != .REL and5521 if (comp.config.output_mode == .Exe) {
4003 elf.shndx.dynamic == .UNDEF and5522 var any_undef = false;
4004 elf.globals.strong_undef.count() > 0)
4005 {
4006 for (elf.globals.strong_undef.keys()) |name| {5523 for (elf.globals.strong_undef.keys()) |name| {
5524 if (elf.dso_globals.contains(name)) continue;
5525 any_undef = true;
4007 comp.link_diags.addError("undefined global symbol '{s}'", .{name.slice(elf)});5526 comp.link_diags.addError("undefined global symbol '{s}'", .{name.slice(elf)});
4008 }5527 }
4009 return error.LinkFailure;5528 if (any_undef) return error.LinkFailure;
4010 }5529 }
40115530
5531 elf.updateDynamicTextrel() catch |err| switch (err) {
5532 error.OutOfMemory => |e| return e,
5533 else => |e| return elf.base.comp.link_diags.fail("updateDynamicTextrel failed: {t}", .{e}),
5534 };
5535
4012 while (try elf.idle(tid)) {}5536 while (try elf.idle(tid)) {}
40135537
4014 const entry_addr: u64 = entry: {5538 const entry_addr: u64 = entry: {
...@@ -4039,6 +5563,47 @@ pub fn flush(...@@ -4039,6 +5563,47 @@ pub fn flush(
4039 else => |e| return comp.link_diags.fail("flush write failed: {t}", .{e}),5563 else => |e| return comp.link_diags.fail("flush write failed: {t}", .{e}),
4040 };5564 };
4041}5565}
5566fn updateDynamicTextrel(elf: *Elf) !void {
5567 if (elf.shndx.dynamic == .UNDEF) return;
5568 const dynamic_ni = elf.shndx.dynamic.get(elf).ni;
5569 switch (elf.shdrPtr(elf.shndx.dynamic)) {
5570 inline else => |shdr, class| if (elf.textrel_count > 0) {
5571 const cur_size = elf.targetLoad(&shdr.size);
5572 const cur_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast(
5573 dynamic_ni.slice(&elf.mf)[0..@intCast(cur_size)],
5574 ));
5575 const has_textrel: bool = for (cur_entries) |*entry| {
5576 if (elf.targetLoad(&entry[0]) == std.elf.DT_TEXTREL) {
5577 break true;
5578 }
5579 } else false;
5580 if (!has_textrel) {
5581 // Add a DT_TEXTREL entry before the final DT_NULL entry.
5582 const new_size = cur_size + @sizeOf([2]class.ElfN().Addr);
5583 _, const node_size = dynamic_ni.location(&elf.mf).resolve(&elf.mf);
5584 if (node_size < new_size) {
5585 try dynamic_ni.resize(&elf.mf, elf.base.comp.gpa, new_size);
5586 }
5587 elf.targetStore(&shdr.size, new_size);
5588 const new_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast(
5589 dynamic_ni.slice(&elf.mf)[0..@intCast(new_size)],
5590 ));
5591 const write_entries = new_entries[new_entries.len - 2 ..][0..2];
5592 assert(elf.targetLoad(&write_entries[0][0]) == std.elf.DT_NULL);
5593 write_entries.* = .{
5594 .{ std.elf.DT_TEXTREL, 0 },
5595 .{ std.elf.DT_NULL, 0 },
5596 };
5597 if (elf.targetEndian() != native_endian) {
5598 std.mem.byteSwapAllElements([2]class.ElfN().Addr, write_entries);
5599 }
5600 }
5601 } else {
5602 // TODO: remove the DT_TEXTREL entry if there is one, because it's not necessary any
5603 // more. It won't cause any issues having it there, it's just inefficient.
5604 },
5605 }
5606}
40425607
4043pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool {5608pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool {
4044 const comp = elf.base.comp;5609 const comp = elf.base.comp;
...@@ -4093,7 +5658,7 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool {...@@ -4093,7 +5658,7 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool {
4093 return comp.link_diags.fail(5658 return comp.link_diags.fail(
4094 "linker failed to read input section '{s}' from \"{f}{f}\": {t}",5659 "linker failed to read input section '{s}' from \"{f}{f}\": {t}",
4095 .{5660 .{
4096 elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf),5661 elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf).slice(elf),
4097 ii.path(elf).fmtEscapeString(),5662 ii.path(elf).fmtEscapeString(),
4098 fmtMemberString(ii.member(elf)),5663 fmtMemberString(ii.member(elf)),
4099 e,5664 e,
...@@ -4104,6 +5669,9 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool {...@@ -4104,6 +5669,9 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool {
4104 break :task;5669 break :task;
4105 }5670 }
4106 if (elf.changed_symtab_index.pop()) |kv| {5671 if (elf.changed_symtab_index.pop()) |kv| {
5672 // We only need to do work in relocatables, because in ELF modules (non-relocatables)
5673 // our `ElfN.Rela` entries use `.dynsym` indices rather than `.symtab` indices, and
5674 // `.dynsym` indices are (at the time of writing) always immutable.
4107 if (elf.ehdrField(.type) == .REL) {5675 if (elf.ehdrField(.type) == .REL) {
4108 const sub_prog_node = elf.mf.update_prog_node.start(kv.key.slice(elf), 0);5676 const sub_prog_node = elf.mf.update_prog_node.start(kv.key.slice(elf), 0);
4109 defer sub_prog_node.end();5677 defer sub_prog_node.end();
...@@ -4111,7 +5679,11 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool {...@@ -4111,7 +5679,11 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool {
4111 var ri = sym.first_target_reloc;5679 var ri = sym.first_target_reloc;
4112 while (ri != .none) {5680 while (ri != .none) {
4113 const reloc = ri.get(elf);5681 const reloc = ri.get(elf);
4114 reloc.updateTargetIndex(elf);5682 reloc.relaSection(elf).relaUpdateSym(
5683 elf,
5684 reloc.rela_index.unwrap().?,
5685 @intFromEnum(reloc.target.index(elf)),
5686 );
4115 ri = reloc.next;5687 ri = reloc.next;
4116 }5688 }
4117 break :task;5689 break :task;
...@@ -4146,13 +5718,13 @@ fn idleProgNode(...@@ -4146,13 +5718,13 @@ fn idleProgNode(
4146 var name: [std.Progress.Node.max_name_len]u8 = undefined;5718 var name: [std.Progress.Node.max_name_len]u8 = undefined;
4147 return prog_node.start(name: switch (node) {5719 return prog_node.start(name: switch (node) {
4148 else => |tag| @tagName(tag),5720 else => |tag| @tagName(tag),
4149 .section => |shndx| shndx.name(elf),5721 .section => |shndx| shndx.name(elf).slice(elf),
4150 .input_section => |isi| {5722 .input_section => |isi| {
4151 const ii = isi.input(elf);5723 const ii = isi.input(elf);
4152 break :name std.fmt.bufPrint(&name, "{f}{f} {s}", .{5724 break :name std.fmt.bufPrint(&name, "{f}{f} {s}", .{
4153 ii.path(elf).fmtEscapeString(),5725 ii.path(elf).fmtEscapeString(),
4154 fmtMemberString(ii.member(elf)),5726 fmtMemberString(ii.member(elf)),
4155 elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf),5727 elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf).slice(elf),
4156 }) catch &name;5728 }) catch &name;
4157 },5729 },
4158 .nav => |nmi| {5730 .nav => |nmi| {
...@@ -4320,110 +5892,56 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {...@@ -4320,110 +5892,56 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {
4320 .section => |shndx| {5892 .section => |shndx| {
4321 try elf.flushFileOffset(ni);5893 try elf.flushFileOffset(ni);
4322 const addr = elf.computeNodeVAddr(ni);5894 const addr = elf.computeNodeVAddr(ni);
4323 switch (elf.shdrPtr(shndx)) {5895 const old_addr: u64, const flags: std.elf.SHF = switch (elf.shdrPtr(shndx)) {
4324 inline else => |shdr, class| {5896 inline else => |shdr| .{
4325 const flags = elf.targetLoad(&shdr.flags).shf;5897 elf.targetLoad(&shdr.addr),
4326 if (flags.ALLOC) {5898 elf.targetLoad(&shdr.flags).shf,
4327 if (elf.shndx.dynamic != .UNDEF) {5899 },
4328 if (shndx == elf.shndx.got) {5900 };
4329 const old_addr = elf.targetLoad(&shdr.addr);5901
4330 const rela_dyn_shndx = shndx.get(elf).rela_shndx;5902 if (flags.ALLOC) {
4331 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(5903 switch (elf.shdrPtr(shndx)) {
4332 rela_dyn_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(5904 inline else => |shdr| elf.targetStore(&shdr.addr, @intCast(addr)),
4333 elf.targetLoad(&@field(5905 }
4334 elf.shdrPtr(rela_dyn_shndx),
4335 @tagName(class),
4336 ).size),
4337 )],
4338 ));
4339 switch (elf.ehdrField(.machine)) {
4340 else => |machine| @panic(@tagName(machine)),
4341 .AARCH64, .PPC64, .RISCV => {},
4342 .X86_64 => for (relas) |*rela| switch (@as(
4343 std.elf.R_X86_64,
4344 @enumFromInt(elf.targetLoad(&rela.info).type),
4345 )) {
4346 else => |@"type"| @panic(@tagName(@"type")),
4347 .RELATIVE => {},
4348 .GLOB_DAT, .DTPMOD64, .DTPOFF64 => elf.targetStore(
4349 &rela.offset,
4350 @intCast(elf.targetLoad(&rela.offset) - old_addr + addr),
4351 ),
4352 },
4353 }
4354 } else if (shndx == elf.shndx.got_plt) {
4355 const target_endian = elf.targetEndian();
4356 const old_addr = elf.targetLoad(&shdr.addr);
4357 const rela_plt_shndx = shndx.get(elf).rela_shndx;
4358 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
4359 rela_plt_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(
4360 elf.targetLoad(&@field(
4361 elf.shdrPtr(rela_plt_shndx),
4362 @tagName(class),
4363 ).size),
4364 )],
4365 ));
4366 const plt_sec_slice = elf.shndx.plt_sec.get(elf).ni.slice(&elf.mf);
4367 switch (elf.ehdrField(.machine)) {
4368 else => |machine| @panic(@tagName(machine)),
4369 .AARCH64, .PPC64, .RISCV => {},
4370 .X86_64 => {
4371 for (relas) |*rela| switch (@as(
4372 std.elf.R_X86_64,
4373 @enumFromInt(elf.targetLoad(&rela.info).type),
4374 )) {
4375 else => |@"type"| @panic(@tagName(@"type")),
4376 .JUMP_SLOT => elf.targetStore(
4377 &rela.offset,
4378 @intCast(elf.targetLoad(&rela.offset) - old_addr + addr),
4379 ),
4380 };
4381 for (0..elf.got.plt.count()) |plt_index| {
4382 const slice = plt_sec_slice[16 * plt_index + 6 ..][0..4];
4383 std.mem.writeInt(
4384 i32,
4385 slice,
4386 @intCast(@as(i64, @bitCast(@as(u64, @bitCast(@as(
4387 i64,
4388 std.mem.readInt(i32, slice, target_endian),
4389 ))) -% old_addr +% addr))),
4390 target_endian,
4391 );
4392 }
4393 },
4394 }
4395 } else if (shndx == elf.shndx.plt_sec) {
4396 const target_endian = elf.targetEndian();
4397 const old_addr = elf.targetLoad(&shdr.addr);
4398 const plt_sec_slice = ni.slice(&elf.mf);
4399 switch (elf.ehdrField(.machine)) {
4400 else => |machine| @panic(@tagName(machine)),
4401 .AARCH64, .PPC64, .RISCV => {},
4402 .X86_64 => for (0..elf.got.plt.count()) |plt_index| {
4403 const slice = plt_sec_slice[16 * plt_index + 6 ..][0..4];
4404 std.mem.writeInt(
4405 i32,
4406 slice,
4407 @intCast(@as(i64, @bitCast(@as(u64, @bitCast(@as(
4408 i64,
4409 std.mem.readInt(i32, slice, target_endian),
4410 ))) -% addr +% old_addr))),
4411 target_endian,
4412 );
4413 },
4414 }
4415 }
4416 }
4417 elf.targetStore(&shdr.addr, @intCast(addr));
4418 shndx.get(elf).lsi.index().flushMoved(elf, addr);
4419 }
44205906
4421 if (shndx == elf.shndx.plt) {5907 // Update global symbols targeting this section
4422 elf.flushMovedNodeRelocs(ni, elf.targetLoad(&shdr.addr), elf.first_plt_reloc);5908 if (elf.node_global_symbols.get(ni)) |first_name| {
4423 } else if (shndx == elf.shndx.dynamic) {5909 assert(first_name != .empty);
4424 elf.flushMovedNodeRelocs(ni, elf.targetLoad(&shdr.addr), elf.first_dynamic_reloc);5910 var name = first_name;
5911 while (name != .empty) {
5912 const global = elf.globalByName(name).?;
5913 const old_sym_addr: u64 = switch (elf.symPtr(global.symtab_index)) {
5914 inline else => |sym| elf.targetLoad(&sym.value),
5915 };
5916 Symbol.Id.global(name).flushMoved(
5917 elf,
5918 old_sym_addr - old_addr + addr,
5919 );
5920 name = global.next_in_node;
4425 }5921 }
4426 },5922 }
5923
5924 Symbol.Id.local(shndx.get(elf).lsi).flushMoved(elf, addr);
5925 }
5926
5927 if (shndx == elf.shndx.got) {
5928 const rela_dyn_shndx = elf.shndx.rela_dyn;
5929 for (elf.got.values()) |opt_rela_index| {
5930 const rela_index = opt_rela_index.unwrap() orelse continue;
5931 rela_dyn_shndx.relaAdjustOffset(elf, rela_index, old_addr, addr);
5932 }
5933 for (elf.got_relocs.items) |*reloc| {
5934 reloc.apply(elf);
5935 }
5936 } else if (shndx == elf.shndx.plt) {
5937 elf.flushMovedNodeRelocs(ni, addr, elf.plt_first_symbol_reloc, .none);
5938 elf.flushMovedPltSection(.plt, old_addr, addr);
5939 } else if (shndx == elf.shndx.got_plt) {
5940 elf.flushMovedPltSection(.got_plt, old_addr, addr);
5941 } else if (shndx == elf.shndx.plt_sec) {
5942 elf.flushMovedPltSection(.plt_sec, old_addr, addr);
5943 } else if (shndx == elf.shndx.dynamic) {
5944 elf.flushMovedNodeRelocs(ni, addr, elf.dynamic_first_symbol_reloc, .none);
4427 }5945 }
4428 },5946 },
4429 .input_section => |isi| {5947 .input_section => |isi| {
...@@ -4448,7 +5966,10 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {...@@ -4448,7 +5966,10 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {
4448 .DEFAULT => elf.targetLoad(&sym.value),5966 .DEFAULT => elf.targetLoad(&sym.value),
4449 },5967 },
4450 };5968 };
4451 lsi.index().flushMoved(elf, old_sym_addr - old_section_addr + new_section_addr);5969 Symbol.Id.local(lsi).flushMoved(
5970 elf,
5971 old_sym_addr - old_section_addr + new_section_addr,
5972 );
4452 }5973 }
44535974
4454 // Update global symbols5975 // Update global symbols
...@@ -4460,26 +5981,38 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {...@@ -4460,26 +5981,38 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {
4460 const old_sym_addr: u64 = switch (elf.symPtr(global.symtab_index)) {5981 const old_sym_addr: u64 = switch (elf.symPtr(global.symtab_index)) {
4461 inline else => |sym| elf.targetLoad(&sym.value),5982 inline else => |sym| elf.targetLoad(&sym.value),
4462 };5983 };
4463 global.flushMoved(elf, old_sym_addr - old_section_addr + new_section_addr);5984 Symbol.Id.global(name).flushMoved(
5985 elf,
5986 old_sym_addr - old_section_addr + new_section_addr,
5987 );
4464 name = global.next_in_node;5988 name = global.next_in_node;
4465 }5989 }
4466 }5990 }
44675991
4468 elf.flushMovedNodeRelocs(ni, new_section_addr, isi.ptrConst(elf).first_reloc);5992 elf.flushMovedNodeRelocs(
5993 ni,
5994 new_section_addr,
5995 isi.ptrConst(elf).first_symbol_reloc,
5996 isi.ptrConst(elf).first_got_reloc,
5997 );
4469 },5998 },
4470 inline .nav, .uav, .lazy_code, .lazy_const_data => |mi| {5999 inline .nav, .uav, .lazy_code, .lazy_const_data => |mi| {
4471 const new_addr = elf.computeNodeVAddr(ni);6000 const new_addr = elf.computeNodeVAddr(ni);
4472 mi.symbol(elf).index().flushMoved(elf, new_addr);6001 Symbol.Id.local(mi.symbol(elf)).flushMoved(elf, new_addr);
4473 if (elf.node_global_symbols.get(ni)) |first_name| {6002 if (elf.node_global_symbols.get(ni)) |first_name| {
4474 assert(first_name != .empty);6003 assert(first_name != .empty);
4475 var name = first_name;6004 var name = first_name;
4476 while (name != .empty) {6005 while (name != .empty) {
4477 const global = elf.globalByName(name).?;6006 Symbol.Id.global(name).flushMoved(elf, new_addr);
4478 global.flushMoved(elf, new_addr);6007 name = elf.globalByName(name).?.next_in_node;
4479 name = global.next_in_node;
4480 }6008 }
4481 }6009 }
4482 elf.flushMovedNodeRelocs(ni, new_addr, mi.firstReloc(elf));6010 elf.flushMovedNodeRelocs(
6011 ni,
6012 new_addr,
6013 mi.firstSymbolReloc(elf),
6014 mi.firstGotReloc(elf),
6015 );
4483 },6016 },
4484 }6017 }
4485 try ni.childrenMoved(elf.base.comp.gpa, &elf.mf);6018 try ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
...@@ -4507,6 +6040,27 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void {...@@ -4507,6 +6040,27 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void {
4507 },6040 },
4508 .TLS => {6041 .TLS => {
4509 elf.targetStore(&ph.memsz, @intCast(size));6042 elf.targetStore(&ph.memsz, @intCast(size));
6043 // TPOFF relocations care about the size of the TLS segment. Re-apply
6044 // those, and also update any GOT entries from GOTTPOFF relocations.
6045 for (elf.tls_size_symbol_relocs.keys()) |reloc| {
6046 reloc.get(elf).apply(elf);
6047 }
6048 for (elf.got.keys(), 0..) |got_key, got_index| {
6049 switch (got_key) {
6050 .reserved,
6051 .symbol,
6052 .tlsld0,
6053 .tlsld1,
6054 .tlsgd0,
6055 .tlsgd1,
6056 => {
6057 @branchHint(.likely);
6058 continue;
6059 },
6060
6061 .tpoff => elf.updateGotEntry(got_index),
6062 }
6063 }
4510 return ni.childrenMoved(elf.base.comp.gpa, &elf.mf);6064 return ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
4511 },6065 },
4512 }6066 }
...@@ -4541,53 +6095,28 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void {...@@ -4541,53 +6095,28 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void {
4541 },6095 },
4542 },6096 },
4543 .section => |shndx| switch (elf.shdrPtr(shndx)) {6097 .section => |shndx| switch (elf.shdrPtr(shndx)) {
4544 inline else => |shdr, class| {6098 inline else => |shdr| {
4545 switch (elf.targetLoad(&shdr.type)) {6099 switch (elf.targetLoad(&shdr.type)) {
4546 else => unreachable,6100 else => unreachable,
6101
4547 .NULL => if (size > 0) elf.targetStore(&shdr.type, .PROGBITS),6102 .NULL => if (size > 0) elf.targetStore(&shdr.type, .PROGBITS),
4548 .PROGBITS => if (size == 0) elf.targetStore(&shdr.type, .NULL),6103 .PROGBITS => if (size == 0) elf.targetStore(&shdr.type, .NULL),
4549 .SYMTAB, .DYNAMIC, .REL, .DYNSYM => return,6104
4550 .STRTAB => {6105 .INIT_ARRAY,
4551 if (elf.shndx.dynamic != .UNDEF) {6106 .FINI_ARRAY,
4552 if (shndx == elf.shndx.dynstr) {6107 .PREINIT_ARRAY,
4553 const dynamic_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast(6108 .STRTAB,
4554 elf.shndx.dynamic.get(elf).ni.slice(&elf.mf),6109 .SYMTAB,
4555 ));6110 .DYNAMIC,
4556 for (dynamic_entries) |*dynamic_entry|6111 .REL,
4557 switch (elf.targetLoad(&dynamic_entry[0])) {6112 .RELA,
4558 else => {},6113 .DYNSYM,
4559 std.elf.DT_STRSZ => dynamic_entry[1] = shdr.size,6114 => return,
4560 };
4561 }
4562 }
4563 return;
4564 },
4565 .RELA => {
4566 if (elf.shndx.dynamic != .UNDEF) {
4567 if (shndx == elf.shndx.got.get(elf).rela_shndx) {
4568 const dynamic_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast(
4569 elf.shndx.dynamic.get(elf).ni.slice(&elf.mf),
4570 ));
4571 for (dynamic_entries) |*dynamic_entry|
4572 switch (elf.targetLoad(&dynamic_entry[0])) {
4573 else => {},
4574 std.elf.DT_RELASZ => dynamic_entry[1] = shdr.size,
4575 };
4576 } else if (shndx == elf.shndx.got_plt.get(elf).rela_shndx) {
4577 const dynamic_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast(
4578 elf.shndx.dynamic.get(elf).ni.slice(&elf.mf),
4579 ));
4580 for (dynamic_entries) |*dynamic_entry|
4581 switch (elf.targetLoad(&dynamic_entry[0])) {
4582 else => {},
4583 std.elf.DT_PLTRELSZ => dynamic_entry[1] = shdr.size,
4584 };
4585 }
4586 }
4587 return;
4588 },
4589 }6115 }
4590 if (shndx != elf.shndx.plt) {6116 if (shndx != elf.shndx.plt and
6117 shndx != elf.shndx.got and
6118 shndx != elf.shndx.got_plt)
6119 {
4591 elf.targetStore(&shdr.size, @intCast(size));6120 elf.targetStore(&shdr.size, @intCast(size));
4592 }6121 }
4593 },6122 },
...@@ -4595,6 +6124,85 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void {...@@ -4595,6 +6124,85 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void {
4595 .input_section, .nav, .uav, .lazy_code, .lazy_const_data => {},6124 .input_section, .nav, .uav, .lazy_code, .lazy_const_data => {},
4596 }6125 }
4597}6126}
6127fn updateDynamicEntry(elf: *Elf, key: u32, new_val: u64) void {
6128 switch (elf.shdrPtr(elf.shndx.dynamic)) {
6129 inline else => |shdr, class| {
6130 const dynamic_size = elf.targetLoad(&shdr.size);
6131 const dynamic_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast(
6132 elf.shndx.dynamic.get(elf).ni.slice(&elf.mf)[0..@intCast(dynamic_size)],
6133 ));
6134 for (dynamic_entries) |*dynamic_entry| {
6135 if (elf.targetLoad(&dynamic_entry[0]) == key) {
6136 elf.targetStore(&dynamic_entry[1], @intCast(new_val));
6137 }
6138 }
6139 },
6140 }
6141}
6142fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_addr: u64, addr: u64) void {
6143 const target_endian = elf.targetEndian();
6144 switch (elf.ehdrField(.machine)) {
6145 else => |machine| @panic(@tagName(machine)),
6146 .X86_64 => {
6147 switch (which) {
6148 .plt => return,
6149 .plt_sec => {
6150 // Re-apply all PLT relocations. If a symbol is in the PLT then the majority of
6151 // its relocations are probably going through the PLT, so we don't bother with
6152 // specific tracking for PLT relocations---instead just re-apply all relocations
6153 // targeting symbols with PLT entries.
6154 for (elf.plt.keys()) |sym| {
6155 sym.index(elf).applyTargetRelocs(elf);
6156 }
6157 // We also need to update all of the references from `.plt.sec` to `.got.plt`.
6158 // However, if there's also a flush pending for `.got.plt`, don't bother doing
6159 // this now, because we'll do it when `.got.plt` is flushed anyway.
6160 if (elf.shndx.got_plt.get(elf).ni.hasMoved(&elf.mf)) {
6161 return;
6162 }
6163 // Exit this `switch` to update those references.
6164 },
6165 .got_plt => {
6166 // Update the offsets of the relocation entries in `.rela.plt`.
6167 const rela_plt_shndx = elf.shndx.rela_plt;
6168 for (0..elf.plt.count()) |plt_index| {
6169 if (elf.pltEntryIsDead(plt_index)) continue;
6170 rela_plt_shndx.relaAdjustOffset(elf, @enumFromInt(plt_index), old_addr, addr);
6171 }
6172 // We also need to update all of the references from `.plt.sec` to `.got.plt`.
6173 // However, if there's also a flush pending for `.plt.sec`, don't bother doing
6174 // this now, because we'll do it when `.plt.sec` is flushed anyway.
6175 if (elf.shndx.plt_sec.get(elf).ni.hasMoved(&elf.mf)) {
6176 return;
6177 }
6178 // Exit this `switch` to update those references.
6179 },
6180 }
6181 // We are updating the references from `.plt.sec` to `.got.plt`.
6182 const got_plt_addr = elf.shndx.got_plt.vaddr(elf);
6183 const plt_sec_addr = elf.shndx.plt_sec.vaddr(elf);
6184 const plt_sec_slice = elf.shndx.plt_sec.get(elf).ni.slice(&elf.mf);
6185 switch (elf.identClass()) {
6186 .NONE, _ => unreachable,
6187 inline else => |class| {
6188 const Addr = class.ElfN().Addr;
6189 for (0..elf.plt.count()) |plt_index| {
6190 const plt_sec_offset = 16 * plt_index;
6191 const got_plt_offset = @sizeOf(Addr) * (3 + plt_index);
6192 std.mem.writeInt(
6193 i32,
6194 plt_sec_slice[plt_sec_offset + 6 ..][0..4],
6195 @intCast(@as(i64, @bitCast(
6196 (got_plt_addr + got_plt_offset) -% (plt_sec_addr + plt_sec_offset + 10),
6197 ))),
6198 target_endian,
6199 );
6200 }
6201 },
6202 }
6203 },
6204 }
6205}
45986206
4599pub fn updateExports(6207pub fn updateExports(
4600 elf: *Elf,6208 elf: *Elf,
...@@ -4633,7 +6241,7 @@ fn updateExportsInner(...@@ -4633,7 +6241,7 @@ fn updateExportsInner(
4633 const exported_lsi: Symbol.LocalIndex, const @"type": std.elf.STT = switch (exported) {6241 const exported_lsi: Symbol.LocalIndex, const @"type": std.elf.STT = switch (exported) {
4634 .nav => |nav| .{6242 .nav => |nav| .{
4635 (try elf.navMapIndex(zcu, nav)).symbol(elf),6243 (try elf.navMapIndex(zcu, nav)).symbol(elf),
4636 navType(ip, ip.getNav(nav).resolved.?, elf.base.comp.config.any_non_single_threaded),6244 elf.navType(ip.getNav(nav).resolved.?),
4637 },6245 },
4638 .uav => |uav| .{ (try elf.uavMapIndex(uav, .none)).symbol(elf), .OBJECT },6246 .uav => |uav| .{ (try elf.uavMapIndex(uav, .none)).symbol(elf), .OBJECT },
4639 };6247 };
...@@ -4735,13 +6343,13 @@ pub fn printNode(...@@ -4735,13 +6343,13 @@ pub fn printNode(
4735 try w.writeByte(')');6343 try w.writeByte(')');
4736 },6344 },
4737 },6345 },
4738 .section => |shndx| try w.print("({s})", .{shndx.name(elf)}),6346 .section => |shndx| try w.print("({s})", .{shndx.name(elf).slice(elf)}),
4739 .input_section => |isi| {6347 .input_section => |isi| {
4740 const ii = isi.input(elf);6348 const ii = isi.input(elf);
4741 try w.print("({f}{f}, {s})", .{6349 try w.print("({f}{f}, {s})", .{
4742 ii.path(elf).fmtEscapeString(),6350 ii.path(elf).fmtEscapeString(),
4743 fmtMemberString(ii.member(elf)),6351 fmtMemberString(ii.member(elf)),
4744 elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf),6352 elf.getNode(isi.node(elf).parent(&elf.mf)).section.name(elf).slice(elf),
4745 });6353 });
4746 },6354 },
4747 .nav => |nmi| {6355 .nav => |nmi| {
src/link/MappedFile.zig+90-6
...@@ -305,12 +305,20 @@ pub const Node = extern struct {...@@ -305,12 +305,20 @@ pub const Node = extern struct {
305 }305 }
306 }306 }
307307
308 pub fn realign(ni: Node.Index, mf: *MappedFile, new_alignment: std.mem.Alignment) void {308 /// Moves and expands a node such that its offset and size are aligned to `new_alignment`.
309 ni.get(mf).flags.alignment = new_alignment;309 ///
310310 /// Asserts that `ni` is not `Node.Index.root`.
311 const old_offset, const old_size = ni.location(mf).resolve(mf);311 pub fn realign(
312 if (!new_alignment.check(@intCast(old_offset)) or !new_alignment.check(@intCast(old_size))) {312 ni: Node.Index,
313 @panic("TODO MappedFile.realign");313 mf: *MappedFile,
314 gpa: std.mem.Allocator,
315 new_alignment: std.mem.Alignment,
316 ) !void {
317 try mf.realignNode(gpa, ni, new_alignment);
318 var writers_it = mf.writers.first;
319 while (writers_it) |writer_node| : (writers_it = writer_node.next) {
320 const w: *Node.Writer = @fieldParentPtr("writer_node", writer_node);
321 w.interface.buffer = w.ni.slice(mf);
314 }322 }
315 }323 }
316324
...@@ -852,6 +860,82 @@ fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested...@@ -852,6 +860,82 @@ fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested
852 }860 }
853}861}
854862
863fn realignNode(
864 mf: *MappedFile,
865 gpa: std.mem.Allocator,
866 ni: Node.Index,
867 new_alignment: std.mem.Alignment,
868) !void {
869 assert(ni != Node.Index.root); // currently unsupported
870
871 const node = ni.get(mf);
872 const old_offset, const size = node.location().resolve(mf);
873
874 assert(new_alignment.compare(.gt, node.flags.alignment));
875
876 defer if (std.debug.runtime_safety) mf.verify();
877
878 node.flags.alignment = new_alignment;
879
880 const new_size = node.flags.alignment.forward(@intCast(size));
881 if (new_alignment.check(@intCast(old_offset))) {
882 if (new_size > size) try mf.resizeNode(gpa, ni, new_size);
883 return;
884 }
885
886 _, const parent_size = node.parent.location(mf).resolve(mf);
887 const trailing_end = trailing_end: switch (node.next) {
888 .none => parent_size,
889 else => |next_ni| {
890 const next_offset, _ = next_ni.location(mf).resolve(mf);
891 break :trailing_end next_offset;
892 },
893 };
894
895 const forward_offset = new_alignment.forward(@intCast(old_offset));
896 if (forward_offset + new_size <= trailing_end) {
897 // Shift into the free space if possible
898 try mf.ensureCapacityForSetLocation(gpa);
899 if (node.flags.has_content) {
900 const old_file_offset = ni.fileLocation(mf, false).offset;
901 const new_file_offset = (old_file_offset - old_offset) + forward_offset;
902 if (new_file_offset < old_file_offset + size) {
903 @memmove(
904 mf.memory_map.memory[@intCast(new_file_offset)..][0..@intCast(size)],
905 mf.memory_map.memory[@intCast(old_file_offset)..][0..@intCast(size)],
906 );
907 } else try mf.moveRange(old_file_offset, new_file_offset, size);
908 @memset(mf.memory_map.memory[@intCast(new_file_offset + size)..][0..@intCast(new_size - size)], 0);
909 }
910
911 ni.setLocationAssumeCapacity(mf, forward_offset, new_size);
912 } else {
913 const temp_size = node.flags.alignment.forward(@intCast(new_size + 1));
914 try mf.resizeNode(gpa, ni, temp_size);
915 const new_offset, _ = ni.location(mf).resolve(mf);
916
917 try mf.ensureCapacityForSetLocation(gpa);
918
919 // Non-fixed nodes may now be aligned if the resize moved them
920 const new_forward_offset = new_alignment.forward(@intCast(new_offset));
921 const final_offset = if (new_forward_offset != new_offset) final_offset: {
922 if (node.flags.has_content) {
923 const old_file_offset = ni.fileLocation(mf, false).offset;
924 const new_file_offset = (old_file_offset - new_offset) + new_forward_offset;
925 @memmove(
926 mf.memory_map.memory[@intCast(new_file_offset)..][0..@intCast(size)],
927 mf.memory_map.memory[@intCast(old_file_offset)..][0..@intCast(size)],
928 );
929 @memset(mf.memory_map.memory[@intCast(old_file_offset)..@intCast(new_file_offset)], 0);
930 }
931
932 break :final_offset new_forward_offset;
933 } else new_offset;
934
935 ni.setLocationAssumeCapacity(mf, final_offset, new_size);
936 }
937}
938
855fn moveRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size: u64) !void {939fn moveRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size: u64) !void {
856 // make a copy of this node at the new location940 // make a copy of this node at the new location
857 try mf.copyRange(old_file_offset, new_file_offset, size);941 try mf.copyRange(old_file_offset, new_file_offset, size);