zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

blob c8faaa33 (213302B) - Raw


      1 base: link.File,
      2 rpath_table: std.StringArrayHashMapUnmanaged(void),
      3 image_base: u64,
      4 emit_relocs: bool,
      5 z_nodelete: bool,
      6 z_notext: bool,
      7 z_defs: bool,
      8 z_origin: bool,
      9 z_nocopyreloc: bool,
     10 z_now: bool,
     11 z_relro: bool,
     12 /// TODO make this non optional and resolve the default in open()
     13 z_common_page_size: ?u64,
     14 /// TODO make this non optional and resolve the default in open()
     15 z_max_page_size: ?u64,
     16 lib_dirs: []const []const u8,
     17 hash_style: HashStyle,
     18 compress_debug_sections: CompressDebugSections,
     19 symbol_wrap_set: std.StringArrayHashMapUnmanaged(void),
     20 sort_section: ?SortSection,
     21 soname: ?[]const u8,
     22 bind_global_refs_locally: bool,
     23 linker_script: ?[]const u8,
     24 version_script: ?[]const u8,
     25 allow_undefined_version: bool,
     26 enable_new_dtags: ?bool,
     27 print_icf_sections: bool,
     28 print_map: bool,
     29 entry_name: ?[]const u8,
     30 
     31 ptr_width: PtrWidth,
     32 
     33 /// If this is not null, an object file is created by LLVM and emitted to zcu_object_sub_path.
     34 llvm_object: ?LlvmObject.Ptr = null,
     35 
     36 /// A list of all input files.
     37 /// Index of each input file also encodes the priority or precedence of one input file
     38 /// over another.
     39 files: std.MultiArrayList(File.Entry) = .{},
     40 /// Long-lived list of all file descriptors.
     41 /// We store them globally rather than per actual File so that we can re-use
     42 /// one file handle per every object file within an archive.
     43 file_handles: std.ArrayListUnmanaged(File.Handle) = .empty,
     44 zig_object_index: ?File.Index = null,
     45 linker_defined_index: ?File.Index = null,
     46 objects: std.ArrayListUnmanaged(File.Index) = .empty,
     47 shared_objects: std.ArrayListUnmanaged(File.Index) = .empty,
     48 
     49 /// List of all output sections and their associated metadata.
     50 sections: std.MultiArrayList(Section) = .{},
     51 /// File offset into the shdr table.
     52 shdr_table_offset: ?u64 = null,
     53 
     54 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
     55 /// Same order as in the file.
     56 phdrs: std.ArrayListUnmanaged(elf.Elf64_Phdr) = .empty,
     57 
     58 /// Special program headers
     59 /// PT_PHDR
     60 phdr_table_index: ?u16 = null,
     61 /// PT_LOAD for PHDR table
     62 /// We add this special load segment to ensure the EHDR and PHDR table are always
     63 /// loaded into memory.
     64 phdr_table_load_index: ?u16 = null,
     65 /// PT_INTERP
     66 phdr_interp_index: ?u16 = null,
     67 /// PT_DYNAMIC
     68 phdr_dynamic_index: ?u16 = null,
     69 /// PT_GNU_EH_FRAME
     70 phdr_gnu_eh_frame_index: ?u16 = null,
     71 /// PT_GNU_STACK
     72 phdr_gnu_stack_index: ?u16 = null,
     73 /// PT_TLS
     74 /// TODO I think ELF permits multiple TLS segments but for now, assume one per file.
     75 phdr_tls_index: ?u16 = null,
     76 
     77 page_size: u32,
     78 default_sym_version: elf.Elf64_Versym,
     79 
     80 /// .shstrtab buffer
     81 shstrtab: std.ArrayListUnmanaged(u8) = .empty,
     82 /// .symtab buffer
     83 symtab: std.ArrayListUnmanaged(elf.Elf64_Sym) = .empty,
     84 /// .strtab buffer
     85 strtab: std.ArrayListUnmanaged(u8) = .empty,
     86 /// Dynamic symbol table. Only populated and emitted when linking dynamically.
     87 dynsym: DynsymSection = .{},
     88 /// .dynstrtab buffer
     89 dynstrtab: std.ArrayListUnmanaged(u8) = .empty,
     90 /// Version symbol table. Only populated and emitted when linking dynamically.
     91 versym: std.ArrayListUnmanaged(elf.Elf64_Versym) = .empty,
     92 /// .verneed section
     93 verneed: VerneedSection = .{},
     94 /// .got section
     95 got: GotSection = .{},
     96 /// .rela.dyn section
     97 rela_dyn: std.ArrayListUnmanaged(elf.Elf64_Rela) = .empty,
     98 /// .dynamic section
     99 dynamic: DynamicSection = .{},
    100 /// .hash section
    101 hash: HashSection = .{},
    102 /// .gnu.hash section
    103 gnu_hash: GnuHashSection = .{},
    104 /// .plt section
    105 plt: PltSection = .{},
    106 /// .got.plt section
    107 got_plt: GotPltSection = .{},
    108 /// .plt.got section
    109 plt_got: PltGotSection = .{},
    110 /// .copyrel section
    111 copy_rel: CopyRelSection = .{},
    112 /// .rela.plt section
    113 rela_plt: std.ArrayListUnmanaged(elf.Elf64_Rela) = .empty,
    114 /// SHT_GROUP sections
    115 /// Applies only to a relocatable.
    116 comdat_group_sections: std.ArrayListUnmanaged(ComdatGroupSection) = .empty,
    117 
    118 copy_rel_section_index: ?u32 = null,
    119 dynamic_section_index: ?u32 = null,
    120 dynstrtab_section_index: ?u32 = null,
    121 dynsymtab_section_index: ?u32 = null,
    122 eh_frame_section_index: ?u32 = null,
    123 eh_frame_rela_section_index: ?u32 = null,
    124 eh_frame_hdr_section_index: ?u32 = null,
    125 hash_section_index: ?u32 = null,
    126 gnu_hash_section_index: ?u32 = null,
    127 got_section_index: ?u32 = null,
    128 got_plt_section_index: ?u32 = null,
    129 interp_section_index: ?u32 = null,
    130 plt_section_index: ?u32 = null,
    131 plt_got_section_index: ?u32 = null,
    132 rela_dyn_section_index: ?u32 = null,
    133 rela_plt_section_index: ?u32 = null,
    134 versym_section_index: ?u32 = null,
    135 verneed_section_index: ?u32 = null,
    136 
    137 shstrtab_section_index: ?u32 = null,
    138 strtab_section_index: ?u32 = null,
    139 symtab_section_index: ?u32 = null,
    140 
    141 resolver: SymbolResolver = .{},
    142 
    143 has_text_reloc: bool = false,
    144 num_ifunc_dynrelocs: usize = 0,
    145 
    146 /// List of range extension thunks.
    147 thunks: std.ArrayListUnmanaged(Thunk) = .empty,
    148 
    149 /// List of output merge sections with deduped contents.
    150 merge_sections: std.ArrayListUnmanaged(MergeSection) = .empty,
    151 comment_merge_section_index: ?MergeSection.Index = null,
    152 
    153 first_eflags: ?elf.Elf64_Word = null,
    154 
    155 /// When allocating, the ideal_capacity is calculated by
    156 /// actual_capacity + (actual_capacity / ideal_factor)
    157 const ideal_factor = 3;
    158 
    159 /// In order for a slice of bytes to be considered eligible to keep metadata pointing at
    160 /// it as a possible place to put new symbols, it must have enough room for this many bytes
    161 /// (plus extra for reserved capacity).
    162 const minimum_atom_size = 64;
    163 pub const min_text_capacity = padToIdeal(minimum_atom_size);
    164 
    165 pub const PtrWidth = enum { p32, p64 };
    166 pub const HashStyle = enum { sysv, gnu, both };
    167 pub const CompressDebugSections = enum { none, zlib, zstd };
    168 pub const SortSection = enum { name, alignment };
    169 
    170 pub fn createEmpty(
    171     arena: Allocator,
    172     comp: *Compilation,
    173     emit: Path,
    174     options: link.File.OpenOptions,
    175 ) !*Elf {
    176     const target = comp.root_mod.resolved_target.result;
    177     assert(target.ofmt == .elf);
    178 
    179     const use_lld = build_options.have_llvm and comp.config.use_lld;
    180     const use_llvm = comp.config.use_llvm;
    181     const opt_zcu = comp.zcu;
    182     const output_mode = comp.config.output_mode;
    183     const link_mode = comp.config.link_mode;
    184     const optimize_mode = comp.root_mod.optimize_mode;
    185     const is_native_os = comp.root_mod.resolved_target.is_native_os;
    186     const ptr_width: PtrWidth = switch (target.ptrBitWidth()) {
    187         0...32 => .p32,
    188         33...64 => .p64,
    189         else => return error.UnsupportedELFArchitecture,
    190     };
    191 
    192     // This is the max page size that the target system can run with, aka the ABI page size. Not to
    193     // be confused with the common page size, which is the page size that's used in practice on most
    194     // systems.
    195     const page_size: u32 = switch (target.cpu.arch) {
    196         .bpfel,
    197         .bpfeb,
    198         .sparc64,
    199         => 0x100000,
    200         .aarch64,
    201         .aarch64_be,
    202         .amdgcn,
    203         .hexagon,
    204         .mips,
    205         .mipsel,
    206         .mips64,
    207         .mips64el,
    208         .powerpc,
    209         .powerpcle,
    210         .powerpc64,
    211         .powerpc64le,
    212         .sparc,
    213         => 0x10000,
    214         .loongarch32,
    215         .loongarch64,
    216         => 0x4000,
    217         .arc,
    218         .m68k,
    219         => 0x2000,
    220         .msp430,
    221         => 0x4,
    222         .avr,
    223         => 0x1,
    224         else => 0x1000,
    225     };
    226 
    227     const is_dyn_lib = output_mode == .Lib and link_mode == .dynamic;
    228     const default_sym_version: elf.Elf64_Versym = if (is_dyn_lib or comp.config.rdynamic)
    229         elf.VER_NDX_GLOBAL
    230     else
    231         elf.VER_NDX_LOCAL;
    232 
    233     // If using LLD to link, this code should produce an object file so that it
    234     // can be passed to LLD.
    235     // If using LLVM to generate the object file for the zig compilation unit,
    236     // we need a place to put the object file so that it can be subsequently
    237     // handled.
    238     const zcu_object_sub_path = if (!use_lld and !use_llvm)
    239         null
    240     else
    241         try std.fmt.allocPrint(arena, "{s}.o", .{emit.sub_path});
    242 
    243     var rpath_table: std.StringArrayHashMapUnmanaged(void) = .empty;
    244     try rpath_table.entries.resize(arena, options.rpath_list.len);
    245     @memcpy(rpath_table.entries.items(.key), options.rpath_list);
    246     try rpath_table.reIndex(arena);
    247 
    248     const self = try arena.create(Elf);
    249     self.* = .{
    250         .base = .{
    251             .tag = .elf,
    252             .comp = comp,
    253             .emit = emit,
    254             .zcu_object_sub_path = zcu_object_sub_path,
    255             .gc_sections = options.gc_sections orelse (optimize_mode != .Debug and output_mode != .Obj),
    256             .print_gc_sections = options.print_gc_sections,
    257             .stack_size = options.stack_size orelse 16777216,
    258             .allow_shlib_undefined = options.allow_shlib_undefined orelse !is_native_os,
    259             .file = null,
    260             .disable_lld_caching = options.disable_lld_caching,
    261             .build_id = options.build_id,
    262         },
    263         .rpath_table = rpath_table,
    264         .ptr_width = ptr_width,
    265         .page_size = page_size,
    266         .default_sym_version = default_sym_version,
    267 
    268         .entry_name = switch (options.entry) {
    269             .disabled => null,
    270             .default => if (output_mode != .Exe) null else defaultEntrySymbolName(target.cpu.arch),
    271             .enabled => defaultEntrySymbolName(target.cpu.arch),
    272             .named => |name| name,
    273         },
    274 
    275         .image_base = b: {
    276             if (is_dyn_lib) break :b 0;
    277             if (output_mode == .Exe and comp.config.pie) break :b 0;
    278             break :b options.image_base orelse switch (ptr_width) {
    279                 .p32 => 0x10000,
    280                 .p64 => 0x1000000,
    281             };
    282         },
    283 
    284         .emit_relocs = options.emit_relocs,
    285         .z_nodelete = options.z_nodelete,
    286         .z_notext = options.z_notext,
    287         .z_defs = options.z_defs,
    288         .z_origin = options.z_origin,
    289         .z_nocopyreloc = options.z_nocopyreloc,
    290         .z_now = options.z_now,
    291         .z_relro = options.z_relro,
    292         .z_common_page_size = options.z_common_page_size,
    293         .z_max_page_size = options.z_max_page_size,
    294         .lib_dirs = options.lib_dirs,
    295         .hash_style = options.hash_style,
    296         .compress_debug_sections = options.compress_debug_sections,
    297         .symbol_wrap_set = options.symbol_wrap_set,
    298         .sort_section = options.sort_section,
    299         .soname = options.soname,
    300         .bind_global_refs_locally = options.bind_global_refs_locally,
    301         .linker_script = options.linker_script,
    302         .version_script = options.version_script,
    303         .allow_undefined_version = options.allow_undefined_version,
    304         .enable_new_dtags = options.enable_new_dtags,
    305         .print_icf_sections = options.print_icf_sections,
    306         .print_map = options.print_map,
    307     };
    308     if (use_llvm and comp.config.have_zcu) {
    309         self.llvm_object = try LlvmObject.create(arena, comp);
    310     }
    311     errdefer self.base.destroy();
    312 
    313     if (use_lld and (use_llvm or !comp.config.have_zcu)) {
    314         // LLVM emits the object file (if any); LLD links it into the final product.
    315         return self;
    316     }
    317 
    318     const is_obj = output_mode == .Obj;
    319     const is_obj_or_ar = is_obj or (output_mode == .Lib and link_mode == .static);
    320 
    321     // What path should this ELF linker code output to?
    322     // If using LLD to link, this code should produce an object file so that it
    323     // can be passed to LLD.
    324     const sub_path = if (use_lld) zcu_object_sub_path.? else emit.sub_path;
    325     self.base.file = try emit.root_dir.handle.createFile(sub_path, .{
    326         .truncate = true,
    327         .read = true,
    328         .mode = link.File.determineMode(use_lld, output_mode, link_mode),
    329     });
    330 
    331     const gpa = comp.gpa;
    332 
    333     // Append null file at index 0
    334     try self.files.append(gpa, .null);
    335     // Append null byte to string tables
    336     try self.shstrtab.append(gpa, 0);
    337     try self.strtab.append(gpa, 0);
    338     // There must always be a null shdr in index 0
    339     _ = try self.addSection(.{});
    340     // Append null symbol in output symtab
    341     try self.symtab.append(gpa, null_sym);
    342 
    343     if (!is_obj_or_ar) {
    344         try self.dynstrtab.append(gpa, 0);
    345 
    346         // Initialize PT_PHDR program header
    347         const p_align: u16 = switch (self.ptr_width) {
    348             .p32 => @alignOf(elf.Elf32_Phdr),
    349             .p64 => @alignOf(elf.Elf64_Phdr),
    350         };
    351         const ehsize: u64 = switch (self.ptr_width) {
    352             .p32 => @sizeOf(elf.Elf32_Ehdr),
    353             .p64 => @sizeOf(elf.Elf64_Ehdr),
    354         };
    355         const phsize: u64 = switch (self.ptr_width) {
    356             .p32 => @sizeOf(elf.Elf32_Phdr),
    357             .p64 => @sizeOf(elf.Elf64_Phdr),
    358         };
    359         const max_nphdrs = comptime getMaxNumberOfPhdrs();
    360         const reserved: u64 = mem.alignForward(u64, padToIdeal(max_nphdrs * phsize), self.page_size);
    361         self.phdr_table_index = try self.addPhdr(.{
    362             .type = elf.PT_PHDR,
    363             .flags = elf.PF_R,
    364             .@"align" = p_align,
    365             .addr = self.image_base + ehsize,
    366             .offset = ehsize,
    367             .filesz = reserved,
    368             .memsz = reserved,
    369         });
    370         self.phdr_table_load_index = try self.addPhdr(.{
    371             .type = elf.PT_LOAD,
    372             .flags = elf.PF_R,
    373             .@"align" = self.page_size,
    374             .addr = self.image_base,
    375             .offset = 0,
    376             .filesz = reserved + ehsize,
    377             .memsz = reserved + ehsize,
    378         });
    379     }
    380 
    381     if (opt_zcu) |zcu| {
    382         if (!use_llvm) {
    383             const index: File.Index = @intCast(try self.files.addOne(gpa));
    384             self.files.set(index, .{ .zig_object = .{
    385                 .index = index,
    386                 .path = try std.fmt.allocPrint(arena, "{s}.o", .{fs.path.stem(
    387                     zcu.main_mod.root_src_path,
    388                 )}),
    389             } });
    390             self.zig_object_index = index;
    391             try self.zigObjectPtr().?.init(self, .{
    392                 .symbol_count_hint = options.symbol_count_hint,
    393                 .program_code_size_hint = options.program_code_size_hint,
    394             });
    395         }
    396     }
    397 
    398     return self;
    399 }
    400 
    401 pub fn open(
    402     arena: Allocator,
    403     comp: *Compilation,
    404     emit: Path,
    405     options: link.File.OpenOptions,
    406 ) !*Elf {
    407     // TODO: restore saved linker state, don't truncate the file, and
    408     // participate in incremental compilation.
    409     return createEmpty(arena, comp, emit, options);
    410 }
    411 
    412 pub fn deinit(self: *Elf) void {
    413     const gpa = self.base.comp.gpa;
    414 
    415     if (self.llvm_object) |llvm_object| llvm_object.deinit();
    416 
    417     for (self.file_handles.items) |fh| {
    418         fh.close();
    419     }
    420     self.file_handles.deinit(gpa);
    421 
    422     for (self.files.items(.tags), self.files.items(.data)) |tag, *data| switch (tag) {
    423         .null => {},
    424         .zig_object => data.zig_object.deinit(gpa),
    425         .linker_defined => data.linker_defined.deinit(gpa),
    426         .object => data.object.deinit(gpa),
    427         .shared_object => data.shared_object.deinit(gpa),
    428     };
    429     self.files.deinit(gpa);
    430     self.objects.deinit(gpa);
    431     self.shared_objects.deinit(gpa);
    432 
    433     for (self.sections.items(.atom_list_2), self.sections.items(.atom_list), self.sections.items(.free_list)) |*atom_list, *atoms, *free_list| {
    434         atom_list.deinit(gpa);
    435         atoms.deinit(gpa);
    436         free_list.deinit(gpa);
    437     }
    438     self.sections.deinit(gpa);
    439     self.phdrs.deinit(gpa);
    440     self.shstrtab.deinit(gpa);
    441     self.symtab.deinit(gpa);
    442     self.strtab.deinit(gpa);
    443     self.resolver.deinit(gpa);
    444 
    445     for (self.thunks.items) |*th| {
    446         th.deinit(gpa);
    447     }
    448     self.thunks.deinit(gpa);
    449     for (self.merge_sections.items) |*sect| {
    450         sect.deinit(gpa);
    451     }
    452     self.merge_sections.deinit(gpa);
    453 
    454     self.got.deinit(gpa);
    455     self.plt.deinit(gpa);
    456     self.plt_got.deinit(gpa);
    457     self.dynsym.deinit(gpa);
    458     self.dynstrtab.deinit(gpa);
    459     self.dynamic.deinit(gpa);
    460     self.hash.deinit(gpa);
    461     self.versym.deinit(gpa);
    462     self.verneed.deinit(gpa);
    463     self.copy_rel.deinit(gpa);
    464     self.rela_dyn.deinit(gpa);
    465     self.rela_plt.deinit(gpa);
    466     self.comdat_group_sections.deinit(gpa);
    467 }
    468 
    469 pub fn getNavVAddr(self: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, reloc_info: link.File.RelocInfo) !u64 {
    470     assert(self.llvm_object == null);
    471     return self.zigObjectPtr().?.getNavVAddr(self, pt, nav_index, reloc_info);
    472 }
    473 
    474 pub fn lowerUav(
    475     self: *Elf,
    476     pt: Zcu.PerThread,
    477     uav: InternPool.Index,
    478     explicit_alignment: InternPool.Alignment,
    479     src_loc: Zcu.LazySrcLoc,
    480 ) !codegen.GenResult {
    481     return self.zigObjectPtr().?.lowerUav(self, pt, uav, explicit_alignment, src_loc);
    482 }
    483 
    484 pub fn getUavVAddr(self: *Elf, uav: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 {
    485     assert(self.llvm_object == null);
    486     return self.zigObjectPtr().?.getUavVAddr(self, uav, reloc_info);
    487 }
    488 
    489 /// Returns end pos of collision, if any.
    490 fn detectAllocCollision(self: *Elf, start: u64, size: u64) !?u64 {
    491     const small_ptr = self.ptr_width == .p32;
    492     const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr);
    493     if (start < ehdr_size)
    494         return ehdr_size;
    495 
    496     var at_end = true;
    497     const end = start + padToIdeal(size);
    498 
    499     if (self.shdr_table_offset) |off| {
    500         const shdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Shdr) else @sizeOf(elf.Elf64_Shdr);
    501         const tight_size = self.sections.items(.shdr).len * shdr_size;
    502         const increased_size = padToIdeal(tight_size);
    503         const test_end = off +| increased_size;
    504         if (start < test_end) {
    505             if (end > off) return test_end;
    506             if (test_end < std.math.maxInt(u64)) at_end = false;
    507         }
    508     }
    509 
    510     for (self.sections.items(.shdr)) |shdr| {
    511         if (shdr.sh_type == elf.SHT_NOBITS) continue;
    512         const increased_size = padToIdeal(shdr.sh_size);
    513         const test_end = shdr.sh_offset +| increased_size;
    514         if (start < test_end) {
    515             if (end > shdr.sh_offset) return test_end;
    516             if (test_end < std.math.maxInt(u64)) at_end = false;
    517         }
    518     }
    519 
    520     for (self.phdrs.items) |phdr| {
    521         if (phdr.p_type != elf.PT_LOAD) continue;
    522         const increased_size = padToIdeal(phdr.p_filesz);
    523         const test_end = phdr.p_offset +| increased_size;
    524         if (start < test_end) {
    525             if (end > phdr.p_offset) return test_end;
    526             if (test_end < std.math.maxInt(u64)) at_end = false;
    527         }
    528     }
    529 
    530     if (at_end) try self.base.file.?.setEndPos(end);
    531     return null;
    532 }
    533 
    534 pub fn allocatedSize(self: *Elf, start: u64) u64 {
    535     if (start == 0) return 0;
    536     var min_pos: u64 = std.math.maxInt(u64);
    537     if (self.shdr_table_offset) |off| {
    538         if (off > start and off < min_pos) min_pos = off;
    539     }
    540     for (self.sections.items(.shdr)) |section| {
    541         if (section.sh_offset <= start) continue;
    542         if (section.sh_offset < min_pos) min_pos = section.sh_offset;
    543     }
    544     for (self.phdrs.items) |phdr| {
    545         if (phdr.p_offset <= start) continue;
    546         if (phdr.p_offset < min_pos) min_pos = phdr.p_offset;
    547     }
    548     return min_pos - start;
    549 }
    550 
    551 pub fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u64) !u64 {
    552     var start: u64 = 0;
    553     while (try self.detectAllocCollision(start, object_size)) |item_end| {
    554         start = mem.alignForward(u64, item_end, min_alignment);
    555     }
    556     return start;
    557 }
    558 
    559 pub fn growSection(self: *Elf, shdr_index: u32, needed_size: u64, min_alignment: u64) !void {
    560     const shdr = &self.sections.items(.shdr)[shdr_index];
    561 
    562     if (shdr.sh_type != elf.SHT_NOBITS) {
    563         const allocated_size = self.allocatedSize(shdr.sh_offset);
    564         log.debug("allocated size {x} of '{s}', needed size {x}", .{
    565             allocated_size,
    566             self.getShString(shdr.sh_name),
    567             needed_size,
    568         });
    569 
    570         if (needed_size > allocated_size) {
    571             const existing_size = shdr.sh_size;
    572             shdr.sh_size = 0;
    573             // Must move the entire section.
    574             const new_offset = try self.findFreeSpace(needed_size, min_alignment);
    575 
    576             log.debug("new '{s}' file offset 0x{x} to 0x{x}", .{
    577                 self.getShString(shdr.sh_name),
    578                 new_offset,
    579                 new_offset + existing_size,
    580             });
    581 
    582             const amt = try self.base.file.?.copyRangeAll(
    583                 shdr.sh_offset,
    584                 self.base.file.?,
    585                 new_offset,
    586                 existing_size,
    587             );
    588             // TODO figure out what to about this error condition - how to communicate it up.
    589             if (amt != existing_size) return error.InputOutput;
    590 
    591             shdr.sh_offset = new_offset;
    592         } else if (shdr.sh_offset + allocated_size == std.math.maxInt(u64)) {
    593             try self.base.file.?.setEndPos(shdr.sh_offset + needed_size);
    594         }
    595     }
    596 
    597     shdr.sh_size = needed_size;
    598     self.markDirty(shdr_index);
    599 }
    600 
    601 fn markDirty(self: *Elf, shdr_index: u32) void {
    602     if (self.zigObjectPtr()) |zo| {
    603         for ([_]?Symbol.Index{
    604             zo.debug_info_index,
    605             zo.debug_abbrev_index,
    606             zo.debug_aranges_index,
    607             zo.debug_str_index,
    608             zo.debug_line_index,
    609             zo.debug_line_str_index,
    610             zo.debug_loclists_index,
    611             zo.debug_rnglists_index,
    612         }, [_]*bool{
    613             &zo.debug_info_section_dirty,
    614             &zo.debug_abbrev_section_dirty,
    615             &zo.debug_aranges_section_dirty,
    616             &zo.debug_str_section_dirty,
    617             &zo.debug_line_section_dirty,
    618             &zo.debug_line_str_section_dirty,
    619             &zo.debug_loclists_section_dirty,
    620             &zo.debug_rnglists_section_dirty,
    621         }) |maybe_sym_index, dirty| {
    622             const sym_index = maybe_sym_index orelse continue;
    623             if (zo.symbol(sym_index).atom(self).?.output_section_index == shdr_index) {
    624                 dirty.* = true;
    625                 break;
    626             }
    627         }
    628     }
    629 }
    630 
    631 const AllocateChunkResult = struct {
    632     value: u64,
    633     placement: Ref,
    634 };
    635 
    636 pub fn allocateChunk(self: *Elf, args: struct {
    637     size: u64,
    638     shndx: u32,
    639     alignment: Atom.Alignment,
    640     requires_padding: bool = true,
    641 }) !AllocateChunkResult {
    642     const slice = self.sections.slice();
    643     const shdr = &slice.items(.shdr)[args.shndx];
    644     const free_list = &slice.items(.free_list)[args.shndx];
    645     const last_atom_ref = &slice.items(.last_atom)[args.shndx];
    646     const new_atom_ideal_capacity = if (args.requires_padding) padToIdeal(args.size) else args.size;
    647 
    648     // First we look for an appropriately sized free list node.
    649     // The list is unordered. We'll just take the first thing that works.
    650     const res: AllocateChunkResult = blk: {
    651         var i: usize = if (self.base.child_pid == null) 0 else free_list.items.len;
    652         while (i < free_list.items.len) {
    653             const big_atom_ref = free_list.items[i];
    654             const big_atom = self.atom(big_atom_ref).?;
    655             // We now have a pointer to a live atom that has too much capacity.
    656             // Is it enough that we could fit this new atom?
    657             const cap = big_atom.capacity(self);
    658             const ideal_capacity = if (args.requires_padding) padToIdeal(cap) else cap;
    659             const ideal_capacity_end_vaddr = std.math.add(u64, @intCast(big_atom.value), ideal_capacity) catch ideal_capacity;
    660             const capacity_end_vaddr = @as(u64, @intCast(big_atom.value)) + cap;
    661             const new_start_vaddr_unaligned = capacity_end_vaddr - new_atom_ideal_capacity;
    662             const new_start_vaddr = args.alignment.backward(new_start_vaddr_unaligned);
    663             if (new_start_vaddr < ideal_capacity_end_vaddr) {
    664                 // Additional bookkeeping here to notice if this free list node
    665                 // should be deleted because the block that it points to has grown to take up
    666                 // more of the extra capacity.
    667                 if (!big_atom.freeListEligible(self)) {
    668                     _ = free_list.swapRemove(i);
    669                 } else {
    670                     i += 1;
    671                 }
    672                 continue;
    673             }
    674             // At this point we know that we will place the new block here. But the
    675             // remaining question is whether there is still yet enough capacity left
    676             // over for there to still be a free list node.
    677             const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr;
    678             const keep_free_list_node = remaining_capacity >= min_text_capacity;
    679 
    680             if (!keep_free_list_node) {
    681                 _ = free_list.swapRemove(i);
    682             }
    683             break :blk .{ .value = new_start_vaddr, .placement = big_atom_ref };
    684         } else if (self.atom(last_atom_ref.*)) |last_atom| {
    685             const ideal_capacity = if (args.requires_padding) padToIdeal(last_atom.size) else last_atom.size;
    686             const ideal_capacity_end_vaddr = @as(u64, @intCast(last_atom.value)) + ideal_capacity;
    687             const new_start_vaddr = args.alignment.forward(ideal_capacity_end_vaddr);
    688             break :blk .{ .value = new_start_vaddr, .placement = last_atom.ref() };
    689         } else {
    690             break :blk .{ .value = 0, .placement = .{} };
    691         }
    692     };
    693 
    694     log.debug("allocated chunk (size({x}),align({x})) at 0x{x} (file(0x{x}))", .{
    695         args.size,
    696         args.alignment.toByteUnits().?,
    697         shdr.sh_addr + res.value,
    698         shdr.sh_offset + res.value,
    699     });
    700 
    701     const expand_section = if (self.atom(res.placement)) |placement_atom|
    702         placement_atom.nextAtom(self) == null
    703     else
    704         true;
    705     if (expand_section) {
    706         const needed_size = res.value + args.size;
    707         try self.growSection(args.shndx, needed_size, args.alignment.toByteUnits().?);
    708     }
    709 
    710     return res;
    711 }
    712 
    713 pub fn flush(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
    714     const use_lld = build_options.have_llvm and self.base.comp.config.use_lld;
    715     if (use_lld) {
    716         return self.linkWithLLD(arena, tid, prog_node);
    717     }
    718     try self.flushModule(arena, tid, prog_node);
    719 }
    720 
    721 pub fn flushModule(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) link.File.FlushError!void {
    722     const tracy = trace(@src());
    723     defer tracy.end();
    724 
    725     const comp = self.base.comp;
    726     const gpa = comp.gpa;
    727 
    728     if (self.llvm_object) |llvm_object| {
    729         try self.base.emitLlvmObject(arena, llvm_object, prog_node);
    730         const use_lld = build_options.have_llvm and comp.config.use_lld;
    731         if (use_lld) return;
    732     }
    733 
    734     const sub_prog_node = prog_node.start("ELF Flush", 0);
    735     defer sub_prog_node.end();
    736 
    737     const target = self.getTarget();
    738     const link_mode = comp.config.link_mode;
    739     const directory = self.base.emit.root_dir; // Just an alias to make it shorter to type.
    740     const module_obj_path: ?[]const u8 = if (self.base.zcu_object_sub_path) |path| blk: {
    741         const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path});
    742         if (fs.path.dirname(full_out_path)) |dirname| {
    743             break :blk try fs.path.join(arena, &.{ dirname, path });
    744         } else {
    745             break :blk path;
    746         }
    747     } else null;
    748 
    749     // --verbose-link
    750     if (comp.verbose_link) try self.dumpArgv(comp);
    751 
    752     if (self.zigObjectPtr()) |zig_object| try zig_object.flushModule(self, tid);
    753     if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, module_obj_path);
    754     if (self.base.isObject()) return relocatable.flushObject(self, comp, module_obj_path);
    755 
    756     const csu = try CsuObjects.init(arena, comp);
    757 
    758     // csu prelude
    759     if (csu.crt0) |path| try parseObjectReportingFailure(self, path);
    760     if (csu.crti) |path| try parseObjectReportingFailure(self, path);
    761     if (csu.crtbegin) |path| try parseObjectReportingFailure(self, path);
    762 
    763     for (comp.objects) |obj| {
    764         if (obj.isObject()) {
    765             try parseObjectReportingFailure(self, obj.path);
    766         } else {
    767             try parseLibraryReportingFailure(self, .{ .path = obj.path }, obj.must_link);
    768         }
    769     }
    770 
    771     // This is a set of object files emitted by clang in a single `build-exe` invocation.
    772     // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up
    773     // in this set.
    774     for (comp.c_object_table.keys()) |key| {
    775         try parseObjectReportingFailure(self, key.status.success.object_path);
    776     }
    777 
    778     if (module_obj_path) |path| try parseObjectReportingFailure(self, path);
    779 
    780     if (comp.config.any_sanitize_thread) try parseCrtFileReportingFailure(self, comp.tsan_lib.?);
    781     if (comp.config.any_fuzz) try parseCrtFileReportingFailure(self, comp.fuzzer_lib.?);
    782 
    783     // libc
    784     if (!comp.skip_linker_dependencies and !comp.config.link_libc) {
    785         if (comp.libc_static_lib) |lib| try parseCrtFileReportingFailure(self, lib);
    786     }
    787 
    788     var system_libs = std.ArrayList(SystemLib).init(arena);
    789 
    790     try system_libs.ensureUnusedCapacity(comp.system_libs.values().len);
    791     for (comp.system_libs.values()) |lib_info| {
    792         system_libs.appendAssumeCapacity(.{ .needed = lib_info.needed, .path = lib_info.path.? });
    793     }
    794 
    795     // libc++ dep
    796     if (comp.config.link_libcpp) {
    797         try system_libs.ensureUnusedCapacity(2);
    798         system_libs.appendAssumeCapacity(.{ .path = comp.libcxxabi_static_lib.?.full_object_path });
    799         system_libs.appendAssumeCapacity(.{ .path = comp.libcxx_static_lib.?.full_object_path });
    800     }
    801 
    802     // libunwind dep
    803     if (comp.config.link_libunwind) {
    804         try system_libs.append(.{ .path = comp.libunwind_static_lib.?.full_object_path });
    805     }
    806 
    807     // libc dep
    808     comp.link_error_flags.missing_libc = false;
    809     if (comp.config.link_libc) {
    810         if (comp.libc_installation) |lc| {
    811             const flags = target_util.libcFullLinkFlags(target);
    812             try system_libs.ensureUnusedCapacity(flags.len);
    813 
    814             var test_path = std.ArrayList(u8).init(arena);
    815             var checked_paths = std.ArrayList([]const u8).init(arena);
    816 
    817             for (flags) |flag| {
    818                 checked_paths.clearRetainingCapacity();
    819                 const lib_name = flag["-l".len..];
    820 
    821                 success: {
    822                     if (!self.base.isStatic()) {
    823                         if (try self.accessLibPath(arena, &test_path, &checked_paths, lc.crt_dir.?, lib_name, .dynamic))
    824                             break :success;
    825                     }
    826                     if (try self.accessLibPath(arena, &test_path, &checked_paths, lc.crt_dir.?, lib_name, .static))
    827                         break :success;
    828 
    829                     try self.reportMissingLibraryError(
    830                         checked_paths.items,
    831                         "missing system library: '{s}' was not found",
    832                         .{lib_name},
    833                     );
    834 
    835                     continue;
    836                 }
    837 
    838                 const resolved_path = try arena.dupe(u8, test_path.items);
    839                 system_libs.appendAssumeCapacity(.{ .path = resolved_path });
    840             }
    841         } else if (target.isGnuLibC()) {
    842             try system_libs.ensureUnusedCapacity(glibc.libs.len + 1);
    843             for (glibc.libs) |lib| {
    844                 if (lib.removed_in) |rem_in| {
    845                     if (target.os.version_range.linux.glibc.order(rem_in) != .lt) continue;
    846                 }
    847 
    848                 const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{
    849                     comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
    850                 });
    851                 system_libs.appendAssumeCapacity(.{ .path = lib_path });
    852             }
    853             system_libs.appendAssumeCapacity(.{
    854                 .path = try comp.get_libc_crt_file(arena, "libc_nonshared.a"),
    855             });
    856         } else if (target.isMusl()) {
    857             const path = try comp.get_libc_crt_file(arena, switch (link_mode) {
    858                 .static => "libc.a",
    859                 .dynamic => "libc.so",
    860             });
    861             try system_libs.append(.{ .path = path });
    862         } else {
    863             comp.link_error_flags.missing_libc = true;
    864         }
    865     }
    866 
    867     for (system_libs.items) |lib| {
    868         try self.parseLibraryReportingFailure(lib, false);
    869     }
    870 
    871     // Finally, as the last input objects we add compiler_rt and CSU postlude (if any).
    872 
    873     // compiler-rt. Since compiler_rt exports symbols like `memset`, it needs
    874     // to be after the shared libraries, so they are picked up from the shared
    875     // libraries, not libcompiler_rt.
    876     if (comp.compiler_rt_lib) |crt_file| {
    877         try parseLibraryReportingFailure(self, .{ .path = crt_file.full_object_path }, false);
    878     } else if (comp.compiler_rt_obj) |crt_file| {
    879         try parseObjectReportingFailure(self, crt_file.full_object_path);
    880     }
    881 
    882     // csu postlude
    883     if (csu.crtend) |path| try parseObjectReportingFailure(self, path);
    884     if (csu.crtn) |path| try parseObjectReportingFailure(self, path);
    885 
    886     if (self.base.hasErrors()) return error.FlushFailure;
    887 
    888     // Dedup shared objects
    889     {
    890         var seen_dsos = std.StringHashMap(void).init(gpa);
    891         defer seen_dsos.deinit();
    892         try seen_dsos.ensureTotalCapacity(@as(u32, @intCast(self.shared_objects.items.len)));
    893 
    894         var i: usize = 0;
    895         while (i < self.shared_objects.items.len) {
    896             const index = self.shared_objects.items[i];
    897             const shared_object = self.file(index).?.shared_object;
    898             const soname = shared_object.soname();
    899             const gop = seen_dsos.getOrPutAssumeCapacity(soname);
    900             if (gop.found_existing) {
    901                 _ = self.shared_objects.orderedRemove(i);
    902             } else i += 1;
    903         }
    904     }
    905 
    906     // If we haven't already, create a linker-generated input file comprising of
    907     // linker-defined synthetic symbols only such as `_DYNAMIC`, etc.
    908     if (self.linker_defined_index == null) {
    909         const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
    910         self.files.set(index, .{ .linker_defined = .{ .index = index } });
    911         self.linker_defined_index = index;
    912         const object = self.linkerDefinedPtr().?;
    913         try object.init(gpa);
    914         try object.initSymbols(self);
    915     }
    916 
    917     // Now, we are ready to resolve the symbols across all input files.
    918     // We will first resolve the files in the ZigObject, next in the parsed
    919     // input Object files.
    920     // Any qualifing unresolved symbol will be upgraded to an absolute, weak
    921     // symbol for potential resolution at load-time.
    922     try self.resolveSymbols();
    923     self.markEhFrameAtomsDead();
    924     try self.resolveMergeSections();
    925 
    926     for (self.objects.items) |index| {
    927         try self.file(index).?.object.convertCommonSymbols(self);
    928     }
    929     self.markImportsExports();
    930 
    931     if (self.base.gc_sections) {
    932         try gc.gcAtoms(self);
    933 
    934         if (self.base.print_gc_sections) {
    935             try gc.dumpPrunedAtoms(self);
    936         }
    937     }
    938 
    939     self.checkDuplicates() catch |err| switch (err) {
    940         error.HasDuplicates => return error.FlushFailure,
    941         else => |e| return e,
    942     };
    943 
    944     try self.addCommentString();
    945     try self.finalizeMergeSections();
    946     try self.initOutputSections();
    947     if (self.linkerDefinedPtr()) |obj| {
    948         try obj.initStartStopSymbols(self);
    949     }
    950     self.claimUnresolved();
    951 
    952     // Scan and create missing synthetic entries such as GOT indirection.
    953     try self.scanRelocs();
    954 
    955     // Generate and emit synthetic sections.
    956     try self.initSyntheticSections();
    957     try self.initSpecialPhdrs();
    958     try self.sortShdrs();
    959 
    960     try self.setDynamicSection(self.rpath_table.keys());
    961     self.sortDynamicSymtab();
    962     try self.setHashSections();
    963     try self.setVersionSymtab();
    964 
    965     try self.sortInitFini();
    966     try self.updateMergeSectionSizes();
    967     try self.updateSectionSizes();
    968 
    969     try self.addLoadPhdrs();
    970     try self.allocatePhdrTable();
    971     try self.allocateAllocSections();
    972     try self.sortPhdrs();
    973     try self.allocateNonAllocSections();
    974     self.allocateSpecialPhdrs();
    975     if (self.linkerDefinedPtr()) |obj| {
    976         obj.allocateSymbols(self);
    977     }
    978 
    979     // Dump the state for easy debugging.
    980     // State can be dumped via `--debug-log link_state`.
    981     if (build_options.enable_logging) {
    982         state_log.debug("{}", .{self.dumpState()});
    983     }
    984 
    985     // Beyond this point, everything has been allocated a virtual address and we can resolve
    986     // the relocations, and commit objects to file.
    987     for (self.objects.items) |index| {
    988         self.file(index).?.object.dirty = false;
    989     }
    990     // TODO: would state tracking be more appropriate here? perhaps even custom relocation type?
    991     self.rela_dyn.clearRetainingCapacity();
    992     self.rela_plt.clearRetainingCapacity();
    993 
    994     if (self.zigObjectPtr()) |zo| {
    995         var has_reloc_errors = false;
    996         for (zo.atoms_indexes.items) |atom_index| {
    997             const atom_ptr = zo.atom(atom_index) orelse continue;
    998             if (!atom_ptr.alive) continue;
    999             const out_shndx = atom_ptr.output_section_index;
   1000             const shdr = &self.sections.items(.shdr)[out_shndx];
   1001             if (shdr.sh_type == elf.SHT_NOBITS) continue;
   1002             const code = try zo.codeAlloc(self, atom_index);
   1003             defer gpa.free(code);
   1004             const file_offset = atom_ptr.offset(self);
   1005             atom_ptr.resolveRelocsAlloc(self, code) catch |err| switch (err) {
   1006                 error.RelocFailure, error.RelaxFailure => has_reloc_errors = true,
   1007                 error.UnsupportedCpuArch => {
   1008                     try self.reportUnsupportedCpuArch();
   1009                     return error.FlushFailure;
   1010                 },
   1011                 else => |e| return e,
   1012             };
   1013             try self.base.file.?.pwriteAll(code, file_offset);
   1014         }
   1015 
   1016         if (has_reloc_errors) return error.FlushFailure;
   1017     }
   1018 
   1019     try self.writePhdrTable();
   1020     try self.writeShdrTable();
   1021     try self.writeAtoms();
   1022     try self.writeMergeSections();
   1023 
   1024     self.writeSyntheticSections() catch |err| switch (err) {
   1025         error.RelocFailure => return error.FlushFailure,
   1026         error.UnsupportedCpuArch => {
   1027             try self.reportUnsupportedCpuArch();
   1028             return error.FlushFailure;
   1029         },
   1030         else => |e| return e,
   1031     };
   1032 
   1033     if (self.base.isExe() and self.linkerDefinedPtr().?.entry_index == null) {
   1034         log.debug("flushing. no_entry_point_found = true", .{});
   1035         comp.link_error_flags.no_entry_point_found = true;
   1036     } else {
   1037         log.debug("flushing. no_entry_point_found = false", .{});
   1038         comp.link_error_flags.no_entry_point_found = false;
   1039         try self.writeElfHeader();
   1040     }
   1041 
   1042     if (self.base.hasErrors()) return error.FlushFailure;
   1043 }
   1044 
   1045 /// --verbose-link output
   1046 fn dumpArgv(self: *Elf, comp: *Compilation) !void {
   1047     const gpa = self.base.comp.gpa;
   1048     var arena_allocator = std.heap.ArenaAllocator.init(gpa);
   1049     defer arena_allocator.deinit();
   1050     const arena = arena_allocator.allocator();
   1051 
   1052     const target = self.getTarget();
   1053     const link_mode = self.base.comp.config.link_mode;
   1054     const directory = self.base.emit.root_dir; // Just an alias to make it shorter to type.
   1055     const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path});
   1056     const module_obj_path: ?[]const u8 = if (self.base.zcu_object_sub_path) |path| blk: {
   1057         if (fs.path.dirname(full_out_path)) |dirname| {
   1058             break :blk try fs.path.join(arena, &.{ dirname, path });
   1059         } else {
   1060             break :blk path;
   1061         }
   1062     } else null;
   1063 
   1064     const csu = try CsuObjects.init(arena, comp);
   1065     const compiler_rt_path: ?[]const u8 = blk: {
   1066         if (comp.compiler_rt_lib) |x| break :blk x.full_object_path;
   1067         if (comp.compiler_rt_obj) |x| break :blk x.full_object_path;
   1068         break :blk null;
   1069     };
   1070 
   1071     var argv = std.ArrayList([]const u8).init(arena);
   1072 
   1073     try argv.append("zig");
   1074 
   1075     if (self.base.isStaticLib()) {
   1076         try argv.append("ar");
   1077     } else {
   1078         try argv.append("ld");
   1079     }
   1080 
   1081     if (self.base.isObject()) {
   1082         try argv.append("-r");
   1083     }
   1084 
   1085     try argv.append("-o");
   1086     try argv.append(full_out_path);
   1087 
   1088     if (self.base.isRelocatable()) {
   1089         for (comp.objects) |obj| {
   1090             try argv.append(obj.path);
   1091         }
   1092 
   1093         for (comp.c_object_table.keys()) |key| {
   1094             try argv.append(key.status.success.object_path);
   1095         }
   1096 
   1097         if (module_obj_path) |p| {
   1098             try argv.append(p);
   1099         }
   1100     } else {
   1101         if (!self.base.isStatic()) {
   1102             if (target.dynamic_linker.get()) |path| {
   1103                 try argv.append("-dynamic-linker");
   1104                 try argv.append(path);
   1105             }
   1106         }
   1107 
   1108         if (self.base.isDynLib()) {
   1109             if (self.soname) |name| {
   1110                 try argv.append("-soname");
   1111                 try argv.append(name);
   1112             }
   1113         }
   1114 
   1115         if (self.entry_name) |name| {
   1116             try argv.appendSlice(&.{ "--entry", name });
   1117         }
   1118 
   1119         for (self.rpath_table.keys()) |rpath| {
   1120             try argv.appendSlice(&.{ "-rpath", rpath });
   1121         }
   1122 
   1123         try argv.appendSlice(&.{
   1124             "-z",
   1125             try std.fmt.allocPrint(arena, "stack-size={d}", .{self.base.stack_size}),
   1126         });
   1127 
   1128         try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{self.image_base}));
   1129 
   1130         if (self.base.gc_sections) {
   1131             try argv.append("--gc-sections");
   1132         }
   1133 
   1134         if (self.base.print_gc_sections) {
   1135             try argv.append("--print-gc-sections");
   1136         }
   1137 
   1138         if (comp.link_eh_frame_hdr) {
   1139             try argv.append("--eh-frame-hdr");
   1140         }
   1141 
   1142         if (comp.config.rdynamic) {
   1143             try argv.append("--export-dynamic");
   1144         }
   1145 
   1146         if (self.z_notext) {
   1147             try argv.append("-z");
   1148             try argv.append("notext");
   1149         }
   1150 
   1151         if (self.z_nocopyreloc) {
   1152             try argv.append("-z");
   1153             try argv.append("nocopyreloc");
   1154         }
   1155 
   1156         if (self.z_now) {
   1157             try argv.append("-z");
   1158             try argv.append("now");
   1159         }
   1160 
   1161         if (self.base.isStatic()) {
   1162             try argv.append("-static");
   1163         } else if (self.isEffectivelyDynLib()) {
   1164             try argv.append("-shared");
   1165         }
   1166 
   1167         if (comp.config.pie and self.base.isExe()) {
   1168             try argv.append("-pie");
   1169         }
   1170 
   1171         if (comp.config.debug_format == .strip) {
   1172             try argv.append("-s");
   1173         }
   1174 
   1175         // csu prelude
   1176         if (csu.crt0) |v| try argv.append(v);
   1177         if (csu.crti) |v| try argv.append(v);
   1178         if (csu.crtbegin) |v| try argv.append(v);
   1179 
   1180         for (self.lib_dirs) |lib_dir| {
   1181             try argv.append("-L");
   1182             try argv.append(lib_dir);
   1183         }
   1184 
   1185         if (comp.config.link_libc) {
   1186             if (self.base.comp.libc_installation) |libc_installation| {
   1187                 try argv.append("-L");
   1188                 try argv.append(libc_installation.crt_dir.?);
   1189             }
   1190         }
   1191 
   1192         var whole_archive = false;
   1193         for (comp.objects) |obj| {
   1194             if (obj.must_link and !whole_archive) {
   1195                 try argv.append("-whole-archive");
   1196                 whole_archive = true;
   1197             } else if (!obj.must_link and whole_archive) {
   1198                 try argv.append("-no-whole-archive");
   1199                 whole_archive = false;
   1200             }
   1201 
   1202             if (obj.loption) {
   1203                 assert(obj.path[0] == ':');
   1204                 try argv.append("-l");
   1205             }
   1206             try argv.append(obj.path);
   1207         }
   1208         if (whole_archive) {
   1209             try argv.append("-no-whole-archive");
   1210             whole_archive = false;
   1211         }
   1212 
   1213         for (comp.c_object_table.keys()) |key| {
   1214             try argv.append(key.status.success.object_path);
   1215         }
   1216 
   1217         if (module_obj_path) |p| {
   1218             try argv.append(p);
   1219         }
   1220 
   1221         if (comp.config.any_sanitize_thread) {
   1222             try argv.append(comp.tsan_lib.?.full_object_path);
   1223         }
   1224 
   1225         if (comp.config.any_fuzz) {
   1226             try argv.append(comp.fuzzer_lib.?.full_object_path);
   1227         }
   1228 
   1229         // libc
   1230         if (!comp.skip_linker_dependencies and !comp.config.link_libc) {
   1231             if (comp.libc_static_lib) |lib| {
   1232                 try argv.append(lib.full_object_path);
   1233             }
   1234         }
   1235 
   1236         // Shared libraries.
   1237         // Worst-case, we need an --as-needed argument for every lib, as well
   1238         // as one before and one after.
   1239         try argv.ensureUnusedCapacity(self.base.comp.system_libs.keys().len * 2 + 2);
   1240         argv.appendAssumeCapacity("--as-needed");
   1241         var as_needed = true;
   1242 
   1243         for (self.base.comp.system_libs.values()) |lib_info| {
   1244             const lib_as_needed = !lib_info.needed;
   1245             switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) {
   1246                 0b00, 0b11 => {},
   1247                 0b01 => {
   1248                     argv.appendAssumeCapacity("--no-as-needed");
   1249                     as_needed = false;
   1250                 },
   1251                 0b10 => {
   1252                     argv.appendAssumeCapacity("--as-needed");
   1253                     as_needed = true;
   1254                 },
   1255             }
   1256             argv.appendAssumeCapacity(lib_info.path.?);
   1257         }
   1258 
   1259         if (!as_needed) {
   1260             argv.appendAssumeCapacity("--as-needed");
   1261             as_needed = true;
   1262         }
   1263 
   1264         // libc++ dep
   1265         if (comp.config.link_libcpp) {
   1266             try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
   1267             try argv.append(comp.libcxx_static_lib.?.full_object_path);
   1268         }
   1269 
   1270         // libunwind dep
   1271         if (comp.config.link_libunwind) {
   1272             try argv.append(comp.libunwind_static_lib.?.full_object_path);
   1273         }
   1274 
   1275         // libc dep
   1276         if (comp.config.link_libc) {
   1277             if (self.base.comp.libc_installation != null) {
   1278                 const needs_grouping = link_mode == .static;
   1279                 if (needs_grouping) try argv.append("--start-group");
   1280                 try argv.appendSlice(target_util.libcFullLinkFlags(target));
   1281                 if (needs_grouping) try argv.append("--end-group");
   1282             } else if (target.isGnuLibC()) {
   1283                 for (glibc.libs) |lib| {
   1284                     if (lib.removed_in) |rem_in| {
   1285                         if (target.os.version_range.linux.glibc.order(rem_in) != .lt) continue;
   1286                     }
   1287 
   1288                     const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{
   1289                         comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
   1290                     });
   1291                     try argv.append(lib_path);
   1292                 }
   1293                 try argv.append(try comp.get_libc_crt_file(arena, "libc_nonshared.a"));
   1294             } else if (target.isMusl()) {
   1295                 try argv.append(try comp.get_libc_crt_file(arena, switch (link_mode) {
   1296                     .static => "libc.a",
   1297                     .dynamic => "libc.so",
   1298                 }));
   1299             }
   1300         }
   1301 
   1302         // compiler-rt
   1303         if (compiler_rt_path) |p| {
   1304             try argv.append(p);
   1305         }
   1306 
   1307         // crt postlude
   1308         if (csu.crtend) |v| try argv.append(v);
   1309         if (csu.crtn) |v| try argv.append(v);
   1310     }
   1311 
   1312     Compilation.dump_argv(argv.items);
   1313 }
   1314 
   1315 pub const ParseError = error{
   1316     /// Indicates the error is already reported on `Compilation.link_errors`.
   1317     LinkFailure,
   1318 
   1319     OutOfMemory,
   1320     Overflow,
   1321     InputOutput,
   1322     EndOfStream,
   1323     FileSystem,
   1324     NotSupported,
   1325     InvalidCharacter,
   1326     UnknownFileType,
   1327 } || LdScript.Error || fs.Dir.AccessError || fs.File.SeekError || fs.File.OpenError || fs.File.ReadError;
   1328 
   1329 fn parseCrtFileReportingFailure(self: *Elf, crt_file: Compilation.CRTFile) error{OutOfMemory}!void {
   1330     if (crt_file.isObject()) {
   1331         try parseObjectReportingFailure(self, crt_file.full_object_path);
   1332     } else {
   1333         try parseLibraryReportingFailure(self, .{ .path = crt_file.full_object_path }, false);
   1334     }
   1335 }
   1336 
   1337 pub fn parseObjectReportingFailure(self: *Elf, path: []const u8) error{OutOfMemory}!void {
   1338     self.parseObject(path) catch |err| switch (err) {
   1339         error.LinkFailure => return, // already reported
   1340         error.OutOfMemory => return error.OutOfMemory,
   1341         else => |e| try self.addParseError(path, "unable to parse object: {s}", .{@errorName(e)}),
   1342     };
   1343 }
   1344 
   1345 pub fn parseLibraryReportingFailure(self: *Elf, lib: SystemLib, must_link: bool) error{OutOfMemory}!void {
   1346     self.parseLibrary(lib, must_link) catch |err| switch (err) {
   1347         error.LinkFailure => return, // already reported
   1348         error.OutOfMemory => return error.OutOfMemory,
   1349         else => |e| try self.addParseError(lib.path, "unable to parse library: {s}", .{@errorName(e)}),
   1350     };
   1351 }
   1352 
   1353 fn parseLibrary(self: *Elf, lib: SystemLib, must_link: bool) ParseError!void {
   1354     const tracy = trace(@src());
   1355     defer tracy.end();
   1356     if (try Archive.isArchive(lib.path)) {
   1357         try self.parseArchive(lib.path, must_link);
   1358     } else if (try SharedObject.isSharedObject(lib.path)) {
   1359         try self.parseSharedObject(lib);
   1360     } else {
   1361         try self.parseLdScript(lib);
   1362     }
   1363 }
   1364 
   1365 fn parseObject(self: *Elf, path: []const u8) ParseError!void {
   1366     const tracy = trace(@src());
   1367     defer tracy.end();
   1368 
   1369     const gpa = self.base.comp.gpa;
   1370     const handle = try fs.cwd().openFile(path, .{});
   1371     const fh = try self.addFileHandle(handle);
   1372 
   1373     const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
   1374     self.files.set(index, .{ .object = .{
   1375         .path = try gpa.dupe(u8, path),
   1376         .file_handle = fh,
   1377         .index = index,
   1378     } });
   1379     try self.objects.append(gpa, index);
   1380 
   1381     const object = self.file(index).?.object;
   1382     try object.parse(self);
   1383 }
   1384 
   1385 fn parseArchive(self: *Elf, path: []const u8, must_link: bool) ParseError!void {
   1386     const tracy = trace(@src());
   1387     defer tracy.end();
   1388 
   1389     const gpa = self.base.comp.gpa;
   1390     const handle = try fs.cwd().openFile(path, .{});
   1391     const fh = try self.addFileHandle(handle);
   1392 
   1393     var archive = Archive{};
   1394     defer archive.deinit(gpa);
   1395     try archive.parse(self, path, fh);
   1396 
   1397     const objects = try archive.objects.toOwnedSlice(gpa);
   1398     defer gpa.free(objects);
   1399 
   1400     for (objects) |extracted| {
   1401         const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
   1402         self.files.set(index, .{ .object = extracted });
   1403         const object = &self.files.items(.data)[index].object;
   1404         object.index = index;
   1405         object.alive = must_link;
   1406         try object.parse(self);
   1407         try self.objects.append(gpa, index);
   1408     }
   1409 }
   1410 
   1411 fn parseSharedObject(self: *Elf, lib: SystemLib) ParseError!void {
   1412     const tracy = trace(@src());
   1413     defer tracy.end();
   1414 
   1415     const gpa = self.base.comp.gpa;
   1416     const handle = try fs.cwd().openFile(lib.path, .{});
   1417     defer handle.close();
   1418 
   1419     const index = @as(File.Index, @intCast(try self.files.addOne(gpa)));
   1420     self.files.set(index, .{ .shared_object = .{
   1421         .path = try gpa.dupe(u8, lib.path),
   1422         .index = index,
   1423         .needed = lib.needed,
   1424         .alive = lib.needed,
   1425     } });
   1426     try self.shared_objects.append(gpa, index);
   1427 
   1428     const shared_object = self.file(index).?.shared_object;
   1429     try shared_object.parse(self, handle);
   1430 }
   1431 
   1432 fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void {
   1433     const tracy = trace(@src());
   1434     defer tracy.end();
   1435 
   1436     const gpa = self.base.comp.gpa;
   1437     const in_file = try fs.cwd().openFile(lib.path, .{});
   1438     defer in_file.close();
   1439     const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32));
   1440     defer gpa.free(data);
   1441 
   1442     var script = LdScript{ .path = lib.path };
   1443     defer script.deinit(gpa);
   1444     try script.parse(data, self);
   1445 
   1446     var arena_allocator = std.heap.ArenaAllocator.init(gpa);
   1447     defer arena_allocator.deinit();
   1448     const arena = arena_allocator.allocator();
   1449 
   1450     var test_path = std.ArrayList(u8).init(arena);
   1451     var checked_paths = std.ArrayList([]const u8).init(arena);
   1452 
   1453     for (script.args.items) |scr_obj| {
   1454         checked_paths.clearRetainingCapacity();
   1455 
   1456         success: {
   1457             if (mem.startsWith(u8, scr_obj.path, "-l")) {
   1458                 const lib_name = scr_obj.path["-l".len..];
   1459 
   1460                 // TODO I think technically we should re-use the mechanism used by the frontend here.
   1461                 // Maybe we should hoist search-strategy all the way here?
   1462                 for (self.lib_dirs) |lib_dir| {
   1463                     if (!self.base.isStatic()) {
   1464                         if (try self.accessLibPath(arena, &test_path, &checked_paths, lib_dir, lib_name, .dynamic))
   1465                             break :success;
   1466                     }
   1467                     if (try self.accessLibPath(arena, &test_path, &checked_paths, lib_dir, lib_name, .static))
   1468                         break :success;
   1469                 }
   1470             } else {
   1471                 var buffer: [fs.max_path_bytes]u8 = undefined;
   1472                 if (fs.realpath(scr_obj.path, &buffer)) |path| {
   1473                     test_path.clearRetainingCapacity();
   1474                     try test_path.writer().writeAll(path);
   1475                     break :success;
   1476                 } else |_| {}
   1477 
   1478                 try checked_paths.append(try arena.dupe(u8, scr_obj.path));
   1479                 for (self.lib_dirs) |lib_dir| {
   1480                     if (try self.accessLibPath(arena, &test_path, &checked_paths, lib_dir, scr_obj.path, null))
   1481                         break :success;
   1482                 }
   1483             }
   1484 
   1485             try self.reportMissingLibraryError(
   1486                 checked_paths.items,
   1487                 "missing library dependency: GNU ld script '{s}' requires '{s}', but file not found",
   1488                 .{
   1489                     lib.path,
   1490                     scr_obj.path,
   1491                 },
   1492             );
   1493             continue;
   1494         }
   1495 
   1496         const full_path = test_path.items;
   1497         self.parseLibrary(.{
   1498             .needed = scr_obj.needed,
   1499             .path = full_path,
   1500         }, false) catch |err| switch (err) {
   1501             error.LinkFailure => continue, // already reported
   1502             else => |e| try self.addParseError(
   1503                 full_path,
   1504                 "unexpected error: parsing library failed with error {s}",
   1505                 .{@errorName(e)},
   1506             ),
   1507         };
   1508     }
   1509 }
   1510 
   1511 pub fn validateEFlags(self: *Elf, file_index: File.Index, e_flags: elf.Elf64_Word) !void {
   1512     if (self.first_eflags == null) {
   1513         self.first_eflags = e_flags;
   1514         return; // there isn't anything to conflict with yet
   1515     }
   1516     const self_eflags: *elf.Elf64_Word = &self.first_eflags.?;
   1517 
   1518     switch (self.getTarget().cpu.arch) {
   1519         .riscv64 => {
   1520             if (e_flags != self_eflags.*) {
   1521                 const riscv_eflags: riscv.RiscvEflags = @bitCast(e_flags);
   1522                 const self_riscv_eflags: *riscv.RiscvEflags = @ptrCast(self_eflags);
   1523 
   1524                 self_riscv_eflags.rvc = self_riscv_eflags.rvc or riscv_eflags.rvc;
   1525                 self_riscv_eflags.tso = self_riscv_eflags.tso or riscv_eflags.tso;
   1526 
   1527                 var any_errors: bool = false;
   1528                 if (self_riscv_eflags.fabi != riscv_eflags.fabi) {
   1529                     any_errors = true;
   1530                     try self.addFileError(
   1531                         file_index,
   1532                         "cannot link object files with different float-point ABIs",
   1533                         .{},
   1534                     );
   1535                 }
   1536                 if (self_riscv_eflags.rve != riscv_eflags.rve) {
   1537                     any_errors = true;
   1538                     try self.addFileError(
   1539                         file_index,
   1540                         "cannot link object files with different RVEs",
   1541                         .{},
   1542                     );
   1543                 }
   1544                 if (any_errors) return error.LinkFailure;
   1545             }
   1546         },
   1547         else => {},
   1548     }
   1549 }
   1550 
   1551 fn accessLibPath(
   1552     self: *Elf,
   1553     arena: Allocator,
   1554     test_path: *std.ArrayList(u8),
   1555     checked_paths: ?*std.ArrayList([]const u8),
   1556     lib_dir_path: []const u8,
   1557     lib_name: []const u8,
   1558     link_mode: ?std.builtin.LinkMode,
   1559 ) !bool {
   1560     const sep = fs.path.sep_str;
   1561     const target = self.getTarget();
   1562     test_path.clearRetainingCapacity();
   1563     const prefix = if (link_mode != null) "lib" else "";
   1564     const suffix = if (link_mode) |mode| switch (mode) {
   1565         .static => target.staticLibSuffix(),
   1566         .dynamic => target.dynamicLibSuffix(),
   1567     } else "";
   1568     try test_path.writer().print("{s}" ++ sep ++ "{s}{s}{s}", .{
   1569         lib_dir_path,
   1570         prefix,
   1571         lib_name,
   1572         suffix,
   1573     });
   1574     if (checked_paths) |cpaths| {
   1575         try cpaths.append(try arena.dupe(u8, test_path.items));
   1576     }
   1577     fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
   1578         error.FileNotFound => return false,
   1579         else => |e| return e,
   1580     };
   1581     return true;
   1582 }
   1583 
   1584 /// When resolving symbols, we approach the problem similarly to `mold`.
   1585 /// 1. Resolve symbols across all objects (including those preemptively extracted archives).
   1586 /// 2. Resolve symbols across all shared objects.
   1587 /// 3. Mark live objects (see `Elf.markLive`)
   1588 /// 4. Reset state of all resolved globals since we will redo this bit on the pruned set.
   1589 /// 5. Remove references to dead objects/shared objects
   1590 /// 6. Re-run symbol resolution on pruned objects and shared objects sets.
   1591 pub fn resolveSymbols(self: *Elf) !void {
   1592     // Resolve symbols in the ZigObject. For now, we assume that it's always live.
   1593     if (self.zigObjectPtr()) |zo| try zo.asFile().resolveSymbols(self);
   1594     // Resolve symbols on the set of all objects and shared objects (even if some are unneeded).
   1595     for (self.objects.items) |index| try self.file(index).?.resolveSymbols(self);
   1596     for (self.shared_objects.items) |index| try self.file(index).?.resolveSymbols(self);
   1597     if (self.linkerDefinedPtr()) |obj| try obj.asFile().resolveSymbols(self);
   1598 
   1599     // Mark live objects.
   1600     self.markLive();
   1601 
   1602     // Reset state of all globals after marking live objects.
   1603     self.resolver.reset();
   1604 
   1605     // Prune dead objects and shared objects.
   1606     var i: usize = 0;
   1607     while (i < self.objects.items.len) {
   1608         const index = self.objects.items[i];
   1609         if (!self.file(index).?.isAlive()) {
   1610             _ = self.objects.orderedRemove(i);
   1611         } else i += 1;
   1612     }
   1613     i = 0;
   1614     while (i < self.shared_objects.items.len) {
   1615         const index = self.shared_objects.items[i];
   1616         if (!self.file(index).?.isAlive()) {
   1617             _ = self.shared_objects.orderedRemove(i);
   1618         } else i += 1;
   1619     }
   1620 
   1621     {
   1622         // Dedup comdat groups.
   1623         var table = std.StringHashMap(Ref).init(self.base.comp.gpa);
   1624         defer table.deinit();
   1625 
   1626         for (self.objects.items) |index| {
   1627             try self.file(index).?.object.resolveComdatGroups(self, &table);
   1628         }
   1629 
   1630         for (self.objects.items) |index| {
   1631             self.file(index).?.object.markComdatGroupsDead(self);
   1632         }
   1633     }
   1634 
   1635     // Re-resolve the symbols.
   1636     if (self.zigObjectPtr()) |zo| try zo.asFile().resolveSymbols(self);
   1637     for (self.objects.items) |index| try self.file(index).?.resolveSymbols(self);
   1638     for (self.shared_objects.items) |index| try self.file(index).?.resolveSymbols(self);
   1639     if (self.linkerDefinedPtr()) |obj| try obj.asFile().resolveSymbols(self);
   1640 }
   1641 
   1642 /// Traverses all objects and shared objects marking any object referenced by
   1643 /// a live object/shared object as alive itself.
   1644 /// This routine will prune unneeded objects extracted from archives and
   1645 /// unneeded shared objects.
   1646 fn markLive(self: *Elf) void {
   1647     if (self.zigObjectPtr()) |zig_object| zig_object.asFile().markLive(self);
   1648     for (self.objects.items) |index| {
   1649         const file_ptr = self.file(index).?;
   1650         if (file_ptr.isAlive()) file_ptr.markLive(self);
   1651     }
   1652     for (self.shared_objects.items) |index| {
   1653         const file_ptr = self.file(index).?;
   1654         if (file_ptr.isAlive()) file_ptr.markLive(self);
   1655     }
   1656 }
   1657 
   1658 pub fn markEhFrameAtomsDead(self: *Elf) void {
   1659     for (self.objects.items) |index| {
   1660         const file_ptr = self.file(index).?;
   1661         if (!file_ptr.isAlive()) continue;
   1662         file_ptr.object.markEhFrameAtomsDead(self);
   1663     }
   1664 }
   1665 
   1666 fn markImportsExports(self: *Elf) void {
   1667     if (self.zigObjectPtr()) |zo| {
   1668         zo.markImportsExports(self);
   1669     }
   1670     for (self.objects.items) |index| {
   1671         self.file(index).?.object.markImportsExports(self);
   1672     }
   1673     if (!self.isEffectivelyDynLib()) {
   1674         for (self.shared_objects.items) |index| {
   1675             self.file(index).?.shared_object.markImportExports(self);
   1676         }
   1677     }
   1678 }
   1679 
   1680 fn claimUnresolved(self: *Elf) void {
   1681     if (self.zigObjectPtr()) |zig_object| {
   1682         zig_object.claimUnresolved(self);
   1683     }
   1684     for (self.objects.items) |index| {
   1685         self.file(index).?.object.claimUnresolved(self);
   1686     }
   1687 }
   1688 
   1689 /// In scanRelocs we will go over all live atoms and scan their relocs.
   1690 /// This will help us work out what synthetics to emit, GOT indirection, etc.
   1691 /// This is also the point where we will report undefined symbols for any
   1692 /// alloc sections.
   1693 fn scanRelocs(self: *Elf) !void {
   1694     const gpa = self.base.comp.gpa;
   1695 
   1696     var undefs = std.AutoArrayHashMap(SymbolResolver.Index, std.ArrayList(Ref)).init(gpa);
   1697     defer {
   1698         for (undefs.values()) |*refs| {
   1699             refs.deinit();
   1700         }
   1701         undefs.deinit();
   1702     }
   1703 
   1704     var has_reloc_errors = false;
   1705     if (self.zigObjectPtr()) |zo| {
   1706         zo.asFile().scanRelocs(self, &undefs) catch |err| switch (err) {
   1707             error.RelaxFailure => unreachable,
   1708             error.UnsupportedCpuArch => {
   1709                 try self.reportUnsupportedCpuArch();
   1710                 return error.FlushFailure;
   1711             },
   1712             error.RelocFailure => has_reloc_errors = true,
   1713             else => |e| return e,
   1714         };
   1715     }
   1716     for (self.objects.items) |index| {
   1717         self.file(index).?.scanRelocs(self, &undefs) catch |err| switch (err) {
   1718             error.RelaxFailure => unreachable,
   1719             error.UnsupportedCpuArch => {
   1720                 try self.reportUnsupportedCpuArch();
   1721                 return error.FlushFailure;
   1722             },
   1723             error.RelocFailure => has_reloc_errors = true,
   1724             else => |e| return e,
   1725         };
   1726     }
   1727 
   1728     try self.reportUndefinedSymbols(&undefs);
   1729 
   1730     if (has_reloc_errors) return error.FlushFailure;
   1731 
   1732     if (self.zigObjectPtr()) |zo| {
   1733         try zo.asFile().createSymbolIndirection(self);
   1734     }
   1735     for (self.objects.items) |index| {
   1736         try self.file(index).?.createSymbolIndirection(self);
   1737     }
   1738     for (self.shared_objects.items) |index| {
   1739         try self.file(index).?.createSymbolIndirection(self);
   1740     }
   1741     if (self.linkerDefinedPtr()) |obj| {
   1742         try obj.asFile().createSymbolIndirection(self);
   1743     }
   1744     if (self.got.flags.needs_tlsld) {
   1745         log.debug("program needs TLSLD", .{});
   1746         try self.got.addTlsLdSymbol(self);
   1747     }
   1748 }
   1749 
   1750 pub fn initOutputSection(self: *Elf, args: struct {
   1751     name: [:0]const u8,
   1752     flags: u64,
   1753     type: u32,
   1754 }) error{OutOfMemory}!u32 {
   1755     const name = blk: {
   1756         if (self.base.isRelocatable()) break :blk args.name;
   1757         if (args.flags & elf.SHF_MERGE != 0) break :blk args.name;
   1758         const name_prefixes: []const [:0]const u8 = &.{
   1759             ".text",       ".data.rel.ro", ".data", ".rodata", ".bss.rel.ro",       ".bss",
   1760             ".init_array", ".fini_array",  ".tbss", ".tdata",  ".gcc_except_table", ".ctors",
   1761             ".dtors",      ".gnu.warning",
   1762         };
   1763         inline for (name_prefixes) |prefix| {
   1764             if (std.mem.eql(u8, args.name, prefix) or std.mem.startsWith(u8, args.name, prefix ++ ".")) {
   1765                 break :blk prefix;
   1766             }
   1767         }
   1768         break :blk args.name;
   1769     };
   1770     const @"type" = tt: {
   1771         if (self.getTarget().cpu.arch == .x86_64 and args.type == elf.SHT_X86_64_UNWIND)
   1772             break :tt elf.SHT_PROGBITS;
   1773         switch (args.type) {
   1774             elf.SHT_NULL => unreachable,
   1775             elf.SHT_PROGBITS => {
   1776                 if (std.mem.eql(u8, args.name, ".init_array") or std.mem.startsWith(u8, args.name, ".init_array."))
   1777                     break :tt elf.SHT_INIT_ARRAY;
   1778                 if (std.mem.eql(u8, args.name, ".fini_array") or std.mem.startsWith(u8, args.name, ".fini_array."))
   1779                     break :tt elf.SHT_FINI_ARRAY;
   1780                 break :tt args.type;
   1781             },
   1782             else => break :tt args.type,
   1783         }
   1784     };
   1785     const flags = blk: {
   1786         var flags = args.flags;
   1787         if (!self.base.isRelocatable()) {
   1788             flags &= ~@as(u64, elf.SHF_COMPRESSED | elf.SHF_GROUP | elf.SHF_GNU_RETAIN);
   1789         }
   1790         break :blk switch (@"type") {
   1791             elf.SHT_INIT_ARRAY, elf.SHT_FINI_ARRAY => flags | elf.SHF_WRITE,
   1792             else => flags,
   1793         };
   1794     };
   1795     const out_shndx = self.sectionByName(name) orelse try self.addSection(.{
   1796         .type = @"type",
   1797         .flags = flags,
   1798         .name = try self.insertShString(name),
   1799     });
   1800     return out_shndx;
   1801 }
   1802 
   1803 fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) !void {
   1804     dev.check(.lld_linker);
   1805 
   1806     const tracy = trace(@src());
   1807     defer tracy.end();
   1808 
   1809     const comp = self.base.comp;
   1810     const gpa = comp.gpa;
   1811 
   1812     const directory = self.base.emit.root_dir; // Just an alias to make it shorter to type.
   1813     const full_out_path = try directory.join(arena, &[_][]const u8{self.base.emit.sub_path});
   1814 
   1815     // If there is no Zig code to compile, then we should skip flushing the output file because it
   1816     // will not be part of the linker line anyway.
   1817     const module_obj_path: ?[]const u8 = if (comp.zcu != null) blk: {
   1818         try self.flushModule(arena, tid, prog_node);
   1819 
   1820         if (fs.path.dirname(full_out_path)) |dirname| {
   1821             break :blk try fs.path.join(arena, &.{ dirname, self.base.zcu_object_sub_path.? });
   1822         } else {
   1823             break :blk self.base.zcu_object_sub_path.?;
   1824         }
   1825     } else null;
   1826 
   1827     const sub_prog_node = prog_node.start("LLD Link", 0);
   1828     defer sub_prog_node.end();
   1829 
   1830     const output_mode = comp.config.output_mode;
   1831     const is_obj = output_mode == .Obj;
   1832     const is_lib = output_mode == .Lib;
   1833     const link_mode = comp.config.link_mode;
   1834     const is_dyn_lib = link_mode == .dynamic and is_lib;
   1835     const is_exe_or_dyn_lib = is_dyn_lib or output_mode == .Exe;
   1836     const have_dynamic_linker = comp.config.link_libc and
   1837         link_mode == .dynamic and is_exe_or_dyn_lib;
   1838     const target = self.getTarget();
   1839     const compiler_rt_path: ?[]const u8 = blk: {
   1840         if (comp.compiler_rt_lib) |x| break :blk x.full_object_path;
   1841         if (comp.compiler_rt_obj) |x| break :blk x.full_object_path;
   1842         break :blk null;
   1843     };
   1844 
   1845     // Here we want to determine whether we can save time by not invoking LLD when the
   1846     // output is unchanged. None of the linker options or the object files that are being
   1847     // linked are in the hash that namespaces the directory we are outputting to. Therefore,
   1848     // we must hash those now, and the resulting digest will form the "id" of the linking
   1849     // job we are about to perform.
   1850     // After a successful link, we store the id in the metadata of a symlink named "lld.id" in
   1851     // the artifact directory. So, now, we check if this symlink exists, and if it matches
   1852     // our digest. If so, we can skip linking. Otherwise, we proceed with invoking LLD.
   1853     const id_symlink_basename = "lld.id";
   1854 
   1855     var man: Cache.Manifest = undefined;
   1856     defer if (!self.base.disable_lld_caching) man.deinit();
   1857 
   1858     var digest: [Cache.hex_digest_len]u8 = undefined;
   1859 
   1860     if (!self.base.disable_lld_caching) {
   1861         man = comp.cache_parent.obtain();
   1862 
   1863         // We are about to obtain this lock, so here we give other processes a chance first.
   1864         self.base.releaseLock();
   1865 
   1866         comptime assert(Compilation.link_hash_implementation_version == 14);
   1867 
   1868         try man.addOptionalFile(self.linker_script);
   1869         try man.addOptionalFile(self.version_script);
   1870         man.hash.add(self.allow_undefined_version);
   1871         man.hash.addOptional(self.enable_new_dtags);
   1872         for (comp.objects) |obj| {
   1873             _ = try man.addFile(obj.path, null);
   1874             man.hash.add(obj.must_link);
   1875             man.hash.add(obj.loption);
   1876         }
   1877         for (comp.c_object_table.keys()) |key| {
   1878             _ = try man.addFile(key.status.success.object_path, null);
   1879         }
   1880         try man.addOptionalFile(module_obj_path);
   1881         try man.addOptionalFile(compiler_rt_path);
   1882         try man.addOptionalFile(if (comp.tsan_lib) |l| l.full_object_path else null);
   1883         try man.addOptionalFile(if (comp.fuzzer_lib) |l| l.full_object_path else null);
   1884 
   1885         // We can skip hashing libc and libc++ components that we are in charge of building from Zig
   1886         // installation sources because they are always a product of the compiler version + target information.
   1887         man.hash.addOptionalBytes(self.entry_name);
   1888         man.hash.add(self.image_base);
   1889         man.hash.add(self.base.gc_sections);
   1890         man.hash.addOptional(self.sort_section);
   1891         man.hash.add(comp.link_eh_frame_hdr);
   1892         man.hash.add(self.emit_relocs);
   1893         man.hash.add(comp.config.rdynamic);
   1894         man.hash.addListOfBytes(self.lib_dirs);
   1895         man.hash.addListOfBytes(self.rpath_table.keys());
   1896         if (output_mode == .Exe) {
   1897             man.hash.add(self.base.stack_size);
   1898             man.hash.add(self.base.build_id);
   1899         }
   1900         man.hash.addListOfBytes(self.symbol_wrap_set.keys());
   1901         man.hash.add(comp.skip_linker_dependencies);
   1902         man.hash.add(self.z_nodelete);
   1903         man.hash.add(self.z_notext);
   1904         man.hash.add(self.z_defs);
   1905         man.hash.add(self.z_origin);
   1906         man.hash.add(self.z_nocopyreloc);
   1907         man.hash.add(self.z_now);
   1908         man.hash.add(self.z_relro);
   1909         man.hash.add(self.z_common_page_size orelse 0);
   1910         man.hash.add(self.z_max_page_size orelse 0);
   1911         man.hash.add(self.hash_style);
   1912         // strip does not need to go into the linker hash because it is part of the hash namespace
   1913         if (comp.config.link_libc) {
   1914             man.hash.add(comp.libc_installation != null);
   1915             if (comp.libc_installation) |libc_installation| {
   1916                 man.hash.addBytes(libc_installation.crt_dir.?);
   1917             }
   1918             if (have_dynamic_linker) {
   1919                 man.hash.addOptionalBytes(target.dynamic_linker.get());
   1920             }
   1921         }
   1922         man.hash.addOptionalBytes(self.soname);
   1923         man.hash.addOptional(comp.version);
   1924         try link.hashAddSystemLibs(&man, comp.system_libs);
   1925         man.hash.addListOfBytes(comp.force_undefined_symbols.keys());
   1926         man.hash.add(self.base.allow_shlib_undefined);
   1927         man.hash.add(self.bind_global_refs_locally);
   1928         man.hash.add(self.compress_debug_sections);
   1929         man.hash.add(comp.config.any_sanitize_thread);
   1930         man.hash.add(comp.config.any_fuzz);
   1931         man.hash.addOptionalBytes(comp.sysroot);
   1932 
   1933         // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
   1934         _ = try man.hit();
   1935         digest = man.final();
   1936 
   1937         var prev_digest_buf: [digest.len]u8 = undefined;
   1938         const prev_digest: []u8 = Cache.readSmallFile(
   1939             directory.handle,
   1940             id_symlink_basename,
   1941             &prev_digest_buf,
   1942         ) catch |err| blk: {
   1943             log.debug("ELF LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
   1944             // Handle this as a cache miss.
   1945             break :blk prev_digest_buf[0..0];
   1946         };
   1947         if (mem.eql(u8, prev_digest, &digest)) {
   1948             log.debug("ELF LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
   1949             // Hot diggity dog! The output binary is already there.
   1950             self.base.lock = man.toOwnedLock();
   1951             return;
   1952         }
   1953         log.debug("ELF LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) });
   1954 
   1955         // We are about to change the output file to be different, so we invalidate the build hash now.
   1956         directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
   1957             error.FileNotFound => {},
   1958             else => |e| return e,
   1959         };
   1960     }
   1961 
   1962     // Due to a deficiency in LLD, we need to special-case BPF to a simple file
   1963     // copy when generating relocatables. Normally, we would expect `lld -r` to work.
   1964     // However, because LLD wants to resolve BPF relocations which it shouldn't, it fails
   1965     // before even generating the relocatable.
   1966     if (output_mode == .Obj and
   1967         (comp.config.lto or target.isBpfFreestanding()))
   1968     {
   1969         // In this case we must do a simple file copy
   1970         // here. TODO: think carefully about how we can avoid this redundant operation when doing
   1971         // build-obj. See also the corresponding TODO in linkAsArchive.
   1972         const the_object_path = blk: {
   1973             if (comp.objects.len != 0)
   1974                 break :blk comp.objects[0].path;
   1975 
   1976             if (comp.c_object_table.count() != 0)
   1977                 break :blk comp.c_object_table.keys()[0].status.success.object_path;
   1978 
   1979             if (module_obj_path) |p|
   1980                 break :blk p;
   1981 
   1982             // TODO I think this is unreachable. Audit this situation when solving the above TODO
   1983             // regarding eliding redundant object -> object transformations.
   1984             return error.NoObjectsToLink;
   1985         };
   1986         // This can happen when using --enable-cache and using the stage1 backend. In this case
   1987         // we can skip the file copy.
   1988         if (!mem.eql(u8, the_object_path, full_out_path)) {
   1989             try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
   1990         }
   1991     } else {
   1992         // Create an LLD command line and invoke it.
   1993         var argv = std.ArrayList([]const u8).init(gpa);
   1994         defer argv.deinit();
   1995         // We will invoke ourselves as a child process to gain access to LLD.
   1996         // This is necessary because LLD does not behave properly as a library -
   1997         // it calls exit() and does not reset all global data between invocations.
   1998         const linker_command = "ld.lld";
   1999         try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, linker_command });
   2000         if (is_obj) {
   2001             try argv.append("-r");
   2002         }
   2003 
   2004         try argv.append("--error-limit=0");
   2005 
   2006         if (comp.sysroot) |sysroot| {
   2007             try argv.append(try std.fmt.allocPrint(arena, "--sysroot={s}", .{sysroot}));
   2008         }
   2009 
   2010         if (comp.config.lto) {
   2011             switch (comp.root_mod.optimize_mode) {
   2012                 .Debug => {},
   2013                 .ReleaseSmall => try argv.append("--lto-O2"),
   2014                 .ReleaseFast, .ReleaseSafe => try argv.append("--lto-O3"),
   2015             }
   2016         }
   2017         switch (comp.root_mod.optimize_mode) {
   2018             .Debug => {},
   2019             .ReleaseSmall => try argv.append("-O2"),
   2020             .ReleaseFast, .ReleaseSafe => try argv.append("-O3"),
   2021         }
   2022 
   2023         if (self.entry_name) |name| {
   2024             try argv.appendSlice(&.{ "--entry", name });
   2025         }
   2026 
   2027         for (comp.force_undefined_symbols.keys()) |sym| {
   2028             try argv.append("-u");
   2029             try argv.append(sym);
   2030         }
   2031 
   2032         switch (self.hash_style) {
   2033             .gnu => try argv.append("--hash-style=gnu"),
   2034             .sysv => try argv.append("--hash-style=sysv"),
   2035             .both => {}, // this is the default
   2036         }
   2037 
   2038         if (output_mode == .Exe) {
   2039             try argv.appendSlice(&.{
   2040                 "-z",
   2041                 try std.fmt.allocPrint(arena, "stack-size={d}", .{self.base.stack_size}),
   2042             });
   2043 
   2044             switch (self.base.build_id) {
   2045                 .none => {},
   2046                 .fast, .uuid, .sha1, .md5 => {
   2047                     try argv.append(try std.fmt.allocPrint(arena, "--build-id={s}", .{
   2048                         @tagName(self.base.build_id),
   2049                     }));
   2050                 },
   2051                 .hexstring => |hs| {
   2052                     try argv.append(try std.fmt.allocPrint(arena, "--build-id=0x{s}", .{
   2053                         std.fmt.fmtSliceHexLower(hs.toSlice()),
   2054                     }));
   2055                 },
   2056             }
   2057         }
   2058 
   2059         try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{self.image_base}));
   2060 
   2061         if (self.linker_script) |linker_script| {
   2062             try argv.append("-T");
   2063             try argv.append(linker_script);
   2064         }
   2065 
   2066         if (self.sort_section) |how| {
   2067             const arg = try std.fmt.allocPrint(arena, "--sort-section={s}", .{@tagName(how)});
   2068             try argv.append(arg);
   2069         }
   2070 
   2071         if (self.base.gc_sections) {
   2072             try argv.append("--gc-sections");
   2073         }
   2074 
   2075         if (self.base.print_gc_sections) {
   2076             try argv.append("--print-gc-sections");
   2077         }
   2078 
   2079         if (self.print_icf_sections) {
   2080             try argv.append("--print-icf-sections");
   2081         }
   2082 
   2083         if (self.print_map) {
   2084             try argv.append("--print-map");
   2085         }
   2086 
   2087         if (comp.link_eh_frame_hdr) {
   2088             try argv.append("--eh-frame-hdr");
   2089         }
   2090 
   2091         if (self.emit_relocs) {
   2092             try argv.append("--emit-relocs");
   2093         }
   2094 
   2095         if (comp.config.rdynamic) {
   2096             try argv.append("--export-dynamic");
   2097         }
   2098 
   2099         if (comp.config.debug_format == .strip) {
   2100             try argv.append("-s");
   2101         }
   2102 
   2103         if (self.z_nodelete) {
   2104             try argv.append("-z");
   2105             try argv.append("nodelete");
   2106         }
   2107         if (self.z_notext) {
   2108             try argv.append("-z");
   2109             try argv.append("notext");
   2110         }
   2111         if (self.z_defs) {
   2112             try argv.append("-z");
   2113             try argv.append("defs");
   2114         }
   2115         if (self.z_origin) {
   2116             try argv.append("-z");
   2117             try argv.append("origin");
   2118         }
   2119         if (self.z_nocopyreloc) {
   2120             try argv.append("-z");
   2121             try argv.append("nocopyreloc");
   2122         }
   2123         if (self.z_now) {
   2124             // LLD defaults to -zlazy
   2125             try argv.append("-znow");
   2126         }
   2127         if (!self.z_relro) {
   2128             // LLD defaults to -zrelro
   2129             try argv.append("-znorelro");
   2130         }
   2131         if (self.z_common_page_size) |size| {
   2132             try argv.append("-z");
   2133             try argv.append(try std.fmt.allocPrint(arena, "common-page-size={d}", .{size}));
   2134         }
   2135         if (self.z_max_page_size) |size| {
   2136             try argv.append("-z");
   2137             try argv.append(try std.fmt.allocPrint(arena, "max-page-size={d}", .{size}));
   2138         }
   2139 
   2140         if (getLDMOption(target)) |ldm| {
   2141             try argv.append("-m");
   2142             try argv.append(ldm);
   2143         }
   2144 
   2145         if (link_mode == .static) {
   2146             if (target.cpu.arch.isArmOrThumb()) {
   2147                 try argv.append("-Bstatic");
   2148             } else {
   2149                 try argv.append("-static");
   2150             }
   2151         } else if (switch (target.os.tag) {
   2152             else => is_dyn_lib,
   2153             .haiku => is_exe_or_dyn_lib,
   2154         }) {
   2155             try argv.append("-shared");
   2156         }
   2157 
   2158         if (comp.config.pie and output_mode == .Exe) {
   2159             try argv.append("-pie");
   2160         }
   2161 
   2162         if (is_exe_or_dyn_lib and target.os.tag == .netbsd) {
   2163             // Add options to produce shared objects with only 2 PT_LOAD segments.
   2164             // NetBSD expects 2 PT_LOAD segments in a shared object, otherwise
   2165             // ld.elf_so fails loading dynamic libraries with "not found" error.
   2166             // See https://github.com/ziglang/zig/issues/9109 .
   2167             try argv.append("--no-rosegment");
   2168             try argv.append("-znorelro");
   2169         }
   2170 
   2171         try argv.append("-o");
   2172         try argv.append(full_out_path);
   2173 
   2174         // csu prelude
   2175         const csu = try CsuObjects.init(arena, comp);
   2176         if (csu.crt0) |v| try argv.append(v);
   2177         if (csu.crti) |v| try argv.append(v);
   2178         if (csu.crtbegin) |v| try argv.append(v);
   2179 
   2180         for (self.rpath_table.keys()) |rpath| {
   2181             try argv.appendSlice(&.{ "-rpath", rpath });
   2182         }
   2183 
   2184         for (self.symbol_wrap_set.keys()) |symbol_name| {
   2185             try argv.appendSlice(&.{ "-wrap", symbol_name });
   2186         }
   2187 
   2188         for (self.lib_dirs) |lib_dir| {
   2189             try argv.append("-L");
   2190             try argv.append(lib_dir);
   2191         }
   2192 
   2193         if (comp.config.link_libc) {
   2194             if (comp.libc_installation) |libc_installation| {
   2195                 try argv.append("-L");
   2196                 try argv.append(libc_installation.crt_dir.?);
   2197             }
   2198 
   2199             if (have_dynamic_linker) {
   2200                 if (target.dynamic_linker.get()) |dynamic_linker| {
   2201                     try argv.append("-dynamic-linker");
   2202                     try argv.append(dynamic_linker);
   2203                 }
   2204             }
   2205         }
   2206 
   2207         if (is_dyn_lib) {
   2208             if (self.soname) |soname| {
   2209                 try argv.append("-soname");
   2210                 try argv.append(soname);
   2211             }
   2212             if (self.version_script) |version_script| {
   2213                 try argv.append("-version-script");
   2214                 try argv.append(version_script);
   2215             }
   2216             if (self.allow_undefined_version) {
   2217                 try argv.append("--undefined-version");
   2218             } else {
   2219                 try argv.append("--no-undefined-version");
   2220             }
   2221             if (self.enable_new_dtags) |enable_new_dtags| {
   2222                 if (enable_new_dtags) {
   2223                     try argv.append("--enable-new-dtags");
   2224                 } else {
   2225                     try argv.append("--disable-new-dtags");
   2226                 }
   2227             }
   2228         }
   2229 
   2230         // Positional arguments to the linker such as object files.
   2231         var whole_archive = false;
   2232         for (comp.objects) |obj| {
   2233             if (obj.must_link and !whole_archive) {
   2234                 try argv.append("-whole-archive");
   2235                 whole_archive = true;
   2236             } else if (!obj.must_link and whole_archive) {
   2237                 try argv.append("-no-whole-archive");
   2238                 whole_archive = false;
   2239             }
   2240 
   2241             if (obj.loption) {
   2242                 assert(obj.path[0] == ':');
   2243                 try argv.append("-l");
   2244             }
   2245             try argv.append(obj.path);
   2246         }
   2247         if (whole_archive) {
   2248             try argv.append("-no-whole-archive");
   2249             whole_archive = false;
   2250         }
   2251 
   2252         for (comp.c_object_table.keys()) |key| {
   2253             try argv.append(key.status.success.object_path);
   2254         }
   2255 
   2256         if (module_obj_path) |p| {
   2257             try argv.append(p);
   2258         }
   2259 
   2260         if (comp.tsan_lib) |lib| {
   2261             assert(comp.config.any_sanitize_thread);
   2262             try argv.append(lib.full_object_path);
   2263         }
   2264 
   2265         if (comp.fuzzer_lib) |lib| {
   2266             assert(comp.config.any_fuzz);
   2267             try argv.append(lib.full_object_path);
   2268         }
   2269 
   2270         // libc
   2271         if (is_exe_or_dyn_lib and
   2272             !comp.skip_linker_dependencies and
   2273             !comp.config.link_libc)
   2274         {
   2275             if (comp.libc_static_lib) |lib| {
   2276                 try argv.append(lib.full_object_path);
   2277             }
   2278         }
   2279 
   2280         // Shared libraries.
   2281         if (is_exe_or_dyn_lib) {
   2282             const system_libs = comp.system_libs.keys();
   2283             const system_libs_values = comp.system_libs.values();
   2284 
   2285             // Worst-case, we need an --as-needed argument for every lib, as well
   2286             // as one before and one after.
   2287             try argv.ensureUnusedCapacity(system_libs.len * 2 + 2);
   2288             argv.appendAssumeCapacity("--as-needed");
   2289             var as_needed = true;
   2290 
   2291             for (system_libs_values) |lib_info| {
   2292                 const lib_as_needed = !lib_info.needed;
   2293                 switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) {
   2294                     0b00, 0b11 => {},
   2295                     0b01 => {
   2296                         argv.appendAssumeCapacity("--no-as-needed");
   2297                         as_needed = false;
   2298                     },
   2299                     0b10 => {
   2300                         argv.appendAssumeCapacity("--as-needed");
   2301                         as_needed = true;
   2302                     },
   2303                 }
   2304 
   2305                 // By this time, we depend on these libs being dynamically linked
   2306                 // libraries and not static libraries (the check for that needs to be earlier),
   2307                 // but they could be full paths to .so files, in which case we
   2308                 // want to avoid prepending "-l".
   2309                 argv.appendAssumeCapacity(lib_info.path.?);
   2310             }
   2311 
   2312             if (!as_needed) {
   2313                 argv.appendAssumeCapacity("--as-needed");
   2314                 as_needed = true;
   2315             }
   2316 
   2317             // libc++ dep
   2318             if (comp.config.link_libcpp) {
   2319                 try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
   2320                 try argv.append(comp.libcxx_static_lib.?.full_object_path);
   2321             }
   2322 
   2323             // libunwind dep
   2324             if (comp.config.link_libunwind) {
   2325                 try argv.append(comp.libunwind_static_lib.?.full_object_path);
   2326             }
   2327 
   2328             // libc dep
   2329             comp.link_error_flags.missing_libc = false;
   2330             if (comp.config.link_libc) {
   2331                 if (comp.libc_installation != null) {
   2332                     const needs_grouping = link_mode == .static;
   2333                     if (needs_grouping) try argv.append("--start-group");
   2334                     try argv.appendSlice(target_util.libcFullLinkFlags(target));
   2335                     if (needs_grouping) try argv.append("--end-group");
   2336                 } else if (target.isGnuLibC()) {
   2337                     for (glibc.libs) |lib| {
   2338                         if (lib.removed_in) |rem_in| {
   2339                             if (target.os.version_range.linux.glibc.order(rem_in) != .lt) continue;
   2340                         }
   2341 
   2342                         const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{
   2343                             comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
   2344                         });
   2345                         try argv.append(lib_path);
   2346                     }
   2347                     try argv.append(try comp.get_libc_crt_file(arena, "libc_nonshared.a"));
   2348                 } else if (target.isMusl()) {
   2349                     try argv.append(try comp.get_libc_crt_file(arena, switch (link_mode) {
   2350                         .static => "libc.a",
   2351                         .dynamic => "libc.so",
   2352                     }));
   2353                 } else {
   2354                     comp.link_error_flags.missing_libc = true;
   2355                 }
   2356             }
   2357         }
   2358 
   2359         // compiler-rt. Since compiler_rt exports symbols like `memset`, it needs
   2360         // to be after the shared libraries, so they are picked up from the shared
   2361         // libraries, not libcompiler_rt.
   2362         if (compiler_rt_path) |p| {
   2363             try argv.append(p);
   2364         }
   2365 
   2366         // crt postlude
   2367         if (csu.crtend) |v| try argv.append(v);
   2368         if (csu.crtn) |v| try argv.append(v);
   2369 
   2370         if (self.base.allow_shlib_undefined) {
   2371             try argv.append("--allow-shlib-undefined");
   2372         }
   2373 
   2374         switch (self.compress_debug_sections) {
   2375             .none => {},
   2376             .zlib => try argv.append("--compress-debug-sections=zlib"),
   2377             .zstd => try argv.append("--compress-debug-sections=zstd"),
   2378         }
   2379 
   2380         if (self.bind_global_refs_locally) {
   2381             try argv.append("-Bsymbolic");
   2382         }
   2383 
   2384         try link.spawnLld(comp, arena, argv.items);
   2385     }
   2386 
   2387     if (!self.base.disable_lld_caching) {
   2388         // Update the file with the digest. If it fails we can continue; it only
   2389         // means that the next invocation will have an unnecessary cache miss.
   2390         Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
   2391             log.warn("failed to save linking hash digest file: {s}", .{@errorName(err)});
   2392         };
   2393         // Again failure here only means an unnecessary cache miss.
   2394         man.writeManifest() catch |err| {
   2395             log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)});
   2396         };
   2397         // We hang on to this lock so that the output file path can be used without
   2398         // other processes clobbering it.
   2399         self.base.lock = man.toOwnedLock();
   2400     }
   2401 }
   2402 
   2403 pub fn writeShdrTable(self: *Elf) !void {
   2404     const gpa = self.base.comp.gpa;
   2405     const target_endian = self.getTarget().cpu.arch.endian();
   2406     const foreign_endian = target_endian != builtin.cpu.arch.endian();
   2407     const shsize: u64 = switch (self.ptr_width) {
   2408         .p32 => @sizeOf(elf.Elf32_Shdr),
   2409         .p64 => @sizeOf(elf.Elf64_Shdr),
   2410     };
   2411     const shalign: u16 = switch (self.ptr_width) {
   2412         .p32 => @alignOf(elf.Elf32_Shdr),
   2413         .p64 => @alignOf(elf.Elf64_Shdr),
   2414     };
   2415 
   2416     const shoff = self.shdr_table_offset orelse 0;
   2417     const needed_size = self.sections.items(.shdr).len * shsize;
   2418 
   2419     if (needed_size > self.allocatedSize(shoff)) {
   2420         self.shdr_table_offset = null;
   2421         self.shdr_table_offset = try self.findFreeSpace(needed_size, shalign);
   2422     }
   2423 
   2424     log.debug("writing section headers from 0x{x} to 0x{x}", .{
   2425         self.shdr_table_offset.?,
   2426         self.shdr_table_offset.? + needed_size,
   2427     });
   2428 
   2429     switch (self.ptr_width) {
   2430         .p32 => {
   2431             const buf = try gpa.alloc(elf.Elf32_Shdr, self.sections.items(.shdr).len);
   2432             defer gpa.free(buf);
   2433 
   2434             for (buf, 0..) |*shdr, i| {
   2435                 assert(self.sections.items(.shdr)[i].sh_offset != math.maxInt(u64));
   2436                 shdr.* = shdrTo32(self.sections.items(.shdr)[i]);
   2437                 if (foreign_endian) {
   2438                     mem.byteSwapAllFields(elf.Elf32_Shdr, shdr);
   2439                 }
   2440             }
   2441             try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
   2442         },
   2443         .p64 => {
   2444             const buf = try gpa.alloc(elf.Elf64_Shdr, self.sections.items(.shdr).len);
   2445             defer gpa.free(buf);
   2446 
   2447             for (buf, 0..) |*shdr, i| {
   2448                 assert(self.sections.items(.shdr)[i].sh_offset != math.maxInt(u64));
   2449                 shdr.* = self.sections.items(.shdr)[i];
   2450                 if (foreign_endian) {
   2451                     mem.byteSwapAllFields(elf.Elf64_Shdr, shdr);
   2452                 }
   2453             }
   2454             try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
   2455         },
   2456     }
   2457 }
   2458 
   2459 fn writePhdrTable(self: *Elf) !void {
   2460     const gpa = self.base.comp.gpa;
   2461     const target_endian = self.getTarget().cpu.arch.endian();
   2462     const foreign_endian = target_endian != builtin.cpu.arch.endian();
   2463     const phdr_table = &self.phdrs.items[self.phdr_table_index.?];
   2464 
   2465     log.debug("writing program headers from 0x{x} to 0x{x}", .{
   2466         phdr_table.p_offset,
   2467         phdr_table.p_offset + phdr_table.p_filesz,
   2468     });
   2469 
   2470     switch (self.ptr_width) {
   2471         .p32 => {
   2472             const buf = try gpa.alloc(elf.Elf32_Phdr, self.phdrs.items.len);
   2473             defer gpa.free(buf);
   2474 
   2475             for (buf, 0..) |*phdr, i| {
   2476                 phdr.* = phdrTo32(self.phdrs.items[i]);
   2477                 if (foreign_endian) {
   2478                     mem.byteSwapAllFields(elf.Elf32_Phdr, phdr);
   2479                 }
   2480             }
   2481             try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset);
   2482         },
   2483         .p64 => {
   2484             const buf = try gpa.alloc(elf.Elf64_Phdr, self.phdrs.items.len);
   2485             defer gpa.free(buf);
   2486 
   2487             for (buf, 0..) |*phdr, i| {
   2488                 phdr.* = self.phdrs.items[i];
   2489                 if (foreign_endian) {
   2490                     mem.byteSwapAllFields(elf.Elf64_Phdr, phdr);
   2491                 }
   2492             }
   2493             try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset);
   2494         },
   2495     }
   2496 }
   2497 
   2498 pub fn writeElfHeader(self: *Elf) !void {
   2499     if (self.base.hasErrors()) return; // We had errors, so skip flushing to render the output unusable
   2500 
   2501     const comp = self.base.comp;
   2502     var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
   2503 
   2504     var index: usize = 0;
   2505     hdr_buf[0..4].* = elf.MAGIC.*;
   2506     index += 4;
   2507 
   2508     hdr_buf[index] = switch (self.ptr_width) {
   2509         .p32 => elf.ELFCLASS32,
   2510         .p64 => elf.ELFCLASS64,
   2511     };
   2512     index += 1;
   2513 
   2514     const target = self.getTarget();
   2515     const endian = target.cpu.arch.endian();
   2516     hdr_buf[index] = switch (endian) {
   2517         .little => elf.ELFDATA2LSB,
   2518         .big => elf.ELFDATA2MSB,
   2519     };
   2520     index += 1;
   2521 
   2522     hdr_buf[index] = 1; // ELF version
   2523     index += 1;
   2524 
   2525     hdr_buf[index] = @intFromEnum(@as(elf.OSABI, switch (target.cpu.arch) {
   2526         .amdgcn => switch (target.os.tag) {
   2527             .amdhsa => .AMDGPU_HSA,
   2528             .amdpal => .AMDGPU_PAL,
   2529             .mesa3d => .AMDGPU_MESA3D,
   2530             else => .NONE,
   2531         },
   2532         .msp430 => .STANDALONE,
   2533         else => switch (target.os.tag) {
   2534             .freebsd, .ps4 => .FREEBSD,
   2535             .hermit => .STANDALONE,
   2536             .illumos, .solaris => .SOLARIS,
   2537             .openbsd => .OPENBSD,
   2538             else => .NONE,
   2539         },
   2540     }));
   2541     index += 1;
   2542 
   2543     // ABI Version, possibly used by glibc but not by static executables
   2544     // padding
   2545     @memset(hdr_buf[index..][0..8], 0);
   2546     index += 8;
   2547 
   2548     assert(index == 16);
   2549 
   2550     const output_mode = comp.config.output_mode;
   2551     const link_mode = comp.config.link_mode;
   2552     const elf_type: elf.ET = switch (output_mode) {
   2553         .Exe => if (comp.config.pie or target.os.tag == .haiku) .DYN else .EXEC,
   2554         .Obj => .REL,
   2555         .Lib => switch (link_mode) {
   2556             .static => @as(elf.ET, .REL),
   2557             .dynamic => .DYN,
   2558         },
   2559     };
   2560     mem.writeInt(u16, hdr_buf[index..][0..2], @intFromEnum(elf_type), endian);
   2561     index += 2;
   2562 
   2563     const machine = target.toElfMachine();
   2564     mem.writeInt(u16, hdr_buf[index..][0..2], @intFromEnum(machine), endian);
   2565     index += 2;
   2566 
   2567     // ELF Version, again
   2568     mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian);
   2569     index += 4;
   2570 
   2571     const e_entry: u64 = if (self.linkerDefinedPtr()) |obj| blk: {
   2572         const entry_sym = obj.entrySymbol(self) orelse break :blk 0;
   2573         break :blk @intCast(entry_sym.address(.{}, self));
   2574     } else 0;
   2575     const phdr_table_offset = if (self.phdr_table_index) |phndx| self.phdrs.items[phndx].p_offset else 0;
   2576     switch (self.ptr_width) {
   2577         .p32 => {
   2578             mem.writeInt(u32, hdr_buf[index..][0..4], @as(u32, @intCast(e_entry)), endian);
   2579             index += 4;
   2580 
   2581             // e_phoff
   2582             mem.writeInt(u32, hdr_buf[index..][0..4], @as(u32, @intCast(phdr_table_offset)), endian);
   2583             index += 4;
   2584 
   2585             // e_shoff
   2586             mem.writeInt(u32, hdr_buf[index..][0..4], @as(u32, @intCast(self.shdr_table_offset.?)), endian);
   2587             index += 4;
   2588         },
   2589         .p64 => {
   2590             // e_entry
   2591             mem.writeInt(u64, hdr_buf[index..][0..8], e_entry, endian);
   2592             index += 8;
   2593 
   2594             // e_phoff
   2595             mem.writeInt(u64, hdr_buf[index..][0..8], phdr_table_offset, endian);
   2596             index += 8;
   2597 
   2598             // e_shoff
   2599             mem.writeInt(u64, hdr_buf[index..][0..8], self.shdr_table_offset.?, endian);
   2600             index += 8;
   2601         },
   2602     }
   2603 
   2604     const e_flags = 0;
   2605     mem.writeInt(u32, hdr_buf[index..][0..4], e_flags, endian);
   2606     index += 4;
   2607 
   2608     const e_ehsize: u16 = switch (self.ptr_width) {
   2609         .p32 => @sizeOf(elf.Elf32_Ehdr),
   2610         .p64 => @sizeOf(elf.Elf64_Ehdr),
   2611     };
   2612     mem.writeInt(u16, hdr_buf[index..][0..2], e_ehsize, endian);
   2613     index += 2;
   2614 
   2615     const e_phentsize: u16 = switch (self.ptr_width) {
   2616         .p32 => @sizeOf(elf.Elf32_Phdr),
   2617         .p64 => @sizeOf(elf.Elf64_Phdr),
   2618     };
   2619     mem.writeInt(u16, hdr_buf[index..][0..2], e_phentsize, endian);
   2620     index += 2;
   2621 
   2622     const e_phnum = @as(u16, @intCast(self.phdrs.items.len));
   2623     mem.writeInt(u16, hdr_buf[index..][0..2], e_phnum, endian);
   2624     index += 2;
   2625 
   2626     const e_shentsize: u16 = switch (self.ptr_width) {
   2627         .p32 => @sizeOf(elf.Elf32_Shdr),
   2628         .p64 => @sizeOf(elf.Elf64_Shdr),
   2629     };
   2630     mem.writeInt(u16, hdr_buf[index..][0..2], e_shentsize, endian);
   2631     index += 2;
   2632 
   2633     const e_shnum = @as(u16, @intCast(self.sections.items(.shdr).len));
   2634     mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian);
   2635     index += 2;
   2636 
   2637     mem.writeInt(u16, hdr_buf[index..][0..2], @intCast(self.shstrtab_section_index.?), endian);
   2638     index += 2;
   2639 
   2640     assert(index == e_ehsize);
   2641 
   2642     try self.base.file.?.pwriteAll(hdr_buf[0..index], 0);
   2643 }
   2644 
   2645 pub fn freeNav(self: *Elf, nav: InternPool.Nav.Index) void {
   2646     if (self.llvm_object) |llvm_object| return llvm_object.freeNav(nav);
   2647     return self.zigObjectPtr().?.freeNav(self, nav);
   2648 }
   2649 
   2650 pub fn updateFunc(self: *Elf, pt: Zcu.PerThread, func_index: InternPool.Index, air: Air, liveness: Liveness) !void {
   2651     if (build_options.skip_non_native and builtin.object_format != .elf) {
   2652         @panic("Attempted to compile for object format that was disabled by build configuration");
   2653     }
   2654     if (self.llvm_object) |llvm_object| return llvm_object.updateFunc(pt, func_index, air, liveness);
   2655     return self.zigObjectPtr().?.updateFunc(self, pt, func_index, air, liveness);
   2656 }
   2657 
   2658 pub fn updateNav(
   2659     self: *Elf,
   2660     pt: Zcu.PerThread,
   2661     nav: InternPool.Nav.Index,
   2662 ) link.File.UpdateNavError!void {
   2663     if (build_options.skip_non_native and builtin.object_format != .elf) {
   2664         @panic("Attempted to compile for object format that was disabled by build configuration");
   2665     }
   2666     if (self.llvm_object) |llvm_object| return llvm_object.updateNav(pt, nav);
   2667     return self.zigObjectPtr().?.updateNav(self, pt, nav);
   2668 }
   2669 
   2670 pub fn updateContainerType(
   2671     self: *Elf,
   2672     pt: Zcu.PerThread,
   2673     ty: InternPool.Index,
   2674 ) link.File.UpdateNavError!void {
   2675     if (build_options.skip_non_native and builtin.object_format != .elf) {
   2676         @panic("Attempted to compile for object format that was disabled by build configuration");
   2677     }
   2678     if (self.llvm_object) |_| return;
   2679     return self.zigObjectPtr().?.updateContainerType(pt, ty);
   2680 }
   2681 
   2682 pub fn updateExports(
   2683     self: *Elf,
   2684     pt: Zcu.PerThread,
   2685     exported: Zcu.Exported,
   2686     export_indices: []const u32,
   2687 ) link.File.UpdateExportsError!void {
   2688     if (build_options.skip_non_native and builtin.object_format != .elf) {
   2689         @panic("Attempted to compile for object format that was disabled by build configuration");
   2690     }
   2691     if (self.llvm_object) |llvm_object| return llvm_object.updateExports(pt, exported, export_indices);
   2692     return self.zigObjectPtr().?.updateExports(self, pt, exported, export_indices);
   2693 }
   2694 
   2695 pub fn updateNavLineNumber(self: *Elf, pt: Zcu.PerThread, nav: InternPool.Nav.Index) !void {
   2696     if (self.llvm_object) |_| return;
   2697     return self.zigObjectPtr().?.updateNavLineNumber(pt, nav);
   2698 }
   2699 
   2700 pub fn deleteExport(
   2701     self: *Elf,
   2702     exported: Zcu.Exported,
   2703     name: InternPool.NullTerminatedString,
   2704 ) void {
   2705     if (self.llvm_object) |_| return;
   2706     return self.zigObjectPtr().?.deleteExport(self, exported, name);
   2707 }
   2708 
   2709 fn checkDuplicates(self: *Elf) !void {
   2710     const gpa = self.base.comp.gpa;
   2711 
   2712     var dupes = std.AutoArrayHashMap(SymbolResolver.Index, std.ArrayListUnmanaged(File.Index)).init(gpa);
   2713     defer {
   2714         for (dupes.values()) |*list| {
   2715             list.deinit(gpa);
   2716         }
   2717         dupes.deinit();
   2718     }
   2719 
   2720     if (self.zigObjectPtr()) |zig_object| {
   2721         try zig_object.checkDuplicates(&dupes, self);
   2722     }
   2723     for (self.objects.items) |index| {
   2724         try self.file(index).?.object.checkDuplicates(&dupes, self);
   2725     }
   2726 
   2727     try self.reportDuplicates(dupes);
   2728 }
   2729 
   2730 pub fn addCommentString(self: *Elf) !void {
   2731     const gpa = self.base.comp.gpa;
   2732     if (self.comment_merge_section_index != null) return;
   2733     const msec_index = try self.getOrCreateMergeSection(".comment", elf.SHF_MERGE | elf.SHF_STRINGS, elf.SHT_PROGBITS);
   2734     const msec = self.mergeSection(msec_index);
   2735     const res = try msec.insertZ(gpa, "zig " ++ builtin.zig_version_string);
   2736     if (res.found_existing) return;
   2737     const msub_index = try msec.addMergeSubsection(gpa);
   2738     const msub = msec.mergeSubsection(msub_index);
   2739     msub.merge_section_index = msec_index;
   2740     msub.string_index = res.key.pos;
   2741     msub.alignment = .@"1";
   2742     msub.size = res.key.len;
   2743     msub.entsize = 1;
   2744     msub.alive = true;
   2745     res.sub.* = msub_index;
   2746     self.comment_merge_section_index = msec_index;
   2747 }
   2748 
   2749 pub fn resolveMergeSections(self: *Elf) !void {
   2750     const tracy = trace(@src());
   2751     defer tracy.end();
   2752 
   2753     var has_errors = false;
   2754     for (self.objects.items) |index| {
   2755         const object = self.file(index).?.object;
   2756         if (!object.alive) continue;
   2757         if (!object.dirty) continue;
   2758         object.initInputMergeSections(self) catch |err| switch (err) {
   2759             error.LinkFailure => has_errors = true,
   2760             else => |e| return e,
   2761         };
   2762     }
   2763 
   2764     if (has_errors) return error.FlushFailure;
   2765 
   2766     for (self.objects.items) |index| {
   2767         const object = self.file(index).?.object;
   2768         if (!object.alive) continue;
   2769         if (!object.dirty) continue;
   2770         try object.initOutputMergeSections(self);
   2771     }
   2772 
   2773     for (self.objects.items) |index| {
   2774         const object = self.file(index).?.object;
   2775         if (!object.alive) continue;
   2776         if (!object.dirty) continue;
   2777         object.resolveMergeSubsections(self) catch |err| switch (err) {
   2778             error.LinkFailure => has_errors = true,
   2779             else => |e| return e,
   2780         };
   2781     }
   2782 
   2783     if (has_errors) return error.LinkFailure;
   2784 }
   2785 
   2786 pub fn finalizeMergeSections(self: *Elf) !void {
   2787     for (self.merge_sections.items) |*msec| {
   2788         try msec.finalize(self.base.comp.gpa);
   2789     }
   2790 }
   2791 
   2792 pub fn updateMergeSectionSizes(self: *Elf) !void {
   2793     for (self.merge_sections.items) |*msec| {
   2794         msec.updateSize();
   2795     }
   2796     for (self.merge_sections.items) |*msec| {
   2797         const shdr = &self.sections.items(.shdr)[msec.output_section_index];
   2798         const offset = msec.alignment.forward(shdr.sh_size);
   2799         const padding = offset - shdr.sh_size;
   2800         msec.value = @intCast(offset);
   2801         shdr.sh_size += padding + msec.size;
   2802         shdr.sh_addralign = @max(shdr.sh_addralign, msec.alignment.toByteUnits() orelse 1);
   2803         shdr.sh_entsize = if (shdr.sh_entsize == 0) msec.entsize else @min(shdr.sh_entsize, msec.entsize);
   2804     }
   2805 }
   2806 
   2807 pub fn writeMergeSections(self: *Elf) !void {
   2808     const gpa = self.base.comp.gpa;
   2809     var buffer = std.ArrayList(u8).init(gpa);
   2810     defer buffer.deinit();
   2811 
   2812     for (self.merge_sections.items) |*msec| {
   2813         const shdr = self.sections.items(.shdr)[msec.output_section_index];
   2814         const fileoff = math.cast(usize, msec.value + shdr.sh_offset) orelse return error.Overflow;
   2815         const size = math.cast(usize, msec.size) orelse return error.Overflow;
   2816         try buffer.ensureTotalCapacity(size);
   2817         buffer.appendNTimesAssumeCapacity(0, size);
   2818 
   2819         for (msec.finalized_subsections.items) |msub_index| {
   2820             const msub = msec.mergeSubsection(msub_index);
   2821             assert(msub.alive);
   2822             const string = msub.getString(self);
   2823             const off = math.cast(usize, msub.value) orelse return error.Overflow;
   2824             @memcpy(buffer.items[off..][0..string.len], string);
   2825         }
   2826 
   2827         try self.base.file.?.pwriteAll(buffer.items, fileoff);
   2828         buffer.clearRetainingCapacity();
   2829     }
   2830 }
   2831 
   2832 fn initOutputSections(self: *Elf) !void {
   2833     for (self.objects.items) |index| {
   2834         try self.file(index).?.object.initOutputSections(self);
   2835     }
   2836     for (self.merge_sections.items) |*msec| {
   2837         if (msec.finalized_subsections.items.len == 0) continue;
   2838         try msec.initOutputSection(self);
   2839     }
   2840 }
   2841 
   2842 fn initSyntheticSections(self: *Elf) !void {
   2843     const comp = self.base.comp;
   2844     const target = self.getTarget();
   2845     const ptr_size = self.ptrWidthBytes();
   2846 
   2847     const needs_eh_frame = blk: {
   2848         if (self.zigObjectPtr()) |zo|
   2849             if (zo.eh_frame_index != null) break :blk true;
   2850         break :blk for (self.objects.items) |index| {
   2851             if (self.file(index).?.object.cies.items.len > 0) break true;
   2852         } else false;
   2853     };
   2854     if (needs_eh_frame) {
   2855         if (self.eh_frame_section_index == null) {
   2856             self.eh_frame_section_index = self.sectionByName(".eh_frame") orelse try self.addSection(.{
   2857                 .name = try self.insertShString(".eh_frame"),
   2858                 .type = if (target.cpu.arch == .x86_64)
   2859                     elf.SHT_X86_64_UNWIND
   2860                 else
   2861                     elf.SHT_PROGBITS,
   2862                 .flags = elf.SHF_ALLOC,
   2863                 .addralign = ptr_size,
   2864                 .offset = std.math.maxInt(u64),
   2865             });
   2866         }
   2867         if (comp.link_eh_frame_hdr and self.eh_frame_hdr_section_index == null) {
   2868             self.eh_frame_hdr_section_index = try self.addSection(.{
   2869                 .name = try self.insertShString(".eh_frame_hdr"),
   2870                 .type = elf.SHT_PROGBITS,
   2871                 .flags = elf.SHF_ALLOC,
   2872                 .addralign = 4,
   2873                 .offset = std.math.maxInt(u64),
   2874             });
   2875         }
   2876     }
   2877 
   2878     if (self.got.entries.items.len > 0 and self.got_section_index == null) {
   2879         self.got_section_index = try self.addSection(.{
   2880             .name = try self.insertShString(".got"),
   2881             .type = elf.SHT_PROGBITS,
   2882             .flags = elf.SHF_ALLOC | elf.SHF_WRITE,
   2883             .addralign = ptr_size,
   2884             .offset = std.math.maxInt(u64),
   2885         });
   2886     }
   2887 
   2888     if (self.got_plt_section_index == null) {
   2889         self.got_plt_section_index = try self.addSection(.{
   2890             .name = try self.insertShString(".got.plt"),
   2891             .type = elf.SHT_PROGBITS,
   2892             .flags = elf.SHF_ALLOC | elf.SHF_WRITE,
   2893             .addralign = @alignOf(u64),
   2894             .offset = std.math.maxInt(u64),
   2895         });
   2896     }
   2897 
   2898     const needs_rela_dyn = blk: {
   2899         if (self.got.flags.needs_rela or self.got.flags.needs_tlsld or self.copy_rel.symbols.items.len > 0)
   2900             break :blk true;
   2901         if (self.zigObjectPtr()) |zig_object| {
   2902             if (zig_object.num_dynrelocs > 0) break :blk true;
   2903         }
   2904         for (self.objects.items) |index| {
   2905             if (self.file(index).?.object.num_dynrelocs > 0) break :blk true;
   2906         }
   2907         break :blk false;
   2908     };
   2909     if (needs_rela_dyn and self.rela_dyn_section_index == null) {
   2910         self.rela_dyn_section_index = try self.addSection(.{
   2911             .name = try self.insertShString(".rela.dyn"),
   2912             .type = elf.SHT_RELA,
   2913             .flags = elf.SHF_ALLOC,
   2914             .addralign = @alignOf(elf.Elf64_Rela),
   2915             .entsize = @sizeOf(elf.Elf64_Rela),
   2916             .offset = std.math.maxInt(u64),
   2917         });
   2918     }
   2919 
   2920     if (self.plt.symbols.items.len > 0) {
   2921         if (self.plt_section_index == null) {
   2922             self.plt_section_index = try self.addSection(.{
   2923                 .name = try self.insertShString(".plt"),
   2924                 .type = elf.SHT_PROGBITS,
   2925                 .flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
   2926                 .addralign = 16,
   2927                 .offset = std.math.maxInt(u64),
   2928             });
   2929         }
   2930         if (self.rela_plt_section_index == null) {
   2931             self.rela_plt_section_index = try self.addSection(.{
   2932                 .name = try self.insertShString(".rela.plt"),
   2933                 .type = elf.SHT_RELA,
   2934                 .flags = elf.SHF_ALLOC,
   2935                 .addralign = @alignOf(elf.Elf64_Rela),
   2936                 .entsize = @sizeOf(elf.Elf64_Rela),
   2937                 .offset = std.math.maxInt(u64),
   2938             });
   2939         }
   2940     }
   2941 
   2942     if (self.plt_got.symbols.items.len > 0 and self.plt_got_section_index == null) {
   2943         self.plt_got_section_index = try self.addSection(.{
   2944             .name = try self.insertShString(".plt.got"),
   2945             .type = elf.SHT_PROGBITS,
   2946             .flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
   2947             .addralign = 16,
   2948             .offset = std.math.maxInt(u64),
   2949         });
   2950     }
   2951 
   2952     if (self.copy_rel.symbols.items.len > 0 and self.copy_rel_section_index == null) {
   2953         self.copy_rel_section_index = try self.addSection(.{
   2954             .name = try self.insertShString(".copyrel"),
   2955             .type = elf.SHT_NOBITS,
   2956             .flags = elf.SHF_ALLOC | elf.SHF_WRITE,
   2957             .offset = std.math.maxInt(u64),
   2958         });
   2959     }
   2960 
   2961     const needs_interp = blk: {
   2962         // On Ubuntu with musl-gcc, we get a weird combo of options looking like this:
   2963         // -dynamic-linker=<path> -static
   2964         // In this case, if we do generate .interp section and segment, we will get
   2965         // a segfault in the dynamic linker trying to load a binary that is static
   2966         // and doesn't contain .dynamic section.
   2967         if (self.base.isStatic() and !comp.config.pie) break :blk false;
   2968         break :blk target.dynamic_linker.get() != null;
   2969     };
   2970     if (needs_interp and self.interp_section_index == null) {
   2971         self.interp_section_index = try self.addSection(.{
   2972             .name = try self.insertShString(".interp"),
   2973             .type = elf.SHT_PROGBITS,
   2974             .flags = elf.SHF_ALLOC,
   2975             .addralign = 1,
   2976             .offset = std.math.maxInt(u64),
   2977         });
   2978     }
   2979 
   2980     if (self.isEffectivelyDynLib() or self.shared_objects.items.len > 0 or comp.config.pie) {
   2981         if (self.dynstrtab_section_index == null) {
   2982             self.dynstrtab_section_index = try self.addSection(.{
   2983                 .name = try self.insertShString(".dynstr"),
   2984                 .flags = elf.SHF_ALLOC,
   2985                 .type = elf.SHT_STRTAB,
   2986                 .entsize = 1,
   2987                 .addralign = 1,
   2988                 .offset = std.math.maxInt(u64),
   2989             });
   2990         }
   2991         if (self.dynamic_section_index == null) {
   2992             self.dynamic_section_index = try self.addSection(.{
   2993                 .name = try self.insertShString(".dynamic"),
   2994                 .flags = elf.SHF_ALLOC | elf.SHF_WRITE,
   2995                 .type = elf.SHT_DYNAMIC,
   2996                 .entsize = @sizeOf(elf.Elf64_Dyn),
   2997                 .addralign = @alignOf(elf.Elf64_Dyn),
   2998                 .offset = std.math.maxInt(u64),
   2999             });
   3000         }
   3001         if (self.dynsymtab_section_index == null) {
   3002             self.dynsymtab_section_index = try self.addSection(.{
   3003                 .name = try self.insertShString(".dynsym"),
   3004                 .flags = elf.SHF_ALLOC,
   3005                 .type = elf.SHT_DYNSYM,
   3006                 .addralign = @alignOf(elf.Elf64_Sym),
   3007                 .entsize = @sizeOf(elf.Elf64_Sym),
   3008                 .info = 1,
   3009                 .offset = std.math.maxInt(u64),
   3010             });
   3011         }
   3012         if (self.hash_section_index == null) {
   3013             self.hash_section_index = try self.addSection(.{
   3014                 .name = try self.insertShString(".hash"),
   3015                 .flags = elf.SHF_ALLOC,
   3016                 .type = elf.SHT_HASH,
   3017                 .addralign = 4,
   3018                 .entsize = 4,
   3019                 .offset = std.math.maxInt(u64),
   3020             });
   3021         }
   3022         if (self.gnu_hash_section_index == null) {
   3023             self.gnu_hash_section_index = try self.addSection(.{
   3024                 .name = try self.insertShString(".gnu.hash"),
   3025                 .flags = elf.SHF_ALLOC,
   3026                 .type = elf.SHT_GNU_HASH,
   3027                 .addralign = 8,
   3028                 .offset = std.math.maxInt(u64),
   3029             });
   3030         }
   3031 
   3032         const needs_versions = for (self.dynsym.entries.items) |entry| {
   3033             const sym = self.symbol(entry.ref).?;
   3034             if (sym.flags.import and sym.version_index & elf.VERSYM_VERSION > elf.VER_NDX_GLOBAL) break true;
   3035         } else false;
   3036         if (needs_versions) {
   3037             if (self.versym_section_index == null) {
   3038                 self.versym_section_index = try self.addSection(.{
   3039                     .name = try self.insertShString(".gnu.version"),
   3040                     .flags = elf.SHF_ALLOC,
   3041                     .type = elf.SHT_GNU_VERSYM,
   3042                     .addralign = @alignOf(elf.Elf64_Versym),
   3043                     .entsize = @sizeOf(elf.Elf64_Versym),
   3044                     .offset = std.math.maxInt(u64),
   3045                 });
   3046             }
   3047             if (self.verneed_section_index == null) {
   3048                 self.verneed_section_index = try self.addSection(.{
   3049                     .name = try self.insertShString(".gnu.version_r"),
   3050                     .flags = elf.SHF_ALLOC,
   3051                     .type = elf.SHT_GNU_VERNEED,
   3052                     .addralign = @alignOf(elf.Elf64_Verneed),
   3053                     .offset = std.math.maxInt(u64),
   3054                 });
   3055             }
   3056         }
   3057     }
   3058 
   3059     try self.initSymtab();
   3060     try self.initShStrtab();
   3061 }
   3062 
   3063 pub fn initSymtab(self: *Elf) !void {
   3064     const small_ptr = switch (self.ptr_width) {
   3065         .p32 => true,
   3066         .p64 => false,
   3067     };
   3068     if (self.symtab_section_index == null) {
   3069         self.symtab_section_index = try self.addSection(.{
   3070             .name = try self.insertShString(".symtab"),
   3071             .type = elf.SHT_SYMTAB,
   3072             .addralign = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym),
   3073             .entsize = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym),
   3074             .offset = std.math.maxInt(u64),
   3075         });
   3076     }
   3077     if (self.strtab_section_index == null) {
   3078         self.strtab_section_index = try self.addSection(.{
   3079             .name = try self.insertShString(".strtab"),
   3080             .type = elf.SHT_STRTAB,
   3081             .entsize = 1,
   3082             .addralign = 1,
   3083             .offset = std.math.maxInt(u64),
   3084         });
   3085     }
   3086 }
   3087 
   3088 pub fn initShStrtab(self: *Elf) !void {
   3089     if (self.shstrtab_section_index == null) {
   3090         self.shstrtab_section_index = try self.addSection(.{
   3091             .name = try self.insertShString(".shstrtab"),
   3092             .type = elf.SHT_STRTAB,
   3093             .entsize = 1,
   3094             .addralign = 1,
   3095             .offset = std.math.maxInt(u64),
   3096         });
   3097     }
   3098 }
   3099 
   3100 fn initSpecialPhdrs(self: *Elf) !void {
   3101     comptime assert(max_number_of_special_phdrs == 5);
   3102 
   3103     if (self.interp_section_index != null and self.phdr_interp_index == null) {
   3104         self.phdr_interp_index = try self.addPhdr(.{
   3105             .type = elf.PT_INTERP,
   3106             .flags = elf.PF_R,
   3107             .@"align" = 1,
   3108         });
   3109     }
   3110     if (self.dynamic_section_index != null and self.phdr_dynamic_index == null) {
   3111         self.phdr_dynamic_index = try self.addPhdr(.{
   3112             .type = elf.PT_DYNAMIC,
   3113             .flags = elf.PF_R | elf.PF_W,
   3114         });
   3115     }
   3116     if (self.eh_frame_hdr_section_index != null and self.phdr_gnu_eh_frame_index == null) {
   3117         self.phdr_gnu_eh_frame_index = try self.addPhdr(.{
   3118             .type = elf.PT_GNU_EH_FRAME,
   3119             .flags = elf.PF_R,
   3120         });
   3121     }
   3122     if (self.phdr_gnu_stack_index == null) {
   3123         self.phdr_gnu_stack_index = try self.addPhdr(.{
   3124             .type = elf.PT_GNU_STACK,
   3125             .flags = elf.PF_W | elf.PF_R,
   3126             .memsz = self.base.stack_size,
   3127             .@"align" = 1,
   3128         });
   3129     }
   3130 
   3131     const has_tls = for (self.sections.items(.shdr)) |shdr| {
   3132         if (shdr.sh_flags & elf.SHF_TLS != 0) break true;
   3133     } else false;
   3134     if (has_tls and self.phdr_tls_index == null) {
   3135         self.phdr_tls_index = try self.addPhdr(.{
   3136             .type = elf.PT_TLS,
   3137             .flags = elf.PF_R,
   3138             .@"align" = 1,
   3139         });
   3140     }
   3141 }
   3142 
   3143 /// We need to sort constructors/destuctors in the following sections:
   3144 /// * .init_array
   3145 /// * .fini_array
   3146 /// * .preinit_array
   3147 /// * .ctors
   3148 /// * .dtors
   3149 /// The prority of inclusion is defined as part of the input section's name. For example, .init_array.10000.
   3150 /// If no priority value has been specified,
   3151 /// * for .init_array, .fini_array and .preinit_array, we automatically assign that section max value of maxInt(i32)
   3152 ///   and push it to the back of the queue,
   3153 /// * for .ctors and .dtors, we automatically assign that section min value of -1
   3154 ///   and push it to the front of the queue,
   3155 /// crtbegin and ctrend are assigned minInt(i32) and maxInt(i32) respectively.
   3156 /// Ties are broken by the file prority which corresponds to the inclusion of input sections in this output section
   3157 /// we are about to sort.
   3158 fn sortInitFini(self: *Elf) !void {
   3159     const gpa = self.base.comp.gpa;
   3160     const slice = self.sections.slice();
   3161 
   3162     const Entry = struct {
   3163         priority: i32,
   3164         atom_ref: Ref,
   3165 
   3166         pub fn lessThan(ctx: *Elf, lhs: @This(), rhs: @This()) bool {
   3167             if (lhs.priority == rhs.priority) {
   3168                 return ctx.atom(lhs.atom_ref).?.priority(ctx) < ctx.atom(rhs.atom_ref).?.priority(ctx);
   3169             }
   3170             return lhs.priority < rhs.priority;
   3171         }
   3172     };
   3173 
   3174     for (slice.items(.shdr), slice.items(.atom_list_2)) |shdr, *atom_list| {
   3175         if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue;
   3176         if (atom_list.atoms.keys().len == 0) continue;
   3177 
   3178         var is_init_fini = false;
   3179         var is_ctor_dtor = false;
   3180         switch (shdr.sh_type) {
   3181             elf.SHT_PREINIT_ARRAY,
   3182             elf.SHT_INIT_ARRAY,
   3183             elf.SHT_FINI_ARRAY,
   3184             => is_init_fini = true,
   3185             else => {
   3186                 const name = self.getShString(shdr.sh_name);
   3187                 is_ctor_dtor = mem.indexOf(u8, name, ".ctors") != null or mem.indexOf(u8, name, ".dtors") != null;
   3188             },
   3189         }
   3190         if (!is_init_fini and !is_ctor_dtor) continue;
   3191 
   3192         var entries = std.ArrayList(Entry).init(gpa);
   3193         try entries.ensureTotalCapacityPrecise(atom_list.atoms.keys().len);
   3194         defer entries.deinit();
   3195 
   3196         for (atom_list.atoms.keys()) |ref| {
   3197             const atom_ptr = self.atom(ref).?;
   3198             const object = atom_ptr.file(self).?.object;
   3199             const priority = blk: {
   3200                 if (is_ctor_dtor) {
   3201                     if (mem.indexOf(u8, object.path, "crtbegin") != null) break :blk std.math.minInt(i32);
   3202                     if (mem.indexOf(u8, object.path, "crtend") != null) break :blk std.math.maxInt(i32);
   3203                 }
   3204                 const default: i32 = if (is_ctor_dtor) -1 else std.math.maxInt(i32);
   3205                 const name = atom_ptr.name(self);
   3206                 var it = mem.splitBackwardsScalar(u8, name, '.');
   3207                 const priority = std.fmt.parseUnsigned(u16, it.first(), 10) catch default;
   3208                 break :blk priority;
   3209             };
   3210             entries.appendAssumeCapacity(.{ .priority = priority, .atom_ref = ref });
   3211         }
   3212 
   3213         mem.sort(Entry, entries.items, self, Entry.lessThan);
   3214 
   3215         atom_list.atoms.clearRetainingCapacity();
   3216         for (entries.items) |entry| {
   3217             _ = atom_list.atoms.getOrPutAssumeCapacity(entry.atom_ref);
   3218         }
   3219     }
   3220 }
   3221 
   3222 fn setDynamicSection(self: *Elf, rpaths: []const []const u8) !void {
   3223     if (self.dynamic_section_index == null) return;
   3224 
   3225     for (self.shared_objects.items) |index| {
   3226         const shared_object = self.file(index).?.shared_object;
   3227         if (!shared_object.alive) continue;
   3228         try self.dynamic.addNeeded(shared_object, self);
   3229     }
   3230 
   3231     if (self.isEffectivelyDynLib()) {
   3232         if (self.soname) |soname| {
   3233             try self.dynamic.setSoname(soname, self);
   3234         }
   3235     }
   3236 
   3237     try self.dynamic.setRpath(rpaths, self);
   3238 }
   3239 
   3240 fn sortDynamicSymtab(self: *Elf) void {
   3241     if (self.gnu_hash_section_index == null) return;
   3242     self.dynsym.sort(self);
   3243 }
   3244 
   3245 fn setVersionSymtab(self: *Elf) !void {
   3246     const gpa = self.base.comp.gpa;
   3247     if (self.versym_section_index == null) return;
   3248     try self.versym.resize(gpa, self.dynsym.count());
   3249     self.versym.items[0] = elf.VER_NDX_LOCAL;
   3250     for (self.dynsym.entries.items, 1..) |entry, i| {
   3251         const sym = self.symbol(entry.ref).?;
   3252         self.versym.items[i] = sym.version_index;
   3253     }
   3254 
   3255     if (self.verneed_section_index) |shndx| {
   3256         try self.verneed.generate(self);
   3257         const shdr = &self.sections.items(.shdr)[shndx];
   3258         shdr.sh_info = @as(u32, @intCast(self.verneed.verneed.items.len));
   3259     }
   3260 }
   3261 
   3262 fn setHashSections(self: *Elf) !void {
   3263     if (self.hash_section_index != null) {
   3264         try self.hash.generate(self);
   3265     }
   3266     if (self.gnu_hash_section_index != null) {
   3267         try self.gnu_hash.calcSize(self);
   3268     }
   3269 }
   3270 
   3271 fn phdrRank(phdr: elf.Elf64_Phdr) u8 {
   3272     switch (phdr.p_type) {
   3273         elf.PT_NULL => return 0,
   3274         elf.PT_PHDR => return 1,
   3275         elf.PT_INTERP => return 2,
   3276         elf.PT_LOAD => return 3,
   3277         elf.PT_DYNAMIC, elf.PT_TLS => return 4,
   3278         elf.PT_GNU_EH_FRAME => return 5,
   3279         elf.PT_GNU_STACK => return 6,
   3280         else => return 7,
   3281     }
   3282 }
   3283 
   3284 fn sortPhdrs(self: *Elf) error{OutOfMemory}!void {
   3285     const Entry = struct {
   3286         phndx: u16,
   3287 
   3288         pub fn lessThan(elf_file: *Elf, lhs: @This(), rhs: @This()) bool {
   3289             const lhs_phdr = elf_file.phdrs.items[lhs.phndx];
   3290             const rhs_phdr = elf_file.phdrs.items[rhs.phndx];
   3291             const lhs_rank = phdrRank(lhs_phdr);
   3292             const rhs_rank = phdrRank(rhs_phdr);
   3293             if (lhs_rank == rhs_rank) return lhs_phdr.p_vaddr < rhs_phdr.p_vaddr;
   3294             return lhs_rank < rhs_rank;
   3295         }
   3296     };
   3297 
   3298     const gpa = self.base.comp.gpa;
   3299     var entries = try std.ArrayList(Entry).initCapacity(gpa, self.phdrs.items.len);
   3300     defer entries.deinit();
   3301     for (0..self.phdrs.items.len) |phndx| {
   3302         entries.appendAssumeCapacity(.{ .phndx = @as(u16, @intCast(phndx)) });
   3303     }
   3304 
   3305     mem.sort(Entry, entries.items, self, Entry.lessThan);
   3306 
   3307     const backlinks = try gpa.alloc(u16, entries.items.len);
   3308     defer gpa.free(backlinks);
   3309     for (entries.items, 0..) |entry, i| {
   3310         backlinks[entry.phndx] = @as(u16, @intCast(i));
   3311     }
   3312 
   3313     const slice = try self.phdrs.toOwnedSlice(gpa);
   3314     defer gpa.free(slice);
   3315 
   3316     try self.phdrs.ensureTotalCapacityPrecise(gpa, slice.len);
   3317     for (entries.items) |sorted| {
   3318         self.phdrs.appendAssumeCapacity(slice[sorted.phndx]);
   3319     }
   3320 
   3321     for (&[_]*?u16{
   3322         &self.phdr_table_index,
   3323         &self.phdr_table_load_index,
   3324         &self.phdr_interp_index,
   3325         &self.phdr_dynamic_index,
   3326         &self.phdr_gnu_eh_frame_index,
   3327         &self.phdr_tls_index,
   3328     }) |maybe_index| {
   3329         if (maybe_index.*) |*index| {
   3330             index.* = backlinks[index.*];
   3331         }
   3332     }
   3333 
   3334     for (self.sections.items(.phndx)) |*maybe_phndx| {
   3335         if (maybe_phndx.*) |*index| {
   3336             index.* = backlinks[index.*];
   3337         }
   3338     }
   3339 }
   3340 
   3341 fn shdrRank(self: *Elf, shndx: u32) u8 {
   3342     const shdr = self.sections.items(.shdr)[shndx];
   3343     const name = self.getShString(shdr.sh_name);
   3344     const flags = shdr.sh_flags;
   3345 
   3346     switch (shdr.sh_type) {
   3347         elf.SHT_NULL => return 0,
   3348         elf.SHT_DYNSYM => return 2,
   3349         elf.SHT_HASH => return 3,
   3350         elf.SHT_GNU_HASH => return 3,
   3351         elf.SHT_GNU_VERSYM => return 4,
   3352         elf.SHT_GNU_VERDEF => return 4,
   3353         elf.SHT_GNU_VERNEED => return 4,
   3354 
   3355         elf.SHT_PREINIT_ARRAY,
   3356         elf.SHT_INIT_ARRAY,
   3357         elf.SHT_FINI_ARRAY,
   3358         => return 0xf1,
   3359 
   3360         elf.SHT_DYNAMIC => return 0xf2,
   3361 
   3362         elf.SHT_RELA, elf.SHT_GROUP => return 0xf,
   3363 
   3364         elf.SHT_PROGBITS => if (flags & elf.SHF_ALLOC != 0) {
   3365             if (flags & elf.SHF_EXECINSTR != 0) {
   3366                 return 0xf0;
   3367             } else if (flags & elf.SHF_WRITE != 0) {
   3368                 return if (flags & elf.SHF_TLS != 0) 0xf3 else 0xf5;
   3369             } else if (mem.eql(u8, name, ".interp")) {
   3370                 return 1;
   3371             } else if (mem.startsWith(u8, name, ".eh_frame")) {
   3372                 return 0xe1;
   3373             } else {
   3374                 return 0xe0;
   3375             }
   3376         } else {
   3377             if (mem.startsWith(u8, name, ".debug")) {
   3378                 return 0xf7;
   3379             } else {
   3380                 return 0xf8;
   3381             }
   3382         },
   3383         elf.SHT_X86_64_UNWIND => return 0xe1,
   3384 
   3385         elf.SHT_NOBITS => return if (flags & elf.SHF_TLS != 0) 0xf4 else 0xf6,
   3386         elf.SHT_SYMTAB => return 0xf9,
   3387         elf.SHT_STRTAB => return if (mem.eql(u8, name, ".dynstr")) 0x4 else 0xfa,
   3388         else => return 0xff,
   3389     }
   3390 }
   3391 
   3392 pub fn sortShdrs(self: *Elf) !void {
   3393     const Entry = struct {
   3394         shndx: u32,
   3395 
   3396         pub fn lessThan(elf_file: *Elf, lhs: @This(), rhs: @This()) bool {
   3397             return elf_file.shdrRank(lhs.shndx) < elf_file.shdrRank(rhs.shndx);
   3398         }
   3399     };
   3400 
   3401     const gpa = self.base.comp.gpa;
   3402     var entries = try std.ArrayList(Entry).initCapacity(gpa, self.sections.items(.shdr).len);
   3403     defer entries.deinit();
   3404     for (0..self.sections.items(.shdr).len) |shndx| {
   3405         entries.appendAssumeCapacity(.{ .shndx = @intCast(shndx) });
   3406     }
   3407 
   3408     mem.sort(Entry, entries.items, self, Entry.lessThan);
   3409 
   3410     const backlinks = try gpa.alloc(u32, entries.items.len);
   3411     defer gpa.free(backlinks);
   3412     for (entries.items, 0..) |entry, i| {
   3413         backlinks[entry.shndx] = @intCast(i);
   3414     }
   3415 
   3416     var slice = self.sections.toOwnedSlice();
   3417     defer slice.deinit(gpa);
   3418 
   3419     try self.sections.ensureTotalCapacity(gpa, slice.len);
   3420     for (entries.items) |sorted| {
   3421         self.sections.appendAssumeCapacity(slice.get(sorted.shndx));
   3422     }
   3423 
   3424     self.resetShdrIndexes(backlinks);
   3425 }
   3426 
   3427 fn resetShdrIndexes(self: *Elf, backlinks: []const u32) void {
   3428     for (&[_]*?u32{
   3429         &self.eh_frame_section_index,
   3430         &self.eh_frame_rela_section_index,
   3431         &self.eh_frame_hdr_section_index,
   3432         &self.got_section_index,
   3433         &self.symtab_section_index,
   3434         &self.strtab_section_index,
   3435         &self.shstrtab_section_index,
   3436         &self.interp_section_index,
   3437         &self.dynamic_section_index,
   3438         &self.dynsymtab_section_index,
   3439         &self.dynstrtab_section_index,
   3440         &self.hash_section_index,
   3441         &self.gnu_hash_section_index,
   3442         &self.plt_section_index,
   3443         &self.got_plt_section_index,
   3444         &self.plt_got_section_index,
   3445         &self.rela_dyn_section_index,
   3446         &self.rela_plt_section_index,
   3447         &self.copy_rel_section_index,
   3448         &self.versym_section_index,
   3449         &self.verneed_section_index,
   3450     }) |maybe_index| {
   3451         if (maybe_index.*) |*index| {
   3452             index.* = backlinks[index.*];
   3453         }
   3454     }
   3455 
   3456     for (self.merge_sections.items) |*msec| {
   3457         msec.output_section_index = backlinks[msec.output_section_index];
   3458     }
   3459 
   3460     const slice = self.sections.slice();
   3461     for (slice.items(.shdr), slice.items(.atom_list_2)) |*shdr, *atom_list| {
   3462         atom_list.output_section_index = backlinks[atom_list.output_section_index];
   3463         for (atom_list.atoms.keys()) |ref| {
   3464             self.atom(ref).?.output_section_index = atom_list.output_section_index;
   3465         }
   3466         if (shdr.sh_type == elf.SHT_RELA) {
   3467             // FIXME:JK we should spin up .symtab potentially earlier, or set all non-dynamic RELA sections
   3468             // to point at symtab
   3469             // shdr.sh_link = backlinks[shdr.sh_link];
   3470             shdr.sh_link = self.symtab_section_index.?;
   3471             shdr.sh_info = backlinks[shdr.sh_info];
   3472         }
   3473     }
   3474 
   3475     if (self.zigObjectPtr()) |zo| {
   3476         for (zo.atoms_indexes.items) |atom_index| {
   3477             const atom_ptr = zo.atom(atom_index) orelse continue;
   3478             atom_ptr.output_section_index = backlinks[atom_ptr.output_section_index];
   3479         }
   3480     }
   3481 
   3482     for (self.comdat_group_sections.items) |*cg| {
   3483         cg.shndx = backlinks[cg.shndx];
   3484     }
   3485 
   3486     if (self.symtab_section_index) |index| {
   3487         const shdr = &slice.items(.shdr)[index];
   3488         shdr.sh_link = self.strtab_section_index.?;
   3489     }
   3490 
   3491     if (self.dynamic_section_index) |index| {
   3492         const shdr = &slice.items(.shdr)[index];
   3493         shdr.sh_link = self.dynstrtab_section_index.?;
   3494     }
   3495 
   3496     if (self.dynsymtab_section_index) |index| {
   3497         const shdr = &slice.items(.shdr)[index];
   3498         shdr.sh_link = self.dynstrtab_section_index.?;
   3499     }
   3500 
   3501     if (self.hash_section_index) |index| {
   3502         const shdr = &slice.items(.shdr)[index];
   3503         shdr.sh_link = self.dynsymtab_section_index.?;
   3504     }
   3505 
   3506     if (self.gnu_hash_section_index) |index| {
   3507         const shdr = &slice.items(.shdr)[index];
   3508         shdr.sh_link = self.dynsymtab_section_index.?;
   3509     }
   3510 
   3511     if (self.versym_section_index) |index| {
   3512         const shdr = &slice.items(.shdr)[index];
   3513         shdr.sh_link = self.dynsymtab_section_index.?;
   3514     }
   3515 
   3516     if (self.verneed_section_index) |index| {
   3517         const shdr = &slice.items(.shdr)[index];
   3518         shdr.sh_link = self.dynstrtab_section_index.?;
   3519     }
   3520 
   3521     if (self.rela_dyn_section_index) |index| {
   3522         const shdr = &slice.items(.shdr)[index];
   3523         shdr.sh_link = self.dynsymtab_section_index orelse 0;
   3524     }
   3525 
   3526     if (self.rela_plt_section_index) |index| {
   3527         const shdr = &slice.items(.shdr)[index];
   3528         shdr.sh_link = self.dynsymtab_section_index.?;
   3529         shdr.sh_info = self.plt_section_index.?;
   3530     }
   3531 
   3532     if (self.eh_frame_rela_section_index) |index| {
   3533         const shdr = &slice.items(.shdr)[index];
   3534         shdr.sh_link = self.symtab_section_index.?;
   3535         shdr.sh_info = self.eh_frame_section_index.?;
   3536     }
   3537 }
   3538 
   3539 fn updateSectionSizes(self: *Elf) !void {
   3540     const slice = self.sections.slice();
   3541     for (slice.items(.shdr), slice.items(.atom_list_2)) |shdr, *atom_list| {
   3542         if (atom_list.atoms.keys().len == 0) continue;
   3543         if (!atom_list.dirty) continue;
   3544         if (self.requiresThunks() and shdr.sh_flags & elf.SHF_EXECINSTR != 0) continue;
   3545         atom_list.updateSize(self);
   3546         try atom_list.allocate(self);
   3547         atom_list.dirty = false;
   3548     }
   3549 
   3550     if (self.requiresThunks()) {
   3551         for (slice.items(.shdr), slice.items(.atom_list_2)) |shdr, *atom_list| {
   3552             if (shdr.sh_flags & elf.SHF_EXECINSTR == 0) continue;
   3553             if (atom_list.atoms.keys().len == 0) continue;
   3554             if (!atom_list.dirty) continue;
   3555 
   3556             // Create jump/branch range extenders if needed.
   3557             try self.createThunks(atom_list);
   3558             try atom_list.allocate(self);
   3559             atom_list.dirty = false;
   3560         }
   3561 
   3562         // FIXME:JK this will hopefully not be needed once we create a link from Atom/Thunk to AtomList.
   3563         for (self.thunks.items) |*th| {
   3564             th.value += slice.items(.atom_list_2)[th.output_section_index].value;
   3565         }
   3566     }
   3567 
   3568     const shdrs = slice.items(.shdr);
   3569     if (self.eh_frame_section_index) |index| {
   3570         shdrs[index].sh_size = try eh_frame.calcEhFrameSize(self);
   3571     }
   3572 
   3573     if (self.eh_frame_hdr_section_index) |index| {
   3574         shdrs[index].sh_size = eh_frame.calcEhFrameHdrSize(self);
   3575     }
   3576 
   3577     if (self.got_section_index) |index| {
   3578         shdrs[index].sh_size = self.got.size(self);
   3579     }
   3580 
   3581     if (self.plt_section_index) |index| {
   3582         shdrs[index].sh_size = self.plt.size(self);
   3583     }
   3584 
   3585     if (self.got_plt_section_index) |index| {
   3586         shdrs[index].sh_size = self.got_plt.size(self);
   3587     }
   3588 
   3589     if (self.plt_got_section_index) |index| {
   3590         shdrs[index].sh_size = self.plt_got.size(self);
   3591     }
   3592 
   3593     if (self.rela_dyn_section_index) |shndx| {
   3594         var num = self.got.numRela(self) + self.copy_rel.numRela();
   3595         if (self.zigObjectPtr()) |zig_object| {
   3596             num += zig_object.num_dynrelocs;
   3597         }
   3598         for (self.objects.items) |index| {
   3599             num += self.file(index).?.object.num_dynrelocs;
   3600         }
   3601         shdrs[shndx].sh_size = num * @sizeOf(elf.Elf64_Rela);
   3602     }
   3603 
   3604     if (self.rela_plt_section_index) |index| {
   3605         shdrs[index].sh_size = self.plt.numRela() * @sizeOf(elf.Elf64_Rela);
   3606     }
   3607 
   3608     if (self.copy_rel_section_index) |index| {
   3609         try self.copy_rel.updateSectionSize(index, self);
   3610     }
   3611 
   3612     if (self.interp_section_index) |index| {
   3613         shdrs[index].sh_size = self.getTarget().dynamic_linker.get().?.len + 1;
   3614     }
   3615 
   3616     if (self.hash_section_index) |index| {
   3617         shdrs[index].sh_size = self.hash.size();
   3618     }
   3619 
   3620     if (self.gnu_hash_section_index) |index| {
   3621         shdrs[index].sh_size = self.gnu_hash.size();
   3622     }
   3623 
   3624     if (self.dynamic_section_index) |index| {
   3625         shdrs[index].sh_size = self.dynamic.size(self);
   3626     }
   3627 
   3628     if (self.dynsymtab_section_index) |index| {
   3629         shdrs[index].sh_size = self.dynsym.size();
   3630     }
   3631 
   3632     if (self.dynstrtab_section_index) |index| {
   3633         shdrs[index].sh_size = self.dynstrtab.items.len;
   3634     }
   3635 
   3636     if (self.versym_section_index) |index| {
   3637         shdrs[index].sh_size = self.versym.items.len * @sizeOf(elf.Elf64_Versym);
   3638     }
   3639 
   3640     if (self.verneed_section_index) |index| {
   3641         shdrs[index].sh_size = self.verneed.size();
   3642     }
   3643 
   3644     try self.updateSymtabSize();
   3645     self.updateShStrtabSize();
   3646 }
   3647 
   3648 // FIXME:JK this is very much obsolete, remove!
   3649 pub fn updateShStrtabSize(self: *Elf) void {
   3650     if (self.shstrtab_section_index) |index| {
   3651         self.sections.items(.shdr)[index].sh_size = self.shstrtab.items.len;
   3652     }
   3653 }
   3654 
   3655 fn shdrToPhdrFlags(sh_flags: u64) u32 {
   3656     const write = sh_flags & elf.SHF_WRITE != 0;
   3657     const exec = sh_flags & elf.SHF_EXECINSTR != 0;
   3658     var out_flags: u32 = elf.PF_R;
   3659     if (write) out_flags |= elf.PF_W;
   3660     if (exec) out_flags |= elf.PF_X;
   3661     return out_flags;
   3662 }
   3663 
   3664 /// Returns maximum number of program headers that may be emitted by the linker.
   3665 /// (This is an upper bound so that we can reserve enough space for the header and progam header
   3666 /// table without running out of space and being forced to move things around.)
   3667 fn getMaxNumberOfPhdrs() u64 {
   3668     // The estimated maximum number of segments the linker can emit for input sections are:
   3669     var num: u64 = max_number_of_object_segments;
   3670     // Any other non-loadable program headers, including TLS, DYNAMIC, GNU_STACK, GNU_EH_FRAME, INTERP:
   3671     num += max_number_of_special_phdrs;
   3672     // PHDR program header and corresponding read-only load segment:
   3673     num += 2;
   3674     return num;
   3675 }
   3676 
   3677 fn addLoadPhdrs(self: *Elf) error{OutOfMemory}!void {
   3678     for (self.sections.items(.shdr)) |shdr| {
   3679         if (shdr.sh_type == elf.SHT_NULL) continue;
   3680         if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue;
   3681         const flags = shdrToPhdrFlags(shdr.sh_flags);
   3682         if (self.getPhdr(.{ .flags = flags, .type = elf.PT_LOAD }) == null) {
   3683             _ = try self.addPhdr(.{ .flags = flags, .type = elf.PT_LOAD });
   3684         }
   3685     }
   3686 }
   3687 
   3688 /// Allocates PHDR table in virtual memory and in file.
   3689 fn allocatePhdrTable(self: *Elf) error{OutOfMemory}!void {
   3690     const phdr_table = &self.phdrs.items[self.phdr_table_index.?];
   3691     const phdr_table_load = &self.phdrs.items[self.phdr_table_load_index.?];
   3692 
   3693     const ehsize: u64 = switch (self.ptr_width) {
   3694         .p32 => @sizeOf(elf.Elf32_Ehdr),
   3695         .p64 => @sizeOf(elf.Elf64_Ehdr),
   3696     };
   3697     const phsize: u64 = switch (self.ptr_width) {
   3698         .p32 => @sizeOf(elf.Elf32_Phdr),
   3699         .p64 => @sizeOf(elf.Elf64_Phdr),
   3700     };
   3701     const needed_size = self.phdrs.items.len * phsize;
   3702     const available_space = self.allocatedSize(phdr_table.p_offset);
   3703 
   3704     if (needed_size > available_space) {
   3705         // In this case, we have two options:
   3706         // 1. increase the available padding for EHDR + PHDR table so that we don't overflow it
   3707         //    (revisit getMaxNumberOfPhdrs())
   3708         // 2. shift everything in file to free more space for EHDR + PHDR table
   3709         // TODO verify `getMaxNumberOfPhdrs()` is accurate and convert this into no-op
   3710         var err = try self.base.addErrorWithNotes(1);
   3711         try err.addMsg("fatal linker error: not enough space reserved for EHDR and PHDR table", .{});
   3712         try err.addNote("required 0x{x}, available 0x{x}", .{ needed_size, available_space });
   3713     }
   3714 
   3715     phdr_table_load.p_filesz = needed_size + ehsize;
   3716     phdr_table_load.p_memsz = needed_size + ehsize;
   3717     phdr_table.p_filesz = needed_size;
   3718     phdr_table.p_memsz = needed_size;
   3719 }
   3720 
   3721 /// Allocates alloc sections and creates load segments for sections
   3722 /// extracted from input object files.
   3723 pub fn allocateAllocSections(self: *Elf) !void {
   3724     // We use this struct to track maximum alignment of all TLS sections.
   3725     // According to https://github.com/rui314/mold/commit/bd46edf3f0fe9e1a787ea453c4657d535622e61f in mold,
   3726     // in-file offsets have to be aligned against the start of TLS program header.
   3727     // If that's not ensured, then in a multi-threaded context, TLS variables across a shared object
   3728     // boundary may not get correctly loaded at an aligned address.
   3729     const Align = struct {
   3730         tls_start_align: u64 = 1,
   3731         first_tls_index: ?usize = null,
   3732 
   3733         fn isFirstTlsShdr(this: @This(), other: usize) bool {
   3734             if (this.first_tls_index) |index| return index == other;
   3735             return false;
   3736         }
   3737 
   3738         fn @"align"(this: @This(), index: usize, sh_addralign: u64, addr: u64) u64 {
   3739             const alignment = if (this.isFirstTlsShdr(index)) this.tls_start_align else sh_addralign;
   3740             return mem.alignForward(u64, addr, alignment);
   3741         }
   3742     };
   3743 
   3744     const slice = self.sections.slice();
   3745     var alignment = Align{};
   3746     for (slice.items(.shdr), 0..) |shdr, i| {
   3747         if (shdr.sh_type == elf.SHT_NULL) continue;
   3748         if (shdr.sh_flags & elf.SHF_TLS == 0) continue;
   3749         if (alignment.first_tls_index == null) alignment.first_tls_index = i;
   3750         alignment.tls_start_align = @max(alignment.tls_start_align, shdr.sh_addralign);
   3751     }
   3752 
   3753     // Next, calculate segment covers by scanning all alloc sections.
   3754     // If a section matches segment flags with the preceeding section,
   3755     // we put it in the same segment. Otherwise, we create a new cover.
   3756     // This algorithm is simple but suboptimal in terms of space re-use:
   3757     // normally we would also take into account any gaps in allocated
   3758     // virtual and file offsets. However, the simple one will do for one
   3759     // as we are more interested in quick turnaround and compatibility
   3760     // with `findFreeSpace` mechanics than anything else.
   3761     const Cover = std.ArrayList(u32);
   3762     const gpa = self.base.comp.gpa;
   3763     var covers: [max_number_of_object_segments]Cover = undefined;
   3764     for (&covers) |*cover| {
   3765         cover.* = Cover.init(gpa);
   3766     }
   3767     defer for (&covers) |*cover| {
   3768         cover.deinit();
   3769     };
   3770 
   3771     for (slice.items(.shdr), 0..) |shdr, shndx| {
   3772         if (shdr.sh_type == elf.SHT_NULL) continue;
   3773         if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue;
   3774         const flags = shdrToPhdrFlags(shdr.sh_flags);
   3775         try covers[flags - 1].append(@intCast(shndx));
   3776     }
   3777 
   3778     // Now we can proceed with allocating the sections in virtual memory.
   3779     // As the base address we take the end address of the PHDR table.
   3780     // When allocating we first find the largest required alignment
   3781     // of any section that is contained in a cover and use it to align
   3782     // the start address of the segement (and first section).
   3783     const phdr_table = &self.phdrs.items[self.phdr_table_load_index.?];
   3784     var addr = phdr_table.p_vaddr + phdr_table.p_memsz;
   3785 
   3786     for (covers) |cover| {
   3787         if (cover.items.len == 0) continue;
   3788 
   3789         var @"align": u64 = self.page_size;
   3790         for (cover.items) |shndx| {
   3791             const shdr = slice.items(.shdr)[shndx];
   3792             if (shdr.sh_type == elf.SHT_NOBITS and shdr.sh_flags & elf.SHF_TLS != 0) continue;
   3793             @"align" = @max(@"align", shdr.sh_addralign);
   3794         }
   3795 
   3796         addr = mem.alignForward(u64, addr, @"align");
   3797 
   3798         var memsz: u64 = 0;
   3799         var filesz: u64 = 0;
   3800         var i: usize = 0;
   3801         while (i < cover.items.len) : (i += 1) {
   3802             const shndx = cover.items[i];
   3803             const shdr = &slice.items(.shdr)[shndx];
   3804             if (shdr.sh_type == elf.SHT_NOBITS and shdr.sh_flags & elf.SHF_TLS != 0) {
   3805                 // .tbss is a little special as it's used only by the loader meaning it doesn't
   3806                 // need to be actually mmap'ed at runtime. We still need to correctly increment
   3807                 // the addresses of every TLS zerofill section tho. Thus, we hack it so that
   3808                 // we increment the start address like normal, however, after we are done,
   3809                 // the next ALLOC section will get its start address allocated within the same
   3810                 // range as the .tbss sections. We will get something like this:
   3811                 //
   3812                 // ...
   3813                 // .tbss 0x10
   3814                 // .tcommon 0x20
   3815                 // .data 0x10
   3816                 // ...
   3817                 var tbss_addr = addr;
   3818                 while (i < cover.items.len and
   3819                     slice.items(.shdr)[cover.items[i]].sh_type == elf.SHT_NOBITS and
   3820                     slice.items(.shdr)[cover.items[i]].sh_flags & elf.SHF_TLS != 0) : (i += 1)
   3821                 {
   3822                     const tbss_shndx = cover.items[i];
   3823                     const tbss_shdr = &slice.items(.shdr)[tbss_shndx];
   3824                     tbss_addr = alignment.@"align"(tbss_shndx, tbss_shdr.sh_addralign, tbss_addr);
   3825                     tbss_shdr.sh_addr = tbss_addr;
   3826                     tbss_addr += tbss_shdr.sh_size;
   3827                 }
   3828                 i -= 1;
   3829                 continue;
   3830             }
   3831             const next = alignment.@"align"(shndx, shdr.sh_addralign, addr);
   3832             const padding = next - addr;
   3833             addr = next;
   3834             shdr.sh_addr = addr;
   3835             if (shdr.sh_type != elf.SHT_NOBITS) {
   3836                 filesz += padding + shdr.sh_size;
   3837             }
   3838             memsz += padding + shdr.sh_size;
   3839             addr += shdr.sh_size;
   3840         }
   3841 
   3842         const first = slice.items(.shdr)[cover.items[0]];
   3843         const phndx = self.getPhdr(.{ .type = elf.PT_LOAD, .flags = shdrToPhdrFlags(first.sh_flags) }).?;
   3844         const phdr = &self.phdrs.items[phndx];
   3845         const allocated_size = self.allocatedSize(phdr.p_offset);
   3846         if (filesz > allocated_size) {
   3847             const old_offset = phdr.p_offset;
   3848             phdr.p_offset = 0;
   3849             var new_offset = try self.findFreeSpace(filesz, @"align");
   3850             phdr.p_offset = new_offset;
   3851 
   3852             log.debug("moving phdr({d}) from 0x{x} to 0x{x}", .{ phndx, old_offset, new_offset });
   3853 
   3854             for (cover.items) |shndx| {
   3855                 const shdr = &slice.items(.shdr)[shndx];
   3856                 slice.items(.phndx)[shndx] = phndx;
   3857                 if (shdr.sh_type == elf.SHT_NOBITS) {
   3858                     shdr.sh_offset = 0;
   3859                     continue;
   3860                 }
   3861                 new_offset = alignment.@"align"(shndx, shdr.sh_addralign, new_offset);
   3862 
   3863                 log.debug("moving {s} from 0x{x} to 0x{x}", .{
   3864                     self.getShString(shdr.sh_name),
   3865                     shdr.sh_offset,
   3866                     new_offset,
   3867                 });
   3868 
   3869                 if (shdr.sh_offset > 0) {
   3870                     // Get size actually commited to the output file.
   3871                     const existing_size = self.sectionSize(shndx);
   3872                     const amt = try self.base.file.?.copyRangeAll(
   3873                         shdr.sh_offset,
   3874                         self.base.file.?,
   3875                         new_offset,
   3876                         existing_size,
   3877                     );
   3878                     if (amt != existing_size) return error.InputOutput;
   3879                 }
   3880 
   3881                 shdr.sh_offset = new_offset;
   3882                 new_offset += shdr.sh_size;
   3883             }
   3884         }
   3885 
   3886         phdr.p_vaddr = first.sh_addr;
   3887         phdr.p_paddr = first.sh_addr;
   3888         phdr.p_memsz = memsz;
   3889         phdr.p_filesz = filesz;
   3890         phdr.p_align = @"align";
   3891 
   3892         addr = mem.alignForward(u64, addr, self.page_size);
   3893     }
   3894 }
   3895 
   3896 /// Allocates non-alloc sections (debug info, symtabs, etc.).
   3897 pub fn allocateNonAllocSections(self: *Elf) !void {
   3898     for (self.sections.items(.shdr), 0..) |*shdr, shndx| {
   3899         if (shdr.sh_type == elf.SHT_NULL) continue;
   3900         if (shdr.sh_flags & elf.SHF_ALLOC != 0) continue;
   3901         const needed_size = shdr.sh_size;
   3902         if (needed_size > self.allocatedSize(shdr.sh_offset)) {
   3903             shdr.sh_size = 0;
   3904             const new_offset = try self.findFreeSpace(needed_size, shdr.sh_addralign);
   3905 
   3906             if (self.zigObjectPtr()) |zo| blk: {
   3907                 const existing_size = for ([_]?Symbol.Index{
   3908                     zo.debug_info_index,
   3909                     zo.debug_abbrev_index,
   3910                     zo.debug_aranges_index,
   3911                     zo.debug_str_index,
   3912                     zo.debug_line_index,
   3913                     zo.debug_line_str_index,
   3914                     zo.debug_loclists_index,
   3915                     zo.debug_rnglists_index,
   3916                 }) |maybe_sym_index| {
   3917                     const sym_index = maybe_sym_index orelse continue;
   3918                     const sym = zo.symbol(sym_index);
   3919                     const atom_ptr = sym.atom(self).?;
   3920                     if (atom_ptr.output_section_index == shndx) break atom_ptr.size;
   3921                 } else break :blk;
   3922                 log.debug("moving {s} from 0x{x} to 0x{x}", .{
   3923                     self.getShString(shdr.sh_name),
   3924                     shdr.sh_offset,
   3925                     new_offset,
   3926                 });
   3927                 const amt = try self.base.file.?.copyRangeAll(
   3928                     shdr.sh_offset,
   3929                     self.base.file.?,
   3930                     new_offset,
   3931                     existing_size,
   3932                 );
   3933                 if (amt != existing_size) return error.InputOutput;
   3934             }
   3935 
   3936             shdr.sh_offset = new_offset;
   3937             shdr.sh_size = needed_size;
   3938         }
   3939     }
   3940 }
   3941 
   3942 fn allocateSpecialPhdrs(self: *Elf) void {
   3943     const slice = self.sections.slice();
   3944 
   3945     for (&[_]struct { ?u16, ?u32 }{
   3946         .{ self.phdr_interp_index, self.interp_section_index },
   3947         .{ self.phdr_dynamic_index, self.dynamic_section_index },
   3948         .{ self.phdr_gnu_eh_frame_index, self.eh_frame_hdr_section_index },
   3949     }) |pair| {
   3950         if (pair[0]) |index| {
   3951             const shdr = slice.items(.shdr)[pair[1].?];
   3952             const phdr = &self.phdrs.items[index];
   3953             phdr.p_align = shdr.sh_addralign;
   3954             phdr.p_offset = shdr.sh_offset;
   3955             phdr.p_vaddr = shdr.sh_addr;
   3956             phdr.p_paddr = shdr.sh_addr;
   3957             phdr.p_filesz = shdr.sh_size;
   3958             phdr.p_memsz = shdr.sh_size;
   3959         }
   3960     }
   3961 
   3962     // Set the TLS segment boundaries.
   3963     // We assume TLS sections are laid out contiguously and that there is
   3964     // a single TLS segment.
   3965     if (self.phdr_tls_index) |index| {
   3966         const shdrs = slice.items(.shdr);
   3967         const phdr = &self.phdrs.items[index];
   3968         var shndx: u32 = 0;
   3969         while (shndx < shdrs.len) {
   3970             const shdr = shdrs[shndx];
   3971             if (shdr.sh_flags & elf.SHF_TLS == 0) {
   3972                 shndx += 1;
   3973                 continue;
   3974             }
   3975             phdr.p_offset = shdr.sh_offset;
   3976             phdr.p_vaddr = shdr.sh_addr;
   3977             phdr.p_paddr = shdr.sh_addr;
   3978             phdr.p_align = shdr.sh_addralign;
   3979             shndx += 1;
   3980             phdr.p_align = @max(phdr.p_align, shdr.sh_addralign);
   3981             if (shdr.sh_type != elf.SHT_NOBITS) {
   3982                 phdr.p_filesz = shdr.sh_offset + shdr.sh_size - phdr.p_offset;
   3983             }
   3984             phdr.p_memsz = shdr.sh_addr + shdr.sh_size - phdr.p_vaddr;
   3985 
   3986             while (shndx < shdrs.len) : (shndx += 1) {
   3987                 const next = shdrs[shndx];
   3988                 if (next.sh_flags & elf.SHF_TLS == 0) break;
   3989                 phdr.p_align = @max(phdr.p_align, next.sh_addralign);
   3990                 if (next.sh_type != elf.SHT_NOBITS) {
   3991                     phdr.p_filesz = next.sh_offset + next.sh_size - phdr.p_offset;
   3992                 }
   3993                 phdr.p_memsz = next.sh_addr + next.sh_size - phdr.p_vaddr;
   3994             }
   3995         }
   3996     }
   3997 }
   3998 
   3999 fn writeAtoms(self: *Elf) !void {
   4000     const gpa = self.base.comp.gpa;
   4001 
   4002     var undefs = std.AutoArrayHashMap(SymbolResolver.Index, std.ArrayList(Ref)).init(gpa);
   4003     defer {
   4004         for (undefs.values()) |*refs| {
   4005             refs.deinit();
   4006         }
   4007         undefs.deinit();
   4008     }
   4009 
   4010     var buffer = std.ArrayList(u8).init(gpa);
   4011     defer buffer.deinit();
   4012 
   4013     const slice = self.sections.slice();
   4014     var has_reloc_errors = false;
   4015     for (slice.items(.shdr), slice.items(.atom_list_2)) |shdr, atom_list| {
   4016         if (shdr.sh_type == elf.SHT_NOBITS) continue;
   4017         if (atom_list.atoms.keys().len == 0) continue;
   4018         atom_list.write(&buffer, &undefs, self) catch |err| switch (err) {
   4019             error.UnsupportedCpuArch => {
   4020                 try self.reportUnsupportedCpuArch();
   4021                 return error.FlushFailure;
   4022             },
   4023             error.RelocFailure, error.RelaxFailure => has_reloc_errors = true,
   4024             else => |e| return e,
   4025         };
   4026     }
   4027 
   4028     try self.reportUndefinedSymbols(&undefs);
   4029     if (has_reloc_errors) return error.FlushFailure;
   4030 
   4031     if (self.requiresThunks()) {
   4032         for (self.thunks.items) |th| {
   4033             const thunk_size = th.size(self);
   4034             try buffer.ensureUnusedCapacity(thunk_size);
   4035             const shdr = slice.items(.shdr)[th.output_section_index];
   4036             const offset = @as(u64, @intCast(th.value)) + shdr.sh_offset;
   4037             try th.write(self, buffer.writer());
   4038             assert(buffer.items.len == thunk_size);
   4039             try self.base.file.?.pwriteAll(buffer.items, offset);
   4040             buffer.clearRetainingCapacity();
   4041         }
   4042     }
   4043 }
   4044 
   4045 pub fn updateSymtabSize(self: *Elf) !void {
   4046     var nlocals: u32 = 0;
   4047     var nglobals: u32 = 0;
   4048     var strsize: u32 = 0;
   4049 
   4050     const gpa = self.base.comp.gpa;
   4051     var files = std.ArrayList(File.Index).init(gpa);
   4052     defer files.deinit();
   4053     try files.ensureTotalCapacityPrecise(self.objects.items.len + self.shared_objects.items.len + 2);
   4054 
   4055     if (self.zig_object_index) |index| files.appendAssumeCapacity(index);
   4056     for (self.objects.items) |index| files.appendAssumeCapacity(index);
   4057     for (self.shared_objects.items) |index| files.appendAssumeCapacity(index);
   4058     if (self.linker_defined_index) |index| files.appendAssumeCapacity(index);
   4059 
   4060     // Section symbols
   4061     nlocals += @intCast(self.sections.slice().len);
   4062 
   4063     if (self.requiresThunks()) for (self.thunks.items) |*th| {
   4064         th.output_symtab_ctx.reset();
   4065         th.output_symtab_ctx.ilocal = nlocals;
   4066         th.calcSymtabSize(self);
   4067         nlocals += th.output_symtab_ctx.nlocals;
   4068         strsize += th.output_symtab_ctx.strsize;
   4069     };
   4070 
   4071     for (files.items) |index| {
   4072         const file_ptr = self.file(index).?;
   4073         const ctx = switch (file_ptr) {
   4074             inline else => |x| &x.output_symtab_ctx,
   4075         };
   4076         ctx.reset();
   4077         ctx.ilocal = nlocals;
   4078         ctx.iglobal = nglobals;
   4079         try file_ptr.updateSymtabSize(self);
   4080         nlocals += ctx.nlocals;
   4081         nglobals += ctx.nglobals;
   4082         strsize += ctx.strsize;
   4083     }
   4084 
   4085     if (self.got_section_index) |_| {
   4086         self.got.output_symtab_ctx.reset();
   4087         self.got.output_symtab_ctx.ilocal = nlocals;
   4088         self.got.updateSymtabSize(self);
   4089         nlocals += self.got.output_symtab_ctx.nlocals;
   4090         strsize += self.got.output_symtab_ctx.strsize;
   4091     }
   4092 
   4093     if (self.plt_section_index) |_| {
   4094         self.plt.output_symtab_ctx.reset();
   4095         self.plt.output_symtab_ctx.ilocal = nlocals;
   4096         self.plt.updateSymtabSize(self);
   4097         nlocals += self.plt.output_symtab_ctx.nlocals;
   4098         strsize += self.plt.output_symtab_ctx.strsize;
   4099     }
   4100 
   4101     if (self.plt_got_section_index) |_| {
   4102         self.plt_got.output_symtab_ctx.reset();
   4103         self.plt_got.output_symtab_ctx.ilocal = nlocals;
   4104         self.plt_got.updateSymtabSize(self);
   4105         nlocals += self.plt_got.output_symtab_ctx.nlocals;
   4106         strsize += self.plt_got.output_symtab_ctx.strsize;
   4107     }
   4108 
   4109     for (files.items) |index| {
   4110         const file_ptr = self.file(index).?;
   4111         const ctx = switch (file_ptr) {
   4112             inline else => |x| &x.output_symtab_ctx,
   4113         };
   4114         ctx.iglobal += nlocals;
   4115     }
   4116 
   4117     const slice = self.sections.slice();
   4118     const symtab_shdr = &slice.items(.shdr)[self.symtab_section_index.?];
   4119     symtab_shdr.sh_info = nlocals;
   4120     symtab_shdr.sh_link = self.strtab_section_index.?;
   4121 
   4122     const sym_size: u64 = switch (self.ptr_width) {
   4123         .p32 => @sizeOf(elf.Elf32_Sym),
   4124         .p64 => @sizeOf(elf.Elf64_Sym),
   4125     };
   4126     const needed_size = (nlocals + nglobals) * sym_size;
   4127     symtab_shdr.sh_size = needed_size;
   4128 
   4129     const strtab = &slice.items(.shdr)[self.strtab_section_index.?];
   4130     strtab.sh_size = strsize + 1;
   4131 }
   4132 
   4133 fn writeSyntheticSections(self: *Elf) !void {
   4134     const gpa = self.base.comp.gpa;
   4135     const slice = self.sections.slice();
   4136 
   4137     if (self.interp_section_index) |shndx| {
   4138         var buffer: [256]u8 = undefined;
   4139         const interp = self.getTarget().dynamic_linker.get().?;
   4140         @memcpy(buffer[0..interp.len], interp);
   4141         buffer[interp.len] = 0;
   4142         const contents = buffer[0 .. interp.len + 1];
   4143         const shdr = slice.items(.shdr)[shndx];
   4144         assert(shdr.sh_size == contents.len);
   4145         try self.base.file.?.pwriteAll(contents, shdr.sh_offset);
   4146     }
   4147 
   4148     if (self.hash_section_index) |shndx| {
   4149         const shdr = slice.items(.shdr)[shndx];
   4150         try self.base.file.?.pwriteAll(self.hash.buffer.items, shdr.sh_offset);
   4151     }
   4152 
   4153     if (self.gnu_hash_section_index) |shndx| {
   4154         const shdr = slice.items(.shdr)[shndx];
   4155         var buffer = try std.ArrayList(u8).initCapacity(gpa, self.gnu_hash.size());
   4156         defer buffer.deinit();
   4157         try self.gnu_hash.write(self, buffer.writer());
   4158         try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
   4159     }
   4160 
   4161     if (self.versym_section_index) |shndx| {
   4162         const shdr = slice.items(.shdr)[shndx];
   4163         try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.versym.items), shdr.sh_offset);
   4164     }
   4165 
   4166     if (self.verneed_section_index) |shndx| {
   4167         const shdr = slice.items(.shdr)[shndx];
   4168         var buffer = try std.ArrayList(u8).initCapacity(gpa, self.verneed.size());
   4169         defer buffer.deinit();
   4170         try self.verneed.write(buffer.writer());
   4171         try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
   4172     }
   4173 
   4174     if (self.dynamic_section_index) |shndx| {
   4175         const shdr = slice.items(.shdr)[shndx];
   4176         var buffer = try std.ArrayList(u8).initCapacity(gpa, self.dynamic.size(self));
   4177         defer buffer.deinit();
   4178         try self.dynamic.write(self, buffer.writer());
   4179         try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
   4180     }
   4181 
   4182     if (self.dynsymtab_section_index) |shndx| {
   4183         const shdr = slice.items(.shdr)[shndx];
   4184         var buffer = try std.ArrayList(u8).initCapacity(gpa, self.dynsym.size());
   4185         defer buffer.deinit();
   4186         try self.dynsym.write(self, buffer.writer());
   4187         try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
   4188     }
   4189 
   4190     if (self.dynstrtab_section_index) |shndx| {
   4191         const shdr = slice.items(.shdr)[shndx];
   4192         try self.base.file.?.pwriteAll(self.dynstrtab.items, shdr.sh_offset);
   4193     }
   4194 
   4195     if (self.eh_frame_section_index) |shndx| {
   4196         const existing_size = existing_size: {
   4197             const zo = self.zigObjectPtr() orelse break :existing_size 0;
   4198             const sym = zo.symbol(zo.eh_frame_index orelse break :existing_size 0);
   4199             break :existing_size sym.atom(self).?.size;
   4200         };
   4201         const shdr = slice.items(.shdr)[shndx];
   4202         const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;
   4203         var buffer = try std.ArrayList(u8).initCapacity(gpa, @intCast(sh_size - existing_size));
   4204         defer buffer.deinit();
   4205         try eh_frame.writeEhFrame(self, buffer.writer());
   4206         assert(buffer.items.len == sh_size - existing_size);
   4207         try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset + existing_size);
   4208     }
   4209 
   4210     if (self.eh_frame_hdr_section_index) |shndx| {
   4211         const shdr = slice.items(.shdr)[shndx];
   4212         const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;
   4213         var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size);
   4214         defer buffer.deinit();
   4215         try eh_frame.writeEhFrameHdr(self, buffer.writer());
   4216         try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
   4217     }
   4218 
   4219     if (self.got_section_index) |index| {
   4220         const shdr = slice.items(.shdr)[index];
   4221         var buffer = try std.ArrayList(u8).initCapacity(gpa, self.got.size(self));
   4222         defer buffer.deinit();
   4223         try self.got.write(self, buffer.writer());
   4224         try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
   4225     }
   4226 
   4227     if (self.rela_dyn_section_index) |shndx| {
   4228         const shdr = slice.items(.shdr)[shndx];
   4229         try self.got.addRela(self);
   4230         try self.copy_rel.addRela(self);
   4231         self.sortRelaDyn();
   4232         try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.rela_dyn.items), shdr.sh_offset);
   4233     }
   4234 
   4235     if (self.plt_section_index) |shndx| {
   4236         const shdr = slice.items(.shdr)[shndx];
   4237         var buffer = try std.ArrayList(u8).initCapacity(gpa, self.plt.size(self));
   4238         defer buffer.deinit();
   4239         try self.plt.write(self, buffer.writer());
   4240         try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
   4241     }
   4242 
   4243     if (self.got_plt_section_index) |shndx| {
   4244         const shdr = slice.items(.shdr)[shndx];
   4245         var buffer = try std.ArrayList(u8).initCapacity(gpa, self.got_plt.size(self));
   4246         defer buffer.deinit();
   4247         try self.got_plt.write(self, buffer.writer());
   4248         try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
   4249     }
   4250 
   4251     if (self.plt_got_section_index) |shndx| {
   4252         const shdr = slice.items(.shdr)[shndx];
   4253         var buffer = try std.ArrayList(u8).initCapacity(gpa, self.plt_got.size(self));
   4254         defer buffer.deinit();
   4255         try self.plt_got.write(self, buffer.writer());
   4256         try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
   4257     }
   4258 
   4259     if (self.rela_plt_section_index) |shndx| {
   4260         const shdr = slice.items(.shdr)[shndx];
   4261         try self.plt.addRela(self);
   4262         try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.rela_plt.items), shdr.sh_offset);
   4263     }
   4264 
   4265     try self.writeSymtab();
   4266     try self.writeShStrtab();
   4267 }
   4268 
   4269 // FIXME:JK again, why is this needed?
   4270 pub fn writeShStrtab(self: *Elf) !void {
   4271     if (self.shstrtab_section_index) |index| {
   4272         const shdr = self.sections.items(.shdr)[index];
   4273         log.debug("writing .shstrtab from 0x{x} to 0x{x}", .{ shdr.sh_offset, shdr.sh_offset + shdr.sh_size });
   4274         try self.base.file.?.pwriteAll(self.shstrtab.items, shdr.sh_offset);
   4275     }
   4276 }
   4277 
   4278 pub fn writeSymtab(self: *Elf) !void {
   4279     const gpa = self.base.comp.gpa;
   4280     const slice = self.sections.slice();
   4281     const symtab_shdr = slice.items(.shdr)[self.symtab_section_index.?];
   4282     const strtab_shdr = slice.items(.shdr)[self.strtab_section_index.?];
   4283     const sym_size: u64 = switch (self.ptr_width) {
   4284         .p32 => @sizeOf(elf.Elf32_Sym),
   4285         .p64 => @sizeOf(elf.Elf64_Sym),
   4286     };
   4287     const nsyms = math.cast(usize, @divExact(symtab_shdr.sh_size, sym_size)) orelse return error.Overflow;
   4288 
   4289     log.debug("writing {d} symbols in .symtab from 0x{x} to 0x{x}", .{
   4290         nsyms,
   4291         symtab_shdr.sh_offset,
   4292         symtab_shdr.sh_offset + symtab_shdr.sh_size,
   4293     });
   4294     log.debug("writing .strtab from 0x{x} to 0x{x}", .{
   4295         strtab_shdr.sh_offset,
   4296         strtab_shdr.sh_offset + strtab_shdr.sh_size,
   4297     });
   4298 
   4299     try self.symtab.resize(gpa, nsyms);
   4300     const needed_strtab_size = math.cast(usize, strtab_shdr.sh_size - 1) orelse return error.Overflow;
   4301     // TODO we could resize instead and in ZigObject/Object always access as slice
   4302     self.strtab.clearRetainingCapacity();
   4303     self.strtab.appendAssumeCapacity(0);
   4304     try self.strtab.ensureUnusedCapacity(gpa, needed_strtab_size);
   4305 
   4306     for (slice.items(.shdr), 0..) |shdr, shndx| {
   4307         const out_sym = &self.symtab.items[shndx];
   4308         out_sym.* = .{
   4309             .st_name = 0,
   4310             .st_value = shdr.sh_addr,
   4311             .st_info = if (shdr.sh_type == elf.SHT_NULL) elf.STT_NOTYPE else elf.STT_SECTION,
   4312             .st_shndx = @intCast(shndx),
   4313             .st_size = 0,
   4314             .st_other = 0,
   4315         };
   4316     }
   4317 
   4318     if (self.requiresThunks()) for (self.thunks.items) |th| {
   4319         th.writeSymtab(self);
   4320     };
   4321 
   4322     if (self.zigObjectPtr()) |zig_object| {
   4323         zig_object.asFile().writeSymtab(self);
   4324     }
   4325 
   4326     for (self.objects.items) |index| {
   4327         const file_ptr = self.file(index).?;
   4328         file_ptr.writeSymtab(self);
   4329     }
   4330 
   4331     for (self.shared_objects.items) |index| {
   4332         const file_ptr = self.file(index).?;
   4333         file_ptr.writeSymtab(self);
   4334     }
   4335 
   4336     if (self.linkerDefinedPtr()) |obj| {
   4337         obj.asFile().writeSymtab(self);
   4338     }
   4339 
   4340     if (self.got_section_index) |_| {
   4341         self.got.writeSymtab(self);
   4342     }
   4343 
   4344     if (self.plt_section_index) |_| {
   4345         self.plt.writeSymtab(self);
   4346     }
   4347 
   4348     if (self.plt_got_section_index) |_| {
   4349         self.plt_got.writeSymtab(self);
   4350     }
   4351 
   4352     const foreign_endian = self.getTarget().cpu.arch.endian() != builtin.cpu.arch.endian();
   4353     switch (self.ptr_width) {
   4354         .p32 => {
   4355             const buf = try gpa.alloc(elf.Elf32_Sym, self.symtab.items.len);
   4356             defer gpa.free(buf);
   4357 
   4358             for (buf, self.symtab.items) |*out, sym| {
   4359                 out.* = .{
   4360                     .st_name = sym.st_name,
   4361                     .st_info = sym.st_info,
   4362                     .st_other = sym.st_other,
   4363                     .st_shndx = sym.st_shndx,
   4364                     .st_value = @as(u32, @intCast(sym.st_value)),
   4365                     .st_size = @as(u32, @intCast(sym.st_size)),
   4366                 };
   4367                 if (foreign_endian) mem.byteSwapAllFields(elf.Elf32_Sym, out);
   4368             }
   4369             try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), symtab_shdr.sh_offset);
   4370         },
   4371         .p64 => {
   4372             if (foreign_endian) {
   4373                 for (self.symtab.items) |*sym| mem.byteSwapAllFields(elf.Elf64_Sym, sym);
   4374             }
   4375             try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.symtab.items), symtab_shdr.sh_offset);
   4376         },
   4377     }
   4378 
   4379     try self.base.file.?.pwriteAll(self.strtab.items, strtab_shdr.sh_offset);
   4380 }
   4381 
   4382 /// Always 4 or 8 depending on whether this is 32-bit ELF or 64-bit ELF.
   4383 pub fn ptrWidthBytes(self: Elf) u8 {
   4384     return switch (self.ptr_width) {
   4385         .p32 => 4,
   4386         .p64 => 8,
   4387     };
   4388 }
   4389 
   4390 /// Does not necessarily match `ptrWidthBytes` for example can be 2 bytes
   4391 /// in a 32-bit ELF file.
   4392 pub fn archPtrWidthBytes(self: Elf) u8 {
   4393     return @intCast(@divExact(self.getTarget().ptrBitWidth(), 8));
   4394 }
   4395 
   4396 fn phdrTo32(phdr: elf.Elf64_Phdr) elf.Elf32_Phdr {
   4397     return .{
   4398         .p_type = phdr.p_type,
   4399         .p_flags = phdr.p_flags,
   4400         .p_offset = @as(u32, @intCast(phdr.p_offset)),
   4401         .p_vaddr = @as(u32, @intCast(phdr.p_vaddr)),
   4402         .p_paddr = @as(u32, @intCast(phdr.p_paddr)),
   4403         .p_filesz = @as(u32, @intCast(phdr.p_filesz)),
   4404         .p_memsz = @as(u32, @intCast(phdr.p_memsz)),
   4405         .p_align = @as(u32, @intCast(phdr.p_align)),
   4406     };
   4407 }
   4408 
   4409 fn shdrTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr {
   4410     return .{
   4411         .sh_name = shdr.sh_name,
   4412         .sh_type = shdr.sh_type,
   4413         .sh_flags = @as(u32, @intCast(shdr.sh_flags)),
   4414         .sh_addr = @as(u32, @intCast(shdr.sh_addr)),
   4415         .sh_offset = @as(u32, @intCast(shdr.sh_offset)),
   4416         .sh_size = @as(u32, @intCast(shdr.sh_size)),
   4417         .sh_link = shdr.sh_link,
   4418         .sh_info = shdr.sh_info,
   4419         .sh_addralign = @as(u32, @intCast(shdr.sh_addralign)),
   4420         .sh_entsize = @as(u32, @intCast(shdr.sh_entsize)),
   4421     };
   4422 }
   4423 
   4424 fn getLDMOption(target: std.Target) ?[]const u8 {
   4425     // This should only return emulations understood by LLD's parseEmulation().
   4426     return switch (target.cpu.arch) {
   4427         .aarch64 => switch (target.os.tag) {
   4428             .linux => "aarch64linux",
   4429             else => "aarch64elf",
   4430         },
   4431         .aarch64_be => switch (target.os.tag) {
   4432             .linux => "aarch64linuxb",
   4433             else => "aarch64elfb",
   4434         },
   4435         .amdgcn => "elf64_amdgpu",
   4436         .arm, .thumb => switch (target.os.tag) {
   4437             .linux => "armelf_linux_eabi",
   4438             else => "armelf",
   4439         },
   4440         .armeb, .thumbeb => switch (target.os.tag) {
   4441             .linux => "armelfb_linux_eabi",
   4442             else => "armelfb",
   4443         },
   4444         .hexagon => "hexagonelf",
   4445         .loongarch32 => "elf32loongarch",
   4446         .loongarch64 => "elf64loongarch",
   4447         .mips => switch (target.os.tag) {
   4448             .freebsd => "elf32btsmip_fbsd",
   4449             else => "elf32btsmip",
   4450         },
   4451         .mipsel => switch (target.os.tag) {
   4452             .freebsd => "elf32ltsmip_fbsd",
   4453             else => "elf32ltsmip",
   4454         },
   4455         .mips64 => switch (target.os.tag) {
   4456             .freebsd => switch (target.abi) {
   4457                 .gnuabin32 => "elf32btsmipn32_fbsd",
   4458                 else => "elf64btsmip_fbsd",
   4459             },
   4460             else => switch (target.abi) {
   4461                 .gnuabin32 => "elf32btsmipn32",
   4462                 else => "elf64btsmip",
   4463             },
   4464         },
   4465         .mips64el => switch (target.os.tag) {
   4466             .freebsd => switch (target.abi) {
   4467                 .gnuabin32 => "elf32ltsmipn32_fbsd",
   4468                 else => "elf64ltsmip_fbsd",
   4469             },
   4470             else => switch (target.abi) {
   4471                 .gnuabin32 => "elf32ltsmipn32",
   4472                 else => "elf64ltsmip",
   4473             },
   4474         },
   4475         .msp430 => "msp430elf",
   4476         .powerpc => switch (target.os.tag) {
   4477             .freebsd => "elf32ppc_fbsd",
   4478             .linux => "elf32ppclinux",
   4479             else => "elf32ppc",
   4480         },
   4481         .powerpcle => switch (target.os.tag) {
   4482             .linux => "elf32lppclinux",
   4483             else => "elf32lppc",
   4484         },
   4485         .powerpc64 => "elf64ppc",
   4486         .powerpc64le => "elf64lppc",
   4487         .riscv32 => "elf32lriscv",
   4488         .riscv64 => "elf64lriscv",
   4489         .s390x => "elf64_s390",
   4490         .sparc64 => "elf64_sparc",
   4491         .x86 => switch (target.os.tag) {
   4492             .elfiamcu => "elf_iamcu",
   4493             .freebsd => "elf_i386_fbsd",
   4494             else => "elf_i386",
   4495         },
   4496         .x86_64 => switch (target.abi) {
   4497             .gnux32, .muslx32 => "elf32_x86_64",
   4498             else => "elf_x86_64",
   4499         },
   4500         else => null,
   4501     };
   4502 }
   4503 
   4504 pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
   4505     return actual_size +| (actual_size / ideal_factor);
   4506 }
   4507 
   4508 // Provide a blueprint of csu (c-runtime startup) objects for supported
   4509 // link modes.
   4510 //
   4511 // This is for cross-mode targets only. For host-mode targets the system
   4512 // compiler can be probed to produce a robust blueprint.
   4513 //
   4514 // Targets requiring a libc for which zig does not bundle a libc are
   4515 // host-mode targets. Unfortunately, host-mode probes are not yet
   4516 // implemented. For now the data is hard-coded here. Such targets are
   4517 // { freebsd, netbsd, openbsd, dragonfly }.
   4518 const CsuObjects = struct {
   4519     crt0: ?[]const u8 = null,
   4520     crti: ?[]const u8 = null,
   4521     crtbegin: ?[]const u8 = null,
   4522     crtend: ?[]const u8 = null,
   4523     crtn: ?[]const u8 = null,
   4524 
   4525     const InitArgs = struct {};
   4526 
   4527     fn init(arena: Allocator, comp: *const Compilation) !CsuObjects {
   4528         // crt objects are only required for libc.
   4529         if (!comp.config.link_libc) return .{};
   4530 
   4531         var result: CsuObjects = .{};
   4532 
   4533         // Flatten crt cases.
   4534         const mode: enum {
   4535             dynamic_lib,
   4536             dynamic_exe,
   4537             dynamic_pie,
   4538             static_exe,
   4539             static_pie,
   4540         } = switch (comp.config.output_mode) {
   4541             .Obj => return CsuObjects{},
   4542             .Lib => switch (comp.config.link_mode) {
   4543                 .dynamic => .dynamic_lib,
   4544                 .static => return CsuObjects{},
   4545             },
   4546             .Exe => switch (comp.config.link_mode) {
   4547                 .dynamic => if (comp.config.pie) .dynamic_pie else .dynamic_exe,
   4548                 .static => if (comp.config.pie) .static_pie else .static_exe,
   4549             },
   4550         };
   4551 
   4552         const target = comp.root_mod.resolved_target.result;
   4553 
   4554         if (target.isAndroid()) {
   4555             switch (mode) {
   4556                 // zig fmt: off
   4557                 .dynamic_lib => result.set( null, null, "crtbegin_so.o",      "crtend_so.o",      null ),
   4558                 .dynamic_exe,
   4559                 .dynamic_pie => result.set( null, null, "crtbegin_dynamic.o", "crtend_android.o", null ),
   4560                 .static_exe,
   4561                 .static_pie  => result.set( null, null, "crtbegin_static.o",  "crtend_android.o", null ),
   4562                 // zig fmt: on
   4563             }
   4564         } else {
   4565             switch (target.os.tag) {
   4566                 .linux => {
   4567                     switch (mode) {
   4568                         // zig fmt: off
   4569                         .dynamic_lib => result.set( null,      "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
   4570                         .dynamic_exe => result.set( "crt1.o",  "crti.o", "crtbegin.o",  "crtend.o",  "crtn.o" ),
   4571                         .dynamic_pie => result.set( "Scrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
   4572                         .static_exe  => result.set( "crt1.o",  "crti.o", "crtbeginT.o", "crtend.o",  "crtn.o" ),
   4573                         .static_pie  => result.set( "rcrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
   4574                         // zig fmt: on
   4575                     }
   4576                     if (comp.libc_installation) |_| {
   4577                         // hosted-glibc provides crtbegin/end objects in platform/compiler-specific dirs
   4578                         // and they are not known at comptime. For now null-out crtbegin/end objects;
   4579                         // there is no feature loss, zig has never linked those objects in before.
   4580                         result.crtbegin = null;
   4581                         result.crtend = null;
   4582                     } else {
   4583                         // Bundled glibc only has Scrt1.o .
   4584                         if (result.crt0 != null and target.isGnuLibC()) result.crt0 = "Scrt1.o";
   4585                     }
   4586                 },
   4587                 .dragonfly => switch (mode) {
   4588                     // zig fmt: off
   4589                     .dynamic_lib => result.set( null,      "crti.o", "crtbeginS.o",  "crtendS.o", "crtn.o" ),
   4590                     .dynamic_exe => result.set( "crt1.o",  "crti.o", "crtbegin.o",   "crtend.o",  "crtn.o" ),
   4591                     .dynamic_pie => result.set( "Scrt1.o", "crti.o", "crtbeginS.o",  "crtendS.o", "crtn.o" ),
   4592                     .static_exe  => result.set( "crt1.o",  "crti.o", "crtbegin.o",   "crtend.o",  "crtn.o" ),
   4593                     .static_pie  => result.set( "Scrt1.o", "crti.o", "crtbeginS.o",  "crtendS.o", "crtn.o" ),
   4594                     // zig fmt: on
   4595                 },
   4596                 .freebsd => switch (mode) {
   4597                     // zig fmt: off
   4598                     .dynamic_lib => result.set( null,      "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
   4599                     .dynamic_exe => result.set( "crt1.o",  "crti.o", "crtbegin.o",  "crtend.o",  "crtn.o" ),
   4600                     .dynamic_pie => result.set( "Scrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
   4601                     .static_exe  => result.set( "crt1.o",  "crti.o", "crtbeginT.o", "crtend.o",  "crtn.o" ),
   4602                     .static_pie  => result.set( "Scrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
   4603                     // zig fmt: on
   4604                 },
   4605                 .netbsd => switch (mode) {
   4606                     // zig fmt: off
   4607                     .dynamic_lib => result.set( null,     "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
   4608                     .dynamic_exe => result.set( "crt0.o", "crti.o", "crtbegin.o",  "crtend.o",  "crtn.o" ),
   4609                     .dynamic_pie => result.set( "crt0.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
   4610                     .static_exe  => result.set( "crt0.o", "crti.o", "crtbeginT.o", "crtend.o",  "crtn.o" ),
   4611                     .static_pie  => result.set( "crt0.o", "crti.o", "crtbeginT.o", "crtendS.o", "crtn.o" ),
   4612                     // zig fmt: on
   4613                 },
   4614                 .openbsd => switch (mode) {
   4615                     // zig fmt: off
   4616                     .dynamic_lib => result.set( null,      null, "crtbeginS.o", "crtendS.o", null ),
   4617                     .dynamic_exe,
   4618                     .dynamic_pie => result.set( "crt0.o",  null, "crtbegin.o",  "crtend.o",  null ),
   4619                     .static_exe,
   4620                     .static_pie  => result.set( "rcrt0.o", null, "crtbegin.o",  "crtend.o",  null ),
   4621                     // zig fmt: on
   4622                 },
   4623                 .haiku => switch (mode) {
   4624                     // zig fmt: off
   4625                     .dynamic_lib => result.set( null,          "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
   4626                     .dynamic_exe => result.set( "start_dyn.o", "crti.o", "crtbegin.o",  "crtend.o",  "crtn.o" ),
   4627                     .dynamic_pie => result.set( "start_dyn.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
   4628                     .static_exe  => result.set( "start_dyn.o", "crti.o", "crtbegin.o",  "crtend.o",  "crtn.o" ),
   4629                     .static_pie  => result.set( "start_dyn.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
   4630                     // zig fmt: on
   4631                 },
   4632                 .solaris, .illumos => switch (mode) {
   4633                     // zig fmt: off
   4634                     .dynamic_lib => result.set( null,     "crti.o", null, null, "crtn.o" ),
   4635                     .dynamic_exe,
   4636                     .dynamic_pie => result.set( "crt1.o", "crti.o", null, null, "crtn.o" ),
   4637                     .static_exe,
   4638                     .static_pie  => result.set( null,     null,     null, null, null     ),
   4639                     // zig fmt: on
   4640                 },
   4641                 else => {},
   4642             }
   4643         }
   4644 
   4645         // Convert each object to a full pathname.
   4646         if (comp.libc_installation) |lci| {
   4647             const crt_dir_path = lci.crt_dir orelse return error.LibCInstallationMissingCRTDir;
   4648             switch (target.os.tag) {
   4649                 .dragonfly => {
   4650                     if (result.crt0) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* });
   4651                     if (result.crti) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* });
   4652                     if (result.crtn) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* });
   4653 
   4654                     var gccv: []const u8 = undefined;
   4655                     if (target.os.version_range.semver.isAtLeast(.{ .major = 5, .minor = 4, .patch = 0 }) orelse true) {
   4656                         gccv = "gcc80";
   4657                     } else {
   4658                         gccv = "gcc54";
   4659                     }
   4660 
   4661                     if (result.crtbegin) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, gccv, obj.* });
   4662                     if (result.crtend) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, gccv, obj.* });
   4663                 },
   4664                 .haiku => {
   4665                     const gcc_dir_path = lci.gcc_dir orelse return error.LibCInstallationMissingCRTDir;
   4666                     if (result.crt0) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* });
   4667                     if (result.crti) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* });
   4668                     if (result.crtn) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* });
   4669 
   4670                     if (result.crtbegin) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ gcc_dir_path, obj.* });
   4671                     if (result.crtend) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ gcc_dir_path, obj.* });
   4672                 },
   4673                 else => {
   4674                     inline for (std.meta.fields(@TypeOf(result))) |f| {
   4675                         if (@field(result, f.name)) |*obj| {
   4676                             obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* });
   4677                         }
   4678                     }
   4679                 },
   4680             }
   4681         } else {
   4682             inline for (std.meta.fields(@TypeOf(result))) |f| {
   4683                 if (@field(result, f.name)) |*obj| {
   4684                     if (comp.crt_files.get(obj.*)) |crtf| {
   4685                         obj.* = crtf.full_object_path;
   4686                     } else {
   4687                         @field(result, f.name) = null;
   4688                     }
   4689                 }
   4690             }
   4691         }
   4692 
   4693         return result;
   4694     }
   4695 
   4696     fn set(
   4697         self: *CsuObjects,
   4698         crt0: ?[]const u8,
   4699         crti: ?[]const u8,
   4700         crtbegin: ?[]const u8,
   4701         crtend: ?[]const u8,
   4702         crtn: ?[]const u8,
   4703     ) void {
   4704         self.crt0 = crt0;
   4705         self.crti = crti;
   4706         self.crtbegin = crtbegin;
   4707         self.crtend = crtend;
   4708         self.crtn = crtn;
   4709     }
   4710 };
   4711 
   4712 /// If a target compiles other output modes as dynamic libraries,
   4713 /// this function returns true for those too.
   4714 pub fn isEffectivelyDynLib(self: Elf) bool {
   4715     if (self.base.isDynLib()) return true;
   4716     return switch (self.getTarget().os.tag) {
   4717         .haiku => self.base.isExe(),
   4718         else => false,
   4719     };
   4720 }
   4721 
   4722 fn getPhdr(self: *Elf, opts: struct {
   4723     type: u32 = 0,
   4724     flags: u32 = 0,
   4725 }) ?u16 {
   4726     for (self.phdrs.items, 0..) |phdr, phndx| {
   4727         if (self.phdr_table_load_index) |index| {
   4728             if (phndx == index) continue;
   4729         }
   4730         if (phdr.p_type == opts.type and phdr.p_flags == opts.flags) return @intCast(phndx);
   4731     }
   4732     return null;
   4733 }
   4734 
   4735 fn addPhdr(self: *Elf, opts: struct {
   4736     type: u32 = 0,
   4737     flags: u32 = 0,
   4738     @"align": u64 = 0,
   4739     offset: u64 = 0,
   4740     addr: u64 = 0,
   4741     filesz: u64 = 0,
   4742     memsz: u64 = 0,
   4743 }) error{OutOfMemory}!u16 {
   4744     const gpa = self.base.comp.gpa;
   4745     const index = @as(u16, @intCast(self.phdrs.items.len));
   4746     try self.phdrs.append(gpa, .{
   4747         .p_type = opts.type,
   4748         .p_flags = opts.flags,
   4749         .p_offset = opts.offset,
   4750         .p_vaddr = opts.addr,
   4751         .p_paddr = opts.addr,
   4752         .p_filesz = opts.filesz,
   4753         .p_memsz = opts.memsz,
   4754         .p_align = opts.@"align",
   4755     });
   4756     return index;
   4757 }
   4758 
   4759 pub fn addRelaShdr(self: *Elf, name: u32, shndx: u32) !u32 {
   4760     const entsize: u64 = switch (self.ptr_width) {
   4761         .p32 => @sizeOf(elf.Elf32_Rela),
   4762         .p64 => @sizeOf(elf.Elf64_Rela),
   4763     };
   4764     const addralign: u64 = switch (self.ptr_width) {
   4765         .p32 => @alignOf(elf.Elf32_Rela),
   4766         .p64 => @alignOf(elf.Elf64_Rela),
   4767     };
   4768     return self.addSection(.{
   4769         .name = name,
   4770         .type = elf.SHT_RELA,
   4771         .flags = elf.SHF_INFO_LINK,
   4772         .entsize = entsize,
   4773         .info = shndx,
   4774         .addralign = addralign,
   4775         .offset = std.math.maxInt(u64),
   4776     });
   4777 }
   4778 
   4779 pub const AddSectionOpts = struct {
   4780     name: u32 = 0,
   4781     type: u32 = elf.SHT_NULL,
   4782     flags: u64 = 0,
   4783     link: u32 = 0,
   4784     info: u32 = 0,
   4785     addralign: u64 = 0,
   4786     entsize: u64 = 0,
   4787     offset: u64 = 0,
   4788 };
   4789 
   4790 pub fn addSection(self: *Elf, opts: AddSectionOpts) !u32 {
   4791     const gpa = self.base.comp.gpa;
   4792     const index: u32 = @intCast(try self.sections.addOne(gpa));
   4793     self.sections.set(index, .{
   4794         .shdr = .{
   4795             .sh_name = opts.name,
   4796             .sh_type = opts.type,
   4797             .sh_flags = opts.flags,
   4798             .sh_addr = 0,
   4799             .sh_offset = opts.offset,
   4800             .sh_size = 0,
   4801             .sh_link = opts.link,
   4802             .sh_info = opts.info,
   4803             .sh_addralign = opts.addralign,
   4804             .sh_entsize = opts.entsize,
   4805         },
   4806     });
   4807     return index;
   4808 }
   4809 
   4810 pub fn sectionByName(self: *Elf, name: [:0]const u8) ?u32 {
   4811     for (self.sections.items(.shdr), 0..) |*shdr, i| {
   4812         const this_name = self.getShString(shdr.sh_name);
   4813         if (mem.eql(u8, this_name, name)) return @intCast(i);
   4814     } else return null;
   4815 }
   4816 
   4817 const RelaDyn = struct {
   4818     offset: u64,
   4819     sym: u64 = 0,
   4820     type: u32,
   4821     addend: i64 = 0,
   4822     target: ?*const Symbol = null,
   4823 };
   4824 
   4825 pub fn addRelaDyn(self: *Elf, opts: RelaDyn) !void {
   4826     try self.rela_dyn.ensureUnusedCapacity(self.base.alloctor, 1);
   4827     self.addRelaDynAssumeCapacity(opts);
   4828 }
   4829 
   4830 pub fn addRelaDynAssumeCapacity(self: *Elf, opts: RelaDyn) void {
   4831     relocs_log.debug("  {s}: [{x} => {d}({s})] + {x}", .{
   4832         relocation.fmtRelocType(opts.type, self.getTarget().cpu.arch),
   4833         opts.offset,
   4834         opts.sym,
   4835         if (opts.target) |sym| sym.name(self) else "",
   4836         opts.addend,
   4837     });
   4838     self.rela_dyn.appendAssumeCapacity(.{
   4839         .r_offset = opts.offset,
   4840         .r_info = (opts.sym << 32) | opts.type,
   4841         .r_addend = opts.addend,
   4842     });
   4843 }
   4844 
   4845 fn sortRelaDyn(self: *Elf) void {
   4846     const Sort = struct {
   4847         fn rank(rel: elf.Elf64_Rela, ctx: *Elf) u2 {
   4848             const cpu_arch = ctx.getTarget().cpu.arch;
   4849             const r_type = rel.r_type();
   4850             const r_kind = relocation.decode(r_type, cpu_arch).?;
   4851             return switch (r_kind) {
   4852                 .rel => 0,
   4853                 .irel => 2,
   4854                 else => 1,
   4855             };
   4856         }
   4857 
   4858         pub fn lessThan(ctx: *Elf, lhs: elf.Elf64_Rela, rhs: elf.Elf64_Rela) bool {
   4859             if (rank(lhs, ctx) == rank(rhs, ctx)) {
   4860                 if (lhs.r_sym() == rhs.r_sym()) return lhs.r_offset < rhs.r_offset;
   4861                 return lhs.r_sym() < rhs.r_sym();
   4862             }
   4863             return rank(lhs, ctx) < rank(rhs, ctx);
   4864         }
   4865     };
   4866     mem.sort(elf.Elf64_Rela, self.rela_dyn.items, self, Sort.lessThan);
   4867 }
   4868 
   4869 pub fn calcNumIRelativeRelocs(self: *Elf) usize {
   4870     var count: usize = self.num_ifunc_dynrelocs;
   4871 
   4872     for (self.got.entries.items) |entry| {
   4873         if (entry.tag != .got) continue;
   4874         const sym = self.symbol(entry.ref).?;
   4875         if (sym.isIFunc(self)) count += 1;
   4876     }
   4877 
   4878     return count;
   4879 }
   4880 
   4881 pub fn getStartStopBasename(self: Elf, shdr: elf.Elf64_Shdr) ?[]const u8 {
   4882     const name = self.getShString(shdr.sh_name);
   4883     if (shdr.sh_flags & elf.SHF_ALLOC != 0 and name.len > 0) {
   4884         if (Elf.isCIdentifier(name)) return name;
   4885     }
   4886     return null;
   4887 }
   4888 
   4889 pub fn isCIdentifier(name: []const u8) bool {
   4890     if (name.len == 0) return false;
   4891     const first_c = name[0];
   4892     if (!std.ascii.isAlphabetic(first_c) and first_c != '_') return false;
   4893     for (name[1..]) |c| {
   4894         if (!std.ascii.isAlphanumeric(c) and c != '_') return false;
   4895     }
   4896     return true;
   4897 }
   4898 
   4899 pub fn addThunk(self: *Elf) !Thunk.Index {
   4900     const index = @as(Thunk.Index, @intCast(self.thunks.items.len));
   4901     const th = try self.thunks.addOne(self.base.comp.gpa);
   4902     th.* = .{};
   4903     return index;
   4904 }
   4905 
   4906 pub fn thunk(self: *Elf, index: Thunk.Index) *Thunk {
   4907     assert(index < self.thunks.items.len);
   4908     return &self.thunks.items[index];
   4909 }
   4910 
   4911 pub fn file(self: *Elf, index: File.Index) ?File {
   4912     const tag = self.files.items(.tags)[index];
   4913     return switch (tag) {
   4914         .null => null,
   4915         .linker_defined => .{ .linker_defined = &self.files.items(.data)[index].linker_defined },
   4916         .zig_object => .{ .zig_object = &self.files.items(.data)[index].zig_object },
   4917         .object => .{ .object = &self.files.items(.data)[index].object },
   4918         .shared_object => .{ .shared_object = &self.files.items(.data)[index].shared_object },
   4919     };
   4920 }
   4921 
   4922 pub fn addFileHandle(self: *Elf, handle: fs.File) !File.HandleIndex {
   4923     const gpa = self.base.comp.gpa;
   4924     const index: File.HandleIndex = @intCast(self.file_handles.items.len);
   4925     const fh = try self.file_handles.addOne(gpa);
   4926     fh.* = handle;
   4927     return index;
   4928 }
   4929 
   4930 pub fn fileHandle(self: Elf, index: File.HandleIndex) File.Handle {
   4931     assert(index < self.file_handles.items.len);
   4932     return self.file_handles.items[index];
   4933 }
   4934 
   4935 pub fn atom(self: *Elf, ref: Ref) ?*Atom {
   4936     const file_ptr = self.file(ref.file) orelse return null;
   4937     return file_ptr.atom(ref.index);
   4938 }
   4939 
   4940 pub fn comdatGroup(self: *Elf, ref: Ref) *ComdatGroup {
   4941     return self.file(ref.file).?.comdatGroup(ref.index);
   4942 }
   4943 
   4944 pub fn symbol(self: *Elf, ref: Ref) ?*Symbol {
   4945     const file_ptr = self.file(ref.file) orelse return null;
   4946     return file_ptr.symbol(ref.index);
   4947 }
   4948 
   4949 pub fn getGlobalSymbol(self: *Elf, name: []const u8, lib_name: ?[]const u8) !u32 {
   4950     return self.zigObjectPtr().?.getGlobalSymbol(self, name, lib_name);
   4951 }
   4952 
   4953 pub fn zigObjectPtr(self: *Elf) ?*ZigObject {
   4954     const index = self.zig_object_index orelse return null;
   4955     return self.file(index).?.zig_object;
   4956 }
   4957 
   4958 pub fn linkerDefinedPtr(self: *Elf) ?*LinkerDefined {
   4959     const index = self.linker_defined_index orelse return null;
   4960     return self.file(index).?.linker_defined;
   4961 }
   4962 
   4963 pub fn getOrCreateMergeSection(self: *Elf, name: [:0]const u8, flags: u64, @"type": u32) !MergeSection.Index {
   4964     const gpa = self.base.comp.gpa;
   4965     const out_name = name: {
   4966         if (self.base.isRelocatable()) break :name name;
   4967         if (mem.eql(u8, name, ".rodata") or mem.startsWith(u8, name, ".rodata"))
   4968             break :name if (flags & elf.SHF_STRINGS != 0) ".rodata.str" else ".rodata.cst";
   4969         break :name name;
   4970     };
   4971     for (self.merge_sections.items, 0..) |msec, index| {
   4972         if (mem.eql(u8, msec.name(self), out_name)) return @intCast(index);
   4973     }
   4974     const out_off = try self.insertShString(out_name);
   4975     const out_flags = flags & ~@as(u64, elf.SHF_COMPRESSED | elf.SHF_GROUP);
   4976     const index = @as(MergeSection.Index, @intCast(self.merge_sections.items.len));
   4977     const msec = try self.merge_sections.addOne(gpa);
   4978     msec.* = .{
   4979         .name_offset = out_off,
   4980         .flags = out_flags,
   4981         .type = @"type",
   4982     };
   4983     return index;
   4984 }
   4985 
   4986 pub fn mergeSection(self: *Elf, index: MergeSection.Index) *MergeSection {
   4987     assert(index < self.merge_sections.items.len);
   4988     return &self.merge_sections.items[index];
   4989 }
   4990 
   4991 pub fn gotAddress(self: *Elf) i64 {
   4992     const shndx = blk: {
   4993         if (self.getTarget().cpu.arch == .x86_64 and self.got_plt_section_index != null)
   4994             break :blk self.got_plt_section_index.?;
   4995         break :blk if (self.got_section_index) |shndx| shndx else null;
   4996     };
   4997     return if (shndx) |index| @intCast(self.sections.items(.shdr)[index].sh_addr) else 0;
   4998 }
   4999 
   5000 pub fn tpAddress(self: *Elf) i64 {
   5001     const index = self.phdr_tls_index orelse return 0;
   5002     const phdr = self.phdrs.items[index];
   5003     const addr = switch (self.getTarget().cpu.arch) {
   5004         .x86_64 => mem.alignForward(u64, phdr.p_vaddr + phdr.p_memsz, phdr.p_align),
   5005         .aarch64 => mem.alignBackward(u64, phdr.p_vaddr - 16, phdr.p_align),
   5006         .riscv64 => phdr.p_vaddr,
   5007         else => |arch| std.debug.panic("TODO implement getTpAddress for {s}", .{@tagName(arch)}),
   5008     };
   5009     return @intCast(addr);
   5010 }
   5011 
   5012 pub fn dtpAddress(self: *Elf) i64 {
   5013     const index = self.phdr_tls_index orelse return 0;
   5014     const phdr = self.phdrs.items[index];
   5015     return @intCast(phdr.p_vaddr);
   5016 }
   5017 
   5018 pub fn tlsAddress(self: *Elf) i64 {
   5019     const index = self.phdr_tls_index orelse return 0;
   5020     const phdr = self.phdrs.items[index];
   5021     return @intCast(phdr.p_vaddr);
   5022 }
   5023 
   5024 pub fn getShString(self: Elf, off: u32) [:0]const u8 {
   5025     assert(off < self.shstrtab.items.len);
   5026     return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.shstrtab.items.ptr + off)), 0);
   5027 }
   5028 
   5029 pub fn insertShString(self: *Elf, name: [:0]const u8) error{OutOfMemory}!u32 {
   5030     const gpa = self.base.comp.gpa;
   5031     const off = @as(u32, @intCast(self.shstrtab.items.len));
   5032     try self.shstrtab.ensureUnusedCapacity(gpa, name.len + 1);
   5033     self.shstrtab.writer(gpa).print("{s}\x00", .{name}) catch unreachable;
   5034     return off;
   5035 }
   5036 
   5037 pub fn getDynString(self: Elf, off: u32) [:0]const u8 {
   5038     assert(off < self.dynstrtab.items.len);
   5039     return mem.sliceTo(@as([*:0]const u8, @ptrCast(self.dynstrtab.items.ptr + off)), 0);
   5040 }
   5041 
   5042 pub fn insertDynString(self: *Elf, name: []const u8) error{OutOfMemory}!u32 {
   5043     const gpa = self.base.comp.gpa;
   5044     const off = @as(u32, @intCast(self.dynstrtab.items.len));
   5045     try self.dynstrtab.ensureUnusedCapacity(gpa, name.len + 1);
   5046     self.dynstrtab.writer(gpa).print("{s}\x00", .{name}) catch unreachable;
   5047     return off;
   5048 }
   5049 
   5050 fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void {
   5051     const gpa = self.base.comp.gpa;
   5052     const max_notes = 4;
   5053 
   5054     try self.base.comp.link_errors.ensureUnusedCapacity(gpa, undefs.count());
   5055 
   5056     for (undefs.keys(), undefs.values()) |key, refs| {
   5057         const undef_sym = self.resolver.keys.items[key - 1];
   5058         const nrefs = @min(refs.items.len, max_notes);
   5059         const nnotes = nrefs + @intFromBool(refs.items.len > max_notes);
   5060 
   5061         var err = try self.base.addErrorWithNotesAssumeCapacity(nnotes);
   5062         try err.addMsg("undefined symbol: {s}", .{undef_sym.name(self)});
   5063 
   5064         for (refs.items[0..nrefs]) |ref| {
   5065             const atom_ptr = self.atom(ref).?;
   5066             const file_ptr = atom_ptr.file(self).?;
   5067             try err.addNote("referenced by {s}:{s}", .{ file_ptr.fmtPath(), atom_ptr.name(self) });
   5068         }
   5069 
   5070         if (refs.items.len > max_notes) {
   5071             const remaining = refs.items.len - max_notes;
   5072             try err.addNote("referenced {d} more times", .{remaining});
   5073         }
   5074     }
   5075 }
   5076 
   5077 fn reportDuplicates(self: *Elf, dupes: anytype) error{ HasDuplicates, OutOfMemory }!void {
   5078     if (dupes.keys().len == 0) return; // Nothing to do
   5079 
   5080     const max_notes = 3;
   5081 
   5082     for (dupes.keys(), dupes.values()) |key, notes| {
   5083         const sym = self.resolver.keys.items[key - 1];
   5084         const nnotes = @min(notes.items.len, max_notes) + @intFromBool(notes.items.len > max_notes);
   5085 
   5086         var err = try self.base.addErrorWithNotes(nnotes + 1);
   5087         try err.addMsg("duplicate symbol definition: {s}", .{sym.name(self)});
   5088         try err.addNote("defined by {}", .{sym.file(self).?.fmtPath()});
   5089 
   5090         var inote: usize = 0;
   5091         while (inote < @min(notes.items.len, max_notes)) : (inote += 1) {
   5092             const file_ptr = self.file(notes.items[inote]).?;
   5093             try err.addNote("defined by {}", .{file_ptr.fmtPath()});
   5094         }
   5095 
   5096         if (notes.items.len > max_notes) {
   5097             const remaining = notes.items.len - max_notes;
   5098             try err.addNote("defined {d} more times", .{remaining});
   5099         }
   5100     }
   5101 
   5102     return error.HasDuplicates;
   5103 }
   5104 
   5105 fn reportMissingLibraryError(
   5106     self: *Elf,
   5107     checked_paths: []const []const u8,
   5108     comptime format: []const u8,
   5109     args: anytype,
   5110 ) error{OutOfMemory}!void {
   5111     var err = try self.base.addErrorWithNotes(checked_paths.len);
   5112     try err.addMsg(format, args);
   5113     for (checked_paths) |path| {
   5114         try err.addNote("tried {s}", .{path});
   5115     }
   5116 }
   5117 
   5118 fn reportUnsupportedCpuArch(self: *Elf) error{OutOfMemory}!void {
   5119     var err = try self.base.addErrorWithNotes(0);
   5120     try err.addMsg("fatal linker error: unsupported CPU architecture {s}", .{
   5121         @tagName(self.getTarget().cpu.arch),
   5122     });
   5123 }
   5124 
   5125 pub fn addParseError(
   5126     self: *Elf,
   5127     path: []const u8,
   5128     comptime format: []const u8,
   5129     args: anytype,
   5130 ) error{OutOfMemory}!void {
   5131     var err = try self.base.addErrorWithNotes(1);
   5132     try err.addMsg(format, args);
   5133     try err.addNote("while parsing {s}", .{path});
   5134 }
   5135 
   5136 pub fn addFileError(
   5137     self: *Elf,
   5138     file_index: File.Index,
   5139     comptime format: []const u8,
   5140     args: anytype,
   5141 ) error{OutOfMemory}!void {
   5142     var err = try self.base.addErrorWithNotes(1);
   5143     try err.addMsg(format, args);
   5144     try err.addNote("while parsing {}", .{self.file(file_index).?.fmtPath()});
   5145 }
   5146 
   5147 pub fn failFile(
   5148     self: *Elf,
   5149     file_index: File.Index,
   5150     comptime format: []const u8,
   5151     args: anytype,
   5152 ) error{ OutOfMemory, LinkFailure } {
   5153     try addFileError(self, file_index, format, args);
   5154     return error.LinkFailure;
   5155 }
   5156 
   5157 pub fn failParse(
   5158     self: *Elf,
   5159     path: []const u8,
   5160     comptime format: []const u8,
   5161     args: anytype,
   5162 ) error{ OutOfMemory, LinkFailure } {
   5163     try addParseError(self, path, format, args);
   5164     return error.LinkFailure;
   5165 }
   5166 
   5167 const FormatShdrCtx = struct {
   5168     elf_file: *Elf,
   5169     shdr: elf.Elf64_Shdr,
   5170 };
   5171 
   5172 fn fmtShdr(self: *Elf, shdr: elf.Elf64_Shdr) std.fmt.Formatter(formatShdr) {
   5173     return .{ .data = .{
   5174         .shdr = shdr,
   5175         .elf_file = self,
   5176     } };
   5177 }
   5178 
   5179 fn formatShdr(
   5180     ctx: FormatShdrCtx,
   5181     comptime unused_fmt_string: []const u8,
   5182     options: std.fmt.FormatOptions,
   5183     writer: anytype,
   5184 ) !void {
   5185     _ = options;
   5186     _ = unused_fmt_string;
   5187     const shdr = ctx.shdr;
   5188     try writer.print("{s} : @{x} ({x}) : align({x}) : size({x}) : entsize({x}) : flags({})", .{
   5189         ctx.elf_file.getShString(shdr.sh_name), shdr.sh_offset,
   5190         shdr.sh_addr,                           shdr.sh_addralign,
   5191         shdr.sh_size,                           shdr.sh_entsize,
   5192         fmtShdrFlags(shdr.sh_flags),
   5193     });
   5194 }
   5195 
   5196 pub fn fmtShdrFlags(sh_flags: u64) std.fmt.Formatter(formatShdrFlags) {
   5197     return .{ .data = sh_flags };
   5198 }
   5199 
   5200 fn formatShdrFlags(
   5201     sh_flags: u64,
   5202     comptime unused_fmt_string: []const u8,
   5203     options: std.fmt.FormatOptions,
   5204     writer: anytype,
   5205 ) !void {
   5206     _ = unused_fmt_string;
   5207     _ = options;
   5208     if (elf.SHF_WRITE & sh_flags != 0) {
   5209         try writer.writeAll("W");
   5210     }
   5211     if (elf.SHF_ALLOC & sh_flags != 0) {
   5212         try writer.writeAll("A");
   5213     }
   5214     if (elf.SHF_EXECINSTR & sh_flags != 0) {
   5215         try writer.writeAll("X");
   5216     }
   5217     if (elf.SHF_MERGE & sh_flags != 0) {
   5218         try writer.writeAll("M");
   5219     }
   5220     if (elf.SHF_STRINGS & sh_flags != 0) {
   5221         try writer.writeAll("S");
   5222     }
   5223     if (elf.SHF_INFO_LINK & sh_flags != 0) {
   5224         try writer.writeAll("I");
   5225     }
   5226     if (elf.SHF_LINK_ORDER & sh_flags != 0) {
   5227         try writer.writeAll("L");
   5228     }
   5229     if (elf.SHF_EXCLUDE & sh_flags != 0) {
   5230         try writer.writeAll("E");
   5231     }
   5232     if (elf.SHF_COMPRESSED & sh_flags != 0) {
   5233         try writer.writeAll("C");
   5234     }
   5235     if (elf.SHF_GROUP & sh_flags != 0) {
   5236         try writer.writeAll("G");
   5237     }
   5238     if (elf.SHF_OS_NONCONFORMING & sh_flags != 0) {
   5239         try writer.writeAll("O");
   5240     }
   5241     if (elf.SHF_TLS & sh_flags != 0) {
   5242         try writer.writeAll("T");
   5243     }
   5244     if (elf.SHF_X86_64_LARGE & sh_flags != 0) {
   5245         try writer.writeAll("l");
   5246     }
   5247     if (elf.SHF_MIPS_ADDR & sh_flags != 0 or elf.SHF_ARM_PURECODE & sh_flags != 0) {
   5248         try writer.writeAll("p");
   5249     }
   5250 }
   5251 
   5252 const FormatPhdrCtx = struct {
   5253     elf_file: *Elf,
   5254     phdr: elf.Elf64_Phdr,
   5255 };
   5256 
   5257 fn fmtPhdr(self: *Elf, phdr: elf.Elf64_Phdr) std.fmt.Formatter(formatPhdr) {
   5258     return .{ .data = .{
   5259         .phdr = phdr,
   5260         .elf_file = self,
   5261     } };
   5262 }
   5263 
   5264 fn formatPhdr(
   5265     ctx: FormatPhdrCtx,
   5266     comptime unused_fmt_string: []const u8,
   5267     options: std.fmt.FormatOptions,
   5268     writer: anytype,
   5269 ) !void {
   5270     _ = options;
   5271     _ = unused_fmt_string;
   5272     const phdr = ctx.phdr;
   5273     const write = phdr.p_flags & elf.PF_W != 0;
   5274     const read = phdr.p_flags & elf.PF_R != 0;
   5275     const exec = phdr.p_flags & elf.PF_X != 0;
   5276     var flags: [3]u8 = [_]u8{'_'} ** 3;
   5277     if (exec) flags[0] = 'X';
   5278     if (write) flags[1] = 'W';
   5279     if (read) flags[2] = 'R';
   5280     const p_type = switch (phdr.p_type) {
   5281         elf.PT_LOAD => "LOAD",
   5282         elf.PT_TLS => "TLS",
   5283         elf.PT_GNU_EH_FRAME => "GNU_EH_FRAME",
   5284         elf.PT_GNU_STACK => "GNU_STACK",
   5285         elf.PT_DYNAMIC => "DYNAMIC",
   5286         elf.PT_INTERP => "INTERP",
   5287         elf.PT_NULL => "NULL",
   5288         elf.PT_PHDR => "PHDR",
   5289         elf.PT_NOTE => "NOTE",
   5290         else => "UNKNOWN",
   5291     };
   5292     try writer.print("{s} : {s} : @{x} ({x}) : align({x}) : filesz({x}) : memsz({x})", .{
   5293         p_type,       flags,         phdr.p_offset, phdr.p_vaddr,
   5294         phdr.p_align, phdr.p_filesz, phdr.p_memsz,
   5295     });
   5296 }
   5297 
   5298 pub fn dumpState(self: *Elf) std.fmt.Formatter(fmtDumpState) {
   5299     return .{ .data = self };
   5300 }
   5301 
   5302 fn fmtDumpState(
   5303     self: *Elf,
   5304     comptime unused_fmt_string: []const u8,
   5305     options: std.fmt.FormatOptions,
   5306     writer: anytype,
   5307 ) !void {
   5308     _ = unused_fmt_string;
   5309     _ = options;
   5310 
   5311     if (self.zigObjectPtr()) |zig_object| {
   5312         try writer.print("zig_object({d}) : {s}\n", .{ zig_object.index, zig_object.path });
   5313         try writer.print("{}{}", .{
   5314             zig_object.fmtAtoms(self),
   5315             zig_object.fmtSymtab(self),
   5316         });
   5317         try writer.writeByte('\n');
   5318     }
   5319 
   5320     for (self.objects.items) |index| {
   5321         const object = self.file(index).?.object;
   5322         try writer.print("object({d}) : {}", .{ index, object.fmtPath() });
   5323         if (!object.alive) try writer.writeAll(" : [*]");
   5324         try writer.writeByte('\n');
   5325         try writer.print("{}{}{}{}{}\n", .{
   5326             object.fmtAtoms(self),
   5327             object.fmtCies(self),
   5328             object.fmtFdes(self),
   5329             object.fmtSymtab(self),
   5330             object.fmtComdatGroups(self),
   5331         });
   5332     }
   5333 
   5334     for (self.shared_objects.items) |index| {
   5335         const shared_object = self.file(index).?.shared_object;
   5336         try writer.print("shared_object({d}) : ", .{index});
   5337         try writer.print("{s}", .{shared_object.path});
   5338         try writer.print(" : needed({})", .{shared_object.needed});
   5339         if (!shared_object.alive) try writer.writeAll(" : [*]");
   5340         try writer.writeByte('\n');
   5341         try writer.print("{}\n", .{shared_object.fmtSymtab(self)});
   5342     }
   5343 
   5344     if (self.linker_defined_index) |index| {
   5345         const linker_defined = self.file(index).?.linker_defined;
   5346         try writer.print("linker_defined({d}) : (linker defined)\n", .{index});
   5347         try writer.print("{}\n", .{linker_defined.fmtSymtab(self)});
   5348     }
   5349 
   5350     const slice = self.sections.slice();
   5351     {
   5352         try writer.writeAll("atom lists\n");
   5353         for (slice.items(.shdr), slice.items(.atom_list_2), 0..) |shdr, atom_list, shndx| {
   5354             try writer.print("shdr({d}) : {s} : {}\n", .{ shndx, self.getShString(shdr.sh_name), atom_list.fmt(self) });
   5355         }
   5356     }
   5357 
   5358     if (self.requiresThunks()) {
   5359         try writer.writeAll("thunks\n");
   5360         for (self.thunks.items, 0..) |th, index| {
   5361             try writer.print("thunk({d}) : {}\n", .{ index, th.fmt(self) });
   5362         }
   5363     }
   5364 
   5365     try writer.print("{}\n", .{self.got.fmt(self)});
   5366     try writer.print("{}\n", .{self.plt.fmt(self)});
   5367 
   5368     try writer.writeAll("Output COMDAT groups\n");
   5369     for (self.comdat_group_sections.items) |cg| {
   5370         try writer.print("  shdr({d}) : COMDAT({})\n", .{ cg.shndx, cg.cg_ref });
   5371     }
   5372 
   5373     try writer.writeAll("\nOutput merge sections\n");
   5374     for (self.merge_sections.items) |msec| {
   5375         try writer.print("  shdr({d}) : {}\n", .{ msec.output_section_index, msec.fmt(self) });
   5376     }
   5377 
   5378     try writer.writeAll("\nOutput shdrs\n");
   5379     for (slice.items(.shdr), slice.items(.phndx), 0..) |shdr, phndx, shndx| {
   5380         try writer.print("  shdr({d}) : phdr({?d}) : {}\n", .{
   5381             shndx,
   5382             phndx,
   5383             self.fmtShdr(shdr),
   5384         });
   5385     }
   5386     try writer.writeAll("\nOutput phdrs\n");
   5387     for (self.phdrs.items, 0..) |phdr, phndx| {
   5388         try writer.print("  phdr({d}) : {}\n", .{ phndx, self.fmtPhdr(phdr) });
   5389     }
   5390 }
   5391 
   5392 /// Caller owns the memory.
   5393 pub fn preadAllAlloc(allocator: Allocator, handle: fs.File, offset: u64, size: u64) ![]u8 {
   5394     const buffer = try allocator.alloc(u8, math.cast(usize, size) orelse return error.Overflow);
   5395     errdefer allocator.free(buffer);
   5396     const amt = try handle.preadAll(buffer, offset);
   5397     if (amt != size) return error.InputOutput;
   5398     return buffer;
   5399 }
   5400 
   5401 /// Binary search
   5402 pub fn bsearch(comptime T: type, haystack: []align(1) const T, predicate: anytype) usize {
   5403     if (!@hasDecl(@TypeOf(predicate), "predicate"))
   5404         @compileError("Predicate is required to define fn predicate(@This(), T) bool");
   5405 
   5406     var min: usize = 0;
   5407     var max: usize = haystack.len;
   5408     while (min < max) {
   5409         const index = (min + max) / 2;
   5410         const curr = haystack[index];
   5411         if (predicate.predicate(curr)) {
   5412             min = index + 1;
   5413         } else {
   5414             max = index;
   5415         }
   5416     }
   5417     return min;
   5418 }
   5419 
   5420 /// Linear search
   5421 pub fn lsearch(comptime T: type, haystack: []align(1) const T, predicate: anytype) usize {
   5422     if (!@hasDecl(@TypeOf(predicate), "predicate"))
   5423         @compileError("Predicate is required to define fn predicate(@This(), T) bool");
   5424 
   5425     var i: usize = 0;
   5426     while (i < haystack.len) : (i += 1) {
   5427         if (predicate.predicate(haystack[i])) break;
   5428     }
   5429     return i;
   5430 }
   5431 
   5432 pub fn getTarget(self: Elf) std.Target {
   5433     return self.base.comp.root_mod.resolved_target.result;
   5434 }
   5435 
   5436 fn requiresThunks(self: Elf) bool {
   5437     return switch (self.getTarget().cpu.arch) {
   5438         .aarch64 => true,
   5439         .x86_64, .riscv64 => false,
   5440         else => @panic("TODO unimplemented architecture"),
   5441     };
   5442 }
   5443 
   5444 /// The following three values are only observed at compile-time and used to emit a compile error
   5445 /// to remind the programmer to update expected maximum numbers of different program header types
   5446 /// so that we reserve enough space for the program header table up-front.
   5447 /// Bump these numbers when adding or deleting a Zig specific pre-allocated segment, or adding
   5448 /// more special-purpose program headers.
   5449 const max_number_of_object_segments = 9;
   5450 const max_number_of_special_phdrs = 5;
   5451 
   5452 const default_entry_addr = 0x8000000;
   5453 
   5454 pub const base_tag: link.File.Tag = .elf;
   5455 
   5456 pub const ComdatGroup = struct {
   5457     signature_off: u32,
   5458     file_index: File.Index,
   5459     shndx: u32,
   5460     members_start: u32,
   5461     members_len: u32,
   5462     alive: bool = true,
   5463 
   5464     pub fn file(cg: ComdatGroup, elf_file: *Elf) File {
   5465         return elf_file.file(cg.file_index).?;
   5466     }
   5467 
   5468     pub fn signature(cg: ComdatGroup, elf_file: *Elf) [:0]const u8 {
   5469         return cg.file(elf_file).object.getString(cg.signature_off);
   5470     }
   5471 
   5472     pub fn comdatGroupMembers(cg: ComdatGroup, elf_file: *Elf) []const u32 {
   5473         const object = cg.file(elf_file).object;
   5474         return object.comdat_group_data.items[cg.members_start..][0..cg.members_len];
   5475     }
   5476 
   5477     pub const Index = u32;
   5478 };
   5479 
   5480 pub const SymtabCtx = struct {
   5481     ilocal: u32 = 0,
   5482     iglobal: u32 = 0,
   5483     nlocals: u32 = 0,
   5484     nglobals: u32 = 0,
   5485     strsize: u32 = 0,
   5486 
   5487     pub fn reset(ctx: *SymtabCtx) void {
   5488         ctx.ilocal = 0;
   5489         ctx.iglobal = 0;
   5490         ctx.nlocals = 0;
   5491         ctx.nglobals = 0;
   5492         ctx.strsize = 0;
   5493     }
   5494 };
   5495 
   5496 pub const null_sym = elf.Elf64_Sym{
   5497     .st_name = 0,
   5498     .st_info = 0,
   5499     .st_other = 0,
   5500     .st_shndx = 0,
   5501     .st_value = 0,
   5502     .st_size = 0,
   5503 };
   5504 
   5505 pub const null_shdr = elf.Elf64_Shdr{
   5506     .sh_name = 0,
   5507     .sh_type = 0,
   5508     .sh_flags = 0,
   5509     .sh_addr = 0,
   5510     .sh_offset = 0,
   5511     .sh_size = 0,
   5512     .sh_link = 0,
   5513     .sh_info = 0,
   5514     .sh_addralign = 0,
   5515     .sh_entsize = 0,
   5516 };
   5517 
   5518 pub const SystemLib = struct {
   5519     needed: bool = false,
   5520     path: []const u8,
   5521 };
   5522 
   5523 pub const Ref = struct {
   5524     index: u32 = 0,
   5525     file: u32 = 0,
   5526 
   5527     pub fn eql(ref: Ref, other: Ref) bool {
   5528         return ref.index == other.index and ref.file == other.file;
   5529     }
   5530 
   5531     pub fn format(
   5532         ref: Ref,
   5533         comptime unused_fmt_string: []const u8,
   5534         options: std.fmt.FormatOptions,
   5535         writer: anytype,
   5536     ) !void {
   5537         _ = unused_fmt_string;
   5538         _ = options;
   5539         try writer.print("ref({},{})", .{ ref.index, ref.file });
   5540     }
   5541 };
   5542 
   5543 pub const SymbolResolver = struct {
   5544     keys: std.ArrayListUnmanaged(Key) = .empty,
   5545     values: std.ArrayListUnmanaged(Ref) = .empty,
   5546     table: std.AutoArrayHashMapUnmanaged(void, void) = .empty,
   5547 
   5548     const Result = struct {
   5549         found_existing: bool,
   5550         index: Index,
   5551         ref: *Ref,
   5552     };
   5553 
   5554     pub fn deinit(resolver: *SymbolResolver, allocator: Allocator) void {
   5555         resolver.keys.deinit(allocator);
   5556         resolver.values.deinit(allocator);
   5557         resolver.table.deinit(allocator);
   5558     }
   5559 
   5560     pub fn getOrPut(
   5561         resolver: *SymbolResolver,
   5562         allocator: Allocator,
   5563         ref: Ref,
   5564         elf_file: *Elf,
   5565     ) !Result {
   5566         const adapter = Adapter{ .keys = resolver.keys.items, .elf_file = elf_file };
   5567         const key = Key{ .index = ref.index, .file_index = ref.file };
   5568         const gop = try resolver.table.getOrPutAdapted(allocator, key, adapter);
   5569         if (!gop.found_existing) {
   5570             try resolver.keys.append(allocator, key);
   5571             _ = try resolver.values.addOne(allocator);
   5572         }
   5573         return .{
   5574             .found_existing = gop.found_existing,
   5575             .index = @intCast(gop.index + 1),
   5576             .ref = &resolver.values.items[gop.index],
   5577         };
   5578     }
   5579 
   5580     pub fn get(resolver: SymbolResolver, index: Index) ?Ref {
   5581         if (index == 0) return null;
   5582         return resolver.values.items[index - 1];
   5583     }
   5584 
   5585     pub fn reset(resolver: *SymbolResolver) void {
   5586         resolver.keys.clearRetainingCapacity();
   5587         resolver.values.clearRetainingCapacity();
   5588         resolver.table.clearRetainingCapacity();
   5589     }
   5590 
   5591     const Key = struct {
   5592         index: Symbol.Index,
   5593         file_index: File.Index,
   5594 
   5595         fn name(key: Key, elf_file: *Elf) [:0]const u8 {
   5596             const ref = Ref{ .index = key.index, .file = key.file_index };
   5597             return elf_file.symbol(ref).?.name(elf_file);
   5598         }
   5599 
   5600         fn file(key: Key, elf_file: *Elf) ?File {
   5601             return elf_file.file(key.file_index);
   5602         }
   5603 
   5604         fn eql(key: Key, other: Key, elf_file: *Elf) bool {
   5605             const key_name = key.name(elf_file);
   5606             const other_name = other.name(elf_file);
   5607             return mem.eql(u8, key_name, other_name);
   5608         }
   5609 
   5610         fn hash(key: Key, elf_file: *Elf) u32 {
   5611             return @truncate(Hash.hash(0, key.name(elf_file)));
   5612         }
   5613     };
   5614 
   5615     const Adapter = struct {
   5616         keys: []const Key,
   5617         elf_file: *Elf,
   5618 
   5619         pub fn eql(ctx: @This(), key: Key, b_void: void, b_map_index: usize) bool {
   5620             _ = b_void;
   5621             const other = ctx.keys[b_map_index];
   5622             return key.eql(other, ctx.elf_file);
   5623         }
   5624 
   5625         pub fn hash(ctx: @This(), key: Key) u32 {
   5626             return key.hash(ctx.elf_file);
   5627         }
   5628     };
   5629 
   5630     pub const Index = u32;
   5631 };
   5632 
   5633 const Section = struct {
   5634     /// Section header.
   5635     shdr: elf.Elf64_Shdr,
   5636 
   5637     /// Assigned program header index if any.
   5638     phndx: ?u32 = null,
   5639 
   5640     /// List of atoms contributing to this section.
   5641     /// TODO currently this is only used for relocations tracking in relocatable mode
   5642     /// but will be merged with atom_list_2.
   5643     atom_list: std.ArrayListUnmanaged(Ref) = .empty,
   5644 
   5645     /// List of atoms contributing to this section.
   5646     /// This can be used by sections that require special handling such as init/fini array, etc.
   5647     atom_list_2: AtomList = .{},
   5648 
   5649     /// Index of the last allocated atom in this section.
   5650     last_atom: Ref = .{ .index = 0, .file = 0 },
   5651 
   5652     /// A list of atoms that have surplus capacity. This list can have false
   5653     /// positives, as functions grow and shrink over time, only sometimes being added
   5654     /// or removed from the freelist.
   5655     ///
   5656     /// An atom has surplus capacity when its overcapacity value is greater than
   5657     /// padToIdeal(minimum_atom_size). That is, when it has so
   5658     /// much extra capacity, that we could fit a small new symbol in it, itself with
   5659     /// ideal_capacity or more.
   5660     ///
   5661     /// Ideal capacity is defined by size + (size / ideal_factor)
   5662     ///
   5663     /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
   5664     /// overcapacity can be negative. A simple way to have negative overcapacity is to
   5665     /// allocate a fresh text block, which will have ideal capacity, and then grow it
   5666     /// by 1 byte. It will then have -1 overcapacity.
   5667     free_list: std.ArrayListUnmanaged(Ref) = .empty,
   5668 };
   5669 
   5670 pub fn sectionSize(self: *Elf, shndx: u32) u64 {
   5671     const last_atom_ref = self.sections.items(.last_atom)[shndx];
   5672     const atom_ptr = self.atom(last_atom_ref) orelse return 0;
   5673     return @as(u64, @intCast(atom_ptr.value)) + atom_ptr.size;
   5674 }
   5675 
   5676 fn defaultEntrySymbolName(cpu_arch: std.Target.Cpu.Arch) []const u8 {
   5677     return switch (cpu_arch) {
   5678         .mips, .mipsel, .mips64, .mips64el => "__start",
   5679         else => "_start",
   5680     };
   5681 }
   5682 
   5683 fn createThunks(elf_file: *Elf, atom_list: *AtomList) !void {
   5684     const gpa = elf_file.base.comp.gpa;
   5685     const cpu_arch = elf_file.getTarget().cpu.arch;
   5686 
   5687     // A branch will need an extender if its target is larger than
   5688     // `2^(jump_bits - 1) - margin` where margin is some arbitrary number.
   5689     const max_distance = switch (cpu_arch) {
   5690         .aarch64 => 0x500_000,
   5691         .x86_64, .riscv64 => unreachable,
   5692         else => @panic("unhandled arch"),
   5693     };
   5694 
   5695     const advance = struct {
   5696         fn advance(list: *AtomList, size: u64, alignment: Atom.Alignment) !i64 {
   5697             const offset = alignment.forward(list.size);
   5698             const padding = offset - list.size;
   5699             list.size += padding + size;
   5700             list.alignment = list.alignment.max(alignment);
   5701             return @intCast(offset);
   5702         }
   5703     }.advance;
   5704 
   5705     for (atom_list.atoms.keys()) |ref| {
   5706         elf_file.atom(ref).?.value = -1;
   5707     }
   5708 
   5709     var i: usize = 0;
   5710     while (i < atom_list.atoms.keys().len) {
   5711         const start = i;
   5712         const start_atom = elf_file.atom(atom_list.atoms.keys()[start]).?;
   5713         assert(start_atom.alive);
   5714         start_atom.value = try advance(atom_list, start_atom.size, start_atom.alignment);
   5715         i += 1;
   5716 
   5717         while (i < atom_list.atoms.keys().len) : (i += 1) {
   5718             const atom_ptr = elf_file.atom(atom_list.atoms.keys()[i]).?;
   5719             assert(atom_ptr.alive);
   5720             if (@as(i64, @intCast(atom_ptr.alignment.forward(atom_list.size))) - start_atom.value >= max_distance)
   5721                 break;
   5722             atom_ptr.value = try advance(atom_list, atom_ptr.size, atom_ptr.alignment);
   5723         }
   5724 
   5725         // Insert a thunk at the group end
   5726         const thunk_index = try elf_file.addThunk();
   5727         const thunk_ptr = elf_file.thunk(thunk_index);
   5728         thunk_ptr.output_section_index = atom_list.output_section_index;
   5729 
   5730         // Scan relocs in the group and create trampolines for any unreachable callsite
   5731         for (atom_list.atoms.keys()[start..i]) |ref| {
   5732             const atom_ptr = elf_file.atom(ref).?;
   5733             const file_ptr = atom_ptr.file(elf_file).?;
   5734             log.debug("atom({}) {s}", .{ ref, atom_ptr.name(elf_file) });
   5735             for (atom_ptr.relocs(elf_file)) |rel| {
   5736                 const is_reachable = switch (cpu_arch) {
   5737                     .aarch64 => r: {
   5738                         const r_type: elf.R_AARCH64 = @enumFromInt(rel.r_type());
   5739                         if (r_type != .CALL26 and r_type != .JUMP26) break :r true;
   5740                         const target_ref = file_ptr.resolveSymbol(rel.r_sym(), elf_file);
   5741                         const target = elf_file.symbol(target_ref).?;
   5742                         if (target.flags.has_plt) break :r false;
   5743                         if (atom_ptr.output_section_index != target.output_section_index) break :r false;
   5744                         const target_atom = target.atom(elf_file).?;
   5745                         if (target_atom.value == -1) break :r false;
   5746                         const saddr = atom_ptr.address(elf_file) + @as(i64, @intCast(rel.r_offset));
   5747                         const taddr = target.address(.{}, elf_file);
   5748                         _ = math.cast(i28, taddr + rel.r_addend - saddr) orelse break :r false;
   5749                         break :r true;
   5750                     },
   5751                     .x86_64, .riscv64 => unreachable,
   5752                     else => @panic("unsupported arch"),
   5753                 };
   5754                 if (is_reachable) continue;
   5755                 const target = file_ptr.resolveSymbol(rel.r_sym(), elf_file);
   5756                 try thunk_ptr.symbols.put(gpa, target, {});
   5757             }
   5758             atom_ptr.addExtra(.{ .thunk = thunk_index }, elf_file);
   5759         }
   5760 
   5761         thunk_ptr.value = try advance(atom_list, thunk_ptr.size(elf_file), Atom.Alignment.fromNonzeroByteUnits(2));
   5762 
   5763         log.debug("thunk({d}) : {}", .{ thunk_index, thunk_ptr.fmt(elf_file) });
   5764     }
   5765 }
   5766 
   5767 const std = @import("std");
   5768 const build_options = @import("build_options");
   5769 const builtin = @import("builtin");
   5770 const assert = std.debug.assert;
   5771 const elf = std.elf;
   5772 const fs = std.fs;
   5773 const log = std.log.scoped(.link);
   5774 const relocs_log = std.log.scoped(.link_relocs);
   5775 const state_log = std.log.scoped(.link_state);
   5776 const math = std.math;
   5777 const mem = std.mem;
   5778 
   5779 const codegen = @import("../codegen.zig");
   5780 const dev = @import("../dev.zig");
   5781 const eh_frame = @import("Elf/eh_frame.zig");
   5782 const gc = @import("Elf/gc.zig");
   5783 const glibc = @import("../glibc.zig");
   5784 const link = @import("../link.zig");
   5785 const merge_section = @import("Elf/merge_section.zig");
   5786 const musl = @import("../musl.zig");
   5787 const relocatable = @import("Elf/relocatable.zig");
   5788 const relocation = @import("Elf/relocation.zig");
   5789 const target_util = @import("../target.zig");
   5790 const trace = @import("../tracy.zig").trace;
   5791 const synthetic_sections = @import("Elf/synthetic_sections.zig");
   5792 
   5793 const Air = @import("../Air.zig");
   5794 const Allocator = std.mem.Allocator;
   5795 const Archive = @import("Elf/Archive.zig");
   5796 pub const Atom = @import("Elf/Atom.zig");
   5797 const AtomList = @import("Elf/AtomList.zig");
   5798 const Cache = std.Build.Cache;
   5799 const Path = Cache.Path;
   5800 const Compilation = @import("../Compilation.zig");
   5801 const ComdatGroupSection = synthetic_sections.ComdatGroupSection;
   5802 const CopyRelSection = synthetic_sections.CopyRelSection;
   5803 const DynamicSection = synthetic_sections.DynamicSection;
   5804 const DynsymSection = synthetic_sections.DynsymSection;
   5805 const Dwarf = @import("Dwarf.zig");
   5806 const Elf = @This();
   5807 const File = @import("Elf/file.zig").File;
   5808 const GnuHashSection = synthetic_sections.GnuHashSection;
   5809 const GotSection = synthetic_sections.GotSection;
   5810 const GotPltSection = synthetic_sections.GotPltSection;
   5811 const Hash = std.hash.Wyhash;
   5812 const HashSection = synthetic_sections.HashSection;
   5813 const InputMergeSection = merge_section.InputMergeSection;
   5814 const LdScript = @import("Elf/LdScript.zig");
   5815 const LinkerDefined = @import("Elf/LinkerDefined.zig");
   5816 const Liveness = @import("../Liveness.zig");
   5817 const LlvmObject = @import("../codegen/llvm.zig").Object;
   5818 const MergeSection = merge_section.MergeSection;
   5819 const MergeSubsection = merge_section.MergeSubsection;
   5820 const Zcu = @import("../Zcu.zig");
   5821 const Object = @import("Elf/Object.zig");
   5822 const InternPool = @import("../InternPool.zig");
   5823 const PltSection = synthetic_sections.PltSection;
   5824 const PltGotSection = synthetic_sections.PltGotSection;
   5825 const SharedObject = @import("Elf/SharedObject.zig");
   5826 const Symbol = @import("Elf/Symbol.zig");
   5827 const StringTable = @import("StringTable.zig");
   5828 const Thunk = @import("Elf/Thunk.zig");
   5829 const Value = @import("../Value.zig");
   5830 const VerneedSection = synthetic_sections.VerneedSection;
   5831 const ZigObject = @import("Elf/ZigObject.zig");
   5832 const riscv = @import("riscv.zig");