1const Elf = @This();
2
3const builtin = @import("builtin");
4const native_endian = builtin.cpu.arch.endian();
5
6const std = @import("std");
7const Io = std.Io;
8const assert = std.debug.assert;
9const log = std.log.scoped(.link);
10
11const codegen = @import("../codegen.zig");
12const Compilation = @import("../Compilation.zig");
13const InternPool = @import("../InternPool.zig");
14const link = @import("../link.zig");
15const MappedFile = @import("MappedFile.zig");
16const target_util = @import("../target.zig");
17const tracy = @import("../tracy.zig");
18const Type = @import("../Type.zig");
19const Value = @import("../Value.zig");
20const Zcu = @import("../Zcu.zig");
21const Alignment = MappedFile.Alignment;
22
23base: link.File,
24options: link.File.OpenOptions,
25mf: MappedFile,
26ni: Node.Known,
27archive: ?Archive,
28nodes: std.MultiArrayList(Node),
29/// Does not contain an item for `SHN_UNDEF`.
30shdrs: std.ArrayList(Section),
31phdrs: std.ArrayList(MappedFile.Node.Index.Optional),
32shndx: struct {
33 got: Section.Index,
34 /// Always `.UNDEF` on some targets (e.g. SPARC).
35 got_plt: Section.Index,
36 plt: Section.Index,
37 /// Only created for x86 targets; `.UNDEF` everywhere else.
38 plt_sec: Section.Index,
39 dynsym: Section.Index,
40 dynstr: Section.Index,
41 dynamic: Section.Index,
42 hash: Section.Index,
43 tdata: Section.Index,
44 rela_dyn: Section.Index,
45 rela_plt: Section.Index,
46 // These sections are created only as needed, and are initially `.UNDEF`.
47 init_array: Section.Index,
48 fini_array: Section.Index,
49 preinit_array: Section.Index,
50},
51dynamic: struct {
52 flags: u32,
53 flags_1: u32,
54 rpath: String(.dynstr),
55 soname: String(.dynstr),
56},
57symtab: std.ArrayList(Symbol),
58globals: struct {
59 strong_def: std.array_hash_map.Auto(String(.strtab), Symbol.Global),
60 weak_def: std.array_hash_map.Auto(String(.strtab), Symbol.Global),
61 strong_undef: std.array_hash_map.Auto(String(.strtab), Symbol.Global),
62 weak_undef: std.array_hash_map.Auto(String(.strtab), Symbol.Global),
63},
64/// Key is the name of an undef global for which we have created a "copy relocation" (`R_*_COPY`).
65copied_globals: std.array_hash_map.Auto(String(.strtab), struct {
66 node: MappedFile.Node.Index,
67 /// The index of this global's runtime relocation in `.rela.dyn`.
68 rela_index: Section.RelaIndex,
69}),
70/// Key is the name of an undef global for which we would *like* to create a copy relocation
71/// (`R_*_COPY`),but cannot because we have not seen an appropriate definition in a linked DSO yet.
72///
73/// Therefore, if, when scanning a DSO input, we discover a definition for one of these symbols, we
74/// will remove it from this map and call `maybeAddCopyRelocation`.
75want_copied_globals: std.array_hash_map.Auto(String(.strtab), void),
76/// Key is a node which is a valid `Symbol.node` value, value is the name of the first global symbol
77/// in that node. That symbol is the head of a linked list: see `Symbol.Global.next_in_node`.
78///
79/// Value is never `.empty`.
80///
81/// We use a separate hash map for this data rather than storing it in `navs` etc to save memory,
82/// because the vast majority of nodes which can export global symbols actually will not.
83node_global_symbols: std.array_hash_map.Auto(MappedFile.Node.Index, String(.strtab)),
84/// Contains all globals symbols defined in any needed DSO. This map serves three purposes:
85///
86/// * If we discover an undefined reference to one of these symbols, we know whether the symbol has
87/// type `STT_FUNC`, in which case we will create a PLT entry.
88///
89/// * If we discover a direct relocation (i.e. no GOT or PLT indirection) targeting one of these
90/// symbols, we know whether the symbol has type `STT_OBJECT` and we know its size and alignment,
91/// so we can emit a copy relocation for that symbol instead of using a text relocation.
92///
93/// * When emitting a dynamic executable, we can detect which undefined references are resolved by a
94/// linked DSO, so can emit "undefined global symbol" errors for any other undefined references.
95dso_globals: std.array_hash_map.Auto(String(.strtab), struct {
96 type: std.elf.STT,
97 size: u64,
98 /// This is usually unnecessary, but if a symbol is given a copy relocation (`R_*_COPY`) and so
99 /// becomes a part of the executable's address space despite being defined by a different DSO,
100 /// we need to know its alignment requirement so that we don't break other code. This isn't
101 /// actually stored on the symbol---instead we compute a maximum alignment from the alignment of
102 /// the section containing the symbol, and the symbol's offset within the section. I know this
103 /// sounds like a terrible hack, but it is *genuinely* how you're supposed to do this. Copy
104 /// relocations suck.
105 alignment: Alignment,
106}),
107shstrtab: StringTable,
108strtab: StringTable,
109dynstr: StringTable,
110
111/// Indices map 1--1 to indices into the actual `.got` section.
112///
113/// Value is the output relocation in `.rela.dyn` for the GOT entry.
114got: std.array_hash_map.Auto(GotKey, Section.RelaIndex.Optional),
115/// Key is the name of a global.
116///
117/// Indices map 1--1 to indices into the actual `.got.plt` section. These also equal indices into
118/// the relocations in `.rela.plt`, because every PLT entry has one output relocation (if a runtime
119/// relocation is no longer necessary, then neither is the corresponding PLT entry!).
120///
121/// PLT entries in this map may be "dead", meaning the PLT entry has been deemed unnecessary so is
122/// available for reuse---see `Elf.pltEntryIsDead`. Such entries must not be targeted by relocs.
123plt: std.array_hash_map.Auto(String(.strtab), void),
124/// The `.plt` section contains zero or more symbol relocations starting at this index.
125plt_first_symbol_reloc: SymbolReloc.Index,
126
127needed: std.array_hash_map.Auto(String(.dynstr), void),
128inputs: std.ArrayList(struct {
129 path: std.Build.Cache.Path,
130 member: ?[]const u8,
131 extra: union {
132 /// Active for static libraries.
133 node: MappedFile.Node.Index,
134 /// Active otherwise.
135 file_symbol: Symbol.LocalIndex,
136 },
137}),
138input_pending_index: u32,
139input_sections: std.ArrayList(InputSection),
140input_section_pending_index: u32,
141/// SPARC has some weird relocations which involve setting some bits to fixed constant values. When
142/// we encounter such a relocation, we queue the action here, and apply them during `idle`.
143one_shot_fixups: std.ArrayList(struct {
144 node: MappedFile.Node.Index,
145 offset: u64,
146 /// The syntax in these tag names matches the syntax used in `SymbolReloc.Type.Simple.dest`.
147 action: enum {
148 @"32[12:10] = 0b000",
149 @"32[12:10] = 0b111",
150 @"32[12:12] = 0b0",
151 },
152}),
153navs: std.array_hash_map.Auto(InternPool.Nav.Index, struct {
154 lsi: Symbol.LocalIndex,
155 /// The start index of the contiguous sequence of symbol relocations in this NAV.
156 first_symbol_reloc: SymbolReloc.Index,
157 /// The start index of the contiguous sequence of GOT relocations in this NAV.
158 first_got_reloc: GotReloc.Index,
159}),
160uavs: std.array_hash_map.Auto(InternPool.Index, struct {
161 lsi: Symbol.LocalIndex,
162 /// The start index of the contiguous sequence of symbol relocations in this UAV.
163 first_symbol_reloc: SymbolReloc.Index,
164 // No `first_got_reloc` field because a UAV never contains GOT relocations.
165}),
166lazy: std.EnumArray(link.File.LazySymbol.Kind, struct {
167 map: std.array_hash_map.Auto(InternPool.Index, struct {
168 lsi: Symbol.LocalIndex,
169 /// The start index of the contiguous sequence of symbol relocations in this lazy code/data.
170 first_symbol_reloc: SymbolReloc.Index,
171 /// The start index of the contiguous sequence of GOT relocations in this lazy code/data.
172 first_got_reloc: GotReloc.Index,
173 }),
174 pending_index: u32,
175}),
176pending_uavs: std.ArrayList(Node.UavMapIndex),
177symbol_relocs: std.ArrayList(SymbolReloc),
178got_relocs: std.ArrayList(GotReloc),
179/// Set of relocations which must be re-applied if the size of the TLS segment changes.
180tls_size_symbol_relocs: std.array_hash_map.Auto(SymbolReloc.Index, void),
181/// Index matches the index into `shdrs`. Like `shdrs`, this map excludes `SHN_UNDEF`.
182section_by_name: std.array_hash_map.Auto(String(.shstrtab), void),
183/// Key is the name of a global symbol which has been moved to a new symtab index. Any relocation
184/// entries which target that symbol must be updated to reference the correct symbol index.
185///
186/// When emitting a relocatable (`ET_REL`), this refers to the index in `.symtab`. Otherwise, it
187/// refers to the index in `.dynsym`.
188changed_symtab_index: std.array_hash_map.Auto(String(.strtab), void),
189/// Counts how many relocations are currently in `.rela.dyn` which would require a `DT_TEXTREL`
190/// entry in the `.dynamic` section. This allows adding `DT_TEXTREL` to the output `.dynamic`
191/// section in `flush` only when it is actually necessary. See also `nodeWantsDsoRelocation`.
192textrel_count: u32,
193
194overflowed_reloc_count: u32,
195misaligned_reloc_count: u32,
196
197const_prog_node: std.Progress.Node,
198synth_prog_node: std.Progress.Node,
199input_prog_node: std.Progress.Node,
200
201const Error = link.Error || error{MappedFileIo};
202
203const Node = union(enum) {
204 /// Only used when emitting a static library.
205 ///
206 /// Contains a header node which is an `.archive_header`.
207 ///
208 /// Contains the following footer nodes:
209 /// * One `.archive_input_member` for each external input in the archive
210 /// * One `.archive_elf_member_header` containing the `ar_hdr` for the ZCU
211 /// * One `.elf` containing the ZCU's actual ELF object
212 ///
213 /// Padding between the headers and footers is absorbed into the "//" member (whose actual
214 /// content is in the `.archive_header` node).
215 archive,
216 /// Only used when emitting a static library.
217 ///
218 /// Contains the archive magic (`ARMAG`), as well as the `ar_hdr` and content for the long file
219 /// name string table member ("//").
220 archive_header,
221 /// Only used when emitting a static library.
222 ///
223 /// Contains the `ar_hdr` and content for one non-ZCU archive member (external link input). Also
224 /// includes the single byte '\n' padding at the end of this archive member, if necessary.
225 archive_input_member: InputIndex,
226 /// Only used when emitting a static library.
227 ///
228 /// Contains the `ar_hdr` for the `.elf` node.
229 archive_elf_member_header,
230
231 elf,
232 ehdr,
233 shdr,
234 segment: u32,
235 /// The section '.plt' may contain relocations via `elf.plt_first_symbol_reloc`.
236 section: Section.Index,
237 /// May contain relocations.
238 input_section: InputSection.Index,
239 /// Value is the name of a global which has an entry in `elf.copied_globals`, so, a global for
240 /// which we have emitted a copy relocation.
241 ///
242 /// TODO it would be better to emit these into `.bss` or `.bss.rel.ro`, once we support those.
243 ///
244 /// TODO: currently, the `elf.copied_globals` entry may not be there---this case exists because
245 /// `MappedFile` does not (yet?) support deleting nodes. See logic in `setGlobalSymbolValue`.
246 copied_global: String(.strtab),
247 /// May contain relocations.
248 nav: NavMapIndex,
249 /// May contain relocations.
250 uav: UavMapIndex,
251 /// May contain relocations.
252 lazy_code: LazyMapRef.Index(.code),
253 /// May contain relocations.
254 lazy_const_data: LazyMapRef.Index(.const_data),
255
256 pub const InputIndex = enum(u32) {
257 _,
258
259 pub fn path(ii: InputIndex, elf: *const Elf) std.Build.Cache.Path {
260 return elf.inputs.items[@backingInt(ii)].path;
261 }
262
263 pub fn member(ii: InputIndex, elf: *const Elf) ?[]const u8 {
264 return elf.inputs.items[@backingInt(ii)].member;
265 }
266
267 pub fn node(ii: InputIndex, elf: *const Elf) MappedFile.Node.Index {
268 return elf.inputs.items[@backingInt(ii)].extra.node;
269 }
270
271 pub fn fileSymbol(ii: InputIndex, elf: *const Elf) Symbol.LocalIndex {
272 return elf.inputs.items[@backingInt(ii)].extra.file_symbol;
273 }
274
275 pub fn localSymbolRange(ii: InputIndex, elf: *Elf) [2]Symbol.LocalIndex {
276 if (@backingInt(ii) + 1 < elf.inputs.items.len) {
277 const next_ii: InputIndex = @fromBackingInt(@backingInt(ii) + 1);
278 return .{ ii.fileSymbol(elf), next_ii.fileSymbol(elf) };
279 } else {
280 const local_symbols_len = switch (elf.shdrPtr(.symtab)) {
281 inline else => |shdr| elf.targetLoad(&shdr.info),
282 };
283 return .{ ii.fileSymbol(elf), @fromBackingInt(local_symbols_len) };
284 }
285 }
286 };
287
288 pub const NavMapIndex = enum(u32) {
289 _,
290
291 pub fn navIndex(nmi: NavMapIndex, elf: *const Elf) InternPool.Nav.Index {
292 return elf.navs.keys()[@backingInt(nmi)];
293 }
294
295 pub fn symbol(nmi: NavMapIndex, elf: *const Elf) Symbol.LocalIndex {
296 return elf.navs.values()[@backingInt(nmi)].lsi;
297 }
298
299 fn firstSymbolReloc(nmi: NavMapIndex, elf: *const Elf) SymbolReloc.Index {
300 return elf.navs.values()[@backingInt(nmi)].first_symbol_reloc;
301 }
302 fn firstGotReloc(nmi: NavMapIndex, elf: *const Elf) GotReloc.Index {
303 return elf.navs.values()[@backingInt(nmi)].first_got_reloc;
304 }
305 };
306
307 pub const UavMapIndex = enum(u32) {
308 _,
309
310 pub fn uavValue(umi: UavMapIndex, elf: *const Elf) InternPool.Index {
311 return elf.uavs.keys()[@backingInt(umi)];
312 }
313
314 pub fn symbol(umi: UavMapIndex, elf: *const Elf) Symbol.LocalIndex {
315 return elf.uavs.values()[@backingInt(umi)].lsi;
316 }
317
318 fn firstSymbolReloc(umi: UavMapIndex, elf: *const Elf) SymbolReloc.Index {
319 return elf.uavs.values()[@backingInt(umi)].first_symbol_reloc;
320 }
321 fn firstGotReloc(umi: UavMapIndex, elf: *const Elf) GotReloc.Index {
322 _ = umi;
323 _ = elf;
324 return .none;
325 }
326 };
327
328 pub const LazyMapRef = struct {
329 kind: link.File.LazySymbol.Kind,
330 index: u32,
331
332 pub fn Index(comptime kind: link.File.LazySymbol.Kind) type {
333 return enum(u32) {
334 _,
335
336 pub fn ref(lmi: @This()) LazyMapRef {
337 return .{ .kind = kind, .index = @backingInt(lmi) };
338 }
339
340 pub fn lazySymbol(lmi: @This(), elf: *const Elf) link.File.LazySymbol {
341 return lmi.ref().lazySymbol(elf);
342 }
343
344 pub fn symbol(lmi: @This(), elf: *const Elf) Symbol.LocalIndex {
345 return lmi.ref().symbol(elf);
346 }
347
348 fn firstSymbolReloc(lmi: @This(), elf: *const Elf) SymbolReloc.Index {
349 return elf.lazy.getPtrConst(kind).map.values()[@backingInt(lmi)].first_symbol_reloc;
350 }
351 fn firstGotReloc(lmi: @This(), elf: *const Elf) GotReloc.Index {
352 return elf.lazy.getPtrConst(kind).map.values()[@backingInt(lmi)].first_got_reloc;
353 }
354 };
355 }
356
357 pub fn lazySymbol(lmr: LazyMapRef, elf: *const Elf) link.File.LazySymbol {
358 return .{ .kind = lmr.kind, .ty = elf.lazy.getPtrConst(lmr.kind).map.keys()[lmr.index] };
359 }
360
361 pub fn symbol(lmr: LazyMapRef, elf: *const Elf) Symbol.LocalIndex {
362 return elf.lazy.getPtrConst(lmr.kind).map.values()[lmr.index].lsi;
363 }
364 };
365
366 pub const Known = struct {
367 elf: MappedFile.Node.Index,
368 ehdr: MappedFile.Node.Index,
369 shdr: MappedFile.Node.Index,
370 rodata: MappedFile.Node.Index,
371 phdr: MappedFile.Node.Index,
372 text: MappedFile.Node.Index,
373 data: MappedFile.Node.Index,
374 data_rel_ro: MappedFile.Node.Index,
375 tls: MappedFile.Node.Index.Optional,
376 };
377
378 comptime {
379 if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Node) == 8);
380 }
381
382 /// In this linker implementation, `link.File.AtomId` is a type-erased `MappedFile.Node.Index`.
383 fn toAtom(ni: MappedFile.Node.Index) link.File.AtomId {
384 return @fromBackingInt(@backingInt(ni));
385 }
386 /// In this linker implementation, `link.File.AtomId` is a type-erased `MappedFile.Node.Index`.
387 fn fromAtom(atom: link.File.AtomId) MappedFile.Node.Index {
388 return @fromBackingInt(@backingInt(atom));
389 }
390};
391
392const InputSection = struct {
393 input: Node.InputIndex,
394 file_location: MappedFile.Node.FileLocation,
395 vaddr: u64,
396 /// The node corresponding to this input section.
397 node: MappedFile.Node.Index,
398 /// The start index of the contiguous sequence of symbol relocations in this input section.
399 first_symbol_reloc: SymbolReloc.Index,
400 /// The start index of the contiguous sequence of GOT relocations in this input section.
401 first_got_reloc: GotReloc.Index,
402
403 const Index = enum(u32) {
404 _,
405
406 fn ptr(isi: InputSection.Index, elf: *Elf) *InputSection {
407 return &elf.input_sections.items[@backingInt(isi)];
408 }
409
410 fn ptrConst(isi: InputSection.Index, elf: *const Elf) *const InputSection {
411 return &elf.input_sections.items[@backingInt(isi)];
412 }
413
414 fn input(isi: InputSection.Index, elf: *const Elf) Node.InputIndex {
415 return isi.ptrConst(elf).input;
416 }
417
418 fn fileLocation(isi: InputSection.Index, elf: *const Elf) MappedFile.Node.FileLocation {
419 return isi.ptrConst(elf).file_location;
420 }
421
422 fn node(isi: InputSection.Index, elf: *const Elf) MappedFile.Node.Index {
423 return isi.ptrConst(elf).node;
424 }
425 };
426};
427
428const Archive = struct {
429 ni: MappedFile.Node.Index,
430 header_ni: MappedFile.Node.Index,
431 elf_member_header_ni: MappedFile.Node.Index,
432
433 elf_member_too_big: bool,
434 strtab_member_too_big: bool,
435};
436
437const Section = struct {
438 /// The node corresponding to this section.
439 ni: MappedFile.Node.Index,
440 /// A symbol which is exactly at the start of this section.
441 ///
442 /// If the section does not have flag `std.elf.SHF.ALLOC`, this is `.null`.
443 lsi: Symbol.LocalIndex,
444 rela: union {
445 /// This field is active if and only if this section is *not* a `SHT_RELA` section.
446 ///
447 /// This field's value refers to this section's corresponding relocation section, if it
448 /// currently has one. If this section does not currently have a relocation section, the
449 /// value is `.UNDEF`.
450 ///
451 /// This field is only ever non-`.UNDEF` when emitting a relocatable (`ET_REL`). While there
452 /// are also output relocations in DSOs, they are all placed in the `.rela.dyn`
453 /// (`elf.shdnx.rela_dyn`) and `.rela.plt` (`elf.shndx.rela_plt`) sections, rather than
454 /// having separate relocation sections for each section.
455 shndx: Section.Index,
456
457 /// This field is active if and only if this section *is* a `SHT_RELA` section.
458 ///
459 /// This is the head of a single-linked list of free `ElfN.Rela` entries in this section.
460 /// Entries in this list have `info.type` set to `R_*_NONE`, have `info.sym` set to 0, and
461 /// have `offset` set to `@enumFromInt(next)` where `next` is `RelaIndex.Optional`. Also,
462 /// `addend` is set to the length of the list starting from this point; so the last node in
463 /// the list has `addend = 1`, the one before it has `addend = 2`, etc. This is so that the
464 /// head node always contains the current length of the list.
465 ///
466 /// It would be okay to store these values (in the `offset` and `addend` fields) in the
467 /// compiler's host endianness, because they will never be read by other tooling. However,
468 /// we nonetheless use target endianness, because using host endianness would introduce an
469 /// unnecessary dependency of the output binary on the compiler's host architecture.
470 free_head: RelaIndex.Optional,
471 },
472
473 const RelaIndex = enum(u32) {
474 none,
475 _,
476
477 const Optional = enum(u32) {
478 none = std.math.maxInt(u32),
479 _,
480
481 fn unwrap(opt: RelaIndex.Optional) ?RelaIndex {
482 return switch (opt) {
483 .none => null,
484 _ => @fromBackingInt(@backingInt(opt)),
485 };
486 }
487 };
488
489 fn toOptional(i: RelaIndex) RelaIndex.Optional {
490 return @fromBackingInt(@backingInt(i));
491 }
492 };
493
494 pub const Index = enum(Tag) {
495 UNDEF = std.elf.SHN_UNDEF,
496 LIVEPATCH = reserve(std.elf.SHN_LIVEPATCH),
497 ABS = reserve(std.elf.SHN_ABS),
498 COMMON = reserve(std.elf.SHN_COMMON),
499
500 symtab = 1,
501 shstrtab,
502 strtab,
503 rodata,
504 text,
505 data,
506 data_rel_ro,
507
508 _,
509
510 pub const Tag = u32;
511
512 pub const LORESERVE: Index = .fromSection(std.elf.SHN_LORESERVE);
513 pub const HIRESERVE: Index = .fromSection(std.elf.SHN_HIRESERVE);
514 comptime {
515 assert(@backingInt(HIRESERVE) == std.math.maxInt(Tag));
516 }
517
518 fn reserve(sec: std.elf.Section) Tag {
519 assert(sec >= std.elf.SHN_LORESERVE and sec <= std.elf.SHN_HIRESERVE);
520 return @as(Tag, std.math.maxInt(Tag) - std.elf.SHN_HIRESERVE) + sec;
521 }
522
523 pub fn fromSection(sec: std.elf.Section) Index {
524 return switch (sec) {
525 std.elf.SHN_UNDEF...std.elf.SHN_LORESERVE - 1 => @fromBackingInt(sec),
526 std.elf.SHN_LORESERVE...std.elf.SHN_HIRESERVE => @fromBackingInt(reserve(sec)),
527 };
528 }
529 pub fn toSection(s: Index) ?std.elf.Section {
530 return switch (@backingInt(s)) {
531 std.elf.SHN_UNDEF...std.elf.SHN_LORESERVE - 1 => |sec| @intCast(sec),
532 std.elf.SHN_LORESERVE...reserve(std.elf.SHN_LORESERVE) - 1 => null,
533 reserve(std.elf.SHN_LORESERVE)...reserve(std.elf.SHN_HIRESERVE) => |sec| @intCast(
534 sec - reserve(std.elf.SHN_LORESERVE) + std.elf.SHN_LORESERVE,
535 ),
536 };
537 }
538
539 fn get(s: Index, elf: *Elf) *Section {
540 return &elf.shdrs.items[@backingInt(s) - 1]; // overflow means you tried to get the `.UNDEF` section
541 }
542
543 fn name(s: Index, elf: *Elf) String(.shstrtab) {
544 return switch (elf.shdrPtr(s)) {
545 inline else => |shdr| @fromBackingInt(elf.targetLoad(&shdr.name)),
546 };
547 }
548
549 fn vaddr(s: Index, elf: *Elf) u64 {
550 return switch (elf.shdrPtr(s)) {
551 inline else => |shdr| elf.targetLoad(&shdr.addr),
552 };
553 }
554
555 fn size(s: Index, elf: *Elf) u64 {
556 return switch (elf.shdrPtr(s)) {
557 inline else => |shdr| elf.targetLoad(&shdr.size),
558 };
559 }
560
561 fn flags(s: Index, elf: *Elf) std.elf.SHF {
562 return switch (elf.shdrPtr(s)) {
563 inline else => |shdr| elf.targetLoad(&shdr.flags).shf,
564 };
565 }
566
567 fn rename(shndx: Index, elf: *Elf, new_name: []const u8) Error!void {
568 const shstrtab_entry = try elf.string(.shstrtab, new_name);
569 switch (elf.shdrPtr(shndx)) {
570 inline else => |shdr| elf.targetStore(&shdr.name, @backingInt(shstrtab_entry)),
571 }
572 }
573
574 fn ensureAligned(shndx: Index, elf: *Elf, min_align: Alignment) Error!void {
575 switch (elf.shdrPtr(shndx)) {
576 inline else => |shdr| {
577 if (elf.targetLoad(&shdr.addralign) >= min_align.toByteUnits()) {
578 return; // already aligned
579 }
580 elf.targetStore(&shdr.addralign, @intCast(min_align.toByteUnits()));
581 },
582 }
583 const ni = shndx.get(elf).ni;
584 if (min_align.compare(.gt, ni.alignment(&elf.mf))) {
585 try ni.realign(&elf.mf, elf.base.comp.gpa, min_align);
586 }
587 switch (elf.getNode(ni.parent(&elf.mf).unwrap().?)) {
588 .elf => {},
589 .segment => |phndx| try elf.ensureSegmentAligned(phndx, min_align),
590 else => unreachable,
591 }
592 }
593
594 /// Asserts that `rela_shndx` is a `SHT_RELA` section and ensures that its node has enough
595 /// unused space to hold `n` additional `ElfN.Rela` entries.
596 fn relaEnsureAdditionalCapacity(rela_shndx: Index, elf: *Elf, n: usize) Error!void {
597 const node = rela_shndx.get(elf).ni;
598 const need_size: u64 = switch (elf.shdrPtr(rela_shndx)) {
599 inline else => |shdr, class| need_size: {
600 assert(elf.targetLoad(&shdr.type) == .RELA);
601 const cur_size = elf.targetLoad(&shdr.size);
602 const ent_size = @sizeOf(class.ElfN().Rela);
603 assert(elf.targetLoad(&shdr.entsize) == ent_size);
604 const free_len: u32 = free_len: {
605 const opt_free_head = rela_shndx.get(elf).rela.free_head;
606 const free_head = opt_free_head.unwrap() orelse break :free_len 0;
607 const relas: []const class.ElfN().Rela = @ptrCast(@alignCast(
608 node.slice(&elf.mf)[0..@intCast(cur_size)],
609 ));
610 const free_len = elf.targetLoad(&relas[@backingInt(free_head)].addend);
611 assert(free_len > 0);
612 break :free_len @intCast(free_len);
613 };
614 const need_additional = n -| free_len;
615 break :need_size cur_size + need_additional * ent_size;
616 },
617 };
618 try node.ensureMinimumSize(&elf.mf, elf.base.comp.gpa, need_size);
619 }
620
621 /// Asserts that `rela_shndx` is a `SHT_RELA` section and deletes the `ElfN.Rela` entry at
622 /// the given `index` in it. The entry is added to the free-list for reuse later. Asserts
623 /// that the relocation entry at `index` is not already free.
624 fn relaDeleteOne(rela_shndx: Index, elf: *Elf, index: RelaIndex) void {
625 switch (elf.shdrPtr(rela_shndx)) {
626 inline else => |shdr, class| {
627 assert(elf.targetLoad(&shdr.type) == .RELA);
628 assert(elf.targetLoad(&shdr.entsize) == @sizeOf(class.ElfN().Rela));
629 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
630 rela_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(elf.targetLoad(&shdr.size))],
631 ));
632 const opt_free_head = rela_shndx.get(elf).rela.free_head;
633 const old_free_len: u32 = free_len: {
634 const free_head = opt_free_head.unwrap() orelse break :free_len 0;
635 const free_len = elf.targetLoad(&relas[@backingInt(free_head)].addend);
636 assert(free_len > 0);
637 break :free_len @intCast(free_len);
638 };
639 const none_reloc_type = MachineRelocType.none(elf).unwrap(elf);
640 {
641 const old_type = elf.targetLoad(&relas[@backingInt(index)].info).type;
642 assert(old_type != none_reloc_type); // bug: `index` is already in the free-list
643 }
644 relas[@backingInt(index)] = .{
645 .offset = @backingInt(opt_free_head), // next
646 .info = .{
647 .type = @intCast(none_reloc_type),
648 .sym = 0,
649 },
650 .addend = @intCast(old_free_len + 1), // list length
651 };
652 if (elf.targetEndian() != native_endian) {
653 std.mem.byteSwapAllFields(class.ElfN().Rela, &relas[@backingInt(index)]);
654 }
655 },
656 }
657 rela_shndx.get(elf).rela.free_head = index.toOptional();
658 }
659
660 /// Asserts that `rela_shndx` is a `SHT_RELA` section and adds a new `ElfN.Rela` entry to it
661 /// with the given field values. Returns the index of the populated entry. Asserts that
662 /// capacity for this operation was already guaranteed using `relaEnsureAdditionalCapacity`.
663 fn relaAddOneAssumeCapacity(rela_shndx: Index, elf: *Elf, opts: struct {
664 type: MachineRelocType,
665 offset: u64,
666 /// This is a raw `u32` because whether this is an index into `.symtab` (`Symbol.Index`)
667 /// or an index into `.dynsym` is contextual.
668 raw_sym_index: u32,
669 addend: i64,
670 }) RelaIndex {
671 switch (elf.shdrPtr(rela_shndx)) {
672 inline else => |shdr, class| {
673 assert(elf.targetLoad(&shdr.type) == .RELA);
674 const ent_size = @sizeOf(class.ElfN().Rela);
675 assert(elf.targetLoad(&shdr.entsize) == ent_size);
676 const new_index: RelaIndex = if (rela_shndx.get(elf).rela.free_head.unwrap()) |free_head| new_index: {
677 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
678 rela_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(elf.targetLoad(&shdr.size))],
679 ));
680 const next: RelaIndex.Optional = @fromBackingInt(@intCast(elf.targetLoad(
681 &relas[@backingInt(free_head)].offset,
682 )));
683 rela_shndx.get(elf).rela.free_head = next;
684
685 const old_free_len: u32 = @intCast(
686 elf.targetLoad(&relas[@backingInt(free_head)].addend),
687 );
688 const new_free_len: u32 = if (next.unwrap()) |i| @intCast(
689 elf.targetLoad(&relas[@backingInt(i)].addend),
690 ) else 0;
691 assert(new_free_len == old_free_len - 1);
692
693 break :new_index free_head;
694 } else new_index: {
695 const old_size = elf.targetLoad(&shdr.size);
696 const new_size = old_size + ent_size;
697 elf.targetStore(&shdr.size, new_size);
698 break :new_index @fromBackingInt(@intCast(@divExact(old_size, ent_size)));
699 };
700 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
701 rela_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(elf.targetLoad(&shdr.size))],
702 ));
703 relas[@backingInt(new_index)] = .{
704 .offset = @intCast(opts.offset),
705 .info = .{
706 .type = @intCast(opts.type.unwrap(elf)),
707 .sym = @intCast(opts.raw_sym_index),
708 },
709 .addend = @intCast(opts.addend),
710 };
711 if (elf.targetEndian() != native_endian) {
712 std.mem.byteSwapAllFields(class.ElfN().Rela, &relas[@backingInt(new_index)]);
713 }
714 return new_index;
715 },
716 }
717 }
718
719 /// Asserts that `rela_shndx` is a `SHT_RELA` section and updates the `info.sym` field of
720 /// the `ElfN.Rela` entry at the given index. As with `relaAddOneAssumeCapacity`, the symbol
721 /// index is a raw `u32`, because it may be an index into `.symtab` or an index into
722 /// `.dynsym`. Asserts that `index` is not in the free-list (i.e. is not deleted).
723 fn relaUpdateSym(rela_shndx: Index, elf: *Elf, index: RelaIndex, raw_sym_index: u32) void {
724 switch (elf.shdrPtr(rela_shndx)) {
725 inline else => |shdr, class| {
726 assert(elf.targetLoad(&shdr.type) == .RELA);
727 assert(elf.targetLoad(&shdr.entsize) == @sizeOf(class.ElfN().Rela));
728 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
729 rela_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(elf.targetLoad(&shdr.size))],
730 ));
731 const rela_info = elf.targetLoad(&relas[@backingInt(index)].info);
732 {
733 const none_reloc_type = MachineRelocType.none(elf).unwrap(elf);
734 assert(rela_info.type != none_reloc_type); // bug: `index` is in the free-list
735 }
736 elf.targetStore(&relas[@backingInt(index)].info, .{
737 .type = rela_info.type,
738 .sym = @intCast(raw_sym_index),
739 });
740 },
741 }
742 }
743
744 /// Asserts that `rela_shndx` is a `SHT_RELA` section and updates the `offset` field of the
745 /// `ElfN.Rela` entry at the given index. Asserts that `index` is not in the free-list (i.e.
746 /// it is not deleted).
747 fn relaSetOffset(rela_shndx: Index, elf: *Elf, index: RelaIndex, new_offset: u64) void {
748 switch (elf.shdrPtr(rela_shndx)) {
749 inline else => |shdr, class| {
750 assert(elf.targetLoad(&shdr.type) == .RELA);
751 assert(elf.targetLoad(&shdr.entsize) == @sizeOf(class.ElfN().Rela));
752 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
753 rela_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(elf.targetLoad(&shdr.size))],
754 ));
755 {
756 const rela_info = elf.targetLoad(&relas[@backingInt(index)].info);
757 const none_reloc_type = MachineRelocType.none(elf).unwrap(elf);
758 assert(rela_info.type != none_reloc_type); // bug: `index` is in the free-list
759 }
760 elf.targetStore(&relas[@backingInt(index)].offset, @intCast(new_offset));
761 },
762 }
763 }
764
765 /// Asserts that `rela_shndx` is a `SHT_RELA` section and updates the `offset` field of the
766 /// `ElfN.Rela` entry at the given index, by subtracting `old_base` and adding `new_base`.
767 /// Asserts that `index` is not in the free-list (i.e. it is not deleted).
768 fn relaAdjustOffset(rela_shndx: Index, elf: *Elf, index: RelaIndex, old_base: u64, new_base: u64) void {
769 switch (elf.shdrPtr(rela_shndx)) {
770 inline else => |shdr, class| {
771 assert(elf.targetLoad(&shdr.type) == .RELA);
772 assert(elf.targetLoad(&shdr.entsize) == @sizeOf(class.ElfN().Rela));
773 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
774 rela_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(elf.targetLoad(&shdr.size))],
775 ));
776 {
777 const rela_info = elf.targetLoad(&relas[@backingInt(index)].info);
778 const none_reloc_type = MachineRelocType.none(elf).unwrap(elf);
779 assert(rela_info.type != none_reloc_type); // bug: `index` is in the free-list
780 }
781 const old_offset = elf.targetLoad(&relas[@backingInt(index)].offset);
782 elf.targetStore(&relas[@backingInt(index)].offset, @intCast(
783 old_offset - old_base + new_base,
784 ));
785 },
786 }
787 }
788
789 /// Asserts that `rela_shndx` is a `SHT_RELA` section, and asserts that `index` refers to an
790 /// `R_*_RELATIVE` relocation inside of it; then, updates that relocation's addend (which is
791 /// an address in this DSO without the runtime load offset applied) to the given value.
792 fn relaSetRelativeOffset(rela_shndx: Index, elf: *Elf, index: RelaIndex, new_addend: u64) void {
793 switch (elf.shdrPtr(rela_shndx)) {
794 inline else => |shdr, class| {
795 assert(elf.targetLoad(&shdr.type) == .RELA);
796 assert(elf.targetLoad(&shdr.entsize) == @sizeOf(class.ElfN().Rela));
797 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
798 rela_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(elf.targetLoad(&shdr.size))],
799 ));
800 {
801 const rela_info = elf.targetLoad(&relas[@backingInt(index)].info);
802 const none_reloc_type = MachineRelocType.none(elf).unwrap(elf);
803 assert(rela_info.type != none_reloc_type); // bug: `index` is in the free-list
804 }
805 const unsigned: class.ElfN().Addr = @intCast(new_addend);
806 elf.targetStore(&relas[@backingInt(index)].addend, @bitCast(unsigned));
807 },
808 }
809 }
810 };
811};
812
813/// Identifies a single entry in the GOT.
814const GotKey = union(enum) {
815 /// The entry is a reserved word, initialized to zero. `initHeaders` will add as many of these
816 /// as the target machine ABI requires.
817 ///
818 /// This `u32` value exists to allow reserving multiple words with distinct keys.
819 reserved: u32,
820
821 /// Value is the address of the given symbol.
822 symbol: Symbol.Id,
823
824 /// Value is the signed offset of the given symbol from the TLS pointer.
825 tpoff: Symbol.Id,
826
827 /// Value is the TLS module ID of the DSO we are creating.
828 ///
829 /// Used for the first of the two GOT entries generated by a TLSLD relocation.
830 tlsld0,
831 /// Value is always 0.
832 ///
833 /// Used for the second of the two GOT entries generated by a TLSLD relocation.
834 tlsld1,
835
836 /// Value is the TLS module ID for the given STT_TLS symbol.
837 ///
838 /// Used for the first of the two GOT entries generated by a TLSGD relocation.
839 tlsgd0: Symbol.Id,
840 /// Value is the offset of the given STT_TLS symbol from the base of the per-module TLS area.
841 ///
842 /// Used for the second of the two GOT entries generated by a TLSGD relocation.
843 tlsgd1: Symbol.Id,
844};
845
846/// A relocation targeting a particular GOT entry.
847const GotReloc = struct {
848 /// The node containing this relocation. Possible values are:
849 /// * An input section
850 /// * A section
851 /// * A NAV, UAV, or lazy code/data
852 /// * `.none`, if this relocation was deleted (in which case it should be ignored)
853 node: MappedFile.Node.Index.Optional,
854 /// The offset of the relocation inside of `node`.
855 offset: u64,
856 target: GotKey,
857 addend: i64,
858 type: GotReloc.Type,
859 result: enum(u8) { ok, overflowed, misaligned },
860
861 /// `GotReloc.Type` has the same structure as `SymbolReloc.Type`, just with different `Target`
862 /// and `Special` enums---consult doc comments on `SymbolReloc.Type` for an overview.
863 const Type = packed struct(u16) {
864 fn simple(target: Target, action: Simple) GotReloc.Type {
865 assert(target != .special);
866 return .{ .target = target, .action = .{ .simple = action } };
867 }
868
869 fn special(s: Special) GotReloc.Type {
870 return .{ .target = .special, .action = .{ .special = s } };
871 }
872
873 target: Target,
874 action: packed union {
875 simple: Simple,
876 special: Special,
877 },
878
879 /// Like `SymbolReloc.Target`, but for GOT relocations. There are fewer tags because there
880 /// are fewer different kinds of GOT relocation.
881 const Target = enum(u3) {
882 /// This is a "special" relocation whose specific type is in the `action.special` field.
883 special,
884
885 /// Absolute address of the GOT entry.
886 abs,
887 /// Offset from the relocation itself to the GOT entry ("PC-relative").
888 rel,
889 /// Offset from the base of the GOT to the GOT entry.
890 offset,
891 };
892
893 const Simple = SymbolReloc.Type.Simple;
894
895 /// Like `SymbolReloc.Special`, but for GOT relocations.
896 const Special = enum(u13) {
897 larch_pcala_hi20,
898 larch_pcala64_lo20,
899 larch_pcala64_hi12,
900
901 sparc_op_lox10,
902 sparc_op_hix22,
903
904 fn applyInner(
905 s: Special,
906 elf: *Elf,
907 got_vaddr: u64,
908 got_offset: u64,
909 addend: u64,
910 dest_vaddr: u64,
911 dest_slice: []u8,
912 ) error{ RelocationMisaligned, RelocationOverflow }!void {
913 switch (s) {
914 .larch_pcala_hi20 => {
915 const val = got_vaddr +% got_offset +% addend;
916 const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]);
917 elf.targetStore(inst, .{
918 .b0_4 = elf.targetLoad(inst).b0_4,
919 .j20 = link.loongarch.pcalaHi20(val, dest_vaddr),
920 .b25_31 = elf.targetLoad(inst).b25_31,
921 });
922 },
923 .larch_pcala64_lo20 => {
924 const val = got_vaddr +% got_offset +% addend;
925 const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]);
926 elf.targetStore(inst, .{
927 .b0_4 = elf.targetLoad(inst).b0_4,
928 .j20 = link.loongarch.pcala64Lo20(val, dest_vaddr),
929 .b25_31 = elf.targetLoad(inst).b25_31,
930 });
931 },
932 .larch_pcala64_hi12 => {
933 const val = got_vaddr +% got_offset +% addend;
934 const inst: *align(1) link.loongarch.K12 = @ptrCast(dest_slice[0..4]);
935 elf.targetStore(inst, .{
936 .b0_9 = elf.targetLoad(inst).b0_9,
937 .k12 = link.loongarch.pcala64Hi12(val, dest_vaddr),
938 .b22_31 = elf.targetLoad(inst).b22_31,
939 });
940 },
941 .sparc_op_lox10 => {
942 const dest_ptr: *align(1) packed struct(u32) {
943 imm13: u13,
944 b13_31: u19,
945 } = @ptrCast(dest_slice);
946 elf.targetStore(dest_ptr, .{
947 .imm13 = @as(u10, @truncate(got_offset)),
948 .b13_31 = elf.targetLoad(dest_ptr).b13_31,
949 });
950 },
951 .sparc_op_hix22 => {
952 const dest_ptr: *align(1) packed struct(u32) {
953 imm22: u22,
954 b22_31: u10,
955 } = @ptrCast(dest_slice);
956 elf.targetStore(dest_ptr, .{
957 .imm22 = @truncate(got_offset >> 10),
958 .b22_31 = elf.targetLoad(dest_ptr).b22_31,
959 });
960 },
961 }
962 }
963 };
964 };
965
966 const Index = enum(u32) {
967 none = std.math.maxInt(u32),
968 _,
969
970 fn get(index: GotReloc.Index, elf: *Elf) *GotReloc {
971 return &elf.got_relocs.items[@backingInt(index)];
972 }
973 };
974
975 fn apply(reloc: *GotReloc, elf: *Elf) void {
976 assert(elf.ehdrType() != .REL);
977 const node = reloc.node.unwrap() orelse {
978 return; // deleted
979 };
980 if (node.hasMoved(&elf.mf) or elf.shndx.got.get(elf).ni.hasMoved(&elf.mf)) {
981 // There's no point applying the relocation now, because it will be re-applied by
982 // `flushMoved` at some point anyway.
983 return;
984 }
985 switch (reloc.result) {
986 .ok => {},
987 .overflowed => elf.overflowed_reloc_count -= 1,
988 .misaligned => elf.misaligned_reloc_count -= 1,
989 }
990 if (reloc.applyInner(elf)) {
991 @branchHint(.likely);
992 reloc.result = .ok;
993 } else |err| switch (err) {
994 error.RelocationOverflow => {
995 reloc.result = .overflowed;
996 elf.overflowed_reloc_count += 1;
997 },
998 error.RelocationMisaligned => {
999 reloc.result = .misaligned;
1000 elf.misaligned_reloc_count += 1;
1001 },
1002 }
1003 }
1004 fn applyInner(reloc: *const GotReloc, elf: *Elf) error{ RelocationOverflow, RelocationMisaligned }!void {
1005 const node = reloc.node.unwrap().?;
1006 const dest_vaddr = elf.getNodeVAddr(node) + reloc.offset;
1007 const dest_slice = node.slice(&elf.mf)[@intCast(reloc.offset)..];
1008
1009 const got_vaddr = elf.shndx.got.vaddr(elf);
1010 const got_index: u64 = elf.got.getIndex(reloc.target).?;
1011 const got_offset: u64 = switch (elf.identClass()) {
1012 .NONE, _ => unreachable,
1013 inline else => |class| @sizeOf(class.ElfN().Addr) * got_index,
1014 };
1015 const addend: u64 = @bitCast(reloc.addend);
1016
1017 const target_val: u64 = switch (reloc.type.target) {
1018 .abs => got_vaddr +% got_offset +% addend,
1019 .rel => got_vaddr +% got_offset +% addend -% dest_vaddr,
1020 .offset => got_offset +% addend,
1021 .special => return reloc.type.action.special.applyInner(
1022 elf,
1023 got_vaddr,
1024 got_offset,
1025 addend,
1026 dest_vaddr,
1027 dest_slice,
1028 ),
1029 };
1030 try reloc.type.action.simple.write(target_val, dest_slice, elf.targetEndian());
1031 }
1032
1033 fn delete(reloc: *GotReloc, elf: *Elf) void {
1034 switch (reloc.result) {
1035 .ok => {},
1036 .overflowed => elf.overflowed_reloc_count -= 1,
1037 .misaligned => elf.misaligned_reloc_count -= 1,
1038 }
1039 reloc.* = .{
1040 .node = .none,
1041 .offset = undefined,
1042 .target = undefined,
1043 .addend = undefined,
1044 .type = undefined,
1045 .result = undefined,
1046 };
1047 }
1048};
1049
1050pub const MachineRelocType = union {
1051 AARCH64: std.elf.R_AARCH64,
1052 LARCH: std.elf.R_LARCH,
1053 PPC64: std.elf.R_PPC64,
1054 RISCV: std.elf.R_RISCV,
1055 SPARC: std.elf.R_SPARC,
1056 X86_64: std.elf.R_X86_64,
1057
1058 pub const Format = struct {
1059 rt: MachineRelocType,
1060 elf: *const Elf,
1061
1062 pub fn format(f: Format, w: *Io.Writer) Io.Writer.Error!void {
1063 switch (f.elf.ehdrMachine()) {
1064 .AARCH64 => try w.print("R_AARCH64_{t}", .{f.rt.AARCH64}),
1065 .LOONGARCH => try w.print("R_LARCH_{t}", .{f.rt.LARCH}),
1066 .PPC64 => try w.print("R_PPC64_{t}", .{f.rt.PPC64}),
1067 .RISCV => try w.print("R_RISCV_{t}", .{f.rt.RISCV}),
1068 .SPARCV9 => try w.print("R_SPARC_{t}", .{f.rt.SPARC}),
1069 .X86_64 => try w.print("R_X86_64_{t}", .{f.rt.X86_64}),
1070 }
1071 }
1072 };
1073
1074 pub fn fmt(rt: MachineRelocType, elf: *const Elf) Format {
1075 return .{ .rt = rt, .elf = elf };
1076 }
1077
1078 pub fn none(elf: *const Elf) MachineRelocType {
1079 return switch (elf.ehdrMachine()) {
1080 .AARCH64 => .{ .AARCH64 = .NONE },
1081 .LOONGARCH => .{ .LARCH = .NONE },
1082 .PPC64 => .{ .PPC64 = .NONE },
1083 .RISCV => .{ .RISCV = .NONE },
1084 .SPARCV9 => .{ .SPARC = .NONE },
1085 .X86_64 => .{ .X86_64 = .NONE },
1086 };
1087 }
1088 pub fn copy(elf: *const Elf) MachineRelocType {
1089 return switch (elf.ehdrMachine()) {
1090 .AARCH64 => .{ .AARCH64 = .COPY },
1091 .LOONGARCH => .{ .LARCH = .COPY },
1092 .PPC64 => .{ .PPC64 = .COPY },
1093 .RISCV => .{ .RISCV = .COPY },
1094 .SPARCV9 => .{ .SPARC = .COPY },
1095 .X86_64 => .{ .X86_64 = .COPY },
1096 };
1097 }
1098 pub fn relative(elf: *const Elf) MachineRelocType {
1099 return switch (elf.ehdrMachine()) {
1100 .AARCH64 => .{ .AARCH64 = .RELATIVE },
1101 .LOONGARCH => .{ .LARCH = .RELATIVE },
1102 .PPC64 => .{ .PPC64 = .RELATIVE },
1103 .RISCV => .{ .RISCV = .RELATIVE },
1104 .SPARCV9 => .{ .SPARC = .RELATIVE },
1105 .X86_64 => .{ .X86_64 = .RELATIVE },
1106 };
1107 }
1108 pub fn jumpSlot(elf: *const Elf) MachineRelocType {
1109 return switch (elf.ehdrMachine()) {
1110 .AARCH64 => .{ .AARCH64 = .JUMP_SLOT },
1111 .LOONGARCH => .{ .LARCH = .JUMP_SLOT },
1112 .PPC64 => .{ .PPC64 = .JMP_SLOT },
1113 .RISCV => .{ .RISCV = .JUMP_SLOT },
1114 .SPARCV9 => .{ .SPARC = .JMP_SLOT },
1115 .X86_64 => .{ .X86_64 = .JUMP_SLOT },
1116 };
1117 }
1118 pub fn globDat(elf: *const Elf) MachineRelocType {
1119 return switch (elf.ehdrMachine()) {
1120 .AARCH64 => .{ .AARCH64 = .GLOB_DAT },
1121 .LOONGARCH => .{ .LARCH = if (elf.identClass() == .@"64") .@"64" else .@"32" },
1122 .PPC64 => .{ .PPC64 = .GLOB_DAT },
1123 .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .@"64" else .@"32" },
1124 .SPARCV9 => .{ .SPARC = .GLOB_DAT },
1125 .X86_64 => .{ .X86_64 = .GLOB_DAT },
1126 };
1127 }
1128 pub fn dtpMod(elf: *const Elf) MachineRelocType {
1129 return switch (elf.ehdrMachine()) {
1130 .AARCH64 => .{ .AARCH64 = if (elf.identClass() == .@"64") .TLS_DTPMOD else .P32_TLS_DTPMOD },
1131 .LOONGARCH => .{ .LARCH = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 },
1132 .PPC64 => .{ .PPC64 = .DTPMOD64 },
1133 .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 },
1134 .SPARCV9 => .{ .SPARC = if (elf.identClass() == .@"64") .TLS_DTPMOD64 else .TLS_DTPMOD32 },
1135 .X86_64 => .{ .X86_64 = .DTPMOD64 },
1136 };
1137 }
1138 pub fn dtpOff(elf: *const Elf) MachineRelocType {
1139 return switch (elf.ehdrMachine()) {
1140 .AARCH64 => .{ .AARCH64 = if (elf.identClass() == .@"64") .TLS_DTPREL else .P32_TLS_DTPREL },
1141 .LOONGARCH => .{ .LARCH = if (elf.identClass() == .@"64") .TLS_DTPREL64 else .TLS_DTPREL32 },
1142 .PPC64 => .{ .PPC64 = .DTPREL64 },
1143 .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .TLS_DTPREL64 else .TLS_DTPREL32 },
1144 .SPARCV9 => .{ .SPARC = if (elf.identClass() == .@"64") .TLS_DTPOFF64 else .TLS_DTPOFF32 },
1145 .X86_64 => .{ .X86_64 = .DTPOFF64 },
1146 };
1147 }
1148 pub fn tpOff(elf: *const Elf) MachineRelocType {
1149 return switch (elf.ehdrMachine()) {
1150 .AARCH64 => .{ .AARCH64 = if (elf.identClass() == .@"64") .TLS_TPREL else .P32_TLS_TPREL },
1151 .LOONGARCH => .{ .LARCH = if (elf.identClass() == .@"64") .TLS_TPREL64 else .TLS_TPREL32 },
1152 .PPC64 => .{ .PPC64 = .TPREL64 },
1153 .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .TLS_TPREL64 else .TLS_TPREL32 },
1154 .SPARCV9 => .{ .SPARC = if (elf.identClass() == .@"64") .TLS_TPOFF64 else .TLS_TPOFF32 },
1155 .X86_64 => .{ .X86_64 = .TPOFF64 },
1156 };
1157 }
1158 pub fn absAddr(elf: *const Elf) MachineRelocType {
1159 return switch (elf.ehdrMachine()) {
1160 .AARCH64 => .{ .AARCH64 = if (elf.identClass() == .@"64") .ABS64 else .P32_ABS32 },
1161 .LOONGARCH => .{ .LARCH = if (elf.identClass() == .@"64") .@"64" else .@"32" },
1162 .PPC64 => .{ .PPC64 = .ADDR64 },
1163 .RISCV => .{ .RISCV = if (elf.identClass() == .@"64") .@"64" else .@"32" },
1164 .SPARCV9 => .{ .SPARC = if (elf.identClass() == .@"64") .@"64" else .@"32" },
1165 .X86_64 => .{ .X86_64 = if (elf.identClass() == .@"64") .@"64" else .@"32" },
1166 };
1167 }
1168 pub fn size32(elf: *const Elf) ?MachineRelocType {
1169 return switch (elf.ehdrMachine()) {
1170 .AARCH64,
1171 .LOONGARCH,
1172 .PPC64,
1173 .RISCV,
1174 => null,
1175
1176 .SPARCV9 => .{ .SPARC = .SIZE32 },
1177 .X86_64 => .{ .X86_64 = .SIZE32 },
1178 };
1179 }
1180 pub fn size64(elf: *const Elf) ?MachineRelocType {
1181 return switch (elf.ehdrMachine()) {
1182 .AARCH64,
1183 .LOONGARCH,
1184 .PPC64,
1185 .RISCV,
1186 => null,
1187
1188 .SPARCV9 => .{ .SPARC = .SIZE64 },
1189 .X86_64 => .{ .X86_64 = .SIZE64 },
1190 };
1191 }
1192
1193 pub fn wrap(int: u32, elf: *const Elf) MachineRelocType {
1194 return switch (elf.ehdrMachine()) {
1195 .AARCH64 => .{ .AARCH64 = @fromBackingInt(int) },
1196 .LOONGARCH => .{ .LARCH = @fromBackingInt(int) },
1197 .PPC64 => .{ .PPC64 = @fromBackingInt(int) },
1198 .RISCV => .{ .RISCV = @fromBackingInt(int) },
1199 .SPARCV9 => .{ .SPARC = @fromBackingInt(int) },
1200 .X86_64 => .{ .X86_64 = @fromBackingInt(int) },
1201 };
1202 }
1203 pub fn unwrap(rt: MachineRelocType, elf: *const Elf) u32 {
1204 return switch (elf.ehdrMachine()) {
1205 .AARCH64 => @backingInt(rt.AARCH64),
1206 .LOONGARCH => @backingInt(rt.LARCH),
1207 .PPC64 => @backingInt(rt.PPC64),
1208 .RISCV => @backingInt(rt.RISCV),
1209 .SPARCV9 => @backingInt(rt.SPARC),
1210 .X86_64 => @backingInt(rt.X86_64),
1211 };
1212 }
1213};
1214
1215/// A relocation targeting an arbitrary symbol with a fixed addend.
1216const SymbolReloc = struct {
1217 /// The node containing this relocation. Possible values are:
1218 /// * An input section
1219 /// * A section
1220 /// * A NAV, UAV, or lazy code/data
1221 node: MappedFile.Node.Index,
1222 /// The offset of the relocation inside of `node`.
1223 offset: u64,
1224 /// A symbol used to compute the relocated value. Precise meaning depends on `@"type"`.
1225 target: Symbol.Id,
1226 /// A signed constant used to compute the relocated value. Precise meaning depends on `@"type"`.
1227 addend: i64,
1228 /// Specifies how to apply the relocation.
1229 ///
1230 /// When emitting a relocatable, this field is `undefined`.
1231 type: SymbolReloc.Type,
1232 /// Forms a linked list of all symbol relocations with the same `target`. This list exists so
1233 /// that all relocations targeting a particular symbol can be re-applied if that symbol moves.
1234 /// Doubly-linked so that relocations can be removed.
1235 next: SymbolReloc.Index,
1236 /// Back-reference in a doubly-linked list---see `next`.
1237 prev: SymbolReloc.Index,
1238 /// If this relocation has a corresponding output relocation, this is its index within the
1239 /// appropriate SHT_RELA section (see `relaSection`). If there is no output relocation
1240 /// corresponding to this relocation, this is `.none`.
1241 ///
1242 /// If we are producing a relocatable, this field is always populated, because all relocations
1243 /// are emitted as output relocations.
1244 ///
1245 /// If we are producing a DSO, this field is populated if this relocation requires a runtime
1246 /// relocation entry. The entry will be removed if we discover a definition which allows us to
1247 /// statically resolve the relocation.
1248 rela_index: Section.RelaIndex.Optional,
1249 result: enum(u8) { ok, overflowed, misaligned },
1250
1251 /// Determines the section in which this relocation will be placed if it is outstanding.
1252 ///
1253 /// When producing a relocatable (ET_REL), the relocation section is `Section.rela.shndx` for
1254 /// the section of `node`, and this function asserts that the aforementioned `rela.shndx` field
1255 /// is populated.
1256 ///
1257 /// When producing a DSO, the relocation section is always `.rela.dyn`. It is not `.rela.plt`
1258 /// because relocations in the GOTPLT are handled specially, without `SymbolReloc` entries.
1259 fn relaSection(sr: *const SymbolReloc, elf: *Elf) Section.Index {
1260 const shndx = switch (elf.ehdrType()) {
1261 .REL => elf.getNodeShndx(sr.node).get(elf).rela.shndx,
1262 .EXEC, .DYN => elf.shndx.rela_dyn,
1263 };
1264 assert(shndx != .UNDEF);
1265 return shndx;
1266 }
1267
1268 const Index = enum(u32) {
1269 none = std.math.maxInt(u32),
1270 _,
1271
1272 fn get(index: SymbolReloc.Index, elf: *Elf) *SymbolReloc {
1273 return &elf.symbol_relocs.items[@backingInt(index)];
1274 }
1275 };
1276
1277 /// Instead of using the ELF relocation enums, we have our own internal representation for
1278 /// relocation types. This representation is more compact (requiring only 16 bits), and allows
1279 /// sharing a lot of relocation handling between multiple relocs and target architectures.
1280 ///
1281 /// A relocation type can be "simple" or "special".
1282 ///
1283 /// "Simple" relocations are designed to cover the majority of cases. They can represent most
1284 /// relocations which either write 8-bit, 16-bit, 32-bit, or 64-bit integers, or which write one
1285 /// contiguous bit-field within such an integer (e.g. an instruction operand). For more details,
1286 /// see `Simple`.
1287 ///
1288 /// "Special" relocations handle anything which does not fit into the above category, such as
1289 /// relocations which write multiple sequences of bits or which need to do unusual arithmetic on
1290 /// a symbol value. The representation is simply a big enum containing all of these exceptional
1291 /// cases---see `Special`. This representation is in use when `Type.target == .special`.
1292 const Type = packed struct(u16) {
1293 /// Helper function for constructing a "simple" relocation type. This mainly exists to
1294 /// improve readability in the relocation lowering logic in `addRelocAssumeCapacity`.
1295 fn simple(target: Target, action: Simple) SymbolReloc.Type {
1296 assert(target != .special);
1297 return .{ .target = target, .action = .{ .simple = action } };
1298 }
1299
1300 /// Helper function for constructing a "special" relocation type. This mainly exists to
1301 /// improve readability in the relocation lowering logic in `addRelocAssumeCapacity`.
1302 fn special(s: Special) SymbolReloc.Type {
1303 return .{ .target = .special, .action = .{ .special = s } };
1304 }
1305
1306 /// See doc comment on `Target`.
1307 target: Target,
1308 /// If `target == .special`, the `special` field is used.
1309 ///
1310 /// Otherwise, the `.simple` field is used.
1311 action: packed union {
1312 simple: Simple,
1313 special: Special,
1314 },
1315
1316 /// If a relocation is "special", indicates that using the value `.@"special"`.
1317 ///
1318 /// Otherwise (for "simple" relocations), `Target` indicates the first step in computing the
1319 /// relocation---whether we care about the target symbol's absolute address, its PC-relative
1320 /// address, its PLT entry, etc.
1321 const Target = enum(u3) {
1322 /// This is a "special" relocation whose specific type is in the `action.special` field.
1323 special,
1324
1325 /// Absolute value of the target symbol.
1326 abs,
1327 /// Offset from the relocation itself to the target symbol ("PC-relative").
1328 rel,
1329 /// Address of the target symbol's PLT entry.
1330 ///
1331 /// If the target symbol does not have a PLT entry, equivalent to `.abs`.
1332 pltabs,
1333 /// Offset from the relocation itself to the target symbol's PLT entry ("PC-relative").
1334 ///
1335 /// If the target symbol does not have a PLT entry, equivalent to `.rel`.
1336 pltrel,
1337 /// Offset of the target TLS symbol from the base of this DSO's own TLS region.
1338 dtpoff,
1339 /// Offset of the target TLS symbol from the raw thread pointer.
1340 tpoff,
1341 /// Size of the target symbol.
1342 size,
1343 };
1344
1345 /// For a "simple" relocation, after the initial value is computed according to `Target`, a
1346 /// `Simple` value communicates how to shift, truncate, and store that value into memory.
1347 const Simple = packed struct(u13) {
1348 /// The field being written to, represented as a sequence of bits in a backing integer
1349 /// of 8, 16, 32, or 64 bits.
1350 ///
1351 /// The `.@"8"`, `.@"16"`, `.@"32"`, and `.@"64"` fields simply write to all bits of the
1352 /// backing integer; i.e. the existing value is entirely overwritten.
1353 ///
1354 /// Other fields are named like "B[H:L]", where "B" is the backing integer type, and
1355 /// "H" and "L" are the indices of the highest and lowest bits in the bit field (in
1356 /// other words, an inclusive bit range). This notation was chosen because it seems to
1357 /// be one of the more common ways that bit relocations are written in ABIs.
1358 ///
1359 /// e.g. 8[6:3] writes the relocated value to this 4-bit field in an 8-bit integer:
1360 ///
1361 /// MSB ___ ### ### ### ### ___ ___ ___ LSB
1362 /// 7 6 5 4 3 2 1 0
1363 /// bit index
1364 ///
1365 /// This enum is not intended to be able to represent every possible bit field in the
1366 /// backing integer types. Instead, to keep `SymbolReloc.Type` compact, fields are added
1367 /// to this enum only as needed. If the enum ever becomes full, some lesser-used tags
1368 /// can have their handling moved into `Special` to free up space.
1369 dest: enum(u6) {
1370 @"8",
1371 @"16",
1372 @"32",
1373 @"64",
1374
1375 @"32[4:0]",
1376 @"32[5:0]",
1377 @"32[6:0]",
1378 @"32[9:0]",
1379 @"32[10:0]",
1380 @"32[11:0]",
1381 @"32[12:0]",
1382 @"32[21:0]",
1383 @"32[21:10]",
1384 @"32[24:5]",
1385 @"32[25:10]",
1386 @"32[29:0]",
1387
1388 /// Returns `true` iff `dest` writes a full address for the target.
1389 ///
1390 /// i.e. checks for `.@"32"` on 32-bit targets; for `.@"64"` on 64-bit targets.
1391 fn isAddr(dest: @This(), elf: *const Elf) bool {
1392 return switch (elf.identClass()) {
1393 .NONE, _ => unreachable,
1394 .@"32" => dest == .@"32",
1395 .@"64" => dest == .@"64",
1396 };
1397 }
1398 },
1399
1400 /// After the relocation value is shifted (see `shift`), it is truncated to the size of
1401 /// the bit field (see `dest`). This field specifies whether the linker will check for,
1402 /// and error in the case of, truncated bits (in other words, relocation overflow).
1403 cast: enum(u2) {
1404 /// Do not perform any check when truncating unused bits.
1405 trunc,
1406 /// Error if the truncated value cannot be zero-extended back to the original value,
1407 /// i.e. if the truncated value is different when interpreted as unsigned.
1408 unsigned,
1409 /// Error if the truncated value cannot be sign-extended back to the original value.
1410 /// i.e. if the truncated value is different when interpreted as signed.
1411 signed,
1412 },
1413
1414 /// The relocation value (computed based on the `Target`) gets shifted to the right by
1415 /// this amount. By default, the shifted-out bits can be anything, but tags ending in
1416 /// "_exact" introduce a check that the shifted-out bits are all zeroes (an error is
1417 /// emitted if not), similar to the behavior of `@shrExact`.
1418 shift: enum(u5) {
1419 @"0",
1420 @"2_exact",
1421 @"10",
1422 @"12",
1423 @"22",
1424 @"32",
1425 @"52",
1426 },
1427
1428 /// Given a value (computed based on the `Target`), applies the shift and truncation
1429 /// operations specified by `s`, then writes the result to the start of `dest_slice` as
1430 /// specified by `s.dest`.
1431 fn write(
1432 s: Simple,
1433 val: u64,
1434 dest_slice: []u8,
1435 target_endian: std.lang.Endian,
1436 ) error{ RelocationMisaligned, RelocationOverflow }!void {
1437 const shift: u6, const shift_exact: bool = switch (s.shift) {
1438 .@"0" => .{ 0, false },
1439 .@"2_exact" => .{ 2, true },
1440 .@"10" => .{ 10, false },
1441 .@"12" => .{ 12, false },
1442 .@"22" => .{ 22, false },
1443 .@"32" => .{ 32, false },
1444 .@"52" => .{ 52, false },
1445 };
1446
1447 if (shift_exact and (val >> shift) << shift != val) {
1448 return error.RelocationMisaligned;
1449 }
1450
1451 const dest_word_bits: u8, const dest_high_bit: u6, const dest_low_bit: u6 = switch (s.dest) {
1452 // zig fmt: off
1453 .@"8" => .{ 8, 7, 0 },
1454 .@"16" => .{ 16, 15, 0 },
1455 .@"32" => .{ 32, 31, 0 },
1456 .@"64" => .{ 64, 63, 0 },
1457 .@"32[4:0]" => .{ 32, 4, 0 },
1458 .@"32[5:0]" => .{ 32, 5, 0 },
1459 .@"32[6:0]" => .{ 32, 6, 0 },
1460 .@"32[9:0]" => .{ 32, 9, 0 },
1461 .@"32[10:0]" => .{ 32, 10, 0 },
1462 .@"32[11:0]" => .{ 32, 11, 0 },
1463 .@"32[12:0]" => .{ 32, 12, 0 },
1464 .@"32[21:0]" => .{ 32, 21, 0 },
1465 .@"32[21:10]" => .{ 32, 21, 10 },
1466 .@"32[24:5]" => .{ 32, 24, 5 },
1467 .@"32[25:10]" => .{ 32, 25, 10 },
1468 .@"32[29:0]" => .{ 32, 29, 0 },
1469 // zig fmt: on
1470 };
1471
1472 // The number of bits we are truncating from the full 64-bit relocation value.
1473 const trunc_bits: u6 = 63 - dest_high_bit + dest_low_bit;
1474
1475 // When we shift, whether we do an arithmetic or logical shift depends on what cast
1476 // behavior we are going to use. If we'll be doing a signed int cast, we must shift
1477 // in sign bits so that we don't incorrectly cause a failure, and vice versa for an
1478 // unsigned int cast. Either is fine when truncating (here we pick logical shift).
1479 const shifted_val: u64 = switch (s.cast) {
1480 .trunc => val >> shift,
1481 inline else => |cast| shifted: {
1482 const ShiftInt = if (cast == .signed) i64 else u64;
1483 const x: ShiftInt = @bitCast(val);
1484 const shifted: ShiftInt = x >> shift;
1485
1486 if ((shifted << trunc_bits) >> trunc_bits != shifted) {
1487 return error.RelocationOverflow;
1488 }
1489
1490 break :shifted @bitCast(shifted);
1491 },
1492 };
1493
1494 // Create a bit-mask for the field being populated, e.g. 8[3:1] -> 0b00001110
1495 const field_mask = (~@as(u64, 0) >> trunc_bits) << dest_low_bit;
1496
1497 // Shift and mask the value to be in the correct bits, leaving the others zeroed.
1498 const masked_field: u64 = (shifted_val << dest_low_bit) & field_mask;
1499
1500 // Now we just need to actually apply the relocation by loading a word, replacing
1501 // the field bits with those in `masked_field`, and storing the result back.
1502 switch (dest_word_bits) {
1503 inline 8, 16, 32, 64 => |bits| {
1504 const word_slice = dest_slice[0..@divExact(bits, 8)];
1505 const Int = @Int(.unsigned, bits);
1506 const old: u64 = std.mem.readInt(Int, word_slice, target_endian);
1507 const new: u64 = (old & ~field_mask) | masked_field;
1508 std.mem.writeInt(Int, word_slice, @intCast(new), target_endian);
1509 },
1510 else => unreachable,
1511 }
1512 }
1513 };
1514
1515 /// Enum representing "special" relocation types, i.e. those which cannot be represented
1516 /// just with `Target` and `Simple`. These relocations have completely custom handling in
1517 /// the `Special.applyInner` function.
1518 const Special = enum(u13) {
1519 larch_pcala_hi20,
1520 larch_pcala64_lo20,
1521 larch_pcala64_hi12,
1522 larch_b21,
1523 larch_b26,
1524 larch_call36,
1525
1526 sparc_le_hix22,
1527
1528 fn applyInner(
1529 s: Special,
1530 elf: *Elf,
1531 target: Symbol.Id,
1532 addend: u64,
1533 dest_vaddr: u64,
1534 dest_slice: []u8,
1535 ) error{ RelocationMisaligned, RelocationOverflow }!void {
1536 switch (s) {
1537 .larch_pcala_hi20 => {
1538 const val = target.value(elf) +% addend;
1539 const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]);
1540 elf.targetStore(inst, .{
1541 .b0_4 = elf.targetLoad(inst).b0_4,
1542 .j20 = link.loongarch.pcalaHi20(val, dest_vaddr),
1543 .b25_31 = elf.targetLoad(inst).b25_31,
1544 });
1545 },
1546 .larch_pcala64_lo20 => {
1547 const val = target.value(elf) +% addend;
1548 const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]);
1549 elf.targetStore(inst, .{
1550 .b0_4 = elf.targetLoad(inst).b0_4,
1551 .j20 = link.loongarch.pcala64Lo20(val, dest_vaddr),
1552 .b25_31 = elf.targetLoad(inst).b25_31,
1553 });
1554 },
1555 .larch_pcala64_hi12 => {
1556 const val = target.value(elf) +% addend;
1557 const inst: *align(1) link.loongarch.K12 = @ptrCast(dest_slice[0..4]);
1558 elf.targetStore(inst, .{
1559 .b0_9 = elf.targetLoad(inst).b0_9,
1560 .k12 = link.loongarch.pcala64Hi12(val, dest_vaddr),
1561 .b22_31 = elf.targetLoad(inst).b22_31,
1562 });
1563 },
1564 .larch_b21, .larch_b26, .larch_call36 => {
1565 const target_vaddr: u64 = elf.pltEntryTargetAddr(target) orelse target.value(elf);
1566 const jump_offset: i64 = @bitCast(target_vaddr +% addend -% dest_vaddr);
1567 if ((jump_offset >> 2) << 2 != jump_offset) {
1568 return error.RelocationMisaligned;
1569 }
1570 const shifted_jump_offset: i64 = @shrExact(jump_offset, 2);
1571 switch (s) {
1572 .larch_b21 => {
1573 if ((shifted_jump_offset << (64 - 21)) >> (64 - 21) != shifted_jump_offset) {
1574 return error.RelocationOverflow;
1575 }
1576 const truncated: i21 = @intCast(shifted_jump_offset);
1577 const parts: packed struct { lo16: u16, hi5: u5 } = @bitCast(truncated);
1578 const inst: *align(1) link.loongarch.D5K16 = @ptrCast(dest_slice[0..4]);
1579 elf.targetStore(inst, .{
1580 .d5 = parts.hi5,
1581 .b5_9 = elf.targetLoad(inst).b5_9,
1582 .k16 = parts.lo16,
1583 .b26_31 = elf.targetLoad(inst).b26_31,
1584 });
1585 },
1586 .larch_b26 => {
1587 if ((shifted_jump_offset << (64 - 26)) >> (64 - 26) != shifted_jump_offset) {
1588 return error.RelocationOverflow;
1589 }
1590 const truncated: i26 = @intCast(shifted_jump_offset);
1591 const parts: packed struct { lo16: u16, hi10: u10 } = @bitCast(truncated);
1592 const inst: *align(1) link.loongarch.D10K16 = @ptrCast(dest_slice[0..4]);
1593 elf.targetStore(inst, .{
1594 .d10 = parts.hi10,
1595 .k16 = parts.lo16,
1596 .b26_31 = elf.targetLoad(inst).b26_31,
1597 });
1598 },
1599 .larch_call36 => {
1600 // The allowed range of destination addresses here is non-trivial:
1601 // [PC - 128 GiB - 0x20_000, PC + 128 GiB - 0x20_000 - 4]
1602 const gib = 1024 * 1024 * 1024;
1603 if (jump_offset < -128 * gib - 0x20_000 or
1604 jump_offset > 128 * gib - 0x20_000 - 4)
1605 {
1606 return error.RelocationOverflow;
1607 }
1608 // The values we write into the instructions are a little weird too:
1609 const hi: i20 = @intCast((shifted_jump_offset +% 0x8000) >> 16);
1610 const lo: i16 = @truncate(shifted_jump_offset);
1611
1612 const inst0: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]);
1613 const inst1: *align(1) link.loongarch.K16 = @ptrCast(dest_slice[4..8]);
1614
1615 const old0 = elf.targetLoad(inst0);
1616 elf.targetStore(inst0, .{ .b0_4 = old0.b0_4, .j20 = @bitCast(hi), .b25_31 = old0.b25_31 });
1617
1618 const old1 = elf.targetLoad(inst1);
1619 elf.targetStore(inst1, .{ .b0_9 = old1.b0_9, .k16 = @bitCast(lo), .b26_31 = old1.b26_31 });
1620 },
1621 else => unreachable,
1622 }
1623 },
1624 .sparc_le_hix22 => {
1625 const tls_phndx = elf.getNode(elf.ni.tls.unwrap().?).segment;
1626 const tls_size: u64 = switch (elf.phdrSlice()) {
1627 inline else => |phdr| tls_size: {
1628 assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS);
1629 break :tls_size elf.targetLoad(&phdr[tls_phndx].memsz);
1630 },
1631 };
1632 const dest_ptr: *align(1) packed struct(u32) {
1633 imm22: u22,
1634 b22_31: u10,
1635 } = @ptrCast(dest_slice);
1636 elf.targetStore(dest_ptr, .{
1637 .imm22 = @truncate(~(target.value(elf) +% addend -% tls_size) >> 10),
1638 .b22_31 = elf.targetLoad(dest_ptr).b22_31,
1639 });
1640 },
1641 }
1642 }
1643 };
1644
1645 fn dependsOnTlsSize(t: SymbolReloc.Type, elf: *const Elf) bool {
1646 return switch (elf.targetTlsVariant()) {
1647 // In TLS variant I, the executable's TLS block starts at a fixed offset from the
1648 // thread pointer, so everything is fine...
1649 .I_original, .I_modified => false,
1650 // ...but in variant II, the executable's TLS block *ends* at a fixed offset from
1651 // the thread pointer, so the offset from the thread pointer to the *start* of the
1652 // TLS block depends on the size of the block, and we need that offset to resolve
1653 // 'tpoff' relocations.
1654 .II => switch (t.target) {
1655 .abs,
1656 .rel,
1657 .pltabs,
1658 .pltrel,
1659 .dtpoff,
1660 .size,
1661 => false,
1662
1663 .tpoff => true,
1664
1665 .special => switch (t.action.special) {
1666 .sparc_le_hix22,
1667 => true,
1668
1669 .larch_pcala_hi20,
1670 .larch_pcala64_lo20,
1671 .larch_pcala64_hi12,
1672 .larch_b21,
1673 .larch_b26,
1674 .larch_call36,
1675 => false,
1676 },
1677 },
1678 };
1679 }
1680 };
1681
1682 fn apply(reloc: *SymbolReloc, elf: *Elf) void {
1683 assert(elf.ehdrType() != .REL);
1684 if (reloc.node.hasMoved(&elf.mf) or reloc.target.hasMoved(elf)) {
1685 // There's no point applying the relocation now, because it will be re-applied by
1686 // `flushMoved` at some point anyway.
1687 return;
1688 }
1689 switch (reloc.result) {
1690 .ok => {},
1691 .overflowed => elf.overflowed_reloc_count -= 1,
1692 .misaligned => elf.misaligned_reloc_count -= 1,
1693 }
1694 if (reloc.applyInner(elf)) {
1695 @branchHint(.likely);
1696 reloc.result = .ok;
1697 } else |err| switch (err) {
1698 error.RelocationOverflow => {
1699 reloc.result = .overflowed;
1700 elf.overflowed_reloc_count += 1;
1701 },
1702 error.RelocationMisaligned => {
1703 reloc.result = .misaligned;
1704 elf.misaligned_reloc_count += 1;
1705 },
1706 }
1707 }
1708 fn applyInner(reloc: *const SymbolReloc, elf: *Elf) error{ RelocationOverflow, RelocationMisaligned }!void {
1709 const dest_vaddr = elf.getNodeVAddr(reloc.node) + reloc.offset;
1710 const dest_slice = reloc.node.slice(&elf.mf)[@intCast(reloc.offset)..];
1711
1712 const addend: u64 = @bitCast(reloc.addend);
1713 const target_val: u64 = type: switch (reloc.type.target) {
1714 .abs => reloc.target.value(elf) +% addend,
1715 .rel => reloc.target.value(elf) +% addend -% dest_vaddr,
1716 .pltabs => {
1717 const plt_entry_addr = elf.pltEntryTargetAddr(reloc.target) orelse continue :type .abs;
1718 break :type plt_entry_addr +% addend;
1719 },
1720 .pltrel => {
1721 const plt_entry_addr = elf.pltEntryTargetAddr(reloc.target) orelse continue :type .rel;
1722 break :type plt_entry_addr +% addend -% dest_vaddr;
1723 },
1724 .dtpoff => reloc.target.value(elf) +% addend,
1725 .tpoff => switch (elf.targetTlsVariant()) {
1726 .I_original => |tls| tls.tcb_size +% reloc.target.value(elf) +% addend,
1727 .I_modified => |tls| 0 -% tls.tp_off +% reloc.target.value(elf) +% addend,
1728 .II => {
1729 const tls_phndx = elf.getNode(elf.ni.tls.unwrap().?).segment;
1730 const tls_size: u64 = switch (elf.phdrSlice()) {
1731 inline else => |phdr| tls_size: {
1732 assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS);
1733 break :tls_size elf.targetLoad(&phdr[tls_phndx].memsz);
1734 },
1735 };
1736 break :type reloc.target.value(elf) +% addend -% tls_size;
1737 },
1738 },
1739 .size => switch (elf.symPtr(reloc.target.index(elf))) {
1740 inline else => |sym| elf.targetLoad(&sym.size),
1741 },
1742 .special => return reloc.type.action.special.applyInner(
1743 elf,
1744 reloc.target,
1745 addend,
1746 dest_vaddr,
1747 dest_slice,
1748 ),
1749 };
1750
1751 // Check for the `R_*_RELATIVE` case now, because it is possible only when no shift or cast
1752 // is required, meaning we can handle it now and return early.
1753 if (reloc.rela_index.unwrap()) |rela_index| switch (elf.classifySymbolValue(reloc.target)) {
1754 .static => unreachable,
1755 .dynamic => return, // the relocation happens at runtime
1756 .static_relative => {
1757 // We have emitted an R_*_RELATIVE relocation to help lower an absolute-address
1758 // relocation. The value computed above is valid, but instead of writing it to the
1759 // destination slice, we actually want to write it to the runtime relocation entry.
1760 switch (elf.identClass()) {
1761 .NONE, _ => unreachable,
1762 .@"32" => assert(reloc.type.action.simple.dest == .@"32"),
1763 .@"64" => assert(reloc.type.action.simple.dest == .@"64"),
1764 }
1765 assert(reloc.type.action.simple.cast == .unsigned);
1766 assert(reloc.type.action.simple.shift == .@"0");
1767 elf.shndx.rela_dyn.relaSetRelativeOffset(elf, rela_index, target_val);
1768 return;
1769 },
1770 };
1771
1772 try reloc.type.action.simple.write(target_val, dest_slice, elf.targetEndian());
1773 }
1774
1775 fn delete(reloc: *SymbolReloc, elf: *Elf, index: SymbolReloc.Index) void {
1776 assert(index.get(elf) == reloc);
1777
1778 reloc.deleteOutputRel(elf);
1779 if (reloc.type.dependsOnTlsSize(elf)) {
1780 assert(elf.tls_size_symbol_relocs.swapRemove(index));
1781 }
1782
1783 switch (reloc.prev) {
1784 .none => {
1785 const target_ptr = reloc.target.index(elf).ptr(elf);
1786 assert(target_ptr.first_target_reloc == index);
1787 target_ptr.first_target_reloc = reloc.next;
1788 },
1789 else => |prev| prev.get(elf).next = reloc.next,
1790 }
1791 switch (reloc.next) {
1792 .none => {},
1793 else => |next| next.get(elf).prev = reloc.prev,
1794 }
1795 switch (reloc.result) {
1796 .ok => {},
1797 .overflowed => elf.overflowed_reloc_count -= 1,
1798 .misaligned => elf.misaligned_reloc_count -= 1,
1799 }
1800
1801 reloc.* = undefined;
1802 }
1803
1804 /// If `reloc.rela_index` is populated, reset it to `.none` and delete the relocation, updating
1805 /// `elf.textrel_count` if necessary.
1806 fn deleteOutputRel(reloc: *SymbolReloc, elf: *Elf) void {
1807 const rela_index = reloc.rela_index.unwrap() orelse return;
1808 reloc.relaSection(elf).relaDeleteOne(elf, rela_index);
1809 switch (elf.ehdrType()) {
1810 .REL => {},
1811 .EXEC, .DYN => switch (elf.nodeWantsDsoRelocation(reloc.node)) {
1812 .no => unreachable, // there *was* a dynamic relocation!
1813 .yes => {},
1814 .yes_textrel => elf.textrel_count -= 1,
1815 },
1816 }
1817 reloc.rela_index = .none;
1818 }
1819};
1820
1821fn ensureDynsymHashCapacity(elf: *Elf, max_dynsym_count: u32) Error!void {
1822 const gpa = elf.base.comp.gpa;
1823
1824 const min_buckets = max_dynsym_count / 2;
1825
1826 const cur_dynsym_count: u32 = switch (elf.shdrPtr(elf.shndx.dynsym)) {
1827 inline else => |shdr, class| @intCast(@divExact(
1828 elf.targetLoad(&shdr.size),
1829 @sizeOf(class.ElfN().Sym),
1830 )),
1831 };
1832
1833 switch (elf.targetDynsymHashInfo()) {
1834 inline else => |info| {
1835 {
1836 const section_slice: []align(@sizeOf(info.Int())) u8 = @alignCast(elf.shndx.hash.get(elf).ni.slice(&elf.mf));
1837 const header: *info.Header() = @ptrCast(section_slice[0..@sizeOf(info.Header())]);
1838 assert(elf.targetLoad(&header.nchain) == cur_dynsym_count);
1839 const nbucket = elf.targetLoad(&header.nbucket);
1840 if (nbucket >= min_buckets) {
1841 // We don't need to add any buckets, but we still need to make sure the section is large
1842 // enough to fit `max_dynsym_count` chains.
1843 const need_size = @sizeOf(info.Header()) + (nbucket + max_dynsym_count) * 4;
1844 try elf.shndx.hash.get(elf).ni.ensureMinimumSize(&elf.mf, gpa, need_size);
1845 return;
1846 }
1847 // We need more buckets, so we'll have to rebuild the hash table.
1848 }
1849
1850 // Rebuilding the hash table is quite expensive, so to avoid doing it too often we use a large
1851 // growth factor (* 2) for `nbucket`.
1852 const new_nbucket = min_buckets * 2;
1853
1854 {
1855 const need_size = @sizeOf(info.Header()) + (new_nbucket + max_dynsym_count) * 4;
1856 try elf.shndx.hash.get(elf).ni.ensureMinimumSize(&elf.mf, gpa, need_size);
1857 }
1858
1859 elf.mf.nodes_lock.lock();
1860 defer elf.mf.nodes_lock.unlock();
1861
1862 const section_slice: []align(@sizeOf(info.Int())) u8 = @alignCast(elf.shndx.hash.get(elf).ni.slice(&elf.mf));
1863 const header: *info.Header() = @ptrCast(section_slice[0..@sizeOf(info.Header())]);
1864 const trailing: []info.Int() = @ptrCast(section_slice[@sizeOf(info.Header())..]);
1865
1866 header.* = .{ .nbucket = new_nbucket, .nchain = cur_dynsym_count };
1867 if (elf.targetEndian() != std.lang.Endian.native) {
1868 std.mem.byteSwapAllFields(info.Header(), header);
1869 }
1870 const buckets: []info.Int() = trailing[0..@intCast(elf.targetLoad(&header.nbucket))];
1871 const chains: []info.Int() = trailing[@intCast(elf.targetLoad(&header.nbucket))..][0..@intCast(elf.targetLoad(&header.nchain))];
1872
1873 @memset(buckets, 0);
1874 chains[0] = 0;
1875 for (1..cur_dynsym_count, chains[1..]) |dynsym_index_usize, *chain| {
1876 const dynsym_index: u32 = @intCast(dynsym_index_usize);
1877 const sym_name: String(.dynstr) = switch (elf.dynsymPtr(dynsym_index)) {
1878 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),
1879 };
1880 const b = std.elf.hash.calculate(sym_name.slice(elf)) % buckets.len;
1881 // Make this symbol the head of that bucket, and chain to the old head.
1882 chain.* = buckets[b];
1883 elf.targetStore(&buckets[b], dynsym_index);
1884 }
1885 },
1886 }
1887}
1888
1889fn appendDynsymHashEntry(elf: *Elf, dynsym_index: u32) void {
1890 switch (elf.targetDynsymHashInfo()) {
1891 inline else => |info| {
1892 const section_slice: []align(@sizeOf(info.Int())) u8 = @alignCast(elf.shndx.hash.get(elf).ni.slice(&elf.mf));
1893 const header: *info.Header() = @ptrCast(section_slice[0..@sizeOf(info.Header())]);
1894 assert(elf.targetLoad(&header.nchain) == dynsym_index);
1895 elf.targetStore(&header.nchain, dynsym_index + 1);
1896
1897 switch (elf.shdrPtr(elf.shndx.hash)) {
1898 inline else => |shdr| elf.targetStore(&shdr.size, elf.targetLoad(&shdr.size) + @sizeOf(info.Int())),
1899 }
1900 },
1901 }
1902
1903 elf.populateDynsymHashEntry(dynsym_index);
1904}
1905fn populateDynsymHashEntry(elf: *Elf, dynsym_index: u32) void {
1906 elf.mf.nodes_lock.lock();
1907 defer elf.mf.nodes_lock.unlock();
1908
1909 assert(dynsym_index != 0);
1910
1911 switch (elf.targetDynsymHashInfo()) {
1912 inline else => |info| {
1913 const section_slice: []align(@sizeOf(info.Int())) u8 = @alignCast(elf.shndx.hash.get(elf).ni.slice(&elf.mf));
1914 const header: *info.Header() = @ptrCast(section_slice[0..@sizeOf(info.Header())]);
1915 const trailing: []info.Int() = @ptrCast(section_slice[@sizeOf(info.Header())..]);
1916
1917 const buckets: []info.Int() = trailing[0..@intCast(elf.targetLoad(&header.nbucket))];
1918 const chains: []info.Int() = trailing[@intCast(elf.targetLoad(&header.nbucket))..][0..@intCast(elf.targetLoad(&header.nchain))];
1919
1920 const sym_name: String(.dynstr) = switch (elf.dynsymPtr(dynsym_index)) {
1921 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),
1922 };
1923 const b = std.elf.hash.calculate(sym_name.slice(elf)) % buckets.len;
1924 // Make this symbol the head of that bucket, and chain to the old head.
1925 chains[dynsym_index] = buckets[b];
1926 elf.targetStore(&buckets[b], dynsym_index);
1927 },
1928 }
1929}
1930fn popDynsymHashEntry(elf: *Elf, dynsym_index: u32) void {
1931 elf.clearDynsymHashEntry(dynsym_index);
1932
1933 switch (elf.targetDynsymHashInfo()) {
1934 inline else => |info| {
1935 const section_slice: []align(@sizeOf(info.Int())) u8 = @alignCast(elf.shndx.hash.get(elf).ni.slice(&elf.mf));
1936 const header: *info.Header() = @ptrCast(section_slice[0..@sizeOf(info.Header())]);
1937 assert(elf.targetLoad(&header.nchain) == dynsym_index + 1);
1938 elf.targetStore(&header.nchain, dynsym_index);
1939
1940 switch (elf.shdrPtr(elf.shndx.hash)) {
1941 inline else => |shdr| elf.targetStore(&shdr.size, elf.targetLoad(&shdr.size) - @sizeOf(info.Int())),
1942 }
1943 },
1944 }
1945}
1946fn clearDynsymHashEntry(elf: *Elf, dynsym_index: u32) void {
1947 elf.mf.nodes_lock.lock();
1948 defer elf.mf.nodes_lock.unlock();
1949
1950 assert(dynsym_index != 0);
1951
1952 switch (elf.targetDynsymHashInfo()) {
1953 inline else => |info| {
1954 const section_slice: []align(@sizeOf(info.Int())) u8 = @alignCast(elf.shndx.hash.get(elf).ni.slice(&elf.mf));
1955 const header: *info.Header() = @ptrCast(section_slice[0..@sizeOf(info.Header())]);
1956 const trailing: []info.Int() = @ptrCast(section_slice[@sizeOf(info.Header())..]);
1957
1958 const buckets: []info.Int() = trailing[0..@intCast(elf.targetLoad(&header.nbucket))];
1959 const chains: []info.Int() = trailing[@intCast(elf.targetLoad(&header.nbucket))..][0..@intCast(elf.targetLoad(&header.nchain))];
1960
1961 const sym_name: String(.dynstr) = switch (elf.dynsymPtr(dynsym_index)) {
1962 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),
1963 };
1964 const b = std.elf.hash.calculate(sym_name.slice(elf)) % buckets.len;
1965
1966 const next_dynsym_index = elf.targetLoad(&chains[dynsym_index]);
1967 elf.targetStore(&chains[dynsym_index], 0);
1968
1969 // To remove `dynsym_index` from the singly-linked list, we need to iterate the chain to find
1970 // and replace it. But since this is, well, a hash table, that's actually fine.
1971 if (elf.targetLoad(&buckets[b]) == dynsym_index) {
1972 elf.targetStore(&buckets[b], next_dynsym_index);
1973 } else {
1974 var cur: usize = @intCast(elf.targetLoad(&buckets[b]));
1975 while (true) {
1976 assert(cur != 0); // `dynsym_index` is definitely somewhere in the chain
1977 if (elf.targetLoad(&chains[cur]) == dynsym_index) break;
1978 cur = @intCast(elf.targetLoad(&chains[cur]));
1979 }
1980 // We found `dynsym_index`; replace it with `next_dynsym_index`.
1981 elf.targetStore(&chains[cur], next_dynsym_index);
1982 }
1983 },
1984 }
1985}
1986
1987fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe_global }) Error!void {
1988 const gpa = elf.base.comp.gpa;
1989
1990 try elf.symtab.ensureUnusedCapacity(gpa, len);
1991
1992 // If adding locals, we may need to move one global out of the way for each local. If adding
1993 // globals, they could all get demoted to STB_LOCAL, meaning we have to move N other globals
1994 // around to keep `.dynsym` compact. Either way, the maximum is N.
1995 try elf.changed_symtab_index.ensureUnusedCapacity(gpa, len);
1996
1997 {
1998 // Ensure the symtab section's node is big enough
1999 const need_node_size: u64 = switch (elf.shdrPtr(.symtab)) {
2000 inline else => |shdr, class| elf.targetLoad(&shdr.size) + len * @sizeOf(class.ElfN().Sym),
2001 };
2002 try Section.Index.symtab.get(elf).ni.ensureMinimumSize(&elf.mf, gpa, need_node_size);
2003 }
2004
2005 switch (kind) {
2006 .all_local => {},
2007 .maybe_global => {
2008 try elf.globals.strong_def.ensureUnusedCapacity(gpa, len);
2009 try elf.globals.weak_def.ensureUnusedCapacity(gpa, len);
2010 try elf.globals.strong_undef.ensureUnusedCapacity(gpa, len);
2011 try elf.globals.weak_undef.ensureUnusedCapacity(gpa, len);
2012
2013 try elf.node_global_symbols.ensureUnusedCapacity(gpa, len);
2014
2015 if (elf.shndx.dynsym != .UNDEF) {
2016 const dynsym_cur_size: u64, const dynsym_ent_size: u32 = switch (elf.shdrPtr(elf.shndx.dynsym)) {
2017 inline else => |shdr, class| .{
2018 elf.targetLoad(&shdr.size),
2019 @sizeOf(class.ElfN().Sym),
2020 },
2021 };
2022 const dynsym_cur_len: u32 = @intCast(@divExact(dynsym_cur_size, dynsym_ent_size));
2023
2024 const dynsym_need_size: u64 = (dynsym_cur_len + len) * dynsym_ent_size;
2025 try elf.shndx.dynsym.get(elf).ni.ensureMinimumSize(&elf.mf, gpa, dynsym_need_size);
2026
2027 try elf.ensureDynsymHashCapacity(dynsym_cur_len + len);
2028
2029 try elf.ensureUnusedPltCapacity(len);
2030 }
2031 },
2032 }
2033}
2034fn ensureUnusedPltCapacity(elf: *Elf, len: u32) Error!void {
2035 const gpa = elf.base.comp.gpa;
2036
2037 try elf.shndx.rela_plt.relaEnsureAdditionalCapacity(elf, len);
2038
2039 try elf.plt.ensureUnusedCapacity(gpa, len);
2040 const need_plt_count = elf.plt.count() + len;
2041
2042 const plt = elf.targetPltInfo();
2043
2044 // Ensure the `.plt` section's node is big enough:
2045 {
2046 const need_size: usize = plt.entry_size * (1 + need_plt_count);
2047 try elf.shndx.plt.get(elf).ni.ensureMinimumSize(&elf.mf, gpa, need_size);
2048 }
2049
2050 // If there is a `.got.plt` section, ensure its node is big enough
2051 if (plt.got_plt) |got_plt| {
2052 const need_size: usize = elf.targetPtrSize() * (got_plt.header_entries + need_plt_count);
2053 try elf.shndx.got_plt.get(elf).ni.ensureMinimumSize(&elf.mf, gpa, need_size);
2054 }
2055
2056 // If there is a `.plt.sec` section, ensure its node is big enough
2057 if (plt.plt_sec) |plt_sec| {
2058 const need_size: usize = plt_sec.entry_size * need_plt_count;
2059 try elf.shndx.plt_sec.get(elf).ni.ensureMinimumSize(&elf.mf, gpa, need_size);
2060 }
2061}
2062/// Given an index into the PLT, returns whether that PLT entry is dead, meaning it may be reused at
2063/// any time and must not be targeted by relocations. See also the doc comment on `Elf.plt`.
2064fn pltEntryIsDead(elf: *Elf, plt_index: usize) bool {
2065 assert(elf.shndx.plt != .UNDEF);
2066 assert(plt_index <= elf.plt.count());
2067 // We track which PLT entries are alive based on the relocation entries, since there is a 1-1
2068 // mapping between PLT entries and `.rela.plt` entries and the relocation entries already have
2069 // a free-list mechanism.
2070 switch (elf.shdrPtr(elf.shndx.rela_plt)) {
2071 inline else => |rela_shdr, class| {
2072 const size = elf.targetLoad(&rela_shdr.size);
2073 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
2074 elf.shndx.rela_plt.get(elf).ni.slice(&elf.mf)[0..@intCast(size)],
2075 ));
2076 const rel_type = elf.targetLoad(&relas[plt_index].info).type;
2077 return rel_type == MachineRelocType.none(elf).unwrap(elf);
2078 },
2079 }
2080}
2081
2082const AddLocalSymbolOptions = struct {
2083 node: MappedFile.Node.Index.Optional,
2084 name: String(.strtab),
2085 value: u64,
2086 size: u64,
2087 type: std.elf.STT,
2088 shndx: Section.Index,
2089};
2090fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.LocalIndex {
2091 switch (elf.shdrPtr(.symtab)) {
2092 inline else => |shdr, class| {
2093 const ent_size = @sizeOf(class.ElfN().Sym);
2094
2095 // `shdr.info` stores the index of the first global symbol. We will replace it with our
2096 // new local symbol, and move the global symbol to a new index at the end of the symtab.
2097 const target_index: Symbol.Index = @fromBackingInt(elf.targetLoad(&shdr.info));
2098
2099 const old_size = elf.targetLoad(&shdr.size);
2100 const new_size = old_size + ent_size;
2101
2102 assert(elf.symtab.items.len == @divExact(old_size, ent_size));
2103
2104 elf.targetStore(&shdr.info, @backingInt(target_index) + 1);
2105 elf.targetStore(&shdr.size, new_size);
2106
2107 const new_index: Symbol.Index = @fromBackingInt(@intCast(elf.symtab.items.len));
2108 elf.symtab.appendAssumeCapacity(undefined);
2109
2110 const target_sym = @field(elf.symPtr(target_index), @tagName(class));
2111
2112 if (target_index != new_index) {
2113 // Move the global at `target_index` to `new_index`. First the symtab entry...
2114 const new_sym = @field(elf.symPtr(new_index), @tagName(class));
2115 new_sym.* = target_sym.*;
2116 // ...then the `elf.symtab` metadata...
2117 new_index.ptr(elf).* = target_index.ptr(elf).*;
2118 // ...then update the `elf.globals` tracking.
2119 const global_name: String(.strtab) = @fromBackingInt(elf.targetLoad(&new_sym.name));
2120 elf.globalByName(global_name).?.symtab_index = new_index;
2121
2122 if (elf.ehdrType() == .REL and target_index.ptr(elf).first_target_reloc != .none) {
2123 // This symbol's index is changing, so queue an update of relocs targeting it.
2124 elf.changed_symtab_index.putAssumeCapacity(global_name, {});
2125 }
2126 }
2127
2128 target_index.ptr(elf).* = .{
2129 .node = opts.node,
2130 .first_target_reloc = .none,
2131 };
2132
2133 target_sym.* = .{
2134 .name = @backingInt(opts.name),
2135 .value = @intCast(opts.value),
2136 .size = @intCast(opts.size),
2137 .info = .{ .type = opts.type, .bind = .LOCAL },
2138 .other = .{ .visibility = .DEFAULT },
2139 .shndx = opts.shndx.toSection().?,
2140 };
2141 if (elf.targetEndian() != native_endian) {
2142 std.mem.byteSwapAllFields(class.ElfN().Sym, target_sym);
2143 }
2144
2145 return @fromBackingInt(@backingInt(target_index));
2146 },
2147 }
2148}
2149
2150const AddGlobalSymbolOptions = struct {
2151 const Name = struct {
2152 strtab: String(.strtab),
2153 dynstr: String(.dynstr),
2154 fn string(elf: *Elf, slice: []const u8) Error!Name {
2155 return .{
2156 .strtab = try elf.string(.strtab, slice),
2157 .dynstr = switch (elf.shndx.dynsym) {
2158 .UNDEF => .empty,
2159 else => try elf.string(.dynstr, slice),
2160 },
2161 };
2162 }
2163 };
2164
2165 node: MappedFile.Node.Index.Optional,
2166 name: Name,
2167 lib_name: ?[]const u8 = null,
2168 value: u64,
2169 size: u64,
2170 type: std.elf.STT,
2171 bind: enum { strong, weak },
2172 visibility: std.elf.STV,
2173 shndx: Section.Index,
2174};
2175fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{MultipleDefinitions}!Symbol.Id {
2176 _ = opts.lib_name; // TODO
2177
2178 if (elf.shndx.dynsym == .UNDEF) {
2179 assert(opts.name.dynstr == .empty);
2180 } else {
2181 assert(std.mem.eql(u8, opts.name.dynstr.slice(elf), opts.name.strtab.slice(elf)));
2182 }
2183
2184 // We break from this `switch` only if this symbol name did not previously exist at all and so
2185 // we have added a new entry to one of the maps in `elf.globals`. In that case we actually need
2186 // a new symtab entry.
2187 const new_global_ptr: *Symbol.Global = if (opts.shndx != .UNDEF) switch (opts.bind) {
2188 .strong => new_global: {
2189 const gop = elf.globals.strong_def.getOrPutAssumeCapacity(opts.name.strtab);
2190 if (gop.found_existing) return error.MultipleDefinitions;
2191 const old_kv = elf.globals.weak_def.fetchSwapRemove(opts.name.strtab) orelse
2192 elf.globals.strong_undef.fetchSwapRemove(opts.name.strtab) orelse
2193 elf.globals.weak_undef.fetchSwapRemove(opts.name.strtab) orelse {
2194 // The symbol did not already exist, so we'll use the "new global" path.
2195 break :new_global gop.value_ptr;
2196 };
2197 gop.value_ptr.* = old_kv.value;
2198 elf.setGlobalSymbolValue(opts.name.strtab, gop.value_ptr, .{
2199 .node = opts.node,
2200 .value = opts.value,
2201 .size = opts.size,
2202 .type = opts.type,
2203 .shndx = opts.shndx,
2204 });
2205 elf.mergeGlobalSymbolVisibility(gop.value_ptr, opts.visibility, .strong);
2206 return .global(opts.name.strtab);
2207 },
2208 .weak => new_global: {
2209 if (elf.globals.strong_def.getPtr(opts.name.strtab)) |global| {
2210 // The existing definition holds, we just merge our visibility in.
2211 elf.mergeGlobalSymbolVisibility(global, opts.visibility, .strong);
2212 return .global(opts.name.strtab);
2213 }
2214 const gop = elf.globals.weak_def.getOrPutAssumeCapacity(opts.name.strtab);
2215 if (gop.found_existing) {
2216 // The existing definition holds, we just merge our visibility in.
2217 elf.mergeGlobalSymbolVisibility(gop.value_ptr, opts.visibility, .weak);
2218 return .global(opts.name.strtab);
2219 }
2220 const old_kv = elf.globals.strong_undef.fetchSwapRemove(opts.name.strtab) orelse
2221 elf.globals.weak_undef.fetchSwapRemove(opts.name.strtab) orelse {
2222 // The symbol did not already exist, so we'll use the "new global" path.
2223 break :new_global gop.value_ptr;
2224 };
2225 gop.value_ptr.* = old_kv.value;
2226 elf.setGlobalSymbolValue(opts.name.strtab, gop.value_ptr, .{
2227 .node = opts.node,
2228 .value = opts.value,
2229 .size = opts.size,
2230 .type = opts.type,
2231 .shndx = opts.shndx,
2232 });
2233 elf.mergeGlobalSymbolVisibility(gop.value_ptr, opts.visibility, .weak);
2234 return .global(opts.name.strtab);
2235 },
2236 } else switch (opts.bind) {
2237 .strong => new_global: {
2238 if (elf.globals.strong_def.getPtr(opts.name.strtab)) |global| {
2239 // The existing definition holds, we just merge our visibility in.
2240 elf.mergeGlobalSymbolVisibility(global, opts.visibility, .strong);
2241 return .global(opts.name.strtab);
2242 }
2243 if (elf.globals.weak_def.getPtr(opts.name.strtab)) |global| {
2244 // The existing definition holds, we just merge our visibility in.
2245 elf.mergeGlobalSymbolVisibility(global, opts.visibility, .weak);
2246 return .global(opts.name.strtab);
2247 }
2248 const gop = elf.globals.strong_undef.getOrPutAssumeCapacity(opts.name.strtab);
2249 if (gop.found_existing) {
2250 // The existing symbol is okay, we just merge our visibility in.
2251 elf.mergeGlobalSymbolVisibility(gop.value_ptr, opts.visibility, .strong);
2252 return .global(opts.name.strtab);
2253 }
2254 const old_kv = elf.globals.weak_undef.fetchSwapRemove(opts.name.strtab) orelse {
2255 // The symbol did not already exist, so we'll use the "new global" path.
2256 break :new_global gop.value_ptr;
2257 };
2258 gop.value_ptr.* = old_kv.value;
2259 elf.mergeGlobalSymbolVisibility(gop.value_ptr, opts.visibility, .strong);
2260 return .global(opts.name.strtab);
2261 },
2262 .weak => new_global: {
2263 if (elf.globals.strong_def.getPtr(opts.name.strtab) orelse
2264 elf.globals.strong_undef.getPtr(opts.name.strtab)) |global|
2265 {
2266 // The existing symbol is okay, we just merge our visibility in.
2267 elf.mergeGlobalSymbolVisibility(global, opts.visibility, .strong);
2268 return .global(opts.name.strtab);
2269 }
2270 if (elf.globals.weak_def.getPtr(opts.name.strtab)) |global| {
2271 // The existing symbol is okay, we just merge our visibility in.
2272 elf.mergeGlobalSymbolVisibility(global, opts.visibility, .weak);
2273 return .global(opts.name.strtab);
2274 }
2275 const gop = elf.globals.weak_undef.getOrPutAssumeCapacity(opts.name.strtab);
2276 if (gop.found_existing) {
2277 // The existing symbol is okay, we just merge our visibility in.
2278 elf.mergeGlobalSymbolVisibility(gop.value_ptr, opts.visibility, .weak);
2279 return .global(opts.name.strtab);
2280 }
2281 break :new_global gop.value_ptr;
2282 },
2283 };
2284
2285 const force_local_bind: bool = switch (opts.visibility) {
2286 .HIDDEN, .INTERNAL => elf.ehdrType() != .REL,
2287 .PROTECTED, .DEFAULT => false,
2288 };
2289
2290 const bind: std.elf.STB = if (force_local_bind) b: {
2291 break :b .LOCAL;
2292 } else switch (opts.bind) {
2293 .strong => .GLOBAL,
2294 .weak => .WEAK,
2295 };
2296
2297 const @"type": std.elf.STT = switch (opts.type) {
2298 .NOTYPE => if (elf.dso_globals.get(opts.name.strtab)) |dso_global| t: {
2299 break :t dso_global.type;
2300 } else .NOTYPE,
2301 else => |t| t,
2302 };
2303
2304 const sym_index: Symbol.Index = @fromBackingInt(@intCast(elf.symtab.items.len));
2305 elf.symtab.appendAssumeCapacity(.{
2306 .node = opts.node,
2307 .first_target_reloc = .none,
2308 });
2309 switch (elf.shdrPtr(.symtab)) {
2310 inline else => |shdr, class| {
2311 const Sym = class.ElfN().Sym;
2312 // Increase the symtab size...
2313 const old_size = elf.targetLoad(&shdr.size);
2314 assert(old_size == @backingInt(sym_index) * @sizeOf(Sym));
2315 elf.targetStore(&shdr.size, old_size + @sizeOf(Sym));
2316 // ...then populate the newly-valid symbol pointer
2317 const sym = @field(elf.symPtr(sym_index), @tagName(class));
2318 sym.* = .{
2319 .name = @backingInt(opts.name.strtab),
2320 .value = @intCast(opts.value),
2321 .size = @intCast(opts.size),
2322 .info = .{ .type = @"type", .bind = bind },
2323 .other = .{ .visibility = opts.visibility },
2324 .shndx = opts.shndx.toSection().?,
2325 };
2326 if (elf.targetEndian() != native_endian) {
2327 std.mem.byteSwapAllFields(Sym, sym);
2328 }
2329 },
2330 }
2331
2332 const old_head: String(.strtab) = old_head: {
2333 const node = opts.node.unwrap() orelse break :old_head .empty;
2334 const gop = elf.node_global_symbols.getOrPutAssumeCapacity(node);
2335 const old_head: String(.strtab) = if (gop.found_existing) gop.value_ptr.* else .empty;
2336 gop.value_ptr.* = opts.name.strtab;
2337 break :old_head old_head;
2338 };
2339
2340 new_global_ptr.* = .{
2341 .symtab_index = sym_index,
2342 .dynsym_index = dynsym_index: {
2343 if (elf.shndx.dynsym == .UNDEF) break :dynsym_index 0;
2344 if (force_local_bind) break :dynsym_index 0;
2345 switch (elf.shdrPtr(elf.shndx.dynsym)) {
2346 inline else => |shdr, class| {
2347 const Sym = class.ElfN().Sym;
2348 // Increase the dynamic symbol table size...
2349 const old_size = elf.targetLoad(&shdr.size);
2350 elf.targetStore(&shdr.size, old_size + @sizeOf(Sym));
2351 const dynsym_index: u32 = @intCast(@divExact(old_size, @sizeOf(Sym)));
2352 // ...then populate the newly-valid symbol pointer
2353 const sym = @field(elf.dynsymPtr(dynsym_index), @tagName(class));
2354 sym.* = .{
2355 .name = @backingInt(opts.name.dynstr),
2356 .value = @intCast(opts.value),
2357 .size = @intCast(opts.size),
2358 .info = .{ .type = @"type", .bind = bind },
2359 .other = .{ .visibility = opts.visibility },
2360 .shndx = opts.shndx.toSection().?,
2361 };
2362 if (elf.targetEndian() != native_endian) {
2363 std.mem.byteSwapAllFields(Sym, sym);
2364 }
2365 elf.appendDynsymHashEntry(dynsym_index);
2366 break :dynsym_index dynsym_index;
2367 },
2368 }
2369 },
2370 .prev_in_node = .empty,
2371 .next_in_node = old_head,
2372 };
2373
2374 if (old_head != .empty) {
2375 const old_head_ptr = elf.globalByName(old_head).?;
2376 assert(old_head_ptr.symtab_index.ptr(elf).node == opts.node);
2377 assert(old_head_ptr.prev_in_node == .empty);
2378 old_head_ptr.prev_in_node = opts.name.strtab;
2379 }
2380
2381 if (force_local_bind) {
2382 elf.moveDemotedGlobal(new_global_ptr);
2383 }
2384
2385 switch (@"type") {
2386 .FUNC, .GNU_IFUNC => if (elf.ehdrType() != .REL and
2387 elf.classifySymbolValue(.global(opts.name.strtab)) == .dynamic)
2388 {
2389 // This STT_FUNC symbol might be defined externally, so it needs a PLT entry.
2390 elf.addPltEntry(opts.name.strtab, new_global_ptr.dynsym_index);
2391 },
2392 else => {},
2393 }
2394
2395 return .global(opts.name.strtab);
2396}
2397fn setGlobalSymbolValue(
2398 elf: *Elf,
2399 global_name: String(.strtab),
2400 global_ptr: *Symbol.Global,
2401 new: struct {
2402 node: MappedFile.Node.Index.Optional,
2403 value: u64,
2404 size: u64,
2405 type: std.elf.STT,
2406 shndx: Section.Index,
2407 },
2408) void {
2409 assert(new.shndx != .UNDEF);
2410 if (global_ptr.symtab_index.ptr(elf).node.unwrap()) |old_node| {
2411 if (global_ptr.next_in_node != .empty) {
2412 const next = elf.globalByName(global_ptr.next_in_node).?;
2413 assert(next.prev_in_node == global_name);
2414 assert(next.symtab_index.ptr(elf).node.unwrap().? == old_node);
2415 next.prev_in_node = global_ptr.prev_in_node;
2416 }
2417 if (global_ptr.prev_in_node != .empty) {
2418 const prev = elf.globalByName(global_ptr.prev_in_node).?;
2419 assert(prev.next_in_node == global_name);
2420 assert(prev.symtab_index.ptr(elf).node.unwrap().? == old_node);
2421 prev.next_in_node = global_ptr.next_in_node;
2422 } else {
2423 // We're the start of the linked list, so we need to change the head.
2424 if (global_ptr.next_in_node == .empty) {
2425 assert(elf.node_global_symbols.fetchSwapRemove(old_node).?.value == global_name);
2426 } else {
2427 elf.node_global_symbols.getPtr(old_node).?.* = global_ptr.next_in_node;
2428 }
2429 }
2430 } else {
2431 assert(global_ptr.next_in_node == .empty);
2432 assert(global_ptr.prev_in_node == .empty);
2433 }
2434
2435 if (elf.copied_globals.fetchSwapRemove(global_name)) |copied_global_kv| {
2436 // This is a quite rare case: there was a definition for this symbol in a shared library
2437 // input, and we ended up emitting a copy relocation for it, but we've now got our *own*
2438 // definition which replaces it. We know that our definition cannot be preempted because we
2439 // are the executable (only executables can have copy relocations!), so we definitely do not
2440 // need the copy relocation.
2441
2442 // All we actually need to do is remove the entry from `copied_globals` (already done), and
2443 // delete the actual `R_*_COPY` relocation. Of course, we also need to re-apply relocations
2444 // targeting this symbol, but we were going to do that at the end of this function anyway.
2445 elf.shndx.rela_dyn.relaDeleteOne(elf, copied_global_kv.value.rela_index);
2446 // TODO: once `MappedFile` has a way to delete a node (so it can re-use the space), we
2447 // should delete `copied_global_kv.value.node`, which is an "orphaned" `copied_global` node.
2448 } else {
2449 _ = elf.want_copied_globals.swapRemove(global_name);
2450 }
2451
2452 global_ptr.symtab_index.ptr(elf).node = new.node;
2453
2454 const old_head: String(.strtab) = old_head: {
2455 const new_node = new.node.unwrap() orelse break :old_head .empty;
2456 const gop = elf.node_global_symbols.getOrPutAssumeCapacity(new_node);
2457 const old_head: String(.strtab) = if (gop.found_existing) gop.value_ptr.* else .empty;
2458 gop.value_ptr.* = global_name;
2459 break :old_head old_head;
2460 };
2461
2462 global_ptr.prev_in_node = .empty;
2463 global_ptr.next_in_node = old_head;
2464
2465 if (old_head != .empty) {
2466 const old_head_ptr = elf.globalByName(old_head).?;
2467 assert(old_head_ptr.symtab_index.ptr(elf).node == new.node);
2468 assert(old_head_ptr.prev_in_node == .empty);
2469 old_head_ptr.prev_in_node = global_name;
2470 }
2471
2472 // Now for the easy bit where we actually update the symtab entry.
2473 switch (elf.symPtr(global_ptr.symtab_index)) {
2474 inline else => |sym| {
2475 // Don't bother with `sym.value` here: it'll be updated by `flushMoved`.
2476 elf.targetStore(&sym.size, @intCast(new.size));
2477 elf.targetStore(&sym.shndx, new.shndx.toSection().?);
2478 const old_bind = elf.targetLoad(&sym.info).bind;
2479 elf.targetStore(&sym.info, .{
2480 .type = new.type,
2481 .bind = old_bind,
2482 });
2483 },
2484 }
2485
2486 // ...and also the dynsym entry if there is one.
2487 if (global_ptr.dynsym_index != 0) switch (elf.dynsymPtr(global_ptr.dynsym_index)) {
2488 inline else => |sym| {
2489 // Don't bother with `sym.value` here: it'll be updated by `flushMoved`.
2490 elf.targetStore(&sym.size, @intCast(new.size));
2491 elf.targetStore(&sym.shndx, new.shndx.toSection().?);
2492 const old_bind = elf.targetLoad(&sym.info).bind;
2493 elf.targetStore(&sym.info, .{
2494 .type = new.type,
2495 .bind = old_bind,
2496 });
2497 },
2498 };
2499
2500 // If this symbol was previously undefined, it may have had a PLT entry. If so, we now need to
2501 // delete its newly-unnecessary runtime relocation to avoid a runtime dynamic linker error.
2502 // This also allows the PLT entry to be reused---see `pltEntryIsDead`.
2503 if (elf.plt.getIndex(global_name)) |plt_index| {
2504 if (!elf.pltEntryIsDead(plt_index) and
2505 elf.classifySymbolValue(.global(global_name)) != .dynamic)
2506 {
2507 elf.shndx.rela_plt.relaDeleteOne(elf, @fromBackingInt(@intCast(plt_index)));
2508 assert(elf.pltEntryIsDead(plt_index));
2509 }
2510 }
2511
2512 // If this symbol was previously undefined, relocations targeting it may have been lowered to
2513 // runtime relocations which we have now discovered we do not need, so delete those. This does
2514 // not apply if the symbol is preemptible, which we check with `classifySymbolValue`.
2515 if (elf.shndx.dynamic != .UNDEF and elf.classifySymbolValue(.global(global_name)) != .dynamic) {
2516 Symbol.Id.global(global_name).deleteDynamicTargetRelocs(elf);
2517 }
2518
2519 // Finally, update the symbol value, re-applying target relocations. Also note that because we
2520 // possibly removed the PLT entry above, some relocations which were previously targeting the
2521 // PLT will now instead target the symbol itself.
2522 Symbol.Id.global(global_name).flushMoved(elf, new.value);
2523}
2524/// When the same global symbol appears in two inputs---even if one symbol is defined and the other
2525/// undefined---their visibility values are combined to determine the resulting visibility, which
2526/// can also affect the bind of the symbol we output.
2527fn mergeGlobalSymbolVisibility(elf: *Elf, global_ptr: *Symbol.Global, other_visibility: std.elf.STV, bind: enum { strong, weak }) void {
2528 const old_visibility: std.elf.STV = switch (elf.symPtr(global_ptr.symtab_index)) {
2529 inline else => |sym| elf.targetLoad(&sym.other).visibility,
2530 };
2531 // The combined visibility is essentially the "strictest" of the two, with most strict being
2532 // INTERNAL, followed by HIDDEN, PROTECTED, DEFAULT.
2533 const new_visibility: std.elf.STV, const newly_hidden: bool = switch (old_visibility) {
2534 .INTERNAL => .{ .INTERNAL, false },
2535 .HIDDEN => switch (other_visibility) {
2536 .INTERNAL => .{ .INTERNAL, false },
2537 .HIDDEN, .PROTECTED, .DEFAULT => .{ .HIDDEN, false },
2538 },
2539 .PROTECTED => switch (other_visibility) {
2540 .INTERNAL => .{ .INTERNAL, true },
2541 .HIDDEN => .{ .HIDDEN, true },
2542 .PROTECTED, .DEFAULT => .{ .PROTECTED, false },
2543 },
2544 .DEFAULT => switch (other_visibility) {
2545 .INTERNAL => .{ .INTERNAL, true },
2546 .HIDDEN => .{ .HIDDEN, true },
2547 .PROTECTED => .{ .PROTECTED, false },
2548 .DEFAULT => .{ .DEFAULT, false },
2549 },
2550 };
2551 // If the symbol is HIDDEN/INTERNAL and we're emitting an ELF module (executable or shared
2552 // object), then the symbol should have binding STB_LOCAL in the output. Therefore, if we are
2553 // putting the global in this state for the first time---let's call it "demoting" the global to
2554 // STB_LOCAL---we need to update its bind in the symtab.
2555 const demote_to_local = newly_hidden and elf.ehdrType() != .REL;
2556 switch (elf.symPtr(global_ptr.symtab_index)) {
2557 inline else => |sym, class| {
2558 const old_info = elf.targetLoad(&sym.info);
2559 const new_info: class.ElfN().Sym.Info = .{
2560 .type = old_info.type,
2561 .bind = if (demote_to_local) b: {
2562 assert(old_info.bind != .LOCAL);
2563 break :b .LOCAL;
2564 } else if (old_info.bind == .LOCAL) .LOCAL else switch (bind) {
2565 .strong => .GLOBAL,
2566 .weak => .WEAK,
2567 },
2568 };
2569 elf.targetStore(&sym.other, .{ .visibility = new_visibility });
2570 elf.targetStore(&sym.info, new_info);
2571 // also update dynsym
2572 if (global_ptr.dynsym_index != 0) {
2573 const dynsym = @field(elf.dynsymPtr(global_ptr.dynsym_index), @tagName(class));
2574 elf.targetStore(&dynsym.other, .{ .visibility = new_visibility });
2575 elf.targetStore(&dynsym.info, new_info);
2576 }
2577 },
2578 }
2579 if (demote_to_local) {
2580 // When demoting a global to STB_LOCAL, we need to move its symtab index so that it is with
2581 // the STB_LOCAL symbols instead of the global symbols.
2582 elf.moveDemotedGlobal(global_ptr);
2583 }
2584}
2585/// If a symbol which was STB_GLOBAL/STB_WEAK becomes STB_LOCAL (see `mergeGlobalSymbolVisibility`),
2586/// the symbol must be moved from the "globals" part of the symtab to the "locals" part, because ELF
2587/// requires that all STB_LOCAL symbols in a symbol table appear before any global symbols.
2588fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {
2589 assert(elf.ehdrType() != .REL); // demotion only happens when emitting an ELF module
2590 switch (elf.shdrPtr(.symtab)) {
2591 inline else => |shdr, class| {
2592 // `shdr.info` stores the index of the first global symbol. We are going to swap the
2593 // demoted symbol with that first global symbol, then increment that start index.
2594 const dest_index: Symbol.Index = @fromBackingInt(elf.targetLoad(&shdr.info));
2595 const src_index = global_ptr.symtab_index;
2596
2597 // This global should currently be in the "global symbols" part of the symtab, since our
2598 // job is to move it *out* of that part:
2599 assert(@backingInt(src_index) >= @backingInt(dest_index));
2600
2601 elf.targetStore(&shdr.info, @backingInt(dest_index) + 1);
2602
2603 if (src_index != dest_index) {
2604 // The demoted global was not the first global in the symtab, so we need to swap it
2605 // to its new location.
2606
2607 const src_sym_ptr = @field(elf.symPtr(src_index), @tagName(class));
2608 const dest_sym_ptr = @field(elf.symPtr(dest_index), @tagName(class));
2609
2610 const this_name: String(.strtab) = @fromBackingInt(elf.targetLoad(&src_sym_ptr.name));
2611 assert(elf.globalByName(this_name).? == global_ptr);
2612
2613 const other_name: String(.strtab) = @fromBackingInt(elf.targetLoad(&dest_sym_ptr.name));
2614 const other_global_ptr = elf.globalByName(other_name).?;
2615 assert(other_global_ptr.symtab_index == dest_index);
2616
2617 // First swap the symtab entries...
2618 std.mem.swap(class.ElfN().Sym, src_sym_ptr, dest_sym_ptr);
2619 // ...then the `elf.symtab` metadata...
2620 std.mem.swap(Symbol, src_index.ptr(elf), dest_index.ptr(elf));
2621 // ...then update the `elf.globals` tracking.
2622 global_ptr.symtab_index = dest_index;
2623 other_global_ptr.symtab_index = src_index;
2624 }
2625
2626 // We also need to get rid of the dynsym entry if there is one. To keep dynsym compact,
2627 // we'll move another symbol into its place just like we did above.
2628 if (global_ptr.dynsym_index != 0) {
2629 const dynsym_shdr = @field(elf.shdrPtr(elf.shndx.dynsym), @tagName(class));
2630
2631 const ent_size = @sizeOf(class.ElfN().Sym);
2632 assert(elf.targetLoad(&dynsym_shdr.entsize) == ent_size);
2633
2634 // We're going to decrease the size of `.dynsym`, thereby removing its last index.
2635 const old_size = elf.targetLoad(&dynsym_shdr.size);
2636 const new_size = old_size - ent_size;
2637 const remove_dynsym_index: u32 = @intCast(@divExact(new_size, ent_size));
2638
2639 elf.popDynsymHashEntry(remove_dynsym_index);
2640
2641 const free_dynsym_index = global_ptr.dynsym_index;
2642 global_ptr.dynsym_index = 0;
2643
2644 if (free_dynsym_index != remove_dynsym_index) {
2645 // The demoted global wasn't the last entry, so move whatever entry we just
2646 // truncated out of dynsym into its place.
2647
2648 elf.clearDynsymHashEntry(free_dynsym_index);
2649
2650 const src_dynsym_ptr = @field(elf.dynsymPtr(remove_dynsym_index), @tagName(class));
2651 const dest_dynsym_ptr = @field(elf.dynsymPtr(free_dynsym_index), @tagName(class));
2652
2653 const moved_name_dynstr: String(.dynstr) = @fromBackingInt(elf.targetLoad(&src_dynsym_ptr.name));
2654 const moved_name = elf.stringExisting(.strtab, moved_name_dynstr.slice(elf));
2655 const moved_global_ptr = elf.globalByName(moved_name).?;
2656
2657 dest_dynsym_ptr.* = src_dynsym_ptr.*;
2658
2659 assert(moved_global_ptr.dynsym_index == remove_dynsym_index);
2660 moved_global_ptr.dynsym_index = free_dynsym_index;
2661
2662 elf.populateDynsymHashEntry(free_dynsym_index);
2663
2664 // Since that symbol's dynsym index has changed, we'll have to update any
2665 // relocation entries targeting it.
2666 elf.changed_symtab_index.putAssumeCapacity(moved_name, {});
2667 }
2668
2669 // Now that we've given that symbol a new home, actually decrease the section size.
2670 elf.targetStore(&dynsym_shdr.size, new_size);
2671 }
2672 },
2673 }
2674}
2675
2676const Symbol = struct {
2677 /// The node which this symbol's value is defined relative to. Possible values are:
2678 /// * `.none` for a SHN_ABS or SHN_UNDEF symbol
2679 /// * A section (the symbol's value is some vaddr in that section)
2680 /// * An input section (the symbol's value is some vaddr in that input section)
2681 /// * A NAV, UAV, or lazy code/data (the symbol's value is exactly the vaddr of that node)
2682 node: MappedFile.Node.Index.Optional,
2683
2684 /// The head of a linked list of relocations targeting this symbol.
2685 first_target_reloc: SymbolReloc.Index,
2686
2687 const Global = struct {
2688 /// The current index of the symtab entry for this global symbol.
2689 symtab_index: Symbol.Index,
2690 /// The current index of the dynsym entry for this global symbol. If the global has been
2691 /// demoted to STB_LOCAL, it does not have a dynsym entry and this field is set to 0.
2692 dynsym_index: u32,
2693
2694 /// The next entry in a linked list of global symbols with the same `Symbol.node` value.
2695 ///
2696 /// If `node` is `.none`, this is `.empty`.
2697 next_in_node: String(.strtab),
2698 /// The previous entry in a linked list of global symbols with the same `Symbol.node` value.
2699 ///
2700 /// If `node` is `.none`, this is `.empty`.
2701 prev_in_node: String(.strtab),
2702 };
2703
2704 /// An index directly into the symtab. These values are not stable (global symbols are sometimes
2705 /// moved to new locations in the symtab) and therefore should only be used ephemerally.
2706 ///
2707 /// Local symbols *do* have stable indices into the symtab; see `LocalIndex`.
2708 ///
2709 /// For a stable reference to an arbitrary symbol, see `Id`.
2710 const Index = enum(u32) {
2711 null = 0,
2712 _,
2713
2714 fn ptr(si: Symbol.Index, elf: *Elf) *Symbol {
2715 return &elf.symtab.items[@backingInt(si)];
2716 }
2717 };
2718
2719 /// A `LocalIndex` is a raw index into the symtab like `Index`, but it guarantees that the
2720 /// symbol in question has STB_LOCAL binding, which guarantees that its symtab index is stable
2721 /// so can be stored long-term without needing to be updated
2722 ///
2723 /// This is because symbols which have STB_LOCAL binding in the output file gain fixed symtab
2724 /// indices, thanks to a combination of a few factors:
2725 /// * We never remove STB_LOCAL symbols
2726 /// * There is no symbol ordering requirement *within* the leading range of STB_LOCAL symbols
2727 /// * A symbol visibility which demotes a global to STB_LOCAL binding can never be reverted by
2728 /// a subsequent operation (different visibilities resolve to the "strictest" one)
2729 const LocalIndex = enum(u32) {
2730 null = 0,
2731 _,
2732
2733 fn index(li: LocalIndex) Index {
2734 return @fromBackingInt(@backingInt(li));
2735 }
2736 };
2737
2738 /// Opaque, stable identifier for a symbol. Does not necessarily equal the index into the symtab.
2739 const Id = packed struct(u32) {
2740 kind: enum(u1) { local, global },
2741 raw: u31,
2742
2743 const @"null": Symbol.Id = .local(.null);
2744
2745 fn local(lsi: Symbol.LocalIndex) Symbol.Id {
2746 return .{ .kind = .local, .raw = @intCast(@backingInt(lsi)) };
2747 }
2748 fn global(name: String(.strtab)) Symbol.Id {
2749 return .{ .kind = .global, .raw = @intCast(@backingInt(name)) };
2750 }
2751 fn unwrap(s: Symbol.Id) union(enum) {
2752 local: Symbol.LocalIndex,
2753 global: String(.strtab),
2754 } {
2755 return switch (s.kind) {
2756 .local => .{ .local = @fromBackingInt(s.raw) },
2757 .global => .{ .global = @fromBackingInt(s.raw) },
2758 };
2759 }
2760
2761 fn toTypeErased(s: Symbol.Id) link.File.SymbolId {
2762 return @bitCast(s);
2763 }
2764 fn fromTypeErased(s: link.File.SymbolId) Symbol.Id {
2765 return @bitCast(s);
2766 }
2767
2768 fn index(s: Symbol.Id, elf: *const Elf) Symbol.Index {
2769 return switch (s.unwrap()) {
2770 .local => |lsi| lsi.index(),
2771 .global => |name| elf.globalByName(name).?.symtab_index,
2772 };
2773 }
2774
2775 /// Returns the value of this symbol, or 0 if it is undefined. If the symbol is an undefined
2776 /// global for which we have emitted a copy relocation, returns the virtual address of that
2777 /// copy relocation, which the symbol is guaranteed to resolve to at runtime.
2778 fn value(s: Symbol.Id, elf: *Elf) u64 {
2779 return switch (elf.symPtr(s.index(elf))) {
2780 inline else => |sym| elf.targetLoad(&sym.value),
2781 };
2782 }
2783
2784 fn flushMoved(sym_id: Symbol.Id, elf: *Elf, new_value: u64) void {
2785 // Update the symbol value in `.symtab`
2786 const sym_index = sym_id.index(elf);
2787 switch (elf.symPtr(sym_index)) {
2788 inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)),
2789 }
2790
2791 // Update the symbol value in `.dynsym` if applicable
2792 switch (sym_id.unwrap()) {
2793 .local => {},
2794 .global => |name| {
2795 const g = elf.globalByName(name).?;
2796 if (g.dynsym_index != 0) {
2797 switch (elf.dynsymPtr(g.dynsym_index)) {
2798 inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)),
2799 }
2800 }
2801 },
2802 }
2803
2804 // Re-apply relocations targeting this symbol
2805 if (elf.ehdrType() != .REL) {
2806 sym_id.applyTargetRelocs(elf);
2807 }
2808
2809 // Update GOT entries targeting this symbol
2810 if (elf.got.getIndex(.{ .symbol = sym_id })) |got_index| {
2811 elf.updateGotEntry(got_index);
2812 }
2813 if (elf.got.getIndex(.{ .tpoff = sym_id })) |got_index| {
2814 elf.updateGotEntry(got_index);
2815 }
2816 if (elf.got.getIndex(.{ .tlsgd0 = sym_id })) |got_index| {
2817 elf.updateGotEntry(got_index);
2818 elf.updateGotEntry(got_index + 1); // tlsgd1
2819 }
2820 }
2821
2822 fn applyTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void {
2823 assert(elf.ehdrType() != .REL);
2824 var ri = sym_id.index(elf).ptr(elf).first_target_reloc;
2825 while (ri != .none) {
2826 const reloc = ri.get(elf);
2827 assert(reloc.target == sym_id);
2828 reloc.apply(elf);
2829 ri = reloc.next;
2830 }
2831 }
2832
2833 /// Scans through all relocations targeting `sym_id` and, for each one with a dynamic
2834 /// relocation entry, either deletes it or converts it to R_*_RELATIVE as required.
2835 ///
2836 /// Asserts we are creating a DSO.
2837 fn deleteDynamicTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void {
2838 assert(elf.ehdrType() != .REL);
2839 assert(elf.shndx.dynamic != .UNDEF);
2840 var ri = sym_id.index(elf).ptr(elf).first_target_reloc;
2841 while (ri != .none) {
2842 const reloc = ri.get(elf);
2843 assert(reloc.target == sym_id);
2844 reloc.deleteOutputRel(elf);
2845 ri = reloc.next;
2846 }
2847 switch (elf.classifySymbolValue(sym_id)) {
2848 .static => return,
2849 .static_relative => {},
2850 .dynamic => unreachable,
2851 }
2852 // We removed the symbol relocations, now add R_*_RELATIVE relocations where needed.
2853 ri = sym_id.index(elf).ptr(elf).first_target_reloc;
2854 while (ri != .none) {
2855 const reloc = ri.get(elf);
2856 ri = reloc.next;
2857 assert(reloc.target == sym_id);
2858 switch (reloc.type.target) {
2859 // Only relocations which resolve to absolute addresses require runtime
2860 // `R_*_RELATIVE` relocations.
2861 .special,
2862 .pltrel,
2863 .rel,
2864 .dtpoff,
2865 .tpoff,
2866 .size,
2867 => continue,
2868
2869 .abs, .pltabs => {},
2870 }
2871 if (!reloc.type.action.simple.dest.isAddr(elf)) continue;
2872 switch (elf.nodeWantsDsoRelocation(reloc.node)) {
2873 .no => continue,
2874 .yes_textrel => elf.textrel_count += 1,
2875 .yes => {},
2876 }
2877 // There is capacity for a relocation because we just deleted one earlier.
2878 reloc.rela_index = elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
2879 .type = .relative(elf),
2880 .offset = elf.getNodeVAddr(reloc.node) + reloc.offset,
2881 .raw_sym_index = 0,
2882 .addend = 0,
2883 }).toOptional();
2884 }
2885 }
2886
2887 /// Returns `true` if the target of `s` has moved, meaning the symbol's value will change at
2888 /// some point due to a call to `flushMoved`.
2889 fn hasMoved(s: Symbol.Id, elf: *Elf) bool {
2890 if (s.index(elf).ptr(elf).node.unwrap()) |node| {
2891 return node.hasMoved(&elf.mf);
2892 }
2893 switch (s.unwrap()) {
2894 .local => {},
2895 .global => |name| if (elf.copied_globals.getPtr(name)) |copied_global| {
2896 return copied_global.node.hasMoved(&elf.mf);
2897 },
2898 }
2899 return false;
2900 }
2901 };
2902};
2903
2904fn globalByName(elf: *const Elf, name: String(.strtab)) ?*Symbol.Global {
2905 if (elf.globals.strong_def.getPtr(name)) |ptr| return ptr;
2906 if (elf.globals.weak_def.getPtr(name)) |ptr| return ptr;
2907 if (elf.globals.strong_undef.getPtr(name)) |ptr| return ptr;
2908 if (elf.globals.weak_undef.getPtr(name)) |ptr| return ptr;
2909 return null;
2910}
2911
2912fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {
2913 /// This symbol's value is guaranteed to equal `sym.value(elf)`.
2914 static,
2915 /// This symbol's value is an offset of `sym.value(elf)` from the runtime-known load address of
2916 /// this DSO (which is position-independent).
2917 static_relative,
2918 /// This symbol's definition does not necessarily come from this DSO, so is not known until RTLD
2919 /// runs. Therefore, a dynamic (runtime) relocation is necessary.
2920 dynamic,
2921} {
2922 const comp = elf.base.comp;
2923
2924 const runtime_load_addr = switch (elf.ehdrType()) {
2925 .REL => unreachable,
2926 .DYN => true,
2927 .EXEC => false,
2928 };
2929
2930 if (elf.shndx.dynamic == .UNDEF) {
2931 // This is a static non-PIE executable---every symbol has a statically known value.
2932 return .static;
2933 }
2934
2935 const shndx: Section.Index, const visibility: std.elf.STV = switch (elf.symPtr(sym.index(elf))) {
2936 inline else => |sym_ptr| .{
2937 .fromSection(elf.targetLoad(&sym_ptr.shndx)),
2938 elf.targetLoad(&sym_ptr.other).visibility,
2939 },
2940 };
2941
2942 switch (sym.unwrap()) {
2943 .local => {
2944 assert(shndx != .UNDEF);
2945 assert(visibility == .DEFAULT);
2946 },
2947 .global => |name| if (visibility == .DEFAULT and comp.config.output_mode != .Exe) {
2948 // An unprotected symbol in a DSO which is not an executable is subject to runtime
2949 // preemption, so a dynamic relocation is required for it even if we have a definition.
2950 return .dynamic;
2951 } else if (elf.copied_globals.contains(name)) {
2952 // This becomes a locally-defined symbol in `.data`.
2953 return if (runtime_load_addr) .static_relative else .static;
2954 },
2955 }
2956
2957 return switch (shndx) {
2958 .UNDEF => switch (visibility) {
2959 .DEFAULT => if (comp.config.link_mode == .static and comp.config.output_mode == .Exe) {
2960 assert(comp.config.pie); // non-PIE static exe should not have a `.dynamic` section
2961 // This is a static PIE---the only dynamic relocations are `R_*_RELATIVE`.
2962 return .static;
2963 } else .dynamic, // external symbol
2964
2965 // If the symbol *cannot* be external, then there's no point making a dynamic relocation
2966 // now---if linking succeeds we won't need anything more than perhaps an `R_*_RELATIVE`.
2967 .INTERNAL, .HIDDEN, .PROTECTED => .static,
2968 },
2969
2970 .ABS => .static,
2971
2972 else => if (runtime_load_addr and
2973 shndx.flags(elf).ALLOC and
2974 !shndx.flags(elf).TLS)
2975 {
2976 return .static_relative;
2977 } else {
2978 return .static;
2979 },
2980 };
2981}
2982
2983pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId {
2984 const lsi: Symbol.LocalIndex = switch (elf.getNode(Node.fromAtom(atom))) {
2985 .archive,
2986 .archive_header,
2987 .archive_input_member,
2988 .archive_elf_member_header,
2989 .elf,
2990 .ehdr,
2991 .shdr,
2992 .segment,
2993 .section,
2994 .input_section,
2995 .copied_global,
2996 => unreachable,
2997
2998 inline .nav,
2999 .uav,
3000 .lazy_code,
3001 .lazy_const_data,
3002 => |i| i.symbol(elf),
3003 };
3004 const s: Symbol.Id = .local(lsi);
3005 return s.toTypeErased();
3006}
3007pub fn lazySymbol(elf: *Elf, lazy: link.File.LazySymbol) link.Error!link.File.SymbolId {
3008 const diags = &elf.base.comp.link_diags;
3009 return elf.lazySymbolInner(lazy) catch |err| switch (err) {
3010 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
3011 else => |e| return e,
3012 };
3013}
3014fn lazySymbolInner(elf: *Elf, lazy: link.File.LazySymbol) Error!link.File.SymbolId {
3015 const gpa = elf.base.comp.gpa;
3016
3017 try elf.ensureUnusedSymbolCapacity(1, .all_local);
3018 try elf.nodes.ensureUnusedCapacity(gpa, 1);
3019 try elf.lazy.getPtr(lazy.kind).map.ensureUnusedCapacity(gpa, 1);
3020
3021 const gop = elf.lazy.getPtr(lazy.kind).map.getOrPutAssumeCapacity(lazy.ty);
3022 if (!gop.found_existing) {
3023 const shndx: Section.Index, const sym_type: std.elf.STT = switch (lazy.kind) {
3024 .code => .{ .text, .FUNC },
3025 .const_data => .{ .rodata, .OBJECT },
3026 };
3027 const node = try shndx.get(elf).ni.addFloatingChild(&elf.mf, gpa, .{});
3028 var name_buf: [64]u8 = undefined;
3029 const name = std.mem.print(
3030 &name_buf,
3031 "__lazy_{t}_{d}",
3032 .{ lazy.kind, @backingInt(lazy.ty) },
3033 ) catch unreachable;
3034 gop.value_ptr.* = .{
3035 .lsi = elf.addLocalSymbolAssumeCapacity(.{
3036 .node = .wrap(node),
3037 .name = try elf.string(.strtab, name),
3038 .value = 0,
3039 .size = 0,
3040 .type = sym_type,
3041 .shndx = shndx,
3042 }),
3043 .first_symbol_reloc = .none,
3044 .first_got_reloc = .none,
3045 };
3046 elf.nodes.appendAssumeCapacity(switch (lazy.kind) {
3047 .code => .{ .lazy_code = @fromBackingInt(@intCast(gop.index)) },
3048 .const_data => .{ .lazy_const_data = @fromBackingInt(@intCast(gop.index)) },
3049 });
3050 elf.synth_prog_node.increaseEstimatedTotalItems(1);
3051 }
3052 const s: Symbol.Id = .local(gop.value_ptr.lsi);
3053 return s.toTypeErased();
3054}
3055pub const ExternSymbolOpts = struct {
3056 name: []const u8,
3057 lib_name: ?[]const u8,
3058 type: std.elf.STT,
3059 linkage: std.lang.GlobalLinkage = .strong,
3060 visibility: std.lang.SymbolVisibility = .default,
3061};
3062pub fn externSymbol(elf: *Elf, opts: ExternSymbolOpts) link.Error!link.File.SymbolId {
3063 const diags = &elf.base.comp.link_diags;
3064 return (elf.externSymbolInner(opts) catch |err| switch (err) {
3065 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
3066 else => |e| return e,
3067 }).toTypeErased();
3068}
3069fn externSymbolInner(elf: *Elf, opts: ExternSymbolOpts) Error!Symbol.Id {
3070 try elf.ensureUnusedSymbolCapacity(1, .maybe_global);
3071 const symbol = elf.addGlobalSymbolAssumeCapacity(.{
3072 .node = .none,
3073 .name = try .string(elf, opts.name),
3074 .lib_name = opts.lib_name,
3075 .value = 0,
3076 .size = 0,
3077 .type = opts.type,
3078 .bind = switch (opts.linkage) {
3079 .strong => .strong,
3080 .weak => .weak,
3081 .internal => return elf.base.comp.link_diags.fail("TODO(Elf2): '.internal' linkage", .{}),
3082 .link_once => return elf.base.comp.link_diags.fail("TODO(Elf2): '.link_once' linkage", .{}),
3083 },
3084 .visibility = switch (opts.visibility) {
3085 .default => .DEFAULT,
3086 .hidden => .HIDDEN,
3087 .protected => .PROTECTED,
3088 },
3089 .shndx = .UNDEF,
3090 }) catch |err| switch (err) {
3091 error.MultipleDefinitions => unreachable, // shndx is undef
3092 };
3093 return symbol;
3094}
3095pub fn addReloc(
3096 elf: *Elf,
3097 atom: link.File.AtomId,
3098 offset: u64,
3099 target: link.File.SymbolId,
3100 addend: i64,
3101 @"type": MachineRelocType,
3102) link.Error!void {
3103 const node: MappedFile.Node.Index = Node.fromAtom(atom);
3104 const diags = &elf.base.comp.link_diags;
3105 elf.ensureUnusedRelocCapacity(node, 1) catch |err| switch (err) {
3106 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
3107 else => |e| return e,
3108 };
3109 elf.addRelocAssumeCapacity(node, offset, .fromTypeErased(target), addend, @"type") catch |err| switch (err) {
3110 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
3111 error.UnknownRelocation => unreachable, // codegen bug
3112 error.NonStaticRelocation => unreachable, // codegen bug
3113 error.UnimplementedRelocation => unreachable, // codegen bug (asking Elf2 for a relocation it does not support)
3114 else => |e| return e,
3115 };
3116}
3117pub fn navSymbol(elf: *Elf, nav_index: InternPool.Nav.Index) link.Error!link.File.SymbolId {
3118 const diags = &elf.base.comp.link_diags;
3119 const zcu = elf.base.comp.zcu.?;
3120 const ip = &zcu.intern_pool;
3121 const nav = ip.getNav(nav_index);
3122 if (nav.getExtern(ip)) |@"extern"| {
3123 return elf.externSymbol(.{
3124 .name = @"extern".name.toSlice(ip),
3125 .lib_name = @"extern".lib_name.toSlice(ip),
3126 .type = elf.navType(nav.resolved.?),
3127 .linkage = @"extern".linkage,
3128 .visibility = @"extern".visibility,
3129 });
3130 }
3131 const nmi = elf.navMapIndex(zcu, nav_index) catch |err| switch (err) {
3132 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
3133 else => |e| return e,
3134 };
3135 const s: Symbol.Id = .local(nmi.symbol(elf));
3136 return s.toTypeErased();
3137}
3138pub fn uavSymbol(
3139 elf: *Elf,
3140 uav_val: InternPool.Index,
3141 uav_align: InternPool.Alignment,
3142) link.Error!link.File.SymbolId {
3143 const diags = &elf.base.comp.link_diags;
3144 const umi = elf.uavMapIndex(uav_val, uav_align) catch |err| switch (err) {
3145 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
3146 else => |e| return e,
3147 };
3148 const s: Symbol.Id = .local(umi.symbol(elf));
3149 return s.toTypeErased();
3150}
3151pub fn getNavVAddr(
3152 elf: *Elf,
3153 pt: Zcu.PerThread,
3154 nav: InternPool.Nav.Index,
3155 reloc_info: link.File.RelocInfo,
3156) link.Error!u64 {
3157 _ = pt;
3158 return elf.getVAddr(reloc_info, try elf.navSymbol(nav));
3159}
3160pub fn getUavVAddr(
3161 elf: *Elf,
3162 uav_val: InternPool.Index,
3163 reloc_info: link.File.RelocInfo,
3164) link.Error!u64 {
3165 return elf.getVAddr(reloc_info, try elf.uavSymbol(uav_val, .none));
3166}
3167pub fn getVAddr(elf: *Elf, reloc_info: link.File.RelocInfo, target: link.File.SymbolId) link.Error!u64 {
3168 try elf.addReloc(
3169 reloc_info.parent.atom_index,
3170 reloc_info.offset,
3171 target,
3172 reloc_info.addend,
3173 .absAddr(elf),
3174 );
3175 return Symbol.Id.fromTypeErased(target).value(elf);
3176}
3177pub fn lowerUav(
3178 elf: *Elf,
3179 pt: Zcu.PerThread,
3180 uav_val: InternPool.Index,
3181 uav_align: InternPool.Alignment,
3182) link.Error!link.File.SymbolId {
3183 _ = pt;
3184 const diags = &elf.base.comp.link_diags;
3185 const umi = elf.uavMapIndex(uav_val, uav_align) catch |err| switch (err) {
3186 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
3187 else => |e| return e,
3188 };
3189 const s: Symbol.Id = .local(umi.symbol(elf));
3190 return s.toTypeErased();
3191}
3192
3193const StringSection = enum {
3194 shstrtab,
3195 strtab,
3196 dynstr,
3197 fn shndx(s: StringSection, elf: *const Elf) Section.Index {
3198 return switch (s) {
3199 .strtab => .strtab,
3200 .shstrtab => .shstrtab,
3201 .dynstr => elf.shndx.dynstr,
3202 };
3203 }
3204};
3205fn String(section: StringSection) type {
3206 return enum(u32) {
3207 empty = 0,
3208 _,
3209
3210 fn slice(str: @This(), elf: *Elf) [:0]const u8 {
3211 const section_node = section.shndx(elf).get(elf).ni;
3212 const overlong = section_node.sliceConst(&elf.mf)[@backingInt(str)..];
3213 return overlong[0..std.mem.findScalar(u8, overlong, 0).? :0];
3214 }
3215 };
3216}
3217fn string(elf: *Elf, comptime section: StringSection, key: []const u8) Error!String(section) {
3218 const st: *StringTable = &@field(elf, @tagName(section));
3219 return @fromBackingInt(try st.get(elf, section.shndx(elf), key));
3220}
3221/// Like `string`, but asserts that the string is already in `section`.
3222fn stringExisting(elf: *Elf, comptime section: StringSection, key: []const u8) String(section) {
3223 const st: *StringTable = &@field(elf, @tagName(section));
3224 return @fromBackingInt(st.getExisting(elf, section.shndx(elf), key));
3225}
3226
3227const StringTable = struct {
3228 map: std.HashMapUnmanaged(u32, void, StringTable.Context, std.hash_map.default_max_load_percentage),
3229
3230 const Context = struct {
3231 slice: []const u8,
3232
3233 pub fn eql(_: Context, lhs_key: u32, rhs_key: u32) bool {
3234 return lhs_key == rhs_key;
3235 }
3236
3237 pub fn hash(ctx: Context, key: u32) u64 {
3238 return std.hash_map.hashString(std.mem.sliceTo(ctx.slice[key..], 0));
3239 }
3240 };
3241
3242 const Adapter = struct {
3243 slice: []const u8,
3244
3245 pub fn eql(adapter: Adapter, lhs_key: []const u8, rhs_key: u32) bool {
3246 return std.mem.startsWith(u8, adapter.slice[rhs_key..], lhs_key) and
3247 adapter.slice[rhs_key + lhs_key.len] == 0;
3248 }
3249
3250 pub fn hash(_: Adapter, key: []const u8) u64 {
3251 assert(std.mem.findScalar(u8, key, 0) == null);
3252 return std.hash_map.hashString(key);
3253 }
3254 };
3255
3256 fn getExisting(st: *StringTable, elf: *Elf, shndx: Section.Index, key: []const u8) u32 {
3257 if (key.len == 0) return 0;
3258 const slice_const = shndx.get(elf).ni.sliceConst(&elf.mf);
3259 const adapter: StringTable.Adapter = .{ .slice = slice_const };
3260 return st.map.getKeyAdapted(key, adapter).?;
3261 }
3262
3263 fn get(st: *StringTable, elf: *Elf, shndx: Section.Index, key: []const u8) Error!u32 {
3264 // If we are in `initHeaders` the strtab might not be initalized yet, so we need to special
3265 // case the empty string.
3266 if (key.len == 0) return 0;
3267
3268 const gpa = elf.base.comp.gpa;
3269 const ni = shndx.get(elf).ni;
3270 const slice_const = ni.sliceConst(&elf.mf);
3271 const gop = try st.map.getOrPutContextAdapted(
3272 gpa,
3273 key,
3274 StringTable.Adapter{ .slice = slice_const },
3275 .{ .slice = slice_const },
3276 );
3277 if (gop.found_existing) return gop.key_ptr.*;
3278 const old_size, const new_size = size: switch (elf.shdrPtr(shndx)) {
3279 inline else => |shdr| {
3280 const old_size: u32 = @intCast(elf.targetLoad(&shdr.size));
3281 const new_size: u32 = @intCast(old_size + key.len + 1);
3282 elf.targetStore(&shdr.size, new_size);
3283 break :size .{ old_size, new_size };
3284 },
3285 };
3286 try ni.ensureMinimumSize(&elf.mf, gpa, new_size);
3287 const slice = ni.slice(&elf.mf)[old_size..];
3288 @memcpy(slice[0..key.len], key);
3289 slice[key.len] = 0;
3290 gop.key_ptr.* = old_size;
3291 return old_size;
3292 }
3293};
3294
3295pub fn open(
3296 arena: std.mem.Allocator,
3297 comp: *Compilation,
3298 path: std.Build.Cache.Path,
3299 options: link.File.OpenOptions,
3300) !*Elf {
3301 return create(arena, comp, path, options);
3302}
3303pub fn createEmpty(
3304 arena: std.mem.Allocator,
3305 comp: *Compilation,
3306 path: std.Build.Cache.Path,
3307 options: link.File.OpenOptions,
3308) !*Elf {
3309 return create(arena, comp, path, options);
3310}
3311fn create(
3312 arena: std.mem.Allocator,
3313 comp: *Compilation,
3314 path: std.Build.Cache.Path,
3315 options: link.File.OpenOptions,
3316) !*Elf {
3317 const io = comp.io;
3318 const target = &comp.root_mod.resolved_target.result;
3319 assert(target.ofmt == .elf);
3320 const class: std.elf.CLASS = switch (target.ptrBitWidth()) {
3321 0...32 => .@"32",
3322 33...64 => .@"64",
3323 else => return error.UnsupportedELFArchitecture,
3324 };
3325 const data: std.elf.DATA = switch (target.cpu.arch.endian()) {
3326 .little => .@"2LSB",
3327 .big => .@"2MSB",
3328 };
3329 const osabi: std.elf.OSABI = switch (target.os.tag) {
3330 else => if (target.abi.isGnu()) .GNU else .NONE,
3331 .freestanding, .other => .STANDALONE,
3332 .netbsd => .NETBSD,
3333 .illumos => .SOLARIS,
3334 .freebsd, .ps4 => .FREEBSD,
3335 .openbsd => .OPENBSD,
3336 .cuda => .CUDA,
3337 .amdhsa => .AMDGPU_HSA,
3338 .amdpal => .AMDGPU_PAL,
3339 .mesa3d => .AMDGPU_MESA3D,
3340 };
3341 const @"type": EhdrType = switch (comp.config.output_mode) {
3342 .Exe => if (comp.config.pie or target.os.tag == .haiku) .DYN else .EXEC,
3343 .Lib => switch (comp.config.link_mode) {
3344 .static => .REL,
3345 .dynamic => .DYN,
3346 },
3347 .Obj => .REL,
3348 };
3349 const machine = EhdrMachine.fromElf(target.toElfMachine()) orelse {
3350 std.debug.panic("TODO(Elf2): add support for target machine '{t}'", .{target.toElfMachine()});
3351 };
3352 const maybe_interp = switch (comp.config.link_mode) {
3353 .static => null,
3354 .dynamic => switch (comp.config.output_mode) {
3355 .Exe => target.dynamic_linker.get(),
3356 .Lib => if (comp.root_mod.resolved_target.is_explicit_dynamic_linker)
3357 target.dynamic_linker.get()
3358 else
3359 null,
3360 .Obj => null,
3361 },
3362 };
3363
3364 const elf = try arena.create(Elf);
3365 const file = try path.root_dir.handle.createFile(io, path.sub_path, .{
3366 .read = true,
3367 .permissions = link.File.determinePermissions(comp.config.output_mode, comp.config.link_mode),
3368 });
3369 errdefer file.close(io);
3370 elf.* = .{
3371 .base = .{
3372 .tag = .elf2,
3373
3374 .comp = comp,
3375 .emit = path,
3376
3377 .file = file,
3378 .gc_sections = false,
3379 .print_gc_sections = false,
3380 .build_id = .none,
3381 .allow_shlib_undefined = false,
3382 .stack_size = 0,
3383 },
3384 .options = options,
3385 .mf = try .init(file, comp.gpa, io),
3386 .ni = .{
3387 .elf = undefined,
3388 .ehdr = undefined,
3389 .shdr = undefined,
3390 .rodata = undefined,
3391 .phdr = undefined,
3392 .text = undefined,
3393 .data = undefined,
3394 .data_rel_ro = undefined,
3395 .tls = .none,
3396 },
3397 .archive = null,
3398 .nodes = .empty,
3399 .shdrs = .empty,
3400 .phdrs = .empty,
3401 .shndx = .{
3402 .got = .UNDEF,
3403 .got_plt = .UNDEF,
3404 .plt = .UNDEF,
3405 .plt_sec = .UNDEF,
3406 .dynsym = .UNDEF,
3407 .dynstr = .UNDEF,
3408 .dynamic = .UNDEF,
3409 .hash = .UNDEF,
3410 .tdata = .UNDEF,
3411 .rela_dyn = .UNDEF,
3412 .rela_plt = .UNDEF,
3413 .init_array = .UNDEF,
3414 .fini_array = .UNDEF,
3415 .preinit_array = .UNDEF,
3416 },
3417 .dynamic = .{
3418 .flags = 0,
3419 .flags_1 = 0,
3420 .rpath = .empty,
3421 .soname = .empty,
3422 },
3423 .symtab = .empty,
3424 .globals = .{
3425 .strong_def = .empty,
3426 .weak_def = .empty,
3427 .strong_undef = .empty,
3428 .weak_undef = .empty,
3429 },
3430 .copied_globals = .empty,
3431 .want_copied_globals = .empty,
3432 .node_global_symbols = .empty,
3433 .dso_globals = .empty,
3434 .shstrtab = .{ .map = .empty },
3435 .strtab = .{ .map = .empty },
3436 .dynstr = .{ .map = .empty },
3437 .got = .empty,
3438 .plt = .empty,
3439 .plt_first_symbol_reloc = .none,
3440 .needed = .empty,
3441 .inputs = .empty,
3442 .input_pending_index = 0,
3443 .input_sections = .empty,
3444 .input_section_pending_index = 0,
3445 .one_shot_fixups = .empty,
3446 .navs = .empty,
3447 .uavs = .empty,
3448 .lazy = comptime .initFill(.{
3449 .map = .empty,
3450 .pending_index = 0,
3451 }),
3452 .pending_uavs = .empty,
3453 .symbol_relocs = .empty,
3454 .got_relocs = .empty,
3455 .tls_size_symbol_relocs = .empty,
3456 .section_by_name = .empty,
3457 .changed_symtab_index = .empty,
3458 .textrel_count = 0,
3459 .overflowed_reloc_count = 0,
3460 .misaligned_reloc_count = 0,
3461 .const_prog_node = .none,
3462 .synth_prog_node = .none,
3463 .input_prog_node = .none,
3464 };
3465 errdefer elf.deinit();
3466
3467 try elf.initHeaders(class, data, osabi, @"type", machine, maybe_interp);
3468 return elf;
3469}
3470
3471pub fn deinit(elf: *Elf) void {
3472 const gpa = elf.base.comp.gpa;
3473 elf.mf.deinit(gpa);
3474 elf.nodes.deinit(gpa);
3475 elf.shdrs.deinit(gpa);
3476 elf.phdrs.deinit(gpa);
3477 elf.symtab.deinit(gpa);
3478 elf.globals.strong_def.deinit(gpa);
3479 elf.globals.weak_def.deinit(gpa);
3480 elf.globals.strong_undef.deinit(gpa);
3481 elf.globals.weak_undef.deinit(gpa);
3482 elf.copied_globals.deinit(gpa);
3483 elf.want_copied_globals.deinit(gpa);
3484 elf.node_global_symbols.deinit(gpa);
3485 elf.dso_globals.deinit(gpa);
3486 elf.shstrtab.map.deinit(gpa);
3487 elf.strtab.map.deinit(gpa);
3488 elf.dynstr.map.deinit(gpa);
3489 elf.got.deinit(gpa);
3490 elf.plt.deinit(gpa);
3491 elf.needed.deinit(gpa);
3492 for (elf.inputs.items) |input| if (input.member) |m| gpa.free(m);
3493 elf.inputs.deinit(gpa);
3494 elf.input_sections.deinit(gpa);
3495 elf.one_shot_fixups.deinit(gpa);
3496 elf.navs.deinit(gpa);
3497 elf.uavs.deinit(gpa);
3498 for (&elf.lazy.values) |*lazy| lazy.map.deinit(gpa);
3499 elf.pending_uavs.deinit(gpa);
3500 elf.symbol_relocs.deinit(gpa);
3501 elf.got_relocs.deinit(gpa);
3502 elf.tls_size_symbol_relocs.deinit(gpa);
3503 elf.section_by_name.deinit(gpa);
3504 elf.changed_symtab_index.deinit(gpa);
3505 elf.* = undefined;
3506}
3507
3508fn initHeaders(
3509 elf: *Elf,
3510 class: std.elf.CLASS,
3511 data: std.elf.DATA,
3512 osabi: std.elf.OSABI,
3513 @"type": EhdrType,
3514 machine: EhdrMachine,
3515 maybe_interp: ?[]const u8,
3516) Error!void {
3517 const comp = elf.base.comp;
3518 const gpa = comp.gpa;
3519
3520 const is_archive = comp.config.output_mode == .Lib and comp.config.link_mode == .static;
3521 const have_dynamic_section = switch (@"type") {
3522 .REL => false,
3523 .EXEC => comp.config.link_mode == .dynamic,
3524 .DYN => true,
3525 };
3526 const addr_align: Alignment = switch (class) {
3527 .NONE, _ => unreachable,
3528 .@"32" => .@"4",
3529 .@"64" => .@"8",
3530 };
3531
3532 // Minimum alignment for an arbitrarily-chosen set of "large" nodes in the file (e.g. common
3533 // sections), to allow `MappedFile` to perform operations more efficiently. The downside to
3534 // using `elf.mf.flags.block_size` is that it causes outputs to be potentially unreproducible
3535 // across host filesystems, so in the future we may want to set this to `.@"1"` when using a
3536 // build mode that requires reproducibility.
3537 //
3538 // It can be handy to temporarily set this to `.@"1"` when working on the linker, because it
3539 // prevents alignment bugs from being hidden by your filesystem's block alignment.
3540 const node_block_align: Alignment = elf.mf.flags.block_size;
3541
3542 const plt: PltInfo = .fromMachine(machine);
3543
3544 const shnum: u32 = shnum: {
3545 var shnum: u32 = 1; // reserved ("null") shdr
3546 shnum += 1; // .symtab
3547 shnum += 1; // .shstrtab
3548 shnum += 1; // .strtab
3549 shnum += @intFromBool(maybe_interp != null); // .interp
3550 shnum += 1; // .rodata
3551 shnum += 1; // .text
3552 shnum += 1; // .data
3553 shnum += @intFromBool(comp.config.any_non_single_threaded); // .tdata
3554 shnum += 1; // .data.rel.ro
3555 if (have_dynamic_section) {
3556 shnum += 1; // .dynamic
3557 shnum += 1; // .dynstr
3558 shnum += 1; // .dynsym
3559 shnum += 1; // .hash
3560 shnum += 1; // .rela.dyn
3561 shnum += 1; // .rela.plt
3562 }
3563 if (@"type" != .REL) {
3564 shnum += 1; // .got
3565 shnum += @intFromBool(plt.got_plt != null); // .got.plt
3566 shnum += 1; // .plt
3567 shnum += @intFromBool(plt.plt_sec != null); // .plt_sec
3568 }
3569 break :shnum shnum;
3570 };
3571
3572 const phndx: struct {
3573 phdr: u32,
3574 interp: u32,
3575 rodata: u32,
3576 text: u32,
3577 data: u32,
3578 /// On most targets this is `undefined`, but on machines where JUMP_SLOT relocations write
3579 /// directly to the PLT, we place the PLT in its own segment in order to avoid making the
3580 /// general data segment RWX.
3581 plt: u32,
3582 tls: u32,
3583 dynamic: u32,
3584 relro: u32,
3585 gnu_stack: u32,
3586 }, const phnum: u32 = ph: {
3587 switch (@"type") {
3588 .REL => break :ph .{ undefined, 0 },
3589 .EXEC, .DYN => {},
3590 }
3591 var phnum: u32 = 0;
3592 break :ph .{
3593 .{
3594 .phdr = phndx: {
3595 defer phnum += 1;
3596 break :phndx phnum;
3597 },
3598 .interp = if (maybe_interp) |_| phndx: {
3599 defer phnum += 1;
3600 break :phndx phnum;
3601 } else undefined,
3602 .rodata = phndx: {
3603 defer phnum += 1;
3604 break :phndx phnum;
3605 },
3606 .text = phndx: {
3607 defer phnum += 1;
3608 break :phndx phnum;
3609 },
3610 .plt = if (plt.got_plt == null) phndx: {
3611 defer phnum += 1;
3612 break :phndx phnum;
3613 } else undefined,
3614 // `data` must be assigned after all other loadable segments so that it has the greatest
3615 // phndx of any loadable segment. This is so that `targetSegmentLoadAddressRestrictions`
3616 // can be obeyed (specifically, the `.data_last` restriction, needed on SPARC).
3617 .data = phndx: {
3618 defer phnum += 1;
3619 break :phndx phnum;
3620 },
3621 .tls = if (comp.config.any_non_single_threaded) phndx: {
3622 defer phnum += 1;
3623 break :phndx phnum;
3624 } else undefined,
3625 .dynamic = if (have_dynamic_section) phndx: {
3626 defer phnum += 1;
3627 break :phndx phnum;
3628 } else undefined,
3629 .relro = phndx: {
3630 defer phnum += 1;
3631 break :phndx phnum;
3632 },
3633 .gnu_stack = phndx: {
3634 defer phnum += 1;
3635 break :phndx phnum;
3636 },
3637 },
3638 // (I don't actually want the trailing comma below, but a `zig fmt` bug forces it.)
3639 phnum,
3640 };
3641 };
3642
3643 const expected_nodes_len = @as(usize, if (is_archive) 3 else 0) + // .archive, .archive_header, .archive_elf_member_header
3644 3 + // `.elf`, `.ehdr`, and `.shdr` nodes
3645 (shnum - 1) + // -1 because the SHN_UNDEF shdr does not have a `.section` node
3646 (phnum -| 1); // -1 because the GNU_STACK phdr does not have a `.segment` node
3647
3648 try elf.nodes.ensureTotalCapacity(gpa, expected_nodes_len);
3649 try elf.shdrs.ensureTotalCapacity(gpa, shnum - 1); // -1 to exclude SHN_UNDEF
3650 try elf.section_by_name.ensureUnusedCapacity(gpa, shnum - 1); // -1 to exclude SHN_UNDEF
3651 try elf.phdrs.resize(gpa, phnum);
3652 try elf.symtab.ensureTotalCapacity(gpa, 1);
3653
3654 if (is_archive) {
3655 elf.nodes.appendAssumeCapacity(.archive);
3656
3657 const archive_ni: MappedFile.Node.Index = .root;
3658
3659 const archive_header_ni = try archive_ni.addOnlyHeaderChild(&elf.mf, gpa, .{
3660 // We intentionally do not set `.alignment = .@"2"` here, because the string table data
3661 // in this node does not need to have an aligned length. (This node's offset is aligned
3662 // regardless by virtue of it being a header.)
3663 .size = std.elf.ARMAG.len + @sizeOf(std.elf.ar_hdr),
3664 // The archive header uses 'next_moved' events to resize the "//" member, so that it
3665 // absorbs all padding between `archive_header_ni` and the actual object file members.
3666 .enable_next_moved = true,
3667 .next_moved = true,
3668 });
3669 elf.nodes.appendAssumeCapacity(.archive_header);
3670 const archive_header_slice = archive_header_ni.slice(&elf.mf);
3671 @memcpy(archive_header_slice[0..std.elf.ARMAG.len], std.elf.ARMAG);
3672 const strtab_ar_hdr: *std.elf.ar_hdr = @ptrCast(archive_header_slice[std.elf.ARMAG.len..]);
3673 strtab_ar_hdr.* = .{
3674 .ar_name = std.elf.STRNAME.*,
3675 .ar_date = @splat(' '),
3676 .ar_uid = @splat(' '),
3677 .ar_gid = @splat(' '),
3678 .ar_mode = @splat(' '),
3679 .ar_size = undefined, // populated by `flushNextMoved` for `archive_header_ni`
3680 .ar_fmag = std.elf.ARFMAG.*,
3681 };
3682
3683 elf.ni.elf = try archive_ni.addOnlyFooterChild(&elf.mf, gpa, .{
3684 .alignment = node_block_align.max(.@"2"),
3685 .bubbles_moved = false,
3686 .resized = true, // ensure that this node's `ar_hdr.ar_size` is updated at least once
3687 });
3688 elf.nodes.appendAssumeCapacity(.elf);
3689
3690 const elf_ar_hdr_ni = try archive_ni.addFooterChildBefore(&elf.mf, gpa, .wrap(elf.ni.elf), .{
3691 .alignment = .@"2",
3692 .size = @sizeOf(std.elf.ar_hdr),
3693 });
3694 elf.nodes.appendAssumeCapacity(.archive_elf_member_header);
3695
3696 // Must be populated before we call `populateArchiveMemberName` below.
3697 elf.archive = .{
3698 .ni = archive_ni,
3699 .header_ni = archive_header_ni,
3700 .elf_member_header_ni = elf_ar_hdr_ni,
3701
3702 .elf_member_too_big = false,
3703 .strtab_member_too_big = false,
3704 };
3705
3706 const elf_ar_hdr: *std.elf.ar_hdr = @ptrCast(elf_ar_hdr_ni.slice(&elf.mf));
3707 elf_ar_hdr.* = .{
3708 .ar_name = undefined, // populated below
3709 .ar_date = "0 ".*,
3710 .ar_uid = "0 ".*,
3711 .ar_gid = "0 ".*,
3712 .ar_mode = "644 ".*,
3713 .ar_size = undefined, // populated by `flushResized` for the `.elf` node
3714 .ar_fmag = std.elf.ARFMAG.*,
3715 };
3716 const zcu_member_name = try std.fmt.allocPrint(gpa, "{s}_zcu.o", .{comp.root_name});
3717 defer gpa.free(zcu_member_name);
3718 // After this call returns, `elf_ar_hdr` is invalidated.
3719 try elf.populateArchiveMemberName(elf_ar_hdr, zcu_member_name);
3720 } else {
3721 elf.ni.elf = .root;
3722 elf.nodes.appendAssumeCapacity(.elf);
3723 }
3724
3725 const entsize: struct { ph: u32, sh: u32 } = switch (class) {
3726 .NONE, _ => unreachable,
3727 inline else => |ct_class| .{
3728 .ph = @sizeOf(ct_class.ElfN().Phdr),
3729 .sh = @sizeOf(ct_class.ElfN().Shdr),
3730 },
3731 };
3732
3733 // We want to create the segment nodes *before* the ehdr, because the ehdr should go inside of
3734 // the rodata segment. Although to my knowledge neither ELF nor any ELF-based OS strictly
3735 // requires this, it is highly conventional and therefore sometimes relied upon.
3736 if (@"type" != .REL) {
3737 // This node will contain the ehdr, which must be at the start of the ELF file, so this
3738 // node must itself be a header of the `.elf` node.
3739 elf.ni.rodata = try elf.ni.elf.addOnlyHeaderChild(&elf.mf, gpa, .{
3740 // Must be at least `addr_align` for `elf.ni.phdr` to be placed inside this node
3741 .alignment = node_block_align.max(addr_align),
3742 .moved = true,
3743 .bubbles_moved = false,
3744 });
3745 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.rodata });
3746 elf.phdrs.items[phndx.rodata] = .wrap(elf.ni.rodata);
3747
3748 elf.ni.phdr = try elf.ni.rodata.addFloatingChild(&elf.mf, gpa, .{
3749 .size = @as(u64, phnum) * entsize.ph,
3750 .alignment = addr_align, // keep in sync with `elf.ni.rodata` alignment above
3751 .moved = true,
3752 .resized = true,
3753 .bubbles_moved = false,
3754 });
3755 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.phdr });
3756 elf.phdrs.items[phndx.phdr] = .wrap(elf.ni.phdr);
3757
3758 elf.ni.text = try elf.ni.elf.addFloatingChild(&elf.mf, gpa, .{
3759 .alignment = node_block_align,
3760 .moved = true,
3761 .bubbles_moved = false,
3762 });
3763 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.text });
3764 elf.phdrs.items[phndx.text] = .wrap(elf.ni.text);
3765
3766 elf.ni.data = try elf.ni.elf.addFloatingChild(&elf.mf, gpa, .{
3767 // Must be at least `addr_align` for `elf.ni.data_rel_ro` to be placed inside this node
3768 .alignment = node_block_align.max(addr_align),
3769 .moved = true,
3770 .bubbles_moved = false,
3771 });
3772 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.data });
3773 elf.phdrs.items[phndx.data] = .wrap(elf.ni.data);
3774
3775 if (plt.got_plt == null) {
3776 const plt_ni = try elf.ni.elf.addFloatingChild(&elf.mf, gpa, .{
3777 .alignment = node_block_align,
3778 .moved = true,
3779 .bubbles_moved = false,
3780 });
3781 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.plt });
3782 elf.phdrs.items[phndx.plt] = .wrap(plt_ni);
3783 }
3784
3785 elf.ni.data_rel_ro = try elf.ni.data.addFloatingChild(&elf.mf, gpa, .{
3786 // Must be at least `addr_align` for the `PT_DYNAMIC` node to be placed inside this one
3787 // later (if `have_dynamic_section`). Keep in sync with `elf.ni.data` alignment above.
3788 .alignment = node_block_align.max(addr_align),
3789 .moved = true,
3790 .bubbles_moved = false,
3791 });
3792 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.relro });
3793 elf.phdrs.items[phndx.relro] = .wrap(elf.ni.data_rel_ro);
3794
3795 if (comp.config.any_non_single_threaded) {
3796 elf.ni.tls = .wrap(try elf.ni.rodata.addFloatingChild(&elf.mf, gpa, .{
3797 .alignment = node_block_align,
3798 .moved = true,
3799 .bubbles_moved = false,
3800 }));
3801 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.tls });
3802 elf.phdrs.items[phndx.tls] = elf.ni.tls;
3803 }
3804
3805 elf.phdrs.items[phndx.gnu_stack] = .none;
3806 } else {
3807 elf.ni.rodata = elf.ni.elf;
3808 elf.ni.text = elf.ni.elf;
3809 elf.ni.data = elf.ni.elf;
3810 elf.ni.data_rel_ro = elf.ni.elf;
3811 if (comp.config.any_non_single_threaded) {
3812 elf.ni.tls = .wrap(elf.ni.elf);
3813 }
3814 }
3815
3816 switch (class) {
3817 .NONE, _ => unreachable,
3818 inline else => |ct_class| {
3819 const ElfN = ct_class.ElfN();
3820 // In loadable modules, the ehdr goes in the rodata segment, as described above.
3821 const parent_ni = switch (@"type") {
3822 .REL => elf.ni.elf,
3823 .DYN, .EXEC => elf.ni.rodata,
3824 };
3825 elf.ni.ehdr = try parent_ni.addOnlyHeaderChild(&elf.mf, gpa, .{
3826 .size = @sizeOf(ElfN.Ehdr),
3827 .alignment = addr_align,
3828 });
3829 elf.nodes.appendAssumeCapacity(.ehdr);
3830
3831 const ehdr: *ElfN.Ehdr = @ptrCast(@alignCast(elf.ni.ehdr.slice(&elf.mf)));
3832 ehdr.ident = .{
3833 .class = class,
3834 .data = data,
3835 .version = 1,
3836 .osabi = osabi,
3837 .abiversion = 0,
3838 };
3839 ehdr.type = @"type".toElf();
3840 ehdr.machine = machine.toElf();
3841 ehdr.version = 1;
3842 ehdr.entry = 0;
3843 ehdr.phoff = 0;
3844 ehdr.shoff = 0;
3845 ehdr.flags = switch (machine) {
3846 .LOONGARCH => .{ .loongarch = .{
3847 .base_abi_modifier = mod: {
3848 const cpu = comp.getTarget().cpu;
3849 if (cpu.has(.loongarch, .d)) break :mod .d;
3850 if (cpu.has(.loongarch, .f)) break :mod .f;
3851 break :mod .s;
3852 },
3853 .abi_extension = .base,
3854 .abi_version = 1,
3855 } },
3856 .SPARCV9 => .{ .sparc = .{
3857 .mm = .rmo,
3858 .ext = .{
3859 .@"32plus" = false,
3860 .sun_us1 = false,
3861 .hal_r1 = false,
3862 .sun_us3 = false,
3863 .le_data = false,
3864 },
3865 } },
3866 .X86_64 => .{ .int = 0 },
3867 .AARCH64, .PPC64, .RISCV => @panic(@tagName(machine)),
3868 };
3869 ehdr.ehsize = @sizeOf(ElfN.Ehdr);
3870 ehdr.phentsize = @sizeOf(ElfN.Phdr);
3871 ehdr.phnum = @min(phnum, std.elf.PN_XNUM);
3872 ehdr.shentsize = @sizeOf(ElfN.Shdr);
3873 ehdr.shnum = 1; // Only the SHN_UNDEF shdr initially---will be incremented by `addSection`
3874 ehdr.shstrndx = std.elf.SHN_UNDEF;
3875 if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(ElfN.Ehdr, ehdr);
3876 },
3877 }
3878
3879 elf.ni.shdr = try elf.ni.elf.addFloatingChild(&elf.mf, gpa, .{
3880 .size = node_block_align.forward(1 * entsize.sh), // as above, only the SHN_UNDEF initially
3881 .alignment = addr_align.max(node_block_align),
3882 .moved = true,
3883 .resized = true,
3884 });
3885 elf.nodes.appendAssumeCapacity(.shdr);
3886
3887 switch (class) {
3888 .NONE, _ => unreachable,
3889 inline else => |ct_class| {
3890 const ElfN = ct_class.ElfN();
3891 const target_endian = elf.targetEndian();
3892
3893 populate_phdrs: {
3894 // Initially we will give every `PT_LOAD` segment this address. When we re-allocate
3895 // segments in the virtual address space in `flushMoved` and `flushResized`, we will
3896 // move some segments to higher addresses to prevent overlap. This address therefore
3897 // becomes the image's "base address"; i.e. the first `PT_LOAD` segment will start
3898 // at this address. The base address could eventually end up higher than this due to
3899 // how we re-allocate the address space, but never lower.
3900 const base_vaddr: u64 = switch (@"type") {
3901 .REL => break :populate_phdrs,
3902 .DYN => 0,
3903 .EXEC => switch (machine) {
3904 .AARCH64 => 0x200000,
3905 .LOONGARCH => 0x10000,
3906 .PPC64 => 0x10000000,
3907 .RISCV => 0x10000,
3908 .SPARCV9 => 0x100000,
3909 .X86_64 => 0x200000,
3910 },
3911 };
3912
3913 // All `PT_LOAD` segments are given this `.@"align"`. However, to avoid bloating the
3914 // binary, their *nodes* are not aligned to this boundary---ELF only requires that
3915 // ecah segment's address equals its file offset modulo this alignment, not that its
3916 // file offset is actually aligned to this boundary. This property is maintained by
3917 // the segment virtual address space allocation logic.
3918 const page_align = elf.targetPageAlign();
3919
3920 // We will populate elements in this slice (by index). The `PT_LOAD` segments are
3921 // actually `PT_NULL` for now, because we initialize `filesz` and `memsz` to zero.
3922 // Any which end up non-empty will have their size populated (and their type set to
3923 // `PT_LOAD`) by the segment virtual address space allocation logic.
3924 const phdr: []ElfN.Phdr = @ptrCast(@alignCast(
3925 elf.ni.phdr.slice(&elf.mf)[0 .. phnum * @sizeOf(ElfN.Phdr)],
3926 ));
3927
3928 const ph_phdr = &phdr[phndx.phdr];
3929 ph_phdr.* = .{
3930 .type = .PHDR,
3931 .offset = 0,
3932 .vaddr = 0,
3933 .paddr = 0,
3934 .filesz = 0,
3935 .memsz = 0,
3936 .flags = .{ .R = true },
3937 .@"align" = @intCast(elf.ni.phdr.alignment(&elf.mf).toByteUnits()),
3938 };
3939
3940 if (maybe_interp) |_| {
3941 const ph_interp = &phdr[phndx.interp];
3942 ph_interp.* = .{
3943 .type = .INTERP,
3944 .offset = 0,
3945 .vaddr = 0,
3946 .paddr = 0,
3947 .filesz = 0,
3948 .memsz = 0,
3949 .flags = .{ .R = true },
3950 .@"align" = 1,
3951 };
3952 }
3953
3954 const ph_rodata = &phdr[phndx.rodata];
3955 ph_rodata.* = .{
3956 .type = .NULL,
3957 .offset = 0,
3958 .vaddr = @intCast(base_vaddr),
3959 .paddr = @intCast(base_vaddr),
3960 .filesz = 0,
3961 .memsz = 0,
3962 .flags = .{ .R = true },
3963 .@"align" = @intCast(page_align.toByteUnits()),
3964 };
3965
3966 const ph_text = &phdr[phndx.text];
3967 ph_text.* = .{
3968 .type = .NULL,
3969 .offset = 0,
3970 .vaddr = @intCast(base_vaddr),
3971 .paddr = @intCast(base_vaddr),
3972 .filesz = 0,
3973 .memsz = 0,
3974 .flags = .{ .R = true, .X = true },
3975 .@"align" = @intCast(page_align.toByteUnits()),
3976 };
3977
3978 const ph_data = &phdr[phndx.data];
3979 ph_data.* = .{
3980 .type = .NULL,
3981 .offset = 0,
3982 .vaddr = @intCast(base_vaddr),
3983 .paddr = @intCast(base_vaddr),
3984 .filesz = 0,
3985 .memsz = 0,
3986 .flags = .{ .R = true, .W = true },
3987 .@"align" = @intCast(page_align.toByteUnits()),
3988 };
3989
3990 if (plt.got_plt == null) {
3991 const ph_plt = &phdr[phndx.plt];
3992 ph_plt.* = .{
3993 .type = .NULL,
3994 .offset = 0,
3995 .vaddr = @intCast(base_vaddr),
3996 .paddr = @intCast(base_vaddr),
3997 .filesz = 0,
3998 .memsz = 0,
3999 .flags = .{ .R = true, .W = true, .X = true },
4000 .@"align" = @intCast(page_align.toByteUnits()),
4001 };
4002 }
4003
4004 if (elf.ni.tls.unwrap()) |tls_segment_ni| {
4005 const ph_tls = &phdr[phndx.tls];
4006 ph_tls.* = .{
4007 .type = .TLS,
4008 .offset = 0,
4009 .vaddr = 0,
4010 .paddr = 0,
4011 .filesz = 0,
4012 .memsz = 0,
4013 .flags = .{ .R = true },
4014 .@"align" = @intCast(tls_segment_ni.alignment(&elf.mf).toByteUnits()),
4015 };
4016 }
4017
4018 if (have_dynamic_section) {
4019 const ph_dynamic = &phdr[phndx.dynamic];
4020 ph_dynamic.* = .{
4021 .type = .DYNAMIC,
4022 .offset = 0,
4023 .vaddr = 0,
4024 .paddr = 0,
4025 .filesz = 0,
4026 .memsz = 0,
4027 .flags = .{ .R = true, .W = true },
4028 .@"align" = @intCast(addr_align.toByteUnits()),
4029 };
4030 }
4031
4032 const ph_relro = &phdr[phndx.relro];
4033 ph_relro.* = .{
4034 .type = .GNU_RELRO,
4035 .offset = 0,
4036 .vaddr = 0,
4037 .paddr = 0,
4038 .filesz = 0,
4039 .memsz = 0,
4040 .flags = .{ .R = true },
4041 .@"align" = @intCast(elf.ni.data_rel_ro.alignment(&elf.mf).toByteUnits()),
4042 };
4043
4044 const ph_gnu_stack = &phdr[phndx.gnu_stack];
4045 ph_gnu_stack.* = .{
4046 .type = .GNU_STACK,
4047 .offset = 0,
4048 .vaddr = 0,
4049 .paddr = 0,
4050 .filesz = 0,
4051 .memsz = @intCast(elf.options.stack_size orelse 0),
4052 .flags = .{ .R = true, .W = true },
4053 .@"align" = 1,
4054 };
4055
4056 if (target_endian != std.lang.Endian.native) {
4057 std.mem.byteSwapAllElements(ElfN.Phdr, phdr);
4058 }
4059 }
4060
4061 const sh_undef: *ElfN.Shdr = @ptrCast(@alignCast(elf.ni.shdr.slice(&elf.mf)));
4062 sh_undef.* = .{
4063 .name = @backingInt(String(.shstrtab).empty),
4064 .type = .NULL,
4065 .flags = .{ .shf = .{} },
4066 .addr = 0,
4067 .offset = 0,
4068 .size = if (shnum < std.elf.SHN_LORESERVE) 0 else shnum,
4069 .link = 0,
4070 .info = if (phnum < std.elf.PN_XNUM) 0 else phnum,
4071 .addralign = 0,
4072 .entsize = 0,
4073 };
4074 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Shdr, sh_undef);
4075
4076 elf.symtab.addOneAssumeCapacity().* = .{
4077 .node = .none,
4078 .first_target_reloc = .none,
4079 };
4080 assert(.symtab == try elf.addSection(elf.ni.elf, .{
4081 .type = .SYMTAB,
4082 .size = @sizeOf(ElfN.Sym) * 1,
4083 .addralign = addr_align,
4084 .entsize = @sizeOf(ElfN.Sym),
4085 .node_align = node_block_align,
4086 .info = 1, // index of first non-local symbol
4087 }));
4088 const symtab_null = @field(elf.symPtr(.null), @tagName(ct_class));
4089 symtab_null.* = .{
4090 .name = @backingInt(String(.strtab).empty),
4091 .value = 0,
4092 .size = 0,
4093 .info = .{ .type = .NOTYPE, .bind = .LOCAL },
4094 .other = .{ .visibility = .DEFAULT },
4095 .shndx = std.elf.SHN_UNDEF,
4096 };
4097 if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Sym, symtab_null);
4098
4099 const ehdr = @field(elf.ehdrPtr(), @tagName(ct_class));
4100 ehdr.shstrndx = ehdr.shnum;
4101 },
4102 }
4103 assert(.shstrtab == try elf.addSection(elf.ni.elf, .{
4104 .type = .STRTAB,
4105 .size = 1,
4106 .entsize = 1,
4107 .node_align = node_block_align,
4108 }));
4109 Section.Index.get(.shstrtab, elf).ni.slice(&elf.mf)[0] = 0;
4110
4111 try Section.Index.symtab.rename(elf, ".symtab");
4112 try Section.Index.shstrtab.rename(elf, ".shstrtab");
4113
4114 assert(.strtab == try elf.addSection(elf.ni.elf, .{
4115 .name = ".strtab",
4116 .type = .STRTAB,
4117 .size = 1,
4118 .entsize = 1,
4119 .node_align = node_block_align,
4120 }));
4121 Section.Index.get(.strtab, elf).ni.slice(&elf.mf)[0] = 0;
4122 switch (elf.shdrPtr(.symtab)) {
4123 inline else => |shdr| elf.targetStore(&shdr.link, @backingInt(Section.Index.strtab)),
4124 }
4125
4126 assert(.rodata == try elf.addSection(elf.ni.rodata, .{
4127 .name = ".rodata",
4128 .flags = .{ .ALLOC = true },
4129 .node_align = node_block_align,
4130 }));
4131 assert(.text == try elf.addSection(elf.ni.text, .{
4132 .name = ".text",
4133 .flags = .{ .ALLOC = true, .EXECINSTR = true },
4134 .node_align = node_block_align,
4135 }));
4136 assert(.data == try elf.addSection(elf.ni.data, .{
4137 .name = ".data",
4138 .flags = .{ .WRITE = true, .ALLOC = true },
4139 .node_align = node_block_align,
4140 }));
4141 assert(.data_rel_ro == try elf.addSection(elf.ni.data_rel_ro, .{
4142 .name = ".data.rel.ro",
4143 .flags = .{ .WRITE = true, .ALLOC = true },
4144 .node_align = node_block_align,
4145 }));
4146 if (@"type" != .REL) {
4147 elf.shndx.got = try elf.addSection(elf.ni.data_rel_ro, .{
4148 .name = ".got",
4149 .type = .PROGBITS,
4150 // Reserve space for the reserved words, populated later.
4151 .size = switch (machine) {
4152 .AARCH64, .PPC64, .RISCV => @panic(@tagName(machine)),
4153 .X86_64 => 3 * elf.targetPtrSize(),
4154 .LOONGARCH, .SPARCV9 => elf.targetPtrSize(),
4155 },
4156 .flags = .{ .WRITE = true, .ALLOC = true },
4157 .addralign = addr_align,
4158 .entsize = @intCast(addr_align.toByteUnits()),
4159 });
4160 {
4161 const init_plt_size = plt.entry_size * plt.header_entries;
4162 if (plt.got_plt) |got_plt| {
4163 const got_plt_segment_ni = if (elf.options.z_now) elf.ni.data_rel_ro else elf.ni.data;
4164 elf.shndx.got_plt = try elf.addSection(got_plt_segment_ni, .{
4165 .name = ".got.plt",
4166 .type = .PROGBITS,
4167 .flags = .{ .WRITE = true, .ALLOC = true },
4168 .size = got_plt.header_entries * elf.targetPtrSize(),
4169 .addralign = addr_align,
4170 .entsize = @intCast(addr_align.toByteUnits()),
4171 });
4172 elf.shndx.plt = try elf.addSection(elf.ni.text, .{
4173 .name = ".plt",
4174 .type = .PROGBITS,
4175 .flags = .{ .ALLOC = true, .EXECINSTR = true },
4176 .size = plt.@"align".forward(init_plt_size),
4177 .addralign = plt.@"align",
4178 .node_align = node_block_align,
4179 });
4180 } else {
4181 elf.shndx.plt = try elf.addSection(elf.phdrs.items[phndx.plt].unwrap().?, .{
4182 .name = ".plt",
4183 .type = .PROGBITS,
4184 .flags = .{ .ALLOC = true, .WRITE = true, .EXECINSTR = true },
4185 .size = plt.@"align".forward(init_plt_size),
4186 .addralign = plt.@"align",
4187 .node_align = node_block_align,
4188 });
4189 }
4190 // And the award for most annoying PLT requirement goes to SPARC, which decided that the
4191 // whole table should have a greater alignment than the size of the individual entries,
4192 // hence this bullshit:
4193 if (plt.@"align".forward(init_plt_size) != init_plt_size) {
4194 switch (elf.shdrPtr(elf.shndx.plt)) {
4195 inline else => |shdr| elf.targetStore(&shdr.size, init_plt_size),
4196 }
4197 }
4198 }
4199 if (plt.plt_sec != null) elf.shndx.plt_sec = try elf.addSection(elf.ni.text, .{
4200 .name = ".plt.sec",
4201 .flags = .{ .ALLOC = true, .EXECINSTR = true },
4202 .addralign = plt.@"align",
4203 .node_align = node_block_align,
4204 });
4205 if (maybe_interp) |interp| {
4206 const interp_ni = try elf.ni.rodata.addFloatingChild(&elf.mf, gpa, .{
4207 .size = interp.len + 1,
4208 .moved = true,
4209 .resized = true,
4210 .bubbles_moved = false,
4211 });
4212 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.interp });
4213 elf.phdrs.items[phndx.interp] = .wrap(interp_ni);
4214
4215 const sec_interp_shndx = try elf.addSection(interp_ni, .{
4216 .name = ".interp",
4217 .type = .PROGBITS,
4218 .flags = .{ .ALLOC = true },
4219 .size = @intCast(interp.len + 1),
4220 });
4221 const sec_interp = sec_interp_shndx.get(elf).ni.slice(&elf.mf);
4222 @memcpy(sec_interp[0..interp.len], interp);
4223 sec_interp[interp.len] = 0;
4224 }
4225 if (have_dynamic_section) {
4226 assert(elf.ni.data_rel_ro.alignment(&elf.mf).compare(.gte, addr_align));
4227 const dynamic_ni = try elf.ni.data_rel_ro.addFloatingChild(&elf.mf, gpa, .{
4228 .alignment = addr_align,
4229 .moved = true,
4230 .bubbles_moved = false,
4231 });
4232 elf.nodes.appendAssumeCapacity(.{ .segment = phndx.dynamic });
4233 elf.phdrs.items[phndx.dynamic] = .wrap(dynamic_ni);
4234
4235 const dynstr_shndx = try elf.addSection(elf.ni.rodata, .{
4236 .name = ".dynstr",
4237 .type = .STRTAB,
4238 .flags = .{ .ALLOC = true },
4239 .size = 1,
4240 .entsize = 1,
4241 .node_align = node_block_align,
4242 });
4243 dynstr_shndx.get(elf).ni.slice(&elf.mf)[0] = 0;
4244 elf.shndx.dynstr = dynstr_shndx;
4245
4246 switch (class) {
4247 .NONE, _ => unreachable,
4248 inline else => |ct_class| {
4249 const Sym = ct_class.ElfN().Sym;
4250 elf.shndx.dynsym = try elf.addSection(elf.ni.rodata, .{
4251 .name = ".dynsym",
4252 .type = .DYNSYM,
4253 .flags = .{ .ALLOC = true },
4254 .size = @sizeOf(Sym) * 1,
4255 .link = dynstr_shndx.toSection().?,
4256 .info = 1,
4257 .addralign = addr_align,
4258 .entsize = @sizeOf(Sym),
4259 .node_align = node_block_align,
4260 });
4261 const dynsym_null = @field(elf.dynsymPtr(0), @tagName(ct_class));
4262 dynsym_null.* = .{
4263 .name = @backingInt(String(.dynstr).empty),
4264 .value = 0,
4265 .size = 0,
4266 .info = .{ .type = .NOTYPE, .bind = .LOCAL },
4267 .other = .{ .visibility = .DEFAULT },
4268 .shndx = std.elf.SHN_UNDEF,
4269 };
4270 if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(
4271 Sym,
4272 dynsym_null,
4273 );
4274 },
4275 }
4276 const rela_size: std.elf.Word = switch (class) {
4277 .NONE, _ => unreachable,
4278 inline else => |ct_class| @sizeOf(ct_class.ElfN().Rela),
4279 };
4280 elf.shndx.rela_dyn = try elf.addSection(elf.ni.rodata, .{
4281 .name = ".rela.dyn",
4282 .type = .RELA,
4283 .flags = .{ .ALLOC = true },
4284 .link = elf.shndx.dynsym.toSection().?,
4285 .addralign = addr_align,
4286 .entsize = rela_size,
4287 .node_align = node_block_align,
4288 });
4289 elf.shndx.rela_plt = try elf.addSection(elf.ni.rodata, .{
4290 .name = ".rela.plt",
4291 .type = .RELA,
4292 .flags = .{ .ALLOC = true, .INFO_LINK = true },
4293 .link = elf.shndx.dynsym.toSection().?,
4294 .info = (if (plt.got_plt != null) elf.shndx.got_plt else elf.shndx.plt).toSection().?,
4295 .addralign = addr_align,
4296 .entsize = rela_size,
4297 .node_align = node_block_align,
4298 });
4299 elf.shndx.dynamic = try elf.addSection(dynamic_ni, .{
4300 .name = ".dynamic",
4301 .type = .DYNAMIC,
4302 .flags = .{ .ALLOC = true, .WRITE = true },
4303 .link = dynstr_shndx.toSection().?,
4304 .entsize = @intCast(addr_align.toByteUnits() * 2),
4305 .addralign = addr_align,
4306 });
4307 switch (elf.targetDynsymHashInfo()) {
4308 inline else => |info| {
4309 elf.shndx.hash = try elf.addSection(elf.ni.rodata, .{
4310 .name = ".hash",
4311 .type = .HASH,
4312 .flags = .{ .ALLOC = true },
4313 .link = elf.shndx.dynsym.toSection().?,
4314 // It's unclear what value is correct for the alignment. binutils uses 8 everywhere,
4315 // while lld uses 4 everywhere (but lld lacks support for the alpha/s390x special
4316 // case). Matching the hash word (= entry) size seems like the actually sane choice,
4317 // and is what mold does too.
4318 .addralign = .fromByteUnits(@sizeOf(info.Int())),
4319 // initially: nbucket = 8 + nchain = 1
4320 .size = @sizeOf(info.Header()) + @sizeOf(info.Int()) * (8 + 1),
4321 });
4322 const hash_slice: []align(@sizeOf(info.Int())) u8 = @alignCast(elf.shndx.hash.get(elf).ni.slice(&elf.mf));
4323 const header: *info.Header() = @ptrCast(hash_slice[0..@sizeOf(info.Header())]);
4324 header.* = .{ .nbucket = 8, .nchain = 1 };
4325 if (elf.targetEndian() != std.lang.Endian.native) {
4326 std.mem.byteSwapAllFields(info.Header(), header);
4327 }
4328 // The initial bucket and chain values are all 0.
4329 @memset(hash_slice[@sizeOf(info.Header())..], 0);
4330 },
4331 }
4332
4333 switch (machine) {
4334 .AARCH64, .PPC64, .RISCV => @panic(@tagName(machine)),
4335 .X86_64 => {
4336 const plt_ni = elf.shndx.plt.get(elf).ni;
4337 const got_plt_sym: Symbol.Id = .local(elf.shndx.got_plt.get(elf).lsi);
4338 @memcpy(plt_ni.slice(&elf.mf)[0..16], &[16]u8{
4339 0xff, 0x35, 0x00, 0x00, 0x00, 0x00, // push 0x0(%rip)
4340 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *0x0(%rip)
4341 0x0f, 0x1f, 0x40, 0x00, // nopl 0x0(%rax)
4342 });
4343 elf.plt_first_symbol_reloc = @fromBackingInt(@intCast(elf.symbol_relocs.items.len));
4344 try elf.ensureUnusedRelocCapacity(plt_ni, 2);
4345 try elf.addSymbolRelocAssumeCapacity(
4346 plt_ni,
4347 2,
4348 got_plt_sym,
4349 8 * 1 - 4,
4350 .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" }),
4351 );
4352 try elf.addSymbolRelocAssumeCapacity(
4353 plt_ni,
4354 8,
4355 got_plt_sym,
4356 8 * 2 - 4,
4357 .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" }),
4358 );
4359 },
4360 .LOONGARCH => {
4361 const plt_ni = elf.shndx.plt.get(elf).ni;
4362 const got_plt_sym: Symbol.Id = .local(elf.shndx.got_plt.get(elf).lsi);
4363 @memcpy(plt_ni.slice(&elf.mf)[0..32], switch (class) {
4364 .NONE, _ => unreachable,
4365 .@"32" => &[32]u8{
4366 0x1a, 0x00, 0x00, 0x0e, // pcalau12i $t2, %pc_hi20(.got.plt)
4367 0x00, 0x11, 0x3d, 0xad, // sub.w $t1, $t1, $t3
4368 0x28, 0x80, 0x01, 0xcf, // ld.w $t3, $t2, %lo12(.got.plt) # _dl_runtime_resolve
4369 0x02, 0xbf, 0x51, 0xad, // addi.w $t1, $t1, -44 # .plt entry
4370 0x02, 0x80, 0x01, 0xcc, // addi.w $t0, $t2, %lo12(.got.plt) # &.got.plt
4371 0x00, 0x44, 0x89, 0xad, // srli.w $t1, $t1, 2 # .plt entry offset
4372 0x28, 0x80, 0x11, 0x8c, // ld.w $t0, $t0, 4 # link map
4373 0x4c, 0x00, 0x01, 0xe0, // jr $t3
4374 },
4375 .@"64" => &[32]u8{
4376 0x1a, 0x00, 0x00, 0x0e, // pcalau12i $t2, %pc_hi20(.got.plt)
4377 0x00, 0x11, 0xbd, 0xad, // sub.d $t1, $t1, $t3
4378 0x28, 0xc0, 0x01, 0xcf, // ld.d $t3, $t2, %lo12(.got.plt) # _dl_runtime_resolve
4379 0x02, 0xff, 0x51, 0xad, // addi.d $t1, $t1, -44 # .plt entry
4380 0x02, 0xc0, 0x01, 0xcc, // addi.d $t0, $t2, %lo12(.got.plt) # &.got.plt
4381 0x00, 0x45, 0x05, 0xad, // srli.d $t1, $t1, 1 # .plt entry offset
4382 0x28, 0xc0, 0x21, 0x8c, // ld.d $t0, $t0, 8 # link map
4383 0x4c, 0x00, 0x01, 0xe0, // jr $t3
4384 },
4385 });
4386 elf.plt_first_symbol_reloc = @fromBackingInt(@intCast(elf.symbol_relocs.items.len));
4387 try elf.ensureUnusedRelocCapacity(plt_ni, 3);
4388 elf.addRelocAssumeCapacity(plt_ni, 0, got_plt_sym, 0, .{ .LARCH = .PCALA_HI20 }) catch |err| switch (err) {
4389 error.UnknownRelocation => unreachable,
4390 error.NonStaticRelocation => unreachable,
4391 error.UnimplementedRelocation => unreachable,
4392 else => |e| return e,
4393 };
4394 elf.addRelocAssumeCapacity(plt_ni, 8, got_plt_sym, 0, .{ .LARCH = .PCALA_LO12 }) catch |err| switch (err) {
4395 error.UnknownRelocation => unreachable,
4396 error.NonStaticRelocation => unreachable,
4397 error.UnimplementedRelocation => unreachable,
4398 else => |e| return e,
4399 };
4400 elf.addRelocAssumeCapacity(plt_ni, 16, got_plt_sym, 0, .{ .LARCH = .PCALA_LO12 }) catch |err| switch (err) {
4401 error.UnknownRelocation => unreachable,
4402 error.NonStaticRelocation => unreachable,
4403 error.UnimplementedRelocation => unreachable,
4404 else => |e| return e,
4405 };
4406 },
4407 .SPARCV9 => {},
4408 }
4409 }
4410
4411 // Populate reserved GOT words.
4412 switch (machine) {
4413 .AARCH64, .PPC64, .RISCV => @panic(@tagName(machine)),
4414 .X86_64 => {
4415 try elf.got.ensureUnusedCapacity(gpa, 3);
4416 elf.got.putAssumeCapacityNoClobber(switch (have_dynamic_section) {
4417 true => .{ .symbol = .local(elf.shndx.dynamic.get(elf).lsi) },
4418 false => .{ .reserved = 0 },
4419 }, .none);
4420 elf.got.putAssumeCapacityNoClobber(.{ .reserved = 1 }, .none);
4421 elf.got.putAssumeCapacityNoClobber(.{ .reserved = 2 }, .none);
4422 },
4423 .LOONGARCH, .SPARCV9 => {
4424 try elf.got.ensureUnusedCapacity(gpa, 1);
4425 elf.got.putAssumeCapacityNoClobber(switch (have_dynamic_section) {
4426 true => .{ .symbol = .local(elf.shndx.dynamic.get(elf).lsi) },
4427 false => .{ .reserved = 0 },
4428 }, .none);
4429 },
4430 }
4431 switch (elf.shdrPtr(elf.shndx.got)) {
4432 inline else => |shdr, ct_class| {
4433 const Addr = ct_class.ElfN().Addr;
4434 assert(elf.targetLoad(&shdr.size) == elf.got.count() * @sizeOf(Addr));
4435 },
4436 }
4437 if (elf.shndx.dynamic != .UNDEF) {
4438 try elf.shndx.rela_dyn.relaEnsureAdditionalCapacity(elf, elf.got.count());
4439 }
4440 for (0..elf.got.count()) |got_index| {
4441 elf.updateGotEntry(got_index);
4442 }
4443
4444 // Create any always-provided linker-defined symbols. The symbols marking the `INIT_ARRAY`/
4445 // `FINI_ARRAY`/`PREINIT_ARRAY` sections are instead created by `createInitFiniArraySection`
4446 // when needed (it seems to be legal to leave those undefined if the section doesn't exist).
4447
4448 try elf.ensureUnusedSymbolCapacity(10, .maybe_global);
4449 // Despite the name, `__dso_handle` is necessary even in static binaries.
4450 _ = elf.addGlobalSymbolAssumeCapacity(.{
4451 .node = .wrap(Section.Index.text.get(elf).ni),
4452 .name = try .string(elf, "__dso_handle"),
4453 .value = Section.Index.text.vaddr(elf),
4454 .size = 0,
4455 .type = .NOTYPE,
4456 .bind = .weak,
4457 .visibility = .HIDDEN,
4458 .shndx = .text,
4459 }) catch |err| switch (err) {
4460 error.MultipleDefinitions => unreachable, // no inputs are processed yet
4461 };
4462 _ = elf.addGlobalSymbolAssumeCapacity(.{
4463 .node = .wrap(elf.shndx.plt.get(elf).ni),
4464 .name = try .string(elf, "_PROCEDURE_LINKAGE_TABLE_"),
4465 .value = elf.shndx.plt.vaddr(elf),
4466 .size = 0,
4467 .type = .NOTYPE,
4468 .bind = .strong,
4469 .visibility = .HIDDEN,
4470 .shndx = elf.shndx.plt,
4471 }) catch |err| switch (err) {
4472 error.MultipleDefinitions => unreachable, // no inputs are processed yet
4473 };
4474 _ = elf.addGlobalSymbolAssumeCapacity(.{
4475 .node = .wrap(elf.shndx.got.get(elf).ni),
4476 .name = try .string(elf, "_GLOBAL_OFFSET_TABLE_"),
4477 .value = switch (machine) {
4478 .AARCH64,
4479 .LOONGARCH,
4480 .PPC64,
4481 .RISCV,
4482 .SPARCV9,
4483 => elf.shndx.got.vaddr(elf),
4484
4485 //.QDSP6,
4486 //.@"386",
4487 .X86_64,
4488 => elf.shndx.got_plt.vaddr(elf),
4489 },
4490 .size = 0,
4491 .type = .NOTYPE,
4492 .bind = .strong,
4493 .visibility = .HIDDEN,
4494 .shndx = elf.shndx.got,
4495 }) catch |err| switch (err) {
4496 error.MultipleDefinitions => unreachable, // no inputs are processed yet
4497 };
4498 _ = elf.addGlobalSymbolAssumeCapacity(.{
4499 .node = .none,
4500 .name = try .string(elf, "__init_array_start"),
4501 .value = 0,
4502 .size = 0,
4503 .type = .NOTYPE,
4504 .bind = .strong,
4505 .visibility = .HIDDEN,
4506 .shndx = .ABS,
4507 }) catch |err| switch (err) {
4508 error.MultipleDefinitions => unreachable, // no inputs are processed yet
4509 };
4510 _ = elf.addGlobalSymbolAssumeCapacity(.{
4511 .node = .none,
4512 .name = try .string(elf, "__init_array_end"),
4513 .value = 0,
4514 .size = 0,
4515 .type = .NOTYPE,
4516 .bind = .strong,
4517 .visibility = .HIDDEN,
4518 .shndx = .ABS,
4519 }) catch |err| switch (err) {
4520 error.MultipleDefinitions => unreachable, // no inputs are processed yet
4521 };
4522 _ = elf.addGlobalSymbolAssumeCapacity(.{
4523 .node = .none,
4524 .name = try .string(elf, "__fini_array_start"),
4525 .value = 0,
4526 .size = 0,
4527 .type = .NOTYPE,
4528 .bind = .strong,
4529 .visibility = .HIDDEN,
4530 .shndx = .ABS,
4531 }) catch |err| switch (err) {
4532 error.MultipleDefinitions => unreachable, // no inputs are processed yet
4533 };
4534 _ = elf.addGlobalSymbolAssumeCapacity(.{
4535 .node = .none,
4536 .name = try .string(elf, "__fini_array_end"),
4537 .value = 0,
4538 .size = 0,
4539 .type = .NOTYPE,
4540 .bind = .strong,
4541 .visibility = .HIDDEN,
4542 .shndx = .ABS,
4543 }) catch |err| switch (err) {
4544 error.MultipleDefinitions => unreachable, // no inputs are processed yet
4545 };
4546 _ = elf.addGlobalSymbolAssumeCapacity(.{
4547 .node = .none,
4548 .name = try .string(elf, "__preinit_array_start"),
4549 .value = 0,
4550 .size = 0,
4551 .type = .NOTYPE,
4552 .bind = .strong,
4553 .visibility = .HIDDEN,
4554 .shndx = .ABS,
4555 }) catch |err| switch (err) {
4556 error.MultipleDefinitions => unreachable, // no inputs are processed yet
4557 };
4558 _ = elf.addGlobalSymbolAssumeCapacity(.{
4559 .node = .none,
4560 .name = try .string(elf, "__preinit_array_end"),
4561 .value = 0,
4562 .size = 0,
4563 .type = .NOTYPE,
4564 .bind = .strong,
4565 .visibility = .HIDDEN,
4566 .shndx = .ABS,
4567 }) catch |err| switch (err) {
4568 error.MultipleDefinitions => unreachable, // no inputs are processed yet
4569 };
4570 if (have_dynamic_section) {
4571 _ = elf.addGlobalSymbolAssumeCapacity(.{
4572 .node = .wrap(elf.shndx.dynamic.get(elf).ni),
4573 .name = try .string(elf, "_DYNAMIC"),
4574 .value = elf.shndx.dynamic.vaddr(elf),
4575 .size = 0,
4576 .type = .NOTYPE,
4577 .bind = .strong,
4578 .visibility = .HIDDEN,
4579 .shndx = elf.shndx.dynamic,
4580 }) catch |err| switch (err) {
4581 error.MultipleDefinitions => unreachable, // no inputs are processed yet
4582 };
4583 }
4584 } else {
4585 assert(maybe_interp == null);
4586 assert(!have_dynamic_section);
4587 }
4588 if (elf.ni.tls.unwrap()) |tls_segment_ni| elf.shndx.tdata = try elf.addSection(tls_segment_ni, .{
4589 .name = ".tdata",
4590 .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true },
4591 .node_align = node_block_align,
4592 });
4593
4594 assert(elf.nodes.len == expected_nodes_len);
4595 assert(elf.shdrs.items.len == shnum - 1); // -1 to exclude SHN_UNDEF
4596
4597 for (1..shnum) |shndx_raw| { // start at 1 to exclude SHN_UNDEF
4598 const shndx: Section.Index = @fromBackingInt(@intCast(shndx_raw));
4599 elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {});
4600 }
4601
4602 if (have_dynamic_section) elf.dynamic = .{
4603 .flags = if (elf.options.z_now) std.elf.DF_BIND_NOW else 0,
4604 .flags_1 = f: {
4605 var f: u32 = 0;
4606 if (elf.options.z_now) f |= std.elf.DF_1_NOW;
4607 if (comp.config.output_mode == .Exe and comp.config.pie) f |= std.elf.DF_1_PIE;
4608 break :f f;
4609 },
4610 .rpath = str: {
4611 var buf: std.ArrayList(u8) = .empty;
4612 defer buf.deinit(gpa);
4613 for (elf.options.rpath_list, 0..) |path, i| {
4614 if (i > 0) try buf.append(gpa, ':');
4615 try buf.appendSlice(gpa, path);
4616 }
4617 break :str try elf.string(.dynstr, buf.items);
4618 },
4619 .soname = str: {
4620 const slice = elf.options.soname orelse break :str .empty;
4621 break :str try elf.string(.dynstr, slice);
4622 },
4623 };
4624
4625 if (@"type" != .REL) switch (elf.targetSegmentLoadAddressRestrictions()) {
4626 .none => {},
4627 .data_last => switch (elf.phdrSlice()) {
4628 inline else => |phdr| {
4629 // Ensure that the segment after `.data` (if any) is not a loadable segment.
4630 const next_phndx = phndx.data + 1;
4631 if (next_phndx < phdr.len) {
4632 switch (elf.targetLoad(&phdr[next_phndx].type)) {
4633 .NULL, .LOAD => unreachable, // data segment should be the last loadable segment
4634 else => {},
4635 }
4636 }
4637 },
4638 },
4639 };
4640}
4641
4642pub fn startProgress(elf: *Elf, prog_node: std.Progress.Node) void {
4643 prog_node.increaseEstimatedTotalItems(4);
4644 elf.const_prog_node = prog_node.start("Constants", elf.pending_uavs.items.len);
4645 elf.synth_prog_node = prog_node.start("Synthetics", count: {
4646 var count: usize = 0;
4647 for (&elf.lazy.values) |*lazy| count += lazy.map.count() - lazy.pending_index;
4648 break :count count;
4649 });
4650 elf.mf.update_prog_node = prog_node.start("Relocations", elf.mf.updates.items.len);
4651 elf.input_prog_node = prog_node.start("Inputs", (elf.inputs.items.len - elf.input_pending_index) +
4652 (elf.input_sections.items.len - elf.input_section_pending_index));
4653}
4654
4655pub fn endProgress(elf: *Elf) void {
4656 elf.input_prog_node.end();
4657 elf.input_prog_node = .none;
4658 elf.mf.update_prog_node.end();
4659 elf.mf.update_prog_node = .none;
4660 elf.synth_prog_node.end();
4661 elf.synth_prog_node = .none;
4662 elf.const_prog_node.end();
4663 elf.const_prog_node = .none;
4664}
4665
4666fn getNode(elf: *const Elf, ni: MappedFile.Node.Index) Node {
4667 return elf.nodes.get(@backingInt(ni));
4668}
4669/// Asserts that `ni` is a section, input section, copied global, NAV, UAV, or lazy code/data.
4670fn getNodeShndx(elf: *const Elf, ni: MappedFile.Node.Index) Section.Index {
4671 return switch (elf.getNode(ni)) {
4672 .archive,
4673 .archive_header,
4674 .archive_input_member,
4675 .archive_elf_member_header,
4676 .elf,
4677 .ehdr,
4678 .shdr,
4679 .segment,
4680 => unreachable,
4681 .section => |shndx| shndx,
4682 .input_section,
4683 .copied_global,
4684 .nav,
4685 .uav,
4686 .lazy_code,
4687 .lazy_const_data,
4688 => elf.getNode(ni.parent(&elf.mf).unwrap().?).section,
4689 };
4690}
4691fn getNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
4692 return switch (elf.getNode(ni)) {
4693 .archive,
4694 .archive_header,
4695 .archive_input_member,
4696 .archive_elf_member_header,
4697 .elf,
4698 .ehdr,
4699 .shdr,
4700 .segment,
4701 .copied_global,
4702 => unreachable,
4703 .section => |shndx| shndx.vaddr(elf),
4704 .input_section => |isi| isi.ptrConst(elf).vaddr,
4705 inline .nav,
4706 .uav,
4707 .lazy_code,
4708 .lazy_const_data,
4709 => |i| Symbol.Id.local(i.symbol(elf)).value(elf),
4710 };
4711}
4712fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
4713 const parent_vaddr = switch (elf.getNode(ni.parent(&elf.mf).unwrap().?)) {
4714 .archive,
4715 .archive_header,
4716 .archive_input_member,
4717 .archive_elf_member_header,
4718 => unreachable,
4719 .elf => return 0,
4720 .ehdr, .shdr => unreachable,
4721 .segment => |phndx| switch (elf.phdrSlice()) {
4722 inline else => |phdr| elf.targetLoad(&phdr[phndx].vaddr),
4723 },
4724 .section => |shndx| if (shndx == elf.shndx.tdata) 0 else shndx.vaddr(elf),
4725 .input_section, .copied_global => unreachable,
4726 inline .nav, .uav, .lazy_code, .lazy_const_data => |i| Symbol.Id.local(i.symbol(elf)).value(elf),
4727 };
4728 const offset, _ = ni.location(&elf.mf).resolve(&elf.mf);
4729 return parent_vaddr + offset;
4730}
4731fn getNodeElfOffset(elf: *Elf, ni: MappedFile.Node.Index) u64 {
4732 return ni.fileLocation(&elf.mf, false).offset - elf.ni.elf.fileLocation(&elf.mf, false).offset;
4733}
4734
4735/// Deletes any existing relocations in the given node, and marks the start of the node's contiguous
4736/// sequence of relocations, so that the caller may append the node's updated relocations.
4737///
4738/// Asserts that `ni` must be a node which supports relocations (see `Elf.Node`). Does not support
4739/// the special-case sections '.plt' and '.dynamic'.
4740fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void {
4741 const symbol_relocs: *SymbolReloc.Index, const got_relocs: ?*GotReloc.Index = switch (elf.getNode(ni)) {
4742 .archive,
4743 .archive_header,
4744 .archive_input_member,
4745 .archive_elf_member_header,
4746 .elf,
4747 .ehdr,
4748 .shdr,
4749 .segment,
4750 .copied_global,
4751 => unreachable, // cannot contain relocs
4752 .section => unreachable, // cannot contain relocs (.plt and .dynamic unsupported)
4753 .input_section => |isi| .{
4754 &elf.input_sections.items[@backingInt(isi)].first_symbol_reloc,
4755 &elf.input_sections.items[@backingInt(isi)].first_got_reloc,
4756 },
4757 .nav => |nmi| .{
4758 &elf.navs.values()[@backingInt(nmi)].first_symbol_reloc,
4759 &elf.navs.values()[@backingInt(nmi)].first_got_reloc,
4760 },
4761 .uav => |umi| .{
4762 &elf.uavs.values()[@backingInt(umi)].first_symbol_reloc,
4763 null,
4764 },
4765 inline .lazy_code, .lazy_const_data => |lmi| .{
4766 &elf.lazy.getPtr(lmi.ref().kind).map.values()[lmi.ref().index].first_symbol_reloc,
4767 &elf.lazy.getPtr(lmi.ref().kind).map.values()[lmi.ref().index].first_got_reloc,
4768 },
4769 };
4770
4771 if (symbol_relocs.* != .none) {
4772 for (
4773 elf.symbol_relocs.items[@backingInt(symbol_relocs.*)..],
4774 @backingInt(symbol_relocs.*)..,
4775 ) |*reloc, index| {
4776 if (reloc.node != ni) break;
4777 reloc.delete(elf, @fromBackingInt(@intCast(index)));
4778 }
4779 }
4780 symbol_relocs.* = @fromBackingInt(@intCast(elf.symbol_relocs.items.len));
4781
4782 if (got_relocs) |ptr| {
4783 if (ptr.* != .none) {
4784 for (elf.got_relocs.items[@backingInt(ptr.*)..]) |*reloc| {
4785 if (reloc.node != ni.toOptional()) break;
4786 reloc.delete(elf);
4787 }
4788 }
4789 ptr.* = @fromBackingInt(@intCast(elf.got_relocs.items.len));
4790 }
4791}
4792
4793/// Given that `node` has moved, updates all relocations in `node` as needed. In relocatables, this
4794/// means updating the relocations' offsets. In ELF modules, this means applying the relocations.
4795fn flushMovedNodeRelocs(
4796 elf: *Elf,
4797 node: MappedFile.Node.Index,
4798 node_vaddr: u64,
4799 first_symbol_reloc: SymbolReloc.Index,
4800 first_got_reloc: GotReloc.Index,
4801) void {
4802 if (first_symbol_reloc != .none) {
4803 for (elf.symbol_relocs.items[@backingInt(first_symbol_reloc)..]) |*reloc| {
4804 if (reloc.node != node) break;
4805 if (reloc.rela_index.unwrap()) |rela_index| {
4806 // The node has moved, so the offset of the relocation within the section might have
4807 // changed, so update the `offset` field of the `ElfN.Rela` entry.
4808 reloc.relaSection(elf).relaSetOffset(elf, rela_index, node_vaddr + reloc.offset);
4809 }
4810 // This is not just the inverse of the above condition, because if `reloc` is relative
4811 // to the base of this DSO, then `rela_index` is an `R_*_RELATIVE` relocation, but we
4812 // still need to call `SymbolReloc.apply` to update that relocation's addend.
4813 if (elf.ehdrType() != .REL) {
4814 reloc.apply(elf);
4815 }
4816 }
4817 }
4818
4819 if (first_got_reloc != .none) {
4820 for (elf.got_relocs.items[@backingInt(first_got_reloc)..]) |*reloc| {
4821 if (reloc.node != node.toOptional()) break;
4822 reloc.apply(elf);
4823 }
4824 }
4825}
4826
4827fn identClass(elf: *const Elf) std.elf.CLASS {
4828 return @fromBackingInt(elf.ni.elf.sliceConst(&elf.mf)[std.elf.EI.CLASS]);
4829}
4830
4831/// Like `std.elf.ET`, but only includes the ELF machine architectures we support, so that we can
4832/// use exhaustive `switch` statements in the linker implementation.
4833const EhdrMachine = enum(u16) {
4834 AARCH64 = @backingInt(std.elf.EM.AARCH64),
4835 LOONGARCH = @backingInt(std.elf.EM.LOONGARCH),
4836 PPC64 = @backingInt(std.elf.EM.PPC64),
4837 RISCV = @backingInt(std.elf.EM.RISCV),
4838 SPARCV9 = @backingInt(std.elf.EM.SPARCV9),
4839 X86_64 = @backingInt(std.elf.EM.X86_64),
4840
4841 fn toElf(m: EhdrMachine) std.elf.EM {
4842 return @bitCast(m);
4843 }
4844 /// Returns `null` if `m` is not a supported ELF machine architecture.
4845 fn fromElf(m: std.elf.EM) ?EhdrMachine {
4846 return std.enums.fromInt(EhdrMachine, @backingInt(m));
4847 }
4848};
4849/// Like `std.elf.ET`, but only includes the types of ELF file we can produce, so that we can use
4850/// exhaustive `switch` statements in the linker implementation.
4851const EhdrType = enum(u16) {
4852 REL = @backingInt(std.elf.ET.REL),
4853 EXEC = @backingInt(std.elf.ET.EXEC),
4854 DYN = @backingInt(std.elf.ET.DYN),
4855 fn toElf(t: EhdrType) std.elf.ET {
4856 return @bitCast(t);
4857 }
4858};
4859fn ehdrMachine(elf: *const Elf) EhdrMachine {
4860 const ehdr_slice = elf.ni.ehdr.sliceConst(&elf.mf);
4861 switch (elf.identClass()) {
4862 .NONE, _ => unreachable,
4863 inline else => |class| {
4864 const ehdr: *const class.ElfN().Ehdr = @ptrCast(@alignCast(ehdr_slice));
4865 return @bitCast(elf.targetLoad(&ehdr.machine));
4866 },
4867 }
4868}
4869fn ehdrType(elf: *const Elf) EhdrType {
4870 const ehdr_slice = elf.ni.ehdr.sliceConst(&elf.mf);
4871 switch (elf.identClass()) {
4872 .NONE, _ => unreachable,
4873 inline else => |class| {
4874 const ehdr: *const class.ElfN().Ehdr = @ptrCast(@alignCast(ehdr_slice));
4875 return @bitCast(elf.targetLoad(&ehdr.type));
4876 },
4877 }
4878}
4879
4880fn targetPtrSize(elf: *const Elf) u8 {
4881 return elf.identClass().size();
4882}
4883/// Page alignment for the target platform.
4884/// Usually this returns the maximum page size supported on the
4885/// target to maximize compatibility but there can be exceptions.
4886fn targetPageAlign(elf: *const Elf) Alignment {
4887 return .fromByteUnits(switch (elf.ehdrMachine()) {
4888 .AARCH64 => 0x10000,
4889 .LOONGARCH => 0x10000,
4890 .PPC64 => 0x10000,
4891 .RISCV => 0x1000,
4892 .SPARCV9 => 0x100000,
4893 .X86_64 => 0x1000,
4894
4895 //.@"68K" => 0x2000,
4896 //.AMDGPU => 0x10000,
4897 //.ARC_COMPACT2 => 0x2000,
4898 //.AVR => 0x1,
4899 //.BPF => 0x100000,
4900 //.MIPS => 0x10000,
4901 //.MSP430 => 0x4,
4902 //.PPC => 0x10000,
4903 //.QDSP6 => 0x10000,
4904 //.SPARC => 0x10000,
4905 //.SPARC32PLUS => 0x10000,
4906 });
4907}
4908fn targetEndian(elf: *const Elf) std.lang.Endian {
4909 const ident_data: std.elf.DATA = @fromBackingInt(elf.ni.elf.sliceConst(&elf.mf)[std.elf.EI.DATA]);
4910 return ident_data.endian();
4911}
4912fn targetTlsVariant(elf: *const Elf) union(enum) {
4913 /// TP points to the start of the TCB, which immediately precedes the executable's TLS block.
4914 I_original: struct { tcb_size: u8 },
4915 /// TP points at a fixed offset from the start of the executable's TLS block.
4916 I_modified: struct { tp_off: u32 },
4917 /// TP points to the TCB, which immediately *succeeds* the executable's TLS block. (In other
4918 /// words, TP points to the *end* of the executable's TLS block.)
4919 II,
4920} {
4921 return switch (elf.ehdrMachine()) {
4922 .AARCH64 => .{ .I_original = .{ .tcb_size = 2 * elf.targetPtrSize() } },
4923 .LOONGARCH => .{ .I_original = .{ .tcb_size = elf.targetPtrSize() } },
4924 .PPC64 => .{ .I_modified = .{ .tp_off = 0x7000 } },
4925 .RISCV => .{ .I_modified = .{ .tp_off = 0 } },
4926 .SPARCV9 => .II,
4927 .X86_64 => .II,
4928 };
4929}
4930const PltInfo = struct {
4931 /// If not `null`, there is a `.got.plt` section containing the target addresses, and the PLT
4932 /// itself is immutable. If `false`, JUMP_SLOT relocations write directly to the `.plt` section,
4933 /// which must therefore be mutable.
4934 got_plt: ?struct { header_entries: u8 },
4935 /// If not `null`, there is a `.plt.sec` section, and every function in the PLT has both a
4936 /// `.plt` entry and a `.plt.sec` entry. Jumps targeting the PLT should jump to the `.plt.sec`
4937 /// entry, not the `.plt` entry. The `.plt.sec` section has no header entries, and is aligned to
4938 /// the same boundary as the `.plt` section.
4939 plt_sec: ?struct { entry_size: u8 },
4940 @"align": Alignment,
4941 entry_size: u8,
4942 header_entries: u8,
4943
4944 fn fromMachine(machine: EhdrMachine) PltInfo {
4945 return switch (machine) {
4946 .AARCH64, .PPC64, .RISCV => @panic(@tagName(machine)),
4947 .LOONGARCH => .{
4948 .got_plt = .{ .header_entries = 2 },
4949 .plt_sec = null,
4950 .@"align" = .@"4",
4951 .entry_size = 16,
4952 .header_entries = 2,
4953 },
4954 .SPARCV9 => .{
4955 .got_plt = null,
4956 .plt_sec = null,
4957 .@"align" = .fromByteUnits(256),
4958 .entry_size = 32,
4959 .header_entries = 4,
4960 },
4961 .X86_64 => .{
4962 .got_plt = .{ .header_entries = 3 },
4963 .plt_sec = .{ .entry_size = 16 },
4964 .@"align" = .@"16",
4965 .entry_size = 16,
4966 .header_entries = 1,
4967 },
4968 };
4969 }
4970};
4971fn targetPltInfo(elf: *const Elf) PltInfo {
4972 return .fromMachine(elf.ehdrMachine());
4973}
4974const DynsymHashInfo = enum(u32) {
4975 @"4" = 4,
4976 @"8" = 8,
4977
4978 fn Int(comptime self: DynsymHashInfo) type {
4979 return switch (self) {
4980 .@"4" => u32,
4981 .@"8" => u64,
4982 };
4983 }
4984
4985 fn Header(comptime self: DynsymHashInfo) type {
4986 return switch (self) {
4987 .@"4" => std.elf.hash.Header32,
4988 .@"8" => std.elf.hash.Header64,
4989 };
4990 }
4991};
4992fn targetDynsymHashInfo(elf: *const Elf) DynsymHashInfo {
4993 return switch (elf.ehdrMachine()) {
4994 else => .@"4",
4995 // TODO: Alpha and S390x will need to use either `."@4"` or `.@"8"` depending on `elf.identClass()`.
4996 };
4997}
4998/// Specifies any restrictions the current target has regarding how segments are ordered in the
4999/// virtual address space. Most targets do not have any such restrictions.
5000fn targetSegmentLoadAddressRestrictions(elf: *const Elf) enum {
5001 none,
5002 /// The "mutable data" segment must be the last loadable segment in the virtual address space.
5003 data_last,
5004} {
5005 return switch (elf.ehdrMachine()) {
5006 .AARCH64,
5007 .PPC64,
5008 .RISCV,
5009 .X86_64,
5010 .LOONGARCH,
5011 => .none,
5012
5013 // SPARC uses `R_SPARC_PC{10,22}` relocations to construct pointers to the GOT, but these
5014 // relocations write an *unsigned* PC-relative offset. This cannot even be worked around by
5015 // using a larger code model, because the crt `_start` assembly always uses these specific
5016 // relocations. Therefore, to avoid relocation errors, all code must appear before the GOT
5017 // in the virtual address space. The easiest way for us to do that is to ensure that the
5018 // "mutable data" segment, containing the GOT, is the last segment in the address space.
5019 .SPARCV9 => .data_last,
5020 };
5021}
5022fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child {
5023 const pointer_ty = @typeInfo(@TypeOf(ptr)).pointer;
5024 const Child = pointer_ty.child;
5025 const alignment = pointer_ty.attrs.@"align" orelse @alignOf(Child);
5026 return switch (@typeInfo(Child)) {
5027 else => @compileError(@typeName(Child)),
5028 .int => std.mem.toNative(Child, ptr.*, elf.targetEndian()),
5029 .@"enum" => |@"enum"| @fromBackingInt(elf.targetLoad(@as(*align(alignment) const @"enum".tag_type, @ptrCast(ptr)))),
5030 .@"struct" => |@"struct"| @bitCast(
5031 elf.targetLoad(@as(*align(alignment) @"struct".backing_integer.?, @ptrCast(ptr))),
5032 ),
5033 };
5034}
5035fn targetStore(elf: *const Elf, ptr: anytype, val: @typeInfo(@TypeOf(ptr)).pointer.child) void {
5036 const pointer_ty = @typeInfo(@TypeOf(ptr)).pointer;
5037 const Child = pointer_ty.child;
5038 const alignment = pointer_ty.attrs.@"align" orelse @alignOf(Child);
5039 return switch (@typeInfo(Child)) {
5040 else => @compileError(@typeName(Child)),
5041 .int => ptr.* = std.mem.nativeTo(Child, val, elf.targetEndian()),
5042 .@"enum" => |@"enum"| elf.targetStore(
5043 @as(*align(alignment) @"enum".tag_type, @ptrCast(ptr)),
5044 @backingInt(val),
5045 ),
5046 .@"struct" => |@"struct"| elf.targetStore(
5047 @as(*align(alignment) @"struct".backing_integer.?, @ptrCast(ptr)),
5048 @bitCast(val),
5049 ),
5050 };
5051}
5052
5053const EhdrPtr = union(std.elf.CLASS) {
5054 NONE: noreturn,
5055 @"32": *std.elf.Elf32.Ehdr,
5056 @"64": *std.elf.Elf64.Ehdr,
5057};
5058fn ehdrPtr(elf: *Elf) EhdrPtr {
5059 const slice = elf.ni.ehdr.slice(&elf.mf);
5060 return switch (elf.identClass()) {
5061 .NONE, _ => unreachable,
5062 inline else => |class| @unionInit(
5063 EhdrPtr,
5064 @tagName(class),
5065 @ptrCast(@alignCast(slice)),
5066 ),
5067 };
5068}
5069
5070const PhdrSlice = union(std.elf.CLASS) {
5071 NONE: noreturn,
5072 @"32": []std.elf.Elf32.Phdr,
5073 @"64": []std.elf.Elf64.Phdr,
5074};
5075fn phdrSlice(elf: *Elf) PhdrSlice {
5076 assert(elf.ehdrType() != .REL);
5077 return switch (elf.identClass()) {
5078 .NONE, _ => unreachable,
5079 inline else => |class| @unionInit(PhdrSlice, @tagName(class), @ptrCast(@alignCast(
5080 elf.ni.phdr.slice(&elf.mf)[0 .. elf.phdrs.items.len * @sizeOf(class.ElfN().Phdr)],
5081 ))),
5082 };
5083}
5084
5085const ShdrPtr = union(std.elf.CLASS) {
5086 NONE: noreturn,
5087 @"32": *std.elf.Elf32.Shdr,
5088 @"64": *std.elf.Elf64.Shdr,
5089};
5090fn shdrPtr(elf: *Elf, shndx: Section.Index) ShdrPtr {
5091 const raw_slice = elf.ni.shdr.slice(&elf.mf);
5092 switch (elf.identClass()) {
5093 .NONE, _ => unreachable,
5094 inline else => |class| {
5095 const shdrs_len = elf.shdrs.items.len + 1; // +1 for SHN_UNDEF
5096 const shdr_slice: []class.ElfN().Shdr = @ptrCast(@alignCast(
5097 raw_slice[0 .. shdrs_len * @sizeOf(class.ElfN().Shdr)],
5098 ));
5099 const shdr_ptr = &shdr_slice[@backingInt(shndx)];
5100 return @unionInit(ShdrPtr, @tagName(class), shdr_ptr);
5101 },
5102 }
5103}
5104
5105const SymPtr = union(std.elf.CLASS) {
5106 NONE: noreturn,
5107 @"32": *std.elf.Elf32.Sym,
5108 @"64": *std.elf.Elf64.Sym,
5109};
5110fn symPtr(elf: *Elf, index: Symbol.Index) SymPtr {
5111 const raw_slice = Section.Index.symtab.get(elf).ni.slice(&elf.mf);
5112 switch (elf.shdrPtr(.symtab)) {
5113 inline else => |shdr, class| {
5114 const size = elf.targetLoad(&shdr.size);
5115 const slice: []class.ElfN().Sym = @ptrCast(@alignCast(raw_slice[0..@intCast(size)]));
5116 return @unionInit(SymPtr, @tagName(class), &slice[@backingInt(index)]);
5117 },
5118 }
5119}
5120fn dynsymPtr(elf: *Elf, index: u32) SymPtr {
5121 const raw_slice = elf.shndx.dynsym.get(elf).ni.slice(&elf.mf);
5122 switch (elf.shdrPtr(elf.shndx.dynsym)) {
5123 inline else => |shdr, class| {
5124 const size = elf.targetLoad(&shdr.size);
5125 const slice: []class.ElfN().Sym = @ptrCast(@alignCast(raw_slice[0..@intCast(size)]));
5126 return @unionInit(SymPtr, @tagName(class), &slice[index]);
5127 },
5128 }
5129}
5130
5131fn navType(elf: *const Elf, nav_resolved: InternPool.Nav.Resolved) std.elf.STT {
5132 const any_non_single_threaded = elf.base.comp.config.any_non_single_threaded;
5133 return if (any_non_single_threaded and nav_resolved.@"threadlocal")
5134 .TLS
5135 else if (elf.base.comp.zcu.?.intern_pool.isFunctionType(nav_resolved.type))
5136 .FUNC
5137 else
5138 .OBJECT;
5139}
5140fn mapInputSection(elf: *Elf, opts: struct {
5141 name: []const u8,
5142 flags: std.elf.SHF,
5143 entsize: std.elf.Xword,
5144}) (Error || error{
5145 UnsupportedSectionFlags,
5146 TlsSectionUnavailable,
5147 StripSection,
5148 SectionFlagsConflict,
5149 SectionTypeConflict,
5150})!Section.Index {
5151 const gpa = elf.base.comp.gpa;
5152 if (opts.flags.INFO_LINK or
5153 opts.flags.LINK_ORDER or
5154 opts.flags.OS_NONCONFORMING or
5155 (opts.flags.EXECINSTR and opts.flags.WRITE) or
5156 (opts.flags.EXECINSTR and opts.flags.TLS))
5157 {
5158 return error.UnsupportedSectionFlags;
5159 }
5160 if (opts.flags.TLS and elf.ni.tls == .none) {
5161 assert(!elf.base.comp.config.any_non_single_threaded);
5162 return error.TlsSectionUnavailable;
5163 }
5164
5165 if (elf.base.comp.config.debug_format == .strip and
5166 std.mem.startsWith(u8, opts.name, ".debug_") and
5167 !opts.flags.ALLOC)
5168 {
5169 return error.StripSection;
5170 }
5171
5172 const name: []const u8 = switch (elf.ehdrType()) {
5173 .REL => opts.name,
5174 .EXEC, .DYN => name: {
5175 if (std.mem.startsWith(u8, opts.name, ".text.")) break :name ".text";
5176 if (std.mem.startsWith(u8, opts.name, ".rodata.")) break :name ".rodata";
5177 if (std.mem.startsWith(u8, opts.name, ".data.")) break :name ".data";
5178 if (std.mem.startsWith(u8, opts.name, ".data.rel.ro.")) break :name ".data.rel.ro";
5179 if (std.mem.startsWith(u8, opts.name, ".tdata.")) break :name ".tdata";
5180 if (std.mem.startsWith(u8, opts.name, ".gcc_except_table.")) break :name ".gcc_except_table";
5181 // TODO: actually generate a bss section!
5182 if (std.mem.eql(u8, opts.name, ".bss")) break :name ".data";
5183 if (std.mem.startsWith(u8, opts.name, ".bss.")) break :name ".data";
5184 // TODO: actually generate a tbss section!
5185 if (std.mem.eql(u8, opts.name, ".tbss")) break :name ".tdata";
5186 if (std.mem.startsWith(u8, opts.name, ".tbss.")) break :name ".tdata";
5187 break :name opts.name;
5188 },
5189 };
5190 const existing_shndx: Section.Index = existing: {
5191 const name_shstrtab = try elf.string(.shstrtab, name);
5192 const gop = try elf.section_by_name.getOrPut(gpa, name_shstrtab);
5193 if (gop.found_existing) {
5194 break :existing @fromBackingInt(@intCast(gop.index + 1)); // +1 to account for SHN_UDNEF
5195 }
5196 errdefer assert(elf.section_by_name.pop().?.key == name_shstrtab);
5197 const parent_node: MappedFile.Node.Index = parent: {
5198 if (!opts.flags.ALLOC) break :parent elf.ni.elf;
5199 if (opts.flags.EXECINSTR) break :parent elf.ni.text;
5200 if (opts.flags.TLS) break :parent elf.ni.tls.unwrap().?;
5201 if (opts.flags.WRITE) break :parent elf.ni.data;
5202 break :parent elf.ni.rodata;
5203 };
5204 assert(gop.index == elf.shdrs.items.len);
5205 return elf.addSection(parent_node, .{
5206 .name = name,
5207 .type = .NULL, // because initial size is 0
5208 .flags = flags: {
5209 // We need to decompress the section for linking.
5210 var flags = opts.flags;
5211 flags.COMPRESSED = false;
5212 break :flags flags;
5213 },
5214 .entsize = std.math.lossyCast(u32, opts.entsize),
5215 });
5216 };
5217 switch (elf.shdrPtr(existing_shndx)) {
5218 inline else => |shdr| {
5219 // Validate that the input is compatible with this section
5220 const cur_flags = elf.targetLoad(&shdr.flags).shf;
5221 if (cur_flags.EXECINSTR != opts.flags.EXECINSTR or
5222 cur_flags.WRITE != opts.flags.WRITE or
5223 cur_flags.TLS != opts.flags.TLS)
5224 {
5225 return error.SectionFlagsConflict;
5226 }
5227
5228 switch (elf.targetLoad(&shdr.type)) {
5229 .NULL, .PROGBITS => {},
5230 else => return error.SectionTypeConflict,
5231 }
5232
5233 // All okay, combine the section flags
5234 elf.targetStore(&shdr.flags, .{ .shf = .{
5235 .EXECINSTR = cur_flags.EXECINSTR,
5236 .WRITE = cur_flags.WRITE,
5237 .TLS = cur_flags.TLS,
5238 .ALLOC = cur_flags.ALLOC or opts.flags.ALLOC,
5239 .STRINGS = cur_flags.STRINGS and opts.flags.STRINGS,
5240 .MERGE = cur_flags.MERGE and opts.flags.MERGE,
5241 } });
5242 },
5243 }
5244 return existing_shndx;
5245}
5246fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node.NavMapIndex {
5247 const gpa = zcu.gpa;
5248 const ip = &zcu.intern_pool;
5249 const nav = ip.getNav(nav_index);
5250
5251 try elf.ensureUnusedSymbolCapacity(1, .all_local);
5252 try elf.nodes.ensureUnusedCapacity(gpa, 1);
5253 try elf.navs.ensureUnusedCapacity(gpa, 1);
5254
5255 const nav_gop = elf.navs.getOrPutAssumeCapacity(nav_index);
5256 const nmi: Node.NavMapIndex = @fromBackingInt(@intCast(nav_gop.index));
5257 if (!nav_gop.found_existing) {
5258 const shndx: Section.Index = section: {
5259 if (nav.resolved.?.@"linksection".toSlice(ip)) |@"linksection"| {
5260 if (elf.mapInputSection(.{
5261 .name = @"linksection",
5262 .flags = .{
5263 .ALLOC = true,
5264 .EXECINSTR = ip.isFunctionType(nav.resolved.?.type),
5265 .WRITE = !nav.resolved.?.@"const",
5266 .TLS = elf.base.comp.config.any_non_single_threaded and
5267 nav.resolved.?.@"threadlocal",
5268 },
5269 .entsize = 0,
5270 })) |shndx| {
5271 break :section shndx;
5272 } else |err| switch (err) {
5273 error.StripSection,
5274 error.TlsSectionUnavailable,
5275 error.UnsupportedSectionFlags,
5276 error.SectionTypeConflict,
5277 error.SectionFlagsConflict,
5278 => {}, // fall back to default behavior below
5279
5280 else => |e| return e,
5281 }
5282 }
5283 if (elf.base.comp.config.any_non_single_threaded and nav.resolved.?.@"threadlocal") {
5284 break :section elf.shndx.tdata;
5285 } else if (!nav.resolved.?.@"const") {
5286 break :section .data;
5287 } else if (ip.isFunctionType(nav.resolved.?.type)) {
5288 break :section .text;
5289 } else {
5290 break :section .data_rel_ro; // TODO: it would be better to use `.rodata` if the NAV value doesn't have relocs
5291 }
5292 };
5293 const alignment: Alignment = switch (Type.fromInterned(nav.resolved.?.type).zigTypeTag(zcu)) {
5294 .@"fn" => a: {
5295 const mod = zcu.navFileScope(nav_index).mod.?;
5296 const target = &mod.resolved_target.result;
5297 const min = target_util.minFunctionAlignment(target);
5298 break :a .fromIp(switch (nav.resolved.?.@"align") {
5299 else => |a| a.maxStrict(min),
5300 .none => switch (mod.optimize_mode) {
5301 .debug,
5302 .safe,
5303 .fast,
5304 => target_util.defaultFunctionAlignment(target),
5305 .small => min,
5306 }.maxStrict(Type.fromInterned(nav.resolved.?.type).abiAlignment(zcu)),
5307 });
5308 },
5309 else => switch (nav.resolved.?.@"align") {
5310 .none => .fromIp(Type.fromInterned(nav.resolved.?.type).abiAlignment(zcu)),
5311 else => |a| .fromIp(a),
5312 },
5313 };
5314 try shndx.ensureAligned(elf, alignment);
5315 const node = try shndx.get(elf).ni.addFloatingChild(&elf.mf, gpa, .{
5316 .alignment = alignment,
5317 });
5318 nav_gop.value_ptr.* = .{
5319 .lsi = elf.addLocalSymbolAssumeCapacity(.{
5320 .node = .wrap(node),
5321 .name = try elf.string(.strtab, nav.fqn.toSlice(ip)),
5322 .value = 0,
5323 .size = 0,
5324 .type = elf.navType(nav.resolved.?),
5325 .shndx = shndx,
5326 }),
5327 .first_symbol_reloc = .none,
5328 .first_got_reloc = .none,
5329 };
5330 elf.nodes.appendAssumeCapacity(.{ .nav = nmi });
5331 }
5332 return nmi;
5333}
5334
5335fn uavMapIndex(
5336 elf: *Elf,
5337 uav_val: InternPool.Index,
5338 uav_align: InternPool.Alignment,
5339) Error!Node.UavMapIndex {
5340 const gpa = elf.base.comp.gpa;
5341 const zcu = elf.base.comp.zcu.?;
5342
5343 try elf.ensureUnusedSymbolCapacity(1, .all_local);
5344 try elf.nodes.ensureUnusedCapacity(gpa, 1);
5345 try elf.uavs.ensureUnusedCapacity(gpa, 1);
5346 try elf.pending_uavs.ensureUnusedCapacity(gpa, 1);
5347
5348 const abi_align = Value.fromInterned(uav_val).typeOf(zcu).abiAlignment(zcu);
5349 const resolved_align: Alignment = switch (uav_align) {
5350 .none => .fromIp(abi_align),
5351 else => |a| .fromIp(a.minStrict(abi_align)),
5352 };
5353
5354 const uav_gop = elf.uavs.getOrPutAssumeCapacity(uav_val);
5355 const umi: Node.UavMapIndex = @fromBackingInt(@intCast(uav_gop.index));
5356 if (!uav_gop.found_existing) {
5357 const shndx: Section.Index = .data_rel_ro; // TODO: it would be better to use `.rodata` if the UAV value doesn't have relocs
5358 try shndx.ensureAligned(elf, resolved_align);
5359 const node = try shndx.get(elf).ni.addFloatingChild(&elf.mf, gpa, .{
5360 .moved = true, // see assert at end of `genUav`
5361 .alignment = resolved_align,
5362 });
5363 var name_buf: [32]u8 = undefined;
5364 const name = std.mem.print(
5365 &name_buf,
5366 "__anon_{d}",
5367 .{@backingInt(uav_val)},
5368 ) catch unreachable;
5369 uav_gop.value_ptr.* = .{
5370 .lsi = elf.addLocalSymbolAssumeCapacity(.{
5371 .node = .wrap(node),
5372 .name = try elf.string(.strtab, name),
5373 .value = 0,
5374 .size = 0,
5375 .type = .OBJECT,
5376 .shndx = shndx,
5377 }),
5378 .first_symbol_reloc = .none,
5379 };
5380 elf.nodes.appendAssumeCapacity(.{ .uav = umi });
5381 elf.const_prog_node.increaseEstimatedTotalItems(1);
5382 elf.pending_uavs.appendAssumeCapacity(umi);
5383 } else {
5384 const node = uav_gop.value_ptr.lsi.index().ptr(elf).node.unwrap().?;
5385 const shndx = elf.getNode(node.parent(&elf.mf).unwrap().?).section;
5386 try shndx.ensureAligned(elf, resolved_align);
5387 if (resolved_align.order(node.alignment(&elf.mf)).compare(.gt)) {
5388 try node.realign(&elf.mf, gpa, resolved_align);
5389 }
5390 }
5391 return umi;
5392}
5393
5394/// Internal error set used by input parsing functions `loadObject`, `loadArchive`, `loadDso`.
5395const LoadParseInputError = Error || Io.File.SeekError || Io.Reader.Error;
5396
5397/// Returns `error.BadMagic` if a DSO or static archive has an incorrect magic number, which
5398/// indicates to the frontend that the input could be a GNU ld script instead.
5399pub fn loadInput(elf: *Elf, input: link.Input) (link.Error || error{BadMagic})!void {
5400 const diags = &elf.base.comp.link_diags;
5401 elf.loadInputInner(input) catch |err| switch (err) {
5402 else => |e| return e,
5403 error.MappedFileIo => return diags.fail(
5404 "failed to write output file: {t}",
5405 .{elf.mf.io_err.?},
5406 ),
5407 };
5408}
5409fn loadInputInner(elf: *Elf, input: link.Input) (Error || error{BadMagic})!void {
5410 const comp = elf.base.comp;
5411 const diags = &comp.link_diags;
5412 const io = comp.io;
5413 var buf: [4096]u8 = undefined;
5414 switch (input) {
5415 .object => |object| {
5416 var fr = object.file.reader(io, &buf);
5417 elf.loadObject(object.path, null, &fr, .{
5418 .offset = fr.logicalPos(),
5419 .size = fr.getSize() catch |err| switch (err) {
5420 error.Canceled => |e| return e,
5421 else => |e| return diags.fail(
5422 "failed to stat \"{f}\": {t}",
5423 .{ object.path.fmtEscapeString(), e },
5424 ),
5425 },
5426 }) catch |err| switch (err) {
5427 else => |e| return e,
5428 error.EndOfStream => return diags.failParse(
5429 object.path,
5430 "unexpected eof",
5431 .{},
5432 ),
5433 error.AccessDenied, error.Unexpected, error.Unseekable => |e| return diags.fail(
5434 "failed to read \"{f}\": {t}",
5435 .{ object.path.fmtEscapeString(), e },
5436 ),
5437 error.ReadFailed => switch (fr.err.?) {
5438 error.Canceled => |e| return e,
5439 else => |e| return diags.fail(
5440 "failed to read \"{f}\": {t}",
5441 .{ object.path.fmtEscapeString(), e },
5442 ),
5443 },
5444 };
5445 },
5446 .archive => |archive| {
5447 var fr = archive.file.reader(io, &buf);
5448 elf.loadArchive(archive.path, &fr) catch |err| switch (err) {
5449 else => |e| return e,
5450 error.EndOfStream => return diags.failParse(
5451 archive.path,
5452 "unexpected eof",
5453 .{},
5454 ),
5455 error.AccessDenied, error.Unexpected, error.Unseekable => |e| return diags.fail(
5456 "failed to read \"{f}\": {t}",
5457 .{ archive.path.fmtEscapeString(), e },
5458 ),
5459 error.ReadFailed => switch (fr.err.?) {
5460 error.Canceled => |e| return e,
5461 else => |e| return diags.fail(
5462 "failed to read \"{f}\": {t}",
5463 .{ archive.path.fmtEscapeString(), e },
5464 ),
5465 },
5466 };
5467 },
5468 .res => unreachable,
5469 .dso => |dso| {
5470 try elf.needed.ensureUnusedCapacity(elf.base.comp.gpa, 1);
5471 var fr = dso.file.reader(io, &buf);
5472 elf.loadDso(dso.path, &fr) catch |err| switch (err) {
5473 else => |e| return e,
5474 error.EndOfStream => return diags.failParse(
5475 dso.path,
5476 "unexpected eof",
5477 .{},
5478 ),
5479 error.AccessDenied, error.Unexpected, error.Unseekable => |e| return diags.fail(
5480 "failed to read \"{f}\": {t}",
5481 .{ dso.path.fmtEscapeString(), e },
5482 ),
5483 error.ReadFailed => switch (fr.err.?) {
5484 error.Canceled => |e| return e,
5485 else => |e| return diags.fail(
5486 "failed to read \"{f}\": {t}",
5487 .{ dso.path.fmtEscapeString(), e },
5488 ),
5489 },
5490 };
5491 },
5492 .dso_exact => |dso_exact| {
5493 log.debug("load dso_exact '{f}'", .{std.zig.fmtString(dso_exact.name)});
5494 if (elf.shndx.dynamic != .UNDEF) {
5495 try elf.needed.put(elf.base.comp.gpa, try elf.string(.dynstr, dso_exact.name), {});
5496 }
5497 // TODO: we need to get a resolved file path from the frontend, because we need to read
5498 // the shared object to discover symbol types.
5499 },
5500 }
5501}
5502fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadParseInputError || error{BadMagic})!void {
5503 const comp = elf.base.comp;
5504 const gpa = comp.gpa;
5505 const diags = &comp.link_diags;
5506 const r = &fr.interface;
5507
5508 log.debug("loadArchive({f})", .{path.fmtEscapeString()});
5509
5510 if (elf.ehdrType() == .REL) return; // this input does not affect the output artifact
5511
5512 {
5513 const magic = r.take(std.elf.ARMAG.len) catch |err| switch (err) {
5514 error.ReadFailed => |e| return e,
5515 error.EndOfStream => return error.BadMagic,
5516 };
5517 if (!std.mem.eql(u8, magic, std.elf.ARMAG)) {
5518 return error.BadMagic;
5519 }
5520 }
5521 var strtab: std.Io.Writer.Allocating = .init(gpa);
5522 defer strtab.deinit();
5523 while (r.takeStruct(std.elf.ar_hdr, native_endian)) |header| {
5524 if (!std.mem.eql(u8, &header.ar_fmag, std.elf.ARFMAG))
5525 return diags.failParse(path, "bad file magic", .{});
5526 const offset = fr.logicalPos();
5527 const size = header.size() catch
5528 return diags.failParse(path, "bad member size", .{});
5529 if (std.mem.eql(u8, &header.ar_name, std.elf.STRNAME)) {
5530 strtab.clearRetainingCapacity();
5531 try strtab.ensureTotalCapacityPrecise(size);
5532 r.streamExact(&strtab.writer, size) catch |err| switch (err) {
5533 error.WriteFailed => return error.OutOfMemory,
5534 else => |e| return e,
5535 };
5536 continue;
5537 }
5538 load_object: {
5539 if (std.mem.eql(u8, &header.ar_name, std.elf.SYMNAME) or
5540 std.mem.eql(u8, &header.ar_name, std.elf.SYM64NAME) or
5541 std.mem.eql(u8, &header.ar_name, std.elf.SYMDEFNAME) or
5542 std.mem.eql(u8, &header.ar_name, std.elf.SYMDEFSORTEDNAME))
5543 {
5544 break :load_object;
5545 }
5546 const member = header.name() orelse member: {
5547 const strtab_offset = header.nameOffset() catch |err| switch (err) {
5548 error.Overflow => break :member error.Overflow,
5549 error.InvalidCharacter => break :load_object,
5550 } orelse break :load_object;
5551 const strtab_written = strtab.written();
5552 if (strtab_offset > strtab_written.len) break :member error.Overflow;
5553 const member = std.mem.sliceTo(strtab_written[strtab_offset..], '\n');
5554 break :member if (std.mem.endsWith(u8, member, "/"))
5555 member[0 .. member.len - "/".len]
5556 else
5557 member;
5558 } catch |err| switch (err) {
5559 error.Overflow => return diags.failParse(path, "bad member name offset", .{}),
5560 };
5561 try elf.loadObject(path, member, fr, .{ .offset = offset, .size = size });
5562 }
5563 try fr.seekTo(std.mem.alignForward(u64, offset + size, 2));
5564 } else |err| switch (err) {
5565 error.EndOfStream => if (!fr.atEnd()) return error.EndOfStream,
5566 else => |e| return e,
5567 }
5568}
5569fn fmtMemberString(member: ?[]const u8) std.fmt.Alt(?[]const u8, memberStringEscape) {
5570 return .{ .data = member };
5571}
5572fn memberStringEscape(member: ?[]const u8, w: *std.Io.Writer) std.Io.Writer.Error!void {
5573 try w.print("({f})", .{std.zig.fmtString(member orelse return)});
5574}
5575fn loadObject(
5576 elf: *Elf,
5577 path: std.Build.Cache.Path,
5578 member: ?[]const u8,
5579 fr: *Io.File.Reader,
5580 fl: MappedFile.Node.FileLocation,
5581) LoadParseInputError!void {
5582 const comp = elf.base.comp;
5583 const gpa = comp.gpa;
5584 const diags = &comp.link_diags;
5585 const r = &fr.interface;
5586
5587 const input_index: Node.InputIndex = @fromBackingInt(@intCast(elf.inputs.items.len));
5588 log.debug("loadObject({f}{f})", .{ path.fmtEscapeString(), fmtMemberString(member) });
5589 elf.checkInputIdent(path, r) catch |err| switch (err) {
5590 else => |e| return e,
5591 error.BadMagic => return diags.failParse(
5592 path,
5593 "bad ELF magic",
5594 .{},
5595 ),
5596 };
5597
5598 const input = try elf.inputs.addOne(gpa);
5599 input.* = .{
5600 .path = path,
5601 .member = if (member) |m| try gpa.dupe(u8, m) else null,
5602 .extra = undefined,
5603 };
5604 if (elf.archive) |*archive| {
5605 // We're creating a static library, so just add this input as an archive member.
5606 assert(member == null); // don't try to put static library members into other static libraries
5607
5608 const first_member_oni = archive.header_ni.next(&elf.mf);
5609
5610 if (first_member_oni.unwrap()) |first_member_ni| switch (elf.getNode(first_member_ni)) {
5611 .archive_input_member, .archive_elf_member_header => {},
5612 .elf => unreachable, // always preceded by `.archive_elf_member_header`
5613 else => unreachable, // never a child of `.archive`
5614 };
5615
5616 try elf.nodes.ensureUnusedCapacity(gpa, 1);
5617 const new_member_ni = try archive.ni.addFooterChildBefore(&elf.mf, gpa, first_member_oni, .{
5618 .size = Alignment.@"2".forward(@sizeOf(std.elf.ar_hdr) + fl.size),
5619 .alignment = .@"2",
5620 });
5621 elf.nodes.appendAssumeCapacity(.{ .archive_input_member = input_index });
5622 input.extra = .{ .node = new_member_ni };
5623 elf.input_prog_node.increaseEstimatedTotalItems(1);
5624
5625 // The contents of the input will be written to the file by an idle task (`flushInput`), but
5626 // we do need to write the input's archive member header (`ar_hdr`) now, for two reasons:
5627 //
5628 // * If the input file has a long name, we need to add it to the archive member name string
5629 // table, which must happen deterministically (i.e. not in an idle task).
5630 //
5631 // * `flushInput` needs to know the actual file size (before padding to the alignment).
5632 const member_ar_hdr: *std.elf.ar_hdr = @ptrCast(
5633 new_member_ni.slice(&elf.mf)[0..@sizeOf(std.elf.ar_hdr)],
5634 );
5635 member_ar_hdr.* = .{
5636 .ar_name = undefined, // populated below
5637 .ar_date = "0 ".*,
5638 .ar_uid = "0 ".*,
5639 .ar_gid = "0 ".*,
5640 .ar_mode = "644 ".*,
5641 .ar_size = undefined, // populated below
5642 .ar_fmag = std.elf.ARFMAG.*,
5643 };
5644
5645 if (std.mem.print(&member_ar_hdr.ar_size, "{d}", .{fl.size})) |size_str| {
5646 @memset(member_ar_hdr.ar_size[size_str.len..], ' ');
5647 } else |err| switch (err) {
5648 error.NoSpaceLeft => return diags.failParse(
5649 path,
5650 "file size of {Bi} exceeds maximum size of archive member",
5651 .{fl.size},
5652 ),
5653 }
5654
5655 const member_name = std.fs.path.basename(path.sub_path);
5656 // After this call returns, `member_ar_hdr` is invalidated.
5657 try elf.populateArchiveMemberName(member_ar_hdr, member_name);
5658
5659 // Since we are not emitting the archive symbol table (yet?) we do not need to parse
5660 // the symbols in this input.
5661 return;
5662 }
5663
5664 elf.input_pending_index += 1;
5665 try elf.ensureUnusedSymbolCapacity(1, .all_local);
5666 input.extra = .{ .file_symbol = elf.addLocalSymbolAssumeCapacity(.{
5667 .node = .none,
5668 .name = try elf.string(.strtab, std.fs.path.stem(member orelse path.sub_path)),
5669 .value = 0,
5670 .size = 0,
5671 .type = .FILE,
5672 .shndx = .ABS,
5673 }) };
5674 const target_endian = elf.targetEndian();
5675 switch (elf.identClass()) {
5676 .NONE, _ => unreachable,
5677 inline else => |class| {
5678 const ElfN = class.ElfN();
5679 const ehdr = try r.peekStruct(ElfN.Ehdr, target_endian);
5680 if (ehdr.type != .REL) return diags.failParse(path, "unsupported object type", .{});
5681 if (ehdr.machine != elf.ehdrMachine().toElf())
5682 return diags.failParse(path, "bad machine", .{});
5683 if (ehdr.shoff == 0 or ehdr.shnum <= 1) return;
5684 if (ehdr.shoff + @as(u64, ehdr.shentsize) * @as(u64, ehdr.shnum) > fl.size)
5685 return diags.failParse(path, "bad section header location", .{});
5686 if (ehdr.shentsize < @sizeOf(ElfN.Shdr))
5687 return diags.failParse(path, "unsupported shentsize", .{});
5688 const sections = try gpa.alloc(struct { shdr: ElfN.Shdr, isi: ?InputSection.Index }, ehdr.shnum);
5689 defer gpa.free(sections);
5690 try fr.seekTo(fl.offset + ehdr.shoff);
5691 for (sections) |*section| {
5692 section.* = .{
5693 .shdr = try r.peekStruct(ElfN.Shdr, target_endian),
5694 .isi = null,
5695 };
5696 try r.discardAll(ehdr.shentsize);
5697 switch (section.shdr.type) {
5698 .NULL, .NOBITS => {},
5699 else => if (section.shdr.offset + section.shdr.size > fl.size)
5700 return diags.failParse(path, "bad section location", .{}),
5701 }
5702 }
5703 const shstrtab = shstrtab: {
5704 if (ehdr.shstrndx == std.elf.SHN_UNDEF or ehdr.shstrndx >= ehdr.shnum)
5705 return diags.failParse(path, "missing section names", .{});
5706 const shdr = &sections[ehdr.shstrndx].shdr;
5707 if (shdr.type != .STRTAB) return diags.failParse(path, "invalid shstrtab type", .{});
5708 const shstrtab = try gpa.alloc(u8, @intCast(shdr.size));
5709 errdefer gpa.free(shstrtab);
5710 try fr.seekTo(fl.offset + shdr.offset);
5711 try r.readSliceAll(shstrtab);
5712 break :shstrtab shstrtab;
5713 };
5714 defer gpa.free(shstrtab);
5715 try elf.nodes.ensureUnusedCapacity(gpa, ehdr.shnum - 1);
5716 try elf.input_sections.ensureUnusedCapacity(gpa, ehdr.shnum - 1);
5717 for (sections[1..]) |*section| {
5718 if (section.shdr.name >= shstrtab.len) continue;
5719 const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0);
5720 const opts: struct {
5721 shndx: Section.Index,
5722 has_file_bits: bool,
5723 node_fixed: bool,
5724 } = switch (section.shdr.type) {
5725 else => continue,
5726 .PROGBITS, .NOBITS => opts: {
5727 const shndx = elf.mapInputSection(.{
5728 .name = name,
5729 .flags = section.shdr.flags.shf,
5730 .entsize = section.shdr.entsize,
5731 }) catch |err| switch (err) {
5732 error.StripSection => continue,
5733 error.TlsSectionUnavailable => return diags.failParse(
5734 path,
5735 "thread-local storage section '{s}' is incompatible with '-fsingle-threaded'",
5736 .{name},
5737 ),
5738 error.UnsupportedSectionFlags => if (!section.shdr.flags.shf.ALLOC) {
5739 // It probably doesn't matter, just skip this section.
5740 continue;
5741 } else return diags.failParse(
5742 path,
5743 "unsupported flags for section '{s}'",
5744 .{name},
5745 ),
5746 error.SectionTypeConflict => if (!section.shdr.flags.shf.ALLOC) {
5747 // It probably doesn't matter, just skip this section.
5748 continue;
5749 } else return diags.failParse(
5750 path,
5751 "type of section '{s}' conflicts with other inputs",
5752 .{name},
5753 ),
5754 error.SectionFlagsConflict => if (!section.shdr.flags.shf.ALLOC) {
5755 // It probably doesn't matter, just skip this section.
5756 continue;
5757 } else return diags.failParse(
5758 path,
5759 "flags of section '{s}' conflict with other inputs",
5760 .{name},
5761 ),
5762 else => |e| return e,
5763 };
5764 if (section.shdr.flags.shf.COMPRESSED) {
5765 // SHF_COMPRESSED is only allowed on non-alloc sections.
5766 if (section.shdr.flags.shf.ALLOC) return diags.failParse(
5767 path,
5768 "section '{s}' has conflicting flags SHF_ALLOC and SHF_COMPRESSED",
5769 .{name},
5770 );
5771 // TODO: handle compressed input sections. We'll need to set a flag to
5772 // indicate that `flushInputSection` needs to decompress the section.
5773 // But because this section isn't SHF_ALLOC, it's probably okay to just
5774 // skip it for now.
5775 continue;
5776 }
5777 break :opts .{
5778 .shndx = shndx,
5779 .has_file_bits = section.shdr.type == .PROGBITS,
5780 // For well-known sections, we know that it's fine to have e.g. random
5781 // padding, so there's no need to make the sections fixed. For custom
5782 // sections, however, we do want fixed nodes to avoid padding.
5783 .node_fixed = shndx != .text and
5784 shndx != .rodata and
5785 shndx != .data and
5786 shndx != .data_rel_ro and
5787 shndx != elf.shndx.tdata,
5788 };
5789 },
5790 inline .INIT_ARRAY, .FINI_ARRAY, .PREINIT_ARRAY => |@"type"| .{
5791 .shndx = shndx: {
5792 // TODO: the input section name may include a "priority" value between 1
5793 // and 65535 which should affect the order we assemble input sections in
5794 const init_fini_section_name: []const u8 = switch (@"type") {
5795 .INIT_ARRAY => "init_array",
5796 .FINI_ARRAY => "fini_array",
5797 .PREINIT_ARRAY => "preinit_array",
5798 else => comptime unreachable,
5799 };
5800 const shndx: *Section.Index = &@field(elf.shndx, init_fini_section_name);
5801 const need_addralign: u8 = switch (class) {
5802 .NONE, _ => unreachable,
5803 .@"32" => 4,
5804 .@"64" => 8,
5805 };
5806 if (section.shdr.addralign != need_addralign) {
5807 return diags.failParse(path, "bad addralign on {t} shdr", .{@"type"});
5808 }
5809 if (shndx.* == .UNDEF) {
5810 try elf.createInitFiniArraySection(shndx, init_fini_section_name, @"type");
5811 }
5812 switch (elf.shdrPtr(shndx.*)) {
5813 inline else => |shdr| {
5814 const old_size = elf.targetLoad(&shdr.size);
5815 const new_size = old_size + section.shdr.size;
5816 elf.targetStore(&shdr.size, @intCast(new_size));
5817 elf.updateInitFiniArraySectionSize(shndx.*, init_fini_section_name);
5818 },
5819 }
5820 break :shndx shndx.*;
5821 },
5822 .has_file_bits = true,
5823 // This node must be fixed to prevent padding from being added between different
5824 // INIT_ARRAY/FINI_ARRAY/PREINIT_ARRAY input sections.
5825 .node_fixed = true,
5826 },
5827 };
5828 const need_align: Alignment = .fromByteUnits(
5829 std.math.ceilPowerOfTwoAssert(usize, @intCast(@max(section.shdr.addralign, 1))),
5830 );
5831 try opts.shndx.ensureAligned(elf, need_align);
5832 const add_node_opts: MappedFile.Node.AddOptions = .{
5833 .size = need_align.forward(section.shdr.size),
5834 .alignment = need_align,
5835 .moved = true, // see assert at end of `flushInputSection`
5836 };
5837 const ni = if (opts.node_fixed) ni: {
5838 const shndx_ni = opts.shndx.get(elf).ni;
5839 const after_oni: MappedFile.Node.Index.Optional = after: {
5840 const last_ni = shndx_ni.last(&elf.mf).unwrap() orelse break :after .none;
5841 break :after switch (last_ni.position(&elf.mf)) {
5842 .header => .wrap(last_ni),
5843 .footer, .floating => .none,
5844 };
5845 };
5846 break :ni try shndx_ni.addHeaderChildAfter(&elf.mf, gpa, after_oni, add_node_opts);
5847 } else ni: {
5848 break :ni try opts.shndx.get(elf).ni.addFloatingChild(&elf.mf, gpa, add_node_opts);
5849 };
5850 elf.nodes.appendAssumeCapacity(.{
5851 .input_section = @fromBackingInt(@intCast(elf.input_sections.items.len)),
5852 });
5853 section.isi = @fromBackingInt(@intCast(elf.input_sections.items.len));
5854 elf.input_sections.addOneAssumeCapacity().* = .{
5855 .input = input_index,
5856 .file_location = .{
5857 .offset = fl.offset + section.shdr.offset,
5858 .size = if (opts.has_file_bits) section.shdr.size else 0,
5859 },
5860 // The section vaddr is initially 0, because the symbol addresses are
5861 // zero-based. This will eventually be updated by `flushMoved`.
5862 .vaddr = 0,
5863 .node = ni,
5864 .first_symbol_reloc = .none,
5865 .first_got_reloc = .none,
5866 };
5867 elf.input_prog_node.increaseEstimatedTotalItems(1);
5868 }
5869 var symmap: std.ArrayList(Symbol.Id) = .empty;
5870 defer symmap.deinit(gpa);
5871 for (sections[1..], 1..) |*symtab, symtab_shndx| switch (symtab.shdr.type) {
5872 else => {},
5873 .SYMTAB => {
5874 if (symtab.shdr.entsize < @sizeOf(ElfN.Sym))
5875 return diags.failParse(path, "unsupported symtab entsize", .{});
5876 const strtab = strtab: {
5877 if (symtab.shdr.link == std.elf.SHN_UNDEF or symtab.shdr.link >= ehdr.shnum)
5878 return diags.failParse(path, "missing symbol names", .{});
5879 const shdr = &sections[symtab.shdr.link].shdr;
5880 if (shdr.type != .STRTAB)
5881 return diags.failParse(path, "invalid strtab type", .{});
5882 const strtab = try gpa.alloc(u8, @intCast(shdr.size));
5883 errdefer gpa.free(strtab);
5884 try fr.seekTo(fl.offset + shdr.offset);
5885 try r.readSliceAll(strtab);
5886 break :strtab strtab;
5887 };
5888 defer gpa.free(strtab);
5889 const symnum = std.math.sub(u32, std.math.divExact(
5890 u32,
5891 @intCast(symtab.shdr.size),
5892 @intCast(symtab.shdr.entsize),
5893 ) catch return diags.failParse(
5894 path,
5895 "symtab section size (0x{x}) is not a multiple of entsize (0x{x})",
5896 .{ symtab.shdr.size, symtab.shdr.entsize },
5897 ), 1) catch continue;
5898 symmap.clearRetainingCapacity();
5899 try symmap.resize(gpa, symnum);
5900 try elf.ensureUnusedSymbolCapacity(symnum, .maybe_global);
5901 try fr.seekTo(fl.offset + symtab.shdr.offset + symtab.shdr.entsize);
5902 for (symmap.items) |*si| {
5903 si.* = .null;
5904 const input_sym = try r.peekStruct(ElfN.Sym, target_endian);
5905 try r.discardAll64(symtab.shdr.entsize);
5906 if (input_sym.name >= strtab.len or input_sym.shndx >= ehdr.shnum) continue;
5907
5908 const name = std.mem.sliceTo(strtab[input_sym.name..], 0);
5909
5910 const sym_type: std.elf.STT = switch (input_sym.info.type) {
5911 .NOTYPE, .OBJECT, .FUNC, .TLS => |t| t,
5912 .SECTION => .NOTYPE,
5913 .FILE, .COMMON, _ => continue,
5914 };
5915
5916 if (input_sym.shndx == std.elf.SHN_UNDEF) switch (input_sym.info.bind) {
5917 else => |bind| return diags.failParse(
5918 path,
5919 "symbol '{s}' has unsupported binding (0x{x})",
5920 .{ name, bind },
5921 ),
5922 .LOCAL => continue,
5923 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {
5924 si.* = elf.addGlobalSymbolAssumeCapacity(.{
5925 .node = .none,
5926 .name = try .string(elf, name),
5927 .value = input_sym.value,
5928 .size = input_sym.size,
5929 .type = sym_type,
5930 .bind = switch (bind) {
5931 .WEAK, .GNU_UNIQUE => .weak,
5932 .GLOBAL => .strong,
5933 else => unreachable,
5934 },
5935 .visibility = input_sym.other.visibility,
5936 .shndx = .UNDEF,
5937 }) catch |err| switch (err) {
5938 error.MultipleDefinitions => unreachable, // shndx is .UNDEF
5939 };
5940 continue;
5941 },
5942 };
5943
5944 const input_section_node = (sections[input_sym.shndx].isi orelse continue).node(elf);
5945
5946 switch (input_sym.info.bind) {
5947 else => |bind| return diags.failParse(
5948 path,
5949 "symbol '{s}' has unsupported binding (0x{x})",
5950 .{ name, bind },
5951 ),
5952 .LOCAL => {
5953 const lsi = elf.addLocalSymbolAssumeCapacity(.{
5954 .node = .wrap(input_section_node),
5955 .name = try elf.string(.strtab, name),
5956 .value = input_sym.value,
5957 .size = input_sym.size,
5958 .type = sym_type,
5959 .shndx = elf.getNodeShndx(input_section_node),
5960 });
5961 si.* = .local(lsi);
5962 },
5963 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {
5964 si.* = elf.addGlobalSymbolAssumeCapacity(.{
5965 .node = .wrap(input_section_node),
5966 .name = try .string(elf, name),
5967 .value = input_sym.value,
5968 .size = input_sym.size,
5969 .type = sym_type,
5970 .bind = switch (bind) {
5971 .WEAK, .GNU_UNIQUE => .weak,
5972 .GLOBAL => .strong,
5973 else => unreachable,
5974 },
5975 .visibility = input_sym.other.visibility,
5976 .shndx = elf.getNodeShndx(input_section_node),
5977 }) catch |err| switch (err) {
5978 error.MultipleDefinitions => return diags.failParse(
5979 path,
5980 "multiple definitions of '{s}'",
5981 .{name},
5982 ),
5983 };
5984 },
5985 }
5986 }
5987 for (sections[1..]) |*rel_sec| switch (rel_sec.shdr.type) {
5988 else => {},
5989 inline .REL, .RELA => |sht| {
5990 if (rel_sec.shdr.link != symtab_shndx or rel_sec.shdr.info == std.elf.SHN_UNDEF or
5991 rel_sec.shdr.info >= ehdr.shnum) continue;
5992 const Rel = switch (sht) {
5993 else => comptime unreachable,
5994 .REL => ElfN.Rel,
5995 .RELA => ElfN.Rela,
5996 };
5997 if (rel_sec.shdr.entsize < @sizeOf(Rel))
5998 return diags.failParse(path, "unsupported rel entsize", .{});
5999
6000 const loc_sec = &sections[rel_sec.shdr.info];
6001 const loc_node = (loc_sec.isi orelse continue).node(elf);
6002 elf.resetNodeRelocs(loc_node);
6003
6004 const relnum = std.math.divExact(
6005 u32,
6006 @intCast(rel_sec.shdr.size),
6007 @intCast(rel_sec.shdr.entsize),
6008 ) catch return diags.failParse(
6009 path,
6010 "relocation section size (0x{x}) is not a multiple of entsize (0x{x})",
6011 .{ rel_sec.shdr.size, rel_sec.shdr.entsize },
6012 );
6013 try elf.ensureUnusedRelocCapacity(loc_node, relnum);
6014 try fr.seekTo(fl.offset + rel_sec.shdr.offset);
6015 for (0..relnum) |_| {
6016 const rel = try r.peekStruct(Rel, target_endian);
6017 try r.discardAll64(rel_sec.shdr.entsize);
6018 if (rel.info.sym == 0) continue;
6019 if (rel.info.sym > symnum) return diags.failParse(
6020 path,
6021 "relocation target symbol index {d} exceeds symtab size",
6022 .{rel.info.sym},
6023 );
6024 const target = symmap.items[rel.info.sym - 1];
6025 if (target == Symbol.Id.null) {
6026 // If this is not an SHF_ALLOC section, then let's not report
6027 // this for now, because it probably doesn't affect the final
6028 // binary's functionality for this section to be a bit broken.
6029 if (loc_sec.shdr.flags.shf.ALLOC) {
6030 diags.addParseError(
6031 path,
6032 "unsupported symbol at index {d} required for relocation",
6033 .{rel.info.sym},
6034 );
6035 }
6036 continue;
6037 }
6038 const rt: MachineRelocType = .wrap(rel.info.type, elf);
6039 elf.addRelocAssumeCapacity(
6040 loc_node,
6041 rel.offset - loc_sec.shdr.addr,
6042 target,
6043 rel.addend,
6044 rt,
6045 ) catch |err| switch (err) {
6046 error.UnknownRelocation => diags.addParseError(
6047 path,
6048 "unknown relocation type '{f}'",
6049 .{rt.fmt(elf)},
6050 ),
6051 error.NonStaticRelocation => diags.addParseError(
6052 path,
6053 "non-static relocation type '{f}'",
6054 .{rt.fmt(elf)},
6055 ),
6056 error.UnimplementedRelocation => diags.addParseError(
6057 path,
6058 "TODO(Elf2): unimplemented relocation type '{f}'",
6059 .{rt.fmt(elf)},
6060 ),
6061 else => |e| return e,
6062 };
6063 }
6064 },
6065 };
6066 },
6067 };
6068 },
6069 }
6070}
6071/// This function may resize the archive header, so therefore invalidates `member_ar_hdr`.
6072fn populateArchiveMemberName(elf: *Elf, member_ar_hdr: *std.elf.ar_hdr, member_name: []const u8) Error!void {
6073 if (std.mem.print(&member_ar_hdr.ar_name, "{s}/", .{member_name})) |name_str| {
6074 @memset(member_ar_hdr.ar_name[name_str.len..], ' ');
6075 return;
6076 } else |err| switch (err) {
6077 error.NoSpaceLeft => {}, // handled below
6078 }
6079
6080 const gpa = elf.base.comp.gpa;
6081 const archive_header_ni = elf.archive.?.header_ni;
6082
6083 // The member's name is too big to put directly in the `ar_name` field, so it needs to go in the
6084 // "long name" string table instead (in the special member named "//").
6085
6086 _, const old_archive_header_size = archive_header_ni.location(&elf.mf).resolve(&elf.mf);
6087
6088 // We're going to add a new string at the end of the table. Update `member_ar_hdr` first,
6089 // because resizing the string table will invalidate it.
6090 const string_table_offset = old_archive_header_size - (std.elf.ARMAG.len + @sizeOf(std.elf.ar_hdr));
6091 if (std.mem.print(&member_ar_hdr.ar_name, "/{d}", .{string_table_offset})) |name_str| {
6092 @memset(member_ar_hdr.ar_name[name_str.len..], ' ');
6093 } else |inner_err| switch (inner_err) {
6094 error.NoSpaceLeft => {
6095 // The string table offset is itself too big to represent. This means the string table's
6096 // *size* is definitely too big to represent (we only get 10 bytes for that whereas we
6097 // get 16 here!), so as long as we still add the string, we're guaranteed to get a link
6098 // error for that reason. Therefore, we can just ignore this error and carry on.
6099 },
6100 }
6101
6102 // We set the size of the archive header node exactly, because we want padding bytes to go into
6103 // the root `.archive` node. That way, those bytes could still be used to grow the string table
6104 // if necessary, but they could also be used for new archive members.
6105 try archive_header_ni.resizeLeaf(&elf.mf, gpa, old_archive_header_size + member_name.len + 2);
6106
6107 const dest_slice = archive_header_ni.slice(&elf.mf)[@intCast(old_archive_header_size)..];
6108 @memcpy(dest_slice[0 .. dest_slice.len - 2], member_name);
6109 @memcpy(dest_slice[dest_slice.len - 2 ..], "/\n"); // yes, the terminator is weird
6110}
6111fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadParseInputError || error{BadMagic})!void {
6112 const comp = elf.base.comp;
6113 const gpa = comp.gpa;
6114 const diags = &comp.link_diags;
6115 const r = &fr.interface;
6116
6117 log.debug("loadDso({f})", .{path.fmtEscapeString()});
6118 try elf.checkInputIdent(path, r);
6119
6120 if (elf.ehdrType() == .REL) return; // this input does not affect the output artifact
6121
6122 const target_endian = elf.targetEndian();
6123 switch (elf.identClass()) {
6124 .NONE, _ => unreachable,
6125 inline else => |class| {
6126 const ElfN = class.ElfN();
6127 const ehdr = try r.peekStruct(ElfN.Ehdr, target_endian);
6128 if (ehdr.type != .DYN) return diags.failParse(path, "unsupported dso type", .{});
6129 if (ehdr.machine != elf.ehdrMachine().toElf())
6130 return diags.failParse(path, "bad machine", .{});
6131 if (ehdr.shnum > 0) try fr.seekTo(ehdr.shoff);
6132 // We're going to need to know the alignment of every section later.
6133 const section_aligns = try gpa.alloc(Alignment, ehdr.shnum);
6134 defer gpa.free(section_aligns);
6135 const dynamic_sh: ElfN.Shdr, const dynsym_sh: ElfN.Shdr = sh: {
6136 var dynamic_sh: ?ElfN.Shdr = null;
6137 var dynsym_sh: ?ElfN.Shdr = null;
6138 for (section_aligns) |*section_align| {
6139 const sh = try r.peekStruct(ElfN.Shdr, target_endian);
6140 try r.discardAll(ehdr.shentsize);
6141 section_align.* = .fromByteUnits(std.math.ceilPowerOfTwoAssert(
6142 usize,
6143 @intCast(@max(sh.addralign, 1)),
6144 ));
6145 switch (sh.type) {
6146 else => {},
6147 .DYNAMIC => dynamic_sh = sh,
6148 .DYNSYM => dynsym_sh = sh,
6149 }
6150 }
6151 break :sh .{
6152 dynamic_sh orelse return diags.failParse(path, "missing SHT_DYNAMIC section", .{}),
6153 dynsym_sh orelse return diags.failParse(path, "missing SHT_DYNSYM section", .{}),
6154 };
6155 };
6156 const dynstr_sh: ElfN.Shdr = sh: {
6157 if (dynsym_sh.link >= ehdr.shnum) {
6158 return diags.failParse(path, "bad dynamic string table section index", .{});
6159 }
6160 try fr.seekTo(ehdr.shoff + dynsym_sh.link * ehdr.shentsize);
6161 break :sh try r.peekStruct(ElfN.Shdr, target_endian);
6162 };
6163
6164 if (dynamic_sh.entsize != @sizeOf(ElfN.Addr) * 2) {
6165 return diags.failParse(path, "bad dynamic section entsize", .{});
6166 }
6167 const dynnum = std.math.divExact(
6168 u32,
6169 @intCast(dynamic_sh.size),
6170 @sizeOf(ElfN.Addr) * 2,
6171 ) catch return diags.failParse(
6172 path,
6173 "dynamic section size (0x{x}) is not a multiple of entsize (0x{x})",
6174 .{ dynamic_sh.size, @sizeOf(ElfN.Addr) * 2 },
6175 );
6176
6177 if (dynsym_sh.entsize < @sizeOf(ElfN.Sym)) {
6178 return diags.failParse(path, "bad dynsym entsize", .{});
6179 }
6180 const symnum = std.math.divExact(
6181 u32,
6182 @intCast(dynsym_sh.size),
6183 @intCast(dynsym_sh.entsize),
6184 ) catch return diags.failParse(
6185 path,
6186 "dynsym size (0x{x}) is not a multiple of entsize (0x{x})",
6187 .{ dynsym_sh.size, dynsym_sh.entsize },
6188 );
6189
6190 const dynstr = try gpa.alloc(u8, @intCast(dynstr_sh.size));
6191 defer gpa.free(dynstr);
6192 try fr.seekTo(dynstr_sh.offset);
6193 try r.readSliceAll(dynstr);
6194
6195 // Find the DT_SONAME dynamic entry so that it can become our DT_NEEDED entry.
6196 try fr.seekTo(dynamic_sh.offset);
6197 const soname: []const u8 = for (0..dynnum) |_| {
6198 const tag = try r.takeInt(ElfN.Addr, target_endian);
6199 const val = try r.takeInt(ElfN.Addr, target_endian);
6200 if (tag == std.elf.DT_SONAME) {
6201 // val is a dynstr index
6202 if (val >= dynstr.len) {
6203 return diags.failParse(path, "bad soname string", .{});
6204 }
6205 break std.mem.sliceTo(dynstr[@intCast(val)..], 0);
6206 }
6207 } else std.fs.path.basename(path.sub_path);
6208 try elf.needed.put(gpa, try elf.string(.dynstr, soname), {});
6209
6210 // Scan the symbol table and populate `elf.dso_globals`.
6211 const first_global = @min(dynsym_sh.info, symnum);
6212 try elf.dso_globals.ensureUnusedCapacity(gpa, symnum - first_global);
6213 try elf.ensureUnusedPltCapacity(symnum - first_global);
6214 try fr.seekTo(dynsym_sh.offset + first_global * dynsym_sh.entsize);
6215 for (first_global..symnum) |_| {
6216 const sym = try r.peekStruct(ElfN.Sym, target_endian);
6217 try r.discardAll(@intCast(dynsym_sh.entsize));
6218
6219 switch (sym.info.bind) {
6220 else => continue,
6221 .GLOBAL, .WEAK, .GNU_UNIQUE => {},
6222 }
6223 // STV_HIDDEN/STV_INTERNAL symbols should be marked as STB_LOCAL and hence skipped
6224 // above, but we might as well double-check.
6225 switch (sym.other.visibility) {
6226 .HIDDEN, .INTERNAL => continue,
6227 .DEFAULT, .PROTECTED => {},
6228 }
6229
6230 if (sym.shndx == std.elf.SHN_UNDEF) continue;
6231 if (sym.shndx >= ehdr.shnum) continue;
6232
6233 if (sym.name >= dynstr.len) {
6234 return diags.failParse(path, "bad symbol name string", .{});
6235 }
6236
6237 // We need to guess the worst-case alignment of the symbol. Yes, I know this seems
6238 // insane---refer to the doc comment on `alignment` in `Elf.dso_globals`.
6239 const sym_align: Alignment = switch (sym.value) {
6240 0 => section_aligns[sym.shndx],
6241 else => section_aligns[sym.shndx].min(@fromBackingInt(@intCast(@ctz(sym.value)))),
6242 };
6243
6244 const name = try elf.string(.strtab, std.mem.sliceTo(dynstr[sym.name..], 0));
6245 const gop = elf.dso_globals.getOrPutAssumeCapacity(name);
6246
6247 if (gop.found_existing and gop.value_ptr.type != .NOTYPE) {
6248 if (sym.size > gop.value_ptr.size or
6249 sym_align.compare(.gt, gop.value_ptr.alignment))
6250 {
6251 gop.value_ptr.size = @max(gop.value_ptr.size, sym.size);
6252 gop.value_ptr.alignment = gop.value_ptr.alignment.max(sym_align);
6253 if (elf.copied_globals.get(name)) |copied_global| {
6254 // We have a copy relocation for this global, but the amount of space we
6255 // reserved for it could be too small or underaligned!
6256 try Section.Index.data.ensureAligned(elf, gop.value_ptr.alignment);
6257 try copied_global.node.resizeLeaf(&elf.mf, gpa, gop.value_ptr.alignment.forward(gop.value_ptr.size));
6258 try copied_global.node.realign(&elf.mf, gpa, gop.value_ptr.alignment);
6259 const global_ptr = elf.globalByName(name).?;
6260 switch (elf.symPtr(global_ptr.symtab_index)) {
6261 inline else => |sym_ptr| elf.targetStore(&sym_ptr.size, @intCast(gop.value_ptr.size)),
6262 }
6263 switch (elf.dynsymPtr(global_ptr.dynsym_index)) {
6264 inline else => |dynsym_ptr| elf.targetStore(&dynsym_ptr.size, @intCast(gop.value_ptr.size)),
6265 }
6266 }
6267 }
6268 continue;
6269 }
6270
6271 gop.value_ptr.* = .{
6272 .type = sym.info.type,
6273 .size = sym.size,
6274 .alignment = sym_align,
6275 };
6276
6277 // If there's already an undefined symbol by this name of type STT_NOTYPE, populate
6278 // its type now.
6279 const global_ptr = elf.globals.strong_undef.getPtr(name) orelse
6280 elf.globals.weak_undef.getPtr(name) orelse
6281 continue;
6282
6283 if (global_ptr.dynsym_index == 0) continue;
6284
6285 if (elf.want_copied_globals.swapRemove(name)) {
6286 // We just found a DSO definition of a symbol for which we wanted a copy
6287 // relocation, so add one if we can!
6288 _ = try elf.maybeAddCopyRelocation(name);
6289 }
6290
6291 const sym_ptr = @field(elf.symPtr(global_ptr.symtab_index), @tagName(class));
6292 errdefer comptime unreachable; // messing with the output file could invalidate `sym_ptr`
6293
6294 switch (elf.targetLoad(&sym_ptr.other).visibility) {
6295 .HIDDEN, .INTERNAL, .PROTECTED => continue,
6296 .DEFAULT => {},
6297 }
6298
6299 const cur_info = elf.targetLoad(&sym_ptr.info);
6300 if (cur_info.type == .NOTYPE) {
6301 const new_type: std.elf.STT = switch (sym.info.type) {
6302 .GNU_IFUNC => .FUNC,
6303 else => |t| t,
6304 };
6305
6306 elf.targetStore(&sym_ptr.info, .{
6307 .bind = cur_info.bind,
6308 .type = new_type,
6309 });
6310
6311 const dynsym_ptr = @field(elf.dynsymPtr(global_ptr.dynsym_index), @tagName(class));
6312 elf.targetStore(&dynsym_ptr.info, .{
6313 .bind = elf.targetLoad(&dynsym_ptr.info).bind,
6314 .type = new_type,
6315 });
6316
6317 if (new_type == .FUNC) {
6318 // We turned STT_NOTYPE into STT_FUNC, so we now need a PLT entry...
6319 elf.addPltEntry(name, global_ptr.dynsym_index);
6320 // ...and therefore, we need to re-apply that symbol's relocations, as
6321 // some might be targeting its PLT entry.
6322 Symbol.Id.global(name).applyTargetRelocs(elf);
6323 }
6324 }
6325 }
6326 },
6327 }
6328}
6329
6330/// Validates that the `std.elf.Ident` present at the start of `r` is a compatible link input.
6331///
6332/// Returns an error if it is incompatible, or if the ident is broken or missing---usually
6333/// `error.AlreadyReported`, but if the magic number is missing or incorrect, returns
6334/// `error.BadMagic` instead.
6335///
6336/// Does not advance the position of `r`. Requires `r` to have a 16-byte buffer.
6337fn checkInputIdent(
6338 elf: *const Elf,
6339 path: std.Build.Cache.Path,
6340 r: *Io.Reader,
6341) error{ BadMagic, EndOfStream, AlreadyReported, ReadFailed }!void {
6342 const diags = &elf.base.comp.link_diags;
6343
6344 const magic = r.peek(std.elf.MAGIC.len) catch |err| switch (err) {
6345 error.ReadFailed => |e| return e,
6346 error.EndOfStream => return error.BadMagic,
6347 };
6348 if (!std.mem.eql(u8, magic, std.elf.MAGIC)) {
6349 return error.BadMagic;
6350 }
6351
6352 const ident = try r.peekStructPointer(std.elf.Ident);
6353 const target: *const std.elf.Ident =
6354 @ptrCast(elf.ni.elf.sliceConst(&elf.mf)[0..@sizeOf(std.elf.Ident)]);
6355
6356 if (ident.class != target.class) return diags.failParse(
6357 path,
6358 "bad ELF class ({?s})",
6359 .{std.enums.tagName(std.elf.CLASS, ident.class)},
6360 );
6361 if (ident.data != target.data) return diags.failParse(
6362 path,
6363 "bad ELF data encoding ({?s})",
6364 .{std.enums.tagName(std.elf.DATA, ident.data)},
6365 );
6366 if (ident.version != target.version) return diags.failParse(
6367 path,
6368 "bad ELF version ({d})",
6369 .{ident.version},
6370 );
6371
6372 // OSABI is a bit more complex. On Linux, `.NONE` and `.GNU` are both valid and both common.
6373 // It sounds reasonable to allow the value we chose *and* allow `.NONE`.
6374 const expect_abiversion: u8 = abiver: {
6375 if (ident.osabi == .NONE) break :abiver 0;
6376 if (ident.osabi == target.osabi) break :abiver target.abiversion;
6377 return diags.failParse(
6378 path,
6379 "bad ELF OS/ABI ({?s})",
6380 .{std.enums.tagName(std.elf.OSABI, ident.osabi)},
6381 );
6382 };
6383 if (ident.abiversion != expect_abiversion) return diags.failParse(
6384 path,
6385 "bad ELF ABI version ({d})",
6386 .{ident.abiversion},
6387 );
6388}
6389
6390fn createInitFiniArraySection(
6391 elf: *Elf,
6392 shndx: *Section.Index,
6393 comptime name: []const u8,
6394 @"type": std.elf.SHT,
6395) Error!void {
6396 assert(shndx.* == .UNDEF);
6397 const gpa = elf.base.comp.gpa;
6398 const addr_align: Alignment = switch (elf.identClass()) {
6399 .NONE, _ => unreachable,
6400 .@"32" => .@"4",
6401 .@"64" => .@"8",
6402 };
6403 assert(elf.section_by_name.count() == elf.shdrs.items.len);
6404 try elf.section_by_name.ensureUnusedCapacity(gpa, 1);
6405 shndx.* = try elf.addSection(elf.ni.data_rel_ro, .{
6406 .name = "." ++ name,
6407 .type = @"type",
6408 .flags = .{ .WRITE = true, .ALLOC = true },
6409 .node_align = addr_align,
6410 });
6411 elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {});
6412 try elf.ensureUnusedSymbolCapacity(2, .maybe_global);
6413 // These symbols definitely already have strong definitions, because we added them alongside the
6414 // other linker-defined symbols, all the way back in `initHeaders`.
6415 const start_sym_name = try elf.string(.strtab, "__" ++ name ++ "_start");
6416 const end_sym_name = try elf.string(.strtab, "__" ++ name ++ "_end");
6417 elf.setGlobalSymbolValue(start_sym_name, elf.globals.strong_def.getPtr(start_sym_name).?, .{
6418 .node = .wrap(shndx.get(elf).ni),
6419 .value = shndx.vaddr(elf),
6420 .size = 0,
6421 .type = .NOTYPE,
6422 .shndx = shndx.*,
6423 });
6424 elf.setGlobalSymbolValue(end_sym_name, elf.globals.strong_def.getPtr(end_sym_name).?, .{
6425 .node = .wrap(shndx.get(elf).ni),
6426 .value = shndx.vaddr(elf),
6427 .size = 0,
6428 .type = .NOTYPE,
6429 .shndx = shndx.*,
6430 });
6431}
6432fn updateInitFiniArraySectionSize(
6433 elf: *Elf,
6434 shndx: Section.Index,
6435 comptime name: []const u8,
6436) void {
6437 const end_vaddr: u64 = switch (elf.shdrPtr(shndx)) {
6438 inline else => |shdr| shndx.vaddr(elf) + elf.targetLoad(&shdr.size),
6439 };
6440 const end_sym_name = elf.stringExisting(.strtab, "__" ++ name ++ "_end");
6441 Symbol.Id.global(end_sym_name).flushMoved(elf, end_vaddr);
6442}
6443
6444pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) link.Error!void {
6445 const sub_prog_node = prog_node.start("ELF Prelink", 0);
6446 defer sub_prog_node.end();
6447
6448 const diags = &elf.base.comp.link_diags;
6449 elf.prelinkInner() catch |err| switch (err) {
6450 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
6451 else => |e| return e,
6452 };
6453}
6454fn prelinkInner(elf: *Elf) Error!void {
6455 const comp = elf.base.comp;
6456 const gpa = comp.gpa;
6457
6458 if (comp.zcu != null and !comp.config.use_llvm and elf.ni.elf == .root) {
6459 // We're using self-hosted codegen---add an input representing the Zig "object".
6460 try elf.ensureUnusedSymbolCapacity(1, .all_local);
6461 try elf.inputs.ensureUnusedCapacity(gpa, 1);
6462 const zcu_name = try std.fmt.allocPrint(gpa, "{s}_zcu", .{comp.root_name});
6463 defer gpa.free(zcu_name);
6464 const zcu_file_symbol = elf.addLocalSymbolAssumeCapacity(.{
6465 .node = .none,
6466 .name = try elf.string(.strtab, zcu_name),
6467 .value = 0,
6468 .size = 0,
6469 .type = .FILE,
6470 .shndx = .ABS,
6471 });
6472 elf.inputs.addOneAssumeCapacity().* = .{
6473 .path = elf.base.emit,
6474 .member = null,
6475 .extra = .{ .file_symbol = zcu_file_symbol },
6476 };
6477 elf.input_pending_index += 1;
6478 }
6479}
6480
6481fn prepareDynamic(elf: *Elf) Error!void {
6482 const comp = elf.base.comp;
6483
6484 if (elf.shndx.dynamic == .UNDEF) return;
6485
6486 // Static PIEs don't need a PLT, so we shouldn't emit the associated dynamic entries.
6487 const use_plt = !(comp.config.output_mode == .Exe and
6488 comp.config.link_mode == .static and
6489 comp.config.pie);
6490
6491 const dynamic_len: u64 = elf.needed.count() + @intFromBool(elf.dynamic.soname != .empty) +
6492 @intFromBool(elf.dynamic.rpath != .empty) +
6493 @intFromBool(elf.dynamic.flags != 0) + @intFromBool(elf.dynamic.flags_1 != 0) +
6494 @as(usize, @intFromBool(elf.shndx.init_array != .UNDEF)) * 2 +
6495 @as(usize, @intFromBool(elf.shndx.fini_array != .UNDEF)) * 2 +
6496 @as(usize, @intFromBool(elf.shndx.preinit_array != .UNDEF)) * 2 +
6497 @as(usize, @intFromBool(use_plt)) * 4 +
6498 @intFromBool(comp.config.output_mode == .Exe) +
6499 @intFromBool(elf.textrel_count > 0) + 9;
6500
6501 const dynamic_size = dynamic_len * 2 * elf.targetPtrSize();
6502
6503 try elf.shndx.dynamic.get(elf).ni.resizeLeaf(&elf.mf, comp.gpa, dynamic_size);
6504 switch (elf.shdrPtr(elf.shndx.dynamic)) {
6505 inline else => |shdr| elf.targetStore(&shdr.size, @intCast(dynamic_size)),
6506 }
6507}
6508
6509fn flushDynamic(elf: *Elf) void {
6510 const comp = elf.base.comp;
6511
6512 if (elf.shndx.dynamic == .UNDEF) return;
6513
6514 switch (elf.identClass()) {
6515 .NONE, _ => unreachable,
6516 inline else => |class| {
6517 const ElfN = class.ElfN();
6518
6519 // Static PIEs don't need a PLT, so we shouldn't emit the associated dynamic entries.
6520 const use_plt = !(comp.config.output_mode == .Exe and
6521 comp.config.link_mode == .static and
6522 comp.config.pie);
6523
6524 const dynamic_size = elf.targetLoad(&@field(elf.shdrPtr(elf.shndx.dynamic), @tagName(class)).size);
6525 const dynamic_slice = elf.shndx.dynamic.get(elf).ni.slice(&elf.mf)[0..@intCast(dynamic_size)];
6526 const dynamic_entries: [][2]ElfN.Addr = @ptrCast(@alignCast(dynamic_slice));
6527
6528 var dynamic_index: usize = 0;
6529
6530 for (
6531 dynamic_entries[dynamic_index..][0..elf.needed.count()],
6532 elf.needed.keys(),
6533 ) |*dynamic_entry, needed| {
6534 dynamic_entry.* = .{ std.elf.DT_NEEDED, @backingInt(needed) };
6535 }
6536 dynamic_index += elf.needed.count();
6537
6538 if (elf.dynamic.soname != .empty) {
6539 dynamic_entries[dynamic_index] = .{ std.elf.DT_SONAME, @backingInt(elf.dynamic.soname) };
6540 dynamic_index += 1;
6541 }
6542 if (elf.dynamic.rpath != .empty) {
6543 dynamic_entries[dynamic_index] = .{ std.elf.DT_RUNPATH, @backingInt(elf.dynamic.rpath) };
6544 dynamic_index += 1;
6545 }
6546 if (elf.dynamic.flags != 0) {
6547 dynamic_entries[dynamic_index] = .{ std.elf.DT_FLAGS, elf.dynamic.flags };
6548 dynamic_index += 1;
6549 }
6550 if (elf.dynamic.flags_1 != 0) {
6551 dynamic_entries[dynamic_index] = .{ std.elf.DT_FLAGS_1, elf.dynamic.flags_1 };
6552 dynamic_index += 1;
6553 }
6554 if (comp.config.output_mode == .Exe) {
6555 dynamic_entries[dynamic_index] = .{ std.elf.DT_DEBUG, 0 };
6556 dynamic_index += 1;
6557 }
6558 if (elf.textrel_count > 0) {
6559 dynamic_entries[dynamic_index] = .{ std.elf.DT_TEXTREL, 0 };
6560 dynamic_index += 1;
6561 }
6562 if (elf.shndx.init_array != .UNDEF) {
6563 dynamic_entries[dynamic_index..][0..2].* = .{
6564 .{ std.elf.DT_INIT_ARRAY, @intCast(elf.shndx.init_array.vaddr(elf)) },
6565 .{ std.elf.DT_INIT_ARRAYSZ, @intCast(elf.shndx.init_array.size(elf)) },
6566 };
6567 dynamic_index += 2;
6568 }
6569 if (elf.shndx.fini_array != .UNDEF) {
6570 dynamic_entries[dynamic_index..][0..2].* = .{
6571 .{ std.elf.DT_FINI_ARRAY, @intCast(elf.shndx.fini_array.vaddr(elf)) },
6572 .{ std.elf.DT_FINI_ARRAYSZ, @intCast(elf.shndx.fini_array.size(elf)) },
6573 };
6574 dynamic_index += 2;
6575 }
6576 if (elf.shndx.preinit_array != .UNDEF) {
6577 dynamic_entries[dynamic_index..][0..2].* = .{
6578 .{ std.elf.DT_PREINIT_ARRAY, @intCast(elf.shndx.preinit_array.vaddr(elf)) },
6579 .{ std.elf.DT_PREINIT_ARRAYSZ, @intCast(elf.shndx.preinit_array.size(elf)) },
6580 };
6581 dynamic_index += 2;
6582 }
6583 if (use_plt) {
6584 // The `DT_PLTGOT` entry usually points to `.got.plt`, but on targets where that
6585 // section does not exist it instead points to `.plt`.
6586 const pltgot_shndx: Section.Index = switch (elf.targetPltInfo().got_plt != null) {
6587 true => elf.shndx.got_plt,
6588 false => elf.shndx.plt,
6589 };
6590 dynamic_entries[dynamic_index..][0..4].* = .{
6591 .{ std.elf.DT_JMPREL, @intCast(elf.shndx.rela_plt.vaddr(elf)) },
6592 .{ std.elf.DT_PLTGOT, @intCast(pltgot_shndx.vaddr(elf)) },
6593 .{ std.elf.DT_PLTRELSZ, @intCast(elf.shndx.rela_plt.size(elf)) },
6594 .{ std.elf.DT_PLTREL, std.elf.DT_RELA },
6595 };
6596 dynamic_index += 4;
6597 }
6598
6599 dynamic_entries[dynamic_index..][0..9].* = .{
6600 .{ std.elf.DT_RELA, @intCast(elf.shndx.rela_dyn.vaddr(elf)) },
6601 .{ std.elf.DT_RELASZ, @intCast(elf.shndx.rela_dyn.size(elf)) },
6602 .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) },
6603 .{ std.elf.DT_SYMTAB, @intCast(elf.shndx.dynsym.vaddr(elf)) },
6604 .{ std.elf.DT_SYMENT, @sizeOf(ElfN.Sym) },
6605 .{ std.elf.DT_STRTAB, @intCast(elf.shndx.dynstr.vaddr(elf)) },
6606 .{ std.elf.DT_STRSZ, @intCast(elf.shndx.dynstr.size(elf)) },
6607 .{ std.elf.DT_HASH, @intCast(elf.shndx.hash.vaddr(elf)) },
6608 .{ std.elf.DT_NULL, 0 },
6609 };
6610 dynamic_index += 9;
6611
6612 assert(dynamic_index == dynamic_entries.len);
6613 if (elf.targetEndian() != native_endian) for (dynamic_entries) |*dynamic_entry|
6614 std.mem.byteSwapAllFields(@TypeOf(dynamic_entry.*), dynamic_entry);
6615 },
6616 }
6617}
6618
6619fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
6620 name: []const u8 = "",
6621 type: std.elf.SHT = .NULL,
6622 flags: std.elf.SHF = .{},
6623 size: std.elf.Xword = 0,
6624 link: std.elf.Word = 0,
6625 info: std.elf.Word = 0,
6626 addralign: Alignment = .@"1",
6627 entsize: std.elf.Word = 0,
6628 node_align: Alignment = .@"1",
6629}) Error!Section.Index {
6630 switch (opts.type) {
6631 .NULL => assert(opts.size == 0),
6632 .PROGBITS => assert(opts.size > 0),
6633 else => {},
6634 }
6635 if (opts.flags.ALLOC and elf.ehdrType() != .REL) {
6636 const phndx = elf.getNode(segment_ni).segment;
6637 try elf.ensureSegmentAligned(phndx, opts.addralign);
6638 }
6639 const gpa = elf.base.comp.gpa;
6640 try elf.nodes.ensureUnusedCapacity(gpa, 1);
6641 try elf.shdrs.ensureUnusedCapacity(gpa, 1);
6642 if (opts.flags.ALLOC) try elf.ensureUnusedSymbolCapacity(1, .all_local);
6643
6644 const shstrtab_entry = try elf.string(.shstrtab, opts.name);
6645 const shndx: Section.Index, const new_shdr_size = shndx: switch (elf.ehdrPtr()) {
6646 inline else => |ehdr, class| {
6647 const shndx, const shnum = alloc_shndx: switch (elf.targetLoad(&ehdr.shnum)) {
6648 1...std.elf.SHN_LORESERVE - 2 => |shndx| {
6649 const shnum = shndx + 1;
6650 elf.targetStore(&ehdr.shnum, shnum);
6651 break :alloc_shndx .{ shndx, shnum };
6652 },
6653 std.elf.SHN_LORESERVE - 1 => |shndx| {
6654 const shnum = shndx + 1;
6655 elf.targetStore(&ehdr.shnum, 0);
6656 elf.targetStore(&@field(elf.shdrPtr(.UNDEF), @tagName(class)).size, shnum);
6657 break :alloc_shndx .{ shndx, shnum };
6658 },
6659 std.elf.SHN_LORESERVE...std.elf.SHN_HIRESERVE => unreachable,
6660 0 => {
6661 const shnum_ptr = &@field(elf.shdrPtr(.UNDEF), @tagName(class)).size;
6662 const shndx: u32 = @intCast(elf.targetLoad(shnum_ptr));
6663 const shnum = shndx + 1;
6664 elf.targetStore(shnum_ptr, shnum);
6665 break :alloc_shndx .{ shndx, shnum };
6666 },
6667 };
6668 assert(shndx < @backingInt(Section.Index.LORESERVE));
6669 break :shndx .{ @fromBackingInt(shndx), @as(u64, elf.targetLoad(&ehdr.shentsize)) * @as(u64, shnum) };
6670 },
6671 };
6672 try elf.ni.shdr.ensureMinimumSize(&elf.mf, gpa, new_shdr_size);
6673 const parent_ni = switch (elf.ehdrType()) {
6674 .REL => elf.ni.elf,
6675 .EXEC, .DYN => segment_ni,
6676 };
6677 assert(opts.addralign.check(opts.size));
6678 const ni = try parent_ni.addFloatingChild(&elf.mf, gpa, .{
6679 .size = opts.node_align.forward(opts.size),
6680 .alignment = opts.addralign.max(opts.node_align),
6681 .resized = opts.size > 0,
6682 });
6683 const addr = elf.computeNodeVAddr(ni);
6684 const lsi: Symbol.LocalIndex = if (opts.flags.ALLOC) elf.addLocalSymbolAssumeCapacity(.{
6685 .node = .wrap(ni),
6686 .name = .empty,
6687 .value = addr,
6688 .size = 0,
6689 .type = .SECTION,
6690 .shndx = shndx,
6691 }) else .null;
6692 elf.shdrs.appendAssumeCapacity(.{ .lsi = lsi, .ni = ni, .rela = switch (opts.type) {
6693 .REL => unreachable,
6694 .RELA => .{ .free_head = .none },
6695 else => .{ .shndx = .UNDEF },
6696 } });
6697 elf.nodes.appendAssumeCapacity(.{ .section = shndx });
6698 switch (elf.shdrPtr(shndx)) {
6699 inline else => |shdr, class| {
6700 shdr.* = .{
6701 .name = @backingInt(shstrtab_entry),
6702 .type = opts.type,
6703 .flags = .{ .shf = opts.flags },
6704 .addr = @intCast(addr),
6705 .offset = @intCast(elf.getNodeElfOffset(ni)),
6706 .size = @intCast(opts.size),
6707 .link = opts.link,
6708 .info = opts.info,
6709 .addralign = @intCast(opts.addralign.toByteUnits()),
6710 .entsize = opts.entsize,
6711 };
6712 if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(class.ElfN().Shdr, shdr);
6713 },
6714 }
6715 return shndx;
6716}
6717
6718fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize) Error!void {
6719 if (len == 0) return;
6720 const gpa = elf.base.comp.gpa;
6721 try elf.symbol_relocs.ensureUnusedCapacity(gpa, len);
6722 try elf.got_relocs.ensureUnusedCapacity(gpa, len);
6723 const class = elf.identClass();
6724 switch (elf.ehdrType()) {
6725 .REL => {
6726 const shndx = elf.getNodeShndx(node);
6727 if (shndx.get(elf).rela.shndx == .UNDEF) {
6728 var bfa_buf: [32]u8 = undefined;
6729 var bfa: std.heap.BufferFirstAllocator = .init(&bfa_buf, gpa);
6730 const allocator = bfa.allocator();
6731
6732 const rela_name = try std.fmt.allocPrint(allocator, ".rela{s}", .{shndx.name(elf).slice(elf)});
6733 defer allocator.free(rela_name);
6734
6735 assert(elf.section_by_name.count() == elf.shdrs.items.len);
6736 try elf.section_by_name.ensureUnusedCapacity(gpa, 1);
6737 const rela_shndx = try elf.addSection(elf.ni.elf, .{
6738 .name = rela_name,
6739 .type = .RELA,
6740 .link = @backingInt(Section.Index.symtab),
6741 .info = shndx.toSection().?,
6742 .addralign = switch (class) {
6743 .NONE, _ => unreachable,
6744 .@"32" => .@"4",
6745 .@"64" => .@"8",
6746 },
6747 .entsize = switch (class) {
6748 .NONE, _ => unreachable,
6749 inline else => |ct_class| @sizeOf(ct_class.ElfN().Rela),
6750 },
6751 .node_align = elf.mf.flags.block_size,
6752 });
6753 elf.section_by_name.putAssumeCapacityNoClobber(rela_shndx.name(elf), {});
6754 shndx.get(elf).rela.shndx = rela_shndx;
6755 }
6756 try shndx.get(elf).rela.shndx.relaEnsureAdditionalCapacity(elf, len);
6757 },
6758 .EXEC, .DYN => {
6759 try elf.tls_size_symbol_relocs.ensureUnusedCapacity(gpa, len);
6760 const new_got_entries = len * 2; // at worst, every reloc is a new TLSGD
6761 try elf.got.ensureUnusedCapacity(gpa, new_got_entries);
6762 const need_got_size = switch (class) {
6763 .NONE, _ => unreachable,
6764 inline else => |ct_class| (elf.got.count() + new_got_entries) * @sizeOf(ct_class.ElfN().Addr),
6765 };
6766 try elf.shndx.got.get(elf).ni.ensureMinimumSize(&elf.mf, gpa, need_got_size);
6767
6768 if (elf.shndx.dynamic != .UNDEF) {
6769 try elf.shndx.rela_dyn.relaEnsureAdditionalCapacity(elf, new_got_entries);
6770 }
6771 },
6772 }
6773}
6774/// Although this function requires a preceding call to `ensureUnusedRelocCapacity`, it is still
6775/// fallible, because there are some rare cases for which we cannot reserve capacity upfront.
6776fn addRelocAssumeCapacity(
6777 elf: *Elf,
6778 node: MappedFile.Node.Index,
6779 offset: u64,
6780 target: Symbol.Id,
6781 addend: i64,
6782 @"type": MachineRelocType,
6783) (Error || error{ UnknownRelocation, NonStaticRelocation, UnimplementedRelocation })!void {
6784 switch (elf.ehdrType()) {
6785 .REL => {
6786 const rela_shndx = elf.getNodeShndx(node).get(elf).rela.shndx;
6787 const rela_index = rela_shndx.relaAddOneAssumeCapacity(elf, .{
6788 .type = @"type",
6789 // This field needs to equal the offset into the section, which is *not* necessarily
6790 // the same thing as our `offset`, which is the offset into `node`. We could compute
6791 // the section offset now, but there's no point, because `flushMovedNodeRelocs` will
6792 // eventually do it for us anyway, so just init to 0.
6793 .offset = 0,
6794 .raw_sym_index = @backingInt(target.index(elf)),
6795 .addend = addend,
6796 });
6797 const ri: SymbolReloc.Index = @fromBackingInt(@intCast(elf.symbol_relocs.items.len));
6798 const next: SymbolReloc.Index = next: {
6799 const target_ptr = target.index(elf).ptr(elf);
6800 const next = target_ptr.first_target_reloc;
6801 target_ptr.first_target_reloc = ri;
6802 break :next next;
6803 };
6804 if (next != .none) {
6805 next.get(elf).prev = ri;
6806 }
6807 elf.symbol_relocs.appendAssumeCapacity(.{
6808 .node = node,
6809 .offset = offset,
6810 .type = undefined,
6811 .target = target,
6812 .addend = addend,
6813 .next = next,
6814 .prev = .none,
6815 .rela_index = rela_index.toOptional(),
6816 .result = .ok,
6817 });
6818 },
6819
6820 .DYN, .EXEC => switch (elf.ehdrMachine()) {
6821 .AARCH64 => switch (@"type".AARCH64) {
6822 .NONE => {},
6823 _ => return error.UnknownRelocation,
6824 else => return error.UnimplementedRelocation,
6825 },
6826 .LOONGARCH => rel_type: switch (@"type".LARCH) {
6827 .NONE => {},
6828 _ => return error.UnknownRelocation,
6829
6830 .COPY,
6831 .JUMP_SLOT,
6832 .RELATIVE,
6833 .IRELATIVE,
6834 => return error.NonStaticRelocation,
6835
6836 else => return error.UnimplementedRelocation,
6837
6838 // These relocations signal that certain relaxations are legal, but this linker does
6839 // not yet implement relaxation, so these are ignored.
6840 .RELAX, .TLS_LE_ADD_R => {},
6841
6842 // Relaxable versions of other relocations. Since we don't yet implement relaxation,
6843 // just use the handling for the non-relaxable versions.
6844 .TLS_LE_LO12_R => continue :rel_type .TLS_LE_LO12,
6845 .TLS_LE_HI20_R => continue :rel_type .TLS_LE_HI20,
6846
6847 // zig fmt: off
6848 .@"32" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })),
6849 .@"64" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })),
6850 .@"32_PCREL" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
6851 .@"64_PCREL" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" })),
6852 .ABS_LO12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .trunc, .shift = .@"0" })),
6853 .ABS_HI20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"12" })),
6854 .ABS64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"32" })),
6855 .ABS64_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .unsigned, .shift = .@"52" })),
6856 .PCALA_LO12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .trunc, .shift = .@"0" })),
6857 .PCALA_HI20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_pcala_hi20)),
6858 .PCALA64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_pcala64_lo20)),
6859 .PCALA64_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_pcala64_hi12)),
6860
6861 .B16 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.pltrel, .{ .dest = .@"32[25:10]", .cast = .signed, .shift = .@"2_exact" })),
6862 .B21 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_b21)),
6863 .B26 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_b26)),
6864 .CALL36 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_call36)),
6865
6866 .TLS_LE_LO12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[21:10]", .cast = .trunc, .shift = .@"0" })),
6867 .TLS_LE_HI20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"12" })),
6868 .TLS_LE64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"32" })),
6869 .TLS_LE64_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[21:10]", .cast = .unsigned, .shift = .@"52" })),
6870
6871 .GOT_PC_LO12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .trunc, .shift = .@"0" })),
6872 .GOT_PC_HI20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .special(.larch_pcala_hi20)),
6873 .GOT64_PC_LO20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .special(.larch_pcala64_lo20)),
6874 .GOT64_PC_HI12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .special(.larch_pcala64_hi12)),
6875 .GOT_LO12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .trunc, .shift = .@"0" })),
6876 .GOT_HI20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.abs, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"12" })),
6877 .GOT64_LO20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.abs, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"32" })),
6878 .GOT64_HI12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .unsigned, .shift = .@"52" })),
6879 // zig fmt: on
6880 },
6881 .PPC64 => switch (@"type".PPC64) {
6882 .NONE => {},
6883 _ => return error.UnknownRelocation,
6884 else => return error.UnimplementedRelocation,
6885 },
6886 .RISCV => switch (@"type".RISCV) {
6887 .NONE => {},
6888 _ => return error.UnknownRelocation,
6889 else => return error.UnimplementedRelocation,
6890 },
6891 .SPARCV9 => switch (@"type".SPARC) {
6892 .NONE => {},
6893 _ => return error.UnknownRelocation,
6894
6895 .COPY,
6896 .GLOB_DAT,
6897 .JMP_SLOT,
6898 .RELATIVE,
6899 .IRELATIVE,
6900 => return error.NonStaticRelocation,
6901
6902 .WDISP22,
6903 .HI22,
6904 .LO10,
6905 .HIPLT22,
6906 .LOPLT10,
6907 .PCPLT22,
6908 .PCPLT10,
6909 .OLO10,
6910 .HH22,
6911 .HM10,
6912 .LM22,
6913 .PC_HH22,
6914 .PC_HM10,
6915 .PC_LM22,
6916 .WDISP16,
6917 .WDISP19,
6918 .HIX22,
6919 .LOX10,
6920 .REGISTER,
6921 .TLS_IE_HI22,
6922 .TLS_IE_LO10,
6923 .TLS_DTPMOD32,
6924 .TLS_DTPMOD64,
6925 .H34,
6926 .WDISP10,
6927 => return error.UnimplementedRelocation,
6928
6929 // These need similar handling to `R_X86_64_GOTOFF64`. No compiler seems to emit them though.
6930 .GOTDATA_HIX22 => return error.UnimplementedRelocation,
6931 .GOTDATA_LOX10 => return error.UnimplementedRelocation,
6932
6933 // These relocations signal that certain relaxations are legal, but this linker does
6934 // not yet implement relaxation, so these are ignored.
6935 .GOTDATA_OP,
6936 .TLS_GD_ADD,
6937 .TLS_LDM_ADD,
6938 .TLS_LDO_ADD,
6939 .TLS_IE_LD,
6940 .TLS_IE_LDX,
6941 .TLS_IE_ADD,
6942 => {},
6943
6944 // zig fmt: off
6945 .@"8" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"8", .cast = .unsigned, .shift = .@"0" })),
6946 .@"16", .UA16 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"16", .cast = .unsigned, .shift = .@"0" })),
6947 .@"32", .UA32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })),
6948 .@"64", .UA64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })),
6949
6950 .@"5" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[4:0]", .cast = .unsigned, .shift = .@"0" })),
6951 .@"6" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[5:0]", .cast = .unsigned, .shift = .@"0" })),
6952 .@"7" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[6:0]", .cast = .unsigned, .shift = .@"0" })),
6953 .@"10" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[9:0]", .cast = .unsigned, .shift = .@"0" })),
6954 .@"11" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[10:0]", .cast = .unsigned, .shift = .@"0" })),
6955 .@"13" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[12:0]", .cast = .unsigned, .shift = .@"0" })),
6956 .@"22" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[21:0]", .cast = .unsigned, .shift = .@"0" })),
6957
6958 .DISP8 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"8", .cast = .signed, .shift = .@"0" })),
6959 .DISP16 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"16", .cast = .signed, .shift = .@"0" })),
6960 .DISP32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
6961 .DISP64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" })),
6962
6963 .SIZE32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.size, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })),
6964 .SIZE64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.size, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })),
6965
6966 .PCPLT32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.pltrel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
6967 .PLT32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.pltabs, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })),
6968 .PLT64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.pltabs, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })),
6969
6970 .WDISP30 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32[29:0]", .cast = .signed, .shift = .@"2_exact" })),
6971 .WPLT30 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.pltrel, .{ .dest = .@"32[29:0]", .cast = .signed, .shift = .@"2_exact" })),
6972 .PC22 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32[21:0]", .cast = .unsigned, .shift = .@"10" })),
6973 .H44 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[21:0]", .cast = .unsigned, .shift = .@"22" })),
6974 .M44 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"12" })),
6975
6976 .TLS_LDO_HIX22 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"32[21:0]", .cast = .trunc, .shift = .@"10" })),
6977 .TLS_LE_HIX22 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.sparc_le_hix22)),
6978 .TLS_DTPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })),
6979 .TLS_DTPOFF64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })),
6980 .TLS_TPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
6981 .TLS_TPOFF64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" })),
6982
6983 .GOT13 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.offset, .{ .dest = .@"32[12:0]", .cast = .unsigned, .shift = .@"0" })),
6984 .GOT22 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.offset, .{ .dest = .@"32[21:0]", .cast = .trunc, .shift = .@"10" })),
6985 .GOTDATA_OP_LOX10 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .special(.sparc_op_lox10)),
6986 .GOTDATA_OP_HIX22 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .special(.sparc_op_hix22)),
6987 .TLS_GD_HI22 => elf.addGotRelocAssumeCapacity(node, offset, .{ .tlsgd0 = target }, addend, .simple(.offset, .{ .dest = .@"32[21:0]", .cast = .trunc, .shift = .@"10" })),
6988 .TLS_LDM_HI22 => elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .simple(.offset, .{ .dest = .@"32[21:0]", .cast = .trunc, .shift = .@"10" })),
6989 // zig fmt: on
6990
6991 .TLS_GD_CALL, .TLS_LDM_CALL => {
6992 const callee_sym = try elf.externSymbolInner(.{
6993 .lib_name = null,
6994 .name = "__tls_get_addr",
6995 .type = .FUNC,
6996 });
6997 try elf.addSymbolRelocAssumeCapacity(node, offset, callee_sym, addend, .simple(.pltrel, .{ .dest = .@"32[29:0]", .cast = .signed, .shift = .@"2_exact" }));
6998 },
6999
7000 // The following relocations are all represented by the ABI as writing to a 13 bit
7001 // field (32[12:0]), but masking out some bits of the value. To simplify our logic
7002 // for applying relocations, we split this action up: we create a relocation writing
7003 // to the 10--12 bit long field which is actually variable, and queue a one-shot
7004 // task to set the constant bits. We can't just write the bits now unfortunately
7005 // because they may be in an input section which has not yet been loaded.
7006 .PC10 => {
7007 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b000" });
7008 try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
7009 },
7010 .L44 => {
7011 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:12] = 0b0" });
7012 try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[11:0]", .cast = .trunc, .shift = .@"0" }));
7013 },
7014 .TLS_LDO_LOX10 => {
7015 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b000" });
7016 try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
7017 },
7018 .TLS_LE_LOX10 => {
7019 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b111" });
7020 try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
7021 },
7022 .GOT10 => {
7023 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b000" });
7024 elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.offset, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
7025 },
7026 .TLS_GD_LO10 => {
7027 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b000" });
7028 elf.addGotRelocAssumeCapacity(node, offset, .{ .tlsgd0 = target }, addend, .simple(.offset, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
7029 },
7030 .TLS_LDM_LO10 => {
7031 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b000" });
7032 elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .simple(.offset, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
7033 },
7034 },
7035 .X86_64 => rel_type: switch (@"type".X86_64) {
7036 .NONE => {},
7037 _ => return error.UnknownRelocation,
7038
7039 .COPY,
7040 .GLOB_DAT,
7041 .JUMP_SLOT,
7042 .RELATIVE64,
7043 .RELATIVE,
7044 .IRELATIVE,
7045 .DTPMOD64,
7046 => return error.NonStaticRelocation,
7047
7048 // TODO: the psABI links to https://www.fsfla.org/~lxoliva/writeups/TLS/RFC-TLSDESC-x86.txt
7049 .GOTPC32_TLSDESC => return error.UnimplementedRelocation,
7050 .TLSDESC_CALL => return error.UnimplementedRelocation,
7051 .TLSDESC => return error.UnimplementedRelocation,
7052
7053 // TODO: these are the address of an arbitrary symbol (or PLT entry) relative to the
7054 // base of the GOT, which is quite annoying. Luckily, they seem to be rare, so I'm
7055 // probably just going to introduce a set (ArrayHashMap) of SymbolReloc.Index which
7056 // need to be re-applied whenever the GOT moves.
7057 .GOTOFF64 => return error.UnimplementedRelocation, // offset of symbol from GOT base
7058 .PLTOFF64 => return error.UnimplementedRelocation, // offset of PLT entry from GOT base (yes, I know, the name is stupid)
7059
7060 // TODO: figure out how to do relaxations. Perhaps we want to remove a `GotReloc`
7061 // and replace it with a `SymbolReloc` when a relaxation becomes possible, but we'd
7062 // need to bear in mind whether incremental updates might make a relaxation
7063 // impossible again or something like that. Relaxations seem kind of hostile to
7064 // incremental compilation, so perhaps we just only support them in non-incremental
7065 // compilations and just apply them in flush or something.
7066
7067 // Relaxable versions of other relocations. Since we don't yet implement relaxation,
7068 // just use the handling for the non-relaxable versions.
7069 .GOTPCRELX, .REX_GOTPCRELX => continue :rel_type .GOTPCREL,
7070
7071 // This relocation was a historical attempt to help linkers optimize uses of symbols
7072 // which have both GOT entries and PLT entries, by encouraging the linker to create
7073 // a `.got.plt` entry instead of a `.got` entry. This makes no sense, because the
7074 // linker already has sufficient knowledge to do that optimization, while compilers
7075 // actually do *not* have sufficient knowledge (since the PLT and GOT relocations
7076 // may not be in the same compilation unit). This relocation has since been removed
7077 // from the psABI, but just in case it appears, we can easily support it by just
7078 // disregarding the PLT stuff and lowering to a normal GOT entry.
7079 //
7080 // More details: https://sourceware.org/pipermail/binutils/2014-November/086548.html
7081 .GOTPLT64 => continue :rel_type .GOT64,
7082
7083 // zig fmt: off
7084 .@"8" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"8", .cast = .unsigned, .shift = .@"0" })),
7085 .@"16" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"16", .cast = .unsigned, .shift = .@"0" })),
7086 .@"32" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })),
7087 .@"32S" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
7088 .@"64" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })),
7089 .PC8 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"8", .cast = .signed, .shift = .@"0" })),
7090 .PC16 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"16", .cast = .signed, .shift = .@"0" })),
7091 .PC32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
7092 .PC64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" })),
7093 .PLT32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.pltrel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
7094 .SIZE32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.size, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })),
7095 .SIZE64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.size, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })),
7096 .DTPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })),
7097 .DTPOFF64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })),
7098 .TPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
7099 .TPOFF64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" })),
7100
7101 .GOT32 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.offset, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })),
7102 .GOT64 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.offset, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })),
7103 .GOTPCREL => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
7104 .GOTPCREL64 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.rel, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" })),
7105 .TLSGD => elf.addGotRelocAssumeCapacity(node, offset, .{ .tlsgd0 = target }, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
7106 .TLSLD => elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
7107 .GOTTPOFF => elf.addGotRelocAssumeCapacity(node, offset, .{ .tpoff = target }, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
7108 // zig fmt: on
7109
7110 .GOTPC64 => {
7111 const got_sym: Symbol.Id = .local(elf.shndx.got.get(elf).lsi);
7112 try elf.addSymbolRelocAssumeCapacity(node, offset, got_sym, addend, .simple(.rel, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" }));
7113 },
7114 .GOTPC32 => {
7115 const got_sym: Symbol.Id = .local(elf.shndx.got.get(elf).lsi);
7116 try elf.addSymbolRelocAssumeCapacity(node, offset, got_sym, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" }));
7117 },
7118 },
7119 },
7120 }
7121}
7122fn addSymbolRelocAssumeCapacity(
7123 elf: *Elf,
7124 node: MappedFile.Node.Index,
7125 offset: u64,
7126 target: Symbol.Id,
7127 addend: i64,
7128 @"type": SymbolReloc.Type,
7129) Error!void {
7130 assert(elf.ehdrType() != .REL);
7131
7132 const rela_index: Section.RelaIndex.Optional = r: {
7133 if (elf.shndx.dynamic == .UNDEF) break :r .none;
7134
7135 // If we emit a runtime relocation entry, its `offset` is a virtual address, so we need to
7136 // determine the vaddr of `node`.
7137 const node_vaddr = elf.getNodeVAddr(node);
7138
7139 // If this is `true`, we will try to create a copy relocation for the target symbol if it is
7140 // not locally defined. If the relocation value is always computed from the target symbol's
7141 // value (even for an external target symbol), and if the target symbol might be of type
7142 // STT_OBJECT, this should probably be `true`.
7143 const try_copy_reloc: bool = switch (@"type".target) {
7144 .rel, .abs => true,
7145
7146 .pltrel,
7147 .pltabs,
7148 .dtpoff,
7149 .tpoff,
7150 .size,
7151 => false,
7152
7153 .special => switch (@"type".action.special) {
7154 .larch_pcala_hi20,
7155 .larch_pcala64_lo20,
7156 .larch_pcala64_hi12,
7157 => true,
7158
7159 .larch_b21,
7160 .larch_b26,
7161 .larch_call36,
7162 .sparc_le_hix22,
7163 => false,
7164 },
7165 };
7166
7167 classify: switch (elf.classifySymbolValue(target)) {
7168 .static => break :r .none,
7169 .static_relative => {
7170 switch (@"type".target) {
7171 // Only relocations which resolve to absolute addresses require runtime
7172 // `R_*_RELATIVE` relocations.
7173 .special,
7174 .pltrel,
7175 .rel,
7176 .dtpoff,
7177 .tpoff,
7178 .size,
7179 => break :r .none,
7180
7181 .abs, .pltabs => {},
7182 }
7183 if (!@"type".action.simple.dest.isAddr(elf)) break :r .none;
7184 switch (elf.nodeWantsDsoRelocation(node)) {
7185 .no => break :r .none,
7186 .yes => {},
7187 .yes_textrel => elf.textrel_count += 1,
7188 }
7189 break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
7190 .type = .relative(elf),
7191 .offset = node_vaddr + offset,
7192 .raw_sym_index = 0,
7193 .addend = 0,
7194 }).toOptional();
7195 },
7196 .dynamic => if (try_copy_reloc and try elf.maybeAddCopyRelocation(target.unwrap().global)) {
7197 switch (elf.classifySymbolValue(target)) {
7198 .static => continue :classify .static,
7199 .static_relative => continue :classify .static_relative,
7200 .dynamic => unreachable, // we just added a copy relocation
7201 }
7202 } else {
7203 const dynamic_reloc_type: MachineRelocType = switch (@"type".target) {
7204 // PLT relocations targeting dynamic symbols actually target that symbol's PLT
7205 // entry, so we should emit an `R_*_RELATIVE` relocation instead.
7206 .pltabs => continue :classify .static_relative,
7207 // ...although PC-relative PLT relocations don't even need that!
7208 .pltrel => break :r .none,
7209 // Weird sizes or computations are not supported as runtime relocations.
7210 .special => break :r .none,
7211 // Relative addresses are not supported as runtime relocations.
7212 .rel => break :r .none,
7213
7214 // On the few targets supporting size relocations, they are valid at runtime.
7215 .size => switch (@"type".action.simple.dest) {
7216 .@"32" => MachineRelocType.size32(elf) orelse break :r .none,
7217 .@"64" => MachineRelocType.size64(elf) orelse break :r .none,
7218 else => break :r .none,
7219 },
7220 // Absolute addresses and TLS offsets can be lowered at runtime provided they
7221 // are address-sized.
7222 .dtpoff => if (@"type".action.simple.dest.isAddr(elf)) .dtpOff(elf) else break :r .none,
7223 .tpoff => if (@"type".action.simple.dest.isAddr(elf)) .tpOff(elf) else break :r .none,
7224 .abs => if (@"type".action.simple.dest.isAddr(elf)) .absAddr(elf) else break :r .none,
7225 };
7226 switch (elf.nodeWantsDsoRelocation(node)) {
7227 .no => break :r .none,
7228 .yes => {},
7229 .yes_textrel => elf.textrel_count += 1,
7230 }
7231 break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
7232 .type = dynamic_reloc_type,
7233 .offset = node_vaddr + offset,
7234 .raw_sym_index = elf.globalByName(target.unwrap().global).?.dynsym_index,
7235 .addend = addend,
7236 }).toOptional();
7237 },
7238 }
7239 };
7240
7241 const ri: SymbolReloc.Index = @fromBackingInt(@intCast(elf.symbol_relocs.items.len));
7242 const target_ptr = target.index(elf).ptr(elf);
7243 const next = target_ptr.first_target_reloc;
7244 target_ptr.first_target_reloc = ri;
7245 if (next != .none) {
7246 next.get(elf).prev = ri;
7247 }
7248 elf.symbol_relocs.appendAssumeCapacity(.{
7249 .node = node,
7250 .offset = offset,
7251 .target = target,
7252 .addend = addend,
7253 .type = @"type",
7254 .next = next,
7255 .prev = .none,
7256 .rela_index = rela_index,
7257 .result = .ok,
7258 });
7259 if (@"type".dependsOnTlsSize(elf)) {
7260 elf.tls_size_symbol_relocs.putAssumeCapacityNoClobber(ri, {});
7261 }
7262
7263 // Actually apply the new relocation!
7264 ri.get(elf).apply(elf);
7265}
7266fn addGotRelocAssumeCapacity(
7267 elf: *Elf,
7268 node: MappedFile.Node.Index,
7269 offset: u64,
7270 target: GotKey,
7271 addend: i64,
7272 @"type": GotReloc.Type,
7273) void {
7274 assert(elf.ehdrType() != .REL);
7275 switch (elf.getNode(node)) {
7276 .archive,
7277 .archive_header,
7278 .archive_input_member,
7279 .archive_elf_member_header,
7280 .elf,
7281 .ehdr,
7282 .shdr,
7283 .segment,
7284 .copied_global,
7285 => unreachable, // cannot contain relocs,
7286 .section,
7287 .uav,
7288 => unreachable, // cannot contain GOT relocs
7289 .input_section,
7290 .nav,
7291 .lazy_code,
7292 .lazy_const_data,
7293 => {},
7294 }
7295
7296 const gop = elf.got.getOrPutAssumeCapacity(target);
7297 if (!gop.found_existing) {
7298 gop.value_ptr.* = .none;
7299 const maybe_next_key: ?GotKey = switch (target) {
7300 .reserved => null,
7301 .tpoff => null,
7302 .symbol => null,
7303 .tlsld0 => .tlsld1,
7304 .tlsgd0 => |sym| .{ .tlsgd1 = sym },
7305 .tlsld1 => unreachable,
7306 .tlsgd1 => unreachable,
7307 };
7308 switch (elf.shdrPtr(elf.shndx.got)) {
7309 inline else => |got_shdr, class| {
7310 const Addr = class.ElfN().Addr;
7311 const old_size = elf.targetLoad(&got_shdr.size);
7312 const new_entry_count = @as(u32, 1) + @intFromBool(maybe_next_key != null);
7313 elf.targetStore(&got_shdr.size, @intCast(old_size + @sizeOf(Addr) * new_entry_count));
7314 },
7315 }
7316 if (maybe_next_key) |next_key| {
7317 elf.got.putAssumeCapacityNoClobber(next_key, .none);
7318 elf.updateGotEntry(gop.index);
7319 elf.updateGotEntry(gop.index + 1);
7320 } else {
7321 elf.updateGotEntry(gop.index);
7322 }
7323 }
7324
7325 elf.got_relocs.appendAssumeCapacity(.{
7326 .node = .wrap(node),
7327 .offset = offset,
7328 .target = target,
7329 .addend = addend,
7330 .type = @"type",
7331 .result = .ok,
7332 });
7333}
7334fn updateGotEntry(elf: *Elf, got_index: usize) void {
7335 assert(elf.ehdrType() != .REL);
7336 const entry_value: union(enum) {
7337 unsigned: u64,
7338 signed: i64,
7339 reloc: struct {
7340 type: MachineRelocType,
7341 dynsym_index: u32,
7342 addend: i64,
7343 },
7344 } = switch (elf.got.keys()[got_index]) {
7345 .reserved => .{ .unsigned = 0 },
7346 .tpoff => |sym_id| val: {
7347 // Only the executable's per-module TLS block is at a known offset from the TLS pointer.
7348 if (elf.base.comp.config.output_mode == .Exe and elf.classifySymbolValue(sym_id) != .dynamic) {
7349 const tls_phndx = elf.getNode(elf.ni.tls.unwrap().?).segment;
7350 const tls_size: u64 = switch (elf.phdrSlice()) {
7351 inline else => |phdr| tls_size: {
7352 assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS);
7353 break :tls_size elf.targetLoad(&phdr[tls_phndx].memsz);
7354 },
7355 };
7356 const sym_value = sym_id.value(elf);
7357 break :val .{ .signed = @bitCast(sym_value -% tls_size) };
7358 }
7359 break :val switch (sym_id.unwrap()) {
7360 // For global symbols, just target the right dynsym with no addend.
7361 .global => |name| .{ .reloc = .{
7362 .type = .tpOff(elf),
7363 .dynsym_index = elf.globalByName(name).?.dynsym_index,
7364 .addend = 0,
7365 } },
7366 // For local symbols, target the null symbol (index 0) so we get the offset to the
7367 // base of our TLS block, and then use `addend` to offset to the right symbol.
7368 .local => .{ .reloc = .{
7369 .type = .tpOff(elf),
7370 .dynsym_index = 0,
7371 .addend = @intCast(sym_id.value(elf)),
7372 } },
7373 };
7374 },
7375 .symbol => |sym| switch (elf.classifySymbolValue(sym)) {
7376 .static => .{ .unsigned = sym.value(elf) },
7377 .static_relative => .{ .reloc = .{
7378 .type = .relative(elf),
7379 .dynsym_index = 0,
7380 .addend = @bitCast(sym.value(elf)),
7381 } },
7382 .dynamic => .{ .reloc = .{
7383 .type = .globDat(elf),
7384 .dynsym_index = elf.globalByName(sym.unwrap().global).?.dynsym_index,
7385 .addend = 0,
7386 } },
7387 },
7388 .tlsgd1 => |sym| switch (elf.classifySymbolValue(sym)) {
7389 .static => .{ .unsigned = sym.value(elf) },
7390 .static_relative => unreachable, // TLS variables should be in TLS sections, which do not return `.static_relative`
7391 .dynamic => .{ .reloc = .{
7392 .type = .dtpOff(elf),
7393 .dynsym_index = elf.globalByName(sym.unwrap().global).?.dynsym_index,
7394 .addend = 0,
7395 } },
7396 },
7397 .tlsgd0 => |sym| switch (elf.base.comp.config.link_mode) {
7398 .static => val: {
7399 assert(elf.base.comp.config.output_mode == .Exe); // static libraries don't have GOTs
7400 break :val .{ .unsigned = 1 }; // TLS module ID for executable
7401 },
7402 .dynamic => .{ .reloc = .{
7403 .type = .dtpMod(elf),
7404 .dynsym_index = switch (elf.classifySymbolValue(sym)) {
7405 .static, .static_relative => 0,
7406 .dynamic => elf.globalByName(sym.unwrap().global).?.dynsym_index,
7407 },
7408 .addend = 0,
7409 } },
7410 },
7411 .tlsld0 => switch (elf.base.comp.config.link_mode) {
7412 .static => val: {
7413 assert(elf.base.comp.config.output_mode == .Exe); // static libraries don't have GOTs
7414 break :val .{ .unsigned = 1 }; // TLS module ID for executable
7415 },
7416 .dynamic => .{ .reloc = .{
7417 .type = .dtpMod(elf),
7418 .dynsym_index = 0,
7419 .addend = 0,
7420 } },
7421 },
7422 .tlsld1 => .{ .unsigned = 0 },
7423 };
7424
7425 // First, write to the GOT itself. If we're planning to use a relocation, we'll just write zeroes.
7426 const got_entry_addr: u64 = switch (elf.shdrPtr(elf.shndx.got)) {
7427 inline else => |got_shdr, class| got_entry_addr: {
7428 const addr_size = @sizeOf(class.ElfN().Addr);
7429 const offset = got_index * addr_size;
7430 const entry_ptr: *class.ElfN().Addr = @ptrCast(@alignCast(
7431 elf.shndx.got.get(elf).ni.slice(&elf.mf)[offset..][0..addr_size],
7432 ));
7433 elf.targetStore(entry_ptr, switch (entry_value) {
7434 .unsigned => |x| @intCast(x),
7435 .signed => |x| switch (class) {
7436 .NONE, _ => comptime unreachable,
7437 .@"32" => @bitCast(@as(i32, @intCast(x))),
7438 .@"64" => @bitCast(x),
7439 },
7440 .reloc => 0,
7441 });
7442 break :got_entry_addr elf.targetLoad(&got_shdr.addr) + offset;
7443 },
7444 };
7445
7446 // Then, add or remove the relocation entry if needed.
7447 if (elf.shndx.dynamic == .UNDEF) {
7448 // There are no relocations in the output file, so there's no reloc to delete and we can't
7449 // add a reloc in any case. (If we *are* requesting a reloc, it'll be because the value of
7450 // this GOT entry is not yet known, e.g. because a symbol is currently undefined.)
7451 return;
7452 }
7453 if (elf.got.values()[got_index].unwrap()) |rela_index| {
7454 // Clear the old relocation entry (although we might immediately re-use it below).
7455 elf.shndx.rela_dyn.relaDeleteOne(elf, rela_index);
7456 }
7457 elf.got.values()[got_index] = switch (entry_value) {
7458 .unsigned, .signed => .none, // no relocation needed
7459 .reloc => |reloc| elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
7460 .type = reloc.type,
7461 .offset = got_entry_addr,
7462 .raw_sym_index = reloc.dynsym_index,
7463 .addend = reloc.addend,
7464 }).toOptional(),
7465 };
7466}
7467
7468/// If `node` cannot contain runtime relocations, returns `.no`.
7469///
7470/// If `node` can contain runtime relocations, `returns `.yes_textrel` if such a relocation requires
7471/// the presence of a `DT_TEXTREL` dynamic entry, or `.yes` otherwise.
7472fn nodeWantsDsoRelocation(elf: *Elf, node: MappedFile.Node.Index) enum { yes, yes_textrel, no } {
7473 const shndx = elf.getNodeShndx(node);
7474 const shf: std.elf.SHF = switch (elf.shdrPtr(shndx)) {
7475 inline else => |shdr| elf.targetLoad(&shdr.flags).shf,
7476 };
7477 if (!shf.ALLOC) return .no;
7478 if (!shf.WRITE) return .yes_textrel;
7479 return .yes;
7480}
7481
7482/// If the given undefined global could have a copy relocation, creates that relocation if it does
7483/// not already exist, and returns `true`.
7484///
7485/// Returns `false` iff a copy relocation cannot currently be created for the global. If it may be
7486/// possible in future, the symbol is added to `elf.want_copied_globals` so that the copy relocation
7487/// will be created if and when we discover a suitable definition in an input DSO.
7488///
7489/// If this function creates a new copy relocation, it will also update relocations targeting the
7490/// global where needed---the caller does not need to do this.
7491///
7492/// Asserts that `elf.shndx.dynamic != .UNDEF` and that `global_name` refers to an *undefined* global.
7493fn maybeAddCopyRelocation(elf: *Elf, global_name: String(.strtab)) Error!bool {
7494 assert(elf.shndx.dynamic != .UNDEF);
7495
7496 // Only dynamic executables may contain `R_*_COPY` relocations.
7497 if (elf.base.comp.config.output_mode != .Exe) return false;
7498
7499 const gpa = elf.base.comp.gpa;
7500
7501 const global_ptr = elf.globals.strong_undef.getPtr(global_name) orelse
7502 elf.globals.weak_undef.getPtr(global_name).?;
7503
7504 assert(global_ptr.dynsym_index != 0);
7505
7506 const dso_global = elf.dso_globals.get(global_name) orelse {
7507 // We do not have a definition to provide the correct size for the symbol. If a definition
7508 // is discovered in a later DSO, we may at that point be able to add a copy relocation.
7509 try elf.want_copied_globals.put(gpa, global_name, {});
7510 return false;
7511 };
7512
7513 if (dso_global.type != .OBJECT) return false;
7514
7515 const gop = try elf.copied_globals.getOrPut(gpa, global_name);
7516 if (gop.found_existing) return true;
7517 errdefer assert(elf.copied_globals.pop().?.key == global_name);
7518
7519 try Section.Index.data.ensureAligned(elf, dso_global.alignment);
7520
7521 try elf.nodes.ensureUnusedCapacity(gpa, 1);
7522 const node = try Section.Index.data.get(elf).ni.addFloatingChild(&elf.mf, gpa, .{
7523 .size = dso_global.alignment.forward(dso_global.size),
7524 .alignment = dso_global.alignment,
7525 });
7526 errdefer comptime unreachable;
7527
7528 const vaddr = elf.computeNodeVAddr(node);
7529 elf.nodes.appendAssumeCapacity(.{ .copied_global = global_name });
7530 const rela_index = elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
7531 .type = .copy(elf),
7532 .offset = vaddr,
7533 .raw_sym_index = global_ptr.dynsym_index,
7534 .addend = 0,
7535 });
7536 gop.value_ptr.* = .{
7537 .node = node,
7538 .rela_index = rela_index,
7539 };
7540
7541 switch (elf.symPtr(global_ptr.symtab_index)) {
7542 inline else => |sym| elf.targetStore(&sym.size, @intCast(dso_global.size)),
7543 }
7544 switch (elf.dynsymPtr(global_ptr.dynsym_index)) {
7545 inline else => |dynsym| elf.targetStore(&dynsym.size, @intCast(dso_global.size)),
7546 }
7547
7548 // Because we now have a copy relocation, any dynamic relocations which target this symbol are
7549 // now incorrect, since we now own the canonical address of the symbol. So delete those relocs
7550 // and then update the symbol's address (and re-apply relocations targeting it of course).
7551 Symbol.Id.global(global_name).deleteDynamicTargetRelocs(elf);
7552 Symbol.Id.global(global_name).flushMoved(elf, vaddr);
7553
7554 return true;
7555}
7556
7557pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) link.Error!void {
7558 const diags = &elf.base.comp.link_diags;
7559 elf.updateNavInner(pt, nav_index) catch |err| switch (err) {
7560 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
7561 else => |e| return e,
7562 };
7563}
7564fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) Error!void {
7565 const zcu = pt.zcu;
7566 const gpa = zcu.gpa;
7567 const ip = &zcu.intern_pool;
7568
7569 const nav = ip.getNav(nav_index);
7570 if (ip.indexToKey(nav.resolved.?.value) == .@"extern") return;
7571 if (!Type.fromInterned(nav.resolved.?.type).hasRuntimeBits(zcu)) return;
7572
7573 const nmi = try elf.navMapIndex(zcu, nav_index);
7574 const ni = nmi.symbol(elf).index().ptr(elf).node.unwrap().?;
7575 elf.resetNodeRelocs(ni);
7576
7577 // Ensure the NAV is marked as moved so that once we're done, `flushMoved` will eventually be
7578 // called to apply the NAV's new relocations.
7579 try ni.moved(gpa, &elf.mf);
7580
7581 {
7582 var nw: MappedFile.Node.Writer = undefined;
7583 ni.writer(&elf.mf, gpa, &nw);
7584 defer nw.deinit();
7585 codegen.generateSymbol(
7586 &elf.base,
7587 pt,
7588 .fromInterned(nav.resolved.?.value),
7589 &nw.interface,
7590 .{ .atom_index = Node.toAtom(ni) },
7591 ) catch |err| switch (err) {
7592 error.WriteFailed => return nw.err.?,
7593 else => |e| return e,
7594 };
7595 switch (elf.symPtr(nmi.symbol(elf).index())) {
7596 inline else => |sym| elf.targetStore(&sym.size, @intCast(nw.interface.end)),
7597 }
7598 }
7599
7600 // The NAV's node is done---now generate any UAVs or lazy code/data which the NAV needs.
7601 try elf.genPending(pt);
7602}
7603
7604pub fn updateFunc(
7605 elf: *Elf,
7606 pt: Zcu.PerThread,
7607 func_index: InternPool.Index,
7608 mir: *const codegen.AnyMir,
7609) link.Error!void {
7610 const diags = &elf.base.comp.link_diags;
7611 elf.updateFuncInner(pt, func_index, mir) catch |err| switch (err) {
7612 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
7613 else => |e| return e,
7614 };
7615}
7616fn updateFuncInner(
7617 elf: *Elf,
7618 pt: Zcu.PerThread,
7619 func_index: InternPool.Index,
7620 mir: *const codegen.AnyMir,
7621) Error!void {
7622 const zcu = pt.zcu;
7623 const gpa = zcu.gpa;
7624 const ip = &zcu.intern_pool;
7625 const func = zcu.funcInfo(func_index);
7626 const nav = ip.getNav(func.owner_nav);
7627
7628 const nmi = try elf.navMapIndex(zcu, func.owner_nav);
7629 log.debug("updateFunc({f}) = {d}", .{ nav.fqn.fmt(ip), nmi.symbol(elf) });
7630 const ni = nmi.symbol(elf).index().ptr(elf).node.unwrap().?;
7631 elf.resetNodeRelocs(ni);
7632
7633 // Ensure the NAV is marked as moved so that once we're done, `flushMoved` will eventually be
7634 // called to apply the NAV's new relocations.
7635 try ni.moved(gpa, &elf.mf);
7636
7637 {
7638 var nw: MappedFile.Node.Writer = undefined;
7639 ni.writer(&elf.mf, gpa, &nw);
7640 defer nw.deinit();
7641 codegen.emitFunction(
7642 &elf.base,
7643 pt,
7644 func_index,
7645 Node.toAtom(ni),
7646 mir,
7647 &nw.interface,
7648 .none,
7649 ) catch |err| switch (err) {
7650 error.WriteFailed => return nw.err.?,
7651 else => |e| return e,
7652 };
7653 switch (elf.symPtr(nmi.symbol(elf).index())) {
7654 inline else => |sym| elf.targetStore(&sym.size, @intCast(nw.interface.end)),
7655 }
7656 }
7657
7658 // The NAV's node is done---now generate any UAVs or lazy code/data which the NAV needs.
7659 try elf.genPending(pt);
7660}
7661
7662pub fn updateErrorData(elf: *Elf, pt: Zcu.PerThread) link.Error!void {
7663 const diags = &elf.base.comp.link_diags;
7664 elf.genLazy(pt, .{
7665 .kind = .const_data,
7666 .index = @intCast(elf.lazy.getPtr(.const_data).map.getIndex(.anyerror_type) orelse return),
7667 }) catch |err| switch (err) {
7668 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
7669 else => |e| return e,
7670 };
7671}
7672
7673pub fn flush(
7674 elf: *Elf,
7675 arena: std.mem.Allocator,
7676 tid: Zcu.PerThread.Id,
7677 prog_node: std.Progress.Node,
7678) link.Error!void {
7679 elf.flushInner(arena, tid, prog_node) catch |err| switch (err) {
7680 error.MappedFileIo => return elf.base.comp.link_diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
7681 else => |e| return e,
7682 };
7683}
7684fn flushInner(
7685 elf: *Elf,
7686 arena: std.mem.Allocator,
7687 tid: Zcu.PerThread.Id,
7688 prog_node: std.Progress.Node,
7689) Error!void {
7690 const comp = elf.base.comp;
7691 const diags = &comp.link_diags;
7692 _ = arena;
7693
7694 const sub_prog_node = prog_node.start("ELF Flush", 0);
7695 defer sub_prog_node.end();
7696
7697 if (comp.config.output_mode == .Exe) {
7698 var any_undef = false;
7699 for (elf.globals.strong_undef.keys()) |name| {
7700 if (elf.dso_globals.contains(name)) continue;
7701 any_undef = true;
7702 diags.addError("undefined global symbol '{s}'", .{name.slice(elf)});
7703 }
7704 if (any_undef) return error.AlreadyReported;
7705 }
7706
7707 try elf.prepareDynamic();
7708
7709 while (try elf.idle(tid)) {}
7710
7711 // We've done the final `idle` loop, so everything is at its final place in the file. We have a
7712 // few more things to check and write now that addresses and offsets are finalized.
7713
7714 if (elf.overflowed_reloc_count > 0) {
7715 diags.addError("failed to apply {d} relocations: overflow", .{elf.overflowed_reloc_count});
7716 }
7717 if (elf.misaligned_reloc_count > 0) {
7718 diags.addError("failed to apply {d} relocations: misaligned value", .{elf.misaligned_reloc_count});
7719 }
7720
7721 if (elf.archive) |*archive| {
7722 if (archive.elf_member_too_big) diags.addError(
7723 "file size of {Bi} exceeds maximum size of archive member",
7724 .{elf.ni.elf.location(&elf.mf).resolve(&elf.mf)[1]},
7725 );
7726 if (archive.strtab_member_too_big) diags.addError(
7727 "archive file name string table exceeds maximum size",
7728 .{},
7729 );
7730 }
7731
7732 elf.flushDynamic();
7733
7734 const entry_addr: u64 = entry: {
7735 const sym_name_slice: []const u8 = name: switch (elf.options.entry) {
7736 .default => switch (comp.config.output_mode) {
7737 .Exe => continue :name .enabled,
7738 .Lib, .Obj => continue :name .disabled,
7739 },
7740 .disabled => break :entry 0,
7741 .enabled => "_start",
7742 .named => |named| named,
7743 };
7744 const sym_name_strtab = try elf.string(.strtab, sym_name_slice);
7745 if (elf.globalByName(sym_name_strtab) == null) break :entry 0;
7746 break :entry Symbol.Id.global(sym_name_strtab).value(elf);
7747 };
7748 switch (elf.ehdrPtr()) {
7749 inline else => |ehdr| elf.targetStore(&ehdr.entry, @intCast(entry_addr)),
7750 }
7751
7752 try elf.mf.flush();
7753
7754 if (elf.options.enable_link_snapshots)
7755 elf.dumpStderr(tid) catch |err|
7756 return comp.link_diags.fail("dumping link snapshot failed: {t}", .{err});
7757}
7758
7759pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {
7760 const comp = elf.base.comp;
7761 const diags = &comp.link_diags;
7762
7763 elf.mf.nodes_lock.lock();
7764 defer elf.mf.nodes_lock.unlock();
7765
7766 assert(elf.pending_uavs.items.len == 0);
7767 for (&elf.lazy.values) |*lazy| {
7768 assert(lazy.pending_index == lazy.map.count());
7769 }
7770
7771 task: {
7772 if (elf.input_pending_index < elf.inputs.items.len) {
7773 const ii: Node.InputIndex = @fromBackingInt(elf.input_pending_index);
7774 elf.input_pending_index += 1;
7775 const sub_prog_node = elf.idleProgNode(tid, elf.input_prog_node, elf.getNode(ii.node(elf)));
7776 defer sub_prog_node.end();
7777 elf.flushInput(ii) catch |err| switch (err) {
7778 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
7779 else => |e| return e,
7780 };
7781 break :task;
7782 }
7783 if (elf.input_section_pending_index < elf.input_sections.items.len) {
7784 const isi: InputSection.Index = @fromBackingInt(elf.input_section_pending_index);
7785 elf.input_section_pending_index += 1;
7786 const sub_prog_node = elf.idleProgNode(tid, elf.input_prog_node, elf.getNode(isi.node(elf)));
7787 defer sub_prog_node.end();
7788 elf.flushInputSection(isi) catch |err| switch (err) {
7789 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
7790 else => |e| return e,
7791 };
7792 break :task;
7793 }
7794 if (elf.one_shot_fixups.items.len > 0) {
7795 // Each of these is very simple, so an unreasonable amount of overhead would be
7796 // introduced if we only did one per `idle` call. Also, there is no risk of this work
7797 // being invalidated. So let's just flush the entire queue at once.
7798 for (elf.one_shot_fixups.items) |isw| {
7799 const dest_slice = isw.node.slice(&elf.mf)[@intCast(isw.offset)..][0..4];
7800 const old: u32 = std.mem.readInt(u32, dest_slice, elf.targetEndian());
7801 const new: u32 = switch (isw.action) {
7802 // zig fmt: off
7803 .@"32[12:10] = 0b000" => old & 0b11111111_11111111_11100011_11111111,
7804 .@"32[12:10] = 0b111" => old | 0b00000000_00000000_00011100_00000000,
7805 .@"32[12:12] = 0b0" => old & 0b11111111_11111111_11101111_11111111,
7806 // zig fmt: on
7807 };
7808 std.mem.writeInt(u32, dest_slice, new, elf.targetEndian());
7809 }
7810 elf.one_shot_fixups.clearRetainingCapacity();
7811 break :task;
7812 }
7813 if (elf.changed_symtab_index.pop()) |kv| {
7814 const sub_prog_node = elf.mf.update_prog_node.start(kv.key.slice(elf), 0);
7815 defer sub_prog_node.end();
7816
7817 const global_name = kv.key;
7818 const global = elf.globalByName(global_name).?;
7819 const sym_id: Symbol.Id = .global(global_name);
7820 const sym = global.symtab_index.ptr(elf);
7821
7822 switch (elf.ehdrType()) {
7823 .REL => {
7824 // Index in `.symtab` has changed. Relocatables are easy, we just need to update
7825 // all of the output relocations.
7826 const symtab_index = @backingInt(global.symtab_index);
7827 var ri = sym.first_target_reloc;
7828 while (ri != .none) {
7829 const reloc = ri.get(elf);
7830 assert(reloc.target == sym_id);
7831 // In relocatables, every symbol relocation has an output relocation.
7832 const rela_index = reloc.rela_index.unwrap().?;
7833 reloc.relaSection(elf).relaUpdateSym(elf, rela_index, symtab_index);
7834 ri = reloc.next;
7835 }
7836 },
7837 // For other `ET_*` values, the index in `.dynsym` has changed. There are a few
7838 // places we might have emitted output relocations, depending on whether or not the
7839 // symbol's value is statically known.
7840 .EXEC, .DYN => switch (elf.classifySymbolValue(sym_id)) {
7841 .static, .static_relative => {
7842 // Since the symbol value is statically known, we definitely aren't emitting
7843 // any relocation targeting it (we might have `R_*_RELATIVE` relocs but they
7844 // don't care about the dynsym index). The only exception is a copy reloc
7845 // could exist (and be the *reason* the symbol value is statically known).
7846 if (elf.copied_globals.get(global_name)) |copied| {
7847 elf.shndx.rela_dyn.relaUpdateSym(elf, copied.rela_index, global.dynsym_index);
7848 }
7849 },
7850 .dynamic => {
7851 assert(!elf.copied_globals.contains(global_name)); // value would be statically known
7852
7853 // Update symbol relocs:
7854 var ri = sym.first_target_reloc;
7855 while (ri != .none) {
7856 const reloc = ri.get(elf);
7857 assert(reloc.target == sym_id);
7858 // There may or may not be a runtime relocation for this symbol reloc.
7859 if (reloc.rela_index.unwrap()) |rela_index| {
7860 elf.shndx.rela_dyn.relaUpdateSym(elf, rela_index, global.dynsym_index);
7861 }
7862 ri = reloc.next;
7863 }
7864
7865 // Update the PLT entry's reloc if there is one:
7866 if (elf.plt.getIndex(global_name)) |plt_index| {
7867 // PLT indices exactly match `.rela.plt` relocation indices.
7868 elf.shndx.rela_plt.relaUpdateSym(elf, @fromBackingInt(@intCast(plt_index)), global.dynsym_index);
7869 }
7870
7871 // Update relocs for any relevant GOT entries:
7872 if (elf.got.getIndex(.{ .symbol = sym_id })) |got_index| {
7873 elf.updateGotEntry(got_index);
7874 }
7875 if (elf.got.getIndex(.{ .tpoff = sym_id })) |got_index| {
7876 elf.updateGotEntry(got_index);
7877 }
7878 if (elf.got.getIndex(.{ .tlsgd0 = sym_id })) |got_index| {
7879 elf.updateGotEntry(got_index);
7880 elf.updateGotEntry(got_index + 1); // tlsgd1
7881 }
7882 },
7883 },
7884 }
7885
7886 break :task;
7887 }
7888 while (elf.mf.updates.pop()) |ni| {
7889 const clean_moved = ni.cleanMoved(&elf.mf);
7890 const clean_resized = ni.cleanResized(&elf.mf);
7891 const clean_next_moved = ni.cleanNextMoved(&elf.mf);
7892 if (clean_moved or clean_resized or clean_next_moved) {
7893 const sub_prog_node = elf.idleProgNode(tid, elf.mf.update_prog_node, elf.getNode(ni));
7894 defer sub_prog_node.end();
7895 if (clean_moved) try elf.flushMoved(ni);
7896 if (clean_resized) try elf.flushResized(ni);
7897 if (clean_next_moved) try elf.flushNextMoved(ni);
7898 break :task;
7899 } else elf.mf.update_prog_node.completeOne();
7900 }
7901 }
7902 if (elf.input_sections.items.len > elf.input_section_pending_index) return true;
7903 if (elf.one_shot_fixups.items.len > 0) return true;
7904 if (elf.changed_symtab_index.count() > 0) return true;
7905 if (elf.mf.updates.items.len > 0) return true;
7906 return false;
7907}
7908
7909fn idleProgNode(
7910 elf: *Elf,
7911 tid: Zcu.PerThread.Id,
7912 prog_node: std.Progress.Node,
7913 node: Node,
7914) std.Progress.Node {
7915 var name: [std.Progress.Node.max_name_len]u8 = undefined;
7916 return prog_node.start(name: switch (node) {
7917 else => |tag| @tagName(tag),
7918 .section => |shndx| shndx.name(elf).slice(elf),
7919 .archive_input_member => |ii| std.mem.print(&name, "{f}{f}", .{
7920 ii.path(elf).fmtEscapeString(),
7921 fmtMemberString(ii.member(elf)),
7922 }) catch &name,
7923 .input_section => |isi| {
7924 const ii = isi.input(elf);
7925 break :name std.mem.print(&name, "{f}{f} {s}", .{
7926 ii.path(elf).fmtEscapeString(),
7927 fmtMemberString(ii.member(elf)),
7928 elf.getNode(isi.node(elf).parent(&elf.mf).unwrap().?).section.name(elf).slice(elf),
7929 }) catch &name;
7930 },
7931 .nav => |nmi| {
7932 const ip = &elf.base.comp.zcu.?.intern_pool;
7933 break :name ip.getNav(nmi.navIndex(elf)).fqn.toSlice(ip);
7934 },
7935 .uav => |umi| std.mem.print(&name, "{f}", .{
7936 Value.fromInterned(umi.uavValue(elf)).fmtValue(.{ .zcu = elf.base.comp.zcu.?, .tid = tid }),
7937 }) catch &name,
7938 }, 0);
7939}
7940
7941fn genPending(elf: *Elf, pt: Zcu.PerThread) Error!void {
7942 const zcu = elf.base.comp.zcu.?;
7943 pending: while (true) {
7944 if (elf.pending_uavs.pop()) |umi| {
7945 var prog_name_buf: [std.Progress.Node.max_name_len]u8 = undefined;
7946 const prog_name = std.mem.print(&prog_name_buf, "{f}", .{
7947 Value.fromInterned(umi.uavValue(elf)).fmtValue(pt),
7948 }) catch &prog_name_buf;
7949 const prog_node = elf.const_prog_node.start(prog_name, 0);
7950 defer prog_node.end();
7951 try elf.genUav(pt, umi);
7952 continue :pending;
7953 }
7954 var lazy_it = elf.lazy.iterator();
7955 while (lazy_it.next()) |lazy| if (lazy.value.pending_index < lazy.value.map.count()) {
7956 const lmr: Node.LazyMapRef = .{ .kind = lazy.key, .index = lazy.value.pending_index };
7957 lazy.value.pending_index += 1;
7958 const lazy_ty: Type = .fromInterned(lmr.lazySymbol(elf).ty);
7959 var prog_name_buf: [std.Progress.Node.max_name_len]u8 = undefined;
7960 const prog_name: []const u8 = switch (lazy_ty.zigTypeTag(zcu)) {
7961 .@"enum" => std.mem.print(&prog_name_buf, "@tagName({f})", .{lazy_ty.fmt(pt)}) catch &prog_name_buf,
7962 .error_set => switch (lmr.kind) {
7963 .code => std.mem.print(&prog_name_buf, "@errorCast({f})", .{lazy_ty.fmt(pt)}) catch &prog_name_buf,
7964 .const_data => "@errorName",
7965 },
7966 else => unreachable,
7967 };
7968 const prog_node = elf.synth_prog_node.start(prog_name, 0);
7969 defer prog_node.end();
7970 try elf.genLazy(pt, lmr);
7971 continue :pending;
7972 };
7973 break;
7974 }
7975}
7976
7977fn genUav(
7978 elf: *Elf,
7979 pt: Zcu.PerThread,
7980 umi: Node.UavMapIndex,
7981) Error!void {
7982 const comp = elf.base.comp;
7983 const gpa = comp.gpa;
7984
7985 const uav_val = umi.uavValue(elf);
7986 const ni = umi.symbol(elf).index().ptr(elf).node.unwrap().?;
7987 elf.resetNodeRelocs(ni);
7988
7989 var nw: MappedFile.Node.Writer = undefined;
7990 ni.writer(&elf.mf, gpa, &nw);
7991 defer nw.deinit();
7992 codegen.generateSymbol(
7993 &elf.base,
7994 pt,
7995 .fromInterned(uav_val),
7996 &nw.interface,
7997 .{ .atom_index = Node.toAtom(ni) },
7998 ) catch |err| switch (err) {
7999 error.WriteFailed => return nw.err.?,
8000 else => |e| return e,
8001 };
8002 switch (elf.symPtr(umi.symbol(elf).index())) {
8003 inline else => |sym| elf.targetStore(&sym.size, @intCast(nw.interface.end)),
8004 }
8005 // The UAV should already be considered to have moved, because it is created as moved and
8006 // pending calls to `genUav` always happen before pending calls to `flushMoved`.
8007 assert(ni.hasMoved(&elf.mf));
8008}
8009
8010fn genLazy(elf: *Elf, pt: Zcu.PerThread, lmr: Node.LazyMapRef) Error!void {
8011 const zcu = pt.zcu;
8012 const gpa = zcu.gpa;
8013
8014 const lazy = lmr.lazySymbol(elf);
8015 const ni = lmr.symbol(elf).index().ptr(elf).node.unwrap().?;
8016 elf.resetNodeRelocs(ni);
8017
8018 // Ensure the lazy node is marked as moved so that once we're done, `flushMoved` will eventually
8019 // be called to apply the lazy node's new relocations.
8020 try ni.moved(gpa, &elf.mf);
8021
8022 var required_alignment: InternPool.Alignment = .none;
8023 var nw: MappedFile.Node.Writer = undefined;
8024 ni.writer(&elf.mf, gpa, &nw);
8025 defer nw.deinit();
8026 codegen.generateLazySymbol(
8027 &elf.base,
8028 pt,
8029 lazy,
8030 &required_alignment,
8031 &nw.interface,
8032 .none,
8033 .{ .atom_index = Node.toAtom(ni) },
8034 ) catch |err| switch (err) {
8035 error.WriteFailed => return nw.err.?,
8036 else => |e| return e,
8037 };
8038 switch (elf.symPtr(lmr.symbol(elf).index())) {
8039 inline else => |sym| elf.targetStore(&sym.size, @intCast(nw.interface.end)),
8040 }
8041}
8042
8043fn flushInput(elf: *Elf, ii: Node.InputIndex) Error!void {
8044 const comp = elf.base.comp;
8045 const io = comp.io;
8046 const diags = &comp.link_diags;
8047 const path = ii.path(elf);
8048 const file = path.root_dir.handle.openFile(io, path.sub_path, .{}) catch |err| switch (err) {
8049 error.Canceled => |e| return e,
8050 else => |e| return diags.fail("failed to open input file \"{f}\": {t}", .{ path.fmtEscapeString(), e }),
8051 };
8052 defer file.close(io);
8053
8054 const slice = ii.node(elf).slice(&elf.mf);
8055
8056 const member_ar_hdr: *const std.elf.ar_hdr = @ptrCast(slice[0..@sizeOf(std.elf.ar_hdr)]);
8057 const input_size: u32 = member_ar_hdr.size() catch |err| switch (err) {
8058 // We wrote the `ar_hdr` ourselves (in `loadObject`), so it is definitely valid.
8059 error.Overflow, error.InvalidCharacter => unreachable,
8060 };
8061
8062 switch (slice.len - @sizeOf(std.elf.ar_hdr) - input_size) {
8063 0 => {},
8064 1 => {
8065 // Alignment added one padding byte, which the format requires to have value '\n'.
8066 slice[slice.len - 1] = '\n';
8067 },
8068 else => unreachable, // node size should agree with the value we wrote into `ar_hdr.ar_size`
8069 }
8070
8071 var fr = file.reader(io, &.{});
8072 var w: Io.Writer = .fixed(slice[@sizeOf(std.elf.ar_hdr)..]);
8073 const n_bytes_read = w.sendFileAll(&fr, .limited(input_size)) catch |err| switch (err) {
8074 error.ReadFailed => return diags.fail("failed to read input \"{f}{f}\": {t}", .{
8075 path.fmtEscapeString(),
8076 fmtMemberString(ii.member(elf)),
8077 fr.err orelse (fr.seek_err orelse fr.size_err.?),
8078 }),
8079 error.WriteFailed => unreachable, // `.limited(input_size)` prevents us writing too many bytes
8080 };
8081 if (n_bytes_read != input_size) {
8082 return diags.fail("failed to load input \"{f}{f}\": file truncated during compilation", .{
8083 path.fmtEscapeString(),
8084 fmtMemberString(ii.member(elf)),
8085 });
8086 }
8087}
8088
8089fn flushInputSection(elf: *Elf, isi: InputSection.Index) Error!void {
8090 const file_loc = isi.fileLocation(elf);
8091 if (file_loc.size == 0) return;
8092 const comp = elf.base.comp;
8093 const io = comp.io;
8094 const gpa = comp.gpa;
8095 const diags = &comp.link_diags;
8096 const ii = isi.input(elf);
8097 const path = ii.path(elf);
8098 const file = path.root_dir.handle.openFile(io, path.sub_path, .{}) catch |err| switch (err) {
8099 error.Canceled => |e| return e,
8100 else => |e| return diags.fail("failed to open input file \"{f}\": {t}", .{ path.fmtEscapeString(), e }),
8101 };
8102 defer file.close(io);
8103 var fr = file.reader(io, &.{});
8104 fr.seekTo(file_loc.offset) catch |err| switch (err) {
8105 error.Canceled => |e| return e,
8106 else => |e| return diags.fail("failed to read input section '{s}' from \"{f}{f}\": {t}", .{
8107 elf.getNode(isi.node(elf).parent(&elf.mf).unwrap().?).section.name(elf).slice(elf),
8108 path.fmtEscapeString(),
8109 fmtMemberString(ii.member(elf)),
8110 e,
8111 }),
8112 };
8113 var nw: MappedFile.Node.Writer = undefined;
8114 isi.node(elf).writer(&elf.mf, gpa, &nw);
8115 defer nw.deinit();
8116 const n_bytes = nw.interface.sendFileAll(&fr, .limited(@intCast(file_loc.size))) catch |err| switch (err) {
8117 error.ReadFailed => return diags.fail("failed to read input section '{s}' from \"{f}{f}\": {t}", .{
8118 elf.getNode(isi.node(elf).parent(&elf.mf).unwrap().?).section.name(elf).slice(elf),
8119 path.fmtEscapeString(),
8120 fmtMemberString(ii.member(elf)),
8121 fr.err orelse (fr.seek_err orelse fr.size_err.?),
8122 }),
8123 error.WriteFailed => return nw.err.?,
8124 };
8125 if (n_bytes != file_loc.size) return diags.fail("failed to read input section '{s}' from \"{f}{f}\": unexpected eof", .{
8126 elf.getNode(isi.node(elf).parent(&elf.mf).unwrap().?).section.name(elf).slice(elf),
8127 path.fmtEscapeString(),
8128 fmtMemberString(ii.member(elf)),
8129 });
8130 // The input section should already be considered to have moved, because it is created as moved
8131 // and pending calls to `flushInputSection` always happen before pending calls to `flushMoved`.
8132 assert(isi.node(elf).hasMoved(&elf.mf));
8133}
8134
8135fn flushElfOffset(elf: *Elf, ni: MappedFile.Node.Index) void {
8136 const elf_offset = elf.getNodeElfOffset(ni);
8137 switch (elf.getNode(ni)) {
8138 else => unreachable,
8139 .ehdr => assert(elf_offset == 0),
8140 .shdr => switch (elf.ehdrPtr()) {
8141 inline else => |ehdr| elf.targetStore(&ehdr.shoff, @intCast(elf_offset)),
8142 },
8143 .segment => |phndx| {
8144 switch (elf.phdrSlice()) {
8145 inline else => |phdr, class| {
8146 const ph = &phdr[phndx];
8147 elf.targetStore(&ph.offset, @intCast(elf_offset));
8148 if (elf.targetLoad(&ph.type) == .PHDR) {
8149 @field(elf.ehdrPtr(), @tagName(class)).phoff = ph.offset;
8150 }
8151 },
8152 }
8153 var child_oni = ni.first(&elf.mf);
8154 while (child_oni.unwrap()) |child_ni| : (child_oni = child_ni.next(&elf.mf)) {
8155 elf.flushElfOffset(child_ni);
8156 }
8157 },
8158 .section => |shndx| switch (elf.shdrPtr(shndx)) {
8159 inline else => |shdr| elf.targetStore(&shdr.offset, @intCast(elf_offset)),
8160 },
8161 }
8162}
8163
8164fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void {
8165 const trace = tracy.trace(@src());
8166 defer trace.end();
8167
8168 switch (elf.getNode(ni)) {
8169 .archive => unreachable,
8170 .archive_header => unreachable,
8171
8172 .archive_input_member,
8173 .archive_elf_member_header,
8174 .elf,
8175 => {
8176 assert(elf.archive != null);
8177 return;
8178 },
8179
8180 .ehdr, .shdr => elf.flushElfOffset(ni),
8181 .segment => |phndx| {
8182 elf.flushElfOffset(ni);
8183 switch (elf.phdrSlice()) {
8184 inline else => |phdr| {
8185 const ph = &phdr[phndx];
8186 switch (elf.targetLoad(&ph.type)) {
8187 else => unreachable,
8188
8189 .NULL, .LOAD => {
8190 try elf.allocateSegmentLoadAddress(phndx);
8191 },
8192
8193 .DYNAMIC,
8194 .INTERP,
8195 .PHDR,
8196 .TLS,
8197 .GNU_RELRO,
8198 => {
8199 const new_vaddr = elf.computeNodeVAddr(ni);
8200 elf.targetStore(&ph.vaddr, @intCast(new_vaddr));
8201 elf.targetStore(&ph.paddr, @intCast(new_vaddr));
8202 },
8203 }
8204 },
8205 }
8206 },
8207 .section => |shndx| {
8208 elf.flushElfOffset(ni);
8209 const addr = elf.computeNodeVAddr(ni);
8210 const old_addr: u64, const flags: std.elf.SHF = switch (elf.shdrPtr(shndx)) {
8211 inline else => |shdr| .{
8212 elf.targetLoad(&shdr.addr),
8213 elf.targetLoad(&shdr.flags).shf,
8214 },
8215 };
8216
8217 if (flags.ALLOC) {
8218 switch (elf.shdrPtr(shndx)) {
8219 inline else => |shdr| elf.targetStore(&shdr.addr, @intCast(addr)),
8220 }
8221
8222 // Update global symbols targeting this section
8223 if (elf.node_global_symbols.get(ni)) |first_name| {
8224 assert(first_name != .empty);
8225 var name = first_name;
8226 while (name != .empty) {
8227 const old_sym_addr = Symbol.Id.global(name).value(elf);
8228 Symbol.Id.global(name).flushMoved(
8229 elf,
8230 old_sym_addr - old_addr + addr,
8231 );
8232 name = elf.globalByName(name).?.next_in_node;
8233 }
8234 }
8235
8236 Symbol.Id.local(shndx.get(elf).lsi).flushMoved(elf, addr);
8237 }
8238
8239 if (shndx == elf.shndx.got) {
8240 const rela_dyn_shndx = elf.shndx.rela_dyn;
8241 for (elf.got.values()) |opt_rela_index| {
8242 const rela_index = opt_rela_index.unwrap() orelse continue;
8243 rela_dyn_shndx.relaAdjustOffset(elf, rela_index, old_addr, addr);
8244 }
8245 for (elf.got_relocs.items) |*reloc| {
8246 reloc.apply(elf);
8247 }
8248 } else if (shndx == elf.shndx.plt) {
8249 elf.flushMovedNodeRelocs(ni, addr, elf.plt_first_symbol_reloc, .none);
8250 elf.flushMovedPltSection(.plt, old_addr, addr);
8251 } else if (shndx == elf.shndx.got_plt) {
8252 elf.flushMovedPltSection(.got_plt, old_addr, addr);
8253 } else if (shndx == elf.shndx.plt_sec) {
8254 elf.flushMovedPltSection(.plt_sec, old_addr, addr);
8255 }
8256 },
8257 .input_section => |isi| {
8258 const old_section_addr = isi.ptr(elf).vaddr;
8259 const new_section_addr = elf.computeNodeVAddr(ni);
8260 isi.ptr(elf).vaddr = new_section_addr;
8261
8262 // Update local symbols
8263 const ii = isi.input(elf);
8264 var lsi, const end_lsi = ii.localSymbolRange(elf);
8265 while (lsi != end_lsi) : (lsi = @fromBackingInt(@backingInt(lsi) + 1)) {
8266 if (lsi.index().ptr(elf).node != ni.toOptional()) continue;
8267 const visibility: std.elf.STV = switch (elf.symPtr(lsi.index())) {
8268 inline else => |sym| elf.targetLoad(&sym.other).visibility,
8269 };
8270 switch (visibility) {
8271 .HIDDEN, .INTERNAL => {
8272 // This is actually a global symbol which got demoted to STB_LOCAL due
8273 // to its visibility. It will be handled in the global symbols pass
8274 // below; don't touch it now.
8275 continue;
8276 },
8277 .PROTECTED => unreachable, // not allowed for an STB_LOCAL symbol
8278 .DEFAULT => {},
8279 }
8280 const old_sym_addr = Symbol.Id.local(lsi).value(elf);
8281 Symbol.Id.local(lsi).flushMoved(
8282 elf,
8283 old_sym_addr - old_section_addr + new_section_addr,
8284 );
8285 }
8286
8287 // Update global symbols
8288 if (elf.node_global_symbols.get(ni)) |first_name| {
8289 assert(first_name != .empty);
8290 var name = first_name;
8291 while (name != .empty) {
8292 const old_sym_addr = Symbol.Id.global(name).value(elf);
8293 Symbol.Id.global(name).flushMoved(
8294 elf,
8295 old_sym_addr - old_section_addr + new_section_addr,
8296 );
8297 name = elf.globalByName(name).?.next_in_node;
8298 }
8299 }
8300
8301 elf.flushMovedNodeRelocs(
8302 ni,
8303 new_section_addr,
8304 isi.ptrConst(elf).first_symbol_reloc,
8305 isi.ptrConst(elf).first_got_reloc,
8306 );
8307 },
8308 .copied_global => |global_name| {
8309 const copied_global = elf.copied_globals.getPtr(global_name) orelse {
8310 // TODO: this node is orphaned, which is possible because `MappedFile` does not yet
8311 // support deleting nodes. See logic in `setGlobalSymbolValue`.
8312 return;
8313 };
8314 assert(copied_global.node == ni);
8315
8316 const new_addr = elf.computeNodeVAddr(ni);
8317 elf.shndx.rela_dyn.relaSetOffset(elf, copied_global.rela_index, new_addr);
8318
8319 Symbol.Id.global(global_name).flushMoved(elf, new_addr);
8320 },
8321 inline .nav, .uav, .lazy_code, .lazy_const_data => |mi| {
8322 const new_addr = elf.computeNodeVAddr(ni);
8323 Symbol.Id.local(mi.symbol(elf)).flushMoved(elf, new_addr);
8324 if (elf.node_global_symbols.get(ni)) |first_name| {
8325 assert(first_name != .empty);
8326 var name = first_name;
8327 while (name != .empty) {
8328 Symbol.Id.global(name).flushMoved(elf, new_addr);
8329 name = elf.globalByName(name).?.next_in_node;
8330 }
8331 }
8332 elf.flushMovedNodeRelocs(
8333 ni,
8334 new_addr,
8335 mi.firstSymbolReloc(elf),
8336 mi.firstGotReloc(elf),
8337 );
8338 },
8339 }
8340 try ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
8341}
8342
8343/// Given the index of a `PT_LOAD`/`PT_NULL` segment, assumes that the phdr's `offset` and `filesz`
8344/// have been updated as needed by the caller, and updates the `@"align"`, `vaddr`, `paddr`, and
8345/// `memsz` fields of the segment, in order to place it at a valid virtual address.
8346///
8347/// TODO: this function is currently a source of non-determinism in the linker, because handling the
8348/// moving or resizing of a segment could reorder them and thereby affect how we handle *future*
8349/// changes to segments.
8350fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Error!void {
8351 const segment_ni = elf.phdrs.items[orig_phndx].unwrap().?;
8352 assert(elf.getNode(segment_ni).segment == orig_phndx);
8353 const page_align = elf.targetPageAlign();
8354 const node_align = segment_ni.alignment(&elf.mf);
8355 const ph_align = page_align.max(node_align);
8356
8357 // If we determine that the segment's virtual address needs to move, then it's a good idea to
8358 // make it less likely that it needs to move *again* in the future, because it is expensive to
8359 // change a segment's load address (a lot of re-flushing is necessary). To do that, we reserve
8360 // more virtual address space than we need (multiplying the actual size by this value). That
8361 // way, there will usually be padding between segments which they can grow into.
8362 //
8363 // TODO: we might want to decrease this multiplier, or even omit it entirely, in cases where
8364 // virtual address space is constrained. For instance, 32-bit targets, or targets where short
8365 // PC-relative relocations between segments are common.
8366 const reserve_size_multiplier = 4;
8367
8368 switch (elf.phdrSlice()) {
8369 inline else => |phdr| {
8370 const offset = elf.targetLoad(&phdr[orig_phndx].offset);
8371 const size = elf.targetLoad(&phdr[orig_phndx].filesz);
8372
8373 if (size == 0) {
8374 assert(elf.targetLoad(&phdr[orig_phndx].type) == .NULL);
8375 } else {
8376 assert(elf.targetLoad(&phdr[orig_phndx].type) == .LOAD);
8377 }
8378
8379 elf.targetStore(&phdr[orig_phndx].memsz, size);
8380 elf.targetStore(&phdr[orig_phndx].@"align", @intCast(ph_align.toByteUnits()));
8381
8382 const orig_vaddr = elf.targetLoad(&phdr[orig_phndx].vaddr);
8383 assert(elf.targetLoad(&phdr[orig_phndx].paddr) == orig_vaddr);
8384
8385 var vaddr: u64 = orig_vaddr;
8386
8387 // First, we will shift the virtual address as needed in order to maintain the required
8388 // property that vaddr is congruent to offset modulo the phdr alignment.
8389 {
8390 // Compute the candidate address by undoing the current offset and then re-offsetting
8391 vaddr = std.mem.alignBackward(u64, vaddr, ph_align.toByteUnits()) + offset % ph_align.toByteUnits();
8392 // If `node_align` is greater than `page_align`, the address we just set might be in
8393 // the previous segment. The first page we "own" is the one in which the old vaddr
8394 // resides, so check against that.
8395 const first_good_vaddr = std.mem.alignBackward(u64, orig_vaddr, page_align.toByteUnits());
8396 if (vaddr < first_good_vaddr) {
8397 // Yep, we crossed into the previous segment's pages, so correct for that by
8398 // offsetting our address by another `ph_align`.
8399 vaddr += ph_align.toByteUnits();
8400 assert(vaddr >= first_good_vaddr);
8401 }
8402 }
8403
8404 // If our size has changed, or if the address shift above caused our "end" address to
8405 // cross a page boundary, then we might be overlapping with the next segment's pages. In
8406 // that case, we will jump past that segment and give ourselves a new address after it.
8407 // We'll need to repeat this for every loadable phdr after us, until we're no longer
8408 // overlapping anything.
8409 var phndx = orig_phndx;
8410 for (phdr[orig_phndx + 1 ..], orig_phndx + 1..) |*next_ph, next_phndx| {
8411 switch (elf.targetLoad(&next_ph.type)) {
8412 .NULL, .LOAD => {},
8413 else => {
8414 // All loadable segments have contiguous indices, so this indicates we have
8415 // become the last loadable segment, meaning we definitely don't overlap any
8416 // other loadable segment.
8417 break;
8418 },
8419 }
8420
8421 const next_vaddr = elf.targetLoad(&next_ph.vaddr);
8422 // Find the first virtual address which the next phdr "owns" by aligning its vaddr
8423 // backwards to the start of the page.
8424 const next_page_vaddr = std.mem.alignBackward(u64, next_vaddr, page_align.toByteUnits());
8425
8426 // Check if the segment fits here. We apply `reserve_size_multiplier`, but only if
8427 // the segment is already known to be moving---making it easier to grow in-place is
8428 // the whole point of the multiplier!
8429 {
8430 const target_size = if (vaddr == orig_vaddr) size else size * reserve_size_multiplier;
8431 if (vaddr + target_size <= next_page_vaddr) {
8432 break; // hooray, we fit here!
8433 }
8434 }
8435
8436 const next_ni = elf.phdrs.items[next_phndx].unwrap().?;
8437
8438 // This segment don't fit here, but before deciding how to proceed, we need to
8439 // consider any target-specific restrictions we are subject to.
8440 switch (elf.targetSegmentLoadAddressRestrictions()) {
8441 .none => {},
8442 .data_last => if (next_ni == elf.ni.data) {
8443 // We can't leapfrog over the data segment. Instead, that segment just needs
8444 // to be shifted forwards to make space for us, and we'll then `break` with
8445 // our current vaddr.
8446
8447 if (next_phndx + 1 < phdr.len) switch (elf.targetLoad(&phdr[next_phndx + 1].type)) {
8448 .NULL, .LOAD => unreachable, // data segment should be the last loadable segment
8449 else => {},
8450 };
8451
8452 const free_vaddr = vaddr + size * reserve_size_multiplier;
8453
8454 const next_align = page_align.max(next_ni.alignment(&elf.mf));
8455 const next_offset = elf.targetLoad(&next_ph.offset);
8456 const next_new_vaddr = next_align.forward(free_vaddr) + next_offset % next_align.toByteUnits();
8457
8458 // This logic for updating the data segment's vaddr is identical to how we
8459 // will update the vaddr of `phndx` when we break from the loop.
8460 elf.targetStore(&next_ph.vaddr, @intCast(next_new_vaddr));
8461 elf.targetStore(&next_ph.paddr, @intCast(next_new_vaddr));
8462 try next_ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
8463
8464 break;
8465 },
8466 }
8467
8468 // We don't fit here, so shift ourselves forward (i.e. swap with `next_phndx`). But
8469 // first we need to adjust `vaddr` to come after it.
8470 const next_size = elf.targetLoad(&next_ph.memsz);
8471 // Instead of putting ourselves right after `next_ph`, we'll go a bit later in the
8472 // address space so that `next_ph` has address space to grow into (like above).
8473 vaddr = ph_align.forward(@intCast(next_vaddr + next_size * 4)) + offset % ph_align.toByteUnits();
8474
8475 // Now just swap the phdrs and update our `phndx`.
8476 std.mem.swap(@TypeOf(next_ph.*), &phdr[phndx], next_ph);
8477 elf.phdrs.items[phndx] = .wrap(next_ni);
8478 elf.nodes.items(.data)[@backingInt(next_ni)] = .{ .segment = phndx };
8479 elf.phdrs.items[next_phndx] = .wrap(segment_ni);
8480 elf.nodes.items(.data)[@backingInt(segment_ni)] = .{ .segment = @intCast(next_phndx) };
8481 phndx = @intCast(next_phndx);
8482 }
8483
8484 if (vaddr != orig_vaddr) {
8485 elf.targetStore(&phdr[phndx].vaddr, @intCast(vaddr));
8486 elf.targetStore(&phdr[phndx].paddr, @intCast(vaddr));
8487 try segment_ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
8488 }
8489 },
8490 }
8491}
8492
8493fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void {
8494 const trace = tracy.trace(@src());
8495 defer trace.end();
8496
8497 _, const size = ni.location(&elf.mf).resolve(&elf.mf);
8498 switch (elf.getNode(ni)) {
8499 .archive, .archive_header => {},
8500 .archive_input_member => unreachable,
8501 .archive_elf_member_header => unreachable,
8502 .elf => if (elf.archive) |*archive| {
8503 const member_ar_hdr: *std.elf.ar_hdr = @ptrCast(
8504 archive.elf_member_header_ni.slice(&elf.mf),
8505 );
8506 if (std.mem.print(&member_ar_hdr.ar_size, "{d}", .{size})) |size_str| {
8507 @memset(member_ar_hdr.ar_size[size_str.len..], ' ');
8508 archive.elf_member_too_big = false;
8509 } else |err| switch (err) {
8510 error.NoSpaceLeft => archive.elf_member_too_big = true,
8511 }
8512 },
8513 .ehdr => unreachable,
8514 .shdr => {},
8515 .segment => |phndx| switch (elf.phdrSlice()) {
8516 inline else => |phdr| {
8517 assert(elf.phdrs.items[phndx].unwrap().? == ni);
8518 const ph = &phdr[phndx];
8519 elf.targetStore(&ph.filesz, @intCast(size));
8520 switch (elf.targetLoad(&ph.type)) {
8521 else => unreachable,
8522 .NULL, .LOAD => {
8523 elf.targetStore(&ph.type, if (size > 0) .LOAD else .NULL);
8524 try elf.allocateSegmentLoadAddress(phndx);
8525 },
8526 .DYNAMIC, .INTERP, .PHDR, std.elf.PT.GNU_RELRO => {
8527 elf.targetStore(&ph.memsz, @intCast(size));
8528 },
8529 .TLS => {
8530 elf.targetStore(&ph.memsz, @intCast(size));
8531 // TPOFF relocations care about the size of the TLS segment. Re-apply
8532 // those, and also update any GOT entries from GOTTPOFF relocations.
8533 for (elf.tls_size_symbol_relocs.keys()) |reloc| {
8534 reloc.get(elf).apply(elf);
8535 }
8536 for (elf.got.keys(), 0..) |got_key, got_index| {
8537 switch (got_key) {
8538 .reserved,
8539 .symbol,
8540 .tlsld0,
8541 .tlsld1,
8542 .tlsgd0,
8543 .tlsgd1,
8544 => {
8545 @branchHint(.likely);
8546 continue;
8547 },
8548
8549 .tpoff => elf.updateGotEntry(got_index),
8550 }
8551 }
8552 try ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
8553 },
8554 }
8555 },
8556 },
8557 .section => |shndx| switch (elf.shdrPtr(shndx)) {
8558 inline else => |shdr| {
8559 switch (elf.targetLoad(&shdr.type)) {
8560 else => unreachable,
8561
8562 .NULL => if (size > 0) elf.targetStore(&shdr.type, .PROGBITS),
8563 .PROGBITS => if (size == 0) elf.targetStore(&shdr.type, .NULL),
8564
8565 .INIT_ARRAY,
8566 .FINI_ARRAY,
8567 .PREINIT_ARRAY,
8568 .STRTAB,
8569 .SYMTAB,
8570 .DYNAMIC,
8571 .REL,
8572 .RELA,
8573 .DYNSYM,
8574 .HASH,
8575 => return,
8576 }
8577 if (shndx != elf.shndx.plt and
8578 shndx != elf.shndx.got and
8579 shndx != elf.shndx.got_plt)
8580 {
8581 elf.targetStore(&shdr.size, @intCast(size));
8582 }
8583 },
8584 },
8585 .input_section, .copied_global, .nav, .uav, .lazy_code, .lazy_const_data => {},
8586 }
8587}
8588
8589fn flushNextMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void {
8590 const trace = tracy.trace(@src());
8591 defer trace.end();
8592
8593 switch (elf.getNode(ni)) {
8594 .archive,
8595 .archive_input_member,
8596 .archive_elf_member_header,
8597 .elf,
8598 .ehdr,
8599 .shdr,
8600 .segment,
8601 .section,
8602 .input_section,
8603 .copied_global,
8604 .nav,
8605 .uav,
8606 .lazy_code,
8607 .lazy_const_data,
8608 => unreachable,
8609
8610 .archive_header => {
8611 const archive = &elf.archive.?;
8612
8613 // Because we can't just throw padding bytes in the middle of an archive file, we need
8614 // the member name string table (the "//" member) to absorb all the padding bytes
8615 // between it (in the `.archive_header` node) and the first actual member.
8616 const next_member_ni = ni.next(&elf.mf).unwrap() orelse {
8617 // I guess there are no link inputs yet? But there will be eventually!
8618 return;
8619 };
8620 const next_member_offset: u64, _ = next_member_ni.location(&elf.mf).resolve(&elf.mf);
8621 const strtab_member_offset = std.elf.ARMAG.len + @sizeOf(std.elf.ar_hdr);
8622 assert(Alignment.@"2".check(next_member_offset));
8623 assert(Alignment.@"2".check(strtab_member_offset));
8624 const strtab_size = next_member_offset - strtab_member_offset;
8625
8626 const member_ar_hdr: *std.elf.ar_hdr = @ptrCast(
8627 archive.header_ni.slice(&elf.mf)[std.elf.ARMAG.len..][0..@sizeOf(std.elf.ar_hdr)],
8628 );
8629 if (std.mem.print(&member_ar_hdr.ar_size, "{d}", .{strtab_size})) |size_str| {
8630 @memset(member_ar_hdr.ar_size[size_str.len..], ' ');
8631 archive.strtab_member_too_big = false;
8632 } else |err| switch (err) {
8633 error.NoSpaceLeft => archive.strtab_member_too_big = true,
8634 }
8635 },
8636 }
8637}
8638
8639fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void {
8640 const target_endian = elf.targetEndian();
8641
8642 // We use the existing free-list tracking of the `.rela.plt` section to also behave as a
8643 // free-list for the PLT itself---see `pltEntryIsDead` for details.
8644 const plt_index: u32 = @backingInt(elf.shndx.rela_plt.relaAddOneAssumeCapacity(elf, .{
8645 .type = .jumpSlot(elf),
8646 .offset = 0, // populated later
8647 .raw_sym_index = dynsym_index,
8648 .addend = 0,
8649 }));
8650
8651 // On architectures without `.got.plt` (e.g. SPARC) these values actually refer to `.plt`.
8652 const got_plt_section: Section.Index, const got_plt_offset: u64 = got_plt: {
8653 const plt = elf.targetPltInfo();
8654 break :got_plt if (plt.got_plt) |got_plt| .{
8655 elf.shndx.got_plt,
8656 elf.targetPtrSize() * (got_plt.header_entries + plt_index),
8657 } else .{
8658 elf.shndx.plt,
8659 plt.entry_size * (plt.header_entries + plt_index),
8660 };
8661 };
8662
8663 // Now that we know the index, we can set the relocation's offset.
8664 elf.shndx.rela_plt.relaSetOffset(elf, @fromBackingInt(plt_index), got_plt_section.vaddr(elf) + got_plt_offset);
8665
8666 if (plt_index < elf.plt.count()) {
8667 // We reused a free entry, so we're already done!
8668 elf.plt.setKey(plt_index, global_name);
8669 return;
8670 }
8671
8672 // We added a new entry, so we now need to extend the PLT sections.
8673 assert(plt_index == elf.plt.count());
8674 elf.plt.putAssumeCapacityNoClobber(global_name, {});
8675
8676 switch (elf.ehdrMachine()) {
8677 .AARCH64, .PPC64, .RISCV => |machine| @panic(@tagName(machine)),
8678 .X86_64 => {
8679 const plt_ni = elf.shndx.plt.get(elf).ni;
8680 const plt_addr = plt_addr: switch (elf.shdrPtr(elf.shndx.plt)) {
8681 inline else => |shdr| {
8682 const old_size = 16 * (1 + plt_index);
8683 assert(elf.targetLoad(&shdr.size) == old_size);
8684 elf.targetStore(&shdr.size, old_size + 16);
8685 const plt_slice = plt_ni.slice(&elf.mf)[old_size..][0..16];
8686 @memcpy(plt_slice, &[16]u8{
8687 0xf3, 0x0f, 0x1e, 0xfa, // endbr64
8688 0x68, 0x00, 0x00, 0x00, 0x00, // push $0x0
8689 0xe9, 0x00, 0x00, 0x00, 0x00, // jmp 0
8690 0x66, 0x90, // xchg %ax,%ax
8691 });
8692 std.mem.writeInt(u32, plt_slice[5..][0..4], plt_index, target_endian);
8693 std.mem.writeInt(
8694 i32,
8695 plt_slice[10..][0..4],
8696 -@as(i32, @intCast(old_size + 14)),
8697 target_endian,
8698 );
8699 break :plt_addr elf.targetLoad(&shdr.addr) + old_size;
8700 },
8701 };
8702
8703 const got_plt_ni = elf.shndx.got_plt.get(elf).ni;
8704 switch (elf.shdrPtr(elf.shndx.got_plt)) {
8705 inline else => |shdr, class| {
8706 assert(elf.targetLoad(&shdr.size) == got_plt_offset);
8707 elf.targetStore(&shdr.size, @intCast(got_plt_offset + @sizeOf(class.ElfN().Addr)));
8708 std.mem.writeInt(
8709 class.ElfN().Addr,
8710 got_plt_ni.slice(&elf.mf)[@intCast(got_plt_offset)..][0..@sizeOf(class.ElfN().Addr)],
8711 @intCast(plt_addr),
8712 target_endian,
8713 );
8714 },
8715 }
8716
8717 const plt_sec_ni = elf.shndx.plt_sec.get(elf).ni;
8718 switch (elf.shdrPtr(elf.shndx.plt_sec)) {
8719 inline else => |shdr| {
8720 const old_size = 16 * plt_index;
8721 elf.targetStore(&shdr.size, old_size + 16);
8722 const plt_sec_slice = plt_sec_ni.slice(&elf.mf)[old_size..][0..16];
8723 @memcpy(plt_sec_slice, &[16]u8{
8724 0xf3, 0x0f, 0x1e, 0xfa, // endbr64
8725 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *0x0(%rip)
8726 0x66, 0x0f, 0x1f, 0x44, 0x00, 0x00, // nopw 0x0(%rax,%rax,1)
8727 });
8728 std.mem.writeInt(
8729 i32,
8730 plt_sec_slice[6..][0..4],
8731 @intCast(@as(i64, @bitCast(
8732 (got_plt_section.vaddr(elf) + got_plt_offset) -% (elf.targetLoad(&shdr.addr) + old_size + 10),
8733 ))),
8734 target_endian,
8735 );
8736 },
8737 }
8738 },
8739 .LOONGARCH => {
8740 // add a .PLT entry, writing the template
8741 const plt_ni = elf.shndx.plt.get(elf).ni;
8742 const plt_addr, const plt_slice = plt_entry: switch (elf.shdrPtr(elf.shndx.plt)) {
8743 inline else => |shdr| {
8744 const old_size = 16 * (1 + plt_index);
8745 assert(elf.targetLoad(&shdr.size) == old_size);
8746 elf.targetStore(&shdr.size, old_size + 16);
8747 const plt_slice = plt_ni.slice(&elf.mf)[old_size..][0..16];
8748 @memcpy(plt_slice, source: switch (elf.identClass()) {
8749 .NONE, _ => unreachable,
8750 inline .@"32", .@"64" => |elf_class| {
8751 const ld_byte = if (elf_class == .@"64") 0xc0 else 0x80;
8752 break :source &[16]u8{
8753 0x1a, 0x00, 0x00, 0x0f, // pcalau12i $t3, %pc_hi20(func@.got.plt)
8754 0x28, ld_byte, 0x01, 0xef, // ld.w/d $t3, $t3, %lo12(func@.got.plt)
8755 0x4c, 0x00, 0x01, 0xed, // jirl $t1, $t3, 0
8756 0x00, 0x2a, 0x00, 0x00, // break
8757 };
8758 },
8759 });
8760 break :plt_entry .{ elf.targetLoad(&shdr.addr) + old_size, plt_slice };
8761 },
8762 };
8763
8764 // add a .GOT.PLT entry, writing the address of the corresponding .PLT entry
8765 const got_plt_ni = elf.shndx.got_plt.get(elf).ni;
8766 switch (elf.shdrPtr(elf.shndx.got_plt)) {
8767 inline else => |shdr, class| {
8768 assert(elf.targetLoad(&shdr.size) == got_plt_offset);
8769 elf.targetStore(&shdr.size, @intCast(got_plt_offset + @sizeOf(class.ElfN().Addr)));
8770 std.mem.writeInt(
8771 class.ElfN().Addr,
8772 got_plt_ni.slice(&elf.mf)[@intCast(got_plt_offset)..][0..@sizeOf(class.ElfN().Addr)],
8773 @intCast(plt_addr),
8774 target_endian,
8775 );
8776 },
8777 }
8778
8779 // relocate the PLT entry to point to the .GOT.PLT entry
8780 const got_plt_abs = got_plt_section.vaddr(elf) + got_plt_offset;
8781 // TODO: handle overflow gracefully
8782 const inst0: *align(1) link.loongarch.J20 = @ptrCast(plt_slice[0..4]);
8783 const inst1: *align(1) link.loongarch.K12 = @ptrCast(plt_slice[4..8]);
8784 elf.targetStore(inst0, .{
8785 .b0_4 = elf.targetLoad(inst0).b0_4,
8786 .j20 = link.loongarch.pcalaHi20(got_plt_abs, plt_addr),
8787 .b25_31 = elf.targetLoad(inst0).b25_31,
8788 });
8789 elf.targetStore(inst1, .{
8790 .b0_9 = elf.targetLoad(inst1).b0_9,
8791 .k12 = @truncate(got_plt_abs),
8792 .b22_31 = elf.targetLoad(inst1).b22_31,
8793 });
8794 },
8795 .SPARCV9 => {
8796 // add a .PLT entry, writing the template
8797 const plt_ni = elf.shndx.plt.get(elf).ni;
8798 switch (elf.shdrPtr(elf.shndx.plt)) {
8799 inline else => |shdr| {
8800 assert(elf.targetLoad(&shdr.size) == got_plt_offset);
8801 elf.targetStore(&shdr.size, @intCast(got_plt_offset + 32));
8802 const Inst = packed union(u32) {
8803 raw: u32,
8804 imm22: packed struct { imm: u22, op: u10 },
8805 disp19: packed struct { disp: u19, op: u13 },
8806 };
8807 const plt_slice: []Inst = @ptrCast(@alignCast(plt_ni.slice(&elf.mf)[@intCast(got_plt_offset)..][0..32]));
8808 @memcpy(plt_slice, &[8]Inst{
8809 // sethi (. - .plt[0]), %g1
8810 .{ .imm22 = .{ .imm = @truncate(got_plt_offset), .op = 0b0000001100 } },
8811 // ba,a %xcc, .plt[1]
8812 .{ .disp19 = .{ .disp = @truncate((got_plt_offset + 4 - 32) >> 2), .op = 0b0011000001101 } },
8813 // nop
8814 .{ .raw = 0x0100_0000 },
8815 // nop
8816 .{ .raw = 0x0100_0000 },
8817 // nop
8818 .{ .raw = 0x0100_0000 },
8819 // nop
8820 .{ .raw = 0x0100_0000 },
8821 // nop
8822 .{ .raw = 0x0100_0000 },
8823 // nop
8824 .{ .raw = 0x0100_0000 },
8825 });
8826 if (elf.targetEndian() != std.lang.Endian.native) {
8827 std.mem.byteSwapAllElements(Inst, plt_slice);
8828 }
8829 },
8830 }
8831 },
8832 }
8833}
8834fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_addr: u64, addr: u64) void {
8835 const target_endian = elf.targetEndian();
8836 switch (elf.ehdrMachine()) {
8837 .AARCH64, .PPC64, .RISCV => |machine| @panic(@tagName(machine)),
8838 .X86_64 => {
8839 switch (which) {
8840 .plt => return,
8841 .plt_sec => {
8842 // Re-apply all PLT relocations. If a symbol is in the PLT then the majority of
8843 // its relocations are probably going through the PLT, so we don't bother with
8844 // specific tracking for PLT relocations---instead just re-apply all relocations
8845 // targeting symbols with PLT entries.
8846 for (elf.plt.keys()) |name| {
8847 Symbol.Id.global(name).applyTargetRelocs(elf);
8848 }
8849 // We also need to update all of the references from `.plt.sec` to `.got.plt`.
8850 // However, if there's also a flush pending for `.got.plt`, don't bother doing
8851 // this now, because we'll do it when `.got.plt` is flushed anyway.
8852 if (elf.shndx.got_plt.get(elf).ni.hasMoved(&elf.mf)) {
8853 return;
8854 }
8855 // Exit this `switch` to update those references.
8856 },
8857 .got_plt => {
8858 // Update the offsets of the relocation entries in `.rela.plt`.
8859 const rela_plt_shndx = elf.shndx.rela_plt;
8860 for (0..elf.plt.count()) |plt_index| {
8861 if (elf.pltEntryIsDead(plt_index)) continue;
8862 rela_plt_shndx.relaAdjustOffset(elf, @fromBackingInt(@intCast(plt_index)), old_addr, addr);
8863 }
8864 // We also need to update all of the references from `.plt.sec` to `.got.plt`.
8865 // However, if there's also a flush pending for `.plt.sec`, don't bother doing
8866 // this now, because we'll do it when `.plt.sec` is flushed anyway.
8867 if (elf.shndx.plt_sec.get(elf).ni.hasMoved(&elf.mf)) {
8868 return;
8869 }
8870 // Exit this `switch` to update those references.
8871 },
8872 }
8873 // We are updating the references from `.plt.sec` to `.got.plt`.
8874 const got_plt_addr = elf.shndx.got_plt.vaddr(elf);
8875 const plt_sec_addr = elf.shndx.plt_sec.vaddr(elf);
8876 const plt_sec_slice = elf.shndx.plt_sec.get(elf).ni.slice(&elf.mf);
8877 switch (elf.identClass()) {
8878 .NONE, _ => unreachable,
8879 inline else => |class| {
8880 const Addr = class.ElfN().Addr;
8881 for (0..elf.plt.count()) |plt_index| {
8882 const plt_sec_offset = 16 * plt_index;
8883 const got_plt_offset = @sizeOf(Addr) * (3 + plt_index);
8884 std.mem.writeInt(
8885 i32,
8886 plt_sec_slice[plt_sec_offset + 6 ..][0..4],
8887 @intCast(@as(i64, @bitCast(
8888 (got_plt_addr + got_plt_offset) -% (plt_sec_addr + plt_sec_offset + 10),
8889 ))),
8890 target_endian,
8891 );
8892 }
8893 },
8894 }
8895 },
8896 .LOONGARCH => {
8897 switch (which) {
8898 .plt => {
8899 // Re-apply all PLT relocations. If a symbol is in the PLT then the majority of
8900 // its relocations are probably going through the PLT, so we don't bother with
8901 // specific tracking for PLT relocations---instead just re-apply all relocations
8902 // targeting symbols with PLT entries.
8903 for (elf.plt.keys()) |name| {
8904 Symbol.Id.global(name).applyTargetRelocs(elf);
8905 }
8906 // We also need to update all of the references from `.plt` to `.got.plt`.
8907 // However, if there's also a flush pending for `.got.plt`, don't bother doing
8908 // this now, because we'll do it when `.got.plt` is flushed anyway.
8909 if (elf.shndx.got_plt.get(elf).ni.hasMoved(&elf.mf)) {
8910 return;
8911 }
8912 // Exit this `switch` to update those references.
8913 },
8914 .plt_sec => unreachable,
8915 .got_plt => {
8916 // Update the offsets of the relocation entries in `.rela.plt`.
8917 const rela_plt_shndx = elf.shndx.rela_plt;
8918 for (0..elf.plt.count()) |plt_index| {
8919 if (elf.pltEntryIsDead(plt_index)) continue;
8920 rela_plt_shndx.relaAdjustOffset(elf, @fromBackingInt(@intCast(plt_index)), old_addr, addr);
8921 }
8922 // We also need to update all of the references from `.plt` to `.got.plt`.
8923 // However, if there's also a flush pending for `.plt`, don't bother doing
8924 // this now, because we'll do it when `.plt` is flushed anyway.
8925 if (elf.shndx.plt.get(elf).ni.hasMoved(&elf.mf)) {
8926 return;
8927 }
8928 // Exit this `switch` to update those references.
8929 },
8930 }
8931 // We are updating the references from `.plt` to `.got.plt`.
8932 const got_plt_addr = elf.shndx.got_plt.vaddr(elf);
8933 const plt_addr = elf.shndx.plt.vaddr(elf);
8934 const plt_slice = elf.shndx.plt.get(elf).ni.slice(&elf.mf);
8935 switch (elf.identClass()) {
8936 .NONE, _ => unreachable,
8937 inline else => |class| {
8938 const Addr = class.ElfN().Addr;
8939 for (0..elf.plt.count()) |plt_index| {
8940 const plt_offset = 16 * plt_index;
8941 const got_plt_offset = @sizeOf(Addr) * (2 + plt_index);
8942 const target_slice = plt_slice[plt_offset..];
8943
8944 const got_plt_abs: u64 = got_plt_addr + got_plt_offset;
8945 // TODO: handle overflow gracefully
8946 const inst0: *align(1) link.loongarch.J20 = @ptrCast(target_slice[0..4]);
8947 const inst1: *align(1) link.loongarch.K12 = @ptrCast(target_slice[4..8]);
8948
8949 elf.targetStore(inst0, .{
8950 .b0_4 = elf.targetLoad(inst0).b0_4,
8951 .j20 = link.loongarch.pcalaHi20(got_plt_abs, plt_addr + plt_offset),
8952 .b25_31 = elf.targetLoad(inst0).b25_31,
8953 });
8954
8955 elf.targetStore(inst1, .{
8956 .b0_9 = elf.targetLoad(inst1).b0_9,
8957 .k12 = @truncate(got_plt_abs),
8958 .b22_31 = elf.targetLoad(inst1).b22_31,
8959 });
8960 }
8961 },
8962 }
8963 },
8964 .SPARCV9 => switch (which) {
8965 .plt => {
8966 // Re-apply all PLT relocations. If a symbol is in the PLT then the majority of
8967 // its relocations are probably going through the PLT, so we don't bother with
8968 // specific tracking for PLT relocations---instead just re-apply all relocations
8969 // targeting symbols with PLT entries.
8970 for (elf.plt.keys()) |name| {
8971 Symbol.Id.global(name).applyTargetRelocs(elf);
8972 }
8973 // Update the offsets of the relocation entries in `.rela.plt`.
8974 const rela_plt_shndx = elf.shndx.rela_plt;
8975 for (0..elf.plt.count()) |plt_index| {
8976 if (elf.pltEntryIsDead(plt_index)) continue;
8977 rela_plt_shndx.relaAdjustOffset(elf, @fromBackingInt(@intCast(plt_index)), old_addr, addr);
8978 }
8979 },
8980 .plt_sec, .got_plt => unreachable,
8981 },
8982 }
8983}
8984
8985pub fn updateExports(
8986 elf: *Elf,
8987 pt: Zcu.PerThread,
8988 export_indices: []const Zcu.Export.Index,
8989) link.Error!void {
8990 const diags = &elf.base.comp.link_diags;
8991 for (export_indices) |export_index| {
8992 elf.updateExportInner(pt, export_index) catch |err| switch (err) {
8993 else => |e| return e,
8994 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
8995 };
8996 }
8997}
8998fn updateExportInner(
8999 elf: *Elf,
9000 pt: Zcu.PerThread,
9001 export_index: Zcu.Export.Index,
9002) Error!void {
9003 const zcu = pt.zcu;
9004 const ip = &zcu.intern_pool;
9005
9006 const @"export" = export_index.ptr(zcu);
9007
9008 switch (@"export".exported) {
9009 .nav => |nav| log.debug("updateExports({f})", .{ip.getNav(nav).fqn.fmt(ip)}),
9010 .uav => |uav| log.debug("updateExports(@as({f}, {f}))", .{
9011 Type.fromInterned(ip.typeOf(uav)).fmt(pt),
9012 Value.fromInterned(uav).fmtValue(pt),
9013 }),
9014 }
9015 try elf.ensureUnusedSymbolCapacity(1, .maybe_global);
9016 const exported_lsi: Symbol.LocalIndex = switch (@"export".exported) {
9017 .nav => |nav| (try elf.navMapIndex(zcu, nav)).symbol(elf),
9018 .uav => |uav| (try elf.uavMapIndex(uav, .none)).symbol(elf),
9019 };
9020
9021 // Initialize the global symbol with the same values that the local one currently has. If the
9022 // NAV/UAV is updated, then `updateNavInner` or `genUav` will update the global symbol sizes,
9023 // and `flushMoved` will update their values.
9024 const cur_value: u64, const cur_size: u64, const @"type": std.elf.STT, const shndx: Section.Index = switch (elf.symPtr(exported_lsi.index())) {
9025 inline else => |exported_sym| .{
9026 elf.targetLoad(&exported_sym.value),
9027 elf.targetLoad(&exported_sym.size),
9028 elf.targetLoad(&exported_sym.info).type,
9029 .fromSection(elf.targetLoad(&exported_sym.shndx)),
9030 },
9031 };
9032
9033 const name = @"export".opts.name.toSlice(ip);
9034 _ = elf.addGlobalSymbolAssumeCapacity(.{
9035 .node = exported_lsi.index().ptr(elf).node,
9036 .name = try .string(elf, name),
9037 .value = cur_value,
9038 .size = cur_size,
9039 .type = @"type",
9040 .bind = switch (@"export".opts.linkage) {
9041 .strong => .strong,
9042 .weak => .weak,
9043 .internal => return elf.base.comp.link_diags.fail("TODO(Elf2): '.internal' linkage", .{}),
9044 .link_once => return elf.base.comp.link_diags.fail("TODO(Elf2): '.link_once' linkage", .{}),
9045 },
9046 .visibility = switch (@"export".opts.visibility) {
9047 .default => .DEFAULT,
9048 .hidden => .HIDDEN,
9049 .protected => .PROTECTED,
9050 },
9051 .shndx = shndx,
9052 }) catch |err| switch (err) {
9053 error.MultipleDefinitions => {
9054 // HACK: because we currently don't/can't delete these exports, we would typically
9055 // get these errors on every non-initial incremental update. Hack around that by
9056 // only emitting this error if the symbol we're conflicting with comes from an input
9057 // section (as opposed to the ZCU).
9058 const conflicting_global = elf.globalByName(try elf.string(.strtab, name)).?;
9059 if (conflicting_global.symtab_index.ptr(elf).node.unwrap()) |conflicting_node| {
9060 if (elf.getNode(conflicting_node) == .input_section) {
9061 return elf.base.comp.link_diags.fail(
9062 "multiple definitions of '{s}'",
9063 .{name},
9064 );
9065 }
9066 }
9067 },
9068 };
9069}
9070
9071fn dumpStderr(elf: *Elf, tid: Zcu.PerThread.Id) !void {
9072 const comp = elf.base.comp;
9073 const io = comp.io;
9074 var buffer: [512]u8 = undefined;
9075 const stderr = try io.lockStderr(&buffer, null);
9076 defer io.unlockStderr();
9077 const w = &stderr.file_writer.interface;
9078 _ = try elf.dump(w, tid);
9079}
9080
9081pub fn dump(elf: *Elf, w: *Io.Writer, tid: Zcu.PerThread.Id) !link.File.DumpResult {
9082 if (elf.options.enable_link_snapshots) {
9083 try elf.printNode(tid, w, .root, 0);
9084 return .enabled;
9085 }
9086 return .disabled;
9087}
9088
9089pub fn printNode(
9090 elf: *Elf,
9091 tid: Zcu.PerThread.Id,
9092 w: *Io.Writer,
9093 ni: MappedFile.Node.Index,
9094 indent: usize,
9095) Io.Writer.Error!void {
9096 const node = elf.getNode(ni);
9097 try w.splatByteAll(' ', indent);
9098 try w.writeAll(@tagName(node));
9099 switch (node) {
9100 else => {},
9101 .segment => |phndx| switch (elf.phdrSlice()) {
9102 inline else => |phdr| {
9103 const ph = &phdr[phndx];
9104 try w.writeByte('(');
9105 const pt = elf.targetLoad(&ph.type);
9106 if (std.enums.tagName(std.elf.PT, pt)) |pt_name|
9107 try w.writeAll(pt_name)
9108 else inline for (@typeInfo(std.elf.PT).@"enum".decl_names) |decl_name| {
9109 const decl_val = @field(std.elf.PT, decl_name);
9110 if (@TypeOf(decl_val) != std.elf.PT) continue;
9111 if (pt == @field(std.elf.PT, decl_name)) break try w.writeAll(decl_name);
9112 } else try w.print("0x{x}", .{pt});
9113 try w.writeAll(", ");
9114 const pf = elf.targetLoad(&ph.flags);
9115 if (pf.R) try w.writeByte('R');
9116 if (pf.W) try w.writeByte('W');
9117 if (pf.X) try w.writeByte('X');
9118 try w.writeByte(')');
9119 },
9120 },
9121 .section => |shndx| try w.print("({s})", .{shndx.name(elf).slice(elf)}),
9122 .input_section => |isi| {
9123 const ii = isi.input(elf);
9124 try w.print("({f}{f}, {s})", .{
9125 ii.path(elf).fmtEscapeString(),
9126 fmtMemberString(ii.member(elf)),
9127 elf.getNode(isi.node(elf).parent(&elf.mf).unwrap().?).section.name(elf).slice(elf),
9128 });
9129 },
9130 .copied_global => |name| try w.print("(copy:{s})", .{name.slice(elf)}),
9131 .nav => |nmi| {
9132 const zcu = elf.base.comp.zcu.?;
9133 const ip = &zcu.intern_pool;
9134 const nav = ip.getNav(nmi.navIndex(elf));
9135 try w.print("({f}, {f})", .{
9136 Type.fromInterned(ip.typeOf(nav.resolved.?.value)).fmt(.{ .zcu = zcu, .tid = tid }),
9137 nav.fqn.fmt(ip),
9138 });
9139 },
9140 .uav => |umi| {
9141 const zcu = elf.base.comp.zcu.?;
9142 const val: Value = .fromInterned(umi.uavValue(elf));
9143 try w.print("({f}, {f})", .{
9144 val.typeOf(zcu).fmt(.{ .zcu = zcu, .tid = tid }),
9145 val.fmtValue(.{ .zcu = zcu, .tid = tid }),
9146 });
9147 },
9148 inline .lazy_code, .lazy_const_data => |lmi| try w.print("({f})", .{
9149 Type.fromInterned(lmi.lazySymbol(elf).ty).fmt(.{
9150 .zcu = elf.base.comp.zcu.?,
9151 .tid = tid,
9152 }),
9153 }),
9154 }
9155 {
9156 const mf_node = &elf.mf.nodes.items[@backingInt(ni)];
9157 const off, const size = mf_node.location().resolve(&elf.mf);
9158 try w.print(" index={d} offset=0x{x} size=0x{x} align=0x{x} {t}{s}{s}{s}{s}\n", .{
9159 @backingInt(ni),
9160 off,
9161 size,
9162 mf_node.flags.alignment.toByteUnits(),
9163 mf_node.flags.position,
9164 if (mf_node.flags.moved) " moved" else "",
9165 if (mf_node.flags.next_moved) " next_moved" else "",
9166 if (mf_node.flags.resized) " resized" else "",
9167 if (mf_node.flags.has_content) " has_content" else "",
9168 });
9169 }
9170 if (ni.first(&elf.mf).unwrap()) |first_ni| {
9171 // non-leaf, just print children
9172 var child_ni = first_ni;
9173 while (true) {
9174 try elf.printNode(tid, w, child_ni, indent + 1);
9175 child_ni = child_ni.next(&elf.mf).unwrap() orelse break;
9176 }
9177 return;
9178 }
9179 const file_loc = ni.fileLocation(&elf.mf, false);
9180 var address = file_loc.offset;
9181 if (file_loc.size == 0) {
9182 try w.splatByteAll(' ', indent + 1);
9183 try w.print("{x:0>8}\n", .{address});
9184 return;
9185 }
9186 const line_len = 0x10;
9187 var line_it = std.mem.window(
9188 u8,
9189 elf.mf.memory_map.memory[@intCast(file_loc.offset)..][0..@intCast(file_loc.size)],
9190 line_len,
9191 line_len,
9192 );
9193 while (line_it.next()) |line_bytes| : (address += line_len) {
9194 try w.splatByteAll(' ', indent + 1);
9195 try w.print("{x:0>8} ", .{address});
9196 for (line_bytes) |byte| try w.print("{x:0>2} ", .{byte});
9197 try w.splatByteAll(' ', 3 * (line_len - line_bytes.len) + 1);
9198 for (line_bytes) |byte| try w.writeByte(if (std.ascii.isPrint(byte)) byte else '.');
9199 try w.writeByte('\n');
9200 }
9201}
9202
9203fn ensureSegmentAligned(elf: *Elf, start_phndx: u32, min_align: Alignment) Error!void {
9204 const gpa = elf.base.comp.gpa;
9205 // We need to loop through parent nodes because segments may be nested (e.g. a PT_TLS segment
9206 // inside a PT_LOAD segment).
9207 var phndx = start_phndx;
9208 while (true) {
9209 // Align the actual node
9210 const seg_ni = elf.phdrs.items[phndx].unwrap().?;
9211 if (min_align.compare(.gt, seg_ni.alignment(&elf.mf))) {
9212 try seg_ni.realign(&elf.mf, gpa, min_align);
9213 }
9214 // Update the phdr `@"align"` field if necessary
9215 switch (elf.phdrSlice()) {
9216 inline else => |phdr| switch (elf.targetLoad(&phdr[phndx].type)) {
9217 .NULL, .LOAD => {
9218 // The `@"align"` field is managed by `allocateSegmentLoadAddress`.
9219 //
9220 // It's very likely that the node was moved and/or resized when we realigned it
9221 // just above, but it is possible that it was not moved *but* still has an
9222 // unaligned virtual address. In that case, we need to ensure the segment's
9223 // virtual address range will be recomputed.
9224 if (!min_align.check(@intCast(elf.targetLoad(&phdr[phndx].vaddr)))) {
9225 try seg_ni.moved(gpa, &elf.mf);
9226 }
9227 },
9228 else => elf.targetStore(&phdr[phndx].@"align", @intCast(@max(
9229 elf.targetLoad(&phdr[phndx].@"align"),
9230 min_align.toByteUnits(),
9231 ))),
9232 },
9233 }
9234 // Continue on to the parent segment, if any
9235 switch (elf.getNode(seg_ni.parent(&elf.mf).unwrap().?)) {
9236 .segment => |parent_phndx| phndx = parent_phndx,
9237 .elf => return,
9238 else => unreachable,
9239 }
9240 }
9241}
9242
9243/// If `sym` has a PLT entry, returns the address of that entry (specifically, the address which a
9244/// branch to the PLT should target). If `sym` does not have a PLT entry, returns `null`.
9245fn pltEntryTargetAddr(elf: *Elf, sym: Symbol.Id) ?u64 {
9246 const index = switch (sym.unwrap()) {
9247 .local => return null,
9248 .global => |name| elf.plt.getIndex(name) orelse return null,
9249 };
9250 if (elf.pltEntryIsDead(index)) return null;
9251 const plt = elf.targetPltInfo();
9252 if (plt.plt_sec) |plt_sec| {
9253 return elf.shndx.plt_sec.vaddr(elf) +% index * plt_sec.entry_size;
9254 } else {
9255 return elf.shndx.plt.vaddr(elf) +% (plt.header_entries + index) * plt.entry_size;
9256 }
9257}