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()
5656 FIND_AND_ADD_CLANG_LIB(clangAnalysisLifetimeSafety)
5757 FIND_AND_ADD_CLANG_LIB(clangAnalysis)
5858 FIND_AND_ADD_CLANG_LIB(clangASTMatchers)
59 FIND_AND_ADD_CLANG_LIB(clangAST)
6059 FIND_AND_ADD_CLANG_LIB(clangParse)
61 FIND_AND_ADD_CLANG_LIB(clangSema)
6260 FIND_AND_ADD_CLANG_LIB(clangAPINotes)
63 FIND_AND_ADD_CLANG_LIB(clangBasic)
6461 FIND_AND_ADD_CLANG_LIB(clangEdit)
6562 FIND_AND_ADD_CLANG_LIB(clangLex)
6663 FIND_AND_ADD_CLANG_LIB(clangRewriteFrontend)
......@@ -73,6 +70,7 @@ else()
7370 FIND_AND_ADD_CLANG_LIB(clangSupport)
7471 FIND_AND_ADD_CLANG_LIB(clangInstallAPI)
7572 FIND_AND_ADD_CLANG_LIB(clangAST)
73 FIND_AND_ADD_CLANG_LIB(clangBasic)
7674endif()
7775
7876if (MSVC)
src/codegen/x86_64/Emit.zig+6-2
......@@ -850,8 +850,12 @@ fn encodeInst(emit: *Emit, lowered_inst: Instruction, reloc_info: []const RelocI
850850 emit.atom_id,
851851 end_offset - 4,
852852 target.symbol,
853 reloc.off,
854 .{ .X86_64 = .@"32S" },
853 if (emit.pic) reloc.off - 4 else reloc.off,
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 } },
855859 ) else if (emit.bin_file.cast(.coff2)) |coff| try coff.addReloc(
856860 @enumFromInt(@intFromEnum(emit.atom_id)),
857861 end_offset - 4,
src/link/Elf2.zig+2558-950
......@@ -34,6 +34,12 @@ shndx: struct {
3434 dynstr: Section.Index,
3535 dynamic: Section.Index,
3636 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,
3743},
3844symtab: std.ArrayList(Symbol),
3945globals: struct {
......@@ -50,16 +56,34 @@ globals: struct {
5056/// We use a separate hash map for this data rather than storing it in `navs` etc to save memory,
5157/// because the vast majority of nodes which can export global symbols actually will not.
5258node_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),
5367shstrtab: StringTable,
5468strtab: StringTable,
5569dynstr: StringTable,
56got: struct {
57 len: u32,
58 tlsld: GotIndex,
59 plt: std.AutoArrayHashMapUnmanaged(Symbol.Id, void),
60},
61first_plt_reloc: Reloc.Index,
62first_dynamic_reloc: Reloc.Index,
70
71/// Indices map 1--1 to indices into the actual `.got` section.
72///
73/// Value is the output relocation in `.rela.dyn` for the GOT entry.
74got: std.array_hash_map.Auto(GotKey, Section.RelaIndex.Optional),
75/// Indices map 1--1 to indices into the actual `.got.plt` section. These also equal indices into
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
6387needed: std.AutoArrayHashMapUnmanaged(String(.dynstr), void),
6488inputs: std.ArrayList(struct {
6589 path: std.Build.Cache.Path,
......@@ -69,29 +93,42 @@ inputs: std.ArrayList(struct {
6993input_sections: std.ArrayList(InputSection),
7094input_section_pending_index: u32,
7195navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, struct {
72 /// The start index of the contiguous sequence of relocations in this NAV.
73 first_reloc: Reloc.Index,
7496 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,
75101}),
76102uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, struct {
77 /// The start index of the contiguous sequence of relocations in this UAV.
78 first_reloc: Reloc.Index,
79103 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.
80107}),
81108lazy: std.EnumArray(link.File.LazySymbol.Kind, struct {
82109 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,
85110 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,
86115 }),
87116 pending_index: u32,
88117}),
89118pending_uavs: std.ArrayList(Node.UavMapIndex),
90relocs: std.ArrayList(Reloc),
91
119symbol_relocs: std.ArrayList(SymbolReloc),
120got_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),
92125/// Key is the name of a global symbol which has been moved to a new symtab index. Any relocation
93126/// entries which target that symbol must be updated to reference the correct symbol index.
94127changed_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
96133const_prog_node: std.Progress.Node,
97134synth_prog_node: std.Progress.Node,
......@@ -106,21 +143,21 @@ const Node = union(enum) {
106143 shdr,
107144 /// Cannot contain relocations.
108145 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`.
110147 ///
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`.
112149 ///
113150 /// Otherwise, cannot contain relocations.
114151 section: Section.Index,
115 /// May contain relocations through the `first_reloc` field in `elf.input_sections`.
152 /// May contain relocations.
116153 input_section: InputSection.Index,
117 /// May contain relocations through the `first_reloc` field in `elf.navs`.
154 /// May contain relocations.
118155 nav: NavMapIndex,
119 /// May contain relocations through the `first_reloc` field in `elf.uavs`.
156 /// May contain relocations.
120157 uav: UavMapIndex,
121 /// May contain relocations through the `first_reloc` field in `elf.lazy.map`.
158 /// May contain relocations.
122159 lazy_code: LazyMapRef.Index(.code),
123 /// May contain relocations through the `first_reloc` field in `elf.lazy.map`.
160 /// May contain relocations.
124161 lazy_const_data: LazyMapRef.Index(.const_data),
125162
126163 pub const InputIndex = enum(u32) {
......@@ -162,8 +199,11 @@ const Node = union(enum) {
162199 return elf.navs.values()[@intFromEnum(nmi)].lsi;
163200 }
164201
165 fn firstReloc(nmi: NavMapIndex, elf: *const Elf) Reloc.Index {
166 return elf.navs.values()[@intFromEnum(nmi)].first_reloc;
202 fn firstSymbolReloc(nmi: NavMapIndex, elf: *const Elf) SymbolReloc.Index {
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;
167207 }
168208 };
169209
......@@ -178,8 +218,13 @@ const Node = union(enum) {
178218 return elf.uavs.values()[@intFromEnum(umi)].lsi;
179219 }
180220
181 fn firstReloc(umi: UavMapIndex, elf: *const Elf) Reloc.Index {
182 return elf.uavs.values()[@intFromEnum(umi)].first_reloc;
221 fn firstSymbolReloc(umi: UavMapIndex, elf: *const Elf) SymbolReloc.Index {
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;
183228 }
184229 };
185230
......@@ -203,8 +248,11 @@ const Node = union(enum) {
203248 return lmi.ref().symbol(elf);
204249 }
205250
206 fn firstReloc(lmi: @This(), elf: *const Elf) Reloc.Index {
207 return lmi.ref().firstReloc(elf);
251 fn firstSymbolReloc(lmi: @This(), elf: *const Elf) SymbolReloc.Index {
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;
208256 }
209257 };
210258 }
......@@ -216,10 +264,6 @@ const Node = union(enum) {
216264 pub fn symbol(lmr: LazyMapRef, elf: *const Elf) Symbol.LocalIndex {
217265 return elf.lazy.getPtrConst(lmr.kind).map.values()[lmr.index].lsi;
218266 }
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 }
223267 };
224268
225269 pub const Known = struct {
......@@ -255,8 +299,10 @@ const InputSection = struct {
255299 vaddr: u64,
256300 /// The node corresponding to this input section.
257301 node: MappedFile.Node.Index,
258 /// The start index of the contiguous sequence of relocations in this input section.
259 first_reloc: Reloc.Index,
302 /// The start index of the contiguous sequence of symbol relocations in this input section.
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
261307 const Index = enum(u32) {
262308 _,
......@@ -290,21 +336,53 @@ const Section = struct {
290336 ///
291337 /// If the section does not have flag `std.elf.SHF.ALLOC`, this is `.null`.
292338 lsi: Symbol.LocalIndex,
293 rela_shndx: Section.Index,
294 rela_free: RelIndex,
339 rela: union {
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) {
297369 none,
298370 _,
299371
300 pub fn wrap(i: ?u32) RelIndex {
301 return @enumFromInt((i orelse return .none) + 1);
302 }
303 pub fn unwrap(ri: RelIndex) ?u32 {
304 return switch (ri) {
305 .none => null,
306 _ => @intFromEnum(ri) - 1,
307 };
372 const Optional = enum(u32) {
373 none = std.math.maxInt(u32),
374 _,
375
376 fn unwrap(opt: RelaIndex.Optional) ?RelaIndex {
377 return switch (opt) {
378 .none => null,
379 _ => @enumFromInt(@intFromEnum(opt)),
380 };
381 }
382 };
383
384 fn toOptional(i: RelaIndex) RelaIndex.Optional {
385 return @enumFromInt(@intFromEnum(i));
308386 }
309387 };
310388
......@@ -357,11 +435,10 @@ const Section = struct {
357435 return &elf.shdrs.items[@intFromEnum(s)];
358436 }
359437
360 fn name(s: Index, elf: *Elf) [:0]const u8 {
361 const str: String(.shstrtab) = switch (elf.shdrPtr(s)) {
438 fn name(s: Index, elf: *Elf) String(.shstrtab) {
439 return switch (elf.shdrPtr(s)) {
362440 inline else => |shdr| @enumFromInt(elf.targetLoad(&shdr.name)),
363441 };
364 return str.slice(elf);
365442 }
366443
367444 fn vaddr(s: Index, elf: *Elf) u64 {
......@@ -377,9 +454,690 @@ const Section = struct {
377454 inline else => |shdr| elf.targetStore(&shdr.name, @intFromEnum(shstrtab_entry)),
378455 }
379456 }
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 }
380661 };
381662};
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
3831141fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe_global }) !void {
3841142 const gpa = elf.base.comp.gpa;
3851143
......@@ -426,59 +1184,70 @@ fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe
4261184 try elf.shndx.dynsym.get(elf).ni.resize(&elf.mf, gpa, new_size);
4271185 }
4281186
429 try elf.got.plt.ensureUnusedCapacity(gpa, len);
430 const need_plt_capacity = elf.got.plt.count() + len;
1187 try elf.ensureUnusedPltCapacity(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)) {
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 }
1195 try elf.shndx.rela_plt.relaEnsureAdditionalCapacity(elf, len);
4421196
443 // Ensure the `.got.plt` section's node is big enough
444 const got_plt_need_size: usize = switch (elf.identClass()) {
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 }
1197 try elf.plt.ensureUnusedCapacity(gpa, len);
1198 const need_plt_capacity = elf.plt.count() + len;
4531199
454 // Ensure the `.plt.sec` section's node is big enough
455 const plt_sec_need_size: usize = 16 * need_plt_capacity;
456 _, const plt_sec_cur_size = elf.shndx.plt_sec.get(elf).ni.location(&elf.mf).resolve(&elf.mf);
457 if (plt_sec_cur_size < plt_sec_need_size) {
458 const new_size = plt_sec_need_size +| plt_sec_need_size / MappedFile.growth_factor;
459 try elf.shndx.plt_sec.get(elf).ni.resize(&elf.mf, gpa, new_size);
460 }
1200 switch (elf.ehdrField(.machine)) {
1201 else => |machine| @panic(@tagName(machine)),
1202 .X86_64 => {
1203 // Ensure the `.plt` section's node is big enough
1204 const plt_need_size: usize = 16 * (1 + need_plt_capacity);
1205 _, const plt_cur_size = elf.shndx.plt.get(elf).ni.location(&elf.mf).resolve(&elf.mf);
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 enough
463 const rela_plt_shndx = elf.shndx.got_plt.get(elf).rela_shndx;
464 const rela_plt_need_size: usize = switch (elf.shdrPtr(rela_plt_shndx)) {
465 inline else => |shdr| @intCast(elf.targetLoad(&shdr.entsize) * need_plt_capacity),
466 };
467 _, const rela_plt_cur_size = rela_plt_shndx.get(elf).ni.location(&elf.mf).resolve(&elf.mf);
468 if (rela_plt_cur_size < rela_plt_need_size) {
469 const new_size = rela_plt_need_size +| rela_plt_need_size / MappedFile.growth_factor;
470 try rela_plt_shndx.get(elf).ni.resize(&elf.mf, gpa, new_size);
471 } else {
472 // Still mark `.rela.plt` as resized so that the DT_PLTRELSZ entry can
473 // be updated if we do indeed add a PLT entry.
474 try rela_plt_shndx.get(elf).ni.resized(gpa, &elf.mf);
475 }
476 },
477 }
1211 // Ensure the `.got.plt` section's node is big enough
1212 const got_plt_need_size: usize = switch (elf.identClass()) {
1213 .NONE, _ => unreachable,
1214 inline else => |class| @sizeOf(class.ElfN().Addr) * (3 + need_plt_capacity),
1215 };
1216 _, const got_plt_cur_size = elf.shndx.got_plt.get(elf).ni.location(&elf.mf).resolve(&elf.mf);
1217 if (got_plt_cur_size < got_plt_need_size) {
1218 const new_size = got_plt_need_size +| got_plt_need_size / MappedFile.growth_factor;
1219 try elf.shndx.got_plt.get(elf).ni.resize(&elf.mf, gpa, new_size);
1220 }
1221
1222 // Ensure the `.plt.sec` section's node is big enough
1223 const plt_sec_need_size: usize = 16 * need_plt_capacity;
1224 _, const plt_sec_cur_size = elf.shndx.plt_sec.get(elf).ni.location(&elf.mf).resolve(&elf.mf);
1225 if (plt_sec_cur_size < plt_sec_need_size) {
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);
4781228 }
4791229 },
4801230 }
4811231}
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
4831252const AddLocalSymbolOptions = struct {
4841253 node: MappedFile.Node.Index,
......@@ -695,6 +1464,11 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
6951464 .weak => .WEAK,
6961465 };
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
6981472 const sym_index: Symbol.Index = @enumFromInt(elf.symtab.items.len);
6991473 elf.symtab.appendAssumeCapacity(.{
7001474 .node = opts.node,
......@@ -713,7 +1487,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
7131487 .name = @intFromEnum(opts.name.strtab),
7141488 .value = @intCast(opts.value),
7151489 .size = @intCast(opts.size),
716 .info = .{ .type = opts.type, .bind = bind },
1490 .info = .{ .type = @"type", .bind = bind },
7171491 .other = .{ .visibility = opts.visibility },
7181492 .shndx = opts.shndx.toSection().?,
7191493 };
......@@ -749,7 +1523,7 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
7491523 .name = @intFromEnum(opts.name.dynstr),
7501524 .value = @intCast(opts.value),
7511525 .size = @intCast(opts.size),
752 .info = .{ .type = opts.type, .bind = bind },
1526 .info = .{ .type = @"type", .bind = bind },
7531527 .other = .{ .visibility = opts.visibility },
7541528 .shndx = opts.shndx.toSection().?,
7551529 };
......@@ -778,12 +1552,13 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
7781552 if (new_global_ptr.dynsym_index != 0 and
7791553 opts.visibility == .DEFAULT and
7801554 opts.shndx == .UNDEF and
781 opts.type == .FUNC)
1555 (@"type" == .FUNC or @"type" == std.elf.STT.GNU_IFUNC))
7821556 {
7831557 // 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 remove
785 // the PLT entry if we later discover a link inpu which resolves this reference.
1558 // We therefore might need a PLT entry, so let's add one now.
7861559 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`.
7871562 }
7881563
7891564 return .global(opts.name.strtab);
......@@ -800,6 +1575,7 @@ fn setGlobalSymbolValue(
8001575 shndx: Section.Index,
8011576 },
8021577) void {
1578 assert(new.shndx != .UNDEF);
8031579 const old_node = global_ptr.symtab_index.ptr(elf).node;
8041580 if (old_node != .none) {
8051581 if (global_ptr.next_in_node != .empty) {
......@@ -874,7 +1650,40 @@ fn setGlobalSymbolValue(
8741650 },
8751651 };
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);
8781687}
8791688/// When the same global symbol appears in two inputs---even if one symbol is defined and the other
8801689/// 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 {
10091818 if (elf.targetEndian() != native_endian) {
10101819 std.mem.byteSwapAllFields(class.ElfN().Sym, dynsym);
10111820 }
1821 global_ptr.dynsym_index = 0;
10121822 }
10131823 },
10141824 }
10151825}
10161826fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void {
10171827 const target_endian = elf.targetEndian();
1018 const plt_index: u32 = @intCast(elf.got.plt.count());
1019 elf.got.plt.putAssumeCapacityNoClobber(.global(global_name), {});
1828
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
10201860 switch (elf.ehdrField(.machine)) {
10211861 else => |machine| @panic(@tagName(machine)),
10221862 .X86_64 => {
......@@ -1044,12 +1884,12 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void
10441884 },
10451885 };
10461886
1047 const got_plt_shndx = elf.shndx.got_plt;
10481887 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)) {
10501889 inline else => |shdr, class| {
10511890 const ent_size = @sizeOf(class.ElfN().Addr);
10521891 const old_size = ent_size * (3 + plt_index);
1892 assert(elf.targetLoad(&shdr.size) == old_size);
10531893 elf.targetStore(&shdr.size, old_size + ent_size);
10541894 std.mem.writeInt(
10551895 class.ElfN().Addr,
......@@ -1057,9 +1897,8 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void
10571897 @intCast(plt_addr),
10581898 target_endian,
10591899 );
1060 break :got_plt_addr elf.targetLoad(&shdr.addr) + old_size;
10611900 },
1062 };
1901 }
10631902
10641903 const plt_sec_ni = elf.shndx.plt_sec.get(elf).ni;
10651904 switch (elf.shdrPtr(elf.shndx.plt_sec)) {
......@@ -1082,30 +1921,6 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void
10821921 );
10831922 },
10841923 }
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 }
11091924 },
11101925 }
11111926}
......@@ -1113,13 +1928,13 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void
11131928const Symbol = struct {
11141929 /// The node which this symbol's value is defined relative to. Possible values are:
11151930 /// * `.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)
11171932 /// * An input section (the symbol's value is some vaddr in that input section)
11181933 /// * A NAV, UAV, or lazy code/data (the symbol's value is exactly the vaddr of that node)
11191934 node: MappedFile.Node.Index,
11201935
11211936 /// The head of a linked list of relocations targeting this symbol.
1122 first_target_reloc: Reloc.Index,
1937 first_target_reloc: SymbolReloc.Index,
11231938
11241939 const Global = struct {
11251940 /// The current index of the symtab entry for this global symbol.
......@@ -1136,16 +1951,6 @@ const Symbol = struct {
11361951 ///
11371952 /// If `node` is `.none`, this is `.empty`.
11381953 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 }
11491954 };
11501955
11511956 /// An index directly into the symtab. These values are not stable (global symbols are sometimes
......@@ -1158,23 +1963,20 @@ const Symbol = struct {
11581963 null = 0,
11591964 _,
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 }
11751966 fn ptr(si: Symbol.Index, elf: *Elf) *Symbol {
11761967 return &elf.symtab.items[@intFromEnum(si)];
11771968 }
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 }
11781980 };
11791981
11801982 /// A `LocalIndex` is a raw index into the symtab like `Index`, but it guarantees that the
......@@ -1239,6 +2041,44 @@ const Symbol = struct {
12392041 };
12402042 }
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
12422082 /// Returns `true` if the target of `s` has moved, meaning the symbol's value will change at
12432083 /// some point due to a call to `flushMoved`.
12442084 fn hasMoved(s: Symbol.Id, elf: *Elf) bool {
......@@ -1305,7 +2145,8 @@ pub fn lazySymbol(elf: *Elf, lazy: link.File.LazySymbol) !link.File.SymbolId {
13052145 .type = sym_type,
13062146 .shndx = shndx,
13072147 }),
1308 .first_reloc = .none,
2148 .first_symbol_reloc = .none,
2149 .first_got_reloc = .none,
13092150 };
13102151 elf.nodes.appendAssumeCapacity(switch (lazy.kind) {
13112152 .code => .{ .lazy_code = @enumFromInt(gop.index) },
......@@ -1354,7 +2195,7 @@ pub fn addReloc(
13542195 offset: u64,
13552196 target: link.File.SymbolId,
13562197 addend: i64,
1357 @"type": Reloc.Type,
2198 @"type": MachineRelocType,
13582199) !void {
13592200 const node: MappedFile.Node.Index = Node.fromAtom(atom);
13602201 try elf.ensureUnusedRelocCapacity(node, 1);
......@@ -1368,7 +2209,7 @@ pub fn navSymbol(elf: *Elf, nav_index: InternPool.Nav.Index) !link.File.SymbolId
13682209 return elf.externSymbol(.{
13692210 .name = @"extern".name.toSlice(ip),
13702211 .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.?),
13722213 .linkage = @"extern".linkage,
13732214 .visibility = @"extern".visibility,
13742215 });
......@@ -1507,310 +2348,44 @@ const StringTable = struct {
15072348 key,
15082349 StringTable.Adapter{ .slice = slice_const },
15092350 .{ .slice = slice_const },
1510 );
1511 if (gop.found_existing) return gop.key_ptr.*;
1512 try ni.resized(gpa, &elf.mf);
1513 const old_size, const new_size = size: switch (elf.shdrPtr(shndx)) {
1514 inline else => |shdr| {
1515 const old_size: u32 = @intCast(elf.targetLoad(&shdr.size));
1516 const new_size: u32 = @intCast(old_size + key.len + 1);
1517 elf.targetStore(&shdr.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;
2351 );
2352 if (gop.found_existing) return gop.key_ptr.*;
2353 const old_size, const new_size = size: switch (elf.shdrPtr(shndx)) {
2354 inline else => |shdr| {
2355 const old_size: u32 = @intCast(elf.targetLoad(&shdr.size));
2356 const new_size: u32 = @intCast(old_size + key.len + 1);
2357 elf.targetStore(&shdr.size, new_size);
2358 break :size .{ old_size, new_size };
17742359 },
1775 .EXEC, .DYN => assert(reloc.index == .none),
2360 };
2361 if (shndx == elf.shndx.dynstr) {
2362 elf.updateDynamicEntry(std.elf.DT_STRSZ, new_size);
17762363 }
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;
17782372 }
2373};
17792374
1780 fn updateTargetIndex(reloc: *const Reloc, elf: *Elf) void {
1781 assert(elf.ehdrField(.type) == .REL);
1782 const sh = elf.getNodeShndx(reloc.node).get(elf);
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 }
2375const GotIndex = enum(u32) {
2376 none = std.math.maxInt(u32),
2377 _,
17962378
1797 fn updateNodeOffset(reloc: *const Reloc, elf: *Elf, node_offset: u64) void {
1798 assert(elf.ehdrField(.type) == .REL);
1799 const total_offset = node_offset + reloc.offset;
1800 const sh = elf.getNodeShndx(reloc.node).get(elf);
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 }
2379 pub fn wrap(i: ?u32) GotIndex {
2380 const gi: GotIndex = @enumFromInt(i orelse return .none);
2381 assert(gi != .none);
2382 return gi;
18102383 }
1811
1812 comptime {
1813 if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Reloc) == 40);
2384 pub fn unwrap(gi: GotIndex) ?u32 {
2385 return switch (gi) {
2386 _ => @intFromEnum(gi),
2387 .none => null,
2388 };
18142389 }
18152390};
18162391
......@@ -1918,6 +2493,11 @@ fn create(
19182493 .dynstr = .UNDEF,
19192494 .dynamic = .UNDEF,
19202495 .tdata = .UNDEF,
2496 .rela_dyn = .UNDEF,
2497 .rela_plt = .UNDEF,
2498 .init_array = .UNDEF,
2499 .fini_array = .UNDEF,
2500 .preinit_array = .UNDEF,
19212501 },
19222502 .symtab = .empty,
19232503 .globals = .{
......@@ -1927,16 +2507,14 @@ fn create(
19272507 .weak_undef = .empty,
19282508 },
19292509 .node_global_symbols = .empty,
2510 .dso_globals = .empty,
19302511 .shstrtab = .{ .map = .empty },
19312512 .strtab = .{ .map = .empty },
19322513 .dynstr = .{ .map = .empty },
1933 .got = .{
1934 .len = 0,
1935 .tlsld = .none,
1936 .plt = .empty,
1937 },
1938 .first_plt_reloc = .none,
1939 .first_dynamic_reloc = .none,
2514 .got = .empty,
2515 .plt = .empty,
2516 .plt_first_symbol_reloc = .none,
2517 .dynamic_first_symbol_reloc = .none,
19402518 .needed = .empty,
19412519 .inputs = .empty,
19422520 .input_sections = .empty,
......@@ -1948,11 +2526,15 @@ fn create(
19482526 .pending_index = 0,
19492527 }),
19502528 .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,
19522533 .changed_symtab_index = .empty,
19532534 .const_prog_node = .none,
19542535 .synth_prog_node = .none,
19552536 .input_prog_node = .none,
2537 .textrel_count = 0,
19562538 };
19572539 errdefer elf.deinit();
19582540
......@@ -1972,10 +2554,12 @@ pub fn deinit(elf: *Elf) void {
19722554 elf.globals.strong_undef.deinit(gpa);
19732555 elf.globals.weak_undef.deinit(gpa);
19742556 elf.node_global_symbols.deinit(gpa);
2557 elf.dso_globals.deinit(gpa);
19752558 elf.shstrtab.map.deinit(gpa);
19762559 elf.strtab.map.deinit(gpa);
19772560 elf.dynstr.map.deinit(gpa);
1978 elf.got.plt.deinit(gpa);
2561 elf.got.deinit(gpa);
2562 elf.plt.deinit(gpa);
19792563 elf.needed.deinit(gpa);
19802564 for (elf.inputs.items) |input| if (input.member) |m| gpa.free(m);
19812565 elf.inputs.deinit(gpa);
......@@ -1984,7 +2568,10 @@ pub fn deinit(elf: *Elf) void {
19842568 elf.uavs.deinit(gpa);
19852569 for (&elf.lazy.values) |*lazy| lazy.map.deinit(gpa);
19862570 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);
19882575 elf.changed_symtab_index.deinit(gpa);
19892576 elf.* = undefined;
19902577}
......@@ -2293,7 +2880,7 @@ fn initHeaders(
22932880 .entsize = 0,
22942881 };
22952882 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
22982885 elf.symtab.addOneAssumeCapacity().* = .{
22992886 .node = .none,
......@@ -2368,8 +2955,15 @@ fn initHeaders(
23682955 if (@"type" != .REL) {
23692956 elf.shndx.got = try elf.addSection(elf.ni.data_rel_ro, .{
23702957 .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 },
23712964 .flags = .{ .WRITE = true, .ALLOC = true },
23722965 .addralign = addr_align,
2966 .entsize = @intCast(addr_align.toByteUnits()),
23732967 });
23742968 elf.shndx.got_plt = try elf.addSection(
23752969 if (elf.options.z_now) elf.ni.data_rel_ro else elf.ni.data,
......@@ -2383,6 +2977,7 @@ fn initHeaders(
23832977 .X86_64 => 3 * 8,
23842978 },
23852979 .addralign = addr_align,
2980 .entsize = @intCast(addr_align.toByteUnits()),
23862981 },
23872982 );
23882983 const plt_size: std.elf.Xword, const plt_align: std.mem.Alignment, const plt_sec =
......@@ -2478,7 +3073,7 @@ fn initHeaders(
24783073 .NONE, _ => unreachable,
24793074 inline else => |ct_class| @sizeOf(ct_class.ElfN().Rela),
24803075 };
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, .{
24823077 .name = ".rela.dyn",
24833078 .type = .RELA,
24843079 .flags = .{ .ALLOC = true },
......@@ -2487,13 +3082,12 @@ fn initHeaders(
24873082 .entsize = rela_size,
24883083 .node_align = elf.mf.flags.block_size,
24893084 });
2490 const got_plt_shndx = elf.shndx.got_plt;
2491 got_plt_shndx.get(elf).rela_shndx = try elf.addSection(elf.ni.rodata, .{
3085 elf.shndx.rela_plt = try elf.addSection(elf.ni.rodata, .{
24923086 .name = ".rela.plt",
24933087 .type = .RELA,
24943088 .flags = .{ .ALLOC = true, .INFO_LINK = true },
24953089 .link = elf.shndx.dynsym.toSection().?,
2496 .info = got_plt_shndx.toSection().?,
3090 .info = elf.shndx.got_plt.toSection().?,
24973091 .addralign = addr_align,
24983092 .entsize = rela_size,
24993093 .node_align = elf.mf.flags.block_size,
......@@ -2516,7 +3110,7 @@ fn initHeaders(
25163110 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *0x0(%rip)
25173111 0x0f, 0x1f, 0x40, 0x00, // nopl 0x0(%rax)
25183112 });
2519 elf.first_plt_reloc = @enumFromInt(elf.relocs.items.len);
3113 elf.plt_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len);
25203114 try elf.ensureUnusedRelocCapacity(plt_ni, 2);
25213115 elf.addRelocAssumeCapacity(
25223116 plt_ni,
......@@ -2544,6 +3138,83 @@ fn initHeaders(
25443138 elf.nodes.appendAssumeCapacity(.{ .segment = tls_phndx });
25453139 elf.phdrs.items[tls_phndx] = elf.ni.tls;
25463140 }
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 }
25473218 } else {
25483219 assert(maybe_interp == null);
25493220 assert(!have_dynamic_section);
......@@ -2554,6 +3225,12 @@ fn initHeaders(
25543225 .addralign = elf.mf.flags.block_size,
25553226 });
25563227 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 }
25573234}
25583235
25593236pub fn startProgress(elf: *Elf, prog_node: std.Progress.Node) void {
......@@ -2586,7 +3263,7 @@ fn getNode(elf: *const Elf, ni: MappedFile.Node.Index) Node {
25863263 return elf.nodes.get(@intFromEnum(ni));
25873264}
25883265/// 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 {
25903267 return switch (elf.getNode(ni)) {
25913268 .file => unreachable,
25923269 .ehdr => unreachable,
......@@ -2624,55 +3301,80 @@ fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
26243301/// Asserts that `ni` must be a node which supports relocations (see `Elf.Node`). Does not support
26253302/// the special-case sections '.plt' and '.dynamic'.
26263303fn 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)) {
26283305 .file => unreachable, // cannot contain relocs
26293306 .ehdr => unreachable, // cannot contain relocs
26303307 .shdr => unreachable, // cannot contain relocs
26313308 .segment => unreachable, // cannot contain relocs
26323309 .section => unreachable, // cannot contain relocs (.plt and .dynamic unsupported)
2633 .input_section => |isi| &elf.input_sections.items[@intFromEnum(isi)].first_reloc,
2634 .nav => |nmi| &elf.navs.values()[@intFromEnum(nmi)].first_reloc,
2635 .uav => |umi| &elf.uavs.values()[@intFromEnum(umi)].first_reloc,
2636 inline .lazy_code, .lazy_const_data => |lmi| &elf.lazy.getPtr(lmi.ref().kind).map.values()[lmi.ref().index].first_reloc,
3310 .input_section => |isi| .{
3311 &elf.input_sections.items[@intFromEnum(isi)].first_symbol_reloc,
3312 &elf.input_sections.items[@intFromEnum(isi)].first_got_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 },
26373326 };
2638 if (first_reloc_ptr.* != .none) {
2639 for (elf.relocs.items[@intFromEnum(first_reloc_ptr.*)..]) |*reloc| {
3327
3328 if (symbol_relocs.* != .none) {
3329 for (
3330 elf.symbol_relocs.items[@intFromEnum(symbol_relocs.*)..],
3331 @intFromEnum(symbol_relocs.*)..,
3332 ) |*reloc, index| {
26403333 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 }
26423345 }
3346 ptr.* = @enumFromInt(elf.got_relocs.items.len);
26433347 }
2644 first_reloc_ptr.* = @enumFromInt(elf.relocs.items.len);
26453348}
26463349
2647/// Given that `node` has moved, updates all relocations in `node` (starting from `first_reloc`) as
2648/// needed. In relocatables, this means updating the offsets of those relocations. In ELF modules,
2649/// this means applying the relocations.
3350/// Given that `node` has moved, updates all relocations in `node` as needed. In relocatables, this
3351/// means updating the relocations' offsets. In ELF modules, this means applying the relocations.
26503352fn flushMovedNodeRelocs(
26513353 elf: *Elf,
26523354 node: MappedFile.Node.Index,
26533355 node_vaddr: u64,
2654 first_reloc: Reloc.Index,
3356 first_symbol_reloc: SymbolReloc.Index,
3357 first_got_reloc: GotReloc.Index,
26553358) void {
2656 if (first_reloc == .none) return;
2657 switch (elf.ehdrField(.type)) {
2658 .NONE, .CORE, _ => unreachable,
2659 .REL => {
2660 // In a relocatable, we're not actually applying any relocations ourselves, but we need
2661 // to update the offsets of the relocation entries since the node they're in has moved.
2662 for (elf.relocs.items[@intFromEnum(first_reloc)..]) |*reloc| {
2663 if (reloc.node != node) break;
2664 reloc.updateNodeOffset(elf, node_vaddr);
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;
3359 if (first_symbol_reloc != .none) {
3360 for (elf.symbol_relocs.items[@intFromEnum(first_symbol_reloc)..]) |*reloc| {
3361 if (reloc.node != node) break;
3362 if (reloc.rela_index.unwrap()) |rela_index| {
3363 // Update the offsets of any `ElfN.Rela` entry we've emitted, since the node they're
3364 // in has moved, so their offset within the section might also have moved.
3365 reloc.relaSection(elf).relaSetOffset(elf, rela_index, node_vaddr + reloc.offset);
3366 } else {
3367 // We've applied this relocation ourselves! Just re-apply it now.
26713368 reloc.apply(elf);
26723369 }
2673 // TODO: once we're emitting runtime relocation entries, we need to update their offsets
2674 // too, like the logic for relocatables above.
2675 },
3370 }
3371 }
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 }
26763378 }
26773379}
26783380
......@@ -2803,28 +3505,137 @@ fn dynsymPtr(elf: *Elf, index: u32) SymPtr {
28033505 }
28043506}
28053507
2806fn navType(
2807 ip: *const InternPool,
2808 nav_resolved: @typeInfo(@FieldType(InternPool.Nav, "resolved")).optional.child,
2809 any_non_single_threaded: bool,
2810) std.elf.STT {
3508fn navType(elf: *const Elf, nav_resolved: InternPool.Nav.Resolved) std.elf.STT {
3509 const any_non_single_threaded = elf.base.comp.config.any_non_single_threaded;
28113510 return if (any_non_single_threaded and nav_resolved.@"threadlocal")
28123511 .TLS
2813 else if (ip.isFunctionType(nav_resolved.type))
3512 else if (elf.base.comp.zcu.?.intern_pool.isFunctionType(nav_resolved.type))
28143513 .FUNC
28153514 else
28163515 .OBJECT;
28173516}
2818fn namedSection(elf: *const Elf, name: []const u8) ?Section.Index {
2819 if (std.mem.eql(u8, name, ".rodata") or
2820 std.mem.startsWith(u8, name, ".rodata.")) return .rodata;
2821 if (std.mem.eql(u8, name, ".text") or
2822 std.mem.startsWith(u8, name, ".text.")) return .text;
2823 if (std.mem.eql(u8, name, ".data") or
2824 std.mem.startsWith(u8, name, ".data.")) return .data;
2825 if (std.mem.eql(u8, name, ".tdata") or
2826 std.mem.startsWith(u8, name, ".tdata.")) return elf.shndx.tdata;
2827 return null;
3517fn mapInputSection(elf: *Elf, opts: struct {
3518 name: []const u8,
3519 flags: std.elf.SHF,
3520 addralign: std.elf.Xword,
3521 entsize: std.elf.Xword,
3522}) !Section.Index {
3523 const gpa = elf.base.comp.gpa;
3524 if (opts.flags.INFO_LINK or
3525 opts.flags.LINK_ORDER or
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;
28283639}
28293640fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavMapIndex {
28303641 const gpa = zcu.gpa;
......@@ -2838,17 +3649,41 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavM
28383649 const nav_gop = elf.navs.getOrPutAssumeCapacity(nav_index);
28393650 const nmi: Node.NavMapIndex = @enumFromInt(nav_gop.index);
28403651 if (!nav_gop.found_existing) {
2841 const sym_type = navType(ip, nav.resolved.?, elf.base.comp.config.any_non_single_threaded);
28423652 const shndx: Section.Index = section: {
28433653 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;
28453686 }
2846 break :section switch (sym_type) {
2847 else => unreachable,
2848 .FUNC => .text,
2849 .OBJECT => .data,
2850 .TLS => elf.shndx.tdata,
2851 };
28523687 };
28533688 const alignment: InternPool.Alignment = switch (Type.fromInterned(nav.resolved.?.type).zigTypeTag(zcu)) {
28543689 .@"fn" => a: {
......@@ -2880,10 +3715,11 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavM
28803715 .name = try elf.string(.strtab, nav.fqn.toSlice(ip)),
28813716 .value = 0,
28823717 .size = 0,
2883 .type = sym_type,
3718 .type = elf.navType(nav.resolved.?),
28843719 .shndx = shndx,
28853720 }),
2886 .first_reloc = .none,
3721 .first_symbol_reloc = .none,
3722 .first_got_reloc = .none,
28873723 };
28883724 elf.nodes.appendAssumeCapacity(.{ .nav = nmi });
28893725 }
......@@ -2932,7 +3768,7 @@ fn uavMapIndex(
29323768 .type = .OBJECT,
29333769 .shndx = shndx,
29343770 }),
2935 .first_reloc = .none,
3771 .first_symbol_reloc = .none,
29363772 };
29373773 elf.nodes.appendAssumeCapacity(.{ .uav = umi });
29383774 elf.const_prog_node.increaseEstimatedTotalItems(1);
......@@ -2940,7 +3776,7 @@ fn uavMapIndex(
29403776 } else {
29413777 const node = uav_gop.value_ptr.lsi.index().ptr(elf).node;
29423778 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());
29443780 }
29453781 }
29463782 return umi;
......@@ -2977,7 +3813,14 @@ pub fn loadInput(elf: *Elf, input: link.Input) (Io.File.Reader.SizeError ||
29773813 else => |e| return e,
29783814 };
29793815 },
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 },
29813824 }
29823825}
29833826fn 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
30063849 continue;
30073850 }
30083851 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 }
30093859 const member = header.name() orelse member: {
30103860 const strtab_offset = header.nameOffset() catch |err| switch (err) {
30113861 error.Overflow => break :member error.Overflow,
......@@ -3021,7 +3871,6 @@ fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void
30213871 } catch |err| switch (err) {
30223872 error.Overflow => return diags.failParse(path, "bad member name offset", .{}),
30233873 };
3024 if (!std.mem.endsWith(u8, member, ".o")) break :load_object;
30253874 try elf.loadObject(path, member, fr, .{ .offset = offset, .size = size });
30263875 }
30273876 try fr.seekTo(std.mem.alignForward(u64, offset + size, 2));
......@@ -3109,47 +3958,146 @@ fn loadObject(
31093958 defer gpa.free(shstrtab);
31103959 try elf.nodes.ensureUnusedCapacity(gpa, ehdr.shnum - 1);
31113960 try elf.input_sections.ensureUnusedCapacity(gpa, ehdr.shnum - 1);
3112 for (sections[1..]) |*section| switch (section.shdr.type) {
3113 else => {},
3114 .PROGBITS, .NOBITS => {
3115 if (section.shdr.name >= shstrtab.len) continue;
3116 const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0);
3117 const shndx: Section.Index = elf.namedSection(name) orelse shndx: {
3118 // TODO: actually generate a .bss section. For now, just throw it into `.data`.
3119 if (std.mem.eql(u8, name, ".bss") or
3120 std.mem.startsWith(u8, name, ".bss.")) break :shndx .data;
3121 if (std.mem.eql(u8, name, ".tbss") or
3122 std.mem.startsWith(u8, name, ".tbss.")) break :shndx elf.shndx.tdata;
3123 break :shndx .UNDEF;
3124 };
3125 if (shndx == .UNDEF) continue;
3126 const ni = try elf.mf.addLastChildNode(gpa, shndx.get(elf).ni, .{
3127 .size = section.shdr.size,
3128 .alignment = .fromByteUnits(std.math.ceilPowerOfTwoAssert(
3129 usize,
3130 @intCast(@max(section.shdr.addralign, 1)),
3131 )),
3132 .moved = true, // see assert at end of `flushInputSection`
3133 });
3134 elf.nodes.appendAssumeCapacity(.{
3135 .input_section = @enumFromInt(elf.input_sections.items.len),
3136 });
3137 section.isi = @enumFromInt(elf.input_sections.items.len);
3138 elf.input_sections.addOneAssumeCapacity().* = .{
3139 .input = input_index,
3140 .file_location = .{
3141 .offset = fl.offset + section.shdr.offset,
3142 .size = if (section.shdr.type == .NOBITS) 0 else section.shdr.size,
3961 for (sections[1..]) |*section| {
3962 if (section.shdr.name >= shstrtab.len) continue;
3963 const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0);
3964 const opts: struct {
3965 shndx: Section.Index,
3966 has_file_bits: bool,
3967 node_fixed: bool,
3968 } = switch (section.shdr.type) {
3969 else => continue,
3970 .PROGBITS, .NOBITS => opts: {
3971 const shndx = elf.mapInputSection(.{
3972 .name = name,
3973 .flags = section.shdr.flags.shf,
3974 .addralign = section.shdr.addralign,
3975 .entsize = section.shdr.entsize,
3976 }) catch |err| switch (err) {
3977 error.StripSection => continue,
3978 error.TlsSectionUnavailable => return diags.failParse(
3979 path,
3980 "thread-local storage section '{s}' is incompatible with '-fsingle-threaded'",
3981 .{name},
3982 ),
3983 error.UnsupportedSectionFlags => if (!section.shdr.flags.shf.ALLOC) {
3984 // It probably doesn't matter, just skip this section.
3985 continue;
3986 } else return diags.failParse(
3987 path,
3988 "unsupported flags for section '{s}'",
3989 .{name},
3990 ),
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.*;
31434066 },
3144 // The section vaddr is initially 0, because the symbol addresses are
3145 // zero-based. This will eventually be updated by `flushMoved`.
3146 .vaddr = 0,
3147 .node = ni,
3148 .first_reloc = .none,
3149 };
3150 elf.synth_prog_node.increaseEstimatedTotalItems(1);
3151 },
3152 };
4067 .has_file_bits = true,
4068 // This node must be fixed to prevent padding from being added between different
4069 // INIT_ARRAY/FINI_ARRAY/PREINIT_ARRAY input sections.
4070 .node_fixed = true,
4071 },
4072 };
4073 const ni = try elf.mf.addLastChildNode(gpa, opts.shndx.get(elf).ni, .{
4074 .size = section.shdr.size,
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 }
31534101 var symmap: std.ArrayList(Symbol.Id) = .empty;
31544102 defer symmap.deinit(gpa);
31554103 for (sections[1..], 1..) |*symtab, symtab_shndx| switch (symtab.shdr.type) {
......@@ -3198,20 +4146,24 @@ fn loadObject(
31984146 };
31994147
32004148 if (input_sym.shndx == std.elf.SHN_UNDEF) switch (input_sym.info.bind) {
3201 _ => |bind| return diags.failParse(
4149 else => |bind| return diags.failParse(
32024150 path,
32034151 "symbol '{s}' has unsupported binding (0x{x})",
32044152 .{ name, bind },
32054153 ),
32064154 .LOCAL => continue,
3207 .GLOBAL, .WEAK => |bind| {
4155 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {
32084156 si.* = elf.addGlobalSymbolAssumeCapacity(.{
32094157 .node = .none,
32104158 .name = try .string(elf, name),
32114159 .value = input_sym.value,
32124160 .size = input_sym.size,
32134161 .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 },
32154167 .visibility = input_sym.other.visibility,
32164168 .shndx = .UNDEF,
32174169 }) catch |err| switch (err) {
......@@ -3224,7 +4176,7 @@ fn loadObject(
32244176 const input_section_node = (sections[input_sym.shndx].isi orelse continue).node(elf);
32254177
32264178 switch (input_sym.info.bind) {
3227 _ => |bind| return diags.failParse(
4179 else => |bind| return diags.failParse(
32284180 path,
32294181 "symbol '{s}' has unsupported binding (0x{x})",
32304182 .{ name, bind },
......@@ -3240,14 +4192,18 @@ fn loadObject(
32404192 });
32414193 si.* = .local(lsi);
32424194 },
3243 .GLOBAL, .WEAK => |bind| {
4195 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {
32444196 si.* = elf.addGlobalSymbolAssumeCapacity(.{
32454197 .node = input_section_node,
32464198 .name = try .string(elf, name),
32474199 .value = input_sym.value,
32484200 .size = input_sym.size,
32494201 .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 },
32514207 .visibility = input_sym.other.visibility,
32524208 .shndx = elf.getNodeShndx(input_section_node),
32534209 }) catch |err| switch (err) {
......@@ -3298,11 +4254,17 @@ fn loadObject(
32984254 .{rel.info.sym},
32994255 );
33004256 const target = symmap.items[rel.info.sym - 1];
3301 if (target == Symbol.Id.null) return diags.failParse(
3302 path,
3303 "unsupported symbol at index {d} required for relocation",
3304 .{rel.info.sym},
3305 );
4257 if (target == Symbol.Id.null) {
4258 // If this is not an SHF_ALLOC section, then let's let this
4259 // slide for now, because it probably doesn't affect the final
4260 // binary's functionality for this section to be a bit broken.
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 }
33064268 elf.addRelocAssumeCapacity(
33074269 loc_node,
33084270 rel.offset - loc_sec.shdr.addr,
......@@ -3320,6 +4282,7 @@ fn loadObject(
33204282}
33214283fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {
33224284 const comp = elf.base.comp;
4285 const gpa = comp.gpa;
33234286 const diags = &comp.link_diags;
33244287 const r = &fr.interface;
33254288
......@@ -3334,68 +4297,157 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {
33344297 if (ehdr.type != .DYN) return diags.failParse(path, "unsupported dso type", .{});
33354298 if (ehdr.machine != elf.ehdrField(.machine))
33364299 return diags.failParse(path, "bad machine", .{});
3337 if (ehdr.phoff == 0 or ehdr.phnum <= 1)
3338 return diags.failParse(path, "no program headers", .{});
3339 try fr.seekTo(ehdr.phoff);
3340 const dynamic_ph = for (0..ehdr.phnum) |_| {
3341 const ph = try r.peekStruct(ElfN.Phdr, target_endian);
3342 try r.discardAll(ehdr.phentsize);
3343 switch (ph.type) {
3344 else => {},
3345 .DYNAMIC => break ph,
4300 if (ehdr.shnum > 0) try fr.seekTo(ehdr.shoff);
4301 const dynamic_sh: ElfN.Shdr, const dynsym_sh: ElfN.Shdr = sh: {
4302 var dynamic_sh: ?ElfN.Shdr = null;
4303 var dynsym_sh: ?ElfN.Shdr = null;
4304 for (0..ehdr.shnum) |_| {
4305 const sh = try r.peekStruct(ElfN.Shdr, target_endian);
4306 try r.discardAll(ehdr.shentsize);
4307 switch (sh.type) {
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", .{});
33464321 }
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 }
33484329 const dynnum = std.math.divExact(
33494330 u32,
3350 @intCast(dynamic_ph.filesz),
4331 @intCast(dynamic_sh.size),
33514332 @sizeOf(ElfN.Addr) * 2,
33524333 ) catch return diags.failParse(
33534334 path,
3354 "dynamic segment filesz (0x{x}) is not a multiple of entsize (0x{x})",
3355 .{ dynamic_ph.filesz, @sizeOf(ElfN.Addr) * 2 },
4335 "dynamic section size (0x{x}) is not a multiple of entsize (0x{x})",
4336 .{ dynamic_sh.size, @sizeOf(ElfN.Addr) * 2 },
33564337 );
3357 var strtab: ?ElfN.Addr = null;
3358 var strsz: ?ElfN.Addr = null;
3359 var soname: ?ElfN.Addr = null;
3360 try fr.seekTo(dynamic_ph.offset);
3361 for (0..dynnum) |_| {
4338
4339 if (dynsym_sh.entsize < @sizeOf(ElfN.Sym)) {
4340 return diags.failParse(path, "bad dynsym entsize", .{});
4341 }
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) |_| {
33624360 const tag = try r.takeInt(ElfN.Addr, target_endian);
33634361 const val = try r.takeInt(ElfN.Addr, target_endian);
3364 switch (tag) {
3365 else => {},
3366 std.elf.DT_STRTAB => strtab = val,
3367 std.elf.DT_STRSZ => strsz = val,
3368 std.elf.DT_SONAME => soname = val,
4362 if (tag == std.elf.DT_SONAME) {
4363 // val is a dynstr index
4364 if (val >= dynstr.len) {
4365 return diags.failParse(path, "bad soname string", .{});
4366 }
4367 break std.mem.sliceTo(dynstr[@intCast(val)..], 0);
33694368 }
3370 }
3371 if (strtab == null or soname == null)
3372 return elf.loadDsoExact(std.fs.path.basename(path.sub_path));
3373 if (strsz) |size| if (soname.? >= size)
3374 return diags.failParse(path, "bad soname string", .{});
3375 try fr.seekTo(ehdr.phoff);
3376 const ph = for (0..ehdr.phnum) |_| {
3377 const ph = try r.peekStruct(ElfN.Phdr, target_endian);
3378 try r.discardAll(ehdr.phentsize);
3379 switch (ph.type) {
3380 else => {},
3381 .LOAD => if (strtab.? >= ph.vaddr and
3382 strtab.? + (strsz orelse 0) <= ph.vaddr + ph.filesz) break ph,
4369 } else std.fs.path.basename(path.sub_path);
4370 try elf.needed.put(gpa, try elf.string(.dynstr, soname), {});
4371
4372 // Scan the symbol table and populate `elf.dso_globals`.
4373 const first_global = @min(dynsym_sh.info, symnum);
4374 try elf.dso_globals.ensureUnusedCapacity(gpa, symnum - first_global);
4375 try elf.ensureUnusedPltCapacity(symnum - first_global);
4376 try fr.seekTo(dynsym_sh.offset + first_global * dynsym_sh.entsize);
4377 for (first_global..symnum) |_| {
4378 const sym = try r.peekStruct(ElfN.Sym, target_endian);
4379 try r.discardAll(@intCast(dynsym_sh.entsize));
4380
4381 switch (sym.info.bind) {
4382 else => continue,
4383 .GLOBAL, .WEAK, .GNU_UNIQUE => {},
33834384 }
3384 } else return diags.failParse(path, "strtab not part of a loaded segment", .{});
3385 try fr.seekTo(strtab.? + soname.? - ph.vaddr + ph.offset);
3386 return elf.loadDsoExact(r.peekSentinel(0) catch |err| switch (err) {
3387 error.StreamTooLong => return diags.failParse(path, "soname too lang", .{}),
3388 else => |e| return e,
3389 });
4385 // STV_HIDDEN/STV_INTERNAL symbols should be marked as STB_LOCAL and hence skipped
4386 // above, but we might as well double-check.
4387 switch (sym.other.visibility) {
4388 .HIDDEN, .INTERNAL => continue,
4389 .DEFAULT, .PROTECTED => {},
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 }
33904448 },
33914449 }
33924450}
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
34004452/// Validates that the `std.elf.Ident` present at the start of `r` is a compatible link input.
34014453///
......@@ -3432,22 +4484,100 @@ fn checkInputIdent(
34324484 .{ident.version},
34334485 );
34344486
3435 // 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`.
3437 const expect_abiversion: u8 = abiver: {
3438 if (ident.osabi == .NONE) break :abiver 0;
3439 if (ident.osabi == target.osabi) break :abiver target.abiversion;
3440 return diags.failParse(
3441 path,
3442 "bad ELF OS/ABI ({?s})",
3443 .{std.enums.tagName(std.elf.OSABI, ident.osabi)},
3444 );
4487 // OSABI is a bit more complex. On Linux, `.NONE` and `.GNU` are both valid and both common.
4488 // It sounds reasonable to allow the value we chose *and* allow `.NONE`.
4489 const expect_abiversion: u8 = abiver: {
4490 if (ident.osabi == .NONE) break :abiver 0;
4491 if (ident.osabi == target.osabi) break :abiver target.abiversion;
4492 return diags.failParse(
4493 path,
4494 "bad ELF OS/ABI ({?s})",
4495 .{std.enums.tagName(std.elf.OSABI, ident.osabi)},
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),
34454578 };
3446 if (ident.abiversion != expect_abiversion) return diags.failParse(
3447 path,
3448 "bad ELF ABI version ({d})",
3449 .{ident.abiversion},
3450 );
4579 const end_sym_name = elf.string(.strtab, "__" ++ name ++ "_end") catch unreachable; // string definitely already exists
4580 Symbol.Id.global(end_sym_name).flushMoved(elf, end_vaddr);
34514581}
34524582
34534583pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) !void {
......@@ -3486,9 +4616,22 @@ fn prelinkInner(elf: *Elf) !void {
34864616 const ElfN = ct_class.ElfN();
34874617 const flags: ElfN.Addr = if (elf.options.z_now) std.elf.DF_BIND_NOW else 0;
34884618 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 };
34894628 const needed_len = elf.needed.count();
34904629 const dynamic_len = needed_len + @intFromBool(elf.options.soname != null) +
4630 @intFromBool(rpath != .empty) +
34914631 @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 +
34924635 @intFromBool(comp.config.output_mode == .Exe) + 12;
34934636 const dynamic_size: u32 = @intCast(@sizeOf(ElfN.Addr) * 2 * dynamic_len);
34944637 const dynamic_ni = elf.shndx.dynamic.get(elf).ni;
......@@ -3508,6 +4651,10 @@ fn prelinkInner(elf: *Elf) !void {
35084651 dynamic_entries[dynamic_index] = .{ std.elf.DT_SONAME, @intFromEnum(try elf.string(.dynstr, soname)) };
35094652 dynamic_index += 1;
35104653 }
4654 if (rpath != .empty) {
4655 dynamic_entries[dynamic_index] = .{ std.elf.DT_RUNPATH, @intFromEnum(rpath) };
4656 dynamic_index += 1;
4657 }
35114658 if (flags != 0) {
35124659 dynamic_entries[dynamic_index] = .{ std.elf.DT_FLAGS, flags };
35134660 dynamic_index += 1;
......@@ -3520,23 +4667,72 @@ fn prelinkInner(elf: *Elf) !void {
35204667 dynamic_entries[dynamic_index] = .{ std.elf.DT_DEBUG, 0 };
35214668 dynamic_index += 1;
35224669 }
3523 const rela_dyn_shndx = elf.shndx.got.get(elf).rela_shndx;
3524 const rela_plt_shndx = elf.shndx.got_plt.get(elf).rela_shndx;
4670 if (elf.shndx.init_array != .UNDEF) {
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 }
35254721 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)) },
35274723 .{ 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,
35294725 ) },
35304726 .{ 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)) },
35324728 .{ 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,
35344730 ) },
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)) },
35364732 .{ 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)) },
35384734 .{ 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)) },
35404736 .{ std.elf.DT_STRSZ, elf.targetLoad(
35414737 &@field(elf.shdrPtr(elf.shndx.dynstr), @tagName(ct_class)).size,
35424738 ) },
......@@ -3547,19 +4743,19 @@ fn prelinkInner(elf: *Elf) !void {
35474743 if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry|
35484744 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);
35514747 try elf.ensureUnusedRelocCapacity(dynamic_ni, 5);
35524748 elf.addRelocAssumeCapacity(
35534749 dynamic_ni,
35544750 @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),
35564752 0,
35574753 .absAddr(elf),
35584754 );
35594755 elf.addRelocAssumeCapacity(
35604756 dynamic_ni,
35614757 @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),
35634759 0,
35644760 .absAddr(elf),
35654761 );
......@@ -3663,7 +4859,11 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
36634859 .type = .SECTION,
36644860 .shndx = shndx,
36654861 }) 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 } });
36674867 elf.nodes.appendAssumeCapacity(.{ .section = shndx });
36684868 const offset = ni.fileLocation(&elf.mf, false).offset;
36694869 switch (elf.shdrPtr(shndx)) {
......@@ -3689,20 +4889,23 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
36894889fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize) !void {
36904890 if (len == 0) return;
36914891 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);
36934894 const class = elf.identClass();
3694 const rela_shndx, const rela_len = rela: switch (elf.ehdrField(.type)) {
4895 switch (elf.ehdrField(.type)) {
36954896 .NONE, .CORE, _ => unreachable,
36964897 .REL => {
36974898 const shndx = elf.getNodeShndx(node);
3698 if (shndx.get(elf).rela_shndx == .UNDEF) {
4899 if (shndx.get(elf).rela.shndx == .UNDEF) {
36994900 var bfa_buf: [32]u8 = undefined;
37004901 var bfa: std.heap.BufferFirstAllocator = .init(&bfa_buf, gpa);
37014902 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)});
37044905 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);
37064909 const rela_shndx = try elf.addSection(.none, .{
37074910 .name = rela_name,
37084911 .type = .RELA,
......@@ -3719,33 +4922,29 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize)
37194922 },
37204923 .node_align = elf.mf.flags.block_size,
37214924 });
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;
37234927 }
3724 break :rela .{ shndx.get(elf).rela_shndx, len };
4928 try shndx.get(elf).rela.shndx.relaEnsureAdditionalCapacity(elf, len);
37254929 },
3726 .EXEC, .DYN => switch (elf.got.tlsld) {
3727 _ => return,
3728 .none => if (elf.shndx.dynamic != .UNDEF) {
3729 try elf.mf.updates.ensureUnusedCapacity(gpa, 1);
3730 const got_ni = elf.shndx.got.get(elf).ni;
3731 _, const got_node_size = got_ni.location(&elf.mf).resolve(&elf.mf);
3732 const got_size = switch (class) {
3733 .NONE, _ => unreachable,
3734 inline else => |ct_class| (elf.got.len + 2) * @sizeOf(ct_class.ElfN().Addr),
3735 };
3736 if (got_size > got_node_size)
3737 try got_ni.resize(&elf.mf, gpa, got_size +| got_size / MappedFile.growth_factor);
3738 break :rela .{ elf.shndx.got.get(elf).rela_shndx, 1 };
3739 } else return,
4930 .EXEC, .DYN => {
4931 try elf.tls_size_symbol_relocs.ensureUnusedCapacity(gpa, len);
4932 const new_got_entries = len * 2; // at worst, every reloc is a new TLSGD
4933 try elf.got.ensureUnusedCapacity(gpa, new_got_entries);
4934 const got_ni = elf.shndx.got.get(elf).ni;
4935 _, const got_node_size = got_ni.location(&elf.mf).resolve(&elf.mf);
4936 const need_got_size = switch (class) {
4937 .NONE, _ => unreachable,
4938 inline else => |ct_class| (elf.got.count() + new_got_entries) * @sizeOf(ct_class.ElfN().Addr),
4939 };
4940 if (need_got_size > got_node_size)
4941 try got_ni.resize(&elf.mf, gpa, need_got_size +| need_got_size / MappedFile.growth_factor);
4942
4943 if (elf.shndx.dynamic != .UNDEF) {
4944 try elf.shndx.rela_dyn.relaEnsureAdditionalCapacity(elf, new_got_entries);
4945 }
37404946 },
3741 };
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);
4947 }
37494948}
37504949fn addRelocAssumeCapacity(
37514950 elf: *Elf,
......@@ -3753,123 +4952,443 @@ fn addRelocAssumeCapacity(
37534952 offset: u64,
37544953 target: Symbol.Id,
37554954 addend: i64,
3756 @"type": Reloc.Type,
4955 @"type": MachineRelocType,
37574956) void {
37584957 assert(node != .none);
3759 const ri: Reloc.Index = @enumFromInt(elf.relocs.items.len);
3760 const next: Reloc.Index = next: {
3761 const target_ptr = target.index(elf).ptr(elf);
3762 const next = target_ptr.first_target_reloc;
3763 target_ptr.first_target_reloc = ri;
3764 break :next next;
4958 switch (elf.ehdrField(.type)) {
4959 .NONE, .CORE, _ => unreachable,
4960 .REL => {
4961 const rela_shndx = elf.getNodeShndx(node).get(elf).rela.shndx;
4962 const rela_index = rela_shndx.relaAddOneAssumeCapacity(elf, .{
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();
37655125 };
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;
37665131 if (next != .none) {
37675132 next.get(elf).prev = ri;
37685133 }
3769 elf.relocs.addOneAssumeCapacity().* = .{
5134 elf.symbol_relocs.appendAssumeCapacity(.{
5135 .node = node,
5136 .offset = offset,
5137 .target = target,
5138 .addend = addend,
37705139 .type = @"type",
3771 .prev = .none,
37725140 .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(.{
37735203 .node = node,
5204 .offset = offset,
37745205 .target = target,
3775 .index = index: switch (elf.ehdrField(.type)) {
3776 .NONE, .CORE, _ => unreachable,
3777 .REL => {
3778 const sh = elf.getNodeShndx(node).get(elf);
3779 switch (elf.shdrPtr(sh.rela_shndx)) {
3780 inline else => |shdr, class| {
3781 const Rela = class.ElfN().Rela;
3782 const ent_size = elf.targetLoad(&shdr.entsize);
3783 const rela_slice = sh.rela_shndx.get(elf).ni.slice(&elf.mf);
3784 const index: u32 = if (sh.rela_free.unwrap()) |index| alloc_index: {
3785 const rela: *Rela = @ptrCast(@alignCast(
3786 rela_slice[@intCast(ent_size * index)..][0..@intCast(ent_size)],
3787 ));
3788 sh.rela_free = @enumFromInt(rela.offset);
3789 break :alloc_index index;
3790 } else alloc_index: {
3791 const old_size = elf.targetLoad(&shdr.size);
3792 const new_size = old_size + ent_size;
3793 elf.targetStore(&shdr.size, @intCast(new_size));
3794 break :alloc_index @intCast(@divExact(old_size, ent_size));
3795 };
3796 const rela: *Rela = @ptrCast(@alignCast(
3797 rela_slice[@intCast(ent_size * index)..][0..@intCast(ent_size)],
3798 ));
3799 // The `offset` field here needs to equal the offset into the section, which
3800 // is *not* the same as our `offset` which is the offset into `node`. We
3801 // could calculate it now, but there's no point since `flushMovedNodeRelocs`
3802 // will eventually do that for us anyway. So for now, just set offset to 0.
3803 rela.* = .{
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);
5206 .addend = addend,
5207 .type = @"type",
5208 });
5209}
5210fn updateGotEntry(elf: *Elf, got_index: usize) void {
5211 const entry_value: union(enum) {
5212 unsigned: u64,
5213 signed: i64,
5214 reloc: struct {
5215 type: MachineRelocType,
5216 dynsym_index: u32,
5217 },
5218 } = switch (elf.got.keys()[got_index]) {
5219 .reserved => .{ .unsigned = 0 },
5220 .tpoff => |sym_id| val: {
5221 // We will break from this block if we require a relocation.
5222 known: {
5223 if (elf.base.comp.config.output_mode != .Exe) {
5224 // Only the executable's per-module TLS block is at a known offset from the
5225 // general TLS pointer.
5226 break :known;
5227 }
5228 switch (sym_id.unwrap()) {
5229 .local => {},
5230 .global => |name| if (elf.globals.strong_undef.contains(name) or
5231 elf.globals.weak_undef.contains(name))
5232 {
5233 // This is an external TLS symbol, so we don't know its offset.
5234 break :known;
38135235 },
38145236 }
3815 },
3816 .EXEC, .DYN => {
3817 switch (elf.ehdrField(.machine)) {
3818 else => |machine| @panic(@tagName(machine)),
3819 .AARCH64, .PPC64, .RISCV => {},
3820 .X86_64 => switch (@"type".X86_64) {
3821 else => {},
3822 .TLSLD => switch (elf.got.tlsld) {
3823 _ => {},
3824 .none => if (elf.shndx.dynamic != .UNDEF) {
3825 const tlsld_index = elf.got.len;
3826 elf.got.tlsld = .wrap(tlsld_index);
3827 elf.got.len = tlsld_index + 2;
3828 const got_addr = got_addr: switch (elf.shdrPtr(elf.shndx.got)) {
3829 inline else => |shdr, class| {
3830 const addr_size = @sizeOf(class.ElfN().Addr);
3831 const old_size = addr_size * tlsld_index;
3832 const new_size = old_size + addr_size * 2;
3833 @memset(
3834 elf.shndx.got.get(elf).ni.slice(&elf.mf)[old_size..new_size],
3835 0,
3836 );
3837 break :got_addr elf.targetLoad(&shdr.addr) + old_size;
3838 },
5237 // It's a symbol which we define, the symbol is not interposable because we're the
5238 // executable, and we know our per-module TLS block's offset because we're the
5239 // executable. We therefore know this value!
5240 const tls_phndx = elf.getNode(elf.ni.tls).segment;
5241 const tls_size: u64 = switch (elf.phdrSlice()) {
5242 inline else => |phdr| tls_size: {
5243 assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS);
5244 break :tls_size elf.targetLoad(&phdr[tls_phndx].memsz);
5245 },
5246 };
5247 const sym_value = sym_id.value(elf);
5248 break :val .{ .signed = @bitCast(sym_value -% tls_size) };
5249 }
5250 break :val .{
5251 .reloc = .{
5252 .type = switch (elf.ehdrField(.machine)) {
5253 else => |machine| @panic(@tagName(machine)),
5254 .X86_64 => .{ .X86_64 = .TPOFF64 },
5255 },
5256 .dynsym_index = switch (sym_id.unwrap()) {
5257 .global => |name| elf.globalByName(name).?.dynsym_index,
5258 // TODO: I have no idea if compilers are even allowed to emit this, but if they
5259 // are then I guess we need to add this local symbol to `.dynsym`?
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,
38395316 };
3840 const rela_dyn_shndx = elf.shndx.got.get(elf).rela_shndx;
3841 const rela_dyn_ni = rela_dyn_shndx.get(elf).ni;
3842 switch (elf.shdrPtr(rela_dyn_shndx)) {
3843 inline else => |shdr, class| {
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);
5317 switch (visibility) {
5318 .DEFAULT => {},
5319 .INTERNAL, .HIDDEN, .PROTECTED => {
5320 break :dsi 0; // non-interposable definition
38605321 },
38615322 }
3862 rela_dyn_ni.resizedAssumeCapacity(&elf.mf);
3863 },
5323 }
5324 // `sym` is either undefined or an interposable definition, so use its
5325 // actual dynsym index.
5326 break :dsi elf.globalByName(name).?.dynsym_index;
38645327 },
38655328 },
3866 }
3867 break :index .none;
5329 },
38685330 },
38695331 },
3870 .offset = offset,
3871 .addend = addend,
5332 .tlsld0 => switch (elf.shndx.dynamic) {
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,
38725390 };
5391 return shf.ALLOC and !shf.WRITE;
38735392}
38745393
38755394pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {
......@@ -3999,16 +5518,21 @@ pub fn flush(
39995518 _ = arena;
40005519 _ = prog_node;
40015520
4002 if (elf.ehdrField(.type) != .REL and
4003 elf.shndx.dynamic == .UNDEF and
4004 elf.globals.strong_undef.count() > 0)
4005 {
5521 if (comp.config.output_mode == .Exe) {
5522 var any_undef = false;
40065523 for (elf.globals.strong_undef.keys()) |name| {
5524 if (elf.dso_globals.contains(name)) continue;
5525 any_undef = true;
40075526 comp.link_diags.addError("undefined global symbol '{s}'", .{name.slice(elf)});
40085527 }
4009 return error.LinkFailure;
5528 if (any_undef) return error.LinkFailure;
40105529 }
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
40125536 while (try elf.idle(tid)) {}
40135537
40145538 const entry_addr: u64 = entry: {
......@@ -4039,6 +5563,47 @@ pub fn flush(
40395563 else => |e| return comp.link_diags.fail("flush write failed: {t}", .{e}),
40405564 };
40415565}
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
40435608pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool {
40445609 const comp = elf.base.comp;
......@@ -4093,7 +5658,7 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool {
40935658 return comp.link_diags.fail(
40945659 "linker failed to read input section '{s}' from \"{f}{f}\": {t}",
40955660 .{
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),
40975662 ii.path(elf).fmtEscapeString(),
40985663 fmtMemberString(ii.member(elf)),
40995664 e,
......@@ -4104,6 +5669,9 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool {
41045669 break :task;
41055670 }
41065671 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.
41075675 if (elf.ehdrField(.type) == .REL) {
41085676 const sub_prog_node = elf.mf.update_prog_node.start(kv.key.slice(elf), 0);
41095677 defer sub_prog_node.end();
......@@ -4111,7 +5679,11 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool {
41115679 var ri = sym.first_target_reloc;
41125680 while (ri != .none) {
41135681 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 );
41155687 ri = reloc.next;
41165688 }
41175689 break :task;
......@@ -4146,13 +5718,13 @@ fn idleProgNode(
41465718 var name: [std.Progress.Node.max_name_len]u8 = undefined;
41475719 return prog_node.start(name: switch (node) {
41485720 else => |tag| @tagName(tag),
4149 .section => |shndx| shndx.name(elf),
5721 .section => |shndx| shndx.name(elf).slice(elf),
41505722 .input_section => |isi| {
41515723 const ii = isi.input(elf);
41525724 break :name std.fmt.bufPrint(&name, "{f}{f} {s}", .{
41535725 ii.path(elf).fmtEscapeString(),
41545726 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),
41565728 }) catch &name;
41575729 },
41585730 .nav => |nmi| {
......@@ -4320,110 +5892,56 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {
43205892 .section => |shndx| {
43215893 try elf.flushFileOffset(ni);
43225894 const addr = elf.computeNodeVAddr(ni);
4323 switch (elf.shdrPtr(shndx)) {
4324 inline else => |shdr, class| {
4325 const flags = elf.targetLoad(&shdr.flags).shf;
4326 if (flags.ALLOC) {
4327 if (elf.shndx.dynamic != .UNDEF) {
4328 if (shndx == elf.shndx.got) {
4329 const old_addr = elf.targetLoad(&shdr.addr);
4330 const rela_dyn_shndx = shndx.get(elf).rela_shndx;
4331 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
4332 rela_dyn_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(
4333 elf.targetLoad(&@field(
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 }
5895 const old_addr: u64, const flags: std.elf.SHF = switch (elf.shdrPtr(shndx)) {
5896 inline else => |shdr| .{
5897 elf.targetLoad(&shdr.addr),
5898 elf.targetLoad(&shdr.flags).shf,
5899 },
5900 };
5901
5902 if (flags.ALLOC) {
5903 switch (elf.shdrPtr(shndx)) {
5904 inline else => |shdr| elf.targetStore(&shdr.addr, @intCast(addr)),
5905 }
44205906
4421 if (shndx == elf.shndx.plt) {
4422 elf.flushMovedNodeRelocs(ni, elf.targetLoad(&shdr.addr), elf.first_plt_reloc);
4423 } else if (shndx == elf.shndx.dynamic) {
4424 elf.flushMovedNodeRelocs(ni, elf.targetLoad(&shdr.addr), elf.first_dynamic_reloc);
5907 // Update global symbols targeting this section
5908 if (elf.node_global_symbols.get(ni)) |first_name| {
5909 assert(first_name != .empty);
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;
44255921 }
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);
44275945 }
44285946 },
44295947 .input_section => |isi| {
......@@ -4448,7 +5966,10 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {
44485966 .DEFAULT => elf.targetLoad(&sym.value),
44495967 },
44505968 };
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 );
44525973 }
44535974
44545975 // Update global symbols
......@@ -4460,26 +5981,38 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {
44605981 const old_sym_addr: u64 = switch (elf.symPtr(global.symtab_index)) {
44615982 inline else => |sym| elf.targetLoad(&sym.value),
44625983 };
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 );
44645988 name = global.next_in_node;
44655989 }
44665990 }
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 );
44695998 },
44705999 inline .nav, .uav, .lazy_code, .lazy_const_data => |mi| {
44716000 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);
44736002 if (elf.node_global_symbols.get(ni)) |first_name| {
44746003 assert(first_name != .empty);
44756004 var name = first_name;
44766005 while (name != .empty) {
4477 const global = elf.globalByName(name).?;
4478 global.flushMoved(elf, new_addr);
4479 name = global.next_in_node;
6006 Symbol.Id.global(name).flushMoved(elf, new_addr);
6007 name = elf.globalByName(name).?.next_in_node;
44806008 }
44816009 }
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 );
44836016 },
44846017 }
44856018 try ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
......@@ -4507,6 +6040,27 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void {
45076040 },
45086041 .TLS => {
45096042 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 }
45106064 return ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
45116065 },
45126066 }
......@@ -4541,53 +6095,28 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void {
45416095 },
45426096 },
45436097 .section => |shndx| switch (elf.shdrPtr(shndx)) {
4544 inline else => |shdr, class| {
6098 inline else => |shdr| {
45456099 switch (elf.targetLoad(&shdr.type)) {
45466100 else => unreachable,
6101
45476102 .NULL => if (size > 0) elf.targetStore(&shdr.type, .PROGBITS),
45486103 .PROGBITS => if (size == 0) elf.targetStore(&shdr.type, .NULL),
4549 .SYMTAB, .DYNAMIC, .REL, .DYNSYM => return,
4550 .STRTAB => {
4551 if (elf.shndx.dynamic != .UNDEF) {
4552 if (shndx == elf.shndx.dynstr) {
4553 const dynamic_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast(
4554 elf.shndx.dynamic.get(elf).ni.slice(&elf.mf),
4555 ));
4556 for (dynamic_entries) |*dynamic_entry|
4557 switch (elf.targetLoad(&dynamic_entry[0])) {
4558 else => {},
4559 std.elf.DT_STRSZ => dynamic_entry[1] = shdr.size,
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 },
6104
6105 .INIT_ARRAY,
6106 .FINI_ARRAY,
6107 .PREINIT_ARRAY,
6108 .STRTAB,
6109 .SYMTAB,
6110 .DYNAMIC,
6111 .REL,
6112 .RELA,
6113 .DYNSYM,
6114 => return,
45896115 }
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 {
45916120 elf.targetStore(&shdr.size, @intCast(size));
45926121 }
45936122 },
......@@ -4595,6 +6124,85 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void {
45956124 .input_section, .nav, .uav, .lazy_code, .lazy_const_data => {},
45966125 }
45976126}
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
45996207pub fn updateExports(
46006208 elf: *Elf,
......@@ -4633,7 +6241,7 @@ fn updateExportsInner(
46336241 const exported_lsi: Symbol.LocalIndex, const @"type": std.elf.STT = switch (exported) {
46346242 .nav => |nav| .{
46356243 (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.?),
46376245 },
46386246 .uav => |uav| .{ (try elf.uavMapIndex(uav, .none)).symbol(elf), .OBJECT },
46396247 };
......@@ -4735,13 +6343,13 @@ pub fn printNode(
47356343 try w.writeByte(')');
47366344 },
47376345 },
4738 .section => |shndx| try w.print("({s})", .{shndx.name(elf)}),
6346 .section => |shndx| try w.print("({s})", .{shndx.name(elf).slice(elf)}),
47396347 .input_section => |isi| {
47406348 const ii = isi.input(elf);
47416349 try w.print("({f}{f}, {s})", .{
47426350 ii.path(elf).fmtEscapeString(),
47436351 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),
47456353 });
47466354 },
47476355 .nav => |nmi| {
src/link/MappedFile.zig+90-6
......@@ -305,12 +305,20 @@ pub const Node = extern struct {
305305 }
306306 }
307307
308 pub fn realign(ni: Node.Index, mf: *MappedFile, new_alignment: std.mem.Alignment) void {
309 ni.get(mf).flags.alignment = new_alignment;
310
311 const old_offset, const old_size = ni.location(mf).resolve(mf);
312 if (!new_alignment.check(@intCast(old_offset)) or !new_alignment.check(@intCast(old_size))) {
313 @panic("TODO MappedFile.realign");
308 /// Moves and expands a node such that its offset and size are aligned to `new_alignment`.
309 ///
310 /// Asserts that `ni` is not `Node.Index.root`.
311 pub fn realign(
312 ni: Node.Index,
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);
314322 }
315323 }
316324
......@@ -852,6 +860,82 @@ fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested
852860 }
853861}
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
855939fn moveRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size: u64) !void {
856940 // make a copy of this node at the new location
857941 try mf.copyRange(old_file_offset, new_file_offset, size);