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 {
3434 dynstr: Section.Index,
3535 dynamic: Section.Index,
3636 tdata: Section.Index,
37 rela_dyn: Section.Index,
38 rela_plt: Section.Index,
3739 // These sections are created only as needed, and are initially `.UNDEF`.
3840 init_array: Section.Index,
3941 fini_array: Section.Index,
......@@ -65,13 +67,23 @@ dso_globals: std.array_hash_map.Auto(String(.strtab), std.elf.STT),
6567shstrtab: StringTable,
6668strtab: StringTable,
6769dynstr: StringTable,
68got: struct {
69 len: u32,
70 tlsld: GotIndex,
71 plt: std.AutoArrayHashMapUnmanaged(Symbol.Id, void),
72},
73first_plt_reloc: Reloc.Index,
74first_dynamic_reloc: Reloc.Index,
70
71/// Indices map 1--1 to indices into the actual `.got` section.
72///
73/// Value is the output relocation in `.rela.dyn` for the GOT entry.
74got: std.array_hash_map.Auto(GotKey, Section.RelaIndex.Optional),
75/// Indices map 1--1 to indices into the actual `.got.plt` section. These also equal indices into
76/// the relocations in `.rela.plt`, because every PLT entry has one output relocation (if a runtime
77/// relocation is no longer necessary, then neither is the corresponding PLT entry!).
78///
79/// PLT entries in this map may be "dead", meaning the PLT entry has been deemed unnecessary so is
80/// available for reuse---see `Elf.pltEntryIsDead`. Such entries must not be targeted by relocs.
81plt: std.array_hash_map.Auto(Symbol.Id, void),
82/// The `.plt` section contains zero or more symbol relocations starting at this index.
83plt_first_symbol_reloc: SymbolReloc.Index,
84/// The `.dynamic` section contains zero or more symbol relocations starting at this index.
85dynamic_first_symbol_reloc: SymbolReloc.Index,
86
7587needed: std.AutoArrayHashMapUnmanaged(String(.dynstr), void),
7688inputs: std.ArrayList(struct {
7789 path: std.Build.Cache.Path,
......@@ -81,31 +93,42 @@ inputs: std.ArrayList(struct {
8193input_sections: std.ArrayList(InputSection),
8294input_section_pending_index: u32,
8395navs: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, struct {
84 /// The start index of the contiguous sequence of relocations in this NAV.
85 first_reloc: Reloc.Index,
8696 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,
87101}),
88102uavs: std.AutoArrayHashMapUnmanaged(InternPool.Index, struct {
89 /// The start index of the contiguous sequence of relocations in this UAV.
90 first_reloc: Reloc.Index,
91103 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.
92107}),
93108lazy: std.EnumArray(link.File.LazySymbol.Kind, struct {
94109 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,
97110 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,
98115 }),
99116 pending_index: u32,
100117}),
101118pending_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),
103123/// Index matches the index into `shdrs`.
104124section_by_name: std.array_hash_map.Auto(String(.shstrtab), void),
105
106125/// Key is the name of a global symbol which has been moved to a new symtab index. Any relocation
107126/// entries which target that symbol must be updated to reference the correct symbol index.
108127changed_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
110133const_prog_node: std.Progress.Node,
111134synth_prog_node: std.Progress.Node,
......@@ -120,21 +143,21 @@ const Node = union(enum) {
120143 shdr,
121144 /// Cannot contain relocations.
122145 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`.
124147 ///
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`.
126149 ///
127150 /// Otherwise, cannot contain relocations.
128151 section: Section.Index,
129 /// May contain relocations through the `first_reloc` field in `elf.input_sections`.
152 /// May contain relocations.
130153 input_section: InputSection.Index,
131 /// May contain relocations through the `first_reloc` field in `elf.navs`.
154 /// May contain relocations.
132155 nav: NavMapIndex,
133 /// May contain relocations through the `first_reloc` field in `elf.uavs`.
156 /// May contain relocations.
134157 uav: UavMapIndex,
135 /// May contain relocations through the `first_reloc` field in `elf.lazy.map`.
158 /// May contain relocations.
136159 lazy_code: LazyMapRef.Index(.code),
137 /// May contain relocations through the `first_reloc` field in `elf.lazy.map`.
160 /// May contain relocations.
138161 lazy_const_data: LazyMapRef.Index(.const_data),
139162
140163 pub const InputIndex = enum(u32) {
......@@ -176,8 +199,11 @@ const Node = union(enum) {
176199 return elf.navs.values()[@intFromEnum(nmi)].lsi;
177200 }
178201
179 fn firstReloc(nmi: NavMapIndex, elf: *const Elf) Reloc.Index {
180 return elf.navs.values()[@intFromEnum(nmi)].first_reloc;
202 fn firstSymbolReloc(nmi: NavMapIndex, elf: *const Elf) SymbolReloc.Index {
203 return elf.navs.values()[@intFromEnum(nmi)].first_symbol_reloc;
204 }
205 fn firstGotReloc(nmi: NavMapIndex, elf: *const Elf) GotReloc.Index {
206 return elf.navs.values()[@intFromEnum(nmi)].first_got_reloc;
181207 }
182208 };
183209
......@@ -192,8 +218,13 @@ const Node = union(enum) {
192218 return elf.uavs.values()[@intFromEnum(umi)].lsi;
193219 }
194220
195 fn firstReloc(umi: UavMapIndex, elf: *const Elf) Reloc.Index {
196 return elf.uavs.values()[@intFromEnum(umi)].first_reloc;
221 fn firstSymbolReloc(umi: UavMapIndex, elf: *const Elf) SymbolReloc.Index {
222 return elf.uavs.values()[@intFromEnum(umi)].first_symbol_reloc;
223 }
224 fn firstGotReloc(umi: UavMapIndex, elf: *const Elf) GotReloc.Index {
225 _ = umi;
226 _ = elf;
227 return .none;
197228 }
198229 };
199230
......@@ -217,8 +248,11 @@ const Node = union(enum) {
217248 return lmi.ref().symbol(elf);
218249 }
219250
220 fn firstReloc(lmi: @This(), elf: *const Elf) Reloc.Index {
221 return lmi.ref().firstReloc(elf);
251 fn firstSymbolReloc(lmi: @This(), elf: *const Elf) SymbolReloc.Index {
252 return elf.lazy.getPtrConst(kind).map.values()[@intFromEnum(lmi)].first_symbol_reloc;
253 }
254 fn firstGotReloc(lmi: @This(), elf: *const Elf) GotReloc.Index {
255 return elf.lazy.getPtrConst(kind).map.values()[@intFromEnum(lmi)].first_got_reloc;
222256 }
223257 };
224258 }
......@@ -230,10 +264,6 @@ const Node = union(enum) {
230264 pub fn symbol(lmr: LazyMapRef, elf: *const Elf) Symbol.LocalIndex {
231265 return elf.lazy.getPtrConst(lmr.kind).map.values()[lmr.index].lsi;
232266 }
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 }
237267 };
238268
239269 pub const Known = struct {
......@@ -269,8 +299,10 @@ const InputSection = struct {
269299 vaddr: u64,
270300 /// The node corresponding to this input section.
271301 node: MappedFile.Node.Index,
272 /// The start index of the contiguous sequence of relocations in this input section.
273 first_reloc: Reloc.Index,
302 /// The start index of the contiguous sequence of symbol relocations in this input section.
303 first_symbol_reloc: SymbolReloc.Index,
304 /// The start index of the contiguous sequence of GOT relocations in this input section.
305 first_got_reloc: GotReloc.Index,
274306
275307 const Index = enum(u32) {
276308 _,
......@@ -304,21 +336,53 @@ const Section = struct {
304336 ///
305337 /// If the section does not have flag `std.elf.SHF.ALLOC`, this is `.null`.
306338 lsi: Symbol.LocalIndex,
307 rela_shndx: Section.Index,
308 rela_free: RelIndex,
339 rela: union {
340 /// This field is active if and only if this section is *not* a `SHT_RELA` section.
341 ///
342 /// This field's value refers to this section's corresponding relocation section, if it
343 /// currently has one. If this section does not currently have a relocation section, the
344 /// value is `.UNDEF`.
345 ///
346 /// This field is only ever non-`.UNDEF` when emitting a relocatable (`ET_REL`). While there
347 /// are also output relocations in DSOs, they are all placed in the `.rela.dyn`
348 /// (`elf.shdnx.rela_dyn`) and `.rela.plt` (`elf.shndx.rela_plt`) sections, rather than
349 /// having separate relocation sections for each section.
350 shndx: Section.Index,
351
352 /// This field is active if and only if this section *is* a `SHT_RELA` section.
353 ///
354 /// This is the head of a single-linked list of free `ElfN.Rela` entries in this section.
355 /// Entries in this list have `info.type` set to `R_*_NONE`, have `info.sym` set to 0, and
356 /// have `offset` set to `@enumFromInt(next)` where `next` is `RelaIndex.Optional`. Also,
357 /// `addend` is set to the length of the list starting from this point; so the last node in
358 /// the list has `addend = 1`, the one before it has `addend = 2`, etc. This is so that the
359 /// head node always contains the current length of the list.
360 ///
361 /// It would be okay to store these values (in the `offset` and `addend` fields) in the
362 /// compiler's host endianness, because they will never be read by other tooling. However,
363 /// we nonetheless use target endianness, because using host endianness would introduce an
364 /// unnecessary dependency of the output binary on the compiler's host architecture.
365 free_head: RelaIndex.Optional,
366 },
309367
310 pub const RelIndex = enum(u32) {
368 const RelaIndex = enum(u32) {
311369 none,
312370 _,
313371
314 pub fn wrap(i: ?u32) RelIndex {
315 return @enumFromInt((i orelse return .none) + 1);
316 }
317 pub fn unwrap(ri: RelIndex) ?u32 {
318 return switch (ri) {
319 .none => null,
320 _ => @intFromEnum(ri) - 1,
321 };
372 const Optional = enum(u32) {
373 none = std.math.maxInt(u32),
374 _,
375
376 fn unwrap(opt: RelaIndex.Optional) ?RelaIndex {
377 return switch (opt) {
378 .none => null,
379 _ => @enumFromInt(@intFromEnum(opt)),
380 };
381 }
382 };
383
384 fn toOptional(i: RelaIndex) RelaIndex.Optional {
385 return @enumFromInt(@intFromEnum(i));
322386 }
323387 };
324388
......@@ -390,9 +454,690 @@ const Section = struct {
390454 inline else => |shdr| elf.targetStore(&shdr.name, @intFromEnum(shstrtab_entry)),
391455 }
392456 }
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 }
393661 };
394662};
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
3961141fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe_global }) !void {
3971142 const gpa = elf.base.comp.gpa;
3981143
......@@ -447,8 +1192,10 @@ fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe
4471192fn ensureUnusedPltCapacity(elf: *Elf, len: u32) !void {
4481193 const gpa = elf.base.comp.gpa;
4491194
450 try elf.got.plt.ensureUnusedCapacity(gpa, len);
451 const need_plt_capacity = elf.got.plt.count() + len;
1195 try elf.shndx.rela_plt.relaEnsureAdditionalCapacity(elf, len);
1196
1197 try elf.plt.ensureUnusedCapacity(gpa, len);
1198 const need_plt_capacity = elf.plt.count() + len;
4521199
4531200 switch (elf.ehdrField(.machine)) {
4541201 else => |machine| @panic(@tagName(machine)),
......@@ -479,21 +1226,25 @@ fn ensureUnusedPltCapacity(elf: *Elf, len: u32) !void {
4791226 const new_size = plt_sec_need_size +| plt_sec_need_size / MappedFile.growth_factor;
4801227 try elf.shndx.plt_sec.get(elf).ni.resize(&elf.mf, gpa, new_size);
4811228 }
482
483 // Ensure the `.rela.plt` section's node is big enough
484 const rela_plt_shndx = elf.shndx.got_plt.get(elf).rela_shndx;
485 const rela_plt_need_size: usize = switch (elf.shdrPtr(rela_plt_shndx)) {
486 inline else => |shdr| @intCast(elf.targetLoad(&shdr.entsize) * need_plt_capacity),
487 };
488 _, const rela_plt_cur_size = rela_plt_shndx.get(elf).ni.location(&elf.mf).resolve(&elf.mf);
489 if (rela_plt_cur_size < rela_plt_need_size) {
490 const new_size = rela_plt_need_size +| rela_plt_need_size / MappedFile.growth_factor;
491 try rela_plt_shndx.get(elf).ni.resize(&elf.mf, gpa, new_size);
492 } else {
493 // Still mark `.rela.plt` as resized so that the DT_PLTRELSZ entry can
494 // be updated if we do indeed add a PLT entry.
495 try rela_plt_shndx.get(elf).ni.resized(gpa, &elf.mf);
496 }
1229 },
1230 }
1231}
1232/// Given an index into the PLT, returns whether that PLT entry is dead, meaning it may be reused at
1233/// any time and must not be targeted by relocations. See also the doc comment on `Elf.plt`.
1234fn pltEntryIsDead(elf: *Elf, plt_index: usize) bool {
1235 assert(elf.shndx.plt != .UNDEF);
1236 assert(plt_index <= elf.plt.count());
1237 // We track which PLT entries are alive based on the relocation entries, since there is a 1-1
1238 // mapping between PLT entries and `.rela.plt` entries and the relocation entries already have
1239 // a free-list mechanism.
1240 switch (elf.shdrPtr(elf.shndx.rela_plt)) {
1241 inline else => |rela_shdr, class| {
1242 const size = elf.targetLoad(&rela_shdr.size);
1243 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
1244 elf.shndx.rela_plt.get(elf).ni.slice(&elf.mf)[0..@intCast(size)],
1245 ));
1246 const rel_type = elf.targetLoad(&relas[plt_index].info).type;
1247 return rel_type == MachineRelocType.none(elf).unwrap(elf);
4971248 },
4981249 }
4991250}
......@@ -801,12 +1552,13 @@ fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{
8011552 if (new_global_ptr.dynsym_index != 0 and
8021553 opts.visibility == .DEFAULT and
8031554 opts.shndx == .UNDEF and
804 @"type" == .FUNC)
1555 (@"type" == .FUNC or @"type" == std.elf.STT.GNU_IFUNC))
8051556 {
8061557 // 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 remove
808 // the PLT entry if we later discover a link inpu which resolves this reference.
1558 // We therefore might need a PLT entry, so let's add one now.
8091559 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`.
8101562 }
8111563
8121564 return .global(opts.name.strtab);
......@@ -823,6 +1575,7 @@ fn setGlobalSymbolValue(
8231575 shndx: Section.Index,
8241576 },
8251577) void {
1578 assert(new.shndx != .UNDEF);
8261579 const old_node = global_ptr.symtab_index.ptr(elf).node;
8271580 if (old_node != .none) {
8281581 if (global_ptr.next_in_node != .empty) {
......@@ -897,7 +1650,40 @@ fn setGlobalSymbolValue(
8971650 },
8981651 };
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);
9011687}
9021688/// When the same global symbol appears in two inputs---even if one symbol is defined and the other
9031689/// 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 {
10321818 if (elf.targetEndian() != native_endian) {
10331819 std.mem.byteSwapAllFields(class.ElfN().Sym, dynsym);
10341820 }
1821 global_ptr.dynsym_index = 0;
10351822 }
10361823 },
10371824 }
10381825}
10391826fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void {
10401827 const target_endian = elf.targetEndian();
1041 const plt_index: u32 = @intCast(elf.got.plt.count());
1042 elf.got.plt.putAssumeCapacityNoClobber(.global(global_name), {});
1828
1829 // We use the existing free-list tracking of the `.rela.plt` section to also behave as a
1830 // free-list for the PLT itself---see `pltEntryIsDead` for details.
1831 const plt_index: u32 = @intFromEnum(elf.shndx.rela_plt.relaAddOneAssumeCapacity(elf, .{
1832 .type = .jumpSlot(elf),
1833 .offset = 0, // populated later
1834 .raw_sym_index = dynsym_index,
1835 .addend = 0,
1836 }));
1837
1838 // Now that we know the index, we can set the relocation's offset.
1839 const got_plt_addr = switch (elf.shdrPtr(elf.shndx.got_plt)) {
1840 inline else => |shdr, class| got_plt_addr: {
1841 const ent_size = @sizeOf(class.ElfN().Addr);
1842 assert(elf.targetLoad(&shdr.entsize) == ent_size);
1843 const offset = ent_size * @as(u64, 3 + plt_index);
1844 assert(offset <= elf.targetLoad(&shdr.size));
1845 break :got_plt_addr elf.targetLoad(&shdr.addr) + offset;
1846 },
1847 };
1848 elf.shndx.rela_plt.relaSetOffset(elf, @enumFromInt(plt_index), got_plt_addr);
1849
1850 if (plt_index < elf.plt.count()) {
1851 // We reused a free entry, so we're already done!
1852 elf.plt.setKey(plt_index, .global(global_name));
1853 return;
1854 }
1855
1856 // We added a new entry, so we now need to extend the PLT sections.
1857 assert(plt_index == elf.plt.count());
1858 elf.plt.putAssumeCapacityNoClobber(.global(global_name), {});
1859
10431860 switch (elf.ehdrField(.machine)) {
10441861 else => |machine| @panic(@tagName(machine)),
10451862 .X86_64 => {
......@@ -1067,12 +1884,12 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void
10671884 },
10681885 };
10691886
1070 const got_plt_shndx = elf.shndx.got_plt;
10711887 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)) {
10731889 inline else => |shdr, class| {
10741890 const ent_size = @sizeOf(class.ElfN().Addr);
10751891 const old_size = ent_size * (3 + plt_index);
1892 assert(elf.targetLoad(&shdr.size) == old_size);
10761893 elf.targetStore(&shdr.size, old_size + ent_size);
10771894 std.mem.writeInt(
10781895 class.ElfN().Addr,
......@@ -1080,9 +1897,8 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void
10801897 @intCast(plt_addr),
10811898 target_endian,
10821899 );
1083 break :got_plt_addr elf.targetLoad(&shdr.addr) + old_size;
10841900 },
1085 };
1901 }
10861902
10871903 const plt_sec_ni = elf.shndx.plt_sec.get(elf).ni;
10881904 switch (elf.shdrPtr(elf.shndx.plt_sec)) {
......@@ -1105,30 +1921,6 @@ fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void
11051921 );
11061922 },
11071923 }
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 }
11321924 },
11331925 }
11341926}
......@@ -1142,7 +1934,7 @@ const Symbol = struct {
11421934 node: MappedFile.Node.Index,
11431935
11441936 /// The head of a linked list of relocations targeting this symbol.
1145 first_target_reloc: Reloc.Index,
1937 first_target_reloc: SymbolReloc.Index,
11461938
11471939 const Global = struct {
11481940 /// The current index of the symtab entry for this global symbol.
......@@ -1159,16 +1951,6 @@ const Symbol = struct {
11591951 ///
11601952 /// If `node` is `.none`, this is `.empty`.
11611953 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 }
11721954 };
11731955
11741956 /// An index directly into the symtab. These values are not stable (global symbols are sometimes
......@@ -1181,23 +1963,20 @@ const Symbol = struct {
11811963 null = 0,
11821964 _,
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 }
11981966 fn ptr(si: Symbol.Index, elf: *Elf) *Symbol {
11991967 return &elf.symtab.items[@intFromEnum(si)];
12001968 }
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 }
12011980 };
12021981
12031982 /// A `LocalIndex` is a raw index into the symtab like `Index`, but it guarantees that the
......@@ -1262,6 +2041,44 @@ const Symbol = struct {
12622041 };
12632042 }
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
12652082 /// Returns `true` if the target of `s` has moved, meaning the symbol's value will change at
12662083 /// some point due to a call to `flushMoved`.
12672084 fn hasMoved(s: Symbol.Id, elf: *Elf) bool {
......@@ -1328,7 +2145,8 @@ pub fn lazySymbol(elf: *Elf, lazy: link.File.LazySymbol) !link.File.SymbolId {
13282145 .type = sym_type,
13292146 .shndx = shndx,
13302147 }),
1331 .first_reloc = .none,
2148 .first_symbol_reloc = .none,
2149 .first_got_reloc = .none,
13322150 };
13332151 elf.nodes.appendAssumeCapacity(switch (lazy.kind) {
13342152 .code => .{ .lazy_code = @enumFromInt(gop.index) },
......@@ -1377,7 +2195,7 @@ pub fn addReloc(
13772195 offset: u64,
13782196 target: link.File.SymbolId,
13792197 addend: i64,
1380 @"type": Reloc.Type,
2198 @"type": MachineRelocType,
13812199) !void {
13822200 const node: MappedFile.Node.Index = Node.fromAtom(atom);
13832201 try elf.ensureUnusedRelocCapacity(node, 1);
......@@ -1532,7 +2350,6 @@ const StringTable = struct {
15322350 .{ .slice = slice_const },
15332351 );
15342352 if (gop.found_existing) return gop.key_ptr.*;
1535 try ni.resized(gpa, &elf.mf);
15362353 const old_size, const new_size = size: switch (elf.shdrPtr(shndx)) {
15372354 inline else => |shdr| {
15382355 const old_size: u32 = @intCast(elf.targetLoad(&shdr.size));
......@@ -1541,6 +2358,9 @@ const StringTable = struct {
15412358 break :size .{ old_size, new_size };
15422359 },
15432360 };
2361 if (shndx == elf.shndx.dynstr) {
2362 elf.updateDynamicEntry(std.elf.DT_STRSZ, new_size);
2363 }
15442364 _, const node_size = ni.location(&elf.mf).resolve(&elf.mf);
15452365 if (new_size > node_size)
15462366 try ni.resize(&elf.mf, gpa, new_size +| new_size / MappedFile.growth_factor);
......@@ -1569,274 +2389,6 @@ const GotIndex = enum(u32) {
15692389 }
15702390};
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
18402392pub fn open(
18412393 arena: std.mem.Allocator,
18422394 comp: *Compilation,
......@@ -1941,6 +2493,8 @@ fn create(
19412493 .dynstr = .UNDEF,
19422494 .dynamic = .UNDEF,
19432495 .tdata = .UNDEF,
2496 .rela_dyn = .UNDEF,
2497 .rela_plt = .UNDEF,
19442498 .init_array = .UNDEF,
19452499 .fini_array = .UNDEF,
19462500 .preinit_array = .UNDEF,
......@@ -1957,13 +2511,10 @@ fn create(
19572511 .shstrtab = .{ .map = .empty },
19582512 .strtab = .{ .map = .empty },
19592513 .dynstr = .{ .map = .empty },
1960 .got = .{
1961 .len = 0,
1962 .tlsld = .none,
1963 .plt = .empty,
1964 },
1965 .first_plt_reloc = .none,
1966 .first_dynamic_reloc = .none,
2514 .got = .empty,
2515 .plt = .empty,
2516 .plt_first_symbol_reloc = .none,
2517 .dynamic_first_symbol_reloc = .none,
19672518 .needed = .empty,
19682519 .inputs = .empty,
19692520 .input_sections = .empty,
......@@ -1975,12 +2526,15 @@ fn create(
19752526 .pending_index = 0,
19762527 }),
19772528 .pending_uavs = .empty,
1978 .relocs = .empty,
2529 .symbol_relocs = .empty,
2530 .got_relocs = .empty,
2531 .tls_size_symbol_relocs = .empty,
19792532 .section_by_name = .empty,
19802533 .changed_symtab_index = .empty,
19812534 .const_prog_node = .none,
19822535 .synth_prog_node = .none,
19832536 .input_prog_node = .none,
2537 .textrel_count = 0,
19842538 };
19852539 errdefer elf.deinit();
19862540
......@@ -2004,7 +2558,8 @@ pub fn deinit(elf: *Elf) void {
20042558 elf.shstrtab.map.deinit(gpa);
20052559 elf.strtab.map.deinit(gpa);
20062560 elf.dynstr.map.deinit(gpa);
2007 elf.got.plt.deinit(gpa);
2561 elf.got.deinit(gpa);
2562 elf.plt.deinit(gpa);
20082563 elf.needed.deinit(gpa);
20092564 for (elf.inputs.items) |input| if (input.member) |m| gpa.free(m);
20102565 elf.inputs.deinit(gpa);
......@@ -2013,7 +2568,9 @@ pub fn deinit(elf: *Elf) void {
20132568 elf.uavs.deinit(gpa);
20142569 for (&elf.lazy.values) |*lazy| lazy.map.deinit(gpa);
20152570 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);
20172574 elf.section_by_name.deinit(gpa);
20182575 elf.changed_symtab_index.deinit(gpa);
20192576 elf.* = undefined;
......@@ -2323,7 +2880,7 @@ fn initHeaders(
23232880 .entsize = 0,
23242881 };
23252882 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
23282885 elf.symtab.addOneAssumeCapacity().* = .{
23292886 .node = .none,
......@@ -2398,8 +2955,15 @@ fn initHeaders(
23982955 if (@"type" != .REL) {
23992956 elf.shndx.got = try elf.addSection(elf.ni.data_rel_ro, .{
24002957 .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 },
24012964 .flags = .{ .WRITE = true, .ALLOC = true },
24022965 .addralign = addr_align,
2966 .entsize = @intCast(addr_align.toByteUnits()),
24032967 });
24042968 elf.shndx.got_plt = try elf.addSection(
24052969 if (elf.options.z_now) elf.ni.data_rel_ro else elf.ni.data,
......@@ -2413,6 +2977,7 @@ fn initHeaders(
24132977 .X86_64 => 3 * 8,
24142978 },
24152979 .addralign = addr_align,
2980 .entsize = @intCast(addr_align.toByteUnits()),
24162981 },
24172982 );
24182983 const plt_size: std.elf.Xword, const plt_align: std.mem.Alignment, const plt_sec =
......@@ -2508,7 +3073,7 @@ fn initHeaders(
25083073 .NONE, _ => unreachable,
25093074 inline else => |ct_class| @sizeOf(ct_class.ElfN().Rela),
25103075 };
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, .{
25123077 .name = ".rela.dyn",
25133078 .type = .RELA,
25143079 .flags = .{ .ALLOC = true },
......@@ -2517,13 +3082,12 @@ fn initHeaders(
25173082 .entsize = rela_size,
25183083 .node_align = elf.mf.flags.block_size,
25193084 });
2520 const got_plt_shndx = elf.shndx.got_plt;
2521 got_plt_shndx.get(elf).rela_shndx = try elf.addSection(elf.ni.rodata, .{
3085 elf.shndx.rela_plt = try elf.addSection(elf.ni.rodata, .{
25223086 .name = ".rela.plt",
25233087 .type = .RELA,
25243088 .flags = .{ .ALLOC = true, .INFO_LINK = true },
25253089 .link = elf.shndx.dynsym.toSection().?,
2526 .info = got_plt_shndx.toSection().?,
3090 .info = elf.shndx.got_plt.toSection().?,
25273091 .addralign = addr_align,
25283092 .entsize = rela_size,
25293093 .node_align = elf.mf.flags.block_size,
......@@ -2546,7 +3110,7 @@ fn initHeaders(
25463110 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *0x0(%rip)
25473111 0x0f, 0x1f, 0x40, 0x00, // nopl 0x0(%rax)
25483112 });
2549 elf.first_plt_reloc = @enumFromInt(elf.relocs.items.len);
3113 elf.plt_first_symbol_reloc = @enumFromInt(elf.symbol_relocs.items.len);
25503114 try elf.ensureUnusedRelocCapacity(plt_ni, 2);
25513115 elf.addRelocAssumeCapacity(
25523116 plt_ni,
......@@ -2575,6 +3139,26 @@ fn initHeaders(
25753139 elf.phdrs.items[tls_phndx] = elf.ni.tls;
25763140 }
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
25783162 // Create any always-provided linker-defined symbols. The symbols marking the `INIT_ARRAY`/
25793163 // `FINI_ARRAY`/`PREINIT_ARRAY` sections are instead created by `createInitFiniArraySection`
25803164 // 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 {
26793263 return elf.nodes.get(@intFromEnum(ni));
26803264}
26813265/// 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 {
26833267 return switch (elf.getNode(ni)) {
26843268 .file => unreachable,
26853269 .ehdr => unreachable,
......@@ -2717,55 +3301,80 @@ fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
27173301/// Asserts that `ni` must be a node which supports relocations (see `Elf.Node`). Does not support
27183302/// the special-case sections '.plt' and '.dynamic'.
27193303fn 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)) {
27213305 .file => unreachable, // cannot contain relocs
27223306 .ehdr => unreachable, // cannot contain relocs
27233307 .shdr => unreachable, // cannot contain relocs
27243308 .segment => unreachable, // cannot contain relocs
27253309 .section => unreachable, // cannot contain relocs (.plt and .dynamic unsupported)
2726 .input_section => |isi| &elf.input_sections.items[@intFromEnum(isi)].first_reloc,
2727 .nav => |nmi| &elf.navs.values()[@intFromEnum(nmi)].first_reloc,
2728 .uav => |umi| &elf.uavs.values()[@intFromEnum(umi)].first_reloc,
2729 inline .lazy_code, .lazy_const_data => |lmi| &elf.lazy.getPtr(lmi.ref().kind).map.values()[lmi.ref().index].first_reloc,
3310 .input_section => |isi| .{
3311 &elf.input_sections.items[@intFromEnum(isi)].first_symbol_reloc,
3312 &elf.input_sections.items[@intFromEnum(isi)].first_got_reloc,
3313 },
3314 .nav => |nmi| .{
3315 &elf.navs.values()[@intFromEnum(nmi)].first_symbol_reloc,
3316 &elf.navs.values()[@intFromEnum(nmi)].first_got_reloc,
3317 },
3318 .uav => |umi| .{
3319 &elf.uavs.values()[@intFromEnum(umi)].first_symbol_reloc,
3320 null,
3321 },
3322 inline .lazy_code, .lazy_const_data => |lmi| .{
3323 &elf.lazy.getPtr(lmi.ref().kind).map.values()[lmi.ref().index].first_symbol_reloc,
3324 &elf.lazy.getPtr(lmi.ref().kind).map.values()[lmi.ref().index].first_got_reloc,
3325 },
27303326 };
2731 if (first_reloc_ptr.* != .none) {
2732 for (elf.relocs.items[@intFromEnum(first_reloc_ptr.*)..]) |*reloc| {
3327
3328 if (symbol_relocs.* != .none) {
3329 for (
3330 elf.symbol_relocs.items[@intFromEnum(symbol_relocs.*)..],
3331 @intFromEnum(symbol_relocs.*)..,
3332 ) |*reloc, index| {
27333333 if (reloc.node != ni) break;
2734 reloc.delete(elf);
3334 reloc.delete(elf, @enumFromInt(index));
27353335 }
27363336 }
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 }
27383348}
27393349
2740/// Given that `node` has moved, updates all relocations in `node` (starting from `first_reloc`) as
2741/// needed. In relocatables, this means updating the offsets of those relocations. In ELF modules,
2742/// this means applying the relocations.
3350/// Given that `node` has moved, updates all relocations in `node` as needed. In relocatables, this
3351/// means updating the relocations' offsets. In ELF modules, this means applying the relocations.
27433352fn flushMovedNodeRelocs(
27443353 elf: *Elf,
27453354 node: MappedFile.Node.Index,
27463355 node_vaddr: u64,
2747 first_reloc: Reloc.Index,
3356 first_symbol_reloc: SymbolReloc.Index,
3357 first_got_reloc: GotReloc.Index,
27483358) void {
2749 if (first_reloc == .none) return;
2750 switch (elf.ehdrField(.type)) {
2751 .NONE, .CORE, _ => unreachable,
2752 .REL => {
2753 // In a relocatable, we're not actually applying any relocations ourselves, but we need
2754 // to update the offsets of the relocation entries since the node they're in has moved.
2755 for (elf.relocs.items[@intFromEnum(first_reloc)..]) |*reloc| {
2756 if (reloc.node != node) break;
2757 reloc.updateNodeOffset(elf, node_vaddr);
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;
3359 if (first_symbol_reloc != .none) {
3360 for (elf.symbol_relocs.items[@intFromEnum(first_symbol_reloc)..]) |*reloc| {
3361 if (reloc.node != node) break;
3362 if (reloc.rela_index.unwrap()) |rela_index| {
3363 // Update the offsets of any `ElfN.Rela` entry we've emitted, since the node they're
3364 // in has moved, so their offset within the section might also have moved.
3365 reloc.relaSection(elf).relaSetOffset(elf, rela_index, node_vaddr + reloc.offset);
3366 } else {
3367 // We've applied this relocation ourselves! Just re-apply it now.
27643368 reloc.apply(elf);
27653369 }
2766 // TODO: once we're emitting runtime relocation entries, we need to update their offsets
2767 // too, like the logic for relocatables above.
2768 },
3370 }
3371 }
3372
3373 if (first_got_reloc != .none) {
3374 for (elf.got_relocs.items[@intFromEnum(first_got_reloc)..]) |*reloc| {
3375 if (reloc.node != node) break;
3376 reloc.apply(elf);
3377 }
27693378 }
27703379}
27713380
......@@ -3109,7 +3718,8 @@ fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) !Node.NavM
31093718 .type = elf.navType(nav.resolved.?),
31103719 .shndx = shndx,
31113720 }),
3112 .first_reloc = .none,
3721 .first_symbol_reloc = .none,
3722 .first_got_reloc = .none,
31133723 };
31143724 elf.nodes.appendAssumeCapacity(.{ .nav = nmi });
31153725 }
......@@ -3158,7 +3768,7 @@ fn uavMapIndex(
31583768 .type = .OBJECT,
31593769 .shndx = shndx,
31603770 }),
3161 .first_reloc = .none,
3771 .first_symbol_reloc = .none,
31623772 };
31633773 elf.nodes.appendAssumeCapacity(.{ .uav = umi });
31643774 elf.const_prog_node.increaseEstimatedTotalItems(1);
......@@ -3447,10 +4057,11 @@ fn loadObject(
34474057 switch (elf.shdrPtr(shndx.*)) {
34484058 inline else => |shdr| {
34494059 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);
34514063 },
34524064 }
3453 try shndx.get(elf).ni.resized(gpa, &elf.mf);
34544065 break :shndx shndx.*;
34554066 },
34564067 .has_file_bits = true,
......@@ -3482,7 +4093,8 @@ fn loadObject(
34824093 // zero-based. This will eventually be updated by `flushMoved`.
34834094 .vaddr = 0,
34844095 .node = ni,
3485 .first_reloc = .none,
4096 .first_symbol_reloc = .none,
4097 .first_got_reloc = .none,
34864098 };
34874099 elf.synth_prog_node.increaseEstimatedTotalItems(1);
34884100 }
......@@ -3777,6 +4389,8 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {
37774389 .DEFAULT, .PROTECTED => {},
37784390 }
37794391
4392 if (sym.shndx == std.elf.SHN_UNDEF) continue;
4393
37804394 if (sym.name >= dynstr.len) {
37814395 return diags.failParse(path, "bad symbol name string", .{});
37824396 }
......@@ -3787,39 +4401,47 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void {
37874401 gop.value_ptr.* = sym.info.type;
37884402 }
37894403
3790 // If there's already an undefined symbol by this name of type STT_NOTYPE, populate
3791 // its type now.
3792 update_sym_type: {
3793 const global_ptr = elf.globals.strong_undef.getPtr(name) orelse
3794 elf.globals.weak_undef.getPtr(name) orelse
3795 break :update_sym_type;
4404 // If there's already an undefined symbol by this name of type STT_NOTYPE, populate
4405 // its type now.
4406 const global_ptr = elf.globals.strong_undef.getPtr(name) orelse
4407 elf.globals.weak_undef.getPtr(name) orelse
4408 continue;
4409
4410 if (global_ptr.dynsym_index == 0) continue;
4411
4412 const sym_ptr = @field(elf.symPtr(global_ptr.symtab_index), @tagName(class));
4413 switch (elf.targetLoad(&sym_ptr.other).visibility) {
4414 .HIDDEN, .INTERNAL, .PROTECTED => continue,
4415 .DEFAULT => {},
4416 }
4417
4418 if (elf.targetLoad(&sym_ptr.shndx) != std.elf.SHN_UNDEF) continue;
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));
3800 switch (elf.targetLoad(&sym_ptr.other).visibility) {
3801 .HIDDEN, .INTERNAL, .PROTECTED => break :update_sym_type,
3802 .DEFAULT => {},
3803 }
4427 elf.targetStore(&sym_ptr.info, .{
4428 .bind = cur_info.bind,
4429 .type = new_type,
4430 });
38044431
3805 const cur_info = elf.targetLoad(&sym_ptr.info);
3806 if (cur_info.type == .NOTYPE) {
3807 elf.targetStore(&sym_ptr.info, .{
3808 .bind = cur_info.bind,
3809 .type = sym.info.type,
3810 });
3811
3812 const dynsym_ptr = @field(elf.dynsymPtr(global_ptr.dynsym_index), @tagName(class));
3813 elf.targetStore(&dynsym_ptr.info, .{
3814 .bind = elf.targetLoad(&dynsym_ptr.info).bind,
3815 .type = sym.info.type,
3816 });
3817
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 }
4432 const dynsym_ptr = @field(elf.dynsymPtr(global_ptr.dynsym_index), @tagName(class));
4433 elf.targetStore(&dynsym_ptr.info, .{
4434 .bind = elf.targetLoad(&dynsym_ptr.info).bind,
4435 .type = new_type,
4436 });
4437
4438 // If we just turned this into an STT_FUNC symbol, then we have determined
4439 // that it needs a PLT entry.
4440 if (new_type == .FUNC) {
4441 elf.addPltEntry(name, global_ptr.dynsym_index);
4442 // ...and therefore, we need to re-apply that symbol's relocations, as
4443 // some might be targeting its PLT entry.
4444 global_ptr.symtab_index.applyTargetRelocs(elf);
38234445 }
38244446 }
38254447 }
......@@ -3934,6 +4556,29 @@ fn createInitFiniArraySection(
39344556 ),
39354557 };
39364558}
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
39384583pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) !void {
39394584 _ = prog_node;
......@@ -4073,17 +4718,15 @@ fn prelinkInner(elf: *Elf) !void {
40734718 );
40744719 dynamic_index += 2;
40754720 }
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;
40784721 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)) },
40804723 .{ 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,
40824725 ) },
40834726 .{ 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)) },
40854728 .{ 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,
40874730 ) },
40884731 .{ std.elf.DT_PLTGOT, @intCast(elf.shndx.got_plt.vaddr(elf)) },
40894732 .{ std.elf.DT_PLTREL, std.elf.DT_RELA },
......@@ -4100,19 +4743,19 @@ fn prelinkInner(elf: *Elf) !void {
41004743 if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry|
41014744 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);
41044747 try elf.ensureUnusedRelocCapacity(dynamic_ni, 5);
41054748 elf.addRelocAssumeCapacity(
41064749 dynamic_ni,
41074750 @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),
41094752 0,
41104753 .absAddr(elf),
41114754 );
41124755 elf.addRelocAssumeCapacity(
41134756 dynamic_ni,
41144757 @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),
41164759 0,
41174760 .absAddr(elf),
41184761 );
......@@ -4216,7 +4859,11 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
42164859 .type = .SECTION,
42174860 .shndx = shndx,
42184861 }) 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 } });
42204867 elf.nodes.appendAssumeCapacity(.{ .section = shndx });
42214868 const offset = ni.fileLocation(&elf.mf, false).offset;
42224869 switch (elf.shdrPtr(shndx)) {
......@@ -4242,13 +4889,14 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
42424889fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize) !void {
42434890 if (len == 0) return;
42444891 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);
42464894 const class = elf.identClass();
4247 const rela_shndx, const rela_len = rela: switch (elf.ehdrField(.type)) {
4895 switch (elf.ehdrField(.type)) {
42484896 .NONE, .CORE, _ => unreachable,
42494897 .REL => {
42504898 const shndx = elf.getNodeShndx(node);
4251 if (shndx.get(elf).rela_shndx == .UNDEF) {
4899 if (shndx.get(elf).rela.shndx == .UNDEF) {
42524900 var bfa_buf: [32]u8 = undefined;
42534901 var bfa: std.heap.BufferFirstAllocator = .init(&bfa_buf, gpa);
42544902 const allocator = bfa.allocator();
......@@ -4275,33 +4923,28 @@ fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize)
42754923 .node_align = elf.mf.flags.block_size,
42764924 });
42774925 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;
42794927 }
4280 break :rela .{ shndx.get(elf).rela_shndx, len };
4928 try shndx.get(elf).rela.shndx.relaEnsureAdditionalCapacity(elf, len);
42814929 },
4282 .EXEC, .DYN => switch (elf.got.tlsld) {
4283 _ => return,
4284 .none => if (elf.shndx.dynamic != .UNDEF) {
4285 try elf.mf.updates.ensureUnusedCapacity(gpa, 1);
4286 const got_ni = elf.shndx.got.get(elf).ni;
4287 _, const got_node_size = got_ni.location(&elf.mf).resolve(&elf.mf);
4288 const got_size = switch (class) {
4289 .NONE, _ => unreachable,
4290 inline else => |ct_class| (elf.got.len + 2) * @sizeOf(ct_class.ElfN().Addr),
4291 };
4292 if (got_size > got_node_size)
4293 try got_ni.resize(&elf.mf, gpa, got_size +| got_size / MappedFile.growth_factor);
4294 break :rela .{ elf.shndx.got.get(elf).rela_shndx, 1 };
4295 } else return,
4930 .EXEC, .DYN => {
4931 try elf.tls_size_symbol_relocs.ensureUnusedCapacity(gpa, len);
4932 const new_got_entries = len * 2; // at worst, every reloc is a new TLSGD
4933 try elf.got.ensureUnusedCapacity(gpa, new_got_entries);
4934 const got_ni = elf.shndx.got.get(elf).ni;
4935 _, const got_node_size = got_ni.location(&elf.mf).resolve(&elf.mf);
4936 const need_got_size = switch (class) {
4937 .NONE, _ => unreachable,
4938 inline else => |ct_class| (elf.got.count() + new_got_entries) * @sizeOf(ct_class.ElfN().Addr),
4939 };
4940 if (need_got_size > got_node_size)
4941 try got_ni.resize(&elf.mf, gpa, need_got_size +| need_got_size / MappedFile.growth_factor);
4942
4943 if (elf.shndx.dynamic != .UNDEF) {
4944 try elf.shndx.rela_dyn.relaEnsureAdditionalCapacity(elf, new_got_entries);
4945 }
42964946 },
4297 };
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);
4947 }
43054948}
43064949fn addRelocAssumeCapacity(
43074950 elf: *Elf,
......@@ -4309,123 +4952,443 @@ fn addRelocAssumeCapacity(
43094952 offset: u64,
43104953 target: Symbol.Id,
43114954 addend: i64,
4312 @"type": Reloc.Type,
4955 @"type": MachineRelocType,
43134956) void {
43144957 assert(node != .none);
4315 const ri: Reloc.Index = @enumFromInt(elf.relocs.items.len);
4316 const next: Reloc.Index = next: {
4317 const target_ptr = target.index(elf).ptr(elf);
4318 const next = target_ptr.first_target_reloc;
4319 target_ptr.first_target_reloc = ri;
4320 break :next next;
4958 switch (elf.ehdrField(.type)) {
4959 .NONE, .CORE, _ => unreachable,
4960 .REL => {
4961 const rela_shndx = elf.getNodeShndx(node).get(elf).rela.shndx;
4962 const rela_index = rela_shndx.relaAddOneAssumeCapacity(elf, .{
4963 .type = @"type",
4964 // This field needs to equal the offset into the section, which is *not* necessarily
4965 // the same thing as our `offset`, which is the offset into `node`. We could compute
4966 // the section offset now, but there's no point, because `flushMovedNodeRelocs` will
4967 // eventually do it for us anyway, so just init to 0.
4968 .offset = 0,
4969 .raw_sym_index = @intFromEnum(target.index(elf)),
4970 .addend = addend,
4971 });
4972 const ri: SymbolReloc.Index = @enumFromInt(elf.symbol_relocs.items.len);
4973 const next: SymbolReloc.Index = next: {
4974 const target_ptr = target.index(elf).ptr(elf);
4975 const next = target_ptr.first_target_reloc;
4976 target_ptr.first_target_reloc = ri;
4977 break :next next;
4978 };
4979 if (next != .none) {
4980 next.get(elf).prev = ri;
4981 }
4982 elf.symbol_relocs.appendAssumeCapacity(.{
4983 .node = node,
4984 .offset = offset,
4985 .type = .write_rela,
4986 .target = target,
4987 .addend = addend,
4988 .next = next,
4989 .prev = .none,
4990 .rela_index = rela_index.toOptional(),
4991 });
4992 },
4993
4994 .DYN, .EXEC => switch (elf.ehdrField(.machine)) {
4995 else => |machine| @panic(@tagName(machine)),
4996 .X86_64 => switch (@"type".X86_64) {
4997 _,
4998 .NONE,
4999 .COPY,
5000 .GLOB_DAT,
5001 .JUMP_SLOT,
5002 .RELATIVE64,
5003 .RELATIVE,
5004 .IRELATIVE,
5005 .@"16",
5006 .PC16,
5007 .@"8",
5008 .PC8,
5009 .DTPMOD64,
5010 .GOTPLT64,
5011 => @panic("TODO: error for illegal or unsupported input relocation"),
5012
5013 // TODO: the psABI links to https://www.fsfla.org/~lxoliva/writeups/TLS/RFC-TLSDESC-x86.txt
5014 .GOTPC32_TLSDESC => @panic("TODO: R_X86_64_GOTPC32_TLSDESC"),
5015 .TLSDESC_CALL => @panic("TODO: R_X86_64_TLSDESC_CALL"),
5016 .TLSDESC => @panic("TODO: R_X86_64_TLSDESC"),
5017
5018 // Relocations targeting a symbol
5019 .@"64" => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs64),
5020 .@"32" => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32),
5021 .@"32S" => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .abs32s),
5022 .PC64 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel64),
5023 .PC32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .rel32),
5024 .PLT32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .pltrel32),
5025 .SIZE64 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .size64),
5026 .SIZE32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .size32),
5027 .DTPOFF64 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .dtpoff64),
5028 .DTPOFF32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .dtpoff32),
5029 .TPOFF64 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff64),
5030 .TPOFF32 => elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .tpoff32),
5031 .GOTPC64 => {
5032 const got_sym: Symbol.Id = .local(elf.shndx.got.get(elf).lsi);
5033 return elf.addSymbolRelocAssumeCapacity(node, offset, got_sym, addend, .rel64);
5034 },
5035 .GOTPC32 => {
5036 const got_sym: Symbol.Id = .local(elf.shndx.got.get(elf).lsi);
5037 return elf.addSymbolRelocAssumeCapacity(node, offset, got_sym, addend, .rel32);
5038 },
5039
5040 // TODO: these are the address of an arbitrary symbol (or PLT entry) relative to the
5041 // base of the GOT, which is quite annoying. Luckily, they seem to be rare, so I'm
5042 // probably just going to introduce a set (ArrayHashMap) of SymbolReloc.Index which
5043 // need to be re-applied whenever the GOT moves.
5044 .GOTOFF64 => @panic("TODO: R_X86_64_GOTOFF64"), // offset of symbol from GOT base
5045 .PLTOFF64 => @panic("TODO: R_X86_64_PLTOFF64"), // offset of PLT entry from GOT base (yes, I know, the name is stupid)
5046
5047 // Relocations targeting a GOT entry
5048 .GOT64 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .offset64),
5049 .GOT32 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .offset32),
5050 .GOTPCREL64 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .rel64),
5051 .GOTPCREL => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .rel32),
5052 // TODO: the next two are relaxable to non-GOT relocations, but I haven't figured
5053 // out how to represent relaxations yet. If we want to remove a `GotReloc` and add a
5054 // `SymbolReloc` at some point, we can't do that in `GotReloc.apply`, because that
5055 // function must be idempotent to ensure reproducible binaries. I think we would
5056 // need to do that as soon as the operation is known to be relaxable (e.g. because
5057 // we found a defininition for a non-preemptible symbol).
5058 .GOTPCRELX => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .rel32),
5059 .REX_GOTPCRELX => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .rel32),
5060
5061 .TLSGD => elf.addGotRelocAssumeCapacity(node, offset, .{ .tlsgd0 = target }, addend, .rel32),
5062 .TLSLD => elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .rel32),
5063 .GOTTPOFF => elf.addGotRelocAssumeCapacity(node, offset, .{ .tpoff = target }, addend, .rel32),
5064 },
5065 },
5066 }
5067}
5068fn addSymbolRelocAssumeCapacity(
5069 elf: *Elf,
5070 node: MappedFile.Node.Index,
5071 offset: u64,
5072 target: Symbol.Id,
5073 addend: i64,
5074 @"type": SymbolReloc.Type,
5075) void {
5076 assert(elf.ehdrField(.type) != .REL);
5077
5078 const rela_index: Section.RelaIndex.Optional = r: {
5079 if (elf.shndx.dynamic == .UNDEF) break :r .none;
5080 const rela_type: MachineRelocType = switch (elf.ehdrField(.machine)) {
5081 else => |machine| @panic(@tagName(machine)),
5082 .X86_64 => .{ .X86_64 = switch (@"type") {
5083 .write_rela => unreachable,
5084 .abs64 => .@"64",
5085 .abs32 => .@"32",
5086 .abs32s => .@"32S",
5087 .rel64 => .PC64,
5088 .rel32 => .PC32,
5089 .pltrel64 => break :r .none,
5090 .pltrel32 => break :r .none,
5091 .dtpoff64 => .DTPOFF64,
5092 .dtpoff32 => .DTPOFF32,
5093 .tpoff64 => .TPOFF64,
5094 .tpoff32 => .TPOFF32,
5095 .size64 => .SIZE64,
5096 .size32 => .SIZE32,
5097 } },
5098 };
5099 const dynsym_index: u32 = switch (target.unwrap()) {
5100 .local => break :r .none,
5101 // TODO: even if the symbol is locally defined, preemption/interposition is a
5102 // possibility, which this condition does not currently consider!
5103 .global => |name| if (elf.globals.strong_def.contains(name) or
5104 elf.globals.weak_def.contains(name))
5105 {
5106 break :r .none;
5107 } else elf.globalByName(name).?.dynsym_index,
5108 };
5109
5110 if (elf.nodeRequiresTextrel(node)) {
5111 elf.textrel_count += 1;
5112 }
5113
5114 // It currently looks like we need a runtime relocation for this.
5115 break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
5116 .type = rela_type,
5117 // This field needs to equal the offset into the section, which is *not* necessarily
5118 // the same thing as our `offset`, which is the offset into `node`. We could compute
5119 // the section offset now, but there's no point, because `flushMovedNodeRelocs` will
5120 // eventually do it for us anyway, so just init to 0.
5121 .offset = 0,
5122 .raw_sym_index = dynsym_index,
5123 .addend = addend,
5124 }).toOptional();
43215125 };
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;
43225131 if (next != .none) {
43235132 next.get(elf).prev = ri;
43245133 }
4325 elf.relocs.addOneAssumeCapacity().* = .{
5134 elf.symbol_relocs.appendAssumeCapacity(.{
5135 .node = node,
5136 .offset = offset,
5137 .target = target,
5138 .addend = addend,
43265139 .type = @"type",
4327 .prev = .none,
43285140 .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(.{
43295203 .node = node,
5204 .offset = offset,
43305205 .target = target,
4331 .index = index: switch (elf.ehdrField(.type)) {
4332 .NONE, .CORE, _ => unreachable,
4333 .REL => {
4334 const sh = elf.getNodeShndx(node).get(elf);
4335 switch (elf.shdrPtr(sh.rela_shndx)) {
4336 inline else => |shdr, class| {
4337 const Rela = class.ElfN().Rela;
4338 const ent_size = elf.targetLoad(&shdr.entsize);
4339 const rela_slice = sh.rela_shndx.get(elf).ni.slice(&elf.mf);
4340 const index: u32 = if (sh.rela_free.unwrap()) |index| alloc_index: {
4341 const rela: *Rela = @ptrCast(@alignCast(
4342 rela_slice[@intCast(ent_size * index)..][0..@intCast(ent_size)],
4343 ));
4344 sh.rela_free = @enumFromInt(rela.offset);
4345 break :alloc_index index;
4346 } else alloc_index: {
4347 const old_size = elf.targetLoad(&shdr.size);
4348 const new_size = old_size + ent_size;
4349 elf.targetStore(&shdr.size, @intCast(new_size));
4350 break :alloc_index @intCast(@divExact(old_size, ent_size));
4351 };
4352 const rela: *Rela = @ptrCast(@alignCast(
4353 rela_slice[@intCast(ent_size * index)..][0..@intCast(ent_size)],
4354 ));
4355 // The `offset` field here needs to equal the offset into the section, which
4356 // is *not* the same as our `offset` which is the offset into `node`. We
4357 // could calculate it now, but there's no point since `flushMovedNodeRelocs`
4358 // will eventually do that for us anyway. So for now, just set offset to 0.
4359 rela.* = .{
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);
5206 .addend = addend,
5207 .type = @"type",
5208 });
5209}
5210fn updateGotEntry(elf: *Elf, got_index: usize) void {
5211 const entry_value: union(enum) {
5212 unsigned: u64,
5213 signed: i64,
5214 reloc: struct {
5215 type: MachineRelocType,
5216 dynsym_index: u32,
5217 },
5218 } = switch (elf.got.keys()[got_index]) {
5219 .reserved => .{ .unsigned = 0 },
5220 .tpoff => |sym_id| val: {
5221 // We will break from this block if we require a relocation.
5222 known: {
5223 if (elf.base.comp.config.output_mode != .Exe) {
5224 // Only the executable's per-module TLS block is at a known offset from the
5225 // general TLS pointer.
5226 break :known;
5227 }
5228 switch (sym_id.unwrap()) {
5229 .local => {},
5230 .global => |name| if (elf.globals.strong_undef.contains(name) or
5231 elf.globals.weak_undef.contains(name))
5232 {
5233 // This is an external TLS symbol, so we don't know its offset.
5234 break :known;
43695235 },
43705236 }
4371 },
4372 .EXEC, .DYN => {
4373 switch (elf.ehdrField(.machine)) {
4374 else => |machine| @panic(@tagName(machine)),
4375 .AARCH64, .PPC64, .RISCV => {},
4376 .X86_64 => switch (@"type".X86_64) {
4377 else => {},
4378 .TLSLD => switch (elf.got.tlsld) {
4379 _ => {},
4380 .none => if (elf.shndx.dynamic != .UNDEF) {
4381 const tlsld_index = elf.got.len;
4382 elf.got.tlsld = .wrap(tlsld_index);
4383 elf.got.len = tlsld_index + 2;
4384 const got_addr = got_addr: switch (elf.shdrPtr(elf.shndx.got)) {
4385 inline else => |shdr, class| {
4386 const addr_size = @sizeOf(class.ElfN().Addr);
4387 const old_size = addr_size * tlsld_index;
4388 const new_size = old_size + addr_size * 2;
4389 @memset(
4390 elf.shndx.got.get(elf).ni.slice(&elf.mf)[old_size..new_size],
4391 0,
4392 );
4393 break :got_addr elf.targetLoad(&shdr.addr) + old_size;
4394 },
5237 // It's a symbol which we define, the symbol is not interposable because we're the
5238 // executable, and we know our per-module TLS block's offset because we're the
5239 // executable. We therefore know this value!
5240 const tls_phndx = elf.getNode(elf.ni.tls).segment;
5241 const tls_size: u64 = switch (elf.phdrSlice()) {
5242 inline else => |phdr| tls_size: {
5243 assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS);
5244 break :tls_size elf.targetLoad(&phdr[tls_phndx].memsz);
5245 },
5246 };
5247 const sym_value = sym_id.value(elf);
5248 break :val .{ .signed = @bitCast(sym_value -% tls_size) };
5249 }
5250 break :val .{
5251 .reloc = .{
5252 .type = switch (elf.ehdrField(.machine)) {
5253 else => |machine| @panic(@tagName(machine)),
5254 .X86_64 => .{ .X86_64 = .TPOFF64 },
5255 },
5256 .dynsym_index = switch (sym_id.unwrap()) {
5257 .global => |name| elf.globalByName(name).?.dynsym_index,
5258 // TODO: I have no idea if compilers are even allowed to emit this, but if they
5259 // are then I guess we need to add this local symbol to `.dynsym`?
5260 .local => @panic("TODO(Elf2): GOT tpoff entry referencing local symbol"),
5261 },
5262 },
5263 };
5264 },
5265 .symbol, .tlsgd1 => |sym_id, tag| val: {
5266 const name = switch (sym_id.unwrap()) {
5267 .local => break :val .{ .unsigned = sym_id.value(elf) },
5268 .global => |name| name,
5269 };
5270 // If the symbol is *defined* in this module, we might be able to avoid the relocation.
5271 if (elf.globals.strong_def.getPtr(name) orelse
5272 elf.globals.weak_def.getPtr(name)) |global|
5273 {
5274 // We have a definition, but it might be interposable (aka preemptible). There
5275 // are two cases where it is not and so we can (and, in fact, must) elide the
5276 // runtime relocation:
5277 // * We are the executable. Symbols from executables cannot be interposed.
5278 // * The symbol's visibility disallows interposition.
5279 if (elf.base.comp.config.output_mode == .Exe) {
5280 // No relocation needed.
5281 break :val .{ .unsigned = sym_id.value(elf) };
5282 }
5283 const visibility: std.elf.STV = switch (elf.symPtr(global.symtab_index)) {
5284 inline else => |sym| elf.targetLoad(&sym.other).visibility,
5285 };
5286 switch (visibility) {
5287 .DEFAULT => {},
5288 .INTERNAL, .HIDDEN, .PROTECTED => {
5289 // No relocation needed.
5290 break :val .{ .unsigned = sym_id.value(elf) };
5291 },
5292 }
5293 }
5294 break :val .{ .reloc = .{
5295 .type = if (tag == .symbol) .globDat(elf) else .dtpOffAddr(elf),
5296 .dynsym_index = elf.globalByName(name).?.dynsym_index,
5297 } };
5298 },
5299 .tlsgd0 => |sym| switch (elf.shndx.dynamic) {
5300 .UNDEF => .{ .unsigned = 1 }, // TLS module ID for exexcutable
5301 else => .{
5302 .reloc = .{
5303 .type = .{ .X86_64 = .DTPMOD64 },
5304 .dynsym_index = switch (sym.unwrap()) {
5305 .local => 0,
5306 .global => |name| dsi: {
5307 // Like in the `.tlsgd1` case, we need to check for a non-interposable definition.
5308 if (elf.globals.strong_def.getPtr(name) orelse
5309 elf.globals.weak_def.getPtr(name)) |global|
5310 {
5311 if (elf.base.comp.config.output_mode == .Exe) {
5312 break :dsi 0; // non-interposable definition
5313 }
5314 const visibility: std.elf.STV = switch (elf.symPtr(global.symtab_index)) {
5315 inline else => |sym_ptr| elf.targetLoad(&sym_ptr.other).visibility,
43955316 };
4396 const rela_dyn_shndx = elf.shndx.got.get(elf).rela_shndx;
4397 const rela_dyn_ni = rela_dyn_shndx.get(elf).ni;
4398 switch (elf.shdrPtr(rela_dyn_shndx)) {
4399 inline else => |shdr, class| {
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);
5317 switch (visibility) {
5318 .DEFAULT => {},
5319 .INTERNAL, .HIDDEN, .PROTECTED => {
5320 break :dsi 0; // non-interposable definition
44165321 },
44175322 }
4418 rela_dyn_ni.resizedAssumeCapacity(&elf.mf);
4419 },
5323 }
5324 // `sym` is either undefined or an interposable definition, so use its
5325 // actual dynsym index.
5326 break :dsi elf.globalByName(name).?.dynsym_index;
44205327 },
44215328 },
4422 }
4423 break :index .none;
5329 },
44245330 },
44255331 },
4426 .offset = offset,
4427 .addend = addend,
5332 .tlsld0 => switch (elf.shndx.dynamic) {
5333 .UNDEF => .{ .unsigned = 1 }, // TLS module ID for exexcutable
5334 else => .{ .reloc = .{
5335 .type = .{ .X86_64 = .DTPMOD64 },
5336 .dynsym_index = 0,
5337 } },
5338 },
5339 .tlsld1 => .{ .unsigned = 0 },
5340 };
5341
5342 // First, write to the GOT itself. If we're planning to use a relocation, we'll just write zeroes.
5343 const got_entry_addr: u64 = switch (elf.shdrPtr(elf.shndx.got)) {
5344 inline else => |got_shdr, class| got_entry_addr: {
5345 const addr_size = @sizeOf(class.ElfN().Addr);
5346 const offset = got_index * addr_size;
5347 const entry_ptr: *class.ElfN().Addr = @ptrCast(@alignCast(
5348 elf.shndx.got.get(elf).ni.slice(&elf.mf)[offset..][0..addr_size],
5349 ));
5350 entry_ptr.* = switch (entry_value) {
5351 .unsigned => |x| @intCast(x),
5352 .signed => |x| switch (class) {
5353 .NONE, _ => comptime unreachable,
5354 .@"32" => @bitCast(@as(i32, @intCast(x))),
5355 .@"64" => @bitCast(x),
5356 },
5357 .reloc => 0,
5358 };
5359 break :got_entry_addr elf.targetLoad(&got_shdr.addr) + offset;
5360 },
44285361 };
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;
44295392}
44305393
44315394pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {
......@@ -4565,6 +5528,11 @@ pub fn flush(
45655528 if (any_undef) return error.LinkFailure;
45665529 }
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
45685536 while (try elf.idle(tid)) {}
45695537
45705538 const entry_addr: u64 = entry: {
......@@ -4595,6 +5563,47 @@ pub fn flush(
45955563 else => |e| return comp.link_diags.fail("flush write failed: {t}", .{e}),
45965564 };
45975565}
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
45995608pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool {
46005609 const comp = elf.base.comp;
......@@ -4660,6 +5669,9 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool {
46605669 break :task;
46615670 }
46625671 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.
46635675 if (elf.ehdrField(.type) == .REL) {
46645676 const sub_prog_node = elf.mf.update_prog_node.start(kv.key.slice(elf), 0);
46655677 defer sub_prog_node.end();
......@@ -4667,7 +5679,11 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool {
46675679 var ri = sym.first_target_reloc;
46685680 while (ri != .none) {
46695681 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 );
46715687 ri = reloc.next;
46725688 }
46735689 break :task;
......@@ -4876,126 +5892,56 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {
48765892 .section => |shndx| {
48775893 try elf.flushFileOffset(ni);
48785894 const addr = elf.computeNodeVAddr(ni);
4879 switch (elf.shdrPtr(shndx)) {
4880 inline else => |shdr, class| {
4881 const flags = elf.targetLoad(&shdr.flags).shf;
4882 if (flags.ALLOC) {
4883 if (elf.shndx.dynamic != .UNDEF) {
4884 if (shndx == elf.shndx.got) {
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 }
5895 const old_addr: u64, const flags: std.elf.SHF = switch (elf.shdrPtr(shndx)) {
5896 inline else => |shdr| .{
5897 elf.targetLoad(&shdr.addr),
5898 elf.targetLoad(&shdr.flags).shf,
5899 },
5900 };
49735901
4974 // Update global symbols targeting this section
4975 if (elf.node_global_symbols.get(ni)) |first_name| {
4976 assert(first_name != .empty);
4977 const old_addr = elf.targetLoad(&shdr.addr);
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 }
5902 if (flags.ALLOC) {
5903 switch (elf.shdrPtr(shndx)) {
5904 inline else => |shdr| elf.targetStore(&shdr.addr, @intCast(addr)),
5905 }
49885906
4989 elf.targetStore(&shdr.addr, @intCast(addr));
4990 shndx.get(elf).lsi.index().flushMoved(elf, addr);
5907 // Update global symbols targeting this section
5908 if (elf.node_global_symbols.get(ni)) |first_name| {
5909 assert(first_name != .empty);
5910 var name = first_name;
5911 while (name != .empty) {
5912 const global = elf.globalByName(name).?;
5913 const old_sym_addr: u64 = switch (elf.symPtr(global.symtab_index)) {
5914 inline else => |sym| elf.targetLoad(&sym.value),
5915 };
5916 Symbol.Id.global(name).flushMoved(
5917 elf,
5918 old_sym_addr - old_addr + addr,
5919 );
5920 name = global.next_in_node;
49915921 }
5922 }
49925923
4993 if (shndx == elf.shndx.plt) {
4994 elf.flushMovedNodeRelocs(ni, elf.targetLoad(&shdr.addr), elf.first_plt_reloc);
4995 } else if (shndx == elf.shndx.dynamic) {
4996 elf.flushMovedNodeRelocs(ni, elf.targetLoad(&shdr.addr), elf.first_dynamic_reloc);
4997 }
4998 },
5924 Symbol.Id.local(shndx.get(elf).lsi).flushMoved(elf, addr);
5925 }
5926
5927 if (shndx == elf.shndx.got) {
5928 const rela_dyn_shndx = elf.shndx.rela_dyn;
5929 for (elf.got.values()) |opt_rela_index| {
5930 const rela_index = opt_rela_index.unwrap() orelse continue;
5931 rela_dyn_shndx.relaAdjustOffset(elf, rela_index, old_addr, addr);
5932 }
5933 for (elf.got_relocs.items) |*reloc| {
5934 reloc.apply(elf);
5935 }
5936 } else if (shndx == elf.shndx.plt) {
5937 elf.flushMovedNodeRelocs(ni, addr, elf.plt_first_symbol_reloc, .none);
5938 elf.flushMovedPltSection(.plt, old_addr, addr);
5939 } else if (shndx == elf.shndx.got_plt) {
5940 elf.flushMovedPltSection(.got_plt, old_addr, addr);
5941 } else if (shndx == elf.shndx.plt_sec) {
5942 elf.flushMovedPltSection(.plt_sec, old_addr, addr);
5943 } else if (shndx == elf.shndx.dynamic) {
5944 elf.flushMovedNodeRelocs(ni, addr, elf.dynamic_first_symbol_reloc, .none);
49995945 }
50005946 },
50015947 .input_section => |isi| {
......@@ -5020,7 +5966,10 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {
50205966 .DEFAULT => elf.targetLoad(&sym.value),
50215967 },
50225968 };
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 );
50245973 }
50255974
50265975 // Update global symbols
......@@ -5032,26 +5981,38 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {
50325981 const old_sym_addr: u64 = switch (elf.symPtr(global.symtab_index)) {
50335982 inline else => |sym| elf.targetLoad(&sym.value),
50345983 };
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 );
50365988 name = global.next_in_node;
50375989 }
50385990 }
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 );
50415998 },
50425999 inline .nav, .uav, .lazy_code, .lazy_const_data => |mi| {
50436000 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);
50456002 if (elf.node_global_symbols.get(ni)) |first_name| {
50466003 assert(first_name != .empty);
50476004 var name = first_name;
50486005 while (name != .empty) {
5049 const global = elf.globalByName(name).?;
5050 global.flushMoved(elf, new_addr);
5051 name = global.next_in_node;
6006 Symbol.Id.global(name).flushMoved(elf, new_addr);
6007 name = elf.globalByName(name).?.next_in_node;
50526008 }
50536009 }
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 );
50556016 },
50566017 }
50576018 try ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
......@@ -5079,6 +6040,27 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void {
50796040 },
50806041 .TLS => {
50816042 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 }
50826064 return ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
50836065 },
50846066 }
......@@ -5113,110 +6095,28 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void {
51136095 },
51146096 },
51156097 .section => |shndx| switch (elf.shdrPtr(shndx)) {
5116 inline else => |shdr, class| {
6098 inline else => |shdr| {
51176099 switch (elf.targetLoad(&shdr.type)) {
51186100 else => unreachable,
6101
51196102 .NULL => if (size > 0) elf.targetStore(&shdr.type, .PROGBITS),
51206103 .PROGBITS => if (size == 0) elf.targetStore(&shdr.type, .NULL),
5121 .SYMTAB, .DYNAMIC, .REL, .DYNSYM => return,
5122 .INIT_ARRAY => {
5123 assert(shndx == elf.shndx.init_array);
5124 if (elf.shndx.dynamic != .UNDEF) {
5125 const dynamic_entries: [][2]class.ElfN().Addr = @ptrCast(@alignCast(
5126 elf.shndx.dynamic.get(elf).ni.slice(&elf.mf),
5127 ));
5128 for (dynamic_entries) |*dynamic_entry|
5129 switch (elf.targetLoad(&dynamic_entry[0])) {
5130 else => {},
5131 std.elf.DT_INIT_ARRAYSZ => dynamic_entry[1] = shdr.size,
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 },
6104
6105 .INIT_ARRAY,
6106 .FINI_ARRAY,
6107 .PREINIT_ARRAY,
6108 .STRTAB,
6109 .SYMTAB,
6110 .DYNAMIC,
6111 .REL,
6112 .RELA,
6113 .DYNSYM,
6114 => return,
52186115 }
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 {
52206120 elf.targetStore(&shdr.size, @intCast(size));
52216121 }
52226122 },
......@@ -5224,6 +6124,85 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void {
52246124 .input_section, .nav, .uav, .lazy_code, .lazy_const_data => {},
52256125 }
52266126}
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
52286207pub fn updateExports(
52296208 elf: *Elf,