zig

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

blob fd615bc1 (148932B) - Raw


      1 const Elf = @This();
      2 
      3 const std = @import("std");
      4 const builtin = @import("builtin");
      5 const mem = std.mem;
      6 const assert = std.debug.assert;
      7 const Allocator = std.mem.Allocator;
      8 const fs = std.fs;
      9 const elf = std.elf;
     10 const log = std.log.scoped(.link);
     11 const DW = std.dwarf;
     12 const leb128 = std.leb;
     13 
     14 const Module = @import("../Module.zig");
     15 const Compilation = @import("../Compilation.zig");
     16 const codegen = @import("../codegen.zig");
     17 const trace = @import("../tracy.zig").trace;
     18 const Package = @import("../Package.zig");
     19 const Value = @import("../value.zig").Value;
     20 const Type = @import("../type.zig").Type;
     21 const link = @import("../link.zig");
     22 const File = link.File;
     23 const build_options = @import("build_options");
     24 const target_util = @import("../target.zig");
     25 const glibc = @import("../glibc.zig");
     26 const musl = @import("../musl.zig");
     27 const Cache = @import("../Cache.zig");
     28 const Air = @import("../Air.zig");
     29 const Liveness = @import("../Liveness.zig");
     30 const LlvmObject = @import("../codegen/llvm.zig").Object;
     31 
     32 const default_entry_addr = 0x8000000;
     33 
     34 pub const base_tag: File.Tag = .elf;
     35 
     36 base: File,
     37 
     38 ptr_width: PtrWidth,
     39 
     40 /// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
     41 llvm_object: ?*LlvmObject = null,
     42 
     43 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
     44 /// Same order as in the file.
     45 sections: std.ArrayListUnmanaged(elf.Elf64_Shdr) = std.ArrayListUnmanaged(elf.Elf64_Shdr){},
     46 shdr_table_offset: ?u64 = null,
     47 
     48 /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
     49 /// Same order as in the file.
     50 program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = std.ArrayListUnmanaged(elf.Elf64_Phdr){},
     51 phdr_table_offset: ?u64 = null,
     52 /// The index into the program headers of a PT_LOAD program header with Read and Execute flags
     53 phdr_load_re_index: ?u16 = null,
     54 /// The index into the program headers of the global offset table.
     55 /// It needs PT_LOAD and Read flags.
     56 phdr_got_index: ?u16 = null,
     57 entry_addr: ?u64 = null,
     58 
     59 debug_strtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
     60 shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
     61 shstrtab_index: ?u16 = null,
     62 
     63 text_section_index: ?u16 = null,
     64 symtab_section_index: ?u16 = null,
     65 got_section_index: ?u16 = null,
     66 debug_info_section_index: ?u16 = null,
     67 debug_abbrev_section_index: ?u16 = null,
     68 debug_str_section_index: ?u16 = null,
     69 debug_aranges_section_index: ?u16 = null,
     70 debug_line_section_index: ?u16 = null,
     71 
     72 debug_abbrev_table_offset: ?u64 = null,
     73 
     74 /// The same order as in the file. ELF requires global symbols to all be after the
     75 /// local symbols, they cannot be mixed. So we must buffer all the global symbols and
     76 /// write them at the end. These are only the local symbols. The length of this array
     77 /// is the value used for sh_info in the .symtab section.
     78 local_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
     79 global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = .{},
     80 
     81 local_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
     82 global_symbol_free_list: std.ArrayListUnmanaged(u32) = .{},
     83 offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
     84 
     85 /// Same order as in the file. The value is the absolute vaddr value.
     86 /// If the vaddr of the executable program header changes, the entire
     87 /// offset table needs to be rewritten.
     88 offset_table: std.ArrayListUnmanaged(u64) = .{},
     89 
     90 phdr_table_dirty: bool = false,
     91 shdr_table_dirty: bool = false,
     92 shstrtab_dirty: bool = false,
     93 debug_strtab_dirty: bool = false,
     94 offset_table_count_dirty: bool = false,
     95 debug_abbrev_section_dirty: bool = false,
     96 debug_aranges_section_dirty: bool = false,
     97 
     98 debug_info_header_dirty: bool = false,
     99 debug_line_header_dirty: bool = false,
    100 
    101 error_flags: File.ErrorFlags = File.ErrorFlags{},
    102 
    103 /// A list of text blocks that have surplus capacity. This list can have false
    104 /// positives, as functions grow and shrink over time, only sometimes being added
    105 /// or removed from the freelist.
    106 ///
    107 /// A text block has surplus capacity when its overcapacity value is greater than
    108 /// padToIdeal(minimum_text_block_size). That is, when it has so
    109 /// much extra capacity, that we could fit a small new symbol in it, itself with
    110 /// ideal_capacity or more.
    111 ///
    112 /// Ideal capacity is defined by size + (size / ideal_factor)
    113 ///
    114 /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
    115 /// overcapacity can be negative. A simple way to have negative overcapacity is to
    116 /// allocate a fresh text block, which will have ideal capacity, and then grow it
    117 /// by 1 byte. It will then have -1 overcapacity.
    118 text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},
    119 last_text_block: ?*TextBlock = null,
    120 
    121 /// A list of `SrcFn` whose Line Number Programs have surplus capacity.
    122 /// This is the same concept as `text_block_free_list`; see those doc comments.
    123 dbg_line_fn_free_list: std.AutoHashMapUnmanaged(*SrcFn, void) = .{},
    124 dbg_line_fn_first: ?*SrcFn = null,
    125 dbg_line_fn_last: ?*SrcFn = null,
    126 
    127 /// A list of `TextBlock` whose corresponding .debug_info tags have surplus capacity.
    128 /// This is the same concept as `text_block_free_list`; see those doc comments.
    129 dbg_info_decl_free_list: std.AutoHashMapUnmanaged(*TextBlock, void) = .{},
    130 dbg_info_decl_first: ?*TextBlock = null,
    131 dbg_info_decl_last: ?*TextBlock = null,
    132 
    133 /// When allocating, the ideal_capacity is calculated by
    134 /// actual_capacity + (actual_capacity / ideal_factor)
    135 const ideal_factor = 3;
    136 
    137 /// In order for a slice of bytes to be considered eligible to keep metadata pointing at
    138 /// it as a possible place to put new symbols, it must have enough room for this many bytes
    139 /// (plus extra for reserved capacity).
    140 const minimum_text_block_size = 64;
    141 const min_text_capacity = padToIdeal(minimum_text_block_size);
    142 
    143 pub const PtrWidth = enum { p32, p64 };
    144 
    145 pub const TextBlock = struct {
    146     /// Each decl always gets a local symbol with the fully qualified name.
    147     /// The vaddr and size are found here directly.
    148     /// The file offset is found by computing the vaddr offset from the section vaddr
    149     /// the symbol references, and adding that to the file offset of the section.
    150     /// If this field is 0, it means the codegen size = 0 and there is no symbol or
    151     /// offset table entry.
    152     local_sym_index: u32,
    153     /// This field is undefined for symbols with size = 0.
    154     offset_table_index: u32,
    155     /// Points to the previous and next neighbors, based on the `text_offset`.
    156     /// This can be used to find, for example, the capacity of this `TextBlock`.
    157     prev: ?*TextBlock,
    158     next: ?*TextBlock,
    159 
    160     /// Previous/next linked list pointers.
    161     /// This is the linked list node for this Decl's corresponding .debug_info tag.
    162     dbg_info_prev: ?*TextBlock,
    163     dbg_info_next: ?*TextBlock,
    164     /// Offset into .debug_info pointing to the tag for this Decl.
    165     dbg_info_off: u32,
    166     /// Size of the .debug_info tag for this Decl, not including padding.
    167     dbg_info_len: u32,
    168 
    169     pub const empty = TextBlock{
    170         .local_sym_index = 0,
    171         .offset_table_index = undefined,
    172         .prev = null,
    173         .next = null,
    174         .dbg_info_prev = null,
    175         .dbg_info_next = null,
    176         .dbg_info_off = undefined,
    177         .dbg_info_len = undefined,
    178     };
    179 
    180     /// Returns how much room there is to grow in virtual address space.
    181     /// File offset relocation happens transparently, so it is not included in
    182     /// this calculation.
    183     fn capacity(self: TextBlock, elf_file: Elf) u64 {
    184         const self_sym = elf_file.local_symbols.items[self.local_sym_index];
    185         if (self.next) |next| {
    186             const next_sym = elf_file.local_symbols.items[next.local_sym_index];
    187             return next_sym.st_value - self_sym.st_value;
    188         } else {
    189             // We are the last block. The capacity is limited only by virtual address space.
    190             return std.math.maxInt(u32) - self_sym.st_value;
    191         }
    192     }
    193 
    194     fn freeListEligible(self: TextBlock, elf_file: Elf) bool {
    195         // No need to keep a free list node for the last block.
    196         const next = self.next orelse return false;
    197         const self_sym = elf_file.local_symbols.items[self.local_sym_index];
    198         const next_sym = elf_file.local_symbols.items[next.local_sym_index];
    199         const cap = next_sym.st_value - self_sym.st_value;
    200         const ideal_cap = padToIdeal(self_sym.st_size);
    201         if (cap <= ideal_cap) return false;
    202         const surplus = cap - ideal_cap;
    203         return surplus >= min_text_capacity;
    204     }
    205 };
    206 
    207 pub const Export = struct {
    208     sym_index: ?u32 = null,
    209 };
    210 
    211 pub const SrcFn = struct {
    212     /// Offset from the beginning of the Debug Line Program header that contains this function.
    213     off: u32,
    214     /// Size of the line number program component belonging to this function, not
    215     /// including padding.
    216     len: u32,
    217 
    218     /// Points to the previous and next neighbors, based on the offset from .debug_line.
    219     /// This can be used to find, for example, the capacity of this `SrcFn`.
    220     prev: ?*SrcFn,
    221     next: ?*SrcFn,
    222 
    223     pub const empty: SrcFn = .{
    224         .off = 0,
    225         .len = 0,
    226         .prev = null,
    227         .next = null,
    228     };
    229 };
    230 
    231 pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Elf {
    232     assert(options.object_format == .elf);
    233 
    234     if (build_options.have_llvm and options.use_llvm) {
    235         const self = try createEmpty(allocator, options);
    236         errdefer self.base.destroy();
    237 
    238         self.llvm_object = try LlvmObject.create(allocator, sub_path, options);
    239         return self;
    240     }
    241 
    242     const file = try options.emit.?.directory.handle.createFile(sub_path, .{
    243         .truncate = false,
    244         .read = true,
    245         .mode = link.determineMode(options),
    246     });
    247     errdefer file.close();
    248 
    249     const self = try createEmpty(allocator, options);
    250     errdefer self.base.destroy();
    251 
    252     self.base.file = file;
    253     self.shdr_table_dirty = true;
    254 
    255     // Index 0 is always a null symbol.
    256     try self.local_symbols.append(allocator, .{
    257         .st_name = 0,
    258         .st_info = 0,
    259         .st_other = 0,
    260         .st_shndx = 0,
    261         .st_value = 0,
    262         .st_size = 0,
    263     });
    264 
    265     // There must always be a null section in index 0
    266     try self.sections.append(allocator, .{
    267         .sh_name = 0,
    268         .sh_type = elf.SHT_NULL,
    269         .sh_flags = 0,
    270         .sh_addr = 0,
    271         .sh_offset = 0,
    272         .sh_size = 0,
    273         .sh_link = 0,
    274         .sh_info = 0,
    275         .sh_addralign = 0,
    276         .sh_entsize = 0,
    277     });
    278 
    279     try self.populateMissingMetadata();
    280 
    281     return self;
    282 }
    283 
    284 pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Elf {
    285     const ptr_width: PtrWidth = switch (options.target.cpu.arch.ptrBitWidth()) {
    286         0...32 => .p32,
    287         33...64 => .p64,
    288         else => return error.UnsupportedELFArchitecture,
    289     };
    290     const self = try gpa.create(Elf);
    291     self.* = .{
    292         .base = .{
    293             .tag = .elf,
    294             .options = options,
    295             .allocator = gpa,
    296             .file = null,
    297         },
    298         .ptr_width = ptr_width,
    299     };
    300     return self;
    301 }
    302 
    303 pub fn deinit(self: *Elf) void {
    304     if (build_options.have_llvm) {
    305         if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator);
    306     }
    307 
    308     self.sections.deinit(self.base.allocator);
    309     self.program_headers.deinit(self.base.allocator);
    310     self.shstrtab.deinit(self.base.allocator);
    311     self.debug_strtab.deinit(self.base.allocator);
    312     self.local_symbols.deinit(self.base.allocator);
    313     self.global_symbols.deinit(self.base.allocator);
    314     self.global_symbol_free_list.deinit(self.base.allocator);
    315     self.local_symbol_free_list.deinit(self.base.allocator);
    316     self.offset_table_free_list.deinit(self.base.allocator);
    317     self.text_block_free_list.deinit(self.base.allocator);
    318     self.dbg_line_fn_free_list.deinit(self.base.allocator);
    319     self.dbg_info_decl_free_list.deinit(self.base.allocator);
    320     self.offset_table.deinit(self.base.allocator);
    321 }
    322 
    323 pub fn getDeclVAddr(self: *Elf, decl: *const Module.Decl) u64 {
    324     assert(self.llvm_object == null);
    325     assert(decl.link.elf.local_sym_index != 0);
    326     return self.local_symbols.items[decl.link.elf.local_sym_index].st_value;
    327 }
    328 
    329 fn getDebugLineProgramOff(self: Elf) u32 {
    330     return self.dbg_line_fn_first.?.off;
    331 }
    332 
    333 fn getDebugLineProgramEnd(self: Elf) u32 {
    334     return self.dbg_line_fn_last.?.off + self.dbg_line_fn_last.?.len;
    335 }
    336 
    337 /// Returns end pos of collision, if any.
    338 fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
    339     const small_ptr = self.ptr_width == .p32;
    340     const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr);
    341     if (start < ehdr_size)
    342         return ehdr_size;
    343 
    344     const end = start + padToIdeal(size);
    345 
    346     if (self.shdr_table_offset) |off| {
    347         const shdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Shdr) else @sizeOf(elf.Elf64_Shdr);
    348         const tight_size = self.sections.items.len * shdr_size;
    349         const increased_size = padToIdeal(tight_size);
    350         const test_end = off + increased_size;
    351         if (end > off and start < test_end) {
    352             return test_end;
    353         }
    354     }
    355 
    356     if (self.phdr_table_offset) |off| {
    357         const phdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Phdr) else @sizeOf(elf.Elf64_Phdr);
    358         const tight_size = self.sections.items.len * phdr_size;
    359         const increased_size = padToIdeal(tight_size);
    360         const test_end = off + increased_size;
    361         if (end > off and start < test_end) {
    362             return test_end;
    363         }
    364     }
    365 
    366     for (self.sections.items) |section| {
    367         const increased_size = padToIdeal(section.sh_size);
    368         const test_end = section.sh_offset + increased_size;
    369         if (end > section.sh_offset and start < test_end) {
    370             return test_end;
    371         }
    372     }
    373     for (self.program_headers.items) |program_header| {
    374         const increased_size = padToIdeal(program_header.p_filesz);
    375         const test_end = program_header.p_offset + increased_size;
    376         if (end > program_header.p_offset and start < test_end) {
    377             return test_end;
    378         }
    379     }
    380     return null;
    381 }
    382 
    383 fn allocatedSize(self: *Elf, start: u64) u64 {
    384     if (start == 0)
    385         return 0;
    386     var min_pos: u64 = std.math.maxInt(u64);
    387     if (self.shdr_table_offset) |off| {
    388         if (off > start and off < min_pos) min_pos = off;
    389     }
    390     if (self.phdr_table_offset) |off| {
    391         if (off > start and off < min_pos) min_pos = off;
    392     }
    393     for (self.sections.items) |section| {
    394         if (section.sh_offset <= start) continue;
    395         if (section.sh_offset < min_pos) min_pos = section.sh_offset;
    396     }
    397     for (self.program_headers.items) |program_header| {
    398         if (program_header.p_offset <= start) continue;
    399         if (program_header.p_offset < min_pos) min_pos = program_header.p_offset;
    400     }
    401     return min_pos - start;
    402 }
    403 
    404 fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u16) u64 {
    405     var start: u64 = 0;
    406     while (self.detectAllocCollision(start, object_size)) |item_end| {
    407         start = mem.alignForwardGeneric(u64, item_end, min_alignment);
    408     }
    409     return start;
    410 }
    411 
    412 /// TODO Improve this to use a table.
    413 fn makeString(self: *Elf, bytes: []const u8) !u32 {
    414     try self.shstrtab.ensureUnusedCapacity(self.base.allocator, bytes.len + 1);
    415     const result = self.shstrtab.items.len;
    416     self.shstrtab.appendSliceAssumeCapacity(bytes);
    417     self.shstrtab.appendAssumeCapacity(0);
    418     return @intCast(u32, result);
    419 }
    420 
    421 /// TODO Improve this to use a table.
    422 fn makeDebugString(self: *Elf, bytes: []const u8) !u32 {
    423     try self.debug_strtab.ensureUnusedCapacity(self.base.allocator, bytes.len + 1);
    424     const result = self.debug_strtab.items.len;
    425     self.debug_strtab.appendSliceAssumeCapacity(bytes);
    426     self.debug_strtab.appendAssumeCapacity(0);
    427     return @intCast(u32, result);
    428 }
    429 
    430 fn getString(self: *Elf, str_off: u32) []const u8 {
    431     assert(str_off < self.shstrtab.items.len);
    432     return mem.sliceTo(@ptrCast([*:0]const u8, self.shstrtab.items.ptr + str_off), 0);
    433 }
    434 
    435 fn updateString(self: *Elf, old_str_off: u32, new_name: []const u8) !u32 {
    436     const existing_name = self.getString(old_str_off);
    437     if (mem.eql(u8, existing_name, new_name)) {
    438         return old_str_off;
    439     }
    440     return self.makeString(new_name);
    441 }
    442 
    443 pub fn populateMissingMetadata(self: *Elf) !void {
    444     assert(self.llvm_object == null);
    445 
    446     const small_ptr = switch (self.ptr_width) {
    447         .p32 => true,
    448         .p64 => false,
    449     };
    450     const ptr_size: u8 = self.ptrWidthBytes();
    451     if (self.phdr_load_re_index == null) {
    452         self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len);
    453         const file_size = self.base.options.program_code_size_hint;
    454         const p_align = 0x1000;
    455         const off = self.findFreeSpace(file_size, p_align);
    456         log.debug("found PT_LOAD free space 0x{x} to 0x{x}", .{ off, off + file_size });
    457         const entry_addr: u64 = self.entry_addr orelse if (self.base.options.target.cpu.arch == .spu_2) @as(u64, 0) else default_entry_addr;
    458         try self.program_headers.append(self.base.allocator, .{
    459             .p_type = elf.PT_LOAD,
    460             .p_offset = off,
    461             .p_filesz = file_size,
    462             .p_vaddr = entry_addr,
    463             .p_paddr = entry_addr,
    464             .p_memsz = file_size,
    465             .p_align = p_align,
    466             .p_flags = elf.PF_X | elf.PF_R,
    467         });
    468         self.entry_addr = null;
    469         self.phdr_table_dirty = true;
    470     }
    471     if (self.phdr_got_index == null) {
    472         self.phdr_got_index = @intCast(u16, self.program_headers.items.len);
    473         const file_size = @as(u64, ptr_size) * self.base.options.symbol_count_hint;
    474         // We really only need ptr alignment but since we are using PROGBITS, linux requires
    475         // page align.
    476         const p_align = if (self.base.options.target.os.tag == .linux) 0x1000 else @as(u16, ptr_size);
    477         const off = self.findFreeSpace(file_size, p_align);
    478         log.debug("found PT_LOAD free space 0x{x} to 0x{x}", .{ off, off + file_size });
    479         // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at.
    480         // we'll need to re-use that function anyway, in case the GOT grows and overlaps something
    481         // else in virtual memory.
    482         const got_addr: u32 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0x4000000 else 0x8000;
    483         try self.program_headers.append(self.base.allocator, .{
    484             .p_type = elf.PT_LOAD,
    485             .p_offset = off,
    486             .p_filesz = file_size,
    487             .p_vaddr = got_addr,
    488             .p_paddr = got_addr,
    489             .p_memsz = file_size,
    490             .p_align = p_align,
    491             .p_flags = elf.PF_R,
    492         });
    493         self.phdr_table_dirty = true;
    494     }
    495     if (self.shstrtab_index == null) {
    496         self.shstrtab_index = @intCast(u16, self.sections.items.len);
    497         assert(self.shstrtab.items.len == 0);
    498         try self.shstrtab.append(self.base.allocator, 0); // need a 0 at position 0
    499         const off = self.findFreeSpace(self.shstrtab.items.len, 1);
    500         log.debug("found shstrtab free space 0x{x} to 0x{x}", .{ off, off + self.shstrtab.items.len });
    501         try self.sections.append(self.base.allocator, .{
    502             .sh_name = try self.makeString(".shstrtab"),
    503             .sh_type = elf.SHT_STRTAB,
    504             .sh_flags = 0,
    505             .sh_addr = 0,
    506             .sh_offset = off,
    507             .sh_size = self.shstrtab.items.len,
    508             .sh_link = 0,
    509             .sh_info = 0,
    510             .sh_addralign = 1,
    511             .sh_entsize = 0,
    512         });
    513         self.shstrtab_dirty = true;
    514         self.shdr_table_dirty = true;
    515     }
    516     if (self.text_section_index == null) {
    517         self.text_section_index = @intCast(u16, self.sections.items.len);
    518         const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
    519 
    520         try self.sections.append(self.base.allocator, .{
    521             .sh_name = try self.makeString(".text"),
    522             .sh_type = elf.SHT_PROGBITS,
    523             .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
    524             .sh_addr = phdr.p_vaddr,
    525             .sh_offset = phdr.p_offset,
    526             .sh_size = phdr.p_filesz,
    527             .sh_link = 0,
    528             .sh_info = 0,
    529             .sh_addralign = phdr.p_align,
    530             .sh_entsize = 0,
    531         });
    532         self.shdr_table_dirty = true;
    533     }
    534     if (self.got_section_index == null) {
    535         self.got_section_index = @intCast(u16, self.sections.items.len);
    536         const phdr = &self.program_headers.items[self.phdr_got_index.?];
    537 
    538         try self.sections.append(self.base.allocator, .{
    539             .sh_name = try self.makeString(".got"),
    540             .sh_type = elf.SHT_PROGBITS,
    541             .sh_flags = elf.SHF_ALLOC,
    542             .sh_addr = phdr.p_vaddr,
    543             .sh_offset = phdr.p_offset,
    544             .sh_size = phdr.p_filesz,
    545             .sh_link = 0,
    546             .sh_info = 0,
    547             .sh_addralign = phdr.p_align,
    548             .sh_entsize = 0,
    549         });
    550         self.shdr_table_dirty = true;
    551     }
    552     if (self.symtab_section_index == null) {
    553         self.symtab_section_index = @intCast(u16, self.sections.items.len);
    554         const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);
    555         const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);
    556         const file_size = self.base.options.symbol_count_hint * each_size;
    557         const off = self.findFreeSpace(file_size, min_align);
    558         log.debug("found symtab free space 0x{x} to 0x{x}", .{ off, off + file_size });
    559 
    560         try self.sections.append(self.base.allocator, .{
    561             .sh_name = try self.makeString(".symtab"),
    562             .sh_type = elf.SHT_SYMTAB,
    563             .sh_flags = 0,
    564             .sh_addr = 0,
    565             .sh_offset = off,
    566             .sh_size = file_size,
    567             // The section header index of the associated string table.
    568             .sh_link = self.shstrtab_index.?,
    569             .sh_info = @intCast(u32, self.local_symbols.items.len),
    570             .sh_addralign = min_align,
    571             .sh_entsize = each_size,
    572         });
    573         self.shdr_table_dirty = true;
    574         try self.writeSymbol(0);
    575     }
    576     if (self.debug_str_section_index == null) {
    577         self.debug_str_section_index = @intCast(u16, self.sections.items.len);
    578         assert(self.debug_strtab.items.len == 0);
    579         try self.sections.append(self.base.allocator, .{
    580             .sh_name = try self.makeString(".debug_str"),
    581             .sh_type = elf.SHT_PROGBITS,
    582             .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS,
    583             .sh_addr = 0,
    584             .sh_offset = 0,
    585             .sh_size = self.debug_strtab.items.len,
    586             .sh_link = 0,
    587             .sh_info = 0,
    588             .sh_addralign = 1,
    589             .sh_entsize = 1,
    590         });
    591         self.debug_strtab_dirty = true;
    592         self.shdr_table_dirty = true;
    593     }
    594     if (self.debug_info_section_index == null) {
    595         self.debug_info_section_index = @intCast(u16, self.sections.items.len);
    596 
    597         const file_size_hint = 200;
    598         const p_align = 1;
    599         const off = self.findFreeSpace(file_size_hint, p_align);
    600         log.debug("found .debug_info free space 0x{x} to 0x{x}", .{
    601             off,
    602             off + file_size_hint,
    603         });
    604         try self.sections.append(self.base.allocator, .{
    605             .sh_name = try self.makeString(".debug_info"),
    606             .sh_type = elf.SHT_PROGBITS,
    607             .sh_flags = 0,
    608             .sh_addr = 0,
    609             .sh_offset = off,
    610             .sh_size = file_size_hint,
    611             .sh_link = 0,
    612             .sh_info = 0,
    613             .sh_addralign = p_align,
    614             .sh_entsize = 0,
    615         });
    616         self.shdr_table_dirty = true;
    617         self.debug_info_header_dirty = true;
    618     }
    619     if (self.debug_abbrev_section_index == null) {
    620         self.debug_abbrev_section_index = @intCast(u16, self.sections.items.len);
    621 
    622         const file_size_hint = 128;
    623         const p_align = 1;
    624         const off = self.findFreeSpace(file_size_hint, p_align);
    625         log.debug("found .debug_abbrev free space 0x{x} to 0x{x}", .{
    626             off,
    627             off + file_size_hint,
    628         });
    629         try self.sections.append(self.base.allocator, .{
    630             .sh_name = try self.makeString(".debug_abbrev"),
    631             .sh_type = elf.SHT_PROGBITS,
    632             .sh_flags = 0,
    633             .sh_addr = 0,
    634             .sh_offset = off,
    635             .sh_size = file_size_hint,
    636             .sh_link = 0,
    637             .sh_info = 0,
    638             .sh_addralign = p_align,
    639             .sh_entsize = 0,
    640         });
    641         self.shdr_table_dirty = true;
    642         self.debug_abbrev_section_dirty = true;
    643     }
    644     if (self.debug_aranges_section_index == null) {
    645         self.debug_aranges_section_index = @intCast(u16, self.sections.items.len);
    646 
    647         const file_size_hint = 160;
    648         const p_align = 16;
    649         const off = self.findFreeSpace(file_size_hint, p_align);
    650         log.debug("found .debug_aranges free space 0x{x} to 0x{x}", .{
    651             off,
    652             off + file_size_hint,
    653         });
    654         try self.sections.append(self.base.allocator, .{
    655             .sh_name = try self.makeString(".debug_aranges"),
    656             .sh_type = elf.SHT_PROGBITS,
    657             .sh_flags = 0,
    658             .sh_addr = 0,
    659             .sh_offset = off,
    660             .sh_size = file_size_hint,
    661             .sh_link = 0,
    662             .sh_info = 0,
    663             .sh_addralign = p_align,
    664             .sh_entsize = 0,
    665         });
    666         self.shdr_table_dirty = true;
    667         self.debug_aranges_section_dirty = true;
    668     }
    669     if (self.debug_line_section_index == null) {
    670         self.debug_line_section_index = @intCast(u16, self.sections.items.len);
    671 
    672         const file_size_hint = 250;
    673         const p_align = 1;
    674         const off = self.findFreeSpace(file_size_hint, p_align);
    675         log.debug("found .debug_line free space 0x{x} to 0x{x}", .{
    676             off,
    677             off + file_size_hint,
    678         });
    679         try self.sections.append(self.base.allocator, .{
    680             .sh_name = try self.makeString(".debug_line"),
    681             .sh_type = elf.SHT_PROGBITS,
    682             .sh_flags = 0,
    683             .sh_addr = 0,
    684             .sh_offset = off,
    685             .sh_size = file_size_hint,
    686             .sh_link = 0,
    687             .sh_info = 0,
    688             .sh_addralign = p_align,
    689             .sh_entsize = 0,
    690         });
    691         self.shdr_table_dirty = true;
    692         self.debug_line_header_dirty = true;
    693     }
    694     const shsize: u64 = switch (self.ptr_width) {
    695         .p32 => @sizeOf(elf.Elf32_Shdr),
    696         .p64 => @sizeOf(elf.Elf64_Shdr),
    697     };
    698     const shalign: u16 = switch (self.ptr_width) {
    699         .p32 => @alignOf(elf.Elf32_Shdr),
    700         .p64 => @alignOf(elf.Elf64_Shdr),
    701     };
    702     if (self.shdr_table_offset == null) {
    703         self.shdr_table_offset = self.findFreeSpace(self.sections.items.len * shsize, shalign);
    704         self.shdr_table_dirty = true;
    705     }
    706     const phsize: u64 = switch (self.ptr_width) {
    707         .p32 => @sizeOf(elf.Elf32_Phdr),
    708         .p64 => @sizeOf(elf.Elf64_Phdr),
    709     };
    710     const phalign: u16 = switch (self.ptr_width) {
    711         .p32 => @alignOf(elf.Elf32_Phdr),
    712         .p64 => @alignOf(elf.Elf64_Phdr),
    713     };
    714     if (self.phdr_table_offset == null) {
    715         self.phdr_table_offset = self.findFreeSpace(self.program_headers.items.len * phsize, phalign);
    716         self.phdr_table_dirty = true;
    717     }
    718     {
    719         // Iterate over symbols, populating free_list and last_text_block.
    720         if (self.local_symbols.items.len != 1) {
    721             @panic("TODO implement setting up free_list and last_text_block from existing ELF file");
    722         }
    723         // We are starting with an empty file. The default values are correct, null and empty list.
    724     }
    725 }
    726 
    727 pub const abbrev_compile_unit = 1;
    728 pub const abbrev_subprogram = 2;
    729 pub const abbrev_subprogram_retvoid = 3;
    730 pub const abbrev_base_type = 4;
    731 pub const abbrev_pad1 = 5;
    732 pub const abbrev_parameter = 6;
    733 
    734 pub fn flush(self: *Elf, comp: *Compilation) !void {
    735     if (build_options.have_llvm and self.base.options.use_lld) {
    736         return self.linkWithLLD(comp);
    737     } else {
    738         switch (self.base.options.effectiveOutputMode()) {
    739             .Exe, .Obj => {},
    740             .Lib => return error.TODOImplementWritingLibFiles,
    741         }
    742         return self.flushModule(comp);
    743     }
    744 }
    745 
    746 pub fn flushModule(self: *Elf, comp: *Compilation) !void {
    747     const tracy = trace(@src());
    748     defer tracy.end();
    749 
    750     if (build_options.have_llvm)
    751         if (self.llvm_object) |llvm_object| return try llvm_object.flushModule(comp);
    752 
    753     // TODO This linker code currently assumes there is only 1 compilation unit and it
    754     // corresponds to the Zig source code.
    755     const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented;
    756 
    757     const target_endian = self.base.options.target.cpu.arch.endian();
    758     const foreign_endian = target_endian != builtin.cpu.arch.endian();
    759     const ptr_width_bytes: u8 = self.ptrWidthBytes();
    760     const init_len_size: usize = switch (self.ptr_width) {
    761         .p32 => 4,
    762         .p64 => 12,
    763     };
    764 
    765     // Unfortunately these have to be buffered and done at the end because ELF does not allow
    766     // mixing local and global symbols within a symbol table.
    767     try self.writeAllGlobalSymbols();
    768 
    769     if (self.debug_abbrev_section_dirty) {
    770         const debug_abbrev_sect = &self.sections.items[self.debug_abbrev_section_index.?];
    771 
    772         // These are LEB encoded but since the values are all less than 127
    773         // we can simply append these bytes.
    774         const abbrev_buf = [_]u8{
    775             abbrev_compile_unit, DW.TAG.compile_unit, DW.CHILDREN.yes, // header
    776             DW.AT.stmt_list,     DW.FORM.sec_offset,  DW.AT.low_pc,
    777             DW.FORM.addr,        DW.AT.high_pc,       DW.FORM.addr,
    778             DW.AT.name,          DW.FORM.strp,        DW.AT.comp_dir,
    779             DW.FORM.strp,        DW.AT.producer,      DW.FORM.strp,
    780             DW.AT.language,      DW.FORM.data2,       0,
    781             0, // table sentinel
    782             abbrev_subprogram,
    783             DW.TAG.subprogram,
    784             DW.CHILDREN.yes, // header
    785             DW.AT.low_pc,
    786             DW.FORM.addr,
    787             DW.AT.high_pc,
    788             DW.FORM.data4,
    789             DW.AT.type,
    790             DW.FORM.ref4,
    791             DW.AT.name,
    792             DW.FORM.string,
    793             0,                         0, // table sentinel
    794             abbrev_subprogram_retvoid,
    795             DW.TAG.subprogram, DW.CHILDREN.yes, // header
    796             DW.AT.low_pc,      DW.FORM.addr,
    797             DW.AT.high_pc,     DW.FORM.data4,
    798             DW.AT.name,        DW.FORM.string,
    799             0,
    800             0, // table sentinel
    801             abbrev_base_type,
    802             DW.TAG.base_type,
    803             DW.CHILDREN.no, // header
    804             DW.AT.encoding,
    805             DW.FORM.data1,
    806             DW.AT.byte_size,
    807             DW.FORM.data1,
    808             DW.AT.name,
    809             DW.FORM.string, 0, 0, // table sentinel
    810             abbrev_pad1, DW.TAG.unspecified_type, DW.CHILDREN.no, // header
    811             0,                0, // table sentinel
    812             abbrev_parameter,
    813             DW.TAG.formal_parameter, DW.CHILDREN.no, // header
    814             DW.AT.location,          DW.FORM.exprloc,
    815             DW.AT.type,              DW.FORM.ref4,
    816             DW.AT.name,              DW.FORM.string,
    817             0,
    818             0, // table sentinel
    819             0,
    820             0,
    821             0, // section sentinel
    822         };
    823 
    824         const needed_size = abbrev_buf.len;
    825         const allocated_size = self.allocatedSize(debug_abbrev_sect.sh_offset);
    826         if (needed_size > allocated_size) {
    827             debug_abbrev_sect.sh_size = 0; // free the space
    828             debug_abbrev_sect.sh_offset = self.findFreeSpace(needed_size, 1);
    829         }
    830         debug_abbrev_sect.sh_size = needed_size;
    831         log.debug(".debug_abbrev start=0x{x} end=0x{x}", .{
    832             debug_abbrev_sect.sh_offset,
    833             debug_abbrev_sect.sh_offset + needed_size,
    834         });
    835 
    836         const abbrev_offset = 0;
    837         self.debug_abbrev_table_offset = abbrev_offset;
    838         try self.base.file.?.pwriteAll(&abbrev_buf, debug_abbrev_sect.sh_offset + abbrev_offset);
    839         if (!self.shdr_table_dirty) {
    840             // Then it won't get written with the others and we need to do it.
    841             try self.writeSectHeader(self.debug_abbrev_section_index.?);
    842         }
    843 
    844         self.debug_abbrev_section_dirty = false;
    845     }
    846 
    847     if (self.debug_info_header_dirty) debug_info: {
    848         // If this value is null it means there is an error in the module;
    849         // leave debug_info_header_dirty=true.
    850         const first_dbg_info_decl = self.dbg_info_decl_first orelse break :debug_info;
    851         const last_dbg_info_decl = self.dbg_info_decl_last.?;
    852         const debug_info_sect = &self.sections.items[self.debug_info_section_index.?];
    853 
    854         // We have a function to compute the upper bound size, because it's needed
    855         // for determining where to put the offset of the first `LinkBlock`.
    856         const needed_bytes = self.dbgInfoNeededHeaderBytes();
    857         var di_buf = try std.ArrayList(u8).initCapacity(self.base.allocator, needed_bytes);
    858         defer di_buf.deinit();
    859 
    860         // initial length - length of the .debug_info contribution for this compilation unit,
    861         // not including the initial length itself.
    862         // We have to come back and write it later after we know the size.
    863         const after_init_len = di_buf.items.len + init_len_size;
    864         // +1 for the final 0 that ends the compilation unit children.
    865         const dbg_info_end = last_dbg_info_decl.dbg_info_off + last_dbg_info_decl.dbg_info_len + 1;
    866         const init_len = dbg_info_end - after_init_len;
    867         switch (self.ptr_width) {
    868             .p32 => {
    869                 mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, init_len), target_endian);
    870             },
    871             .p64 => {
    872                 di_buf.appendNTimesAssumeCapacity(0xff, 4);
    873                 mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), init_len, target_endian);
    874             },
    875         }
    876         mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4, target_endian); // DWARF version
    877         const abbrev_offset = self.debug_abbrev_table_offset.?;
    878         switch (self.ptr_width) {
    879             .p32 => {
    880                 mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, abbrev_offset), target_endian);
    881                 di_buf.appendAssumeCapacity(4); // address size
    882             },
    883             .p64 => {
    884                 mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), abbrev_offset, target_endian);
    885                 di_buf.appendAssumeCapacity(8); // address size
    886             },
    887         }
    888         // Write the form for the compile unit, which must match the abbrev table above.
    889         const name_strp = try self.makeDebugString(module.root_pkg.root_src_path);
    890         const comp_dir_strp = try self.makeDebugString(module.root_pkg.root_src_directory.path orelse ".");
    891         const producer_strp = try self.makeDebugString(link.producer_string);
    892         // Currently only one compilation unit is supported, so the address range is simply
    893         // identical to the main program header virtual address and memory size.
    894         const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?];
    895         const low_pc = text_phdr.p_vaddr;
    896         const high_pc = text_phdr.p_vaddr + text_phdr.p_memsz;
    897 
    898         di_buf.appendAssumeCapacity(abbrev_compile_unit);
    899         self.writeDwarfAddrAssumeCapacity(&di_buf, 0); // DW.AT.stmt_list, DW.FORM.sec_offset
    900         self.writeDwarfAddrAssumeCapacity(&di_buf, low_pc);
    901         self.writeDwarfAddrAssumeCapacity(&di_buf, high_pc);
    902         self.writeDwarfAddrAssumeCapacity(&di_buf, name_strp);
    903         self.writeDwarfAddrAssumeCapacity(&di_buf, comp_dir_strp);
    904         self.writeDwarfAddrAssumeCapacity(&di_buf, producer_strp);
    905         // We are still waiting on dwarf-std.org to assign DW_LANG_Zig a number:
    906         // http://dwarfstd.org/ShowIssue.php?issue=171115.1
    907         // Until then we say it is C99.
    908         mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), DW.LANG.C99, target_endian);
    909 
    910         if (di_buf.items.len > first_dbg_info_decl.dbg_info_off) {
    911             // Move the first N decls to the end to make more padding for the header.
    912             @panic("TODO: handle .debug_info header exceeding its padding");
    913         }
    914         const jmp_amt = first_dbg_info_decl.dbg_info_off - di_buf.items.len;
    915         try self.pwriteDbgInfoNops(0, di_buf.items, jmp_amt, false, debug_info_sect.sh_offset);
    916         self.debug_info_header_dirty = false;
    917     }
    918 
    919     if (self.debug_aranges_section_dirty) {
    920         const debug_aranges_sect = &self.sections.items[self.debug_aranges_section_index.?];
    921 
    922         // Enough for all the data without resizing. When support for more compilation units
    923         // is added, the size of this section will become more variable.
    924         var di_buf = try std.ArrayList(u8).initCapacity(self.base.allocator, 100);
    925         defer di_buf.deinit();
    926 
    927         // initial length - length of the .debug_aranges contribution for this compilation unit,
    928         // not including the initial length itself.
    929         // We have to come back and write it later after we know the size.
    930         const init_len_index = di_buf.items.len;
    931         di_buf.items.len += init_len_size;
    932         const after_init_len = di_buf.items.len;
    933         mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 2, target_endian); // version
    934         // When more than one compilation unit is supported, this will be the offset to it.
    935         // For now it is always at offset 0 in .debug_info.
    936         self.writeDwarfAddrAssumeCapacity(&di_buf, 0); // .debug_info offset
    937         di_buf.appendAssumeCapacity(ptr_width_bytes); // address_size
    938         di_buf.appendAssumeCapacity(0); // segment_selector_size
    939 
    940         const end_header_offset = di_buf.items.len;
    941         const begin_entries_offset = mem.alignForward(end_header_offset, ptr_width_bytes * 2);
    942         di_buf.appendNTimesAssumeCapacity(0, begin_entries_offset - end_header_offset);
    943 
    944         // Currently only one compilation unit is supported, so the address range is simply
    945         // identical to the main program header virtual address and memory size.
    946         const text_phdr = &self.program_headers.items[self.phdr_load_re_index.?];
    947         self.writeDwarfAddrAssumeCapacity(&di_buf, text_phdr.p_vaddr);
    948         self.writeDwarfAddrAssumeCapacity(&di_buf, text_phdr.p_memsz);
    949 
    950         // Sentinel.
    951         self.writeDwarfAddrAssumeCapacity(&di_buf, 0);
    952         self.writeDwarfAddrAssumeCapacity(&di_buf, 0);
    953 
    954         // Go back and populate the initial length.
    955         const init_len = di_buf.items.len - after_init_len;
    956         switch (self.ptr_width) {
    957             .p32 => {
    958                 mem.writeInt(u32, di_buf.items[init_len_index..][0..4], @intCast(u32, init_len), target_endian);
    959             },
    960             .p64 => {
    961                 // initial length - length of the .debug_aranges contribution for this compilation unit,
    962                 // not including the initial length itself.
    963                 di_buf.items[init_len_index..][0..4].* = [_]u8{ 0xff, 0xff, 0xff, 0xff };
    964                 mem.writeInt(u64, di_buf.items[init_len_index + 4 ..][0..8], init_len, target_endian);
    965             },
    966         }
    967 
    968         const needed_size = di_buf.items.len;
    969         const allocated_size = self.allocatedSize(debug_aranges_sect.sh_offset);
    970         if (needed_size > allocated_size) {
    971             debug_aranges_sect.sh_size = 0; // free the space
    972             debug_aranges_sect.sh_offset = self.findFreeSpace(needed_size, 16);
    973         }
    974         debug_aranges_sect.sh_size = needed_size;
    975         log.debug(".debug_aranges start=0x{x} end=0x{x}", .{
    976             debug_aranges_sect.sh_offset,
    977             debug_aranges_sect.sh_offset + needed_size,
    978         });
    979 
    980         try self.base.file.?.pwriteAll(di_buf.items, debug_aranges_sect.sh_offset);
    981         if (!self.shdr_table_dirty) {
    982             // Then it won't get written with the others and we need to do it.
    983             try self.writeSectHeader(self.debug_aranges_section_index.?);
    984         }
    985 
    986         self.debug_aranges_section_dirty = false;
    987     }
    988     if (self.debug_line_header_dirty) debug_line: {
    989         if (self.dbg_line_fn_first == null) {
    990             break :debug_line; // Error in module; leave debug_line_header_dirty=true.
    991         }
    992         const dbg_line_prg_off = self.getDebugLineProgramOff();
    993         const dbg_line_prg_end = self.getDebugLineProgramEnd();
    994         assert(dbg_line_prg_end != 0);
    995 
    996         const debug_line_sect = &self.sections.items[self.debug_line_section_index.?];
    997 
    998         // The size of this header is variable, depending on the number of directories,
    999         // files, and padding. We have a function to compute the upper bound size, however,
   1000         // because it's needed for determining where to put the offset of the first `SrcFn`.
   1001         const needed_bytes = self.dbgLineNeededHeaderBytes();
   1002         var di_buf = try std.ArrayList(u8).initCapacity(self.base.allocator, needed_bytes);
   1003         defer di_buf.deinit();
   1004 
   1005         // initial length - length of the .debug_line contribution for this compilation unit,
   1006         // not including the initial length itself.
   1007         const after_init_len = di_buf.items.len + init_len_size;
   1008         const init_len = dbg_line_prg_end - after_init_len;
   1009         switch (self.ptr_width) {
   1010             .p32 => {
   1011                 mem.writeInt(u32, di_buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, init_len), target_endian);
   1012             },
   1013             .p64 => {
   1014                 di_buf.appendNTimesAssumeCapacity(0xff, 4);
   1015                 mem.writeInt(u64, di_buf.addManyAsArrayAssumeCapacity(8), init_len, target_endian);
   1016             },
   1017         }
   1018 
   1019         mem.writeInt(u16, di_buf.addManyAsArrayAssumeCapacity(2), 4, target_endian); // version
   1020 
   1021         // Empirically, debug info consumers do not respect this field, or otherwise
   1022         // consider it to be an error when it does not point exactly to the end of the header.
   1023         // Therefore we rely on the NOP jump at the beginning of the Line Number Program for
   1024         // padding rather than this field.
   1025         const before_header_len = di_buf.items.len;
   1026         di_buf.items.len += ptr_width_bytes; // We will come back and write this.
   1027         const after_header_len = di_buf.items.len;
   1028 
   1029         const opcode_base = DW.LNS.set_isa + 1;
   1030         di_buf.appendSliceAssumeCapacity(&[_]u8{
   1031             1, // minimum_instruction_length
   1032             1, // maximum_operations_per_instruction
   1033             1, // default_is_stmt
   1034             1, // line_base (signed)
   1035             1, // line_range
   1036             opcode_base,
   1037 
   1038             // Standard opcode lengths. The number of items here is based on `opcode_base`.
   1039             // The value is the number of LEB128 operands the instruction takes.
   1040             0, // `DW.LNS.copy`
   1041             1, // `DW.LNS.advance_pc`
   1042             1, // `DW.LNS.advance_line`
   1043             1, // `DW.LNS.set_file`
   1044             1, // `DW.LNS.set_column`
   1045             0, // `DW.LNS.negate_stmt`
   1046             0, // `DW.LNS.set_basic_block`
   1047             0, // `DW.LNS.const_add_pc`
   1048             1, // `DW.LNS.fixed_advance_pc`
   1049             0, // `DW.LNS.set_prologue_end`
   1050             0, // `DW.LNS.set_epilogue_begin`
   1051             1, // `DW.LNS.set_isa`
   1052             0, // include_directories (none except the compilation unit cwd)
   1053         });
   1054         // file_names[0]
   1055         di_buf.appendSliceAssumeCapacity(module.root_pkg.root_src_path); // relative path name
   1056         di_buf.appendSliceAssumeCapacity(&[_]u8{
   1057             0, // null byte for the relative path name
   1058             0, // directory_index
   1059             0, // mtime (TODO supply this)
   1060             0, // file size bytes (TODO supply this)
   1061             0, // file_names sentinel
   1062         });
   1063 
   1064         const header_len = di_buf.items.len - after_header_len;
   1065         switch (self.ptr_width) {
   1066             .p32 => {
   1067                 mem.writeInt(u32, di_buf.items[before_header_len..][0..4], @intCast(u32, header_len), target_endian);
   1068             },
   1069             .p64 => {
   1070                 mem.writeInt(u64, di_buf.items[before_header_len..][0..8], header_len, target_endian);
   1071             },
   1072         }
   1073 
   1074         // We use NOPs because consumers empirically do not respect the header length field.
   1075         if (di_buf.items.len > dbg_line_prg_off) {
   1076             // Move the first N files to the end to make more padding for the header.
   1077             @panic("TODO: handle .debug_line header exceeding its padding");
   1078         }
   1079         const jmp_amt = dbg_line_prg_off - di_buf.items.len;
   1080         try self.pwriteDbgLineNops(0, di_buf.items, jmp_amt, debug_line_sect.sh_offset);
   1081         self.debug_line_header_dirty = false;
   1082     }
   1083 
   1084     if (self.phdr_table_dirty) {
   1085         const phsize: u64 = switch (self.ptr_width) {
   1086             .p32 => @sizeOf(elf.Elf32_Phdr),
   1087             .p64 => @sizeOf(elf.Elf64_Phdr),
   1088         };
   1089         const phalign: u16 = switch (self.ptr_width) {
   1090             .p32 => @alignOf(elf.Elf32_Phdr),
   1091             .p64 => @alignOf(elf.Elf64_Phdr),
   1092         };
   1093         const allocated_size = self.allocatedSize(self.phdr_table_offset.?);
   1094         const needed_size = self.program_headers.items.len * phsize;
   1095 
   1096         if (needed_size > allocated_size) {
   1097             self.phdr_table_offset = null; // free the space
   1098             self.phdr_table_offset = self.findFreeSpace(needed_size, phalign);
   1099         }
   1100 
   1101         switch (self.ptr_width) {
   1102             .p32 => {
   1103                 const buf = try self.base.allocator.alloc(elf.Elf32_Phdr, self.program_headers.items.len);
   1104                 defer self.base.allocator.free(buf);
   1105 
   1106                 for (buf) |*phdr, i| {
   1107                     phdr.* = progHeaderTo32(self.program_headers.items[i]);
   1108                     if (foreign_endian) {
   1109                         mem.bswapAllFields(elf.Elf32_Phdr, phdr);
   1110                     }
   1111                 }
   1112                 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
   1113             },
   1114             .p64 => {
   1115                 const buf = try self.base.allocator.alloc(elf.Elf64_Phdr, self.program_headers.items.len);
   1116                 defer self.base.allocator.free(buf);
   1117 
   1118                 for (buf) |*phdr, i| {
   1119                     phdr.* = self.program_headers.items[i];
   1120                     if (foreign_endian) {
   1121                         mem.bswapAllFields(elf.Elf64_Phdr, phdr);
   1122                     }
   1123                 }
   1124                 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
   1125             },
   1126         }
   1127         self.phdr_table_dirty = false;
   1128     }
   1129 
   1130     {
   1131         const shstrtab_sect = &self.sections.items[self.shstrtab_index.?];
   1132         if (self.shstrtab_dirty or self.shstrtab.items.len != shstrtab_sect.sh_size) {
   1133             const allocated_size = self.allocatedSize(shstrtab_sect.sh_offset);
   1134             const needed_size = self.shstrtab.items.len;
   1135 
   1136             if (needed_size > allocated_size) {
   1137                 shstrtab_sect.sh_size = 0; // free the space
   1138                 shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);
   1139             }
   1140             shstrtab_sect.sh_size = needed_size;
   1141             log.debug("writing shstrtab start=0x{x} end=0x{x}", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + needed_size });
   1142 
   1143             try self.base.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset);
   1144             if (!self.shdr_table_dirty) {
   1145                 // Then it won't get written with the others and we need to do it.
   1146                 try self.writeSectHeader(self.shstrtab_index.?);
   1147             }
   1148             self.shstrtab_dirty = false;
   1149         }
   1150     }
   1151     {
   1152         const debug_strtab_sect = &self.sections.items[self.debug_str_section_index.?];
   1153         if (self.debug_strtab_dirty or self.debug_strtab.items.len != debug_strtab_sect.sh_size) {
   1154             const allocated_size = self.allocatedSize(debug_strtab_sect.sh_offset);
   1155             const needed_size = self.debug_strtab.items.len;
   1156 
   1157             if (needed_size > allocated_size) {
   1158                 debug_strtab_sect.sh_size = 0; // free the space
   1159                 debug_strtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);
   1160             }
   1161             debug_strtab_sect.sh_size = needed_size;
   1162             log.debug("debug_strtab start=0x{x} end=0x{x}", .{ debug_strtab_sect.sh_offset, debug_strtab_sect.sh_offset + needed_size });
   1163 
   1164             try self.base.file.?.pwriteAll(self.debug_strtab.items, debug_strtab_sect.sh_offset);
   1165             if (!self.shdr_table_dirty) {
   1166                 // Then it won't get written with the others and we need to do it.
   1167                 try self.writeSectHeader(self.debug_str_section_index.?);
   1168             }
   1169             self.debug_strtab_dirty = false;
   1170         }
   1171     }
   1172     if (self.shdr_table_dirty) {
   1173         const shsize: u64 = switch (self.ptr_width) {
   1174             .p32 => @sizeOf(elf.Elf32_Shdr),
   1175             .p64 => @sizeOf(elf.Elf64_Shdr),
   1176         };
   1177         const shalign: u16 = switch (self.ptr_width) {
   1178             .p32 => @alignOf(elf.Elf32_Shdr),
   1179             .p64 => @alignOf(elf.Elf64_Shdr),
   1180         };
   1181         const allocated_size = self.allocatedSize(self.shdr_table_offset.?);
   1182         const needed_size = self.sections.items.len * shsize;
   1183 
   1184         if (needed_size > allocated_size) {
   1185             self.shdr_table_offset = null; // free the space
   1186             self.shdr_table_offset = self.findFreeSpace(needed_size, shalign);
   1187         }
   1188 
   1189         switch (self.ptr_width) {
   1190             .p32 => {
   1191                 const buf = try self.base.allocator.alloc(elf.Elf32_Shdr, self.sections.items.len);
   1192                 defer self.base.allocator.free(buf);
   1193 
   1194                 for (buf) |*shdr, i| {
   1195                     shdr.* = sectHeaderTo32(self.sections.items[i]);
   1196                     log.debug("writing section {}", .{shdr.*});
   1197                     if (foreign_endian) {
   1198                         mem.bswapAllFields(elf.Elf32_Shdr, shdr);
   1199                     }
   1200                 }
   1201                 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
   1202             },
   1203             .p64 => {
   1204                 const buf = try self.base.allocator.alloc(elf.Elf64_Shdr, self.sections.items.len);
   1205                 defer self.base.allocator.free(buf);
   1206 
   1207                 for (buf) |*shdr, i| {
   1208                     shdr.* = self.sections.items[i];
   1209                     log.debug("writing section {}", .{shdr.*});
   1210                     if (foreign_endian) {
   1211                         mem.bswapAllFields(elf.Elf64_Shdr, shdr);
   1212                     }
   1213                 }
   1214                 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
   1215             },
   1216         }
   1217         self.shdr_table_dirty = false;
   1218     }
   1219     if (self.entry_addr == null and self.base.options.effectiveOutputMode() == .Exe) {
   1220         log.debug("flushing. no_entry_point_found = true", .{});
   1221         self.error_flags.no_entry_point_found = true;
   1222     } else {
   1223         log.debug("flushing. no_entry_point_found = false", .{});
   1224         self.error_flags.no_entry_point_found = false;
   1225         try self.writeElfHeader();
   1226     }
   1227 
   1228     // The point of flush() is to commit changes, so in theory, nothing should
   1229     // be dirty after this. However, it is possible for some things to remain
   1230     // dirty because they fail to be written in the event of compile errors,
   1231     // such as debug_line_header_dirty and debug_info_header_dirty.
   1232     assert(!self.debug_abbrev_section_dirty);
   1233     assert(!self.debug_aranges_section_dirty);
   1234     assert(!self.phdr_table_dirty);
   1235     assert(!self.shdr_table_dirty);
   1236     assert(!self.shstrtab_dirty);
   1237     assert(!self.debug_strtab_dirty);
   1238 }
   1239 
   1240 fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
   1241     const tracy = trace(@src());
   1242     defer tracy.end();
   1243 
   1244     var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
   1245     defer arena_allocator.deinit();
   1246     const arena = &arena_allocator.allocator;
   1247 
   1248     const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type.
   1249 
   1250     // If there is no Zig code to compile, then we should skip flushing the output file because it
   1251     // will not be part of the linker line anyway.
   1252     const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {
   1253         // stage1 puts the object file in the cache directory.
   1254         if (self.base.options.use_stage1) {
   1255             const obj_basename = try std.zig.binNameAlloc(arena, .{
   1256                 .root_name = self.base.options.root_name,
   1257                 .target = self.base.options.target,
   1258                 .output_mode = .Obj,
   1259             });
   1260             const o_directory = module.zig_cache_artifact_directory;
   1261             const full_obj_path = try o_directory.join(arena, &[_][]const u8{obj_basename});
   1262             break :blk full_obj_path;
   1263         }
   1264 
   1265         try self.flushModule(comp);
   1266         const obj_basename = self.base.intermediary_basename.?;
   1267         const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename});
   1268         break :blk full_obj_path;
   1269     } else null;
   1270 
   1271     const is_obj = self.base.options.output_mode == .Obj;
   1272     const is_lib = self.base.options.output_mode == .Lib;
   1273     const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
   1274     const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;
   1275     const have_dynamic_linker = self.base.options.link_libc and
   1276         self.base.options.link_mode == .Dynamic and is_exe_or_dyn_lib;
   1277     const target = self.base.options.target;
   1278     const gc_sections = self.base.options.gc_sections orelse !is_obj;
   1279     const stack_size = self.base.options.stack_size_override orelse 16777216;
   1280     const allow_shlib_undefined = self.base.options.allow_shlib_undefined orelse !self.base.options.is_native_os;
   1281     const compiler_rt_path: ?[]const u8 = blk: {
   1282         if (comp.compiler_rt_static_lib) |x| break :blk x.full_object_path;
   1283         if (comp.compiler_rt_obj) |x| break :blk x.full_object_path;
   1284         break :blk null;
   1285     };
   1286 
   1287     // Here we want to determine whether we can save time by not invoking LLD when the
   1288     // output is unchanged. None of the linker options or the object files that are being
   1289     // linked are in the hash that namespaces the directory we are outputting to. Therefore,
   1290     // we must hash those now, and the resulting digest will form the "id" of the linking
   1291     // job we are about to perform.
   1292     // After a successful link, we store the id in the metadata of a symlink named "id.txt" in
   1293     // the artifact directory. So, now, we check if this symlink exists, and if it matches
   1294     // our digest. If so, we can skip linking. Otherwise, we proceed with invoking LLD.
   1295     const id_symlink_basename = "lld.id";
   1296 
   1297     var man: Cache.Manifest = undefined;
   1298     defer if (!self.base.options.disable_lld_caching) man.deinit();
   1299 
   1300     var digest: [Cache.hex_digest_len]u8 = undefined;
   1301 
   1302     if (!self.base.options.disable_lld_caching) {
   1303         man = comp.cache_parent.obtain();
   1304 
   1305         // We are about to obtain this lock, so here we give other processes a chance first.
   1306         self.base.releaseLock();
   1307 
   1308         try man.addOptionalFile(self.base.options.linker_script);
   1309         try man.addOptionalFile(self.base.options.version_script);
   1310         try man.addListOfFiles(self.base.options.objects);
   1311         for (comp.c_object_table.keys()) |key| {
   1312             _ = try man.addFile(key.status.success.object_path, null);
   1313         }
   1314         try man.addOptionalFile(module_obj_path);
   1315         try man.addOptionalFile(compiler_rt_path);
   1316 
   1317         // We can skip hashing libc and libc++ components that we are in charge of building from Zig
   1318         // installation sources because they are always a product of the compiler version + target information.
   1319         man.hash.add(stack_size);
   1320         man.hash.addOptional(self.base.options.image_base_override);
   1321         man.hash.add(gc_sections);
   1322         man.hash.add(self.base.options.eh_frame_hdr);
   1323         man.hash.add(self.base.options.emit_relocs);
   1324         man.hash.add(self.base.options.rdynamic);
   1325         man.hash.addListOfBytes(self.base.options.lib_dirs);
   1326         man.hash.addListOfBytes(self.base.options.rpath_list);
   1327         man.hash.add(self.base.options.each_lib_rpath);
   1328         man.hash.add(self.base.options.skip_linker_dependencies);
   1329         man.hash.add(self.base.options.z_nodelete);
   1330         man.hash.add(self.base.options.z_notext);
   1331         man.hash.add(self.base.options.z_defs);
   1332         man.hash.add(self.base.options.z_origin);
   1333         man.hash.add(self.base.options.z_noexecstack);
   1334         man.hash.add(self.base.options.z_now);
   1335         man.hash.add(self.base.options.z_relro);
   1336         if (self.base.options.link_libc) {
   1337             man.hash.add(self.base.options.libc_installation != null);
   1338             if (self.base.options.libc_installation) |libc_installation| {
   1339                 man.hash.addBytes(libc_installation.crt_dir.?);
   1340             }
   1341             if (have_dynamic_linker) {
   1342                 man.hash.addOptionalBytes(self.base.options.dynamic_linker);
   1343             }
   1344         }
   1345         man.hash.addOptionalBytes(self.base.options.soname);
   1346         man.hash.addOptional(self.base.options.version);
   1347         link.hashAddSystemLibs(&man.hash, self.base.options.system_libs);
   1348         man.hash.add(allow_shlib_undefined);
   1349         man.hash.add(self.base.options.bind_global_refs_locally);
   1350         man.hash.add(self.base.options.tsan);
   1351         man.hash.addOptionalBytes(self.base.options.sysroot);
   1352         man.hash.add(self.base.options.linker_optimization);
   1353 
   1354         // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
   1355         _ = try man.hit();
   1356         digest = man.final();
   1357 
   1358         var prev_digest_buf: [digest.len]u8 = undefined;
   1359         const prev_digest: []u8 = Cache.readSmallFile(
   1360             directory.handle,
   1361             id_symlink_basename,
   1362             &prev_digest_buf,
   1363         ) catch |err| blk: {
   1364             log.debug("ELF LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
   1365             // Handle this as a cache miss.
   1366             break :blk prev_digest_buf[0..0];
   1367         };
   1368         if (mem.eql(u8, prev_digest, &digest)) {
   1369             log.debug("ELF LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
   1370             // Hot diggity dog! The output binary is already there.
   1371             self.base.lock = man.toOwnedLock();
   1372             return;
   1373         }
   1374         log.debug("ELF LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) });
   1375 
   1376         // We are about to change the output file to be different, so we invalidate the build hash now.
   1377         directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
   1378             error.FileNotFound => {},
   1379             else => |e| return e,
   1380         };
   1381     }
   1382 
   1383     const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
   1384 
   1385     // Due to a deficiency in LLD, we need to special-case BPF to a simple file copy when generating
   1386     // relocatables. Normally, we would expect `lld -r` to work. However, because LLD wants to resolve
   1387     // BPF relocations which it shouldn't, it fails before even generating the relocatable.
   1388     if (self.base.options.output_mode == .Obj and (self.base.options.lto or target.isBpfFreestanding())) {
   1389         // In this case we must do a simple file copy
   1390         // here. TODO: think carefully about how we can avoid this redundant operation when doing
   1391         // build-obj. See also the corresponding TODO in linkAsArchive.
   1392         const the_object_path = blk: {
   1393             if (self.base.options.objects.len != 0)
   1394                 break :blk self.base.options.objects[0];
   1395 
   1396             if (comp.c_object_table.count() != 0)
   1397                 break :blk comp.c_object_table.keys()[0].status.success.object_path;
   1398 
   1399             if (module_obj_path) |p|
   1400                 break :blk p;
   1401 
   1402             // TODO I think this is unreachable. Audit this situation when solving the above TODO
   1403             // regarding eliding redundant object -> object transformations.
   1404             return error.NoObjectsToLink;
   1405         };
   1406         // This can happen when using --enable-cache and using the stage1 backend. In this case
   1407         // we can skip the file copy.
   1408         if (!mem.eql(u8, the_object_path, full_out_path)) {
   1409             try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
   1410         }
   1411     } else {
   1412 
   1413         // Create an LLD command line and invoke it.
   1414         var argv = std.ArrayList([]const u8).init(self.base.allocator);
   1415         defer argv.deinit();
   1416         // We will invoke ourselves as a child process to gain access to LLD.
   1417         // This is necessary because LLD does not behave properly as a library -
   1418         // it calls exit() and does not reset all global data between invocations.
   1419         try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "ld.lld" });
   1420         if (is_obj) {
   1421             try argv.append("-r");
   1422         }
   1423 
   1424         try argv.append("-error-limit=0");
   1425 
   1426         if (self.base.options.sysroot) |sysroot| {
   1427             try argv.append(try std.fmt.allocPrint(arena, "--sysroot={s}", .{sysroot}));
   1428         }
   1429 
   1430         if (self.base.options.lto) {
   1431             switch (self.base.options.optimize_mode) {
   1432                 .Debug => {},
   1433                 .ReleaseSmall => try argv.append("--lto-O2"),
   1434                 .ReleaseFast, .ReleaseSafe => try argv.append("--lto-O3"),
   1435             }
   1436         }
   1437         try argv.append(try std.fmt.allocPrint(arena, "-O{d}", .{
   1438             self.base.options.linker_optimization,
   1439         }));
   1440 
   1441         if (self.base.options.output_mode == .Exe) {
   1442             try argv.append("-z");
   1443             try argv.append(try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size}));
   1444         }
   1445 
   1446         if (self.base.options.image_base_override) |image_base| {
   1447             try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{image_base}));
   1448         }
   1449 
   1450         if (self.base.options.linker_script) |linker_script| {
   1451             try argv.append("-T");
   1452             try argv.append(linker_script);
   1453         }
   1454 
   1455         if (gc_sections) {
   1456             try argv.append("--gc-sections");
   1457         }
   1458 
   1459         if (self.base.options.eh_frame_hdr) {
   1460             try argv.append("--eh-frame-hdr");
   1461         }
   1462 
   1463         if (self.base.options.emit_relocs) {
   1464             try argv.append("--emit-relocs");
   1465         }
   1466 
   1467         if (self.base.options.rdynamic) {
   1468             try argv.append("--export-dynamic");
   1469         }
   1470 
   1471         if (self.base.options.z_nodelete) {
   1472             try argv.append("-z");
   1473             try argv.append("nodelete");
   1474         }
   1475         if (self.base.options.z_notext) {
   1476             try argv.append("-z");
   1477             try argv.append("notext");
   1478         }
   1479         if (self.base.options.z_defs) {
   1480             try argv.append("-z");
   1481             try argv.append("defs");
   1482         }
   1483         if (self.base.options.z_origin) {
   1484             try argv.append("-z");
   1485             try argv.append("origin");
   1486         }
   1487         if (self.base.options.z_noexecstack) {
   1488             try argv.append("-z");
   1489             try argv.append("noexecstack");
   1490         }
   1491         if (self.base.options.z_now) {
   1492             try argv.append("-z");
   1493             try argv.append("now");
   1494         }
   1495         if (self.base.options.z_relro) {
   1496             try argv.append("-z");
   1497             try argv.append("relro");
   1498         }
   1499 
   1500         if (getLDMOption(target)) |ldm| {
   1501             // Any target ELF will use the freebsd osabi if suffixed with "_fbsd".
   1502             const arg = if (target.os.tag == .freebsd)
   1503                 try std.fmt.allocPrint(arena, "{s}_fbsd", .{ldm})
   1504             else
   1505                 ldm;
   1506             try argv.append("-m");
   1507             try argv.append(arg);
   1508         }
   1509 
   1510         if (self.base.options.link_mode == .Static) {
   1511             if (target.cpu.arch.isARM() or target.cpu.arch.isThumb()) {
   1512                 try argv.append("-Bstatic");
   1513             } else {
   1514                 try argv.append("-static");
   1515             }
   1516         } else if (is_dyn_lib) {
   1517             try argv.append("-shared");
   1518         }
   1519 
   1520         if (self.base.options.pie and self.base.options.output_mode == .Exe) {
   1521             try argv.append("-pie");
   1522         }
   1523 
   1524         if (self.base.options.link_mode == .Dynamic and target.os.tag == .netbsd) {
   1525             // Add options to produce shared objects with only 2 PT_LOAD segments.
   1526             // NetBSD expects 2 PT_LOAD segments in a shared object, otherwise
   1527             // ld.elf_so fails to load, emitting a general "not found" error.
   1528             // See https://github.com/ziglang/zig/issues/9109 .
   1529             try argv.append("--no-rosegment");
   1530             try argv.append("-znorelro");
   1531         }
   1532 
   1533         try argv.append("-o");
   1534         try argv.append(full_out_path);
   1535 
   1536         // csu prelude
   1537         var csu = try CsuObjects.init(arena, self.base.options, comp);
   1538         if (csu.crt0) |v| try argv.append(v);
   1539         if (csu.crti) |v| try argv.append(v);
   1540         if (csu.crtbegin) |v| try argv.append(v);
   1541 
   1542         // rpaths
   1543         var rpath_table = std.StringHashMap(void).init(self.base.allocator);
   1544         defer rpath_table.deinit();
   1545         for (self.base.options.rpath_list) |rpath| {
   1546             if ((try rpath_table.fetchPut(rpath, {})) == null) {
   1547                 try argv.append("-rpath");
   1548                 try argv.append(rpath);
   1549             }
   1550         }
   1551         if (self.base.options.each_lib_rpath) {
   1552             var test_path = std.ArrayList(u8).init(self.base.allocator);
   1553             defer test_path.deinit();
   1554             for (self.base.options.lib_dirs) |lib_dir_path| {
   1555                 for (self.base.options.system_libs.keys()) |link_lib| {
   1556                     test_path.clearRetainingCapacity();
   1557                     const sep = fs.path.sep_str;
   1558                     try test_path.writer().print("{s}" ++ sep ++ "lib{s}.so", .{
   1559                         lib_dir_path, link_lib,
   1560                     });
   1561                     fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
   1562                         error.FileNotFound => continue,
   1563                         else => |e| return e,
   1564                     };
   1565                     if ((try rpath_table.fetchPut(lib_dir_path, {})) == null) {
   1566                         try argv.append("-rpath");
   1567                         try argv.append(lib_dir_path);
   1568                     }
   1569                 }
   1570             }
   1571         }
   1572 
   1573         for (self.base.options.lib_dirs) |lib_dir| {
   1574             try argv.append("-L");
   1575             try argv.append(lib_dir);
   1576         }
   1577 
   1578         if (self.base.options.link_libc) {
   1579             if (self.base.options.libc_installation) |libc_installation| {
   1580                 try argv.append("-L");
   1581                 try argv.append(libc_installation.crt_dir.?);
   1582             }
   1583 
   1584             if (have_dynamic_linker) {
   1585                 if (self.base.options.dynamic_linker) |dynamic_linker| {
   1586                     try argv.append("-dynamic-linker");
   1587                     try argv.append(dynamic_linker);
   1588                 }
   1589             }
   1590         }
   1591 
   1592         if (is_dyn_lib) {
   1593             if (self.base.options.soname) |soname| {
   1594                 try argv.append("-soname");
   1595                 try argv.append(soname);
   1596             }
   1597             if (self.base.options.version_script) |version_script| {
   1598                 try argv.append("-version-script");
   1599                 try argv.append(version_script);
   1600             }
   1601         }
   1602 
   1603         // Positional arguments to the linker such as object files.
   1604         try argv.appendSlice(self.base.options.objects);
   1605 
   1606         for (comp.c_object_table.keys()) |key| {
   1607             try argv.append(key.status.success.object_path);
   1608         }
   1609 
   1610         if (module_obj_path) |p| {
   1611             try argv.append(p);
   1612         }
   1613 
   1614         // TSAN
   1615         if (self.base.options.tsan) {
   1616             try argv.append(comp.tsan_static_lib.?.full_object_path);
   1617         }
   1618 
   1619         // libc
   1620         if (is_exe_or_dyn_lib and
   1621             !self.base.options.skip_linker_dependencies and
   1622             !self.base.options.link_libc)
   1623         {
   1624             if (comp.libc_static_lib) |lib| {
   1625                 try argv.append(lib.full_object_path);
   1626             }
   1627         }
   1628 
   1629         // compiler-rt
   1630         if (compiler_rt_path) |p| {
   1631             try argv.append(p);
   1632         }
   1633 
   1634         // Shared libraries.
   1635         if (is_exe_or_dyn_lib) {
   1636             const system_libs = self.base.options.system_libs.keys();
   1637             const system_libs_values = self.base.options.system_libs.values();
   1638 
   1639             // Worst-case, we need an --as-needed argument for every lib, as well
   1640             // as one before and one after.
   1641             try argv.ensureUnusedCapacity(system_libs.len * 2 + 2);
   1642             argv.appendAssumeCapacity("--as-needed");
   1643             var as_needed = true;
   1644 
   1645             for (system_libs) |link_lib, i| {
   1646                 const lib_as_needed = !system_libs_values[i].needed;
   1647                 switch ((@as(u2, @boolToInt(lib_as_needed)) << 1) | @boolToInt(as_needed)) {
   1648                     0b00, 0b11 => {},
   1649                     0b01 => {
   1650                         argv.appendAssumeCapacity("--no-as-needed");
   1651                         as_needed = false;
   1652                     },
   1653                     0b10 => {
   1654                         argv.appendAssumeCapacity("--as-needed");
   1655                         as_needed = true;
   1656                     },
   1657                 }
   1658 
   1659                 // By this time, we depend on these libs being dynamically linked
   1660                 // libraries and not static libraries (the check for that needs to be earlier),
   1661                 // but they could be full paths to .so files, in which case we
   1662                 // want to avoid prepending "-l".
   1663                 const ext = Compilation.classifyFileExt(link_lib);
   1664                 const arg = if (ext == .shared_library) link_lib else try std.fmt.allocPrint(arena, "-l{s}", .{link_lib});
   1665                 argv.appendAssumeCapacity(arg);
   1666             }
   1667 
   1668             if (!as_needed) {
   1669                 argv.appendAssumeCapacity("--as-needed");
   1670                 as_needed = true;
   1671             }
   1672 
   1673             // libc++ dep
   1674             if (self.base.options.link_libcpp) {
   1675                 try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
   1676                 try argv.append(comp.libcxx_static_lib.?.full_object_path);
   1677             }
   1678 
   1679             // libunwind dep
   1680             if (self.base.options.link_libunwind) {
   1681                 try argv.append(comp.libunwind_static_lib.?.full_object_path);
   1682             }
   1683 
   1684             // libc dep
   1685             if (self.base.options.link_libc) {
   1686                 if (self.base.options.libc_installation != null) {
   1687                     const needs_grouping = self.base.options.link_mode == .Static;
   1688                     if (needs_grouping) try argv.append("--start-group");
   1689                     try argv.appendSlice(target_util.libcFullLinkFlags(target));
   1690                     if (needs_grouping) try argv.append("--end-group");
   1691                 } else if (target.isGnuLibC()) {
   1692                     for (glibc.libs) |lib| {
   1693                         const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{
   1694                             comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
   1695                         });
   1696                         try argv.append(lib_path);
   1697                     }
   1698                     try argv.append(try comp.get_libc_crt_file(arena, "libc_nonshared.a"));
   1699                 } else if (target.isMusl()) {
   1700                     try argv.append(try comp.get_libc_crt_file(arena, switch (self.base.options.link_mode) {
   1701                         .Static => "libc.a",
   1702                         .Dynamic => "libc.so",
   1703                     }));
   1704                 } else {
   1705                     unreachable; // Compiler was supposed to emit an error for not being able to provide libc.
   1706                 }
   1707             }
   1708         }
   1709 
   1710         // crt postlude
   1711         if (csu.crtend) |v| try argv.append(v);
   1712         if (csu.crtn) |v| try argv.append(v);
   1713 
   1714         if (allow_shlib_undefined) {
   1715             try argv.append("--allow-shlib-undefined");
   1716         }
   1717 
   1718         if (self.base.options.bind_global_refs_locally) {
   1719             try argv.append("-Bsymbolic");
   1720         }
   1721 
   1722         if (self.base.options.verbose_link) {
   1723             // Skip over our own name so that the LLD linker name is the first argv item.
   1724             Compilation.dump_argv(argv.items[1..]);
   1725         }
   1726 
   1727         // Sadly, we must run LLD as a child process because it does not behave
   1728         // properly as a library.
   1729         const child = try std.ChildProcess.init(argv.items, arena);
   1730         defer child.deinit();
   1731 
   1732         if (comp.clang_passthrough_mode) {
   1733             child.stdin_behavior = .Inherit;
   1734             child.stdout_behavior = .Inherit;
   1735             child.stderr_behavior = .Inherit;
   1736 
   1737             const term = child.spawnAndWait() catch |err| {
   1738                 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
   1739                 return error.UnableToSpawnSelf;
   1740             };
   1741             switch (term) {
   1742                 .Exited => |code| {
   1743                     if (code != 0) {
   1744                         // TODO https://github.com/ziglang/zig/issues/6342
   1745                         std.process.exit(1);
   1746                     }
   1747                 },
   1748                 else => std.process.abort(),
   1749             }
   1750         } else {
   1751             child.stdin_behavior = .Ignore;
   1752             child.stdout_behavior = .Ignore;
   1753             child.stderr_behavior = .Pipe;
   1754 
   1755             try child.spawn();
   1756 
   1757             const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024);
   1758 
   1759             const term = child.wait() catch |err| {
   1760                 log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
   1761                 return error.UnableToSpawnSelf;
   1762             };
   1763 
   1764             switch (term) {
   1765                 .Exited => |code| {
   1766                     if (code != 0) {
   1767                         // TODO parse this output and surface with the Compilation API rather than
   1768                         // directly outputting to stderr here.
   1769                         std.debug.print("{s}", .{stderr});
   1770                         return error.LLDReportedFailure;
   1771                     }
   1772                 },
   1773                 else => {
   1774                     log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
   1775                     return error.LLDCrashed;
   1776                 },
   1777             }
   1778 
   1779             if (stderr.len != 0) {
   1780                 log.warn("unexpected LLD stderr:\n{s}", .{stderr});
   1781             }
   1782         }
   1783     }
   1784 
   1785     if (!self.base.options.disable_lld_caching) {
   1786         // Update the file with the digest. If it fails we can continue; it only
   1787         // means that the next invocation will have an unnecessary cache miss.
   1788         Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
   1789             log.warn("failed to save linking hash digest file: {s}", .{@errorName(err)});
   1790         };
   1791         // Again failure here only means an unnecessary cache miss.
   1792         man.writeManifest() catch |err| {
   1793             log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)});
   1794         };
   1795         // We hang on to this lock so that the output file path can be used without
   1796         // other processes clobbering it.
   1797         self.base.lock = man.toOwnedLock();
   1798     }
   1799 }
   1800 
   1801 fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) void {
   1802     const target_endian = self.base.options.target.cpu.arch.endian();
   1803     switch (self.ptr_width) {
   1804         .p32 => mem.writeInt(u32, buf.addManyAsArrayAssumeCapacity(4), @intCast(u32, addr), target_endian),
   1805         .p64 => mem.writeInt(u64, buf.addManyAsArrayAssumeCapacity(8), addr, target_endian),
   1806     }
   1807 }
   1808 
   1809 fn writeElfHeader(self: *Elf) !void {
   1810     var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
   1811 
   1812     var index: usize = 0;
   1813     hdr_buf[0..4].* = "\x7fELF".*;
   1814     index += 4;
   1815 
   1816     hdr_buf[index] = switch (self.ptr_width) {
   1817         .p32 => elf.ELFCLASS32,
   1818         .p64 => elf.ELFCLASS64,
   1819     };
   1820     index += 1;
   1821 
   1822     const endian = self.base.options.target.cpu.arch.endian();
   1823     hdr_buf[index] = switch (endian) {
   1824         .Little => elf.ELFDATA2LSB,
   1825         .Big => elf.ELFDATA2MSB,
   1826     };
   1827     index += 1;
   1828 
   1829     hdr_buf[index] = 1; // ELF version
   1830     index += 1;
   1831 
   1832     // OS ABI, often set to 0 regardless of target platform
   1833     // ABI Version, possibly used by glibc but not by static executables
   1834     // padding
   1835     mem.set(u8, hdr_buf[index..][0..9], 0);
   1836     index += 9;
   1837 
   1838     assert(index == 16);
   1839 
   1840     const elf_type = switch (self.base.options.effectiveOutputMode()) {
   1841         .Exe => elf.ET.EXEC,
   1842         .Obj => elf.ET.REL,
   1843         .Lib => switch (self.base.options.link_mode) {
   1844             .Static => elf.ET.REL,
   1845             .Dynamic => elf.ET.DYN,
   1846         },
   1847     };
   1848     mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf_type), endian);
   1849     index += 2;
   1850 
   1851     const machine = self.base.options.target.cpu.arch.toElfMachine();
   1852     mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(machine), endian);
   1853     index += 2;
   1854 
   1855     // ELF Version, again
   1856     mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian);
   1857     index += 4;
   1858 
   1859     const e_entry = if (elf_type == .REL) 0 else self.entry_addr.?;
   1860 
   1861     switch (self.ptr_width) {
   1862         .p32 => {
   1863             mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, e_entry), endian);
   1864             index += 4;
   1865 
   1866             // e_phoff
   1867             mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.phdr_table_offset.?), endian);
   1868             index += 4;
   1869 
   1870             // e_shoff
   1871             mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.shdr_table_offset.?), endian);
   1872             index += 4;
   1873         },
   1874         .p64 => {
   1875             // e_entry
   1876             mem.writeInt(u64, hdr_buf[index..][0..8], e_entry, endian);
   1877             index += 8;
   1878 
   1879             // e_phoff
   1880             mem.writeInt(u64, hdr_buf[index..][0..8], self.phdr_table_offset.?, endian);
   1881             index += 8;
   1882 
   1883             // e_shoff
   1884             mem.writeInt(u64, hdr_buf[index..][0..8], self.shdr_table_offset.?, endian);
   1885             index += 8;
   1886         },
   1887     }
   1888 
   1889     const e_flags = 0;
   1890     mem.writeInt(u32, hdr_buf[index..][0..4], e_flags, endian);
   1891     index += 4;
   1892 
   1893     const e_ehsize: u16 = switch (self.ptr_width) {
   1894         .p32 => @sizeOf(elf.Elf32_Ehdr),
   1895         .p64 => @sizeOf(elf.Elf64_Ehdr),
   1896     };
   1897     mem.writeInt(u16, hdr_buf[index..][0..2], e_ehsize, endian);
   1898     index += 2;
   1899 
   1900     const e_phentsize: u16 = switch (self.ptr_width) {
   1901         .p32 => @sizeOf(elf.Elf32_Phdr),
   1902         .p64 => @sizeOf(elf.Elf64_Phdr),
   1903     };
   1904     mem.writeInt(u16, hdr_buf[index..][0..2], e_phentsize, endian);
   1905     index += 2;
   1906 
   1907     const e_phnum = @intCast(u16, self.program_headers.items.len);
   1908     mem.writeInt(u16, hdr_buf[index..][0..2], e_phnum, endian);
   1909     index += 2;
   1910 
   1911     const e_shentsize: u16 = switch (self.ptr_width) {
   1912         .p32 => @sizeOf(elf.Elf32_Shdr),
   1913         .p64 => @sizeOf(elf.Elf64_Shdr),
   1914     };
   1915     mem.writeInt(u16, hdr_buf[index..][0..2], e_shentsize, endian);
   1916     index += 2;
   1917 
   1918     const e_shnum = @intCast(u16, self.sections.items.len);
   1919     mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian);
   1920     index += 2;
   1921 
   1922     mem.writeInt(u16, hdr_buf[index..][0..2], self.shstrtab_index.?, endian);
   1923     index += 2;
   1924 
   1925     assert(index == e_ehsize);
   1926 
   1927     try self.base.file.?.pwriteAll(hdr_buf[0..index], 0);
   1928 }
   1929 
   1930 fn freeTextBlock(self: *Elf, text_block: *TextBlock) void {
   1931     var already_have_free_list_node = false;
   1932     {
   1933         var i: usize = 0;
   1934         // TODO turn text_block_free_list into a hash map
   1935         while (i < self.text_block_free_list.items.len) {
   1936             if (self.text_block_free_list.items[i] == text_block) {
   1937                 _ = self.text_block_free_list.swapRemove(i);
   1938                 continue;
   1939             }
   1940             if (self.text_block_free_list.items[i] == text_block.prev) {
   1941                 already_have_free_list_node = true;
   1942             }
   1943             i += 1;
   1944         }
   1945     }
   1946     // TODO process free list for dbg info just like we do above for vaddrs
   1947 
   1948     if (self.last_text_block == text_block) {
   1949         // TODO shrink the .text section size here
   1950         self.last_text_block = text_block.prev;
   1951     }
   1952     if (self.dbg_info_decl_first == text_block) {
   1953         self.dbg_info_decl_first = text_block.dbg_info_next;
   1954     }
   1955     if (self.dbg_info_decl_last == text_block) {
   1956         // TODO shrink the .debug_info section size here
   1957         self.dbg_info_decl_last = text_block.dbg_info_prev;
   1958     }
   1959 
   1960     if (text_block.prev) |prev| {
   1961         prev.next = text_block.next;
   1962 
   1963         if (!already_have_free_list_node and prev.freeListEligible(self.*)) {
   1964             // The free list is heuristics, it doesn't have to be perfect, so we can
   1965             // ignore the OOM here.
   1966             self.text_block_free_list.append(self.base.allocator, prev) catch {};
   1967         }
   1968     } else {
   1969         text_block.prev = null;
   1970     }
   1971 
   1972     if (text_block.next) |next| {
   1973         next.prev = text_block.prev;
   1974     } else {
   1975         text_block.next = null;
   1976     }
   1977 
   1978     if (text_block.dbg_info_prev) |prev| {
   1979         prev.dbg_info_next = text_block.dbg_info_next;
   1980 
   1981         // TODO the free list logic like we do for text blocks above
   1982     } else {
   1983         text_block.dbg_info_prev = null;
   1984     }
   1985 
   1986     if (text_block.dbg_info_next) |next| {
   1987         next.dbg_info_prev = text_block.dbg_info_prev;
   1988     } else {
   1989         text_block.dbg_info_next = null;
   1990     }
   1991 }
   1992 
   1993 fn shrinkTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64) void {
   1994     _ = self;
   1995     _ = text_block;
   1996     _ = new_block_size;
   1997     // TODO check the new capacity, and if it crosses the size threshold into a big enough
   1998     // capacity, insert a free list node for it.
   1999 }
   2000 
   2001 fn growTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
   2002     const sym = self.local_symbols.items[text_block.local_sym_index];
   2003     const align_ok = mem.alignBackwardGeneric(u64, sym.st_value, alignment) == sym.st_value;
   2004     const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*);
   2005     if (!need_realloc) return sym.st_value;
   2006     return self.allocateTextBlock(text_block, new_block_size, alignment);
   2007 }
   2008 
   2009 fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
   2010     const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
   2011     const shdr = &self.sections.items[self.text_section_index.?];
   2012     const new_block_ideal_capacity = padToIdeal(new_block_size);
   2013 
   2014     // We use these to indicate our intention to update metadata, placing the new block,
   2015     // and possibly removing a free list node.
   2016     // It would be simpler to do it inside the for loop below, but that would cause a
   2017     // problem if an error was returned later in the function. So this action
   2018     // is actually carried out at the end of the function, when errors are no longer possible.
   2019     var block_placement: ?*TextBlock = null;
   2020     var free_list_removal: ?usize = null;
   2021 
   2022     // First we look for an appropriately sized free list node.
   2023     // The list is unordered. We'll just take the first thing that works.
   2024     const vaddr = blk: {
   2025         var i: usize = 0;
   2026         while (i < self.text_block_free_list.items.len) {
   2027             const big_block = self.text_block_free_list.items[i];
   2028             // We now have a pointer to a live text block that has too much capacity.
   2029             // Is it enough that we could fit this new text block?
   2030             const sym = self.local_symbols.items[big_block.local_sym_index];
   2031             const capacity = big_block.capacity(self.*);
   2032             const ideal_capacity = padToIdeal(capacity);
   2033             const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
   2034             const capacity_end_vaddr = sym.st_value + capacity;
   2035             const new_start_vaddr_unaligned = capacity_end_vaddr - new_block_ideal_capacity;
   2036             const new_start_vaddr = mem.alignBackwardGeneric(u64, new_start_vaddr_unaligned, alignment);
   2037             if (new_start_vaddr < ideal_capacity_end_vaddr) {
   2038                 // Additional bookkeeping here to notice if this free list node
   2039                 // should be deleted because the block that it points to has grown to take up
   2040                 // more of the extra capacity.
   2041                 if (!big_block.freeListEligible(self.*)) {
   2042                     _ = self.text_block_free_list.swapRemove(i);
   2043                 } else {
   2044                     i += 1;
   2045                 }
   2046                 continue;
   2047             }
   2048             // At this point we know that we will place the new block here. But the
   2049             // remaining question is whether there is still yet enough capacity left
   2050             // over for there to still be a free list node.
   2051             const remaining_capacity = new_start_vaddr - ideal_capacity_end_vaddr;
   2052             const keep_free_list_node = remaining_capacity >= min_text_capacity;
   2053 
   2054             // Set up the metadata to be updated, after errors are no longer possible.
   2055             block_placement = big_block;
   2056             if (!keep_free_list_node) {
   2057                 free_list_removal = i;
   2058             }
   2059             break :blk new_start_vaddr;
   2060         } else if (self.last_text_block) |last| {
   2061             const sym = self.local_symbols.items[last.local_sym_index];
   2062             const ideal_capacity = padToIdeal(sym.st_size);
   2063             const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
   2064             const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
   2065             // Set up the metadata to be updated, after errors are no longer possible.
   2066             block_placement = last;
   2067             break :blk new_start_vaddr;
   2068         } else {
   2069             break :blk phdr.p_vaddr;
   2070         }
   2071     };
   2072 
   2073     const expand_text_section = block_placement == null or block_placement.?.next == null;
   2074     if (expand_text_section) {
   2075         const text_capacity = self.allocatedSize(shdr.sh_offset);
   2076         const needed_size = (vaddr + new_block_size) - phdr.p_vaddr;
   2077         if (needed_size > text_capacity) {
   2078             // Must move the entire text section.
   2079             const new_offset = self.findFreeSpace(needed_size, 0x1000);
   2080             const text_size = if (self.last_text_block) |last| blk: {
   2081                 const sym = self.local_symbols.items[last.local_sym_index];
   2082                 break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr;
   2083             } else 0;
   2084             const amt = try self.base.file.?.copyRangeAll(shdr.sh_offset, self.base.file.?, new_offset, text_size);
   2085             if (amt != text_size) return error.InputOutput;
   2086             shdr.sh_offset = new_offset;
   2087             phdr.p_offset = new_offset;
   2088         }
   2089         self.last_text_block = text_block;
   2090 
   2091         shdr.sh_size = needed_size;
   2092         phdr.p_memsz = needed_size;
   2093         phdr.p_filesz = needed_size;
   2094 
   2095         // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address
   2096         // range of the compilation unit. When we expand the text section, this range changes,
   2097         // so the DW_TAG.compile_unit tag of the .debug_info section becomes dirty.
   2098         self.debug_info_header_dirty = true;
   2099         // This becomes dirty for the same reason. We could potentially make this more
   2100         // fine-grained with the addition of support for more compilation units. It is planned to
   2101         // model each package as a different compilation unit.
   2102         self.debug_aranges_section_dirty = true;
   2103 
   2104         self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
   2105         self.shdr_table_dirty = true; // TODO look into making only the one section dirty
   2106     }
   2107 
   2108     // This function can also reallocate a text block.
   2109     // In this case we need to "unplug" it from its previous location before
   2110     // plugging it in to its new location.
   2111     if (text_block.prev) |prev| {
   2112         prev.next = text_block.next;
   2113     }
   2114     if (text_block.next) |next| {
   2115         next.prev = text_block.prev;
   2116     }
   2117 
   2118     if (block_placement) |big_block| {
   2119         text_block.prev = big_block;
   2120         text_block.next = big_block.next;
   2121         big_block.next = text_block;
   2122     } else {
   2123         text_block.prev = null;
   2124         text_block.next = null;
   2125     }
   2126     if (free_list_removal) |i| {
   2127         _ = self.text_block_free_list.swapRemove(i);
   2128     }
   2129     return vaddr;
   2130 }
   2131 
   2132 pub fn allocateDeclIndexes(self: *Elf, decl: *Module.Decl) !void {
   2133     if (self.llvm_object) |_| return;
   2134 
   2135     if (decl.link.elf.local_sym_index != 0) return;
   2136 
   2137     try self.local_symbols.ensureUnusedCapacity(self.base.allocator, 1);
   2138     try self.offset_table.ensureUnusedCapacity(self.base.allocator, 1);
   2139 
   2140     if (self.local_symbol_free_list.popOrNull()) |i| {
   2141         log.debug("reusing symbol index {d} for {s}", .{ i, decl.name });
   2142         decl.link.elf.local_sym_index = i;
   2143     } else {
   2144         log.debug("allocating symbol index {d} for {s}", .{ self.local_symbols.items.len, decl.name });
   2145         decl.link.elf.local_sym_index = @intCast(u32, self.local_symbols.items.len);
   2146         _ = self.local_symbols.addOneAssumeCapacity();
   2147     }
   2148 
   2149     if (self.offset_table_free_list.popOrNull()) |i| {
   2150         decl.link.elf.offset_table_index = i;
   2151     } else {
   2152         decl.link.elf.offset_table_index = @intCast(u32, self.offset_table.items.len);
   2153         _ = self.offset_table.addOneAssumeCapacity();
   2154         self.offset_table_count_dirty = true;
   2155     }
   2156 
   2157     const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
   2158 
   2159     self.local_symbols.items[decl.link.elf.local_sym_index] = .{
   2160         .st_name = 0,
   2161         .st_info = 0,
   2162         .st_other = 0,
   2163         .st_shndx = 0,
   2164         .st_value = phdr.p_vaddr,
   2165         .st_size = 0,
   2166     };
   2167     self.offset_table.items[decl.link.elf.offset_table_index] = 0;
   2168 }
   2169 
   2170 pub fn freeDecl(self: *Elf, decl: *Module.Decl) void {
   2171     if (build_options.have_llvm) {
   2172         if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl);
   2173     }
   2174 
   2175     // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
   2176     self.freeTextBlock(&decl.link.elf);
   2177     if (decl.link.elf.local_sym_index != 0) {
   2178         self.local_symbol_free_list.append(self.base.allocator, decl.link.elf.local_sym_index) catch {};
   2179         self.offset_table_free_list.append(self.base.allocator, decl.link.elf.offset_table_index) catch {};
   2180 
   2181         self.local_symbols.items[decl.link.elf.local_sym_index].st_info = 0;
   2182 
   2183         decl.link.elf.local_sym_index = 0;
   2184     }
   2185     // TODO make this logic match freeTextBlock. Maybe abstract the logic out since the same thing
   2186     // is desired for both.
   2187     _ = self.dbg_line_fn_free_list.remove(&decl.fn_link.elf);
   2188     if (decl.fn_link.elf.prev) |prev| {
   2189         self.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {};
   2190         prev.next = decl.fn_link.elf.next;
   2191         if (decl.fn_link.elf.next) |next| {
   2192             next.prev = prev;
   2193         } else {
   2194             self.dbg_line_fn_last = prev;
   2195         }
   2196     } else if (decl.fn_link.elf.next) |next| {
   2197         self.dbg_line_fn_first = next;
   2198         next.prev = null;
   2199     }
   2200     if (self.dbg_line_fn_first == &decl.fn_link.elf) {
   2201         self.dbg_line_fn_first = decl.fn_link.elf.next;
   2202     }
   2203     if (self.dbg_line_fn_last == &decl.fn_link.elf) {
   2204         self.dbg_line_fn_last = decl.fn_link.elf.prev;
   2205     }
   2206 }
   2207 
   2208 fn deinitRelocs(gpa: *Allocator, table: *File.DbgInfoTypeRelocsTable) void {
   2209     var it = table.valueIterator();
   2210     while (it.next()) |value| {
   2211         value.relocs.deinit(gpa);
   2212     }
   2213     table.deinit(gpa);
   2214 }
   2215 
   2216 fn updateDeclCode(self: *Elf, decl: *Module.Decl, code: []const u8, stt_bits: u8) !*elf.Elf64_Sym {
   2217     const required_alignment = decl.ty.abiAlignment(self.base.options.target);
   2218 
   2219     assert(decl.link.elf.local_sym_index != 0); // Caller forgot to allocateDeclIndexes()
   2220     const local_sym = &self.local_symbols.items[decl.link.elf.local_sym_index];
   2221     if (local_sym.st_size != 0) {
   2222         const capacity = decl.link.elf.capacity(self.*);
   2223         const need_realloc = code.len > capacity or
   2224             !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);
   2225         if (need_realloc) {
   2226             const vaddr = try self.growTextBlock(&decl.link.elf, code.len, required_alignment);
   2227             log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl.name, local_sym.st_value, vaddr });
   2228             if (vaddr != local_sym.st_value) {
   2229                 local_sym.st_value = vaddr;
   2230 
   2231                 log.debug("  (writing new offset table entry)", .{});
   2232                 self.offset_table.items[decl.link.elf.offset_table_index] = vaddr;
   2233                 try self.writeOffsetTableEntry(decl.link.elf.offset_table_index);
   2234             }
   2235         } else if (code.len < local_sym.st_size) {
   2236             self.shrinkTextBlock(&decl.link.elf, code.len);
   2237         }
   2238         local_sym.st_size = code.len;
   2239         local_sym.st_name = try self.updateString(local_sym.st_name, mem.sliceTo(decl.name, 0));
   2240         local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits;
   2241         local_sym.st_other = 0;
   2242         local_sym.st_shndx = self.text_section_index.?;
   2243         // TODO this write could be avoided if no fields of the symbol were changed.
   2244         try self.writeSymbol(decl.link.elf.local_sym_index);
   2245     } else {
   2246         const decl_name = mem.sliceTo(decl.name, 0);
   2247         const name_str_index = try self.makeString(decl_name);
   2248         const vaddr = try self.allocateTextBlock(&decl.link.elf, code.len, required_alignment);
   2249         log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, vaddr });
   2250         errdefer self.freeTextBlock(&decl.link.elf);
   2251 
   2252         local_sym.* = .{
   2253             .st_name = name_str_index,
   2254             .st_info = (elf.STB_LOCAL << 4) | stt_bits,
   2255             .st_other = 0,
   2256             .st_shndx = self.text_section_index.?,
   2257             .st_value = vaddr,
   2258             .st_size = code.len,
   2259         };
   2260         self.offset_table.items[decl.link.elf.offset_table_index] = vaddr;
   2261 
   2262         try self.writeSymbol(decl.link.elf.local_sym_index);
   2263         try self.writeOffsetTableEntry(decl.link.elf.offset_table_index);
   2264     }
   2265 
   2266     const section_offset = local_sym.st_value - self.program_headers.items[self.phdr_load_re_index.?].p_vaddr;
   2267     const file_offset = self.sections.items[self.text_section_index.?].sh_offset + section_offset;
   2268     try self.base.file.?.pwriteAll(code, file_offset);
   2269 
   2270     return local_sym;
   2271 }
   2272 
   2273 fn finishUpdateDecl(
   2274     self: *Elf,
   2275     module: *Module,
   2276     decl: *Module.Decl,
   2277     dbg_info_type_relocs: *File.DbgInfoTypeRelocsTable,
   2278     dbg_info_buffer: *std.ArrayList(u8),
   2279 ) !void {
   2280     // Now we emit the .debug_info types of the Decl. These will count towards the size of
   2281     // the buffer, so we have to do it before computing the offset, and we can't perform the actual
   2282     // relocations yet.
   2283     {
   2284         var it = dbg_info_type_relocs.iterator();
   2285         while (it.next()) |entry| {
   2286             entry.value_ptr.off = @intCast(u32, dbg_info_buffer.items.len);
   2287             try self.addDbgInfoType(entry.key_ptr.*, dbg_info_buffer);
   2288         }
   2289     }
   2290 
   2291     const text_block = &decl.link.elf;
   2292     try self.updateDeclDebugInfoAllocation(text_block, @intCast(u32, dbg_info_buffer.items.len));
   2293 
   2294     const target_endian = self.base.options.target.cpu.arch.endian();
   2295 
   2296     {
   2297         // Now that we have the offset assigned we can finally perform type relocations.
   2298         var it = dbg_info_type_relocs.valueIterator();
   2299         while (it.next()) |value| {
   2300             for (value.relocs.items) |off| {
   2301                 mem.writeInt(
   2302                     u32,
   2303                     dbg_info_buffer.items[off..][0..4],
   2304                     text_block.dbg_info_off + value.off,
   2305                     target_endian,
   2306                 );
   2307             }
   2308         }
   2309     }
   2310 
   2311     try self.writeDeclDebugInfo(text_block, dbg_info_buffer.items);
   2312 
   2313     // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
   2314     const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{};
   2315     return self.updateDeclExports(module, decl, decl_exports);
   2316 }
   2317 
   2318 pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
   2319     if (build_options.skip_non_native and builtin.object_format != .elf) {
   2320         @panic("Attempted to compile for object format that was disabled by build configuration");
   2321     }
   2322     if (build_options.have_llvm) {
   2323         if (self.llvm_object) |llvm_object| return llvm_object.updateFunc(module, func, air, liveness);
   2324     }
   2325 
   2326     const tracy = trace(@src());
   2327     defer tracy.end();
   2328 
   2329     var code_buffer = std.ArrayList(u8).init(self.base.allocator);
   2330     defer code_buffer.deinit();
   2331 
   2332     // For functions we need to add a prologue to the debug line program.
   2333     var dbg_line_buffer = try std.ArrayList(u8).initCapacity(self.base.allocator, 26);
   2334     defer dbg_line_buffer.deinit();
   2335 
   2336     var dbg_info_buffer = std.ArrayList(u8).init(self.base.allocator);
   2337     defer dbg_info_buffer.deinit();
   2338 
   2339     var dbg_info_type_relocs: File.DbgInfoTypeRelocsTable = .{};
   2340     defer deinitRelocs(self.base.allocator, &dbg_info_type_relocs);
   2341 
   2342     const decl = func.owner_decl;
   2343     const line_off = @intCast(u28, decl.src_line + func.lbrace_line);
   2344 
   2345     const ptr_width_bytes = self.ptrWidthBytes();
   2346     dbg_line_buffer.appendSliceAssumeCapacity(&[_]u8{
   2347         DW.LNS.extended_op,
   2348         ptr_width_bytes + 1,
   2349         DW.LNE.set_address,
   2350     });
   2351     // This is the "relocatable" vaddr, corresponding to `code_buffer` index `0`.
   2352     assert(dbg_line_vaddr_reloc_index == dbg_line_buffer.items.len);
   2353     dbg_line_buffer.items.len += ptr_width_bytes;
   2354 
   2355     dbg_line_buffer.appendAssumeCapacity(DW.LNS.advance_line);
   2356     // This is the "relocatable" relative line offset from the previous function's end curly
   2357     // to this function's begin curly.
   2358     assert(self.getRelocDbgLineOff() == dbg_line_buffer.items.len);
   2359     // Here we use a ULEB128-fixed-4 to make sure this field can be overwritten later.
   2360     leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), line_off);
   2361 
   2362     dbg_line_buffer.appendAssumeCapacity(DW.LNS.set_file);
   2363     assert(self.getRelocDbgFileIndex() == dbg_line_buffer.items.len);
   2364     // Once we support more than one source file, this will have the ability to be more
   2365     // than one possible value.
   2366     const file_index = 1;
   2367     leb128.writeUnsignedFixed(4, dbg_line_buffer.addManyAsArrayAssumeCapacity(4), file_index);
   2368 
   2369     // Emit a line for the begin curly with prologue_end=false. The codegen will
   2370     // do the work of setting prologue_end=true and epilogue_begin=true.
   2371     dbg_line_buffer.appendAssumeCapacity(DW.LNS.copy);
   2372 
   2373     // .debug_info subprogram
   2374     const decl_name_with_null = decl.name[0 .. mem.sliceTo(decl.name, 0).len + 1];
   2375     try dbg_info_buffer.ensureUnusedCapacity(25 + decl_name_with_null.len);
   2376 
   2377     const fn_ret_type = decl.ty.fnReturnType();
   2378     const fn_ret_has_bits = fn_ret_type.hasCodeGenBits();
   2379     if (fn_ret_has_bits) {
   2380         dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram);
   2381     } else {
   2382         dbg_info_buffer.appendAssumeCapacity(abbrev_subprogram_retvoid);
   2383     }
   2384     // These get overwritten after generating the machine code. These values are
   2385     // "relocations" and have to be in this fixed place so that functions can be
   2386     // moved in virtual address space.
   2387     assert(dbg_info_low_pc_reloc_index == dbg_info_buffer.items.len);
   2388     dbg_info_buffer.items.len += ptr_width_bytes; // DW.AT.low_pc,  DW.FORM.addr
   2389     assert(self.getRelocDbgInfoSubprogramHighPC() == dbg_info_buffer.items.len);
   2390     dbg_info_buffer.items.len += 4; // DW.AT.high_pc,  DW.FORM.data4
   2391     if (fn_ret_has_bits) {
   2392         const gop = try dbg_info_type_relocs.getOrPut(self.base.allocator, fn_ret_type);
   2393         if (!gop.found_existing) {
   2394             gop.value_ptr.* = .{
   2395                 .off = undefined,
   2396                 .relocs = .{},
   2397             };
   2398         }
   2399         try gop.value_ptr.relocs.append(self.base.allocator, @intCast(u32, dbg_info_buffer.items.len));
   2400         dbg_info_buffer.items.len += 4; // DW.AT.type,  DW.FORM.ref4
   2401     }
   2402     dbg_info_buffer.appendSliceAssumeCapacity(decl_name_with_null); // DW.AT.name, DW.FORM.string
   2403 
   2404     const res = try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{
   2405         .dwarf = .{
   2406             .dbg_line = &dbg_line_buffer,
   2407             .dbg_info = &dbg_info_buffer,
   2408             .dbg_info_type_relocs = &dbg_info_type_relocs,
   2409         },
   2410     });
   2411     const code = switch (res) {
   2412         .appended => code_buffer.items,
   2413         .fail => |em| {
   2414             decl.analysis = .codegen_failure;
   2415             try module.failed_decls.put(module.gpa, decl, em);
   2416             return;
   2417         },
   2418     };
   2419 
   2420     const local_sym = try self.updateDeclCode(decl, code, elf.STT_FUNC);
   2421 
   2422     const target_endian = self.base.options.target.cpu.arch.endian();
   2423 
   2424     // Since the Decl is a function, we need to update the .debug_line program.
   2425     // Perform the relocations based on vaddr.
   2426     switch (self.ptr_width) {
   2427         .p32 => {
   2428             {
   2429                 const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..4];
   2430                 mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_value), target_endian);
   2431             }
   2432             {
   2433                 const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..4];
   2434                 mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_value), target_endian);
   2435             }
   2436         },
   2437         .p64 => {
   2438             {
   2439                 const ptr = dbg_line_buffer.items[dbg_line_vaddr_reloc_index..][0..8];
   2440                 mem.writeInt(u64, ptr, local_sym.st_value, target_endian);
   2441             }
   2442             {
   2443                 const ptr = dbg_info_buffer.items[dbg_info_low_pc_reloc_index..][0..8];
   2444                 mem.writeInt(u64, ptr, local_sym.st_value, target_endian);
   2445             }
   2446         },
   2447     }
   2448     {
   2449         const ptr = dbg_info_buffer.items[self.getRelocDbgInfoSubprogramHighPC()..][0..4];
   2450         mem.writeInt(u32, ptr, @intCast(u32, local_sym.st_size), target_endian);
   2451     }
   2452 
   2453     try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS.extended_op, 1, DW.LNE.end_sequence });
   2454 
   2455     // Now we have the full contents and may allocate a region to store it.
   2456 
   2457     // This logic is nearly identical to the logic below in `updateDeclDebugInfoAllocation` for
   2458     // `TextBlock` and the .debug_info. If you are editing this logic, you
   2459     // probably need to edit that logic too.
   2460 
   2461     const debug_line_sect = &self.sections.items[self.debug_line_section_index.?];
   2462     const src_fn = &decl.fn_link.elf;
   2463     src_fn.len = @intCast(u32, dbg_line_buffer.items.len);
   2464     if (self.dbg_line_fn_last) |last| not_first: {
   2465         if (src_fn.next) |next| {
   2466             // Update existing function - non-last item.
   2467             if (src_fn.off + src_fn.len + min_nop_size > next.off) {
   2468                 // It grew too big, so we move it to a new location.
   2469                 if (src_fn.prev) |prev| {
   2470                     self.dbg_line_fn_free_list.put(self.base.allocator, prev, {}) catch {};
   2471                     prev.next = src_fn.next;
   2472                 }
   2473                 assert(src_fn.prev != next);
   2474                 next.prev = src_fn.prev;
   2475                 src_fn.next = null;
   2476                 // Populate where it used to be with NOPs.
   2477                 const file_pos = debug_line_sect.sh_offset + src_fn.off;
   2478                 try self.pwriteDbgLineNops(0, &[0]u8{}, src_fn.len, file_pos);
   2479                 // TODO Look at the free list before appending at the end.
   2480                 src_fn.prev = last;
   2481                 last.next = src_fn;
   2482                 self.dbg_line_fn_last = src_fn;
   2483 
   2484                 src_fn.off = last.off + padToIdeal(last.len);
   2485             }
   2486         } else if (src_fn.prev == null) {
   2487             if (src_fn == last) {
   2488                 // Special case: there is only 1 function and it is being updated.
   2489                 // In this case there is nothing to do. The function's length has
   2490                 // already been updated, and the logic below takes care of
   2491                 // resizing the .debug_line section.
   2492                 break :not_first;
   2493             }
   2494             // Append new function.
   2495             // TODO Look at the free list before appending at the end.
   2496             src_fn.prev = last;
   2497             last.next = src_fn;
   2498             self.dbg_line_fn_last = src_fn;
   2499 
   2500             src_fn.off = last.off + padToIdeal(last.len);
   2501         }
   2502     } else {
   2503         // This is the first function of the Line Number Program.
   2504         self.dbg_line_fn_first = src_fn;
   2505         self.dbg_line_fn_last = src_fn;
   2506 
   2507         src_fn.off = padToIdeal(self.dbgLineNeededHeaderBytes());
   2508     }
   2509 
   2510     const last_src_fn = self.dbg_line_fn_last.?;
   2511     const needed_size = last_src_fn.off + last_src_fn.len;
   2512     if (needed_size != debug_line_sect.sh_size) {
   2513         if (needed_size > self.allocatedSize(debug_line_sect.sh_offset)) {
   2514             const new_offset = self.findFreeSpace(needed_size, 1);
   2515             const existing_size = last_src_fn.off;
   2516             log.debug("moving .debug_line section: {d} bytes from 0x{x} to 0x{x}", .{
   2517                 existing_size,
   2518                 debug_line_sect.sh_offset,
   2519                 new_offset,
   2520             });
   2521             const amt = try self.base.file.?.copyRangeAll(debug_line_sect.sh_offset, self.base.file.?, new_offset, existing_size);
   2522             if (amt != existing_size) return error.InputOutput;
   2523             debug_line_sect.sh_offset = new_offset;
   2524         }
   2525         debug_line_sect.sh_size = needed_size;
   2526         self.shdr_table_dirty = true; // TODO look into making only the one section dirty
   2527         self.debug_line_header_dirty = true;
   2528     }
   2529     const prev_padding_size: u32 = if (src_fn.prev) |prev| src_fn.off - (prev.off + prev.len) else 0;
   2530     const next_padding_size: u32 = if (src_fn.next) |next| next.off - (src_fn.off + src_fn.len) else 0;
   2531 
   2532     // We only have support for one compilation unit so far, so the offsets are directly
   2533     // from the .debug_line section.
   2534     const file_pos = debug_line_sect.sh_offset + src_fn.off;
   2535     try self.pwriteDbgLineNops(prev_padding_size, dbg_line_buffer.items, next_padding_size, file_pos);
   2536 
   2537     // .debug_info - End the TAG.subprogram children.
   2538     try dbg_info_buffer.append(0);
   2539 
   2540     return self.finishUpdateDecl(module, decl, &dbg_info_type_relocs, &dbg_info_buffer);
   2541 }
   2542 
   2543 pub fn updateDecl(self: *Elf, module: *Module, decl: *Module.Decl) !void {
   2544     if (build_options.skip_non_native and builtin.object_format != .elf) {
   2545         @panic("Attempted to compile for object format that was disabled by build configuration");
   2546     }
   2547     if (build_options.have_llvm) {
   2548         if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(module, decl);
   2549     }
   2550 
   2551     const tracy = trace(@src());
   2552     defer tracy.end();
   2553 
   2554     if (decl.val.tag() == .extern_fn) {
   2555         return; // TODO Should we do more when front-end analyzed extern decl?
   2556     }
   2557     if (decl.val.castTag(.variable)) |payload| {
   2558         const variable = payload.data;
   2559         if (variable.is_extern) {
   2560             return; // TODO Should we do more when front-end analyzed extern decl?
   2561         }
   2562     }
   2563 
   2564     var code_buffer = std.ArrayList(u8).init(self.base.allocator);
   2565     defer code_buffer.deinit();
   2566 
   2567     var dbg_line_buffer = std.ArrayList(u8).init(self.base.allocator);
   2568     defer dbg_line_buffer.deinit();
   2569 
   2570     var dbg_info_buffer = std.ArrayList(u8).init(self.base.allocator);
   2571     defer dbg_info_buffer.deinit();
   2572 
   2573     var dbg_info_type_relocs: File.DbgInfoTypeRelocsTable = .{};
   2574     defer deinitRelocs(self.base.allocator, &dbg_info_type_relocs);
   2575 
   2576     // TODO implement .debug_info for global variables
   2577     const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val;
   2578     const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
   2579         .ty = decl.ty,
   2580         .val = decl_val,
   2581     }, &code_buffer, .{
   2582         .dwarf = .{
   2583             .dbg_line = &dbg_line_buffer,
   2584             .dbg_info = &dbg_info_buffer,
   2585             .dbg_info_type_relocs = &dbg_info_type_relocs,
   2586         },
   2587     });
   2588     const code = switch (res) {
   2589         .externally_managed => |x| x,
   2590         .appended => code_buffer.items,
   2591         .fail => |em| {
   2592             decl.analysis = .codegen_failure;
   2593             try module.failed_decls.put(module.gpa, decl, em);
   2594             return;
   2595         },
   2596     };
   2597 
   2598     _ = try self.updateDeclCode(decl, code, elf.STT_OBJECT);
   2599     return self.finishUpdateDecl(module, decl, &dbg_info_type_relocs, &dbg_info_buffer);
   2600 }
   2601 
   2602 /// Asserts the type has codegen bits.
   2603 fn addDbgInfoType(self: *Elf, ty: Type, dbg_info_buffer: *std.ArrayList(u8)) !void {
   2604     switch (ty.zigTypeTag()) {
   2605         .Void => unreachable,
   2606         .NoReturn => unreachable,
   2607         .Bool => {
   2608             try dbg_info_buffer.appendSlice(&[_]u8{
   2609                 abbrev_base_type,
   2610                 DW.ATE.boolean, // DW.AT.encoding ,  DW.FORM.data1
   2611                 1, // DW.AT.byte_size,  DW.FORM.data1
   2612                 'b', 'o', 'o', 'l', 0, // DW.AT.name,  DW.FORM.string
   2613             });
   2614         },
   2615         .Int => {
   2616             const info = ty.intInfo(self.base.options.target);
   2617             try dbg_info_buffer.ensureUnusedCapacity(12);
   2618             dbg_info_buffer.appendAssumeCapacity(abbrev_base_type);
   2619             // DW.AT.encoding, DW.FORM.data1
   2620             dbg_info_buffer.appendAssumeCapacity(switch (info.signedness) {
   2621                 .signed => DW.ATE.signed,
   2622                 .unsigned => DW.ATE.unsigned,
   2623             });
   2624             // DW.AT.byte_size,  DW.FORM.data1
   2625             dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(self.base.options.target)));
   2626             // DW.AT.name,  DW.FORM.string
   2627             try dbg_info_buffer.writer().print("{}\x00", .{ty});
   2628         },
   2629         .Optional => {
   2630             if (ty.isPtrLikeOptional()) {
   2631                 try dbg_info_buffer.ensureUnusedCapacity(12);
   2632                 dbg_info_buffer.appendAssumeCapacity(abbrev_base_type);
   2633                 // DW.AT.encoding, DW.FORM.data1
   2634                 dbg_info_buffer.appendAssumeCapacity(DW.ATE.address);
   2635                 // DW.AT.byte_size,  DW.FORM.data1
   2636                 dbg_info_buffer.appendAssumeCapacity(@intCast(u8, ty.abiSize(self.base.options.target)));
   2637                 // DW.AT.name,  DW.FORM.string
   2638                 try dbg_info_buffer.writer().print("{}\x00", .{ty});
   2639             } else {
   2640                 log.debug("TODO implement .debug_info for type '{}'", .{ty});
   2641                 try dbg_info_buffer.append(abbrev_pad1);
   2642             }
   2643         },
   2644         else => {
   2645             log.debug("TODO implement .debug_info for type '{}'", .{ty});
   2646             try dbg_info_buffer.append(abbrev_pad1);
   2647         },
   2648     }
   2649 }
   2650 
   2651 fn updateDeclDebugInfoAllocation(self: *Elf, text_block: *TextBlock, len: u32) !void {
   2652     const tracy = trace(@src());
   2653     defer tracy.end();
   2654 
   2655     // This logic is nearly identical to the logic above in `updateDecl` for
   2656     // `SrcFn` and the line number programs. If you are editing this logic, you
   2657     // probably need to edit that logic too.
   2658 
   2659     const debug_info_sect = &self.sections.items[self.debug_info_section_index.?];
   2660     text_block.dbg_info_len = len;
   2661     if (self.dbg_info_decl_last) |last| not_first: {
   2662         if (text_block.dbg_info_next) |next| {
   2663             // Update existing Decl - non-last item.
   2664             if (text_block.dbg_info_off + text_block.dbg_info_len + min_nop_size > next.dbg_info_off) {
   2665                 // It grew too big, so we move it to a new location.
   2666                 if (text_block.dbg_info_prev) |prev| {
   2667                     self.dbg_info_decl_free_list.put(self.base.allocator, prev, {}) catch {};
   2668                     prev.dbg_info_next = text_block.dbg_info_next;
   2669                 }
   2670                 next.dbg_info_prev = text_block.dbg_info_prev;
   2671                 text_block.dbg_info_next = null;
   2672                 // Populate where it used to be with NOPs.
   2673                 const file_pos = debug_info_sect.sh_offset + text_block.dbg_info_off;
   2674                 try self.pwriteDbgInfoNops(0, &[0]u8{}, text_block.dbg_info_len, false, file_pos);
   2675                 // TODO Look at the free list before appending at the end.
   2676                 text_block.dbg_info_prev = last;
   2677                 last.dbg_info_next = text_block;
   2678                 self.dbg_info_decl_last = text_block;
   2679 
   2680                 text_block.dbg_info_off = last.dbg_info_off + padToIdeal(last.dbg_info_len);
   2681             }
   2682         } else if (text_block.dbg_info_prev == null) {
   2683             if (text_block == last) {
   2684                 // Special case: there is only 1 .debug_info block and it is being updated.
   2685                 // In this case there is nothing to do. The block's length has
   2686                 // already been updated, and logic in writeDeclDebugInfo takes care of
   2687                 // resizing the .debug_info section.
   2688                 break :not_first;
   2689             }
   2690             // Append new Decl.
   2691             // TODO Look at the free list before appending at the end.
   2692             text_block.dbg_info_prev = last;
   2693             last.dbg_info_next = text_block;
   2694             self.dbg_info_decl_last = text_block;
   2695 
   2696             text_block.dbg_info_off = last.dbg_info_off + padToIdeal(last.dbg_info_len);
   2697         }
   2698     } else {
   2699         // This is the first Decl of the .debug_info
   2700         self.dbg_info_decl_first = text_block;
   2701         self.dbg_info_decl_last = text_block;
   2702 
   2703         text_block.dbg_info_off = padToIdeal(self.dbgInfoNeededHeaderBytes());
   2704     }
   2705 }
   2706 
   2707 fn writeDeclDebugInfo(self: *Elf, text_block: *TextBlock, dbg_info_buf: []const u8) !void {
   2708     const tracy = trace(@src());
   2709     defer tracy.end();
   2710 
   2711     // This logic is nearly identical to the logic above in `updateDecl` for
   2712     // `SrcFn` and the line number programs. If you are editing this logic, you
   2713     // probably need to edit that logic too.
   2714 
   2715     const debug_info_sect = &self.sections.items[self.debug_info_section_index.?];
   2716 
   2717     const last_decl = self.dbg_info_decl_last.?;
   2718     // +1 for a trailing zero to end the children of the decl tag.
   2719     const needed_size = last_decl.dbg_info_off + last_decl.dbg_info_len + 1;
   2720     if (needed_size != debug_info_sect.sh_size) {
   2721         if (needed_size > self.allocatedSize(debug_info_sect.sh_offset)) {
   2722             const new_offset = self.findFreeSpace(needed_size, 1);
   2723             const existing_size = last_decl.dbg_info_off;
   2724             log.debug("moving .debug_info section: {} bytes from 0x{x} to 0x{x}", .{
   2725                 existing_size,
   2726                 debug_info_sect.sh_offset,
   2727                 new_offset,
   2728             });
   2729             const amt = try self.base.file.?.copyRangeAll(debug_info_sect.sh_offset, self.base.file.?, new_offset, existing_size);
   2730             if (amt != existing_size) return error.InputOutput;
   2731             debug_info_sect.sh_offset = new_offset;
   2732         }
   2733         debug_info_sect.sh_size = needed_size;
   2734         self.shdr_table_dirty = true; // TODO look into making only the one section dirty
   2735         self.debug_info_header_dirty = true;
   2736     }
   2737     const prev_padding_size: u32 = if (text_block.dbg_info_prev) |prev|
   2738         text_block.dbg_info_off - (prev.dbg_info_off + prev.dbg_info_len)
   2739     else
   2740         0;
   2741     const next_padding_size: u32 = if (text_block.dbg_info_next) |next|
   2742         next.dbg_info_off - (text_block.dbg_info_off + text_block.dbg_info_len)
   2743     else
   2744         0;
   2745 
   2746     // To end the children of the decl tag.
   2747     const trailing_zero = text_block.dbg_info_next == null;
   2748 
   2749     // We only have support for one compilation unit so far, so the offsets are directly
   2750     // from the .debug_info section.
   2751     const file_pos = debug_info_sect.sh_offset + text_block.dbg_info_off;
   2752     try self.pwriteDbgInfoNops(prev_padding_size, dbg_info_buf, next_padding_size, trailing_zero, file_pos);
   2753 }
   2754 
   2755 pub fn updateDeclExports(
   2756     self: *Elf,
   2757     module: *Module,
   2758     decl: *Module.Decl,
   2759     exports: []const *Module.Export,
   2760 ) !void {
   2761     if (build_options.skip_non_native and builtin.object_format != .elf) {
   2762         @panic("Attempted to compile for object format that was disabled by build configuration");
   2763     }
   2764     if (build_options.have_llvm) {
   2765         if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(module, decl, exports);
   2766     }
   2767 
   2768     const tracy = trace(@src());
   2769     defer tracy.end();
   2770 
   2771     try self.global_symbols.ensureUnusedCapacity(self.base.allocator, exports.len);
   2772     if (decl.link.elf.local_sym_index == 0) return;
   2773     const decl_sym = self.local_symbols.items[decl.link.elf.local_sym_index];
   2774 
   2775     for (exports) |exp| {
   2776         if (exp.options.section) |section_name| {
   2777             if (!mem.eql(u8, section_name, ".text")) {
   2778                 try module.failed_exports.ensureUnusedCapacity(module.gpa, 1);
   2779                 module.failed_exports.putAssumeCapacityNoClobber(
   2780                     exp,
   2781                     try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: ExportOptions.section", .{}),
   2782                 );
   2783                 continue;
   2784             }
   2785         }
   2786         const stb_bits: u8 = switch (exp.options.linkage) {
   2787             .Internal => elf.STB_LOCAL,
   2788             .Strong => blk: {
   2789                 if (mem.eql(u8, exp.options.name, "_start")) {
   2790                     self.entry_addr = decl_sym.st_value;
   2791                 }
   2792                 break :blk elf.STB_GLOBAL;
   2793             },
   2794             .Weak => elf.STB_WEAK,
   2795             .LinkOnce => {
   2796                 try module.failed_exports.ensureUnusedCapacity(module.gpa, 1);
   2797                 module.failed_exports.putAssumeCapacityNoClobber(
   2798                     exp,
   2799                     try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: GlobalLinkage.LinkOnce", .{}),
   2800                 );
   2801                 continue;
   2802             },
   2803         };
   2804         const stt_bits: u8 = @truncate(u4, decl_sym.st_info);
   2805         if (exp.link.elf.sym_index) |i| {
   2806             const sym = &self.global_symbols.items[i];
   2807             sym.* = .{
   2808                 .st_name = try self.updateString(sym.st_name, exp.options.name),
   2809                 .st_info = (stb_bits << 4) | stt_bits,
   2810                 .st_other = 0,
   2811                 .st_shndx = self.text_section_index.?,
   2812                 .st_value = decl_sym.st_value,
   2813                 .st_size = decl_sym.st_size,
   2814             };
   2815         } else {
   2816             const name = try self.makeString(exp.options.name);
   2817             const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: {
   2818                 _ = self.global_symbols.addOneAssumeCapacity();
   2819                 break :blk self.global_symbols.items.len - 1;
   2820             };
   2821             self.global_symbols.items[i] = .{
   2822                 .st_name = name,
   2823                 .st_info = (stb_bits << 4) | stt_bits,
   2824                 .st_other = 0,
   2825                 .st_shndx = self.text_section_index.?,
   2826                 .st_value = decl_sym.st_value,
   2827                 .st_size = decl_sym.st_size,
   2828             };
   2829 
   2830             exp.link.elf.sym_index = @intCast(u32, i);
   2831         }
   2832     }
   2833 }
   2834 
   2835 /// Must be called only after a successful call to `updateDecl`.
   2836 pub fn updateDeclLineNumber(self: *Elf, module: *Module, decl: *const Module.Decl) !void {
   2837     _ = module;
   2838     const tracy = trace(@src());
   2839     defer tracy.end();
   2840 
   2841     if (self.llvm_object) |_| return;
   2842 
   2843     const func = decl.val.castTag(.function).?.data;
   2844     const casted_line_off = @intCast(u28, decl.src_line + func.lbrace_line);
   2845 
   2846     const shdr = &self.sections.items[self.debug_line_section_index.?];
   2847     const file_pos = shdr.sh_offset + decl.fn_link.elf.off + self.getRelocDbgLineOff();
   2848     var data: [4]u8 = undefined;
   2849     leb128.writeUnsignedFixed(4, &data, casted_line_off);
   2850     try self.base.file.?.pwriteAll(&data, file_pos);
   2851 }
   2852 
   2853 pub fn deleteExport(self: *Elf, exp: Export) void {
   2854     if (self.llvm_object) |_| return;
   2855 
   2856     const sym_index = exp.sym_index orelse return;
   2857     self.global_symbol_free_list.append(self.base.allocator, sym_index) catch {};
   2858     self.global_symbols.items[sym_index].st_info = 0;
   2859 }
   2860 
   2861 fn writeProgHeader(self: *Elf, index: usize) !void {
   2862     const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian();
   2863     const offset = self.program_headers.items[index].p_offset;
   2864     switch (self.ptr_width) {
   2865         .p32 => {
   2866             var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])};
   2867             if (foreign_endian) {
   2868                 mem.bswapAllFields(elf.Elf32_Phdr, &phdr[0]);
   2869             }
   2870             return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
   2871         },
   2872         .p64 => {
   2873             var phdr = [1]elf.Elf64_Phdr{self.program_headers.items[index]};
   2874             if (foreign_endian) {
   2875                 mem.bswapAllFields(elf.Elf64_Phdr, &phdr[0]);
   2876             }
   2877             return self.base.file.?.pwriteAll(mem.sliceAsBytes(&phdr), offset);
   2878         },
   2879     }
   2880 }
   2881 
   2882 fn writeSectHeader(self: *Elf, index: usize) !void {
   2883     const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian();
   2884     switch (self.ptr_width) {
   2885         .p32 => {
   2886             var shdr: [1]elf.Elf32_Shdr = undefined;
   2887             shdr[0] = sectHeaderTo32(self.sections.items[index]);
   2888             if (foreign_endian) {
   2889                 mem.bswapAllFields(elf.Elf32_Shdr, &shdr[0]);
   2890             }
   2891             const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf32_Shdr);
   2892             return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
   2893         },
   2894         .p64 => {
   2895             var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]};
   2896             if (foreign_endian) {
   2897                 mem.bswapAllFields(elf.Elf64_Shdr, &shdr[0]);
   2898             }
   2899             const offset = self.shdr_table_offset.? + index * @sizeOf(elf.Elf64_Shdr);
   2900             return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
   2901         },
   2902     }
   2903 }
   2904 
   2905 fn writeOffsetTableEntry(self: *Elf, index: usize) !void {
   2906     const shdr = &self.sections.items[self.got_section_index.?];
   2907     const phdr = &self.program_headers.items[self.phdr_got_index.?];
   2908     const entry_size: u16 = self.archPtrWidthBytes();
   2909     if (self.offset_table_count_dirty) {
   2910         // TODO Also detect virtual address collisions.
   2911         const allocated_size = self.allocatedSize(shdr.sh_offset);
   2912         const needed_size = self.offset_table.items.len * entry_size;
   2913         if (needed_size > allocated_size) {
   2914             // Must move the entire got section.
   2915             const new_offset = self.findFreeSpace(needed_size, entry_size);
   2916             const amt = try self.base.file.?.copyRangeAll(shdr.sh_offset, self.base.file.?, new_offset, shdr.sh_size);
   2917             if (amt != shdr.sh_size) return error.InputOutput;
   2918             shdr.sh_offset = new_offset;
   2919             phdr.p_offset = new_offset;
   2920         }
   2921         shdr.sh_size = needed_size;
   2922         phdr.p_memsz = needed_size;
   2923         phdr.p_filesz = needed_size;
   2924 
   2925         self.shdr_table_dirty = true; // TODO look into making only the one section dirty
   2926         self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
   2927 
   2928         self.offset_table_count_dirty = false;
   2929     }
   2930     const endian = self.base.options.target.cpu.arch.endian();
   2931     const off = shdr.sh_offset + @as(u64, entry_size) * index;
   2932     switch (entry_size) {
   2933         2 => {
   2934             var buf: [2]u8 = undefined;
   2935             mem.writeInt(u16, &buf, @intCast(u16, self.offset_table.items[index]), endian);
   2936             try self.base.file.?.pwriteAll(&buf, off);
   2937         },
   2938         4 => {
   2939             var buf: [4]u8 = undefined;
   2940             mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);
   2941             try self.base.file.?.pwriteAll(&buf, off);
   2942         },
   2943         8 => {
   2944             var buf: [8]u8 = undefined;
   2945             mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
   2946             try self.base.file.?.pwriteAll(&buf, off);
   2947         },
   2948         else => unreachable,
   2949     }
   2950 }
   2951 
   2952 fn writeSymbol(self: *Elf, index: usize) !void {
   2953     const tracy = trace(@src());
   2954     defer tracy.end();
   2955 
   2956     const syms_sect = &self.sections.items[self.symtab_section_index.?];
   2957     // Make sure we are not pointlessly writing symbol data that will have to get relocated
   2958     // due to running out of space.
   2959     if (self.local_symbols.items.len != syms_sect.sh_info) {
   2960         const sym_size: u64 = switch (self.ptr_width) {
   2961             .p32 => @sizeOf(elf.Elf32_Sym),
   2962             .p64 => @sizeOf(elf.Elf64_Sym),
   2963         };
   2964         const sym_align: u16 = switch (self.ptr_width) {
   2965             .p32 => @alignOf(elf.Elf32_Sym),
   2966             .p64 => @alignOf(elf.Elf64_Sym),
   2967         };
   2968         const needed_size = (self.local_symbols.items.len + self.global_symbols.items.len) * sym_size;
   2969         if (needed_size > self.allocatedSize(syms_sect.sh_offset)) {
   2970             // Move all the symbols to a new file location.
   2971             const new_offset = self.findFreeSpace(needed_size, sym_align);
   2972             const existing_size = @as(u64, syms_sect.sh_info) * sym_size;
   2973             const amt = try self.base.file.?.copyRangeAll(syms_sect.sh_offset, self.base.file.?, new_offset, existing_size);
   2974             if (amt != existing_size) return error.InputOutput;
   2975             syms_sect.sh_offset = new_offset;
   2976         }
   2977         syms_sect.sh_info = @intCast(u32, self.local_symbols.items.len);
   2978         syms_sect.sh_size = needed_size; // anticipating adding the global symbols later
   2979         self.shdr_table_dirty = true; // TODO look into only writing one section
   2980     }
   2981     const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian();
   2982     switch (self.ptr_width) {
   2983         .p32 => {
   2984             var sym = [1]elf.Elf32_Sym{
   2985                 .{
   2986                     .st_name = self.local_symbols.items[index].st_name,
   2987                     .st_value = @intCast(u32, self.local_symbols.items[index].st_value),
   2988                     .st_size = @intCast(u32, self.local_symbols.items[index].st_size),
   2989                     .st_info = self.local_symbols.items[index].st_info,
   2990                     .st_other = self.local_symbols.items[index].st_other,
   2991                     .st_shndx = self.local_symbols.items[index].st_shndx,
   2992                 },
   2993             };
   2994             if (foreign_endian) {
   2995                 mem.bswapAllFields(elf.Elf32_Sym, &sym[0]);
   2996             }
   2997             const off = syms_sect.sh_offset + @sizeOf(elf.Elf32_Sym) * index;
   2998             try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
   2999         },
   3000         .p64 => {
   3001             var sym = [1]elf.Elf64_Sym{self.local_symbols.items[index]};
   3002             if (foreign_endian) {
   3003                 mem.bswapAllFields(elf.Elf64_Sym, &sym[0]);
   3004             }
   3005             const off = syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index;
   3006             try self.base.file.?.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
   3007         },
   3008     }
   3009 }
   3010 
   3011 fn writeAllGlobalSymbols(self: *Elf) !void {
   3012     const syms_sect = &self.sections.items[self.symtab_section_index.?];
   3013     const sym_size: u64 = switch (self.ptr_width) {
   3014         .p32 => @sizeOf(elf.Elf32_Sym),
   3015         .p64 => @sizeOf(elf.Elf64_Sym),
   3016     };
   3017     const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian();
   3018     const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size;
   3019     switch (self.ptr_width) {
   3020         .p32 => {
   3021             const buf = try self.base.allocator.alloc(elf.Elf32_Sym, self.global_symbols.items.len);
   3022             defer self.base.allocator.free(buf);
   3023 
   3024             for (buf) |*sym, i| {
   3025                 sym.* = .{
   3026                     .st_name = self.global_symbols.items[i].st_name,
   3027                     .st_value = @intCast(u32, self.global_symbols.items[i].st_value),
   3028                     .st_size = @intCast(u32, self.global_symbols.items[i].st_size),
   3029                     .st_info = self.global_symbols.items[i].st_info,
   3030                     .st_other = self.global_symbols.items[i].st_other,
   3031                     .st_shndx = self.global_symbols.items[i].st_shndx,
   3032                 };
   3033                 if (foreign_endian) {
   3034                     mem.bswapAllFields(elf.Elf32_Sym, sym);
   3035                 }
   3036             }
   3037             try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
   3038         },
   3039         .p64 => {
   3040             const buf = try self.base.allocator.alloc(elf.Elf64_Sym, self.global_symbols.items.len);
   3041             defer self.base.allocator.free(buf);
   3042 
   3043             for (buf) |*sym, i| {
   3044                 sym.* = .{
   3045                     .st_name = self.global_symbols.items[i].st_name,
   3046                     .st_value = self.global_symbols.items[i].st_value,
   3047                     .st_size = self.global_symbols.items[i].st_size,
   3048                     .st_info = self.global_symbols.items[i].st_info,
   3049                     .st_other = self.global_symbols.items[i].st_other,
   3050                     .st_shndx = self.global_symbols.items[i].st_shndx,
   3051                 };
   3052                 if (foreign_endian) {
   3053                     mem.bswapAllFields(elf.Elf64_Sym, sym);
   3054                 }
   3055             }
   3056             try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
   3057         },
   3058     }
   3059 }
   3060 
   3061 /// Always 4 or 8 depending on whether this is 32-bit ELF or 64-bit ELF.
   3062 fn ptrWidthBytes(self: Elf) u8 {
   3063     return switch (self.ptr_width) {
   3064         .p32 => 4,
   3065         .p64 => 8,
   3066     };
   3067 }
   3068 
   3069 /// Does not necessarily match `ptrWidthBytes` for example can be 2 bytes
   3070 /// in a 32-bit ELF file.
   3071 fn archPtrWidthBytes(self: Elf) u8 {
   3072     return @intCast(u8, self.base.options.target.cpu.arch.ptrBitWidth() / 8);
   3073 }
   3074 
   3075 /// The reloc offset for the virtual address of a function in its Line Number Program.
   3076 /// Size is a virtual address integer.
   3077 const dbg_line_vaddr_reloc_index = 3;
   3078 /// The reloc offset for the virtual address of a function in its .debug_info TAG.subprogram.
   3079 /// Size is a virtual address integer.
   3080 const dbg_info_low_pc_reloc_index = 1;
   3081 
   3082 /// The reloc offset for the line offset of a function from the previous function's line.
   3083 /// It's a fixed-size 4-byte ULEB128.
   3084 fn getRelocDbgLineOff(self: Elf) usize {
   3085     return dbg_line_vaddr_reloc_index + self.ptrWidthBytes() + 1;
   3086 }
   3087 
   3088 fn getRelocDbgFileIndex(self: Elf) usize {
   3089     return self.getRelocDbgLineOff() + 5;
   3090 }
   3091 
   3092 fn getRelocDbgInfoSubprogramHighPC(self: Elf) u32 {
   3093     return dbg_info_low_pc_reloc_index + self.ptrWidthBytes();
   3094 }
   3095 
   3096 fn dbgLineNeededHeaderBytes(self: Elf) u32 {
   3097     const directory_entry_format_count = 1;
   3098     const file_name_entry_format_count = 1;
   3099     const directory_count = 1;
   3100     const file_name_count = 1;
   3101     const root_src_dir_path_len = if (self.base.options.module.?.root_pkg.root_src_directory.path) |p| p.len else 1; // "."
   3102     return @intCast(u32, 53 + directory_entry_format_count * 2 + file_name_entry_format_count * 2 +
   3103         directory_count * 8 + file_name_count * 8 +
   3104         // These are encoded as DW.FORM.string rather than DW.FORM.strp as we would like
   3105         // because of a workaround for readelf and gdb failing to understand DWARFv5 correctly.
   3106         root_src_dir_path_len +
   3107         self.base.options.module.?.root_pkg.root_src_path.len);
   3108 }
   3109 
   3110 fn dbgInfoNeededHeaderBytes(self: Elf) u32 {
   3111     _ = self;
   3112     return 120;
   3113 }
   3114 
   3115 const min_nop_size = 2;
   3116 
   3117 /// Writes to the file a buffer, prefixed and suffixed by the specified number of
   3118 /// bytes of NOPs. Asserts each padding size is at least `min_nop_size` and total padding bytes
   3119 /// are less than 1044480 bytes (if this limit is ever reached, this function can be
   3120 /// improved to make more than one pwritev call, or the limit can be raised by a fixed
   3121 /// amount by increasing the length of `vecs`).
   3122 fn pwriteDbgLineNops(
   3123     self: *Elf,
   3124     prev_padding_size: usize,
   3125     buf: []const u8,
   3126     next_padding_size: usize,
   3127     offset: u64,
   3128 ) !void {
   3129     const tracy = trace(@src());
   3130     defer tracy.end();
   3131 
   3132     const page_of_nops = [1]u8{DW.LNS.negate_stmt} ** 4096;
   3133     const three_byte_nop = [3]u8{ DW.LNS.advance_pc, 0b1000_0000, 0 };
   3134     var vecs: [512]std.os.iovec_const = undefined;
   3135     var vec_index: usize = 0;
   3136     {
   3137         var padding_left = prev_padding_size;
   3138         if (padding_left % 2 != 0) {
   3139             vecs[vec_index] = .{
   3140                 .iov_base = &three_byte_nop,
   3141                 .iov_len = three_byte_nop.len,
   3142             };
   3143             vec_index += 1;
   3144             padding_left -= three_byte_nop.len;
   3145         }
   3146         while (padding_left > page_of_nops.len) {
   3147             vecs[vec_index] = .{
   3148                 .iov_base = &page_of_nops,
   3149                 .iov_len = page_of_nops.len,
   3150             };
   3151             vec_index += 1;
   3152             padding_left -= page_of_nops.len;
   3153         }
   3154         if (padding_left > 0) {
   3155             vecs[vec_index] = .{
   3156                 .iov_base = &page_of_nops,
   3157                 .iov_len = padding_left,
   3158             };
   3159             vec_index += 1;
   3160         }
   3161     }
   3162 
   3163     vecs[vec_index] = .{
   3164         .iov_base = buf.ptr,
   3165         .iov_len = buf.len,
   3166     };
   3167     vec_index += 1;
   3168 
   3169     {
   3170         var padding_left = next_padding_size;
   3171         if (padding_left % 2 != 0) {
   3172             vecs[vec_index] = .{
   3173                 .iov_base = &three_byte_nop,
   3174                 .iov_len = three_byte_nop.len,
   3175             };
   3176             vec_index += 1;
   3177             padding_left -= three_byte_nop.len;
   3178         }
   3179         while (padding_left > page_of_nops.len) {
   3180             vecs[vec_index] = .{
   3181                 .iov_base = &page_of_nops,
   3182                 .iov_len = page_of_nops.len,
   3183             };
   3184             vec_index += 1;
   3185             padding_left -= page_of_nops.len;
   3186         }
   3187         if (padding_left > 0) {
   3188             vecs[vec_index] = .{
   3189                 .iov_base = &page_of_nops,
   3190                 .iov_len = padding_left,
   3191             };
   3192             vec_index += 1;
   3193         }
   3194     }
   3195     try self.base.file.?.pwritevAll(vecs[0..vec_index], offset - prev_padding_size);
   3196 }
   3197 
   3198 /// Writes to the file a buffer, prefixed and suffixed by the specified number of
   3199 /// bytes of padding.
   3200 fn pwriteDbgInfoNops(
   3201     self: *Elf,
   3202     prev_padding_size: usize,
   3203     buf: []const u8,
   3204     next_padding_size: usize,
   3205     trailing_zero: bool,
   3206     offset: u64,
   3207 ) !void {
   3208     const tracy = trace(@src());
   3209     defer tracy.end();
   3210 
   3211     const page_of_nops = [1]u8{abbrev_pad1} ** 4096;
   3212     var vecs: [32]std.os.iovec_const = undefined;
   3213     var vec_index: usize = 0;
   3214     {
   3215         var padding_left = prev_padding_size;
   3216         while (padding_left > page_of_nops.len) {
   3217             vecs[vec_index] = .{
   3218                 .iov_base = &page_of_nops,
   3219                 .iov_len = page_of_nops.len,
   3220             };
   3221             vec_index += 1;
   3222             padding_left -= page_of_nops.len;
   3223         }
   3224         if (padding_left > 0) {
   3225             vecs[vec_index] = .{
   3226                 .iov_base = &page_of_nops,
   3227                 .iov_len = padding_left,
   3228             };
   3229             vec_index += 1;
   3230         }
   3231     }
   3232 
   3233     vecs[vec_index] = .{
   3234         .iov_base = buf.ptr,
   3235         .iov_len = buf.len,
   3236     };
   3237     vec_index += 1;
   3238 
   3239     {
   3240         var padding_left = next_padding_size;
   3241         while (padding_left > page_of_nops.len) {
   3242             vecs[vec_index] = .{
   3243                 .iov_base = &page_of_nops,
   3244                 .iov_len = page_of_nops.len,
   3245             };
   3246             vec_index += 1;
   3247             padding_left -= page_of_nops.len;
   3248         }
   3249         if (padding_left > 0) {
   3250             vecs[vec_index] = .{
   3251                 .iov_base = &page_of_nops,
   3252                 .iov_len = padding_left,
   3253             };
   3254             vec_index += 1;
   3255         }
   3256     }
   3257 
   3258     if (trailing_zero) {
   3259         var zbuf = [1]u8{0};
   3260         vecs[vec_index] = .{
   3261             .iov_base = &zbuf,
   3262             .iov_len = zbuf.len,
   3263         };
   3264         vec_index += 1;
   3265     }
   3266 
   3267     try self.base.file.?.pwritevAll(vecs[0..vec_index], offset - prev_padding_size);
   3268 }
   3269 
   3270 fn progHeaderTo32(phdr: elf.Elf64_Phdr) elf.Elf32_Phdr {
   3271     return .{
   3272         .p_type = phdr.p_type,
   3273         .p_flags = phdr.p_flags,
   3274         .p_offset = @intCast(u32, phdr.p_offset),
   3275         .p_vaddr = @intCast(u32, phdr.p_vaddr),
   3276         .p_paddr = @intCast(u32, phdr.p_paddr),
   3277         .p_filesz = @intCast(u32, phdr.p_filesz),
   3278         .p_memsz = @intCast(u32, phdr.p_memsz),
   3279         .p_align = @intCast(u32, phdr.p_align),
   3280     };
   3281 }
   3282 
   3283 fn sectHeaderTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr {
   3284     return .{
   3285         .sh_name = shdr.sh_name,
   3286         .sh_type = shdr.sh_type,
   3287         .sh_flags = @intCast(u32, shdr.sh_flags),
   3288         .sh_addr = @intCast(u32, shdr.sh_addr),
   3289         .sh_offset = @intCast(u32, shdr.sh_offset),
   3290         .sh_size = @intCast(u32, shdr.sh_size),
   3291         .sh_link = shdr.sh_link,
   3292         .sh_info = shdr.sh_info,
   3293         .sh_addralign = @intCast(u32, shdr.sh_addralign),
   3294         .sh_entsize = @intCast(u32, shdr.sh_entsize),
   3295     };
   3296 }
   3297 
   3298 fn getLDMOption(target: std.Target) ?[]const u8 {
   3299     switch (target.cpu.arch) {
   3300         .i386 => return "elf_i386",
   3301         .aarch64 => return "aarch64linux",
   3302         .aarch64_be => return "aarch64_be_linux",
   3303         .arm, .thumb => return "armelf_linux_eabi",
   3304         .armeb, .thumbeb => return "armebelf_linux_eabi",
   3305         .powerpc => return "elf32ppclinux",
   3306         .powerpc64 => return "elf64ppc",
   3307         .powerpc64le => return "elf64lppc",
   3308         .sparc, .sparcel => return "elf32_sparc",
   3309         .sparcv9 => return "elf64_sparc",
   3310         .mips => return "elf32btsmip",
   3311         .mipsel => return "elf32ltsmip",
   3312         .mips64 => {
   3313             if (target.abi == .gnuabin32) {
   3314                 return "elf32btsmipn32";
   3315             } else {
   3316                 return "elf64btsmip";
   3317             }
   3318         },
   3319         .mips64el => {
   3320             if (target.abi == .gnuabin32) {
   3321                 return "elf32ltsmipn32";
   3322             } else {
   3323                 return "elf64ltsmip";
   3324             }
   3325         },
   3326         .s390x => return "elf64_s390",
   3327         .x86_64 => {
   3328             if (target.abi == .gnux32) {
   3329                 return "elf32_x86_64";
   3330             } else {
   3331                 return "elf_x86_64";
   3332             }
   3333         },
   3334         .riscv32 => return "elf32lriscv",
   3335         .riscv64 => return "elf64lriscv",
   3336         else => return null,
   3337     }
   3338 }
   3339 
   3340 fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
   3341     // TODO https://github.com/ziglang/zig/issues/1284
   3342     return std.math.add(@TypeOf(actual_size), actual_size, actual_size / ideal_factor) catch
   3343         std.math.maxInt(@TypeOf(actual_size));
   3344 }
   3345 
   3346 // Provide a blueprint of csu (c-runtime startup) objects for supported
   3347 // link modes.
   3348 //
   3349 // This is for cross-mode targets only. For host-mode targets the system
   3350 // compiler can be probed to produce a robust blueprint.
   3351 //
   3352 // Targets requiring a libc for which zig does not bundle a libc are
   3353 // host-mode targets. Unfortunately, host-mode probes are not yet
   3354 // implemented. For now the data is hard-coded here. Such targets are
   3355 // { freebsd, netbsd, openbsd, dragonfly }.
   3356 const CsuObjects = struct {
   3357     crt0: ?[]const u8 = null,
   3358     crti: ?[]const u8 = null,
   3359     crtbegin: ?[]const u8 = null,
   3360     crtend: ?[]const u8 = null,
   3361     crtn: ?[]const u8 = null,
   3362 
   3363     fn init(arena: *mem.Allocator, link_options: link.Options, comp: *const Compilation) !CsuObjects {
   3364         // crt objects are only required for libc.
   3365         if (!link_options.link_libc) return CsuObjects{};
   3366 
   3367         var result: CsuObjects = .{};
   3368 
   3369         // TODO: https://github.com/ziglang/zig/issues/4629
   3370         // - use inline enum type
   3371         // - reduce to enum-literals for values
   3372         const Mode = enum {
   3373             dynamic_lib,
   3374             dynamic_exe,
   3375             dynamic_pie,
   3376             static_exe,
   3377             static_pie,
   3378         };
   3379 
   3380         // Flatten crt case types.
   3381         const mode: Mode = switch (link_options.output_mode) {
   3382             .Obj => return CsuObjects{},
   3383             .Lib => switch (link_options.link_mode) {
   3384                 .Dynamic => Mode.dynamic_lib,
   3385                 .Static => return CsuObjects{},
   3386             },
   3387             .Exe => switch (link_options.link_mode) {
   3388                 .Dynamic => if (link_options.pie) Mode.dynamic_pie else Mode.dynamic_exe,
   3389                 .Static => if (link_options.pie) Mode.static_pie else Mode.static_exe,
   3390             },
   3391         };
   3392 
   3393         if (link_options.target.isAndroid()) {
   3394             switch (mode) {
   3395                 // zig fmt: off
   3396                 .dynamic_lib => result.set( null, null, "crtbegin_so.o",      "crtend_so.o",      null ),
   3397                 .dynamic_exe,
   3398                 .dynamic_pie => result.set( null, null, "crtbegin_dynamic.o", "crtend_android.o", null ),
   3399                 .static_exe,
   3400                 .static_pie  => result.set( null, null, "crtbegin_static.o",  "crtend_android.o", null ),
   3401                 // zig fmt: on
   3402             }
   3403         } else {
   3404             switch (link_options.target.os.tag) {
   3405                 .linux => {
   3406                     switch (mode) {
   3407                         // zig fmt: off
   3408                         .dynamic_lib => result.set( null,      "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
   3409                         .dynamic_exe => result.set( "crt1.o",  "crti.o", "crtbegin.o",  "crtend.o",  "crtn.o" ),
   3410                         .dynamic_pie => result.set( "Scrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
   3411                         .static_exe  => result.set( "crt1.o",  "crti.o", "crtbeginT.o", "crtend.o",  "crtn.o" ),
   3412                         .static_pie  => result.set( "rcrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
   3413                         // zig fmt: on
   3414                     }
   3415                     if (link_options.libc_installation) |_| {
   3416                         // hosted-glibc provides crtbegin/end objects in platform/compiler-specific dirs
   3417                         // and they are not known at comptime. For now null-out crtbegin/end objects;
   3418                         // there is no feature loss, zig has never linked those objects in before.
   3419                         // TODO: probe for paths, ie. `cc -print-file-name`
   3420                         result.crtbegin = null;
   3421                         result.crtend = null;
   3422                     } else {
   3423                         // Bundled glibc only has Scrt1.o .
   3424                         if (result.crt0 != null and link_options.target.isGnuLibC()) result.crt0 = "Scrt1.o";
   3425                     }
   3426                 },
   3427                 .dragonfly => switch (mode) {
   3428                     // zig fmt: off
   3429                     .dynamic_lib => result.set( null,      "crti.o", "crtbeginS.o",  "crtendS.o", "crtn.o" ),
   3430                     .dynamic_exe => result.set( "crt1.o",  "crti.o", "crtbegin.o",   "crtend.o",  "crtn.o" ),
   3431                     .dynamic_pie => result.set( "Scrt1.o", "crti.o", "crtbeginS.o",  "crtendS.o", "crtn.o" ),
   3432                     .static_exe  => result.set( "crt1.o",  "crti.o", "crtbegin.o",   "crtend.o",  "crtn.o" ),
   3433                     .static_pie  => result.set( "Scrt1.o", "crti.o", "crtbeginS.o",  "crtendS.o", "crtn.o" ),
   3434                     // zig fmt: on
   3435                 },
   3436                 .freebsd => switch (mode) {
   3437                     // zig fmt: off
   3438                     .dynamic_lib => result.set( null,      "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
   3439                     .dynamic_exe => result.set( "crt1.o",  "crti.o", "crtbegin.o",  "crtend.o",  "crtn.o" ),
   3440                     .dynamic_pie => result.set( "Scrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
   3441                     .static_exe  => result.set( "crt1.o",  "crti.o", "crtbeginT.o", "crtend.o",  "crtn.o" ),
   3442                     .static_pie  => result.set( "Scrt1.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
   3443                     // zig fmt: on
   3444                 },
   3445                 .netbsd => switch (mode) {
   3446                     // zig fmt: off
   3447                     .dynamic_lib => result.set( null,     "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
   3448                     .dynamic_exe => result.set( "crt0.o", "crti.o", "crtbegin.o",  "crtend.o",  "crtn.o" ),
   3449                     .dynamic_pie => result.set( "crt0.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
   3450                     .static_exe  => result.set( "crt0.o", "crti.o", "crtbeginT.o", "crtend.o",  "crtn.o" ),
   3451                     .static_pie  => result.set( "crt0.o", "crti.o", "crtbeginT.o", "crtendS.o", "crtn.o" ),
   3452                     // zig fmt: on
   3453                 },
   3454                 .openbsd => switch (mode) {
   3455                     // zig fmt: off
   3456                     .dynamic_lib => result.set( null,      null, "crtbeginS.o", "crtendS.o", null ),
   3457                     .dynamic_exe,
   3458                     .dynamic_pie => result.set( "crt0.o",  null, "crtbegin.o",  "crtend.o",  null ),
   3459                     .static_exe,
   3460                     .static_pie  => result.set( "rcrt0.o", null, "crtbegin.o",  "crtend.o",  null ),
   3461                     // zig fmt: on
   3462                 },
   3463                 .haiku => switch (mode) {
   3464                     // zig fmt: off
   3465                     .dynamic_lib => result.set( null,          "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
   3466                     .dynamic_exe => result.set( "start_dyn.o", "crti.o", "crtbegin.o",  "crtend.o",  "crtn.o" ),
   3467                     .dynamic_pie => result.set( "start_dyn.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
   3468                     .static_exe  => result.set( "start_dyn.o", "crti.o", "crtbegin.o",  "crtend.o",  "crtn.o" ),
   3469                     .static_pie  => result.set( "start_dyn.o", "crti.o", "crtbeginS.o", "crtendS.o", "crtn.o" ),
   3470                     // zig fmt: on
   3471                 },
   3472                 .solaris => switch (mode) {
   3473                     // zig fmt: off
   3474                     .dynamic_lib => result.set( null,     "crti.o", null, null, "crtn.o" ),
   3475                     .dynamic_exe,
   3476                     .dynamic_pie => result.set( "crt1.o", "crti.o", null, null, "crtn.o" ),
   3477                     .static_exe,
   3478                     .static_pie  => result.set( null,     null,     null, null, null     ),
   3479                     // zig fmt: on
   3480                 },
   3481                 else => {},
   3482             }
   3483         }
   3484 
   3485         // Convert each object to a full pathname.
   3486         if (link_options.libc_installation) |lci| {
   3487             const crt_dir_path = lci.crt_dir orelse return error.LibCInstallationMissingCRTDir;
   3488             switch (link_options.target.os.tag) {
   3489                 .dragonfly => {
   3490                     if (result.crt0) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* });
   3491                     if (result.crti) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* });
   3492                     if (result.crtn) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* });
   3493 
   3494                     var gccv: []const u8 = undefined;
   3495                     if (link_options.target.os.version_range.semver.isAtLeast(.{ .major = 5, .minor = 4 }) orelse true) {
   3496                         gccv = "gcc80";
   3497                     } else {
   3498                         gccv = "gcc54";
   3499                     }
   3500 
   3501                     if (result.crtbegin) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, gccv, obj.* });
   3502                     if (result.crtend) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, gccv, obj.* });
   3503                 },
   3504                 .haiku => {
   3505                     const gcc_dir_path = lci.gcc_dir orelse return error.LibCInstallationMissingCRTDir;
   3506                     if (result.crt0) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* });
   3507                     if (result.crti) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* });
   3508                     if (result.crtn) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* });
   3509 
   3510                     if (result.crtbegin) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ gcc_dir_path, obj.* });
   3511                     if (result.crtend) |*obj| obj.* = try fs.path.join(arena, &[_][]const u8{ gcc_dir_path, obj.* });
   3512                 },
   3513                 else => {
   3514                     inline for (std.meta.fields(@TypeOf(result))) |f| {
   3515                         if (@field(result, f.name)) |*obj| {
   3516                             obj.* = try fs.path.join(arena, &[_][]const u8{ crt_dir_path, obj.* });
   3517                         }
   3518                     }
   3519                 },
   3520             }
   3521         } else {
   3522             inline for (std.meta.fields(@TypeOf(result))) |f| {
   3523                 if (@field(result, f.name)) |*obj| {
   3524                     if (comp.crt_files.get(obj.*)) |crtf| {
   3525                         obj.* = crtf.full_object_path;
   3526                     } else {
   3527                         @field(result, f.name) = null;
   3528                     }
   3529                 }
   3530             }
   3531         }
   3532 
   3533         return result;
   3534     }
   3535 
   3536     fn set(
   3537         self: *CsuObjects,
   3538         crt0: ?[]const u8,
   3539         crti: ?[]const u8,
   3540         crtbegin: ?[]const u8,
   3541         crtend: ?[]const u8,
   3542         crtn: ?[]const u8,
   3543     ) void {
   3544         self.crt0 = crt0;
   3545         self.crti = crti;
   3546         self.crtbegin = crtbegin;
   3547         self.crtend = crtend;
   3548         self.crtn = crtn;
   3549     }
   3550 };