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 }