1const Elf = @This();
2
3const std = @import("std");
4const Io = std.Io;
5const assert = std.debug.assert;
6const log = std.log.scoped(.link);
7
8const codegen = @import("../codegen.zig");
9const Compilation = @import("../Compilation.zig");
10const Dwarf = @import("Dwarf2.zig");
11const InternPool = @import("../InternPool.zig");
12const link = @import("../link.zig");
13const MappedFile = link.MappedFile;
14const target_util = @import("../target.zig");
15const tracy = @import("../tracy.zig");
16const Type = @import("../Type.zig");
17const Value = @import("../Value.zig");
18const Zcu = @import("../Zcu.zig");
19const Alignment = MappedFile.Alignment;
20
21base: link.File,
22options: link.File.OpenOptions,
23mf: MappedFile,
24ni: struct {
25 elf: MappedFile.Node.Index,
26 ehdr: MappedFile.Node.Index,
27 shdr: MappedFile.Node.Index,
28 rodata: MappedFile.Node.Index,
29 phdr: MappedFile.Node.Index,
30 text: MappedFile.Node.Index,
31 data: MappedFile.Node.Index,
32 data_rel_ro: MappedFile.Node.Index,
33 tls: MappedFile.Node.Index.Optional,
34 gnu_eh_frame: MappedFile.Node.Index.Optional,
35},
36archive: ?Archive,
37nodes: std.MultiArrayList(Node),
38/// Does not contain an item for `SHN_UNDEF`.
39shdrs: std.ArrayList(Section),
40phdrs: std.ArrayList(MappedFile.Node.Index.Optional),
41shndx: struct {
42 got: Section.Index,
43 /// Always `.UNDEF` on some targets (e.g. SPARC).
44 got_plt: Section.Index,
45 plt: Section.Index,
46 /// Only created for x86 targets; `.UNDEF` everywhere else.
47 plt_sec: Section.Index,
48 dynsym: Section.Index,
49 dynstr: Section.Index,
50 dynamic: Section.Index,
51 hash: Section.Index,
52 tdata: Section.Index,
53 rela_dyn: Section.Index,
54 rela_plt: Section.Index,
55 debug_abbrev: Section.Index,
56 eh_frame_hdr: Section.Index,
57 eh_frame: Section.Index,
58 debug_frame: Section.Index,
59 debug_info: Section.Index,
60 debug_line: Section.Index,
61 debug_line_str: Section.Index,
62 debug_rnglists: Section.Index,
63 debug_str: Section.Index,
64 debug_str_offsets: Section.Index,
65 // These sections are created only as needed, and are initially `.UNDEF`.
66 init_array: Section.Index,
67 fini_array: Section.Index,
68 preinit_array: Section.Index,
69},
70dynamic: struct {
71 flags: u32,
72 flags_1: u32,
73 rpath: String(.dynstr),
74 soname: String(.dynstr),
75},
76symtab: std.ArrayList(Symbol),
77globals: struct {
78 strong_def: std.array_hash_map.Auto(String(.strtab), Symbol.Global),
79 weak_def: std.array_hash_map.Auto(String(.strtab), Symbol.Global),
80 strong_undef: std.array_hash_map.Auto(String(.strtab), Symbol.Global),
81 weak_undef: std.array_hash_map.Auto(String(.strtab), Symbol.Global),
82},
83/// Key is the name of an undef global for which we have created a "copy relocation" (`R_*_COPY`).
84copied_globals: std.array_hash_map.Auto(String(.strtab), struct {
85 node: MappedFile.Node.Index,
86 /// The index of this global's runtime relocation in `.rela.dyn`.
87 rela_index: Section.RelaIndex,
88}),
89/// Key is the name of an undef global for which we would *like* to create a copy relocation
90/// (`R_*_COPY`),but cannot because we have not seen an appropriate definition in a linked DSO yet.
91///
92/// Therefore, if, when scanning a DSO input, we discover a definition for one of these symbols, we
93/// will remove it from this map and call `maybeAddCopyRelocation`.
94want_copied_globals: std.array_hash_map.Auto(String(.strtab), void),
95/// Key is a node which is a valid `Symbol.node` value, value is the name of the first global symbol
96/// in that node. That symbol is the head of a linked list: see `Symbol.Global.next_in_node`.
97///
98/// Value is never `.empty`.
99///
100/// We use a separate hash map for this data rather than storing it in `navs` etc to save memory,
101/// because the vast majority of nodes which can export global symbols actually will not.
102node_global_symbols: std.array_hash_map.Auto(MappedFile.Node.Index, String(.strtab)),
103/// Contains all globals symbols defined in any needed DSO. This map serves three purposes:
104///
105/// * If we discover an undefined reference to one of these symbols, we know whether the symbol has
106/// type `STT_FUNC`, in which case we will create a PLT entry.
107///
108/// * If we discover a direct relocation (i.e. no GOT or PLT indirection) targeting one of these
109/// symbols, we know whether the symbol has type `STT_OBJECT` and we know its size and alignment,
110/// so we can emit a copy relocation for that symbol instead of using a text relocation.
111///
112/// * When emitting a dynamic executable, we can detect which undefined references are resolved by a
113/// linked DSO, so can emit "undefined global symbol" errors for any other undefined references.
114dso_globals: std.array_hash_map.Auto(String(.strtab), struct {
115 type: std.elf.STT,
116 size: u64,
117 /// This is usually unnecessary, but if a symbol is given a copy relocation (`R_*_COPY`) and so
118 /// becomes a part of the executable's address space despite being defined by a different DSO,
119 /// we need to know its alignment requirement so that we don't break other code. This isn't
120 /// actually stored on the symbol---instead we compute a maximum alignment from the alignment of
121 /// the section containing the symbol, and the symbol's offset within the section. I know this
122 /// sounds like a terrible hack, but it is *genuinely* how you're supposed to do this. Copy
123 /// relocations suck.
124 alignment: Alignment,
125}),
126shstrtab: StringTable,
127strtab: StringTable,
128dynstr: StringTable,
129
130/// Indices map 1--1 to indices into the actual `.got` section.
131///
132/// Value is the output relocation in `.rela.dyn` for the GOT entry.
133got: std.array_hash_map.Auto(GotKey, Section.RelaIndex.Optional),
134/// Key is the name of a global.
135///
136/// Indices map 1--1 to indices into the actual `.got.plt` section. These also equal indices into
137/// the relocations in `.rela.plt`, because every PLT entry has one output relocation (if a runtime
138/// relocation is no longer necessary, then neither is the corresponding PLT entry!).
139///
140/// PLT entries in this map may be "dead", meaning the PLT entry has been deemed unnecessary so is
141/// available for reuse---see `Elf.pltEntryIsDead`. Such entries must not be targeted by relocs.
142plt: std.array_hash_map.Auto(String(.strtab), void),
143/// The `.plt` section contains zero or more symbol relocations starting at this index.
144plt_first_symbol_reloc: SymbolReloc.Index,
145/// The `.eh_frame_hdr` section contains zero or more symbol relocations starting at this index.
146eh_frame_hdr_first_symbol_reloc: SymbolReloc.Index,
147
148needed: std.array_hash_map.Auto(String(.dynstr), void),
149inputs: std.ArrayList(struct {
150 path: std.Build.Cache.Path,
151 member: ?[]const u8,
152 extra: union {
153 /// Active for static libraries.
154 node: MappedFile.Node.Index,
155 /// Active otherwise.
156 file_symbol: Symbol.LocalIndex,
157 },
158}),
159input_pending_index: u32,
160input_sections: std.ArrayList(InputSection),
161input_section_pending_index: u32,
162/// SPARC has some weird relocations which involve setting some bits to fixed constant values. When
163/// we encounter such a relocation, we queue the action here, and apply them during `idle`.
164one_shot_fixups: std.ArrayList(struct {
165 node: MappedFile.Node.Index,
166 offset: u64,
167 /// The syntax in these tag names matches the syntax used in `SymbolReloc.Type.Simple.dest`.
168 action: enum {
169 @"32[12:10] = 0b000",
170 @"32[12:10] = 0b111",
171 @"32[12:12] = 0b0",
172 },
173}),
174navs: std.array_hash_map.Auto(InternPool.Nav.Index, struct {
175 lsi: Symbol.LocalIndex,
176 /// The start index of the contiguous sequence of symbol relocations in this NAV.
177 first_symbol_reloc: SymbolReloc.Index,
178 /// The start index of the contiguous sequence of GOT relocations in this NAV.
179 first_got_reloc: GotReloc.Index,
180}),
181uavs: std.array_hash_map.Auto(InternPool.Index, struct {
182 lsi: Symbol.LocalIndex,
183 /// The start index of the contiguous sequence of symbol relocations in this UAV.
184 first_symbol_reloc: SymbolReloc.Index,
185 // No `first_got_reloc` field because a UAV never contains GOT relocations.
186}),
187lazy: std.EnumArray(link.File.LazySymbol.Kind, struct {
188 map: std.array_hash_map.Auto(InternPool.Index, struct {
189 lsi: Symbol.LocalIndex,
190 /// The start index of the contiguous sequence of symbol relocations in this lazy code/data.
191 first_symbol_reloc: SymbolReloc.Index,
192 /// The start index of the contiguous sequence of GOT relocations in this lazy code/data.
193 first_got_reloc: GotReloc.Index,
194 }),
195 pending_index: u32,
196}),
197pending_uavs: std.ArrayList(Node.UavMapIndex),
198symbol_relocs: std.ArrayList(SymbolReloc),
199node_relocs: std.ArrayList(NodeReloc),
200got_relocs: std.ArrayList(GotReloc),
201/// Set of relocations which must be re-applied if the size of the TLS segment changes.
202tls_size_symbol_relocs: std.array_hash_map.Auto(SymbolReloc.Index, void),
203/// Index matches the index into `shdrs`. Like `shdrs`, this map excludes `SHN_UNDEF`.
204section_by_name: std.array_hash_map.Auto(String(.shstrtab), void),
205/// Key is the name of a global symbol which has been moved to a new symtab index. Any relocation
206/// entries which target that symbol must be updated to reference the correct symbol index.
207///
208/// When emitting a relocatable (`ET_REL`), this refers to the index in `.symtab`. Otherwise, it
209/// refers to the index in `.dynsym`.
210changed_symtab_index: std.array_hash_map.Auto(String(.strtab), void),
211/// Counts how many relocations are currently in `.rela.dyn` which would require a `DT_TEXTREL`
212/// entry in the `.dynamic` section. This allows adding `DT_TEXTREL` to the output `.dynamic`
213/// section in `flush` only when it is actually necessary. See also `nodeWantsDsoRelocation`.
214textrel_count: u32,
215
216dwarf: Dwarf,
217dwarf_shared: std.enums.EnumArray(Dwarf.SharedSection, dwarf_relocs.Shared),
218dwarf_units: []dwarf_relocs.Unit,
219dwarf_consts: std.array_hash_map.Auto(link.ConstPool.Index, dwarf_relocs.Const),
220dwarf_globals: std.ArrayList(dwarf_relocs.Global),
221dwarf_funcs: std.ArrayList(dwarf_relocs.Func),
222dwarf_decls: std.array_hash_map.Auto(Dwarf.Decl.Index, dwarf_relocs.Decl),
223
224overflowed_reloc_count: u32,
225misaligned_reloc_count: u32,
226
227const_prog_node: std.Progress.Node,
228input_prog_node: std.Progress.Node,
229
230const Error = link.Error || error{MappedFileIo};
231
232const Node = union(enum) {
233 deleted,
234
235 /// Only used when emitting a static library.
236 ///
237 /// Contains a header node which is an `.archive_header`.
238 ///
239 /// Contains the following footer nodes:
240 /// * One `.archive_input_member` for each external input in the archive
241 /// * One `.archive_elf_member_header` containing the `ar_hdr` for the ZCU
242 /// * One `.elf` containing the ZCU's actual ELF object
243 ///
244 /// Padding between the headers and footers is absorbed into the "//" member (whose actual
245 /// content is in the `.archive_header` node).
246 archive,
247 /// Only used when emitting a static library.
248 ///
249 /// Contains the archive magic (`ARMAG`), as well as the `ar_hdr` and content for the long file
250 /// name string table member ("//").
251 archive_header,
252 /// Only used when emitting a static library.
253 ///
254 /// Contains the `ar_hdr` and content for one non-ZCU archive member (external link input). Also
255 /// includes the single byte '\n' padding at the end of this archive member, if necessary.
256 archive_input_member: InputIndex,
257 /// Only used when emitting a static library.
258 ///
259 /// Contains the `ar_hdr` for the `.elf` node.
260 archive_elf_member_header,
261
262 elf,
263 ehdr,
264 shdr,
265 segment: u32,
266 section: Section.Index,
267 /// The section '.plt' may contain relocations via `elf.plt_first_symbol_reloc`.
268 section_manual_size: Section.Index,
269 /// May contain relocations.
270 input_section: InputSection.Index,
271 /// Value is the name of a global which has an entry in `elf.copied_globals`, so, a global for
272 /// which we have emitted a copy relocation.
273 ///
274 /// TODO it would be better to emit these into `.bss` or `.bss.rel.ro`, once we support those.
275 ///
276 /// TODO: currently, the `elf.copied_globals` entry may not be there---this case exists because
277 /// `MappedFile` does not (yet?) support deleting nodes. See logic in `setGlobalSymbolValue`.
278 copied_global: String(.strtab),
279 /// May contain relocations.
280 nav: NavMapIndex,
281 /// May contain relocations.
282 uav: UavMapIndex,
283 /// May contain relocations.
284 lazy_code: LazyMapRef.Index(.code),
285 /// May contain relocations.
286 lazy_const_data: LazyMapRef.Index(.const_data),
287
288 debug_shared: Dwarf.SharedSection,
289 eh_frame_footer,
290 unit_padding,
291 unit_frame: Dwarf.Unit.Index,
292 unit_frame_cie: Dwarf.Unit.Index,
293 unit_debug_info: Dwarf.Unit.Index,
294 unit_debug_info_header: Dwarf.Unit.Index,
295 unit_debug_info_footer: Dwarf.Unit.Index,
296 unit_debug_line: Dwarf.Unit.Index,
297 unit_debug_line_header: Dwarf.Unit.Index,
298 unit_debug_rnglists: Dwarf.Unit.Index,
299
300 const_debug_info: link.ConstPool.Index,
301 global_debug_info: Dwarf.Global.Index,
302 func_frame_fde: Dwarf.Func.Index,
303 func_debug_info: Dwarf.Func.Index,
304 func_debug_line: Dwarf.Func.Index,
305 decl_debug_info: Dwarf.Decl.Index,
306
307 pub const InputIndex = enum(u32) {
308 _,
309
310 pub fn path(ii: InputIndex, elf: *const Elf) std.Build.Cache.Path {
311 return elf.inputs.items[@backingInt(ii)].path;
312 }
313
314 pub fn member(ii: InputIndex, elf: *const Elf) ?[]const u8 {
315 return elf.inputs.items[@backingInt(ii)].member;
316 }
317
318 pub fn node(ii: InputIndex, elf: *const Elf) MappedFile.Node.Index {
319 return elf.inputs.items[@backingInt(ii)].extra.node;
320 }
321
322 pub fn fileSymbol(ii: InputIndex, elf: *const Elf) Symbol.LocalIndex {
323 return elf.inputs.items[@backingInt(ii)].extra.file_symbol;
324 }
325
326 pub fn localSymbolRange(ii: InputIndex, elf: *Elf) [2]Symbol.LocalIndex {
327 if (@backingInt(ii) + 1 < elf.inputs.items.len) {
328 const next_ii: InputIndex = @fromBackingInt(@backingInt(ii) + 1);
329 return .{ ii.fileSymbol(elf), next_ii.fileSymbol(elf) };
330 } else {
331 const local_symbols_len = switch (elf.shdrPtr(.symtab)) {
332 inline else => |shdr| elf.targetLoad(&shdr.info),
333 };
334 return .{ ii.fileSymbol(elf), @fromBackingInt(local_symbols_len) };
335 }
336 }
337 };
338
339 pub const NavMapIndex = enum(u32) {
340 _,
341
342 pub fn nav(nmi: NavMapIndex, elf: *const Elf) InternPool.Nav.Index {
343 return elf.navs.keys()[@backingInt(nmi)];
344 }
345
346 pub fn symbol(nmi: NavMapIndex, elf: *const Elf) Symbol.LocalIndex {
347 return elf.navs.values()[@backingInt(nmi)].lsi;
348 }
349
350 fn firstSymbolReloc(nmi: NavMapIndex, elf: *const Elf) SymbolReloc.Index {
351 return elf.navs.values()[@backingInt(nmi)].first_symbol_reloc;
352 }
353 fn firstGotReloc(nmi: NavMapIndex, elf: *const Elf) GotReloc.Index {
354 return elf.navs.values()[@backingInt(nmi)].first_got_reloc;
355 }
356 };
357
358 pub const UavMapIndex = enum(u32) {
359 _,
360
361 pub fn uavValue(umi: UavMapIndex, elf: *const Elf) InternPool.Index {
362 return elf.uavs.keys()[@backingInt(umi)];
363 }
364
365 pub fn symbol(umi: UavMapIndex, elf: *const Elf) Symbol.LocalIndex {
366 return elf.uavs.values()[@backingInt(umi)].lsi;
367 }
368
369 fn firstSymbolReloc(umi: UavMapIndex, elf: *const Elf) SymbolReloc.Index {
370 return elf.uavs.values()[@backingInt(umi)].first_symbol_reloc;
371 }
372 fn firstGotReloc(umi: UavMapIndex, elf: *const Elf) GotReloc.Index {
373 _ = umi;
374 _ = elf;
375 return .none;
376 }
377 };
378
379 pub const LazyMapRef = struct {
380 kind: link.File.LazySymbol.Kind,
381 index: u32,
382
383 pub fn Index(comptime kind: link.File.LazySymbol.Kind) type {
384 return enum(u32) {
385 _,
386
387 pub fn ref(lmi: @This()) LazyMapRef {
388 return .{ .kind = kind, .index = @backingInt(lmi) };
389 }
390
391 pub fn lazySymbol(lmi: @This(), elf: *const Elf) link.File.LazySymbol {
392 return lmi.ref().lazySymbol(elf);
393 }
394
395 pub fn symbol(lmi: @This(), elf: *const Elf) Symbol.LocalIndex {
396 return lmi.ref().symbol(elf);
397 }
398
399 fn firstSymbolReloc(lmi: @This(), elf: *const Elf) SymbolReloc.Index {
400 return elf.lazy.getPtrConst(kind).map.values()[@backingInt(lmi)].first_symbol_reloc;
401 }
402 fn firstGotReloc(lmi: @This(), elf: *const Elf) GotReloc.Index {
403 return elf.lazy.getPtrConst(kind).map.values()[@backingInt(lmi)].first_got_reloc;
404 }
405 };
406 }
407
408 pub fn lazySymbol(lmr: LazyMapRef, elf: *const Elf) link.File.LazySymbol {
409 return .{ .kind = lmr.kind, .ty = elf.lazy.getPtrConst(lmr.kind).map.keys()[lmr.index] };
410 }
411
412 pub fn symbol(lmr: LazyMapRef, elf: *const Elf) Symbol.LocalIndex {
413 return elf.lazy.getPtrConst(lmr.kind).map.values()[lmr.index].lsi;
414 }
415 };
416
417 comptime {
418 if (!std.debug.runtime_safety) std.debug.assert(@sizeOf(Node) == 8);
419 }
420
421 /// In this linker implementation, `link.File.AtomId` is a type-erased `MappedFile.Node.Index`.
422 fn toAtom(ni: MappedFile.Node.Index) link.File.AtomId {
423 return @fromBackingInt(@backingInt(ni));
424 }
425 /// In this linker implementation, `link.File.AtomId` is a type-erased `MappedFile.Node.Index`.
426 fn fromAtom(atom: link.File.AtomId) MappedFile.Node.Index {
427 return @fromBackingInt(@backingInt(atom));
428 }
429};
430
431const InputSection = struct {
432 input: Node.InputIndex,
433 file_location: MappedFile.Node.FileLocation,
434 vaddr: u64,
435 /// The node corresponding to this input section.
436 node: MappedFile.Node.Index,
437 /// The start index of the contiguous sequence of symbol relocations in this input section.
438 first_symbol_reloc: SymbolReloc.Index,
439 /// The start index of the contiguous sequence of GOT relocations in this input section.
440 first_got_reloc: GotReloc.Index,
441
442 const Index = enum(u32) {
443 _,
444
445 fn ptr(isi: InputSection.Index, elf: *Elf) *InputSection {
446 return &elf.input_sections.items[@backingInt(isi)];
447 }
448
449 fn ptrConst(isi: InputSection.Index, elf: *const Elf) *const InputSection {
450 return &elf.input_sections.items[@backingInt(isi)];
451 }
452
453 fn input(isi: InputSection.Index, elf: *const Elf) Node.InputIndex {
454 return isi.ptrConst(elf).input;
455 }
456
457 fn fileLocation(isi: InputSection.Index, elf: *const Elf) MappedFile.Node.FileLocation {
458 return isi.ptrConst(elf).file_location;
459 }
460
461 fn node(isi: InputSection.Index, elf: *const Elf) MappedFile.Node.Index {
462 return isi.ptrConst(elf).node;
463 }
464 };
465};
466
467const Archive = struct {
468 ni: MappedFile.Node.Index,
469 header_ni: MappedFile.Node.Index,
470 elf_member_header_ni: MappedFile.Node.Index,
471
472 elf_member_too_big: bool,
473 strtab_member_too_big: bool,
474};
475
476const Section = struct {
477 /// The node corresponding to this section.
478 ni: MappedFile.Node.Index,
479 /// A symbol which is exactly at the start of this section.
480 ///
481 /// When not emitting a relocatable, or for special section types, this is `.null`.
482 lsi: Symbol.LocalIndex,
483 rela: union {
484 /// This field is active if and only if this section is *not* a `SHT_RELA` section.
485 ///
486 /// This field's value refers to this section's corresponding relocation section, if it
487 /// currently has one. If this section does not currently have a relocation section, the
488 /// value is `.UNDEF`.
489 ///
490 /// This field is only ever non-`.UNDEF` when emitting a relocatable (`ET_REL`). While there
491 /// are also output relocations in DSOs, they are all placed in the `.rela.dyn`
492 /// (`elf.shdnx.rela_dyn`) and `.rela.plt` (`elf.shndx.rela_plt`) sections, rather than
493 /// having separate relocation sections for each section.
494 shndx: Section.Index,
495
496 /// This field is active if and only if this section *is* a `SHT_RELA` section.
497 ///
498 /// This is the head of a single-linked list of free `ElfN.Rela` entries in this section.
499 /// Entries in this list have `info.type` set to `R_*_NONE`, have `info.sym` set to 0, and
500 /// have `offset` set to `@enumFromInt(next)` where `next` is `RelaIndex.Optional`. Also,
501 /// `addend` is set to the length of the list starting from this point; so the last node in
502 /// the list has `addend = 1`, the one before it has `addend = 2`, etc. This is so that the
503 /// head node always contains the current length of the list.
504 ///
505 /// It would be okay to store these values (in the `offset` and `addend` fields) in the
506 /// compiler's host endianness, because they will never be read by other tooling. However,
507 /// we nonetheless use target endianness, because using host endianness would introduce an
508 /// unnecessary dependency of the output binary on the compiler's host architecture.
509 free_head: RelaIndex.Optional,
510 },
511
512 const RelaIndex = enum(u32) {
513 none,
514 _,
515
516 const Optional = enum(u32) {
517 none = std.math.maxInt(u32),
518 _,
519
520 fn unwrap(opt: RelaIndex.Optional) ?RelaIndex {
521 return switch (opt) {
522 .none => null,
523 _ => @fromBackingInt(@backingInt(opt)),
524 };
525 }
526 };
527
528 fn toOptional(i: RelaIndex) RelaIndex.Optional {
529 return @fromBackingInt(@backingInt(i));
530 }
531 };
532
533 pub const Index = enum(Tag) {
534 UNDEF = std.elf.SHN_UNDEF,
535 LIVEPATCH = reserve(std.elf.SHN_LIVEPATCH),
536 ABS = reserve(std.elf.SHN_ABS),
537 COMMON = reserve(std.elf.SHN_COMMON),
538
539 symtab = 1,
540 shstrtab,
541 strtab,
542 rodata,
543 text,
544 data,
545 data_rel_ro,
546
547 _,
548
549 pub const Tag = u32;
550
551 pub const LORESERVE: Index = .fromSection(std.elf.SHN_LORESERVE);
552 pub const HIRESERVE: Index = .fromSection(std.elf.SHN_HIRESERVE);
553 comptime {
554 assert(@backingInt(HIRESERVE) == std.math.maxInt(Tag));
555 }
556
557 fn reserve(sec: std.elf.Section) Tag {
558 assert(sec >= std.elf.SHN_LORESERVE and sec <= std.elf.SHN_HIRESERVE);
559 return @as(Tag, std.math.maxInt(Tag) - std.elf.SHN_HIRESERVE) + sec;
560 }
561
562 pub fn fromSection(sec: std.elf.Section) Index {
563 return switch (sec) {
564 std.elf.SHN_UNDEF...std.elf.SHN_LORESERVE - 1 => @fromBackingInt(sec),
565 std.elf.SHN_LORESERVE...std.elf.SHN_HIRESERVE => @fromBackingInt(reserve(sec)),
566 };
567 }
568 pub fn toSection(shndx: Index) ?std.elf.Section {
569 return switch (@backingInt(shndx)) {
570 std.elf.SHN_UNDEF...std.elf.SHN_LORESERVE - 1 => |sec| @intCast(sec),
571 std.elf.SHN_LORESERVE...reserve(std.elf.SHN_LORESERVE) - 1 => null,
572 reserve(std.elf.SHN_LORESERVE)...reserve(std.elf.SHN_HIRESERVE) => |sec| @intCast(
573 sec - reserve(std.elf.SHN_LORESERVE) + std.elf.SHN_LORESERVE,
574 ),
575 };
576 }
577
578 fn get(shndx: Index, elf: *Elf) *Section {
579 return &elf.shdrs.items[@backingInt(shndx) - 1]; // overflow means you tried to get the `.UNDEF` section
580 }
581
582 fn name(shndx: Index, elf: *Elf) String(.shstrtab) {
583 return switch (elf.shdrPtr(shndx)) {
584 inline else => |shdr| @fromBackingInt(elf.targetLoad(&shdr.name)),
585 };
586 }
587
588 fn vaddr(shndx: Index, elf: *Elf) u64 {
589 return switch (elf.shdrPtr(shndx)) {
590 inline else => |shdr| elf.targetLoad(&shdr.addr),
591 };
592 }
593
594 fn size(shndx: Index, elf: *Elf) u64 {
595 return switch (elf.shdrPtr(shndx)) {
596 inline else => |shdr| elf.targetLoad(&shdr.size),
597 };
598 }
599
600 fn setSize(shndx: Index, elf: *Elf, new_size: u64) void {
601 return switch (elf.shdrPtr(shndx)) {
602 inline else => |shdr| {
603 elf.targetStore(&shdr.type, switch (new_size) {
604 0 => .NULL,
605 else => .PROGBITS,
606 });
607 elf.targetStore(&shdr.size, @intCast(new_size));
608 },
609 };
610 }
611
612 fn flags(s: Index, elf: *Elf) std.elf.SHF {
613 return switch (elf.shdrPtr(s)) {
614 inline else => |shdr| elf.targetLoad(&shdr.flags).shf,
615 };
616 }
617
618 fn rename(shndx: Index, elf: *Elf, new_name: []const u8) Error!void {
619 const shstrtab_entry = try elf.string(.shstrtab, new_name);
620 switch (elf.shdrPtr(shndx)) {
621 inline else => |shdr| elf.targetStore(&shdr.name, @backingInt(shstrtab_entry)),
622 }
623 }
624
625 fn ensureAligned(shndx: Index, elf: *Elf, min_align: Alignment) Error!void {
626 switch (elf.shdrPtr(shndx)) {
627 inline else => |shdr| {
628 if (elf.targetLoad(&shdr.addralign) >= min_align.toByteUnits()) {
629 return; // already aligned
630 }
631 elf.targetStore(&shdr.addralign, @intCast(min_align.toByteUnits()));
632 },
633 }
634 const ni = shndx.get(elf).ni;
635 if (min_align.compare(.gt, ni.alignment(&elf.mf))) {
636 try ni.realign(elf.base.comp.gpa, &elf.mf, min_align);
637 }
638 switch (elf.getNode(ni.parent(&elf.mf).unwrap().?)) {
639 .elf => {},
640 .segment => |phndx| try elf.ensureSegmentAligned(phndx, min_align),
641 else => unreachable,
642 }
643 }
644
645 /// Asserts that `rela_shndx` is a `SHT_RELA` section and ensures that its node has enough
646 /// unused space to hold `n` additional `ElfN.Rela` entries.
647 fn relaEnsureAdditionalCapacity(rela_shndx: Index, elf: *Elf, n: usize) Error!void {
648 const node = rela_shndx.get(elf).ni;
649 const need_size: u64 = switch (elf.shdrPtr(rela_shndx)) {
650 inline else => |shdr, class| need_size: {
651 assert(elf.targetLoad(&shdr.type) == .RELA);
652 const cur_size = elf.targetLoad(&shdr.size);
653 const ent_size = @sizeOf(class.ElfN().Rela);
654 assert(elf.targetLoad(&shdr.entsize) == ent_size);
655 const free_len: u32 = free_len: {
656 const opt_free_head = rela_shndx.get(elf).rela.free_head;
657 const free_head = opt_free_head.unwrap() orelse break :free_len 0;
658 const relas: []const class.ElfN().Rela = @ptrCast(@alignCast(
659 node.slice(&elf.mf)[0..@intCast(cur_size)],
660 ));
661 const free_len = elf.targetLoad(&relas[@backingInt(free_head)].addend);
662 assert(free_len > 0);
663 break :free_len @intCast(free_len);
664 };
665 const need_additional = n -| free_len;
666 break :need_size cur_size + need_additional * ent_size;
667 },
668 };
669 try node.ensureMinimumSize(elf.base.comp.gpa, &elf.mf, need_size);
670 }
671
672 /// Asserts that `rela_shndx` is a `SHT_RELA` section and deletes the `ElfN.Rela` entry at
673 /// the given `index` in it. The entry is added to the free-list for reuse later. Asserts
674 /// that the relocation entry at `index` is not already free.
675 fn relaDeleteOne(rela_shndx: Index, elf: *Elf, index: RelaIndex) void {
676 switch (elf.shdrPtr(rela_shndx)) {
677 inline else => |shdr, class| {
678 assert(elf.targetLoad(&shdr.type) == .RELA);
679 assert(elf.targetLoad(&shdr.entsize) == @sizeOf(class.ElfN().Rela));
680 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
681 rela_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(elf.targetLoad(&shdr.size))],
682 ));
683 const opt_free_head = rela_shndx.get(elf).rela.free_head;
684 const old_free_len: u32 = free_len: {
685 const free_head = opt_free_head.unwrap() orelse break :free_len 0;
686 const free_len = elf.targetLoad(&relas[@backingInt(free_head)].addend);
687 assert(free_len > 0);
688 break :free_len @intCast(free_len);
689 };
690 const none_reloc_type = MachineRelocType.none(elf).unwrap(elf);
691 {
692 const old_type = elf.targetLoad(&relas[@backingInt(index)].info).type;
693 assert(old_type != none_reloc_type); // bug: `index` is already in the free-list
694 }
695 relas[@backingInt(index)] = .{
696 .offset = @backingInt(opt_free_head), // next
697 .info = .{
698 .type = @intCast(none_reloc_type),
699 .sym = 0,
700 },
701 .addend = @intCast(old_free_len + 1), // list length
702 };
703 if (elf.targetEndian() != std.lang.Endian.native) {
704 std.mem.byteSwapAllFields(class.ElfN().Rela, &relas[@backingInt(index)]);
705 }
706 },
707 }
708 rela_shndx.get(elf).rela.free_head = index.toOptional();
709 }
710
711 /// Asserts that `rela_shndx` is a `SHT_RELA` section and adds a new `ElfN.Rela` entry to it
712 /// with the given field values. Returns the index of the populated entry. Asserts that
713 /// capacity for this operation was already guaranteed using `relaEnsureAdditionalCapacity`.
714 fn relaAddOneAssumeCapacity(rela_shndx: Index, elf: *Elf, opts: struct {
715 type: MachineRelocType,
716 offset: u64,
717 /// This is a raw `u32` because whether this is an index into `.symtab` (`Symbol.Index`)
718 /// or an index into `.dynsym` is contextual.
719 raw_sym_index: u32,
720 addend: i64,
721 }) RelaIndex {
722 switch (elf.shdrPtr(rela_shndx)) {
723 inline else => |shdr, class| {
724 assert(elf.targetLoad(&shdr.type) == .RELA);
725 const ent_size = @sizeOf(class.ElfN().Rela);
726 assert(elf.targetLoad(&shdr.entsize) == ent_size);
727 const new_index: RelaIndex = if (rela_shndx.get(elf).rela.free_head.unwrap()) |free_head| new_index: {
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 next: RelaIndex.Optional = @fromBackingInt(@intCast(elf.targetLoad(
732 &relas[@backingInt(free_head)].offset,
733 )));
734 rela_shndx.get(elf).rela.free_head = next;
735
736 const old_free_len: u32 = @intCast(
737 elf.targetLoad(&relas[@backingInt(free_head)].addend),
738 );
739 const new_free_len: u32 = if (next.unwrap()) |i| @intCast(
740 elf.targetLoad(&relas[@backingInt(i)].addend),
741 ) else 0;
742 assert(new_free_len == old_free_len - 1);
743
744 break :new_index free_head;
745 } else new_index: {
746 const old_size = elf.targetLoad(&shdr.size);
747 const new_size = old_size + ent_size;
748 elf.targetStore(&shdr.size, new_size);
749 break :new_index @fromBackingInt(@intCast(@divExact(old_size, ent_size)));
750 };
751 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
752 rela_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(elf.targetLoad(&shdr.size))],
753 ));
754 relas[@backingInt(new_index)] = .{
755 .offset = @intCast(opts.offset),
756 .info = .{
757 .type = @intCast(opts.type.unwrap(elf)),
758 .sym = @intCast(opts.raw_sym_index),
759 },
760 .addend = @intCast(opts.addend),
761 };
762 if (elf.targetEndian() != std.lang.Endian.native) {
763 std.mem.byteSwapAllFields(class.ElfN().Rela, &relas[@backingInt(new_index)]);
764 }
765 return new_index;
766 },
767 }
768 }
769
770 /// Asserts that `rela_shndx` is a `SHT_RELA` section and updates the `info.sym` field of
771 /// the `ElfN.Rela` entry at the given index. As with `relaAddOneAssumeCapacity`, the symbol
772 /// index is a raw `u32`, because it may be an index into `.symtab` or an index into
773 /// `.dynsym`. Asserts that `index` is not in the free-list (i.e. is not deleted).
774 fn relaUpdateSym(rela_shndx: Index, elf: *Elf, index: RelaIndex, raw_sym_index: u32) void {
775 switch (elf.shdrPtr(rela_shndx)) {
776 inline else => |shdr, class| {
777 assert(elf.targetLoad(&shdr.type) == .RELA);
778 assert(elf.targetLoad(&shdr.entsize) == @sizeOf(class.ElfN().Rela));
779 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
780 rela_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(elf.targetLoad(&shdr.size))],
781 ));
782 const rela_info = elf.targetLoad(&relas[@backingInt(index)].info);
783 {
784 const none_reloc_type = MachineRelocType.none(elf).unwrap(elf);
785 assert(rela_info.type != none_reloc_type); // bug: `index` is in the free-list
786 }
787 elf.targetStore(&relas[@backingInt(index)].info, .{
788 .type = rela_info.type,
789 .sym = @intCast(raw_sym_index),
790 });
791 },
792 }
793 }
794
795 /// Asserts that `rela_shndx` is a `SHT_RELA` section and updates the `offset` field of the
796 /// `ElfN.Rela` entry at the given index. Asserts that `index` is not in the free-list (i.e.
797 /// it is not deleted).
798 fn relaSetOffset(rela_shndx: Index, elf: *Elf, index: RelaIndex, new_offset: u64) void {
799 switch (elf.shdrPtr(rela_shndx)) {
800 inline else => |shdr, class| {
801 assert(elf.targetLoad(&shdr.type) == .RELA);
802 assert(elf.targetLoad(&shdr.entsize) == @sizeOf(class.ElfN().Rela));
803 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
804 rela_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(elf.targetLoad(&shdr.size))],
805 ));
806 {
807 const rela_info = elf.targetLoad(&relas[@backingInt(index)].info);
808 const none_reloc_type = MachineRelocType.none(elf).unwrap(elf);
809 assert(rela_info.type != none_reloc_type); // bug: `index` is in the free-list
810 }
811 elf.targetStore(&relas[@backingInt(index)].offset, @intCast(new_offset));
812 },
813 }
814 }
815
816 /// Asserts that `rela_shndx` is a `SHT_RELA` section and updates the `offset` field of the
817 /// `ElfN.Rela` entry at the given index, by subtracting `old_base` and adding `new_base`.
818 /// Asserts that `index` is not in the free-list (i.e. it is not deleted).
819 fn relaAdjustOffset(rela_shndx: Index, elf: *Elf, index: RelaIndex, old_base: u64, new_base: u64) void {
820 switch (elf.shdrPtr(rela_shndx)) {
821 inline else => |shdr, class| {
822 assert(elf.targetLoad(&shdr.type) == .RELA);
823 assert(elf.targetLoad(&shdr.entsize) == @sizeOf(class.ElfN().Rela));
824 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
825 rela_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(elf.targetLoad(&shdr.size))],
826 ));
827 {
828 const rela_info = elf.targetLoad(&relas[@backingInt(index)].info);
829 const none_reloc_type = MachineRelocType.none(elf).unwrap(elf);
830 assert(rela_info.type != none_reloc_type); // bug: `index` is in the free-list
831 }
832 const old_offset = elf.targetLoad(&relas[@backingInt(index)].offset);
833 elf.targetStore(&relas[@backingInt(index)].offset, @intCast(
834 old_offset - old_base + new_base,
835 ));
836 },
837 }
838 }
839
840 /// Asserts that `rela_shndx` is a `SHT_RELA` section and updates the `addend` field of the
841 /// `ElfN.Rela` entry at the given index. Asserts that `index` is not in the free-list (i.e.
842 /// it is not deleted).
843 fn relaSetAddend(rela_shndx: Index, elf: *Elf, index: RelaIndex, new_addend: u64) void {
844 switch (elf.shdrPtr(rela_shndx)) {
845 inline else => |shdr, class| {
846 assert(elf.targetLoad(&shdr.type) == .RELA);
847 assert(elf.targetLoad(&shdr.entsize) == @sizeOf(class.ElfN().Rela));
848 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
849 rela_shndx.get(elf).ni.slice(&elf.mf)[0..@intCast(elf.targetLoad(&shdr.size))],
850 ));
851 {
852 const rela_info = elf.targetLoad(&relas[@backingInt(index)].info);
853 const none_reloc_type = MachineRelocType.none(elf).unwrap(elf);
854 assert(rela_info.type != none_reloc_type); // bug: `index` is in the free-list
855 }
856 const unsigned: class.ElfN().Addr = @intCast(new_addend);
857 elf.targetStore(&relas[@backingInt(index)].addend, @bitCast(unsigned));
858 },
859 }
860 }
861
862 fn debugFrameFormat(shndx: Index, elf: *Elf) ?Dwarf.Frame.Format {
863 if (shndx == elf.shndx.eh_frame) return .eh_frame;
864 if (shndx == elf.shndx.debug_frame) return .debug_frame;
865 return null;
866 }
867 };
868};
869fn debugFrameFooterSize(elf: *Elf, frame_format: Dwarf.Frame.Format) usize {
870 return switch (frame_format) {
871 .eh_frame => switch (elf.ehdrType()) {
872 .REL => 0,
873 .EXEC, .DYN => 4,
874 },
875 .debug_frame => 0,
876 };
877}
878
879const dwarf_relocs = struct {
880 const Shared = struct {
881 first_target_reloc: NodeReloc.Index,
882 };
883 const Unit = struct {
884 frame_cie_first_target_reloc: NodeReloc.Index,
885 debug_info_header_first_target_reloc: NodeReloc.Index,
886 debug_info_header_first_node_reloc: NodeReloc.Index,
887 debug_line_header_first_target_reloc: NodeReloc.Index,
888 debug_line_header_first_node_reloc: NodeReloc.Index,
889 debug_rnglists_first_target_reloc: NodeReloc.Index,
890 debug_rnglists_symbol_relocs: std.array_hash_map.Auto(SymbolReloc.Index, void),
891 };
892 const Const = struct {
893 debug_info_first_target_reloc: NodeReloc.Index,
894 debug_info_first_symbol_reloc: SymbolReloc.Index,
895 debug_info_first_node_reloc: NodeReloc.Index,
896 };
897 const Global = struct {
898 debug_info_first_target_reloc: NodeReloc.Index,
899 debug_info_first_symbol_reloc: SymbolReloc.Index,
900 debug_info_first_node_reloc: NodeReloc.Index,
901 };
902 const Func = struct {
903 frame_fde_first_symbol_reloc: SymbolReloc.Index,
904 frame_fde_first_node_reloc: NodeReloc.Index,
905 debug_info_first_target_reloc: NodeReloc.Index,
906 debug_info_first_symbol_reloc: SymbolReloc.Index,
907 debug_info_first_node_reloc: NodeReloc.Index,
908 debug_line_first_symbol_reloc: SymbolReloc.Index,
909 debug_line_first_node_reloc: NodeReloc.Index,
910 };
911 const Decl = struct {
912 debug_info_first_target_reloc: NodeReloc.Index,
913 debug_info_first_node_reloc: NodeReloc.Index,
914 };
915};
916
917pub const MachineRelocType = union {
918 AARCH64: std.elf.R_AARCH64,
919 LARCH: std.elf.R_LARCH,
920 PPC64: std.elf.R_PPC64,
921 RISCV: std.elf.R_RISCV,
922 SPARC: std.elf.R_SPARC,
923 X86_64: std.elf.R_X86_64,
924
925 pub const Format = struct {
926 rt: MachineRelocType,
927 elf: *const Elf,
928
929 pub fn format(f: Format, w: *Io.Writer) Io.Writer.Error!void {
930 switch (f.elf.ehdrMachine()) {
931 .AARCH64 => try w.print("R_AARCH64_{t}", .{f.rt.AARCH64}),
932 .LOONGARCH => try w.print("R_LARCH_{t}", .{f.rt.LARCH}),
933 .PPC64 => try w.print("R_PPC64_{t}", .{f.rt.PPC64}),
934 .RISCV => try w.print("R_RISCV_{t}", .{f.rt.RISCV}),
935 .SPARCV9 => try w.print("R_SPARC_{t}", .{f.rt.SPARC}),
936 .X86_64 => try w.print("R_X86_64_{t}", .{f.rt.X86_64}),
937 }
938 }
939 };
940
941 pub fn fmt(rt: MachineRelocType, elf: *const Elf) Format {
942 return .{ .rt = rt, .elf = elf };
943 }
944
945 pub fn none(elf: *const Elf) MachineRelocType {
946 return switch (elf.ehdrMachine()) {
947 .AARCH64 => .{ .AARCH64 = .NONE },
948 .LOONGARCH => .{ .LARCH = .NONE },
949 .PPC64 => .{ .PPC64 = .NONE },
950 .RISCV => .{ .RISCV = .NONE },
951 .SPARCV9 => .{ .SPARC = .NONE },
952 .X86_64 => .{ .X86_64 = .NONE },
953 };
954 }
955 pub fn copy(elf: *const Elf) MachineRelocType {
956 return switch (elf.ehdrMachine()) {
957 .AARCH64 => .{ .AARCH64 = .COPY },
958 .LOONGARCH => .{ .LARCH = .COPY },
959 .PPC64 => .{ .PPC64 = .COPY },
960 .RISCV => .{ .RISCV = .COPY },
961 .SPARCV9 => .{ .SPARC = .COPY },
962 .X86_64 => .{ .X86_64 = .COPY },
963 };
964 }
965 pub fn relative(elf: *const Elf) MachineRelocType {
966 return switch (elf.ehdrMachine()) {
967 .AARCH64 => .{ .AARCH64 = .RELATIVE },
968 .LOONGARCH => .{ .LARCH = .RELATIVE },
969 .PPC64 => .{ .PPC64 = .RELATIVE },
970 .RISCV => .{ .RISCV = .RELATIVE },
971 .SPARCV9 => .{ .SPARC = .RELATIVE },
972 .X86_64 => .{ .X86_64 = .RELATIVE },
973 };
974 }
975 pub fn jumpSlot(elf: *const Elf) MachineRelocType {
976 return switch (elf.ehdrMachine()) {
977 .AARCH64 => .{ .AARCH64 = .JUMP_SLOT },
978 .LOONGARCH => .{ .LARCH = .JUMP_SLOT },
979 .PPC64 => .{ .PPC64 = .JMP_SLOT },
980 .RISCV => .{ .RISCV = .JUMP_SLOT },
981 .SPARCV9 => .{ .SPARC = .JMP_SLOT },
982 .X86_64 => .{ .X86_64 = .JUMP_SLOT },
983 };
984 }
985 pub fn globDat(elf: *const Elf) MachineRelocType {
986 return switch (elf.ehdrMachine()) {
987 .AARCH64 => .{ .AARCH64 = .GLOB_DAT },
988 .LOONGARCH => .{ .LARCH = switch (elf.identClass()) {
989 .NONE, _ => unreachable,
990 .@"32" => .@"32",
991 .@"64" => .@"64",
992 } },
993 .PPC64 => .{ .PPC64 = .GLOB_DAT },
994 .RISCV => .{ .RISCV = switch (elf.identClass()) {
995 .NONE, _ => unreachable,
996 .@"32" => .@"32",
997 .@"64" => .@"64",
998 } },
999 .SPARCV9 => .{ .SPARC = .GLOB_DAT },
1000 .X86_64 => .{ .X86_64 = .GLOB_DAT },
1001 };
1002 }
1003 pub fn dtpMod(elf: *const Elf) MachineRelocType {
1004 return switch (elf.ehdrMachine()) {
1005 .AARCH64 => .{ .AARCH64 = switch (elf.identClass()) {
1006 .NONE, _ => unreachable,
1007 .@"32" => .P32_TLS_DTPMOD,
1008 .@"64" => .TLS_DTPMOD,
1009 } },
1010 .LOONGARCH => .{ .LARCH = switch (elf.identClass()) {
1011 .NONE, _ => unreachable,
1012 .@"32" => .TLS_DTPMOD32,
1013 .@"64" => .TLS_DTPMOD64,
1014 } },
1015 .PPC64 => .{ .PPC64 = .DTPMOD64 },
1016 .RISCV => .{ .RISCV = switch (elf.identClass()) {
1017 .NONE, _ => unreachable,
1018 .@"32" => .TLS_DTPMOD32,
1019 .@"64" => .TLS_DTPMOD64,
1020 } },
1021 .SPARCV9 => .{ .SPARC = switch (elf.identClass()) {
1022 .NONE, _ => unreachable,
1023 .@"32" => .TLS_DTPMOD32,
1024 .@"64" => .TLS_DTPMOD64,
1025 } },
1026 .X86_64 => .{ .X86_64 = .DTPMOD64 },
1027 };
1028 }
1029 pub fn dtpOff(elf: *const Elf) MachineRelocType {
1030 return switch (elf.ehdrMachine()) {
1031 .AARCH64 => .{ .AARCH64 = switch (elf.identClass()) {
1032 .NONE, _ => unreachable,
1033 .@"32" => .P32_TLS_DTPREL,
1034 .@"64" => .TLS_DTPREL,
1035 } },
1036 .LOONGARCH => .{ .LARCH = switch (elf.identClass()) {
1037 .NONE, _ => unreachable,
1038 .@"32" => .TLS_DTPREL32,
1039 .@"64" => .TLS_DTPREL64,
1040 } },
1041 .PPC64 => .{ .PPC64 = .DTPREL64 },
1042 .RISCV => .{ .RISCV = switch (elf.identClass()) {
1043 .NONE, _ => unreachable,
1044 .@"32" => .TLS_DTPREL32,
1045 .@"64" => .TLS_DTPREL64,
1046 } },
1047 .SPARCV9 => .{ .SPARC = switch (elf.identClass()) {
1048 .NONE, _ => unreachable,
1049 .@"32" => .TLS_DTPOFF32,
1050 .@"64" => .TLS_DTPOFF64,
1051 } },
1052 .X86_64 => .{ .X86_64 = .DTPOFF64 },
1053 };
1054 }
1055 pub fn tpOff(elf: *const Elf) MachineRelocType {
1056 return switch (elf.ehdrMachine()) {
1057 .AARCH64 => .{ .AARCH64 = switch (elf.identClass()) {
1058 .NONE, _ => unreachable,
1059 .@"32" => .P32_TLS_TPREL,
1060 .@"64" => .TLS_TPREL,
1061 } },
1062 .LOONGARCH => .{ .LARCH = switch (elf.identClass()) {
1063 .NONE, _ => unreachable,
1064 .@"32" => .TLS_TPREL32,
1065 .@"64" => .TLS_TPREL64,
1066 } },
1067 .PPC64 => .{ .PPC64 = .TPREL64 },
1068 .RISCV => .{ .RISCV = switch (elf.identClass()) {
1069 .NONE, _ => unreachable,
1070 .@"32" => .TLS_TPREL32,
1071 .@"64" => .TLS_TPREL64,
1072 } },
1073 .SPARCV9 => .{ .SPARC = switch (elf.identClass()) {
1074 .NONE, _ => unreachable,
1075 .@"32" => .TLS_TPOFF32,
1076 .@"64" => .TLS_TPOFF64,
1077 } },
1078 .X86_64 => .{ .X86_64 = .TPOFF64 },
1079 };
1080 }
1081 pub fn absAddr(elf: *const Elf) MachineRelocType {
1082 return switch (elf.identClass()) {
1083 .NONE, _ => unreachable,
1084 .@"32" => .abs32(elf),
1085 .@"64" => .abs64(elf),
1086 };
1087 }
1088 pub fn abs32(elf: *const Elf) MachineRelocType {
1089 return switch (elf.ehdrMachine()) {
1090 .AARCH64 => .{ .AARCH64 = .P32_ABS32 },
1091 .LOONGARCH => .{ .LARCH = .@"32" },
1092 .PPC64 => .{ .PPC64 = .ADDR32 },
1093 .RISCV => .{ .RISCV = .@"32" },
1094 .SPARCV9 => .{ .SPARC = .@"32" },
1095 .X86_64 => .{ .X86_64 = .@"32" },
1096 };
1097 }
1098 pub fn abs64(elf: *const Elf) MachineRelocType {
1099 return switch (elf.ehdrMachine()) {
1100 .AARCH64 => .{ .AARCH64 = .ABS64 },
1101 .LOONGARCH => .{ .LARCH = .@"64" },
1102 .PPC64 => .{ .PPC64 = .ADDR64 },
1103 .RISCV => .{ .RISCV = .@"64" },
1104 .SPARCV9 => .{ .SPARC = .@"64" },
1105 .X86_64 => .{ .X86_64 = .@"64" },
1106 };
1107 }
1108 pub fn rel32(elf: *const Elf) MachineRelocType {
1109 return switch (elf.ehdrMachine()) {
1110 .AARCH64 => .{ .AARCH64 = .PREL32 },
1111 .LOONGARCH => .{ .LARCH = .@"32_PCREL" },
1112 .PPC64 => .{ .PPC64 = .REL32 },
1113 .RISCV => .{ .RISCV = .@"32_PCREL" },
1114 .SPARCV9 => .{ .SPARC = .DISP32 },
1115 .X86_64 => .{ .X86_64 = .PC32 },
1116 };
1117 }
1118 pub fn rel64(elf: *const Elf) MachineRelocType {
1119 return switch (elf.ehdrMachine()) {
1120 .AARCH64 => .{ .AARCH64 = .PREL64 },
1121 .LOONGARCH => unreachable,
1122 .PPC64 => .{ .PPC64 = .REL64 },
1123 .RISCV => unreachable,
1124 .SPARCV9 => .{ .SPARC = .DISP64 },
1125 .X86_64 => .{ .X86_64 = .PC64 },
1126 };
1127 }
1128 pub fn size32(elf: *const Elf) ?MachineRelocType {
1129 return switch (elf.ehdrMachine()) {
1130 .AARCH64,
1131 .LOONGARCH,
1132 .PPC64,
1133 .RISCV,
1134 => null,
1135
1136 .SPARCV9 => .{ .SPARC = .SIZE32 },
1137 .X86_64 => .{ .X86_64 = .SIZE32 },
1138 };
1139 }
1140 pub fn size64(elf: *const Elf) ?MachineRelocType {
1141 return switch (elf.ehdrMachine()) {
1142 .AARCH64,
1143 .LOONGARCH,
1144 .PPC64,
1145 .RISCV,
1146 => null,
1147
1148 .SPARCV9 => .{ .SPARC = .SIZE64 },
1149 .X86_64 => .{ .X86_64 = .SIZE64 },
1150 };
1151 }
1152
1153 pub fn wrap(int: u32, elf: *const Elf) MachineRelocType {
1154 return switch (elf.ehdrMachine()) {
1155 .AARCH64 => .{ .AARCH64 = @fromBackingInt(int) },
1156 .LOONGARCH => .{ .LARCH = @fromBackingInt(int) },
1157 .PPC64 => .{ .PPC64 = @fromBackingInt(int) },
1158 .RISCV => .{ .RISCV = @fromBackingInt(int) },
1159 .SPARCV9 => .{ .SPARC = @fromBackingInt(int) },
1160 .X86_64 => .{ .X86_64 = @fromBackingInt(int) },
1161 };
1162 }
1163 pub fn unwrap(rt: MachineRelocType, elf: *const Elf) u32 {
1164 return switch (elf.ehdrMachine()) {
1165 .AARCH64 => @backingInt(rt.AARCH64),
1166 .LOONGARCH => @backingInt(rt.LARCH),
1167 .PPC64 => @backingInt(rt.PPC64),
1168 .RISCV => @backingInt(rt.RISCV),
1169 .SPARCV9 => @backingInt(rt.SPARC),
1170 .X86_64 => @backingInt(rt.X86_64),
1171 };
1172 }
1173};
1174
1175/// A relocation targeting an arbitrary symbol with a fixed addend.
1176const SymbolReloc = struct {
1177 /// The node containing this relocation. Possible values are:
1178 /// * An input section
1179 /// * A section
1180 /// * A NAV, UAV, or lazy code/data
1181 /// * `.none`, if this relocation was deleted (in which case it should be ignored)
1182 node: MappedFile.Node.Index.Optional,
1183 /// The offset of the relocation inside of `node`.
1184 offset: u64,
1185 /// A symbol used to compute the relocated value. Precise meaning depends on `@"type"`.
1186 target: Symbol.Id,
1187 /// A signed constant used to compute the relocated value. Precise meaning depends on `@"type"`.
1188 addend: i64,
1189 /// Specifies how to apply the relocation.
1190 ///
1191 /// When emitting a relocatable, this field is `undefined`.
1192 type: SymbolReloc.Type,
1193 /// Forms a linked list of all symbol relocations with the same `target`. This list exists so
1194 /// that all relocations targeting a particular symbol can be re-applied if that symbol moves.
1195 /// Doubly-linked so that relocations can be removed.
1196 next: SymbolReloc.Index,
1197 /// Back-reference in a doubly-linked list---see `next`.
1198 prev: SymbolReloc.Index,
1199 /// If this relocation has a corresponding output relocation, this is its index within the
1200 /// appropriate SHT_RELA section (see `relaSection`). If there is no output relocation
1201 /// corresponding to this relocation, this is `.none`.
1202 ///
1203 /// If we are producing a relocatable, this field is always populated, because all relocations
1204 /// are emitted as output relocations.
1205 ///
1206 /// If we are producing a DSO, this field is populated if this relocation requires a runtime
1207 /// relocation entry. The entry will be removed if we discover a definition which allows us to
1208 /// statically resolve the relocation.
1209 rela_index: Section.RelaIndex.Optional,
1210 result: enum(u8) { ok, overflowed, misaligned },
1211
1212 /// Determines the section in which this relocation will be placed if it is outstanding.
1213 ///
1214 /// When producing a relocatable (ET_REL), the relocation section is `Section.rela.shndx` for
1215 /// the section of `node`, and this function asserts that the aforementioned `rela.shndx` field
1216 /// is populated.
1217 ///
1218 /// When producing a DSO, the relocation section is always `.rela.dyn`. It is not `.rela.plt`
1219 /// because relocations in the GOTPLT are handled specially, without `SymbolReloc` entries.
1220 fn relaSection(sr: *const SymbolReloc, elf: *Elf) Section.Index {
1221 const shndx = switch (elf.ehdrType()) {
1222 .REL => elf.getNodeShndx(sr.node.unwrap().?).get(elf).rela.shndx,
1223 .EXEC, .DYN => elf.shndx.rela_dyn,
1224 };
1225 assert(shndx != .UNDEF);
1226 return shndx;
1227 }
1228
1229 /// Instead of using the ELF relocation enums, we have our own internal representation for
1230 /// relocation types. This representation is more compact (requiring only 16 bits), and allows
1231 /// sharing a lot of relocation handling between multiple relocs and target architectures.
1232 ///
1233 /// A relocation type can be "simple" or "special".
1234 ///
1235 /// "Simple" relocations are designed to cover the majority of cases. They can represent most
1236 /// relocations which either write 8-bit, 16-bit, 32-bit, or 64-bit integers, or which write one
1237 /// contiguous bit-field within such an integer (e.g. an instruction operand). For more details,
1238 /// see `Simple`.
1239 ///
1240 /// "Special" relocations handle anything which does not fit into the above category, such as
1241 /// relocations which write multiple sequences of bits or which need to do unusual arithmetic on
1242 /// a symbol value. The representation is simply a big enum containing all of these exceptional
1243 /// cases---see `Special`. This representation is in use when `Type.target == .special`.
1244 const Type = packed struct(u16) {
1245 /// Helper function for constructing a "simple" relocation type. This mainly exists to
1246 /// improve readability in the relocation lowering logic in `addRelocAssumeCapacity`.
1247 fn simple(target: Target, action: Simple) SymbolReloc.Type {
1248 assert(target != .special);
1249 return .{ .target = target, .action = .{ .simple = action } };
1250 }
1251
1252 /// Helper function for constructing a "special" relocation type. This mainly exists to
1253 /// improve readability in the relocation lowering logic in `addRelocAssumeCapacity`.
1254 fn special(s: Special) SymbolReloc.Type {
1255 return .{ .target = .special, .action = .{ .special = s } };
1256 }
1257
1258 /// See doc comment on `Target`.
1259 target: Target,
1260 /// If `target == .special`, the `special` field is used.
1261 ///
1262 /// Otherwise, the `.simple` field is used.
1263 action: packed union {
1264 simple: Simple,
1265 special: Special,
1266 },
1267
1268 /// If a relocation is "special", indicates that using the value `.@"special"`.
1269 ///
1270 /// Otherwise (for "simple" relocations), `Target` indicates the first step in computing the
1271 /// relocation---whether we care about the target symbol's absolute address, its PC-relative
1272 /// address, its PLT entry, etc.
1273 const Target = enum(u3) {
1274 /// This is a "special" relocation whose specific type is in the `action.special` field.
1275 special,
1276
1277 /// Absolute value of the target symbol.
1278 abs,
1279 /// Offset from the relocation itself to the target symbol ("PC-relative").
1280 rel,
1281 /// Address of the target symbol's PLT entry.
1282 ///
1283 /// If the target symbol does not have a PLT entry, equivalent to `.abs`.
1284 pltabs,
1285 /// Offset from the relocation itself to the target symbol's PLT entry ("PC-relative").
1286 ///
1287 /// If the target symbol does not have a PLT entry, equivalent to `.rel`.
1288 pltrel,
1289 /// Offset of the target TLS symbol from the base of this DSO's own TLS region.
1290 dtpoff,
1291 /// Offset of the target TLS symbol from the raw thread pointer.
1292 tpoff,
1293 /// Size of the target symbol.
1294 size,
1295 };
1296
1297 /// For a "simple" relocation, after the initial value is computed according to `Target`, a
1298 /// `Simple` value communicates how to shift, truncate, and store that value into memory.
1299 const Simple = packed struct(u13) {
1300 /// The field being written to, represented as a sequence of bits in a backing integer
1301 /// of 8, 16, 32, or 64 bits.
1302 ///
1303 /// The `.@"8"`, `.@"16"`, `.@"32"`, and `.@"64"` fields simply write to all bits of the
1304 /// backing integer; i.e. the existing value is entirely overwritten.
1305 ///
1306 /// Other fields are named like "B[H:L]", where "B" is the backing integer type, and
1307 /// "H" and "L" are the indices of the highest and lowest bits in the bit field (in
1308 /// other words, an inclusive bit range). This notation was chosen because it seems to
1309 /// be one of the more common ways that bit relocations are written in ABIs.
1310 ///
1311 /// e.g. 8[6:3] writes the relocated value to this 4-bit field in an 8-bit integer:
1312 ///
1313 /// MSB ___ ### ### ### ### ___ ___ ___ LSB
1314 /// 7 6 5 4 3 2 1 0
1315 /// bit index
1316 ///
1317 /// This enum is not intended to be able to represent every possible bit field in the
1318 /// backing integer types. Instead, to keep `SymbolReloc.Type` compact, fields are added
1319 /// to this enum only as needed. If the enum ever becomes full, some lesser-used tags
1320 /// can have their handling moved into `Special` to free up space.
1321 dest: enum(u6) {
1322 @"8",
1323 @"16",
1324 @"32",
1325 @"64",
1326
1327 @"32[4:0]",
1328 @"32[5:0]",
1329 @"32[6:0]",
1330 @"32[9:0]",
1331 @"32[10:0]",
1332 @"32[11:0]",
1333 @"32[12:0]",
1334 @"32[21:0]",
1335 @"32[21:10]",
1336 @"32[24:5]",
1337 @"32[25:10]",
1338 @"32[29:0]",
1339
1340 /// Returns `true` iff `dest` writes a full address for the target.
1341 ///
1342 /// i.e. checks for `.@"32"` on 32-bit targets; for `.@"64"` on 64-bit targets.
1343 fn isAddr(dest: @This(), elf: *const Elf) bool {
1344 return switch (elf.identClass()) {
1345 .NONE, _ => unreachable,
1346 .@"32" => dest == .@"32",
1347 .@"64" => dest == .@"64",
1348 };
1349 }
1350 },
1351
1352 /// After the relocation value is shifted (see `shift`), it is truncated to the size of
1353 /// the bit field (see `dest`). This field specifies whether the linker will check for,
1354 /// and error in the case of, truncated bits (in other words, relocation overflow).
1355 cast: enum(u2) {
1356 /// Do not perform any check when truncating unused bits.
1357 trunc,
1358 /// Error if the truncated value cannot be zero-extended back to the original value,
1359 /// i.e. if the truncated value is different when interpreted as unsigned.
1360 unsigned,
1361 /// Error if the truncated value cannot be sign-extended back to the original value.
1362 /// i.e. if the truncated value is different when interpreted as signed.
1363 signed,
1364 },
1365
1366 /// The relocation value (computed based on the `Target`) gets shifted to the right by
1367 /// this amount. By default, the shifted-out bits can be anything, but tags ending in
1368 /// "_exact" introduce a check that the shifted-out bits are all zeroes (an error is
1369 /// emitted if not), similar to the behavior of `@shrExact`.
1370 shift: enum(u5) {
1371 @"0",
1372 @"2_exact",
1373 @"10",
1374 @"12",
1375 @"22",
1376 @"32",
1377 @"52",
1378 },
1379
1380 /// Given a value (computed based on the `Target`), applies the shift and truncation
1381 /// operations specified by `s`, then writes the result to the start of `dest_slice` as
1382 /// specified by `s.dest`.
1383 fn write(
1384 s: Simple,
1385 val: u64,
1386 dest_slice: []u8,
1387 target_endian: std.lang.Endian,
1388 ) error{ RelocationMisaligned, RelocationOverflow }!void {
1389 const shift: u6, const shift_exact: bool = switch (s.shift) {
1390 .@"0" => .{ 0, false },
1391 .@"2_exact" => .{ 2, true },
1392 .@"10" => .{ 10, false },
1393 .@"12" => .{ 12, false },
1394 .@"22" => .{ 22, false },
1395 .@"32" => .{ 32, false },
1396 .@"52" => .{ 52, false },
1397 };
1398
1399 if (shift_exact and (val >> shift) << shift != val) {
1400 return error.RelocationMisaligned;
1401 }
1402
1403 const dest_word_bits: u8, const dest_high_bit: u6, const dest_low_bit: u6 = switch (s.dest) {
1404 // zig fmt: off
1405 .@"8" => .{ 8, 7, 0 },
1406 .@"16" => .{ 16, 15, 0 },
1407 .@"32" => .{ 32, 31, 0 },
1408 .@"64" => .{ 64, 63, 0 },
1409 .@"32[4:0]" => .{ 32, 4, 0 },
1410 .@"32[5:0]" => .{ 32, 5, 0 },
1411 .@"32[6:0]" => .{ 32, 6, 0 },
1412 .@"32[9:0]" => .{ 32, 9, 0 },
1413 .@"32[10:0]" => .{ 32, 10, 0 },
1414 .@"32[11:0]" => .{ 32, 11, 0 },
1415 .@"32[12:0]" => .{ 32, 12, 0 },
1416 .@"32[21:0]" => .{ 32, 21, 0 },
1417 .@"32[21:10]" => .{ 32, 21, 10 },
1418 .@"32[24:5]" => .{ 32, 24, 5 },
1419 .@"32[25:10]" => .{ 32, 25, 10 },
1420 .@"32[29:0]" => .{ 32, 29, 0 },
1421 // zig fmt: on
1422 };
1423
1424 // The number of bits we are truncating from the full 64-bit relocation value.
1425 const trunc_bits: u6 = 63 - dest_high_bit + dest_low_bit;
1426
1427 // When we shift, whether we do an arithmetic or logical shift depends on what cast
1428 // behavior we are going to use. If we'll be doing a signed int cast, we must shift
1429 // in sign bits so that we don't incorrectly cause a failure, and vice versa for an
1430 // unsigned int cast. Either is fine when truncating (here we pick logical shift).
1431 const shifted_val: u64 = switch (s.cast) {
1432 .trunc => val >> shift,
1433 inline else => |cast| shifted: {
1434 const ShiftInt = if (cast == .signed) i64 else u64;
1435 const x: ShiftInt = @bitCast(val);
1436 const shifted: ShiftInt = x >> shift;
1437
1438 if ((shifted << trunc_bits) >> trunc_bits != shifted) {
1439 return error.RelocationOverflow;
1440 }
1441
1442 break :shifted @bitCast(shifted);
1443 },
1444 };
1445
1446 // Create a bit-mask for the field being populated, e.g. 8[3:1] -> 0b00001110
1447 const field_mask = (~@as(u64, 0) >> trunc_bits) << dest_low_bit;
1448
1449 // Shift and mask the value to be in the correct bits, leaving the others zeroed.
1450 const masked_field: u64 = (shifted_val << dest_low_bit) & field_mask;
1451
1452 // Now we just need to actually apply the relocation by loading a word, replacing
1453 // the field bits with those in `masked_field`, and storing the result back.
1454 switch (dest_word_bits) {
1455 inline 8, 16, 32, 64 => |bits| {
1456 const word_slice = dest_slice[0..@divExact(bits, 8)];
1457 const Int = @Int(.unsigned, bits);
1458 const old: u64 = std.mem.readInt(Int, word_slice, target_endian);
1459 const new: u64 = (old & ~field_mask) | masked_field;
1460 std.mem.writeInt(Int, word_slice, @intCast(new), target_endian);
1461 },
1462 else => unreachable,
1463 }
1464 }
1465 };
1466
1467 /// Enum representing "special" relocation types, i.e. those which cannot be represented
1468 /// just with `Target` and `Simple`. These relocations have completely custom handling in
1469 /// the `Special.applyInner` function.
1470 const Special = enum(u13) {
1471 larch_pcala_hi20,
1472 larch_pcala64_lo20,
1473 larch_pcala64_hi12,
1474 larch_b21,
1475 larch_b26,
1476 larch_call36,
1477
1478 sparc_le_hix22,
1479
1480 fn applyInner(
1481 s: Special,
1482 elf: *Elf,
1483 target: Symbol.Id,
1484 addend: u64,
1485 dest_vaddr: u64,
1486 dest_slice: []u8,
1487 ) error{ RelocationMisaligned, RelocationOverflow }!void {
1488 switch (s) {
1489 .larch_pcala_hi20 => {
1490 const val = target.value(elf) +% addend;
1491 const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]);
1492 elf.targetStore(inst, .{
1493 .b0_4 = elf.targetLoad(inst).b0_4,
1494 .j20 = link.loongarch.pcalaHi20(val, dest_vaddr),
1495 .b25_31 = elf.targetLoad(inst).b25_31,
1496 });
1497 },
1498 .larch_pcala64_lo20 => {
1499 const val = target.value(elf) +% addend;
1500 const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]);
1501 elf.targetStore(inst, .{
1502 .b0_4 = elf.targetLoad(inst).b0_4,
1503 .j20 = link.loongarch.pcala64Lo20(val, dest_vaddr),
1504 .b25_31 = elf.targetLoad(inst).b25_31,
1505 });
1506 },
1507 .larch_pcala64_hi12 => {
1508 const val = target.value(elf) +% addend;
1509 const inst: *align(1) link.loongarch.K12 = @ptrCast(dest_slice[0..4]);
1510 elf.targetStore(inst, .{
1511 .b0_9 = elf.targetLoad(inst).b0_9,
1512 .k12 = link.loongarch.pcala64Hi12(val, dest_vaddr),
1513 .b22_31 = elf.targetLoad(inst).b22_31,
1514 });
1515 },
1516 .larch_b21, .larch_b26, .larch_call36 => {
1517 const target_vaddr: u64 = elf.pltEntryTargetAddr(target) orelse target.value(elf);
1518 const jump_offset: i64 = @bitCast(target_vaddr +% addend -% dest_vaddr);
1519 if ((jump_offset >> 2) << 2 != jump_offset) {
1520 return error.RelocationMisaligned;
1521 }
1522 const shifted_jump_offset: i64 = @shrExact(jump_offset, 2);
1523 switch (s) {
1524 .larch_b21 => {
1525 if ((shifted_jump_offset << (64 - 21)) >> (64 - 21) != shifted_jump_offset) {
1526 return error.RelocationOverflow;
1527 }
1528 const truncated: i21 = @intCast(shifted_jump_offset);
1529 const parts: packed struct { lo16: u16, hi5: u5 } = @bitCast(truncated);
1530 const inst: *align(1) link.loongarch.D5K16 = @ptrCast(dest_slice[0..4]);
1531 elf.targetStore(inst, .{
1532 .d5 = parts.hi5,
1533 .b5_9 = elf.targetLoad(inst).b5_9,
1534 .k16 = parts.lo16,
1535 .b26_31 = elf.targetLoad(inst).b26_31,
1536 });
1537 },
1538 .larch_b26 => {
1539 if ((shifted_jump_offset << (64 - 26)) >> (64 - 26) != shifted_jump_offset) {
1540 return error.RelocationOverflow;
1541 }
1542 const truncated: i26 = @intCast(shifted_jump_offset);
1543 const parts: packed struct { lo16: u16, hi10: u10 } = @bitCast(truncated);
1544 const inst: *align(1) link.loongarch.D10K16 = @ptrCast(dest_slice[0..4]);
1545 elf.targetStore(inst, .{
1546 .d10 = parts.hi10,
1547 .k16 = parts.lo16,
1548 .b26_31 = elf.targetLoad(inst).b26_31,
1549 });
1550 },
1551 .larch_call36 => {
1552 // The allowed range of destination addresses here is non-trivial:
1553 // [PC - 128 GiB - 0x20_000, PC + 128 GiB - 0x20_000 - 4]
1554 const gib = 1024 * 1024 * 1024;
1555 if (jump_offset < -128 * gib - 0x20_000 or
1556 jump_offset > 128 * gib - 0x20_000 - 4)
1557 {
1558 return error.RelocationOverflow;
1559 }
1560 // The values we write into the instructions are a little weird too:
1561 const hi: i20 = @intCast((shifted_jump_offset +% 0x8000) >> 16);
1562 const lo: i16 = @truncate(shifted_jump_offset);
1563
1564 const inst0: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]);
1565 const inst1: *align(1) link.loongarch.K16 = @ptrCast(dest_slice[4..8]);
1566
1567 const old0 = elf.targetLoad(inst0);
1568 elf.targetStore(inst0, .{ .b0_4 = old0.b0_4, .j20 = @bitCast(hi), .b25_31 = old0.b25_31 });
1569
1570 const old1 = elf.targetLoad(inst1);
1571 elf.targetStore(inst1, .{ .b0_9 = old1.b0_9, .k16 = @bitCast(lo), .b26_31 = old1.b26_31 });
1572 },
1573 else => unreachable,
1574 }
1575 },
1576 .sparc_le_hix22 => {
1577 const tls_phndx = elf.getNode(elf.ni.tls.unwrap().?).segment;
1578 const tls_size: u64 = switch (elf.phdrSlice()) {
1579 inline else => |phdr| tls_size: {
1580 assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS);
1581 break :tls_size elf.targetLoad(&phdr[tls_phndx].memsz);
1582 },
1583 };
1584 const dest_ptr: *align(1) packed struct(u32) {
1585 imm22: u22,
1586 b22_31: u10,
1587 } = @ptrCast(dest_slice);
1588 elf.targetStore(dest_ptr, .{
1589 .imm22 = @truncate(~(target.value(elf) +% addend -% tls_size) >> 10),
1590 .b22_31 = elf.targetLoad(dest_ptr).b22_31,
1591 });
1592 },
1593 }
1594 }
1595 };
1596
1597 fn dependsOnTlsSize(t: SymbolReloc.Type, elf: *const Elf) bool {
1598 return switch (elf.targetTlsVariant()) {
1599 // In TLS variant I, the executable's TLS block starts at a fixed offset from the
1600 // thread pointer, so everything is fine...
1601 .I_original, .I_modified => false,
1602 // ...but in variant II, the executable's TLS block *ends* at a fixed offset from
1603 // the thread pointer, so the offset from the thread pointer to the *start* of the
1604 // TLS block depends on the size of the block, and we need that offset to resolve
1605 // 'tpoff' relocations.
1606 .II => switch (t.target) {
1607 .abs,
1608 .rel,
1609 .pltabs,
1610 .pltrel,
1611 .dtpoff,
1612 .size,
1613 => false,
1614
1615 .tpoff => true,
1616
1617 .special => switch (t.action.special) {
1618 .sparc_le_hix22,
1619 => true,
1620
1621 .larch_pcala_hi20,
1622 .larch_pcala64_lo20,
1623 .larch_pcala64_hi12,
1624 .larch_b21,
1625 .larch_b26,
1626 .larch_call36,
1627 => false,
1628 },
1629 },
1630 };
1631 }
1632 };
1633
1634 const Index = enum(u32) {
1635 none = std.math.maxInt(u32),
1636 _,
1637
1638 fn get(index: SymbolReloc.Index, elf: *Elf) *SymbolReloc {
1639 return &elf.symbol_relocs.items[@backingInt(index)];
1640 }
1641 };
1642
1643 fn flushMovedNode(reloc: *SymbolReloc, elf: *Elf, node_vaddr: u64) void {
1644 if (reloc.rela_index.unwrap()) |rela_index| {
1645 // The node has moved, so the offset of the relocation within the section might have
1646 // changed, so update the `offset` field of the `ElfN.Rela` entry.
1647 reloc.relaSection(elf).relaSetOffset(elf, rela_index, node_vaddr + reloc.offset);
1648 }
1649 // This is not just the inverse of the above condition, because if `reloc` is relative
1650 // to the base of this DSO, then `rela_index` is an `R_*_RELATIVE` relocation, but we
1651 // still need to call `SymbolReloc.apply` to update that relocation's addend.
1652 if (elf.ehdrType() != .REL) {
1653 reloc.apply(elf);
1654 }
1655 }
1656
1657 fn apply(reloc: *SymbolReloc, elf: *Elf) void {
1658 assert(elf.ehdrType() != .REL);
1659 const node = reloc.node.unwrap() orelse return; // deleted
1660 if (node.hasMoved(&elf.mf) or reloc.target.hasMoved(elf)) {
1661 // There's no point applying the relocation now, because it will be re-applied by
1662 // `flushMoved` at some point anyway.
1663 return;
1664 }
1665 switch (reloc.result) {
1666 .ok => {},
1667 .overflowed => elf.overflowed_reloc_count -= 1,
1668 .misaligned => elf.misaligned_reloc_count -= 1,
1669 }
1670 if (reloc.applyInner(elf)) {
1671 @branchHint(.likely);
1672 reloc.result = .ok;
1673 } else |err| switch (err) {
1674 error.RelocationOverflow => {
1675 reloc.result = .overflowed;
1676 elf.overflowed_reloc_count += 1;
1677 },
1678 error.RelocationMisaligned => {
1679 reloc.result = .misaligned;
1680 elf.misaligned_reloc_count += 1;
1681 },
1682 }
1683 }
1684 fn applyInner(reloc: *const SymbolReloc, elf: *Elf) error{ RelocationOverflow, RelocationMisaligned }!void {
1685 const node = reloc.node.unwrap().?;
1686 const dest_vaddr = elf.getNodeVAddr(node) + reloc.offset;
1687 const dest_slice = node.slice(&elf.mf)[@intCast(reloc.offset)..];
1688
1689 const addend: u64 = @bitCast(reloc.addend);
1690 const target_val: u64 = type: switch (reloc.type.target) {
1691 .abs => reloc.target.value(elf) +% addend,
1692 .rel => reloc.target.value(elf) +% addend -% dest_vaddr,
1693 .pltabs => {
1694 const plt_entry_addr = elf.pltEntryTargetAddr(reloc.target) orelse continue :type .abs;
1695 break :type plt_entry_addr +% addend;
1696 },
1697 .pltrel => {
1698 const plt_entry_addr = elf.pltEntryTargetAddr(reloc.target) orelse continue :type .rel;
1699 break :type plt_entry_addr +% addend -% dest_vaddr;
1700 },
1701 .dtpoff => reloc.target.value(elf) +% addend,
1702 .tpoff => switch (elf.targetTlsVariant()) {
1703 .I_original => |tls| tls.tcb_size +% reloc.target.value(elf) +% addend,
1704 .I_modified => |tls| 0 -% tls.tp_off +% reloc.target.value(elf) +% addend,
1705 .II => {
1706 const tls_phndx = elf.getNode(elf.ni.tls.unwrap().?).segment;
1707 const tls_size: u64 = switch (elf.phdrSlice()) {
1708 inline else => |phdr| tls_size: {
1709 assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS);
1710 break :tls_size elf.targetLoad(&phdr[tls_phndx].memsz);
1711 },
1712 };
1713 break :type reloc.target.value(elf) +% addend -% tls_size;
1714 },
1715 },
1716 .size => switch (elf.symPtr(reloc.target.index(elf))) {
1717 inline else => |sym| elf.targetLoad(&sym.size),
1718 },
1719 .special => return reloc.type.action.special.applyInner(
1720 elf,
1721 reloc.target,
1722 addend,
1723 dest_vaddr,
1724 dest_slice,
1725 ),
1726 };
1727
1728 // Check for the `R_*_RELATIVE` case now, because it is possible only when no shift or cast
1729 // is required, meaning we can handle it now and return early.
1730 if (reloc.rela_index.unwrap()) |rela_index| switch (elf.classifySymbolValue(reloc.target)) {
1731 .static => unreachable,
1732 .dynamic => return, // the relocation happens at runtime
1733 .static_relative => {
1734 // We have emitted an R_*_RELATIVE relocation to help lower an absolute-address
1735 // relocation. The value computed above is valid, but instead of writing it to the
1736 // destination slice, we actually want to write it to the runtime relocation entry.
1737 switch (elf.identClass()) {
1738 .NONE, _ => unreachable,
1739 .@"32" => assert(reloc.type.action.simple.dest == .@"32"),
1740 .@"64" => assert(reloc.type.action.simple.dest == .@"64"),
1741 }
1742 assert(reloc.type.action.simple.cast == .unsigned);
1743 assert(reloc.type.action.simple.shift == .@"0");
1744 elf.shndx.rela_dyn.relaSetAddend(elf, rela_index, target_val);
1745 return;
1746 },
1747 };
1748
1749 try reloc.type.action.simple.write(target_val, dest_slice, elf.targetEndian());
1750 }
1751
1752 fn delete(reloc: *SymbolReloc, elf: *Elf, index: SymbolReloc.Index) void {
1753 assert(index.get(elf) == reloc);
1754
1755 reloc.deleteOutputRel(elf);
1756 if (reloc.type.dependsOnTlsSize(elf)) {
1757 assert(elf.tls_size_symbol_relocs.swapRemove(index));
1758 }
1759
1760 switch (reloc.prev) {
1761 .none => {
1762 const first_target_reloc = &reloc.target.index(elf).ptr(elf).first_target_reloc;
1763 assert(first_target_reloc.* == index);
1764 first_target_reloc.* = reloc.next;
1765 },
1766 else => |prev| prev.get(elf).next = reloc.next,
1767 }
1768 switch (reloc.next) {
1769 .none => {},
1770 else => |next| next.get(elf).prev = reloc.prev,
1771 }
1772 switch (reloc.result) {
1773 .ok => {},
1774 .overflowed => elf.overflowed_reloc_count -= 1,
1775 .misaligned => elf.misaligned_reloc_count -= 1,
1776 }
1777
1778 reloc.* = undefined;
1779 reloc.node = .none;
1780 }
1781
1782 /// If `reloc.rela_index` is populated, reset it to `.none` and delete the relocation, updating
1783 /// `elf.textrel_count` if necessary.
1784 fn deleteOutputRel(reloc: *SymbolReloc, elf: *Elf) void {
1785 const rela_index = reloc.rela_index.unwrap() orelse return;
1786 reloc.relaSection(elf).relaDeleteOne(elf, rela_index);
1787 switch (elf.ehdrType()) {
1788 .REL => {},
1789 .EXEC, .DYN => switch (elf.nodeWantsDsoRelocation(reloc.node.unwrap().?)) {
1790 .no => unreachable, // there *was* a dynamic relocation!
1791 .yes => {},
1792 .yes_textrel => elf.textrel_count -= 1,
1793 },
1794 }
1795 reloc.rela_index = .none;
1796 }
1797};
1798
1799/// A relocation targeting an arbitrary node (within a section) with a fixed addend.
1800/// This represents a symbol reloc against the section symbol containing the node
1801/// with a variable addend that changes when the target node moves.
1802const NodeReloc = struct {
1803 node: MappedFile.Node.Index.Optional,
1804 offset: u64,
1805 target: MappedFile.Node.Index,
1806 addend: i64,
1807 type: NodeReloc.Type,
1808 next: NodeReloc.Index,
1809 prev: NodeReloc.Index,
1810 rela_index: Section.RelaIndex.Optional,
1811 result: enum(u8) { ok, overflowed, misaligned },
1812
1813 const Type = enum { abs32, abs64 };
1814
1815 const Index = enum(u32) {
1816 none = std.math.maxInt(u32),
1817 _,
1818
1819 fn get(index: NodeReloc.Index, elf: *Elf) *NodeReloc {
1820 return &elf.node_relocs.items[@backingInt(index)];
1821 }
1822 };
1823
1824 fn flushMovedNode(reloc: *NodeReloc, elf: *Elf, node_vaddr: u64) void {
1825 if (reloc.rela_index.unwrap()) |rela_index| {
1826 assert(elf.ehdrType() == .REL);
1827 // The node has moved, so the offset of the relocation within the section might have
1828 // changed, so update the `offset` field of the `ElfN.Rela` entry.
1829 elf.getNodeShndx(reloc.node.unwrap().?).get(elf).rela.shndx.relaSetOffset(elf, rela_index, node_vaddr + reloc.offset);
1830 } else {
1831 assert(elf.ehdrType() != .REL);
1832 reloc.apply(elf);
1833 }
1834 }
1835
1836 fn flushMovedTarget(reloc: *NodeReloc, elf: *Elf, target_section_offset: u64) void {
1837 if (reloc.rela_index.unwrap()) |rela_index| {
1838 assert(elf.ehdrType() == .REL);
1839 // The target has moved, so the `addend` field of the `ElfN.Rela` entry needs to be updated.
1840 elf.getNodeShndx(reloc.node.unwrap().?).get(elf).rela.shndx.relaSetAddend(elf, rela_index, target_section_offset +% @as(u64, @bitCast(reloc.addend)));
1841 } else {
1842 assert(elf.ehdrType() != .REL);
1843 reloc.apply(elf);
1844 }
1845 }
1846
1847 fn apply(reloc: *NodeReloc, elf: *Elf) void {
1848 const node = reloc.node.unwrap() orelse return; // deleted
1849 if (reloc.rela_index.unwrap()) |rela_index| {
1850 assert(elf.ehdrType() == .REL);
1851 _ = rela_index;
1852 } else {
1853 assert(elf.ehdrType() != .REL);
1854 if (node.hasMoved(&elf.mf) or reloc.target.hasMoved(&elf.mf)) {
1855 // There's no point applying the relocation now, because it will be re-applied by
1856 // `flushMoved` at some point anyway.
1857 return;
1858 }
1859 switch (reloc.result) {
1860 .ok => {},
1861 .overflowed => elf.overflowed_reloc_count -= 1,
1862 .misaligned => elf.misaligned_reloc_count -= 1,
1863 }
1864 if (reloc.applyInner(elf)) {
1865 @branchHint(.likely);
1866 reloc.result = .ok;
1867 } else |err| switch (err) {
1868 error.RelocationOverflow => {
1869 reloc.result = .overflowed;
1870 elf.overflowed_reloc_count += 1;
1871 },
1872 error.RelocationMisaligned => {
1873 reloc.result = .misaligned;
1874 elf.misaligned_reloc_count += 1;
1875 },
1876 }
1877 }
1878 }
1879 fn applyInner(reloc: *const NodeReloc, elf: *Elf) error{ RelocationOverflow, RelocationMisaligned }!void {
1880 const simple: SymbolReloc.Type.Simple = .{ .dest = switch (reloc.type) {
1881 .abs32 => .@"32",
1882 .abs64 => .@"64",
1883 }, .cast = .unsigned, .shift = .@"0" };
1884 const addend: u64 = @bitCast(reloc.addend);
1885 const target_val = elf.getNodeVAddr(reloc.target) +% addend;
1886 const dest_slice = reloc.node.unwrap().?.slice(&elf.mf)[@intCast(reloc.offset)..];
1887 try simple.write(target_val, dest_slice, elf.targetEndian());
1888 }
1889
1890 fn delete(reloc: *NodeReloc, elf: *Elf) void {
1891 reloc.deleteOutputRel(elf);
1892
1893 switch (reloc.prev) {
1894 .none => {
1895 const first_target_reloc = switch (elf.getNode(reloc.target)) {
1896 else => unreachable,
1897 .debug_shared => |ss| &elf.dwarf_shared.getPtr(ss).first_target_reloc,
1898 .unit_frame_cie => |ui| &elf.dwarf_units[@backingInt(ui)].frame_cie_first_target_reloc,
1899 .unit_debug_info_header => |ui| &elf.dwarf_units[@backingInt(ui)].debug_info_header_first_target_reloc,
1900 .unit_debug_line_header => |ui| &elf.dwarf_units[@backingInt(ui)].debug_line_header_first_target_reloc,
1901 .unit_debug_rnglists => |ui| &elf.dwarf_units[@backingInt(ui)].debug_rnglists_first_target_reloc,
1902 .const_debug_info => |cpi| &elf.dwarf_consts.getPtr(cpi).?.debug_info_first_target_reloc,
1903 .global_debug_info => |gi| &elf.dwarf_globals.items[@backingInt(gi)].debug_info_first_target_reloc,
1904 .func_debug_info => |fi| &elf.dwarf_funcs.items[@backingInt(fi)].debug_info_first_target_reloc,
1905 .decl_debug_info => |di| &elf.dwarf_decls.getPtr(di).?.debug_info_first_target_reloc,
1906 };
1907 first_target_reloc.* = reloc.next;
1908 },
1909 else => |prev| prev.get(elf).next = reloc.next,
1910 }
1911 switch (reloc.next) {
1912 .none => {},
1913 else => |next| next.get(elf).prev = reloc.prev,
1914 }
1915 switch (reloc.result) {
1916 .ok => {},
1917 .overflowed => elf.overflowed_reloc_count -= 1,
1918 .misaligned => elf.misaligned_reloc_count -= 1,
1919 }
1920
1921 reloc.* = undefined;
1922 reloc.node = .none;
1923 }
1924
1925 /// If `reloc.rela_index` is populated, reset it to `.none` and delete the relocation.
1926 fn deleteOutputRel(reloc: *NodeReloc, elf: *Elf) void {
1927 const rela_index = reloc.rela_index.unwrap() orelse return;
1928 assert(elf.ehdrType() == .REL);
1929 elf.getNodeShndx(reloc.node.unwrap().?).get(elf).rela.shndx.relaDeleteOne(elf, rela_index);
1930 reloc.rela_index = .none;
1931 }
1932};
1933
1934/// Identifies a single entry in the GOT.
1935const GotKey = union(enum) {
1936 /// The entry is a reserved word, initialized to zero. `initHeaders` will add as many of these
1937 /// as the target machine ABI requires.
1938 ///
1939 /// This `u32` value exists to allow reserving multiple words with distinct keys.
1940 reserved: u32,
1941
1942 /// Value is the address of the given symbol.
1943 symbol: Symbol.Id,
1944
1945 /// Value is the signed offset of the given symbol from the TLS pointer.
1946 tpoff: Symbol.Id,
1947
1948 /// Value is the TLS module ID of the DSO we are creating.
1949 ///
1950 /// Used for the first of the two GOT entries generated by a TLSLD relocation.
1951 tlsld0,
1952 /// Value is always 0.
1953 ///
1954 /// Used for the second of the two GOT entries generated by a TLSLD relocation.
1955 tlsld1,
1956
1957 /// Value is the TLS module ID for the given STT_TLS symbol.
1958 ///
1959 /// Used for the first of the two GOT entries generated by a TLSGD relocation.
1960 tlsgd0: Symbol.Id,
1961 /// Value is the offset of the given STT_TLS symbol from the base of the per-module TLS area.
1962 ///
1963 /// Used for the second of the two GOT entries generated by a TLSGD relocation.
1964 tlsgd1: Symbol.Id,
1965};
1966
1967/// A relocation targeting a particular GOT entry.
1968const GotReloc = struct {
1969 /// The node containing this relocation. Possible values are:
1970 /// * An input section
1971 /// * A section
1972 /// * A NAV, UAV, or lazy code/data
1973 /// * `.none`, if this relocation was deleted (in which case it should be ignored)
1974 node: MappedFile.Node.Index.Optional,
1975 /// The offset of the relocation inside of `node`.
1976 offset: u64,
1977 target: GotKey,
1978 addend: i64,
1979 type: GotReloc.Type,
1980 result: enum(u8) { ok, overflowed, misaligned },
1981
1982 /// `GotReloc.Type` has the same structure as `SymbolReloc.Type`, just with different `Target`
1983 /// and `Special` enums---consult doc comments on `SymbolReloc.Type` for an overview.
1984 const Type = packed struct(u16) {
1985 fn simple(target: Target, action: Simple) GotReloc.Type {
1986 assert(target != .special);
1987 return .{ .target = target, .action = .{ .simple = action } };
1988 }
1989
1990 fn special(s: Special) GotReloc.Type {
1991 return .{ .target = .special, .action = .{ .special = s } };
1992 }
1993
1994 target: Target,
1995 action: packed union {
1996 simple: Simple,
1997 special: Special,
1998 },
1999
2000 /// Like `SymbolReloc.Target`, but for GOT relocations. There are fewer tags because there
2001 /// are fewer different kinds of GOT relocation.
2002 const Target = enum(u3) {
2003 /// This is a "special" relocation whose specific type is in the `action.special` field.
2004 special,
2005
2006 /// Absolute address of the GOT entry.
2007 abs,
2008 /// Offset from the relocation itself to the GOT entry ("PC-relative").
2009 rel,
2010 /// Offset from the base of the GOT to the GOT entry.
2011 offset,
2012 };
2013
2014 const Simple = SymbolReloc.Type.Simple;
2015
2016 /// Like `SymbolReloc.Special`, but for GOT relocations.
2017 const Special = enum(u13) {
2018 larch_pcala_hi20,
2019 larch_pcala64_lo20,
2020 larch_pcala64_hi12,
2021
2022 sparc_op_lox10,
2023 sparc_op_hix22,
2024
2025 fn applyInner(
2026 s: Special,
2027 elf: *Elf,
2028 got_vaddr: u64,
2029 got_offset: u64,
2030 addend: u64,
2031 dest_vaddr: u64,
2032 dest_slice: []u8,
2033 ) error{ RelocationMisaligned, RelocationOverflow }!void {
2034 switch (s) {
2035 .larch_pcala_hi20 => {
2036 const val = got_vaddr +% got_offset +% addend;
2037 const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]);
2038 elf.targetStore(inst, .{
2039 .b0_4 = elf.targetLoad(inst).b0_4,
2040 .j20 = link.loongarch.pcalaHi20(val, dest_vaddr),
2041 .b25_31 = elf.targetLoad(inst).b25_31,
2042 });
2043 },
2044 .larch_pcala64_lo20 => {
2045 const val = got_vaddr +% got_offset +% addend;
2046 const inst: *align(1) link.loongarch.J20 = @ptrCast(dest_slice[0..4]);
2047 elf.targetStore(inst, .{
2048 .b0_4 = elf.targetLoad(inst).b0_4,
2049 .j20 = link.loongarch.pcala64Lo20(val, dest_vaddr),
2050 .b25_31 = elf.targetLoad(inst).b25_31,
2051 });
2052 },
2053 .larch_pcala64_hi12 => {
2054 const val = got_vaddr +% got_offset +% addend;
2055 const inst: *align(1) link.loongarch.K12 = @ptrCast(dest_slice[0..4]);
2056 elf.targetStore(inst, .{
2057 .b0_9 = elf.targetLoad(inst).b0_9,
2058 .k12 = link.loongarch.pcala64Hi12(val, dest_vaddr),
2059 .b22_31 = elf.targetLoad(inst).b22_31,
2060 });
2061 },
2062 .sparc_op_lox10 => {
2063 const dest_ptr: *align(1) packed struct(u32) {
2064 imm13: u13,
2065 b13_31: u19,
2066 } = @ptrCast(dest_slice);
2067 elf.targetStore(dest_ptr, .{
2068 .imm13 = @as(u10, @truncate(got_offset)),
2069 .b13_31 = elf.targetLoad(dest_ptr).b13_31,
2070 });
2071 },
2072 .sparc_op_hix22 => {
2073 const dest_ptr: *align(1) packed struct(u32) {
2074 imm22: u22,
2075 b22_31: u10,
2076 } = @ptrCast(dest_slice);
2077 elf.targetStore(dest_ptr, .{
2078 .imm22 = @truncate(got_offset >> 10),
2079 .b22_31 = elf.targetLoad(dest_ptr).b22_31,
2080 });
2081 },
2082 }
2083 }
2084 };
2085 };
2086
2087 const Index = enum(u32) {
2088 none = std.math.maxInt(u32),
2089 _,
2090
2091 fn get(index: GotReloc.Index, elf: *Elf) *GotReloc {
2092 return &elf.got_relocs.items[@backingInt(index)];
2093 }
2094 };
2095
2096 fn apply(reloc: *GotReloc, elf: *Elf) void {
2097 assert(elf.ehdrType() != .REL);
2098 const node = reloc.node.unwrap() orelse return; // deleted
2099 if (node.hasMoved(&elf.mf) or elf.shndx.got.get(elf).ni.hasMoved(&elf.mf)) {
2100 // There's no point applying the relocation now, because it will be re-applied by
2101 // `flushMoved` at some point anyway.
2102 return;
2103 }
2104 switch (reloc.result) {
2105 .ok => {},
2106 .overflowed => elf.overflowed_reloc_count -= 1,
2107 .misaligned => elf.misaligned_reloc_count -= 1,
2108 }
2109 if (reloc.applyInner(elf)) {
2110 @branchHint(.likely);
2111 reloc.result = .ok;
2112 } else |err| switch (err) {
2113 error.RelocationOverflow => {
2114 reloc.result = .overflowed;
2115 elf.overflowed_reloc_count += 1;
2116 },
2117 error.RelocationMisaligned => {
2118 reloc.result = .misaligned;
2119 elf.misaligned_reloc_count += 1;
2120 },
2121 }
2122 }
2123 fn applyInner(reloc: *const GotReloc, elf: *Elf) error{ RelocationOverflow, RelocationMisaligned }!void {
2124 const node = reloc.node.unwrap().?;
2125 const dest_vaddr = elf.getNodeVAddr(node) + reloc.offset;
2126 const dest_slice = node.slice(&elf.mf)[@intCast(reloc.offset)..];
2127
2128 const got_vaddr = elf.shndx.got.vaddr(elf);
2129 const got_index: u64 = elf.got.getIndex(reloc.target).?;
2130 const got_offset: u64 = switch (elf.identClass()) {
2131 .NONE, _ => unreachable,
2132 inline else => |class| @sizeOf(class.ElfN().Addr) * got_index,
2133 };
2134 const addend: u64 = @bitCast(reloc.addend);
2135
2136 const target_val: u64 = switch (reloc.type.target) {
2137 .abs => got_vaddr +% got_offset +% addend,
2138 .rel => got_vaddr +% got_offset +% addend -% dest_vaddr,
2139 .offset => got_offset +% addend,
2140 .special => return reloc.type.action.special.applyInner(
2141 elf,
2142 got_vaddr,
2143 got_offset,
2144 addend,
2145 dest_vaddr,
2146 dest_slice,
2147 ),
2148 };
2149 try reloc.type.action.simple.write(target_val, dest_slice, elf.targetEndian());
2150 }
2151
2152 fn delete(reloc: *GotReloc, elf: *Elf) void {
2153 switch (reloc.result) {
2154 .ok => {},
2155 .overflowed => elf.overflowed_reloc_count -= 1,
2156 .misaligned => elf.misaligned_reloc_count -= 1,
2157 }
2158 reloc.* = .{
2159 .node = .none,
2160 .offset = undefined,
2161 .target = undefined,
2162 .addend = undefined,
2163 .type = undefined,
2164 .result = undefined,
2165 };
2166 }
2167};
2168
2169fn ensureUnusedSymbolCapacity(elf: *Elf, len: u32, kind: enum { all_local, maybe_global }) Error!void {
2170 const gpa = elf.base.comp.gpa;
2171
2172 try elf.symtab.ensureUnusedCapacity(gpa, len);
2173
2174 // If adding locals, we may need to move one global out of the way for each local. If adding
2175 // globals, they could all get demoted to STB_LOCAL, meaning we have to move N other globals
2176 // around to keep `.dynsym` compact. Either way, the maximum is N.
2177 try elf.changed_symtab_index.ensureUnusedCapacity(gpa, len);
2178
2179 {
2180 // Ensure the symtab section's node is big enough
2181 const need_node_size: u64 = switch (elf.shdrPtr(.symtab)) {
2182 inline else => |shdr, class| elf.targetLoad(&shdr.size) + len * @sizeOf(class.ElfN().Sym),
2183 };
2184 try Section.Index.symtab.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, need_node_size);
2185 }
2186
2187 switch (kind) {
2188 .all_local => {},
2189 .maybe_global => {
2190 try elf.globals.strong_def.ensureUnusedCapacity(gpa, len);
2191 try elf.globals.weak_def.ensureUnusedCapacity(gpa, len);
2192 try elf.globals.strong_undef.ensureUnusedCapacity(gpa, len);
2193 try elf.globals.weak_undef.ensureUnusedCapacity(gpa, len);
2194
2195 try elf.node_global_symbols.ensureUnusedCapacity(gpa, len);
2196
2197 if (elf.shndx.dynsym != .UNDEF) {
2198 const dynsym_cur_size: u64, const dynsym_ent_size: u32 = switch (elf.shdrPtr(elf.shndx.dynsym)) {
2199 inline else => |shdr, class| .{
2200 elf.targetLoad(&shdr.size),
2201 @sizeOf(class.ElfN().Sym),
2202 },
2203 };
2204 const dynsym_cur_len: u32 = @intCast(@divExact(dynsym_cur_size, dynsym_ent_size));
2205
2206 const dynsym_need_size: u64 = (dynsym_cur_len + len) * dynsym_ent_size;
2207 try elf.shndx.dynsym.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, dynsym_need_size);
2208
2209 try elf.ensureDynsymHashCapacity(dynsym_cur_len + len);
2210
2211 try elf.ensureUnusedPltCapacity(len);
2212 }
2213 },
2214 }
2215}
2216
2217fn ensureDynsymHashCapacity(elf: *Elf, max_dynsym_count: u32) Error!void {
2218 const gpa = elf.base.comp.gpa;
2219
2220 const min_buckets = max_dynsym_count / 2;
2221
2222 const cur_dynsym_count: u32 = switch (elf.shdrPtr(elf.shndx.dynsym)) {
2223 inline else => |shdr, class| @intCast(@divExact(
2224 elf.targetLoad(&shdr.size),
2225 @sizeOf(class.ElfN().Sym),
2226 )),
2227 };
2228
2229 switch (elf.targetDynsymHashInfo()) {
2230 inline else => |info| {
2231 {
2232 const section_slice: []align(@sizeOf(info.Int())) u8 = @alignCast(elf.shndx.hash.get(elf).ni.slice(&elf.mf));
2233 const header: *info.Header() = @ptrCast(section_slice[0..@sizeOf(info.Header())]);
2234 assert(elf.targetLoad(&header.nchain) == cur_dynsym_count);
2235 const nbucket = elf.targetLoad(&header.nbucket);
2236 if (nbucket >= min_buckets) {
2237 // We don't need to add any buckets, but we still need to make sure the section is large
2238 // enough to fit `max_dynsym_count` chains.
2239 const need_size = @sizeOf(info.Header()) + (nbucket + max_dynsym_count) * 4;
2240 try elf.shndx.hash.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, need_size);
2241 return;
2242 }
2243 // We need more buckets, so we'll have to rebuild the hash table.
2244 }
2245
2246 // Rebuilding the hash table is quite expensive, so to avoid doing it too often we use a large
2247 // growth factor (* 2) for `nbucket`.
2248 const new_nbucket = min_buckets * 2;
2249
2250 {
2251 const need_size = @sizeOf(info.Header()) + (new_nbucket + max_dynsym_count) * 4;
2252 try elf.shndx.hash.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, need_size);
2253 }
2254
2255 elf.mf.nodes_lock.lock();
2256 defer elf.mf.nodes_lock.unlock();
2257
2258 const section_slice: []align(@sizeOf(info.Int())) u8 = @alignCast(elf.shndx.hash.get(elf).ni.slice(&elf.mf));
2259 const header: *info.Header() = @ptrCast(section_slice[0..@sizeOf(info.Header())]);
2260 const trailing: []info.Int() = @ptrCast(section_slice[@sizeOf(info.Header())..]);
2261
2262 header.* = .{ .nbucket = new_nbucket, .nchain = cur_dynsym_count };
2263 if (elf.targetEndian() != std.lang.Endian.native) {
2264 std.mem.byteSwapAllFields(info.Header(), header);
2265 }
2266 const buckets: []info.Int() = trailing[0..@intCast(elf.targetLoad(&header.nbucket))];
2267 const chains: []info.Int() = trailing[@intCast(elf.targetLoad(&header.nbucket))..][0..@intCast(elf.targetLoad(&header.nchain))];
2268
2269 @memset(buckets, 0);
2270 chains[0] = 0;
2271 for (1..cur_dynsym_count, chains[1..]) |dynsym_index_usize, *chain| {
2272 const dynsym_index: u32 = @intCast(dynsym_index_usize);
2273 const sym_name: String(.dynstr) = switch (elf.dynsymPtr(dynsym_index)) {
2274 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),
2275 };
2276 const b = std.elf.hash.calculate(sym_name.slice(elf)) % buckets.len;
2277 // Make this symbol the head of that bucket, and chain to the old head.
2278 chain.* = buckets[b];
2279 elf.targetStore(&buckets[b], dynsym_index);
2280 }
2281 },
2282 }
2283}
2284
2285fn appendDynsymHashEntry(elf: *Elf, dynsym_index: u32) void {
2286 switch (elf.targetDynsymHashInfo()) {
2287 inline else => |info| {
2288 const section_slice: []align(@sizeOf(info.Int())) u8 = @alignCast(elf.shndx.hash.get(elf).ni.slice(&elf.mf));
2289 const header: *info.Header() = @ptrCast(section_slice[0..@sizeOf(info.Header())]);
2290 assert(elf.targetLoad(&header.nchain) == dynsym_index);
2291 elf.targetStore(&header.nchain, dynsym_index + 1);
2292
2293 switch (elf.shdrPtr(elf.shndx.hash)) {
2294 inline else => |shdr| elf.targetStore(&shdr.size, elf.targetLoad(&shdr.size) + @sizeOf(info.Int())),
2295 }
2296 },
2297 }
2298
2299 elf.populateDynsymHashEntry(dynsym_index);
2300}
2301fn populateDynsymHashEntry(elf: *Elf, dynsym_index: u32) void {
2302 elf.mf.nodes_lock.lock();
2303 defer elf.mf.nodes_lock.unlock();
2304
2305 assert(dynsym_index != 0);
2306
2307 switch (elf.targetDynsymHashInfo()) {
2308 inline else => |info| {
2309 const section_slice: []align(@sizeOf(info.Int())) u8 = @alignCast(elf.shndx.hash.get(elf).ni.slice(&elf.mf));
2310 const header: *info.Header() = @ptrCast(section_slice[0..@sizeOf(info.Header())]);
2311 const trailing: []info.Int() = @ptrCast(section_slice[@sizeOf(info.Header())..]);
2312
2313 const buckets: []info.Int() = trailing[0..@intCast(elf.targetLoad(&header.nbucket))];
2314 const chains: []info.Int() = trailing[@intCast(elf.targetLoad(&header.nbucket))..][0..@intCast(elf.targetLoad(&header.nchain))];
2315
2316 const sym_name: String(.dynstr) = switch (elf.dynsymPtr(dynsym_index)) {
2317 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),
2318 };
2319 const b = std.elf.hash.calculate(sym_name.slice(elf)) % buckets.len;
2320 // Make this symbol the head of that bucket, and chain to the old head.
2321 chains[dynsym_index] = buckets[b];
2322 elf.targetStore(&buckets[b], dynsym_index);
2323 },
2324 }
2325}
2326fn popDynsymHashEntry(elf: *Elf, dynsym_index: u32) void {
2327 elf.clearDynsymHashEntry(dynsym_index);
2328
2329 switch (elf.targetDynsymHashInfo()) {
2330 inline else => |info| {
2331 const section_slice: []align(@sizeOf(info.Int())) u8 = @alignCast(elf.shndx.hash.get(elf).ni.slice(&elf.mf));
2332 const header: *info.Header() = @ptrCast(section_slice[0..@sizeOf(info.Header())]);
2333 assert(elf.targetLoad(&header.nchain) == dynsym_index + 1);
2334 elf.targetStore(&header.nchain, dynsym_index);
2335
2336 switch (elf.shdrPtr(elf.shndx.hash)) {
2337 inline else => |shdr| elf.targetStore(&shdr.size, elf.targetLoad(&shdr.size) - @sizeOf(info.Int())),
2338 }
2339 },
2340 }
2341}
2342fn clearDynsymHashEntry(elf: *Elf, dynsym_index: u32) void {
2343 elf.mf.nodes_lock.lock();
2344 defer elf.mf.nodes_lock.unlock();
2345
2346 assert(dynsym_index != 0);
2347
2348 switch (elf.targetDynsymHashInfo()) {
2349 inline else => |info| {
2350 const section_slice: []align(@sizeOf(info.Int())) u8 = @alignCast(elf.shndx.hash.get(elf).ni.slice(&elf.mf));
2351 const header: *info.Header() = @ptrCast(section_slice[0..@sizeOf(info.Header())]);
2352 const trailing: []info.Int() = @ptrCast(section_slice[@sizeOf(info.Header())..]);
2353
2354 const buckets: []info.Int() = trailing[0..@intCast(elf.targetLoad(&header.nbucket))];
2355 const chains: []info.Int() = trailing[@intCast(elf.targetLoad(&header.nbucket))..][0..@intCast(elf.targetLoad(&header.nchain))];
2356
2357 const sym_name: String(.dynstr) = switch (elf.dynsymPtr(dynsym_index)) {
2358 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),
2359 };
2360 const b = std.elf.hash.calculate(sym_name.slice(elf)) % buckets.len;
2361
2362 const next_dynsym_index = elf.targetLoad(&chains[dynsym_index]);
2363 elf.targetStore(&chains[dynsym_index], 0);
2364
2365 // To remove `dynsym_index` from the singly-linked list, we need to iterate the chain to find
2366 // and replace it. But since this is, well, a hash table, that's actually fine.
2367 if (elf.targetLoad(&buckets[b]) == dynsym_index) {
2368 elf.targetStore(&buckets[b], next_dynsym_index);
2369 } else {
2370 var cur: usize = @intCast(elf.targetLoad(&buckets[b]));
2371 while (true) {
2372 assert(cur != 0); // `dynsym_index` is definitely somewhere in the chain
2373 if (elf.targetLoad(&chains[cur]) == dynsym_index) break;
2374 cur = @intCast(elf.targetLoad(&chains[cur]));
2375 }
2376 // We found `dynsym_index`; replace it with `next_dynsym_index`.
2377 elf.targetStore(&chains[cur], next_dynsym_index);
2378 }
2379 },
2380 }
2381}
2382
2383fn ensureUnusedPltCapacity(elf: *Elf, len: u32) Error!void {
2384 const gpa = elf.base.comp.gpa;
2385
2386 try elf.shndx.rela_plt.relaEnsureAdditionalCapacity(elf, len);
2387
2388 try elf.plt.ensureUnusedCapacity(gpa, len);
2389 const need_plt_count = elf.plt.count() + len;
2390
2391 const plt = elf.targetPltInfo();
2392
2393 // Ensure the `.plt` section's node is big enough:
2394 {
2395 const need_size: usize = plt.entry_size * (1 + need_plt_count);
2396 try elf.shndx.plt.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, need_size);
2397 }
2398
2399 // If there is a `.got.plt` section, ensure its node is big enough
2400 if (plt.got_plt) |got_plt| {
2401 const need_size: usize = elf.targetPtrSize() * (got_plt.header_entries + need_plt_count);
2402 try elf.shndx.got_plt.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, need_size);
2403 }
2404
2405 // If there is a `.plt.sec` section, ensure its node is big enough
2406 if (plt.plt_sec) |plt_sec| {
2407 const need_size: usize = plt_sec.entry_size * need_plt_count;
2408 try elf.shndx.plt_sec.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, need_size);
2409 }
2410}
2411/// Given an index into the PLT, returns whether that PLT entry is dead, meaning it may be reused at
2412/// any time and must not be targeted by relocations. See also the doc comment on `Elf.plt`.
2413fn pltEntryIsDead(elf: *Elf, plt_index: usize) bool {
2414 assert(elf.shndx.plt != .UNDEF);
2415 assert(plt_index <= elf.plt.count());
2416 // We track which PLT entries are alive based on the relocation entries, since there is a 1-1
2417 // mapping between PLT entries and `.rela.plt` entries and the relocation entries already have
2418 // a free-list mechanism.
2419 switch (elf.shdrPtr(elf.shndx.rela_plt)) {
2420 inline else => |rela_shdr, class| {
2421 const size = elf.targetLoad(&rela_shdr.size);
2422 const relas: []class.ElfN().Rela = @ptrCast(@alignCast(
2423 elf.shndx.rela_plt.get(elf).ni.slice(&elf.mf)[0..@intCast(size)],
2424 ));
2425 const rel_type = elf.targetLoad(&relas[plt_index].info).type;
2426 return rel_type == MachineRelocType.none(elf).unwrap(elf);
2427 },
2428 }
2429}
2430
2431const AddLocalSymbolOptions = struct {
2432 node: MappedFile.Node.Index.Optional,
2433 name: String(.strtab),
2434 value: u64,
2435 size: u64,
2436 type: std.elf.STT,
2437 shndx: Section.Index,
2438};
2439fn addLocalSymbolAssumeCapacity(elf: *Elf, opts: AddLocalSymbolOptions) Symbol.LocalIndex {
2440 switch (elf.shdrPtr(.symtab)) {
2441 inline else => |shdr, class| {
2442 const ent_size = @sizeOf(class.ElfN().Sym);
2443
2444 // `shdr.info` stores the index of the first global symbol. We will replace it with our
2445 // new local symbol, and move the global symbol to a new index at the end of the symtab.
2446 const target_index: Symbol.Index = @fromBackingInt(elf.targetLoad(&shdr.info));
2447
2448 const old_size = elf.targetLoad(&shdr.size);
2449 const new_size = old_size + ent_size;
2450
2451 assert(elf.symtab.items.len == @divExact(old_size, ent_size));
2452
2453 elf.targetStore(&shdr.info, @backingInt(target_index) + 1);
2454 elf.targetStore(&shdr.size, new_size);
2455
2456 const new_index: Symbol.Index = @fromBackingInt(@intCast(elf.symtab.items.len));
2457 elf.symtab.appendAssumeCapacity(undefined);
2458
2459 const target_sym = @field(elf.symPtr(target_index), @tagName(class));
2460
2461 if (target_index != new_index) {
2462 // Move the global at `target_index` to `new_index`. First the symtab entry...
2463 const new_sym = @field(elf.symPtr(new_index), @tagName(class));
2464 new_sym.* = target_sym.*;
2465 // ...then the `elf.symtab` metadata...
2466 new_index.ptr(elf).* = target_index.ptr(elf).*;
2467 // ...then update the `elf.globals` tracking.
2468 const global_name: String(.strtab) = @fromBackingInt(elf.targetLoad(&new_sym.name));
2469 elf.globalByName(global_name).?.symtab_index = new_index;
2470
2471 if (elf.ehdrType() == .REL and target_index.ptr(elf).first_target_reloc != .none) {
2472 // This symbol's index is changing, so queue an update of relocs targeting it.
2473 elf.changed_symtab_index.putAssumeCapacity(global_name, {});
2474 }
2475 }
2476
2477 target_index.ptr(elf).* = .{
2478 .node = opts.node,
2479 .first_target_reloc = .none,
2480 };
2481
2482 target_sym.* = .{
2483 .name = @backingInt(opts.name),
2484 .value = @intCast(opts.value),
2485 .size = @intCast(opts.size),
2486 .info = .{ .type = opts.type, .bind = .LOCAL },
2487 .other = .{ .visibility = .DEFAULT },
2488 .shndx = opts.shndx.toSection().?,
2489 };
2490 if (elf.targetEndian() != std.lang.Endian.native) {
2491 std.mem.byteSwapAllFields(class.ElfN().Sym, target_sym);
2492 }
2493
2494 return @fromBackingInt(@backingInt(target_index));
2495 },
2496 }
2497}
2498
2499const AddGlobalSymbolOptions = struct {
2500 const Name = struct {
2501 strtab: String(.strtab),
2502 dynstr: String(.dynstr),
2503 fn string(elf: *Elf, slice: []const u8) Error!Name {
2504 return .{
2505 .strtab = try elf.string(.strtab, slice),
2506 .dynstr = switch (elf.shndx.dynsym) {
2507 .UNDEF => .empty,
2508 else => try elf.string(.dynstr, slice),
2509 },
2510 };
2511 }
2512 };
2513
2514 node: MappedFile.Node.Index.Optional,
2515 name: Name,
2516 lib_name: ?[]const u8 = null,
2517 value: u64,
2518 size: u64,
2519 type: std.elf.STT,
2520 bind: enum { strong, weak },
2521 visibility: std.elf.STV,
2522 shndx: Section.Index,
2523};
2524fn addGlobalSymbolAssumeCapacity(elf: *Elf, opts: AddGlobalSymbolOptions) error{MultipleDefinitions}!Symbol.Id {
2525 _ = opts.lib_name; // TODO
2526
2527 if (elf.shndx.dynsym == .UNDEF) {
2528 assert(opts.name.dynstr == .empty);
2529 } else {
2530 assert(std.mem.eql(u8, opts.name.dynstr.slice(elf), opts.name.strtab.slice(elf)));
2531 }
2532
2533 // We break from this `switch` only if this symbol name did not previously exist at all and so
2534 // we have added a new entry to one of the maps in `elf.globals`. In that case we actually need
2535 // a new symtab entry.
2536 const new_global_ptr: *Symbol.Global = if (opts.shndx != .UNDEF) switch (opts.bind) {
2537 .strong => new_global: {
2538 const gop = elf.globals.strong_def.getOrPutAssumeCapacity(opts.name.strtab);
2539 if (gop.found_existing) return error.MultipleDefinitions;
2540 const old_kv = elf.globals.weak_def.fetchSwapRemove(opts.name.strtab) orelse
2541 elf.globals.strong_undef.fetchSwapRemove(opts.name.strtab) orelse
2542 elf.globals.weak_undef.fetchSwapRemove(opts.name.strtab) orelse {
2543 // The symbol did not already exist, so we'll use the "new global" path.
2544 break :new_global gop.value_ptr;
2545 };
2546 gop.value_ptr.* = old_kv.value;
2547 elf.setGlobalSymbolValue(opts.name.strtab, gop.value_ptr, .{
2548 .node = opts.node,
2549 .value = opts.value,
2550 .size = opts.size,
2551 .type = opts.type,
2552 .shndx = opts.shndx,
2553 });
2554 elf.mergeGlobalSymbolVisibility(gop.value_ptr, opts.visibility, .strong);
2555 return .global(opts.name.strtab);
2556 },
2557 .weak => new_global: {
2558 if (elf.globals.strong_def.getPtr(opts.name.strtab)) |global| {
2559 // The existing definition holds, we just merge our visibility in.
2560 elf.mergeGlobalSymbolVisibility(global, opts.visibility, .strong);
2561 return .global(opts.name.strtab);
2562 }
2563 const gop = elf.globals.weak_def.getOrPutAssumeCapacity(opts.name.strtab);
2564 if (gop.found_existing) {
2565 // The existing definition holds, we just merge our visibility in.
2566 elf.mergeGlobalSymbolVisibility(gop.value_ptr, opts.visibility, .weak);
2567 return .global(opts.name.strtab);
2568 }
2569 const old_kv = elf.globals.strong_undef.fetchSwapRemove(opts.name.strtab) orelse
2570 elf.globals.weak_undef.fetchSwapRemove(opts.name.strtab) orelse {
2571 // The symbol did not already exist, so we'll use the "new global" path.
2572 break :new_global gop.value_ptr;
2573 };
2574 gop.value_ptr.* = old_kv.value;
2575 elf.setGlobalSymbolValue(opts.name.strtab, gop.value_ptr, .{
2576 .node = opts.node,
2577 .value = opts.value,
2578 .size = opts.size,
2579 .type = opts.type,
2580 .shndx = opts.shndx,
2581 });
2582 elf.mergeGlobalSymbolVisibility(gop.value_ptr, opts.visibility, .weak);
2583 return .global(opts.name.strtab);
2584 },
2585 } else switch (opts.bind) {
2586 .strong => new_global: {
2587 if (elf.globals.strong_def.getPtr(opts.name.strtab)) |global| {
2588 // The existing definition holds, we just merge our visibility in.
2589 elf.mergeGlobalSymbolVisibility(global, opts.visibility, .strong);
2590 return .global(opts.name.strtab);
2591 }
2592 if (elf.globals.weak_def.getPtr(opts.name.strtab)) |global| {
2593 // The existing definition holds, we just merge our visibility in.
2594 elf.mergeGlobalSymbolVisibility(global, opts.visibility, .weak);
2595 return .global(opts.name.strtab);
2596 }
2597 const gop = elf.globals.strong_undef.getOrPutAssumeCapacity(opts.name.strtab);
2598 if (gop.found_existing) {
2599 // The existing symbol is okay, we just merge our visibility in.
2600 elf.mergeGlobalSymbolVisibility(gop.value_ptr, opts.visibility, .strong);
2601 return .global(opts.name.strtab);
2602 }
2603 const old_kv = elf.globals.weak_undef.fetchSwapRemove(opts.name.strtab) orelse {
2604 // The symbol did not already exist, so we'll use the "new global" path.
2605 break :new_global gop.value_ptr;
2606 };
2607 gop.value_ptr.* = old_kv.value;
2608 elf.mergeGlobalSymbolVisibility(gop.value_ptr, opts.visibility, .strong);
2609 return .global(opts.name.strtab);
2610 },
2611 .weak => new_global: {
2612 if (elf.globals.strong_def.getPtr(opts.name.strtab) orelse
2613 elf.globals.strong_undef.getPtr(opts.name.strtab)) |global|
2614 {
2615 // The existing symbol is okay, we just merge our visibility in.
2616 elf.mergeGlobalSymbolVisibility(global, opts.visibility, .strong);
2617 return .global(opts.name.strtab);
2618 }
2619 if (elf.globals.weak_def.getPtr(opts.name.strtab)) |global| {
2620 // The existing symbol is okay, we just merge our visibility in.
2621 elf.mergeGlobalSymbolVisibility(global, opts.visibility, .weak);
2622 return .global(opts.name.strtab);
2623 }
2624 const gop = elf.globals.weak_undef.getOrPutAssumeCapacity(opts.name.strtab);
2625 if (gop.found_existing) {
2626 // The existing symbol is okay, we just merge our visibility in.
2627 elf.mergeGlobalSymbolVisibility(gop.value_ptr, opts.visibility, .weak);
2628 return .global(opts.name.strtab);
2629 }
2630 break :new_global gop.value_ptr;
2631 },
2632 };
2633
2634 const force_local_bind: bool = switch (opts.visibility) {
2635 .HIDDEN, .INTERNAL => elf.ehdrType() != .REL,
2636 .PROTECTED, .DEFAULT => false,
2637 };
2638
2639 const bind: std.elf.STB = if (force_local_bind) b: {
2640 break :b .LOCAL;
2641 } else switch (opts.bind) {
2642 .strong => .GLOBAL,
2643 .weak => .WEAK,
2644 };
2645
2646 const @"type": std.elf.STT = switch (opts.type) {
2647 .NOTYPE => if (elf.dso_globals.get(opts.name.strtab)) |dso_global| t: {
2648 break :t dso_global.type;
2649 } else .NOTYPE,
2650 else => |t| t,
2651 };
2652
2653 const sym_index: Symbol.Index = @fromBackingInt(@intCast(elf.symtab.items.len));
2654 elf.symtab.appendAssumeCapacity(.{
2655 .node = opts.node,
2656 .first_target_reloc = .none,
2657 });
2658 switch (elf.shdrPtr(.symtab)) {
2659 inline else => |shdr, class| {
2660 const Sym = class.ElfN().Sym;
2661 // Increase the symtab size...
2662 const old_size = elf.targetLoad(&shdr.size);
2663 assert(old_size == @backingInt(sym_index) * @sizeOf(Sym));
2664 elf.targetStore(&shdr.size, old_size + @sizeOf(Sym));
2665 // ...then populate the newly-valid symbol pointer
2666 const sym = @field(elf.symPtr(sym_index), @tagName(class));
2667 sym.* = .{
2668 .name = @backingInt(opts.name.strtab),
2669 .value = @intCast(opts.value),
2670 .size = @intCast(opts.size),
2671 .info = .{ .type = @"type", .bind = bind },
2672 .other = .{ .visibility = opts.visibility },
2673 .shndx = opts.shndx.toSection().?,
2674 };
2675 if (elf.targetEndian() != std.lang.Endian.native) {
2676 std.mem.byteSwapAllFields(Sym, sym);
2677 }
2678 },
2679 }
2680
2681 const old_head: String(.strtab) = old_head: {
2682 const node = opts.node.unwrap() orelse break :old_head .empty;
2683 const gop = elf.node_global_symbols.getOrPutAssumeCapacity(node);
2684 const old_head: String(.strtab) = if (gop.found_existing) gop.value_ptr.* else .empty;
2685 gop.value_ptr.* = opts.name.strtab;
2686 break :old_head old_head;
2687 };
2688
2689 new_global_ptr.* = .{
2690 .symtab_index = sym_index,
2691 .dynsym_index = dynsym_index: {
2692 if (elf.shndx.dynsym == .UNDEF) break :dynsym_index 0;
2693 if (force_local_bind) break :dynsym_index 0;
2694 switch (elf.shdrPtr(elf.shndx.dynsym)) {
2695 inline else => |shdr, class| {
2696 const Sym = class.ElfN().Sym;
2697 // Increase the dynamic symbol table size...
2698 const old_size = elf.targetLoad(&shdr.size);
2699 elf.targetStore(&shdr.size, old_size + @sizeOf(Sym));
2700 const dynsym_index: u32 = @intCast(@divExact(old_size, @sizeOf(Sym)));
2701 // ...then populate the newly-valid symbol pointer
2702 const sym = @field(elf.dynsymPtr(dynsym_index), @tagName(class));
2703 sym.* = .{
2704 .name = @backingInt(opts.name.dynstr),
2705 .value = @intCast(opts.value),
2706 .size = @intCast(opts.size),
2707 .info = .{ .type = @"type", .bind = bind },
2708 .other = .{ .visibility = opts.visibility },
2709 .shndx = opts.shndx.toSection().?,
2710 };
2711 if (elf.targetEndian() != std.lang.Endian.native) {
2712 std.mem.byteSwapAllFields(Sym, sym);
2713 }
2714 elf.appendDynsymHashEntry(dynsym_index);
2715 break :dynsym_index dynsym_index;
2716 },
2717 }
2718 },
2719 .prev_in_node = .empty,
2720 .next_in_node = old_head,
2721 };
2722
2723 if (old_head != .empty) {
2724 const old_head_ptr = elf.globalByName(old_head).?;
2725 assert(old_head_ptr.symtab_index.ptr(elf).node == opts.node);
2726 assert(old_head_ptr.prev_in_node == .empty);
2727 old_head_ptr.prev_in_node = opts.name.strtab;
2728 }
2729
2730 if (force_local_bind) {
2731 elf.moveDemotedGlobal(new_global_ptr);
2732 }
2733
2734 switch (@"type") {
2735 .FUNC, .GNU_IFUNC => if (elf.ehdrType() != .REL and
2736 elf.classifySymbolValue(.global(opts.name.strtab)) == .dynamic)
2737 {
2738 // This STT_FUNC symbol might be defined externally, so it needs a PLT entry.
2739 elf.addPltEntry(opts.name.strtab, new_global_ptr.dynsym_index);
2740 },
2741 else => {},
2742 }
2743
2744 return .global(opts.name.strtab);
2745}
2746fn setGlobalSymbolValue(
2747 elf: *Elf,
2748 global_name: String(.strtab),
2749 global_ptr: *Symbol.Global,
2750 new: struct {
2751 node: MappedFile.Node.Index.Optional,
2752 value: u64,
2753 size: u64,
2754 type: std.elf.STT,
2755 shndx: Section.Index,
2756 },
2757) void {
2758 assert(new.shndx != .UNDEF);
2759 if (global_ptr.symtab_index.ptr(elf).node.unwrap()) |old_node| {
2760 if (global_ptr.next_in_node != .empty) {
2761 const next = elf.globalByName(global_ptr.next_in_node).?;
2762 assert(next.prev_in_node == global_name);
2763 assert(next.symtab_index.ptr(elf).node.unwrap().? == old_node);
2764 next.prev_in_node = global_ptr.prev_in_node;
2765 }
2766 if (global_ptr.prev_in_node != .empty) {
2767 const prev = elf.globalByName(global_ptr.prev_in_node).?;
2768 assert(prev.next_in_node == global_name);
2769 assert(prev.symtab_index.ptr(elf).node.unwrap().? == old_node);
2770 prev.next_in_node = global_ptr.next_in_node;
2771 } else {
2772 // We're the start of the linked list, so we need to change the head.
2773 if (global_ptr.next_in_node == .empty) {
2774 assert(elf.node_global_symbols.fetchSwapRemove(old_node).?.value == global_name);
2775 } else {
2776 elf.node_global_symbols.getPtr(old_node).?.* = global_ptr.next_in_node;
2777 }
2778 }
2779 } else {
2780 assert(global_ptr.next_in_node == .empty);
2781 assert(global_ptr.prev_in_node == .empty);
2782 }
2783
2784 if (elf.copied_globals.fetchSwapRemove(global_name)) |copied_global_kv| {
2785 // This is a quite rare case: there was a definition for this symbol in a shared library
2786 // input, and we ended up emitting a copy relocation for it, but we've now got our *own*
2787 // definition which replaces it. We know that our definition cannot be preempted because we
2788 // are the executable (only executables can have copy relocations!), so we definitely do not
2789 // need the copy relocation.
2790
2791 // All we actually need to do is remove the entry from `copied_globals` (already done), and
2792 // delete the actual `R_*_COPY` relocation. Of course, we also need to re-apply relocations
2793 // targeting this symbol, but we were going to do that at the end of this function anyway.
2794 elf.shndx.rela_dyn.relaDeleteOne(elf, copied_global_kv.value.rela_index);
2795 // TODO: once `MappedFile` has a way to delete a node (so it can re-use the space), we
2796 // should delete `copied_global_kv.value.node`, which is an "orphaned" `copied_global` node.
2797 } else {
2798 _ = elf.want_copied_globals.swapRemove(global_name);
2799 }
2800
2801 global_ptr.symtab_index.ptr(elf).node = new.node;
2802
2803 const old_head: String(.strtab) = old_head: {
2804 const new_node = new.node.unwrap() orelse break :old_head .empty;
2805 const gop = elf.node_global_symbols.getOrPutAssumeCapacity(new_node);
2806 const old_head: String(.strtab) = if (gop.found_existing) gop.value_ptr.* else .empty;
2807 gop.value_ptr.* = global_name;
2808 break :old_head old_head;
2809 };
2810
2811 global_ptr.prev_in_node = .empty;
2812 global_ptr.next_in_node = old_head;
2813
2814 if (old_head != .empty) {
2815 const old_head_ptr = elf.globalByName(old_head).?;
2816 assert(old_head_ptr.symtab_index.ptr(elf).node == new.node);
2817 assert(old_head_ptr.prev_in_node == .empty);
2818 old_head_ptr.prev_in_node = global_name;
2819 }
2820
2821 // Now for the easy bit where we actually update the symtab entry.
2822 switch (elf.symPtr(global_ptr.symtab_index)) {
2823 inline else => |sym| {
2824 // Don't bother with `sym.value` here: it'll be updated by `flushMoved`.
2825 elf.targetStore(&sym.size, @intCast(new.size));
2826 elf.targetStore(&sym.shndx, new.shndx.toSection().?);
2827 const old_bind = elf.targetLoad(&sym.info).bind;
2828 elf.targetStore(&sym.info, .{
2829 .type = new.type,
2830 .bind = old_bind,
2831 });
2832 },
2833 }
2834
2835 // ...and also the dynsym entry if there is one.
2836 if (global_ptr.dynsym_index != 0) switch (elf.dynsymPtr(global_ptr.dynsym_index)) {
2837 inline else => |sym| {
2838 // Don't bother with `sym.value` here: it'll be updated by `flushMoved`.
2839 elf.targetStore(&sym.size, @intCast(new.size));
2840 elf.targetStore(&sym.shndx, new.shndx.toSection().?);
2841 const old_bind = elf.targetLoad(&sym.info).bind;
2842 elf.targetStore(&sym.info, .{
2843 .type = new.type,
2844 .bind = old_bind,
2845 });
2846 },
2847 };
2848
2849 // If this symbol was previously undefined, it may have had a PLT entry. If so, we now need to
2850 // delete its newly-unnecessary runtime relocation to avoid a runtime dynamic linker error.
2851 // This also allows the PLT entry to be reused---see `pltEntryIsDead`.
2852 if (elf.plt.getIndex(global_name)) |plt_index| {
2853 if (!elf.pltEntryIsDead(plt_index) and
2854 elf.classifySymbolValue(.global(global_name)) != .dynamic)
2855 {
2856 elf.shndx.rela_plt.relaDeleteOne(elf, @fromBackingInt(@intCast(plt_index)));
2857 assert(elf.pltEntryIsDead(plt_index));
2858 }
2859 }
2860
2861 // If this symbol was previously undefined, relocations targeting it may have been lowered to
2862 // runtime relocations which we have now discovered we do not need, so delete those. This does
2863 // not apply if the symbol is preemptible, which we check with `classifySymbolValue`.
2864 if (elf.shndx.dynamic != .UNDEF and elf.classifySymbolValue(.global(global_name)) != .dynamic) {
2865 Symbol.Id.global(global_name).deleteDynamicTargetRelocs(elf);
2866 }
2867
2868 // Finally, update the symbol value, re-applying target relocations. Also note that because we
2869 // possibly removed the PLT entry above, some relocations which were previously targeting the
2870 // PLT will now instead target the symbol itself.
2871 Symbol.Id.global(global_name).flushMoved(elf, new.value);
2872}
2873/// When the same global symbol appears in two inputs---even if one symbol is defined and the other
2874/// undefined---their visibility values are combined to determine the resulting visibility, which
2875/// can also affect the bind of the symbol we output.
2876fn mergeGlobalSymbolVisibility(elf: *Elf, global_ptr: *Symbol.Global, other_visibility: std.elf.STV, bind: enum { strong, weak }) void {
2877 const old_visibility: std.elf.STV = switch (elf.symPtr(global_ptr.symtab_index)) {
2878 inline else => |sym| elf.targetLoad(&sym.other).visibility,
2879 };
2880 // The combined visibility is essentially the "strictest" of the two, with most strict being
2881 // INTERNAL, followed by HIDDEN, PROTECTED, DEFAULT.
2882 const new_visibility: std.elf.STV, const newly_hidden: bool = switch (old_visibility) {
2883 .INTERNAL => .{ .INTERNAL, false },
2884 .HIDDEN => switch (other_visibility) {
2885 .INTERNAL => .{ .INTERNAL, false },
2886 .HIDDEN, .PROTECTED, .DEFAULT => .{ .HIDDEN, false },
2887 },
2888 .PROTECTED => switch (other_visibility) {
2889 .INTERNAL => .{ .INTERNAL, true },
2890 .HIDDEN => .{ .HIDDEN, true },
2891 .PROTECTED, .DEFAULT => .{ .PROTECTED, false },
2892 },
2893 .DEFAULT => switch (other_visibility) {
2894 .INTERNAL => .{ .INTERNAL, true },
2895 .HIDDEN => .{ .HIDDEN, true },
2896 .PROTECTED => .{ .PROTECTED, false },
2897 .DEFAULT => .{ .DEFAULT, false },
2898 },
2899 };
2900 // If the symbol is HIDDEN/INTERNAL and we're emitting an ELF module (executable or shared
2901 // object), then the symbol should have binding STB_LOCAL in the output. Therefore, if we are
2902 // putting the global in this state for the first time---let's call it "demoting" the global to
2903 // STB_LOCAL---we need to update its bind in the symtab.
2904 const demote_to_local = newly_hidden and elf.ehdrType() != .REL;
2905 switch (elf.symPtr(global_ptr.symtab_index)) {
2906 inline else => |sym, class| {
2907 const old_info = elf.targetLoad(&sym.info);
2908 const new_info: class.ElfN().Sym.Info = .{
2909 .type = old_info.type,
2910 .bind = if (demote_to_local) b: {
2911 assert(old_info.bind != .LOCAL);
2912 break :b .LOCAL;
2913 } else if (old_info.bind == .LOCAL) .LOCAL else switch (bind) {
2914 .strong => .GLOBAL,
2915 .weak => .WEAK,
2916 },
2917 };
2918 elf.targetStore(&sym.other, .{ .visibility = new_visibility });
2919 elf.targetStore(&sym.info, new_info);
2920 // also update dynsym
2921 if (global_ptr.dynsym_index != 0) {
2922 const dynsym = @field(elf.dynsymPtr(global_ptr.dynsym_index), @tagName(class));
2923 elf.targetStore(&dynsym.other, .{ .visibility = new_visibility });
2924 elf.targetStore(&dynsym.info, new_info);
2925 }
2926 },
2927 }
2928 if (demote_to_local) {
2929 // When demoting a global to STB_LOCAL, we need to move its symtab index so that it is with
2930 // the STB_LOCAL symbols instead of the global symbols.
2931 elf.moveDemotedGlobal(global_ptr);
2932 }
2933}
2934/// If a symbol which was STB_GLOBAL/STB_WEAK becomes STB_LOCAL (see `mergeGlobalSymbolVisibility`),
2935/// the symbol must be moved from the "globals" part of the symtab to the "locals" part, because ELF
2936/// requires that all STB_LOCAL symbols in a symbol table appear before any global symbols.
2937fn moveDemotedGlobal(elf: *Elf, global_ptr: *Symbol.Global) void {
2938 assert(elf.ehdrType() != .REL); // demotion only happens when emitting an ELF module
2939 switch (elf.shdrPtr(.symtab)) {
2940 inline else => |shdr, class| {
2941 // `shdr.info` stores the index of the first global symbol. We are going to swap the
2942 // demoted symbol with that first global symbol, then increment that start index.
2943 const dest_index: Symbol.Index = @fromBackingInt(elf.targetLoad(&shdr.info));
2944 const src_index = global_ptr.symtab_index;
2945
2946 // This global should currently be in the "global symbols" part of the symtab, since our
2947 // job is to move it *out* of that part:
2948 assert(@backingInt(src_index) >= @backingInt(dest_index));
2949
2950 elf.targetStore(&shdr.info, @backingInt(dest_index) + 1);
2951
2952 if (src_index != dest_index) {
2953 // The demoted global was not the first global in the symtab, so we need to swap it
2954 // to its new location.
2955
2956 const src_sym_ptr = @field(elf.symPtr(src_index), @tagName(class));
2957 const dest_sym_ptr = @field(elf.symPtr(dest_index), @tagName(class));
2958
2959 const this_name: String(.strtab) = @fromBackingInt(elf.targetLoad(&src_sym_ptr.name));
2960 assert(elf.globalByName(this_name).? == global_ptr);
2961
2962 const other_name: String(.strtab) = @fromBackingInt(elf.targetLoad(&dest_sym_ptr.name));
2963 const other_global_ptr = elf.globalByName(other_name).?;
2964 assert(other_global_ptr.symtab_index == dest_index);
2965
2966 // First swap the symtab entries...
2967 std.mem.swap(class.ElfN().Sym, src_sym_ptr, dest_sym_ptr);
2968 // ...then the `elf.symtab` metadata...
2969 std.mem.swap(Symbol, src_index.ptr(elf), dest_index.ptr(elf));
2970 // ...then update the `elf.globals` tracking.
2971 global_ptr.symtab_index = dest_index;
2972 other_global_ptr.symtab_index = src_index;
2973 }
2974
2975 // We also need to get rid of the dynsym entry if there is one. To keep dynsym compact,
2976 // we'll move another symbol into its place just like we did above.
2977 if (global_ptr.dynsym_index != 0) {
2978 const dynsym_shdr = @field(elf.shdrPtr(elf.shndx.dynsym), @tagName(class));
2979
2980 const ent_size = @sizeOf(class.ElfN().Sym);
2981 assert(elf.targetLoad(&dynsym_shdr.entsize) == ent_size);
2982
2983 // We're going to decrease the size of `.dynsym`, thereby removing its last index.
2984 const old_size = elf.targetLoad(&dynsym_shdr.size);
2985 const new_size = old_size - ent_size;
2986 const remove_dynsym_index: u32 = @intCast(@divExact(new_size, ent_size));
2987
2988 elf.popDynsymHashEntry(remove_dynsym_index);
2989
2990 const free_dynsym_index = global_ptr.dynsym_index;
2991 global_ptr.dynsym_index = 0;
2992
2993 if (free_dynsym_index != remove_dynsym_index) {
2994 // The demoted global wasn't the last entry, so move whatever entry we just
2995 // truncated out of dynsym into its place.
2996
2997 elf.clearDynsymHashEntry(free_dynsym_index);
2998
2999 const src_dynsym_ptr = @field(elf.dynsymPtr(remove_dynsym_index), @tagName(class));
3000 const dest_dynsym_ptr = @field(elf.dynsymPtr(free_dynsym_index), @tagName(class));
3001
3002 const moved_name_dynstr: String(.dynstr) = @fromBackingInt(elf.targetLoad(&src_dynsym_ptr.name));
3003 const moved_name = elf.stringExisting(.strtab, moved_name_dynstr.slice(elf));
3004 const moved_global_ptr = elf.globalByName(moved_name).?;
3005
3006 dest_dynsym_ptr.* = src_dynsym_ptr.*;
3007
3008 assert(moved_global_ptr.dynsym_index == remove_dynsym_index);
3009 moved_global_ptr.dynsym_index = free_dynsym_index;
3010
3011 elf.populateDynsymHashEntry(free_dynsym_index);
3012
3013 // Since that symbol's dynsym index has changed, we'll have to update any
3014 // relocation entries targeting it.
3015 elf.changed_symtab_index.putAssumeCapacity(moved_name, {});
3016 }
3017
3018 // Now that we've given that symbol a new home, actually decrease the section size.
3019 elf.targetStore(&dynsym_shdr.size, new_size);
3020 }
3021 },
3022 }
3023}
3024
3025const Symbol = struct {
3026 /// The node which this symbol's value is defined relative to. Possible values are:
3027 /// * `.none` for a SHN_ABS or SHN_UNDEF symbol
3028 /// * A section (the symbol's value is some vaddr in that section)
3029 /// * An input section (the symbol's value is some vaddr in that input section)
3030 /// * A NAV, UAV, or lazy code/data (the symbol's value is exactly the vaddr of that node)
3031 node: MappedFile.Node.Index.Optional,
3032
3033 /// The head of a linked list of relocations targeting this symbol.
3034 first_target_reloc: SymbolReloc.Index,
3035
3036 const Global = struct {
3037 /// The current index of the symtab entry for this global symbol.
3038 symtab_index: Symbol.Index,
3039 /// The current index of the dynsym entry for this global symbol. If the global has been
3040 /// demoted to STB_LOCAL, it does not have a dynsym entry and this field is set to 0.
3041 dynsym_index: u32,
3042
3043 /// The next entry in a linked list of global symbols with the same `Symbol.node` value.
3044 ///
3045 /// If `node` is `.none`, this is `.empty`.
3046 next_in_node: String(.strtab),
3047 /// The previous entry in a linked list of global symbols with the same `Symbol.node` value.
3048 ///
3049 /// If `node` is `.none`, this is `.empty`.
3050 prev_in_node: String(.strtab),
3051 };
3052
3053 /// An index directly into the symtab. These values are not stable (global symbols are sometimes
3054 /// moved to new locations in the symtab) and therefore should only be used ephemerally.
3055 ///
3056 /// Local symbols *do* have stable indices into the symtab; see `LocalIndex`.
3057 ///
3058 /// For a stable reference to an arbitrary symbol, see `Id`.
3059 const Index = enum(u32) {
3060 null = 0,
3061 _,
3062
3063 fn ptr(si: Symbol.Index, elf: *Elf) *Symbol {
3064 return &elf.symtab.items[@backingInt(si)];
3065 }
3066 };
3067
3068 /// A `LocalIndex` is a raw index into the symtab like `Index`, but it guarantees that the
3069 /// symbol in question has STB_LOCAL binding, which guarantees that its symtab index is stable
3070 /// so can be stored long-term without needing to be updated
3071 ///
3072 /// This is because symbols which have STB_LOCAL binding in the output file gain fixed symtab
3073 /// indices, thanks to a combination of a few factors:
3074 /// * We never remove STB_LOCAL symbols
3075 /// * There is no symbol ordering requirement *within* the leading range of STB_LOCAL symbols
3076 /// * A symbol visibility which demotes a global to STB_LOCAL binding can never be reverted by
3077 /// a subsequent operation (different visibilities resolve to the "strictest" one)
3078 const LocalIndex = enum(u32) {
3079 null = 0,
3080 _,
3081
3082 fn index(li: LocalIndex) Index {
3083 return @fromBackingInt(@backingInt(li));
3084 }
3085 };
3086
3087 /// Opaque, stable identifier for a symbol. Does not necessarily equal the index into the symtab.
3088 const Id = packed struct(u32) {
3089 kind: enum(u1) { local, global },
3090 raw: u31,
3091
3092 const @"null": Symbol.Id = .local(.null);
3093
3094 fn local(lsi: Symbol.LocalIndex) Symbol.Id {
3095 return .{ .kind = .local, .raw = @intCast(@backingInt(lsi)) };
3096 }
3097 fn global(name: String(.strtab)) Symbol.Id {
3098 return .{ .kind = .global, .raw = @intCast(@backingInt(name)) };
3099 }
3100 fn unwrap(s: Symbol.Id) union(enum) {
3101 local: Symbol.LocalIndex,
3102 global: String(.strtab),
3103 } {
3104 return switch (s.kind) {
3105 .local => .{ .local = @fromBackingInt(s.raw) },
3106 .global => .{ .global = @fromBackingInt(s.raw) },
3107 };
3108 }
3109
3110 fn toTypeErased(s: Symbol.Id) link.File.SymbolId {
3111 return @bitCast(s);
3112 }
3113 fn fromTypeErased(s: link.File.SymbolId) Symbol.Id {
3114 return @bitCast(s);
3115 }
3116
3117 fn index(s: Symbol.Id, elf: *const Elf) Symbol.Index {
3118 return switch (s.unwrap()) {
3119 .local => |lsi| lsi.index(),
3120 .global => |name| elf.globalByName(name).?.symtab_index,
3121 };
3122 }
3123
3124 /// Returns the value of this symbol, or 0 if it is undefined. If the symbol is an undefined
3125 /// global for which we have emitted a copy relocation, returns the virtual address of that
3126 /// copy relocation, which the symbol is guaranteed to resolve to at runtime.
3127 fn value(s: Symbol.Id, elf: *Elf) u64 {
3128 return switch (elf.symPtr(s.index(elf))) {
3129 inline else => |sym| elf.targetLoad(&sym.value),
3130 };
3131 }
3132
3133 fn flushMoved(sym_id: Symbol.Id, elf: *Elf, new_value: u64) void {
3134 // Update the symbol value in `.symtab`
3135 const sym_index = sym_id.index(elf);
3136 switch (elf.symPtr(sym_index)) {
3137 inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)),
3138 }
3139
3140 // Update the symbol value in `.dynsym` if applicable
3141 switch (sym_id.unwrap()) {
3142 .local => {},
3143 .global => |name| {
3144 const g = elf.globalByName(name).?;
3145 if (g.dynsym_index != 0) {
3146 switch (elf.dynsymPtr(g.dynsym_index)) {
3147 inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)),
3148 }
3149 }
3150 },
3151 }
3152
3153 // Re-apply relocations targeting this symbol
3154 if (elf.ehdrType() != .REL) {
3155 sym_id.applyTargetRelocs(elf);
3156 }
3157
3158 // Update GOT entries targeting this symbol
3159 if (elf.got.getIndex(.{ .symbol = sym_id })) |got_index| {
3160 elf.updateGotEntry(got_index);
3161 }
3162 if (elf.got.getIndex(.{ .tpoff = sym_id })) |got_index| {
3163 elf.updateGotEntry(got_index);
3164 }
3165 if (elf.got.getIndex(.{ .tlsgd0 = sym_id })) |got_index| {
3166 elf.updateGotEntry(got_index);
3167 elf.updateGotEntry(got_index + 1); // tlsgd1
3168 }
3169 }
3170
3171 fn applyTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void {
3172 assert(elf.ehdrType() != .REL);
3173 var ri = sym_id.index(elf).ptr(elf).first_target_reloc;
3174 while (ri != .none) {
3175 const reloc = ri.get(elf);
3176 assert(reloc.target == sym_id);
3177 reloc.apply(elf);
3178 ri = reloc.next;
3179 }
3180 }
3181
3182 /// Scans through all relocations targeting `sym_id` and, for each one with a dynamic
3183 /// relocation entry, either deletes it or converts it to R_*_RELATIVE as required.
3184 ///
3185 /// Asserts we are creating a DSO.
3186 fn deleteDynamicTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void {
3187 assert(elf.ehdrType() != .REL);
3188 assert(elf.shndx.dynamic != .UNDEF);
3189 var ri = sym_id.index(elf).ptr(elf).first_target_reloc;
3190 while (ri != .none) {
3191 const reloc = ri.get(elf);
3192 assert(reloc.target == sym_id);
3193 reloc.deleteOutputRel(elf);
3194 ri = reloc.next;
3195 }
3196 switch (elf.classifySymbolValue(sym_id)) {
3197 .static => return,
3198 .static_relative => {},
3199 .dynamic => unreachable,
3200 }
3201 // We removed the symbol relocations, now add R_*_RELATIVE relocations where needed.
3202 ri = sym_id.index(elf).ptr(elf).first_target_reloc;
3203 while (ri != .none) {
3204 const reloc = ri.get(elf);
3205 ri = reloc.next;
3206 assert(reloc.target == sym_id);
3207 switch (reloc.type.target) {
3208 // Only relocations which resolve to absolute addresses require runtime
3209 // `R_*_RELATIVE` relocations.
3210 .special,
3211 .pltrel,
3212 .rel,
3213 .dtpoff,
3214 .tpoff,
3215 .size,
3216 => continue,
3217
3218 .abs, .pltabs => {},
3219 }
3220 if (!reloc.type.action.simple.dest.isAddr(elf)) continue;
3221 const node = reloc.node.unwrap().?;
3222 switch (elf.nodeWantsDsoRelocation(node)) {
3223 .no => continue,
3224 .yes_textrel => elf.textrel_count += 1,
3225 .yes => {},
3226 }
3227 // There is capacity for a relocation because we just deleted one earlier.
3228 reloc.rela_index = elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
3229 .type = .relative(elf),
3230 .offset = elf.getNodeVAddr(node) + reloc.offset,
3231 .raw_sym_index = 0,
3232 .addend = 0,
3233 }).toOptional();
3234 }
3235 }
3236
3237 /// Returns `true` if the target of `s` has moved, meaning the symbol's value will change at
3238 /// some point due to a call to `flushMoved`.
3239 fn hasMoved(s: Symbol.Id, elf: *Elf) bool {
3240 if (s.index(elf).ptr(elf).node.unwrap()) |node| {
3241 return node.hasMoved(&elf.mf);
3242 }
3243 switch (s.unwrap()) {
3244 .local => {},
3245 .global => |name| if (elf.copied_globals.getPtr(name)) |copied_global| {
3246 return copied_global.node.hasMoved(&elf.mf);
3247 },
3248 }
3249 return false;
3250 }
3251 };
3252};
3253
3254fn globalByName(elf: *const Elf, name: String(.strtab)) ?*Symbol.Global {
3255 if (elf.globals.strong_def.getPtr(name)) |ptr| return ptr;
3256 if (elf.globals.weak_def.getPtr(name)) |ptr| return ptr;
3257 if (elf.globals.strong_undef.getPtr(name)) |ptr| return ptr;
3258 if (elf.globals.weak_undef.getPtr(name)) |ptr| return ptr;
3259 return null;
3260}
3261
3262fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {
3263 /// This symbol's value is guaranteed to equal `sym.value(elf)`.
3264 static,
3265 /// This symbol's value is an offset of `sym.value(elf)` from the runtime-known load address of
3266 /// this DSO (which is position-independent).
3267 static_relative,
3268 /// This symbol's definition does not necessarily come from this DSO, so is not known until RTLD
3269 /// runs. Therefore, a dynamic (runtime) relocation is necessary.
3270 dynamic,
3271} {
3272 const comp = elf.base.comp;
3273
3274 const runtime_load_addr = switch (elf.ehdrType()) {
3275 .REL => unreachable,
3276 .DYN => true,
3277 .EXEC => false,
3278 };
3279
3280 if (elf.shndx.dynamic == .UNDEF) {
3281 // This is a static non-PIE executable---every symbol has a statically known value.
3282 return .static;
3283 }
3284
3285 const shndx: Section.Index, const visibility: std.elf.STV = switch (elf.symPtr(sym.index(elf))) {
3286 inline else => |sym_ptr| .{
3287 .fromSection(elf.targetLoad(&sym_ptr.shndx)),
3288 elf.targetLoad(&sym_ptr.other).visibility,
3289 },
3290 };
3291
3292 switch (sym.unwrap()) {
3293 .local => {
3294 assert(shndx != .UNDEF);
3295 assert(visibility == .DEFAULT);
3296 },
3297 .global => |name| if (visibility == .DEFAULT and comp.config.output_mode != .Exe) {
3298 // An unprotected symbol in a DSO which is not an executable is subject to runtime
3299 // preemption, so a dynamic relocation is required for it even if we have a definition.
3300 return .dynamic;
3301 } else if (elf.copied_globals.contains(name)) {
3302 // This becomes a locally-defined symbol in `.data`.
3303 return if (runtime_load_addr) .static_relative else .static;
3304 },
3305 }
3306
3307 return switch (shndx) {
3308 .UNDEF => switch (visibility) {
3309 .DEFAULT => if (comp.config.link_mode == .static and comp.config.output_mode == .Exe) {
3310 assert(comp.config.pie); // non-PIE static exe should not have a `.dynamic` section
3311 // This is a static PIE---the only dynamic relocations are `R_*_RELATIVE`.
3312 return .static;
3313 } else .dynamic, // external symbol
3314
3315 // If the symbol *cannot* be external, then there's no point making a dynamic relocation
3316 // now---if linking succeeds we won't need anything more than perhaps an `R_*_RELATIVE`.
3317 .INTERNAL, .HIDDEN, .PROTECTED => .static,
3318 },
3319
3320 .ABS => .static,
3321
3322 else => if (runtime_load_addr and
3323 shndx.flags(elf).ALLOC and
3324 !shndx.flags(elf).TLS)
3325 {
3326 return .static_relative;
3327 } else {
3328 return .static;
3329 },
3330 };
3331}
3332
3333pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId {
3334 const lsi: Symbol.LocalIndex = switch (elf.getNode(Node.fromAtom(atom))) {
3335 .deleted,
3336 .archive,
3337 .archive_header,
3338 .archive_input_member,
3339 .archive_elf_member_header,
3340 .elf,
3341 .ehdr,
3342 .shdr,
3343 .segment,
3344 .section,
3345 .section_manual_size,
3346 .input_section,
3347 .copied_global,
3348 .debug_shared,
3349 .eh_frame_footer,
3350 .unit_padding,
3351 .unit_frame,
3352 .unit_frame_cie,
3353 .unit_debug_info,
3354 .unit_debug_info_header,
3355 .unit_debug_info_footer,
3356 .unit_debug_line,
3357 .unit_debug_line_header,
3358 .unit_debug_rnglists,
3359 .const_debug_info,
3360 .global_debug_info,
3361 .func_frame_fde,
3362 .func_debug_info,
3363 .func_debug_line,
3364 .decl_debug_info,
3365 => unreachable,
3366 inline .nav,
3367 .uav,
3368 .lazy_code,
3369 .lazy_const_data,
3370 => |i| i.symbol(elf),
3371 };
3372 const s: Symbol.Id = .local(lsi);
3373 return s.toTypeErased();
3374}
3375pub fn lazySymbol(elf: *Elf, lazy: link.File.LazySymbol) link.Error!link.File.SymbolId {
3376 return elf.lazySymbolInner(lazy) catch |err| switch (err) {
3377 else => |e| return e,
3378 error.MappedFileIo => return elf.base.comp.link_diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
3379 };
3380}
3381fn lazySymbolInner(elf: *Elf, lazy: link.File.LazySymbol) Error!link.File.SymbolId {
3382 const gpa = elf.base.comp.gpa;
3383
3384 try elf.ensureUnusedSymbolCapacity(1, .all_local);
3385 try elf.nodes.ensureUnusedCapacity(gpa, 1);
3386 try elf.lazy.getPtr(lazy.kind).map.ensureUnusedCapacity(gpa, 1);
3387
3388 const gop = elf.lazy.getPtr(lazy.kind).map.getOrPutAssumeCapacity(lazy.ty);
3389 if (!gop.found_existing) {
3390 const shndx: Section.Index, const sym_type: std.elf.STT = switch (lazy.kind) {
3391 .code => .{ .text, .FUNC },
3392 .const_data => .{ .rodata, .OBJECT },
3393 };
3394 const node = elf.addNodeAssumeCapacity(
3395 try shndx.get(elf).ni.addFloatingChild(gpa, &elf.mf, .{}),
3396 switch (lazy.kind) {
3397 .code => .{ .lazy_code = @fromBackingInt(@intCast(gop.index)) },
3398 .const_data => .{ .lazy_const_data = @fromBackingInt(@intCast(gop.index)) },
3399 },
3400 );
3401 var name_buf: [std.fmt.count("__lazy_const_data_{d}", .{std.math.maxInt(u32)})]u8 = undefined;
3402 const name = std.mem.print(&name_buf, "__lazy_{t}_{d}", .{ lazy.kind, gop.index }) catch
3403 unreachable;
3404 gop.value_ptr.* = .{
3405 .lsi = elf.addLocalSymbolAssumeCapacity(.{
3406 .node = .wrap(node),
3407 .name = try elf.string(.strtab, name),
3408 .value = 0,
3409 .size = 0,
3410 .type = sym_type,
3411 .shndx = shndx,
3412 }),
3413 .first_symbol_reloc = .none,
3414 .first_got_reloc = .none,
3415 };
3416 elf.base.comp.link_prog_node.increaseEstimatedTotalItems(1);
3417 }
3418 const s: Symbol.Id = .local(gop.value_ptr.lsi);
3419 return s.toTypeErased();
3420}
3421pub const ExternSymbolOpts = struct {
3422 name: []const u8,
3423 lib_name: ?[]const u8,
3424 type: std.elf.STT,
3425 linkage: std.lang.GlobalLinkage = .strong,
3426 visibility: std.lang.SymbolVisibility = .default,
3427};
3428pub fn externSymbol(elf: *Elf, opts: ExternSymbolOpts) link.Error!link.File.SymbolId {
3429 const diags = &elf.base.comp.link_diags;
3430 return (elf.externSymbolInner(opts) catch |err| switch (err) {
3431 else => |e| return e,
3432 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
3433 }).toTypeErased();
3434}
3435fn externSymbolInner(elf: *Elf, opts: ExternSymbolOpts) Error!Symbol.Id {
3436 try elf.ensureUnusedSymbolCapacity(1, .maybe_global);
3437 const symbol = elf.addGlobalSymbolAssumeCapacity(.{
3438 .node = .none,
3439 .name = try .string(elf, opts.name),
3440 .lib_name = opts.lib_name,
3441 .value = 0,
3442 .size = 0,
3443 .type = opts.type,
3444 .bind = switch (opts.linkage) {
3445 .strong => .strong,
3446 .weak => .weak,
3447 .internal => return elf.base.comp.link_diags.fail("TODO(Elf2): '.internal' linkage", .{}),
3448 .link_once => return elf.base.comp.link_diags.fail("TODO(Elf2): '.link_once' linkage", .{}),
3449 },
3450 .visibility = switch (opts.visibility) {
3451 .default => .DEFAULT,
3452 .hidden => .HIDDEN,
3453 .protected => .PROTECTED,
3454 },
3455 .shndx = .UNDEF,
3456 }) catch |err| switch (err) {
3457 error.MultipleDefinitions => unreachable, // shndx is undef
3458 };
3459 return symbol;
3460}
3461pub fn addReloc(
3462 elf: *Elf,
3463 atom: link.File.AtomId,
3464 offset: u64,
3465 target: link.File.SymbolId,
3466 addend: i64,
3467 @"type": MachineRelocType,
3468) link.Error!void {
3469 const node: MappedFile.Node.Index = Node.fromAtom(atom);
3470 const diags = &elf.base.comp.link_diags;
3471 elf.ensureUnusedRelocCapacity(node, 1) catch |err| switch (err) {
3472 else => |e| return e,
3473 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
3474 };
3475 elf.addRelocAssumeCapacity(node, offset, .fromTypeErased(target), addend, @"type") catch |err| switch (err) {
3476 else => |e| return e,
3477 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
3478 error.UnknownRelocation => unreachable, // codegen bug
3479 error.NonStaticRelocation => unreachable, // codegen bug
3480 error.UnimplementedRelocation => unreachable, // codegen bug (asking Elf2 for a relocation it does not support)
3481 };
3482}
3483pub fn addNodeReloc(
3484 elf: *Elf,
3485 node: MappedFile.Node.Index,
3486 offset: u64,
3487 target: MappedFile.Node.Index,
3488 addend: i64,
3489 @"type": NodeReloc.Type,
3490) link.Error!void {
3491 const diags = &elf.base.comp.link_diags;
3492 elf.ensureUnusedRelocCapacity(node, 1) catch |err| switch (err) {
3493 else => |e| return e,
3494 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
3495 };
3496 elf.addNodeRelocAssumeCapacity(node, offset, target, addend, @"type") catch |err| switch (err) {
3497 else => |e| return e,
3498 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
3499 };
3500}
3501pub fn navSymbol(elf: *Elf, nav_index: InternPool.Nav.Index) link.Error!link.File.SymbolId {
3502 const diags = &elf.base.comp.link_diags;
3503 const zcu = elf.base.comp.zcu.?;
3504 const ip = &zcu.intern_pool;
3505 const nav = ip.getNav(nav_index);
3506 if (nav.getExtern(ip)) |@"extern"| {
3507 return elf.externSymbol(.{
3508 .name = @"extern".name.toSlice(ip),
3509 .lib_name = @"extern".lib_name.toSlice(ip),
3510 .type = elf.navType(nav.resolved.?),
3511 .linkage = @"extern".linkage,
3512 .visibility = @"extern".visibility,
3513 });
3514 }
3515 const nmi = elf.navMapIndex(zcu, nav_index) catch |err| switch (err) {
3516 else => |e| return e,
3517 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
3518 };
3519 const s: Symbol.Id = .local(nmi.symbol(elf));
3520 return s.toTypeErased();
3521}
3522pub fn uavSymbol(
3523 elf: *Elf,
3524 uav_val: InternPool.Index,
3525 uav_align: InternPool.Alignment,
3526) link.Error!link.File.SymbolId {
3527 const diags = &elf.base.comp.link_diags;
3528 const umi = elf.uavMapIndex(uav_val, uav_align) catch |err| switch (err) {
3529 else => |e| return e,
3530 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
3531 };
3532 const s: Symbol.Id = .local(umi.symbol(elf));
3533 return s.toTypeErased();
3534}
3535pub fn getNavVAddr(
3536 elf: *Elf,
3537 pt: Zcu.PerThread,
3538 nav: InternPool.Nav.Index,
3539 reloc_info: link.File.RelocInfo,
3540) link.Error!u64 {
3541 _ = pt;
3542 return elf.getVAddr(reloc_info, try elf.navSymbol(nav));
3543}
3544pub fn getUavVAddr(
3545 elf: *Elf,
3546 uav_val: InternPool.Index,
3547 reloc_info: link.File.RelocInfo,
3548) link.Error!u64 {
3549 return elf.getVAddr(reloc_info, try elf.uavSymbol(uav_val, .none));
3550}
3551pub fn getVAddr(elf: *Elf, reloc_info: link.File.RelocInfo, target: link.File.SymbolId) link.Error!u64 {
3552 try elf.addReloc(
3553 switch (reloc_info.parent) {
3554 .none => unreachable,
3555 .atom_index => |atom_id| atom_id,
3556 .debug_output => |debug_output| Node.toAtom(debug_output.dwarf2.info_writer.ni),
3557 },
3558 reloc_info.offset,
3559 target,
3560 reloc_info.addend,
3561 .absAddr(elf),
3562 );
3563 return Symbol.Id.fromTypeErased(target).value(elf);
3564}
3565pub fn lowerUav(
3566 elf: *Elf,
3567 pt: Zcu.PerThread,
3568 uav_val: InternPool.Index,
3569 uav_align: InternPool.Alignment,
3570) link.Error!link.File.SymbolId {
3571 _ = pt;
3572 const diags = &elf.base.comp.link_diags;
3573 const umi = elf.uavMapIndex(uav_val, uav_align) catch |err| switch (err) {
3574 else => |e| return e,
3575 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
3576 };
3577 const s: Symbol.Id = .local(umi.symbol(elf));
3578 return s.toTypeErased();
3579}
3580
3581const StringSection = enum {
3582 shstrtab,
3583 strtab,
3584 dynstr,
3585 fn shndx(s: StringSection, elf: *const Elf) Section.Index {
3586 return switch (s) {
3587 .strtab => .strtab,
3588 .shstrtab => .shstrtab,
3589 .dynstr => elf.shndx.dynstr,
3590 };
3591 }
3592};
3593fn String(section: StringSection) type {
3594 return enum(u32) {
3595 empty = 0,
3596 _,
3597
3598 fn slice(str: @This(), elf: *Elf) [:0]const u8 {
3599 const section_node = section.shndx(elf).get(elf).ni;
3600 const overlong = section_node.sliceConst(&elf.mf)[@backingInt(str)..];
3601 return overlong[0..std.mem.findScalar(u8, overlong, 0).? :0];
3602 }
3603 };
3604}
3605fn string(elf: *Elf, comptime section: StringSection, key: []const u8) Error!String(section) {
3606 const st: *StringTable = &@field(elf, @tagName(section));
3607 return @fromBackingInt(try st.get(elf, section.shndx(elf), key));
3608}
3609/// Like `string`, but asserts that the string is already in `section`.
3610fn stringExisting(elf: *Elf, comptime section: StringSection, key: []const u8) String(section) {
3611 const st: *StringTable = &@field(elf, @tagName(section));
3612 return @fromBackingInt(st.getExisting(elf, section.shndx(elf), key));
3613}
3614
3615const StringTable = struct {
3616 map: std.HashMapUnmanaged(u32, void, StringTable.Context, std.hash_map.default_max_load_percentage),
3617
3618 const Context = struct {
3619 slice: []const u8,
3620
3621 pub fn eql(_: Context, lhs_key: u32, rhs_key: u32) bool {
3622 return lhs_key == rhs_key;
3623 }
3624
3625 pub fn hash(ctx: Context, key: u32) u64 {
3626 return std.hash_map.hashString(std.mem.sliceTo(ctx.slice[key..], 0));
3627 }
3628 };
3629
3630 const Adapter = struct {
3631 slice: []const u8,
3632
3633 pub fn eql(adapter: Adapter, lhs_key: []const u8, rhs_key: u32) bool {
3634 return std.mem.startsWith(u8, adapter.slice[rhs_key..], lhs_key) and
3635 adapter.slice[rhs_key + lhs_key.len] == 0;
3636 }
3637
3638 pub fn hash(_: Adapter, key: []const u8) u64 {
3639 assert(std.mem.findScalar(u8, key, 0) == null);
3640 return std.hash_map.hashString(key);
3641 }
3642 };
3643
3644 fn getExisting(st: *StringTable, elf: *Elf, shndx: Section.Index, key: []const u8) u32 {
3645 if (key.len == 0) return 0;
3646 const slice_const = shndx.get(elf).ni.sliceConst(&elf.mf);
3647 const adapter: StringTable.Adapter = .{ .slice = slice_const };
3648 return st.map.getKeyAdapted(key, adapter).?;
3649 }
3650
3651 fn get(st: *StringTable, elf: *Elf, shndx: Section.Index, key: []const u8) Error!u32 {
3652 // If we are in `initHeaders` the strtab might not be initalized yet, so we need to special
3653 // case the empty string.
3654 if (key.len == 0) return 0;
3655
3656 const gpa = elf.base.comp.gpa;
3657 const ni = shndx.get(elf).ni;
3658 const slice_const = ni.sliceConst(&elf.mf);
3659 const gop = try st.map.getOrPutContextAdapted(
3660 gpa,
3661 key,
3662 StringTable.Adapter{ .slice = slice_const },
3663 .{ .slice = slice_const },
3664 );
3665 if (gop.found_existing) return gop.key_ptr.*;
3666 const old_size, const new_size = size: switch (elf.shdrPtr(shndx)) {
3667 inline else => |shdr| {
3668 const old_size: u32 = @intCast(elf.targetLoad(&shdr.size));
3669 const new_size: u32 = @intCast(old_size + key.len + 1);
3670 elf.targetStore(&shdr.size, new_size);
3671 break :size .{ old_size, new_size };
3672 },
3673 };
3674 try ni.ensureMinimumSize(gpa, &elf.mf, new_size);
3675 const slice = ni.slice(&elf.mf)[old_size..];
3676 @memcpy(slice[0..key.len], key);
3677 slice[key.len] = 0;
3678 gop.key_ptr.* = old_size;
3679 return old_size;
3680 }
3681};
3682
3683pub fn open(
3684 arena: std.mem.Allocator,
3685 comp: *Compilation,
3686 path: std.Build.Cache.Path,
3687 options: link.File.OpenOptions,
3688) !*Elf {
3689 return create(arena, comp, path, options);
3690}
3691pub fn createEmpty(
3692 arena: std.mem.Allocator,
3693 comp: *Compilation,
3694 path: std.Build.Cache.Path,
3695 options: link.File.OpenOptions,
3696) !*Elf {
3697 return create(arena, comp, path, options);
3698}
3699fn create(
3700 arena: std.mem.Allocator,
3701 comp: *Compilation,
3702 path: std.Build.Cache.Path,
3703 options: link.File.OpenOptions,
3704) !*Elf {
3705 const io = comp.io;
3706 const target = &comp.root_mod.resolved_target.result;
3707 assert(target.ofmt == .elf);
3708 const class: std.elf.CLASS = switch (target.ptrBitWidth()) {
3709 0...32 => .@"32",
3710 33...64 => .@"64",
3711 else => return error.UnsupportedELFArchitecture,
3712 };
3713 const data: std.elf.DATA = switch (target.cpu.arch.endian()) {
3714 .little => .@"2LSB",
3715 .big => .@"2MSB",
3716 };
3717 const osabi: std.elf.OSABI = switch (target.os.tag) {
3718 else => if (target.abi.isGnu()) .GNU else .NONE,
3719 .freestanding, .other => .STANDALONE,
3720 .netbsd => .NETBSD,
3721 .illumos => .SOLARIS,
3722 .freebsd, .ps4 => .FREEBSD,
3723 .openbsd => .OPENBSD,
3724 .cuda => .CUDA,
3725 .amdhsa => .AMDGPU_HSA,
3726 .amdpal => .AMDGPU_PAL,
3727 .mesa3d => .AMDGPU_MESA3D,
3728 };
3729 const @"type": EhdrType = switch (comp.config.output_mode) {
3730 .Exe => if (comp.config.pie or target.os.tag == .haiku) .DYN else .EXEC,
3731 .Lib => switch (comp.config.link_mode) {
3732 .static => .REL,
3733 .dynamic => .DYN,
3734 },
3735 .Obj => .REL,
3736 };
3737 const machine = EhdrMachine.fromElf(target.toElfMachine()) orelse {
3738 std.debug.panic("TODO(Elf2): add support for target machine '{t}'", .{target.toElfMachine()});
3739 };
3740 const maybe_interp = switch (comp.config.link_mode) {
3741 .static => null,
3742 .dynamic => switch (comp.config.output_mode) {
3743 .Exe => target.dynamic_linker.get(),
3744 .Lib => if (comp.root_mod.resolved_target.is_explicit_dynamic_linker)
3745 target.dynamic_linker.get()
3746 else
3747 null,
3748 .Obj => null,
3749 },
3750 };
3751
3752 const elf = try arena.create(Elf);
3753 const file = try path.root_dir.handle.createFile(io, path.sub_path, .{
3754 .read = true,
3755 .permissions = link.File.determinePermissions(comp.config.output_mode, comp.config.link_mode),
3756 });
3757 errdefer file.close(io);
3758 elf.* = .{
3759 .base = .{
3760 .tag = .elf2,
3761
3762 .comp = comp,
3763 .emit = path,
3764
3765 .file = file,
3766 .gc_sections = false,
3767 .print_gc_sections = false,
3768 .build_id = .none,
3769 .allow_shlib_undefined = false,
3770 .stack_size = 0,
3771 },
3772 .options = options,
3773 .mf = try .init(file, comp.gpa, io),
3774 .ni = .{
3775 .elf = undefined,
3776 .ehdr = undefined,
3777 .shdr = undefined,
3778 .rodata = undefined,
3779 .phdr = undefined,
3780 .text = undefined,
3781 .data = undefined,
3782 .data_rel_ro = undefined,
3783 .tls = .none,
3784 .gnu_eh_frame = .none,
3785 },
3786 .archive = null,
3787 .nodes = .empty,
3788 .shdrs = .empty,
3789 .phdrs = .empty,
3790 .shndx = .{
3791 .got = .UNDEF,
3792 .got_plt = .UNDEF,
3793 .plt = .UNDEF,
3794 .plt_sec = .UNDEF,
3795 .dynsym = .UNDEF,
3796 .dynstr = .UNDEF,
3797 .dynamic = .UNDEF,
3798 .hash = .UNDEF,
3799 .tdata = .UNDEF,
3800 .rela_dyn = .UNDEF,
3801 .rela_plt = .UNDEF,
3802 .debug_abbrev = .UNDEF,
3803 .eh_frame_hdr = .UNDEF,
3804 .eh_frame = .UNDEF,
3805 .debug_frame = .UNDEF,
3806 .debug_info = .UNDEF,
3807 .debug_line = .UNDEF,
3808 .debug_line_str = .UNDEF,
3809 .debug_rnglists = .UNDEF,
3810 .debug_str = .UNDEF,
3811 .debug_str_offsets = .UNDEF,
3812 .init_array = .UNDEF,
3813 .fini_array = .UNDEF,
3814 .preinit_array = .UNDEF,
3815 },
3816 .dynamic = .{
3817 .flags = 0,
3818 .flags_1 = 0,
3819 .rpath = .empty,
3820 .soname = .empty,
3821 },
3822 .symtab = .empty,
3823 .globals = .{
3824 .strong_def = .empty,
3825 .weak_def = .empty,
3826 .strong_undef = .empty,
3827 .weak_undef = .empty,
3828 },
3829 .copied_globals = .empty,
3830 .want_copied_globals = .empty,
3831 .node_global_symbols = .empty,
3832 .dso_globals = .empty,
3833 .shstrtab = .{ .map = .empty },
3834 .strtab = .{ .map = .empty },
3835 .dynstr = .{ .map = .empty },
3836 .got = .empty,
3837 .plt = .empty,
3838 .plt_first_symbol_reloc = .none,
3839 .eh_frame_hdr_first_symbol_reloc = .none,
3840 .needed = .empty,
3841 .inputs = .empty,
3842 .input_pending_index = 0,
3843 .input_sections = .empty,
3844 .input_section_pending_index = 0,
3845 .one_shot_fixups = .empty,
3846 .navs = .empty,
3847 .uavs = .empty,
3848 .lazy = comptime .initFill(.{
3849 .map = .empty,
3850 .pending_index = 0,
3851 }),
3852 .pending_uavs = .empty,
3853 .symbol_relocs = .empty,
3854 .node_relocs = .empty,
3855 .got_relocs = .empty,
3856 .tls_size_symbol_relocs = .empty,
3857 .section_by_name = .empty,
3858 .changed_symtab_index = .empty,
3859 .textrel_count = 0,
3860
3861 .dwarf = .init(&elf.base, switch (comp.config.debug_format) {
3862 .strip => .@"32", // for .eh_frame
3863 .dwarf => |v| v,
3864 .code_view => unreachable,
3865 }),
3866 .dwarf_shared = comptime .initFill(.{
3867 .first_target_reloc = .none,
3868 }),
3869 .dwarf_units = &.{},
3870 .dwarf_consts = .empty,
3871 .dwarf_globals = .empty,
3872 .dwarf_funcs = .empty,
3873 .dwarf_decls = .empty,
3874
3875 .overflowed_reloc_count = 0,
3876 .misaligned_reloc_count = 0,
3877
3878 .const_prog_node = .none,
3879 .input_prog_node = .none,
3880 };
3881 errdefer elf.deinit();
3882
3883 try elf.initHeaders(class, data, osabi, @"type", machine, maybe_interp);
3884 return elf;
3885}
3886
3887pub fn deinit(elf: *Elf) void {
3888 const gpa = elf.base.comp.gpa;
3889 elf.mf.deinit(gpa);
3890 elf.nodes.deinit(gpa);
3891 elf.shdrs.deinit(gpa);
3892 elf.phdrs.deinit(gpa);
3893 elf.symtab.deinit(gpa);
3894 elf.globals.strong_def.deinit(gpa);
3895 elf.globals.weak_def.deinit(gpa);
3896 elf.globals.strong_undef.deinit(gpa);
3897 elf.globals.weak_undef.deinit(gpa);
3898 elf.copied_globals.deinit(gpa);
3899 elf.want_copied_globals.deinit(gpa);
3900 elf.node_global_symbols.deinit(gpa);
3901 elf.dso_globals.deinit(gpa);
3902 elf.shstrtab.map.deinit(gpa);
3903 elf.strtab.map.deinit(gpa);
3904 elf.dynstr.map.deinit(gpa);
3905 elf.got.deinit(gpa);
3906 elf.plt.deinit(gpa);
3907 elf.needed.deinit(gpa);
3908 for (elf.inputs.items) |input| if (input.member) |m| gpa.free(m);
3909 elf.inputs.deinit(gpa);
3910 elf.input_sections.deinit(gpa);
3911 elf.one_shot_fixups.deinit(gpa);
3912 elf.navs.deinit(gpa);
3913 elf.uavs.deinit(gpa);
3914 for (&elf.lazy.values) |*lazy| lazy.map.deinit(gpa);
3915 elf.pending_uavs.deinit(gpa);
3916 elf.symbol_relocs.deinit(gpa);
3917 elf.node_relocs.deinit(gpa);
3918 elf.got_relocs.deinit(gpa);
3919 elf.tls_size_symbol_relocs.deinit(gpa);
3920 elf.section_by_name.deinit(gpa);
3921 elf.changed_symtab_index.deinit(gpa);
3922
3923 elf.dwarf.deinit();
3924 for (elf.dwarf_units) |*dwarf_unit| dwarf_unit.debug_rnglists_symbol_relocs.deinit(gpa);
3925 gpa.free(elf.dwarf_units);
3926 elf.dwarf_consts.deinit(gpa);
3927 elf.dwarf_globals.deinit(gpa);
3928 elf.dwarf_funcs.deinit(gpa);
3929 elf.dwarf_decls.deinit(gpa);
3930
3931 elf.* = undefined;
3932}
3933
3934fn initHeaders(
3935 elf: *Elf,
3936 class: std.elf.CLASS,
3937 data: std.elf.DATA,
3938 osabi: std.elf.OSABI,
3939 @"type": EhdrType,
3940 machine: EhdrMachine,
3941 maybe_interp: ?[]const u8,
3942) Error!void {
3943 const comp = elf.base.comp;
3944 const gpa = comp.gpa;
3945
3946 const is_archive = comp.config.output_mode == .Lib and comp.config.link_mode == .static;
3947 const have_dynamic = switch (@"type") {
3948 .REL => false,
3949 .EXEC => comp.config.link_mode == .dynamic,
3950 .DYN => true,
3951 };
3952 const have_eh_frame = machine == .X86_64 and comp.config.any_unwind_tables;
3953 const have_debug_frame = machine == .X86_64 and switch (comp.config.debug_format) {
3954 .strip => false,
3955 .dwarf => !comp.config.any_unwind_tables,
3956 .code_view => unreachable,
3957 };
3958 const addr_align: Alignment = switch (class) {
3959 .NONE, _ => unreachable,
3960 .@"32" => .@"4",
3961 .@"64" => .@"8",
3962 };
3963
3964 // Minimum alignment for an arbitrarily-chosen set of "large" nodes in the file (e.g. common
3965 // sections), to allow `MappedFile` to perform operations more efficiently. The downside to
3966 // using `elf.mf.flags.block_size` is that it causes outputs to be potentially unreproducible
3967 // across host filesystems, so in the future we may want to set this to `.@"1"` when using a
3968 // build mode that requires reproducibility.
3969 //
3970 // It can be handy to temporarily set this to `.@"1"` when working on the linker, because it
3971 // prevents alignment bugs from being hidden by your filesystem's block alignment.
3972 const node_block_align = elf.mf.flags.block_size;
3973
3974 const plt: PltInfo = .fromMachine(machine);
3975
3976 const shnum: u32 = shnum: {
3977 var shnum: u32 = 1; // reserved ("null") shdr
3978 shnum += 1; // .symtab
3979 shnum += 1; // .shstrtab
3980 shnum += 1; // .strtab
3981 shnum += @intFromBool(maybe_interp != null); // .interp
3982 shnum += 1; // .rodata
3983 shnum += 1; // .text
3984 shnum += 1; // .data
3985 shnum += @intFromBool(comp.config.any_non_single_threaded); // .tdata
3986 shnum += 1; // .data.rel.ro
3987 if (have_dynamic) {
3988 shnum += 1; // .dynamic
3989 shnum += 1; // .dynstr
3990 shnum += 1; // .dynsym
3991 shnum += 1; // .hash
3992 shnum += 1; // .rela.dyn
3993 shnum += 1; // .rela.plt
3994 }
3995 if (have_eh_frame) {
3996 shnum += @intFromBool(@"type" != .REL); // .eh_frame_hdr
3997 shnum += 1; // .eh_frame
3998 }
3999 switch (comp.config.debug_format) {
4000 .strip => {},
4001 .dwarf => {
4002 shnum += 1; // .debug_abbrev
4003 shnum += @intFromBool(have_debug_frame); // .debug_frame
4004 shnum += 1; // .debug_info
4005 shnum += 1; // .debug_line
4006 shnum += 1; // .debug_line_str
4007 shnum += 1; // .debug_rnglists
4008 shnum += 1; // .debug_str
4009 shnum += 1; // .debug_str_offsets
4010 },
4011 .code_view => unreachable,
4012 }
4013 if (@"type" != .REL) {
4014 shnum += 1; // .got
4015 shnum += @intFromBool(plt.got_plt != null); // .got.plt
4016 shnum += 1; // .plt
4017 shnum += @intFromBool(plt.plt_sec != null); // .plt_sec
4018 }
4019 break :shnum shnum;
4020 };
4021
4022 const phndx: struct {
4023 phdr: u32,
4024 interp: u32,
4025 rodata: u32,
4026 text: u32,
4027 /// On most targets this is `undefined`, but on machines where JUMP_SLOT relocations write
4028 /// directly to the PLT, we place the PLT in its own segment in order to avoid making the
4029 /// general data segment RWX.
4030 plt: u32,
4031 data: u32,
4032 tls: u32,
4033 dynamic: u32,
4034 relro: u32,
4035 gnu_eh_frame: u32,
4036 gnu_stack: u32,
4037 }, const phnum: u32 = ph: {
4038 switch (@"type") {
4039 .REL => break :ph .{ undefined, 0 },
4040 .EXEC, .DYN => {},
4041 }
4042 var phnum: u32 = 0;
4043 break :ph .{
4044 .{
4045 .phdr = phndx: {
4046 defer phnum += 1;
4047 break :phndx phnum;
4048 },
4049 .interp = if (maybe_interp) |_| phndx: {
4050 defer phnum += 1;
4051 break :phndx phnum;
4052 } else undefined,
4053 .rodata = phndx: {
4054 defer phnum += 1;
4055 break :phndx phnum;
4056 },
4057 .text = phndx: {
4058 defer phnum += 1;
4059 break :phndx phnum;
4060 },
4061 .plt = if (plt.got_plt == null) phndx: {
4062 defer phnum += 1;
4063 break :phndx phnum;
4064 } else undefined,
4065 // `data` must be assigned after all other loadable segments so that it has the greatest
4066 // phndx of any loadable segment. This is so that `targetSegmentLoadAddressRestrictions`
4067 // can be obeyed (specifically, the `.data_last` restriction, needed on SPARC).
4068 .data = phndx: {
4069 defer phnum += 1;
4070 break :phndx phnum;
4071 },
4072 .tls = if (comp.config.any_non_single_threaded) phndx: {
4073 defer phnum += 1;
4074 break :phndx phnum;
4075 } else undefined,
4076 .dynamic = if (have_dynamic) phndx: {
4077 defer phnum += 1;
4078 break :phndx phnum;
4079 } else undefined,
4080 .relro = phndx: {
4081 defer phnum += 1;
4082 break :phndx phnum;
4083 },
4084 .gnu_eh_frame = if (have_eh_frame) phndx: {
4085 defer phnum += 1;
4086 break :phndx phnum;
4087 } else undefined,
4088 .gnu_stack = phndx: {
4089 defer phnum += 1;
4090 break :phndx phnum;
4091 },
4092 },
4093 // (I don't actually want the trailing comma below, but a `zig fmt` bug forces it.)
4094 phnum,
4095 };
4096 };
4097
4098 const expected_nodes_len = @as(usize, if (is_archive) 3 else 0) + // .archive, .archive_header, .archive_elf_member_header
4099 3 + // `.elf`, `.ehdr`, and `.shdr` nodes
4100 (shnum - 1) + // -1 because the SHN_UNDEF shdr does not have a `.section` node
4101 (phnum -| 1) + // -1 because the GNU_STACK phdr does not have a `.segment` node
4102 @intFromBool(have_eh_frame and @"type" != .REL); // eh_frame_footer
4103
4104 try elf.nodes.ensureTotalCapacity(gpa, expected_nodes_len);
4105 try elf.shdrs.ensureTotalCapacity(gpa, shnum - 1); // -1 to exclude SHN_UNDEF
4106 try elf.section_by_name.ensureUnusedCapacity(gpa, shnum - 1); // -1 to exclude SHN_UNDEF
4107 try elf.phdrs.resize(gpa, phnum);
4108 try elf.symtab.ensureTotalCapacity(gpa, 1);
4109
4110 if (is_archive) {
4111 const archive_ni = elf.addNodeAssumeCapacity(.root, .archive);
4112
4113 const archive_header_ni = elf.addNodeAssumeCapacity(
4114 try archive_ni.addOnlyHeaderChild(gpa, &elf.mf, .{
4115 // We intentionally do not set `.alignment = .@"2"` here, because the string table data
4116 // in this node does not need to have an aligned length. (This node's offset is aligned
4117 // regardless by virtue of it being a header.)
4118 .size = std.elf.ARMAG.len + @sizeOf(std.elf.ar_hdr),
4119 // The archive header uses 'next_moved' events to resize the "//" member, so that it
4120 // absorbs all padding between `archive_header_ni` and the actual object file members.
4121 .enable_next_moved = true,
4122 .next_moved = true,
4123 }),
4124 .archive_header,
4125 );
4126 const archive_header_slice = archive_header_ni.slice(&elf.mf);
4127 @memcpy(archive_header_slice[0..std.elf.ARMAG.len], std.elf.ARMAG);
4128 const strtab_ar_hdr: *std.elf.ar_hdr = @ptrCast(archive_header_slice[std.elf.ARMAG.len..]);
4129 strtab_ar_hdr.* = .{
4130 .ar_name = std.elf.STRNAME.*,
4131 .ar_date = @splat(' '),
4132 .ar_uid = @splat(' '),
4133 .ar_gid = @splat(' '),
4134 .ar_mode = @splat(' '),
4135 .ar_size = undefined, // populated by `flushNextMoved` for `archive_header_ni`
4136 .ar_fmag = std.elf.ARFMAG.*,
4137 };
4138
4139 elf.ni.elf = elf.addNodeAssumeCapacity(try archive_ni.addOnlyFooterChild(gpa, &elf.mf, .{
4140 .alignment = node_block_align.max(.@"2"),
4141 .bubbles_moved = false,
4142 .resized = true, // ensure that this node's `ar_hdr.ar_size` is updated at least once
4143 }), .elf);
4144
4145 const elf_ar_hdr_ni = elf.addNodeAssumeCapacity(
4146 try archive_ni.addFooterChildBefore(gpa, &elf.mf, .wrap(elf.ni.elf), .{
4147 .alignment = .@"2",
4148 .size = @sizeOf(std.elf.ar_hdr),
4149 }),
4150 .archive_elf_member_header,
4151 );
4152
4153 // Must be populated before we call `populateArchiveMemberName` below.
4154 elf.archive = .{
4155 .ni = archive_ni,
4156 .header_ni = archive_header_ni,
4157 .elf_member_header_ni = elf_ar_hdr_ni,
4158
4159 .elf_member_too_big = false,
4160 .strtab_member_too_big = false,
4161 };
4162
4163 const elf_ar_hdr: *std.elf.ar_hdr = @ptrCast(elf_ar_hdr_ni.slice(&elf.mf));
4164 elf_ar_hdr.* = .{
4165 .ar_name = undefined, // populated below
4166 .ar_date = "0 ".*,
4167 .ar_uid = "0 ".*,
4168 .ar_gid = "0 ".*,
4169 .ar_mode = "644 ".*,
4170 .ar_size = undefined, // populated by `flushResized` for the `.elf` node
4171 .ar_fmag = std.elf.ARFMAG.*,
4172 };
4173 const zcu_member_name = try std.fmt.allocPrint(gpa, "{s}_zcu.o", .{comp.root_name});
4174 defer gpa.free(zcu_member_name);
4175 // After this call returns, `elf_ar_hdr` is invalidated.
4176 try elf.populateArchiveMemberName(elf_ar_hdr, zcu_member_name);
4177 } else elf.ni.elf = elf.addNodeAssumeCapacity(.root, .elf);
4178
4179 const entsize: struct { ph: u32, sh: u32 } = switch (class) {
4180 .NONE, _ => unreachable,
4181 inline else => |ct_class| .{
4182 .ph = @sizeOf(ct_class.ElfN().Phdr),
4183 .sh = @sizeOf(ct_class.ElfN().Shdr),
4184 },
4185 };
4186
4187 // We want to create the segment nodes *before* the ehdr, because the ehdr should go inside of
4188 // the rodata segment. Although to my knowledge neither ELF nor any ELF-based OS strictly
4189 // requires this, it is highly conventional and therefore sometimes relied upon.
4190 if (@"type" != .REL) {
4191 // This node will contain the ehdr, which must be at the start of the ELF file, so this
4192 // node must itself be a header of the `.elf` node.
4193 elf.ni.rodata = elf.addNodeAssumeCapacity(try elf.ni.elf.addOnlyHeaderChild(gpa, &elf.mf, .{
4194 // Must be at least `addr_align` for `elf.ni.phdr` to be placed inside this node
4195 .alignment = node_block_align.max(addr_align),
4196 .moved = true,
4197 .bubbles_moved = false,
4198 }), .{ .segment = phndx.rodata });
4199 elf.phdrs.items[phndx.rodata] = .wrap(elf.ni.rodata);
4200
4201 elf.ni.phdr = elf.addNodeAssumeCapacity(try elf.ni.rodata.addFloatingChild(gpa, &elf.mf, .{
4202 .size = @as(u64, phnum) * entsize.ph,
4203 .alignment = addr_align, // keep in sync with `elf.ni.rodata` alignment above
4204 .moved = true,
4205 .resized = true,
4206 .bubbles_moved = false,
4207 }), .{ .segment = phndx.phdr });
4208 elf.phdrs.items[phndx.phdr] = .wrap(elf.ni.phdr);
4209
4210 elf.ni.text = elf.addNodeAssumeCapacity(try elf.ni.elf.addFloatingChild(gpa, &elf.mf, .{
4211 .alignment = node_block_align,
4212 .moved = true,
4213 .bubbles_moved = false,
4214 }), .{ .segment = phndx.text });
4215 elf.phdrs.items[phndx.text] = .wrap(elf.ni.text);
4216
4217 elf.ni.data = elf.addNodeAssumeCapacity(try elf.ni.elf.addFloatingChild(gpa, &elf.mf, .{
4218 // Must be at least `addr_align` for `elf.ni.data_rel_ro` to be placed inside this node
4219 .alignment = node_block_align.max(addr_align),
4220 .moved = true,
4221 .bubbles_moved = false,
4222 }), .{ .segment = phndx.data });
4223 elf.phdrs.items[phndx.data] = .wrap(elf.ni.data);
4224
4225 if (plt.got_plt == null) elf.phdrs.items[phndx.plt] = .wrap(elf.addNodeAssumeCapacity(
4226 try elf.ni.elf.addFloatingChild(gpa, &elf.mf, .{
4227 .alignment = node_block_align,
4228 .moved = true,
4229 .bubbles_moved = false,
4230 }),
4231 .{ .segment = phndx.plt },
4232 ));
4233
4234 elf.ni.data_rel_ro = elf.addNodeAssumeCapacity(try elf.ni.data.addFloatingChild(gpa, &elf.mf, .{
4235 // Must be at least `addr_align` for the `PT_DYNAMIC` node to be placed inside this one
4236 // later (if `have_dynamic_section`). Keep in sync with `elf.ni.data` alignment above.
4237 .alignment = node_block_align.max(addr_align),
4238 .moved = true,
4239 .bubbles_moved = false,
4240 }), .{ .segment = phndx.relro });
4241 elf.phdrs.items[phndx.relro] = .wrap(elf.ni.data_rel_ro);
4242
4243 if (comp.config.any_non_single_threaded) {
4244 elf.ni.tls = .wrap(elf.addNodeAssumeCapacity(
4245 try elf.ni.rodata.addFloatingChild(gpa, &elf.mf, .{
4246 .alignment = node_block_align,
4247 .moved = true,
4248 .bubbles_moved = false,
4249 }),
4250 .{ .segment = phndx.tls },
4251 ));
4252 elf.phdrs.items[phndx.tls] = elf.ni.tls;
4253 }
4254
4255 elf.phdrs.items[phndx.gnu_stack] = .none;
4256 } else {
4257 elf.ni.rodata = elf.ni.elf;
4258 elf.ni.text = elf.ni.elf;
4259 elf.ni.data = elf.ni.elf;
4260 elf.ni.data_rel_ro = elf.ni.elf;
4261 if (comp.config.any_non_single_threaded) {
4262 elf.ni.tls = .wrap(elf.ni.elf);
4263 }
4264 }
4265
4266 switch (class) {
4267 .NONE, _ => unreachable,
4268 inline else => |ct_class| {
4269 const ElfN = ct_class.ElfN();
4270 // In loadable modules, the ehdr goes in the rodata segment, as described above.
4271 const parent_ni = switch (@"type") {
4272 .REL => elf.ni.elf,
4273 .DYN, .EXEC => elf.ni.rodata,
4274 };
4275 elf.ni.ehdr = elf.addNodeAssumeCapacity(try parent_ni.addOnlyHeaderChild(gpa, &elf.mf, .{
4276 .size = @sizeOf(ElfN.Ehdr),
4277 .alignment = addr_align,
4278 }), .ehdr);
4279
4280 const ehdr: *ElfN.Ehdr = @ptrCast(@alignCast(elf.ni.ehdr.slice(&elf.mf)));
4281 ehdr.ident = .{
4282 .class = class,
4283 .data = data,
4284 .version = 1,
4285 .osabi = osabi,
4286 .abiversion = 0,
4287 };
4288 ehdr.type = @"type".toElf();
4289 ehdr.machine = machine.toElf();
4290 ehdr.version = 1;
4291 ehdr.entry = 0;
4292 ehdr.phoff = 0;
4293 ehdr.shoff = 0;
4294 ehdr.flags = switch (machine) {
4295 .LOONGARCH => .{ .loongarch = .{
4296 .base_abi_modifier = mod: {
4297 const cpu = comp.getTarget().cpu;
4298 if (cpu.has(.loongarch, .d)) break :mod .d;
4299 if (cpu.has(.loongarch, .f)) break :mod .f;
4300 break :mod .s;
4301 },
4302 .abi_extension = .base,
4303 .abi_version = 1,
4304 } },
4305 .SPARCV9 => .{ .sparc = .{
4306 .mm = .rmo,
4307 .ext = .{
4308 .@"32plus" = false,
4309 .sun_us1 = false,
4310 .hal_r1 = false,
4311 .sun_us3 = false,
4312 .le_data = false,
4313 },
4314 } },
4315 .X86_64 => .{ .int = 0 },
4316 .AARCH64, .PPC64, .RISCV => @panic(@tagName(machine)),
4317 };
4318 ehdr.ehsize = @sizeOf(ElfN.Ehdr);
4319 ehdr.phentsize = @sizeOf(ElfN.Phdr);
4320 ehdr.phnum = @min(phnum, std.elf.PN_XNUM);
4321 ehdr.shentsize = @sizeOf(ElfN.Shdr);
4322 ehdr.shnum = 1; // Only the SHN_UNDEF shdr initially---will be incremented by `addSection`
4323 ehdr.shstrndx = std.elf.SHN_UNDEF;
4324 if (elf.targetEndian() != std.lang.Endian.native) std.mem.byteSwapAllFields(ElfN.Ehdr, ehdr);
4325 },
4326 }
4327
4328 elf.ni.shdr = elf.addNodeAssumeCapacity(try elf.ni.elf.addFloatingChild(gpa, &elf.mf, .{
4329 .size = 1 * entsize.sh, // as above, only the null shdr initially
4330 .alignment = addr_align,
4331 .moved = true,
4332 .resized = true,
4333 }), .shdr);
4334
4335 switch (class) {
4336 .NONE, _ => unreachable,
4337 inline else => |ct_class| {
4338 const ElfN = ct_class.ElfN();
4339 const target_endian = elf.targetEndian();
4340
4341 populate_phdrs: {
4342 // Initially we will give every `PT_LOAD` segment this address. When we re-allocate
4343 // segments in the virtual address space in `flushMoved` and `flushResized`, we will
4344 // move some segments to higher addresses to prevent overlap. This address therefore
4345 // becomes the image's "base address"; i.e. the first `PT_LOAD` segment will start
4346 // at this address. The base address could eventually end up higher than this due to
4347 // how we re-allocate the address space, but never lower.
4348 const base_vaddr: u64 = switch (@"type") {
4349 .REL => break :populate_phdrs,
4350 .DYN => 0,
4351 .EXEC => switch (machine) {
4352 .AARCH64 => 0x200000,
4353 .LOONGARCH => 0x10000,
4354 .PPC64 => 0x10000000,
4355 .RISCV => 0x10000,
4356 .SPARCV9 => 0x100000,
4357 .X86_64 => 0x200000,
4358 },
4359 };
4360
4361 // All `PT_LOAD` segments are given this `.@"align"`. However, to avoid bloating the
4362 // binary, their *nodes* are not aligned to this boundary---ELF only requires that
4363 // ecah segment's address equals its file offset modulo this alignment, not that its
4364 // file offset is actually aligned to this boundary. This property is maintained by
4365 // the segment virtual address space allocation logic.
4366 const page_align = elf.targetPageAlign();
4367
4368 // We will populate elements in this slice (by index). The `PT_LOAD` segments are
4369 // actually `PT_NULL` for now, because we initialize `filesz` and `memsz` to zero.
4370 // Any which end up non-empty will have their size populated (and their type set to
4371 // `PT_LOAD`) by the segment virtual address space allocation logic.
4372 const phdr: []ElfN.Phdr = @ptrCast(@alignCast(
4373 elf.ni.phdr.slice(&elf.mf)[0 .. phnum * @sizeOf(ElfN.Phdr)],
4374 ));
4375
4376 phdr[phndx.phdr] = .{
4377 .type = .PHDR,
4378 .offset = 0,
4379 .vaddr = 0,
4380 .paddr = 0,
4381 .filesz = 0,
4382 .memsz = 0,
4383 .flags = .{ .R = true },
4384 .@"align" = @intCast(elf.ni.phdr.alignment(&elf.mf).toByteUnits()),
4385 };
4386
4387 if (maybe_interp) |_| phdr[phndx.interp] = .{
4388 .type = .INTERP,
4389 .offset = 0,
4390 .vaddr = 0,
4391 .paddr = 0,
4392 .filesz = 0,
4393 .memsz = 0,
4394 .flags = .{ .R = true },
4395 .@"align" = 1,
4396 };
4397
4398 phdr[phndx.rodata] = .{
4399 .type = .NULL,
4400 .offset = 0,
4401 .vaddr = @intCast(base_vaddr),
4402 .paddr = @intCast(base_vaddr),
4403 .filesz = 0,
4404 .memsz = 0,
4405 .flags = .{ .R = true },
4406 .@"align" = @intCast(page_align.toByteUnits()),
4407 };
4408
4409 phdr[phndx.text] = .{
4410 .type = .NULL,
4411 .offset = 0,
4412 .vaddr = @intCast(base_vaddr),
4413 .paddr = @intCast(base_vaddr),
4414 .filesz = 0,
4415 .memsz = 0,
4416 .flags = .{ .R = true, .X = true },
4417 .@"align" = @intCast(page_align.toByteUnits()),
4418 };
4419
4420 phdr[phndx.data] = .{
4421 .type = .NULL,
4422 .offset = 0,
4423 .vaddr = @intCast(base_vaddr),
4424 .paddr = @intCast(base_vaddr),
4425 .filesz = 0,
4426 .memsz = 0,
4427 .flags = .{ .R = true, .W = true },
4428 .@"align" = @intCast(page_align.toByteUnits()),
4429 };
4430
4431 if (plt.got_plt == null) phdr[phndx.plt] = .{
4432 .type = .NULL,
4433 .offset = 0,
4434 .vaddr = @intCast(base_vaddr),
4435 .paddr = @intCast(base_vaddr),
4436 .filesz = 0,
4437 .memsz = 0,
4438 .flags = .{ .R = true, .W = true, .X = true },
4439 .@"align" = @intCast(page_align.toByteUnits()),
4440 };
4441
4442 if (elf.ni.tls.unwrap()) |tls_segment_ni| phdr[phndx.tls] = .{
4443 .type = .TLS,
4444 .offset = 0,
4445 .vaddr = 0,
4446 .paddr = 0,
4447 .filesz = 0,
4448 .memsz = 0,
4449 .flags = .{ .R = true },
4450 .@"align" = @intCast(tls_segment_ni.alignment(&elf.mf).toByteUnits()),
4451 };
4452
4453 if (have_dynamic) phdr[phndx.dynamic] = .{
4454 .type = .DYNAMIC,
4455 .offset = 0,
4456 .vaddr = 0,
4457 .paddr = 0,
4458 .filesz = 0,
4459 .memsz = 0,
4460 .flags = .{ .R = true, .W = true },
4461 .@"align" = @intCast(addr_align.toByteUnits()),
4462 };
4463
4464 phdr[phndx.relro] = .{
4465 .type = .GNU_RELRO,
4466 .offset = 0,
4467 .vaddr = 0,
4468 .paddr = 0,
4469 .filesz = 0,
4470 .memsz = 0,
4471 .flags = .{ .R = true },
4472 .@"align" = @intCast(elf.ni.data_rel_ro.alignment(&elf.mf).toByteUnits()),
4473 };
4474
4475 if (have_eh_frame) phdr[phndx.gnu_eh_frame] = .{
4476 .type = .GNU_EH_FRAME,
4477 .offset = 0,
4478 .vaddr = 0,
4479 .paddr = 0,
4480 .filesz = @sizeOf(Dwarf.EhFrameHdr),
4481 .memsz = @sizeOf(Dwarf.EhFrameHdr),
4482 .flags = .{ .R = true },
4483 .@"align" = 4,
4484 };
4485
4486 phdr[phndx.gnu_stack] = .{
4487 .type = .GNU_STACK,
4488 .offset = 0,
4489 .vaddr = 0,
4490 .paddr = 0,
4491 .filesz = 0,
4492 .memsz = @intCast(elf.options.stack_size orelse 0),
4493 .flags = .{ .R = true, .W = true },
4494 .@"align" = 1,
4495 };
4496
4497 if (target_endian != std.lang.Endian.native) {
4498 std.mem.byteSwapAllElements(ElfN.Phdr, phdr);
4499 }
4500 }
4501
4502 const sh_undef: *ElfN.Shdr = @ptrCast(@alignCast(elf.ni.shdr.slice(&elf.mf)));
4503 sh_undef.* = .{
4504 .name = @backingInt(String(.shstrtab).empty),
4505 .type = .NULL,
4506 .flags = .{ .shf = .{} },
4507 .addr = 0,
4508 .offset = 0,
4509 .size = if (shnum < std.elf.SHN_LORESERVE) 0 else shnum,
4510 .link = 0,
4511 .info = if (phnum < std.elf.PN_XNUM) 0 else phnum,
4512 .addralign = 0,
4513 .entsize = 0,
4514 };
4515 if (target_endian != std.lang.Endian.native) std.mem.byteSwapAllFields(ElfN.Shdr, sh_undef);
4516
4517 elf.symtab.addOneAssumeCapacity().* = .{
4518 .node = .none,
4519 .first_target_reloc = .none,
4520 };
4521 assert(.symtab == try elf.addSection(elf.ni.elf, .{
4522 .type = .SYMTAB,
4523 .size = @sizeOf(ElfN.Sym) * 1,
4524 .addralign = addr_align,
4525 .entsize = @sizeOf(ElfN.Sym),
4526 .node_align = node_block_align,
4527 .info = 1, // index of first non-local symbol
4528 .manual_size = true,
4529 }));
4530 const symtab_null = @field(elf.symPtr(.null), @tagName(ct_class));
4531 symtab_null.* = .{
4532 .name = @backingInt(String(.strtab).empty),
4533 .value = 0,
4534 .size = 0,
4535 .info = .{ .type = .NOTYPE, .bind = .LOCAL },
4536 .other = .{ .visibility = .DEFAULT },
4537 .shndx = std.elf.SHN_UNDEF,
4538 };
4539 if (target_endian != std.lang.Endian.native) std.mem.byteSwapAllFields(ElfN.Sym, symtab_null);
4540
4541 const ehdr = @field(elf.ehdrPtr(), @tagName(ct_class));
4542 ehdr.shstrndx = ehdr.shnum;
4543 },
4544 }
4545 assert(.shstrtab == try elf.addSection(elf.ni.elf, .{
4546 .type = .STRTAB,
4547 .size = 1,
4548 .entsize = 1,
4549 .node_align = node_block_align,
4550 .manual_size = true,
4551 }));
4552 Section.Index.get(.shstrtab, elf).ni.slice(&elf.mf)[0] = 0;
4553
4554 try Section.Index.symtab.rename(elf, ".symtab");
4555 try Section.Index.shstrtab.rename(elf, ".shstrtab");
4556
4557 assert(.strtab == try elf.addSection(elf.ni.elf, .{
4558 .name = ".strtab",
4559 .type = .STRTAB,
4560 .size = 1,
4561 .entsize = 1,
4562 .node_align = node_block_align,
4563 .manual_size = true,
4564 }));
4565 Section.Index.get(.strtab, elf).ni.slice(&elf.mf)[0] = 0;
4566 switch (elf.shdrPtr(.symtab)) {
4567 inline else => |shdr| elf.targetStore(&shdr.link, @backingInt(Section.Index.strtab)),
4568 }
4569
4570 assert(.rodata == try elf.addSection(elf.ni.rodata, .{
4571 .name = ".rodata",
4572 .flags = .{ .ALLOC = true },
4573 .node_align = node_block_align,
4574 }));
4575 assert(.text == try elf.addSection(elf.ni.text, .{
4576 .name = ".text",
4577 .flags = .{ .ALLOC = true, .EXECINSTR = true },
4578 .node_align = node_block_align,
4579 }));
4580 assert(.data == try elf.addSection(elf.ni.data, .{
4581 .name = ".data",
4582 .flags = .{ .WRITE = true, .ALLOC = true },
4583 .node_align = node_block_align,
4584 }));
4585 assert(.data_rel_ro == try elf.addSection(elf.ni.data_rel_ro, .{
4586 .name = ".data.rel.ro",
4587 .flags = .{ .WRITE = true, .ALLOC = true },
4588 .node_align = node_block_align,
4589 }));
4590 if (@"type" != .REL) {
4591 elf.shndx.got = try elf.addSection(elf.ni.data_rel_ro, .{
4592 .name = ".got",
4593 .type = .PROGBITS,
4594 // Reserve space for the reserved words, populated later.
4595 .size = switch (machine) {
4596 .AARCH64, .PPC64, .RISCV => @panic(@tagName(machine)),
4597 .X86_64 => 3 * elf.targetPtrSize(),
4598 .LOONGARCH, .SPARCV9 => elf.targetPtrSize(),
4599 },
4600 .flags = .{ .WRITE = true, .ALLOC = true },
4601 .addralign = addr_align,
4602 .entsize = @intCast(addr_align.toByteUnits()),
4603 .manual_size = true,
4604 });
4605 {
4606 const init_plt_size = plt.entry_size * plt.header_entries;
4607 if (plt.got_plt) |got_plt| {
4608 const got_plt_segment_ni = if (elf.options.z_now) elf.ni.data_rel_ro else elf.ni.data;
4609 elf.shndx.got_plt = try elf.addSection(got_plt_segment_ni, .{
4610 .name = ".got.plt",
4611 .type = .PROGBITS,
4612 .flags = .{ .WRITE = true, .ALLOC = true },
4613 .size = got_plt.header_entries * elf.targetPtrSize(),
4614 .addralign = addr_align,
4615 .entsize = @intCast(addr_align.toByteUnits()),
4616 .manual_size = true,
4617 });
4618 elf.shndx.plt = try elf.addSection(elf.ni.text, .{
4619 .name = ".plt",
4620 .type = .PROGBITS,
4621 .flags = .{ .ALLOC = true, .EXECINSTR = true },
4622 .size = plt.@"align".forward(init_plt_size),
4623 .addralign = plt.@"align",
4624 .node_align = node_block_align,
4625 .manual_size = true,
4626 });
4627 } else {
4628 elf.shndx.plt = try elf.addSection(elf.phdrs.items[phndx.plt].unwrap().?, .{
4629 .name = ".plt",
4630 .type = .PROGBITS,
4631 .flags = .{ .ALLOC = true, .WRITE = true, .EXECINSTR = true },
4632 .size = plt.@"align".forward(init_plt_size),
4633 .addralign = plt.@"align",
4634 .node_align = node_block_align,
4635 .manual_size = true,
4636 });
4637 }
4638 // And the award for most annoying PLT requirement goes to SPARC, which decided that the
4639 // whole table should have a greater alignment than the size of the individual entries,
4640 // hence this bullshit:
4641 if (plt.@"align".forward(init_plt_size) != init_plt_size) {
4642 switch (elf.shdrPtr(elf.shndx.plt)) {
4643 inline else => |shdr| elf.targetStore(&shdr.size, init_plt_size),
4644 }
4645 }
4646 }
4647 if (plt.plt_sec != null) elf.shndx.plt_sec = try elf.addSection(elf.ni.text, .{
4648 .name = ".plt.sec",
4649 .flags = .{ .ALLOC = true, .EXECINSTR = true },
4650 .addralign = plt.@"align",
4651 .node_align = node_block_align,
4652 });
4653 if (maybe_interp) |interp| {
4654 const interp_ni = elf.addNodeAssumeCapacity(
4655 try elf.ni.rodata.addFloatingChild(gpa, &elf.mf, .{
4656 .size = interp.len + 1,
4657 .moved = true,
4658 .resized = true,
4659 .bubbles_moved = false,
4660 }),
4661 .{ .segment = phndx.interp },
4662 );
4663 elf.phdrs.items[phndx.interp] = .wrap(interp_ni);
4664
4665 const sec_interp_shndx = try elf.addSection(interp_ni, .{
4666 .name = ".interp",
4667 .type = .PROGBITS,
4668 .flags = .{ .ALLOC = true },
4669 .size = @intCast(interp.len + 1),
4670 });
4671 const sec_interp = sec_interp_shndx.get(elf).ni.slice(&elf.mf);
4672 @memcpy(sec_interp[0..interp.len], interp);
4673 sec_interp[interp.len] = 0;
4674 }
4675 if (have_dynamic) {
4676 assert(elf.ni.data_rel_ro.alignment(&elf.mf).compare(.gte, addr_align));
4677 const dynamic_ni = elf.addNodeAssumeCapacity(
4678 try elf.ni.data_rel_ro.addFloatingChild(gpa, &elf.mf, .{
4679 .alignment = addr_align,
4680 .moved = true,
4681 .bubbles_moved = false,
4682 }),
4683 .{ .segment = phndx.dynamic },
4684 );
4685 elf.phdrs.items[phndx.dynamic] = .wrap(dynamic_ni);
4686
4687 const dynstr_shndx = try elf.addSection(elf.ni.rodata, .{
4688 .name = ".dynstr",
4689 .type = .STRTAB,
4690 .flags = .{ .ALLOC = true },
4691 .size = 1,
4692 .entsize = 1,
4693 .node_align = node_block_align,
4694 .manual_size = true,
4695 });
4696 dynstr_shndx.get(elf).ni.slice(&elf.mf)[0] = 0;
4697 elf.shndx.dynstr = dynstr_shndx;
4698
4699 switch (class) {
4700 .NONE, _ => unreachable,
4701 inline else => |ct_class| {
4702 const Sym = ct_class.ElfN().Sym;
4703 elf.shndx.dynsym = try elf.addSection(elf.ni.rodata, .{
4704 .name = ".dynsym",
4705 .type = .DYNSYM,
4706 .flags = .{ .ALLOC = true },
4707 .size = @sizeOf(Sym) * 1,
4708 .link = dynstr_shndx.toSection().?,
4709 .info = 1,
4710 .addralign = addr_align,
4711 .entsize = @sizeOf(Sym),
4712 .node_align = node_block_align,
4713 .manual_size = true,
4714 });
4715 const dynsym_null = @field(elf.dynsymPtr(0), @tagName(ct_class));
4716 dynsym_null.* = .{
4717 .name = @backingInt(String(.dynstr).empty),
4718 .value = 0,
4719 .size = 0,
4720 .info = .{ .type = .NOTYPE, .bind = .LOCAL },
4721 .other = .{ .visibility = .DEFAULT },
4722 .shndx = std.elf.SHN_UNDEF,
4723 };
4724 if (elf.targetEndian() != std.lang.Endian.native) std.mem.byteSwapAllFields(
4725 Sym,
4726 dynsym_null,
4727 );
4728 },
4729 }
4730 const rela_size: std.elf.Word = switch (class) {
4731 .NONE, _ => unreachable,
4732 inline else => |ct_class| @sizeOf(ct_class.ElfN().Rela),
4733 };
4734 elf.shndx.rela_dyn = try elf.addSection(elf.ni.rodata, .{
4735 .name = ".rela.dyn",
4736 .type = .RELA,
4737 .flags = .{ .ALLOC = true },
4738 .link = elf.shndx.dynsym.toSection().?,
4739 .addralign = addr_align,
4740 .entsize = rela_size,
4741 .node_align = node_block_align,
4742 .manual_size = true,
4743 });
4744 elf.shndx.rela_plt = try elf.addSection(elf.ni.rodata, .{
4745 .name = ".rela.plt",
4746 .type = .RELA,
4747 .flags = .{ .ALLOC = true, .INFO_LINK = true },
4748 .link = elf.shndx.dynsym.toSection().?,
4749 .info = (if (plt.got_plt != null) elf.shndx.got_plt else elf.shndx.plt).toSection().?,
4750 .addralign = addr_align,
4751 .entsize = rela_size,
4752 .node_align = node_block_align,
4753 .manual_size = true,
4754 });
4755 elf.shndx.dynamic = try elf.addSection(dynamic_ni, .{
4756 .name = ".dynamic",
4757 .type = .DYNAMIC,
4758 .flags = .{ .ALLOC = true, .WRITE = true },
4759 .link = dynstr_shndx.toSection().?,
4760 .entsize = @intCast(addr_align.toByteUnits() * 2),
4761 .addralign = addr_align,
4762 .manual_size = true,
4763 });
4764 switch (elf.targetDynsymHashInfo()) {
4765 inline else => |info| {
4766 elf.shndx.hash = try elf.addSection(elf.ni.rodata, .{
4767 .name = ".hash",
4768 .type = .HASH,
4769 .flags = .{ .ALLOC = true },
4770 .link = elf.shndx.dynsym.toSection().?,
4771 // It's unclear what value is correct for the alignment. binutils uses 8 everywhere,
4772 // while lld uses 4 everywhere (but lld lacks support for the alpha/s390x special
4773 // case). Matching the hash word (= entry) size seems like the actually sane choice,
4774 // and is what mold does too.
4775 .addralign = .fromByteUnits(@sizeOf(info.Int())),
4776 // initially: nbucket = 8 + nchain = 1
4777 .size = @sizeOf(info.Header()) + @sizeOf(info.Int()) * (8 + 1),
4778 .manual_size = true,
4779 });
4780 const hash_slice: []align(@sizeOf(info.Int())) u8 = @alignCast(elf.shndx.hash.get(elf).ni.slice(&elf.mf));
4781 const header: *info.Header() = @ptrCast(hash_slice[0..@sizeOf(info.Header())]);
4782 header.* = .{ .nbucket = 8, .nchain = 1 };
4783 if (elf.targetEndian() != std.lang.Endian.native) {
4784 std.mem.byteSwapAllFields(info.Header(), header);
4785 }
4786 // The initial bucket and chain values are all 0.
4787 @memset(hash_slice[@sizeOf(info.Header())..], 0);
4788 },
4789 }
4790
4791 switch (machine) {
4792 .AARCH64, .PPC64, .RISCV => @panic(@tagName(machine)),
4793 .X86_64 => {
4794 const plt_ni = elf.shndx.plt.get(elf).ni;
4795 const got_plt_sym: Symbol.Id = .local(elf.shndx.got_plt.get(elf).lsi);
4796 @memcpy(plt_ni.slice(&elf.mf)[0..16], &[16]u8{
4797 0xff, 0x35, 0x00, 0x00, 0x00, 0x00, // push 0x0(%rip)
4798 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *0x0(%rip)
4799 0x0f, 0x1f, 0x40, 0x00, // nopl 0x0(%rax)
4800 });
4801 elf.plt_first_symbol_reloc = @fromBackingInt(@intCast(elf.symbol_relocs.items.len));
4802 try elf.ensureUnusedRelocCapacity(plt_ni, 2);
4803 try elf.addSymbolRelocAssumeCapacity(
4804 plt_ni,
4805 2,
4806 got_plt_sym,
4807 8 * 1 - 4,
4808 .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" }),
4809 );
4810 try elf.addSymbolRelocAssumeCapacity(
4811 plt_ni,
4812 8,
4813 got_plt_sym,
4814 8 * 2 - 4,
4815 .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" }),
4816 );
4817 },
4818 .LOONGARCH => {
4819 const plt_ni = elf.shndx.plt.get(elf).ni;
4820 const got_plt_sym: Symbol.Id = .local(elf.shndx.got_plt.get(elf).lsi);
4821 @memcpy(plt_ni.slice(&elf.mf)[0..32], switch (class) {
4822 .NONE, _ => unreachable,
4823 .@"32" => &[32]u8{
4824 0x1a, 0x00, 0x00, 0x0e, // pcalau12i $t2, %pc_hi20(.got.plt)
4825 0x00, 0x11, 0x3d, 0xad, // sub.w $t1, $t1, $t3
4826 0x28, 0x80, 0x01, 0xcf, // ld.w $t3, $t2, %lo12(.got.plt) # _dl_runtime_resolve
4827 0x02, 0xbf, 0x51, 0xad, // addi.w $t1, $t1, -44 # .plt entry
4828 0x02, 0x80, 0x01, 0xcc, // addi.w $t0, $t2, %lo12(.got.plt) # &.got.plt
4829 0x00, 0x44, 0x89, 0xad, // srli.w $t1, $t1, 2 # .plt entry offset
4830 0x28, 0x80, 0x11, 0x8c, // ld.w $t0, $t0, 4 # link map
4831 0x4c, 0x00, 0x01, 0xe0, // jr $t3
4832 },
4833 .@"64" => &[32]u8{
4834 0x1a, 0x00, 0x00, 0x0e, // pcalau12i $t2, %pc_hi20(.got.plt)
4835 0x00, 0x11, 0xbd, 0xad, // sub.d $t1, $t1, $t3
4836 0x28, 0xc0, 0x01, 0xcf, // ld.d $t3, $t2, %lo12(.got.plt) # _dl_runtime_resolve
4837 0x02, 0xff, 0x51, 0xad, // addi.d $t1, $t1, -44 # .plt entry
4838 0x02, 0xc0, 0x01, 0xcc, // addi.d $t0, $t2, %lo12(.got.plt) # &.got.plt
4839 0x00, 0x45, 0x05, 0xad, // srli.d $t1, $t1, 1 # .plt entry offset
4840 0x28, 0xc0, 0x21, 0x8c, // ld.d $t0, $t0, 8 # link map
4841 0x4c, 0x00, 0x01, 0xe0, // jr $t3
4842 },
4843 });
4844 elf.plt_first_symbol_reloc = @fromBackingInt(@intCast(elf.symbol_relocs.items.len));
4845 try elf.ensureUnusedRelocCapacity(plt_ni, 3);
4846 elf.addRelocAssumeCapacity(plt_ni, 0, got_plt_sym, 0, .{ .LARCH = .PCALA_HI20 }) catch |err| switch (err) {
4847 else => |e| return e,
4848 error.UnknownRelocation => unreachable,
4849 error.NonStaticRelocation => unreachable,
4850 error.UnimplementedRelocation => unreachable,
4851 };
4852 elf.addRelocAssumeCapacity(plt_ni, 8, got_plt_sym, 0, .{ .LARCH = .PCALA_LO12 }) catch |err| switch (err) {
4853 else => |e| return e,
4854 error.UnknownRelocation => unreachable,
4855 error.NonStaticRelocation => unreachable,
4856 error.UnimplementedRelocation => unreachable,
4857 };
4858 elf.addRelocAssumeCapacity(plt_ni, 16, got_plt_sym, 0, .{ .LARCH = .PCALA_LO12 }) catch |err| switch (err) {
4859 else => |e| return e,
4860 error.UnknownRelocation => unreachable,
4861 error.NonStaticRelocation => unreachable,
4862 error.UnimplementedRelocation => unreachable,
4863 };
4864 },
4865 .SPARCV9 => {},
4866 }
4867 }
4868 if (have_eh_frame) {
4869 const gnu_eh_frame = elf.addNodeAssumeCapacity(
4870 try elf.ni.rodata.addFloatingChild(gpa, &elf.mf, .{
4871 .size = @sizeOf(Dwarf.EhFrameHdr),
4872 .alignment = .@"4",
4873 .moved = true,
4874 .bubbles_moved = false,
4875 }),
4876 .{ .segment = phndx.gnu_eh_frame },
4877 );
4878 elf.ni.gnu_eh_frame = .wrap(gnu_eh_frame);
4879 elf.phdrs.items[phndx.gnu_eh_frame] = elf.ni.gnu_eh_frame;
4880
4881 elf.shndx.eh_frame_hdr = try elf.addSection(gnu_eh_frame, .{
4882 .name = ".eh_frame_hdr",
4883 .type = .PROGBITS,
4884 .flags = .{ .ALLOC = true },
4885 .size = @sizeOf(Dwarf.EhFrameHdr),
4886 .addralign = .@"4",
4887 });
4888 elf.shndx.eh_frame = try elf.addSection(elf.ni.rodata, .{
4889 .name = ".eh_frame",
4890 .flags = .{ .ALLOC = true },
4891 .addralign = addr_align,
4892 .node_align = elf.mf.flags.block_size,
4893 .manual_size = true,
4894 });
4895
4896 const eh_frame_hdr_ni = elf.shndx.eh_frame_hdr.get(elf).ni;
4897 elf.eh_frame_hdr_first_symbol_reloc =
4898 @fromBackingInt(@intCast(elf.symbol_relocs.items.len));
4899 try elf.dwarf.genEhFrameHdr(
4900 Node.toAtom(eh_frame_hdr_ni),
4901 @ptrCast(@alignCast(eh_frame_hdr_ni.slice(&elf.mf))),
4902 Symbol.Id.local(elf.shndx.eh_frame.get(elf).lsi).toTypeErased(),
4903 );
4904 _ = elf.addNodeAssumeCapacity(
4905 try elf.shndx.eh_frame.get(elf).ni.addOnlyFooterChild(gpa, &elf.mf, .{
4906 .size = addr_align.forward(4),
4907 .alignment = addr_align,
4908 }),
4909 .eh_frame_footer,
4910 );
4911 }
4912
4913 // Populate reserved GOT words.
4914 switch (machine) {
4915 .AARCH64, .PPC64, .RISCV => @panic(@tagName(machine)),
4916 .X86_64 => {
4917 try elf.got.ensureUnusedCapacity(gpa, 3);
4918 elf.got.putAssumeCapacityNoClobber(switch (have_dynamic) {
4919 true => .{ .symbol = .local(elf.shndx.dynamic.get(elf).lsi) },
4920 false => .{ .reserved = 0 },
4921 }, .none);
4922 elf.got.putAssumeCapacityNoClobber(.{ .reserved = 1 }, .none);
4923 elf.got.putAssumeCapacityNoClobber(.{ .reserved = 2 }, .none);
4924 },
4925 .LOONGARCH, .SPARCV9 => {
4926 try elf.got.ensureUnusedCapacity(gpa, 1);
4927 elf.got.putAssumeCapacityNoClobber(switch (have_dynamic) {
4928 true => .{ .symbol = .local(elf.shndx.dynamic.get(elf).lsi) },
4929 false => .{ .reserved = 0 },
4930 }, .none);
4931 },
4932 }
4933 switch (elf.shdrPtr(elf.shndx.got)) {
4934 inline else => |shdr, ct_class| {
4935 const Addr = ct_class.ElfN().Addr;
4936 assert(elf.targetLoad(&shdr.size) == elf.got.count() * @sizeOf(Addr));
4937 },
4938 }
4939 if (elf.shndx.dynamic != .UNDEF) {
4940 try elf.shndx.rela_dyn.relaEnsureAdditionalCapacity(elf, elf.got.count());
4941 }
4942 for (0..elf.got.count()) |got_index| {
4943 elf.updateGotEntry(got_index);
4944 }
4945
4946 // Create any always-provided linker-defined symbols. The symbols marking the `INIT_ARRAY`/
4947 // `FINI_ARRAY`/`PREINIT_ARRAY` sections are instead created by `createInitFiniArraySection`
4948 // when needed (it seems to be legal to leave those undefined if the section doesn't exist).
4949
4950 try elf.ensureUnusedSymbolCapacity(10, .maybe_global);
4951 // Despite the name, `__dso_handle` is necessary even in static binaries.
4952 _ = elf.addGlobalSymbolAssumeCapacity(.{
4953 .node = .wrap(Section.Index.text.get(elf).ni),
4954 .name = try .string(elf, "__dso_handle"),
4955 .value = Section.Index.text.vaddr(elf),
4956 .size = 0,
4957 .type = .NOTYPE,
4958 .bind = .weak,
4959 .visibility = .HIDDEN,
4960 .shndx = .text,
4961 }) catch |err| switch (err) {
4962 error.MultipleDefinitions => unreachable, // no inputs are processed yet
4963 };
4964 _ = elf.addGlobalSymbolAssumeCapacity(.{
4965 .node = .wrap(elf.shndx.plt.get(elf).ni),
4966 .name = try .string(elf, "_PROCEDURE_LINKAGE_TABLE_"),
4967 .value = elf.shndx.plt.vaddr(elf),
4968 .size = 0,
4969 .type = .NOTYPE,
4970 .bind = .strong,
4971 .visibility = .HIDDEN,
4972 .shndx = elf.shndx.plt,
4973 }) catch |err| switch (err) {
4974 error.MultipleDefinitions => unreachable, // no inputs are processed yet
4975 };
4976 _ = elf.addGlobalSymbolAssumeCapacity(.{
4977 .node = .wrap(elf.shndx.got.get(elf).ni),
4978 .name = try .string(elf, "_GLOBAL_OFFSET_TABLE_"),
4979 .value = switch (machine) {
4980 .AARCH64,
4981 .LOONGARCH,
4982 .PPC64,
4983 .RISCV,
4984 .SPARCV9,
4985 => elf.shndx.got.vaddr(elf),
4986
4987 //.QDSP6,
4988 //.@"386",
4989 .X86_64,
4990 => elf.shndx.got_plt.vaddr(elf),
4991 },
4992 .size = 0,
4993 .type = .NOTYPE,
4994 .bind = .strong,
4995 .visibility = .HIDDEN,
4996 .shndx = elf.shndx.got,
4997 }) catch |err| switch (err) {
4998 error.MultipleDefinitions => unreachable, // no inputs are processed yet
4999 };
5000 _ = elf.addGlobalSymbolAssumeCapacity(.{
5001 .node = .none,
5002 .name = try .string(elf, "__init_array_start"),
5003 .value = 0,
5004 .size = 0,
5005 .type = .NOTYPE,
5006 .bind = .strong,
5007 .visibility = .HIDDEN,
5008 .shndx = .ABS,
5009 }) catch |err| switch (err) {
5010 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5011 };
5012 _ = elf.addGlobalSymbolAssumeCapacity(.{
5013 .node = .none,
5014 .name = try .string(elf, "__init_array_end"),
5015 .value = 0,
5016 .size = 0,
5017 .type = .NOTYPE,
5018 .bind = .strong,
5019 .visibility = .HIDDEN,
5020 .shndx = .ABS,
5021 }) catch |err| switch (err) {
5022 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5023 };
5024 _ = elf.addGlobalSymbolAssumeCapacity(.{
5025 .node = .none,
5026 .name = try .string(elf, "__fini_array_start"),
5027 .value = 0,
5028 .size = 0,
5029 .type = .NOTYPE,
5030 .bind = .strong,
5031 .visibility = .HIDDEN,
5032 .shndx = .ABS,
5033 }) catch |err| switch (err) {
5034 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5035 };
5036 _ = elf.addGlobalSymbolAssumeCapacity(.{
5037 .node = .none,
5038 .name = try .string(elf, "__fini_array_end"),
5039 .value = 0,
5040 .size = 0,
5041 .type = .NOTYPE,
5042 .bind = .strong,
5043 .visibility = .HIDDEN,
5044 .shndx = .ABS,
5045 }) catch |err| switch (err) {
5046 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5047 };
5048 _ = elf.addGlobalSymbolAssumeCapacity(.{
5049 .node = .none,
5050 .name = try .string(elf, "__preinit_array_start"),
5051 .value = 0,
5052 .size = 0,
5053 .type = .NOTYPE,
5054 .bind = .strong,
5055 .visibility = .HIDDEN,
5056 .shndx = .ABS,
5057 }) catch |err| switch (err) {
5058 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5059 };
5060 _ = elf.addGlobalSymbolAssumeCapacity(.{
5061 .node = .none,
5062 .name = try .string(elf, "__preinit_array_end"),
5063 .value = 0,
5064 .size = 0,
5065 .type = .NOTYPE,
5066 .bind = .strong,
5067 .visibility = .HIDDEN,
5068 .shndx = .ABS,
5069 }) catch |err| switch (err) {
5070 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5071 };
5072 if (have_dynamic) {
5073 _ = elf.addGlobalSymbolAssumeCapacity(.{
5074 .node = .wrap(elf.shndx.dynamic.get(elf).ni),
5075 .name = try .string(elf, "_DYNAMIC"),
5076 .value = elf.shndx.dynamic.vaddr(elf),
5077 .size = 0,
5078 .type = .NOTYPE,
5079 .bind = .strong,
5080 .visibility = .HIDDEN,
5081 .shndx = elf.shndx.dynamic,
5082 }) catch |err| switch (err) {
5083 error.MultipleDefinitions => unreachable, // no inputs are processed yet
5084 };
5085 }
5086 } else {
5087 assert(maybe_interp == null);
5088 assert(!have_dynamic);
5089 if (have_eh_frame) elf.shndx.eh_frame = try elf.addSection(elf.ni.rodata, .{
5090 .name = ".eh_frame",
5091 .type = if (machine == .X86_64) .X86_64_UNWIND else .NULL,
5092 .flags = .{ .ALLOC = true },
5093 .addralign = addr_align,
5094 .node_align = elf.mf.flags.block_size,
5095 .manual_size = true,
5096 });
5097 }
5098 if (elf.ni.tls.unwrap()) |tls_segment_ni| elf.shndx.tdata = try elf.addSection(tls_segment_ni, .{
5099 .name = ".tdata",
5100 .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true },
5101 .node_align = node_block_align,
5102 });
5103 switch (comp.config.debug_format) {
5104 .strip => {},
5105 .dwarf => {
5106 elf.shndx.debug_abbrev = try elf.addSection(elf.ni.elf, .{ .name = ".debug_abbrev" });
5107 if (have_debug_frame) elf.shndx.debug_frame = try elf.addSection(elf.ni.elf, .{
5108 .name = ".debug_frame",
5109 .addralign = addr_align,
5110 .node_align = elf.mf.flags.block_size,
5111 .manual_size = true,
5112 });
5113 elf.shndx.debug_info = try elf.addSection(elf.ni.elf, .{
5114 .name = ".debug_info",
5115 .node_align = elf.mf.flags.block_size,
5116 });
5117 elf.shndx.debug_line = try elf.addSection(elf.ni.elf, .{
5118 .name = ".debug_line",
5119 .node_align = elf.mf.flags.block_size,
5120 });
5121 elf.shndx.debug_line_str = try elf.addSection(elf.ni.elf, .{
5122 .name = ".debug_line_str",
5123 .flags = .{ .MERGE = true, .STRINGS = true },
5124 });
5125 elf.shndx.debug_rnglists = try elf.addSection(elf.ni.elf, .{
5126 .name = ".debug_rnglists",
5127 .node_align = elf.mf.flags.block_size,
5128 });
5129 elf.shndx.debug_str = try elf.addSection(elf.ni.elf, .{
5130 .name = ".debug_str",
5131 .flags = .{ .MERGE = true, .STRINGS = true },
5132 });
5133 elf.shndx.debug_str_offsets = try elf.addSection(elf.ni.elf, .{
5134 .name = ".debug_str_offsets",
5135 });
5136 },
5137 .code_view => unreachable,
5138 }
5139
5140 assert(elf.nodes.len == expected_nodes_len);
5141 assert(elf.shdrs.items.len == shnum - 1); // -1 to exclude SHN_UNDEF
5142
5143 for (1..shnum) |shndx_raw| { // start at 1 to exclude SHN_UNDEF
5144 const shndx: Section.Index = @fromBackingInt(@intCast(shndx_raw));
5145 elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {});
5146 }
5147
5148 if (have_dynamic) elf.dynamic = .{
5149 .flags = if (elf.options.z_now) std.elf.DF_BIND_NOW else 0,
5150 .flags_1 = f: {
5151 var f: u32 = 0;
5152 if (elf.options.z_now) f |= std.elf.DF_1_NOW;
5153 if (comp.config.output_mode == .Exe and comp.config.pie) f |= std.elf.DF_1_PIE;
5154 break :f f;
5155 },
5156 .rpath = str: {
5157 var buf: std.ArrayList(u8) = .empty;
5158 defer buf.deinit(gpa);
5159 for (elf.options.rpath_list, 0..) |path, i| {
5160 if (i > 0) try buf.append(gpa, ':');
5161 try buf.appendSlice(gpa, path);
5162 }
5163 break :str try elf.string(.dynstr, buf.items);
5164 },
5165 .soname = str: {
5166 const slice = elf.options.soname orelse break :str .empty;
5167 break :str try elf.string(.dynstr, slice);
5168 },
5169 };
5170
5171 if (@"type" != .REL) switch (elf.targetSegmentLoadAddressRestrictions()) {
5172 .none => {},
5173 .data_last => switch (elf.phdrSlice()) {
5174 inline else => |phdr| {
5175 // Ensure that the segment after `.data` (if any) is not a loadable segment.
5176 const next_phndx = phndx.data + 1;
5177 if (next_phndx < phdr.len) {
5178 switch (elf.targetLoad(&phdr[next_phndx].type)) {
5179 .NULL, .LOAD => unreachable, // data segment should be the last loadable segment
5180 else => {},
5181 }
5182 }
5183 },
5184 },
5185 };
5186}
5187
5188pub fn startProgress(elf: *Elf, prog_node: std.Progress.Node) void {
5189 prog_node.increaseEstimatedTotalItems(4);
5190 elf.const_prog_node = prog_node.start("Constants", elf.pending_uavs.items.len);
5191 elf.mf.update_prog_node = prog_node.start("Relocations", elf.mf.updates.items.len);
5192 elf.input_prog_node = prog_node.start("Inputs", (elf.inputs.items.len - elf.input_pending_index) +
5193 (elf.input_sections.items.len - elf.input_section_pending_index));
5194}
5195
5196pub fn endProgress(elf: *Elf) void {
5197 elf.input_prog_node.end();
5198 elf.input_prog_node = .none;
5199 elf.mf.update_prog_node.end();
5200 elf.mf.update_prog_node = .none;
5201 elf.const_prog_node.end();
5202 elf.const_prog_node = .none;
5203}
5204
5205fn getNode(elf: *const Elf, ni: MappedFile.Node.Index) Node {
5206 return elf.nodes.get(@backingInt(ni));
5207}
5208/// Asserts that `ni` is a section, input section, copied global, NAV, UAV, or lazy code/data.
5209fn getNodeShndx(elf: *const Elf, ni: MappedFile.Node.Index) Section.Index {
5210 return switch (elf.getNode(ni)) {
5211 .deleted,
5212 .archive,
5213 .archive_header,
5214 .archive_input_member,
5215 .archive_elf_member_header,
5216 .elf,
5217 .ehdr,
5218 .shdr,
5219 .segment,
5220 => unreachable,
5221 .section, .section_manual_size => |shndx| shndx,
5222 .input_section,
5223 .copied_global,
5224 .nav,
5225 .uav,
5226 .lazy_code,
5227 .lazy_const_data,
5228 .debug_shared,
5229 .eh_frame_footer,
5230 .unit_padding,
5231 .unit_frame,
5232 .unit_debug_info,
5233 .unit_debug_line,
5234 .unit_debug_rnglists,
5235 => switch (elf.getNode(ni.parent(&elf.mf).unwrap().?)) {
5236 else => unreachable,
5237 .section, .section_manual_size => |shndx| shndx,
5238 },
5239 .unit_frame_cie,
5240 .unit_debug_info_header,
5241 .unit_debug_info_footer,
5242 .unit_debug_line_header,
5243 .const_debug_info,
5244 .global_debug_info,
5245 .func_frame_fde,
5246 .func_debug_info,
5247 .func_debug_line,
5248 .decl_debug_info,
5249 => switch (elf.getNode(ni.parent(&elf.mf).unwrap().?.parent(&elf.mf).unwrap().?)) {
5250 else => unreachable,
5251 .section, .section_manual_size => |shndx| shndx,
5252 },
5253 };
5254}
5255fn getNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
5256 return switch (elf.getNode(ni)) {
5257 .deleted,
5258 .archive,
5259 .archive_header,
5260 .archive_input_member,
5261 .archive_elf_member_header,
5262 .elf,
5263 .ehdr,
5264 .shdr,
5265 .segment,
5266 .copied_global,
5267 => unreachable,
5268 .section, .section_manual_size => |shndx| shndx.vaddr(elf),
5269 .input_section => |isi| isi.ptrConst(elf).vaddr,
5270 inline .nav,
5271 .uav,
5272 .lazy_code,
5273 .lazy_const_data,
5274 => |i| Symbol.Id.local(i.symbol(elf)).value(elf),
5275 .debug_shared,
5276 .eh_frame_footer,
5277 .unit_padding,
5278 .unit_frame,
5279 .unit_frame_cie,
5280 .unit_debug_info,
5281 .unit_debug_info_header,
5282 .unit_debug_info_footer,
5283 .unit_debug_line,
5284 .unit_debug_line_header,
5285 .unit_debug_rnglists,
5286 .const_debug_info,
5287 .global_debug_info,
5288 .func_frame_fde,
5289 .func_debug_info,
5290 .func_debug_line,
5291 .decl_debug_info,
5292 => elf.computeNodeVAddr(ni),
5293 };
5294}
5295fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
5296 const parent_ni = ni.parent(&elf.mf).unwrap().?;
5297 const parent_vaddr = parent_vaddr: switch (elf.getNode(parent_ni)) {
5298 .deleted,
5299 .archive,
5300 .archive_header,
5301 .archive_input_member,
5302 .archive_elf_member_header,
5303 => unreachable,
5304 .elf => return 0,
5305 .ehdr, .shdr => unreachable,
5306 .segment => |phndx| switch (elf.phdrSlice()) {
5307 inline else => |phdr| elf.targetLoad(&phdr[phndx].vaddr),
5308 },
5309 .section, .section_manual_size => |shndx| if (shndx == elf.shndx.tdata) 0 else shndx.vaddr(elf),
5310 .input_section, .copied_global => unreachable,
5311 inline .nav,
5312 .uav,
5313 .lazy_code,
5314 .lazy_const_data,
5315 => |i| Symbol.Id.local(i.symbol(elf)).value(elf),
5316 .debug_shared, .eh_frame_footer, .unit_padding => unreachable,
5317 .unit_frame, .unit_debug_info, .unit_debug_line => {
5318 const section_offset, _ = parent_ni.location(&elf.mf).resolve(&elf.mf);
5319 break :parent_vaddr elf.getNodeShndx(parent_ni).vaddr(elf) + section_offset;
5320 },
5321 .unit_frame_cie,
5322 .unit_debug_info_header,
5323 .unit_debug_info_footer,
5324 .unit_debug_line_header,
5325 .unit_debug_rnglists,
5326 .const_debug_info,
5327 .global_debug_info,
5328 .func_frame_fde,
5329 .func_debug_info,
5330 .func_debug_line,
5331 .decl_debug_info,
5332 => unreachable,
5333 };
5334 const offset, _ = ni.location(&elf.mf).resolve(&elf.mf);
5335 return parent_vaddr + offset;
5336}
5337fn computeNodeSectionOffset(elf: *Elf, ni: MappedFile.Node.Index) u64 {
5338 const parent_ni = ni.parent(&elf.mf).unwrap().?;
5339 const parent_section_offset = parent_section_offset: switch (elf.getNode(parent_ni)) {
5340 .deleted,
5341 .archive,
5342 .archive_header,
5343 .archive_input_member,
5344 .archive_elf_member_header,
5345 .elf,
5346 .ehdr,
5347 .shdr,
5348 .segment,
5349 => unreachable,
5350 .section, .section_manual_size => 0,
5351 .input_section, .copied_global => unreachable,
5352 .nav, .uav, .lazy_code, .lazy_const_data => unreachable,
5353 .debug_shared, .eh_frame_footer, .unit_padding => unreachable,
5354 .unit_frame, .unit_debug_info, .unit_debug_line => {
5355 const parent_section_offset, _ = parent_ni.location(&elf.mf).resolve(&elf.mf);
5356 break :parent_section_offset parent_section_offset;
5357 },
5358 .unit_frame_cie,
5359 .unit_debug_info_header,
5360 .unit_debug_info_footer,
5361 .unit_debug_line_header,
5362 .unit_debug_rnglists,
5363 .const_debug_info,
5364 .global_debug_info,
5365 .func_frame_fde,
5366 .func_debug_info,
5367 .func_debug_line,
5368 .decl_debug_info,
5369 => unreachable,
5370 };
5371 const offset, _ = ni.location(&elf.mf).resolve(&elf.mf);
5372 return parent_section_offset + offset;
5373}
5374fn computeNodeElfOffset(elf: *Elf, ni: MappedFile.Node.Index) u64 {
5375 return ni.fileLocation(&elf.mf, false).offset - elf.ni.elf.fileLocation(&elf.mf, false).offset;
5376}
5377
5378/// Deletes any existing relocations in the given node, and marks the start of the node's contiguous
5379/// sequence of relocations, so that the caller may append the node's updated relocations.
5380///
5381/// Asserts that `ni` must be a node which supports relocations (see `Elf.Node`). Does not support
5382/// the special-case sections '.plt', '.dynamic', and '.eh_frame_hdr'.
5383pub fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void {
5384 const opts: struct {
5385 first_symbol_reloc: ?*SymbolReloc.Index = null,
5386 skip_symbol_relocs: MappedFile.Node.Index.Optional = .none,
5387 first_node_reloc: ?*NodeReloc.Index = null,
5388 skip_node_relocs: MappedFile.Node.Index.Optional = .none,
5389 first_got_reloc: ?*GotReloc.Index = null,
5390 } = switch (elf.getNode(ni)) {
5391 .deleted,
5392 .archive,
5393 .archive_header,
5394 .archive_input_member,
5395 .archive_elf_member_header,
5396 .elf,
5397 .ehdr,
5398 .shdr,
5399 .segment,
5400 .copied_global,
5401 .debug_shared,
5402 .eh_frame_footer,
5403 .unit_padding,
5404 .unit_frame,
5405 .unit_frame_cie,
5406 .unit_debug_info,
5407 .unit_debug_line,
5408 => unreachable, // cannot contain relocs
5409 .section,
5410 .section_manual_size,
5411 => unreachable, // cannot contain relocs (.plt, .dynamic, and .eh_frame_hdr unsupported)
5412 .input_section => |isi| .{
5413 .first_symbol_reloc = &elf.input_sections.items[@backingInt(isi)].first_symbol_reloc,
5414 .first_got_reloc = &elf.input_sections.items[@backingInt(isi)].first_got_reloc,
5415 },
5416 .nav => |nmi| .{
5417 .first_symbol_reloc = &elf.navs.values()[@backingInt(nmi)].first_symbol_reloc,
5418 .first_got_reloc = &elf.navs.values()[@backingInt(nmi)].first_got_reloc,
5419 },
5420 .uav => |umi| .{
5421 .first_symbol_reloc = &elf.uavs.values()[@backingInt(umi)].first_symbol_reloc,
5422 },
5423 inline .lazy_code, .lazy_const_data => |lmi| .{
5424 .first_symbol_reloc = &elf.lazy.getPtr(lmi.ref().kind).map.values()[lmi.ref().index].first_symbol_reloc,
5425 .first_got_reloc = &elf.lazy.getPtr(lmi.ref().kind).map.values()[lmi.ref().index].first_got_reloc,
5426 },
5427 .unit_debug_info_header => |ui| .{
5428 .first_node_reloc = &elf.dwarf_units[@backingInt(ui)].debug_info_header_first_node_reloc,
5429 },
5430 .unit_debug_info_footer => unreachable, // cannot contain relocs
5431 .unit_debug_line_header => |ui| .{
5432 .first_node_reloc = &elf.dwarf_units[@backingInt(ui)].debug_line_header_first_node_reloc,
5433 },
5434 .unit_debug_rnglists => unreachable, // cannot contain relocs
5435 .const_debug_info => |cpi| .{
5436 .first_symbol_reloc = &elf.dwarf_consts.getPtr(cpi).?.debug_info_first_symbol_reloc,
5437 .first_node_reloc = &elf.dwarf_consts.getPtr(cpi).?.debug_info_first_node_reloc,
5438 },
5439 .global_debug_info => |gi| .{
5440 .first_symbol_reloc = &elf.dwarf_globals.items[@backingInt(gi)].debug_info_first_symbol_reloc,
5441 .first_node_reloc = &elf.dwarf_globals.items[@backingInt(gi)].debug_info_first_node_reloc,
5442 },
5443 .func_frame_fde => |fi| .{
5444 .first_symbol_reloc = &elf.dwarf_funcs.items[@backingInt(fi)].frame_fde_first_symbol_reloc,
5445 .first_node_reloc = &elf.dwarf_funcs.items[@backingInt(fi)].frame_fde_first_node_reloc,
5446 },
5447 .func_debug_info => |fi| .{
5448 .first_symbol_reloc = &elf.dwarf_funcs.items[@backingInt(fi)].debug_info_first_symbol_reloc,
5449 .skip_symbol_relocs = if (elf.navs.getPtr(fi.nav(&elf.dwarf))) |nav|
5450 nav.lsi.index().ptr(elf).node
5451 else
5452 .none,
5453 .first_node_reloc = &elf.dwarf_funcs.items[@backingInt(fi)].debug_info_first_node_reloc,
5454 .skip_node_relocs = fi.get(&elf.dwarf).debug_line_ni,
5455 },
5456 .func_debug_line => |fi| .{
5457 .first_symbol_reloc = &elf.dwarf_funcs.items[@backingInt(fi)].debug_line_first_symbol_reloc,
5458 .first_node_reloc = &elf.dwarf_funcs.items[@backingInt(fi)].debug_line_first_node_reloc,
5459 .skip_node_relocs = fi.get(&elf.dwarf).debug_info_ni,
5460 },
5461 .decl_debug_info => |di| .{
5462 .first_node_reloc = &elf.dwarf_decls.getPtr(di).?.debug_info_first_node_reloc,
5463 },
5464 };
5465
5466 if (opts.first_symbol_reloc) |ptr| {
5467 if (ptr.* != .none) {
5468 for (elf.symbol_relocs.items[@backingInt(ptr.*)..], @backingInt(ptr.*)..) |*reloc, index| {
5469 if (reloc.node != ni.toOptional()) {
5470 if (reloc.node == .none) continue;
5471 if (reloc.node == opts.skip_symbol_relocs) continue;
5472 break;
5473 }
5474 reloc.delete(elf, @fromBackingInt(@intCast(index)));
5475 }
5476 }
5477 ptr.* = @fromBackingInt(@intCast(elf.symbol_relocs.items.len));
5478 }
5479
5480 if (opts.first_node_reloc) |ptr| {
5481 if (ptr.* != .none) {
5482 for (elf.node_relocs.items[@backingInt(ptr.*)..]) |*reloc| {
5483 if (reloc.node != ni.toOptional()) {
5484 if (reloc.node == .none) continue;
5485 if (reloc.node == opts.skip_node_relocs) continue;
5486 break;
5487 }
5488 reloc.delete(elf);
5489 }
5490 }
5491 ptr.* = @fromBackingInt(@intCast(elf.node_relocs.items.len));
5492 }
5493
5494 if (opts.first_got_reloc) |ptr| {
5495 if (ptr.* != .none) {
5496 for (elf.got_relocs.items[@backingInt(ptr.*)..]) |*reloc| {
5497 if (reloc.node != ni.toOptional()) {
5498 if (reloc.node == .none) continue;
5499 break;
5500 }
5501 reloc.delete(elf);
5502 }
5503 }
5504 ptr.* = @fromBackingInt(@intCast(elf.got_relocs.items.len));
5505 }
5506}
5507
5508/// Given that `node` has moved, updates all relocations in `node` as needed. In relocatables, this
5509/// means updating the relocations' offsets. In ELF modules, this means applying the relocations.
5510fn flushMovedNodeRelocs(
5511 elf: *Elf,
5512 node: MappedFile.Node.Index,
5513 node_vaddr: u64,
5514 opts: struct {
5515 first_symbol_reloc: SymbolReloc.Index = .none,
5516 skip_symbol_relocs: MappedFile.Node.Index.Optional = .none,
5517 first_node_reloc: NodeReloc.Index = .none,
5518 skip_node_relocs: MappedFile.Node.Index.Optional = .none,
5519 first_got_reloc: GotReloc.Index = .none,
5520 },
5521) void {
5522 if (opts.first_symbol_reloc != .none) {
5523 for (elf.symbol_relocs.items[@backingInt(opts.first_symbol_reloc)..]) |*reloc| {
5524 if (reloc.node != node.toOptional()) {
5525 if (reloc.node == .none) continue;
5526 if (reloc.node == opts.skip_symbol_relocs) continue;
5527 break;
5528 }
5529 reloc.flushMovedNode(elf, node_vaddr);
5530 }
5531 }
5532
5533 if (opts.first_node_reloc != .none) {
5534 for (elf.node_relocs.items[@backingInt(opts.first_node_reloc)..]) |*reloc| {
5535 if (reloc.node != node.toOptional()) {
5536 if (reloc.node == .none) continue;
5537 if (reloc.node == opts.skip_node_relocs) continue;
5538 break;
5539 }
5540 reloc.flushMovedNode(elf, node_vaddr);
5541 }
5542 }
5543
5544 if (opts.first_got_reloc != .none) {
5545 for (elf.got_relocs.items[@backingInt(opts.first_got_reloc)..]) |*reloc| {
5546 if (reloc.node != node.toOptional()) {
5547 if (reloc.node == .none) continue;
5548 break;
5549 }
5550 reloc.apply(elf);
5551 }
5552 }
5553}
5554
5555fn identClass(elf: *const Elf) std.elf.CLASS {
5556 return @fromBackingInt(elf.ni.elf.sliceConst(&elf.mf)[std.elf.EI.CLASS]);
5557}
5558
5559/// Like `std.elf.ET`, but only includes the ELF machine architectures we support, so that we can
5560/// use exhaustive `switch` statements in the linker implementation.
5561const EhdrMachine = enum(u16) {
5562 AARCH64 = @backingInt(std.elf.EM.AARCH64),
5563 LOONGARCH = @backingInt(std.elf.EM.LOONGARCH),
5564 PPC64 = @backingInt(std.elf.EM.PPC64),
5565 RISCV = @backingInt(std.elf.EM.RISCV),
5566 SPARCV9 = @backingInt(std.elf.EM.SPARCV9),
5567 X86_64 = @backingInt(std.elf.EM.X86_64),
5568
5569 fn toElf(m: EhdrMachine) std.elf.EM {
5570 return @bitCast(m);
5571 }
5572 /// Returns `null` if `m` is not a supported ELF machine architecture.
5573 fn fromElf(m: std.elf.EM) ?EhdrMachine {
5574 return std.enums.fromInt(EhdrMachine, @backingInt(m));
5575 }
5576};
5577/// Like `std.elf.ET`, but only includes the types of ELF file we can produce, so that we can use
5578/// exhaustive `switch` statements in the linker implementation.
5579const EhdrType = enum(u16) {
5580 REL = @backingInt(std.elf.ET.REL),
5581 EXEC = @backingInt(std.elf.ET.EXEC),
5582 DYN = @backingInt(std.elf.ET.DYN),
5583 fn toElf(t: EhdrType) std.elf.ET {
5584 return @bitCast(t);
5585 }
5586};
5587fn ehdrMachine(elf: *const Elf) EhdrMachine {
5588 const ehdr_slice = elf.ni.ehdr.sliceConst(&elf.mf);
5589 switch (elf.identClass()) {
5590 .NONE, _ => unreachable,
5591 inline else => |class| {
5592 const ehdr: *const class.ElfN().Ehdr = @ptrCast(@alignCast(ehdr_slice));
5593 return @bitCast(elf.targetLoad(&ehdr.machine));
5594 },
5595 }
5596}
5597fn ehdrType(elf: *const Elf) EhdrType {
5598 const ehdr_slice = elf.ni.ehdr.sliceConst(&elf.mf);
5599 switch (elf.identClass()) {
5600 .NONE, _ => unreachable,
5601 inline else => |class| {
5602 const ehdr: *const class.ElfN().Ehdr = @ptrCast(@alignCast(ehdr_slice));
5603 return @bitCast(elf.targetLoad(&ehdr.type));
5604 },
5605 }
5606}
5607
5608fn targetPtrSize(elf: *const Elf) u8 {
5609 return elf.identClass().size();
5610}
5611/// Page alignment for the target platform.
5612/// Usually this returns the maximum page size supported on the
5613/// target to maximize compatibility but there can be exceptions.
5614fn targetPageAlign(elf: *const Elf) Alignment {
5615 return .fromByteUnits(switch (elf.ehdrMachine()) {
5616 .AARCH64 => 0x10000,
5617 .LOONGARCH => 0x10000,
5618 .PPC64 => 0x10000,
5619 .RISCV => 0x1000,
5620 .SPARCV9 => 0x100000,
5621 .X86_64 => 0x1000,
5622
5623 //.@"68K" => 0x2000,
5624 //.AMDGPU => 0x10000,
5625 //.ARC_COMPACT2 => 0x2000,
5626 //.AVR => 0x1,
5627 //.BPF => 0x100000,
5628 //.MIPS => 0x10000,
5629 //.MSP430 => 0x4,
5630 //.PPC => 0x10000,
5631 //.QDSP6 => 0x10000,
5632 //.SPARC => 0x10000,
5633 //.SPARC32PLUS => 0x10000,
5634 });
5635}
5636fn targetEndian(elf: *const Elf) std.lang.Endian {
5637 const ident_data: std.elf.DATA = @fromBackingInt(elf.ni.elf.sliceConst(&elf.mf)[std.elf.EI.DATA]);
5638 return ident_data.endian();
5639}
5640fn targetTlsVariant(elf: *const Elf) union(enum) {
5641 /// TP points to the start of the TCB, which immediately precedes the executable's TLS block.
5642 I_original: struct { tcb_size: u8 },
5643 /// TP points at a fixed offset from the start of the executable's TLS block.
5644 I_modified: struct { tp_off: u32 },
5645 /// TP points to the TCB, which immediately *succeeds* the executable's TLS block. (In other
5646 /// words, TP points to the *end* of the executable's TLS block.)
5647 II,
5648} {
5649 return switch (elf.ehdrMachine()) {
5650 .AARCH64 => .{ .I_original = .{ .tcb_size = 2 * elf.targetPtrSize() } },
5651 .LOONGARCH => .{ .I_original = .{ .tcb_size = elf.targetPtrSize() } },
5652 .PPC64 => .{ .I_modified = .{ .tp_off = 0x7000 } },
5653 .RISCV => .{ .I_modified = .{ .tp_off = 0 } },
5654 .SPARCV9 => .II,
5655 .X86_64 => .II,
5656 };
5657}
5658const PltInfo = struct {
5659 /// If not `null`, there is a `.got.plt` section containing the target addresses, and the PLT
5660 /// itself is immutable. If `false`, JUMP_SLOT relocations write directly to the `.plt` section,
5661 /// which must therefore be mutable.
5662 got_plt: ?struct { header_entries: u8 },
5663 /// If not `null`, there is a `.plt.sec` section, and every function in the PLT has both a
5664 /// `.plt` entry and a `.plt.sec` entry. Jumps targeting the PLT should jump to the `.plt.sec`
5665 /// entry, not the `.plt` entry. The `.plt.sec` section has no header entries, and is aligned to
5666 /// the same boundary as the `.plt` section.
5667 plt_sec: ?struct { entry_size: u8 },
5668 @"align": Alignment,
5669 entry_size: u8,
5670 header_entries: u8,
5671
5672 fn fromMachine(machine: EhdrMachine) PltInfo {
5673 return switch (machine) {
5674 .AARCH64, .PPC64, .RISCV => @panic(@tagName(machine)),
5675 .LOONGARCH => .{
5676 .got_plt = .{ .header_entries = 2 },
5677 .plt_sec = null,
5678 .@"align" = .@"4",
5679 .entry_size = 16,
5680 .header_entries = 2,
5681 },
5682 .SPARCV9 => .{
5683 .got_plt = null,
5684 .plt_sec = null,
5685 .@"align" = .fromByteUnits(256),
5686 .entry_size = 32,
5687 .header_entries = 4,
5688 },
5689 .X86_64 => .{
5690 .got_plt = .{ .header_entries = 3 },
5691 .plt_sec = .{ .entry_size = 16 },
5692 .@"align" = .@"16",
5693 .entry_size = 16,
5694 .header_entries = 1,
5695 },
5696 };
5697 }
5698};
5699fn targetPltInfo(elf: *const Elf) PltInfo {
5700 return .fromMachine(elf.ehdrMachine());
5701}
5702const DynsymHashInfo = enum(u32) {
5703 @"4" = 4,
5704 @"8" = 8,
5705
5706 fn Int(comptime self: DynsymHashInfo) type {
5707 return switch (self) {
5708 .@"4" => u32,
5709 .@"8" => u64,
5710 };
5711 }
5712
5713 fn Header(comptime self: DynsymHashInfo) type {
5714 return switch (self) {
5715 .@"4" => std.elf.hash.Header32,
5716 .@"8" => std.elf.hash.Header64,
5717 };
5718 }
5719};
5720fn targetDynsymHashInfo(elf: *const Elf) DynsymHashInfo {
5721 return switch (elf.ehdrMachine()) {
5722 else => .@"4",
5723 // TODO: Alpha and S390x will need to use either `."@4"` or `.@"8"` depending on `elf.identClass()`.
5724 };
5725}
5726/// Specifies any restrictions the current target has regarding how segments are ordered in the
5727/// virtual address space. Most targets do not have any such restrictions.
5728fn targetSegmentLoadAddressRestrictions(elf: *const Elf) enum {
5729 none,
5730 /// The "mutable data" segment must be the last loadable segment in the virtual address space.
5731 data_last,
5732} {
5733 return switch (elf.ehdrMachine()) {
5734 .AARCH64,
5735 .PPC64,
5736 .RISCV,
5737 .X86_64,
5738 .LOONGARCH,
5739 => .none,
5740
5741 // SPARC uses `R_SPARC_PC{10,22}` relocations to construct pointers to the GOT, but these
5742 // relocations write an *unsigned* PC-relative offset. This cannot even be worked around by
5743 // using a larger code model, because the crt `_start` assembly always uses these specific
5744 // relocations. Therefore, to avoid relocation errors, all code must appear before the GOT
5745 // in the virtual address space. The easiest way for us to do that is to ensure that the
5746 // "mutable data" segment, containing the GOT, is the last segment in the address space.
5747 .SPARCV9 => .data_last,
5748 };
5749}
5750fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child {
5751 const pointer_ty = @typeInfo(@TypeOf(ptr)).pointer;
5752 const Child = pointer_ty.child;
5753 const alignment = pointer_ty.attrs.@"align" orelse @alignOf(Child);
5754 return switch (@typeInfo(Child)) {
5755 else => @compileError(@typeName(Child)),
5756 .int => std.mem.toNative(Child, ptr.*, elf.targetEndian()),
5757 .@"enum" => |@"enum"| @fromBackingInt(elf.targetLoad(@as(*align(alignment) const @"enum".tag_type, @ptrCast(ptr)))),
5758 .@"struct" => |@"struct"| @bitCast(
5759 elf.targetLoad(@as(*align(alignment) @"struct".backing_integer.?, @ptrCast(ptr))),
5760 ),
5761 };
5762}
5763fn targetStore(elf: *const Elf, ptr: anytype, val: @typeInfo(@TypeOf(ptr)).pointer.child) void {
5764 const pointer_ty = @typeInfo(@TypeOf(ptr)).pointer;
5765 const Child = pointer_ty.child;
5766 const alignment = pointer_ty.attrs.@"align" orelse @alignOf(Child);
5767 return switch (@typeInfo(Child)) {
5768 else => @compileError(@typeName(Child)),
5769 .int => ptr.* = std.mem.nativeTo(Child, val, elf.targetEndian()),
5770 .@"enum" => |@"enum"| elf.targetStore(
5771 @as(*align(alignment) @"enum".tag_type, @ptrCast(ptr)),
5772 @backingInt(val),
5773 ),
5774 .@"struct" => |@"struct"| elf.targetStore(
5775 @as(*align(alignment) @"struct".backing_integer.?, @ptrCast(ptr)),
5776 @bitCast(val),
5777 ),
5778 };
5779}
5780
5781const EhdrPtr = union(std.elf.CLASS) {
5782 NONE: noreturn,
5783 @"32": *std.elf.Elf32.Ehdr,
5784 @"64": *std.elf.Elf64.Ehdr,
5785};
5786fn ehdrPtr(elf: *Elf) EhdrPtr {
5787 const slice = elf.ni.ehdr.slice(&elf.mf);
5788 return switch (elf.identClass()) {
5789 .NONE, _ => unreachable,
5790 inline else => |class| @unionInit(
5791 EhdrPtr,
5792 @tagName(class),
5793 @ptrCast(@alignCast(slice)),
5794 ),
5795 };
5796}
5797
5798const PhdrSlice = union(std.elf.CLASS) {
5799 NONE: noreturn,
5800 @"32": []std.elf.Elf32.Phdr,
5801 @"64": []std.elf.Elf64.Phdr,
5802};
5803fn phdrSlice(elf: *Elf) PhdrSlice {
5804 assert(elf.ehdrType() != .REL);
5805 return switch (elf.identClass()) {
5806 .NONE, _ => unreachable,
5807 inline else => |class| @unionInit(PhdrSlice, @tagName(class), @ptrCast(@alignCast(
5808 elf.ni.phdr.slice(&elf.mf)[0 .. elf.phdrs.items.len * @sizeOf(class.ElfN().Phdr)],
5809 ))),
5810 };
5811}
5812
5813const ShdrPtr = union(std.elf.CLASS) {
5814 NONE: noreturn,
5815 @"32": *std.elf.Elf32.Shdr,
5816 @"64": *std.elf.Elf64.Shdr,
5817};
5818fn shdrPtr(elf: *Elf, shndx: Section.Index) ShdrPtr {
5819 const slice = elf.ni.shdr.slice(&elf.mf);
5820 switch (elf.identClass()) {
5821 .NONE, _ => unreachable,
5822 inline else => |class| {
5823 const shdr_slice: []class.ElfN().Shdr = @ptrCast(@alignCast(
5824 slice[0 .. @sizeOf(class.ElfN().Shdr) * (1 + elf.shdrs.items.len)],
5825 ));
5826 const shdr_ptr = &shdr_slice[@backingInt(shndx)];
5827 return @unionInit(ShdrPtr, @tagName(class), shdr_ptr);
5828 },
5829 }
5830}
5831
5832const SymPtr = union(std.elf.CLASS) {
5833 NONE: noreturn,
5834 @"32": *std.elf.Elf32.Sym,
5835 @"64": *std.elf.Elf64.Sym,
5836};
5837fn symPtr(elf: *Elf, index: Symbol.Index) SymPtr {
5838 const raw_slice = Section.Index.symtab.get(elf).ni.slice(&elf.mf);
5839 switch (elf.shdrPtr(.symtab)) {
5840 inline else => |shdr, class| {
5841 const size = elf.targetLoad(&shdr.size);
5842 const slice: []class.ElfN().Sym = @ptrCast(@alignCast(raw_slice[0..@intCast(size)]));
5843 return @unionInit(SymPtr, @tagName(class), &slice[@backingInt(index)]);
5844 },
5845 }
5846}
5847fn dynsymPtr(elf: *Elf, index: u32) SymPtr {
5848 const raw_slice = elf.shndx.dynsym.get(elf).ni.slice(&elf.mf);
5849 switch (elf.shdrPtr(elf.shndx.dynsym)) {
5850 inline else => |shdr, class| {
5851 const size = elf.targetLoad(&shdr.size);
5852 const slice: []class.ElfN().Sym = @ptrCast(@alignCast(raw_slice[0..@intCast(size)]));
5853 return @unionInit(SymPtr, @tagName(class), &slice[index]);
5854 },
5855 }
5856}
5857
5858fn navType(elf: *const Elf, nav_resolved: InternPool.Nav.Resolved) std.elf.STT {
5859 const any_non_single_threaded = elf.base.comp.config.any_non_single_threaded;
5860 return if (any_non_single_threaded and nav_resolved.@"threadlocal")
5861 .TLS
5862 else if (elf.base.comp.zcu.?.intern_pool.isFunctionType(nav_resolved.type))
5863 .FUNC
5864 else
5865 .OBJECT;
5866}
5867fn mapInputSection(elf: *Elf, opts: struct {
5868 name: []const u8,
5869 flags: std.elf.SHF,
5870 entsize: std.elf.Xword,
5871}) (Error || error{
5872 UnsupportedSectionFlags,
5873 TlsSectionUnavailable,
5874 StripSection,
5875 SectionFlagsConflict,
5876 SectionTypeConflict,
5877})!Section.Index {
5878 const gpa = elf.base.comp.gpa;
5879 if (opts.flags.INFO_LINK or
5880 opts.flags.LINK_ORDER or
5881 opts.flags.OS_NONCONFORMING or
5882 (opts.flags.EXECINSTR and opts.flags.WRITE) or
5883 (opts.flags.EXECINSTR and opts.flags.TLS))
5884 {
5885 return error.UnsupportedSectionFlags;
5886 }
5887 if (opts.flags.TLS and elf.ni.tls == .none) {
5888 assert(!elf.base.comp.config.any_non_single_threaded);
5889 return error.TlsSectionUnavailable;
5890 }
5891
5892 if (elf.base.comp.config.debug_format == .strip and
5893 std.mem.startsWith(u8, opts.name, ".debug_") and
5894 !opts.flags.ALLOC)
5895 {
5896 return error.StripSection;
5897 }
5898
5899 const name: []const u8 = switch (elf.ehdrType()) {
5900 .REL => opts.name,
5901 .EXEC, .DYN => name: {
5902 if (std.mem.startsWith(u8, opts.name, ".text.")) break :name ".text";
5903 if (std.mem.startsWith(u8, opts.name, ".rodata.")) break :name ".rodata";
5904 if (std.mem.startsWith(u8, opts.name, ".data.")) break :name ".data";
5905 if (std.mem.startsWith(u8, opts.name, ".data.rel.ro.")) break :name ".data.rel.ro";
5906 if (std.mem.startsWith(u8, opts.name, ".tdata.")) break :name ".tdata";
5907 if (std.mem.startsWith(u8, opts.name, ".gcc_except_table.")) break :name ".gcc_except_table";
5908 // TODO: actually generate a bss section!
5909 if (std.mem.eql(u8, opts.name, ".bss")) break :name ".data";
5910 if (std.mem.startsWith(u8, opts.name, ".bss.")) break :name ".data";
5911 // TODO: actually generate a tbss section!
5912 if (std.mem.eql(u8, opts.name, ".tbss")) break :name ".tdata";
5913 if (std.mem.startsWith(u8, opts.name, ".tbss.")) break :name ".tdata";
5914 break :name opts.name;
5915 },
5916 };
5917 const existing_shndx: Section.Index = existing: {
5918 const name_shstrtab = try elf.string(.shstrtab, name);
5919 const gop = try elf.section_by_name.getOrPut(gpa, name_shstrtab);
5920 if (gop.found_existing) {
5921 break :existing @fromBackingInt(@intCast(gop.index + 1)); // +1 to account for SHN_UDNEF
5922 }
5923 errdefer assert(elf.section_by_name.pop().?.key == name_shstrtab);
5924 const parent_node: MappedFile.Node.Index = parent: {
5925 if (!opts.flags.ALLOC) break :parent elf.ni.elf;
5926 if (opts.flags.EXECINSTR) break :parent elf.ni.text;
5927 if (opts.flags.TLS) break :parent elf.ni.tls.unwrap().?;
5928 if (opts.flags.WRITE) break :parent elf.ni.data;
5929 break :parent elf.ni.rodata;
5930 };
5931 assert(gop.index == elf.shdrs.items.len);
5932 return elf.addSection(parent_node, .{
5933 .name = name,
5934 .type = .NULL, // because initial size is 0
5935 .flags = flags: {
5936 // We need to decompress the section for linking.
5937 var flags = opts.flags;
5938 flags.COMPRESSED = false;
5939 break :flags flags;
5940 },
5941 .entsize = std.math.lossyCast(u32, opts.entsize),
5942 });
5943 };
5944 switch (elf.shdrPtr(existing_shndx)) {
5945 inline else => |shdr| {
5946 // Validate that the input is compatible with this section
5947 const cur_flags = elf.targetLoad(&shdr.flags).shf;
5948 if (cur_flags.EXECINSTR != opts.flags.EXECINSTR or
5949 cur_flags.WRITE != opts.flags.WRITE or
5950 cur_flags.TLS != opts.flags.TLS)
5951 {
5952 return error.SectionFlagsConflict;
5953 }
5954
5955 switch (elf.targetLoad(&shdr.type)) {
5956 .NULL, .PROGBITS, .X86_64_UNWIND => {},
5957 else => return error.SectionTypeConflict,
5958 }
5959
5960 // All okay, combine the section flags
5961 elf.targetStore(&shdr.flags, .{ .shf = .{
5962 .EXECINSTR = cur_flags.EXECINSTR,
5963 .WRITE = cur_flags.WRITE,
5964 .TLS = cur_flags.TLS,
5965 .ALLOC = cur_flags.ALLOC or opts.flags.ALLOC,
5966 .STRINGS = cur_flags.STRINGS and opts.flags.STRINGS,
5967 .MERGE = cur_flags.MERGE and opts.flags.MERGE,
5968 } });
5969 },
5970 }
5971 return existing_shndx;
5972}
5973fn navMapIndex(elf: *Elf, zcu: *Zcu, nav_index: InternPool.Nav.Index) Error!Node.NavMapIndex {
5974 const gpa = zcu.gpa;
5975 const ip = &zcu.intern_pool;
5976 const nav = ip.getNav(nav_index);
5977
5978 try elf.ensureUnusedSymbolCapacity(1, .all_local);
5979 try elf.nodes.ensureUnusedCapacity(gpa, 1);
5980 try elf.navs.ensureUnusedCapacity(gpa, 1);
5981
5982 const nav_gop = elf.navs.getOrPutAssumeCapacity(nav_index);
5983 const nmi: Node.NavMapIndex = @fromBackingInt(@intCast(nav_gop.index));
5984 if (!nav_gop.found_existing) {
5985 const shndx: Section.Index = section: {
5986 if (nav.resolved.?.@"linksection".toSlice(ip)) |@"linksection"| {
5987 if (elf.mapInputSection(.{
5988 .name = @"linksection",
5989 .flags = .{
5990 .ALLOC = true,
5991 .EXECINSTR = ip.isFunctionType(nav.resolved.?.type),
5992 .WRITE = !nav.resolved.?.@"const",
5993 .TLS = elf.base.comp.config.any_non_single_threaded and
5994 nav.resolved.?.@"threadlocal",
5995 },
5996 .entsize = 0,
5997 })) |shndx| {
5998 break :section shndx;
5999 } else |err| switch (err) {
6000 else => |e| return e,
6001 error.StripSection,
6002 error.TlsSectionUnavailable,
6003 error.UnsupportedSectionFlags,
6004 error.SectionTypeConflict,
6005 error.SectionFlagsConflict,
6006 => {}, // fall back to default behavior below
6007
6008 }
6009 }
6010 if (elf.base.comp.config.any_non_single_threaded and nav.resolved.?.@"threadlocal") {
6011 break :section elf.shndx.tdata;
6012 } else if (!nav.resolved.?.@"const") {
6013 break :section .data;
6014 } else if (ip.isFunctionType(nav.resolved.?.type)) {
6015 break :section .text;
6016 } else {
6017 break :section .data_rel_ro; // TODO: it would be better to use `.rodata` if the NAV value doesn't have relocs
6018 }
6019 };
6020 const alignment: Alignment = switch (Type.fromInterned(nav.resolved.?.type).zigTypeTag(zcu)) {
6021 .@"fn" => a: {
6022 const mod = zcu.navFileScope(nav_index).mod.?;
6023 const target = &mod.resolved_target.result;
6024 break :a .fromIp(switch (nav.resolved.?.@"align") {
6025 else => |a| a.maxStrict(target_util.minFunctionAlignment(target)),
6026 .none => switch (mod.optimize_mode) {
6027 .debug, .safe, .fast => target_util.defaultFunctionAlignment(target),
6028 .small => target_util.minFunctionAlignment(target),
6029 }.maxStrict(Type.fromInterned(nav.resolved.?.type).abiAlignment(zcu)),
6030 });
6031 },
6032 else => switch (nav.resolved.?.@"align") {
6033 .none => .fromIp(Type.fromInterned(nav.resolved.?.type).abiAlignment(zcu)),
6034 else => |a| .fromIp(a),
6035 },
6036 };
6037 try shndx.ensureAligned(elf, alignment);
6038 const node = elf.addNodeAssumeCapacity(try shndx.get(elf).ni.addFloatingChild(gpa, &elf.mf, .{
6039 .alignment = alignment,
6040 }), .{ .nav = nmi });
6041 nav_gop.value_ptr.* = .{
6042 .lsi = elf.addLocalSymbolAssumeCapacity(.{
6043 .node = .wrap(node),
6044 .name = try elf.string(.strtab, nav.fqn.toSlice(ip)),
6045 .value = 0,
6046 .size = 0,
6047 .type = elf.navType(nav.resolved.?),
6048 .shndx = shndx,
6049 }),
6050 .first_symbol_reloc = .none,
6051 .first_got_reloc = .none,
6052 };
6053 }
6054 return nmi;
6055}
6056
6057fn uavMapIndex(
6058 elf: *Elf,
6059 uav_val: InternPool.Index,
6060 uav_align: InternPool.Alignment,
6061) Error!Node.UavMapIndex {
6062 const gpa = elf.base.comp.gpa;
6063 const zcu = elf.base.comp.zcu.?;
6064
6065 try elf.ensureUnusedSymbolCapacity(1, .all_local);
6066 try elf.nodes.ensureUnusedCapacity(gpa, 1);
6067 try elf.uavs.ensureUnusedCapacity(gpa, 1);
6068 try elf.pending_uavs.ensureUnusedCapacity(gpa, 1);
6069
6070 const abi_align = Value.fromInterned(uav_val).typeOf(zcu).abiAlignment(zcu);
6071 const resolved_align: Alignment = switch (uav_align) {
6072 .none => .fromIp(abi_align),
6073 else => |a| .fromIp(a.minStrict(abi_align)),
6074 };
6075
6076 const uav_gop = elf.uavs.getOrPutAssumeCapacity(uav_val);
6077 const umi: Node.UavMapIndex = @fromBackingInt(@intCast(uav_gop.index));
6078 if (!uav_gop.found_existing) {
6079 const shndx: Section.Index = .data_rel_ro; // TODO: it would be better to use `.rodata` if the UAV value doesn't have relocs
6080 try shndx.ensureAligned(elf, resolved_align);
6081 const node = elf.addNodeAssumeCapacity(try shndx.get(elf).ni.addFloatingChild(gpa, &elf.mf, .{
6082 .moved = true, // see assert at end of `genUav`
6083 .alignment = resolved_align,
6084 }), .{ .uav = umi });
6085 var name_buf: [std.fmt.count("__anon_{d}", .{std.math.maxInt(u32)})]u8 = undefined;
6086 const name = std.mem.print(&name_buf, "__anon_{d}", .{umi}) catch unreachable;
6087 uav_gop.value_ptr.* = .{
6088 .lsi = elf.addLocalSymbolAssumeCapacity(.{
6089 .node = .wrap(node),
6090 .name = try elf.string(.strtab, name),
6091 .value = 0,
6092 .size = 0,
6093 .type = .OBJECT,
6094 .shndx = shndx,
6095 }),
6096 .first_symbol_reloc = .none,
6097 };
6098 elf.const_prog_node.increaseEstimatedTotalItems(1);
6099 elf.pending_uavs.appendAssumeCapacity(umi);
6100 } else {
6101 const node = uav_gop.value_ptr.lsi.index().ptr(elf).node.unwrap().?;
6102 const shndx = elf.getNodeShndx(node);
6103 try shndx.ensureAligned(elf, resolved_align);
6104 if (resolved_align.order(node.alignment(&elf.mf)).compare(.gt)) {
6105 try node.realign(gpa, &elf.mf, resolved_align);
6106 }
6107 }
6108 return umi;
6109}
6110
6111/// Internal error set used by input parsing functions `loadObject`, `loadArchive`, `loadDso`.
6112const LoadParseInputError = Error || Io.File.SeekError || Io.Reader.Error;
6113
6114/// Returns `error.BadMagic` if a DSO or static archive has an incorrect magic number, which
6115/// indicates to the frontend that the input could be a GNU ld script instead.
6116pub fn loadInput(elf: *Elf, input: link.Input) (link.Error || error{BadMagic})!void {
6117 const diags = &elf.base.comp.link_diags;
6118 elf.loadInputInner(input) catch |err| switch (err) {
6119 else => |e| return e,
6120 error.MappedFileIo => return diags.fail(
6121 "failed to write output file: {t}",
6122 .{elf.mf.io_err.?},
6123 ),
6124 };
6125}
6126fn loadInputInner(elf: *Elf, input: link.Input) (Error || error{BadMagic})!void {
6127 const comp = elf.base.comp;
6128 const diags = &comp.link_diags;
6129 const io = comp.io;
6130 var buf: [4096]u8 = undefined;
6131 switch (input) {
6132 .object => |object| {
6133 var fr = object.file.reader(io, &buf);
6134 elf.loadObject(object.path, null, &fr, .{
6135 .offset = fr.logicalPos(),
6136 .size = fr.getSize() catch |err| switch (err) {
6137 error.Canceled => |e| return e,
6138 else => |e| return diags.fail(
6139 "failed to stat \"{f}\": {t}",
6140 .{ object.path.fmtEscapeString(), e },
6141 ),
6142 },
6143 }) catch |err| switch (err) {
6144 else => |e| return e,
6145 error.EndOfStream => return diags.failParse(
6146 object.path,
6147 "unexpected eof",
6148 .{},
6149 ),
6150 error.AccessDenied, error.Unexpected, error.Unseekable => |e| return diags.fail(
6151 "failed to read \"{f}\": {t}",
6152 .{ object.path.fmtEscapeString(), e },
6153 ),
6154 error.ReadFailed => switch (fr.err.?) {
6155 error.Canceled => |e| return e,
6156 else => |e| return diags.fail(
6157 "failed to read \"{f}\": {t}",
6158 .{ object.path.fmtEscapeString(), e },
6159 ),
6160 },
6161 };
6162 },
6163 .archive => |archive| {
6164 var fr = archive.file.reader(io, &buf);
6165 elf.loadArchive(archive.path, &fr) catch |err| switch (err) {
6166 else => |e| return e,
6167 error.EndOfStream => return diags.failParse(
6168 archive.path,
6169 "unexpected eof",
6170 .{},
6171 ),
6172 error.AccessDenied, error.Unexpected, error.Unseekable => |e| return diags.fail(
6173 "failed to read \"{f}\": {t}",
6174 .{ archive.path.fmtEscapeString(), e },
6175 ),
6176 error.ReadFailed => switch (fr.err.?) {
6177 error.Canceled => |e| return e,
6178 else => |e| return diags.fail(
6179 "failed to read \"{f}\": {t}",
6180 .{ archive.path.fmtEscapeString(), e },
6181 ),
6182 },
6183 };
6184 },
6185 .res => unreachable,
6186 .dso => |dso| {
6187 try elf.needed.ensureUnusedCapacity(elf.base.comp.gpa, 1);
6188 var fr = dso.file.reader(io, &buf);
6189 elf.loadDso(dso.path, &fr) catch |err| switch (err) {
6190 else => |e| return e,
6191 error.EndOfStream => return diags.failParse(
6192 dso.path,
6193 "unexpected eof",
6194 .{},
6195 ),
6196 error.AccessDenied, error.Unexpected, error.Unseekable => |e| return diags.fail(
6197 "failed to read \"{f}\": {t}",
6198 .{ dso.path.fmtEscapeString(), e },
6199 ),
6200 error.ReadFailed => switch (fr.err.?) {
6201 error.Canceled => |e| return e,
6202 else => |e| return diags.fail(
6203 "failed to read \"{f}\": {t}",
6204 .{ dso.path.fmtEscapeString(), e },
6205 ),
6206 },
6207 };
6208 },
6209 .dso_exact => |dso_exact| {
6210 log.debug("load dso_exact '{f}'", .{std.zig.fmtString(dso_exact.name)});
6211 if (elf.shndx.dynamic != .UNDEF) {
6212 try elf.needed.put(elf.base.comp.gpa, try elf.string(.dynstr, dso_exact.name), {});
6213 }
6214 // TODO: we need to get a resolved file path from the frontend, because we need to read
6215 // the shared object to discover symbol types.
6216 },
6217 }
6218}
6219fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadParseInputError || error{BadMagic})!void {
6220 const comp = elf.base.comp;
6221 const gpa = comp.gpa;
6222 const diags = &comp.link_diags;
6223 const r = &fr.interface;
6224
6225 log.debug("loadArchive({f})", .{path.fmtEscapeString()});
6226
6227 if (elf.ehdrType() == .REL) return; // this input does not affect the output artifact
6228
6229 {
6230 const magic = r.take(std.elf.ARMAG.len) catch |err| switch (err) {
6231 error.ReadFailed => |e| return e,
6232 error.EndOfStream => return error.BadMagic,
6233 };
6234 if (!std.mem.eql(u8, magic, std.elf.ARMAG)) {
6235 return error.BadMagic;
6236 }
6237 }
6238 var strtab: Io.Writer.Allocating = .init(gpa);
6239 defer strtab.deinit();
6240 while (r.takeStruct(std.elf.ar_hdr, .native)) |header| {
6241 if (!std.mem.eql(u8, &header.ar_fmag, std.elf.ARFMAG))
6242 return diags.failParse(path, "bad file magic", .{});
6243 const offset = fr.logicalPos();
6244 const size = header.size() catch
6245 return diags.failParse(path, "bad member size", .{});
6246 if (std.mem.eql(u8, &header.ar_name, std.elf.STRNAME)) {
6247 strtab.clearRetainingCapacity();
6248 try strtab.ensureTotalCapacityPrecise(size);
6249 r.streamExact(&strtab.writer, size) catch |err| switch (err) {
6250 else => |e| return e,
6251 error.WriteFailed => return error.OutOfMemory,
6252 };
6253 continue;
6254 }
6255 load_object: {
6256 if (std.mem.eql(u8, &header.ar_name, std.elf.SYMNAME) or
6257 std.mem.eql(u8, &header.ar_name, std.elf.SYM64NAME) or
6258 std.mem.eql(u8, &header.ar_name, std.elf.SYMDEFNAME) or
6259 std.mem.eql(u8, &header.ar_name, std.elf.SYMDEFSORTEDNAME))
6260 {
6261 break :load_object;
6262 }
6263 const member = header.name() orelse member: {
6264 const strtab_offset = header.nameOffset() catch |err| switch (err) {
6265 error.Overflow => break :member error.Overflow,
6266 error.InvalidCharacter => break :load_object,
6267 } orelse break :load_object;
6268 const strtab_written = strtab.written();
6269 if (strtab_offset > strtab_written.len) break :member error.Overflow;
6270 const member = std.mem.sliceTo(strtab_written[strtab_offset..], '\n');
6271 break :member if (std.mem.endsWith(u8, member, "/"))
6272 member[0 .. member.len - "/".len]
6273 else
6274 member;
6275 } catch |err| switch (err) {
6276 error.Overflow => return diags.failParse(path, "bad member name offset", .{}),
6277 };
6278 try elf.loadObject(path, member, fr, .{ .offset = offset, .size = size });
6279 }
6280 try fr.seekTo(std.mem.alignForward(u64, offset + size, 2));
6281 } else |err| switch (err) {
6282 else => |e| return e,
6283 error.EndOfStream => if (!fr.atEnd()) return error.EndOfStream,
6284 }
6285}
6286fn fmtMemberString(member: ?[]const u8) std.fmt.Alt(?[]const u8, memberStringEscape) {
6287 return .{ .data = member };
6288}
6289fn memberStringEscape(member: ?[]const u8, w: *Io.Writer) Io.Writer.Error!void {
6290 try w.print("({f})", .{std.zig.fmtString(member orelse return)});
6291}
6292fn loadObject(
6293 elf: *Elf,
6294 path: std.Build.Cache.Path,
6295 member: ?[]const u8,
6296 fr: *Io.File.Reader,
6297 fl: MappedFile.Node.FileLocation,
6298) LoadParseInputError!void {
6299 const comp = elf.base.comp;
6300 const gpa = comp.gpa;
6301 const diags = &comp.link_diags;
6302 const r = &fr.interface;
6303
6304 const input_index: Node.InputIndex = @fromBackingInt(@intCast(elf.inputs.items.len));
6305 log.debug("loadObject({f}{f})", .{ path.fmtEscapeString(), fmtMemberString(member) });
6306 elf.checkInputIdent(path, r) catch |err| switch (err) {
6307 else => |e| return e,
6308 error.BadMagic => return diags.failParse(
6309 path,
6310 "bad ELF magic",
6311 .{},
6312 ),
6313 };
6314
6315 const input = try elf.inputs.addOne(gpa);
6316 input.* = .{
6317 .path = path,
6318 .member = if (member) |m| try gpa.dupe(u8, m) else null,
6319 .extra = undefined,
6320 };
6321 if (elf.archive) |*archive| {
6322 // We're creating a static library, so just add this input as an archive member.
6323 assert(member == null); // don't try to put static library members into other static libraries
6324
6325 const first_member_oni = archive.header_ni.next(&elf.mf);
6326
6327 if (first_member_oni.unwrap()) |first_member_ni| switch (elf.getNode(first_member_ni)) {
6328 .archive_input_member, .archive_elf_member_header => {},
6329 .elf => unreachable, // always preceded by `.archive_elf_member_header`
6330 else => unreachable, // never a child of `.archive`
6331 };
6332
6333 try elf.nodes.ensureUnusedCapacity(gpa, 1);
6334 const new_member_ni = elf.addNodeAssumeCapacity(
6335 try archive.ni.addFooterChildBefore(gpa, &elf.mf, first_member_oni, .{
6336 .size = Alignment.@"2".forward(@sizeOf(std.elf.ar_hdr) + fl.size),
6337 .alignment = .@"2",
6338 }),
6339 .{ .archive_input_member = input_index },
6340 );
6341 input.extra = .{ .node = new_member_ni };
6342 elf.input_prog_node.increaseEstimatedTotalItems(1);
6343
6344 // The contents of the input will be written to the file by an idle task (`flushInput`), but
6345 // we do need to write the input's archive member header (`ar_hdr`) now, for two reasons:
6346 //
6347 // * If the input file has a long name, we need to add it to the archive member name string
6348 // table, which must happen deterministically (i.e. not in an idle task).
6349 //
6350 // * `flushInput` needs to know the actual file size (before padding to the alignment).
6351 const member_ar_hdr: *std.elf.ar_hdr = @ptrCast(
6352 new_member_ni.slice(&elf.mf)[0..@sizeOf(std.elf.ar_hdr)],
6353 );
6354 member_ar_hdr.* = .{
6355 .ar_name = undefined, // populated below
6356 .ar_date = "0 ".*,
6357 .ar_uid = "0 ".*,
6358 .ar_gid = "0 ".*,
6359 .ar_mode = "644 ".*,
6360 .ar_size = undefined, // populated below
6361 .ar_fmag = std.elf.ARFMAG.*,
6362 };
6363
6364 if (std.mem.print(&member_ar_hdr.ar_size, "{d}", .{fl.size})) |size_str| {
6365 @memset(member_ar_hdr.ar_size[size_str.len..], ' ');
6366 } else |err| switch (err) {
6367 error.NoSpaceLeft => return diags.failParse(
6368 path,
6369 "file size of {Bi} exceeds maximum size of archive member",
6370 .{fl.size},
6371 ),
6372 }
6373
6374 const member_name = std.fs.path.basename(path.sub_path);
6375 // After this call returns, `member_ar_hdr` is invalidated.
6376 try elf.populateArchiveMemberName(member_ar_hdr, member_name);
6377
6378 // Since we are not emitting the archive symbol table (yet?) we do not need to parse
6379 // the symbols in this input.
6380 return;
6381 }
6382
6383 elf.input_pending_index += 1;
6384 try elf.ensureUnusedSymbolCapacity(1, .all_local);
6385 input.extra = .{ .file_symbol = elf.addLocalSymbolAssumeCapacity(.{
6386 .node = .none,
6387 .name = try elf.string(.strtab, std.fs.path.stem(member orelse path.sub_path)),
6388 .value = 0,
6389 .size = 0,
6390 .type = .FILE,
6391 .shndx = .ABS,
6392 }) };
6393 const target_endian = elf.targetEndian();
6394 switch (elf.identClass()) {
6395 .NONE, _ => unreachable,
6396 inline else => |class| {
6397 const ElfN = class.ElfN();
6398 const ehdr = try r.peekStruct(ElfN.Ehdr, target_endian);
6399 if (ehdr.type != .REL) return diags.failParse(path, "unsupported object type", .{});
6400 if (ehdr.machine != elf.ehdrMachine().toElf())
6401 return diags.failParse(path, "bad machine", .{});
6402 if (ehdr.shoff == 0 or ehdr.shnum <= 1) return;
6403 if (ehdr.shoff + @as(u64, ehdr.shentsize) * @as(u64, ehdr.shnum) > fl.size)
6404 return diags.failParse(path, "bad section header location", .{});
6405 if (ehdr.shentsize < @sizeOf(ElfN.Shdr))
6406 return diags.failParse(path, "unsupported shentsize", .{});
6407 const sections = try gpa.alloc(struct { shdr: ElfN.Shdr, isi: ?InputSection.Index }, ehdr.shnum);
6408 defer gpa.free(sections);
6409 try fr.seekTo(fl.offset + ehdr.shoff);
6410 for (sections) |*section| {
6411 section.* = .{
6412 .shdr = try r.peekStruct(ElfN.Shdr, target_endian),
6413 .isi = null,
6414 };
6415 try r.discardAll(ehdr.shentsize);
6416 switch (section.shdr.type) {
6417 .NULL, .NOBITS => {},
6418 else => if (section.shdr.offset + section.shdr.size > fl.size)
6419 return diags.failParse(path, "bad section location", .{}),
6420 }
6421 }
6422 const shstrtab = shstrtab: {
6423 if (ehdr.shstrndx == std.elf.SHN_UNDEF or ehdr.shstrndx >= ehdr.shnum)
6424 return diags.failParse(path, "missing section names", .{});
6425 const shdr = &sections[ehdr.shstrndx].shdr;
6426 if (shdr.type != .STRTAB) return diags.failParse(path, "invalid shstrtab type", .{});
6427 const shstrtab = try gpa.alloc(u8, @intCast(shdr.size));
6428 errdefer gpa.free(shstrtab);
6429 try fr.seekTo(fl.offset + shdr.offset);
6430 try r.readSliceAll(shstrtab);
6431 break :shstrtab shstrtab;
6432 };
6433 defer gpa.free(shstrtab);
6434 try elf.nodes.ensureUnusedCapacity(gpa, ehdr.shnum - 1);
6435 try elf.input_sections.ensureUnusedCapacity(gpa, ehdr.shnum - 1);
6436 for (sections[1..]) |*section| {
6437 if (section.shdr.name >= shstrtab.len) continue;
6438 const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0);
6439 if (!comp.config.any_unwind_tables and std.mem.eql(u8, name, ".eh_frame")) continue;
6440 const opts: struct {
6441 shndx: Section.Index,
6442 node_fixed: bool,
6443 } = switch (section.shdr.type) {
6444 else => continue,
6445 .PROGBITS, .NOBITS, .X86_64_UNWIND => opts: {
6446 const shndx = elf.mapInputSection(.{
6447 .name = name,
6448 .flags = section.shdr.flags.shf,
6449 .entsize = section.shdr.entsize,
6450 }) catch |err| switch (err) {
6451 else => |e| return e,
6452 error.StripSection => continue,
6453 error.TlsSectionUnavailable => return diags.failParse(
6454 path,
6455 "thread-local storage section '{s}' is incompatible with '-fsingle-threaded'",
6456 .{name},
6457 ),
6458 error.UnsupportedSectionFlags => if (!section.shdr.flags.shf.ALLOC) {
6459 // It probably doesn't matter, just skip this section.
6460 continue;
6461 } else return diags.failParse(
6462 path,
6463 "unsupported flags for section '{s}'",
6464 .{name},
6465 ),
6466 error.SectionTypeConflict => if (!section.shdr.flags.shf.ALLOC) {
6467 // It probably doesn't matter, just skip this section.
6468 continue;
6469 } else return diags.failParse(
6470 path,
6471 "type of section '{s}' conflicts with other inputs",
6472 .{name},
6473 ),
6474 error.SectionFlagsConflict => if (!section.shdr.flags.shf.ALLOC) {
6475 // It probably doesn't matter, just skip this section.
6476 continue;
6477 } else return diags.failParse(
6478 path,
6479 "flags of section '{s}' conflict with other inputs",
6480 .{name},
6481 ),
6482 };
6483 if (section.shdr.flags.shf.COMPRESSED) {
6484 // SHF_COMPRESSED is only allowed on non-alloc sections.
6485 if (section.shdr.flags.shf.ALLOC) return diags.failParse(
6486 path,
6487 "section '{s}' has conflicting flags SHF_ALLOC and SHF_COMPRESSED",
6488 .{name},
6489 );
6490 // TODO: handle compressed input sections. We'll need to set a flag to
6491 // indicate that `flushInputSection` needs to decompress the section.
6492 // But because this section isn't SHF_ALLOC, it's probably okay to just
6493 // skip it for now.
6494 continue;
6495 }
6496 break :opts .{
6497 .shndx = shndx,
6498 // For well-known sections, we know that it's fine to have e.g. random
6499 // padding, so there's no need to make the sections fixed. For custom
6500 // sections, however, we do want fixed nodes to avoid padding.
6501 .node_fixed = shndx != .text and
6502 shndx != .rodata and
6503 shndx != .data and
6504 shndx != .data_rel_ro and
6505 shndx != elf.shndx.tdata,
6506 };
6507 },
6508 inline .INIT_ARRAY, .FINI_ARRAY, .PREINIT_ARRAY => |@"type"| .{
6509 .shndx = shndx: {
6510 // TODO: the input section name may include a "priority" value between 1
6511 // and 65535 which should affect the order we assemble input sections in
6512 const init_fini_section_name: []const u8 = switch (@"type") {
6513 .INIT_ARRAY => "init_array",
6514 .FINI_ARRAY => "fini_array",
6515 .PREINIT_ARRAY => "preinit_array",
6516 else => comptime unreachable,
6517 };
6518 const shndx: *Section.Index = &@field(elf.shndx, init_fini_section_name);
6519 const need_addralign: u8 = switch (class) {
6520 .NONE, _ => unreachable,
6521 .@"32" => 4,
6522 .@"64" => 8,
6523 };
6524 if (section.shdr.addralign != need_addralign) {
6525 return diags.failParse(path, "bad addralign on {t} shdr", .{@"type"});
6526 }
6527 if (shndx.* == .UNDEF) {
6528 try elf.createInitFiniArraySection(shndx, init_fini_section_name, @"type");
6529 }
6530 switch (elf.shdrPtr(shndx.*)) {
6531 inline else => |shdr| {
6532 const old_size = elf.targetLoad(&shdr.size);
6533 const new_size = old_size + section.shdr.size;
6534 elf.targetStore(&shdr.size, @intCast(new_size));
6535 elf.updateInitFiniArraySectionSize(shndx.*, init_fini_section_name);
6536 },
6537 }
6538 break :shndx shndx.*;
6539 },
6540 // This node must be fixed to prevent padding from being added between different
6541 // INIT_ARRAY/FINI_ARRAY/PREINIT_ARRAY input sections.
6542 .node_fixed = true,
6543 },
6544 };
6545 const need_align: Alignment = .fromByteUnits(
6546 std.math.ceilPowerOfTwoAssert(usize, @intCast(@max(section.shdr.addralign, 1))),
6547 );
6548 try opts.shndx.ensureAligned(elf, need_align);
6549 const add_node_opts: MappedFile.Node.AddOptions = .{
6550 .size = need_align.forward(section.shdr.size),
6551 .alignment = need_align,
6552 .moved = true, // see assert at end of `flushInputSection`
6553 };
6554 const ni = elf.addNodeAssumeCapacity(
6555 if (opts.node_fixed) ni: {
6556 const shndx_ni = opts.shndx.get(elf).ni;
6557 const after_oni: MappedFile.Node.Index.Optional = after: {
6558 const last_ni = shndx_ni.last(&elf.mf).unwrap() orelse break :after .none;
6559 break :after switch (last_ni.position(&elf.mf)) {
6560 .header => .wrap(last_ni),
6561 .footer, .floating => .none,
6562 };
6563 };
6564 break :ni try shndx_ni.addHeaderChildAfter(gpa, &elf.mf, after_oni, add_node_opts);
6565 } else try opts.shndx.get(elf).ni.addFloatingChild(gpa, &elf.mf, add_node_opts),
6566 .{ .input_section = @fromBackingInt(@intCast(elf.input_sections.items.len)) },
6567 );
6568 section.isi = @fromBackingInt(@intCast(elf.input_sections.items.len));
6569 elf.input_sections.addOneAssumeCapacity().* = .{
6570 .input = input_index,
6571 .file_location = .{
6572 .offset = fl.offset + section.shdr.offset,
6573 .size = if (section.shdr.type == .NOBITS) 0 else section.shdr.size,
6574 },
6575 // The section vaddr is initially 0, because the symbol addresses are
6576 // zero-based. This will eventually be updated by `flushMoved`.
6577 .vaddr = 0,
6578 .node = ni,
6579 .first_symbol_reloc = .none,
6580 .first_got_reloc = .none,
6581 };
6582 elf.input_prog_node.increaseEstimatedTotalItems(1);
6583 }
6584 var symmap: std.ArrayList(Symbol.Id) = .empty;
6585 defer symmap.deinit(gpa);
6586 for (sections[1..], 1..) |*symtab, symtab_shndx| switch (symtab.shdr.type) {
6587 else => {},
6588 .SYMTAB => {
6589 if (symtab.shdr.entsize < @sizeOf(ElfN.Sym))
6590 return diags.failParse(path, "unsupported symtab entsize", .{});
6591 const strtab = strtab: {
6592 if (symtab.shdr.link == std.elf.SHN_UNDEF or symtab.shdr.link >= ehdr.shnum)
6593 return diags.failParse(path, "missing symbol names", .{});
6594 const shdr = &sections[symtab.shdr.link].shdr;
6595 if (shdr.type != .STRTAB)
6596 return diags.failParse(path, "invalid strtab type", .{});
6597 const strtab = try gpa.alloc(u8, @intCast(shdr.size));
6598 errdefer gpa.free(strtab);
6599 try fr.seekTo(fl.offset + shdr.offset);
6600 try r.readSliceAll(strtab);
6601 break :strtab strtab;
6602 };
6603 defer gpa.free(strtab);
6604 const symnum = std.math.sub(u32, std.math.divExact(
6605 u32,
6606 @intCast(symtab.shdr.size),
6607 @intCast(symtab.shdr.entsize),
6608 ) catch return diags.failParse(
6609 path,
6610 "symtab section size (0x{x}) is not a multiple of entsize (0x{x})",
6611 .{ symtab.shdr.size, symtab.shdr.entsize },
6612 ), 1) catch continue;
6613 symmap.clearRetainingCapacity();
6614 try symmap.resize(gpa, symnum);
6615 try elf.ensureUnusedSymbolCapacity(symnum, .maybe_global);
6616 try fr.seekTo(fl.offset + symtab.shdr.offset + symtab.shdr.entsize);
6617 for (symmap.items) |*si| {
6618 si.* = .null;
6619 const input_sym = try r.peekStruct(ElfN.Sym, target_endian);
6620 try r.discardAll64(symtab.shdr.entsize);
6621 if (input_sym.name >= strtab.len or input_sym.shndx >= ehdr.shnum) continue;
6622
6623 const name = std.mem.sliceTo(strtab[input_sym.name..], 0);
6624
6625 const sym_type: std.elf.STT = switch (input_sym.info.type) {
6626 .NOTYPE, .OBJECT, .FUNC, .TLS => |t| t,
6627 .SECTION => .NOTYPE,
6628 .FILE, .COMMON, _ => continue,
6629 };
6630
6631 if (input_sym.shndx == std.elf.SHN_UNDEF) switch (input_sym.info.bind) {
6632 else => |bind| return diags.failParse(
6633 path,
6634 "symbol '{s}' has unsupported binding (0x{x})",
6635 .{ name, bind },
6636 ),
6637 .LOCAL => continue,
6638 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {
6639 si.* = elf.addGlobalSymbolAssumeCapacity(.{
6640 .node = .none,
6641 .name = try .string(elf, name),
6642 .value = input_sym.value,
6643 .size = input_sym.size,
6644 .type = sym_type,
6645 .bind = switch (bind) {
6646 .WEAK, .GNU_UNIQUE => .weak,
6647 .GLOBAL => .strong,
6648 else => unreachable,
6649 },
6650 .visibility = input_sym.other.visibility,
6651 .shndx = .UNDEF,
6652 }) catch |err| switch (err) {
6653 error.MultipleDefinitions => unreachable, // shndx is .UNDEF
6654 };
6655 continue;
6656 },
6657 };
6658
6659 const input_section_node = (sections[input_sym.shndx].isi orelse continue).node(elf);
6660
6661 switch (input_sym.info.bind) {
6662 else => |bind| return diags.failParse(
6663 path,
6664 "symbol '{s}' has unsupported binding (0x{x})",
6665 .{ name, bind },
6666 ),
6667 .LOCAL => {
6668 const lsi = elf.addLocalSymbolAssumeCapacity(.{
6669 .node = .wrap(input_section_node),
6670 .name = try elf.string(.strtab, name),
6671 .value = input_sym.value,
6672 .size = input_sym.size,
6673 .type = sym_type,
6674 .shndx = elf.getNodeShndx(input_section_node),
6675 });
6676 si.* = .local(lsi);
6677 },
6678 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {
6679 si.* = elf.addGlobalSymbolAssumeCapacity(.{
6680 .node = .wrap(input_section_node),
6681 .name = try .string(elf, name),
6682 .value = input_sym.value,
6683 .size = input_sym.size,
6684 .type = sym_type,
6685 .bind = switch (bind) {
6686 .WEAK, .GNU_UNIQUE => .weak,
6687 .GLOBAL => .strong,
6688 else => unreachable,
6689 },
6690 .visibility = input_sym.other.visibility,
6691 .shndx = elf.getNodeShndx(input_section_node),
6692 }) catch |err| switch (err) {
6693 error.MultipleDefinitions => return diags.failParse(
6694 path,
6695 "multiple definitions of '{s}'",
6696 .{name},
6697 ),
6698 };
6699 },
6700 }
6701 }
6702 for (sections[1..]) |*rel_sec| switch (rel_sec.shdr.type) {
6703 else => {},
6704 inline .REL, .RELA => |sht| {
6705 if (rel_sec.shdr.link != symtab_shndx or rel_sec.shdr.info == std.elf.SHN_UNDEF or
6706 rel_sec.shdr.info >= ehdr.shnum) continue;
6707 const Rel = switch (sht) {
6708 else => comptime unreachable,
6709 .REL => ElfN.Rel,
6710 .RELA => ElfN.Rela,
6711 };
6712 if (rel_sec.shdr.entsize < @sizeOf(Rel))
6713 return diags.failParse(path, "unsupported rel entsize", .{});
6714
6715 const loc_sec = &sections[rel_sec.shdr.info];
6716 const loc_node = (loc_sec.isi orelse continue).node(elf);
6717 elf.resetNodeRelocs(loc_node);
6718
6719 const relnum = std.math.divExact(
6720 u32,
6721 @intCast(rel_sec.shdr.size),
6722 @intCast(rel_sec.shdr.entsize),
6723 ) catch return diags.failParse(
6724 path,
6725 "relocation section size (0x{x}) is not a multiple of entsize (0x{x})",
6726 .{ rel_sec.shdr.size, rel_sec.shdr.entsize },
6727 );
6728 try elf.ensureUnusedRelocCapacity(loc_node, relnum);
6729 try fr.seekTo(fl.offset + rel_sec.shdr.offset);
6730 for (0..relnum) |_| {
6731 const rel = try r.peekStruct(Rel, target_endian);
6732 try r.discardAll64(rel_sec.shdr.entsize);
6733 if (rel.info.sym == 0) continue;
6734 if (rel.info.sym > symnum) return diags.failParse(
6735 path,
6736 "relocation target symbol index {d} exceeds symtab size",
6737 .{rel.info.sym},
6738 );
6739 const target = symmap.items[rel.info.sym - 1];
6740 if (target == Symbol.Id.null) {
6741 // If this is not an SHF_ALLOC section, then let's not report
6742 // this for now, because it probably doesn't affect the final
6743 // binary's functionality for this section to be a bit broken.
6744 if (loc_sec.shdr.flags.shf.ALLOC) {
6745 diags.addParseError(
6746 path,
6747 "unsupported symbol at index {d} required for relocation",
6748 .{rel.info.sym},
6749 );
6750 }
6751 continue;
6752 }
6753 const rt: MachineRelocType = .wrap(rel.info.type, elf);
6754 elf.addRelocAssumeCapacity(
6755 loc_node,
6756 rel.offset - loc_sec.shdr.addr,
6757 target,
6758 rel.addend,
6759 rt,
6760 ) catch |err| switch (err) {
6761 else => |e| return e,
6762 error.UnknownRelocation => diags.addParseError(
6763 path,
6764 "unknown relocation type '{f}'",
6765 .{rt.fmt(elf)},
6766 ),
6767 error.NonStaticRelocation => diags.addParseError(
6768 path,
6769 "non-static relocation type '{f}'",
6770 .{rt.fmt(elf)},
6771 ),
6772 error.UnimplementedRelocation => diags.addParseError(
6773 path,
6774 "TODO(Elf2): unimplemented relocation type '{f}'",
6775 .{rt.fmt(elf)},
6776 ),
6777 };
6778 }
6779 },
6780 };
6781 },
6782 };
6783 },
6784 }
6785}
6786/// This function may resize the archive header, so therefore invalidates `member_ar_hdr`.
6787fn populateArchiveMemberName(elf: *Elf, member_ar_hdr: *std.elf.ar_hdr, member_name: []const u8) Error!void {
6788 if (std.mem.print(&member_ar_hdr.ar_name, "{s}/", .{member_name})) |name_str| {
6789 @memset(member_ar_hdr.ar_name[name_str.len..], ' ');
6790 return;
6791 } else |err| switch (err) {
6792 error.NoSpaceLeft => {}, // handled below
6793 }
6794
6795 const gpa = elf.base.comp.gpa;
6796 const archive_header_ni = elf.archive.?.header_ni;
6797
6798 // The member's name is too big to put directly in the `ar_name` field, so it needs to go in the
6799 // "long name" string table instead (in the special member named "//").
6800
6801 _, const old_archive_header_size = archive_header_ni.location(&elf.mf).resolve(&elf.mf);
6802
6803 // We're going to add a new string at the end of the table. Update `member_ar_hdr` first,
6804 // because resizing the string table will invalidate it.
6805 const string_table_offset = old_archive_header_size - (std.elf.ARMAG.len + @sizeOf(std.elf.ar_hdr));
6806 if (std.mem.print(&member_ar_hdr.ar_name, "/{d}", .{string_table_offset})) |name_str| {
6807 @memset(member_ar_hdr.ar_name[name_str.len..], ' ');
6808 } else |inner_err| switch (inner_err) {
6809 error.NoSpaceLeft => {
6810 // The string table offset is itself too big to represent. This means the string table's
6811 // *size* is definitely too big to represent (we only get 10 bytes for that whereas we
6812 // get 16 here!), so as long as we still add the string, we're guaranteed to get a link
6813 // error for that reason. Therefore, we can just ignore this error and carry on.
6814 },
6815 }
6816
6817 // We set the size of the archive header node exactly, because we want padding bytes to go into
6818 // the root `.archive` node. That way, those bytes could still be used to grow the string table
6819 // if necessary, but they could also be used for new archive members.
6820 try archive_header_ni.resizeLeaf(gpa, &elf.mf, old_archive_header_size + member_name.len + 2);
6821
6822 const dest_slice = archive_header_ni.slice(&elf.mf)[@intCast(old_archive_header_size)..];
6823 @memcpy(dest_slice[0 .. dest_slice.len - 2], member_name);
6824 @memcpy(dest_slice[dest_slice.len - 2 ..], "/\n"); // yes, the terminator is weird
6825}
6826fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadParseInputError || error{BadMagic})!void {
6827 const comp = elf.base.comp;
6828 const gpa = comp.gpa;
6829 const diags = &comp.link_diags;
6830 const r = &fr.interface;
6831
6832 log.debug("loadDso({f})", .{path.fmtEscapeString()});
6833 try elf.checkInputIdent(path, r);
6834
6835 if (elf.ehdrType() == .REL) return; // this input does not affect the output artifact
6836
6837 const target_endian = elf.targetEndian();
6838 switch (elf.identClass()) {
6839 .NONE, _ => unreachable,
6840 inline else => |class| {
6841 const ElfN = class.ElfN();
6842 const ehdr = try r.peekStruct(ElfN.Ehdr, target_endian);
6843 if (ehdr.type != .DYN) return diags.failParse(path, "unsupported dso type", .{});
6844 if (ehdr.machine != elf.ehdrMachine().toElf())
6845 return diags.failParse(path, "bad machine", .{});
6846 if (ehdr.shnum > 0) try fr.seekTo(ehdr.shoff);
6847 // We're going to need to know the alignment of every section later.
6848 const section_aligns = try gpa.alloc(Alignment, ehdr.shnum);
6849 defer gpa.free(section_aligns);
6850 const dynamic_sh: ElfN.Shdr, const dynsym_sh: ElfN.Shdr = sh: {
6851 var dynamic_sh: ?ElfN.Shdr = null;
6852 var dynsym_sh: ?ElfN.Shdr = null;
6853 for (section_aligns) |*section_align| {
6854 const sh = try r.peekStruct(ElfN.Shdr, target_endian);
6855 try r.discardAll(ehdr.shentsize);
6856 section_align.* = .fromByteUnits(std.math.ceilPowerOfTwoAssert(
6857 usize,
6858 @intCast(@max(sh.addralign, 1)),
6859 ));
6860 switch (sh.type) {
6861 else => {},
6862 .DYNAMIC => dynamic_sh = sh,
6863 .DYNSYM => dynsym_sh = sh,
6864 }
6865 }
6866 break :sh .{
6867 dynamic_sh orelse return diags.failParse(path, "missing SHT_DYNAMIC section", .{}),
6868 dynsym_sh orelse return diags.failParse(path, "missing SHT_DYNSYM section", .{}),
6869 };
6870 };
6871 const dynstr_sh: ElfN.Shdr = sh: {
6872 if (dynsym_sh.link >= ehdr.shnum) {
6873 return diags.failParse(path, "bad dynamic string table section index", .{});
6874 }
6875 try fr.seekTo(ehdr.shoff + dynsym_sh.link * ehdr.shentsize);
6876 break :sh try r.peekStruct(ElfN.Shdr, target_endian);
6877 };
6878
6879 if (dynamic_sh.entsize != @sizeOf(ElfN.Addr) * 2) {
6880 return diags.failParse(path, "bad dynamic section entsize", .{});
6881 }
6882 const dynnum = std.math.divExact(
6883 u32,
6884 @intCast(dynamic_sh.size),
6885 @sizeOf(ElfN.Addr) * 2,
6886 ) catch return diags.failParse(
6887 path,
6888 "dynamic section size (0x{x}) is not a multiple of entsize (0x{x})",
6889 .{ dynamic_sh.size, @sizeOf(ElfN.Addr) * 2 },
6890 );
6891
6892 if (dynsym_sh.entsize < @sizeOf(ElfN.Sym)) {
6893 return diags.failParse(path, "bad dynsym entsize", .{});
6894 }
6895 const symnum = std.math.divExact(
6896 u32,
6897 @intCast(dynsym_sh.size),
6898 @intCast(dynsym_sh.entsize),
6899 ) catch return diags.failParse(
6900 path,
6901 "dynsym size (0x{x}) is not a multiple of entsize (0x{x})",
6902 .{ dynsym_sh.size, dynsym_sh.entsize },
6903 );
6904
6905 const dynstr = try gpa.alloc(u8, @intCast(dynstr_sh.size));
6906 defer gpa.free(dynstr);
6907 try fr.seekTo(dynstr_sh.offset);
6908 try r.readSliceAll(dynstr);
6909
6910 // Find the DT_SONAME dynamic entry so that it can become our DT_NEEDED entry.
6911 try fr.seekTo(dynamic_sh.offset);
6912 const soname: []const u8 = for (0..dynnum) |_| {
6913 const tag = try r.takeInt(ElfN.Addr, target_endian);
6914 const val = try r.takeInt(ElfN.Addr, target_endian);
6915 if (tag == std.elf.DT_SONAME) {
6916 // val is a dynstr index
6917 if (val >= dynstr.len) {
6918 return diags.failParse(path, "bad soname string", .{});
6919 }
6920 break std.mem.sliceTo(dynstr[@intCast(val)..], 0);
6921 }
6922 } else std.fs.path.basename(path.sub_path);
6923 try elf.needed.put(gpa, try elf.string(.dynstr, soname), {});
6924
6925 // Scan the symbol table and populate `elf.dso_globals`.
6926 const first_global = @min(dynsym_sh.info, symnum);
6927 try elf.dso_globals.ensureUnusedCapacity(gpa, symnum - first_global);
6928 try elf.ensureUnusedPltCapacity(symnum - first_global);
6929 try fr.seekTo(dynsym_sh.offset + first_global * dynsym_sh.entsize);
6930 for (first_global..symnum) |_| {
6931 const sym = try r.peekStruct(ElfN.Sym, target_endian);
6932 try r.discardAll(@intCast(dynsym_sh.entsize));
6933
6934 switch (sym.info.bind) {
6935 else => continue,
6936 .GLOBAL, .WEAK, .GNU_UNIQUE => {},
6937 }
6938 // STV_HIDDEN/STV_INTERNAL symbols should be marked as STB_LOCAL and hence skipped
6939 // above, but we might as well double-check.
6940 switch (sym.other.visibility) {
6941 .HIDDEN, .INTERNAL => continue,
6942 .DEFAULT, .PROTECTED => {},
6943 }
6944
6945 if (sym.shndx == std.elf.SHN_UNDEF) continue;
6946 if (sym.shndx >= ehdr.shnum) continue;
6947
6948 if (sym.name >= dynstr.len) {
6949 return diags.failParse(path, "bad symbol name string", .{});
6950 }
6951
6952 // We need to guess the worst-case alignment of the symbol. Yes, I know this seems
6953 // insane---refer to the doc comment on `alignment` in `Elf.dso_globals`.
6954 const sym_align: Alignment = switch (sym.value) {
6955 0 => section_aligns[sym.shndx],
6956 else => section_aligns[sym.shndx].min(@fromBackingInt(@intCast(@ctz(sym.value)))),
6957 };
6958
6959 const name = try elf.string(.strtab, std.mem.sliceTo(dynstr[sym.name..], 0));
6960 const gop = elf.dso_globals.getOrPutAssumeCapacity(name);
6961
6962 if (gop.found_existing and gop.value_ptr.type != .NOTYPE) {
6963 if (sym.size > gop.value_ptr.size or
6964 sym_align.compare(.gt, gop.value_ptr.alignment))
6965 {
6966 gop.value_ptr.size = @max(gop.value_ptr.size, sym.size);
6967 gop.value_ptr.alignment = gop.value_ptr.alignment.max(sym_align);
6968 if (elf.copied_globals.get(name)) |copied_global| {
6969 // We have a copy relocation for this global, but the amount of space we
6970 // reserved for it could be too small or underaligned!
6971 try Section.Index.data.ensureAligned(elf, gop.value_ptr.alignment);
6972 try copied_global.node.resizeLeaf(
6973 gpa,
6974 &elf.mf,
6975 gop.value_ptr.alignment.forward(gop.value_ptr.size),
6976 );
6977 try copied_global.node.realign(gpa, &elf.mf, gop.value_ptr.alignment);
6978 const global_ptr = elf.globalByName(name).?;
6979 switch (elf.symPtr(global_ptr.symtab_index)) {
6980 inline else => |sym_ptr| elf.targetStore(&sym_ptr.size, @intCast(gop.value_ptr.size)),
6981 }
6982 switch (elf.dynsymPtr(global_ptr.dynsym_index)) {
6983 inline else => |dynsym_ptr| elf.targetStore(&dynsym_ptr.size, @intCast(gop.value_ptr.size)),
6984 }
6985 }
6986 }
6987 continue;
6988 }
6989
6990 gop.value_ptr.* = .{
6991 .type = sym.info.type,
6992 .size = sym.size,
6993 .alignment = sym_align,
6994 };
6995
6996 // If there's already an undefined symbol by this name of type STT_NOTYPE, populate
6997 // its type now.
6998 const global_ptr = elf.globals.strong_undef.getPtr(name) orelse
6999 elf.globals.weak_undef.getPtr(name) orelse
7000 continue;
7001
7002 if (global_ptr.dynsym_index == 0) continue;
7003
7004 if (elf.want_copied_globals.swapRemove(name)) {
7005 // We just found a DSO definition of a symbol for which we wanted a copy
7006 // relocation, so add one if we can!
7007 _ = try elf.maybeAddCopyRelocation(name);
7008 }
7009
7010 const sym_ptr = @field(elf.symPtr(global_ptr.symtab_index), @tagName(class));
7011 errdefer comptime unreachable; // messing with the output file could invalidate `sym_ptr`
7012
7013 switch (elf.targetLoad(&sym_ptr.other).visibility) {
7014 .HIDDEN, .INTERNAL, .PROTECTED => continue,
7015 .DEFAULT => {},
7016 }
7017
7018 const cur_info = elf.targetLoad(&sym_ptr.info);
7019 if (cur_info.type == .NOTYPE) {
7020 const new_type: std.elf.STT = switch (sym.info.type) {
7021 .GNU_IFUNC => .FUNC,
7022 else => |t| t,
7023 };
7024
7025 elf.targetStore(&sym_ptr.info, .{
7026 .bind = cur_info.bind,
7027 .type = new_type,
7028 });
7029
7030 const dynsym_ptr = @field(elf.dynsymPtr(global_ptr.dynsym_index), @tagName(class));
7031 elf.targetStore(&dynsym_ptr.info, .{
7032 .bind = elf.targetLoad(&dynsym_ptr.info).bind,
7033 .type = new_type,
7034 });
7035
7036 if (new_type == .FUNC) {
7037 // We turned STT_NOTYPE into STT_FUNC, so we now need a PLT entry...
7038 elf.addPltEntry(name, global_ptr.dynsym_index);
7039 // ...and therefore, we need to re-apply that symbol's relocations, as
7040 // some might be targeting its PLT entry.
7041 Symbol.Id.global(name).applyTargetRelocs(elf);
7042 }
7043 }
7044 }
7045 },
7046 }
7047}
7048
7049/// Validates that the `std.elf.Ident` present at the start of `r` is a compatible link input.
7050///
7051/// Returns an error if it is incompatible, or if the ident is broken or missing---usually
7052/// `error.AlreadyReported`, but if the magic number is missing or incorrect, returns
7053/// `error.BadMagic` instead.
7054///
7055/// Does not advance the position of `r`. Requires `r` to have a 16-byte buffer.
7056fn checkInputIdent(
7057 elf: *const Elf,
7058 path: std.Build.Cache.Path,
7059 r: *Io.Reader,
7060) error{ BadMagic, EndOfStream, AlreadyReported, ReadFailed }!void {
7061 const diags = &elf.base.comp.link_diags;
7062
7063 const magic = r.peek(std.elf.MAGIC.len) catch |err| switch (err) {
7064 error.ReadFailed => |e| return e,
7065 error.EndOfStream => return error.BadMagic,
7066 };
7067 if (!std.mem.eql(u8, magic, std.elf.MAGIC)) {
7068 return error.BadMagic;
7069 }
7070
7071 const ident = try r.peekStructPointer(std.elf.Ident);
7072 const target: *const std.elf.Ident =
7073 @ptrCast(elf.ni.elf.sliceConst(&elf.mf)[0..@sizeOf(std.elf.Ident)]);
7074
7075 if (ident.class != target.class) return diags.failParse(
7076 path,
7077 "bad ELF class ({?s})",
7078 .{std.enums.tagName(std.elf.CLASS, ident.class)},
7079 );
7080 if (ident.data != target.data) return diags.failParse(
7081 path,
7082 "bad ELF data encoding ({?s})",
7083 .{std.enums.tagName(std.elf.DATA, ident.data)},
7084 );
7085 if (ident.version != target.version) return diags.failParse(
7086 path,
7087 "bad ELF version ({d})",
7088 .{ident.version},
7089 );
7090
7091 // OSABI is a bit more complex. On Linux, `.NONE` and `.GNU` are both valid and both common.
7092 // It sounds reasonable to allow the value we chose *and* allow `.NONE`.
7093 const expect_abiversion: u8 = abiver: {
7094 if (ident.osabi == .NONE) break :abiver 0;
7095 if (ident.osabi == target.osabi) break :abiver target.abiversion;
7096 return diags.failParse(
7097 path,
7098 "bad ELF OS/ABI ({?s})",
7099 .{std.enums.tagName(std.elf.OSABI, ident.osabi)},
7100 );
7101 };
7102 if (ident.abiversion != expect_abiversion) return diags.failParse(
7103 path,
7104 "bad ELF ABI version ({d})",
7105 .{ident.abiversion},
7106 );
7107}
7108
7109fn createInitFiniArraySection(
7110 elf: *Elf,
7111 shndx: *Section.Index,
7112 comptime name: []const u8,
7113 @"type": std.elf.SHT,
7114) Error!void {
7115 assert(shndx.* == .UNDEF);
7116 const gpa = elf.base.comp.gpa;
7117 const addr_align: Alignment = switch (elf.identClass()) {
7118 .NONE, _ => unreachable,
7119 .@"32" => .@"4",
7120 .@"64" => .@"8",
7121 };
7122 assert(elf.section_by_name.count() == elf.shdrs.items.len);
7123 try elf.section_by_name.ensureUnusedCapacity(gpa, 1);
7124 shndx.* = try elf.addSection(elf.ni.data_rel_ro, .{
7125 .name = "." ++ name,
7126 .type = @"type",
7127 .flags = .{ .WRITE = true, .ALLOC = true },
7128 .node_align = addr_align,
7129 .manual_size = true,
7130 });
7131 elf.section_by_name.putAssumeCapacityNoClobber(shndx.name(elf), {});
7132 try elf.ensureUnusedSymbolCapacity(2, .maybe_global);
7133 // These symbols definitely already have strong definitions, because we added them alongside the
7134 // other linker-defined symbols, all the way back in `initHeaders`.
7135 const start_sym_name = try elf.string(.strtab, "__" ++ name ++ "_start");
7136 const end_sym_name = try elf.string(.strtab, "__" ++ name ++ "_end");
7137 elf.setGlobalSymbolValue(start_sym_name, elf.globals.strong_def.getPtr(start_sym_name).?, .{
7138 .node = .wrap(shndx.get(elf).ni),
7139 .value = shndx.vaddr(elf),
7140 .size = 0,
7141 .type = .NOTYPE,
7142 .shndx = shndx.*,
7143 });
7144 elf.setGlobalSymbolValue(end_sym_name, elf.globals.strong_def.getPtr(end_sym_name).?, .{
7145 .node = .wrap(shndx.get(elf).ni),
7146 .value = shndx.vaddr(elf),
7147 .size = 0,
7148 .type = .NOTYPE,
7149 .shndx = shndx.*,
7150 });
7151}
7152fn updateInitFiniArraySectionSize(
7153 elf: *Elf,
7154 shndx: Section.Index,
7155 comptime name: []const u8,
7156) void {
7157 const end_vaddr: u64 = switch (elf.shdrPtr(shndx)) {
7158 inline else => |shdr| shndx.vaddr(elf) + elf.targetLoad(&shdr.size),
7159 };
7160 const end_sym_name = elf.stringExisting(.strtab, "__" ++ name ++ "_end");
7161 Symbol.Id.global(end_sym_name).flushMoved(elf, end_vaddr);
7162}
7163
7164pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) link.Error!void {
7165 const sub_prog_node = prog_node.start("ELF Prelink", 0);
7166 defer sub_prog_node.end();
7167
7168 const diags = &elf.base.comp.link_diags;
7169 elf.prelinkInner() catch |err| switch (err) {
7170 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
7171 else => |e| return e,
7172 };
7173}
7174fn prelinkInner(elf: *Elf) Error!void {
7175 const comp = elf.base.comp;
7176 const gpa = comp.gpa;
7177 if (comp.zcu) |_| self_hosted_codegen: {
7178 if (comp.config.use_llvm) break :self_hosted_codegen;
7179
7180 // We're using self-hosted codegen---add an input representing the Zig "object".
7181 try elf.ensureUnusedSymbolCapacity(1, .all_local);
7182 try elf.inputs.ensureUnusedCapacity(gpa, 1);
7183 const zcu_name = try std.fmt.allocPrint(gpa, "{s}_zcu", .{comp.root_name});
7184 defer gpa.free(zcu_name);
7185 const zcu_file_symbol = elf.addLocalSymbolAssumeCapacity(.{
7186 .node = .none,
7187 .name = try elf.string(.strtab, zcu_name),
7188 .value = 0,
7189 .size = 0,
7190 .type = .FILE,
7191 .shndx = .ABS,
7192 });
7193 elf.inputs.addOneAssumeCapacity().* = .{
7194 .path = elf.base.emit,
7195 .member = null,
7196 .extra = .{ .file_symbol = zcu_file_symbol },
7197 };
7198 elf.input_pending_index += 1;
7199
7200 try elf.nodes.ensureUnusedCapacity(gpa, 5 + 4);
7201
7202 switch (elf.shndx.debug_abbrev) {
7203 .UNDEF => {},
7204 else => |debug_abbrev_shndx| elf.dwarf.debug_abbrev.ni = .wrap(elf.addNodeAssumeCapacity(
7205 try debug_abbrev_shndx.get(elf).ni.addFloatingChild(gpa, &elf.mf, .{}),
7206 .{ .debug_shared = .debug_abbrev },
7207 )),
7208 }
7209 switch (elf.shndx.debug_line_str) {
7210 .UNDEF => {},
7211 else => |debug_line_str_shndx| elf.dwarf.debug_line_str.ni =
7212 .wrap(elf.addNodeAssumeCapacity(
7213 try debug_line_str_shndx.get(elf).ni.addFloatingChild(gpa, &elf.mf, .{}),
7214 .{ .debug_shared = .debug_line_str },
7215 )),
7216 }
7217 switch (elf.shndx.debug_str) {
7218 .UNDEF => {},
7219 else => |debug_str_shndx| elf.dwarf.debug_str.ni = .wrap(elf.addNodeAssumeCapacity(
7220 try debug_str_shndx.get(elf).ni.addFloatingChild(gpa, &elf.mf, .{}),
7221 .{ .debug_shared = .debug_str },
7222 )),
7223 }
7224 switch (elf.shndx.debug_str_offsets) {
7225 .UNDEF => {},
7226 else => |debug_str_offsets_shndx| elf.dwarf.debug_str_offsets.ni =
7227 .wrap(elf.addNodeAssumeCapacity(
7228 try debug_str_offsets_shndx.get(elf).ni.addFloatingChild(gpa, &elf.mf, .{}),
7229 .{ .debug_shared = .debug_str_offsets },
7230 )),
7231 }
7232
7233 for ([5]Section.Index{
7234 elf.shndx.eh_frame,
7235 elf.shndx.debug_frame,
7236 elf.shndx.debug_info,
7237 elf.shndx.debug_line,
7238 elf.shndx.debug_rnglists,
7239 }) |debug_shndx| {
7240 if (debug_shndx == .UNDEF) continue;
7241 const debug_ni = debug_shndx.get(elf).ni;
7242 const frame_format = debug_shndx.debugFrameFormat(elf);
7243 const unit_padding_ni = elf.addNodeAssumeCapacity(
7244 try debug_ni.addHeaderChildAfter(gpa, &elf.mf, last_header_oni: {
7245 var last_header_oni = debug_ni.last(&elf.mf);
7246 while (last_header_oni.unwrap()) |last_header_ni|
7247 switch (last_header_ni.position(&elf.mf)) {
7248 .header => break,
7249 .footer => last_header_oni = last_header_ni.prev(&elf.mf),
7250 .floating => unreachable,
7251 };
7252 break :last_header_oni last_header_oni;
7253 }, .{
7254 .alignment = if (frame_format) |_| switch (elf.identClass()) {
7255 .NONE, _ => unreachable,
7256 .@"32" => .@"4",
7257 .@"64" => .@"8",
7258 } else .@"1",
7259 .next_moved = true,
7260 .enable_next_moved = true,
7261 }),
7262 .unit_padding,
7263 );
7264 var debug_nw: MappedFile.Node.Writer = undefined;
7265 unit_padding_ni.writer(gpa, &elf.mf, &debug_nw);
7266 defer debug_nw.deinit();
7267 (if (frame_format) |format|
7268 elf.dwarf.genDebugFrameCie(&debug_nw.interface, null, format)
7269 else
7270 elf.dwarf.genUnitPadding(&debug_nw.interface)) catch |err| switch (err) {
7271 error.WriteFailed => return debug_nw.err.?,
7272 };
7273 }
7274 }
7275}
7276
7277pub fn zcuFilesReady(elf: *Elf, zcu: *Zcu) link.Error!void {
7278 elf.zcuFilesReadyInner(zcu) catch |err| switch (err) {
7279 else => |e| return e,
7280 error.MappedFileIo => return elf.base.comp.link_diags.fail(
7281 "failed to write output file: {t}",
7282 .{elf.mf.io_err.?},
7283 ),
7284 };
7285}
7286fn zcuFilesReadyInner(elf: *Elf, zcu: *Zcu) Error!void {
7287 const gpa = zcu.gpa;
7288 const units_len = zcu.module_roots.count();
7289 if (elf.dwarf_units.len == 0) {
7290 @branchHint(.unlikely);
7291 try elf.dwarf.initUnits(gpa, units_len);
7292 elf.dwarf_units = try gpa.alloc(dwarf_relocs.Unit, zcu.module_roots.count());
7293 @memset(elf.dwarf_units, .{
7294 .frame_cie_first_target_reloc = .none,
7295 .debug_info_header_first_target_reloc = .none,
7296 .debug_info_header_first_node_reloc = .none,
7297 .debug_line_header_first_target_reloc = .none,
7298 .debug_line_header_first_node_reloc = .none,
7299 .debug_rnglists_first_target_reloc = .none,
7300 .debug_rnglists_symbol_relocs = .empty,
7301 });
7302 }
7303 if (!try elf.dwarf.updateUnits(zcu)) return;
7304 try elf.nodes.ensureUnusedCapacity(gpa, 5 * units_len);
7305 for (0..units_len) |unit_index| {
7306 const ui: Dwarf.Unit.Index = @fromBackingInt(@intCast(unit_index));
7307 const unit = ui.get(&elf.dwarf);
7308 if (!unit.alive) continue;
7309 switch (elf.shndx.debug_info) {
7310 .UNDEF => {},
7311 else => |debug_info_shndx| {
7312 const debug_info_ni = unit.debug_info_ni.unwrap() orelse debug_info_ni: {
7313 const debug_info_ni = elf.addNodeAssumeCapacity(
7314 try debug_info_shndx.get(elf).ni.addFloatingChild(gpa, &elf.mf, .{
7315 .alignment = elf.mf.flags.block_size,
7316 .enable_next_moved = true,
7317 }),
7318 .{ .unit_debug_info = ui },
7319 );
7320 unit.debug_info_ni = .wrap(debug_info_ni);
7321 break :debug_info_ni debug_info_ni;
7322 };
7323 if (unit.debug_info_header_ni == .none) unit.debug_info_header_ni = .wrap(
7324 elf.addNodeAssumeCapacity(try debug_info_ni.addOnlyHeaderChild(gpa, &elf.mf, .{
7325 .next_moved = true,
7326 .enable_next_moved = true,
7327 }), .{ .unit_debug_info_header = ui }),
7328 );
7329 if (unit.debug_info_footer_ni == .none) unit.debug_info_footer_ni = .wrap(
7330 elf.addNodeAssumeCapacity(try debug_info_ni.addOnlyFooterChild(gpa, &elf.mf, .{
7331 .size = comptime Dwarf.uleb128Size(@backingInt(Dwarf.AbbrevCode.null)) * 2,
7332 }), .{ .unit_debug_info_footer = ui }),
7333 );
7334 },
7335 }
7336 switch (elf.shndx.debug_line) {
7337 .UNDEF => {},
7338 else => |debug_line_shndx| {
7339 const debug_line_ni = unit.debug_line_ni.unwrap() orelse debug_line_ni: {
7340 const debug_line_ni = elf.addNodeAssumeCapacity(
7341 try debug_line_shndx.get(elf).ni.addFloatingChild(gpa, &elf.mf, .{
7342 .alignment = elf.mf.flags.block_size,
7343 .enable_next_moved = true,
7344 }),
7345 .{ .unit_debug_line = ui },
7346 );
7347 unit.debug_line_ni = .wrap(debug_line_ni);
7348 break :debug_line_ni debug_line_ni;
7349 };
7350 if (unit.debug_line_header_ni == .none) unit.debug_line_header_ni = .wrap(
7351 elf.addNodeAssumeCapacity(try debug_line_ni.addOnlyHeaderChild(gpa, &elf.mf, .{
7352 // Idle tasks are going to try to keep this up to date before we are able to
7353 // write out the full header, so just reserve space for them to do so.
7354 .size = elf.dwarf.unitLengthSize(),
7355 .enable_next_moved = true,
7356 }), .{ .unit_debug_line_header = ui }),
7357 );
7358 },
7359 }
7360 switch (elf.shndx.debug_rnglists) {
7361 .UNDEF => {},
7362 else => |debug_rnglists_shndx| {
7363 const debug_rnglists_ni = unit.debug_rnglists_ni.unwrap() orelse debug_rnglists_ni: {
7364 const debug_rnglists_ni = elf.addNodeAssumeCapacity(
7365 try debug_rnglists_shndx.get(elf).ni.addFloatingChild(gpa, &elf.mf, .{
7366 .next_moved = true,
7367 .enable_next_moved = true,
7368 }),
7369 .{ .unit_debug_rnglists = ui },
7370 );
7371 unit.debug_rnglists_ni = .wrap(debug_rnglists_ni);
7372 break :debug_rnglists_ni debug_rnglists_ni;
7373 };
7374
7375 var drh_nw: MappedFile.Node.Writer = undefined;
7376 debug_rnglists_ni.writer(gpa, &elf.mf, &drh_nw);
7377 defer drh_nw.deinit();
7378 elf.dwarf.genDebugRnglistsHeader(unit, &drh_nw) catch |err| switch (err) {
7379 else => |e| return e,
7380 error.WriteFailed => return drh_nw.err.?,
7381 };
7382 },
7383 }
7384 }
7385 for (0..units_len) |unit_index| {
7386 const ui: Dwarf.Unit.Index = @fromBackingInt(@intCast(unit_index));
7387 const unit = ui.get(&elf.dwarf);
7388 if (unit.debug_info_header_ni == .none) continue;
7389 var dih_nw: MappedFile.Node.Writer = undefined;
7390 const debug_info_header_ni = unit.debug_info_header_ni.unwrap().?;
7391 debug_info_header_ni.writer(gpa, &elf.mf, &dih_nw);
7392 defer dih_nw.deinit();
7393 elf.resetNodeRelocs(debug_info_header_ni);
7394 elf.dwarf.genDebugInfoHeader(zcu, ui.mod(&elf.dwarf), unit, &dih_nw) catch |err| switch (err) {
7395 else => |e| return e,
7396 error.WriteFailed => return dih_nw.err.?,
7397 };
7398 }
7399}
7400
7401fn flushFiles(elf: *Elf) Error!void {
7402 const gpa = elf.base.comp.gpa;
7403 if (elf.shndx.debug_line != .UNDEF) for (elf.dwarf.units) |*unit| {
7404 if (!unit.cleanDebugLineHeaderChanged()) continue;
7405 assert(unit.alive);
7406 const debug_line_header_ni = unit.debug_line_header_ni.unwrap().?;
7407 try debug_line_header_ni.parent(&elf.mf).unwrap().?.nextMoved(gpa, &elf.mf);
7408 try debug_line_header_ni.moved(gpa, &elf.mf);
7409 try debug_line_header_ni.nextMoved(gpa, &elf.mf);
7410 var dlh_nw: MappedFile.Node.Writer = undefined;
7411 debug_line_header_ni.writer(gpa, &elf.mf, &dlh_nw);
7412 defer dlh_nw.deinit();
7413 elf.resetNodeRelocs(debug_line_header_ni);
7414 elf.dwarf.genDebugLineHeader(unit, &dlh_nw, elf.base.comp.zcu.?) catch |err| switch (err) {
7415 else => |e| return e,
7416 error.WriteFailed => return dlh_nw.err.?,
7417 };
7418 };
7419}
7420
7421fn prepareDynamic(elf: *Elf) Error!void {
7422 const comp = elf.base.comp;
7423
7424 if (elf.shndx.dynamic == .UNDEF) return;
7425
7426 // Static PIEs don't need a PLT, so we shouldn't emit the associated dynamic entries.
7427 const use_plt = !(comp.config.output_mode == .Exe and
7428 comp.config.link_mode == .static and
7429 comp.config.pie);
7430
7431 const dynamic_len: u64 = elf.needed.count() + @intFromBool(elf.dynamic.soname != .empty) +
7432 @intFromBool(elf.dynamic.rpath != .empty) +
7433 @intFromBool(elf.dynamic.flags != 0) + @intFromBool(elf.dynamic.flags_1 != 0) +
7434 @as(usize, @intFromBool(elf.shndx.init_array != .UNDEF)) * 2 +
7435 @as(usize, @intFromBool(elf.shndx.fini_array != .UNDEF)) * 2 +
7436 @as(usize, @intFromBool(elf.shndx.preinit_array != .UNDEF)) * 2 +
7437 @as(usize, @intFromBool(use_plt)) * 4 +
7438 @intFromBool(comp.config.output_mode == .Exe) +
7439 @intFromBool(elf.textrel_count > 0) + 9;
7440
7441 const dynamic_size = dynamic_len * 2 * elf.targetPtrSize();
7442
7443 try elf.shndx.dynamic.get(elf).ni.resizeLeaf(comp.gpa, &elf.mf, dynamic_size);
7444 switch (elf.shdrPtr(elf.shndx.dynamic)) {
7445 inline else => |shdr| elf.targetStore(&shdr.size, @intCast(dynamic_size)),
7446 }
7447}
7448
7449fn flushDynamic(elf: *Elf) void {
7450 const comp = elf.base.comp;
7451
7452 if (elf.shndx.dynamic == .UNDEF) return;
7453
7454 switch (elf.identClass()) {
7455 .NONE, _ => unreachable,
7456 inline else => |class| {
7457 const ElfN = class.ElfN();
7458
7459 // Static PIEs don't need a PLT, so we shouldn't emit the associated dynamic entries.
7460 const use_plt = !(comp.config.output_mode == .Exe and
7461 comp.config.link_mode == .static and
7462 comp.config.pie);
7463
7464 const dynamic_size = elf.targetLoad(&@field(elf.shdrPtr(elf.shndx.dynamic), @tagName(class)).size);
7465 const dynamic_slice = elf.shndx.dynamic.get(elf).ni.slice(&elf.mf)[0..@intCast(dynamic_size)];
7466 const dynamic_entries: [][2]ElfN.Addr = @ptrCast(@alignCast(dynamic_slice));
7467
7468 var dynamic_index: usize = 0;
7469
7470 for (
7471 dynamic_entries[dynamic_index..][0..elf.needed.count()],
7472 elf.needed.keys(),
7473 ) |*dynamic_entry, needed| {
7474 dynamic_entry.* = .{ std.elf.DT_NEEDED, @backingInt(needed) };
7475 }
7476 dynamic_index += elf.needed.count();
7477
7478 if (elf.dynamic.soname != .empty) {
7479 dynamic_entries[dynamic_index] = .{ std.elf.DT_SONAME, @backingInt(elf.dynamic.soname) };
7480 dynamic_index += 1;
7481 }
7482 if (elf.dynamic.rpath != .empty) {
7483 dynamic_entries[dynamic_index] = .{ std.elf.DT_RUNPATH, @backingInt(elf.dynamic.rpath) };
7484 dynamic_index += 1;
7485 }
7486 if (elf.dynamic.flags != 0) {
7487 dynamic_entries[dynamic_index] = .{ std.elf.DT_FLAGS, elf.dynamic.flags };
7488 dynamic_index += 1;
7489 }
7490 if (elf.dynamic.flags_1 != 0) {
7491 dynamic_entries[dynamic_index] = .{ std.elf.DT_FLAGS_1, elf.dynamic.flags_1 };
7492 dynamic_index += 1;
7493 }
7494 if (comp.config.output_mode == .Exe) {
7495 dynamic_entries[dynamic_index] = .{ std.elf.DT_DEBUG, 0 };
7496 dynamic_index += 1;
7497 }
7498 if (elf.textrel_count > 0) {
7499 dynamic_entries[dynamic_index] = .{ std.elf.DT_TEXTREL, 0 };
7500 dynamic_index += 1;
7501 }
7502 if (elf.shndx.init_array != .UNDEF) {
7503 dynamic_entries[dynamic_index..][0..2].* = .{
7504 .{ std.elf.DT_INIT_ARRAY, @intCast(elf.shndx.init_array.vaddr(elf)) },
7505 .{ std.elf.DT_INIT_ARRAYSZ, @intCast(elf.shndx.init_array.size(elf)) },
7506 };
7507 dynamic_index += 2;
7508 }
7509 if (elf.shndx.fini_array != .UNDEF) {
7510 dynamic_entries[dynamic_index..][0..2].* = .{
7511 .{ std.elf.DT_FINI_ARRAY, @intCast(elf.shndx.fini_array.vaddr(elf)) },
7512 .{ std.elf.DT_FINI_ARRAYSZ, @intCast(elf.shndx.fini_array.size(elf)) },
7513 };
7514 dynamic_index += 2;
7515 }
7516 if (elf.shndx.preinit_array != .UNDEF) {
7517 dynamic_entries[dynamic_index..][0..2].* = .{
7518 .{ std.elf.DT_PREINIT_ARRAY, @intCast(elf.shndx.preinit_array.vaddr(elf)) },
7519 .{ std.elf.DT_PREINIT_ARRAYSZ, @intCast(elf.shndx.preinit_array.size(elf)) },
7520 };
7521 dynamic_index += 2;
7522 }
7523 if (use_plt) {
7524 // The `DT_PLTGOT` entry usually points to `.got.plt`, but on targets where that
7525 // section does not exist it instead points to `.plt`.
7526 const pltgot_shndx: Section.Index = switch (elf.targetPltInfo().got_plt != null) {
7527 true => elf.shndx.got_plt,
7528 false => elf.shndx.plt,
7529 };
7530 dynamic_entries[dynamic_index..][0..4].* = .{
7531 .{ std.elf.DT_JMPREL, @intCast(elf.shndx.rela_plt.vaddr(elf)) },
7532 .{ std.elf.DT_PLTGOT, @intCast(pltgot_shndx.vaddr(elf)) },
7533 .{ std.elf.DT_PLTRELSZ, @intCast(elf.shndx.rela_plt.size(elf)) },
7534 .{ std.elf.DT_PLTREL, std.elf.DT_RELA },
7535 };
7536 dynamic_index += 4;
7537 }
7538
7539 dynamic_entries[dynamic_index..][0..9].* = .{
7540 .{ std.elf.DT_RELA, @intCast(elf.shndx.rela_dyn.vaddr(elf)) },
7541 .{ std.elf.DT_RELASZ, @intCast(elf.shndx.rela_dyn.size(elf)) },
7542 .{ std.elf.DT_RELAENT, @sizeOf(ElfN.Rela) },
7543 .{ std.elf.DT_SYMTAB, @intCast(elf.shndx.dynsym.vaddr(elf)) },
7544 .{ std.elf.DT_SYMENT, @sizeOf(ElfN.Sym) },
7545 .{ std.elf.DT_STRTAB, @intCast(elf.shndx.dynstr.vaddr(elf)) },
7546 .{ std.elf.DT_STRSZ, @intCast(elf.shndx.dynstr.size(elf)) },
7547 .{ std.elf.DT_HASH, @intCast(elf.shndx.hash.vaddr(elf)) },
7548 .{ std.elf.DT_NULL, 0 },
7549 };
7550 dynamic_index += 9;
7551
7552 assert(dynamic_index == dynamic_entries.len);
7553 if (elf.targetEndian() != std.lang.Endian.native) for (dynamic_entries) |*dynamic_entry|
7554 std.mem.byteSwapAllFields(@TypeOf(dynamic_entry.*), dynamic_entry);
7555 },
7556 }
7557}
7558
7559fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
7560 name: []const u8 = "",
7561 type: std.elf.SHT = .NULL,
7562 flags: std.elf.SHF = .{},
7563 size: std.elf.Xword = 0,
7564 link: std.elf.Word = 0,
7565 info: std.elf.Word = 0,
7566 addralign: Alignment = .@"1",
7567 entsize: std.elf.Word = 0,
7568 node_align: Alignment = .@"1",
7569 manual_size: bool = false,
7570}) Error!Section.Index {
7571 switch (opts.type) {
7572 .NULL => assert(opts.size == 0),
7573 .PROGBITS => assert(opts.size > 0),
7574 else => {},
7575 }
7576 if (opts.flags.ALLOC and elf.ehdrType() != .REL) {
7577 const phndx = elf.getNode(segment_ni).segment;
7578 try elf.ensureSegmentAligned(phndx, opts.addralign);
7579 }
7580 const gpa = elf.base.comp.gpa;
7581 try elf.nodes.ensureUnusedCapacity(gpa, 1);
7582 try elf.shdrs.ensureUnusedCapacity(gpa, 1);
7583 const want_symbol = opts.flags.ALLOC or switch (opts.type) {
7584 .NULL, .PROGBITS, .NOBITS, .X86_64_UNWIND => elf.ehdrType() == .REL,
7585 else => false,
7586 };
7587 if (want_symbol) try elf.ensureUnusedSymbolCapacity(1, .all_local);
7588
7589 const shstrtab_entry = try elf.string(.shstrtab, opts.name);
7590 const shndx: Section.Index, const new_shdr_size = shndx: switch (elf.ehdrPtr()) {
7591 inline else => |ehdr, class| {
7592 const shndx, const shnum = alloc_shndx: switch (elf.targetLoad(&ehdr.shnum)) {
7593 1...std.elf.SHN_LORESERVE - 2 => |shndx| {
7594 const shnum = shndx + 1;
7595 elf.targetStore(&ehdr.shnum, shnum);
7596 break :alloc_shndx .{ shndx, shnum };
7597 },
7598 std.elf.SHN_LORESERVE - 1 => |shndx| {
7599 const shnum = shndx + 1;
7600 elf.targetStore(&ehdr.shnum, 0);
7601 elf.targetStore(&@field(elf.shdrPtr(.UNDEF), @tagName(class)).size, shnum);
7602 break :alloc_shndx .{ shndx, shnum };
7603 },
7604 std.elf.SHN_LORESERVE...std.elf.SHN_HIRESERVE => unreachable,
7605 0 => {
7606 const shnum_ptr = &@field(elf.shdrPtr(.UNDEF), @tagName(class)).size;
7607 const shndx: u32 = @intCast(elf.targetLoad(shnum_ptr));
7608 const shnum = shndx + 1;
7609 elf.targetStore(shnum_ptr, shnum);
7610 break :alloc_shndx .{ shndx, shnum };
7611 },
7612 };
7613 assert(shndx < @backingInt(Section.Index.LORESERVE));
7614 break :shndx .{ @fromBackingInt(shndx), @as(u64, elf.targetLoad(&ehdr.shentsize)) * @as(u64, shnum) };
7615 },
7616 };
7617 try elf.ni.shdr.ensureMinimumSize(gpa, &elf.mf, new_shdr_size);
7618 const parent_ni = switch (elf.ehdrType()) {
7619 .REL => elf.ni.elf,
7620 .EXEC, .DYN => segment_ni,
7621 };
7622 assert(opts.addralign.check(opts.size));
7623 const ni = elf.addNodeAssumeCapacity(try parent_ni.addFloatingChild(gpa, &elf.mf, .{
7624 .size = opts.node_align.forward(opts.size),
7625 .alignment = opts.addralign.max(opts.node_align),
7626 .resized = opts.size > 0,
7627 .bubbles_moved = opts.flags.ALLOC,
7628 }), switch (opts.manual_size) {
7629 false => .{ .section = shndx },
7630 true => .{ .section_manual_size = shndx },
7631 });
7632 const addr = elf.computeNodeVAddr(ni);
7633 elf.shdrs.appendAssumeCapacity(.{
7634 .lsi = if (want_symbol) elf.addLocalSymbolAssumeCapacity(.{
7635 .node = ni.toOptional(),
7636 .name = .empty,
7637 .value = addr,
7638 .size = 0,
7639 .type = .SECTION,
7640 .shndx = shndx,
7641 }) else .null,
7642 .ni = ni,
7643 .rela = switch (opts.type) {
7644 .REL => unreachable,
7645 .RELA => .{ .free_head = .none },
7646 else => .{ .shndx = .UNDEF },
7647 },
7648 });
7649 switch (elf.shdrPtr(shndx)) {
7650 inline else => |shdr, class| {
7651 shdr.* = .{
7652 .name = @backingInt(shstrtab_entry),
7653 .type = opts.type,
7654 .flags = .{ .shf = opts.flags },
7655 .addr = @intCast(addr),
7656 .offset = @intCast(elf.computeNodeElfOffset(ni)),
7657 .size = @intCast(opts.size),
7658 .link = opts.link,
7659 .info = opts.info,
7660 .addralign = @intCast(opts.addralign.toByteUnits()),
7661 .entsize = opts.entsize,
7662 };
7663 if (elf.targetEndian() != std.lang.Endian.native) std.mem.byteSwapAllFields(class.ElfN().Shdr, shdr);
7664 },
7665 }
7666 return shndx;
7667}
7668
7669fn ensureUnusedRelocCapacity(elf: *Elf, node: MappedFile.Node.Index, len: usize) Error!void {
7670 if (len == 0) return;
7671 const gpa = elf.base.comp.gpa;
7672 try elf.symbol_relocs.ensureUnusedCapacity(gpa, len);
7673 try elf.node_relocs.ensureUnusedCapacity(gpa, len);
7674 try elf.got_relocs.ensureUnusedCapacity(gpa, len);
7675 const class = elf.identClass();
7676 switch (elf.ehdrType()) {
7677 .REL => {
7678 const shndx = elf.getNodeShndx(node);
7679 if (shndx.get(elf).rela.shndx == .UNDEF) {
7680 var bfa_buf: [32]u8 = undefined;
7681 var bfa: std.heap.BufferFirstAllocator = .init(&bfa_buf, gpa);
7682 const allocator = bfa.allocator();
7683
7684 const rela_name = try std.fmt.allocPrint(allocator, ".rela{s}", .{shndx.name(elf).slice(elf)});
7685 defer allocator.free(rela_name);
7686
7687 assert(elf.section_by_name.count() == elf.shdrs.items.len);
7688 try elf.section_by_name.ensureUnusedCapacity(gpa, 1);
7689 const rela_shndx = try elf.addSection(elf.ni.elf, .{
7690 .name = rela_name,
7691 .type = .RELA,
7692 .link = @backingInt(Section.Index.symtab),
7693 .info = shndx.toSection().?,
7694 .addralign = switch (class) {
7695 .NONE, _ => unreachable,
7696 .@"32" => .@"4",
7697 .@"64" => .@"8",
7698 },
7699 .entsize = switch (class) {
7700 .NONE, _ => unreachable,
7701 inline else => |ct_class| @sizeOf(ct_class.ElfN().Rela),
7702 },
7703 .node_align = elf.mf.flags.block_size,
7704 .manual_size = true,
7705 });
7706 elf.section_by_name.putAssumeCapacityNoClobber(rela_shndx.name(elf), {});
7707 shndx.get(elf).rela.shndx = rela_shndx;
7708 }
7709 try shndx.get(elf).rela.shndx.relaEnsureAdditionalCapacity(elf, len);
7710 },
7711 .EXEC, .DYN => {
7712 try elf.tls_size_symbol_relocs.ensureUnusedCapacity(gpa, len);
7713 const new_got_entries = len * 2; // at worst, every reloc is a new TLSGD
7714 try elf.got.ensureUnusedCapacity(gpa, new_got_entries);
7715 const need_got_size = switch (class) {
7716 .NONE, _ => unreachable,
7717 inline else => |ct_class| (elf.got.count() + new_got_entries) * @sizeOf(ct_class.ElfN().Addr),
7718 };
7719 try elf.shndx.got.get(elf).ni.ensureMinimumSize(gpa, &elf.mf, need_got_size);
7720
7721 if (elf.shndx.dynamic != .UNDEF) {
7722 try elf.shndx.rela_dyn.relaEnsureAdditionalCapacity(elf, new_got_entries);
7723 }
7724 },
7725 }
7726}
7727/// Although this function requires a preceding call to `ensureUnusedRelocCapacity`, it is still
7728/// fallible, because there are some rare cases for which we cannot reserve capacity upfront.
7729fn addRelocAssumeCapacity(
7730 elf: *Elf,
7731 node: MappedFile.Node.Index,
7732 offset: u64,
7733 target: Symbol.Id,
7734 addend: i64,
7735 @"type": MachineRelocType,
7736) (Error || error{ UnknownRelocation, NonStaticRelocation, UnimplementedRelocation })!void {
7737 switch (elf.ehdrType()) {
7738 .REL => {
7739 const rela_shndx = elf.getNodeShndx(node).get(elf).rela.shndx;
7740 const rela_index = rela_shndx.relaAddOneAssumeCapacity(elf, .{
7741 .type = @"type",
7742 // This field needs to equal the offset into the section, which is *not* necessarily
7743 // the same thing as our `offset`, which is the offset into `node`. We could compute
7744 // the section offset now, but there's no point, because `flushMovedNodeRelocs` will
7745 // eventually do it for us anyway, so just init to 0.
7746 .offset = 0,
7747 .raw_sym_index = @backingInt(target.index(elf)),
7748 .addend = addend,
7749 });
7750 const ri: SymbolReloc.Index = @fromBackingInt(@intCast(elf.symbol_relocs.items.len));
7751 const first_target_reloc = &target.index(elf).ptr(elf).first_target_reloc;
7752 const next = first_target_reloc.*;
7753 first_target_reloc.* = ri;
7754 if (next != .none) next.get(elf).prev = ri;
7755 elf.symbol_relocs.appendAssumeCapacity(.{
7756 .node = node.toOptional(),
7757 .offset = offset,
7758 .type = undefined,
7759 .target = target,
7760 .addend = addend,
7761 .next = next,
7762 .prev = .none,
7763 .rela_index = rela_index.toOptional(),
7764 .result = .ok,
7765 });
7766 },
7767 .DYN, .EXEC => switch (elf.ehdrMachine()) {
7768 .AARCH64 => switch (@"type".AARCH64) {
7769 .NONE => {},
7770 _ => return error.UnknownRelocation,
7771 else => return error.UnimplementedRelocation,
7772 },
7773 .LOONGARCH => rel_type: switch (@"type".LARCH) {
7774 .NONE => {},
7775 _ => return error.UnknownRelocation,
7776
7777 .COPY,
7778 .JUMP_SLOT,
7779 .RELATIVE,
7780 .IRELATIVE,
7781 => return error.NonStaticRelocation,
7782
7783 else => return error.UnimplementedRelocation,
7784
7785 // These relocations signal that certain relaxations are legal, but this linker does
7786 // not yet implement relaxation, so these are ignored.
7787 .RELAX, .TLS_LE_ADD_R => {},
7788
7789 // Relaxable versions of other relocations. Since we don't yet implement relaxation,
7790 // just use the handling for the non-relaxable versions.
7791 .TLS_LE_LO12_R => continue :rel_type .TLS_LE_LO12,
7792 .TLS_LE_HI20_R => continue :rel_type .TLS_LE_HI20,
7793
7794 // zig fmt: off
7795 .@"32" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })),
7796 .@"64" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })),
7797 .@"32_PCREL" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
7798 .@"64_PCREL" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" })),
7799 .ABS_LO12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .trunc, .shift = .@"0" })),
7800 .ABS_HI20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"12" })),
7801 .ABS64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"32" })),
7802 .ABS64_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .unsigned, .shift = .@"52" })),
7803 .PCALA_LO12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .trunc, .shift = .@"0" })),
7804 .PCALA_HI20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_pcala_hi20)),
7805 .PCALA64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_pcala64_lo20)),
7806 .PCALA64_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_pcala64_hi12)),
7807
7808 .B16 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.pltrel, .{ .dest = .@"32[25:10]", .cast = .signed, .shift = .@"2_exact" })),
7809 .B21 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_b21)),
7810 .B26 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_b26)),
7811 .CALL36 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.larch_call36)),
7812
7813 .TLS_LE_LO12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[21:10]", .cast = .trunc, .shift = .@"0" })),
7814 .TLS_LE_HI20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"12" })),
7815 .TLS_LE64_LO20 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"32" })),
7816 .TLS_LE64_HI12 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[21:10]", .cast = .unsigned, .shift = .@"52" })),
7817
7818 .GOT_PC_LO12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .trunc, .shift = .@"0" })),
7819 .GOT_PC_HI20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .special(.larch_pcala_hi20)),
7820 .GOT64_PC_LO20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .special(.larch_pcala64_lo20)),
7821 .GOT64_PC_HI12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .special(.larch_pcala64_hi12)),
7822 .GOT_LO12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .trunc, .shift = .@"0" })),
7823 .GOT_HI20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.abs, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"12" })),
7824 .GOT64_LO20 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.abs, .{ .dest = .@"32[24:5]", .cast = .trunc, .shift = .@"32" })),
7825 .GOT64_HI12 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.abs, .{ .dest = .@"32[21:10]", .cast = .unsigned, .shift = .@"52" })),
7826 // zig fmt: on
7827 },
7828 .PPC64 => switch (@"type".PPC64) {
7829 .NONE => {},
7830 _ => return error.UnknownRelocation,
7831 else => return error.UnimplementedRelocation,
7832 },
7833 .RISCV => switch (@"type".RISCV) {
7834 .NONE => {},
7835 _ => return error.UnknownRelocation,
7836 else => return error.UnimplementedRelocation,
7837 },
7838 .SPARCV9 => switch (@"type".SPARC) {
7839 .NONE => {},
7840 _ => return error.UnknownRelocation,
7841
7842 .COPY,
7843 .GLOB_DAT,
7844 .JMP_SLOT,
7845 .RELATIVE,
7846 .IRELATIVE,
7847 => return error.NonStaticRelocation,
7848
7849 .WDISP22,
7850 .HI22,
7851 .LO10,
7852 .HIPLT22,
7853 .LOPLT10,
7854 .PCPLT22,
7855 .PCPLT10,
7856 .OLO10,
7857 .HH22,
7858 .HM10,
7859 .LM22,
7860 .PC_HH22,
7861 .PC_HM10,
7862 .PC_LM22,
7863 .WDISP16,
7864 .WDISP19,
7865 .HIX22,
7866 .LOX10,
7867 .REGISTER,
7868 .TLS_IE_HI22,
7869 .TLS_IE_LO10,
7870 .TLS_DTPMOD32,
7871 .TLS_DTPMOD64,
7872 .H34,
7873 .WDISP10,
7874 => return error.UnimplementedRelocation,
7875
7876 // These need similar handling to `R_X86_64_GOTOFF64`. No compiler seems to emit them though.
7877 .GOTDATA_HIX22 => return error.UnimplementedRelocation,
7878 .GOTDATA_LOX10 => return error.UnimplementedRelocation,
7879
7880 // These relocations signal that certain relaxations are legal, but this linker does
7881 // not yet implement relaxation, so these are ignored.
7882 .GOTDATA_OP,
7883 .TLS_GD_ADD,
7884 .TLS_LDM_ADD,
7885 .TLS_LDO_ADD,
7886 .TLS_IE_LD,
7887 .TLS_IE_LDX,
7888 .TLS_IE_ADD,
7889 => {},
7890
7891 // zig fmt: off
7892 .@"8" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"8", .cast = .unsigned, .shift = .@"0" })),
7893 .@"16", .UA16 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"16", .cast = .unsigned, .shift = .@"0" })),
7894 .@"32", .UA32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })),
7895 .@"64", .UA64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })),
7896
7897 .@"5" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[4:0]", .cast = .unsigned, .shift = .@"0" })),
7898 .@"6" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[5:0]", .cast = .unsigned, .shift = .@"0" })),
7899 .@"7" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[6:0]", .cast = .unsigned, .shift = .@"0" })),
7900 .@"10" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[9:0]", .cast = .unsigned, .shift = .@"0" })),
7901 .@"11" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[10:0]", .cast = .unsigned, .shift = .@"0" })),
7902 .@"13" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[12:0]", .cast = .unsigned, .shift = .@"0" })),
7903 .@"22" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[21:0]", .cast = .unsigned, .shift = .@"0" })),
7904
7905 .DISP8 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"8", .cast = .signed, .shift = .@"0" })),
7906 .DISP16 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"16", .cast = .signed, .shift = .@"0" })),
7907 .DISP32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
7908 .DISP64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" })),
7909
7910 .SIZE32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.size, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })),
7911 .SIZE64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.size, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })),
7912
7913 .PCPLT32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.pltrel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
7914 .PLT32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.pltabs, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })),
7915 .PLT64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.pltabs, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })),
7916
7917 .WDISP30 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32[29:0]", .cast = .signed, .shift = .@"2_exact" })),
7918 .WPLT30 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.pltrel, .{ .dest = .@"32[29:0]", .cast = .signed, .shift = .@"2_exact" })),
7919 .PC22 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32[21:0]", .cast = .unsigned, .shift = .@"10" })),
7920 .H44 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[21:0]", .cast = .unsigned, .shift = .@"22" })),
7921 .M44 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"12" })),
7922
7923 .TLS_LDO_HIX22 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"32[21:0]", .cast = .trunc, .shift = .@"10" })),
7924 .TLS_LE_HIX22 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .special(.sparc_le_hix22)),
7925 .TLS_DTPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })),
7926 .TLS_DTPOFF64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })),
7927 .TLS_TPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
7928 .TLS_TPOFF64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" })),
7929
7930 .GOT13 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.offset, .{ .dest = .@"32[12:0]", .cast = .unsigned, .shift = .@"0" })),
7931 .GOT22 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.offset, .{ .dest = .@"32[21:0]", .cast = .trunc, .shift = .@"10" })),
7932 .GOTDATA_OP_LOX10 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .special(.sparc_op_lox10)),
7933 .GOTDATA_OP_HIX22 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .special(.sparc_op_hix22)),
7934 .TLS_GD_HI22 => elf.addGotRelocAssumeCapacity(node, offset, .{ .tlsgd0 = target }, addend, .simple(.offset, .{ .dest = .@"32[21:0]", .cast = .trunc, .shift = .@"10" })),
7935 .TLS_LDM_HI22 => elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .simple(.offset, .{ .dest = .@"32[21:0]", .cast = .trunc, .shift = .@"10" })),
7936 // zig fmt: on
7937
7938 .TLS_GD_CALL, .TLS_LDM_CALL => {
7939 const callee_sym = try elf.externSymbolInner(.{
7940 .lib_name = null,
7941 .name = "__tls_get_addr",
7942 .type = .FUNC,
7943 });
7944 try elf.addSymbolRelocAssumeCapacity(node, offset, callee_sym, addend, .simple(.pltrel, .{ .dest = .@"32[29:0]", .cast = .signed, .shift = .@"2_exact" }));
7945 },
7946
7947 // The following relocations are all represented by the ABI as writing to a 13 bit
7948 // field (32[12:0]), but masking out some bits of the value. To simplify our logic
7949 // for applying relocations, we split this action up: we create a relocation writing
7950 // to the 10--12 bit long field which is actually variable, and queue a one-shot
7951 // task to set the constant bits. We can't just write the bits now unfortunately
7952 // because they may be in an input section which has not yet been loaded.
7953 .PC10 => {
7954 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b000" });
7955 try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
7956 },
7957 .L44 => {
7958 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:12] = 0b0" });
7959 try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32[11:0]", .cast = .trunc, .shift = .@"0" }));
7960 },
7961 .TLS_LDO_LOX10 => {
7962 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b000" });
7963 try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
7964 },
7965 .TLS_LE_LOX10 => {
7966 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b111" });
7967 try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
7968 },
7969 .GOT10 => {
7970 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b000" });
7971 elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.offset, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
7972 },
7973 .TLS_GD_LO10 => {
7974 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b000" });
7975 elf.addGotRelocAssumeCapacity(node, offset, .{ .tlsgd0 = target }, addend, .simple(.offset, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
7976 },
7977 .TLS_LDM_LO10 => {
7978 try elf.one_shot_fixups.append(elf.base.comp.gpa, .{ .node = node, .offset = offset, .action = .@"32[12:10] = 0b000" });
7979 elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .simple(.offset, .{ .dest = .@"32[9:0]", .cast = .trunc, .shift = .@"0" }));
7980 },
7981 },
7982 .X86_64 => rel_type: switch (@"type".X86_64) {
7983 .NONE => {},
7984 _ => return error.UnknownRelocation,
7985
7986 .COPY,
7987 .GLOB_DAT,
7988 .JUMP_SLOT,
7989 .RELATIVE64,
7990 .RELATIVE,
7991 .IRELATIVE,
7992 .DTPMOD64,
7993 => return error.NonStaticRelocation,
7994
7995 // TODO: the psABI links to https://www.fsfla.org/~lxoliva/writeups/TLS/RFC-TLSDESC-x86.txt
7996 .GOTPC32_TLSDESC => return error.UnimplementedRelocation,
7997 .TLSDESC_CALL => return error.UnimplementedRelocation,
7998 .TLSDESC => return error.UnimplementedRelocation,
7999
8000 // TODO: these are the address of an arbitrary symbol (or PLT entry) relative to the
8001 // base of the GOT, which is quite annoying. Luckily, they seem to be rare, so I'm
8002 // probably just going to introduce a set (ArrayHashMap) of SymbolReloc.Index which
8003 // need to be re-applied whenever the GOT moves.
8004 .GOTOFF64 => return error.UnimplementedRelocation, // offset of symbol from GOT base
8005 .PLTOFF64 => return error.UnimplementedRelocation, // offset of PLT entry from GOT base (yes, I know, the name is stupid)
8006
8007 // TODO: figure out how to do relaxations. Perhaps we want to remove a `GotReloc`
8008 // and replace it with a `SymbolReloc` when a relaxation becomes possible, but we'd
8009 // need to bear in mind whether incremental updates might make a relaxation
8010 // impossible again or something like that. Relaxations seem kind of hostile to
8011 // incremental compilation, so perhaps we just only support them in non-incremental
8012 // compilations and just apply them in flush or something.
8013
8014 // Relaxable versions of other relocations. Since we don't yet implement relaxation,
8015 // just use the handling for the non-relaxable versions.
8016 .GOTPCRELX, .REX_GOTPCRELX => continue :rel_type .GOTPCREL,
8017
8018 // This relocation was a historical attempt to help linkers optimize uses of symbols
8019 // which have both GOT entries and PLT entries, by encouraging the linker to create
8020 // a `.got.plt` entry instead of a `.got` entry. This makes no sense, because the
8021 // linker already has sufficient knowledge to do that optimization, while compilers
8022 // actually do *not* have sufficient knowledge (since the PLT and GOT relocations
8023 // may not be in the same compilation unit). This relocation has since been removed
8024 // from the psABI, but just in case it appears, we can easily support it by just
8025 // disregarding the PLT stuff and lowering to a normal GOT entry.
8026 //
8027 // More details: https://sourceware.org/pipermail/binutils/2014-November/086548.html
8028 .GOTPLT64 => continue :rel_type .GOT64,
8029
8030 // zig fmt: off
8031 .@"8" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"8", .cast = .unsigned, .shift = .@"0" })),
8032 .@"16" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"16", .cast = .unsigned, .shift = .@"0" })),
8033 .@"32" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })),
8034 .@"32S" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
8035 .@"64" => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.abs, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })),
8036 .PC8 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"8", .cast = .signed, .shift = .@"0" })),
8037 .PC16 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"16", .cast = .signed, .shift = .@"0" })),
8038 .PC32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
8039 .PC64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.rel, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" })),
8040 .PLT32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.pltrel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
8041 .SIZE32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.size, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })),
8042 .SIZE64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.size, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })),
8043 .DTPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })),
8044 .DTPOFF64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.dtpoff, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })),
8045 .TPOFF32 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
8046 .TPOFF64 => try elf.addSymbolRelocAssumeCapacity(node, offset, target, addend, .simple(.tpoff, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" })),
8047
8048 .GOT32 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.offset, .{ .dest = .@"32", .cast = .unsigned, .shift = .@"0" })),
8049 .GOT64 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.offset, .{ .dest = .@"64", .cast = .unsigned, .shift = .@"0" })),
8050 .GOTPCREL => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
8051 .GOTPCREL64 => elf.addGotRelocAssumeCapacity(node, offset, .{ .symbol = target }, addend, .simple(.rel, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" })),
8052 .TLSGD => elf.addGotRelocAssumeCapacity(node, offset, .{ .tlsgd0 = target }, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
8053 .TLSLD => elf.addGotRelocAssumeCapacity(node, offset, .tlsld0, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
8054 .GOTTPOFF => elf.addGotRelocAssumeCapacity(node, offset, .{ .tpoff = target }, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" })),
8055 // zig fmt: on
8056
8057 .GOTPC64 => {
8058 const got_sym: Symbol.Id = .local(elf.shndx.got.get(elf).lsi);
8059 try elf.addSymbolRelocAssumeCapacity(node, offset, got_sym, addend, .simple(.rel, .{ .dest = .@"64", .cast = .signed, .shift = .@"0" }));
8060 },
8061 .GOTPC32 => {
8062 const got_sym: Symbol.Id = .local(elf.shndx.got.get(elf).lsi);
8063 try elf.addSymbolRelocAssumeCapacity(node, offset, got_sym, addend, .simple(.rel, .{ .dest = .@"32", .cast = .signed, .shift = .@"0" }));
8064 },
8065 },
8066 },
8067 }
8068}
8069fn addSymbolRelocAssumeCapacity(
8070 elf: *Elf,
8071 node: MappedFile.Node.Index,
8072 offset: u64,
8073 target: Symbol.Id,
8074 addend: i64,
8075 @"type": SymbolReloc.Type,
8076) Error!void {
8077 assert(elf.ehdrType() != .REL);
8078
8079 const rela_index: Section.RelaIndex.Optional = r: {
8080 if (elf.shndx.dynamic == .UNDEF) break :r .none;
8081
8082 // If we emit a runtime relocation entry, its `offset` is a virtual address, so we need to
8083 // determine the vaddr of `node`.
8084 const node_vaddr = elf.getNodeVAddr(node);
8085
8086 // If this is `true`, we will try to create a copy relocation for the target symbol if it is
8087 // not locally defined. If the relocation value is always computed from the target symbol's
8088 // value (even for an external target symbol), and if the target symbol might be of type
8089 // STT_OBJECT, this should probably be `true`.
8090 const try_copy_reloc: bool = switch (@"type".target) {
8091 .rel, .abs => true,
8092
8093 .pltrel,
8094 .pltabs,
8095 .dtpoff,
8096 .tpoff,
8097 .size,
8098 => false,
8099
8100 .special => switch (@"type".action.special) {
8101 .larch_pcala_hi20,
8102 .larch_pcala64_lo20,
8103 .larch_pcala64_hi12,
8104 => true,
8105
8106 .larch_b21,
8107 .larch_b26,
8108 .larch_call36,
8109 .sparc_le_hix22,
8110 => false,
8111 },
8112 };
8113
8114 classify: switch (elf.classifySymbolValue(target)) {
8115 .static => break :r .none,
8116 .static_relative => {
8117 switch (@"type".target) {
8118 // Only relocations which resolve to absolute addresses require runtime
8119 // `R_*_RELATIVE` relocations.
8120 .special,
8121 .pltrel,
8122 .rel,
8123 .dtpoff,
8124 .tpoff,
8125 .size,
8126 => break :r .none,
8127
8128 .abs, .pltabs => {},
8129 }
8130 if (!@"type".action.simple.dest.isAddr(elf)) break :r .none;
8131 switch (elf.nodeWantsDsoRelocation(node)) {
8132 .no => break :r .none,
8133 .yes => {},
8134 .yes_textrel => elf.textrel_count += 1,
8135 }
8136 break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
8137 .type = .relative(elf),
8138 .offset = node_vaddr + offset,
8139 .raw_sym_index = 0,
8140 .addend = 0,
8141 }).toOptional();
8142 },
8143 .dynamic => if (try_copy_reloc and try elf.maybeAddCopyRelocation(target.unwrap().global)) {
8144 switch (elf.classifySymbolValue(target)) {
8145 .static => continue :classify .static,
8146 .static_relative => continue :classify .static_relative,
8147 .dynamic => unreachable, // we just added a copy relocation
8148 }
8149 } else {
8150 const dynamic_reloc_type: MachineRelocType = switch (@"type".target) {
8151 // PLT relocations targeting dynamic symbols actually target that symbol's PLT
8152 // entry, so we should emit an `R_*_RELATIVE` relocation instead.
8153 .pltabs => continue :classify .static_relative,
8154 // ...although PC-relative PLT relocations don't even need that!
8155 .pltrel => break :r .none,
8156 // Weird sizes or computations are not supported as runtime relocations.
8157 .special => break :r .none,
8158 // Relative addresses are not supported as runtime relocations.
8159 .rel => break :r .none,
8160
8161 // On the few targets supporting size relocations, they are valid at runtime.
8162 .size => switch (@"type".action.simple.dest) {
8163 .@"32" => MachineRelocType.size32(elf) orelse break :r .none,
8164 .@"64" => MachineRelocType.size64(elf) orelse break :r .none,
8165 else => break :r .none,
8166 },
8167 // Absolute addresses and TLS offsets can be lowered at runtime provided they
8168 // are address-sized.
8169 .dtpoff => if (@"type".action.simple.dest.isAddr(elf)) .dtpOff(elf) else break :r .none,
8170 .tpoff => if (@"type".action.simple.dest.isAddr(elf)) .tpOff(elf) else break :r .none,
8171 .abs => if (@"type".action.simple.dest.isAddr(elf)) .absAddr(elf) else break :r .none,
8172 };
8173 switch (elf.nodeWantsDsoRelocation(node)) {
8174 .no => break :r .none,
8175 .yes => {},
8176 .yes_textrel => elf.textrel_count += 1,
8177 }
8178 break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
8179 .type = dynamic_reloc_type,
8180 .offset = node_vaddr + offset,
8181 .raw_sym_index = elf.globalByName(target.unwrap().global).?.dynsym_index,
8182 .addend = addend,
8183 }).toOptional();
8184 },
8185 }
8186 };
8187
8188 const ri: SymbolReloc.Index = @fromBackingInt(@intCast(elf.symbol_relocs.items.len));
8189 const first_target_reloc = &target.index(elf).ptr(elf).first_target_reloc;
8190 const next = first_target_reloc.*;
8191 first_target_reloc.* = ri;
8192 if (next != .none) next.get(elf).prev = ri;
8193 elf.symbol_relocs.appendAssumeCapacity(.{
8194 .node = node.toOptional(),
8195 .offset = offset,
8196 .target = target,
8197 .addend = addend,
8198 .type = @"type",
8199 .next = next,
8200 .prev = .none,
8201 .rela_index = rela_index,
8202 .result = .ok,
8203 });
8204 if (@"type".dependsOnTlsSize(elf)) {
8205 elf.tls_size_symbol_relocs.putAssumeCapacityNoClobber(ri, {});
8206 }
8207
8208 // Actually apply the new relocation!
8209 ri.get(elf).apply(elf);
8210}
8211fn addNodeRelocAssumeCapacity(
8212 elf: *Elf,
8213 node: MappedFile.Node.Index,
8214 offset: u64,
8215 target: MappedFile.Node.Index,
8216 addend: i64,
8217 @"type": NodeReloc.Type,
8218) Error!void {
8219 const shndx = elf.getNodeShndx(target);
8220 assert(!shndx.flags(elf).ALLOC); // not yet needed so not implemented
8221 const first_target_reloc = switch (elf.getNode(target)) {
8222 else => unreachable,
8223 .debug_shared => |ss| &elf.dwarf_shared.getPtr(ss).first_target_reloc,
8224 .unit_frame_cie => |ui| &elf.dwarf_units[@backingInt(ui)].frame_cie_first_target_reloc,
8225 .unit_debug_info_header => |ui| &elf.dwarf_units[@backingInt(ui)].debug_info_header_first_target_reloc,
8226 .unit_debug_line_header => |ui| &elf.dwarf_units[@backingInt(ui)].debug_line_header_first_target_reloc,
8227 .unit_debug_rnglists => |ui| &elf.dwarf_units[@backingInt(ui)].debug_rnglists_first_target_reloc,
8228 .const_debug_info => |cpi| &elf.dwarf_consts.getPtr(cpi).?.debug_info_first_target_reloc,
8229 .global_debug_info => |gi| &elf.dwarf_globals.items[@backingInt(gi)].debug_info_first_target_reloc,
8230 .func_debug_info => |fi| &elf.dwarf_funcs.items[@backingInt(fi)].debug_info_first_target_reloc,
8231 .decl_debug_info => |di| &elf.dwarf_decls.getPtr(di).?.debug_info_first_target_reloc,
8232 };
8233 const next = first_target_reloc.*;
8234 const ri: NodeReloc.Index = @fromBackingInt(@intCast(elf.node_relocs.items.len));
8235 first_target_reloc.* = ri;
8236 if (next != .none) next.get(elf).prev = ri;
8237 switch (elf.ehdrType()) {
8238 .REL => {
8239 const rela_shndx = elf.getNodeShndx(node).get(elf).rela.shndx;
8240 const rela_index = rela_shndx.relaAddOneAssumeCapacity(elf, .{
8241 .type = switch (elf.ehdrMachine()) {
8242 .AARCH64 => .{ .AARCH64 = switch (@"type") {
8243 .abs32 => .ABS32,
8244 .abs64 => .ABS64,
8245 } },
8246 .LOONGARCH => .{ .LARCH = switch (@"type") {
8247 .abs32 => .@"32",
8248 .abs64 => .@"64",
8249 } },
8250 .PPC64 => .{ .PPC64 = switch (@"type") {
8251 .abs32 => .ADDR32,
8252 .abs64 => .ADDR64,
8253 } },
8254 .RISCV => .{ .RISCV = switch (@"type") {
8255 .abs32 => .@"32",
8256 .abs64 => .@"64",
8257 } },
8258 .SPARCV9 => .{ .SPARC = switch (@"type") {
8259 .abs32 => .UA32,
8260 .abs64 => .UA64,
8261 } },
8262 .X86_64 => .{ .X86_64 = switch (@"type") {
8263 .abs32 => .@"32",
8264 .abs64 => .@"64",
8265 } },
8266 },
8267 // This field needs to equal the offset into the section, which is *not* necessarily
8268 // the same thing as our `offset`, which is the offset into `node`. We could compute
8269 // the section offset now, but there's no point, because `flushMovedNodeRelocs` will
8270 // eventually do it for us anyway, so just init to 0.
8271 .offset = 0,
8272 .raw_sym_index = @backingInt(switch (shndx.get(elf).lsi) {
8273 .null => unreachable,
8274 else => |lsi| lsi.index(),
8275 }),
8276 .addend = 0,
8277 });
8278 elf.node_relocs.appendAssumeCapacity(.{
8279 .node = node.toOptional(),
8280 .offset = offset,
8281 .type = undefined,
8282 .target = target,
8283 .addend = addend,
8284 .next = next,
8285 .prev = .none,
8286 .rela_index = rela_index.toOptional(),
8287 .result = .ok,
8288 });
8289 },
8290 .DYN, .EXEC => {
8291 elf.node_relocs.appendAssumeCapacity(.{
8292 .node = node.toOptional(),
8293 .offset = offset,
8294 .target = target,
8295 .addend = addend,
8296 .type = @"type",
8297 .next = next,
8298 .prev = .none,
8299 .rela_index = .none,
8300 .result = .ok,
8301 });
8302
8303 // Actually apply the new relocation!
8304 ri.get(elf).apply(elf);
8305 },
8306 }
8307}
8308fn addGotRelocAssumeCapacity(
8309 elf: *Elf,
8310 node: MappedFile.Node.Index,
8311 offset: u64,
8312 target: GotKey,
8313 addend: i64,
8314 @"type": GotReloc.Type,
8315) void {
8316 assert(elf.ehdrType() != .REL);
8317 switch (elf.getNode(node)) {
8318 .deleted,
8319 .archive,
8320 .archive_header,
8321 .archive_input_member,
8322 .archive_elf_member_header,
8323 .elf,
8324 .ehdr,
8325 .shdr,
8326 .segment,
8327 .copied_global,
8328 .debug_shared,
8329 .eh_frame_footer,
8330 .unit_padding,
8331 .unit_frame,
8332 .unit_frame_cie,
8333 .unit_debug_info,
8334 .unit_debug_info_header,
8335 .unit_debug_info_footer,
8336 .unit_debug_line,
8337 .unit_debug_line_header,
8338 .unit_debug_rnglists,
8339 .const_debug_info,
8340 .global_debug_info,
8341 .func_frame_fde,
8342 .func_debug_info,
8343 .func_debug_line,
8344 .decl_debug_info,
8345 => unreachable, // cannot contain relocs,
8346 .section,
8347 .section_manual_size,
8348 .uav,
8349 => unreachable, // cannot contain GOT relocs
8350 .input_section,
8351 .nav,
8352 .lazy_code,
8353 .lazy_const_data,
8354 => {},
8355 }
8356
8357 const gop = elf.got.getOrPutAssumeCapacity(target);
8358 if (!gop.found_existing) {
8359 gop.value_ptr.* = .none;
8360 const maybe_next_key: ?GotKey = switch (target) {
8361 .reserved => null,
8362 .tpoff => null,
8363 .symbol => null,
8364 .tlsld0 => .tlsld1,
8365 .tlsgd0 => |sym| .{ .tlsgd1 = sym },
8366 .tlsld1 => unreachable,
8367 .tlsgd1 => unreachable,
8368 };
8369 switch (elf.shdrPtr(elf.shndx.got)) {
8370 inline else => |got_shdr, class| {
8371 const Addr = class.ElfN().Addr;
8372 const old_size = elf.targetLoad(&got_shdr.size);
8373 const new_entry_count = @as(u32, 1) + @intFromBool(maybe_next_key != null);
8374 elf.targetStore(&got_shdr.size, @intCast(old_size + @sizeOf(Addr) * new_entry_count));
8375 },
8376 }
8377 if (maybe_next_key) |next_key| {
8378 elf.got.putAssumeCapacityNoClobber(next_key, .none);
8379 elf.updateGotEntry(gop.index);
8380 elf.updateGotEntry(gop.index + 1);
8381 } else {
8382 elf.updateGotEntry(gop.index);
8383 }
8384 }
8385
8386 elf.got_relocs.appendAssumeCapacity(.{
8387 .node = .wrap(node),
8388 .offset = offset,
8389 .target = target,
8390 .addend = addend,
8391 .type = @"type",
8392 .result = .ok,
8393 });
8394}
8395fn updateGotEntry(elf: *Elf, got_index: usize) void {
8396 assert(elf.ehdrType() != .REL);
8397 const entry_value: union(enum) {
8398 unsigned: u64,
8399 signed: i64,
8400 reloc: struct {
8401 type: MachineRelocType,
8402 dynsym_index: u32,
8403 addend: i64,
8404 },
8405 } = switch (elf.got.keys()[got_index]) {
8406 .reserved => .{ .unsigned = 0 },
8407 .tpoff => |sym_id| val: {
8408 // Only the executable's per-module TLS block is at a known offset from the TLS pointer.
8409 if (elf.base.comp.config.output_mode == .Exe and elf.classifySymbolValue(sym_id) != .dynamic) {
8410 const tls_phndx = elf.getNode(elf.ni.tls.unwrap().?).segment;
8411 const tls_size: u64 = switch (elf.phdrSlice()) {
8412 inline else => |phdr| tls_size: {
8413 assert(elf.targetLoad(&phdr[tls_phndx].type) == .TLS);
8414 break :tls_size elf.targetLoad(&phdr[tls_phndx].memsz);
8415 },
8416 };
8417 const sym_value = sym_id.value(elf);
8418 break :val .{ .signed = @bitCast(sym_value -% tls_size) };
8419 }
8420 break :val switch (sym_id.unwrap()) {
8421 // For global symbols, just target the right dynsym with no addend.
8422 .global => |name| .{ .reloc = .{
8423 .type = .tpOff(elf),
8424 .dynsym_index = elf.globalByName(name).?.dynsym_index,
8425 .addend = 0,
8426 } },
8427 // For local symbols, target the null symbol (index 0) so we get the offset to the
8428 // base of our TLS block, and then use `addend` to offset to the right symbol.
8429 .local => .{ .reloc = .{
8430 .type = .tpOff(elf),
8431 .dynsym_index = 0,
8432 .addend = @intCast(sym_id.value(elf)),
8433 } },
8434 };
8435 },
8436 .symbol => |sym| switch (elf.classifySymbolValue(sym)) {
8437 .static => .{ .unsigned = sym.value(elf) },
8438 .static_relative => .{ .reloc = .{
8439 .type = .relative(elf),
8440 .dynsym_index = 0,
8441 .addend = @bitCast(sym.value(elf)),
8442 } },
8443 .dynamic => .{ .reloc = .{
8444 .type = .globDat(elf),
8445 .dynsym_index = elf.globalByName(sym.unwrap().global).?.dynsym_index,
8446 .addend = 0,
8447 } },
8448 },
8449 .tlsgd1 => |sym| switch (elf.classifySymbolValue(sym)) {
8450 .static => .{ .unsigned = sym.value(elf) },
8451 .static_relative => unreachable, // TLS variables should be in TLS sections, which do not return `.static_relative`
8452 .dynamic => .{ .reloc = .{
8453 .type = .dtpOff(elf),
8454 .dynsym_index = elf.globalByName(sym.unwrap().global).?.dynsym_index,
8455 .addend = 0,
8456 } },
8457 },
8458 .tlsgd0 => |sym| switch (elf.base.comp.config.link_mode) {
8459 .static => val: {
8460 assert(elf.base.comp.config.output_mode == .Exe); // static libraries don't have GOTs
8461 break :val .{ .unsigned = 1 }; // TLS module ID for executable
8462 },
8463 .dynamic => .{ .reloc = .{
8464 .type = .dtpMod(elf),
8465 .dynsym_index = switch (elf.classifySymbolValue(sym)) {
8466 .static, .static_relative => 0,
8467 .dynamic => elf.globalByName(sym.unwrap().global).?.dynsym_index,
8468 },
8469 .addend = 0,
8470 } },
8471 },
8472 .tlsld0 => switch (elf.base.comp.config.link_mode) {
8473 .static => val: {
8474 assert(elf.base.comp.config.output_mode == .Exe); // static libraries don't have GOTs
8475 break :val .{ .unsigned = 1 }; // TLS module ID for executable
8476 },
8477 .dynamic => .{ .reloc = .{
8478 .type = .dtpMod(elf),
8479 .dynsym_index = 0,
8480 .addend = 0,
8481 } },
8482 },
8483 .tlsld1 => .{ .unsigned = 0 },
8484 };
8485
8486 // First, write to the GOT itself. If we're planning to use a relocation, we'll just write zeroes.
8487 const got_entry_addr: u64 = switch (elf.shdrPtr(elf.shndx.got)) {
8488 inline else => |got_shdr, class| got_entry_addr: {
8489 const addr_size = @sizeOf(class.ElfN().Addr);
8490 const offset = got_index * addr_size;
8491 const entry_ptr: *class.ElfN().Addr = @ptrCast(@alignCast(
8492 elf.shndx.got.get(elf).ni.slice(&elf.mf)[offset..][0..addr_size],
8493 ));
8494 elf.targetStore(entry_ptr, switch (entry_value) {
8495 .unsigned => |x| @intCast(x),
8496 .signed => |x| switch (class) {
8497 .NONE, _ => comptime unreachable,
8498 .@"32" => @bitCast(@as(i32, @intCast(x))),
8499 .@"64" => @bitCast(x),
8500 },
8501 .reloc => 0,
8502 });
8503 break :got_entry_addr elf.targetLoad(&got_shdr.addr) + offset;
8504 },
8505 };
8506
8507 // Then, add or remove the relocation entry if needed.
8508 if (elf.shndx.dynamic == .UNDEF) {
8509 // There are no relocations in the output file, so there's no reloc to delete and we can't
8510 // add a reloc in any case. (If we *are* requesting a reloc, it'll be because the value of
8511 // this GOT entry is not yet known, e.g. because a symbol is currently undefined.)
8512 return;
8513 }
8514 if (elf.got.values()[got_index].unwrap()) |rela_index| {
8515 // Clear the old relocation entry (although we might immediately re-use it below).
8516 elf.shndx.rela_dyn.relaDeleteOne(elf, rela_index);
8517 }
8518 elf.got.values()[got_index] = switch (entry_value) {
8519 .unsigned, .signed => .none, // no relocation needed
8520 .reloc => |reloc| elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
8521 .type = reloc.type,
8522 .offset = got_entry_addr,
8523 .raw_sym_index = reloc.dynsym_index,
8524 .addend = reloc.addend,
8525 }).toOptional(),
8526 };
8527}
8528
8529/// If `node` cannot contain runtime relocations, returns `.no`.
8530///
8531/// If `node` can contain runtime relocations, `returns `.yes_textrel` if such a relocation requires
8532/// the presence of a `DT_TEXTREL` dynamic entry, or `.yes` otherwise.
8533fn nodeWantsDsoRelocation(elf: *Elf, node: MappedFile.Node.Index) enum { yes, yes_textrel, no } {
8534 const shndx = elf.getNodeShndx(node);
8535 const shf: std.elf.SHF = switch (elf.shdrPtr(shndx)) {
8536 inline else => |shdr| elf.targetLoad(&shdr.flags).shf,
8537 };
8538 if (!shf.ALLOC) return .no;
8539 if (!shf.WRITE) return .yes_textrel;
8540 return .yes;
8541}
8542
8543/// If the given undefined global could have a copy relocation, creates that relocation if it does
8544/// not already exist, and returns `true`.
8545///
8546/// Returns `false` iff a copy relocation cannot currently be created for the global. If it may be
8547/// possible in future, the symbol is added to `elf.want_copied_globals` so that the copy relocation
8548/// will be created if and when we discover a suitable definition in an input DSO.
8549///
8550/// If this function creates a new copy relocation, it will also update relocations targeting the
8551/// global where needed---the caller does not need to do this.
8552///
8553/// Asserts that `elf.shndx.dynamic != .UNDEF` and that `global_name` refers to an *undefined* global.
8554fn maybeAddCopyRelocation(elf: *Elf, global_name: String(.strtab)) Error!bool {
8555 assert(elf.shndx.dynamic != .UNDEF);
8556
8557 // Only dynamic executables may contain `R_*_COPY` relocations.
8558 if (elf.base.comp.config.output_mode != .Exe) return false;
8559
8560 const gpa = elf.base.comp.gpa;
8561
8562 const global_ptr = elf.globals.strong_undef.getPtr(global_name) orelse
8563 elf.globals.weak_undef.getPtr(global_name).?;
8564
8565 assert(global_ptr.dynsym_index != 0);
8566
8567 const dso_global = elf.dso_globals.get(global_name) orelse {
8568 // We do not have a definition to provide the correct size for the symbol. If a definition
8569 // is discovered in a later DSO, we may at that point be able to add a copy relocation.
8570 try elf.want_copied_globals.put(gpa, global_name, {});
8571 return false;
8572 };
8573
8574 if (dso_global.type != .OBJECT) return false;
8575
8576 const gop = try elf.copied_globals.getOrPut(gpa, global_name);
8577 if (gop.found_existing) return true;
8578 errdefer assert(elf.copied_globals.pop().?.key == global_name);
8579
8580 try Section.Index.data.ensureAligned(elf, dso_global.alignment);
8581
8582 try elf.nodes.ensureUnusedCapacity(gpa, 1);
8583 const node = elf.addNodeAssumeCapacity(
8584 try Section.Index.data.get(elf).ni.addFloatingChild(gpa, &elf.mf, .{
8585 .size = dso_global.alignment.forward(dso_global.size),
8586 .alignment = dso_global.alignment,
8587 }),
8588 .{ .copied_global = global_name },
8589 );
8590 errdefer comptime unreachable;
8591
8592 const vaddr = elf.computeNodeVAddr(node);
8593 const rela_index = elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
8594 .type = .copy(elf),
8595 .offset = vaddr,
8596 .raw_sym_index = global_ptr.dynsym_index,
8597 .addend = 0,
8598 });
8599 gop.value_ptr.* = .{
8600 .node = node,
8601 .rela_index = rela_index,
8602 };
8603
8604 switch (elf.symPtr(global_ptr.symtab_index)) {
8605 inline else => |sym| elf.targetStore(&sym.size, @intCast(dso_global.size)),
8606 }
8607 switch (elf.dynsymPtr(global_ptr.dynsym_index)) {
8608 inline else => |dynsym| elf.targetStore(&dynsym.size, @intCast(dso_global.size)),
8609 }
8610
8611 // Because we now have a copy relocation, any dynamic relocations which target this symbol are
8612 // now incorrect, since we now own the canonical address of the symbol. So delete those relocs
8613 // and then update the symbol's address (and re-apply relocations targeting it of course).
8614 Symbol.Id.global(global_name).deleteDynamicTargetRelocs(elf);
8615 Symbol.Id.global(global_name).flushMoved(elf, vaddr);
8616
8617 return true;
8618}
8619
8620pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) link.Error!void {
8621 elf.updateNavInner(pt, nav_index) catch |err| switch (err) {
8622 else => |e| return e,
8623 error.MappedFileIo => return elf.base.comp.link_diags.fail(
8624 "failed to write output file: {t}",
8625 .{elf.mf.io_err.?},
8626 ),
8627 };
8628}
8629fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) Error!void {
8630 const zcu = pt.zcu;
8631 const gpa = zcu.gpa;
8632 const ip = &zcu.intern_pool;
8633
8634 const nav = ip.getNav(nav_index);
8635 if (ip.indexToKey(nav.resolved.?.value) == .@"extern") return;
8636 if (!Type.fromInterned(nav.resolved.?.type).hasRuntimeBits(zcu)) {
8637 if (elf.ehdrMachine() != .X86_64) return;
8638 const mod = zcu.fileByIndex(nav.srcInst(ip).resolveFile(ip)).mod.?;
8639 return if (!mod.strip) elf.dwarf.updateComptimeNav(pt, nav_index);
8640 }
8641
8642 const nmi = try elf.navMapIndex(zcu, nav_index);
8643 const ni = nmi.symbol(elf).index().ptr(elf).node.unwrap().?;
8644
8645 // Ensure the NAV is marked as moved so that once we're done, `flushMoved` will eventually be
8646 // called to apply the NAV's new relocations.
8647 try ni.moved(gpa, &elf.mf);
8648
8649 {
8650 var nw: MappedFile.Node.Writer = undefined;
8651 ni.writer(gpa, &elf.mf, &nw);
8652 defer nw.deinit();
8653 elf.resetNodeRelocs(ni);
8654 codegen.generateSymbol(
8655 &elf.base,
8656 pt,
8657 .fromInterned(nav.resolved.?.value),
8658 &nw.interface,
8659 .{ .atom_index = Node.toAtom(ni) },
8660 ) catch |err| switch (err) {
8661 else => |e| return e,
8662 error.WriteFailed => return nw.err.?,
8663 };
8664 switch (elf.symPtr(nmi.symbol(elf).index())) {
8665 inline else => |sym| elf.targetStore(&sym.size, @intCast(nw.interface.end)),
8666 }
8667 }
8668
8669 // The NAV's node is done---now generate any UAVs or lazy code/data which the NAV needs.
8670 try elf.genPending(pt);
8671 try elf.dwarf.const_pool.flushPending(pt, .{ .elf2 = elf });
8672}
8673
8674pub fn updateContainerType(
8675 elf: *Elf,
8676 pt: Zcu.PerThread,
8677 ty: InternPool.Index,
8678 success: bool,
8679) link.Error!void {
8680 elf.updateContainerTypeInner(pt, ty, success) catch |err| switch (err) {
8681 else => |e| return e,
8682 error.MappedFileIo => return elf.base.comp.link_diags.fail(
8683 "failed to write output file: {t}",
8684 .{elf.mf.io_err.?},
8685 ),
8686 };
8687}
8688pub fn updateContainerTypeInner(
8689 elf: *Elf,
8690 pt: Zcu.PerThread,
8691 ty: InternPool.Index,
8692 success: bool,
8693) Error!void {
8694 switch (elf.base.comp.config.debug_format) {
8695 .strip => {},
8696 .dwarf => {
8697 try elf.dwarf.const_pool.updateContainerType(pt, .{ .elf2 = elf }, ty, success);
8698 try elf.dwarf.const_pool.flushPending(pt, .{ .elf2 = elf });
8699 },
8700 .code_view => unreachable,
8701 }
8702 if (!success) return;
8703 var lazy_it = elf.lazy.iterator();
8704 while (lazy_it.next()) |lazy| if (lazy.value.map.getIndex(ty)) |lmi| {
8705 if (lazy.value.pending_index <= lmi) continue;
8706 // This type has changed on this incremental update, so update the lazy code/data.
8707 try elf.genLazy(pt, .{ .kind = lazy.key, .index = @intCast(lmi) });
8708 };
8709}
8710
8711pub fn addConst(
8712 elf: *Elf,
8713 _: Zcu.PerThread,
8714 cpi: link.ConstPool.Index,
8715 val: InternPool.Index,
8716) link.Error!void {
8717 switch (elf.base.comp.config.debug_format) {
8718 .strip => {},
8719 .dwarf => {
8720 const gpa = elf.base.comp.gpa;
8721 try elf.nodes.ensureUnusedCapacity(gpa, 1);
8722 try elf.dwarf.consts.ensureUnusedCapacity(gpa, 1);
8723 try elf.dwarf_consts.ensureUnusedCapacity(gpa, 1);
8724 try elf.dwarf.addConst(cpi, val, &addConstNode);
8725 },
8726 .code_view => unreachable,
8727 }
8728}
8729fn addConstNode(lf: *link.File, ui: Dwarf.Unit.Index, cpi: link.ConstPool.Index) link.Error!MappedFile.Node.Index {
8730 const elf = lf.cast(.elf2).?;
8731 const unit = ui.get(&elf.dwarf);
8732 const debug_info_ni = elf.addNodeAssumeCapacity(
8733 unit.debug_info_ni.unwrap().?.addFloatingChild(lf.comp.gpa, &elf.mf, .{
8734 .enable_next_moved = true,
8735 }) catch |err| switch (err) {
8736 else => |e| return e,
8737 error.MappedFileIo => return lf.comp.link_diags.fail("failed to write output file: {t}", .{
8738 elf.mf.io_err.?,
8739 }),
8740 },
8741 .{ .const_debug_info = cpi },
8742 );
8743 elf.dwarf_consts.putAssumeCapacityNoClobber(cpi, .{
8744 .debug_info_first_target_reloc = .none,
8745 .debug_info_first_symbol_reloc = .none,
8746 .debug_info_first_node_reloc = .none,
8747 });
8748 return debug_info_ni;
8749}
8750
8751pub fn updateConst(
8752 elf: *Elf,
8753 pt: Zcu.PerThread,
8754 cpi: link.ConstPool.Index,
8755 val: InternPool.Index,
8756) link.Error!void {
8757 switch (val) {
8758 .anyerror_type => {}, // handled in `updateErrorData` instead
8759 else => try elf.updateConstInner(pt, cpi, val, .complete),
8760 }
8761}
8762fn updateConstInner(
8763 elf: *Elf,
8764 pt: Zcu.PerThread,
8765 cpi: link.ConstPool.Index,
8766 val: InternPool.Index,
8767 complete: enum { incomplete, complete },
8768) link.Error!void {
8769 switch (elf.base.comp.config.debug_format) {
8770 .strip => {},
8771 .dwarf => {
8772 {
8773 switch (pt.zcu.intern_pool.indexToKey(val)) {
8774 else => {},
8775 .func => |func| {
8776 const fi = try elf.dwarf.getFunc(func.owner_nav);
8777 switch (fi.get(&elf.dwarf).state) {
8778 .unresolved => {},
8779 .resolved => return,
8780 }
8781 },
8782 }
8783 const gpa = elf.base.comp.gpa;
8784 const debug_info_ni = Dwarf.Const.get(cpi, &elf.dwarf).debug_info_ni.unwrap().?;
8785 try debug_info_ni.moved(gpa, &elf.mf);
8786 var di_nw: MappedFile.Node.Writer = undefined;
8787 debug_info_ni.writer(gpa, &elf.mf, &di_nw);
8788 defer di_nw.deinit();
8789 elf.resetNodeRelocs(debug_info_ni);
8790 switch (complete) {
8791 .incomplete => try elf.dwarf.updateConstIncomplete(pt, &di_nw, val),
8792 .complete => try elf.dwarf.updateConst(pt, &di_nw, val),
8793 }
8794 }
8795 try elf.genPending(pt);
8796 },
8797 .code_view => unreachable,
8798 }
8799}
8800
8801pub fn updateConstIncomplete(
8802 elf: *Elf,
8803 pt: Zcu.PerThread,
8804 cpi: link.ConstPool.Index,
8805 val: InternPool.Index,
8806) link.Error!void {
8807 return elf.updateConstInner(pt, cpi, val, .incomplete);
8808}
8809
8810pub fn updateFunc(
8811 elf: *Elf,
8812 pt: Zcu.PerThread,
8813 func_index: InternPool.Index,
8814 mir: *const codegen.AnyMir,
8815) link.Error!void {
8816 elf.updateFuncInner(pt, func_index, mir) catch |err| switch (err) {
8817 else => |e| return e,
8818 error.MappedFileIo => return elf.base.comp.link_diags.fail(
8819 "failed to write output file: {t}",
8820 .{elf.mf.io_err.?},
8821 ),
8822 };
8823}
8824fn updateFuncInner(
8825 elf: *Elf,
8826 pt: Zcu.PerThread,
8827 func_index: InternPool.Index,
8828 mir: *const codegen.AnyMir,
8829) Error!void {
8830 const zcu = pt.zcu;
8831 const gpa = zcu.gpa;
8832 const ip = &zcu.intern_pool;
8833 const func = zcu.funcInfo(func_index);
8834 const nav = ip.getNav(func.owner_nav);
8835
8836 const nmi = try elf.navMapIndex(zcu, func.owner_nav);
8837 log.debug("updateFunc({f}) = {d}", .{ nav.fqn.fmt(ip), nmi.symbol(elf) });
8838 const lsi = nmi.symbol(elf);
8839 const ni = lsi.index().ptr(elf).node.unwrap().?;
8840
8841 // Ensure the NAV is marked as moved so that once we're done, `flushMoved` will eventually be
8842 // called to apply the NAV's new relocations.
8843 try ni.moved(gpa, &elf.mf);
8844
8845 {
8846 var nw: MappedFile.Node.Writer = undefined;
8847 ni.writer(gpa, &elf.mf, &nw);
8848 defer nw.deinit();
8849 var debug_output_buf: Dwarf.WipNav.Debug = undefined;
8850 const debug_output: link.File.DebugInfoOutput, const dwarf_func = debug_output: {
8851 if (elf.ehdrMachine() != .X86_64) break :debug_output .{ .none, undefined };
8852 const dwarf = &elf.dwarf;
8853 const src_inst = nav.srcInst(ip);
8854 const mod = zcu.fileByIndex(src_inst.resolveFile(ip)).mod.?;
8855 if (mod.strip and mod.unwind_tables == .none) break :debug_output .{ .none, undefined };
8856
8857 try elf.nodes.ensureUnusedCapacity(gpa, 4);
8858 const dwarf_fi = try dwarf.getFunc(func.owner_nav);
8859
8860 const wip_nav = &debug_output_buf.wip_nav;
8861 wip_nav.* = .{
8862 .dwarf = dwarf,
8863 .unit = dwarf.getUnit(mod),
8864 .func = func_index,
8865 .func_si = Symbol.Id.local(lsi).toTypeErased(),
8866 .cfi = .{
8867 .loc = 0,
8868 .cfa = dwarf.frame.header.initial_instructions[0].def_cfa,
8869 },
8870 .frame_format = switch (mod.unwind_tables) {
8871 .none => .debug_frame,
8872 .sync, .async => .eh_frame,
8873 },
8874 .fde_writer = undefined,
8875 .frame_func_length = undefined,
8876 };
8877 const unit = wip_nav.unit.get(dwarf);
8878
8879 const frame_align: Alignment = switch (elf.identClass()) {
8880 .NONE, _ => unreachable,
8881 .@"32" => .@"4",
8882 .@"64" => .@"8",
8883 };
8884 const frame_ni = unit.frame_ni.unwrap() orelse frame_ni: {
8885 const frame_ni = elf.addNodeAssumeCapacity(try switch (wip_nav.frame_format) {
8886 .debug_frame => elf.shndx.debug_frame,
8887 .eh_frame => elf.shndx.eh_frame,
8888 }.get(elf).ni.addFloatingChild(gpa, &elf.mf, .{
8889 .alignment = frame_align.max(elf.mf.flags.block_size),
8890 .enable_next_moved = true,
8891 }), .{ .unit_frame = wip_nav.unit });
8892 unit.frame_ni = .wrap(frame_ni);
8893 break :frame_ni frame_ni;
8894 };
8895 if (unit.cie_ni == .none) {
8896 const cie_ni = elf.addNodeAssumeCapacity(
8897 try frame_ni.addOnlyHeaderChild(gpa, &elf.mf, .{
8898 .alignment = frame_align,
8899 .next_moved = true,
8900 .enable_next_moved = true,
8901 }),
8902 .{ .unit_frame_cie = wip_nav.unit },
8903 );
8904 unit.cie_ni = .wrap(cie_ni);
8905 var cie_nw: MappedFile.Node.Writer = undefined;
8906 cie_ni.writer(gpa, &elf.mf, &cie_nw);
8907 defer cie_nw.deinit();
8908 dwarf.genDebugFrameCie(&cie_nw.interface, switch (elf.ehdrMachine()) {
8909 else => unreachable,
8910 .X86_64 => .x86_64,
8911 }, wip_nav.frame_format) catch |err| switch (err) {
8912 error.WriteFailed => return cie_nw.err.?,
8913 };
8914 }
8915 const dwarf_func = dwarf_fi.get(dwarf);
8916 const fde_ni = if (dwarf_func.fde_ni.unwrap()) |fde_ni| fde_ni: {
8917 try fde_ni.moved(gpa, &elf.mf);
8918 try fde_ni.nextMoved(gpa, &elf.mf);
8919 break :fde_ni fde_ni;
8920 } else fde_ni: {
8921 const fde_ni = elf.addNodeAssumeCapacity(try frame_ni.addFloatingChild(gpa, &elf.mf, .{
8922 .alignment = frame_align,
8923 .moved = true,
8924 .next_moved = true,
8925 .enable_next_moved = true,
8926 }), .{ .func_frame_fde = dwarf_fi });
8927 dwarf_func.fde_ni = .wrap(fde_ni);
8928 break :fde_ni fde_ni;
8929 };
8930 fde_ni.writer(gpa, &elf.mf, &wip_nav.fde_writer);
8931
8932 if (mod.strip) break :debug_output .{ .{ .eh_frame = wip_nav }, dwarf_func };
8933
8934 const debug = &debug_output_buf;
8935 debug.pt = pt;
8936 debug.any_children = false;
8937 debug.blocks = .empty;
8938 dwarf_func.state = .resolved;
8939
8940 const debug_info_ni = dwarf_func.debug_info_ni.unwrap().?;
8941 try dwarf.decls.put(zcu.comp.gpa, src_inst, .{
8942 .debug_info_ni = debug_info_ni.toOptional(),
8943 });
8944 try debug_info_ni.moved(gpa, &elf.mf);
8945 try debug_info_ni.nextMoved(gpa, &elf.mf);
8946 debug_info_ni.writer(gpa, &elf.mf, &debug.info_writer);
8947
8948 const debug_line_ni = dwarf_func.debug_line_ni.unwrap() orelse debug_line_ni: {
8949 const debug_line_ni = elf.addNodeAssumeCapacity(
8950 try unit.debug_line_ni.unwrap().?.addFloatingChild(gpa, &elf.mf, .{
8951 .moved = true,
8952 .next_moved = true,
8953 .enable_next_moved = true,
8954 }),
8955 .{ .func_debug_line = dwarf_fi },
8956 );
8957 dwarf_func.debug_line_ni = .wrap(debug_line_ni);
8958 break :debug_line_ni debug_line_ni;
8959 };
8960 debug_line_ni.writer(gpa, &elf.mf, &debug.line_writer);
8961
8962 break :debug_output .{ .{ .dwarf2 = debug }, dwarf_func };
8963 };
8964 defer switch (debug_output) {
8965 .dwarf => unreachable,
8966 inline .eh_frame, .dwarf2 => |dwarf| dwarf.deinit(),
8967 .none => {},
8968 };
8969 switch (debug_output) {
8970 .dwarf => unreachable,
8971 .eh_frame => |wip_nav| {
8972 elf.resetNodeRelocs(dwarf_func.fde_ni.unwrap().?);
8973 try wip_nav.genDebugFrameHeader();
8974 },
8975 .dwarf2 => |debug| {
8976 elf.resetNodeRelocs(dwarf_func.fde_ni.unwrap().?);
8977 try debug.wip_nav.genDebugFrameHeader();
8978 elf.resetNodeRelocs(dwarf_func.debug_line_ni.unwrap().?);
8979 try debug.startDebugLine();
8980 elf.resetNodeRelocs(dwarf_func.debug_info_ni.unwrap().?);
8981 try debug.startFuncDebugInfo();
8982 },
8983 .none => {},
8984 }
8985 elf.resetNodeRelocs(ni);
8986 codegen.emitFunction(
8987 &elf.base,
8988 pt,
8989 func_index,
8990 Node.toAtom(ni),
8991 mir,
8992 &nw.interface,
8993 debug_output,
8994 ) catch |err| switch (err) {
8995 else => |e| return e,
8996 error.WriteFailed => if (nw.err) |e| return e,
8997 };
8998 const func_length = nw.interface.end;
8999 switch (elf.symPtr(nmi.symbol(elf).index())) {
9000 inline else => |sym| elf.targetStore(&sym.size, @intCast(func_length)),
9001 }
9002 switch (debug_output) {
9003 .dwarf => unreachable,
9004 .eh_frame => |wip_nav| wip_nav.finishDebugFrameFde(func_length),
9005 .dwarf2 => |debug| {
9006 try debug.finishFunc(func_length);
9007 const unit = debug.wip_nav.unit.get(debug.wip_nav.dwarf);
9008 {
9009 var dr_nw: MappedFile.Node.Writer = undefined;
9010 unit.debug_rnglists_ni.unwrap().?.writer(gpa, &elf.mf, &dr_nw);
9011 defer dr_nw.deinit();
9012 const first_symbol_reloc = elf.symbol_relocs.items.len;
9013 debug.wip_nav.dwarf.genDebugRnglists(
9014 unit,
9015 &dr_nw,
9016 debug.wip_nav.func_si,
9017 func_length,
9018 ) catch |err| switch (err) {
9019 else => |e| return e,
9020 error.WriteFailed => return dr_nw.err.?,
9021 };
9022 const symbol_relocs = &elf.dwarf_units[@backingInt(debug.wip_nav.unit)]
9023 .debug_rnglists_symbol_relocs;
9024 try symbol_relocs.ensureUnusedCapacity(gpa, elf.symbol_relocs.items.len -
9025 first_symbol_reloc);
9026 for (first_symbol_reloc..elf.symbol_relocs.items.len) |symbol_ri|
9027 symbol_relocs.putAssumeCapacityNoClobber(
9028 @fromBackingInt(@intCast(symbol_ri)),
9029 {},
9030 );
9031 }
9032 debug.wip_nav.finishDebugFrameFde(func_length);
9033 if (func.analysisUnordered(ip).inferred_error_set) {
9034 const ies = ip.getIfExists(.{ .inferred_error_set_type = func_index }).?;
9035 if (elf.dwarf.const_pool.getIfExists(ies)) |cpi|
9036 try elf.updateConstInner(pt, cpi, ies, .complete);
9037 }
9038 },
9039 .none => {},
9040 }
9041 }
9042
9043 // The NAV's node is done---now generate any UAVs or lazy code/data which the NAV needs.
9044 try elf.genPending(pt);
9045 try elf.dwarf.const_pool.flushPending(pt, .{ .elf2 = elf });
9046}
9047
9048pub fn updateLineNumber(
9049 elf: *Elf,
9050 _: Zcu.PerThread,
9051 inst: InternPool.TrackedInst.Index,
9052 line: u32,
9053) void {
9054 elf.dwarf.updateLineNumber(&elf.mf, inst, line);
9055}
9056
9057pub fn lostTracking(
9058 elf: *Elf,
9059 _: Zcu.PerThread,
9060 inst: InternPool.TrackedInst.Index,
9061) link.Error!void {
9062 const di = elf.dwarf.getDeclIfExists(inst) orelse return;
9063 const decl_ni = di.get(&elf.dwarf).debug_info_ni.unwrap() orelse return;
9064 const comp = elf.base.comp;
9065 var di_nw: MappedFile.Node.Writer = undefined;
9066 decl_ni.writer(comp.gpa, &elf.mf, &di_nw);
9067 defer di_nw.deinit();
9068 elf.resetNodeRelocs(decl_ni);
9069 elf.dwarf.lostTracking(&di_nw) catch |err| switch (err) {
9070 else => |e| return e,
9071 error.WriteFailed => unreachable,
9072 };
9073 decl_ni.resizeLeaf(comp.gpa, &elf.mf, di_nw.interface.end) catch |err| switch (err) {
9074 else => |e| return e,
9075 error.MappedFileIo => return comp.link_diags.fail("failed to write output file: {t}", .{
9076 elf.mf.io_err.?,
9077 }),
9078 };
9079}
9080
9081pub fn updateErrorData(elf: *Elf, pt: Zcu.PerThread) link.Error!void {
9082 if (elf.lazy.getPtr(.const_data).map.getIndex(.anyerror_type)) |lmi| try elf.genLazyInner(pt, .{
9083 .kind = .const_data,
9084 .index = @intCast(lmi),
9085 });
9086 if (elf.dwarf.const_pool.getIfExists(.anyerror_type)) |cpi|
9087 try elf.updateConstInner(pt, cpi, .anyerror_type, .complete);
9088}
9089
9090pub fn flush(
9091 elf: *Elf,
9092 arena: std.mem.Allocator,
9093 tid: Zcu.PerThread.Id,
9094 prog_node: std.Progress.Node,
9095) link.Error!void {
9096 elf.flushInner(arena, tid, prog_node) catch |err| switch (err) {
9097 else => |e| return e,
9098 error.MappedFileIo => return elf.base.comp.link_diags.fail(
9099 "failed to write output file: {t}",
9100 .{elf.mf.io_err.?},
9101 ),
9102 };
9103}
9104fn flushInner(
9105 elf: *Elf,
9106 arena: std.mem.Allocator,
9107 tid: Zcu.PerThread.Id,
9108 prog_node: std.Progress.Node,
9109) Error!void {
9110 const comp = elf.base.comp;
9111 const diags = &comp.link_diags;
9112 _ = arena;
9113
9114 const sub_prog_node = prog_node.start("ELF Flush", 0);
9115 defer sub_prog_node.end();
9116
9117 try elf.flushFiles();
9118
9119 if (comp.config.output_mode == .Exe) {
9120 var any_undef = false;
9121 for (elf.globals.strong_undef.keys()) |name| {
9122 if (elf.dso_globals.contains(name)) continue;
9123 any_undef = true;
9124 diags.addError("undefined global symbol '{s}'", .{name.slice(elf)});
9125 }
9126 if (any_undef) return error.AlreadyReported;
9127 }
9128
9129 try elf.prepareDynamic();
9130
9131 while (try elf.idle(tid)) {}
9132
9133 assert(elf.pending_uavs.items.len == 0);
9134 assert(elf.dwarf.const_pool.pending.items.len == 0);
9135
9136 // We've done the final `idle` loop, so everything is at its final place in the file. We have a
9137 // few more things to check and write now that addresses and offsets are finalized.
9138 elf.mf.nodes_lock.lock();
9139 defer elf.mf.nodes_lock.unlock();
9140
9141 if (elf.overflowed_reloc_count > 0) {
9142 diags.addError("failed to apply {d} relocations: overflow", .{elf.overflowed_reloc_count});
9143 }
9144 if (elf.misaligned_reloc_count > 0) {
9145 diags.addError("failed to apply {d} relocations: misaligned value", .{elf.misaligned_reloc_count});
9146 }
9147
9148 if (elf.archive) |*archive| {
9149 if (archive.elf_member_too_big) diags.addError(
9150 "file size of {Bi} exceeds maximum size of archive member",
9151 .{elf.ni.elf.location(&elf.mf).resolve(&elf.mf)[1]},
9152 );
9153 if (archive.strtab_member_too_big) diags.addError(
9154 "archive file name string table exceeds maximum size",
9155 .{},
9156 );
9157 }
9158
9159 elf.flushDynamic();
9160
9161 const entry_addr: u64 = entry: {
9162 const sym_name_slice: []const u8 = name: switch (elf.options.entry) {
9163 .default => switch (comp.config.output_mode) {
9164 .Exe => continue :name .enabled,
9165 .Lib, .Obj => continue :name .disabled,
9166 },
9167 .disabled => break :entry 0,
9168 .enabled => "_start",
9169 .named => |named| named,
9170 };
9171 const sym_name_strtab = try elf.string(.strtab, sym_name_slice);
9172 if (elf.globalByName(sym_name_strtab) == null) break :entry 0;
9173 break :entry Symbol.Id.global(sym_name_strtab).value(elf);
9174 };
9175 switch (elf.ehdrPtr()) {
9176 inline else => |ehdr| elf.targetStore(&ehdr.entry, @intCast(entry_addr)),
9177 }
9178
9179 try elf.mf.flush();
9180
9181 if (elf.options.enable_link_snapshots)
9182 elf.dumpStderr(tid) catch |err|
9183 return diags.fail("dumping link snapshot failed: {t}", .{err});
9184}
9185
9186pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) link.Error!bool {
9187 // This function is called non-deterministically, and so must not affect the layout of any nodes.
9188 elf.mf.nodes_lock.lock();
9189 defer elf.mf.nodes_lock.unlock();
9190
9191 const comp = elf.base.comp;
9192 const diags = &comp.link_diags;
9193
9194 assert(elf.pending_uavs.items.len == 0);
9195 assert(elf.dwarf.const_pool.pending.items.len == 0);
9196
9197 task: {
9198 if (elf.input_pending_index < elf.inputs.items.len) {
9199 const ii: Node.InputIndex = @fromBackingInt(elf.input_pending_index);
9200 elf.input_pending_index += 1;
9201 const sub_prog_node = elf.idleProgNode(tid, elf.input_prog_node, elf.getNode(ii.node(elf)));
9202 defer sub_prog_node.end();
9203 elf.flushInput(ii) catch |err| switch (err) {
9204 else => |e| return e,
9205 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
9206 };
9207 break :task;
9208 }
9209 if (elf.input_section_pending_index < elf.input_sections.items.len) {
9210 const isi: InputSection.Index = @fromBackingInt(elf.input_section_pending_index);
9211 elf.input_section_pending_index += 1;
9212 const sub_prog_node = elf.idleProgNode(tid, elf.input_prog_node, elf.getNode(isi.node(elf)));
9213 defer sub_prog_node.end();
9214 elf.flushInputSection(isi) catch |err| switch (err) {
9215 else => |e| return e,
9216 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
9217 };
9218 break :task;
9219 }
9220 if (elf.one_shot_fixups.items.len > 0) {
9221 // Each of these is very simple, so an unreasonable amount of overhead would be
9222 // introduced if we only did one per `idle` call. Also, there is no risk of this work
9223 // being invalidated. So let's just flush the entire queue at once.
9224 for (elf.one_shot_fixups.items) |isw| {
9225 const dest_slice = isw.node.slice(&elf.mf)[@intCast(isw.offset)..][0..4];
9226 const old: u32 = std.mem.readInt(u32, dest_slice, elf.targetEndian());
9227 const new: u32 = switch (isw.action) {
9228 // zig fmt: off
9229 .@"32[12:10] = 0b000" => old & 0b11111111_11111111_11100011_11111111,
9230 .@"32[12:10] = 0b111" => old | 0b00000000_00000000_00011100_00000000,
9231 .@"32[12:12] = 0b0" => old & 0b11111111_11111111_11101111_11111111,
9232 // zig fmt: on
9233 };
9234 std.mem.writeInt(u32, dest_slice, new, elf.targetEndian());
9235 }
9236 elf.one_shot_fixups.clearRetainingCapacity();
9237 break :task;
9238 }
9239 if (elf.changed_symtab_index.pop()) |kv| {
9240 const sub_prog_node = elf.mf.update_prog_node.start(kv.key.slice(elf), 0);
9241 defer sub_prog_node.end();
9242
9243 const global_name = kv.key;
9244 const global = elf.globalByName(global_name).?;
9245 const sym_id: Symbol.Id = .global(global_name);
9246 const sym = global.symtab_index.ptr(elf);
9247
9248 switch (elf.ehdrType()) {
9249 .REL => {
9250 // Index in `.symtab` has changed. Relocatables are easy, we just need to update
9251 // all of the output relocations.
9252 const symtab_index = @backingInt(global.symtab_index);
9253 var ri = sym.first_target_reloc;
9254 while (ri != .none) {
9255 const reloc = ri.get(elf);
9256 assert(reloc.target == sym_id);
9257 // In relocatables, every symbol relocation has an output relocation.
9258 const rela_index = reloc.rela_index.unwrap().?;
9259 reloc.relaSection(elf).relaUpdateSym(elf, rela_index, symtab_index);
9260 ri = reloc.next;
9261 }
9262 },
9263 // For other `ET_*` values, the index in `.dynsym` has changed. There are a few
9264 // places we might have emitted output relocations, depending on whether or not the
9265 // symbol's value is statically known.
9266 .EXEC, .DYN => switch (elf.classifySymbolValue(sym_id)) {
9267 .static, .static_relative => {
9268 // Since the symbol value is statically known, we definitely aren't emitting
9269 // any relocation targeting it (we might have `R_*_RELATIVE` relocs but they
9270 // don't care about the dynsym index). The only exception is a copy reloc
9271 // could exist (and be the *reason* the symbol value is statically known).
9272 if (elf.copied_globals.get(global_name)) |copied| {
9273 elf.shndx.rela_dyn.relaUpdateSym(elf, copied.rela_index, global.dynsym_index);
9274 }
9275 },
9276 .dynamic => {
9277 assert(!elf.copied_globals.contains(global_name)); // value would be statically known
9278
9279 // Update symbol relocs:
9280 var ri = sym.first_target_reloc;
9281 while (ri != .none) {
9282 const reloc = ri.get(elf);
9283 assert(reloc.target == sym_id);
9284 // There may or may not be a runtime relocation for this symbol reloc.
9285 if (reloc.rela_index.unwrap()) |rela_index| {
9286 elf.shndx.rela_dyn.relaUpdateSym(elf, rela_index, global.dynsym_index);
9287 }
9288 ri = reloc.next;
9289 }
9290
9291 // Update the PLT entry's reloc if there is one:
9292 if (elf.plt.getIndex(global_name)) |plt_index| {
9293 // PLT indices exactly match `.rela.plt` relocation indices.
9294 elf.shndx.rela_plt.relaUpdateSym(elf, @fromBackingInt(@intCast(plt_index)), global.dynsym_index);
9295 }
9296
9297 // Update relocs for any relevant GOT entries:
9298 if (elf.got.getIndex(.{ .symbol = sym_id })) |got_index| {
9299 elf.updateGotEntry(got_index);
9300 }
9301 if (elf.got.getIndex(.{ .tpoff = sym_id })) |got_index| {
9302 elf.updateGotEntry(got_index);
9303 }
9304 if (elf.got.getIndex(.{ .tlsgd0 = sym_id })) |got_index| {
9305 elf.updateGotEntry(got_index);
9306 elf.updateGotEntry(got_index + 1); // tlsgd1
9307 }
9308 },
9309 },
9310 }
9311
9312 break :task;
9313 }
9314 while (elf.mf.updates.pop()) |ni| : (elf.mf.update_prog_node.completeOne()) {
9315 if (ni.pendingDelete(&elf.mf)) continue;
9316 const clean_moved = ni.cleanMoved(&elf.mf);
9317 const clean_resized = ni.cleanResized(&elf.mf);
9318 const clean_next_moved = ni.cleanNextMoved(&elf.mf);
9319 if (!clean_moved and !clean_resized and !clean_next_moved) continue;
9320 const sub_prog_node = elf.idleProgNode(tid, elf.mf.update_prog_node, elf.getNode(ni));
9321 defer sub_prog_node.end();
9322 if (clean_moved) try elf.flushMoved(ni);
9323 if (clean_resized) try elf.flushResized(ni);
9324 if (clean_moved or clean_resized or clean_next_moved) try elf.flushPadding(ni);
9325 break :task;
9326 }
9327 }
9328 if (elf.input_sections.items.len > elf.input_section_pending_index) return true;
9329 if (elf.one_shot_fixups.items.len > 0) return true;
9330 if (elf.changed_symtab_index.count() > 0) return true;
9331 if (elf.mf.updates.items.len > 0) return true;
9332 return false;
9333}
9334
9335fn idleProgNode(
9336 elf: *Elf,
9337 tid: Zcu.PerThread.Id,
9338 prog_node: std.Progress.Node,
9339 node: Node,
9340) std.Progress.Node {
9341 var name: [std.Progress.Node.max_name_len]u8 = undefined;
9342 return prog_node.start(name: switch (node) {
9343 else => |tag| @tagName(tag),
9344 .archive_input_member => |ii| std.mem.print(&name, "{f}{f}", .{
9345 ii.path(elf).fmtEscapeString(),
9346 fmtMemberString(ii.member(elf)),
9347 }) catch &name,
9348 .section, .section_manual_size => |shndx| shndx.name(elf).slice(elf),
9349 .input_section => |isi| {
9350 const ii = isi.input(elf);
9351 break :name std.mem.print(&name, "{f}{f} {s}", .{
9352 ii.path(elf).fmtEscapeString(),
9353 fmtMemberString(ii.member(elf)),
9354 elf.getNodeShndx(isi.node(elf)).name(elf).slice(elf),
9355 }) catch &name;
9356 },
9357 .nav => |nmi| {
9358 const ip = &elf.base.comp.zcu.?.intern_pool;
9359 break :name ip.getNav(nmi.nav(elf)).fqn.toSlice(ip);
9360 },
9361 .uav => |umi| std.mem.print(&name, "{f}", .{
9362 Value.fromInterned(umi.uavValue(elf)).fmtValue(.{ .zcu = elf.base.comp.zcu.?, .tid = tid }),
9363 }) catch &name,
9364 .debug_shared => |ss| switch (ss) {
9365 .debug_abbrev => "debug info abbrevs",
9366 .debug_str, .debug_str_offsets => "debug info strings",
9367 .debug_line_str => "line info strings",
9368 },
9369 .unit_frame,
9370 .unit_frame_cie,
9371 .unit_debug_info,
9372 .unit_debug_info_header,
9373 .unit_debug_info_footer,
9374 .unit_debug_line,
9375 .unit_debug_line_header,
9376 .unit_debug_rnglists,
9377 => |ui, tag| std.mem.print(&name, "{s} info for {s}", .{
9378 switch (tag) {
9379 else => unreachable,
9380 .unit_frame, .unit_frame_cie => "unwind",
9381 .unit_debug_info,
9382 .unit_debug_info_header,
9383 .unit_debug_info_footer,
9384 .unit_debug_rnglists,
9385 => "debug",
9386 .unit_debug_line, .unit_debug_line_header => "line",
9387 },
9388 ui.mod(&elf.dwarf).fully_qualified_name,
9389 }) catch &name,
9390 .const_debug_info => |cpi| switch (cpi.val(&elf.dwarf.const_pool)) {
9391 .generic_poison_type => "anytype",
9392 else => |val| std.mem.print(&name, "debug info for {f}", .{
9393 Value.fromInterned(val).fmtValue(.{ .zcu = elf.base.comp.zcu.?, .tid = tid }),
9394 }) catch &name,
9395 },
9396 .global_debug_info => |gi| {
9397 const ip = &elf.base.comp.zcu.?.intern_pool;
9398 break :name std.mem.print(&name, "debug info for {f}", .{
9399 ip.getNav(gi.nav(&elf.dwarf)).fqn.fmt(ip),
9400 }) catch &name;
9401 },
9402 .func_frame_fde, .func_debug_info, .func_debug_line => |fi, tag| {
9403 const ip = &elf.base.comp.zcu.?.intern_pool;
9404 break :name std.mem.print(&name, "{s} info for {f}", .{
9405 switch (tag) {
9406 else => unreachable,
9407 .func_frame_fde => "unwind",
9408 .func_debug_info => "debug",
9409 .func_debug_line => "line",
9410 },
9411 ip.getNav(fi.nav(&elf.dwarf)).fqn.fmt(ip),
9412 }) catch &name;
9413 },
9414 .decl_debug_info => |di| {
9415 const comp = elf.base.comp;
9416 const zcu = comp.zcu.?;
9417 break :name std.mem.print(&name, "debug info for {f}", .{
9418 zcu.fileByIndex(di.srcInst(&elf.dwarf).resolveFile(&zcu.intern_pool)).path.fmt(comp),
9419 }) catch &name;
9420 },
9421 }, 0);
9422}
9423
9424fn genPending(elf: *Elf, pt: Zcu.PerThread) link.Error!void {
9425 while (elf.pending_uavs.pop()) |umi| {
9426 var prog_name_buf: [std.Progress.Node.max_name_len]u8 = undefined;
9427 const prog_name = std.mem.print(&prog_name_buf, "{f}", .{
9428 Value.fromInterned(umi.uavValue(elf)).fmtValue(pt),
9429 }) catch &prog_name_buf;
9430 const prog_node = elf.const_prog_node.start(prog_name, 0);
9431 defer prog_node.end();
9432 try elf.genUav(pt, umi);
9433 }
9434 var lazy_it = elf.lazy.iterator();
9435 while (lazy_it.next()) |lazy| while (lazy.value.pending_index < lazy.value.map.count()) {
9436 try elf.genLazy(pt, .{ .kind = lazy.key, .index = lazy.value.pending_index });
9437 lazy.value.pending_index += 1;
9438 };
9439 switch (elf.base.comp.config.debug_format) {
9440 .strip => {},
9441 .dwarf => {
9442 const gpa = elf.base.comp.gpa;
9443 while (true) {
9444 const pending = elf.dwarf.pending_decl;
9445 if (pending.instance_val == .none) break;
9446 elf.dwarf.pending_decl = .{ .di = undefined, .instance_val = .none };
9447 const debug_info_ni = pending.di.get(&elf.dwarf).debug_info_ni.unwrap().?;
9448 try debug_info_ni.moved(gpa, &elf.mf);
9449 var di_nw: MappedFile.Node.Writer = undefined;
9450 debug_info_ni.writer(gpa, &elf.mf, &di_nw);
9451 defer di_nw.deinit();
9452 elf.resetNodeRelocs(debug_info_ni);
9453 try elf.dwarf.genDecl(pt, &di_nw, pending.instance_val);
9454 }
9455 },
9456 .code_view => unreachable,
9457 }
9458}
9459
9460fn genUav(
9461 elf: *Elf,
9462 pt: Zcu.PerThread,
9463 umi: Node.UavMapIndex,
9464) link.Error!void {
9465 const comp = elf.base.comp;
9466 const gpa = comp.gpa;
9467
9468 const uav_val = umi.uavValue(elf);
9469 const ni = umi.symbol(elf).index().ptr(elf).node.unwrap().?;
9470
9471 var nw: MappedFile.Node.Writer = undefined;
9472 ni.writer(gpa, &elf.mf, &nw);
9473 defer nw.deinit();
9474 elf.resetNodeRelocs(ni);
9475 codegen.generateSymbol(
9476 &elf.base,
9477 pt,
9478 .fromInterned(uav_val),
9479 &nw.interface,
9480 .{ .atom_index = Node.toAtom(ni) },
9481 ) catch |err| switch (err) {
9482 else => |e| return e,
9483 error.WriteFailed => switch (nw.err.?) {
9484 else => |e| return e,
9485 error.MappedFileIo => return comp.link_diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
9486 },
9487 };
9488 switch (elf.symPtr(umi.symbol(elf).index())) {
9489 inline else => |sym| elf.targetStore(&sym.size, @intCast(nw.interface.end)),
9490 }
9491 // The UAV should already be considered to have moved, because it is created as moved and
9492 // pending calls to `genUav` always happen before pending calls to `flushMoved`.
9493 assert(ni.hasMoved(&elf.mf));
9494}
9495
9496fn genLazy(elf: *Elf, pt: Zcu.PerThread, lmr: Node.LazyMapRef) link.Error!void {
9497 const lazy = lmr.lazySymbol(elf);
9498 if (lazy.ty == .anyerror_type) return;
9499 const lazy_ty: Type = .fromInterned(lazy.ty);
9500 var prog_name_buf: [std.Progress.Node.max_name_len]u8 = undefined;
9501 const prog_name: []const u8 = switch (lazy_ty.zigTypeTag(pt.zcu)) {
9502 .@"enum" => std.mem.print(&prog_name_buf, "@tagName({f})", .{lazy_ty.fmt(pt)}) catch &prog_name_buf,
9503 .error_set => switch (lmr.kind) {
9504 .code => std.mem.print(&prog_name_buf, "@errorCast({f})", .{lazy_ty.fmt(pt)}) catch &prog_name_buf,
9505 .const_data => "@errorName(anyerror)",
9506 },
9507 else => unreachable,
9508 };
9509 const prog_node = elf.base.comp.link_prog_node.start(prog_name, 0);
9510 defer prog_node.end();
9511 try elf.genLazyInner(pt, lmr);
9512}
9513fn genLazyInner(elf: *Elf, pt: Zcu.PerThread, lmr: Node.LazyMapRef) link.Error!void {
9514 const zcu = pt.zcu;
9515 const gpa = zcu.gpa;
9516
9517 const lazy = lmr.lazySymbol(elf);
9518 const ni = lmr.symbol(elf).index().ptr(elf).node.unwrap().?;
9519
9520 // Ensure the lazy node is marked as moved so that once we're done, `flushMoved` will eventually
9521 // be called to apply the lazy node's new relocations.
9522 try ni.moved(gpa, &elf.mf);
9523
9524 var required_alignment: InternPool.Alignment = .none;
9525 var nw: MappedFile.Node.Writer = undefined;
9526 ni.writer(gpa, &elf.mf, &nw);
9527 defer nw.deinit();
9528 elf.resetNodeRelocs(ni);
9529 codegen.generateLazySymbol(
9530 &elf.base,
9531 pt,
9532 lazy,
9533 &required_alignment,
9534 &nw.interface,
9535 .none,
9536 .{ .atom_index = Node.toAtom(ni) },
9537 ) catch |err| switch (err) {
9538 else => |e| return e,
9539 error.WriteFailed => return switch (nw.err.?) {
9540 else => |e| return e,
9541 error.MappedFileIo => return elf.base.comp.link_diags.fail(
9542 "failed to write output file: {t}",
9543 .{elf.mf.io_err.?},
9544 ),
9545 },
9546 };
9547 switch (elf.symPtr(lmr.symbol(elf).index())) {
9548 inline else => |sym| elf.targetStore(&sym.size, @intCast(nw.interface.end)),
9549 }
9550}
9551
9552fn flushInput(elf: *Elf, ii: Node.InputIndex) Error!void {
9553 const comp = elf.base.comp;
9554 const io = comp.io;
9555 const diags = &comp.link_diags;
9556 const path = ii.path(elf);
9557 const file = path.root_dir.handle.openFile(io, path.sub_path, .{}) catch |err| switch (err) {
9558 error.Canceled => |e| return e,
9559 else => |e| return diags.fail("failed to open input file \"{f}\": {t}", .{ path.fmtEscapeString(), e }),
9560 };
9561 defer file.close(io);
9562
9563 const slice = ii.node(elf).slice(&elf.mf);
9564
9565 const member_ar_hdr: *const std.elf.ar_hdr = @ptrCast(slice[0..@sizeOf(std.elf.ar_hdr)]);
9566 const input_size: u32 = member_ar_hdr.size() catch |err| switch (err) {
9567 // We wrote the `ar_hdr` ourselves (in `loadObject`), so it is definitely valid.
9568 error.Overflow, error.InvalidCharacter => unreachable,
9569 };
9570
9571 switch (slice.len - @sizeOf(std.elf.ar_hdr) - input_size) {
9572 0 => {},
9573 1 => {
9574 // Alignment added one padding byte, which the format requires to have value '\n'.
9575 slice[slice.len - 1] = '\n';
9576 },
9577 else => unreachable, // node size should agree with the value we wrote into `ar_hdr.ar_size`
9578 }
9579
9580 var fr = file.reader(io, &.{});
9581 var w: Io.Writer = .fixed(slice[@sizeOf(std.elf.ar_hdr)..]);
9582 const n_bytes_read = w.sendFileAll(&fr, .limited(input_size)) catch |err| switch (err) {
9583 error.ReadFailed => return diags.fail("failed to read input \"{f}{f}\": {t}", .{
9584 path.fmtEscapeString(),
9585 fmtMemberString(ii.member(elf)),
9586 fr.err orelse (fr.seek_err orelse fr.size_err.?),
9587 }),
9588 error.WriteFailed => unreachable, // `.limited(input_size)` prevents us writing too many bytes
9589 };
9590 if (n_bytes_read != input_size) {
9591 return diags.fail("failed to load input \"{f}{f}\": file truncated during compilation", .{
9592 path.fmtEscapeString(),
9593 fmtMemberString(ii.member(elf)),
9594 });
9595 }
9596}
9597
9598fn flushInputSection(elf: *Elf, isi: InputSection.Index) Error!void {
9599 const file_loc = isi.fileLocation(elf);
9600 if (file_loc.size == 0) return;
9601 const comp = elf.base.comp;
9602 const io = comp.io;
9603 const gpa = comp.gpa;
9604 const diags = &comp.link_diags;
9605 const ii = isi.input(elf);
9606 const path = ii.path(elf);
9607 const file = path.root_dir.handle.openFile(io, path.sub_path, .{}) catch |err| switch (err) {
9608 error.Canceled => |e| return e,
9609 else => |e| return diags.fail("failed to open input file \"{f}\": {t}", .{ path.fmtEscapeString(), e }),
9610 };
9611 defer file.close(io);
9612 var fr = file.reader(io, &.{});
9613 fr.seekTo(file_loc.offset) catch |err| switch (err) {
9614 error.Canceled => |e| return e,
9615 else => |e| return diags.fail("failed to read input section '{s}' from \"{f}{f}\": {t}", .{
9616 elf.getNodeShndx(isi.node(elf)).name(elf).slice(elf),
9617 path.fmtEscapeString(),
9618 fmtMemberString(ii.member(elf)),
9619 e,
9620 }),
9621 };
9622 var nw: MappedFile.Node.Writer = undefined;
9623 isi.node(elf).writer(gpa, &elf.mf, &nw);
9624 defer nw.deinit();
9625 const n_bytes = nw.interface.sendFileAll(&fr, .limited(@intCast(file_loc.size))) catch |err| switch (err) {
9626 error.ReadFailed => return diags.fail("failed to read input section '{s}' from \"{f}{f}\": {t}", .{
9627 elf.getNodeShndx(isi.node(elf)).name(elf).slice(elf),
9628 path.fmtEscapeString(),
9629 fmtMemberString(ii.member(elf)),
9630 fr.err orelse (fr.seek_err orelse fr.size_err.?),
9631 }),
9632 error.WriteFailed => return nw.err.?,
9633 };
9634 if (n_bytes != file_loc.size) return diags.fail("failed to read input section '{s}' from \"{f}{f}\": unexpected eof", .{
9635 elf.getNodeShndx(isi.node(elf)).name(elf).slice(elf),
9636 path.fmtEscapeString(),
9637 fmtMemberString(ii.member(elf)),
9638 });
9639 // The input section should already be considered to have moved, because it is created as moved
9640 // and pending calls to `flushInputSection` always happen before pending calls to `flushMoved`.
9641 assert(isi.node(elf).hasMoved(&elf.mf));
9642}
9643
9644fn flushElfOffset(elf: *Elf, ni: MappedFile.Node.Index) void {
9645 const elf_offset = elf.computeNodeElfOffset(ni);
9646 switch (elf.getNode(ni)) {
9647 else => unreachable,
9648 .ehdr => assert(elf_offset == 0),
9649 .shdr => switch (elf.ehdrPtr()) {
9650 inline else => |ehdr| elf.targetStore(&ehdr.shoff, @intCast(elf_offset)),
9651 },
9652 .segment => |phndx| {
9653 switch (elf.phdrSlice()) {
9654 inline else => |phdr, class| {
9655 const ph = &phdr[phndx];
9656 elf.targetStore(&ph.offset, @intCast(elf_offset));
9657 if (elf.targetLoad(&ph.type) == .PHDR) {
9658 @field(elf.ehdrPtr(), @tagName(class)).phoff = ph.offset;
9659 }
9660 },
9661 }
9662 var child_oni = ni.first(&elf.mf);
9663 while (child_oni.unwrap()) |child_ni| : (child_oni = child_ni.next(&elf.mf)) {
9664 elf.flushElfOffset(child_ni);
9665 }
9666 },
9667 .section, .section_manual_size => |shndx| switch (elf.shdrPtr(shndx)) {
9668 inline else => |shdr| elf.targetStore(&shdr.offset, @intCast(elf_offset)),
9669 },
9670 }
9671}
9672
9673fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void {
9674 const trace = tracy.trace(@src());
9675 defer trace.end();
9676
9677 switch (elf.getNode(ni)) {
9678 .deleted => unreachable,
9679 .archive, .archive_header => unreachable,
9680 .archive_input_member, .archive_elf_member_header, .elf => {
9681 assert(elf.archive != null);
9682 return;
9683 },
9684 .ehdr, .shdr => elf.flushElfOffset(ni),
9685 .segment => |phndx| {
9686 elf.flushElfOffset(ni);
9687 switch (elf.phdrSlice()) {
9688 inline else => |phdr| {
9689 const ph = &phdr[phndx];
9690 switch (elf.targetLoad(&ph.type)) {
9691 else => unreachable,
9692
9693 .NULL, .LOAD => {
9694 try elf.allocateSegmentLoadAddress(phndx);
9695 },
9696
9697 .DYNAMIC,
9698 .INTERP,
9699 .PHDR,
9700 .TLS,
9701 .GNU_EH_FRAME,
9702 .GNU_RELRO,
9703 => {
9704 const new_vaddr = elf.computeNodeVAddr(ni);
9705 elf.targetStore(&ph.vaddr, @intCast(new_vaddr));
9706 elf.targetStore(&ph.paddr, @intCast(new_vaddr));
9707 },
9708 }
9709 },
9710 }
9711 },
9712 .section, .section_manual_size => |shndx| {
9713 elf.flushElfOffset(ni);
9714 const addr = elf.computeNodeVAddr(ni);
9715 const old_addr: u64, const flags: std.elf.SHF = switch (elf.shdrPtr(shndx)) {
9716 inline else => |shdr| .{ elf.targetLoad(&shdr.addr), elf.targetLoad(&shdr.flags).shf },
9717 };
9718
9719 if (flags.ALLOC) {
9720 switch (elf.shdrPtr(shndx)) {
9721 inline else => |shdr| elf.targetStore(&shdr.addr, @intCast(addr)),
9722 }
9723
9724 // Update global symbols targeting this section
9725 if (elf.node_global_symbols.get(ni)) |first_name| {
9726 assert(first_name != .empty);
9727 var name = first_name;
9728 while (name != .empty) {
9729 const old_sym_addr = Symbol.Id.global(name).value(elf);
9730 Symbol.Id.global(name).flushMoved(elf, old_sym_addr - old_addr + addr);
9731 name = elf.globalByName(name).?.next_in_node;
9732 }
9733 }
9734
9735 Symbol.Id.local(shndx.get(elf).lsi).flushMoved(elf, addr);
9736 }
9737
9738 if (shndx == elf.shndx.got) {
9739 const rela_dyn_shndx = elf.shndx.rela_dyn;
9740 for (elf.got.values()) |opt_rela_index| {
9741 const rela_index = opt_rela_index.unwrap() orelse continue;
9742 rela_dyn_shndx.relaAdjustOffset(elf, rela_index, old_addr, addr);
9743 }
9744 for (elf.got_relocs.items) |*reloc| {
9745 reloc.apply(elf);
9746 }
9747 } else if (shndx == elf.shndx.plt) {
9748 elf.flushMovedNodeRelocs(ni, addr, .{
9749 .first_symbol_reloc = elf.plt_first_symbol_reloc,
9750 });
9751 elf.flushMovedPltSection(.plt, old_addr, addr);
9752 } else if (shndx == elf.shndx.got_plt) {
9753 elf.flushMovedPltSection(.got_plt, old_addr, addr);
9754 } else if (shndx == elf.shndx.plt_sec) {
9755 elf.flushMovedPltSection(.plt_sec, old_addr, addr);
9756 } else if (shndx == elf.shndx.eh_frame_hdr) {
9757 elf.flushMovedNodeRelocs(ni, addr, .{
9758 .first_symbol_reloc = elf.eh_frame_hdr_first_symbol_reloc,
9759 });
9760 }
9761 },
9762 .input_section => |isi| {
9763 const old_section_addr = isi.ptr(elf).vaddr;
9764 const new_section_addr = elf.computeNodeVAddr(ni);
9765 isi.ptr(elf).vaddr = new_section_addr;
9766
9767 // Update local symbols
9768 const ii = isi.input(elf);
9769 var lsi, const end_lsi = ii.localSymbolRange(elf);
9770 while (lsi != end_lsi) : (lsi = @fromBackingInt(@backingInt(lsi) + 1)) {
9771 if (lsi.index().ptr(elf).node != ni.toOptional()) continue;
9772 const visibility: std.elf.STV = switch (elf.symPtr(lsi.index())) {
9773 inline else => |sym| elf.targetLoad(&sym.other).visibility,
9774 };
9775 switch (visibility) {
9776 .HIDDEN, .INTERNAL => {
9777 // This is actually a global symbol which got demoted to STB_LOCAL due
9778 // to its visibility. It will be handled in the global symbols pass
9779 // below; don't touch it now.
9780 continue;
9781 },
9782 .PROTECTED => unreachable, // not allowed for an STB_LOCAL symbol
9783 .DEFAULT => {},
9784 }
9785 const old_sym_addr = Symbol.Id.local(lsi).value(elf);
9786 Symbol.Id.local(lsi).flushMoved(
9787 elf,
9788 old_sym_addr - old_section_addr + new_section_addr,
9789 );
9790 }
9791
9792 // Update global symbols
9793 if (elf.node_global_symbols.get(ni)) |first_name| {
9794 assert(first_name != .empty);
9795 var name = first_name;
9796 while (name != .empty) {
9797 const old_sym_addr = Symbol.Id.global(name).value(elf);
9798 Symbol.Id.global(name).flushMoved(
9799 elf,
9800 old_sym_addr - old_section_addr + new_section_addr,
9801 );
9802 name = elf.globalByName(name).?.next_in_node;
9803 }
9804 }
9805
9806 elf.flushMovedNodeRelocs(ni, new_section_addr, .{
9807 .first_symbol_reloc = isi.ptrConst(elf).first_symbol_reloc,
9808 .first_got_reloc = isi.ptrConst(elf).first_got_reloc,
9809 });
9810 },
9811 .copied_global => |global_name| {
9812 const copied_global = elf.copied_globals.getPtr(global_name) orelse {
9813 // TODO: this node is orphaned, which is possible because `MappedFile` does not yet
9814 // support deleting nodes. See logic in `setGlobalSymbolValue`.
9815 return;
9816 };
9817 assert(copied_global.node == ni);
9818
9819 const new_addr = elf.computeNodeVAddr(ni);
9820 elf.shndx.rela_dyn.relaSetOffset(elf, copied_global.rela_index, new_addr);
9821
9822 Symbol.Id.global(global_name).flushMoved(elf, new_addr);
9823 },
9824 inline .nav, .uav, .lazy_code, .lazy_const_data => |mi, tag| {
9825 const new_addr = elf.computeNodeVAddr(ni);
9826 Symbol.Id.local(mi.symbol(elf)).flushMoved(elf, new_addr);
9827 if (elf.node_global_symbols.get(ni)) |first_name| {
9828 assert(first_name != .empty);
9829 var name = first_name;
9830 while (name != .empty) {
9831 Symbol.Id.global(name).flushMoved(elf, new_addr);
9832 name = elf.globalByName(name).?.next_in_node;
9833 }
9834 }
9835 elf.flushMovedNodeRelocs(ni, new_addr, .{
9836 .first_symbol_reloc = mi.firstSymbolReloc(elf),
9837 .skip_symbol_relocs = switch (tag) {
9838 else => comptime unreachable,
9839 .nav => if (elf.dwarf.getFuncIfExists(mi.nav(elf))) |dwarf_fi|
9840 dwarf_fi.get(&elf.dwarf).debug_info_ni
9841 else
9842 .none,
9843 .uav, .lazy_code, .lazy_const_data => .none,
9844 },
9845 .first_got_reloc = mi.firstGotReloc(elf),
9846 });
9847 },
9848 .debug_shared => |ss| {
9849 const target_section_offset = elf.computeNodeSectionOffset(ni);
9850 var target_ri = elf.dwarf_shared.getPtr(ss).first_target_reloc;
9851 while (target_ri != .none) {
9852 const target_reloc = target_ri.get(elf);
9853 assert(target_reloc.target == ni);
9854 target_reloc.flushMovedTarget(elf, target_section_offset);
9855 target_ri = target_reloc.next;
9856 }
9857 },
9858 .eh_frame_footer, .unit_padding, .unit_frame, .unit_debug_info, .unit_debug_line => {},
9859 .unit_frame_cie => |ui| {
9860 const target_section_offset = elf.computeNodeSectionOffset(ni);
9861 var target_ri = elf.dwarf_units[@backingInt(ui)].frame_cie_first_target_reloc;
9862 while (target_ri != .none) {
9863 const target_reloc = target_ri.get(elf);
9864 assert(target_reloc.target == ni);
9865 target_reloc.flushMovedTarget(elf, target_section_offset);
9866 target_ri = target_reloc.next;
9867 }
9868 },
9869 .unit_debug_info_header => |ui| {
9870 const dwarf_unit = &elf.dwarf_units[@backingInt(ui)];
9871 const target_section_offset = elf.computeNodeSectionOffset(ni);
9872 var target_ri = dwarf_unit.debug_info_header_first_target_reloc;
9873 while (target_ri != .none) {
9874 const target_reloc = target_ri.get(elf);
9875 assert(target_reloc.target == ni);
9876 target_reloc.flushMovedTarget(elf, target_section_offset);
9877 target_ri = target_reloc.next;
9878 }
9879 elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{
9880 .first_node_reloc = dwarf_unit.debug_info_header_first_node_reloc,
9881 });
9882 },
9883 .unit_debug_info_footer => {},
9884 .unit_debug_line_header => |ui| {
9885 const dwarf_unit = &elf.dwarf_units[@backingInt(ui)];
9886 const target_section_offset = elf.computeNodeSectionOffset(ni);
9887 var target_ri = dwarf_unit.debug_line_header_first_target_reloc;
9888 while (target_ri != .none) {
9889 const target_reloc = target_ri.get(elf);
9890 assert(target_reloc.target == ni);
9891 target_reloc.flushMovedTarget(elf, target_section_offset);
9892 target_ri = target_reloc.next;
9893 }
9894 elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{
9895 .first_node_reloc = dwarf_unit.debug_line_header_first_node_reloc,
9896 });
9897 },
9898 .unit_debug_rnglists => |ui| {
9899 const dwarf_unit = &elf.dwarf_units[@backingInt(ui)];
9900 const target_section_offset = elf.computeNodeSectionOffset(ni);
9901 var target_ri = dwarf_unit.debug_rnglists_first_target_reloc;
9902 while (target_ri != .none) {
9903 const target_reloc = target_ri.get(elf);
9904 assert(target_reloc.target == ni);
9905 target_reloc.flushMovedTarget(elf, target_section_offset);
9906 target_ri = target_reloc.next;
9907 }
9908 const node_vaddr = elf.computeNodeVAddr(ni);
9909 for (dwarf_unit.debug_rnglists_symbol_relocs.keys()) |symbol_ri| {
9910 const symbol_reloc = symbol_ri.get(elf);
9911 assert(symbol_reloc.node.unwrap().? == ni);
9912 symbol_reloc.flushMovedNode(elf, node_vaddr);
9913 }
9914 },
9915 .const_debug_info => |cpi| {
9916 const dwarf_const = &elf.dwarf_consts.get(cpi).?;
9917 const target_section_offset = elf.computeNodeSectionOffset(ni);
9918 var target_ri = dwarf_const.debug_info_first_target_reloc;
9919 while (target_ri != .none) {
9920 const target_reloc = target_ri.get(elf);
9921 assert(target_reloc.target == ni);
9922 target_reloc.flushMovedTarget(elf, target_section_offset);
9923 target_ri = target_reloc.next;
9924 }
9925 elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{
9926 .first_symbol_reloc = dwarf_const.debug_info_first_symbol_reloc,
9927 .first_node_reloc = dwarf_const.debug_info_first_node_reloc,
9928 });
9929 },
9930 .global_debug_info => |gi| {
9931 const dwarf_global = &elf.dwarf_globals.items[@backingInt(gi)];
9932 const target_section_offset = elf.computeNodeSectionOffset(ni);
9933 var target_ri = dwarf_global.debug_info_first_target_reloc;
9934 while (target_ri != .none) {
9935 const target_reloc = target_ri.get(elf);
9936 assert(target_reloc.target == ni);
9937 target_reloc.flushMovedTarget(elf, target_section_offset);
9938 target_ri = target_reloc.next;
9939 }
9940 elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{
9941 .first_symbol_reloc = dwarf_global.debug_info_first_symbol_reloc,
9942 .first_node_reloc = dwarf_global.debug_info_first_node_reloc,
9943 });
9944 },
9945 .func_frame_fde => |fi| {
9946 const dwarf_func = &elf.dwarf_funcs.items[@backingInt(fi)];
9947 const zcu = elf.base.comp.zcu.?;
9948 const mod = zcu.navFileScope(fi.nav(&elf.dwarf)).mod.?;
9949 switch (mod.unwind_tables) {
9950 .none => {},
9951 .sync, .async => {
9952 const offset, _ = ni.location(&elf.mf).resolve(&elf.mf);
9953 elf.dwarf.updateEhFrameFde(ni.slice(&elf.mf), offset);
9954 },
9955 }
9956 elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{
9957 .first_symbol_reloc = dwarf_func.frame_fde_first_symbol_reloc,
9958 .first_node_reloc = dwarf_func.frame_fde_first_node_reloc,
9959 });
9960 },
9961 .func_debug_info => |fi| {
9962 const dwarf_func = &elf.dwarf_funcs.items[@backingInt(fi)];
9963 const target_section_offset = elf.computeNodeSectionOffset(ni);
9964 var target_ri = dwarf_func.debug_info_first_target_reloc;
9965 while (target_ri != .none) {
9966 const target_reloc = target_ri.get(elf);
9967 assert(target_reloc.target == ni);
9968 target_reloc.flushMovedTarget(elf, target_section_offset);
9969 target_ri = target_reloc.next;
9970 }
9971 elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{
9972 .first_symbol_reloc = dwarf_func.debug_info_first_symbol_reloc,
9973 .skip_symbol_relocs = if (elf.navs.getPtr(fi.nav(&elf.dwarf))) |nav|
9974 nav.lsi.index().ptr(elf).node
9975 else
9976 .none,
9977 .first_node_reloc = dwarf_func.debug_info_first_node_reloc,
9978 .skip_node_relocs = fi.get(&elf.dwarf).debug_line_ni,
9979 });
9980 },
9981 .func_debug_line => |fi| {
9982 const dwarf_func = &elf.dwarf_funcs.items[@backingInt(fi)];
9983 elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{
9984 .first_symbol_reloc = dwarf_func.debug_line_first_symbol_reloc,
9985 .first_node_reloc = dwarf_func.debug_line_first_node_reloc,
9986 .skip_node_relocs = fi.get(&elf.dwarf).debug_info_ni,
9987 });
9988 },
9989 .decl_debug_info => |di| {
9990 const dwarf_decl = &elf.dwarf_decls.get(di).?;
9991 const target_section_offset = elf.computeNodeSectionOffset(ni);
9992 var target_ri = dwarf_decl.debug_info_first_target_reloc;
9993 while (target_ri != .none) {
9994 const target_reloc = target_ri.get(elf);
9995 assert(target_reloc.target == ni);
9996 target_reloc.flushMovedTarget(elf, target_section_offset);
9997 target_ri = target_reloc.next;
9998 }
9999 elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{
10000 .first_node_reloc = dwarf_decl.debug_info_first_node_reloc,
10001 });
10002 },
10003 }
10004 try ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
10005}
10006
10007/// Given the index of a `PT_LOAD`/`PT_NULL` segment, assumes that the phdr's `offset` and `filesz`
10008/// have been updated as needed by the caller, and updates the `@"align"`, `vaddr`, `paddr`, and
10009/// `memsz` fields of the segment, in order to place it at a valid virtual address.
10010///
10011/// TODO: this function is currently a source of non-determinism in the linker, because handling the
10012/// moving or resizing of a segment could reorder them and thereby affect how we handle *future*
10013/// changes to segments.
10014fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Error!void {
10015 const segment_ni = elf.phdrs.items[orig_phndx].unwrap().?;
10016 assert(elf.getNode(segment_ni).segment == orig_phndx);
10017 const page_align = elf.targetPageAlign();
10018 const node_align = segment_ni.alignment(&elf.mf);
10019 const ph_align = page_align.max(node_align);
10020
10021 // If we determine that the segment's virtual address needs to move, then it's a good idea to
10022 // make it less likely that it needs to move *again* in the future, because it is expensive to
10023 // change a segment's load address (a lot of re-flushing is necessary). To do that, we reserve
10024 // more virtual address space than we need (multiplying the actual size by this value). That
10025 // way, there will usually be padding between segments which they can grow into.
10026 //
10027 // TODO: we might want to decrease this multiplier, or even omit it entirely, in cases where
10028 // virtual address space is constrained. For instance, 32-bit targets, or targets where short
10029 // PC-relative relocations between segments are common.
10030 const reserve_size_multiplier = 4;
10031
10032 switch (elf.phdrSlice()) {
10033 inline else => |phdr| {
10034 const offset = elf.targetLoad(&phdr[orig_phndx].offset);
10035 const size = elf.targetLoad(&phdr[orig_phndx].filesz);
10036
10037 if (size == 0) {
10038 assert(elf.targetLoad(&phdr[orig_phndx].type) == .NULL);
10039 } else {
10040 assert(elf.targetLoad(&phdr[orig_phndx].type) == .LOAD);
10041 }
10042
10043 elf.targetStore(&phdr[orig_phndx].memsz, size);
10044 elf.targetStore(&phdr[orig_phndx].@"align", @intCast(ph_align.toByteUnits()));
10045
10046 const orig_vaddr = elf.targetLoad(&phdr[orig_phndx].vaddr);
10047 assert(elf.targetLoad(&phdr[orig_phndx].paddr) == orig_vaddr);
10048
10049 var vaddr: u64 = orig_vaddr;
10050
10051 // First, we will shift the virtual address as needed in order to maintain the required
10052 // property that vaddr is congruent to offset modulo the phdr alignment.
10053 {
10054 // Compute the candidate address by undoing the current offset and then re-offsetting
10055 vaddr = std.mem.alignBackward(u64, vaddr, ph_align.toByteUnits()) + offset % ph_align.toByteUnits();
10056 // If `node_align` is greater than `page_align`, the address we just set might be in
10057 // the previous segment. The first page we "own" is the one in which the old vaddr
10058 // resides, so check against that.
10059 const first_good_vaddr = std.mem.alignBackward(u64, orig_vaddr, page_align.toByteUnits());
10060 if (vaddr < first_good_vaddr) {
10061 // Yep, we crossed into the previous segment's pages, so correct for that by
10062 // offsetting our address by another `ph_align`.
10063 vaddr += ph_align.toByteUnits();
10064 assert(vaddr >= first_good_vaddr);
10065 }
10066 }
10067
10068 // If our size has changed, or if the address shift above caused our "end" address to
10069 // cross a page boundary, then we might be overlapping with the next segment's pages. In
10070 // that case, we will jump past that segment and give ourselves a new address after it.
10071 // We'll need to repeat this for every loadable phdr after us, until we're no longer
10072 // overlapping anything.
10073 var phndx = orig_phndx;
10074 for (phdr[orig_phndx + 1 ..], orig_phndx + 1..) |*next_ph, next_phndx| {
10075 switch (elf.targetLoad(&next_ph.type)) {
10076 .NULL, .LOAD => {},
10077 else => {
10078 // All loadable segments have contiguous indices, so this indicates we have
10079 // become the last loadable segment, meaning we definitely don't overlap any
10080 // other loadable segment.
10081 break;
10082 },
10083 }
10084
10085 const next_vaddr = elf.targetLoad(&next_ph.vaddr);
10086 // Find the first virtual address which the next phdr "owns" by aligning its vaddr
10087 // backwards to the start of the page.
10088 const next_page_vaddr = std.mem.alignBackward(u64, next_vaddr, page_align.toByteUnits());
10089
10090 // Check if the segment fits here. We apply `reserve_size_multiplier`, but only if
10091 // the segment is already known to be moving---making it easier to grow in-place is
10092 // the whole point of the multiplier!
10093 {
10094 const target_size = if (vaddr == orig_vaddr) size else size * reserve_size_multiplier;
10095 if (vaddr + target_size <= next_page_vaddr) {
10096 break; // hooray, we fit here!
10097 }
10098 }
10099
10100 const next_ni = elf.phdrs.items[next_phndx].unwrap().?;
10101
10102 // This segment don't fit here, but before deciding how to proceed, we need to
10103 // consider any target-specific restrictions we are subject to.
10104 switch (elf.targetSegmentLoadAddressRestrictions()) {
10105 .none => {},
10106 .data_last => if (next_ni == elf.ni.data) {
10107 // We can't leapfrog over the data segment. Instead, that segment just needs
10108 // to be shifted forwards to make space for us, and we'll then `break` with
10109 // our current vaddr.
10110
10111 if (next_phndx + 1 < phdr.len) switch (elf.targetLoad(&phdr[next_phndx + 1].type)) {
10112 .NULL, .LOAD => unreachable, // data segment should be the last loadable segment
10113 else => {},
10114 };
10115
10116 const free_vaddr = vaddr + size * reserve_size_multiplier;
10117
10118 const next_align = page_align.max(next_ni.alignment(&elf.mf));
10119 const next_offset = elf.targetLoad(&next_ph.offset);
10120 const next_new_vaddr = next_align.forward(free_vaddr) + next_offset % next_align.toByteUnits();
10121
10122 // This logic for updating the data segment's vaddr is identical to how we
10123 // will update the vaddr of `phndx` when we break from the loop.
10124 elf.targetStore(&next_ph.vaddr, @intCast(next_new_vaddr));
10125 elf.targetStore(&next_ph.paddr, @intCast(next_new_vaddr));
10126 try next_ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
10127
10128 break;
10129 },
10130 }
10131
10132 // We don't fit here, so shift ourselves forward (i.e. swap with `next_phndx`). But
10133 // first we need to adjust `vaddr` to come after it.
10134 const next_size = elf.targetLoad(&next_ph.memsz);
10135 // Instead of putting ourselves right after `next_ph`, we'll go a bit later in the
10136 // address space so that `next_ph` has address space to grow into (like above).
10137 vaddr = ph_align.forward(@intCast(next_vaddr + next_size * 4)) + offset % ph_align.toByteUnits();
10138
10139 // Now just swap the phdrs and update our `phndx`.
10140 std.mem.swap(@TypeOf(next_ph.*), &phdr[phndx], next_ph);
10141 elf.phdrs.items[phndx] = .wrap(next_ni);
10142 elf.nodes.items(.data)[@backingInt(next_ni)] = .{ .segment = phndx };
10143 elf.phdrs.items[next_phndx] = .wrap(segment_ni);
10144 elf.nodes.items(.data)[@backingInt(segment_ni)] = .{ .segment = @intCast(next_phndx) };
10145 phndx = @intCast(next_phndx);
10146 }
10147
10148 if (vaddr != orig_vaddr) {
10149 elf.targetStore(&phdr[phndx].vaddr, @intCast(vaddr));
10150 elf.targetStore(&phdr[phndx].paddr, @intCast(vaddr));
10151 try segment_ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
10152 }
10153 },
10154 }
10155}
10156
10157fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void {
10158 const trace = tracy.trace(@src());
10159 defer trace.end();
10160
10161 _, const size = ni.location(&elf.mf).resolve(&elf.mf);
10162 switch (elf.getNode(ni)) {
10163 .deleted => unreachable,
10164 .archive, .archive_header => {},
10165 .archive_input_member => unreachable,
10166 .archive_elf_member_header => unreachable,
10167 .elf => if (elf.archive) |*archive| {
10168 const member_ar_hdr: *std.elf.ar_hdr = @ptrCast(
10169 archive.elf_member_header_ni.slice(&elf.mf),
10170 );
10171 if (std.mem.print(&member_ar_hdr.ar_size, "{d}", .{size})) |size_str| {
10172 @memset(member_ar_hdr.ar_size[size_str.len..], ' ');
10173 archive.elf_member_too_big = false;
10174 } else |err| switch (err) {
10175 error.NoSpaceLeft => archive.elf_member_too_big = true,
10176 }
10177 },
10178 .ehdr => unreachable,
10179 .shdr => {},
10180 .segment => |phndx| switch (elf.phdrSlice()) {
10181 inline else => |phdr| {
10182 assert(elf.phdrs.items[phndx].unwrap().? == ni);
10183 const ph = &phdr[phndx];
10184 elf.targetStore(&ph.filesz, @intCast(size));
10185 switch (elf.targetLoad(&ph.type)) {
10186 else => unreachable,
10187 .NULL, .LOAD => {
10188 elf.targetStore(&ph.type, if (size > 0) .LOAD else .NULL);
10189 try elf.allocateSegmentLoadAddress(phndx);
10190 },
10191 .DYNAMIC, .INTERP, .PHDR, .GNU_EH_FRAME, .GNU_RELRO => {
10192 elf.targetStore(&ph.memsz, @intCast(size));
10193 },
10194 .TLS => {
10195 elf.targetStore(&ph.memsz, @intCast(size));
10196 // TPOFF relocations care about the size of the TLS segment. Re-apply
10197 // those, and also update any GOT entries from GOTTPOFF relocations.
10198 for (elf.tls_size_symbol_relocs.keys()) |reloc| {
10199 reloc.get(elf).apply(elf);
10200 }
10201 for (elf.got.keys(), 0..) |got_key, got_index| {
10202 switch (got_key) {
10203 .reserved,
10204 .symbol,
10205 .tlsld0,
10206 .tlsld1,
10207 .tlsgd0,
10208 .tlsgd1,
10209 => {
10210 @branchHint(.likely);
10211 continue;
10212 },
10213
10214 .tpoff => elf.updateGotEntry(got_index),
10215 }
10216 }
10217 try ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
10218 },
10219 }
10220 },
10221 },
10222 .section => |shndx| switch (elf.shdrPtr(shndx)) {
10223 inline else => |shdr| {
10224 switch (elf.targetLoad(&shdr.type)) {
10225 else => unreachable,
10226 .NULL => if (size > 0) elf.targetStore(&shdr.type, .PROGBITS),
10227 .PROGBITS => if (size == 0) elf.targetStore(&shdr.type, .NULL),
10228 .X86_64_UNWIND => {},
10229 }
10230 elf.targetStore(&shdr.size, @intCast(size));
10231 },
10232 },
10233 .section_manual_size,
10234 .input_section,
10235 .copied_global,
10236 .nav,
10237 .uav,
10238 .lazy_code,
10239 .lazy_const_data,
10240 .debug_shared,
10241 .eh_frame_footer,
10242 .unit_padding,
10243 .unit_frame,
10244 .unit_frame_cie,
10245 .unit_debug_info,
10246 .unit_debug_info_header,
10247 .unit_debug_info_footer,
10248 .unit_debug_line,
10249 .unit_debug_line_header,
10250 .unit_debug_rnglists,
10251 .const_debug_info,
10252 .global_debug_info,
10253 .func_frame_fde,
10254 .func_debug_info,
10255 .func_debug_line,
10256 .decl_debug_info,
10257 => {},
10258 }
10259}
10260
10261fn flushPadding(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void {
10262 const trace = tracy.trace(@src());
10263 defer trace.end();
10264
10265 switch (elf.getNode(ni)) {
10266 .deleted => unreachable,
10267 .archive,
10268 .archive_input_member,
10269 .archive_elf_member_header,
10270 .elf,
10271 .ehdr,
10272 .shdr,
10273 .segment,
10274 .section,
10275 .section_manual_size,
10276 .input_section,
10277 .copied_global,
10278 .nav,
10279 .uav,
10280 .lazy_code,
10281 .lazy_const_data,
10282 .debug_shared,
10283 .eh_frame_footer,
10284 .unit_debug_info_footer,
10285 => {},
10286
10287 .archive_header => {
10288 const archive = &elf.archive.?;
10289
10290 // Because we can't just throw padding bytes in the middle of an archive file, we need
10291 // the member name string table (the "//" member) to absorb all the padding bytes
10292 // between it (in the `.archive_header` node) and the first actual member.
10293 const next_member_ni = ni.next(&elf.mf).unwrap() orelse {
10294 // I guess there are no link inputs yet? But there will be eventually!
10295 return;
10296 };
10297 const next_member_offset: u64, _ = next_member_ni.location(&elf.mf).resolve(&elf.mf);
10298 const strtab_member_offset = std.elf.ARMAG.len + @sizeOf(std.elf.ar_hdr);
10299 assert(Alignment.@"2".check(next_member_offset));
10300 assert(Alignment.@"2".check(strtab_member_offset));
10301 const strtab_size = next_member_offset - strtab_member_offset;
10302
10303 const member_ar_hdr: *std.elf.ar_hdr = @ptrCast(
10304 archive.header_ni.slice(&elf.mf)[std.elf.ARMAG.len..][0..@sizeOf(std.elf.ar_hdr)],
10305 );
10306 if (std.mem.print(&member_ar_hdr.ar_size, "{d}", .{strtab_size})) |size_str| {
10307 @memset(member_ar_hdr.ar_size[size_str.len..], ' ');
10308 archive.strtab_member_too_big = false;
10309 } else |err| switch (err) {
10310 error.NoSpaceLeft => archive.strtab_member_too_big = true,
10311 }
10312 },
10313 .unit_padding,
10314 .unit_frame_cie,
10315 .unit_debug_info_header,
10316 .unit_debug_line_header,
10317 .unit_debug_rnglists,
10318 .const_debug_info,
10319 .global_debug_info,
10320 .func_frame_fde,
10321 .func_debug_info,
10322 .func_debug_line,
10323 .decl_debug_info,
10324 => |_, tag| {
10325 const offset, const size = ni.location(&elf.mf).resolve(&elf.mf);
10326 const parent_ni = ni.parent(&elf.mf).unwrap().?;
10327 const slice = slice: {
10328 if (ni.next(&elf.mf).unwrap()) |next_ni| switch (next_ni.position(&elf.mf)) {
10329 .header => unreachable,
10330 .footer => {},
10331 .floating => {
10332 const parent_slice = parent_ni.slicePadding(&elf.mf);
10333 const next_offset, _ = next_ni.location(&elf.mf).resolve(&elf.mf);
10334 break :slice parent_slice[@intCast(offset)..@intCast(next_offset)];
10335 },
10336 };
10337 switch (tag) {
10338 else => unreachable,
10339 .unit_padding, .unit_debug_rnglists => {
10340 const parent_slice = parent_ni.slicePadding(&elf.mf);
10341 const frame_shndx = elf.getNodeShndx(parent_ni);
10342 const frame_format = frame_shndx.debugFrameFormat(elf) orelse
10343 break :slice parent_slice[@intCast(offset)..];
10344 const footer_size = elf.debugFrameFooterSize(frame_format);
10345 @memset(parent_slice[@intCast(offset + size)..][0..footer_size], 0);
10346 frame_shndx.setSize(elf, offset + size + footer_size);
10347 break :slice parent_slice[@intCast(offset)..][0..@intCast(size)];
10348 },
10349 .unit_frame_cie, .func_frame_fde => {
10350 const parent_offset, _ = parent_ni.location(&elf.mf).resolve(&elf.mf);
10351 const frame_ni = parent_ni.parent(&elf.mf).unwrap().?;
10352 const frame_slice = frame_ni.slicePadding(&elf.mf);
10353 const frame_shndx = elf.getNode(frame_ni).section_manual_size;
10354 const frame_format = frame_shndx.debugFrameFormat(elf).?;
10355 if (parent_ni.next(&elf.mf).unwrap()) |parent_next_ni| {
10356 switch (parent_next_ni.position(&elf.mf)) {
10357 .header => unreachable,
10358 .footer => {},
10359 .floating => {
10360 const parent_next_offset, _ =
10361 parent_next_ni.location(&elf.mf).resolve(&elf.mf);
10362 const slice = frame_slice[@intCast(
10363 parent_offset + offset,
10364 )..@intCast(parent_next_offset)];
10365 var fw: Io.Writer = .fixed(slice[@intCast(size)..]);
10366 elf.dwarf.genDebugFrameCie(
10367 &fw,
10368 null,
10369 frame_format,
10370 ) catch |err| switch (err) {
10371 error.WriteFailed => break :slice slice,
10372 };
10373 elf.dwarf.updateUnitLength(fw.buffer, fw.buffer.len);
10374 break :slice slice[0..@intCast(size)];
10375 },
10376 }
10377 }
10378 const footer_size = elf.debugFrameFooterSize(frame_format);
10379 @memset(
10380 frame_slice[@intCast(parent_offset + offset + size)..][0..footer_size],
10381 0,
10382 );
10383 frame_shndx.setSize(elf, parent_offset + offset + size + footer_size);
10384 break :slice frame_slice[@intCast(parent_offset + offset)..][0..@intCast(size)];
10385 },
10386 .unit_debug_info_header,
10387 .unit_debug_line_header,
10388 .const_debug_info,
10389 .global_debug_info,
10390 .func_debug_info,
10391 .func_debug_line,
10392 .decl_debug_info,
10393 => {
10394 const parent_offset, _ = parent_ni.location(&elf.mf).resolve(&elf.mf);
10395 const debug_ni = parent_ni.parent(&elf.mf).unwrap().?;
10396 const debug_slice = debug_ni.slicePadding(&elf.mf);
10397 var fw: Io.Writer = .fixed(buffer: {
10398 if (parent_ni.next(&elf.mf).unwrap()) |parent_next_ni| {
10399 switch (parent_next_ni.position(&elf.mf)) {
10400 .header => unreachable,
10401 .footer => {},
10402 .floating => {
10403 const parent_next_offset, _ =
10404 parent_next_ni.location(&elf.mf).resolve(&elf.mf);
10405 break :buffer debug_slice[@intCast(
10406 parent_offset,
10407 )..@intCast(parent_next_offset)];
10408 },
10409 }
10410 }
10411 break :buffer debug_slice[@intCast(parent_offset)..];
10412 });
10413 fw.end = @intCast(offset + size);
10414 switch (tag) {
10415 else => unreachable,
10416 .unit_debug_info_header,
10417 .const_debug_info,
10418 .global_debug_info,
10419 .func_debug_info,
10420 .decl_debug_info,
10421 => for (0..2) |_| fw.writeUleb128(@backingInt(Dwarf.AbbrevCode.null)) catch
10422 unreachable,
10423 .unit_debug_line_header, .func_debug_line => {},
10424 }
10425 const unit_padding_offset = fw.end;
10426 const unit_padding = fw.unusedCapacitySlice();
10427 elf.dwarf.genUnitPadding(&fw) catch |err| switch (err) {
10428 error.WriteFailed => {
10429 fw.end = unit_padding_offset;
10430 elf.dwarf.updateUnitLength(fw.buffer, fw.buffer.len);
10431 switch (tag) {
10432 else => unreachable,
10433 .unit_debug_info_header,
10434 .const_debug_info,
10435 .global_debug_info,
10436 .func_debug_info,
10437 .decl_debug_info,
10438 => {
10439 comptime assert(
10440 Dwarf.uleb128Size(@backingInt(Dwarf.AbbrevCode.null)) == 1,
10441 );
10442 @memset(
10443 fw.unusedCapacitySlice(),
10444 @backingInt(Dwarf.AbbrevCode.null),
10445 );
10446 },
10447 .unit_debug_line_header,
10448 .func_debug_line,
10449 => Dwarf.genDebugLinePadding(&fw, fw.unusedCapacityLen()) catch
10450 unreachable,
10451 }
10452 return;
10453 },
10454 };
10455 elf.dwarf.updateUnitLength(fw.buffer, unit_padding_offset);
10456 elf.dwarf.updateUnitLength(unit_padding, unit_padding.len);
10457 return;
10458 },
10459 }
10460 };
10461 var fw: Io.Writer = .fixed(slice[@intCast(size)..]);
10462 switch (tag) {
10463 else => unreachable,
10464 .unit_padding => elf.dwarf.updateUnitLength(slice, slice.len),
10465 .unit_frame_cie, .func_frame_fde => {
10466 elf.dwarf.updateUnitLength(slice, slice.len);
10467 @memset(fw.buffer, std.dwarf.CFA.nop);
10468 },
10469 .unit_debug_info_header,
10470 .const_debug_info,
10471 .global_debug_info,
10472 .func_debug_info,
10473 .decl_debug_info,
10474 => elf.dwarf.genDebugInfoPadding(&fw, fw.buffer.len) catch unreachable,
10475 .unit_debug_line_header,
10476 .func_debug_line,
10477 => Dwarf.genDebugLinePadding(&fw, fw.buffer.len) catch unreachable,
10478 .unit_debug_rnglists => {
10479 elf.dwarf.genUnitPadding(&fw) catch |err| switch (err) {
10480 error.WriteFailed => {
10481 elf.dwarf.updateUnitLength(slice, slice.len);
10482 @memset(fw.buffer, std.dwarf.RLE.end_of_list);
10483 return;
10484 },
10485 };
10486 elf.dwarf.updateUnitLength(slice, size);
10487 elf.dwarf.updateUnitLength(fw.buffer, fw.buffer.len);
10488 },
10489 }
10490 },
10491 .unit_frame, .unit_debug_info, .unit_debug_line => {
10492 var last_ni = ni.last(&elf.mf).unwrap() orelse return;
10493 while (last_ni.position(&elf.mf) == .footer)
10494 last_ni = last_ni.prev(&elf.mf).unwrap() orelse return;
10495 try last_ni.nextMoved(elf.base.comp.gpa, &elf.mf);
10496 },
10497 }
10498}
10499
10500fn addPltEntry(elf: *Elf, global_name: String(.strtab), dynsym_index: u32) void {
10501 const target_endian = elf.targetEndian();
10502
10503 // We use the existing free-list tracking of the `.rela.plt` section to also behave as a
10504 // free-list for the PLT itself---see `pltEntryIsDead` for details.
10505 const plt_index: u32 = @backingInt(elf.shndx.rela_plt.relaAddOneAssumeCapacity(elf, .{
10506 .type = .jumpSlot(elf),
10507 .offset = 0, // populated later
10508 .raw_sym_index = dynsym_index,
10509 .addend = 0,
10510 }));
10511
10512 // On architectures without `.got.plt` (e.g. SPARC) these values actually refer to `.plt`.
10513 const got_plt_section: Section.Index, const got_plt_offset: u64 = got_plt: {
10514 const plt = elf.targetPltInfo();
10515 break :got_plt if (plt.got_plt) |got_plt| .{
10516 elf.shndx.got_plt,
10517 elf.targetPtrSize() * (got_plt.header_entries + plt_index),
10518 } else .{
10519 elf.shndx.plt,
10520 plt.entry_size * (plt.header_entries + plt_index),
10521 };
10522 };
10523
10524 // Now that we know the index, we can set the relocation's offset.
10525 elf.shndx.rela_plt.relaSetOffset(elf, @fromBackingInt(plt_index), got_plt_section.vaddr(elf) + got_plt_offset);
10526
10527 if (plt_index < elf.plt.count()) {
10528 // We reused a free entry, so we're already done!
10529 elf.plt.setKey(plt_index, global_name);
10530 return;
10531 }
10532
10533 // We added a new entry, so we now need to extend the PLT sections.
10534 assert(plt_index == elf.plt.count());
10535 elf.plt.putAssumeCapacityNoClobber(global_name, {});
10536
10537 switch (elf.ehdrMachine()) {
10538 .AARCH64, .PPC64, .RISCV => |machine| @panic(@tagName(machine)),
10539 .X86_64 => {
10540 const plt_ni = elf.shndx.plt.get(elf).ni;
10541 const plt_addr = plt_addr: switch (elf.shdrPtr(elf.shndx.plt)) {
10542 inline else => |shdr| {
10543 const old_size = 16 * (1 + plt_index);
10544 assert(elf.targetLoad(&shdr.size) == old_size);
10545 elf.targetStore(&shdr.size, old_size + 16);
10546 const plt_slice = plt_ni.slice(&elf.mf)[old_size..][0..16];
10547 @memcpy(plt_slice, &[16]u8{
10548 0xf3, 0x0f, 0x1e, 0xfa, // endbr64
10549 0x68, 0x00, 0x00, 0x00, 0x00, // push $0x0
10550 0xe9, 0x00, 0x00, 0x00, 0x00, // jmp 0
10551 0x66, 0x90, // xchg %ax,%ax
10552 });
10553 std.mem.writeInt(u32, plt_slice[5..][0..4], plt_index, target_endian);
10554 std.mem.writeInt(
10555 i32,
10556 plt_slice[10..][0..4],
10557 -@as(i32, @intCast(old_size + 14)),
10558 target_endian,
10559 );
10560 break :plt_addr elf.targetLoad(&shdr.addr) + old_size;
10561 },
10562 };
10563
10564 const got_plt_ni = elf.shndx.got_plt.get(elf).ni;
10565 switch (elf.shdrPtr(elf.shndx.got_plt)) {
10566 inline else => |shdr, class| {
10567 assert(elf.targetLoad(&shdr.size) == got_plt_offset);
10568 elf.targetStore(&shdr.size, @intCast(got_plt_offset + @sizeOf(class.ElfN().Addr)));
10569 std.mem.writeInt(
10570 class.ElfN().Addr,
10571 got_plt_ni.slice(&elf.mf)[@intCast(got_plt_offset)..][0..@sizeOf(class.ElfN().Addr)],
10572 @intCast(plt_addr),
10573 target_endian,
10574 );
10575 },
10576 }
10577
10578 const plt_sec_ni = elf.shndx.plt_sec.get(elf).ni;
10579 switch (elf.shdrPtr(elf.shndx.plt_sec)) {
10580 inline else => |shdr| {
10581 const old_size = 16 * plt_index;
10582 elf.targetStore(&shdr.size, old_size + 16);
10583 const plt_sec_slice = plt_sec_ni.slice(&elf.mf)[old_size..][0..16];
10584 @memcpy(plt_sec_slice, &[16]u8{
10585 0xf3, 0x0f, 0x1e, 0xfa, // endbr64
10586 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp *0x0(%rip)
10587 0x66, 0x0f, 0x1f, 0x44, 0x00, 0x00, // nopw 0x0(%rax,%rax,1)
10588 });
10589 std.mem.writeInt(
10590 i32,
10591 plt_sec_slice[6..][0..4],
10592 @intCast(@as(i64, @bitCast(
10593 (got_plt_section.vaddr(elf) + got_plt_offset) -% (elf.targetLoad(&shdr.addr) + old_size + 10),
10594 ))),
10595 target_endian,
10596 );
10597 },
10598 }
10599 },
10600 .LOONGARCH => {
10601 // add a .PLT entry, writing the template
10602 const plt_ni = elf.shndx.plt.get(elf).ni;
10603 const plt_addr, const plt_slice = plt_entry: switch (elf.shdrPtr(elf.shndx.plt)) {
10604 inline else => |shdr| {
10605 const old_size = 16 * (1 + plt_index);
10606 assert(elf.targetLoad(&shdr.size) == old_size);
10607 elf.targetStore(&shdr.size, old_size + 16);
10608 const plt_slice = plt_ni.slice(&elf.mf)[old_size..][0..16];
10609 @memcpy(plt_slice, source: switch (elf.identClass()) {
10610 .NONE, _ => unreachable,
10611 inline .@"32", .@"64" => |elf_class| {
10612 const ld_byte = if (elf_class == .@"64") 0xc0 else 0x80;
10613 break :source &[16]u8{
10614 0x1a, 0x00, 0x00, 0x0f, // pcalau12i $t3, %pc_hi20(func@.got.plt)
10615 0x28, ld_byte, 0x01, 0xef, // ld.w/d $t3, $t3, %lo12(func@.got.plt)
10616 0x4c, 0x00, 0x01, 0xed, // jirl $t1, $t3, 0
10617 0x00, 0x2a, 0x00, 0x00, // break
10618 };
10619 },
10620 });
10621 break :plt_entry .{ elf.targetLoad(&shdr.addr) + old_size, plt_slice };
10622 },
10623 };
10624
10625 // add a .GOT.PLT entry, writing the address of the corresponding .PLT entry
10626 const got_plt_ni = elf.shndx.got_plt.get(elf).ni;
10627 switch (elf.shdrPtr(elf.shndx.got_plt)) {
10628 inline else => |shdr, class| {
10629 assert(elf.targetLoad(&shdr.size) == got_plt_offset);
10630 elf.targetStore(&shdr.size, @intCast(got_plt_offset + @sizeOf(class.ElfN().Addr)));
10631 std.mem.writeInt(
10632 class.ElfN().Addr,
10633 got_plt_ni.slice(&elf.mf)[@intCast(got_plt_offset)..][0..@sizeOf(class.ElfN().Addr)],
10634 @intCast(plt_addr),
10635 target_endian,
10636 );
10637 },
10638 }
10639
10640 // relocate the PLT entry to point to the .GOT.PLT entry
10641 const got_plt_abs = got_plt_section.vaddr(elf) + got_plt_offset;
10642 // TODO: handle overflow gracefully
10643 const inst0: *align(1) link.loongarch.J20 = @ptrCast(plt_slice[0..4]);
10644 const inst1: *align(1) link.loongarch.K12 = @ptrCast(plt_slice[4..8]);
10645 elf.targetStore(inst0, .{
10646 .b0_4 = elf.targetLoad(inst0).b0_4,
10647 .j20 = link.loongarch.pcalaHi20(got_plt_abs, plt_addr),
10648 .b25_31 = elf.targetLoad(inst0).b25_31,
10649 });
10650 elf.targetStore(inst1, .{
10651 .b0_9 = elf.targetLoad(inst1).b0_9,
10652 .k12 = @truncate(got_plt_abs),
10653 .b22_31 = elf.targetLoad(inst1).b22_31,
10654 });
10655 },
10656 .SPARCV9 => {
10657 // add a .PLT entry, writing the template
10658 const plt_ni = elf.shndx.plt.get(elf).ni;
10659 switch (elf.shdrPtr(elf.shndx.plt)) {
10660 inline else => |shdr| {
10661 assert(elf.targetLoad(&shdr.size) == got_plt_offset);
10662 elf.targetStore(&shdr.size, @intCast(got_plt_offset + 32));
10663 const Inst = packed union(u32) {
10664 raw: u32,
10665 imm22: packed struct { imm: u22, op: u10 },
10666 disp19: packed struct { disp: u19, op: u13 },
10667 };
10668 const plt_slice: []Inst = @ptrCast(@alignCast(plt_ni.slice(&elf.mf)[@intCast(got_plt_offset)..][0..32]));
10669 @memcpy(plt_slice, &[8]Inst{
10670 // sethi (. - .plt[0]), %g1
10671 .{ .imm22 = .{ .imm = @truncate(got_plt_offset), .op = 0b0000001100 } },
10672 // ba,a %xcc, .plt[1]
10673 .{ .disp19 = .{ .disp = @truncate((got_plt_offset + 4 - 32) >> 2), .op = 0b0011000001101 } },
10674 // nop
10675 .{ .raw = 0x0100_0000 },
10676 // nop
10677 .{ .raw = 0x0100_0000 },
10678 // nop
10679 .{ .raw = 0x0100_0000 },
10680 // nop
10681 .{ .raw = 0x0100_0000 },
10682 // nop
10683 .{ .raw = 0x0100_0000 },
10684 // nop
10685 .{ .raw = 0x0100_0000 },
10686 });
10687 if (elf.targetEndian() != std.lang.Endian.native) {
10688 std.mem.byteSwapAllElements(Inst, plt_slice);
10689 }
10690 },
10691 }
10692 },
10693 }
10694}
10695fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_addr: u64, addr: u64) void {
10696 const target_endian = elf.targetEndian();
10697 switch (elf.ehdrMachine()) {
10698 .AARCH64, .PPC64, .RISCV => |machine| @panic(@tagName(machine)),
10699 .X86_64 => {
10700 switch (which) {
10701 .plt => return,
10702 .plt_sec => {
10703 // Re-apply all PLT relocations. If a symbol is in the PLT then the majority of
10704 // its relocations are probably going through the PLT, so we don't bother with
10705 // specific tracking for PLT relocations---instead just re-apply all relocations
10706 // targeting symbols with PLT entries.
10707 for (elf.plt.keys()) |name| {
10708 Symbol.Id.global(name).applyTargetRelocs(elf);
10709 }
10710 // We also need to update all of the references from `.plt.sec` to `.got.plt`.
10711 // However, if there's also a flush pending for `.got.plt`, don't bother doing
10712 // this now, because we'll do it when `.got.plt` is flushed anyway.
10713 if (elf.shndx.got_plt.get(elf).ni.hasMoved(&elf.mf)) {
10714 return;
10715 }
10716 // Exit this `switch` to update those references.
10717 },
10718 .got_plt => {
10719 // Update the offsets of the relocation entries in `.rela.plt`.
10720 const rela_plt_shndx = elf.shndx.rela_plt;
10721 for (0..elf.plt.count()) |plt_index| {
10722 if (elf.pltEntryIsDead(plt_index)) continue;
10723 rela_plt_shndx.relaAdjustOffset(elf, @fromBackingInt(@intCast(plt_index)), old_addr, addr);
10724 }
10725 // We also need to update all of the references from `.plt.sec` to `.got.plt`.
10726 // However, if there's also a flush pending for `.plt.sec`, don't bother doing
10727 // this now, because we'll do it when `.plt.sec` is flushed anyway.
10728 if (elf.shndx.plt_sec.get(elf).ni.hasMoved(&elf.mf)) {
10729 return;
10730 }
10731 // Exit this `switch` to update those references.
10732 },
10733 }
10734 // We are updating the references from `.plt.sec` to `.got.plt`.
10735 const got_plt_addr = elf.shndx.got_plt.vaddr(elf);
10736 const plt_sec_addr = elf.shndx.plt_sec.vaddr(elf);
10737 const plt_sec_slice = elf.shndx.plt_sec.get(elf).ni.slice(&elf.mf);
10738 switch (elf.identClass()) {
10739 .NONE, _ => unreachable,
10740 inline else => |class| {
10741 const Addr = class.ElfN().Addr;
10742 for (0..elf.plt.count()) |plt_index| {
10743 const plt_sec_offset = 16 * plt_index;
10744 const got_plt_offset = @sizeOf(Addr) * (3 + plt_index);
10745 std.mem.writeInt(
10746 i32,
10747 plt_sec_slice[plt_sec_offset + 6 ..][0..4],
10748 @intCast(@as(i64, @bitCast(
10749 (got_plt_addr + got_plt_offset) -% (plt_sec_addr + plt_sec_offset + 10),
10750 ))),
10751 target_endian,
10752 );
10753 }
10754 },
10755 }
10756 },
10757 .LOONGARCH => {
10758 switch (which) {
10759 .plt => {
10760 // Re-apply all PLT relocations. If a symbol is in the PLT then the majority of
10761 // its relocations are probably going through the PLT, so we don't bother with
10762 // specific tracking for PLT relocations---instead just re-apply all relocations
10763 // targeting symbols with PLT entries.
10764 for (elf.plt.keys()) |name| {
10765 Symbol.Id.global(name).applyTargetRelocs(elf);
10766 }
10767 // We also need to update all of the references from `.plt` to `.got.plt`.
10768 // However, if there's also a flush pending for `.got.plt`, don't bother doing
10769 // this now, because we'll do it when `.got.plt` is flushed anyway.
10770 if (elf.shndx.got_plt.get(elf).ni.hasMoved(&elf.mf)) {
10771 return;
10772 }
10773 // Exit this `switch` to update those references.
10774 },
10775 .plt_sec => unreachable,
10776 .got_plt => {
10777 // Update the offsets of the relocation entries in `.rela.plt`.
10778 const rela_plt_shndx = elf.shndx.rela_plt;
10779 for (0..elf.plt.count()) |plt_index| {
10780 if (elf.pltEntryIsDead(plt_index)) continue;
10781 rela_plt_shndx.relaAdjustOffset(elf, @fromBackingInt(@intCast(plt_index)), old_addr, addr);
10782 }
10783 // We also need to update all of the references from `.plt` to `.got.plt`.
10784 // However, if there's also a flush pending for `.plt`, don't bother doing
10785 // this now, because we'll do it when `.plt` is flushed anyway.
10786 if (elf.shndx.plt.get(elf).ni.hasMoved(&elf.mf)) {
10787 return;
10788 }
10789 // Exit this `switch` to update those references.
10790 },
10791 }
10792 // We are updating the references from `.plt` to `.got.plt`.
10793 const got_plt_addr = elf.shndx.got_plt.vaddr(elf);
10794 const plt_addr = elf.shndx.plt.vaddr(elf);
10795 const plt_slice = elf.shndx.plt.get(elf).ni.slice(&elf.mf);
10796 switch (elf.identClass()) {
10797 .NONE, _ => unreachable,
10798 inline else => |class| {
10799 const Addr = class.ElfN().Addr;
10800 for (0..elf.plt.count()) |plt_index| {
10801 const plt_offset = 16 * plt_index;
10802 const got_plt_offset = @sizeOf(Addr) * (2 + plt_index);
10803 const target_slice = plt_slice[plt_offset..];
10804
10805 const got_plt_abs: u64 = got_plt_addr + got_plt_offset;
10806 // TODO: handle overflow gracefully
10807 const inst0: *align(1) link.loongarch.J20 = @ptrCast(target_slice[0..4]);
10808 const inst1: *align(1) link.loongarch.K12 = @ptrCast(target_slice[4..8]);
10809
10810 elf.targetStore(inst0, .{
10811 .b0_4 = elf.targetLoad(inst0).b0_4,
10812 .j20 = link.loongarch.pcalaHi20(got_plt_abs, plt_addr + plt_offset),
10813 .b25_31 = elf.targetLoad(inst0).b25_31,
10814 });
10815
10816 elf.targetStore(inst1, .{
10817 .b0_9 = elf.targetLoad(inst1).b0_9,
10818 .k12 = @truncate(got_plt_abs),
10819 .b22_31 = elf.targetLoad(inst1).b22_31,
10820 });
10821 }
10822 },
10823 }
10824 },
10825 .SPARCV9 => switch (which) {
10826 .plt => {
10827 // Re-apply all PLT relocations. If a symbol is in the PLT then the majority of
10828 // its relocations are probably going through the PLT, so we don't bother with
10829 // specific tracking for PLT relocations---instead just re-apply all relocations
10830 // targeting symbols with PLT entries.
10831 for (elf.plt.keys()) |name| {
10832 Symbol.Id.global(name).applyTargetRelocs(elf);
10833 }
10834 // Update the offsets of the relocation entries in `.rela.plt`.
10835 const rela_plt_shndx = elf.shndx.rela_plt;
10836 for (0..elf.plt.count()) |plt_index| {
10837 if (elf.pltEntryIsDead(plt_index)) continue;
10838 rela_plt_shndx.relaAdjustOffset(elf, @fromBackingInt(@intCast(plt_index)), old_addr, addr);
10839 }
10840 },
10841 .plt_sec, .got_plt => unreachable,
10842 },
10843 }
10844}
10845
10846pub fn updateExports(
10847 elf: *Elf,
10848 pt: Zcu.PerThread,
10849 export_indices: []const Zcu.Export.Index,
10850) link.Error!void {
10851 for (export_indices) |export_index| {
10852 elf.updateExportInner(pt, export_index) catch |err| switch (err) {
10853 else => |e| return e,
10854 error.MappedFileIo => return elf.base.comp.link_diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
10855 };
10856 }
10857}
10858fn updateExportInner(
10859 elf: *Elf,
10860 pt: Zcu.PerThread,
10861 export_index: Zcu.Export.Index,
10862) Error!void {
10863 const zcu = pt.zcu;
10864 const ip = &zcu.intern_pool;
10865
10866 const @"export" = export_index.ptr(zcu);
10867
10868 switch (@"export".exported) {
10869 .nav => |nav| log.debug("updateExports({f})", .{ip.getNav(nav).fqn.fmt(ip)}),
10870 .uav => |uav| log.debug("updateExports(@as({f}, {f}))", .{
10871 Type.fromInterned(ip.typeOf(uav)).fmt(pt),
10872 Value.fromInterned(uav).fmtValue(pt),
10873 }),
10874 }
10875 try elf.ensureUnusedSymbolCapacity(1, .maybe_global);
10876 const exported_lsi: Symbol.LocalIndex = switch (@"export".exported) {
10877 .nav => |nav| (try elf.navMapIndex(zcu, nav)).symbol(elf),
10878 .uav => |uav| (try elf.uavMapIndex(uav, .none)).symbol(elf),
10879 };
10880
10881 // Initialize the global symbol with the same values that the local one currently has. If the
10882 // NAV/UAV is updated, then `updateNavInner` or `genUav` will update the global symbol sizes,
10883 // and `flushMoved` will update their values.
10884 const cur_value: u64, const cur_size: u64, const @"type": std.elf.STT, const shndx: Section.Index = switch (elf.symPtr(exported_lsi.index())) {
10885 inline else => |exported_sym| .{
10886 elf.targetLoad(&exported_sym.value),
10887 elf.targetLoad(&exported_sym.size),
10888 elf.targetLoad(&exported_sym.info).type,
10889 .fromSection(elf.targetLoad(&exported_sym.shndx)),
10890 },
10891 };
10892
10893 const name = @"export".opts.name.toSlice(ip);
10894 _ = elf.addGlobalSymbolAssumeCapacity(.{
10895 .node = exported_lsi.index().ptr(elf).node,
10896 .name = try .string(elf, name),
10897 .value = cur_value,
10898 .size = cur_size,
10899 .type = @"type",
10900 .bind = switch (@"export".opts.linkage) {
10901 .strong => .strong,
10902 .weak => .weak,
10903 .internal => return elf.base.comp.link_diags.fail("TODO(Elf2): '.internal' linkage", .{}),
10904 .link_once => return elf.base.comp.link_diags.fail("TODO(Elf2): '.link_once' linkage", .{}),
10905 },
10906 .visibility = switch (@"export".opts.visibility) {
10907 .default => .DEFAULT,
10908 .hidden => .HIDDEN,
10909 .protected => .PROTECTED,
10910 },
10911 .shndx = shndx,
10912 }) catch |err| switch (err) {
10913 error.MultipleDefinitions => {
10914 // HACK: because we currently don't/can't delete these exports, we would typically
10915 // get these errors on every non-initial incremental update. Hack around that by
10916 // only emitting this error if the symbol we're conflicting with comes from an input
10917 // section (as opposed to the ZCU).
10918 const conflicting_global = elf.globalByName(try elf.string(.strtab, name)).?;
10919 if (conflicting_global.symtab_index.ptr(elf).node.unwrap()) |conflicting_node| {
10920 if (elf.getNode(conflicting_node) == .input_section) {
10921 return elf.base.comp.link_diags.fail(
10922 "multiple definitions of '{s}'",
10923 .{name},
10924 );
10925 }
10926 }
10927 },
10928 };
10929}
10930
10931fn dumpStderr(elf: *Elf, tid: Zcu.PerThread.Id) Io.File.Writer.Error!void {
10932 const comp = elf.base.comp;
10933 const io = comp.io;
10934 var buffer: [512]u8 = undefined;
10935 const stderr = try io.lockStderr(&buffer, null);
10936 defer io.unlockStderr();
10937 const w = &stderr.file_writer.interface;
10938 _ = elf.dump(w, tid) catch |err| switch (err) {
10939 error.WriteFailed => return stderr.file_writer.err.?,
10940 };
10941}
10942
10943pub fn dump(elf: *Elf, w: *Io.Writer, tid: Zcu.PerThread.Id) Io.Writer.Error!link.File.DumpResult {
10944 if (elf.options.enable_link_snapshots) {
10945 try elf.printNode(tid, w, .root, 0);
10946 return .enabled;
10947 }
10948 return .disabled;
10949}
10950
10951pub fn printNode(
10952 elf: *Elf,
10953 tid: Zcu.PerThread.Id,
10954 w: *Io.Writer,
10955 ni: MappedFile.Node.Index,
10956 indent: usize,
10957) Io.Writer.Error!void {
10958 const node = elf.getNode(ni);
10959 try w.splatByteAll(' ', indent);
10960 try w.writeAll(@tagName(node));
10961 switch (node) {
10962 else => {},
10963 .segment => |phndx| switch (elf.phdrSlice()) {
10964 inline else => |phdr| {
10965 const ph = &phdr[phndx];
10966 try w.writeByte('(');
10967 const pt = elf.targetLoad(&ph.type);
10968 if (std.enums.tagName(std.elf.PT, pt)) |pt_name|
10969 try w.writeAll(pt_name)
10970 else inline for (@typeInfo(std.elf.PT).@"enum".decl_names) |decl_name| {
10971 const decl_val = @field(std.elf.PT, decl_name);
10972 if (@TypeOf(decl_val) != std.elf.PT) continue;
10973 if (pt == @field(std.elf.PT, decl_name)) break try w.writeAll(decl_name);
10974 } else try w.print("0x{x}", .{pt});
10975 try w.writeAll(", ");
10976 const pf = elf.targetLoad(&ph.flags);
10977 if (pf.R) try w.writeByte('R');
10978 if (pf.W) try w.writeByte('W');
10979 if (pf.X) try w.writeByte('X');
10980 try w.writeByte(')');
10981 },
10982 },
10983 .section, .section_manual_size => |shndx| try w.print("({s})", .{shndx.name(elf).slice(elf)}),
10984 .input_section => |isi| {
10985 const ii = isi.input(elf);
10986 try w.print("({f}{f}, {s})", .{
10987 ii.path(elf).fmtEscapeString(),
10988 fmtMemberString(ii.member(elf)),
10989 elf.getNodeShndx(isi.node(elf)).name(elf).slice(elf),
10990 });
10991 },
10992 .copied_global => |name| try w.print("(copy:{s})", .{name.slice(elf)}),
10993 .nav => |nmi| {
10994 const zcu = elf.base.comp.zcu.?;
10995 const ip = &zcu.intern_pool;
10996 const nav = ip.getNav(nmi.nav(elf));
10997 try w.print("({f}, {f})", .{
10998 Type.fromInterned(nav.resolved.?.type).fmt(.{ .zcu = zcu, .tid = tid }),
10999 nav.fqn.fmt(ip),
11000 });
11001 },
11002 .uav => |umi| {
11003 const zcu = elf.base.comp.zcu.?;
11004 const val: Value = .fromInterned(umi.uavValue(elf));
11005 try w.print("({f}, {f})", .{
11006 val.typeOf(zcu).fmt(.{ .zcu = zcu, .tid = tid }),
11007 val.fmtValue(.{ .zcu = zcu, .tid = tid }),
11008 });
11009 },
11010 inline .lazy_code, .lazy_const_data => |lmi| try w.print("({f})", .{
11011 Type.fromInterned(lmi.lazySymbol(elf).ty).fmt(.{
11012 .zcu = elf.base.comp.zcu.?,
11013 .tid = tid,
11014 }),
11015 }),
11016 .debug_shared => |ss| try w.print("({})", .{ss}),
11017 .unit_frame,
11018 .unit_frame_cie,
11019 .unit_debug_info,
11020 .unit_debug_info_header,
11021 .unit_debug_info_footer,
11022 .unit_debug_line,
11023 .unit_debug_line_header,
11024 .unit_debug_rnglists,
11025 => |ui| try w.print("({s})", .{ui.mod(&elf.dwarf).fully_qualified_name}),
11026 .const_debug_info => |cpi| switch (cpi.val(&elf.dwarf.const_pool)) {
11027 .generic_poison_type => try w.writeAll("(anytype)"),
11028 else => |val| try w.print("({f})", .{
11029 Value.fromInterned(val).fmtValue(.{ .zcu = elf.base.comp.zcu.?, .tid = tid }),
11030 }),
11031 },
11032 .global_debug_info => |gi| {
11033 const zcu = elf.base.comp.zcu.?;
11034 const ip = &zcu.intern_pool;
11035 const nav = ip.getNav(gi.nav(&elf.dwarf));
11036 try w.writeByte('(');
11037 if (nav.resolved) |resolved| try w.print("{f}, ", .{
11038 Type.fromInterned(resolved.type).fmt(.{ .zcu = zcu, .tid = tid }),
11039 });
11040 try w.print("{f})", .{nav.fqn.fmt(ip)});
11041 },
11042 .func_frame_fde, .func_debug_info, .func_debug_line => |fi| {
11043 const zcu = elf.base.comp.zcu.?;
11044 const ip = &zcu.intern_pool;
11045 const nav = ip.getNav(fi.nav(&elf.dwarf));
11046 try w.writeByte('(');
11047 if (nav.resolved) |resolved| try w.print("{f}, ", .{
11048 Type.fromInterned(resolved.type).fmt(.{ .zcu = zcu, .tid = tid }),
11049 });
11050 try w.print("{f})", .{nav.fqn.fmt(ip)});
11051 },
11052 .decl_debug_info => |di| {
11053 const comp = elf.base.comp;
11054 const zcu = comp.zcu.?;
11055 const ip = &zcu.intern_pool;
11056 const src_inst = di.srcInst(&elf.dwarf);
11057 try w.print("({f}, ", .{zcu.fileByIndex(src_inst.resolveFile(ip)).path.fmt(comp)});
11058 if (src_inst.resolve(ip)) |inst| try w.print("%{d}", .{inst}) else try w.writeAll("lost");
11059 try w.writeByte(')');
11060 },
11061 }
11062 {
11063 const mf_node = &elf.mf.nodes.items[@backingInt(ni)];
11064 const off, const size = mf_node.location().resolve(&elf.mf);
11065 try w.print(" index={d} offset=0x{x} size=0x{x} align=0x{x} {t}{s}{s}{s}{s}{s}{s}\n", .{
11066 @backingInt(ni),
11067 off,
11068 size,
11069 mf_node.flags.alignment.toByteUnits(),
11070 mf_node.flags.position,
11071 if (mf_node.flags.bubbles_moved) " bubbles_moved" else "",
11072 if (mf_node.flags.resized) " moved" else "",
11073 if (mf_node.flags.resized) " resized" else "",
11074 if (mf_node.flags.enable_next_moved) " enable_next_moved" else "",
11075 if (mf_node.flags.next_moved) " next_moved" else "",
11076 if (mf_node.flags.has_content) " has_content" else "",
11077 });
11078 }
11079 if (ni.first(&elf.mf).unwrap()) |first_ni| {
11080 // non-leaf, just print children
11081 var child_ni = first_ni;
11082 while (true) {
11083 try elf.printNode(tid, w, child_ni, indent + 1);
11084 child_ni = child_ni.next(&elf.mf).unwrap() orelse break;
11085 }
11086 return;
11087 }
11088 const start_address: usize, const end_address: usize = file_loc: {
11089 const file_loc = ni.fileLocation(&elf.mf, false);
11090 break :file_loc .{ @intCast(file_loc.offset), @intCast(file_loc.offset + file_loc.size) };
11091 };
11092 var address = start_address;
11093 const line_len = 0x10;
11094 while (true) : (address = @min(std.mem.alignForward(usize, address + 1, line_len), end_address)) {
11095 try w.splatByteAll(' ', indent + 1);
11096 try w.print("{x:0>8}", .{address});
11097 if (address == end_address) break try w.writeByte('\n');
11098 try w.splatByteAll(' ', 2);
11099 const start_byte_address = std.mem.alignBackward(usize, address, line_len);
11100 const end_byte_address = start_byte_address + line_len;
11101 for (start_byte_address..end_byte_address) |byte_address|
11102 if (byte_address < start_address or byte_address >= end_address)
11103 try w.splatByteAll(' ', 3)
11104 else
11105 try w.print("{x:0>2} ", .{elf.mf.memory_map.memory[byte_address]});
11106 try w.writeByte(' ');
11107 for (start_byte_address..@min(end_address, end_byte_address)) |byte_address|
11108 try w.writeByte(if (byte_address < start_address or byte_address >= end_address) ' ' else char: {
11109 const byte = elf.mf.memory_map.memory[byte_address];
11110 break :char if (std.ascii.isPrint(byte)) byte else '.';
11111 });
11112 try w.writeByte('\n');
11113 }
11114}
11115
11116fn ensureSegmentAligned(elf: *Elf, start_phndx: u32, min_align: Alignment) Error!void {
11117 const gpa = elf.base.comp.gpa;
11118 // We need to loop through parent nodes because segments may be nested (e.g. a PT_TLS segment
11119 // inside a PT_LOAD segment).
11120 var phndx = start_phndx;
11121 while (true) {
11122 // Align the actual node
11123 const seg_ni = elf.phdrs.items[phndx].unwrap().?;
11124 if (min_align.compare(.gt, seg_ni.alignment(&elf.mf))) {
11125 try seg_ni.realign(gpa, &elf.mf, min_align);
11126 }
11127 // Update the phdr `@"align"` field if necessary
11128 switch (elf.phdrSlice()) {
11129 inline else => |phdr| switch (elf.targetLoad(&phdr[phndx].type)) {
11130 .NULL, .LOAD => {
11131 // The `@"align"` field is managed by `allocateSegmentLoadAddress`.
11132 //
11133 // It's very likely that the node was moved and/or resized when we realigned it
11134 // just above, but it is possible that it was not moved *but* still has an
11135 // unaligned virtual address. In that case, we need to ensure the segment's
11136 // virtual address range will be recomputed.
11137 if (!min_align.check(@intCast(elf.targetLoad(&phdr[phndx].vaddr)))) {
11138 try seg_ni.moved(gpa, &elf.mf);
11139 }
11140 },
11141 else => elf.targetStore(&phdr[phndx].@"align", @intCast(@max(
11142 elf.targetLoad(&phdr[phndx].@"align"),
11143 min_align.toByteUnits(),
11144 ))),
11145 },
11146 }
11147 // Continue on to the parent segment, if any
11148 switch (elf.getNode(seg_ni.parent(&elf.mf).unwrap().?)) {
11149 .segment => |parent_phndx| phndx = parent_phndx,
11150 .elf => return,
11151 else => unreachable,
11152 }
11153 }
11154}
11155
11156pub fn addNodeAssumeCapacity(elf: *Elf, ni: MappedFile.Node.Index, node: Node) MappedFile.Node.Index {
11157 if (elf.nodes.len - @backingInt(ni) > 0) {
11158 assert(elf.getNode(ni) == .deleted);
11159 elf.nodes.set(@backingInt(ni), node);
11160 } else elf.nodes.appendAssumeCapacity(node);
11161 return ni;
11162}
11163
11164fn deleteNode(elf: *Elf, node: *MappedFile.Node.Index.Optional) std.mem.Allocator.Error!void {
11165 const ni = node.unwrap().?;
11166 try ni.delete(elf.base.comp.gpa, &elf.mf);
11167 elf.nodes.set(@backingInt(ni), .deleted);
11168 node.* = .none;
11169}
11170
11171/// If `sym` has a PLT entry, returns the address of that entry (specifically, the address which a
11172/// branch to the PLT should target). If `sym` does not have a PLT entry, returns `null`.
11173fn pltEntryTargetAddr(elf: *Elf, sym: Symbol.Id) ?u64 {
11174 const index = switch (sym.unwrap()) {
11175 .local => return null,
11176 .global => |name| elf.plt.getIndex(name) orelse return null,
11177 };
11178 if (elf.pltEntryIsDead(index)) return null;
11179 const plt = elf.targetPltInfo();
11180 if (plt.plt_sec) |plt_sec| {
11181 return elf.shndx.plt_sec.vaddr(elf) +% index * plt_sec.entry_size;
11182 } else {
11183 return elf.shndx.plt.vaddr(elf) +% (plt.header_entries + index) * plt.entry_size;
11184 }
11185}