zig

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

blob a01b9cf7 (64381B) - Raw


      1 const Coff = @This();
      2 
      3 const std = @import("std");
      4 const builtin = @import("builtin");
      5 const log = std.log.scoped(.link);
      6 const Allocator = std.mem.Allocator;
      7 const assert = std.debug.assert;
      8 const fs = std.fs;
      9 const allocPrint = std.fmt.allocPrint;
     10 const mem = std.mem;
     11 
     12 const lldMain = @import("../main.zig").lldMain;
     13 const trace = @import("../tracy.zig").trace;
     14 const Module = @import("../Module.zig");
     15 const Compilation = @import("../Compilation.zig");
     16 const codegen = @import("../codegen.zig");
     17 const link = @import("../link.zig");
     18 const build_options = @import("build_options");
     19 const Cache = @import("../Cache.zig");
     20 const mingw = @import("../mingw.zig");
     21 const Air = @import("../Air.zig");
     22 const Liveness = @import("../Liveness.zig");
     23 const LlvmObject = @import("../codegen/llvm.zig").Object;
     24 const TypedValue = @import("../TypedValue.zig");
     25 
     26 const allocation_padding = 4 / 3;
     27 const minimum_text_block_size = 64 * allocation_padding;
     28 
     29 const section_alignment = 4096;
     30 const file_alignment = 512;
     31 const default_image_base = 0x400_000;
     32 const section_table_size = 2 * 40;
     33 comptime {
     34     assert(mem.isAligned(default_image_base, section_alignment));
     35 }
     36 
     37 pub const base_tag: link.File.Tag = .coff;
     38 
     39 const msdos_stub = @embedFile("msdos-stub.bin");
     40 
     41 /// If this is not null, an object file is created by LLVM and linked with LLD afterwards.
     42 llvm_object: ?*LlvmObject = null,
     43 
     44 base: link.File,
     45 ptr_width: PtrWidth,
     46 error_flags: link.File.ErrorFlags = .{},
     47 
     48 text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{},
     49 last_text_block: ?*TextBlock = null,
     50 
     51 /// Section table file pointer.
     52 section_table_offset: u32 = 0,
     53 /// Section data file pointer.
     54 section_data_offset: u32 = 0,
     55 /// Optional header file pointer.
     56 optional_header_offset: u32 = 0,
     57 
     58 /// Absolute virtual address of the offset table when the executable is loaded in memory.
     59 offset_table_virtual_address: u32 = 0,
     60 /// Current size of the offset table on disk, must be a multiple of `file_alignment`
     61 offset_table_size: u32 = 0,
     62 /// Contains absolute virtual addresses
     63 offset_table: std.ArrayListUnmanaged(u64) = .{},
     64 /// Free list of offset table indices
     65 offset_table_free_list: std.ArrayListUnmanaged(u32) = .{},
     66 
     67 /// Virtual address of the entry point procedure relative to image base.
     68 entry_addr: ?u32 = null,
     69 
     70 /// Absolute virtual address of the text section when the executable is loaded in memory.
     71 text_section_virtual_address: u32 = 0,
     72 /// Current size of the `.text` section on disk, must be a multiple of `file_alignment`
     73 text_section_size: u32 = 0,
     74 
     75 offset_table_size_dirty: bool = false,
     76 text_section_size_dirty: bool = false,
     77 /// This flag is set when the virtual size of the whole image file when loaded in memory has changed
     78 /// and needs to be updated in the optional header.
     79 size_of_image_dirty: bool = false,
     80 
     81 pub const PtrWidth = enum { p32, p64 };
     82 
     83 pub const TextBlock = struct {
     84     /// Offset of the code relative to the start of the text section
     85     text_offset: u32,
     86     /// Used size of the text block
     87     size: u32,
     88     /// This field is undefined for symbols with size = 0.
     89     offset_table_index: u32,
     90     /// Points to the previous and next neighbors, based on the `text_offset`.
     91     /// This can be used to find, for example, the capacity of this `TextBlock`.
     92     prev: ?*TextBlock,
     93     next: ?*TextBlock,
     94 
     95     pub const empty = TextBlock{
     96         .text_offset = 0,
     97         .size = 0,
     98         .offset_table_index = undefined,
     99         .prev = null,
    100         .next = null,
    101     };
    102 
    103     /// Returns how much room there is to grow in virtual address space.
    104     fn capacity(self: TextBlock) u64 {
    105         if (self.next) |next| {
    106             return next.text_offset - self.text_offset;
    107         }
    108         // This is the last block, the capacity is only limited by the address space.
    109         return std.math.maxInt(u32) - self.text_offset;
    110     }
    111 
    112     fn freeListEligible(self: TextBlock) bool {
    113         // No need to keep a free list node for the last block.
    114         const next = self.next orelse return false;
    115         const cap = next.text_offset - self.text_offset;
    116         const ideal_cap = self.size * allocation_padding;
    117         if (cap <= ideal_cap) return false;
    118         const surplus = cap - ideal_cap;
    119         return surplus >= minimum_text_block_size;
    120     }
    121 
    122     /// Absolute virtual address of the text block when the file is loaded in memory.
    123     fn getVAddr(self: TextBlock, coff: Coff) u32 {
    124         return coff.text_section_virtual_address + self.text_offset;
    125     }
    126 };
    127 
    128 pub const SrcFn = void;
    129 
    130 pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Coff {
    131     assert(options.target.ofmt == .coff);
    132 
    133     if (build_options.have_llvm and options.use_llvm) {
    134         return createEmpty(allocator, options);
    135     }
    136 
    137     const self = try createEmpty(allocator, options);
    138     errdefer self.base.destroy();
    139 
    140     const file = try options.emit.?.directory.handle.createFile(sub_path, .{
    141         .truncate = false,
    142         .read = true,
    143         .mode = link.determineMode(options),
    144     });
    145     self.base.file = file;
    146 
    147     // TODO Write object specific relocations, COFF symbol table, then enable object file output.
    148     switch (options.output_mode) {
    149         .Exe => {},
    150         .Obj => return error.TODOImplementWritingObjFiles,
    151         .Lib => return error.TODOImplementWritingLibFiles,
    152     }
    153 
    154     var coff_file_header_offset: u32 = 0;
    155     if (options.output_mode == .Exe) {
    156         // Write the MS-DOS stub and the PE signature
    157         try self.base.file.?.pwriteAll(msdos_stub ++ "PE\x00\x00", 0);
    158         coff_file_header_offset = msdos_stub.len + 4;
    159     }
    160 
    161     // COFF file header
    162     const data_directory_count = 0;
    163     var hdr_data: [112 + data_directory_count * 8 + section_table_size]u8 = undefined;
    164     var index: usize = 0;
    165 
    166     const machine = self.base.options.target.cpu.arch.toCoffMachine();
    167     if (machine == .Unknown) {
    168         return error.UnsupportedCOFFArchitecture;
    169     }
    170     mem.writeIntLittle(u16, hdr_data[0..2], @enumToInt(machine));
    171     index += 2;
    172 
    173     // Number of sections (we only use .got, .text)
    174     mem.writeIntLittle(u16, hdr_data[index..][0..2], 2);
    175     index += 2;
    176     // TimeDateStamp (u32), PointerToSymbolTable (u32), NumberOfSymbols (u32)
    177     mem.set(u8, hdr_data[index..][0..12], 0);
    178     index += 12;
    179 
    180     const optional_header_size = switch (options.output_mode) {
    181         .Exe => data_directory_count * 8 + switch (self.ptr_width) {
    182             .p32 => @as(u16, 96),
    183             .p64 => 112,
    184         },
    185         else => 0,
    186     };
    187 
    188     const section_table_offset = coff_file_header_offset + 20 + optional_header_size;
    189     const default_offset_table_size = file_alignment;
    190     const default_size_of_code = 0;
    191 
    192     self.section_data_offset = mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, file_alignment);
    193     const section_data_relative_virtual_address = mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, section_alignment);
    194     self.offset_table_virtual_address = default_image_base + section_data_relative_virtual_address;
    195     self.offset_table_size = default_offset_table_size;
    196     self.section_table_offset = section_table_offset;
    197     self.text_section_virtual_address = default_image_base + section_data_relative_virtual_address + section_alignment;
    198     self.text_section_size = default_size_of_code;
    199 
    200     // Size of file when loaded in memory
    201     const size_of_image = mem.alignForwardGeneric(u32, self.text_section_virtual_address - default_image_base + default_size_of_code, section_alignment);
    202 
    203     mem.writeIntLittle(u16, hdr_data[index..][0..2], optional_header_size);
    204     index += 2;
    205 
    206     // Characteristics
    207     var characteristics: u16 = std.coff.IMAGE_FILE_DEBUG_STRIPPED | std.coff.IMAGE_FILE_RELOCS_STRIPPED; // TODO Remove debug info stripped flag when necessary
    208     if (options.output_mode == .Exe) {
    209         characteristics |= std.coff.IMAGE_FILE_EXECUTABLE_IMAGE;
    210     }
    211     switch (self.ptr_width) {
    212         .p32 => characteristics |= std.coff.IMAGE_FILE_32BIT_MACHINE,
    213         .p64 => characteristics |= std.coff.IMAGE_FILE_LARGE_ADDRESS_AWARE,
    214     }
    215     mem.writeIntLittle(u16, hdr_data[index..][0..2], characteristics);
    216     index += 2;
    217 
    218     assert(index == 20);
    219     try self.base.file.?.pwriteAll(hdr_data[0..index], coff_file_header_offset);
    220 
    221     if (options.output_mode == .Exe) {
    222         self.optional_header_offset = coff_file_header_offset + 20;
    223         // Optional header
    224         index = 0;
    225         mem.writeIntLittle(u16, hdr_data[0..2], switch (self.ptr_width) {
    226             .p32 => @as(u16, 0x10b),
    227             .p64 => 0x20b,
    228         });
    229         index += 2;
    230 
    231         // Linker version (u8 + u8)
    232         mem.set(u8, hdr_data[index..][0..2], 0);
    233         index += 2;
    234 
    235         // SizeOfCode (UNUSED, u32), SizeOfInitializedData (u32), SizeOfUninitializedData (u32), AddressOfEntryPoint (u32), BaseOfCode (UNUSED, u32)
    236         mem.set(u8, hdr_data[index..][0..20], 0);
    237         index += 20;
    238 
    239         if (self.ptr_width == .p32) {
    240             // Base of data relative to the image base (UNUSED)
    241             mem.set(u8, hdr_data[index..][0..4], 0);
    242             index += 4;
    243 
    244             // Image base address
    245             mem.writeIntLittle(u32, hdr_data[index..][0..4], default_image_base);
    246             index += 4;
    247         } else {
    248             // Image base address
    249             mem.writeIntLittle(u64, hdr_data[index..][0..8], default_image_base);
    250             index += 8;
    251         }
    252 
    253         // Section alignment
    254         mem.writeIntLittle(u32, hdr_data[index..][0..4], section_alignment);
    255         index += 4;
    256         // File alignment
    257         mem.writeIntLittle(u32, hdr_data[index..][0..4], file_alignment);
    258         index += 4;
    259         // Required OS version, 6.0 is vista
    260         mem.writeIntLittle(u16, hdr_data[index..][0..2], 6);
    261         index += 2;
    262         mem.writeIntLittle(u16, hdr_data[index..][0..2], 0);
    263         index += 2;
    264         // Image version
    265         mem.set(u8, hdr_data[index..][0..4], 0);
    266         index += 4;
    267         // Required subsystem version, same as OS version
    268         mem.writeIntLittle(u16, hdr_data[index..][0..2], 6);
    269         index += 2;
    270         mem.writeIntLittle(u16, hdr_data[index..][0..2], 0);
    271         index += 2;
    272         // Reserved zeroes (u32)
    273         mem.set(u8, hdr_data[index..][0..4], 0);
    274         index += 4;
    275         mem.writeIntLittle(u32, hdr_data[index..][0..4], size_of_image);
    276         index += 4;
    277         mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset);
    278         index += 4;
    279         // CheckSum (u32)
    280         mem.set(u8, hdr_data[index..][0..4], 0);
    281         index += 4;
    282         // Subsystem, TODO: Let users specify the subsystem, always CUI for now
    283         mem.writeIntLittle(u16, hdr_data[index..][0..2], 3);
    284         index += 2;
    285         // DLL characteristics
    286         mem.writeIntLittle(u16, hdr_data[index..][0..2], 0x0);
    287         index += 2;
    288 
    289         switch (self.ptr_width) {
    290             .p32 => {
    291                 // Size of stack reserve + commit
    292                 mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000_000);
    293                 index += 4;
    294                 mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000);
    295                 index += 4;
    296                 // Size of heap reserve + commit
    297                 mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x100_000);
    298                 index += 4;
    299                 mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000);
    300                 index += 4;
    301             },
    302             .p64 => {
    303                 // Size of stack reserve + commit
    304                 mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000_000);
    305                 index += 8;
    306                 mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000);
    307                 index += 8;
    308                 // Size of heap reserve + commit
    309                 mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x100_000);
    310                 index += 8;
    311                 mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000);
    312                 index += 8;
    313             },
    314         }
    315 
    316         // Reserved zeroes
    317         mem.set(u8, hdr_data[index..][0..4], 0);
    318         index += 4;
    319 
    320         // Number of data directories
    321         mem.writeIntLittle(u32, hdr_data[index..][0..4], data_directory_count);
    322         index += 4;
    323         // Initialize data directories to zero
    324         mem.set(u8, hdr_data[index..][0 .. data_directory_count * 8], 0);
    325         index += data_directory_count * 8;
    326 
    327         assert(index == optional_header_size);
    328     }
    329 
    330     // Write section table.
    331     // First, the .got section
    332     hdr_data[index..][0..8].* = ".got\x00\x00\x00\x00".*;
    333     index += 8;
    334     if (options.output_mode == .Exe) {
    335         // Virtual size (u32)
    336         mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size);
    337         index += 4;
    338         // Virtual address (u32)
    339         mem.writeIntLittle(u32, hdr_data[index..][0..4], self.offset_table_virtual_address - default_image_base);
    340         index += 4;
    341     } else {
    342         mem.set(u8, hdr_data[index..][0..8], 0);
    343         index += 8;
    344     }
    345     // Size of raw data (u32)
    346     mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size);
    347     index += 4;
    348     // File pointer to the start of the section
    349     mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset);
    350     index += 4;
    351     // Pointer to relocations (u32), PointerToLinenumbers (u32), NumberOfRelocations (u16), NumberOfLinenumbers (u16)
    352     mem.set(u8, hdr_data[index..][0..12], 0);
    353     index += 12;
    354     // Section flags
    355     mem.writeIntLittle(u32, hdr_data[index..][0..4], std.coff.IMAGE_SCN_CNT_INITIALIZED_DATA | std.coff.IMAGE_SCN_MEM_READ);
    356     index += 4;
    357     // Then, the .text section
    358     hdr_data[index..][0..8].* = ".text\x00\x00\x00".*;
    359     index += 8;
    360     if (options.output_mode == .Exe) {
    361         // Virtual size (u32)
    362         mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code);
    363         index += 4;
    364         // Virtual address (u32)
    365         mem.writeIntLittle(u32, hdr_data[index..][0..4], self.text_section_virtual_address - default_image_base);
    366         index += 4;
    367     } else {
    368         mem.set(u8, hdr_data[index..][0..8], 0);
    369         index += 8;
    370     }
    371     // Size of raw data (u32)
    372     mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code);
    373     index += 4;
    374     // File pointer to the start of the section
    375     mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset + default_offset_table_size);
    376     index += 4;
    377     // Pointer to relocations (u32), PointerToLinenumbers (u32), NumberOfRelocations (u16), NumberOfLinenumbers (u16)
    378     mem.set(u8, hdr_data[index..][0..12], 0);
    379     index += 12;
    380     // Section flags
    381     mem.writeIntLittle(
    382         u32,
    383         hdr_data[index..][0..4],
    384         std.coff.IMAGE_SCN_CNT_CODE | std.coff.IMAGE_SCN_MEM_EXECUTE | std.coff.IMAGE_SCN_MEM_READ | std.coff.IMAGE_SCN_MEM_WRITE,
    385     );
    386     index += 4;
    387 
    388     assert(index == optional_header_size + section_table_size);
    389     try self.base.file.?.pwriteAll(hdr_data[0..index], self.optional_header_offset);
    390     try self.base.file.?.setEndPos(self.section_data_offset + default_offset_table_size + default_size_of_code);
    391 
    392     return self;
    393 }
    394 
    395 pub fn createEmpty(gpa: Allocator, options: link.Options) !*Coff {
    396     const ptr_width: PtrWidth = switch (options.target.cpu.arch.ptrBitWidth()) {
    397         0...32 => .p32,
    398         33...64 => .p64,
    399         else => return error.UnsupportedCOFFArchitecture,
    400     };
    401     const self = try gpa.create(Coff);
    402     errdefer gpa.destroy(self);
    403     self.* = .{
    404         .base = .{
    405             .tag = .coff,
    406             .options = options,
    407             .allocator = gpa,
    408             .file = null,
    409         },
    410         .ptr_width = ptr_width,
    411     };
    412 
    413     const use_llvm = build_options.have_llvm and options.use_llvm;
    414     const use_stage1 = build_options.have_stage1 and options.use_stage1;
    415     if (use_llvm and !use_stage1) {
    416         self.llvm_object = try LlvmObject.create(gpa, options);
    417     }
    418     return self;
    419 }
    420 
    421 pub fn allocateDeclIndexes(self: *Coff, decl_index: Module.Decl.Index) !void {
    422     if (self.llvm_object) |_| return;
    423 
    424     try self.offset_table.ensureUnusedCapacity(self.base.allocator, 1);
    425 
    426     const decl = self.base.options.module.?.declPtr(decl_index);
    427     if (self.offset_table_free_list.popOrNull()) |i| {
    428         decl.link.coff.offset_table_index = i;
    429     } else {
    430         decl.link.coff.offset_table_index = @intCast(u32, self.offset_table.items.len);
    431         _ = self.offset_table.addOneAssumeCapacity();
    432 
    433         const entry_size = self.base.options.target.cpu.arch.ptrBitWidth() / 8;
    434         if (self.offset_table.items.len > self.offset_table_size / entry_size) {
    435             self.offset_table_size_dirty = true;
    436         }
    437     }
    438 
    439     self.offset_table.items[decl.link.coff.offset_table_index] = 0;
    440 }
    441 
    442 fn allocateTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
    443     const new_block_min_capacity = new_block_size * allocation_padding;
    444 
    445     // We use these to indicate our intention to update metadata, placing the new block,
    446     // and possibly removing a free list node.
    447     // It would be simpler to do it inside the for loop below, but that would cause a
    448     // problem if an error was returned later in the function. So this action
    449     // is actually carried out at the end of the function, when errors are no longer possible.
    450     var block_placement: ?*TextBlock = null;
    451     var free_list_removal: ?usize = null;
    452 
    453     const vaddr = blk: {
    454         var i: usize = 0;
    455         while (i < self.text_block_free_list.items.len) {
    456             const free_block = self.text_block_free_list.items[i];
    457 
    458             const next_block_text_offset = free_block.text_offset + free_block.capacity();
    459             const new_block_text_offset = mem.alignForwardGeneric(u64, free_block.getVAddr(self.*) + free_block.size, alignment) - self.text_section_virtual_address;
    460             if (new_block_text_offset < next_block_text_offset and next_block_text_offset - new_block_text_offset >= new_block_min_capacity) {
    461                 block_placement = free_block;
    462 
    463                 const remaining_capacity = next_block_text_offset - new_block_text_offset - new_block_min_capacity;
    464                 if (remaining_capacity < minimum_text_block_size) {
    465                     free_list_removal = i;
    466                 }
    467 
    468                 break :blk new_block_text_offset + self.text_section_virtual_address;
    469             } else {
    470                 if (!free_block.freeListEligible()) {
    471                     _ = self.text_block_free_list.swapRemove(i);
    472                 } else {
    473                     i += 1;
    474                 }
    475                 continue;
    476             }
    477         } else if (self.last_text_block) |last| {
    478             const new_block_vaddr = mem.alignForwardGeneric(u64, last.getVAddr(self.*) + last.size, alignment);
    479             block_placement = last;
    480             break :blk new_block_vaddr;
    481         } else {
    482             break :blk self.text_section_virtual_address;
    483         }
    484     };
    485 
    486     const expand_text_section = block_placement == null or block_placement.?.next == null;
    487     if (expand_text_section) {
    488         const needed_size = @intCast(u32, mem.alignForwardGeneric(u64, vaddr + new_block_size - self.text_section_virtual_address, file_alignment));
    489         if (needed_size > self.text_section_size) {
    490             const current_text_section_virtual_size = mem.alignForwardGeneric(u32, self.text_section_size, section_alignment);
    491             const new_text_section_virtual_size = mem.alignForwardGeneric(u32, needed_size, section_alignment);
    492             if (current_text_section_virtual_size != new_text_section_virtual_size) {
    493                 self.size_of_image_dirty = true;
    494                 // Write new virtual size
    495                 var buf: [4]u8 = undefined;
    496                 mem.writeIntLittle(u32, &buf, new_text_section_virtual_size);
    497                 try self.base.file.?.pwriteAll(&buf, self.section_table_offset + 40 + 8);
    498             }
    499 
    500             self.text_section_size = needed_size;
    501             self.text_section_size_dirty = true;
    502         }
    503         self.last_text_block = text_block;
    504     }
    505     text_block.text_offset = @intCast(u32, vaddr - self.text_section_virtual_address);
    506     text_block.size = @intCast(u32, new_block_size);
    507 
    508     // This function can also reallocate a text block.
    509     // In this case we need to "unplug" it from its previous location before
    510     // plugging it in to its new location.
    511     if (text_block.prev) |prev| {
    512         prev.next = text_block.next;
    513     }
    514     if (text_block.next) |next| {
    515         next.prev = text_block.prev;
    516     }
    517 
    518     if (block_placement) |big_block| {
    519         text_block.prev = big_block;
    520         text_block.next = big_block.next;
    521         big_block.next = text_block;
    522     } else {
    523         text_block.prev = null;
    524         text_block.next = null;
    525     }
    526     if (free_list_removal) |i| {
    527         _ = self.text_block_free_list.swapRemove(i);
    528     }
    529     return vaddr;
    530 }
    531 
    532 fn growTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 {
    533     const block_vaddr = text_block.getVAddr(self.*);
    534     const align_ok = mem.alignBackwardGeneric(u64, block_vaddr, alignment) == block_vaddr;
    535     const need_realloc = !align_ok or new_block_size > text_block.capacity();
    536     if (!need_realloc) return @as(u64, block_vaddr);
    537     return self.allocateTextBlock(text_block, new_block_size, alignment);
    538 }
    539 
    540 fn shrinkTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64) void {
    541     text_block.size = @intCast(u32, new_block_size);
    542     if (text_block.capacity() - text_block.size >= minimum_text_block_size) {
    543         self.text_block_free_list.append(self.base.allocator, text_block) catch {};
    544     }
    545 }
    546 
    547 fn freeTextBlock(self: *Coff, text_block: *TextBlock) void {
    548     var already_have_free_list_node = false;
    549     {
    550         var i: usize = 0;
    551         // TODO turn text_block_free_list into a hash map
    552         while (i < self.text_block_free_list.items.len) {
    553             if (self.text_block_free_list.items[i] == text_block) {
    554                 _ = self.text_block_free_list.swapRemove(i);
    555                 continue;
    556             }
    557             if (self.text_block_free_list.items[i] == text_block.prev) {
    558                 already_have_free_list_node = true;
    559             }
    560             i += 1;
    561         }
    562     }
    563     if (self.last_text_block == text_block) {
    564         self.last_text_block = text_block.prev;
    565     }
    566     if (text_block.prev) |prev| {
    567         prev.next = text_block.next;
    568 
    569         if (!already_have_free_list_node and prev.freeListEligible()) {
    570             // The free list is heuristics, it doesn't have to be perfect, so we can
    571             // ignore the OOM here.
    572             self.text_block_free_list.append(self.base.allocator, prev) catch {};
    573         }
    574     }
    575 
    576     if (text_block.next) |next| {
    577         next.prev = text_block.prev;
    578     }
    579 }
    580 
    581 fn writeOffsetTableEntry(self: *Coff, index: usize) !void {
    582     const entry_size = self.base.options.target.cpu.arch.ptrBitWidth() / 8;
    583     const endian = self.base.options.target.cpu.arch.endian();
    584 
    585     const offset_table_start = self.section_data_offset;
    586     if (self.offset_table_size_dirty) {
    587         const current_raw_size = self.offset_table_size;
    588         const new_raw_size = self.offset_table_size * 2;
    589         log.debug("growing offset table from raw size {} to {}\n", .{ current_raw_size, new_raw_size });
    590 
    591         // Move the text section to a new place in the executable
    592         const current_text_section_start = self.section_data_offset + current_raw_size;
    593         const new_text_section_start = self.section_data_offset + new_raw_size;
    594 
    595         const amt = try self.base.file.?.copyRangeAll(current_text_section_start, self.base.file.?, new_text_section_start, self.text_section_size);
    596         if (amt != self.text_section_size) return error.InputOutput;
    597 
    598         // Write the new raw size in the .got header
    599         var buf: [8]u8 = undefined;
    600         mem.writeIntLittle(u32, buf[0..4], new_raw_size);
    601         try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 16);
    602         // Write the new .text section file offset in the .text section header
    603         mem.writeIntLittle(u32, buf[0..4], new_text_section_start);
    604         try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 20);
    605 
    606         const current_virtual_size = mem.alignForwardGeneric(u32, self.offset_table_size, section_alignment);
    607         const new_virtual_size = mem.alignForwardGeneric(u32, new_raw_size, section_alignment);
    608         // If we had to move in the virtual address space, we need to fix the VAs in the offset table, as well as the virtual address of the `.text` section
    609         // and the virtual size of the `.got` section
    610 
    611         if (new_virtual_size != current_virtual_size) {
    612             log.debug("growing offset table from virtual size {} to {}\n", .{ current_virtual_size, new_virtual_size });
    613             self.size_of_image_dirty = true;
    614             const va_offset = new_virtual_size - current_virtual_size;
    615 
    616             // Write .got virtual size
    617             mem.writeIntLittle(u32, buf[0..4], new_virtual_size);
    618             try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 8);
    619 
    620             // Write .text new virtual address
    621             self.text_section_virtual_address = self.text_section_virtual_address + va_offset;
    622             mem.writeIntLittle(u32, buf[0..4], self.text_section_virtual_address - default_image_base);
    623             try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 12);
    624 
    625             // Fix the VAs in the offset table
    626             for (self.offset_table.items) |*va, idx| {
    627                 if (va.* != 0) {
    628                     va.* += va_offset;
    629 
    630                     switch (entry_size) {
    631                         4 => {
    632                             mem.writeInt(u32, buf[0..4], @intCast(u32, va.*), endian);
    633                             try self.base.file.?.pwriteAll(buf[0..4], offset_table_start + idx * entry_size);
    634                         },
    635                         8 => {
    636                             mem.writeInt(u64, &buf, va.*, endian);
    637                             try self.base.file.?.pwriteAll(&buf, offset_table_start + idx * entry_size);
    638                         },
    639                         else => unreachable,
    640                     }
    641                 }
    642             }
    643         }
    644         self.offset_table_size = new_raw_size;
    645         self.offset_table_size_dirty = false;
    646     }
    647     // Write the new entry
    648     switch (entry_size) {
    649         4 => {
    650             var buf: [4]u8 = undefined;
    651             mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian);
    652             try self.base.file.?.pwriteAll(&buf, offset_table_start + index * entry_size);
    653         },
    654         8 => {
    655             var buf: [8]u8 = undefined;
    656             mem.writeInt(u64, &buf, self.offset_table.items[index], endian);
    657             try self.base.file.?.pwriteAll(&buf, offset_table_start + index * entry_size);
    658         },
    659         else => unreachable,
    660     }
    661 }
    662 
    663 pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
    664     if (build_options.skip_non_native and builtin.object_format != .coff) {
    665         @panic("Attempted to compile for object format that was disabled by build configuration");
    666     }
    667     if (build_options.have_llvm) {
    668         if (self.llvm_object) |llvm_object| {
    669             return llvm_object.updateFunc(module, func, air, liveness);
    670         }
    671     }
    672     const tracy = trace(@src());
    673     defer tracy.end();
    674 
    675     var code_buffer = std.ArrayList(u8).init(self.base.allocator);
    676     defer code_buffer.deinit();
    677 
    678     const decl_index = func.owner_decl;
    679     const decl = module.declPtr(decl_index);
    680     const res = try codegen.generateFunction(
    681         &self.base,
    682         decl.srcLoc(),
    683         func,
    684         air,
    685         liveness,
    686         &code_buffer,
    687         .none,
    688     );
    689     const code = switch (res) {
    690         .appended => code_buffer.items,
    691         .fail => |em| {
    692             decl.analysis = .codegen_failure;
    693             try module.failed_decls.put(module.gpa, decl_index, em);
    694             return;
    695         },
    696     };
    697 
    698     return self.finishUpdateDecl(module, func.owner_decl, code);
    699 }
    700 
    701 pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.Index) !u32 {
    702     _ = self;
    703     _ = tv;
    704     _ = decl_index;
    705     log.debug("TODO lowerUnnamedConst for Coff", .{});
    706     return error.AnalysisFail;
    707 }
    708 
    709 pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !void {
    710     if (build_options.skip_non_native and builtin.object_format != .coff) {
    711         @panic("Attempted to compile for object format that was disabled by build configuration");
    712     }
    713     if (build_options.have_llvm) {
    714         if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(module, decl_index);
    715     }
    716     const tracy = trace(@src());
    717     defer tracy.end();
    718 
    719     const decl = module.declPtr(decl_index);
    720 
    721     if (decl.val.tag() == .extern_fn) {
    722         return; // TODO Should we do more when front-end analyzed extern decl?
    723     }
    724 
    725     // TODO COFF/PE debug information
    726     // TODO Implement exports
    727 
    728     var code_buffer = std.ArrayList(u8).init(self.base.allocator);
    729     defer code_buffer.deinit();
    730 
    731     const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
    732         .ty = decl.ty,
    733         .val = decl.val,
    734     }, &code_buffer, .none, .{
    735         .parent_atom_index = 0,
    736     });
    737     const code = switch (res) {
    738         .externally_managed => |x| x,
    739         .appended => code_buffer.items,
    740         .fail => |em| {
    741             decl.analysis = .codegen_failure;
    742             try module.failed_decls.put(module.gpa, decl_index, em);
    743             return;
    744         },
    745     };
    746 
    747     return self.finishUpdateDecl(module, decl_index, code);
    748 }
    749 
    750 fn finishUpdateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index, code: []const u8) !void {
    751     const decl = module.declPtr(decl_index);
    752     const required_alignment = decl.ty.abiAlignment(self.base.options.target);
    753     const curr_size = decl.link.coff.size;
    754     if (curr_size != 0) {
    755         const capacity = decl.link.coff.capacity();
    756         const need_realloc = code.len > capacity or
    757             !mem.isAlignedGeneric(u32, decl.link.coff.text_offset, required_alignment);
    758         if (need_realloc) {
    759             const curr_vaddr = self.text_section_virtual_address + decl.link.coff.text_offset;
    760             const vaddr = try self.growTextBlock(&decl.link.coff, code.len, required_alignment);
    761             log.debug("growing {s} from 0x{x} to 0x{x}\n", .{ decl.name, curr_vaddr, vaddr });
    762             if (vaddr != curr_vaddr) {
    763                 log.debug("  (writing new offset table entry)\n", .{});
    764                 self.offset_table.items[decl.link.coff.offset_table_index] = vaddr;
    765                 try self.writeOffsetTableEntry(decl.link.coff.offset_table_index);
    766             }
    767         } else if (code.len < curr_size) {
    768             self.shrinkTextBlock(&decl.link.coff, code.len);
    769         }
    770     } else {
    771         const vaddr = try self.allocateTextBlock(&decl.link.coff, code.len, required_alignment);
    772         log.debug("allocated text block for {s} at 0x{x} (size: {Bi})\n", .{
    773             mem.sliceTo(decl.name, 0),
    774             vaddr,
    775             std.fmt.fmtIntSizeDec(code.len),
    776         });
    777         errdefer self.freeTextBlock(&decl.link.coff);
    778         self.offset_table.items[decl.link.coff.offset_table_index] = vaddr;
    779         try self.writeOffsetTableEntry(decl.link.coff.offset_table_index);
    780     }
    781 
    782     // Write the code into the file
    783     try self.base.file.?.pwriteAll(code, self.section_data_offset + self.offset_table_size + decl.link.coff.text_offset);
    784 
    785     // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated.
    786     const decl_exports = module.decl_exports.get(decl_index) orelse &[0]*Module.Export{};
    787     return self.updateDeclExports(module, decl_index, decl_exports);
    788 }
    789 
    790 pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void {
    791     if (build_options.have_llvm) {
    792         if (self.llvm_object) |llvm_object| return llvm_object.freeDecl(decl_index);
    793     }
    794 
    795     const mod = self.base.options.module.?;
    796     const decl = mod.declPtr(decl_index);
    797 
    798     // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
    799     self.freeTextBlock(&decl.link.coff);
    800     self.offset_table_free_list.append(self.base.allocator, decl.link.coff.offset_table_index) catch {};
    801 }
    802 
    803 pub fn updateDeclExports(
    804     self: *Coff,
    805     module: *Module,
    806     decl_index: Module.Decl.Index,
    807     exports: []const *Module.Export,
    808 ) !void {
    809     if (build_options.skip_non_native and builtin.object_format != .coff) {
    810         @panic("Attempted to compile for object format that was disabled by build configuration");
    811     }
    812 
    813     // Even in the case of LLVM, we need to notice certain exported symbols in order to
    814     // detect the default subsystem.
    815     for (exports) |exp| {
    816         const exported_decl = module.declPtr(exp.exported_decl);
    817         if (exported_decl.getFunction() == null) continue;
    818         const winapi_cc = switch (self.base.options.target.cpu.arch) {
    819             .i386 => std.builtin.CallingConvention.Stdcall,
    820             else => std.builtin.CallingConvention.C,
    821         };
    822         const decl_cc = exported_decl.ty.fnCallingConvention();
    823         if (decl_cc == .C and mem.eql(u8, exp.options.name, "main") and
    824             self.base.options.link_libc)
    825         {
    826             module.stage1_flags.have_c_main = true;
    827         } else if (decl_cc == winapi_cc and self.base.options.target.os.tag == .windows) {
    828             if (mem.eql(u8, exp.options.name, "WinMain")) {
    829                 module.stage1_flags.have_winmain = true;
    830             } else if (mem.eql(u8, exp.options.name, "wWinMain")) {
    831                 module.stage1_flags.have_wwinmain = true;
    832             } else if (mem.eql(u8, exp.options.name, "WinMainCRTStartup")) {
    833                 module.stage1_flags.have_winmain_crt_startup = true;
    834             } else if (mem.eql(u8, exp.options.name, "wWinMainCRTStartup")) {
    835                 module.stage1_flags.have_wwinmain_crt_startup = true;
    836             } else if (mem.eql(u8, exp.options.name, "DllMainCRTStartup")) {
    837                 module.stage1_flags.have_dllmain_crt_startup = true;
    838             }
    839         }
    840     }
    841 
    842     if (build_options.have_llvm) {
    843         if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(module, decl_index, exports);
    844     }
    845 
    846     const decl = module.declPtr(decl_index);
    847     for (exports) |exp| {
    848         if (exp.options.section) |section_name| {
    849             if (!mem.eql(u8, section_name, ".text")) {
    850                 try module.failed_exports.ensureUnusedCapacity(module.gpa, 1);
    851                 module.failed_exports.putAssumeCapacityNoClobber(
    852                     exp,
    853                     try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: ExportOptions.section", .{}),
    854                 );
    855                 continue;
    856             }
    857         }
    858         if (mem.eql(u8, exp.options.name, "_start")) {
    859             self.entry_addr = decl.link.coff.getVAddr(self.*) - default_image_base;
    860         } else {
    861             try module.failed_exports.ensureUnusedCapacity(module.gpa, 1);
    862             module.failed_exports.putAssumeCapacityNoClobber(
    863                 exp,
    864                 try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: Exports other than '_start'", .{}),
    865             );
    866             continue;
    867         }
    868     }
    869 }
    870 
    871 pub fn flush(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !void {
    872     if (self.base.options.emit == null) {
    873         if (build_options.have_llvm) {
    874             if (self.llvm_object) |llvm_object| {
    875                 return try llvm_object.flushModule(comp, prog_node);
    876             }
    877         }
    878         return;
    879     }
    880     if (build_options.have_llvm and self.base.options.use_lld) {
    881         return self.linkWithLLD(comp, prog_node);
    882     } else {
    883         switch (self.base.options.effectiveOutputMode()) {
    884             .Exe, .Obj => {},
    885             .Lib => return error.TODOImplementWritingLibFiles,
    886         }
    887         return self.flushModule(comp, prog_node);
    888     }
    889 }
    890 
    891 pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !void {
    892     const tracy = trace(@src());
    893     defer tracy.end();
    894 
    895     if (build_options.have_llvm) {
    896         if (self.llvm_object) |llvm_object| {
    897             return try llvm_object.flushModule(comp, prog_node);
    898         }
    899     }
    900 
    901     var sub_prog_node = prog_node.start("COFF Flush", 0);
    902     sub_prog_node.activate();
    903     defer sub_prog_node.end();
    904 
    905     if (self.text_section_size_dirty) {
    906         // Write the new raw size in the .text header
    907         var buf: [4]u8 = undefined;
    908         mem.writeIntLittle(u32, &buf, self.text_section_size);
    909         try self.base.file.?.pwriteAll(&buf, self.section_table_offset + 40 + 16);
    910         try self.base.file.?.setEndPos(self.section_data_offset + self.offset_table_size + self.text_section_size);
    911         self.text_section_size_dirty = false;
    912     }
    913 
    914     if (self.base.options.output_mode == .Exe and self.size_of_image_dirty) {
    915         const new_size_of_image = mem.alignForwardGeneric(u32, self.text_section_virtual_address - default_image_base + self.text_section_size, section_alignment);
    916         var buf: [4]u8 = undefined;
    917         mem.writeIntLittle(u32, &buf, new_size_of_image);
    918         try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 56);
    919         self.size_of_image_dirty = false;
    920     }
    921 
    922     if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
    923         log.debug("flushing. no_entry_point_found = true\n", .{});
    924         self.error_flags.no_entry_point_found = true;
    925     } else {
    926         log.debug("flushing. no_entry_point_found = false\n", .{});
    927         self.error_flags.no_entry_point_found = false;
    928 
    929         if (self.base.options.output_mode == .Exe) {
    930             // Write AddressOfEntryPoint
    931             var buf: [4]u8 = undefined;
    932             mem.writeIntLittle(u32, &buf, self.entry_addr.?);
    933             try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 16);
    934         }
    935     }
    936 }
    937 
    938 fn linkWithLLD(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Node) !void {
    939     const tracy = trace(@src());
    940     defer tracy.end();
    941 
    942     var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator);
    943     defer arena_allocator.deinit();
    944     const arena = arena_allocator.allocator();
    945 
    946     const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type.
    947     const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path});
    948 
    949     // If there is no Zig code to compile, then we should skip flushing the output file because it
    950     // will not be part of the linker line anyway.
    951     const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: {
    952         const use_stage1 = build_options.have_stage1 and self.base.options.use_stage1;
    953         if (use_stage1) {
    954             const obj_basename = try std.zig.binNameAlloc(arena, .{
    955                 .root_name = self.base.options.root_name,
    956                 .target = self.base.options.target,
    957                 .output_mode = .Obj,
    958             });
    959             switch (self.base.options.cache_mode) {
    960                 .incremental => break :blk try module.zig_cache_artifact_directory.join(
    961                     arena,
    962                     &[_][]const u8{obj_basename},
    963                 ),
    964                 .whole => break :blk try fs.path.join(arena, &.{
    965                     fs.path.dirname(full_out_path).?, obj_basename,
    966                 }),
    967             }
    968         }
    969 
    970         try self.flushModule(comp, prog_node);
    971 
    972         if (fs.path.dirname(full_out_path)) |dirname| {
    973             break :blk try fs.path.join(arena, &.{ dirname, self.base.intermediary_basename.? });
    974         } else {
    975             break :blk self.base.intermediary_basename.?;
    976         }
    977     } else null;
    978 
    979     var sub_prog_node = prog_node.start("LLD Link", 0);
    980     sub_prog_node.activate();
    981     sub_prog_node.context.refresh();
    982     defer sub_prog_node.end();
    983 
    984     const is_lib = self.base.options.output_mode == .Lib;
    985     const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib;
    986     const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe;
    987     const link_in_crt = self.base.options.link_libc and is_exe_or_dyn_lib;
    988     const target = self.base.options.target;
    989 
    990     // See link/Elf.zig for comments on how this mechanism works.
    991     const id_symlink_basename = "lld.id";
    992 
    993     var man: Cache.Manifest = undefined;
    994     defer if (!self.base.options.disable_lld_caching) man.deinit();
    995 
    996     var digest: [Cache.hex_digest_len]u8 = undefined;
    997 
    998     if (!self.base.options.disable_lld_caching) {
    999         man = comp.cache_parent.obtain();
   1000         self.base.releaseLock();
   1001 
   1002         comptime assert(Compilation.link_hash_implementation_version == 7);
   1003 
   1004         for (self.base.options.objects) |obj| {
   1005             _ = try man.addFile(obj.path, null);
   1006             man.hash.add(obj.must_link);
   1007         }
   1008         for (comp.c_object_table.keys()) |key| {
   1009             _ = try man.addFile(key.status.success.object_path, null);
   1010         }
   1011         try man.addOptionalFile(module_obj_path);
   1012         man.hash.addOptionalBytes(self.base.options.entry);
   1013         man.hash.addOptional(self.base.options.stack_size_override);
   1014         man.hash.addOptional(self.base.options.image_base_override);
   1015         man.hash.addListOfBytes(self.base.options.lib_dirs);
   1016         man.hash.add(self.base.options.skip_linker_dependencies);
   1017         if (self.base.options.link_libc) {
   1018             man.hash.add(self.base.options.libc_installation != null);
   1019             if (self.base.options.libc_installation) |libc_installation| {
   1020                 man.hash.addBytes(libc_installation.crt_dir.?);
   1021                 if (target.abi == .msvc) {
   1022                     man.hash.addBytes(libc_installation.msvc_lib_dir.?);
   1023                     man.hash.addBytes(libc_installation.kernel32_lib_dir.?);
   1024                 }
   1025             }
   1026         }
   1027         link.hashAddSystemLibs(&man.hash, self.base.options.system_libs);
   1028         man.hash.addOptional(self.base.options.subsystem);
   1029         man.hash.add(self.base.options.is_test);
   1030         man.hash.add(self.base.options.tsaware);
   1031         man.hash.add(self.base.options.nxcompat);
   1032         man.hash.add(self.base.options.dynamicbase);
   1033         // strip does not need to go into the linker hash because it is part of the hash namespace
   1034         man.hash.addOptional(self.base.options.major_subsystem_version);
   1035         man.hash.addOptional(self.base.options.minor_subsystem_version);
   1036 
   1037         // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock.
   1038         _ = try man.hit();
   1039         digest = man.final();
   1040         var prev_digest_buf: [digest.len]u8 = undefined;
   1041         const prev_digest: []u8 = Cache.readSmallFile(
   1042             directory.handle,
   1043             id_symlink_basename,
   1044             &prev_digest_buf,
   1045         ) catch |err| blk: {
   1046             log.debug("COFF LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
   1047             // Handle this as a cache miss.
   1048             break :blk prev_digest_buf[0..0];
   1049         };
   1050         if (mem.eql(u8, prev_digest, &digest)) {
   1051             log.debug("COFF LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
   1052             // Hot diggity dog! The output binary is already there.
   1053             self.base.lock = man.toOwnedLock();
   1054             return;
   1055         }
   1056         log.debug("COFF LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) });
   1057 
   1058         // We are about to change the output file to be different, so we invalidate the build hash now.
   1059         directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
   1060             error.FileNotFound => {},
   1061             else => |e| return e,
   1062         };
   1063     }
   1064 
   1065     if (self.base.options.output_mode == .Obj) {
   1066         // LLD's COFF driver does not support the equivalent of `-r` so we do a simple file copy
   1067         // here. TODO: think carefully about how we can avoid this redundant operation when doing
   1068         // build-obj. See also the corresponding TODO in linkAsArchive.
   1069         const the_object_path = blk: {
   1070             if (self.base.options.objects.len != 0)
   1071                 break :blk self.base.options.objects[0].path;
   1072 
   1073             if (comp.c_object_table.count() != 0)
   1074                 break :blk comp.c_object_table.keys()[0].status.success.object_path;
   1075 
   1076             if (module_obj_path) |p|
   1077                 break :blk p;
   1078 
   1079             // TODO I think this is unreachable. Audit this situation when solving the above TODO
   1080             // regarding eliding redundant object -> object transformations.
   1081             return error.NoObjectsToLink;
   1082         };
   1083         // This can happen when using --enable-cache and using the stage1 backend. In this case
   1084         // we can skip the file copy.
   1085         if (!mem.eql(u8, the_object_path, full_out_path)) {
   1086             try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
   1087         }
   1088     } else {
   1089         // Create an LLD command line and invoke it.
   1090         var argv = std.ArrayList([]const u8).init(self.base.allocator);
   1091         defer argv.deinit();
   1092         // We will invoke ourselves as a child process to gain access to LLD.
   1093         // This is necessary because LLD does not behave properly as a library -
   1094         // it calls exit() and does not reset all global data between invocations.
   1095         try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, "lld-link" });
   1096 
   1097         try argv.append("-ERRORLIMIT:0");
   1098         try argv.append("-NOLOGO");
   1099         if (!self.base.options.strip) {
   1100             try argv.append("-DEBUG");
   1101         }
   1102         if (self.base.options.lto) {
   1103             switch (self.base.options.optimize_mode) {
   1104                 .Debug => {},
   1105                 .ReleaseSmall => try argv.append("-OPT:lldlto=2"),
   1106                 .ReleaseFast, .ReleaseSafe => try argv.append("-OPT:lldlto=3"),
   1107             }
   1108         }
   1109         if (self.base.options.output_mode == .Exe) {
   1110             const stack_size = self.base.options.stack_size_override orelse 16777216;
   1111             try argv.append(try allocPrint(arena, "-STACK:{d}", .{stack_size}));
   1112         }
   1113         if (self.base.options.image_base_override) |image_base| {
   1114             try argv.append(try std.fmt.allocPrint(arena, "-BASE:{d}", .{image_base}));
   1115         }
   1116 
   1117         if (target.cpu.arch == .i386) {
   1118             try argv.append("-MACHINE:X86");
   1119         } else if (target.cpu.arch == .x86_64) {
   1120             try argv.append("-MACHINE:X64");
   1121         } else if (target.cpu.arch.isARM()) {
   1122             if (target.cpu.arch.ptrBitWidth() == 32) {
   1123                 try argv.append("-MACHINE:ARM");
   1124             } else {
   1125                 try argv.append("-MACHINE:ARM64");
   1126             }
   1127         }
   1128 
   1129         if (is_dyn_lib) {
   1130             try argv.append("-DLL");
   1131         }
   1132 
   1133         if (self.base.options.entry) |entry| {
   1134             try argv.append(try allocPrint(arena, "-ENTRY:{s}", .{entry}));
   1135         }
   1136 
   1137         if (self.base.options.tsaware) {
   1138             try argv.append("-tsaware");
   1139         }
   1140         if (self.base.options.nxcompat) {
   1141             try argv.append("-nxcompat");
   1142         }
   1143         if (self.base.options.dynamicbase) {
   1144             try argv.append("-dynamicbase");
   1145         }
   1146 
   1147         try argv.append(try allocPrint(arena, "-OUT:{s}", .{full_out_path}));
   1148 
   1149         if (self.base.options.implib_emit) |emit| {
   1150             const implib_out_path = try emit.directory.join(arena, &[_][]const u8{emit.sub_path});
   1151             try argv.append(try allocPrint(arena, "-IMPLIB:{s}", .{implib_out_path}));
   1152         }
   1153 
   1154         if (self.base.options.link_libc) {
   1155             if (self.base.options.libc_installation) |libc_installation| {
   1156                 try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.crt_dir.?}));
   1157 
   1158                 if (target.abi == .msvc) {
   1159                     try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.msvc_lib_dir.?}));
   1160                     try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.kernel32_lib_dir.?}));
   1161                 }
   1162             }
   1163         }
   1164 
   1165         for (self.base.options.lib_dirs) |lib_dir| {
   1166             try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{lib_dir}));
   1167         }
   1168 
   1169         try argv.ensureUnusedCapacity(self.base.options.objects.len);
   1170         for (self.base.options.objects) |obj| {
   1171             if (obj.must_link) {
   1172                 argv.appendAssumeCapacity(try allocPrint(arena, "-WHOLEARCHIVE:{s}", .{obj.path}));
   1173             } else {
   1174                 argv.appendAssumeCapacity(obj.path);
   1175             }
   1176         }
   1177 
   1178         for (comp.c_object_table.keys()) |key| {
   1179             try argv.append(key.status.success.object_path);
   1180         }
   1181 
   1182         if (module_obj_path) |p| {
   1183             try argv.append(p);
   1184         }
   1185 
   1186         const resolved_subsystem: ?std.Target.SubSystem = blk: {
   1187             if (self.base.options.subsystem) |explicit| break :blk explicit;
   1188             switch (target.os.tag) {
   1189                 .windows => {
   1190                     if (self.base.options.module) |module| {
   1191                         if (module.stage1_flags.have_dllmain_crt_startup or is_dyn_lib)
   1192                             break :blk null;
   1193                         if (module.stage1_flags.have_c_main or self.base.options.is_test or
   1194                             module.stage1_flags.have_winmain_crt_startup or
   1195                             module.stage1_flags.have_wwinmain_crt_startup)
   1196                         {
   1197                             break :blk .Console;
   1198                         }
   1199                         if (module.stage1_flags.have_winmain or module.stage1_flags.have_wwinmain)
   1200                             break :blk .Windows;
   1201                     }
   1202                 },
   1203                 .uefi => break :blk .EfiApplication,
   1204                 else => {},
   1205             }
   1206             break :blk null;
   1207         };
   1208 
   1209         const Mode = enum { uefi, win32 };
   1210         const mode: Mode = mode: {
   1211             if (resolved_subsystem) |subsystem| {
   1212                 const subsystem_suffix = ss: {
   1213                     if (self.base.options.major_subsystem_version) |major| {
   1214                         if (self.base.options.minor_subsystem_version) |minor| {
   1215                             break :ss try allocPrint(arena, ",{d}.{d}", .{ major, minor });
   1216                         } else {
   1217                             break :ss try allocPrint(arena, ",{d}", .{major});
   1218                         }
   1219                     }
   1220                     break :ss "";
   1221                 };
   1222 
   1223                 switch (subsystem) {
   1224                     .Console => {
   1225                         try argv.append(try allocPrint(arena, "-SUBSYSTEM:console{s}", .{
   1226                             subsystem_suffix,
   1227                         }));
   1228                         break :mode .win32;
   1229                     },
   1230                     .EfiApplication => {
   1231                         try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_application{s}", .{
   1232                             subsystem_suffix,
   1233                         }));
   1234                         break :mode .uefi;
   1235                     },
   1236                     .EfiBootServiceDriver => {
   1237                         try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_boot_service_driver{s}", .{
   1238                             subsystem_suffix,
   1239                         }));
   1240                         break :mode .uefi;
   1241                     },
   1242                     .EfiRom => {
   1243                         try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_rom{s}", .{
   1244                             subsystem_suffix,
   1245                         }));
   1246                         break :mode .uefi;
   1247                     },
   1248                     .EfiRuntimeDriver => {
   1249                         try argv.append(try allocPrint(arena, "-SUBSYSTEM:efi_runtime_driver{s}", .{
   1250                             subsystem_suffix,
   1251                         }));
   1252                         break :mode .uefi;
   1253                     },
   1254                     .Native => {
   1255                         try argv.append(try allocPrint(arena, "-SUBSYSTEM:native{s}", .{
   1256                             subsystem_suffix,
   1257                         }));
   1258                         break :mode .win32;
   1259                     },
   1260                     .Posix => {
   1261                         try argv.append(try allocPrint(arena, "-SUBSYSTEM:posix{s}", .{
   1262                             subsystem_suffix,
   1263                         }));
   1264                         break :mode .win32;
   1265                     },
   1266                     .Windows => {
   1267                         try argv.append(try allocPrint(arena, "-SUBSYSTEM:windows{s}", .{
   1268                             subsystem_suffix,
   1269                         }));
   1270                         break :mode .win32;
   1271                     },
   1272                 }
   1273             } else if (target.os.tag == .uefi) {
   1274                 break :mode .uefi;
   1275             } else {
   1276                 break :mode .win32;
   1277             }
   1278         };
   1279 
   1280         switch (mode) {
   1281             .uefi => try argv.appendSlice(&[_][]const u8{
   1282                 "-BASE:0",
   1283                 "-ENTRY:EfiMain",
   1284                 "-OPT:REF",
   1285                 "-SAFESEH:NO",
   1286                 "-MERGE:.rdata=.data",
   1287                 "-ALIGN:32",
   1288                 "-NODEFAULTLIB",
   1289                 "-SECTION:.xdata,D",
   1290             }),
   1291             .win32 => {
   1292                 if (link_in_crt) {
   1293                     if (target.abi.isGnu()) {
   1294                         try argv.append("-lldmingw");
   1295 
   1296                         if (target.cpu.arch == .i386) {
   1297                             try argv.append("-ALTERNATENAME:__image_base__=___ImageBase");
   1298                         } else {
   1299                             try argv.append("-ALTERNATENAME:__image_base__=__ImageBase");
   1300                         }
   1301 
   1302                         if (is_dyn_lib) {
   1303                             try argv.append(try comp.get_libc_crt_file(arena, "dllcrt2.obj"));
   1304                             if (target.cpu.arch == .i386) {
   1305                                 try argv.append("-ALTERNATENAME:__DllMainCRTStartup@12=_DllMainCRTStartup@12");
   1306                             } else {
   1307                                 try argv.append("-ALTERNATENAME:_DllMainCRTStartup=DllMainCRTStartup");
   1308                             }
   1309                         } else {
   1310                             try argv.append(try comp.get_libc_crt_file(arena, "crt2.obj"));
   1311                         }
   1312 
   1313                         try argv.append(try comp.get_libc_crt_file(arena, "mingw32.lib"));
   1314                         try argv.append(try comp.get_libc_crt_file(arena, "mingwex.lib"));
   1315                         try argv.append(try comp.get_libc_crt_file(arena, "msvcrt-os.lib"));
   1316 
   1317                         for (mingw.always_link_libs) |name| {
   1318                             if (!self.base.options.system_libs.contains(name)) {
   1319                                 const lib_basename = try allocPrint(arena, "{s}.lib", .{name});
   1320                                 try argv.append(try comp.get_libc_crt_file(arena, lib_basename));
   1321                             }
   1322                         }
   1323                     } else {
   1324                         const lib_str = switch (self.base.options.link_mode) {
   1325                             .Dynamic => "",
   1326                             .Static => "lib",
   1327                         };
   1328                         const d_str = switch (self.base.options.optimize_mode) {
   1329                             .Debug => "d",
   1330                             else => "",
   1331                         };
   1332                         switch (self.base.options.link_mode) {
   1333                             .Static => try argv.append(try allocPrint(arena, "libcmt{s}.lib", .{d_str})),
   1334                             .Dynamic => try argv.append(try allocPrint(arena, "msvcrt{s}.lib", .{d_str})),
   1335                         }
   1336 
   1337                         try argv.append(try allocPrint(arena, "{s}vcruntime{s}.lib", .{ lib_str, d_str }));
   1338                         try argv.append(try allocPrint(arena, "{s}ucrt{s}.lib", .{ lib_str, d_str }));
   1339 
   1340                         //Visual C++ 2015 Conformance Changes
   1341                         //https://msdn.microsoft.com/en-us/library/bb531344.aspx
   1342                         try argv.append("legacy_stdio_definitions.lib");
   1343 
   1344                         // msvcrt depends on kernel32 and ntdll
   1345                         try argv.append("kernel32.lib");
   1346                         try argv.append("ntdll.lib");
   1347                     }
   1348                 } else {
   1349                     try argv.append("-NODEFAULTLIB");
   1350                     if (!is_lib) {
   1351                         if (self.base.options.module) |module| {
   1352                             if (module.stage1_flags.have_winmain_crt_startup) {
   1353                                 try argv.append("-ENTRY:WinMainCRTStartup");
   1354                             } else {
   1355                                 try argv.append("-ENTRY:wWinMainCRTStartup");
   1356                             }
   1357                         } else {
   1358                             try argv.append("-ENTRY:wWinMainCRTStartup");
   1359                         }
   1360                     }
   1361                 }
   1362             },
   1363         }
   1364 
   1365         // libc++ dep
   1366         if (self.base.options.link_libcpp) {
   1367             try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
   1368             try argv.append(comp.libcxx_static_lib.?.full_object_path);
   1369         }
   1370 
   1371         // libunwind dep
   1372         if (self.base.options.link_libunwind) {
   1373             try argv.append(comp.libunwind_static_lib.?.full_object_path);
   1374         }
   1375 
   1376         if (is_exe_or_dyn_lib and !self.base.options.skip_linker_dependencies) {
   1377             if (!self.base.options.link_libc) {
   1378                 if (comp.libc_static_lib) |lib| {
   1379                     try argv.append(lib.full_object_path);
   1380                 }
   1381             }
   1382             // MinGW doesn't provide libssp symbols
   1383             if (target.abi.isGnu()) {
   1384                 if (comp.libssp_static_lib) |lib| {
   1385                     try argv.append(lib.full_object_path);
   1386                 }
   1387             }
   1388             // MSVC compiler_rt is missing some stuff, so we build it unconditionally but
   1389             // and rely on weak linkage to allow MSVC compiler_rt functions to override ours.
   1390             if (comp.compiler_rt_lib) |lib| {
   1391                 try argv.append(lib.full_object_path);
   1392             }
   1393         }
   1394 
   1395         try argv.ensureUnusedCapacity(self.base.options.system_libs.count());
   1396         for (self.base.options.system_libs.keys()) |key| {
   1397             const lib_basename = try allocPrint(arena, "{s}.lib", .{key});
   1398             if (comp.crt_files.get(lib_basename)) |crt_file| {
   1399                 argv.appendAssumeCapacity(crt_file.full_object_path);
   1400                 continue;
   1401             }
   1402             if (try self.findLib(arena, lib_basename)) |full_path| {
   1403                 argv.appendAssumeCapacity(full_path);
   1404                 continue;
   1405             }
   1406             if (target.abi.isGnu()) {
   1407                 const fallback_name = try allocPrint(arena, "lib{s}.dll.a", .{key});
   1408                 if (try self.findLib(arena, fallback_name)) |full_path| {
   1409                     argv.appendAssumeCapacity(full_path);
   1410                     continue;
   1411                 }
   1412             }
   1413             log.err("DLL import library for -l{s} not found", .{key});
   1414             return error.DllImportLibraryNotFound;
   1415         }
   1416 
   1417         if (self.base.options.verbose_link) {
   1418             // Skip over our own name so that the LLD linker name is the first argv item.
   1419             Compilation.dump_argv(argv.items[1..]);
   1420         }
   1421 
   1422         if (std.process.can_spawn) {
   1423             // If possible, we run LLD as a child process because it does not always
   1424             // behave properly as a library, unfortunately.
   1425             // https://github.com/ziglang/zig/issues/3825
   1426             var child = std.ChildProcess.init(argv.items, arena);
   1427             if (comp.clang_passthrough_mode) {
   1428                 child.stdin_behavior = .Inherit;
   1429                 child.stdout_behavior = .Inherit;
   1430                 child.stderr_behavior = .Inherit;
   1431 
   1432                 const term = child.spawnAndWait() catch |err| {
   1433                     log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
   1434                     return error.UnableToSpawnSelf;
   1435                 };
   1436                 switch (term) {
   1437                     .Exited => |code| {
   1438                         if (code != 0) {
   1439                             std.process.exit(code);
   1440                         }
   1441                     },
   1442                     else => std.process.abort(),
   1443                 }
   1444             } else {
   1445                 child.stdin_behavior = .Ignore;
   1446                 child.stdout_behavior = .Ignore;
   1447                 child.stderr_behavior = .Pipe;
   1448 
   1449                 try child.spawn();
   1450 
   1451                 const stderr = try child.stderr.?.reader().readAllAlloc(arena, 10 * 1024 * 1024);
   1452 
   1453                 const term = child.wait() catch |err| {
   1454                     log.err("unable to spawn {s}: {s}", .{ argv.items[0], @errorName(err) });
   1455                     return error.UnableToSpawnSelf;
   1456                 };
   1457 
   1458                 switch (term) {
   1459                     .Exited => |code| {
   1460                         if (code != 0) {
   1461                             // TODO parse this output and surface with the Compilation API rather than
   1462                             // directly outputting to stderr here.
   1463                             std.debug.print("{s}", .{stderr});
   1464                             return error.LLDReportedFailure;
   1465                         }
   1466                     },
   1467                     else => {
   1468                         log.err("{s} terminated with stderr:\n{s}", .{ argv.items[0], stderr });
   1469                         return error.LLDCrashed;
   1470                     },
   1471                 }
   1472 
   1473                 if (stderr.len != 0) {
   1474                     log.warn("unexpected LLD stderr:\n{s}", .{stderr});
   1475                 }
   1476             }
   1477         } else {
   1478             const exit_code = try lldMain(arena, argv.items, false);
   1479             if (exit_code != 0) {
   1480                 if (comp.clang_passthrough_mode) {
   1481                     std.process.exit(exit_code);
   1482                 } else {
   1483                     return error.LLDReportedFailure;
   1484                 }
   1485             }
   1486         }
   1487     }
   1488 
   1489     if (!self.base.options.disable_lld_caching) {
   1490         // Update the file with the digest. If it fails we can continue; it only
   1491         // means that the next invocation will have an unnecessary cache miss.
   1492         Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| {
   1493             log.warn("failed to save linking hash digest file: {s}", .{@errorName(err)});
   1494         };
   1495         // Again failure here only means an unnecessary cache miss.
   1496         man.writeManifest() catch |err| {
   1497             log.warn("failed to write cache manifest when linking: {s}", .{@errorName(err)});
   1498         };
   1499         // We hang on to this lock so that the output file path can be used without
   1500         // other processes clobbering it.
   1501         self.base.lock = man.toOwnedLock();
   1502     }
   1503 }
   1504 
   1505 fn findLib(self: *Coff, arena: Allocator, name: []const u8) !?[]const u8 {
   1506     for (self.base.options.lib_dirs) |lib_dir| {
   1507         const full_path = try fs.path.join(arena, &.{ lib_dir, name });
   1508         fs.cwd().access(full_path, .{}) catch |err| switch (err) {
   1509             error.FileNotFound => continue,
   1510             else => |e| return e,
   1511         };
   1512         return full_path;
   1513     }
   1514     return null;
   1515 }
   1516 
   1517 pub fn getDeclVAddr(
   1518     self: *Coff,
   1519     decl_index: Module.Decl.Index,
   1520     reloc_info: link.File.RelocInfo,
   1521 ) !u64 {
   1522     _ = reloc_info;
   1523     const mod = self.base.options.module.?;
   1524     const decl = mod.declPtr(decl_index);
   1525     assert(self.llvm_object == null);
   1526     return self.text_section_virtual_address + decl.link.coff.text_offset;
   1527 }
   1528 
   1529 pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !void {
   1530     _ = self;
   1531     _ = module;
   1532     _ = decl;
   1533     // TODO Implement this
   1534 }
   1535 
   1536 pub fn deinit(self: *Coff) void {
   1537     if (build_options.have_llvm) {
   1538         if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator);
   1539     }
   1540 
   1541     self.text_block_free_list.deinit(self.base.allocator);
   1542     self.offset_table.deinit(self.base.allocator);
   1543     self.offset_table_free_list.deinit(self.base.allocator);
   1544 }