blob 9be5b9ca (48961B) - Raw
1 const std = @import("std"); 2 const build_options = @import("build_options"); 3 const builtin = @import("builtin"); 4 const assert = std.debug.assert; 5 const fs = std.fs; 6 const mem = std.mem; 7 const log = std.log.scoped(.link); 8 const trace = @import("tracy.zig").trace; 9 const wasi_libc = @import("wasi_libc.zig"); 10 11 const Air = @import("Air.zig"); 12 const Allocator = std.mem.Allocator; 13 const Cache = @import("Cache.zig"); 14 const Compilation = @import("Compilation.zig"); 15 const LibCInstallation = @import("libc_installation.zig").LibCInstallation; 16 const Liveness = @import("Liveness.zig"); 17 const Module = @import("Module.zig"); 18 const Package = @import("Package.zig"); 19 const Type = @import("type.zig").Type; 20 const TypedValue = @import("TypedValue.zig"); 21 22 /// When adding a new field, remember to update `hashAddSystemLibs`. 23 pub const SystemLib = struct { 24 needed: bool = false, 25 weak: bool = false, 26 }; 27 28 pub const CacheMode = enum { incremental, whole }; 29 30 pub fn hashAddSystemLibs( 31 hh: *Cache.HashHelper, 32 hm: std.StringArrayHashMapUnmanaged(SystemLib), 33 ) void { 34 const keys = hm.keys(); 35 hh.add(keys.len); 36 hh.addListOfBytes(keys); 37 for (hm.values()) |value| { 38 hh.add(value.needed); 39 hh.add(value.weak); 40 } 41 } 42 43 pub const producer_string = if (builtin.is_test) "zig test" else "zig " ++ build_options.version; 44 45 pub const Emit = struct { 46 /// Where the output will go. 47 directory: Compilation.Directory, 48 /// Path to the output file, relative to `directory`. 49 sub_path: []const u8, 50 51 /// Returns the full path to `basename` if it were in the same directory as the 52 /// `Emit` sub_path. 53 pub fn basenamePath(emit: Emit, arena: Allocator, basename: [:0]const u8) ![:0]const u8 { 54 const full_path = if (emit.directory.path) |p| 55 try fs.path.join(arena, &[_][]const u8{ p, emit.sub_path }) 56 else 57 emit.sub_path; 58 59 if (fs.path.dirname(full_path)) |dirname| { 60 return try fs.path.joinZ(arena, &.{ dirname, basename }); 61 } else { 62 return basename; 63 } 64 } 65 }; 66 67 pub const Options = struct { 68 /// This is `null` when `-fno-emit-bin` is used. 69 emit: ?Emit, 70 /// This is `null` not building a Windows DLL, or when `-fno-emit-implib` is used. 71 implib_emit: ?Emit, 72 target: std.Target, 73 output_mode: std.builtin.OutputMode, 74 link_mode: std.builtin.LinkMode, 75 optimize_mode: std.builtin.Mode, 76 machine_code_model: std.builtin.CodeModel, 77 root_name: [:0]const u8, 78 /// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`. 79 module: ?*Module, 80 dynamic_linker: ?[]const u8, 81 /// The root path for the dynamic linker and system libraries (as well as frameworks on Darwin) 82 sysroot: ?[]const u8, 83 /// Used for calculating how much space to reserve for symbols in case the binary file 84 /// does not already have a symbol table. 85 symbol_count_hint: u64 = 32, 86 /// Used for calculating how much space to reserve for executable program code in case 87 /// the binary file does not already have such a section. 88 program_code_size_hint: u64 = 256 * 1024, 89 entry_addr: ?u64 = null, 90 entry: ?[]const u8, 91 stack_size_override: ?u64, 92 image_base_override: ?u64, 93 /// 0 means no stack protector 94 /// other value means stack protector with that buffer size. 95 stack_protector: u32, 96 cache_mode: CacheMode, 97 include_compiler_rt: bool, 98 /// Set to `true` to omit debug info. 99 strip: bool, 100 /// If this is true then this link code is responsible for outputting an object 101 /// file and then using LLD to link it together with the link options and other objects. 102 /// Otherwise (depending on `use_llvm`) this link code directly outputs and updates the final binary. 103 use_lld: bool, 104 /// If this is true then this link code is responsible for making an LLVM IR Module, 105 /// outputting it to an object file, and then linking that together with link options and 106 /// other objects. 107 /// Otherwise (depending on `use_lld`) this link code directly outputs and updates the final binary. 108 use_llvm: bool, 109 link_libc: bool, 110 link_libcpp: bool, 111 link_libunwind: bool, 112 function_sections: bool, 113 no_builtin: bool, 114 eh_frame_hdr: bool, 115 emit_relocs: bool, 116 rdynamic: bool, 117 z_nodelete: bool, 118 z_notext: bool, 119 z_defs: bool, 120 z_origin: bool, 121 z_nocopyreloc: bool, 122 z_now: bool, 123 z_relro: bool, 124 z_common_page_size: ?u64, 125 z_max_page_size: ?u64, 126 tsaware: bool, 127 nxcompat: bool, 128 dynamicbase: bool, 129 linker_optimization: u8, 130 compress_debug_sections: CompressDebugSections, 131 bind_global_refs_locally: bool, 132 import_memory: bool, 133 import_symbols: bool, 134 import_table: bool, 135 export_table: bool, 136 initial_memory: ?u64, 137 max_memory: ?u64, 138 shared_memory: bool, 139 export_symbol_names: []const []const u8, 140 global_base: ?u64, 141 is_native_os: bool, 142 is_native_abi: bool, 143 pic: bool, 144 pie: bool, 145 lto: bool, 146 valgrind: bool, 147 tsan: bool, 148 stack_check: bool, 149 red_zone: bool, 150 omit_frame_pointer: bool, 151 single_threaded: bool, 152 verbose_link: bool, 153 dll_export_fns: bool, 154 error_return_tracing: bool, 155 skip_linker_dependencies: bool, 156 parent_compilation_link_libc: bool, 157 each_lib_rpath: bool, 158 build_id: bool, 159 disable_lld_caching: bool, 160 is_test: bool, 161 hash_style: HashStyle, 162 major_subsystem_version: ?u32, 163 minor_subsystem_version: ?u32, 164 gc_sections: ?bool = null, 165 allow_shlib_undefined: ?bool, 166 subsystem: ?std.Target.SubSystem, 167 linker_script: ?[]const u8, 168 version_script: ?[]const u8, 169 soname: ?[]const u8, 170 llvm_cpu_features: ?[*:0]const u8, 171 print_gc_sections: bool, 172 print_icf_sections: bool, 173 print_map: bool, 174 opt_bisect_limit: i32, 175 176 objects: []Compilation.LinkObject, 177 framework_dirs: []const []const u8, 178 frameworks: std.StringArrayHashMapUnmanaged(SystemLib), 179 system_libs: std.StringArrayHashMapUnmanaged(SystemLib), 180 wasi_emulated_libs: []const wasi_libc.CRTFile, 181 lib_dirs: []const []const u8, 182 rpath_list: []const []const u8, 183 184 /// List of symbols forced as undefined in the symbol table 185 /// thus forcing their resolution by the linker. 186 /// Corresponds to `-u <symbol>` for ELF and `/include:<symbol>` for COFF/PE. 187 /// TODO add handling for MachO. 188 force_undefined_symbols: std.StringArrayHashMapUnmanaged(void), 189 190 version: ?std.builtin.Version, 191 compatibility_version: ?std.builtin.Version, 192 libc_installation: ?*const LibCInstallation, 193 194 /// WASI-only. Type of WASI execution model ("command" or "reactor"). 195 wasi_exec_model: std.builtin.WasiExecModel = undefined, 196 197 /// (Zig compiler development) Enable dumping of linker's state as JSON. 198 enable_link_snapshots: bool = false, 199 200 /// (Darwin) Path and version of the native SDK if detected. 201 native_darwin_sdk: ?std.zig.system.darwin.DarwinSDK = null, 202 203 /// (Darwin) Install name for the dylib 204 install_name: ?[]const u8 = null, 205 206 /// (Darwin) Path to entitlements file 207 entitlements: ?[]const u8 = null, 208 209 /// (Darwin) size of the __PAGEZERO segment 210 pagezero_size: ?u64 = null, 211 212 /// (Darwin) search strategy for system libraries 213 search_strategy: ?File.MachO.SearchStrategy = null, 214 215 /// (Darwin) set minimum space for future expansion of the load commands 216 headerpad_size: ?u32 = null, 217 218 /// (Darwin) set enough space as if all paths were MATPATHLEN 219 headerpad_max_install_names: bool = false, 220 221 /// (Darwin) remove dylibs that are unreachable by the entry point or exported symbols 222 dead_strip_dylibs: bool = false, 223 224 /// (Windows) PDB source path prefix to instruct the linker how to resolve relative 225 /// paths when consolidating CodeView streams into a single PDB file. 226 pdb_source_path: ?[]const u8 = null, 227 228 /// (Windows) PDB output path 229 pdb_out_path: ?[]const u8 = null, 230 231 /// (Windows) .def file to specify when linking 232 module_definition_file: ?[]const u8 = null, 233 234 pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode { 235 return if (options.use_lld) .Obj else options.output_mode; 236 } 237 238 pub fn move(self: *Options) Options { 239 const copied_state = self.*; 240 self.frameworks = .{}; 241 self.system_libs = .{}; 242 self.force_undefined_symbols = .{}; 243 return copied_state; 244 } 245 }; 246 247 pub const HashStyle = enum { sysv, gnu, both }; 248 249 pub const CompressDebugSections = enum { none, zlib }; 250 251 pub const File = struct { 252 tag: Tag, 253 options: Options, 254 file: ?fs.File, 255 allocator: Allocator, 256 /// When linking with LLD, this linker code will output an object file only at 257 /// this location, and then this path can be placed on the LLD linker line. 258 intermediary_basename: ?[]const u8 = null, 259 260 /// Prevents other processes from clobbering files in the output directory 261 /// of this linking operation. 262 lock: ?Cache.Lock = null, 263 264 pub const LinkBlock = union { 265 elf: Elf.TextBlock, 266 coff: Coff.Atom, 267 macho: MachO.Atom, 268 plan9: Plan9.DeclBlock, 269 c: void, 270 wasm: Wasm.DeclBlock, 271 spirv: void, 272 nvptx: void, 273 }; 274 275 pub const LinkFn = union { 276 elf: Dwarf.SrcFn, 277 coff: Coff.SrcFn, 278 macho: Dwarf.SrcFn, 279 plan9: void, 280 c: void, 281 wasm: Wasm.FnData, 282 spirv: SpirV.FnData, 283 nvptx: void, 284 }; 285 286 pub const Export = union { 287 elf: Elf.Export, 288 coff: Coff.Export, 289 macho: MachO.Export, 290 plan9: Plan9.Export, 291 c: void, 292 wasm: Wasm.Export, 293 spirv: void, 294 nvptx: void, 295 }; 296 297 /// Attempts incremental linking, if the file already exists. If 298 /// incremental linking fails, falls back to truncating the file and 299 /// rewriting it. A malicious file is detected as incremental link failure 300 /// and does not cause Illegal Behavior. This operation is not atomic. 301 pub fn openPath(allocator: Allocator, options: Options) !*File { 302 const have_macho = !build_options.only_c; 303 if (have_macho and options.target.ofmt == .macho) { 304 return &(try MachO.openPath(allocator, options)).base; 305 } 306 307 if (options.emit == null) { 308 return switch (options.target.ofmt) { 309 .coff => &(try Coff.createEmpty(allocator, options)).base, 310 .elf => &(try Elf.createEmpty(allocator, options)).base, 311 .macho => unreachable, 312 .wasm => &(try Wasm.createEmpty(allocator, options)).base, 313 .plan9 => return &(try Plan9.createEmpty(allocator, options)).base, 314 .c => unreachable, // Reported error earlier. 315 .spirv => &(try SpirV.createEmpty(allocator, options)).base, 316 .nvptx => &(try NvPtx.createEmpty(allocator, options)).base, 317 .hex => return error.HexObjectFormatUnimplemented, 318 .raw => return error.RawObjectFormatUnimplemented, 319 .dxcontainer => return error.DirectXContainerObjectFormatUnimplemented, 320 }; 321 } 322 const emit = options.emit.?; 323 const use_lld = build_options.have_llvm and options.use_lld; // comptime-known false when !have_llvm 324 const sub_path = if (use_lld) blk: { 325 if (options.module == null) { 326 // No point in opening a file, we would not write anything to it. 327 // Initialize with empty. 328 return switch (options.target.ofmt) { 329 .coff => &(try Coff.createEmpty(allocator, options)).base, 330 .elf => &(try Elf.createEmpty(allocator, options)).base, 331 .macho => unreachable, 332 .plan9 => &(try Plan9.createEmpty(allocator, options)).base, 333 .wasm => &(try Wasm.createEmpty(allocator, options)).base, 334 .c => unreachable, // Reported error earlier. 335 .spirv => &(try SpirV.createEmpty(allocator, options)).base, 336 .nvptx => &(try NvPtx.createEmpty(allocator, options)).base, 337 .hex => return error.HexObjectFormatUnimplemented, 338 .raw => return error.RawObjectFormatUnimplemented, 339 .dxcontainer => return error.DirectXContainerObjectFormatUnimplemented, 340 }; 341 } 342 // Open a temporary object file, not the final output file because we 343 // want to link with LLD. 344 break :blk try std.fmt.allocPrint(allocator, "{s}{s}", .{ 345 emit.sub_path, options.target.ofmt.fileExt(options.target.cpu.arch), 346 }); 347 } else emit.sub_path; 348 errdefer if (use_lld) allocator.free(sub_path); 349 350 const file: *File = f: { 351 switch (options.target.ofmt) { 352 .coff => { 353 if (build_options.only_c) unreachable; 354 break :f &(try Coff.openPath(allocator, sub_path, options)).base; 355 }, 356 .elf => { 357 if (build_options.only_c) unreachable; 358 break :f &(try Elf.openPath(allocator, sub_path, options)).base; 359 }, 360 .macho => unreachable, 361 .plan9 => { 362 if (build_options.only_c) unreachable; 363 break :f &(try Plan9.openPath(allocator, sub_path, options)).base; 364 }, 365 .wasm => { 366 if (build_options.only_c) unreachable; 367 break :f &(try Wasm.openPath(allocator, sub_path, options)).base; 368 }, 369 .c => { 370 break :f &(try C.openPath(allocator, sub_path, options)).base; 371 }, 372 .spirv => { 373 if (build_options.only_c) unreachable; 374 break :f &(try SpirV.openPath(allocator, sub_path, options)).base; 375 }, 376 .nvptx => { 377 if (build_options.only_c) unreachable; 378 break :f &(try NvPtx.openPath(allocator, sub_path, options)).base; 379 }, 380 .hex => return error.HexObjectFormatUnimplemented, 381 .raw => return error.RawObjectFormatUnimplemented, 382 .dxcontainer => return error.DirectXContainerObjectFormatUnimplemented, 383 } 384 }; 385 386 if (use_lld) { 387 // TODO this intermediary_basename isn't enough; in the case of `zig build-exe`, 388 // we also want to put the intermediary object file in the cache while the 389 // main emit directory is the cwd. 390 file.intermediary_basename = sub_path; 391 } 392 393 return file; 394 } 395 396 pub fn cast(base: *File, comptime T: type) ?*T { 397 if (base.tag != T.base_tag) 398 return null; 399 400 return @fieldParentPtr(T, "base", base); 401 } 402 403 pub fn makeWritable(base: *File) !void { 404 switch (base.tag) { 405 .coff, .elf, .macho, .plan9, .wasm => { 406 if (build_options.only_c) unreachable; 407 if (base.file != null) return; 408 const emit = base.options.emit orelse return; 409 base.file = try emit.directory.handle.createFile(emit.sub_path, .{ 410 .truncate = false, 411 .read = true, 412 .mode = determineMode(base.options), 413 }); 414 }, 415 .c, .spirv, .nvptx => {}, 416 } 417 } 418 419 pub fn makeExecutable(base: *File) !void { 420 switch (base.options.output_mode) { 421 .Obj => return, 422 .Lib => switch (base.options.link_mode) { 423 .Static => return, 424 .Dynamic => {}, 425 }, 426 .Exe => {}, 427 } 428 switch (base.tag) { 429 .macho => if (base.file) |f| { 430 if (build_options.only_c) unreachable; 431 if (comptime builtin.target.isDarwin() and builtin.target.cpu.arch == .aarch64) { 432 if (base.options.target.cpu.arch == .aarch64) { 433 // XNU starting with Big Sur running on arm64 is caching inodes of running binaries. 434 // Any change to the binary will effectively invalidate the kernel's cache 435 // resulting in a SIGKILL on each subsequent run. Since when doing incremental 436 // linking we're modifying a binary in-place, this will end up with the kernel 437 // killing it on every subsequent run. To circumvent it, we will copy the file 438 // into a new inode, remove the original file, and rename the copy to match 439 // the original file. This is super messy, but there doesn't seem any other 440 // way to please the XNU. 441 const emit = base.options.emit orelse return; 442 try emit.directory.handle.copyFile(emit.sub_path, emit.directory.handle, emit.sub_path, .{}); 443 } 444 } 445 f.close(); 446 base.file = null; 447 }, 448 .coff, .elf, .plan9, .wasm => if (base.file) |f| { 449 if (build_options.only_c) unreachable; 450 if (base.intermediary_basename != null) { 451 // The file we have open is not the final file that we want to 452 // make executable, so we don't have to close it. 453 return; 454 } 455 f.close(); 456 base.file = null; 457 }, 458 .c, .spirv, .nvptx => {}, 459 } 460 } 461 462 pub const UpdateDeclError = error{ 463 OutOfMemory, 464 Overflow, 465 Underflow, 466 FileTooBig, 467 InputOutput, 468 FilesOpenedWithWrongFlags, 469 IsDir, 470 NoSpaceLeft, 471 Unseekable, 472 PermissionDenied, 473 SwapFile, 474 CorruptedData, 475 SystemResources, 476 OperationAborted, 477 BrokenPipe, 478 ConnectionResetByPeer, 479 ConnectionTimedOut, 480 NotOpenForReading, 481 WouldBlock, 482 AccessDenied, 483 Unexpected, 484 DiskQuota, 485 NotOpenForWriting, 486 AnalysisFail, 487 CodegenFail, 488 EmitFail, 489 NameTooLong, 490 CurrentWorkingDirectoryUnlinked, 491 LockViolation, 492 NetNameDeleted, 493 }; 494 495 /// Called from within the CodeGen to lower a local variable instantion as an unnamed 496 /// constant. Returns the symbol index of the lowered constant in the read-only section 497 /// of the final binary. 498 pub fn lowerUnnamedConst(base: *File, tv: TypedValue, decl_index: Module.Decl.Index) UpdateDeclError!u32 { 499 if (build_options.only_c) @compileError("unreachable"); 500 const decl = base.options.module.?.declPtr(decl_index); 501 log.debug("lowerUnnamedConst {*} ({s})", .{ decl, decl.name }); 502 switch (base.tag) { 503 // zig fmt: off 504 .coff => return @fieldParentPtr(Coff, "base", base).lowerUnnamedConst(tv, decl_index), 505 .elf => return @fieldParentPtr(Elf, "base", base).lowerUnnamedConst(tv, decl_index), 506 .macho => return @fieldParentPtr(MachO, "base", base).lowerUnnamedConst(tv, decl_index), 507 .plan9 => return @fieldParentPtr(Plan9, "base", base).lowerUnnamedConst(tv, decl_index), 508 .spirv => unreachable, 509 .c => unreachable, 510 .wasm => return @fieldParentPtr(Wasm, "base", base).lowerUnnamedConst(tv, decl_index), 511 .nvptx => unreachable, 512 // zig fmt: on 513 } 514 } 515 516 /// Called from within CodeGen to retrieve the symbol index of a global symbol. 517 /// If no symbol exists yet with this name, a new undefined global symbol will 518 /// be created. This symbol may get resolved once all relocatables are (re-)linked. 519 pub fn getGlobalSymbol(base: *File, name: []const u8) UpdateDeclError!u32 { 520 if (build_options.only_c) @compileError("unreachable"); 521 log.debug("getGlobalSymbol '{s}'", .{name}); 522 switch (base.tag) { 523 // zig fmt: off 524 .coff => return @fieldParentPtr(Coff, "base", base).getGlobalSymbol(name), 525 .elf => unreachable, 526 .macho => return @fieldParentPtr(MachO, "base", base).getGlobalSymbol(name), 527 .plan9 => unreachable, 528 .spirv => unreachable, 529 .c => unreachable, 530 .wasm => return @fieldParentPtr(Wasm, "base", base).getGlobalSymbol(name), 531 .nvptx => unreachable, 532 // zig fmt: on 533 } 534 } 535 536 /// May be called before or after updateDeclExports but must be called 537 /// after allocateDeclIndexes for any given Decl. 538 pub fn updateDecl(base: *File, module: *Module, decl_index: Module.Decl.Index) UpdateDeclError!void { 539 const decl = module.declPtr(decl_index); 540 log.debug("updateDecl {*} ({s}), type={}", .{ decl, decl.name, decl.ty.fmtDebug() }); 541 assert(decl.has_tv); 542 if (build_options.only_c) { 543 assert(base.tag == .c); 544 return @fieldParentPtr(C, "base", base).updateDecl(module, decl_index); 545 } 546 switch (base.tag) { 547 // zig fmt: off 548 .coff => return @fieldParentPtr(Coff, "base", base).updateDecl(module, decl_index), 549 .elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl_index), 550 .macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl_index), 551 .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl_index), 552 .wasm => return @fieldParentPtr(Wasm, "base", base).updateDecl(module, decl_index), 553 .spirv => return @fieldParentPtr(SpirV, "base", base).updateDecl(module, decl_index), 554 .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDecl(module, decl_index), 555 .nvptx => return @fieldParentPtr(NvPtx, "base", base).updateDecl(module, decl_index), 556 // zig fmt: on 557 } 558 } 559 560 /// May be called before or after updateDeclExports but must be called 561 /// after allocateDeclIndexes for any given Decl. 562 pub fn updateFunc(base: *File, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) UpdateDeclError!void { 563 const owner_decl = module.declPtr(func.owner_decl); 564 log.debug("updateFunc {*} ({s}), type={}", .{ 565 owner_decl, owner_decl.name, owner_decl.ty.fmtDebug(), 566 }); 567 if (build_options.only_c) { 568 assert(base.tag == .c); 569 return @fieldParentPtr(C, "base", base).updateFunc(module, func, air, liveness); 570 } 571 switch (base.tag) { 572 // zig fmt: off 573 .coff => return @fieldParentPtr(Coff, "base", base).updateFunc(module, func, air, liveness), 574 .elf => return @fieldParentPtr(Elf, "base", base).updateFunc(module, func, air, liveness), 575 .macho => return @fieldParentPtr(MachO, "base", base).updateFunc(module, func, air, liveness), 576 .c => return @fieldParentPtr(C, "base", base).updateFunc(module, func, air, liveness), 577 .wasm => return @fieldParentPtr(Wasm, "base", base).updateFunc(module, func, air, liveness), 578 .spirv => return @fieldParentPtr(SpirV, "base", base).updateFunc(module, func, air, liveness), 579 .plan9 => return @fieldParentPtr(Plan9, "base", base).updateFunc(module, func, air, liveness), 580 .nvptx => return @fieldParentPtr(NvPtx, "base", base).updateFunc(module, func, air, liveness), 581 // zig fmt: on 582 } 583 } 584 585 pub fn updateDeclLineNumber(base: *File, module: *Module, decl: *Module.Decl) UpdateDeclError!void { 586 log.debug("updateDeclLineNumber {*} ({s}), line={}", .{ 587 decl, decl.name, decl.src_line + 1, 588 }); 589 assert(decl.has_tv); 590 if (build_options.only_c) { 591 assert(base.tag == .c); 592 return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl); 593 } 594 switch (base.tag) { 595 .coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl), 596 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl), 597 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl), 598 .c => return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl), 599 .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclLineNumber(module, decl), 600 .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDeclLineNumber(module, decl), 601 .spirv, .nvptx => {}, 602 } 603 } 604 605 /// Must be called before any call to updateDecl or updateDeclExports for 606 /// any given Decl. 607 /// TODO we're transitioning to deleting this function and instead having 608 /// each linker backend notice the first time updateDecl or updateFunc is called, or 609 /// a callee referenced from AIR. 610 pub fn allocateDeclIndexes(base: *File, decl_index: Module.Decl.Index) error{OutOfMemory}!void { 611 const decl = base.options.module.?.declPtr(decl_index); 612 log.debug("allocateDeclIndexes {*} ({s})", .{ decl, decl.name }); 613 if (build_options.only_c) { 614 assert(base.tag == .c); 615 return; 616 } 617 switch (base.tag) { 618 .wasm => return @fieldParentPtr(Wasm, "base", base).allocateDeclIndexes(decl_index), 619 .plan9 => return @fieldParentPtr(Plan9, "base", base).allocateDeclIndexes(decl_index), 620 621 .coff, 622 .elf, 623 .macho, 624 .c, 625 .spirv, 626 .nvptx, 627 => {}, 628 } 629 } 630 631 pub fn releaseLock(self: *File) void { 632 if (self.lock) |*lock| { 633 lock.release(); 634 self.lock = null; 635 } 636 } 637 638 pub fn toOwnedLock(self: *File) Cache.Lock { 639 const lock = self.lock.?; 640 self.lock = null; 641 return lock; 642 } 643 644 pub fn destroy(base: *File) void { 645 base.releaseLock(); 646 if (base.file) |f| f.close(); 647 if (base.intermediary_basename) |sub_path| base.allocator.free(sub_path); 648 base.options.frameworks.deinit(base.allocator); 649 base.options.system_libs.deinit(base.allocator); 650 base.options.force_undefined_symbols.deinit(base.allocator); 651 switch (base.tag) { 652 .coff => { 653 if (build_options.only_c) unreachable; 654 const parent = @fieldParentPtr(Coff, "base", base); 655 parent.deinit(); 656 base.allocator.destroy(parent); 657 }, 658 .elf => { 659 if (build_options.only_c) unreachable; 660 const parent = @fieldParentPtr(Elf, "base", base); 661 parent.deinit(); 662 base.allocator.destroy(parent); 663 }, 664 .macho => { 665 if (build_options.only_c) unreachable; 666 const parent = @fieldParentPtr(MachO, "base", base); 667 parent.deinit(); 668 base.allocator.destroy(parent); 669 }, 670 .c => { 671 const parent = @fieldParentPtr(C, "base", base); 672 parent.deinit(); 673 base.allocator.destroy(parent); 674 }, 675 .wasm => { 676 if (build_options.only_c) unreachable; 677 const parent = @fieldParentPtr(Wasm, "base", base); 678 parent.deinit(); 679 base.allocator.destroy(parent); 680 }, 681 .spirv => { 682 if (build_options.only_c) unreachable; 683 const parent = @fieldParentPtr(SpirV, "base", base); 684 parent.deinit(); 685 base.allocator.destroy(parent); 686 }, 687 .plan9 => { 688 if (build_options.only_c) unreachable; 689 const parent = @fieldParentPtr(Plan9, "base", base); 690 parent.deinit(); 691 base.allocator.destroy(parent); 692 }, 693 .nvptx => { 694 if (build_options.only_c) unreachable; 695 const parent = @fieldParentPtr(NvPtx, "base", base); 696 parent.deinit(); 697 base.allocator.destroy(parent); 698 }, 699 } 700 } 701 702 /// TODO audit this error set. most of these should be collapsed into one error, 703 /// and ErrorFlags should be updated to convey the meaning to the user. 704 pub const FlushError = error{ 705 BadDwarfCfi, 706 CacheUnavailable, 707 CurrentWorkingDirectoryUnlinked, 708 DivisionByZero, 709 DllImportLibraryNotFound, 710 EmptyStubFile, 711 ExpectedFuncType, 712 FailedToEmit, 713 FailedToResolveRelocationTarget, 714 FileSystem, 715 FilesOpenedWithWrongFlags, 716 FlushFailure, 717 FrameworkNotFound, 718 FunctionSignatureMismatch, 719 GlobalTypeMismatch, 720 InvalidCharacter, 721 InvalidEntryKind, 722 InvalidFeatureSet, 723 InvalidFormat, 724 InvalidIndex, 725 InvalidInitFunc, 726 InvalidMagicByte, 727 InvalidWasmVersion, 728 LLDCrashed, 729 LLDReportedFailure, 730 LLD_LinkingIsTODO_ForSpirV, 731 LibCInstallationMissingCRTDir, 732 LibCInstallationNotAvailable, 733 LibraryNotFound, 734 LinkingWithoutZigSourceUnimplemented, 735 MalformedArchive, 736 MalformedDwarf, 737 MalformedSection, 738 MemoryTooBig, 739 MemoryTooSmall, 740 MismatchedCpuArchitecture, 741 MissAlignment, 742 MissingEndForBody, 743 MissingEndForExpression, 744 /// TODO: this should be removed from the error set in favor of using ErrorFlags 745 MissingMainEntrypoint, 746 /// TODO: this should be removed from the error set in favor of using ErrorFlags 747 MissingSection, 748 MissingSymbol, 749 MissingTableSymbols, 750 ModuleNameMismatch, 751 MultipleSymbolDefinitions, 752 NoObjectsToLink, 753 NotObject, 754 NotObjectFile, 755 NotSupported, 756 OutOfMemory, 757 Overflow, 758 PermissionDenied, 759 StreamTooLong, 760 SwapFile, 761 SymbolCollision, 762 SymbolMismatchingType, 763 TODOImplementPlan9Objs, 764 TODOImplementWritingLibFiles, 765 TODOImplementWritingStaticLibFiles, 766 UnableToSpawnSelf, 767 UnableToSpawnWasm, 768 UnableToWriteArchive, 769 UndefinedLocal, 770 /// TODO: merge with UndefinedSymbolReference 771 UndefinedSymbol, 772 /// TODO: merge with UndefinedSymbol 773 UndefinedSymbolReference, 774 Underflow, 775 UnexpectedRemainder, 776 UnexpectedTable, 777 UnexpectedValue, 778 UnhandledDwFormValue, 779 UnhandledSymbolType, 780 UnknownFeature, 781 Unseekable, 782 UnsupportedCpuArchitecture, 783 UnsupportedVersion, 784 } || 785 fs.File.WriteFileError || 786 fs.File.OpenError || 787 std.ChildProcess.SpawnError || 788 fs.Dir.CopyFileError; 789 790 /// Commit pending changes and write headers. Takes into account final output mode 791 /// and `use_lld`, not only `effectiveOutputMode`. 792 pub fn flush(base: *File, comp: *Compilation, prog_node: *std.Progress.Node) FlushError!void { 793 if (build_options.only_c) { 794 assert(base.tag == .c); 795 return @fieldParentPtr(C, "base", base).flush(comp, prog_node); 796 } 797 if (comp.clang_preprocessor_mode == .yes) { 798 const emit = base.options.emit orelse return; // -fno-emit-bin 799 // TODO: avoid extra link step when it's just 1 object file (the `zig cc -c` case) 800 // Until then, we do `lld -r -o output.o input.o` even though the output is the same 801 // as the input. For the preprocessing case (`zig cc -E -o foo`) we copy the file 802 // to the final location. See also the corresponding TODO in Coff linking. 803 const full_out_path = try emit.directory.join(comp.gpa, &[_][]const u8{emit.sub_path}); 804 defer comp.gpa.free(full_out_path); 805 assert(comp.c_object_table.count() == 1); 806 const the_key = comp.c_object_table.keys()[0]; 807 const cached_pp_file_path = the_key.status.success.object_path; 808 try fs.cwd().copyFile(cached_pp_file_path, fs.cwd(), full_out_path, .{}); 809 return; 810 } 811 812 const use_lld = build_options.have_llvm and base.options.use_lld; 813 if (use_lld and base.options.output_mode == .Lib and base.options.link_mode == .Static) { 814 return base.linkAsArchive(comp, prog_node); 815 } 816 switch (base.tag) { 817 .coff => return @fieldParentPtr(Coff, "base", base).flush(comp, prog_node), 818 .elf => return @fieldParentPtr(Elf, "base", base).flush(comp, prog_node), 819 .macho => return @fieldParentPtr(MachO, "base", base).flush(comp, prog_node), 820 .c => return @fieldParentPtr(C, "base", base).flush(comp, prog_node), 821 .wasm => return @fieldParentPtr(Wasm, "base", base).flush(comp, prog_node), 822 .spirv => return @fieldParentPtr(SpirV, "base", base).flush(comp, prog_node), 823 .plan9 => return @fieldParentPtr(Plan9, "base", base).flush(comp, prog_node), 824 .nvptx => return @fieldParentPtr(NvPtx, "base", base).flush(comp, prog_node), 825 } 826 } 827 828 /// Commit pending changes and write headers. Works based on `effectiveOutputMode` 829 /// rather than final output mode. 830 pub fn flushModule(base: *File, comp: *Compilation, prog_node: *std.Progress.Node) FlushError!void { 831 if (build_options.only_c) { 832 assert(base.tag == .c); 833 return @fieldParentPtr(C, "base", base).flushModule(comp, prog_node); 834 } 835 switch (base.tag) { 836 .coff => return @fieldParentPtr(Coff, "base", base).flushModule(comp, prog_node), 837 .elf => return @fieldParentPtr(Elf, "base", base).flushModule(comp, prog_node), 838 .macho => return @fieldParentPtr(MachO, "base", base).flushModule(comp, prog_node), 839 .c => return @fieldParentPtr(C, "base", base).flushModule(comp, prog_node), 840 .wasm => return @fieldParentPtr(Wasm, "base", base).flushModule(comp, prog_node), 841 .spirv => return @fieldParentPtr(SpirV, "base", base).flushModule(comp, prog_node), 842 .plan9 => return @fieldParentPtr(Plan9, "base", base).flushModule(comp, prog_node), 843 .nvptx => return @fieldParentPtr(NvPtx, "base", base).flushModule(comp, prog_node), 844 } 845 } 846 847 /// Called when a Decl is deleted from the Module. 848 pub fn freeDecl(base: *File, decl_index: Module.Decl.Index) void { 849 if (build_options.only_c) { 850 assert(base.tag == .c); 851 return @fieldParentPtr(C, "base", base).freeDecl(decl_index); 852 } 853 switch (base.tag) { 854 .coff => @fieldParentPtr(Coff, "base", base).freeDecl(decl_index), 855 .elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl_index), 856 .macho => @fieldParentPtr(MachO, "base", base).freeDecl(decl_index), 857 .c => @fieldParentPtr(C, "base", base).freeDecl(decl_index), 858 .wasm => @fieldParentPtr(Wasm, "base", base).freeDecl(decl_index), 859 .spirv => @fieldParentPtr(SpirV, "base", base).freeDecl(decl_index), 860 .plan9 => @fieldParentPtr(Plan9, "base", base).freeDecl(decl_index), 861 .nvptx => @fieldParentPtr(NvPtx, "base", base).freeDecl(decl_index), 862 } 863 } 864 865 pub fn errorFlags(base: *File) ErrorFlags { 866 switch (base.tag) { 867 .coff => return @fieldParentPtr(Coff, "base", base).error_flags, 868 .elf => return @fieldParentPtr(Elf, "base", base).error_flags, 869 .macho => return @fieldParentPtr(MachO, "base", base).error_flags, 870 .plan9 => return @fieldParentPtr(Plan9, "base", base).error_flags, 871 .c => return .{ .no_entry_point_found = false }, 872 .wasm, .spirv, .nvptx => return ErrorFlags{}, 873 } 874 } 875 876 pub const UpdateDeclExportsError = error{ 877 OutOfMemory, 878 AnalysisFail, 879 }; 880 881 /// May be called before or after updateDecl, but must be called after 882 /// allocateDeclIndexes for any given Decl. 883 pub fn updateDeclExports( 884 base: *File, 885 module: *Module, 886 decl_index: Module.Decl.Index, 887 exports: []const *Module.Export, 888 ) UpdateDeclExportsError!void { 889 const decl = module.declPtr(decl_index); 890 log.debug("updateDeclExports {*} ({s})", .{ decl, decl.name }); 891 assert(decl.has_tv); 892 if (build_options.only_c) { 893 assert(base.tag == .c); 894 return @fieldParentPtr(C, "base", base).updateDeclExports(module, decl_index, exports); 895 } 896 switch (base.tag) { 897 .coff => return @fieldParentPtr(Coff, "base", base).updateDeclExports(module, decl_index, exports), 898 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl_index, exports), 899 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclExports(module, decl_index, exports), 900 .c => return @fieldParentPtr(C, "base", base).updateDeclExports(module, decl_index, exports), 901 .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclExports(module, decl_index, exports), 902 .spirv => return @fieldParentPtr(SpirV, "base", base).updateDeclExports(module, decl_index, exports), 903 .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDeclExports(module, decl_index, exports), 904 .nvptx => return @fieldParentPtr(NvPtx, "base", base).updateDeclExports(module, decl_index, exports), 905 } 906 } 907 908 pub const RelocInfo = struct { 909 parent_atom_index: u32, 910 offset: u64, 911 addend: u32, 912 }; 913 914 /// Get allocated `Decl`'s address in virtual memory. 915 /// The linker is passed information about the containing atom, `parent_atom_index`, and offset within it's 916 /// memory buffer, `offset`, so that it can make a note of potential relocation sites, should the 917 /// `Decl`'s address was not yet resolved, or the containing atom gets moved in virtual memory. 918 /// May be called before or after updateFunc/updateDecl therefore it is up to the linker to allocate 919 /// the block/atom. 920 pub fn getDeclVAddr(base: *File, decl_index: Module.Decl.Index, reloc_info: RelocInfo) !u64 { 921 if (build_options.only_c) unreachable; 922 switch (base.tag) { 923 .coff => return @fieldParentPtr(Coff, "base", base).getDeclVAddr(decl_index, reloc_info), 924 .elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl_index, reloc_info), 925 .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl_index, reloc_info), 926 .plan9 => return @fieldParentPtr(Plan9, "base", base).getDeclVAddr(decl_index, reloc_info), 927 .c => unreachable, 928 .wasm => return @fieldParentPtr(Wasm, "base", base).getDeclVAddr(decl_index, reloc_info), 929 .spirv => unreachable, 930 .nvptx => unreachable, 931 } 932 } 933 934 /// This function is called by the frontend before flush(). It communicates that 935 /// `options.bin_file.emit` directory needs to be renamed from 936 /// `[zig-cache]/tmp/[random]` to `[zig-cache]/o/[digest]`. 937 /// The frontend would like to simply perform a file system rename, however, 938 /// some linker backends care about the file paths of the objects they are linking. 939 /// So this function call tells linker backends to rename the paths of object files 940 /// to observe the new directory path. 941 /// Linker backends which do not have this requirement can fall back to the simple 942 /// implementation at the bottom of this function. 943 /// This function is only called when CacheMode is `whole`. 944 pub fn renameTmpIntoCache( 945 base: *File, 946 cache_directory: Compilation.Directory, 947 tmp_dir_sub_path: []const u8, 948 o_sub_path: []const u8, 949 ) !void { 950 // So far, none of the linker backends need to respond to this event, however, 951 // it makes sense that they might want to. So we leave this mechanism here 952 // for now. Once the linker backends get more mature, if it turns out this 953 // is not needed we can refactor this into having the frontend do the rename 954 // directly, and remove this function from link.zig. 955 _ = base; 956 while (true) { 957 if (builtin.os.tag == .windows) { 958 // Work around windows `renameW` can't fail with `PathAlreadyExists` 959 // See https://github.com/ziglang/zig/issues/8362 960 if (cache_directory.handle.access(o_sub_path, .{})) |_| { 961 try cache_directory.handle.deleteTree(o_sub_path); 962 continue; 963 } else |err| switch (err) { 964 error.FileNotFound => {}, 965 else => |e| return e, 966 } 967 std.fs.rename( 968 cache_directory.handle, 969 tmp_dir_sub_path, 970 cache_directory.handle, 971 o_sub_path, 972 ) catch |err| { 973 log.err("unable to rename cache dir {s} to {s}: {s}", .{ tmp_dir_sub_path, o_sub_path, @errorName(err) }); 974 return err; 975 }; 976 break; 977 } else { 978 std.fs.rename( 979 cache_directory.handle, 980 tmp_dir_sub_path, 981 cache_directory.handle, 982 o_sub_path, 983 ) catch |err| switch (err) { 984 error.PathAlreadyExists => { 985 try cache_directory.handle.deleteTree(o_sub_path); 986 continue; 987 }, 988 else => |e| return e, 989 }; 990 break; 991 } 992 } 993 } 994 995 pub fn linkAsArchive(base: *File, comp: *Compilation, prog_node: *std.Progress.Node) FlushError!void { 996 const tracy = trace(@src()); 997 defer tracy.end(); 998 999 var arena_allocator = std.heap.ArenaAllocator.init(base.allocator); 1000 defer arena_allocator.deinit(); 1001 const arena = arena_allocator.allocator(); 1002 1003 const directory = base.options.emit.?.directory; // Just an alias to make it shorter to type. 1004 const full_out_path = try directory.join(arena, &[_][]const u8{base.options.emit.?.sub_path}); 1005 const full_out_path_z = try arena.dupeZ(u8, full_out_path); 1006 1007 // If there is no Zig code to compile, then we should skip flushing the output file 1008 // because it will not be part of the linker line anyway. 1009 const module_obj_path: ?[]const u8 = if (base.options.module != null) blk: { 1010 try base.flushModule(comp, prog_node); 1011 1012 const dirname = fs.path.dirname(full_out_path_z) orelse "."; 1013 break :blk try fs.path.join(arena, &.{ dirname, base.intermediary_basename.? }); 1014 } else null; 1015 1016 log.debug("module_obj_path={s}", .{if (module_obj_path) |s| s else "(null)"}); 1017 1018 const compiler_rt_path: ?[]const u8 = if (base.options.include_compiler_rt) 1019 comp.compiler_rt_obj.?.full_object_path 1020 else 1021 null; 1022 1023 // This function follows the same pattern as link.Elf.linkWithLLD so if you want some 1024 // insight as to what's going on here you can read that function body which is more 1025 // well-commented. 1026 1027 const id_symlink_basename = "llvm-ar.id"; 1028 1029 var man: Cache.Manifest = undefined; 1030 defer if (!base.options.disable_lld_caching) man.deinit(); 1031 1032 var digest: [Cache.hex_digest_len]u8 = undefined; 1033 1034 if (!base.options.disable_lld_caching) { 1035 man = comp.cache_parent.obtain(); 1036 1037 // We are about to obtain this lock, so here we give other processes a chance first. 1038 base.releaseLock(); 1039 1040 for (base.options.objects) |obj| { 1041 _ = try man.addFile(obj.path, null); 1042 man.hash.add(obj.must_link); 1043 } 1044 for (comp.c_object_table.keys()) |key| { 1045 _ = try man.addFile(key.status.success.object_path, null); 1046 } 1047 try man.addOptionalFile(module_obj_path); 1048 try man.addOptionalFile(compiler_rt_path); 1049 1050 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. 1051 _ = try man.hit(); 1052 digest = man.final(); 1053 1054 var prev_digest_buf: [digest.len]u8 = undefined; 1055 const prev_digest: []u8 = Cache.readSmallFile( 1056 directory.handle, 1057 id_symlink_basename, 1058 &prev_digest_buf, 1059 ) catch |err| b: { 1060 log.debug("archive new_digest={s} readFile error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) }); 1061 break :b prev_digest_buf[0..0]; 1062 }; 1063 if (mem.eql(u8, prev_digest, &digest)) { 1064 log.debug("archive digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)}); 1065 base.lock = man.toOwnedLock(); 1066 return; 1067 } 1068 1069 // We are about to change the output file to be different, so we invalidate the build hash now. 1070 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { 1071 error.FileNotFound => {}, 1072 else => |e| return e, 1073 }; 1074 } 1075 1076 const num_object_files = base.options.objects.len + comp.c_object_table.count() + 2; 1077 var object_files = try std.ArrayList([*:0]const u8).initCapacity(base.allocator, num_object_files); 1078 defer object_files.deinit(); 1079 1080 for (base.options.objects) |obj| { 1081 object_files.appendAssumeCapacity(try arena.dupeZ(u8, obj.path)); 1082 } 1083 for (comp.c_object_table.keys()) |key| { 1084 object_files.appendAssumeCapacity(try arena.dupeZ(u8, key.status.success.object_path)); 1085 } 1086 if (module_obj_path) |p| { 1087 object_files.appendAssumeCapacity(try arena.dupeZ(u8, p)); 1088 } 1089 if (compiler_rt_path) |p| { 1090 object_files.appendAssumeCapacity(try arena.dupeZ(u8, p)); 1091 } 1092 1093 if (base.options.verbose_link) { 1094 std.debug.print("ar rcs {s}", .{full_out_path_z}); 1095 for (object_files.items) |arg| { 1096 std.debug.print(" {s}", .{arg}); 1097 } 1098 std.debug.print("\n", .{}); 1099 } 1100 1101 const llvm_bindings = @import("codegen/llvm/bindings.zig"); 1102 const llvm = @import("codegen/llvm.zig"); 1103 const os_tag = llvm.targetOs(base.options.target.os.tag); 1104 const bad = llvm_bindings.WriteArchive(full_out_path_z, object_files.items.ptr, object_files.items.len, os_tag); 1105 if (bad) return error.UnableToWriteArchive; 1106 1107 if (!base.options.disable_lld_caching) { 1108 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { 1109 log.warn("failed to save archive hash digest file: {s}", .{@errorName(err)}); 1110 }; 1111 1112 man.writeManifest() catch |err| { 1113 log.warn("failed to write cache manifest when archiving: {s}", .{@errorName(err)}); 1114 }; 1115 1116 base.lock = man.toOwnedLock(); 1117 } 1118 } 1119 1120 pub const Tag = enum { 1121 coff, 1122 elf, 1123 macho, 1124 c, 1125 wasm, 1126 spirv, 1127 plan9, 1128 nvptx, 1129 }; 1130 1131 pub const ErrorFlags = struct { 1132 no_entry_point_found: bool = false, 1133 missing_libc: bool = false, 1134 }; 1135 1136 pub const C = @import("link/C.zig"); 1137 pub const Coff = @import("link/Coff.zig"); 1138 pub const Plan9 = @import("link/Plan9.zig"); 1139 pub const Elf = @import("link/Elf.zig"); 1140 pub const MachO = @import("link/MachO.zig"); 1141 pub const SpirV = @import("link/SpirV.zig"); 1142 pub const Wasm = @import("link/Wasm.zig"); 1143 pub const NvPtx = @import("link/NvPtx.zig"); 1144 pub const Dwarf = @import("link/Dwarf.zig"); 1145 }; 1146 1147 pub fn determineMode(options: Options) fs.File.Mode { 1148 // On common systems with a 0o022 umask, 0o777 will still result in a file created 1149 // with 0o755 permissions, but it works appropriately if the system is configured 1150 // more leniently. As another data point, C's fopen seems to open files with the 1151 // 666 mode. 1152 const executable_mode = if (builtin.target.os.tag == .windows) 0 else 0o777; 1153 switch (options.effectiveOutputMode()) { 1154 .Lib => return switch (options.link_mode) { 1155 .Dynamic => executable_mode, 1156 .Static => fs.File.default_mode, 1157 }, 1158 .Exe => return executable_mode, 1159 .Obj => return fs.File.default_mode, 1160 } 1161 }