authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-05-24 11:41:30+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-05-26 06:48:53+01:00
logad06fe07c531b76f99d655680b240916561e21e1
tree04089fd1efa6850cc0dc9f6bb61dab888137eff0
parent71e1d7cb8bfe52ccf82f5ba64d1c5d6c5b5077fc
signaturelock-open Commit is signed but in an unrecognized format.

Elf2: non-trivial GOT, better dynamic linking support

It's a bit tricky to give this commit a clear description, sorry---it's a lot of semi-related enhancements. The main things are probably: * Implement creating arbitrary GOT entries, so we can finally resolve GOT relocations (e.g. `R_X86_64_[REX_]GOTPCREL[X]`) * Introduce a new representation for relocations which is more memory-efficient, can handle GOT relocations, and (theoretically) helps to abstract over different target machines * Start emitting runtime relocations when a relocation is not resolvable, and add the `DT_TEXTREL` entry to `.dynamic` when requires * "Free" PLT slots when a symbol becomes defined, and allow reusing those free slots * Implement the majority of x86_64 relocation types The actual impact of these changes is that this linker is now relatively functional (ignoring debug information and stack unwinding information, which is still unimplemented). In particular, it is able to successfully link the Zig compiler against LLVM, static *or* dynamic. There is one caveat to this, which is that because we are not yet emitting `R_X86_64_COPY` relocations, errors like this one are possible when running a compiler dynamically linked with Elf2: ./zig-dynamic-from-elf2/bin/zig: Symbol `__libc_single_threaded' causes overflow in R_X86_64_PC32 relocation In some cases, this is unproblematic, but in others, it will cause random crashes when calling into the LLVM API. You can work around this by passing `-DCMAKE_POSITION_INDEPENDENT_CODE=ON` to CMake so that libzigcpp is built as PIC. Resolves: https://codeberg.org/ziglang/zig/issues/30780

1 files changed, 1812 insertions(+), 833 deletions(-)

src/link/Elf2.zig+1812-833
...@@ -34,6 +34,8 @@ shndx: struct {...@@ -34,6 +34,8 @@ shndx: struct {
34 dynstr: Section.Index,34 dynstr: Section.Index,
35 dynamic: Section.Index,35 dynamic: Section.Index,
36 tdata: Section.Index,36 tdata: Section.Index,
37 rela_dyn: Section.Index,
38 rela_plt: Section.Index,
37 // These sections are created only as needed, and are initially `.UNDEF`.39 // These sections are created only as needed, and are initially `.UNDEF`.
38 init_array: Section.Index,40 init_array: Section.Index,
39 fini_array: Section.Index,41 fini_array: Section.Index,
...@@ -65,13 +67,23 @@ dso_globals: std.array_hash_map.Auto(String(.strtab), std.elf.STT),...@@ -65,13 +67,23 @@ dso_globals: std.array_hash_map.Auto(String(.strtab), std.elf.STT),
65shstrtab: StringTable,67shstrtab: StringTable,
66strtab: StringTable,68strtab: StringTable,
67dynstr: StringTable,69dynstr: StringTable,
68got: struct {70
69 len: u32,71/// Indices map 1--1 to indices into the actual `.got` section.
70 tlsld: GotIndex,72///
71 plt: std.AutoArrayHashMapUnmanaged(Symbol.Id, void),73/// Value is the output relocation in `.rela.dyn` for the GOT entry.
72},74got: std.array_hash_map.Auto(GotKey, Section.RelaIndex.Optional),
73first_plt_reloc: Reloc.Index,75/// Indices map 1--1 to indices into the actual `.got.plt` section. These also equal indices into
74first_dynamic_reloc: Reloc.Index,76/// the relocations in `.rela.plt`, because every PLT entry has one output relocation (if a runtime
77/// relocation is no longer necessary, then neither is the corresponding PLT entry!).
78///
79/// PLT entries in this map may be "dead", meaning the PLT entry has been deemed unnecessary so is
80/// available for reuse---see `Elf.pltEntryIsDead`. Such entries must not be targeted by relocs.
81plt: std.array_hash_map.Auto(Symbol.Id, void),
82/// The `.plt` section contains zero or more symbol relocations starting at this index.
83plt_first_symbol_reloc: SymbolReloc.Index,
84/// The `.dynamic` section contains zero or more symbol relocations starting at this index.
85dynamic_first_symbol_reloc: SymbolReloc.Index,
86
75needed: std.AutoArrayHashMapUnmanaged(String(.dynstr), void),87needed: std.AutoArrayHashMapUnmanaged(String(.dynstr), void),
76inputs: std.ArrayList(struct {88inputs: std.ArrayList(struct {
77 path: std.Build.Cache.Path,89 path: std.Build.Cache.Path,
...@@ -81,31 +93,42 @@ inputs: std.ArrayList(struct {...@@ -81,31 +93,42 @@ inputs: std.ArrayList(struct {
81input_sections: std.ArrayList(InputSection),93input_sections: std.ArrayList(InputSection),
82input_section_pending_index: u32,94input_section_pending_index: u32,
83navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, struct {95navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, struct {
84 /// The start index of the contiguous sequence of relocations in this NAV.
85 first_reloc: Reloc.Index,
86 lsi: Symbol.LocalIndex,96 lsi: Symbol.LocalIndex,
97 /// The start index of the contiguous sequence of symbol relocations in this NAV.
98 first_symbol_reloc: SymbolReloc.Index,
99 /// The start index of the contiguous sequence of GOT relocations in this NAV.
100 first_got_reloc: GotReloc.Index,
87}),101}),
88uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, struct {102uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, struct {
89 /// The start index of the contiguous sequence of relocations in this UAV.
90 first_reloc: Reloc.Index,
91 lsi: Symbol.LocalIndex,103 lsi: Symbol.LocalIndex,
104 /// The start index of the contiguous sequence of symbol relocations in this UAV.
105 first_symbol_reloc: SymbolReloc.Index,
106 // No `first_got_reloc` field because a UAV never contains GOT relocations.
92}),107}),
93lazy: std.EnumArray(link.File.LazySymbol.Kind, struct {108lazy: std.EnumArray(link.File.LazySymbol.Kind, struct {
94 map: std.AutoArrayHashMapUnmanaged(InternPool.Index, struct {109 map: std.AutoArrayHashMapUnmanaged(InternPool.Index, struct {
95 /// The start index of the contiguous sequence of relocations in this lazy code/data.
96 first_reloc: Reloc.Index,
97 lsi: Symbol.LocalIndex,110 lsi: Symbol.LocalIndex,
111 /// The start index of the contiguous sequence of symbol relocations in this lazy code/data.
112 first_symbol_reloc: SymbolReloc.Index,
113 /// The start index of the contiguous sequence of GOT relocations in this lazy code/data.
114 first_got_reloc: GotReloc.Index,
98 }),115 }),
99 pending_index: u32,116 pending_index: u32,
100}),117}),
101pending_uavs: std.ArrayList(Node.UavMapIndex),118pending_uavs: std.ArrayList(Node.UavMapIndex),
102relocs: std.ArrayList(Reloc),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),
103/// Index matches the index into `shdrs`.123/// Index matches the index into `shdrs`.
104section_by_name: std.array_hash_map.Auto(String(.shstrtab), void),124section_by_name: std.array_hash_map.Auto(String(.shstrtab), void),
105
106/// Key is the name of a global symbol which has been moved to a new symtab index. Any relocation125/// Key is the name of a global symbol which has been moved to a new symtab index. Any relocation
107/// entries which target that symbol must be updated to reference the correct symbol index.126/// entries which target that symbol must be updated to reference the correct symbol index.
108changed_symtab_index: std.array_hash_map.Auto(String(.strtab), void),127changed_symtab_index: std.array_hash_map.Auto(String(.strtab), void),
128/// Counts how many relocations are currently in `.rela.dyn` which would require a `DT_TEXTREL`
129/// entry in the `.dynamic` section. This allows adding `DT_TEXTREL` to the output `.dynamic`
130/// section in `flush` only when it is actually necessary. See also `nodeRequiresTextrel`.
131textrel_count: u32,
109132
110const_prog_node: std.Progress.Node,133const_prog_node: std.Progress.Node,
111synth_prog_node: std.Progress.Node,134synth_prog_node: std.Progress.Node,
...@@ -120,21 +143,21 @@ const Node = union(enum) {...@@ -120,21 +143,21 @@ const Node = union(enum) {
120 shdr,143 shdr,
121 /// Cannot contain relocations.144 /// Cannot contain relocations.
122 segment: u32,145 segment: u32,
123 /// The section '.plt' may contain relocations via `elf.first_plt_reloc`.146 /// The section '.plt' may contain relocations via `elf.plt_first_symbol_reloc`.
124 ///147 ///
125 /// The section '.dynamic' may contain relocations via `elf.first_dynamic_reloc`.148 /// The section '.dynamic' may contain relocations via `elf.dynamic_first_symbol_reloc`.
126 ///149 ///
127 /// Otherwise, cannot contain relocations.150 /// Otherwise, cannot contain relocations.
128 section: Section.Index,151 section: Section.Index,
129 /// May contain relocations through the `first_reloc` field in `elf.input_sections`.152 /// May contain relocations.
130 input_section: InputSection.Index,153 input_section: InputSection.Index,
131 /// May contain relocations through the `first_reloc` field in `elf.navs`.154 /// May contain relocations.
132 nav: NavMapIndex,155 nav: NavMapIndex,
133 /// May contain relocations through the `first_reloc` field in `elf.uavs`.156 /// May contain relocations.
134 uav: UavMapIndex,157 uav: UavMapIndex,
135 /// May contain relocations through the `first_reloc` field in `elf.lazy.map`.158 /// May contain relocations.
136 lazy_code: LazyMapRef.Index(.code),159 lazy_code: LazyMapRef.Index(.code),
137 /// May contain relocations through the `first_reloc` field in `elf.lazy.map`.160 /// May contain relocations.
138 lazy_const_data: LazyMapRef.Index(.const_data),161 lazy_const_data: LazyMapRef.Index(.const_data),
139162
140 pub const InputIndex = enum(u32) {163 pub const InputIndex = enum(u32) {
...@@ -176,8 +199,11 @@ const Node = union(enum) {...@@ -176,8 +199,11 @@ const Node = union(enum) {
176 return elf.navs.values()[@intFromEnum(nmi)].lsi;199 return elf.navs.values()[@intFromEnum(nmi)].lsi;
177 }200 }
178201
179 fn firstReloc(nmi: NavMapIndex, elf: *const Elf) Reloc.Index {202 fn firstSymbolReloc(nmi: NavMapIndex, elf: *const Elf) SymbolReloc.Index {
180 return elf.navs.values()[@intFromEnum(nmi)].first_reloc;203 return elf.navs.values()[@intFromEnum(nmi)].first_symbol_reloc;
204 }
205 fn firstGotReloc(nmi: NavMapIndex, elf: *const Elf) GotReloc.Index {
206 return elf.navs.values()[@intFromEnum(nmi)].first_got_reloc;
181 }207 }
182 };208 };
183209
...@@ -192,8 +218,13 @@ const Node = union(enum) {...@@ -192,8 +218,13 @@ const Node = union(enum) {
192 return elf.uavs.values()[@intFromEnum(umi)].lsi;218 return elf.uavs.values()[@intFromEnum(umi)].lsi;
193 }219 }
194220
195 fn firstReloc(umi: UavMapIndex, elf: *const Elf) Reloc.Index {221 fn firstSymbolReloc(umi: UavMapIndex, elf: *const Elf) SymbolReloc.Index {
196 return elf.uavs.values()[@intFromEnum(umi)].first_reloc;222 return elf.uavs.values()[@intFromEnum(umi)].first_symbol_reloc;
223 }
224 fn firstGotReloc(umi: UavMapIndex, elf: *const Elf) GotReloc.Index {
225 _ = umi;
226 _ = elf;
227 return .none;
197 }228 }
198 };229 };
199230
...@@ -217,8 +248,11 @@ const Node = union(enum) {...@@ -217,8 +248,11 @@ const Node = union(enum) {
217 return lmi.ref().symbol(elf);248 return lmi.ref().symbol(elf);
218 }249 }
219250
220 fn firstReloc(lmi: @This(), elf: *const Elf) Reloc.Index {251 fn firstSymbolReloc(lmi: @This(), elf: *const Elf) SymbolReloc.Index {
221 return lmi.ref().firstReloc(elf);252 return elf.lazy.getPtrConst(kind).map.values()[@intFromEnum(lmi)].first_symbol_reloc;
253 }
254 fn firstGotReloc(lmi: @This(), elf: *const Elf) GotReloc.Index {
255 return elf.lazy.getPtrConst(kind).map.values()[@intFromEnum(lmi)].first_got_reloc;
222 }256 }
223 };257 };
224 }258 }
...@@ -230,10 +264,6 @@ const Node = union(enum) {...@@ -230,10 +264,6 @@ const Node = union(enum) {
230 pub fn symbol(lmr: LazyMapRef, elf: *const Elf) Symbol.LocalIndex {264 pub fn symbol(lmr: LazyMapRef, elf: *const Elf) Symbol.LocalIndex {
231 return elf.lazy.getPtrConst(lmr.kind).map.values()[lmr.index].lsi;265 return elf.lazy.getPtrConst(lmr.kind).map.values()[lmr.index].lsi;
232 }266 }
233
234 fn firstReloc(lmr: LazyMapRef, elf: *const Elf) Reloc.Index {
235 return elf.lazy.getPtrConst(lmr.kind).map.values()[lmr.index].first_reloc;
236 }
237 };267 };
238268
239 pub const Known = struct {269 pub const Known = struct {
...@@ -269,8 +299,10 @@ const InputSection = struct {...@@ -269,8 +299,10 @@ const InputSection = struct {
269 vaddr: u64,299 vaddr: u64,
270 /// The node corresponding to this input section.300 /// The node corresponding to this input section.
271 node: MappedFile.Node.Index,301 node: MappedFile.Node.Index,
272 /// The start index of the contiguous sequence of relocations in this input section.302 /// The start index of the contiguous sequence of symbol relocations in this input section.
273 first_reloc: Reloc.Index,303 first_symbol_reloc: SymbolReloc.Index,
304 /// The start index of the contiguous sequence of GOT relocations in this input section.
305 first_got_reloc: GotReloc.Index,
274306
275 const Index = enum(u32) {307 const Index = enum(u32) {
276 _,308 _,
...@@ -304,21 +336,53 @@ const Section = struct {...@@ -304,21 +336,53 @@ const Section = struct {
304 ///336 ///
305 /// If the section does not have flag `std.elf.SHF.ALLOC`, this is `.null`.337 /// If the section does not have flag `std.elf.SHF.ALLOC`, this is `.null`.
306 lsi: Symbol.LocalIndex,338 lsi: Symbol.LocalIndex,
307 rela_shndx: Section.Index,339 rela: union {
308 rela_free: RelIndex,340 /// This field is active if and only if this section is *not* a `SHT_RELA` section.
341 ///
342 /// This field's value refers to this section's corresponding relocation section, if it
343 /// currently has one. If this section does not currently have a relocation section, the
344 /// value is `.UNDEF`.
345 ///
346 /// This field is only ever non-`.UNDEF` when emitting a relocatable (`ET_REL`). While there
347 /// are also output relocations in DSOs, they are all placed in the `.rela.dyn`
348 /// (`elf.shdnx.rela_dyn`) and `.rela.plt` (`elf.shndx.rela_plt`) sections, rather than
349 /// having separate relocation sections for each section.
350 shndx: Section.Index,
351
352 /// This field is active if and only if this section *is* a `SHT_RELA` section.
353 ///
354 /// This is the head of a single-linked list of free `ElfN.Rela` entries in this section.
355 /// Entries in this list have `info.type` set to `R_*_NONE`, have `info.sym` set to 0, and
356 /// have `offset` set to `@enumFromInt(next)` where `next` is `RelaIndex.Optional`. Also,
357 /// `addend` is set to the length of the list starting from this point; so the last node in
358 /// the list has `addend = 1`, the one before it has `addend = 2`, etc. This is so that the
359 /// head node always contains the current length of the list.
360 ///
361 /// It would be okay to store these values (in the `offset` and `addend` fields) in the
362 /// compiler's host endianness, because they will never be read by other tooling. However,
363 /// we nonetheless use target endianness, because using host endianness would introduce an
364 /// unnecessary dependency of the output binary on the compiler's host architecture.
365 free_head: RelaIndex.Optional,
366 },
309367
310 pub const RelIndex = enum(u32) {368 const RelaIndex = enum(u32) {
311 none,369 none,
312 _,370 _,
313371
314 pub fn wrap(i: ?u32) RelIndex {372 const Optional = enum(u32) {
315 return @enumFromInt((i orelse return .none) + 1);373 none = std.math.maxInt(u32),
316 }374 _,
317 pub fn unwrap(ri: RelIndex) ?u32 {375
318 return switch (ri) {376 fn unwrap(opt: RelaIndex.Optional) ?RelaIndex {
319 .none => null,377 return switch (opt) {
320 _ => @intFromEnum(ri) - 1,378 .none => null,
321 };379 _ => @enumFromInt(@intFromEnum(opt)),
380 };
381 }
382 };
383
384 fn toOptional(i: RelaIndex) RelaIndex.Optional {
385 return @enumFromInt(@intFromEnum(i));
322 }386 }
323 };387 };
324388
...@@ -390,9 +454,690 @@ const Section = struct {...@@ -390,9 +454,690 @@ const Section = struct {
390 inline else => |shdr| elf.targetStore(&shdr.name, @intFromEnum(shstrtab_entry)),454 inline else => |shdr| elf.targetStore(&shdr.name, @intFromEnum(shstrtab_entry)),
391 }455 }
392 }456 }
457
458 /// Asserts that `shndx` is a `SHT_RELA` section and ensures that its node has enough unused
459 /// space to hold `n` additional `ElfN.Rela` entries.
460 fn relaEnsureAdditionalCapacity(rela_shndx: Index, elf: *Elf, n: usize) !void {
461 const node = rela_shndx.get(elf).ni;
462 const need_size: u64 = switch (elf.shdrPtr(rela_shndx)) {
463 inline else => |shdr, class| need_size: {
464 assert(elf.targetLoad(&shdr.type) == .RELA);
465 const cur_size = elf.targetLoad(&shdr.size);
466 const ent_size = @sizeOf(class.ElfN().Rela);
467 assert(elf.targetLoad(&shdr.entsize) == ent_size);
468 const free_len: u32 = free_len: {
469 const opt_free_head = rela_shndx.get(elf).rela.free_head;
470 const free_head = opt_free_head.unwrap() orelse break :free_len 0;
471 const relas: []const class.ElfN().Rela = @ptrCast(@alignCast(
472 node.slice(&elf.mf)[0..@intCast(cur_size)],
473 ));
474 const free_len = elf.targetLoad(&relas[@intFromEnum(free_head)].addend);
475 assert(free_len > 0);
476 break :free_len @intCast(free_len);
477 };
478 const need_additional = n -| free_len;
479 break :need_size cur_size + need_additional * ent_size;
480 },
481 };
482 _, const cur_node_size = node.location(&elf.mf).resolve(&elf.mf);
483 if (need_size > cur_node_size) {
484 const gpa = elf.base.comp.gpa;
485 try node.resize(&elf.mf, gpa, need_size +| need_size / MappedFile.growth_factor);
486 }
487 }
488
489 /// Asserts that `shndx` is a `SHT_RELA` section and deletes the `ElfN.Rela` entry at the
490 /// given `index` in it. The entry is added to the free-list for reuse later. Asserts that
491 /// the relocation entry at `index` is not already free.
492 fn relaDeleteOne(rela_shndx: Index, elf: *Elf, index: RelaIndex) void {
493 switch (elf.shdrPtr(rela_shndx)) {
494 inline else => |shdr, class| {
495 assert(elf.targetLoad(&shdr.type) == .RELA);
496 assert(elf.targetLoad(&shdr.entsize) == @sizeOf(class.ElfN().Rela));
497 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
498 rela_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(elf.targetLoad(&shdr.size))],
499 ));
500 const opt_free_head = rela_shndx.get(elf).rela.free_head;
501 const old_free_len: u32 = free_len: {
502 const free_head = opt_free_head.unwrap() orelse break :free_len 0;
503 const free_len = elf.targetLoad(&relas[@intFromEnum(free_head)].addend);
504 assert(free_len > 0);
505 break :free_len @intCast(free_len);
506 };
507 const none_reloc_type = MachineRelocType.none(elf).unwrap(elf);
508 {
509 const old_type = elf.targetLoad(&relas[@intFromEnum(index)].info).type;
510 assert(old_type != none_reloc_type); // bug: `index` is already in the free-list
511 }
512 relas[@intFromEnum(index)] = .{
513 .offset = @intFromEnum(opt_free_head), // next
514 .info = .{
515 .type = @intCast(none_reloc_type),
516 .sym = 0,
517 },
518 .addend = @intCast(old_free_len + 1), // list length
519 };
520 if (elf.targetEndian() != native_endian) {
521 std.mem.byteSwapAllFields(class.ElfN().Rela, &relas[@intFromEnum(index)]);
522 }
523 },
524 }
525 rela_shndx.get(elf).rela.free_head = index.toOptional();
526 }
527
528 /// Asserts that `shndx` is a `SHT_RELA` section and adds a new `ElfN.Rela` entry to it with
529 /// the given field values. Returns the index of the populated entry. Asserts that capacity
530 /// for this operation was already guaranteed using `relaEnsureAdditionalCapacity`.
531 fn relaAddOneAssumeCapacity(rela_shndx: Index, elf: *Elf, opts: struct {
532 type: MachineRelocType,
533 offset: u64,
534 /// This is a raw `u32` because whether this is an index into `.symtab` (`Symbol.Index`)
535 /// or an index into `.dynsym` is contextual.
536 raw_sym_index: u32,
537 addend: i64,
538 }) RelaIndex {
539 switch (elf.shdrPtr(rela_shndx)) {
540 inline else => |shdr, class| {
541 assert(elf.targetLoad(&shdr.type) == .RELA);
542 const ent_size = @sizeOf(class.ElfN().Rela);
543 assert(elf.targetLoad(&shdr.entsize) == ent_size);
544 const new_index: RelaIndex = if (rela_shndx.get(elf).rela.free_head.unwrap()) |free_head| new_index: {
545 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
546 rela_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(elf.targetLoad(&shdr.size))],
547 ));
548 const next: RelaIndex.Optional = @enumFromInt(elf.targetLoad(
549 &relas[@intFromEnum(free_head)].offset,
550 ));
551 rela_shndx.get(elf).rela.free_head = next;
552
553 const old_free_len: u32 = @intCast(
554 elf.targetLoad(&relas[@intFromEnum(free_head)].addend),
555 );
556 const new_free_len: u32 = if (next.unwrap()) |i| @intCast(
557 elf.targetLoad(&relas[@intFromEnum(i)].addend),
558 ) else 0;
559 assert(new_free_len == old_free_len - 1);
560
561 break :new_index free_head;
562 } else new_index: {
563 const old_size = elf.targetLoad(&shdr.size);
564 const new_size = old_size + ent_size;
565 elf.targetStore(&shdr.size, new_size);
566 if (rela_shndx == elf.shndx.rela_dyn) {
567 elf.updateDynamicEntry(std.elf.DT_RELASZ, new_size);
568 } else if (rela_shndx == elf.shndx.rela_plt) {
569 elf.updateDynamicEntry(std.elf.DT_PLTRELSZ, new_size);
570 }
571 break :new_index @enumFromInt(@divExact(old_size, ent_size));
572 };
573 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
574 rela_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(elf.targetLoad(&shdr.size))],
575 ));
576 relas[@intFromEnum(new_index)] = .{
577 .offset = @intCast(opts.offset),
578 .info = .{
579 .type = @intCast(opts.type.unwrap(elf)),
580 .sym = @intCast(opts.raw_sym_index),
581 },
582 .addend = @intCast(opts.addend),
583 };
584 if (elf.targetEndian() != native_endian) {
585 std.mem.byteSwapAllFields(class.ElfN().Rela, &relas[@intFromEnum(new_index)]);
586 }
587 return new_index;
588 },
589 }
590 }
591
592 /// Asserts that `shndx` is a `SHT_RELA` section and updates the `info.sym` field of the
593 /// `ElfN.Rela` entry at the given index. As with `relaAddOneAssumeCapacity`, the symbol
594 /// index is a raw `u32`, because it may be an index into `.symtab` or an index into
595 /// `.dynsym`. Asserts that `index` is not in the free-list (i.e. is not deleted).
596 fn relaUpdateSym(rela_shndx: Index, elf: *Elf, index: RelaIndex, raw_sym_index: u32) void {
597 switch (elf.shdrPtr(rela_shndx)) {
598 inline else => |shdr, class| {
599 assert(elf.targetLoad(&shdr.type) == .RELA);
600 assert(elf.targetLoad(&shdr.entsize) == @sizeOf(class.ElfN().Rela));
601 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
602 rela_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(elf.targetLoad(&shdr.size))],
603 ));
604 const rela_info = elf.targetLoad(&relas[@intFromEnum(index)].info);
605 {
606 const none_reloc_type = MachineRelocType.none(elf).unwrap(elf);
607 assert(rela_info.type != none_reloc_type); // bug: `index` is in the free-list
608 }
609 elf.targetStore(&relas[@intFromEnum(index)].info, .{
610 .type = rela_info.type,
611 .sym = @intCast(raw_sym_index),
612 });
613 },
614 }
615 }
616
617 /// Asserts that `shndx` is a `SHT_RELA` section and updates the `offset` field of the
618 /// `ElfN.Rela` entry at the given index. Asserts that `index` is not in the free-list (i.e.
619 /// it is not deleted).
620 fn relaSetOffset(rela_shndx: Index, elf: *Elf, index: RelaIndex, new_offset: u64) void {
621 switch (elf.shdrPtr(rela_shndx)) {
622 inline else => |shdr, class| {
623 assert(elf.targetLoad(&shdr.type) == .RELA);
624 assert(elf.targetLoad(&shdr.entsize) == @sizeOf(class.ElfN().Rela));
625 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
626 rela_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(elf.targetLoad(&shdr.size))],
627 ));
628 {
629 const rela_info = elf.targetLoad(&relas[@intFromEnum(index)].info);
630 const none_reloc_type = MachineRelocType.none(elf).unwrap(elf);
631 assert(rela_info.type != none_reloc_type); // bug: `index` is in the free-list
632 }
633 elf.targetStore(&relas[@intFromEnum(index)].offset, @intCast(new_offset));
634 },
635 }
636 }
637
638 /// Asserts that `shndx` is a `SHT_RELA` section and updates the `offset` field of the
639 /// `ElfN.Rela` entry at the given index, by subtracting `old_base` and adding `new_base`.
640 /// Asserts that `index` is not in the free-list (i.e. it is not deleted).
641 fn relaAdjustOffset(rela_shndx: Index, elf: *Elf, index: RelaIndex, old_base: u64, new_base: u64) void {
642 switch (elf.shdrPtr(rela_shndx)) {
643 inline else => |shdr, class| {
644 assert(elf.targetLoad(&shdr.type) == .RELA);
645 assert(elf.targetLoad(&shdr.entsize) == @sizeOf(class.ElfN().Rela));
646 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
647 rela_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(elf.targetLoad(&shdr.size))],
648 ));
649 {
650 const rela_info = elf.targetLoad(&relas[@intFromEnum(index)].info);
651 const none_reloc_type = MachineRelocType.none(elf).unwrap(elf);
652 assert(rela_info.type != none_reloc_type); // bug: `index` is in the free-list
653 }
654 const old_offset = elf.targetLoad(&relas[@intFromEnum(index)].offset);
655 elf.targetStore(&relas[@intFromEnum(index)].offset, @intCast(
656 old_offset - old_base + new_base,
657 ));
658 },
659 }
660 }
393 };661 };
394};662};
395663
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
396fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe_global }) !void {1141fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe_global }) !void {
397 const gpa = elf.base.comp.gpa;1142 const gpa = elf.base.comp.gpa;
3981143
...@@ -447,8 +1192,10 @@ fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe...@@ -447,8 +1192,10 @@ fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe
447fn ensureUnusedPltCapacity(elf: *Elf, len: u32) !void {1192fn ensureUnusedPltCapacity(elf: *Elf, len: u32) !void {
448 const gpa = elf.base.comp.gpa;1193 const gpa = elf.base.comp.gpa;
4491194
450 try elf.got.plt.ensureUnusedCapacity(gpa, len);1195 try elf.shndx.rela_plt.relaEnsureAdditionalCapacity(elf, len);
451 const need_plt_capacity = elf.got.plt.count() + len;1196
1197 try elf.plt.ensureUnusedCapacity(gpa, len);
1198 const need_plt_capacity = elf.plt.count() + len;
4521199
453 switch (elf.ehdrField(.machine)) {1200 switch (elf.ehdrField(.machine)) {
454 else => |machine| @panic(@tagName(machine)),1201 else => |machine| @panic(@tagName(machine)),
...@@ -479,21 +1226,25 @@ fn ensureUnusedPltCapacity(elf: *Elf, len: u32) !void {...@@ -479,21 +1226,25 @@ fn ensureUnusedPltCapacity(elf: *Elf, len: u32) !void {
479 const new_size = plt_sec_need_size +| plt_sec_need_size / MappedFile.growth_factor;1226 const new_size = plt_sec_need_size +| plt_sec_need_size / MappedFile.growth_factor;
480 try elf.shndx.plt_sec.get(elf).ni.resize(&elf.mf, gpa, new_size);1227 try elf.shndx.plt_sec.get(elf).ni.resize(&elf.mf, gpa, new_size);
481 }1228 }
4821229 },
483 // Ensure the `.rela.plt` section's node is big enough1230 }
484 const rela_plt_shndx = elf.shndx.got_plt.get(elf).rela_shndx;1231}
485 const rela_plt_need_size: usize = switch (elf.shdrPtr(rela_plt_shndx)) {1232/// Given an index into the PLT, returns whether that PLT entry is dead, meaning it may be reused at
486 inline else => |shdr| @intCast(elf.targetLoad(&shdr.entsize) * need_plt_capacity),1233/// any time and must not be targeted by relocations. See also the doc comment on `Elf.plt`.
487 };1234fn pltEntryIsDead(elf: *Elf, plt_index: usize) bool {
488 _, const rela_plt_cur_size = rela_plt_shndx.get(elf).ni.location(&elf.mf).resolve(&elf.mf);1235 assert(elf.shndx.plt != .UNDEF);
489 if (rela_plt_cur_size < rela_plt_need_size) {1236 assert(plt_index <= elf.plt.count());
490 const new_size = rela_plt_need_size +| rela_plt_need_size / MappedFile.growth_factor;1237 // We track which PLT entries are alive based on the relocation entries, since there is a 1-1
491 try rela_plt_shndx.get(elf).ni.resize(&elf.mf, gpa, new_size);1238 // mapping between PLT entries and `.rela.plt` entries and the relocation entries already have
492 } else {1239 // a free-list mechanism.
493 // Still mark `.rela.plt` as resized so that the DT_PLTRELSZ entry can1240 switch (elf.shdrPtr(elf.shndx.rela_plt)) {
494 // be updated if we do indeed add a PLT entry.1241 inline else => |rela_shdr, class| {
495 try rela_plt_shndx.get(elf).ni.resized(gpa, &elf.mf);1242 const size = elf.targetLoad(&rela_shdr.size);
496 }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);
497 },1248 },
498 }1249 }
499}1250}
...@@ -801,12 +1552,13 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{...@@ -801,12 +1552,13 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
801 if (new_global_ptr.dynsym_index != 0 and1552 if (new_global_ptr.dynsym_index != 0 and
802 opts.visibility == .DEFAULT and1553 opts.visibility == .DEFAULT and
803 opts.shndx == .UNDEF and1554 opts.shndx == .UNDEF and
804 @"type" == .FUNC)1555 (@"type" == .FUNC or @"type" == std.elf.STT.GNU_IFUNC))
805 {1556 {
806 // We're adding an undefined global STT_FUNC symbol which could be resolved by another DSO.1557 // We're adding an undefined global STT_FUNC symbol which could be resolved by another DSO.
807 // We therefore might need a PLT entry, so let's add one now. TODO: it'd be good to remove1558 // We therefore might need a PLT entry, so let's add one now.
808 // the PLT entry if we later discover a link inpu which resolves this reference.
809 elf.addPltEntry(opts.name.strtab, new_global_ptr.dynsym_index);1559 elf.addPltEntry(opts.name.strtab, new_global_ptr.dynsym_index);
1560 // TODO: we also need to emit a PLT entry if the symbol could be preempted/interposed! By
1561 // not doing that we're basically implementing the behavior of `-Bsymbolic-functions`.
810 }1562 }
8111563
812 return .global(opts.name.strtab);1564 return .global(opts.name.strtab);
...@@ -823,6 +1575,7 @@ fn setGlobalSymbolValue(...@@ -823,6 +1575,7 @@ fn setGlobalSymbolValue(
823 shndx: Section.Index,1575 shndx: Section.Index,
824 },1576 },
825) void {1577) void {
1578 assert(new.shndx != .UNDEF);
826 const old_node = global_ptr.symtab_index.ptr(elf).node;1579 const old_node = global_ptr.symtab_index.ptr(elf).node;
827 if (old_node != .none) {1580 if (old_node != .none) {
828 if (global_ptr.next_in_node != .empty) {1581 if (global_ptr.next_in_node != .empty) {
...@@ -897,7 +1650,40 @@ fn setGlobalSymbolValue(...@@ -897,7 +1650,40 @@ fn setGlobalSymbolValue(
897 },1650 },
898 };1651 };
8991652
900 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);
901}1687}
902/// When the same global symbol appears in two inputs---even if one symbol is defined and the other1688/// When the same global symbol appears in two inputs---even if one symbol is defined and the other
903/// undefined---their visibility values are combined to determine the resulting visibility, which1689/// undefined---their visibility values are combined to determine the resulting visibility, which
...@@ -1032,14 +1818,45 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {...@@ -1032,14 +1818,45 @@ fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {
1032 if (elf.targetEndian() != native_endian) {1818 if (elf.targetEndian() != native_endian) {
1033 std.mem.byteSwapAllFields(class.ElfN().Sym, dynsym);1819 std.mem.byteSwapAllFields(class.ElfN().Sym, dynsym);
1034 }1820 }
1821 global_ptr.dynsym_index = 0;
1035 }1822 }
1036 },1823 },
1037 }1824 }
1038}1825}
1039fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void {1826fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void {
1040 const target_endian = elf.targetEndian();1827 const target_endian = elf.targetEndian();
1041 const plt_index: u32 = @intCast(elf.got.plt.count());1828
1042 elf.got.plt.putAssumeCapacityNoClobber(.global(global_name), {});1829 // We use the existing free-list tracking of the `.rela.plt` section to also behave as a
1830 // free-list for the PLT itself---see `pltEntryIsDead` for details.
1831 const plt_index: u32 = @intFromEnum(elf.shndx.rela_plt.relaAddOneAssumeCapacity(elf, .{
1832 .type = .jumpSlot(elf),
1833 .offset = 0, // populated later
1834 .raw_sym_index = dynsym_index,
1835 .addend = 0,
1836 }));
1837
1838 // Now that we know the index, we can set the relocation's offset.
1839 const got_plt_addr = switch (elf.shdrPtr(elf.shndx.got_plt)) {
1840 inline else => |shdr, class| got_plt_addr: {
1841 const ent_size = @sizeOf(class.ElfN().Addr);
1842 assert(elf.targetLoad(&shdr.entsize) == ent_size);
1843 const offset = ent_size * @as(u64, 3 + plt_index);
1844 assert(offset <= elf.targetLoad(&shdr.size));
1845 break :got_plt_addr elf.targetLoad(&shdr.addr) + offset;
1846 },
1847 };
1848 elf.shndx.rela_plt.relaSetOffset(elf, @enumFromInt(plt_index), got_plt_addr);
1849
1850 if (plt_index < elf.plt.count()) {
1851 // We reused a free entry, so we're already done!
1852 elf.plt.setKey(plt_index, .global(global_name));
1853 return;
1854 }
1855
1856 // We added a new entry, so we now need to extend the PLT sections.
1857 assert(plt_index == elf.plt.count());
1858 elf.plt.putAssumeCapacityNoClobber(.global(global_name), {});
1859
1043 switch (elf.ehdrField(.machine)) {1860 switch (elf.ehdrField(.machine)) {
1044 else => |machine| @panic(@tagName(machine)),1861 else => |machine| @panic(@tagName(machine)),
1045 .X86_64 => {1862 .X86_64 => {
...@@ -1067,12 +1884,12 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void...@@ -1067,12 +1884,12 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void
1067 },1884 },
1068 };1885 };
10691886
1070 const got_plt_shndx = elf.shndx.got_plt;
1071 const got_plt_ni = elf.shndx.got_plt.get(elf).ni;1887 const got_plt_ni = elf.shndx.got_plt.get(elf).ni;
1072 const got_plt_addr = got_plt_addr: switch (elf.shdrPtr(got_plt_shndx)) {1888 switch (elf.shdrPtr(elf.shndx.got_plt)) {
1073 inline else => |shdr, class| {1889 inline else => |shdr, class| {
1074 const ent_size = @sizeOf(class.ElfN().Addr);1890 const ent_size = @sizeOf(class.ElfN().Addr);
1075 const old_size = ent_size * (3 + plt_index);1891 const old_size = ent_size * (3 + plt_index);
1892 assert(elf.targetLoad(&shdr.size) == old_size);
1076 elf.targetStore(&shdr.size, old_size + ent_size);1893 elf.targetStore(&shdr.size, old_size + ent_size);
1077 std.mem.writeInt(1894 std.mem.writeInt(
1078 class.ElfN().Addr,1895 class.ElfN().Addr,
...@@ -1080,9 +1897,8 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void...@@ -1080,9 +1897,8 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void
1080 @intCast(plt_addr),1897 @intCast(plt_addr),
1081 target_endian,1898 target_endian,
1082 );1899 );
1083 break :got_plt_addr elf.targetLoad(&shdr.addr) + old_size;
1084 },1900 },
1085 };1901 }
10861902
1087 const plt_sec_ni = elf.shndx.plt_sec.get(elf).ni;1903 const plt_sec_ni = elf.shndx.plt_sec.get(elf).ni;
1088 switch (elf.shdrPtr(elf.shndx.plt_sec)) {1904 switch (elf.shdrPtr(elf.shndx.plt_sec)) {
...@@ -1105,30 +1921,6 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void...@@ -1105,30 +1921,6 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void
1105 );1921 );
1106 },1922 },
1107 }1923 }
1108
1109 const rela_plt_shndx = got_plt_shndx.get(elf).rela_shndx;
1110 const rela_plt_ni = rela_plt_shndx.get(elf).ni;
1111 switch (elf.shdrPtr(rela_plt_shndx)) {
1112 inline else => |shdr, class| {
1113 const Rela = class.ElfN().Rela;
1114 const rela_size = elf.targetLoad(&shdr.entsize);
1115 const old_size = rela_size * plt_index;
1116 const new_size = old_size + rela_size;
1117 elf.targetStore(&shdr.size, new_size);
1118 const rela: *Rela = @ptrCast(@alignCast(
1119 rela_plt_ni.slice(&elf.mf)[@intCast(old_size)..@intCast(new_size)],
1120 ));
1121 rela.* = .{
1122 .offset = @intCast(got_plt_addr),
1123 .info = .{
1124 .type = @intFromEnum(std.elf.R_X86_64.JUMP_SLOT),
1125 .sym = @intCast(dynsym_index),
1126 },
1127 .addend = 0,
1128 };
1129 if (target_endian != native_endian) std.mem.byteSwapAllFields(Rela, rela);
1130 },
1131 }
1132 },1924 },
1133 }1925 }
1134}1926}
...@@ -1142,7 +1934,7 @@ const Symbol = struct {...@@ -1142,7 +1934,7 @@ const Symbol = struct {
1142 node: MappedFile.Node.Index,1934 node: MappedFile.Node.Index,
11431935
1144 /// The head of a linked list of relocations targeting this symbol.1936 /// The head of a linked list of relocations targeting this symbol.
1145 first_target_reloc: Reloc.Index,1937 first_target_reloc: SymbolReloc.Index,
11461938
1147 const Global = struct {1939 const Global = struct {
1148 /// The current index of the symtab entry for this global symbol.1940 /// The current index of the symtab entry for this global symbol.
...@@ -1159,16 +1951,6 @@ const Symbol = struct {...@@ -1159,16 +1951,6 @@ const Symbol = struct {
1159 ///1951 ///
1160 /// If `node` is `.none`, this is `.empty`.1952 /// If `node` is `.none`, this is `.empty`.
1161 prev_in_node: String(.strtab),1953 prev_in_node: String(.strtab),
1162
1163 /// Like `Symbol.Index.flushMoved`, but also updates the dynamic symbol table if necessary.
1164 fn flushMoved(g: *const Global, elf: *Elf, value: u64) void {
1165 g.symtab_index.flushMoved(elf, value);
1166 if (g.dynsym_index != 0) {
1167 switch (elf.dynsymPtr(g.dynsym_index)) {
1168 inline else => |sym| elf.targetStore(&sym.value, @intCast(value)),
1169 }
1170 }
1171 }
1172 };1954 };
11731955
1174 /// An index directly into the symtab. These values are not stable (global symbols are sometimes1956 /// An index directly into the symtab. These values are not stable (global symbols are sometimes
...@@ -1181,23 +1963,20 @@ const Symbol = struct {...@@ -1181,23 +1963,20 @@ const Symbol = struct {
1181 null = 0,1963 null = 0,
1182 _,1964 _,
11831965
1184 fn flushMoved(si: Symbol.Index, elf: *Elf, value: u64) void {
1185 switch (elf.symPtr(si)) {
1186 inline else => |sym| elf.targetStore(&sym.value, @intCast(value)),
1187 }
1188 if (elf.ehdrField(.type) != .REL) {
1189 var ri = si.ptr(elf).first_target_reloc;
1190 while (ri != .none) {
1191 const reloc = ri.get(elf);
1192 assert(reloc.target.index(elf) == si);
1193 reloc.apply(elf);
1194 ri = reloc.next;
1195 }
1196 }
1197 }
1198 fn ptr(si: Symbol.Index, elf: *Elf) *Symbol {1966 fn ptr(si: Symbol.Index, elf: *Elf) *Symbol {
1199 return &elf.symtab.items[@intFromEnum(si)];1967 return &elf.symtab.items[@intFromEnum(si)];
1200 }1968 }
1969
1970 fn applyTargetRelocs(si: Symbol.Index, elf: *Elf) void {
1971 assert(elf.ehdrField(.type) != .REL);
1972 var ri = si.ptr(elf).first_target_reloc;
1973 while (ri != .none) {
1974 const reloc = ri.get(elf);
1975 assert(reloc.target.index(elf) == si);
1976 reloc.apply(elf);
1977 ri = reloc.next;
1978 }
1979 }
1201 };1980 };
12021981
1203 /// A `LocalIndex` is a raw index into the symtab like `Index`, but it guarantees that the1982 /// A `LocalIndex` is a raw index into the symtab like `Index`, but it guarantees that the
...@@ -1262,6 +2041,44 @@ const Symbol = struct {...@@ -1262,6 +2041,44 @@ const Symbol = struct {
1262 };2041 };
1263 }2042 }
12642043
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
1265 /// Returns `true` if the target of `s` has moved, meaning the symbol's value will change at2082 /// Returns `true` if the target of `s` has moved, meaning the symbol's value will change at
1266 /// some point due to a call to `flushMoved`.2083 /// some point due to a call to `flushMoved`.
1267 fn hasMoved(s: Symbol.Id, elf: *Elf) bool {2084 fn hasMoved(s: Symbol.Id, elf: *Elf) bool {
...@@ -1328,7 +2145,8 @@ pub fn lazySymbol(elf: *Elf, lazy: link.File.LazySymbol) !link.File.SymbolId {...@@ -1328,7 +2145,8 @@ pub fn lazySymbol(elf: *Elf, lazy: link.File.LazySymbol) !link.File.SymbolId {
1328 .type = sym_type,2145 .type = sym_type,
1329 .shndx = shndx,2146 .shndx = shndx,
1330 }),2147 }),
1331 .first_reloc = .none,2148 .first_symbol_reloc = .none,
2149 .first_got_reloc = .none,
1332 };2150 };
1333 elf.nodes.appendAssumeCapacity(switch (lazy.kind) {2151 elf.nodes.appendAssumeCapacity(switch (lazy.kind) {
1334 .code => .{ .lazy_code = @enumFromInt(gop.index) },2152 .code => .{ .lazy_code = @enumFromInt(gop.index) },
...@@ -1377,7 +2195,7 @@ pub fn addReloc(...@@ -1377,7 +2195,7 @@ pub fn addReloc(
1377 offset: u64,2195 offset: u64,
1378 target: link.File.SymbolId,2196 target: link.File.SymbolId,
1379 addend: i64,2197 addend: i64,
1380 @"type": Reloc.Type,2198 @"type": MachineRelocType,
1381) !void {2199) !void {
1382 const node: MappedFile.Node.Index = Node.fromAtom(atom);2200 const node: MappedFile.Node.Index = Node.fromAtom(atom);
1383 try elf.ensureUnusedRelocCapacity(node, 1);2201 try elf.ensureUnusedRelocCapacity(node, 1);
...@@ -1532,7 +2350,6 @@ const StringTable = struct {...@@ -1532,7 +2350,6 @@ const StringTable = struct {
1532 .{ .slice = slice_const },2350 .{ .slice = slice_const },
1533 );2351 );
1534 if (gop.found_existing) return gop.key_ptr.*;2352 if (gop.found_existing) return gop.key_ptr.*;
1535 try ni.resized(gpa, &elf.mf);
1536 const old_size, const new_size = size: switch (elf.shdrPtr(shndx)) {2353 const old_size, const new_size = size: switch (elf.shdrPtr(shndx)) {
1537 inline else => |shdr| {2354 inline else => |shdr| {
1538 const old_size: u32 = @intCast(elf.targetLoad(&shdr.size));2355 const old_size: u32 = @intCast(elf.targetLoad(&shdr.size));
...@@ -1541,6 +2358,9 @@ const StringTable = struct {...@@ -1541,6 +2358,9 @@ const StringTable = struct {
1541 break :size .{ old_size, new_size };2358 break :size .{ old_size, new_size };
1542 },2359 },
1543 };2360 };
2361 if (shndx == elf.shndx.dynstr) {
2362 elf.updateDynamicEntry(std.elf.DT_STRSZ, new_size);
2363 }
1544 _, const node_size = ni.location(&elf.mf).resolve(&elf.mf);2364 _, const node_size = ni.location(&elf.mf).resolve(&elf.mf);
1545 if (new_size > node_size)2365 if (new_size > node_size)
1546 try ni.resize(&elf.mf, gpa, new_size +| new_size / MappedFile.growth_factor);2366 try ni.resize(&elf.mf, gpa, new_size +| new_size / MappedFile.growth_factor);
...@@ -1569,274 +2389,6 @@ const GotIndex = enum(u32) {...@@ -1569,274 +2389,6 @@ const GotIndex = enum(u32) {
1569 }2389 }
1570};2390};
15712391
1572const Reloc = extern struct {
1573 type: Reloc.Type,
1574 prev: Reloc.Index,
1575 next: Reloc.Index,
1576 node: MappedFile.Node.Index,
1577 target: Symbol.Id,
1578 index: Section.RelIndex,
1579 offset: u64,
1580 addend: i64,
1581
1582 pub const Type = extern union {
1583 X86_64: std.elf.R_X86_64,
1584 AARCH64: std.elf.R_AARCH64,
1585 RISCV: std.elf.R_RISCV,
1586 PPC64: std.elf.R_PPC64,
1587
1588 pub fn none(elf: *Elf) Reloc.Type {
1589 return switch (elf.ehdrField(.machine)) {
1590 else => unreachable,
1591 .AARCH64 => .{ .AARCH64 = .NONE },
1592 .PPC64 => .{ .PPC64 = .NONE },
1593 .RISCV => .{ .RISCV = .NONE },
1594 .X86_64 => .{ .X86_64 = .NONE },
1595 };
1596 }
1597 pub fn absAddr(elf: *Elf) Reloc.Type {
1598 return switch (elf.ehdrField(.machine)) {
1599 else => unreachable,
1600 .AARCH64 => .{ .AARCH64 = .ABS64 },
1601 .PPC64 => .{ .PPC64 = .ADDR64 },
1602 .RISCV => .{ .RISCV = .@"64" },
1603 .X86_64 => .{ .X86_64 = .@"64" },
1604 };
1605 }
1606 pub fn sizeAddr(elf: *Elf) Reloc.Type {
1607 return switch (elf.ehdrField(.machine)) {
1608 else => unreachable,
1609 .X86_64 => .{ .X86_64 = .SIZE64 },
1610 };
1611 }
1612
1613 pub fn wrap(int: u32, elf: *Elf) Reloc.Type {
1614 return switch (elf.ehdrField(.machine)) {
1615 else => unreachable,
1616 inline .AARCH64,
1617 .PPC64,
1618 .RISCV,
1619 .X86_64,
1620 => |machine| @unionInit(Reloc.Type, @tagName(machine), @enumFromInt(int)),
1621 };
1622 }
1623 pub fn unwrap(rt: Reloc.Type, elf: *Elf) u32 {
1624 return switch (elf.ehdrField(.machine)) {
1625 else => unreachable,
1626 inline .AARCH64,
1627 .PPC64,
1628 .RISCV,
1629 .X86_64,
1630 => |machine| @intFromEnum(@field(rt, @tagName(machine))),
1631 };
1632 }
1633 };
1634
1635 pub const Index = enum(u32) {
1636 none = std.math.maxInt(u32),
1637 _,
1638
1639 pub fn get(si: Reloc.Index, elf: *Elf) *Reloc {
1640 return &elf.relocs.items[@intFromEnum(si)];
1641 }
1642 };
1643
1644 pub fn apply(reloc: *const Reloc, elf: *Elf) void {
1645 assert(elf.ehdrField(.type) != .REL);
1646 assert(reloc.node != .none);
1647 if (reloc.node.hasMoved(&elf.mf) or reloc.target.hasMoved(elf)) {
1648 // There's no point applying the relocation now, because it will be re-applied by
1649 // `flushMoved` at some point anyway.
1650 return;
1651 }
1652 const node_vaddr: u64 = switch (elf.getNode(reloc.node)) {
1653 .file => unreachable,
1654 .ehdr => unreachable,
1655 .shdr => unreachable,
1656 .segment => unreachable,
1657 .section => |shndx| shndx.vaddr(elf),
1658 .input_section => |isi| isi.ptrConst(elf).vaddr,
1659 inline .nav,
1660 .uav,
1661 .lazy_code,
1662 .lazy_const_data,
1663 => |i| Symbol.Id.local(i.symbol(elf)).value(elf),
1664 };
1665 const dest_vaddr = node_vaddr + reloc.offset;
1666 const dest_slice = reloc.node.slice(&elf.mf)[@intCast(reloc.offset)..];
1667 const target_endian = elf.targetEndian();
1668 switch (elf.symPtr(reloc.target.index(elf))) {
1669 inline else => |target_sym, class| {
1670 const target_value = elf.targetLoad(&target_sym.value) +% @as(u64, @bitCast(reloc.addend));
1671 switch (elf.ehdrField(.machine)) {
1672 else => |machine| @panic(@tagName(machine)),
1673 .X86_64 => switch (reloc.type.X86_64) {
1674 else => |kind| @panic(@tagName(kind)),
1675 .@"64" => std.mem.writeInt(
1676 u64,
1677 dest_slice[0..8],
1678 target_value,
1679 target_endian,
1680 ),
1681 .PC32 => std.mem.writeInt(
1682 i32,
1683 dest_slice[0..4],
1684 @intCast(@as(i64, @bitCast(target_value -% dest_vaddr))),
1685 target_endian,
1686 ),
1687 .PLT32 => std.mem.writeInt(
1688 i32,
1689 dest_slice[0..4],
1690 @intCast(@as(i64, @bitCast(if (elf.got.plt.getIndex(reloc.target)) |plt_index|
1691 elf.targetLoad(&@field(
1692 elf.shdrPtr(elf.shndx.plt_sec),
1693 @tagName(class),
1694 ).addr) +% 16 * plt_index +%
1695 @as(u64, @bitCast(reloc.addend)) -% dest_vaddr
1696 else
1697 target_value -% dest_vaddr))),
1698 target_endian,
1699 ),
1700 .@"32" => std.mem.writeInt(
1701 u32,
1702 dest_slice[0..4],
1703 @intCast(target_value),
1704 target_endian,
1705 ),
1706 .@"32S" => std.mem.writeInt(
1707 i32,
1708 dest_slice[0..4],
1709 @intCast(@as(i64, @bitCast(target_value))),
1710 target_endian,
1711 ),
1712 .TLSLD => std.mem.writeInt(
1713 i32,
1714 dest_slice[0..4],
1715 @intCast(@as(i64, @bitCast(
1716 elf.shndx.got.vaddr(elf) +%
1717 @as(u64, @bitCast(reloc.addend)) +%
1718 @as(u64, 8) * elf.got.tlsld.unwrap().? -%
1719 dest_vaddr,
1720 ))),
1721 target_endian,
1722 ),
1723 .DTPOFF32 => std.mem.writeInt(
1724 i32,
1725 dest_slice[0..4],
1726 @intCast(@as(i64, @bitCast(target_value))),
1727 target_endian,
1728 ),
1729 .TPOFF32 => {
1730 const phdr = @field(elf.phdrSlice(), @tagName(class));
1731 const ph = &phdr[elf.getNode(elf.ni.tls).segment];
1732 assert(elf.targetLoad(&ph.type) == .TLS);
1733 std.mem.writeInt(
1734 i32,
1735 dest_slice[0..4],
1736 @intCast(@as(i64, @bitCast(target_value -% elf.targetLoad(&ph.memsz)))),
1737 target_endian,
1738 );
1739 },
1740 .SIZE32 => std.mem.writeInt(
1741 u32,
1742 dest_slice[0..4],
1743 @intCast(
1744 elf.targetLoad(&target_sym.size) +% @as(u64, @bitCast(reloc.addend)),
1745 ),
1746 target_endian,
1747 ),
1748 .SIZE64 => std.mem.writeInt(
1749 u64,
1750 dest_slice[0..8],
1751 elf.targetLoad(&target_sym.size) +% @as(u64, @bitCast(reloc.addend)),
1752 target_endian,
1753 ),
1754 },
1755 }
1756 },
1757 }
1758 }
1759
1760 pub fn delete(reloc: *Reloc, elf: *Elf) void {
1761 switch (reloc.prev) {
1762 .none => {
1763 const target_ptr = reloc.target.index(elf).ptr(elf);
1764 assert(target_ptr.first_target_reloc.get(elf) == reloc);
1765 target_ptr.first_target_reloc = reloc.next;
1766 },
1767 else => |prev| prev.get(elf).next = reloc.next,
1768 }
1769 switch (reloc.next) {
1770 .none => {},
1771 else => |next| next.get(elf).prev = reloc.prev,
1772 }
1773 switch (elf.ehdrField(.type)) {
1774 .NONE, .CORE, _ => unreachable,
1775 .REL => {
1776 const sh = elf.getNodeShndx(reloc.node).get(elf);
1777 switch (elf.shdrPtr(sh.rela_shndx)) {
1778 inline else => |shdr, class| {
1779 const Rela = class.ElfN().Rela;
1780 const ent_size = elf.targetLoad(&shdr.entsize);
1781 const start = ent_size * reloc.index.unwrap().?;
1782 const rela_slice = sh.rela_shndx.get(elf).ni.slice(&elf.mf);
1783 const rela: *Rela = @ptrCast(@alignCast(
1784 rela_slice[@intCast(start)..][0..@intCast(ent_size)],
1785 ));
1786 rela.* = .{
1787 .offset = @intFromEnum(sh.rela_free),
1788 .info = .{
1789 .type = @intCast(Reloc.Type.none(elf).unwrap(elf)),
1790 .sym = 0,
1791 },
1792 .addend = 0,
1793 };
1794 },
1795 }
1796 sh.rela_free = reloc.index;
1797 },
1798 .EXEC, .DYN => assert(reloc.index == .none),
1799 }
1800 reloc.* = undefined;
1801 }
1802
1803 fn updateTargetIndex(reloc: *const Reloc, elf: *Elf) void {
1804 assert(elf.ehdrField(.type) == .REL);
1805 const sh = elf.getNodeShndx(reloc.node).get(elf);
1806 switch (elf.shdrPtr(sh.rela_shndx)) {
1807 inline else => |shdr, class| {
1808 assert(elf.targetLoad(&shdr.entsize) == @sizeOf(class.ElfN().Rela));
1809 const size = elf.targetLoad(&shdr.size);
1810 const raw_rela_slice = sh.rela_shndx.get(elf).ni.slice(&elf.mf);
1811 const rela_slice: []class.ElfN().Rela = @ptrCast(@alignCast(raw_rela_slice[0..@intCast(size)]));
1812 elf.targetStore(&rela_slice[reloc.index.unwrap().?].info, .{
1813 .type = @intCast(reloc.type.unwrap(elf)),
1814 .sym = @intCast(@intFromEnum(reloc.target.index(elf))),
1815 });
1816 },
1817 }
1818 }
1819
1820 fn updateNodeOffset(reloc: *const Reloc, elf: *Elf, node_offset: u64) void {
1821 assert(elf.ehdrField(.type) == .REL);
1822 const total_offset = node_offset + reloc.offset;
1823 const sh = elf.getNodeShndx(reloc.node).get(elf);
1824 switch (elf.shdrPtr(sh.rela_shndx)) {
1825 inline else => |shdr, class| {
1826 assert(elf.targetLoad(&shdr.entsize) == @sizeOf(class.ElfN().Rela));
1827 const size = elf.targetLoad(&shdr.size);
1828 const raw_rela_slice = sh.rela_shndx.get(elf).ni.slice(&elf.mf);
1829 const rela_slice: []class.ElfN().Rela = @ptrCast(@alignCast(raw_rela_slice[0..@intCast(size)]));
1830 elf.targetStore(&rela_slice[reloc.index.unwrap().?].offset, @intCast(total_offset));
1831 },
1832 }
1833 }
1834
1835 comptime {
1836 if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Reloc) == 40);
1837 }
1838};
1839
1840pub fn open(2392pub fn open(
1841 arena: std.mem.Allocator,2393 arena: std.mem.Allocator,
1842 comp: *Compilation,2394 comp: *Compilation,
...@@ -1941,6 +2493,8 @@ fn create(...@@ -1941,6 +2493,8 @@ fn create(
1941 .dynstr = .UNDEF,2493 .dynstr = .UNDEF,
1942 .dynamic = .UNDEF,2494 .dynamic = .UNDEF,
1943 .tdata = .UNDEF,2495 .tdata = .UNDEF,
2496 .rela_dyn = .UNDEF,
2497 .rela_plt = .UNDEF,
1944 .init_array = .UNDEF,2498 .init_array = .UNDEF,
1945 .fini_array = .UNDEF,2499 .fini_array = .UNDEF,
1946 .preinit_array = .UNDEF,2500 .preinit_array = .UNDEF,
...@@ -1957,13 +2511,10 @@ fn create(...@@ -1957,13 +2511,10 @@ fn create(
1957 .shstrtab = .{ .map = .empty },2511 .shstrtab = .{ .map = .empty },
1958 .strtab = .{ .map = .empty },2512 .strtab = .{ .map = .empty },
1959 .dynstr = .{ .map = .empty },2513 .dynstr = .{ .map = .empty },
1960 .got = .{2514 .got = .empty,
1961 .len = 0,2515 .plt = .empty,
1962 .tlsld = .none,2516 .plt_first_symbol_reloc = .none,
1963 .plt = .empty,2517 .dynamic_first_symbol_reloc = .none,
1964 },
1965 .first_plt_reloc = .none,
1966 .first_dynamic_reloc = .none,
1967 .needed = .empty,2518 .needed = .empty,
1968 .inputs = .empty,2519 .inputs = .empty,
1969 .input_sections = .empty,2520 .input_sections = .empty,
...@@ -1975,12 +2526,15 @@ fn create(...@@ -1975,12 +2526,15 @@ fn create(
1975 .pending_index = 0,2526 .pending_index = 0,
1976 }),2527 }),
1977 .pending_uavs = .empty,2528 .pending_uavs = .empty,
1978 .relocs = .empty,2529 .symbol_relocs = .empty,
2530 .got_relocs = .empty,
2531 .tls_size_symbol_relocs = .empty,
1979 .section_by_name = .empty,2532 .section_by_name = .empty,
1980 .changed_symtab_index = .empty,2533 .changed_symtab_index = .empty,
1981 .const_prog_node = .none,2534 .const_prog_node = .none,
1982 .synth_prog_node = .none,2535 .synth_prog_node = .none,
1983 .input_prog_node = .none,2536 .input_prog_node = .none,
2537 .textrel_count = 0,
1984 };2538 };
1985 errdefer elf.deinit();2539 errdefer elf.deinit();
19862540
...@@ -2004,7 +2558,8 @@ pub fn deinit(elf: *Elf) void {...@@ -2004,7 +2558,8 @@ pub fn deinit(elf: *Elf) void {
2004 elf.shstrtab.map.deinit(gpa);2558 elf.shstrtab.map.deinit(gpa);
2005 elf.strtab.map.deinit(gpa);2559 elf.strtab.map.deinit(gpa);
2006 elf.dynstr.map.deinit(gpa);2560 elf.dynstr.map.deinit(gpa);
2007 elf.got.plt.deinit(gpa);2561 elf.got.deinit(gpa);
2562 elf.plt.deinit(gpa);
2008 elf.needed.deinit(gpa);2563 elf.needed.deinit(gpa);
2009 for (elf.inputs.items) |input| if (input.member) |m| gpa.free(m);2564 for (elf.inputs.items) |input| if (input.member) |m| gpa.free(m);
2010 elf.inputs.deinit(gpa);2565 elf.inputs.deinit(gpa);
...@@ -2013,7 +2568,9 @@ pub fn deinit(elf: *Elf) void {...@@ -2013,7 +2568,9 @@ pub fn deinit(elf: *Elf) void {
2013 elf.uavs.deinit(gpa);2568 elf.uavs.deinit(gpa);
2014 for (&elf.lazy.values) |*lazy| lazy.map.deinit(gpa);2569 for (&elf.lazy.values) |*lazy| lazy.map.deinit(gpa);
2015 elf.pending_uavs.deinit(gpa);2570 elf.pending_uavs.deinit(gpa);
2016 elf.relocs.deinit(gpa);2571 elf.symbol_relocs.deinit(gpa);
2572 elf.got_relocs.deinit(gpa);
2573 elf.tls_size_symbol_relocs.deinit(gpa);
2017 elf.section_by_name.deinit(gpa);2574 elf.section_by_name.deinit(gpa);
2018 elf.changed_symtab_index.deinit(gpa);2575 elf.changed_symtab_index.deinit(gpa);
2019 elf.* = undefined;2576 elf.* = undefined;
...@@ -2323,7 +2880,7 @@ fn initHeaders(...@@ -2323,7 +2880,7 @@ fn initHeaders(
2323 .entsize = 0,2880 .entsize = 0,
2324 };2881 };
2325 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Shdr, sh_undef);2882 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Shdr, sh_undef);
2326 elf.shdrs.appendAssumeCapacity(.{ .lsi = .null, .ni = .none, .rela_shndx = .UNDEF, .rela_free = .none });2883 elf.shdrs.appendAssumeCapacity(.{ .lsi = .null, .ni = .none, .rela = .{ .shndx = .UNDEF } });
23272884
2328 elf.symtab.addOneAssumeCapacity().* = .{2885 elf.symtab.addOneAssumeCapacity().* = .{
2329 .node = .none,2886 .node = .none,
...@@ -2398,8 +2955,15 @@ fn initHeaders(...@@ -2398,8 +2955,15 @@ fn initHeaders(
2398 if (@"type" != .REL) {2955 if (@"type" != .REL) {
2399 elf.shndx.got = try elf.addSection(elf.ni.data_rel_ro, .{2956 elf.shndx.got = try elf.addSection(elf.ni.data_rel_ro, .{
2400 .name = ".got",2957 .name = ".got",
2958 .type = .PROGBITS,
2959 // Reserve space for the reserved words, populated later.
2960 .size = switch (machine) {
2961 else => @panic(@tagName(machine)),
2962 .X86_64 => 3 * 8,
2963 },
2401 .flags = .{ .WRITE = true, .ALLOC = true },2964 .flags = .{ .WRITE = true, .ALLOC = true },
2402 .addralign = addr_align,2965 .addralign = addr_align,
2966 .entsize = @intCast(addr_align.toByteUnits()),
2403 });2967 });
2404 elf.shndx.got_plt = try elf.addSection(2968 elf.shndx.got_plt = try elf.addSection(
2405 if (elf.options.z_now) elf.ni.data_rel_ro else elf.ni.data,2969 if (elf.options.z_now) elf.ni.data_rel_ro else elf.ni.data,
...@@ -2413,6 +2977,7 @@ fn initHeaders(...@@ -2413,6 +2977,7 @@ fn initHeaders(
2413 .X86_64 => 3 * 8,2977 .X86_64 => 3 * 8,
2414 },2978 },
2415 .addralign = addr_align,2979 .addralign = addr_align,
2980 .entsize = @intCast(addr_align.toByteUnits()),
2416 },2981 },
2417 );2982 );
2418 const plt_size: std.elf.Xword, const plt_align: std.mem.Alignment, const plt_sec =2983 const plt_size: std.elf.Xword, const plt_align: std.mem.Alignment, const plt_sec =
...@@ -2508,7 +3073,7 @@ fn initHeaders(...@@ -2508,7 +3073,7 @@ fn initHeaders(
2508 .NONE, _ => unreachable,3073 .NONE, _ => unreachable,
2509 inline else => |ct_class| @sizeOf(ct_class.ElfN().Rela),3074 inline else => |ct_class| @sizeOf(ct_class.ElfN().Rela),
2510 };3075 };
2511 elf.shndx.got.get(elf).rela_shndx = try elf.addSection(elf.ni.rodata, .{3076 elf.shndx.rela_dyn = try elf.addSection(elf.ni.rodata, .{
2512 .name = ".rela.dyn",3077 .name = ".rela.dyn",
2513 .type = .RELA,3078 .type = .RELA,
2514 .flags = .{ .ALLOC = true },3079 .flags = .{ .ALLOC = true },
...@@ -2517,13 +3082,12 @@ fn initHeaders(...@@ -2517,13 +3082,12 @@ fn initHeaders(
2517 .entsize = rela_size,3082 .entsize = rela_size,
2518 .node_align = elf.mf.flags.block_size,3083 .node_align = elf.mf.flags.block_size,
2519 });3084 });
2520 const got_plt_shndx = elf.shndx.got_plt;3085 elf.shndx.rela_plt = try elf.addSection(elf.ni.rodata, .{
2521 got_plt_shndx.get(elf).rela_shndx = try elf.addSection(elf.ni.rodata, .{
2522 .name = ".rela.plt",3086 .name = ".rela.plt",
2523 .type = .RELA,3087 .type = .RELA,
2524 .flags = .{ .ALLOC = true, .INFO_LINK = true },3088 .flags = .{ .ALLOC = true, .INFO_LINK = true },
2525 .link = elf.shndx.dynsym.toSection().?,3089 .link = elf.shndx.dynsym.toSection().?,
2526 .info = got_plt_shndx.toSection().?,3090 .info = elf.shndx.got_plt.toSection().?,
2527 .addralign = addr_align,3091 .addralign = addr_align,
2528 .entsize = rela_size,3092 .entsize = rela_size,
2529 .node_align = elf.mf.flags.block_size,3093 .node_align = elf.mf.flags.block_size,
...@@ -2546,7 +3110,7 @@ fn initHeaders(...@@ -2546,7 +3110,7 @@ fn initHeaders(
2546 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *0x0(%rip)3110 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *0x0(%rip)
2547 0x0f, 0x1f, 0x40, 0x00, // nopl 0x0(%rax)3111 0x0f, 0x1f, 0x40, 0x00, // nopl 0x0(%rax)
2548 });3112 });
2549 elf.first_plt_reloc = @enumFromInt(elf.relocs.items.len);3113 elf.plt_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len);
2550 try elf.ensureUnusedRelocCapacity(plt_ni, 2);3114 try elf.ensureUnusedRelocCapacity(plt_ni, 2);
2551 elf.addRelocAssumeCapacity(3115 elf.addRelocAssumeCapacity(
2552 plt_ni,3116 plt_ni,
...@@ -2575,6 +3139,26 @@ fn initHeaders(...@@ -2575,6 +3139,26 @@ fn initHeaders(
2575 elf.phdrs.items[tls_phndx] = elf.ni.tls;3139 elf.phdrs.items[tls_phndx] = elf.ni.tls;
2576 }3140 }
25773141
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
2578 // Create any always-provided linker-defined symbols. The symbols marking the `INIT_ARRAY`/3162 // Create any always-provided linker-defined symbols. The symbols marking the `INIT_ARRAY`/
2579 // `FINI_ARRAY`/`PREINIT_ARRAY` sections are instead created by `createInitFiniArraySection`3163 // `FINI_ARRAY`/`PREINIT_ARRAY` sections are instead created by `createInitFiniArraySection`
2580 // when needed (it seems to be legal to leave those undefined if the section doesn't exist).3164 // when needed (it seems to be legal to leave those undefined if the section doesn't exist).
...@@ -2679,7 +3263,7 @@ fn getNode(elf: *const Elf, ni: MappedFile.Node.Index) Node {...@@ -2679,7 +3263,7 @@ fn getNode(elf: *const Elf, ni: MappedFile.Node.Index) Node {
2679 return elf.nodes.get(@intFromEnum(ni));3263 return elf.nodes.get(@intFromEnum(ni));
2680}3264}
2681/// Asserts that `ni` is a section, input section, NAV, UAV, or lazy code/data.3265/// Asserts that `ni` is a section, input section, NAV, UAV, or lazy code/data.
2682fn getNodeShndx(elf: *Elf, ni: MappedFile.Node.Index) Section.Index {3266fn getNodeShndx(elf: *const Elf, ni: MappedFile.Node.Index) Section.Index {
2683 return switch (elf.getNode(ni)) {3267 return switch (elf.getNode(ni)) {
2684 .file => unreachable,3268 .file => unreachable,
2685 .ehdr => unreachable,3269 .ehdr => unreachable,
...@@ -2717,55 +3301,80 @@ fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {...@@ -2717,55 +3301,80 @@ fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
2717/// Asserts that `ni` must be a node which supports relocations (see `Elf.Node`). Does not support3301/// Asserts that `ni` must be a node which supports relocations (see `Elf.Node`). Does not support
2718/// the special-case sections '.plt' and '.dynamic'.3302/// the special-case sections '.plt' and '.dynamic'.
2719fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void {3303fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void {
2720 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)) {
2721 .file => unreachable, // cannot contain relocs3305 .file => unreachable, // cannot contain relocs
2722 .ehdr => unreachable, // cannot contain relocs3306 .ehdr => unreachable, // cannot contain relocs
2723 .shdr => unreachable, // cannot contain relocs3307 .shdr => unreachable, // cannot contain relocs
2724 .segment => unreachable, // cannot contain relocs3308 .segment => unreachable, // cannot contain relocs
2725 .section => unreachable, // cannot contain relocs (.plt and .dynamic unsupported)3309 .section => unreachable, // cannot contain relocs (.plt and .dynamic unsupported)
2726 .input_section => |isi| &elf.input_sections.items[@intFromEnum(isi)].first_reloc,3310 .input_section => |isi| .{
2727 .nav => |nmi| &elf.navs.values()[@intFromEnum(nmi)].first_reloc,3311 &elf.input_sections.items[@intFromEnum(isi)].first_symbol_reloc,
2728 .uav => |umi| &elf.uavs.values()[@intFromEnum(umi)].first_reloc,3312 &elf.input_sections.items[@intFromEnum(isi)].first_got_reloc,
2729 inline .lazy_code, .lazy_const_data => |lmi| &elf.lazy.getPtr(lmi.ref().kind).map.values()[lmi.ref().index].first_reloc,3313 },
3314 .nav => |nmi| .{
3315 &elf.navs.values()[@intFromEnum(nmi)].first_symbol_reloc,
3316 &elf.navs.values()[@intFromEnum(nmi)].first_got_reloc,
3317 },
3318 .uav => |umi| .{
3319 &elf.uavs.values()[@intFromEnum(umi)].first_symbol_reloc,
3320 null,
3321 },
3322 inline .lazy_code, .lazy_const_data => |lmi| .{
3323 &elf.lazy.getPtr(lmi.ref().kind).map.values()[lmi.ref().index].first_symbol_reloc,
3324 &elf.lazy.getPtr(lmi.ref().kind).map.values()[lmi.ref().index].first_got_reloc,
3325 },
2730 };3326 };
2731 if (first_reloc_ptr.* != .none) {3327
2732 for (elf.relocs.items[@intFromEnum(first_reloc_ptr.*)..]) |*reloc| {3328 if (symbol_relocs.* != .none) {
3329 for (
3330 elf.symbol_relocs.items[@intFromEnum(symbol_relocs.*)..],
3331 @intFromEnum(symbol_relocs.*)..,
3332 ) |*reloc, index| {
2733 if (reloc.node != ni) break;3333 if (reloc.node != ni) break;
2734 reloc.delete(elf);3334 reloc.delete(elf, @enumFromInt(index));
2735 }3335 }
2736 }3336 }
2737 first_reloc_ptr.* = @enumFromInt(elf.relocs.items.len);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 }
3345 }
3346 ptr.* = @enumFromInt(elf.got_relocs.items.len);
3347 }
2738}3348}
27393349
2740/// Given that `node` has moved, updates all relocations in `node` (starting from `first_reloc`) as3350/// Given that `node` has moved, updates all relocations in `node` as needed. In relocatables, this
2741/// needed. In relocatables, this means updating the offsets of those relocations. In ELF modules,3351/// means updating the relocations' offsets. In ELF modules, this means applying the relocations.
2742/// this means applying the relocations.
2743fn flushMovedNodeRelocs(3352fn flushMovedNodeRelocs(
2744 elf: *Elf,3353 elf: *Elf,
2745 node: MappedFile.Node.Index,3354 node: MappedFile.Node.Index,
2746 node_vaddr: u64,3355 node_vaddr: u64,
2747 first_reloc: Reloc.Index,3356 first_symbol_reloc: SymbolReloc.Index,
3357 first_got_reloc: GotReloc.Index,
2748) void {3358) void {
2749 if (first_reloc == .none) return;3359 if (first_symbol_reloc != .none) {
2750 switch (elf.ehdrField(.type)) {3360 for (elf.symbol_relocs.items[@intFromEnum(first_symbol_reloc)..]) |*reloc| {
2751 .NONE, .CORE, _ => unreachable,3361 if (reloc.node != node) break;
2752 .REL => {3362 if (reloc.rela_index.unwrap()) |rela_index| {
2753 // In a relocatable, we're not actually applying any relocations ourselves, but we need3363 // Update the offsets of any `ElfN.Rela` entry we've emitted, since the node they're
2754 // to update the offsets of the relocation entries since the node they're in has moved.3364 // in has moved, so their offset within the section might also have moved.
2755 for (elf.relocs.items[@intFromEnum(first_reloc)..]) |*reloc| {3365 reloc.relaSection(elf).relaSetOffset(elf, rela_index, node_vaddr + reloc.offset);
2756 if (reloc.node != node) break;3366 } else {
2757 reloc.updateNodeOffset(elf, node_vaddr);3367 // We've applied this relocation ourselves! Just re-apply it now.
2758 }
2759 },
2760 .EXEC, .DYN => {
2761 // For an ELF module, we just need to apply relocations.
2762 for (elf.relocs.items[@intFromEnum(first_reloc)..]) |*reloc| {
2763 if (reloc.node != node) break;
2764 reloc.apply(elf);3368 reloc.apply(elf);
2765 }3369 }
2766 // TODO: once we're emitting runtime relocation entries, we need to update their offsets3370 }
2767 // too, like the logic for relocatables above.3371 }
2768 },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 }
2769 }3378 }
2770}3379}
27713380
...@@ -3109,7 +3718,8 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavM...@@ -3109,7 +3718,8 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavM
3109 .type = elf.navType(nav.resolved.?),3718 .type = elf.navType(nav.resolved.?),
3110 .shndx = shndx,3719 .shndx = shndx,
3111 }),3720 }),
3112 .first_reloc = .none,3721 .first_symbol_reloc = .none,
3722 .first_got_reloc = .none,
3113 };3723 };
3114 elf.nodes.appendAssumeCapacity(.{ .nav = nmi });3724 elf.nodes.appendAssumeCapacity(.{ .nav = nmi });
3115 }3725 }
...@@ -3158,7 +3768,7 @@ fn uavMapIndex(...@@ -3158,7 +3768,7 @@ fn uavMapIndex(
3158 .type = .OBJECT,3768 .type = .OBJECT,
3159 .shndx = shndx,3769 .shndx = shndx,
3160 }),3770 }),
3161 .first_reloc = .none,3771 .first_symbol_reloc = .none,
3162 };3772 };
3163 elf.nodes.appendAssumeCapacity(.{ .uav = umi });3773 elf.nodes.appendAssumeCapacity(.{ .uav = umi });
3164 elf.const_prog_node.increaseEstimatedTotalItems(1);3774 elf.const_prog_node.increaseEstimatedTotalItems(1);
...@@ -3447,10 +4057,11 @@ fn loadObject(...@@ -3447,10 +4057,11 @@ fn loadObject(
3447 switch (elf.shdrPtr(shndx.*)) {4057 switch (elf.shdrPtr(shndx.*)) {
3448 inline else => |shdr| {4058 inline else => |shdr| {
3449 const old_size = elf.targetLoad(&shdr.size);4059 const old_size = elf.targetLoad(&shdr.size);
3450 elf.targetStore(&shdr.size, @intCast(old_size + section.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);
3451 },4063 },
3452 }4064 }
3453 try shndx.get(elf).ni.resized(gpa, &elf.mf);
3454 break :shndx shndx.*;4065 break :shndx shndx.*;
3455 },4066 },
3456 .has_file_bits = true,4067 .has_file_bits = true,
...@@ -3482,7 +4093,8 @@ fn loadObject(...@@ -3482,7 +4093,8 @@ fn loadObject(
3482 // zero-based. This will eventually be updated by `flushMoved`.4093 // zero-based. This will eventually be updated by `flushMoved`.
3483 .vaddr = 0,4094 .vaddr = 0,
3484 .node = ni,4095 .node = ni,
3485 .first_reloc = .none,4096 .first_symbol_reloc = .none,
4097 .first_got_reloc = .none,
3486 };4098 };
3487 elf.synth_prog_node.increaseEstimatedTotalItems(1);4099 elf.synth_prog_node.increaseEstimatedTotalItems(1);
3488 }4100 }
...@@ -3777,6 +4389,8 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {...@@ -3777,6 +4389,8 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {
3777 .DEFAULT, .PROTECTED => {},4389 .DEFAULT, .PROTECTED => {},
3778 }4390 }
37794391
4392 if (sym.shndx == std.elf.SHN_UNDEF) continue;
4393
3780 if (sym.name >= dynstr.len) {4394 if (sym.name >= dynstr.len) {
3781 return diags.failParse(path, "bad symbol name string", .{});4395 return diags.failParse(path, "bad symbol name string", .{});
3782 }4396 }
...@@ -3787,39 +4401,47 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {...@@ -3787,39 +4401,47 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {
3787 gop.value_ptr.* = sym.info.type;4401 gop.value_ptr.* = sym.info.type;
3788 }4402 }
37894403
3790 // If there's already an undefined symbol by this name of type STT_NOTYPE, populate4404 // If there's already an undefined symbol by this name of type STT_NOTYPE, populate
3791 // its type now.4405 // its type now.
3792 update_sym_type: {4406 const global_ptr = elf.globals.strong_undef.getPtr(name) orelse
3793 const global_ptr = elf.globals.strong_undef.getPtr(name) orelse4407 elf.globals.weak_undef.getPtr(name) orelse
3794 elf.globals.weak_undef.getPtr(name) orelse4408 continue;
3795 break :update_sym_type;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;
37964419
3797 if (global_ptr.dynsym_index == 0) break :update_sym_type;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 };
37984426
3799 const sym_ptr = @field(elf.symPtr(global_ptr.symtab_index), @tagName(class));4427 elf.targetStore(&sym_ptr.info, .{
3800 switch (elf.targetLoad(&sym_ptr.other).visibility) {4428 .bind = cur_info.bind,
3801 .HIDDEN, .INTERNAL, .PROTECTED => break :update_sym_type,4429 .type = new_type,
3802 .DEFAULT => {},4430 });
3803 }
38044431
3805 const cur_info = elf.targetLoad(&sym_ptr.info);4432 const dynsym_ptr = @field(elf.dynsymPtr(global_ptr.dynsym_index), @tagName(class));
3806 if (cur_info.type == .NOTYPE) {4433 elf.targetStore(&dynsym_ptr.info, .{
3807 elf.targetStore(&sym_ptr.info, .{4434 .bind = elf.targetLoad(&dynsym_ptr.info).bind,
3808 .bind = cur_info.bind,4435 .type = new_type,
3809 .type = sym.info.type,4436 });
3810 });4437
38114438 // If we just turned this into an STT_FUNC symbol, then we have determined
3812 const dynsym_ptr = @field(elf.dynsymPtr(global_ptr.dynsym_index), @tagName(class));4439 // that it needs a PLT entry.
3813 elf.targetStore(&dynsym_ptr.info, .{4440 if (new_type == .FUNC) {
3814 .bind = elf.targetLoad(&dynsym_ptr.info).bind,4441 elf.addPltEntry(name, global_ptr.dynsym_index);
3815 .type = sym.info.type,4442 // ...and therefore, we need to re-apply that symbol's relocations, as
3816 });4443 // some might be targeting its PLT entry.
38174444 global_ptr.symtab_index.applyTargetRelocs(elf);
3818 if (sym.info.type == .FUNC) {
3819 // We've just determined that this symbol actually needs a PLT entry.
3820 elf.addPltEntry(name, global_ptr.dynsym_index);
3821 // TODO: we therefore need to re-apply PLT32 relocs for that symbol!
3822 }
3823 }4445 }
3824 }4446 }
3825 }4447 }
...@@ -3934,6 +4556,29 @@ fn createInitFiniArraySection(...@@ -3934,6 +4556,29 @@ fn createInitFiniArraySection(
3934 ),4556 ),
3935 };4557 };
3936}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),
4578 };
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);
4581}
39374582
3938pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) !void {4583pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) !void {
3939 _ = prog_node;4584 _ = prog_node;
...@@ -4073,17 +4718,15 @@ fn prelinkInner(elf: *Elf) !void {...@@ -4073,17 +4718,15 @@ fn prelinkInner(elf: *Elf) !void {
4073 );4718 );
4074 dynamic_index += 2;4719 dynamic_index += 2;
4075 }4720 }
4076 const rela_dyn_shndx = elf.shndx.got.get(elf).rela_shndx;
4077 const rela_plt_shndx = elf.shndx.got_plt.get(elf).rela_shndx;
4078 dynamic_entries[dynamic_index..][0..12].* = .{4721 dynamic_entries[dynamic_index..][0..12].* = .{
4079 .{ std.elf.DT_RELA, @intCast(rela_dyn_shndx.vaddr(elf)) },4722 .{ std.elf.DT_RELA, @intCast(elf.shndx.rela_dyn.vaddr(elf)) },
4080 .{ std.elf.DT_RELASZ, elf.targetLoad(4723 .{ std.elf.DT_RELASZ, elf.targetLoad(
4081 &@field(elf.shdrPtr(rela_dyn_shndx), @tagName(ct_class)).size,4724 &@field(elf.shdrPtr(elf.shndx.rela_dyn), @tagName(ct_class)).size,
4082 ) },4725 ) },
4083 .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) },4726 .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) },
4084 .{ std.elf.DT_JMPREL, @intCast(rela_plt_shndx.vaddr(elf)) },4727 .{ std.elf.DT_JMPREL, @intCast(elf.shndx.rela_plt.vaddr(elf)) },
4085 .{ std.elf.DT_PLTRELSZ, elf.targetLoad(4728 .{ std.elf.DT_PLTRELSZ, elf.targetLoad(
4086 &@field(elf.shdrPtr(rela_plt_shndx), @tagName(ct_class)).size,4729 &@field(elf.shdrPtr(elf.shndx.rela_plt), @tagName(ct_class)).size,
4087 ) },4730 ) },
4088 .{ std.elf.DT_PLTGOT, @intCast(elf.shndx.got_plt.vaddr(elf)) },4731 .{ std.elf.DT_PLTGOT, @intCast(elf.shndx.got_plt.vaddr(elf)) },
4089 .{ std.elf.DT_PLTREL, std.elf.DT_RELA },4732 .{ std.elf.DT_PLTREL, std.elf.DT_RELA },
...@@ -4100,19 +4743,19 @@ fn prelinkInner(elf: *Elf) !void {...@@ -4100,19 +4743,19 @@ fn prelinkInner(elf: *Elf) !void {
4100 if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry|4743 if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry|
4101 std.mem.byteSwapAllFields(@TypeOf(dynamic_entry.*), dynamic_entry);4744 std.mem.byteSwapAllFields(@TypeOf(dynamic_entry.*), dynamic_entry);
41024745
4103 elf.first_dynamic_reloc = @enumFromInt(elf.relocs.items.len);4746 elf.dynamic_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len);
4104 try elf.ensureUnusedRelocCapacity(dynamic_ni, 5);4747 try elf.ensureUnusedRelocCapacity(dynamic_ni, 5);
4105 elf.addRelocAssumeCapacity(4748 elf.addRelocAssumeCapacity(
4106 dynamic_ni,4749 dynamic_ni,
4107 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 12) + 1),4750 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 12) + 1),
4108 .local(rela_dyn_shndx.get(elf).lsi),4751 .local(elf.shndx.rela_dyn.get(elf).lsi),
4109 0,4752 0,
4110 .absAddr(elf),4753 .absAddr(elf),
4111 );4754 );
4112 elf.addRelocAssumeCapacity(4755 elf.addRelocAssumeCapacity(
4113 dynamic_ni,4756 dynamic_ni,
4114 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 9) + 1),4757 @sizeOf(ElfN.Addr) * (2 * (dynamic_len - 9) + 1),
4115 .local(rela_plt_shndx.get(elf).lsi),4758 .local(elf.shndx.rela_plt.get(elf).lsi),
4116 0,4759 0,
4117 .absAddr(elf),4760 .absAddr(elf),
4118 );4761 );
...@@ -4216,7 +4859,11 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {...@@ -4216,7 +4859,11 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
4216 .type = .SECTION,4859 .type = .SECTION,
4217 .shndx = shndx,4860 .shndx = shndx,
4218 }) else .null;4861 }) else .null;
4219 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 } });
4220 elf.nodes.appendAssumeCapacity(.{ .section = shndx });4867 elf.nodes.appendAssumeCapacity(.{ .section = shndx });
4221 const offset = ni.fileLocation(&elf.mf, false).offset;4868 const offset = ni.fileLocation(&elf.mf, false).offset;
4222 switch (elf.shdrPtr(shndx)) {4869 switch (elf.shdrPtr(shndx)) {
...@@ -4242,13 +4889,14 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {...@@ -4242,13 +4889,14 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
4242fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize) !void {4889fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize) !void {
4243 if (len == 0) return;4890 if (len == 0) return;
4244 const gpa = elf.base.comp.gpa;4891 const gpa = elf.base.comp.gpa;
4245 try elf.relocs.ensureUnusedCapacity(gpa, len);4892 try elf.symbol_relocs.ensureUnusedCapacity(gpa, len);
4893 try elf.got_relocs.ensureUnusedCapacity(gpa, len);
4246 const class = elf.identClass();4894 const class = elf.identClass();
4247 const rela_shndx, const rela_len = rela: switch (elf.ehdrField(.type)) {4895 switch (elf.ehdrField(.type)) {
4248 .NONE, .CORE, _ => unreachable,4896 .NONE, .CORE, _ => unreachable,
4249 .REL => {4897 .REL => {
4250 const shndx = elf.getNodeShndx(node);4898 const shndx = elf.getNodeShndx(node);
4251 if (shndx.get(elf).rela_shndx == .UNDEF) {4899 if (shndx.get(elf).rela.shndx == .UNDEF) {
4252 var bfa_buf: [32]u8 = undefined;4900 var bfa_buf: [32]u8 = undefined;
4253 var bfa: std.heap.BufferFirstAllocator = .init(&bfa_buf, gpa);4901 var bfa: std.heap.BufferFirstAllocator = .init(&bfa_buf, gpa);
4254 const allocator = bfa.allocator();4902 const allocator = bfa.allocator();
...@@ -4275,33 +4923,28 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize)...@@ -4275,33 +4923,28 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize)
4275 .node_align = elf.mf.flags.block_size,4923 .node_align = elf.mf.flags.block_size,
4276 });4924 });
4277 elf.section_by_name.putAssumeCapacityNoClobber(rela_shndx.name(elf), {});4925 elf.section_by_name.putAssumeCapacityNoClobber(rela_shndx.name(elf), {});
4278 shndx.get(elf).rela_shndx = rela_shndx;4926 shndx.get(elf).rela.shndx = rela_shndx;
4279 }4927 }
4280 break :rela .{ shndx.get(elf).rela_shndx, len };4928 try shndx.get(elf).rela.shndx.relaEnsureAdditionalCapacity(elf, len);
4281 },4929 },
4282 .EXEC, .DYN => switch (elf.got.tlsld) {4930 .EXEC, .DYN => {
4283 _ => return,4931 try elf.tls_size_symbol_relocs.ensureUnusedCapacity(gpa, len);
4284 .none => if (elf.shndx.dynamic != .UNDEF) {4932 const new_got_entries = len * 2; // at worst, every reloc is a new TLSGD
4285 try elf.mf.updates.ensureUnusedCapacity(gpa, 1);4933 try elf.got.ensureUnusedCapacity(gpa, new_got_entries);
4286 const got_ni = elf.shndx.got.get(elf).ni;4934 const got_ni = elf.shndx.got.get(elf).ni;
4287 _, const got_node_size = got_ni.location(&elf.mf).resolve(&elf.mf);4935 _, const got_node_size = got_ni.location(&elf.mf).resolve(&elf.mf);
4288 const got_size = switch (class) {4936 const need_got_size = switch (class) {
4289 .NONE, _ => unreachable,4937 .NONE, _ => unreachable,
4290 inline else => |ct_class| (elf.got.len + 2) * @sizeOf(ct_class.ElfN().Addr),4938 inline else => |ct_class| (elf.got.count() + new_got_entries) * @sizeOf(ct_class.ElfN().Addr),
4291 };4939 };
4292 if (got_size > got_node_size)4940 if (need_got_size > got_node_size)
4293 try got_ni.resize(&elf.mf, gpa, got_size +| got_size / MappedFile.growth_factor);4941 try got_ni.resize(&elf.mf, gpa, need_got_size +| need_got_size / MappedFile.growth_factor);
4294 break :rela .{ elf.shndx.got.get(elf).rela_shndx, 1 };4942
4295 } else return,4943 if (elf.shndx.dynamic != .UNDEF) {
4944 try elf.shndx.rela_dyn.relaEnsureAdditionalCapacity(elf, new_got_entries);
4945 }
4296 },4946 },
4297 };4947 }
4298 const rela_ni = rela_shndx.get(elf).ni;
4299 _, const rela_node_size = rela_ni.location(&elf.mf).resolve(&elf.mf);
4300 const rela_size = switch (elf.shdrPtr(rela_shndx)) {
4301 inline else => |shdr| elf.targetLoad(&shdr.size) + elf.targetLoad(&shdr.entsize) * rela_len,
4302 };
4303 if (rela_size > rela_node_size)
4304 try rela_ni.resize(&elf.mf, gpa, rela_size +| rela_size / MappedFile.growth_factor);
4305}4948}
4306fn addRelocAssumeCapacity(4949fn addRelocAssumeCapacity(
4307 elf: *Elf,4950 elf: *Elf,
...@@ -4309,123 +4952,443 @@ fn addRelocAssumeCapacity(...@@ -4309,123 +4952,443 @@ fn addRelocAssumeCapacity(
4309 offset: u64,4952 offset: u64,
4310 target: Symbol.Id,4953 target: Symbol.Id,
4311 addend: i64,4954 addend: i64,
4312 @"type": Reloc.Type,4955 @"type": MachineRelocType,
4313) void {4956) void {
4314 assert(node != .none);4957 assert(node != .none);
4315 const ri: Reloc.Index = @enumFromInt(elf.relocs.items.len);4958 switch (elf.ehdrField(.type)) {
4316 const next: Reloc.Index = next: {4959 .NONE, .CORE, _ => unreachable,
4317 const target_ptr = target.index(elf).ptr(elf);4960 .REL => {
4318 const next = target_ptr.first_target_reloc;4961 const rela_shndx = elf.getNodeShndx(node).get(elf).rela.shndx;
4319 target_ptr.first_target_reloc = ri;4962 const rela_index = rela_shndx.relaAddOneAssumeCapacity(elf, .{
4320 break :next next;4963 .type = @"type",
4964 // This field needs to equal the offset into the section, which is *not* necessarily
4965 // the same thing as our `offset`, which is the offset into `node`. We could compute
4966 // the section offset now, but there's no point, because `flushMovedNodeRelocs` will
4967 // eventually do it for us anyway, so just init to 0.
4968 .offset = 0,
4969 .raw_sym_index = @intFromEnum(target.index(elf)),
4970 .addend = addend,
4971 });
4972 const ri: SymbolReloc.Index = @enumFromInt(elf.symbol_relocs.items.len);
4973 const next: SymbolReloc.Index = next: {
4974 const target_ptr = target.index(elf).ptr(elf);
4975 const next = target_ptr.first_target_reloc;
4976 target_ptr.first_target_reloc = ri;
4977 break :next next;
4978 };
4979 if (next != .none) {
4980 next.get(elf).prev = ri;
4981 }
4982 elf.symbol_relocs.appendAssumeCapacity(.{
4983 .node = node,
4984 .offset = offset,
4985 .type = .write_rela,
4986 .target = target,
4987 .addend = addend,
4988 .next = next,
4989 .prev = .none,
4990 .rela_index = rela_index.toOptional(),
4991 });
4992 },
4993
4994 .DYN, .EXEC => switch (elf.ehdrField(.machine)) {
4995 else => |machine| @panic(@tagName(machine)),
4996 .X86_64 => switch (@"type".X86_64) {
4997 _,
4998 .NONE,
4999 .COPY,
5000 .GLOB_DAT,
5001 .JUMP_SLOT,
5002 .RELATIVE64,
5003 .RELATIVE,
5004 .IRELATIVE,
5005 .@"16",
5006 .PC16,
5007 .@"8",
5008 .PC8,
5009 .DTPMOD64,
5010 .GOTPLT64,
5011 => @panic("TODO: error for illegal or unsupported input relocation"),
5012
5013 // TODO: the psABI links to https://www.fsfla.org/~lxoliva/writeups/TLS/RFC-TLSDESC-x86.txt
5014 .GOTPC32_TLSDESC => @panic("TODO: R_X86_64_GOTPC32_TLSDESC"),
5015 .TLSDESC_CALL => @panic("TODO: R_X86_64_TLSDESC_CALL"),
5016 .TLSDESC => @panic("TODO: R_X86_64_TLSDESC"),
5017
5018 // Relocations targeting a symbol
5019 .@"64" => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs64),
5020 .@"32" => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32),
5021 .@"32S" => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32s),
5022 .PC64 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel64),
5023 .PC32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel32),
5024 .PLT32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .pltrel32),
5025 .SIZE64 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .size64),
5026 .SIZE32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .size32),
5027 .DTPOFF64 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .dtpoff64),
5028 .DTPOFF32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .dtpoff32),
5029 .TPOFF64 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff64),
5030 .TPOFF32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff32),
5031 .GOTPC64 => {
5032 const got_sym: Symbol.Id = .local(elf.shndx.got.get(elf).lsi);
5033 return elf.addSymbolRelocAssumeCapacity(node, offset, got_sym, addend, .rel64);
5034 },
5035 .GOTPC32 => {
5036 const got_sym: Symbol.Id = .local(elf.shndx.got.get(elf).lsi);
5037 return elf.addSymbolRelocAssumeCapacity(node, offset, got_sym, addend, .rel32);
5038 },
5039
5040 // TODO: these are the address of an arbitrary symbol (or PLT entry) relative to the
5041 // base of the GOT, which is quite annoying. Luckily, they seem to be rare, so I'm
5042 // probably just going to introduce a set (ArrayHashMap) of SymbolReloc.Index which
5043 // need to be re-applied whenever the GOT moves.
5044 .GOTOFF64 => @panic("TODO: R_X86_64_GOTOFF64"), // offset of symbol from GOT base
5045 .PLTOFF64 => @panic("TODO: R_X86_64_PLTOFF64"), // offset of PLT entry from GOT base (yes, I know, the name is stupid)
5046
5047 // Relocations targeting a GOT entry
5048 .GOT64 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .offset64),
5049 .GOT32 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .offset32),
5050 .GOTPCREL64 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .rel64),
5051 .GOTPCREL => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .rel32),
5052 // TODO: the next two are relaxable to non-GOT relocations, but I haven't figured
5053 // out how to represent relaxations yet. If we want to remove a `GotReloc` and add a
5054 // `SymbolReloc` at some point, we can't do that in `GotReloc.apply`, because that
5055 // function must be idempotent to ensure reproducible binaries. I think we would
5056 // need to do that as soon as the operation is known to be relaxable (e.g. because
5057 // we found a defininition for a non-preemptible symbol).
5058 .GOTPCRELX => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .rel32),
5059 .REX_GOTPCRELX => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .rel32),
5060
5061 .TLSGD => elf.addGotRelocAssumeCapacity(node, offset, .{ .tlsgd0 = target }, addend, .rel32),
5062 .TLSLD => elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .rel32),
5063 .GOTTPOFF => elf.addGotRelocAssumeCapacity(node, offset, .{ .tpoff = target }, addend, .rel32),
5064 },
5065 },
5066 }
5067}
5068fn addSymbolRelocAssumeCapacity(
5069 elf: *Elf,
5070 node: MappedFile.Node.Index,
5071 offset: u64,
5072 target: Symbol.Id,
5073 addend: i64,
5074 @"type": SymbolReloc.Type,
5075) void {
5076 assert(elf.ehdrField(.type) != .REL);
5077
5078 const rela_index: Section.RelaIndex.Optional = r: {
5079 if (elf.shndx.dynamic == .UNDEF) break :r .none;
5080 const rela_type: MachineRelocType = switch (elf.ehdrField(.machine)) {
5081 else => |machine| @panic(@tagName(machine)),
5082 .X86_64 => .{ .X86_64 = switch (@"type") {
5083 .write_rela => unreachable,
5084 .abs64 => .@"64",
5085 .abs32 => .@"32",
5086 .abs32s => .@"32S",
5087 .rel64 => .PC64,
5088 .rel32 => .PC32,
5089 .pltrel64 => break :r .none,
5090 .pltrel32 => break :r .none,
5091 .dtpoff64 => .DTPOFF64,
5092 .dtpoff32 => .DTPOFF32,
5093 .tpoff64 => .TPOFF64,
5094 .tpoff32 => .TPOFF32,
5095 .size64 => .SIZE64,
5096 .size32 => .SIZE32,
5097 } },
5098 };
5099 const dynsym_index: u32 = switch (target.unwrap()) {
5100 .local => break :r .none,
5101 // TODO: even if the symbol is locally defined, preemption/interposition is a
5102 // possibility, which this condition does not currently consider!
5103 .global => |name| if (elf.globals.strong_def.contains(name) or
5104 elf.globals.weak_def.contains(name))
5105 {
5106 break :r .none;
5107 } else elf.globalByName(name).?.dynsym_index,
5108 };
5109
5110 if (elf.nodeRequiresTextrel(node)) {
5111 elf.textrel_count += 1;
5112 }
5113
5114 // It currently looks like we need a runtime relocation for this.
5115 break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
5116 .type = rela_type,
5117 // This field needs to equal the offset into the section, which is *not* necessarily
5118 // the same thing as our `offset`, which is the offset into `node`. We could compute
5119 // the section offset now, but there's no point, because `flushMovedNodeRelocs` will
5120 // eventually do it for us anyway, so just init to 0.
5121 .offset = 0,
5122 .raw_sym_index = dynsym_index,
5123 .addend = addend,
5124 }).toOptional();
4321 };5125 };
5126
5127 const ri: SymbolReloc.Index = @enumFromInt(elf.symbol_relocs.items.len);
5128 const target_ptr = target.index(elf).ptr(elf);
5129 const next = target_ptr.first_target_reloc;
5130 target_ptr.first_target_reloc = ri;
4322 if (next != .none) {5131 if (next != .none) {
4323 next.get(elf).prev = ri;5132 next.get(elf).prev = ri;
4324 }5133 }
4325 elf.relocs.addOneAssumeCapacity().* = .{5134 elf.symbol_relocs.appendAssumeCapacity(.{
5135 .node = node,
5136 .offset = offset,
5137 .target = target,
5138 .addend = addend,
4326 .type = @"type",5139 .type = @"type",
4327 .prev = .none,
4328 .next = next,5140 .next = next,
5141 .prev = .none,
5142 .rela_index = rela_index,
5143 });
5144 if (@"type".dependsOnTlsSize()) {
5145 elf.tls_size_symbol_relocs.putAssumeCapacityNoClobber(ri, {});
5146 }
5147}
5148fn addGotRelocAssumeCapacity(
5149 elf: *Elf,
5150 node: MappedFile.Node.Index,
5151 offset: u64,
5152 target: GotKey,
5153 addend: i64,
5154 @"type": GotReloc.Type,
5155) void {
5156 assert(elf.ehdrField(.type) != .REL);
5157 switch (elf.getNode(node)) {
5158 .input_section,
5159 .nav,
5160 .lazy_code,
5161 .lazy_const_data,
5162 => {},
5163
5164 .section => unreachable, // cannot contain GOT relocs
5165 .uav => unreachable, // cannot contain GOT relocs
5166
5167 .file => unreachable, // cannot contain relocs
5168 .ehdr => unreachable, // cannot contain relocs
5169 .shdr => unreachable, // cannot contain relocs
5170 .segment => unreachable, // cannot contain relocs
5171 }
5172
5173 const gop = elf.got.getOrPutAssumeCapacity(target);
5174 if (!gop.found_existing) {
5175 gop.value_ptr.* = .none;
5176 const maybe_next_key: ?GotKey = switch (target) {
5177 .reserved => null,
5178 .tpoff => null,
5179 .symbol => null,
5180 .tlsld0 => .tlsld1,
5181 .tlsgd0 => |sym| .{ .tlsgd1 = sym },
5182 .tlsld1 => unreachable,
5183 .tlsgd1 => unreachable,
5184 };
5185 switch (elf.shdrPtr(elf.shndx.got)) {
5186 inline else => |got_shdr, class| {
5187 const Addr = class.ElfN().Addr;
5188 const old_size = elf.targetLoad(&got_shdr.size);
5189 const new_entry_count = @as(u32, 1) + @intFromBool(maybe_next_key != null);
5190 elf.targetStore(&got_shdr.size, @intCast(old_size + @sizeOf(Addr) * new_entry_count));
5191 },
5192 }
5193 if (maybe_next_key) |next_key| {
5194 elf.got.putAssumeCapacityNoClobber(next_key, .none);
5195 elf.updateGotEntry(gop.index);
5196 elf.updateGotEntry(gop.index + 1);
5197 } else {
5198 elf.updateGotEntry(gop.index);
5199 }
5200 }
5201
5202 elf.got_relocs.appendAssumeCapacity(.{
4329 .node = node,5203 .node = node,
5204 .offset = offset,
4330 .target = target,5205 .target = target,
4331 .index = index: switch (elf.ehdrField(.type)) {5206 .addend = addend,
4332 .NONE, .CORE, _ => unreachable,5207 .type = @"type",
4333 .REL => {5208 });
4334 const sh = elf.getNodeShndx(node).get(elf);5209}
4335 switch (elf.shdrPtr(sh.rela_shndx)) {5210fn updateGotEntry(elf: *Elf, got_index: usize) void {
4336 inline else => |shdr, class| {5211 const entry_value: union(enum) {
4337 const Rela = class.ElfN().Rela;5212 unsigned: u64,
4338 const ent_size = elf.targetLoad(&shdr.entsize);5213 signed: i64,
4339 const rela_slice = sh.rela_shndx.get(elf).ni.slice(&elf.mf);5214 reloc: struct {
4340 const index: u32 = if (sh.rela_free.unwrap()) |index| alloc_index: {5215 type: MachineRelocType,
4341 const rela: *Rela = @ptrCast(@alignCast(5216 dynsym_index: u32,
4342 rela_slice[@intCast(ent_size * index)..][0..@intCast(ent_size)],5217 },
4343 ));5218 } = switch (elf.got.keys()[got_index]) {
4344 sh.rela_free = @enumFromInt(rela.offset);5219 .reserved => .{ .unsigned = 0 },
4345 break :alloc_index index;5220 .tpoff => |sym_id| val: {
4346 } else alloc_index: {5221 // We will break from this block if we require a relocation.
4347 const old_size = elf.targetLoad(&shdr.size);5222 known: {
4348 const new_size = old_size + ent_size;5223 if (elf.base.comp.config.output_mode != .Exe) {
4349 elf.targetStore(&shdr.size, @intCast(new_size));5224 // Only the executable's per-module TLS block is at a known offset from the
4350 break :alloc_index @intCast(@divExact(old_size, ent_size));5225 // general TLS pointer.
4351 };5226 break :known;
4352 const rela: *Rela = @ptrCast(@alignCast(5227 }
4353 rela_slice[@intCast(ent_size * index)..][0..@intCast(ent_size)],5228 switch (sym_id.unwrap()) {
4354 ));5229 .local => {},
4355 // The `offset` field here needs to equal the offset into the section, which5230 .global => |name| if (elf.globals.strong_undef.contains(name) or
4356 // is *not* the same as our `offset` which is the offset into `node`. We5231 elf.globals.weak_undef.contains(name))
4357 // could calculate it now, but there's no point since `flushMovedNodeRelocs`5232 {
4358 // will eventually do that for us anyway. So for now, just set offset to 0.5233 // This is an external TLS symbol, so we don't know its offset.
4359 rela.* = .{5234 break :known;
4360 .offset = 0,
4361 .info = .{
4362 .type = @intCast(@"type".unwrap(elf)),
4363 .sym = @intCast(@intFromEnum(target.index(elf))),
4364 },
4365 .addend = @intCast(addend),
4366 };
4367 if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(Rela, rela);
4368 break :index .wrap(index);
4369 },5235 },
4370 }5236 }
4371 },5237 // It's a symbol which we define, the symbol is not interposable because we're the
4372 .EXEC, .DYN => {5238 // executable, and we know our per-module TLS block's offset because we're the
4373 switch (elf.ehdrField(.machine)) {5239 // executable. We therefore know this value!
4374 else => |machine| @panic(@tagName(machine)),5240 const tls_phndx = elf.getNode(elf.ni.tls).segment;
4375 .AARCH64, .PPC64, .RISCV => {},5241 const tls_size: u64 = switch (elf.phdrSlice()) {
4376 .X86_64 => switch (@"type".X86_64) {5242 inline else => |phdr| tls_size: {
4377 else => {},5243 assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS);
4378 .TLSLD => switch (elf.got.tlsld) {5244 break :tls_size elf.targetLoad(&phdr[tls_phndx].memsz);
4379 _ => {},5245 },
4380 .none => if (elf.shndx.dynamic != .UNDEF) {5246 };
4381 const tlsld_index = elf.got.len;5247 const sym_value = sym_id.value(elf);
4382 elf.got.tlsld = .wrap(tlsld_index);5248 break :val .{ .signed = @bitCast(sym_value -% tls_size) };
4383 elf.got.len = tlsld_index + 2;5249 }
4384 const got_addr = got_addr: switch (elf.shdrPtr(elf.shndx.got)) {5250 break :val .{
4385 inline else => |shdr, class| {5251 .reloc = .{
4386 const addr_size = @sizeOf(class.ElfN().Addr);5252 .type = switch (elf.ehdrField(.machine)) {
4387 const old_size = addr_size * tlsld_index;5253 else => |machine| @panic(@tagName(machine)),
4388 const new_size = old_size + addr_size * 2;5254 .X86_64 => .{ .X86_64 = .TPOFF64 },
4389 @memset(5255 },
4390 elf.shndx.got.get(elf).ni.slice(&elf.mf)[old_size..new_size],5256 .dynsym_index = switch (sym_id.unwrap()) {
4391 0,5257 .global => |name| elf.globalByName(name).?.dynsym_index,
4392 );5258 // TODO: I have no idea if compilers are even allowed to emit this, but if they
4393 break :got_addr elf.targetLoad(&shdr.addr) + old_size;5259 // are then I guess we need to add this local symbol to `.dynsym`?
4394 },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,
4395 };5316 };
4396 const rela_dyn_shndx = elf.shndx.got.get(elf).rela_shndx;5317 switch (visibility) {
4397 const rela_dyn_ni = rela_dyn_shndx.get(elf).ni;5318 .DEFAULT => {},
4398 switch (elf.shdrPtr(rela_dyn_shndx)) {5319 .INTERNAL, .HIDDEN, .PROTECTED => {
4399 inline else => |shdr, class| {5320 break :dsi 0; // non-interposable definition
4400 const Rela = class.ElfN().Rela;
4401 const old_size = elf.targetLoad(&shdr.size);
4402 const new_size = old_size + elf.targetLoad(&shdr.entsize);
4403 elf.targetStore(&shdr.size, new_size);
4404 const rela: *Rela = @ptrCast(@alignCast(rela_dyn_ni
4405 .slice(&elf.mf)[@intCast(old_size)..@intCast(new_size)]));
4406 rela.* = .{
4407 .offset = @intCast(got_addr),
4408 .info = .{
4409 .type = @intFromEnum(std.elf.R_X86_64.DTPMOD64),
4410 .sym = 0,
4411 },
4412 .addend = 0,
4413 };
4414 if (elf.targetEndian() != native_endian)
4415 std.mem.byteSwapAllFields(Rela, rela);
4416 },5321 },
4417 }5322 }
4418 rela_dyn_ni.resizedAssumeCapacity(&elf.mf);5323 }
4419 },5324 // `sym` is either undefined or an interposable definition, so use its
5325 // actual dynsym index.
5326 break :dsi elf.globalByName(name).?.dynsym_index;
4420 },5327 },
4421 },5328 },
4422 }5329 },
4423 break :index .none;
4424 },5330 },
4425 },5331 },
4426 .offset = offset,5332 .tlsld0 => switch (elf.shndx.dynamic) {
4427 .addend = addend,5333 .UNDEF => .{ .unsigned = 1 }, // TLS module ID for exexcutable
5334 else => .{ .reloc = .{
5335 .type = .{ .X86_64 = .DTPMOD64 },
5336 .dynsym_index = 0,
5337 } },
5338 },
5339 .tlsld1 => .{ .unsigned = 0 },
5340 };
5341
5342 // First, write to the GOT itself. If we're planning to use a relocation, we'll just write zeroes.
5343 const got_entry_addr: u64 = switch (elf.shdrPtr(elf.shndx.got)) {
5344 inline else => |got_shdr, class| got_entry_addr: {
5345 const addr_size = @sizeOf(class.ElfN().Addr);
5346 const offset = got_index * addr_size;
5347 const entry_ptr: *class.ElfN().Addr = @ptrCast(@alignCast(
5348 elf.shndx.got.get(elf).ni.slice(&elf.mf)[offset..][0..addr_size],
5349 ));
5350 entry_ptr.* = switch (entry_value) {
5351 .unsigned => |x| @intCast(x),
5352 .signed => |x| switch (class) {
5353 .NONE, _ => comptime unreachable,
5354 .@"32" => @bitCast(@as(i32, @intCast(x))),
5355 .@"64" => @bitCast(x),
5356 },
5357 .reloc => 0,
5358 };
5359 break :got_entry_addr elf.targetLoad(&got_shdr.addr) + offset;
5360 },
4428 };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,
5390 };
5391 return shf.ALLOC and !shf.WRITE;
4429}5392}
44305393
4431pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {5394pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {
...@@ -4565,6 +5528,11 @@ pub fn flush(...@@ -4565,6 +5528,11 @@ pub fn flush(
4565 if (any_undef) return error.LinkFailure;5528 if (any_undef) return error.LinkFailure;
4566 }5529 }
45675530
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
4568 while (try elf.idle(tid)) {}5536 while (try elf.idle(tid)) {}
45695537
4570 const entry_addr: u64 = entry: {5538 const entry_addr: u64 = entry: {
...@@ -4595,6 +5563,47 @@ pub fn flush(...@@ -4595,6 +5563,47 @@ pub fn flush(
4595 else => |e| return comp.link_diags.fail("flush write failed: {t}", .{e}),5563 else => |e| return comp.link_diags.fail("flush write failed: {t}", .{e}),
4596 };5564 };
4597}5565}
5566fn updateDynamicTextrel(elf: *Elf) !void {
5567 if (elf.shndx.dynamic == .UNDEF) return;
5568 const dynamic_ni = elf.shndx.dynamic.get(elf).ni;
5569 switch (elf.shdrPtr(elf.shndx.dynamic)) {
5570 inline else => |shdr, class| if (elf.textrel_count > 0) {
5571 const cur_size = elf.targetLoad(&shdr.size);
5572 const cur_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast(
5573 dynamic_ni.slice(&elf.mf)[0..@intCast(cur_size)],
5574 ));
5575 const has_textrel: bool = for (cur_entries) |*entry| {
5576 if (elf.targetLoad(&entry[0]) == std.elf.DT_TEXTREL) {
5577 break true;
5578 }
5579 } else false;
5580 if (!has_textrel) {
5581 // Add a DT_TEXTREL entry before the final DT_NULL entry.
5582 const new_size = cur_size + @sizeOf([2]class.ElfN().Addr);
5583 _, const node_size = dynamic_ni.location(&elf.mf).resolve(&elf.mf);
5584 if (node_size < new_size) {
5585 try dynamic_ni.resize(&elf.mf, elf.base.comp.gpa, new_size);
5586 }
5587 elf.targetStore(&shdr.size, new_size);
5588 const new_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast(
5589 dynamic_ni.slice(&elf.mf)[0..@intCast(new_size)],
5590 ));
5591 const write_entries = new_entries[new_entries.len - 2 ..][0..2];
5592 assert(elf.targetLoad(&write_entries[0][0]) == std.elf.DT_NULL);
5593 write_entries.* = .{
5594 .{ std.elf.DT_TEXTREL, 0 },
5595 .{ std.elf.DT_NULL, 0 },
5596 };
5597 if (elf.targetEndian() != native_endian) {
5598 std.mem.byteSwapAllElements([2]class.ElfN().Addr, write_entries);
5599 }
5600 }
5601 } else {
5602 // TODO: remove the DT_TEXTREL entry if there is one, because it's not necessary any
5603 // more. It won't cause any issues having it there, it's just inefficient.
5604 },
5605 }
5606}
45985607
4599pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool {5608pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool {
4600 const comp = elf.base.comp;5609 const comp = elf.base.comp;
...@@ -4660,6 +5669,9 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool {...@@ -4660,6 +5669,9 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool {
4660 break :task;5669 break :task;
4661 }5670 }
4662 if (elf.changed_symtab_index.pop()) |kv| {5671 if (elf.changed_symtab_index.pop()) |kv| {
5672 // We only need to do work in relocatables, because in ELF modules (non-relocatables)
5673 // our `ElfN.Rela` entries use `.dynsym` indices rather than `.symtab` indices, and
5674 // `.dynsym` indices are (at the time of writing) always immutable.
4663 if (elf.ehdrField(.type) == .REL) {5675 if (elf.ehdrField(.type) == .REL) {
4664 const sub_prog_node = elf.mf.update_prog_node.start(kv.key.slice(elf), 0);5676 const sub_prog_node = elf.mf.update_prog_node.start(kv.key.slice(elf), 0);
4665 defer sub_prog_node.end();5677 defer sub_prog_node.end();
...@@ -4667,7 +5679,11 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool {...@@ -4667,7 +5679,11 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool {
4667 var ri = sym.first_target_reloc;5679 var ri = sym.first_target_reloc;
4668 while (ri != .none) {5680 while (ri != .none) {
4669 const reloc = ri.get(elf);5681 const reloc = ri.get(elf);
4670 reloc.updateTargetIndex(elf);5682 reloc.relaSection(elf).relaUpdateSym(
5683 elf,
5684 reloc.rela_index.unwrap().?,
5685 @intFromEnum(reloc.target.index(elf)),
5686 );
4671 ri = reloc.next;5687 ri = reloc.next;
4672 }5688 }
4673 break :task;5689 break :task;
...@@ -4876,126 +5892,56 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {...@@ -4876,126 +5892,56 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {
4876 .section => |shndx| {5892 .section => |shndx| {
4877 try elf.flushFileOffset(ni);5893 try elf.flushFileOffset(ni);
4878 const addr = elf.computeNodeVAddr(ni);5894 const addr = elf.computeNodeVAddr(ni);
4879 switch (elf.shdrPtr(shndx)) {5895 const old_addr: u64, const flags: std.elf.SHF = switch (elf.shdrPtr(shndx)) {
4880 inline else => |shdr, class| {5896 inline else => |shdr| .{
4881 const flags = elf.targetLoad(&shdr.flags).shf;5897 elf.targetLoad(&shdr.addr),
4882 if (flags.ALLOC) {5898 elf.targetLoad(&shdr.flags).shf,
4883 if (elf.shndx.dynamic != .UNDEF) {5899 },
4884 if (shndx == elf.shndx.got) {5900 };
4885 const old_addr = elf.targetLoad(&shdr.addr);
4886 const rela_dyn_shndx = shndx.get(elf).rela_shndx;
4887 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
4888 rela_dyn_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(
4889 elf.targetLoad(&@field(
4890 elf.shdrPtr(rela_dyn_shndx),
4891 @tagName(class),
4892 ).size),
4893 )],
4894 ));
4895 switch (elf.ehdrField(.machine)) {
4896 else => |machine| @panic(@tagName(machine)),
4897 .AARCH64, .PPC64, .RISCV => {},
4898 .X86_64 => for (relas) |*rela| switch (@as(
4899 std.elf.R_X86_64,
4900 @enumFromInt(elf.targetLoad(&rela.info).type),
4901 )) {
4902 else => |@"type"| @panic(@tagName(@"type")),
4903 .RELATIVE => {},
4904 .GLOB_DAT, .DTPMOD64, .DTPOFF64 => elf.targetStore(
4905 &rela.offset,
4906 @intCast(elf.targetLoad(&rela.offset) - old_addr + addr),
4907 ),
4908 },
4909 }
4910 } else if (shndx == elf.shndx.got_plt) {
4911 const target_endian = elf.targetEndian();
4912 const old_addr = elf.targetLoad(&shdr.addr);
4913 const rela_plt_shndx = shndx.get(elf).rela_shndx;
4914 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
4915 rela_plt_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(
4916 elf.targetLoad(&@field(
4917 elf.shdrPtr(rela_plt_shndx),
4918 @tagName(class),
4919 ).size),
4920 )],
4921 ));
4922 const plt_sec_slice = elf.shndx.plt_sec.get(elf).ni.slice(&elf.mf);
4923 switch (elf.ehdrField(.machine)) {
4924 else => |machine| @panic(@tagName(machine)),
4925 .AARCH64, .PPC64, .RISCV => {},
4926 .X86_64 => {
4927 for (relas) |*rela| switch (@as(
4928 std.elf.R_X86_64,
4929 @enumFromInt(elf.targetLoad(&rela.info).type),
4930 )) {
4931 else => |@"type"| @panic(@tagName(@"type")),
4932 .JUMP_SLOT => elf.targetStore(
4933 &rela.offset,
4934 @intCast(elf.targetLoad(&rela.offset) - old_addr + addr),
4935 ),
4936 };
4937 for (0..elf.got.plt.count()) |plt_index| {
4938 const slice = plt_sec_slice[16 * plt_index + 6 ..][0..4];
4939 std.mem.writeInt(
4940 i32,
4941 slice,
4942 @intCast(@as(i64, @bitCast(@as(u64, @bitCast(@as(
4943 i64,
4944 std.mem.readInt(i32, slice, target_endian),
4945 ))) -% old_addr +% addr))),
4946 target_endian,
4947 );
4948 }
4949 },
4950 }
4951 } else if (shndx == elf.shndx.plt_sec) {
4952 const target_endian = elf.targetEndian();
4953 const old_addr = elf.targetLoad(&shdr.addr);
4954 const plt_sec_slice = ni.slice(&elf.mf);
4955 switch (elf.ehdrField(.machine)) {
4956 else => |machine| @panic(@tagName(machine)),
4957 .AARCH64, .PPC64, .RISCV => {},
4958 .X86_64 => for (0..elf.got.plt.count()) |plt_index| {
4959 const slice = plt_sec_slice[16 * plt_index + 6 ..][0..4];
4960 std.mem.writeInt(
4961 i32,
4962 slice,
4963 @intCast(@as(i64, @bitCast(@as(u64, @bitCast(@as(
4964 i64,
4965 std.mem.readInt(i32, slice, target_endian),
4966 ))) -% addr +% old_addr))),
4967 target_endian,
4968 );
4969 },
4970 }
4971 }
4972 }
49735901
4974 // Update global symbols targeting this section5902 if (flags.ALLOC) {
4975 if (elf.node_global_symbols.get(ni)) |first_name| {5903 switch (elf.shdrPtr(shndx)) {
4976 assert(first_name != .empty);5904 inline else => |shdr| elf.targetStore(&shdr.addr, @intCast(addr)),
4977 const old_addr = elf.targetLoad(&shdr.addr);5905 }
4978 var name = first_name;
4979 while (name != .empty) {
4980 const global = elf.globalByName(name).?;
4981 const old_sym_addr: u64 = switch (elf.symPtr(global.symtab_index)) {
4982 inline else => |sym| elf.targetLoad(&sym.value),
4983 };
4984 global.flushMoved(elf, old_sym_addr - old_addr + addr);
4985 name = global.next_in_node;
4986 }
4987 }
49885906
4989 elf.targetStore(&shdr.addr, @intCast(addr));5907 // Update global symbols targeting this section
4990 shndx.get(elf).lsi.index().flushMoved(elf, addr);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;
4991 }5921 }
5922 }
49925923
4993 if (shndx == elf.shndx.plt) {5924 Symbol.Id.local(shndx.get(elf).lsi).flushMoved(elf, addr);
4994 elf.flushMovedNodeRelocs(ni, elf.targetLoad(&shdr.addr), elf.first_plt_reloc);5925 }
4995 } else if (shndx == elf.shndx.dynamic) {5926
4996 elf.flushMovedNodeRelocs(ni, elf.targetLoad(&shdr.addr), elf.first_dynamic_reloc);5927 if (shndx == elf.shndx.got) {
4997 }5928 const rela_dyn_shndx = elf.shndx.rela_dyn;
4998 },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);
4999 }5945 }
5000 },5946 },
5001 .input_section => |isi| {5947 .input_section => |isi| {
...@@ -5020,7 +5966,10 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {...@@ -5020,7 +5966,10 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {
5020 .DEFAULT => elf.targetLoad(&sym.value),5966 .DEFAULT => elf.targetLoad(&sym.value),
5021 },5967 },
5022 };5968 };
5023 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 );
5024 }5973 }
50255974
5026 // Update global symbols5975 // Update global symbols
...@@ -5032,26 +5981,38 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {...@@ -5032,26 +5981,38 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {
5032 const old_sym_addr: u64 = switch (elf.symPtr(global.symtab_index)) {5981 const old_sym_addr: u64 = switch (elf.symPtr(global.symtab_index)) {
5033 inline else => |sym| elf.targetLoad(&sym.value),5982 inline else => |sym| elf.targetLoad(&sym.value),
5034 };5983 };
5035 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 );
5036 name = global.next_in_node;5988 name = global.next_in_node;
5037 }5989 }
5038 }5990 }
50395991
5040 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 );
5041 },5998 },
5042 inline .nav, .uav, .lazy_code, .lazy_const_data => |mi| {5999 inline .nav, .uav, .lazy_code, .lazy_const_data => |mi| {
5043 const new_addr = elf.computeNodeVAddr(ni);6000 const new_addr = elf.computeNodeVAddr(ni);
5044 mi.symbol(elf).index().flushMoved(elf, new_addr);6001 Symbol.Id.local(mi.symbol(elf)).flushMoved(elf, new_addr);
5045 if (elf.node_global_symbols.get(ni)) |first_name| {6002 if (elf.node_global_symbols.get(ni)) |first_name| {
5046 assert(first_name != .empty);6003 assert(first_name != .empty);
5047 var name = first_name;6004 var name = first_name;
5048 while (name != .empty) {6005 while (name != .empty) {
5049 const global = elf.globalByName(name).?;6006 Symbol.Id.global(name).flushMoved(elf, new_addr);
5050 global.flushMoved(elf, new_addr);6007 name = elf.globalByName(name).?.next_in_node;
5051 name = global.next_in_node;
5052 }6008 }
5053 }6009 }
5054 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 );
5055 },6016 },
5056 }6017 }
5057 try ni.childrenMoved(elf.base.comp.gpa, &elf.mf);6018 try ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
...@@ -5079,6 +6040,27 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void {...@@ -5079,6 +6040,27 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void {
5079 },6040 },
5080 .TLS => {6041 .TLS => {
5081 elf.targetStore(&ph.memsz, @intCast(size));6042 elf.targetStore(&ph.memsz, @intCast(size));
6043 // TPOFF relocations care about the size of the TLS segment. Re-apply
6044 // those, and also update any GOT entries from GOTTPOFF relocations.
6045 for (elf.tls_size_symbol_relocs.keys()) |reloc| {
6046 reloc.get(elf).apply(elf);
6047 }
6048 for (elf.got.keys(), 0..) |got_key, got_index| {
6049 switch (got_key) {
6050 .reserved,
6051 .symbol,
6052 .tlsld0,
6053 .tlsld1,
6054 .tlsgd0,
6055 .tlsgd1,
6056 => {
6057 @branchHint(.likely);
6058 continue;
6059 },
6060
6061 .tpoff => elf.updateGotEntry(got_index),
6062 }
6063 }
5082 return ni.childrenMoved(elf.base.comp.gpa, &elf.mf);6064 return ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
5083 },6065 },
5084 }6066 }
...@@ -5113,110 +6095,28 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void {...@@ -5113,110 +6095,28 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void {
5113 },6095 },
5114 },6096 },
5115 .section => |shndx| switch (elf.shdrPtr(shndx)) {6097 .section => |shndx| switch (elf.shdrPtr(shndx)) {
5116 inline else => |shdr, class| {6098 inline else => |shdr| {
5117 switch (elf.targetLoad(&shdr.type)) {6099 switch (elf.targetLoad(&shdr.type)) {
5118 else => unreachable,6100 else => unreachable,
6101
5119 .NULL => if (size > 0) elf.targetStore(&shdr.type, .PROGBITS),6102 .NULL => if (size > 0) elf.targetStore(&shdr.type, .PROGBITS),
5120 .PROGBITS => if (size == 0) elf.targetStore(&shdr.type, .NULL),6103 .PROGBITS => if (size == 0) elf.targetStore(&shdr.type, .NULL),
5121 .SYMTAB, .DYNAMIC, .REL, .DYNSYM => return,6104
5122 .INIT_ARRAY => {6105 .INIT_ARRAY,
5123 assert(shndx == elf.shndx.init_array);6106 .FINI_ARRAY,
5124 if (elf.shndx.dynamic != .UNDEF) {6107 .PREINIT_ARRAY,
5125 const dynamic_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast(6108 .STRTAB,
5126 elf.shndx.dynamic.get(elf).ni.slice(&elf.mf),6109 .SYMTAB,
5127 ));6110 .DYNAMIC,
5128 for (dynamic_entries) |*dynamic_entry|6111 .REL,
5129 switch (elf.targetLoad(&dynamic_entry[0])) {6112 .RELA,
5130 else => {},6113 .DYNSYM,
5131 std.elf.DT_INIT_ARRAYSZ => dynamic_entry[1] = shdr.size,6114 => return,
5132 };
5133 }
5134 const end_sym_index = elf.globalByName(elf.string(.strtab, "__init_array_end") catch unreachable).?.symtab_index;
5135 const end_sym_ptr = @field(elf.symPtr(end_sym_index), @tagName(class));
5136 const end_vaddr = shndx.vaddr(elf) + elf.targetLoad(&shdr.size);
5137 elf.targetStore(&end_sym_ptr.value, @intCast(end_vaddr));
5138 end_sym_index.flushMoved(elf, end_vaddr);
5139 return;
5140 },
5141 .FINI_ARRAY => {
5142 assert(shndx == elf.shndx.fini_array);
5143 if (elf.shndx.dynamic != .UNDEF) {
5144 const dynamic_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast(
5145 elf.shndx.dynamic.get(elf).ni.slice(&elf.mf),
5146 ));
5147 for (dynamic_entries) |*dynamic_entry|
5148 switch (elf.targetLoad(&dynamic_entry[0])) {
5149 else => {},
5150 std.elf.DT_FINI_ARRAYSZ => dynamic_entry[1] = shdr.size,
5151 };
5152 }
5153 const end_sym_index = elf.globalByName(elf.string(.strtab, "__fini_array_end") catch unreachable).?.symtab_index;
5154 const end_sym_ptr = @field(elf.symPtr(end_sym_index), @tagName(class));
5155 const end_vaddr = shndx.vaddr(elf) + elf.targetLoad(&shdr.size);
5156 elf.targetStore(&end_sym_ptr.value, @intCast(end_vaddr));
5157 end_sym_index.flushMoved(elf, end_vaddr);
5158 return;
5159 },
5160 .PREINIT_ARRAY => {
5161 assert(shndx == elf.shndx.preinit_array);
5162 if (elf.shndx.dynamic != .UNDEF) {
5163 const dynamic_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast(
5164 elf.shndx.dynamic.get(elf).ni.slice(&elf.mf),
5165 ));
5166 for (dynamic_entries) |*dynamic_entry|
5167 switch (elf.targetLoad(&dynamic_entry[0])) {
5168 else => {},
5169 std.elf.DT_PREINIT_ARRAYSZ => dynamic_entry[1] = shdr.size,
5170 };
5171 }
5172 const end_sym_index = elf.globalByName(elf.string(.strtab, "__preinit_array_end") catch unreachable).?.symtab_index;
5173 const end_sym_ptr = @field(elf.symPtr(end_sym_index), @tagName(class));
5174 const end_vaddr = shndx.vaddr(elf) + elf.targetLoad(&shdr.size);
5175 elf.targetStore(&end_sym_ptr.value, @intCast(end_vaddr));
5176 end_sym_index.flushMoved(elf, end_vaddr);
5177 return;
5178 },
5179 .STRTAB => {
5180 if (elf.shndx.dynamic != .UNDEF) {
5181 if (shndx == elf.shndx.dynstr) {
5182 const dynamic_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast(
5183 elf.shndx.dynamic.get(elf).ni.slice(&elf.mf),
5184 ));
5185 for (dynamic_entries) |*dynamic_entry|
5186 switch (elf.targetLoad(&dynamic_entry[0])) {
5187 else => {},
5188 std.elf.DT_STRSZ => dynamic_entry[1] = shdr.size,
5189 };
5190 }
5191 }
5192 return;
5193 },
5194 .RELA => {
5195 if (elf.shndx.dynamic != .UNDEF) {
5196 if (shndx == elf.shndx.got.get(elf).rela_shndx) {
5197 const dynamic_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast(
5198 elf.shndx.dynamic.get(elf).ni.slice(&elf.mf),
5199 ));
5200 for (dynamic_entries) |*dynamic_entry|
5201 switch (elf.targetLoad(&dynamic_entry[0])) {
5202 else => {},
5203 std.elf.DT_RELASZ => dynamic_entry[1] = shdr.size,
5204 };
5205 } else if (shndx == elf.shndx.got_plt.get(elf).rela_shndx) {
5206 const dynamic_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast(
5207 elf.shndx.dynamic.get(elf).ni.slice(&elf.mf),
5208 ));
5209 for (dynamic_entries) |*dynamic_entry|
5210 switch (elf.targetLoad(&dynamic_entry[0])) {
5211 else => {},
5212 std.elf.DT_PLTRELSZ => dynamic_entry[1] = shdr.size,
5213 };
5214 }
5215 }
5216 return;
5217 },
5218 }6115 }
5219 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 {
5220 elf.targetStore(&shdr.size, @intCast(size));6120 elf.targetStore(&shdr.size, @intCast(size));
5221 }6121 }
5222 },6122 },
...@@ -5224,6 +6124,85 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void {...@@ -5224,6 +6124,85 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void {
5224 .input_section, .nav, .uav, .lazy_code, .lazy_const_data => {},6124 .input_section, .nav, .uav, .lazy_code, .lazy_const_data => {},
5225 }6125 }
5226}6126}
6127fn updateDynamicEntry(elf: *Elf, key: u32, new_val: u64) void {
6128 switch (elf.shdrPtr(elf.shndx.dynamic)) {
6129 inline else => |shdr, class| {
6130 const dynamic_size = elf.targetLoad(&shdr.size);
6131 const dynamic_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast(
6132 elf.shndx.dynamic.get(elf).ni.slice(&elf.mf)[0..@intCast(dynamic_size)],
6133 ));
6134 for (dynamic_entries) |*dynamic_entry| {
6135 if (elf.targetLoad(&dynamic_entry[0]) == key) {
6136 elf.targetStore(&dynamic_entry[1], @intCast(new_val));
6137 }
6138 }
6139 },
6140 }
6141}
6142fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_addr: u64, addr: u64) void {
6143 const target_endian = elf.targetEndian();
6144 switch (elf.ehdrField(.machine)) {
6145 else => |machine| @panic(@tagName(machine)),
6146 .X86_64 => {
6147 switch (which) {
6148 .plt => return,
6149 .plt_sec => {
6150 // Re-apply all PLT relocations. If a symbol is in the PLT then the majority of
6151 // its relocations are probably going through the PLT, so we don't bother with
6152 // specific tracking for PLT relocations---instead just re-apply all relocations
6153 // targeting symbols with PLT entries.
6154 for (elf.plt.keys()) |sym| {
6155 sym.index(elf).applyTargetRelocs(elf);
6156 }
6157 // We also need to update all of the references from `.plt.sec` to `.got.plt`.
6158 // However, if there's also a flush pending for `.got.plt`, don't bother doing
6159 // this now, because we'll do it when `.got.plt` is flushed anyway.
6160 if (elf.shndx.got_plt.get(elf).ni.hasMoved(&elf.mf)) {
6161 return;
6162 }
6163 // Exit this `switch` to update those references.
6164 },
6165 .got_plt => {
6166 // Update the offsets of the relocation entries in `.rela.plt`.
6167 const rela_plt_shndx = elf.shndx.rela_plt;
6168 for (0..elf.plt.count()) |plt_index| {
6169 if (elf.pltEntryIsDead(plt_index)) continue;
6170 rela_plt_shndx.relaAdjustOffset(elf, @enumFromInt(plt_index), old_addr, addr);
6171 }
6172 // We also need to update all of the references from `.plt.sec` to `.got.plt`.
6173 // However, if there's also a flush pending for `.plt.sec`, don't bother doing
6174 // this now, because we'll do it when `.plt.sec` is flushed anyway.
6175 if (elf.shndx.plt_sec.get(elf).ni.hasMoved(&elf.mf)) {
6176 return;
6177 }
6178 // Exit this `switch` to update those references.
6179 },
6180 }
6181 // We are updating the references from `.plt.sec` to `.got.plt`.
6182 const got_plt_addr = elf.shndx.got_plt.vaddr(elf);
6183 const plt_sec_addr = elf.shndx.plt_sec.vaddr(elf);
6184 const plt_sec_slice = elf.shndx.plt_sec.get(elf).ni.slice(&elf.mf);
6185 switch (elf.identClass()) {
6186 .NONE, _ => unreachable,
6187 inline else => |class| {
6188 const Addr = class.ElfN().Addr;
6189 for (0..elf.plt.count()) |plt_index| {
6190 const plt_sec_offset = 16 * plt_index;
6191 const got_plt_offset = @sizeOf(Addr) * (3 + plt_index);
6192 std.mem.writeInt(
6193 i32,
6194 plt_sec_slice[plt_sec_offset + 6 ..][0..4],
6195 @intCast(@as(i64, @bitCast(
6196 (got_plt_addr + got_plt_offset) -% (plt_sec_addr + plt_sec_offset + 10),
6197 ))),
6198 target_endian,
6199 );
6200 }
6201 },
6202 }
6203 },
6204 }
6205}
52276206
5228pub fn updateExports(6207pub fn updateExports(
5229 elf: *Elf,6208 elf: *Elf,