blob eef6e827 (90443B) - 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 = std.Build.Cache; 14 const Path = std.Build.Cache.Path; 15 const Directory = std.Build.Cache.Directory; 16 const Compilation = @import("Compilation.zig"); 17 const LibCInstallation = std.zig.LibCInstallation; 18 const Liveness = @import("Liveness.zig"); 19 const Zcu = @import("Zcu.zig"); 20 const InternPool = @import("InternPool.zig"); 21 const Type = @import("Type.zig"); 22 const Value = @import("Value.zig"); 23 const LlvmObject = @import("codegen/llvm.zig").Object; 24 const lldMain = @import("main.zig").lldMain; 25 const Package = @import("Package.zig"); 26 const dev = @import("dev.zig"); 27 const ThreadSafeQueue = @import("ThreadSafeQueue.zig").ThreadSafeQueue; 28 const target_util = @import("target.zig"); 29 30 pub const LdScript = @import("link/LdScript.zig"); 31 32 pub const Diags = struct { 33 /// Stored here so that function definitions can distinguish between 34 /// needing an allocator for things besides error reporting. 35 gpa: Allocator, 36 mutex: std.Thread.Mutex, 37 msgs: std.ArrayListUnmanaged(Msg), 38 flags: Flags, 39 lld: std.ArrayListUnmanaged(Lld), 40 41 pub const Flags = packed struct { 42 no_entry_point_found: bool = false, 43 missing_libc: bool = false, 44 alloc_failure_occurred: bool = false, 45 46 const Int = blk: { 47 const bits = @typeInfo(@This()).@"struct".fields.len; 48 break :blk @Type(.{ .int = .{ 49 .signedness = .unsigned, 50 .bits = bits, 51 } }); 52 }; 53 54 pub fn anySet(ef: Flags) bool { 55 return @as(Int, @bitCast(ef)) > 0; 56 } 57 }; 58 59 pub const Lld = struct { 60 /// Allocated with gpa. 61 msg: []const u8, 62 context_lines: []const []const u8 = &.{}, 63 64 pub fn deinit(self: *Lld, gpa: Allocator) void { 65 for (self.context_lines) |line| gpa.free(line); 66 gpa.free(self.context_lines); 67 gpa.free(self.msg); 68 self.* = undefined; 69 } 70 }; 71 72 pub const Msg = struct { 73 msg: []const u8, 74 notes: []Msg = &.{}, 75 76 pub fn deinit(self: *Msg, gpa: Allocator) void { 77 for (self.notes) |*note| note.deinit(gpa); 78 gpa.free(self.notes); 79 gpa.free(self.msg); 80 } 81 }; 82 83 pub const ErrorWithNotes = struct { 84 diags: *Diags, 85 /// Allocated index in diags.msgs array. 86 index: usize, 87 /// Next available note slot. 88 note_slot: usize = 0, 89 90 pub fn addMsg( 91 err: ErrorWithNotes, 92 comptime format: []const u8, 93 args: anytype, 94 ) error{OutOfMemory}!void { 95 const gpa = err.diags.gpa; 96 const err_msg = &err.diags.msgs.items[err.index]; 97 err_msg.msg = try std.fmt.allocPrint(gpa, format, args); 98 } 99 100 pub fn addNote( 101 err: *ErrorWithNotes, 102 comptime format: []const u8, 103 args: anytype, 104 ) error{OutOfMemory}!void { 105 const gpa = err.diags.gpa; 106 const err_msg = &err.diags.msgs.items[err.index]; 107 assert(err.note_slot < err_msg.notes.len); 108 err_msg.notes[err.note_slot] = .{ .msg = try std.fmt.allocPrint(gpa, format, args) }; 109 err.note_slot += 1; 110 } 111 }; 112 113 pub fn init(gpa: Allocator) Diags { 114 return .{ 115 .gpa = gpa, 116 .mutex = .{}, 117 .msgs = .empty, 118 .flags = .{}, 119 .lld = .empty, 120 }; 121 } 122 123 pub fn deinit(diags: *Diags) void { 124 const gpa = diags.gpa; 125 126 for (diags.msgs.items) |*item| item.deinit(gpa); 127 diags.msgs.deinit(gpa); 128 129 for (diags.lld.items) |*item| item.deinit(gpa); 130 diags.lld.deinit(gpa); 131 132 diags.* = undefined; 133 } 134 135 pub fn hasErrors(diags: *Diags) bool { 136 return diags.msgs.items.len > 0 or diags.flags.anySet(); 137 } 138 139 pub fn lockAndParseLldStderr(diags: *Diags, prefix: []const u8, stderr: []const u8) void { 140 diags.mutex.lock(); 141 defer diags.mutex.unlock(); 142 143 diags.parseLldStderr(prefix, stderr) catch diags.setAllocFailure(); 144 } 145 146 fn parseLldStderr( 147 diags: *Diags, 148 prefix: []const u8, 149 stderr: []const u8, 150 ) Allocator.Error!void { 151 const gpa = diags.gpa; 152 153 var context_lines = std.ArrayList([]const u8).init(gpa); 154 defer context_lines.deinit(); 155 156 var current_err: ?*Lld = null; 157 var lines = mem.splitSequence(u8, stderr, if (builtin.os.tag == .windows) "\r\n" else "\n"); 158 while (lines.next()) |line| { 159 if (line.len > prefix.len + ":".len and 160 mem.eql(u8, line[0..prefix.len], prefix) and line[prefix.len] == ':') 161 { 162 if (current_err) |err| { 163 err.context_lines = try context_lines.toOwnedSlice(); 164 } 165 166 var split = mem.splitSequence(u8, line, "error: "); 167 _ = split.first(); 168 169 const duped_msg = try std.fmt.allocPrint(gpa, "{s}: {s}", .{ prefix, split.rest() }); 170 errdefer gpa.free(duped_msg); 171 172 current_err = try diags.lld.addOne(gpa); 173 current_err.?.* = .{ .msg = duped_msg }; 174 } else if (current_err != null) { 175 const context_prefix = ">>> "; 176 var trimmed = mem.trimRight(u8, line, &std.ascii.whitespace); 177 if (mem.startsWith(u8, trimmed, context_prefix)) { 178 trimmed = trimmed[context_prefix.len..]; 179 } 180 181 if (trimmed.len > 0) { 182 const duped_line = try gpa.dupe(u8, trimmed); 183 try context_lines.append(duped_line); 184 } 185 } 186 } 187 188 if (current_err) |err| { 189 err.context_lines = try context_lines.toOwnedSlice(); 190 } 191 } 192 193 pub fn fail(diags: *Diags, comptime format: []const u8, args: anytype) error{LinkFailure} { 194 @branchHint(.cold); 195 addError(diags, format, args); 196 return error.LinkFailure; 197 } 198 199 pub fn addError(diags: *Diags, comptime format: []const u8, args: anytype) void { 200 @branchHint(.cold); 201 const gpa = diags.gpa; 202 const eu_main_msg = std.fmt.allocPrint(gpa, format, args); 203 diags.mutex.lock(); 204 defer diags.mutex.unlock(); 205 addErrorLockedFallible(diags, eu_main_msg) catch |err| switch (err) { 206 error.OutOfMemory => diags.setAllocFailureLocked(), 207 }; 208 } 209 210 fn addErrorLockedFallible(diags: *Diags, eu_main_msg: Allocator.Error![]u8) Allocator.Error!void { 211 const gpa = diags.gpa; 212 const main_msg = try eu_main_msg; 213 errdefer gpa.free(main_msg); 214 try diags.msgs.append(gpa, .{ .msg = main_msg }); 215 } 216 217 pub fn addErrorWithNotes(diags: *Diags, note_count: usize) error{OutOfMemory}!ErrorWithNotes { 218 @branchHint(.cold); 219 const gpa = diags.gpa; 220 diags.mutex.lock(); 221 defer diags.mutex.unlock(); 222 try diags.msgs.ensureUnusedCapacity(gpa, 1); 223 return addErrorWithNotesAssumeCapacity(diags, note_count); 224 } 225 226 pub fn addErrorWithNotesAssumeCapacity(diags: *Diags, note_count: usize) error{OutOfMemory}!ErrorWithNotes { 227 @branchHint(.cold); 228 const gpa = diags.gpa; 229 const index = diags.msgs.items.len; 230 const err = diags.msgs.addOneAssumeCapacity(); 231 err.* = .{ 232 .msg = undefined, 233 .notes = try gpa.alloc(Msg, note_count), 234 }; 235 return .{ 236 .diags = diags, 237 .index = index, 238 }; 239 } 240 241 pub fn addMissingLibraryError( 242 diags: *Diags, 243 checked_paths: []const []const u8, 244 comptime format: []const u8, 245 args: anytype, 246 ) void { 247 @branchHint(.cold); 248 const gpa = diags.gpa; 249 const eu_main_msg = std.fmt.allocPrint(gpa, format, args); 250 diags.mutex.lock(); 251 defer diags.mutex.unlock(); 252 addMissingLibraryErrorLockedFallible(diags, checked_paths, eu_main_msg) catch |err| switch (err) { 253 error.OutOfMemory => diags.setAllocFailureLocked(), 254 }; 255 } 256 257 fn addMissingLibraryErrorLockedFallible( 258 diags: *Diags, 259 checked_paths: []const []const u8, 260 eu_main_msg: Allocator.Error![]u8, 261 ) Allocator.Error!void { 262 const gpa = diags.gpa; 263 const main_msg = try eu_main_msg; 264 errdefer gpa.free(main_msg); 265 try diags.msgs.ensureUnusedCapacity(gpa, 1); 266 const notes = try gpa.alloc(Msg, checked_paths.len); 267 errdefer gpa.free(notes); 268 for (checked_paths, notes) |path, *note| { 269 note.* = .{ .msg = try std.fmt.allocPrint(gpa, "tried {s}", .{path}) }; 270 } 271 diags.msgs.appendAssumeCapacity(.{ 272 .msg = main_msg, 273 .notes = notes, 274 }); 275 } 276 277 pub fn addParseError( 278 diags: *Diags, 279 path: Path, 280 comptime format: []const u8, 281 args: anytype, 282 ) void { 283 @branchHint(.cold); 284 const gpa = diags.gpa; 285 const eu_main_msg = std.fmt.allocPrint(gpa, format, args); 286 diags.mutex.lock(); 287 defer diags.mutex.unlock(); 288 addParseErrorLockedFallible(diags, path, eu_main_msg) catch |err| switch (err) { 289 error.OutOfMemory => diags.setAllocFailureLocked(), 290 }; 291 } 292 293 fn addParseErrorLockedFallible(diags: *Diags, path: Path, m: Allocator.Error![]u8) Allocator.Error!void { 294 const gpa = diags.gpa; 295 const main_msg = try m; 296 errdefer gpa.free(main_msg); 297 try diags.msgs.ensureUnusedCapacity(gpa, 1); 298 const note = try std.fmt.allocPrint(gpa, "while parsing {}", .{path}); 299 errdefer gpa.free(note); 300 const notes = try gpa.create([1]Msg); 301 errdefer gpa.destroy(notes); 302 notes.* = .{.{ .msg = note }}; 303 diags.msgs.appendAssumeCapacity(.{ 304 .msg = main_msg, 305 .notes = notes, 306 }); 307 } 308 309 pub fn failParse( 310 diags: *Diags, 311 path: Path, 312 comptime format: []const u8, 313 args: anytype, 314 ) error{LinkFailure} { 315 @branchHint(.cold); 316 addParseError(diags, path, format, args); 317 return error.LinkFailure; 318 } 319 320 pub fn setAllocFailure(diags: *Diags) void { 321 @branchHint(.cold); 322 diags.mutex.lock(); 323 defer diags.mutex.unlock(); 324 setAllocFailureLocked(diags); 325 } 326 327 fn setAllocFailureLocked(diags: *Diags) void { 328 log.debug("memory allocation failure", .{}); 329 diags.flags.alloc_failure_occurred = true; 330 } 331 332 pub fn addMessagesToBundle(diags: *const Diags, bundle: *std.zig.ErrorBundle.Wip) Allocator.Error!void { 333 for (diags.msgs.items) |link_err| { 334 try bundle.addRootErrorMessage(.{ 335 .msg = try bundle.addString(link_err.msg), 336 .notes_len = @intCast(link_err.notes.len), 337 }); 338 const notes_start = try bundle.reserveNotes(@intCast(link_err.notes.len)); 339 for (link_err.notes, 0..) |note, i| { 340 bundle.extra.items[notes_start + i] = @intFromEnum(try bundle.addErrorMessage(.{ 341 .msg = try bundle.addString(note.msg), 342 })); 343 } 344 } 345 } 346 }; 347 348 pub const producer_string = if (builtin.is_test) "zig test" else "zig " ++ build_options.version; 349 350 pub const File = struct { 351 tag: Tag, 352 353 /// The owner of this output File. 354 comp: *Compilation, 355 emit: Path, 356 357 file: ?fs.File, 358 /// When linking with LLD, this linker code will output an object file only at 359 /// this location, and then this path can be placed on the LLD linker line. 360 zcu_object_sub_path: ?[]const u8 = null, 361 disable_lld_caching: bool, 362 gc_sections: bool, 363 print_gc_sections: bool, 364 build_id: std.zig.BuildId, 365 allow_shlib_undefined: bool, 366 stack_size: u64, 367 368 /// Prevents other processes from clobbering files in the output directory 369 /// of this linking operation. 370 lock: ?Cache.Lock = null, 371 child_pid: ?std.process.Child.Id = null, 372 373 pub const OpenOptions = struct { 374 symbol_count_hint: u64 = 32, 375 program_code_size_hint: u64 = 256 * 1024, 376 377 /// This may depend on what symbols are found during the linking process. 378 entry: Entry, 379 /// Virtual address of the entry point procedure relative to image base. 380 entry_addr: ?u64, 381 stack_size: ?u64, 382 image_base: ?u64, 383 emit_relocs: bool, 384 z_nodelete: bool, 385 z_notext: bool, 386 z_defs: bool, 387 z_origin: bool, 388 z_nocopyreloc: bool, 389 z_now: bool, 390 z_relro: bool, 391 z_common_page_size: ?u64, 392 z_max_page_size: ?u64, 393 tsaware: bool, 394 nxcompat: bool, 395 dynamicbase: bool, 396 compress_debug_sections: Elf.CompressDebugSections, 397 bind_global_refs_locally: bool, 398 import_symbols: bool, 399 import_table: bool, 400 export_table: bool, 401 initial_memory: ?u64, 402 max_memory: ?u64, 403 export_symbol_names: []const []const u8, 404 global_base: ?u64, 405 build_id: std.zig.BuildId, 406 disable_lld_caching: bool, 407 hash_style: Elf.HashStyle, 408 sort_section: ?Elf.SortSection, 409 major_subsystem_version: ?u16, 410 minor_subsystem_version: ?u16, 411 gc_sections: ?bool, 412 repro: bool, 413 allow_shlib_undefined: ?bool, 414 allow_undefined_version: bool, 415 enable_new_dtags: ?bool, 416 subsystem: ?std.Target.SubSystem, 417 linker_script: ?[]const u8, 418 version_script: ?[]const u8, 419 soname: ?[]const u8, 420 print_gc_sections: bool, 421 print_icf_sections: bool, 422 print_map: bool, 423 424 /// Use a wrapper function for symbol. Any undefined reference to symbol 425 /// will be resolved to __wrap_symbol. Any undefined reference to 426 /// __real_symbol will be resolved to symbol. This can be used to provide a 427 /// wrapper for a system function. The wrapper function should be called 428 /// __wrap_symbol. If it wishes to call the system function, it should call 429 /// __real_symbol. 430 symbol_wrap_set: std.StringArrayHashMapUnmanaged(void), 431 432 compatibility_version: ?std.SemanticVersion, 433 434 // TODO: remove this. libraries are resolved by the frontend. 435 lib_directories: []const Directory, 436 framework_dirs: []const []const u8, 437 rpath_list: []const []const u8, 438 439 /// Zig compiler development linker flags. 440 /// Enable dumping of linker's state as JSON. 441 enable_link_snapshots: bool, 442 443 /// Darwin-specific linker flags: 444 /// Install name for the dylib 445 install_name: ?[]const u8, 446 /// Path to entitlements file 447 entitlements: ?[]const u8, 448 /// size of the __PAGEZERO segment 449 pagezero_size: ?u64, 450 /// Set minimum space for future expansion of the load commands 451 headerpad_size: ?u32, 452 /// Set enough space as if all paths were MATPATHLEN 453 headerpad_max_install_names: bool, 454 /// Remove dylibs that are unreachable by the entry point or exported symbols 455 dead_strip_dylibs: bool, 456 frameworks: []const MachO.Framework, 457 darwin_sdk_layout: ?MachO.SdkLayout, 458 /// Force load all members of static archives that implement an 459 /// Objective-C class or category 460 force_load_objc: bool, 461 462 /// Windows-specific linker flags: 463 /// PDB source path prefix to instruct the linker how to resolve relative 464 /// paths when consolidating CodeView streams into a single PDB file. 465 pdb_source_path: ?[]const u8, 466 /// PDB output path 467 pdb_out_path: ?[]const u8, 468 /// .def file to specify when linking 469 module_definition_file: ?[]const u8, 470 471 pub const Entry = union(enum) { 472 default, 473 disabled, 474 enabled, 475 named: []const u8, 476 }; 477 }; 478 479 /// Attempts incremental linking, if the file already exists. If 480 /// incremental linking fails, falls back to truncating the file and 481 /// rewriting it. A malicious file is detected as incremental link failure 482 /// and does not cause Illegal Behavior. This operation is not atomic. 483 /// `arena` is used for allocations with the same lifetime as the created File. 484 pub fn open( 485 arena: Allocator, 486 comp: *Compilation, 487 emit: Path, 488 options: OpenOptions, 489 ) !*File { 490 switch (Tag.fromObjectFormat(comp.root_mod.resolved_target.result.ofmt)) { 491 inline else => |tag| { 492 dev.check(tag.devFeature()); 493 const ptr = try tag.Type().open(arena, comp, emit, options); 494 return &ptr.base; 495 }, 496 } 497 } 498 499 pub fn createEmpty( 500 arena: Allocator, 501 comp: *Compilation, 502 emit: Path, 503 options: OpenOptions, 504 ) !*File { 505 switch (Tag.fromObjectFormat(comp.root_mod.resolved_target.result.ofmt)) { 506 inline else => |tag| { 507 dev.check(tag.devFeature()); 508 const ptr = try tag.Type().createEmpty(arena, comp, emit, options); 509 return &ptr.base; 510 }, 511 } 512 } 513 514 pub fn cast(base: *File, comptime tag: Tag) if (dev.env.supports(tag.devFeature())) ?*tag.Type() else ?noreturn { 515 return if (dev.env.supports(tag.devFeature()) and base.tag == tag) @fieldParentPtr("base", base) else null; 516 } 517 518 pub fn makeWritable(base: *File) !void { 519 dev.check(.make_writable); 520 const comp = base.comp; 521 const gpa = comp.gpa; 522 switch (base.tag) { 523 .coff, .elf, .macho, .plan9, .wasm => { 524 if (base.file != null) return; 525 dev.checkAny(&.{ .coff_linker, .elf_linker, .macho_linker, .plan9_linker, .wasm_linker }); 526 const emit = base.emit; 527 if (base.child_pid) |pid| { 528 if (builtin.os.tag == .windows) { 529 base.cast(.coff).?.ptraceAttach(pid) catch |err| { 530 log.warn("attaching failed with error: {s}", .{@errorName(err)}); 531 }; 532 } else { 533 // If we try to open the output file in write mode while it is running, 534 // it will return ETXTBSY. So instead, we copy the file, atomically rename it 535 // over top of the exe path, and then proceed normally. This changes the inode, 536 // avoiding the error. 537 const tmp_sub_path = try std.fmt.allocPrint(gpa, "{s}-{x}", .{ 538 emit.sub_path, std.crypto.random.int(u32), 539 }); 540 defer gpa.free(tmp_sub_path); 541 try emit.root_dir.handle.copyFile(emit.sub_path, emit.root_dir.handle, tmp_sub_path, .{}); 542 try emit.root_dir.handle.rename(tmp_sub_path, emit.sub_path); 543 switch (builtin.os.tag) { 544 .linux => std.posix.ptrace(std.os.linux.PTRACE.ATTACH, pid, 0, 0) catch |err| { 545 log.warn("ptrace failure: {s}", .{@errorName(err)}); 546 }, 547 .macos => base.cast(.macho).?.ptraceAttach(pid) catch |err| { 548 log.warn("attaching failed with error: {s}", .{@errorName(err)}); 549 }, 550 .windows => unreachable, 551 else => return error.HotSwapUnavailableOnHostOperatingSystem, 552 } 553 } 554 } 555 const use_lld = build_options.have_llvm and comp.config.use_lld; 556 const output_mode = comp.config.output_mode; 557 const link_mode = comp.config.link_mode; 558 base.file = try emit.root_dir.handle.createFile(emit.sub_path, .{ 559 .truncate = false, 560 .read = true, 561 .mode = determineMode(use_lld, output_mode, link_mode), 562 }); 563 }, 564 .c, .spirv, .nvptx => dev.checkAny(&.{ .c_linker, .spirv_linker, .nvptx_linker }), 565 } 566 } 567 568 pub fn makeExecutable(base: *File) !void { 569 dev.check(.make_executable); 570 const comp = base.comp; 571 const output_mode = comp.config.output_mode; 572 const link_mode = comp.config.link_mode; 573 const use_lld = build_options.have_llvm and comp.config.use_lld; 574 575 switch (output_mode) { 576 .Obj => return, 577 .Lib => switch (link_mode) { 578 .static => return, 579 .dynamic => {}, 580 }, 581 .Exe => {}, 582 } 583 switch (base.tag) { 584 .elf => if (base.file) |f| { 585 dev.check(.elf_linker); 586 if (base.zcu_object_sub_path != null and use_lld) { 587 // The file we have open is not the final file that we want to 588 // make executable, so we don't have to close it. 589 return; 590 } 591 f.close(); 592 base.file = null; 593 594 if (base.child_pid) |pid| { 595 switch (builtin.os.tag) { 596 .linux => std.posix.ptrace(std.os.linux.PTRACE.DETACH, pid, 0, 0) catch |err| { 597 log.warn("ptrace failure: {s}", .{@errorName(err)}); 598 }, 599 else => return error.HotSwapUnavailableOnHostOperatingSystem, 600 } 601 } 602 }, 603 .coff, .macho, .plan9, .wasm => if (base.file) |f| { 604 dev.checkAny(&.{ .coff_linker, .macho_linker, .plan9_linker, .wasm_linker }); 605 if (base.zcu_object_sub_path != null) { 606 // The file we have open is not the final file that we want to 607 // make executable, so we don't have to close it. 608 return; 609 } 610 f.close(); 611 base.file = null; 612 613 if (base.child_pid) |pid| { 614 switch (builtin.os.tag) { 615 .macos => base.cast(.macho).?.ptraceDetach(pid) catch |err| { 616 log.warn("detaching failed with error: {s}", .{@errorName(err)}); 617 }, 618 .windows => base.cast(.coff).?.ptraceDetach(pid), 619 else => return error.HotSwapUnavailableOnHostOperatingSystem, 620 } 621 } 622 }, 623 .c, .spirv, .nvptx => dev.checkAny(&.{ .c_linker, .spirv_linker, .nvptx_linker }), 624 } 625 } 626 627 pub const DebugInfoOutput = union(enum) { 628 dwarf: *Dwarf.WipNav, 629 plan9: *Plan9.DebugInfoOutput, 630 none, 631 }; 632 pub const UpdateDebugInfoError = Dwarf.UpdateError; 633 pub const FlushDebugInfoError = Dwarf.FlushError; 634 635 /// Note that `LinkFailure` is not a member of this error set because the error message 636 /// must be attached to `Zcu.failed_codegen` rather than `Compilation.link_diags`. 637 pub const UpdateNavError = error{ 638 Overflow, 639 OutOfMemory, 640 /// Indicates the error is already reported and stored in 641 /// `failed_codegen` on the Zcu. 642 CodegenFail, 643 }; 644 645 /// Called from within CodeGen to retrieve the symbol index of a global symbol. 646 /// If no symbol exists yet with this name, a new undefined global symbol will 647 /// be created. This symbol may get resolved once all relocatables are (re-)linked. 648 /// Optionally, it is possible to specify where to expect the symbol defined if it 649 /// is an import. 650 pub fn getGlobalSymbol(base: *File, name: []const u8, lib_name: ?[]const u8) UpdateNavError!u32 { 651 log.debug("getGlobalSymbol '{s}' (expected in '{?s}')", .{ name, lib_name }); 652 switch (base.tag) { 653 .plan9 => unreachable, 654 .spirv => unreachable, 655 .c => unreachable, 656 .nvptx => unreachable, 657 inline else => |tag| { 658 dev.check(tag.devFeature()); 659 return @as(*tag.Type(), @fieldParentPtr("base", base)).getGlobalSymbol(name, lib_name); 660 }, 661 } 662 } 663 664 /// May be called before or after updateExports for any given Nav. 665 pub fn updateNav(base: *File, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) UpdateNavError!void { 666 const nav = pt.zcu.intern_pool.getNav(nav_index); 667 assert(nav.status == .fully_resolved); 668 switch (base.tag) { 669 inline else => |tag| { 670 dev.check(tag.devFeature()); 671 return @as(*tag.Type(), @fieldParentPtr("base", base)).updateNav(pt, nav_index); 672 }, 673 } 674 } 675 676 pub const UpdateContainerTypeError = error{ 677 OutOfMemory, 678 /// `Zcu.failed_types` is already populated with the error message. 679 TypeFailureReported, 680 }; 681 682 pub fn updateContainerType(base: *File, pt: Zcu.PerThread, ty: InternPool.Index) UpdateContainerTypeError!void { 683 switch (base.tag) { 684 else => {}, 685 inline .elf => |tag| { 686 dev.check(tag.devFeature()); 687 return @as(*tag.Type(), @fieldParentPtr("base", base)).updateContainerType(pt, ty); 688 }, 689 } 690 } 691 692 /// May be called before or after updateExports for any given Decl. 693 pub fn updateFunc( 694 base: *File, 695 pt: Zcu.PerThread, 696 func_index: InternPool.Index, 697 air: Air, 698 liveness: Liveness, 699 ) UpdateNavError!void { 700 switch (base.tag) { 701 inline else => |tag| { 702 dev.check(tag.devFeature()); 703 return @as(*tag.Type(), @fieldParentPtr("base", base)).updateFunc(pt, func_index, air, liveness); 704 }, 705 } 706 } 707 708 /// On an incremental update, fixup the line number of all `Nav`s at the given `TrackedInst`, because 709 /// its line number has changed. The ZIR instruction `ti_id` has tag `.declaration`. 710 pub fn updateLineNumber(base: *File, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) UpdateNavError!void { 711 { 712 const ti = ti_id.resolveFull(&pt.zcu.intern_pool).?; 713 const file = pt.zcu.fileByIndex(ti.file); 714 assert(file.zir_loaded); 715 const inst = file.zir.instructions.get(@intFromEnum(ti.inst)); 716 assert(inst.tag == .declaration); 717 } 718 719 switch (base.tag) { 720 .spirv, .nvptx => {}, 721 inline else => |tag| { 722 dev.check(tag.devFeature()); 723 return @as(*tag.Type(), @fieldParentPtr("base", base)).updateLineNumber(pt, ti_id); 724 }, 725 } 726 } 727 728 pub fn releaseLock(self: *File) void { 729 if (self.lock) |*lock| { 730 lock.release(); 731 self.lock = null; 732 } 733 } 734 735 pub fn toOwnedLock(self: *File) Cache.Lock { 736 const lock = self.lock.?; 737 self.lock = null; 738 return lock; 739 } 740 741 pub fn destroy(base: *File) void { 742 base.releaseLock(); 743 if (base.file) |f| f.close(); 744 switch (base.tag) { 745 inline else => |tag| { 746 dev.check(tag.devFeature()); 747 @as(*tag.Type(), @fieldParentPtr("base", base)).deinit(); 748 }, 749 } 750 } 751 752 pub const FlushError = error{ 753 /// Indicates an error will be present in `Compilation.link_diags`. 754 LinkFailure, 755 OutOfMemory, 756 }; 757 758 /// Commit pending changes and write headers. Takes into account final output mode 759 /// and `use_lld`, not only `effectiveOutputMode`. 760 /// `arena` has the lifetime of the call to `Compilation.update`. 761 pub fn flush(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) FlushError!void { 762 const comp = base.comp; 763 if (comp.clang_preprocessor_mode == .yes or comp.clang_preprocessor_mode == .pch) { 764 dev.check(.clang_command); 765 const emit = base.emit; 766 // TODO: avoid extra link step when it's just 1 object file (the `zig cc -c` case) 767 // Until then, we do `lld -r -o output.o input.o` even though the output is the same 768 // as the input. For the preprocessing case (`zig cc -E -o foo`) we copy the file 769 // to the final location. See also the corresponding TODO in Coff linking. 770 assert(comp.c_object_table.count() == 1); 771 const the_key = comp.c_object_table.keys()[0]; 772 const cached_pp_file_path = the_key.status.success.object_path; 773 cached_pp_file_path.root_dir.handle.copyFile(cached_pp_file_path.sub_path, emit.root_dir.handle, emit.sub_path, .{}) catch |err| { 774 const diags = &base.comp.link_diags; 775 return diags.fail("failed to copy '{'}' to '{'}': {s}", .{ 776 @as(Path, cached_pp_file_path), @as(Path, emit), @errorName(err), 777 }); 778 }; 779 return; 780 } 781 782 const use_lld = build_options.have_llvm and comp.config.use_lld; 783 const output_mode = comp.config.output_mode; 784 const link_mode = comp.config.link_mode; 785 if (use_lld and output_mode == .Lib and link_mode == .static) { 786 return base.linkAsArchive(arena, tid, prog_node); 787 } 788 switch (base.tag) { 789 inline else => |tag| { 790 dev.check(tag.devFeature()); 791 return @as(*tag.Type(), @fieldParentPtr("base", base)).flush(arena, tid, prog_node); 792 }, 793 } 794 } 795 796 /// Commit pending changes and write headers. Works based on `effectiveOutputMode` 797 /// rather than final output mode. 798 pub fn flushModule(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) FlushError!void { 799 switch (base.tag) { 800 inline else => |tag| { 801 dev.check(tag.devFeature()); 802 return @as(*tag.Type(), @fieldParentPtr("base", base)).flushModule(arena, tid, prog_node); 803 }, 804 } 805 } 806 807 pub const UpdateExportsError = error{ 808 OutOfMemory, 809 AnalysisFail, 810 }; 811 812 /// This is called for every exported thing. `exports` is almost always 813 /// a list of size 1, meaning that `exported` is exported once. However, it is possible 814 /// to export the same thing with multiple different symbol names (aliases). 815 /// May be called before or after updateDecl for any given Decl. 816 pub fn updateExports( 817 base: *File, 818 pt: Zcu.PerThread, 819 exported: Zcu.Exported, 820 export_indices: []const Zcu.Export.Index, 821 ) UpdateExportsError!void { 822 switch (base.tag) { 823 inline else => |tag| { 824 dev.check(tag.devFeature()); 825 return @as(*tag.Type(), @fieldParentPtr("base", base)).updateExports(pt, exported, export_indices); 826 }, 827 } 828 } 829 830 pub const RelocInfo = struct { 831 parent: Parent, 832 offset: u64, 833 addend: u32, 834 835 pub const Parent = union(enum) { 836 none, 837 atom_index: u32, 838 debug_output: DebugInfoOutput, 839 }; 840 }; 841 842 /// Get allocated `Nav`'s address in virtual memory. 843 /// The linker is passed information about the containing atom, `parent_atom_index`, and offset within it's 844 /// memory buffer, `offset`, so that it can make a note of potential relocation sites, should the 845 /// `Nav`'s address was not yet resolved, or the containing atom gets moved in virtual memory. 846 /// May be called before or after updateFunc/updateNav therefore it is up to the linker to allocate 847 /// the block/atom. 848 pub fn getNavVAddr(base: *File, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index, reloc_info: RelocInfo) !u64 { 849 switch (base.tag) { 850 .c => unreachable, 851 .spirv => unreachable, 852 .nvptx => unreachable, 853 .wasm => unreachable, 854 inline else => |tag| { 855 dev.check(tag.devFeature()); 856 return @as(*tag.Type(), @fieldParentPtr("base", base)).getNavVAddr(pt, nav_index, reloc_info); 857 }, 858 } 859 } 860 861 pub fn lowerUav( 862 base: *File, 863 pt: Zcu.PerThread, 864 decl_val: InternPool.Index, 865 decl_align: InternPool.Alignment, 866 src_loc: Zcu.LazySrcLoc, 867 ) !@import("codegen.zig").GenResult { 868 switch (base.tag) { 869 .c => unreachable, 870 .spirv => unreachable, 871 .nvptx => unreachable, 872 .wasm => unreachable, 873 inline else => |tag| { 874 dev.check(tag.devFeature()); 875 return @as(*tag.Type(), @fieldParentPtr("base", base)).lowerUav(pt, decl_val, decl_align, src_loc); 876 }, 877 } 878 } 879 880 pub fn getUavVAddr(base: *File, decl_val: InternPool.Index, reloc_info: RelocInfo) !u64 { 881 switch (base.tag) { 882 .c => unreachable, 883 .spirv => unreachable, 884 .nvptx => unreachable, 885 .wasm => unreachable, 886 inline else => |tag| { 887 dev.check(tag.devFeature()); 888 return @as(*tag.Type(), @fieldParentPtr("base", base)).getUavVAddr(decl_val, reloc_info); 889 }, 890 } 891 } 892 893 pub fn deleteExport( 894 base: *File, 895 exported: Zcu.Exported, 896 name: InternPool.NullTerminatedString, 897 ) void { 898 switch (base.tag) { 899 .plan9, 900 .spirv, 901 .nvptx, 902 => {}, 903 904 inline else => |tag| { 905 dev.check(tag.devFeature()); 906 return @as(*tag.Type(), @fieldParentPtr("base", base)).deleteExport(exported, name); 907 }, 908 } 909 } 910 911 /// Opens a path as an object file and parses it into the linker. 912 fn openLoadObject(base: *File, path: Path) anyerror!void { 913 const diags = &base.comp.link_diags; 914 const input = try openObjectInput(diags, path); 915 errdefer input.object.file.close(); 916 try loadInput(base, input); 917 } 918 919 /// Opens a path as a static library and parses it into the linker. 920 /// If `query` is non-null, allows GNU ld scripts. 921 fn openLoadArchive(base: *File, path: Path, opt_query: ?UnresolvedInput.Query) anyerror!void { 922 if (opt_query) |query| { 923 const archive = try openObject(path, query.must_link, query.hidden); 924 errdefer archive.file.close(); 925 loadInput(base, .{ .archive = archive }) catch |err| switch (err) { 926 error.BadMagic, error.UnexpectedEndOfFile => { 927 if (base.tag != .elf) return err; 928 try loadGnuLdScript(base, path, query, archive.file); 929 archive.file.close(); 930 return; 931 }, 932 else => return err, 933 }; 934 } else { 935 const archive = try openObject(path, false, false); 936 errdefer archive.file.close(); 937 try loadInput(base, .{ .archive = archive }); 938 } 939 } 940 941 /// Opens a path as a shared library and parses it into the linker. 942 /// Handles GNU ld scripts. 943 fn openLoadDso(base: *File, path: Path, query: UnresolvedInput.Query) anyerror!void { 944 const dso = try openDso(path, query.needed, query.weak, query.reexport); 945 errdefer dso.file.close(); 946 loadInput(base, .{ .dso = dso }) catch |err| switch (err) { 947 error.BadMagic, error.UnexpectedEndOfFile => { 948 if (base.tag != .elf) return err; 949 try loadGnuLdScript(base, path, query, dso.file); 950 dso.file.close(); 951 return; 952 }, 953 else => return err, 954 }; 955 } 956 957 fn loadGnuLdScript(base: *File, path: Path, parent_query: UnresolvedInput.Query, file: fs.File) anyerror!void { 958 const diags = &base.comp.link_diags; 959 const gpa = base.comp.gpa; 960 const stat = try file.stat(); 961 const size = std.math.cast(u32, stat.size) orelse return error.FileTooBig; 962 const buf = try gpa.alloc(u8, size); 963 defer gpa.free(buf); 964 const n = try file.preadAll(buf, 0); 965 if (buf.len != n) return error.UnexpectedEndOfFile; 966 var ld_script = try LdScript.parse(gpa, diags, path, buf); 967 defer ld_script.deinit(gpa); 968 for (ld_script.args) |arg| { 969 const query: UnresolvedInput.Query = .{ 970 .needed = arg.needed or parent_query.needed, 971 .weak = parent_query.weak, 972 .reexport = parent_query.reexport, 973 .preferred_mode = parent_query.preferred_mode, 974 .search_strategy = parent_query.search_strategy, 975 .allow_so_scripts = parent_query.allow_so_scripts, 976 }; 977 if (mem.startsWith(u8, arg.path, "-l")) { 978 @panic("TODO"); 979 } else { 980 if (fs.path.isAbsolute(arg.path)) { 981 const new_path = Path.initCwd(try gpa.dupe(u8, arg.path)); 982 switch (Compilation.classifyFileExt(arg.path)) { 983 .shared_library => try openLoadDso(base, new_path, query), 984 .object => try openLoadObject(base, new_path), 985 .static_library => try openLoadArchive(base, new_path, query), 986 else => diags.addParseError(path, "GNU ld script references file with unrecognized extension: {s}", .{arg.path}), 987 } 988 } else { 989 @panic("TODO"); 990 } 991 } 992 } 993 } 994 995 pub fn loadInput(base: *File, input: Input) anyerror!void { 996 const use_lld = build_options.have_llvm and base.comp.config.use_lld; 997 if (use_lld) return; 998 switch (base.tag) { 999 inline .elf, .wasm => |tag| { 1000 dev.check(tag.devFeature()); 1001 return @as(*tag.Type(), @fieldParentPtr("base", base)).loadInput(input); 1002 }, 1003 else => {}, 1004 } 1005 } 1006 1007 /// Called when all linker inputs have been sent via `loadInput`. After 1008 /// this, `loadInput` will not be called anymore. 1009 pub fn prelink(base: *File) FlushError!void { 1010 const use_lld = build_options.have_llvm and base.comp.config.use_lld; 1011 if (use_lld) return; 1012 1013 // In this case, an object file is created by the LLVM backend, so 1014 // there is no prelink phase. The Zig code is linked as a standard 1015 // object along with the others. 1016 if (base.zcu_object_sub_path != null) return; 1017 1018 switch (base.tag) { 1019 inline .wasm => |tag| { 1020 dev.check(tag.devFeature()); 1021 return @as(*tag.Type(), @fieldParentPtr("base", base)).prelink(); 1022 }, 1023 else => {}, 1024 } 1025 } 1026 1027 pub fn linkAsArchive(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) FlushError!void { 1028 dev.check(.lld_linker); 1029 1030 const tracy = trace(@src()); 1031 defer tracy.end(); 1032 1033 const comp = base.comp; 1034 1035 const directory = base.emit.root_dir; // Just an alias to make it shorter to type. 1036 const full_out_path = try directory.join(arena, &[_][]const u8{base.emit.sub_path}); 1037 const full_out_path_z = try arena.dupeZ(u8, full_out_path); 1038 const opt_zcu = comp.zcu; 1039 1040 // If there is no Zig code to compile, then we should skip flushing the output file 1041 // because it will not be part of the linker line anyway. 1042 const zcu_obj_path: ?[]const u8 = if (opt_zcu != null) blk: { 1043 try base.flushModule(arena, tid, prog_node); 1044 1045 const dirname = fs.path.dirname(full_out_path_z) orelse "."; 1046 break :blk try fs.path.join(arena, &.{ dirname, base.zcu_object_sub_path.? }); 1047 } else null; 1048 1049 log.debug("zcu_obj_path={s}", .{if (zcu_obj_path) |s| s else "(null)"}); 1050 1051 const compiler_rt_path: ?Path = if (comp.include_compiler_rt) 1052 comp.compiler_rt_obj.?.full_object_path 1053 else 1054 null; 1055 1056 // This function follows the same pattern as link.Elf.linkWithLLD so if you want some 1057 // insight as to what's going on here you can read that function body which is more 1058 // well-commented. 1059 1060 const id_symlink_basename = "llvm-ar.id"; 1061 1062 var man: Cache.Manifest = undefined; 1063 defer if (!base.disable_lld_caching) man.deinit(); 1064 1065 const link_inputs = comp.link_inputs; 1066 1067 var digest: [Cache.hex_digest_len]u8 = undefined; 1068 1069 if (!base.disable_lld_caching) { 1070 man = comp.cache_parent.obtain(); 1071 1072 // We are about to obtain this lock, so here we give other processes a chance first. 1073 base.releaseLock(); 1074 1075 try hashInputs(&man, link_inputs); 1076 1077 for (comp.c_object_table.keys()) |key| { 1078 _ = try man.addFilePath(key.status.success.object_path, null); 1079 } 1080 for (comp.win32_resource_table.keys()) |key| { 1081 _ = try man.addFile(key.status.success.res_path, null); 1082 } 1083 try man.addOptionalFile(zcu_obj_path); 1084 try man.addOptionalFilePath(compiler_rt_path); 1085 1086 // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. 1087 _ = try man.hit(); 1088 digest = man.final(); 1089 1090 var prev_digest_buf: [digest.len]u8 = undefined; 1091 const prev_digest: []u8 = Cache.readSmallFile( 1092 directory.handle, 1093 id_symlink_basename, 1094 &prev_digest_buf, 1095 ) catch |err| b: { 1096 log.debug("archive new_digest={s} readFile error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) }); 1097 break :b prev_digest_buf[0..0]; 1098 }; 1099 if (mem.eql(u8, prev_digest, &digest)) { 1100 log.debug("archive digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)}); 1101 base.lock = man.toOwnedLock(); 1102 return; 1103 } 1104 1105 // We are about to change the output file to be different, so we invalidate the build hash now. 1106 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { 1107 error.FileNotFound => {}, 1108 else => |e| return e, 1109 }; 1110 } 1111 1112 var object_files: std.ArrayListUnmanaged([*:0]const u8) = .empty; 1113 1114 try object_files.ensureUnusedCapacity(arena, link_inputs.len); 1115 for (link_inputs) |input| { 1116 object_files.appendAssumeCapacity(try input.path().?.toStringZ(arena)); 1117 } 1118 1119 try object_files.ensureUnusedCapacity(arena, comp.c_object_table.count() + 1120 comp.win32_resource_table.count() + 2); 1121 1122 for (comp.c_object_table.keys()) |key| { 1123 object_files.appendAssumeCapacity(try key.status.success.object_path.toStringZ(arena)); 1124 } 1125 for (comp.win32_resource_table.keys()) |key| { 1126 object_files.appendAssumeCapacity(try arena.dupeZ(u8, key.status.success.res_path)); 1127 } 1128 if (zcu_obj_path) |p| object_files.appendAssumeCapacity(try arena.dupeZ(u8, p)); 1129 if (compiler_rt_path) |p| object_files.appendAssumeCapacity(try p.toStringZ(arena)); 1130 1131 if (comp.verbose_link) { 1132 std.debug.print("ar rcs {s}", .{full_out_path_z}); 1133 for (object_files.items) |arg| { 1134 std.debug.print(" {s}", .{arg}); 1135 } 1136 std.debug.print("\n", .{}); 1137 } 1138 1139 const llvm_bindings = @import("codegen/llvm/bindings.zig"); 1140 const llvm = @import("codegen/llvm.zig"); 1141 const target = comp.root_mod.resolved_target.result; 1142 llvm.initializeLLVMTarget(target.cpu.arch); 1143 const bad = llvm_bindings.WriteArchive( 1144 full_out_path_z, 1145 object_files.items.ptr, 1146 object_files.items.len, 1147 switch (target.os.tag) { 1148 .aix => .AIXBIG, 1149 .windows => .COFF, 1150 else => if (target.os.tag.isDarwin()) .DARWIN else .GNU, 1151 }, 1152 ); 1153 if (bad) return error.UnableToWriteArchive; 1154 1155 if (!base.disable_lld_caching) { 1156 Cache.writeSmallFile(directory.handle, id_symlink_basename, &digest) catch |err| { 1157 log.warn("failed to save archive hash digest file: {s}", .{@errorName(err)}); 1158 }; 1159 1160 if (man.have_exclusive_lock) { 1161 man.writeManifest() catch |err| { 1162 log.warn("failed to write cache manifest when archiving: {s}", .{@errorName(err)}); 1163 }; 1164 } 1165 1166 base.lock = man.toOwnedLock(); 1167 } 1168 } 1169 1170 pub const Tag = enum { 1171 coff, 1172 elf, 1173 macho, 1174 c, 1175 wasm, 1176 spirv, 1177 plan9, 1178 nvptx, 1179 1180 pub fn Type(comptime tag: Tag) type { 1181 return switch (tag) { 1182 .coff => Coff, 1183 .elf => Elf, 1184 .macho => MachO, 1185 .c => C, 1186 .wasm => Wasm, 1187 .spirv => SpirV, 1188 .plan9 => Plan9, 1189 .nvptx => NvPtx, 1190 }; 1191 } 1192 1193 pub fn fromObjectFormat(ofmt: std.Target.ObjectFormat) Tag { 1194 return switch (ofmt) { 1195 .coff => .coff, 1196 .elf => .elf, 1197 .macho => .macho, 1198 .wasm => .wasm, 1199 .plan9 => .plan9, 1200 .c => .c, 1201 .spirv => .spirv, 1202 .nvptx => .nvptx, 1203 .goff => @panic("TODO implement goff object format"), 1204 .xcoff => @panic("TODO implement xcoff object format"), 1205 .hex => @panic("TODO implement hex object format"), 1206 .raw => @panic("TODO implement raw object format"), 1207 }; 1208 } 1209 1210 pub fn devFeature(tag: Tag) dev.Feature { 1211 return @field(dev.Feature, @tagName(tag) ++ "_linker"); 1212 } 1213 }; 1214 1215 pub const LazySymbol = struct { 1216 pub const Kind = enum { code, const_data }; 1217 1218 kind: Kind, 1219 ty: InternPool.Index, 1220 }; 1221 1222 pub fn effectiveOutputMode( 1223 use_lld: bool, 1224 output_mode: std.builtin.OutputMode, 1225 ) std.builtin.OutputMode { 1226 return if (use_lld) .Obj else output_mode; 1227 } 1228 1229 pub fn determineMode( 1230 use_lld: bool, 1231 output_mode: std.builtin.OutputMode, 1232 link_mode: std.builtin.LinkMode, 1233 ) fs.File.Mode { 1234 // On common systems with a 0o022 umask, 0o777 will still result in a file created 1235 // with 0o755 permissions, but it works appropriately if the system is configured 1236 // more leniently. As another data point, C's fopen seems to open files with the 1237 // 666 mode. 1238 const executable_mode = if (builtin.target.os.tag == .windows) 0 else 0o777; 1239 switch (effectiveOutputMode(use_lld, output_mode)) { 1240 .Lib => return switch (link_mode) { 1241 .dynamic => executable_mode, 1242 .static => fs.File.default_mode, 1243 }, 1244 .Exe => return executable_mode, 1245 .Obj => return fs.File.default_mode, 1246 } 1247 } 1248 1249 pub fn isStatic(self: File) bool { 1250 return self.comp.config.link_mode == .static; 1251 } 1252 1253 pub fn isObject(self: File) bool { 1254 const output_mode = self.comp.config.output_mode; 1255 return output_mode == .Obj; 1256 } 1257 1258 pub fn isExe(self: File) bool { 1259 const output_mode = self.comp.config.output_mode; 1260 return output_mode == .Exe; 1261 } 1262 1263 pub fn isStaticLib(self: File) bool { 1264 const output_mode = self.comp.config.output_mode; 1265 return output_mode == .Lib and self.isStatic(); 1266 } 1267 1268 pub fn isRelocatable(self: File) bool { 1269 return self.isObject() or self.isStaticLib(); 1270 } 1271 1272 pub fn isDynLib(self: File) bool { 1273 const output_mode = self.comp.config.output_mode; 1274 return output_mode == .Lib and !self.isStatic(); 1275 } 1276 1277 pub fn emitLlvmObject( 1278 base: File, 1279 arena: Allocator, 1280 llvm_object: LlvmObject.Ptr, 1281 prog_node: std.Progress.Node, 1282 ) !void { 1283 return base.comp.emitLlvmObject(arena, .{ 1284 .root_dir = base.emit.root_dir, 1285 .sub_path = std.fs.path.dirname(base.emit.sub_path) orelse "", 1286 }, .{ 1287 .directory = null, 1288 .basename = base.zcu_object_sub_path.?, 1289 }, llvm_object, prog_node); 1290 } 1291 1292 pub fn cgFail( 1293 base: *File, 1294 nav_index: InternPool.Nav.Index, 1295 comptime format: []const u8, 1296 args: anytype, 1297 ) error{ CodegenFail, OutOfMemory } { 1298 @branchHint(.cold); 1299 return base.comp.zcu.?.codegenFail(nav_index, format, args); 1300 } 1301 1302 pub const C = @import("link/C.zig"); 1303 pub const Coff = @import("link/Coff.zig"); 1304 pub const Plan9 = @import("link/Plan9.zig"); 1305 pub const Elf = @import("link/Elf.zig"); 1306 pub const MachO = @import("link/MachO.zig"); 1307 pub const SpirV = @import("link/SpirV.zig"); 1308 pub const Wasm = @import("link/Wasm.zig"); 1309 pub const NvPtx = @import("link/NvPtx.zig"); 1310 pub const Dwarf = @import("link/Dwarf.zig"); 1311 }; 1312 1313 /// Does all the tasks in the queue. Runs in exactly one separate thread 1314 /// from the rest of compilation. All tasks performed here are 1315 /// single-threaded with respect to one another. 1316 pub fn flushTaskQueue(tid: usize, comp: *Compilation) void { 1317 // As soon as check() is called, another `flushTaskQueue` call could occur, 1318 // so the safety lock must go after the check. 1319 while (comp.link_task_queue.check()) |tasks| { 1320 comp.link_task_queue_safety.lock(); 1321 defer comp.link_task_queue_safety.unlock(); 1322 for (tasks) |task| doTask(comp, tid, task); 1323 } 1324 } 1325 1326 pub const Task = union(enum) { 1327 /// Loads the objects, shared objects, and archives that are already 1328 /// known from the command line. 1329 load_explicitly_provided, 1330 /// Loads the shared objects and archives by resolving 1331 /// `target_util.libcFullLinkFlags()` against the host libc 1332 /// installation. 1333 load_host_libc, 1334 /// Tells the linker to load an object file by path. 1335 load_object: Path, 1336 /// Tells the linker to load a static library by path. 1337 load_archive: Path, 1338 /// Tells the linker to load a shared library, possibly one that is a 1339 /// GNU ld script. 1340 load_dso: Path, 1341 /// Tells the linker to load an input which could be an object file, 1342 /// archive, or shared library. 1343 load_input: Input, 1344 1345 /// Write the constant value for a Decl to the output file. 1346 codegen_nav: InternPool.Nav.Index, 1347 /// Write the machine code for a function to the output file. 1348 codegen_func: CodegenFunc, 1349 codegen_type: InternPool.Index, 1350 1351 update_line_number: InternPool.TrackedInst.Index, 1352 1353 pub const CodegenFunc = struct { 1354 /// This will either be a non-generic `func_decl` or a `func_instance`. 1355 func: InternPool.Index, 1356 /// This `Air` is owned by the `Job` and allocated with `gpa`. 1357 /// It must be deinited when the job is processed. 1358 air: Air, 1359 }; 1360 }; 1361 1362 pub fn doTask(comp: *Compilation, tid: usize, task: Task) void { 1363 const diags = &comp.link_diags; 1364 switch (task) { 1365 .load_explicitly_provided => if (comp.bin_file) |base| { 1366 const prog_node = comp.work_queue_progress_node.start("Parse Linker Inputs", comp.link_inputs.len); 1367 defer prog_node.end(); 1368 for (comp.link_inputs) |input| { 1369 base.loadInput(input) catch |err| switch (err) { 1370 error.LinkFailure => return, // error reported via diags 1371 else => |e| switch (input) { 1372 .dso => |dso| diags.addParseError(dso.path, "failed to parse shared library: {s}", .{@errorName(e)}), 1373 .object => |obj| diags.addParseError(obj.path, "failed to parse object: {s}", .{@errorName(e)}), 1374 .archive => |obj| diags.addParseError(obj.path, "failed to parse archive: {s}", .{@errorName(e)}), 1375 .res => |res| diags.addParseError(res.path, "failed to parse Windows resource: {s}", .{@errorName(e)}), 1376 .dso_exact => diags.addError("failed to handle dso_exact: {s}", .{@errorName(e)}), 1377 }, 1378 }; 1379 prog_node.completeOne(); 1380 } 1381 }, 1382 .load_host_libc => if (comp.bin_file) |base| { 1383 const prog_node = comp.work_queue_progress_node.start("Linker Parse Host libc", 0); 1384 defer prog_node.end(); 1385 1386 const target = comp.root_mod.resolved_target.result; 1387 const flags = target_util.libcFullLinkFlags(target); 1388 const crt_dir = comp.libc_installation.?.crt_dir.?; 1389 const sep = std.fs.path.sep_str; 1390 for (flags) |flag| { 1391 assert(mem.startsWith(u8, flag, "-l")); 1392 const lib_name = flag["-l".len..]; 1393 switch (comp.config.link_mode) { 1394 .dynamic => { 1395 const dso_path = Path.initCwd( 1396 std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}{s}{s}", .{ 1397 crt_dir, target.libPrefix(), lib_name, target.dynamicLibSuffix(), 1398 }) catch return diags.setAllocFailure(), 1399 ); 1400 base.openLoadDso(dso_path, .{ 1401 .preferred_mode = .dynamic, 1402 .search_strategy = .paths_first, 1403 }) catch |err| switch (err) { 1404 error.FileNotFound => { 1405 // Also try static. 1406 const archive_path = Path.initCwd( 1407 std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}{s}{s}", .{ 1408 crt_dir, target.libPrefix(), lib_name, target.staticLibSuffix(), 1409 }) catch return diags.setAllocFailure(), 1410 ); 1411 base.openLoadArchive(archive_path, .{ 1412 .preferred_mode = .dynamic, 1413 .search_strategy = .paths_first, 1414 }) catch |archive_err| switch (archive_err) { 1415 error.LinkFailure => return, // error reported via diags 1416 else => |e| diags.addParseError(dso_path, "failed to parse archive {}: {s}", .{ archive_path, @errorName(e) }), 1417 }; 1418 }, 1419 error.LinkFailure => return, // error reported via diags 1420 else => |e| diags.addParseError(dso_path, "failed to parse shared library: {s}", .{@errorName(e)}), 1421 }; 1422 }, 1423 .static => { 1424 const path = Path.initCwd( 1425 std.fmt.allocPrint(comp.arena, "{s}" ++ sep ++ "{s}{s}{s}", .{ 1426 crt_dir, target.libPrefix(), lib_name, target.staticLibSuffix(), 1427 }) catch return diags.setAllocFailure(), 1428 ); 1429 // glibc sometimes makes even archive files GNU ld scripts. 1430 base.openLoadArchive(path, .{ 1431 .preferred_mode = .static, 1432 .search_strategy = .no_fallback, 1433 }) catch |err| switch (err) { 1434 error.LinkFailure => return, // error reported via diags 1435 else => |e| diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(e)}), 1436 }; 1437 }, 1438 } 1439 } 1440 }, 1441 .load_object => |path| if (comp.bin_file) |base| { 1442 const prog_node = comp.work_queue_progress_node.start("Linker Parse Object", 0); 1443 defer prog_node.end(); 1444 base.openLoadObject(path) catch |err| switch (err) { 1445 error.LinkFailure => return, // error reported via diags 1446 else => |e| diags.addParseError(path, "failed to parse object: {s}", .{@errorName(e)}), 1447 }; 1448 }, 1449 .load_archive => |path| if (comp.bin_file) |base| { 1450 const prog_node = comp.work_queue_progress_node.start("Linker Parse Archive", 0); 1451 defer prog_node.end(); 1452 base.openLoadArchive(path, null) catch |err| switch (err) { 1453 error.LinkFailure => return, // error reported via link_diags 1454 else => |e| diags.addParseError(path, "failed to parse archive: {s}", .{@errorName(e)}), 1455 }; 1456 }, 1457 .load_dso => |path| if (comp.bin_file) |base| { 1458 const prog_node = comp.work_queue_progress_node.start("Linker Parse Shared Library", 0); 1459 defer prog_node.end(); 1460 base.openLoadDso(path, .{ 1461 .preferred_mode = .dynamic, 1462 .search_strategy = .paths_first, 1463 }) catch |err| switch (err) { 1464 error.LinkFailure => return, // error reported via link_diags 1465 else => |e| diags.addParseError(path, "failed to parse shared library: {s}", .{@errorName(e)}), 1466 }; 1467 }, 1468 .load_input => |input| if (comp.bin_file) |base| { 1469 const prog_node = comp.work_queue_progress_node.start("Linker Parse Input", 0); 1470 defer prog_node.end(); 1471 base.loadInput(input) catch |err| switch (err) { 1472 error.LinkFailure => return, // error reported via link_diags 1473 else => |e| { 1474 if (input.path()) |path| { 1475 diags.addParseError(path, "failed to parse linker input: {s}", .{@errorName(e)}); 1476 } else { 1477 diags.addError("failed to {s}: {s}", .{ input.taskName(), @errorName(e) }); 1478 } 1479 }, 1480 }; 1481 }, 1482 .codegen_nav => |nav_index| { 1483 const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid)); 1484 defer pt.deactivate(); 1485 pt.linkerUpdateNav(nav_index) catch |err| switch (err) { 1486 error.OutOfMemory => diags.setAllocFailure(), 1487 }; 1488 }, 1489 .codegen_func => |func| { 1490 const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid)); 1491 defer pt.deactivate(); 1492 // This call takes ownership of `func.air`. 1493 pt.linkerUpdateFunc(func.func, func.air) catch |err| switch (err) { 1494 error.OutOfMemory => diags.setAllocFailure(), 1495 }; 1496 }, 1497 .codegen_type => |ty| { 1498 const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid)); 1499 defer pt.deactivate(); 1500 pt.linkerUpdateContainerType(ty) catch |err| switch (err) { 1501 error.OutOfMemory => diags.setAllocFailure(), 1502 }; 1503 }, 1504 .update_line_number => |ti| { 1505 const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid)); 1506 defer pt.deactivate(); 1507 pt.linkerUpdateLineNumber(ti) catch |err| switch (err) { 1508 error.OutOfMemory => diags.setAllocFailure(), 1509 }; 1510 }, 1511 } 1512 } 1513 1514 pub fn spawnLld( 1515 comp: *Compilation, 1516 arena: Allocator, 1517 argv: []const []const u8, 1518 ) !void { 1519 if (comp.verbose_link) { 1520 // Skip over our own name so that the LLD linker name is the first argv item. 1521 Compilation.dump_argv(argv[1..]); 1522 } 1523 1524 // If possible, we run LLD as a child process because it does not always 1525 // behave properly as a library, unfortunately. 1526 // https://github.com/ziglang/zig/issues/3825 1527 if (!std.process.can_spawn) { 1528 const exit_code = try lldMain(arena, argv, false); 1529 if (exit_code == 0) return; 1530 if (comp.clang_passthrough_mode) std.process.exit(exit_code); 1531 return error.LLDReportedFailure; 1532 } 1533 1534 var stderr: []u8 = &.{}; 1535 defer comp.gpa.free(stderr); 1536 1537 var child = std.process.Child.init(argv, arena); 1538 const term = (if (comp.clang_passthrough_mode) term: { 1539 child.stdin_behavior = .Inherit; 1540 child.stdout_behavior = .Inherit; 1541 child.stderr_behavior = .Inherit; 1542 1543 break :term child.spawnAndWait(); 1544 } else term: { 1545 child.stdin_behavior = .Ignore; 1546 child.stdout_behavior = .Ignore; 1547 child.stderr_behavior = .Pipe; 1548 1549 child.spawn() catch |err| break :term err; 1550 stderr = try child.stderr.?.reader().readAllAlloc(comp.gpa, std.math.maxInt(usize)); 1551 break :term child.wait(); 1552 }) catch |first_err| term: { 1553 const err = switch (first_err) { 1554 error.NameTooLong => err: { 1555 const s = fs.path.sep_str; 1556 const rand_int = std.crypto.random.int(u64); 1557 const rsp_path = "tmp" ++ s ++ std.fmt.hex(rand_int) ++ ".rsp"; 1558 1559 const rsp_file = try comp.local_cache_directory.handle.createFileZ(rsp_path, .{}); 1560 defer comp.local_cache_directory.handle.deleteFileZ(rsp_path) catch |err| 1561 log.warn("failed to delete response file {s}: {s}", .{ rsp_path, @errorName(err) }); 1562 { 1563 defer rsp_file.close(); 1564 var rsp_buf = std.io.bufferedWriter(rsp_file.writer()); 1565 const rsp_writer = rsp_buf.writer(); 1566 for (argv[2..]) |arg| { 1567 try rsp_writer.writeByte('"'); 1568 for (arg) |c| { 1569 switch (c) { 1570 '\"', '\\' => try rsp_writer.writeByte('\\'), 1571 else => {}, 1572 } 1573 try rsp_writer.writeByte(c); 1574 } 1575 try rsp_writer.writeByte('"'); 1576 try rsp_writer.writeByte('\n'); 1577 } 1578 try rsp_buf.flush(); 1579 } 1580 1581 var rsp_child = std.process.Child.init(&.{ argv[0], argv[1], try std.fmt.allocPrint( 1582 arena, 1583 "@{s}", 1584 .{try comp.local_cache_directory.join(arena, &.{rsp_path})}, 1585 ) }, arena); 1586 if (comp.clang_passthrough_mode) { 1587 rsp_child.stdin_behavior = .Inherit; 1588 rsp_child.stdout_behavior = .Inherit; 1589 rsp_child.stderr_behavior = .Inherit; 1590 1591 break :term rsp_child.spawnAndWait() catch |err| break :err err; 1592 } else { 1593 rsp_child.stdin_behavior = .Ignore; 1594 rsp_child.stdout_behavior = .Ignore; 1595 rsp_child.stderr_behavior = .Pipe; 1596 1597 rsp_child.spawn() catch |err| break :err err; 1598 stderr = try rsp_child.stderr.?.reader().readAllAlloc(comp.gpa, std.math.maxInt(usize)); 1599 break :term rsp_child.wait() catch |err| break :err err; 1600 } 1601 }, 1602 else => first_err, 1603 }; 1604 log.err("unable to spawn LLD {s}: {s}", .{ argv[0], @errorName(err) }); 1605 return error.UnableToSpawnSelf; 1606 }; 1607 1608 switch (term) { 1609 .Exited => |code| if (code != 0) { 1610 if (comp.clang_passthrough_mode) std.process.exit(code); 1611 const diags = &comp.link_diags; 1612 diags.lockAndParseLldStderr(argv[1], stderr); 1613 return error.LLDReportedFailure; 1614 }, 1615 else => { 1616 if (comp.clang_passthrough_mode) std.process.abort(); 1617 log.err("{s} terminated with stderr:\n{s}", .{ argv[0], stderr }); 1618 return error.LLDCrashed; 1619 }, 1620 } 1621 1622 if (stderr.len > 0) log.warn("unexpected LLD stderr:\n{s}", .{stderr}); 1623 } 1624 1625 /// Provided by the CLI, processed into `LinkInput` instances at the start of 1626 /// the compilation pipeline. 1627 pub const UnresolvedInput = union(enum) { 1628 /// A library name that could potentially be dynamic or static depending on 1629 /// query parameters, resolved according to library directories. 1630 /// This could potentially resolve to a GNU ld script, resulting in more 1631 /// library dependencies. 1632 name_query: NameQuery, 1633 /// When a file path is provided, query info is still needed because the 1634 /// path may point to a .so file which may actually be a GNU ld script that 1635 /// references library names which need to be resolved. 1636 path_query: PathQuery, 1637 /// Strings that come from GNU ld scripts. Is it a filename? Is it a path? 1638 /// Who knows! Fuck around and find out. 1639 ambiguous_name: NameQuery, 1640 /// Put exactly this string in the dynamic section, no rpath. 1641 dso_exact: Input.DsoExact, 1642 1643 pub const NameQuery = struct { 1644 name: []const u8, 1645 query: Query, 1646 }; 1647 1648 pub const PathQuery = struct { 1649 path: Path, 1650 query: Query, 1651 }; 1652 1653 pub const Query = struct { 1654 needed: bool = false, 1655 weak: bool = false, 1656 reexport: bool = false, 1657 must_link: bool = false, 1658 hidden: bool = false, 1659 allow_so_scripts: bool = false, 1660 preferred_mode: std.builtin.LinkMode, 1661 search_strategy: SearchStrategy, 1662 1663 fn fallbackMode(q: Query) std.builtin.LinkMode { 1664 assert(q.search_strategy != .no_fallback); 1665 return switch (q.preferred_mode) { 1666 .dynamic => .static, 1667 .static => .dynamic, 1668 }; 1669 } 1670 }; 1671 1672 pub const SearchStrategy = enum { 1673 paths_first, 1674 mode_first, 1675 no_fallback, 1676 }; 1677 }; 1678 1679 pub const Input = union(enum) { 1680 object: Object, 1681 archive: Object, 1682 res: Res, 1683 /// May not be a GNU ld script. Those are resolved when converting from 1684 /// `UnresolvedInput` to `Input` values. 1685 dso: Dso, 1686 dso_exact: DsoExact, 1687 1688 pub const Object = struct { 1689 path: Path, 1690 file: fs.File, 1691 must_link: bool, 1692 hidden: bool, 1693 }; 1694 1695 pub const Res = struct { 1696 path: Path, 1697 file: fs.File, 1698 }; 1699 1700 pub const Dso = struct { 1701 path: Path, 1702 file: fs.File, 1703 needed: bool, 1704 weak: bool, 1705 reexport: bool, 1706 }; 1707 1708 pub const DsoExact = struct { 1709 /// Includes the ":" prefix. This is intended to be put into the DSO 1710 /// section verbatim with no corresponding rpaths. 1711 name: []const u8, 1712 }; 1713 1714 /// Returns `null` in the case of `dso_exact`. 1715 pub fn path(input: Input) ?Path { 1716 return switch (input) { 1717 .object, .archive => |obj| obj.path, 1718 inline .res, .dso => |x| x.path, 1719 .dso_exact => null, 1720 }; 1721 } 1722 1723 /// Returns `null` in the case of `dso_exact`. 1724 pub fn pathAndFile(input: Input) ?struct { Path, fs.File } { 1725 return switch (input) { 1726 .object, .archive => |obj| .{ obj.path, obj.file }, 1727 inline .res, .dso => |x| .{ x.path, x.file }, 1728 .dso_exact => null, 1729 }; 1730 } 1731 1732 pub fn taskName(input: Input) []const u8 { 1733 return switch (input) { 1734 .object, .archive => |obj| obj.path.basename(), 1735 inline .res, .dso => |x| x.path.basename(), 1736 .dso_exact => "dso_exact", 1737 }; 1738 } 1739 }; 1740 1741 pub fn hashInputs(man: *Cache.Manifest, link_inputs: []const Input) !void { 1742 for (link_inputs) |link_input| { 1743 man.hash.add(@as(@typeInfo(Input).@"union".tag_type.?, link_input)); 1744 switch (link_input) { 1745 .object, .archive => |obj| { 1746 _ = try man.addOpenedFile(obj.path, obj.file, null); 1747 man.hash.add(obj.must_link); 1748 man.hash.add(obj.hidden); 1749 }, 1750 .res => |res| { 1751 _ = try man.addOpenedFile(res.path, res.file, null); 1752 }, 1753 .dso => |dso| { 1754 _ = try man.addOpenedFile(dso.path, dso.file, null); 1755 man.hash.add(dso.needed); 1756 man.hash.add(dso.weak); 1757 man.hash.add(dso.reexport); 1758 }, 1759 .dso_exact => |dso_exact| { 1760 man.hash.addBytes(dso_exact.name); 1761 }, 1762 } 1763 } 1764 } 1765 1766 pub fn resolveInputs( 1767 gpa: Allocator, 1768 arena: Allocator, 1769 target: std.Target, 1770 /// This function mutates this array but does not take ownership. 1771 /// Allocated with `gpa`. 1772 unresolved_inputs: *std.ArrayListUnmanaged(UnresolvedInput), 1773 /// Allocated with `gpa`. 1774 resolved_inputs: *std.ArrayListUnmanaged(Input), 1775 lib_directories: []const Cache.Directory, 1776 color: std.zig.Color, 1777 ) Allocator.Error!void { 1778 var checked_paths: std.ArrayListUnmanaged(u8) = .empty; 1779 defer checked_paths.deinit(gpa); 1780 1781 var ld_script_bytes: std.ArrayListUnmanaged(u8) = .empty; 1782 defer ld_script_bytes.deinit(gpa); 1783 1784 var failed_libs: std.ArrayListUnmanaged(struct { 1785 name: []const u8, 1786 strategy: UnresolvedInput.SearchStrategy, 1787 checked_paths: []const u8, 1788 preferred_mode: std.builtin.LinkMode, 1789 }) = .empty; 1790 1791 // Convert external system libs into a stack so that items can be 1792 // pushed to it. 1793 // 1794 // This is necessary because shared objects might turn out to be 1795 // "linker scripts" that in fact resolve to one or more other 1796 // external system libs, including parameters such as "needed". 1797 // 1798 // Unfortunately, such files need to be detected immediately, so 1799 // that this library search logic can be applied to them. 1800 mem.reverse(UnresolvedInput, unresolved_inputs.items); 1801 1802 syslib: while (unresolved_inputs.popOrNull()) |unresolved_input| { 1803 const name_query: UnresolvedInput.NameQuery = switch (unresolved_input) { 1804 .name_query => |nq| nq, 1805 .ambiguous_name => |an| an: { 1806 const lib_name, const link_mode = stripLibPrefixAndSuffix(an.name, target) orelse { 1807 try resolvePathInput(gpa, arena, unresolved_inputs, resolved_inputs, &ld_script_bytes, target, .{ 1808 .path = Path.initCwd(an.name), 1809 .query = an.query, 1810 }, color); 1811 continue; 1812 }; 1813 break :an .{ 1814 .name = lib_name, 1815 .query = .{ 1816 .needed = an.query.needed, 1817 .weak = an.query.weak, 1818 .reexport = an.query.reexport, 1819 .must_link = an.query.must_link, 1820 .hidden = an.query.hidden, 1821 .allow_so_scripts = an.query.allow_so_scripts, 1822 .preferred_mode = link_mode, 1823 .search_strategy = .no_fallback, 1824 }, 1825 }; 1826 }, 1827 .path_query => |pq| { 1828 try resolvePathInput(gpa, arena, unresolved_inputs, resolved_inputs, &ld_script_bytes, target, pq, color); 1829 continue; 1830 }, 1831 .dso_exact => |dso_exact| { 1832 try resolved_inputs.append(gpa, .{ .dso_exact = dso_exact }); 1833 continue; 1834 }, 1835 }; 1836 const query = name_query.query; 1837 1838 // Checked in the first pass above while looking for libc libraries. 1839 assert(!fs.path.isAbsolute(name_query.name)); 1840 1841 checked_paths.clearRetainingCapacity(); 1842 1843 switch (query.search_strategy) { 1844 .mode_first, .no_fallback => { 1845 // check for preferred mode 1846 for (lib_directories) |lib_directory| switch (try resolveLibInput( 1847 gpa, 1848 arena, 1849 unresolved_inputs, 1850 resolved_inputs, 1851 &checked_paths, 1852 &ld_script_bytes, 1853 lib_directory, 1854 name_query, 1855 target, 1856 query.preferred_mode, 1857 color, 1858 )) { 1859 .ok => continue :syslib, 1860 .no_match => {}, 1861 }; 1862 // check for fallback mode 1863 if (query.search_strategy == .no_fallback) { 1864 try failed_libs.append(arena, .{ 1865 .name = name_query.name, 1866 .strategy = query.search_strategy, 1867 .checked_paths = try arena.dupe(u8, checked_paths.items), 1868 .preferred_mode = query.preferred_mode, 1869 }); 1870 continue :syslib; 1871 } 1872 for (lib_directories) |lib_directory| switch (try resolveLibInput( 1873 gpa, 1874 arena, 1875 unresolved_inputs, 1876 resolved_inputs, 1877 &checked_paths, 1878 &ld_script_bytes, 1879 lib_directory, 1880 name_query, 1881 target, 1882 query.fallbackMode(), 1883 color, 1884 )) { 1885 .ok => continue :syslib, 1886 .no_match => {}, 1887 }; 1888 try failed_libs.append(arena, .{ 1889 .name = name_query.name, 1890 .strategy = query.search_strategy, 1891 .checked_paths = try arena.dupe(u8, checked_paths.items), 1892 .preferred_mode = query.preferred_mode, 1893 }); 1894 continue :syslib; 1895 }, 1896 .paths_first => { 1897 for (lib_directories) |lib_directory| { 1898 // check for preferred mode 1899 switch (try resolveLibInput( 1900 gpa, 1901 arena, 1902 unresolved_inputs, 1903 resolved_inputs, 1904 &checked_paths, 1905 &ld_script_bytes, 1906 lib_directory, 1907 name_query, 1908 target, 1909 query.preferred_mode, 1910 color, 1911 )) { 1912 .ok => continue :syslib, 1913 .no_match => {}, 1914 } 1915 1916 // check for fallback mode 1917 switch (try resolveLibInput( 1918 gpa, 1919 arena, 1920 unresolved_inputs, 1921 resolved_inputs, 1922 &checked_paths, 1923 &ld_script_bytes, 1924 lib_directory, 1925 name_query, 1926 target, 1927 query.fallbackMode(), 1928 color, 1929 )) { 1930 .ok => continue :syslib, 1931 .no_match => {}, 1932 } 1933 } 1934 try failed_libs.append(arena, .{ 1935 .name = name_query.name, 1936 .strategy = query.search_strategy, 1937 .checked_paths = try arena.dupe(u8, checked_paths.items), 1938 .preferred_mode = query.preferred_mode, 1939 }); 1940 continue :syslib; 1941 }, 1942 } 1943 @compileError("unreachable"); 1944 } 1945 1946 if (failed_libs.items.len > 0) { 1947 for (failed_libs.items) |f| { 1948 const searched_paths = if (f.checked_paths.len == 0) " none" else f.checked_paths; 1949 std.log.err("unable to find {s} system library '{s}' using strategy '{s}'. searched paths:{s}", .{ 1950 @tagName(f.preferred_mode), f.name, @tagName(f.strategy), searched_paths, 1951 }); 1952 } 1953 std.process.exit(1); 1954 } 1955 } 1956 1957 const ResolveLibInputResult = enum { ok, no_match }; 1958 const fatal = std.process.fatal; 1959 1960 fn resolveLibInput( 1961 gpa: Allocator, 1962 arena: Allocator, 1963 /// Allocated via `gpa`. 1964 unresolved_inputs: *std.ArrayListUnmanaged(UnresolvedInput), 1965 /// Allocated via `gpa`. 1966 resolved_inputs: *std.ArrayListUnmanaged(Input), 1967 /// Allocated via `gpa`. 1968 checked_paths: *std.ArrayListUnmanaged(u8), 1969 /// Allocated via `gpa`. 1970 ld_script_bytes: *std.ArrayListUnmanaged(u8), 1971 lib_directory: Directory, 1972 name_query: UnresolvedInput.NameQuery, 1973 target: std.Target, 1974 link_mode: std.builtin.LinkMode, 1975 color: std.zig.Color, 1976 ) Allocator.Error!ResolveLibInputResult { 1977 try resolved_inputs.ensureUnusedCapacity(gpa, 1); 1978 1979 const lib_name = name_query.name; 1980 1981 if (target.isDarwin() and link_mode == .dynamic) tbd: { 1982 // Prefer .tbd over .dylib. 1983 const test_path: Path = .{ 1984 .root_dir = lib_directory, 1985 .sub_path = try std.fmt.allocPrint(arena, "lib{s}.tbd", .{lib_name}), 1986 }; 1987 try checked_paths.writer(gpa).print("\n {}", .{test_path}); 1988 var file = test_path.root_dir.handle.openFile(test_path.sub_path, .{}) catch |err| switch (err) { 1989 error.FileNotFound => break :tbd, 1990 else => |e| fatal("unable to search for tbd library '{}': {s}", .{ test_path, @errorName(e) }), 1991 }; 1992 errdefer file.close(); 1993 return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, name_query.query); 1994 } 1995 1996 { 1997 const test_path: Path = .{ 1998 .root_dir = lib_directory, 1999 .sub_path = try std.fmt.allocPrint(arena, "{s}{s}{s}", .{ 2000 target.libPrefix(), lib_name, switch (link_mode) { 2001 .static => target.staticLibSuffix(), 2002 .dynamic => target.dynamicLibSuffix(), 2003 }, 2004 }), 2005 }; 2006 try checked_paths.writer(gpa).print("\n {}", .{test_path}); 2007 switch (try resolvePathInputLib(gpa, arena, unresolved_inputs, resolved_inputs, ld_script_bytes, target, .{ 2008 .path = test_path, 2009 .query = name_query.query, 2010 }, link_mode, color)) { 2011 .no_match => {}, 2012 .ok => return .ok, 2013 } 2014 } 2015 2016 // In the case of Darwin, the main check will be .dylib, so here we 2017 // additionally check for .so files. 2018 if (target.isDarwin() and link_mode == .dynamic) so: { 2019 const test_path: Path = .{ 2020 .root_dir = lib_directory, 2021 .sub_path = try std.fmt.allocPrint(arena, "lib{s}.so", .{lib_name}), 2022 }; 2023 try checked_paths.writer(gpa).print("\n {}", .{test_path}); 2024 var file = test_path.root_dir.handle.openFile(test_path.sub_path, .{}) catch |err| switch (err) { 2025 error.FileNotFound => break :so, 2026 else => |e| fatal("unable to search for so library '{}': {s}", .{ 2027 test_path, @errorName(e), 2028 }), 2029 }; 2030 errdefer file.close(); 2031 return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, name_query.query); 2032 } 2033 2034 // In the case of MinGW, the main check will be .lib but we also need to 2035 // look for `libfoo.a`. 2036 if (target.isMinGW() and link_mode == .static) mingw: { 2037 const test_path: Path = .{ 2038 .root_dir = lib_directory, 2039 .sub_path = try std.fmt.allocPrint(arena, "lib{s}.a", .{lib_name}), 2040 }; 2041 try checked_paths.writer(gpa).print("\n {}", .{test_path}); 2042 var file = test_path.root_dir.handle.openFile(test_path.sub_path, .{}) catch |err| switch (err) { 2043 error.FileNotFound => break :mingw, 2044 else => |e| fatal("unable to search for static library '{}': {s}", .{ test_path, @errorName(e) }), 2045 }; 2046 errdefer file.close(); 2047 return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, name_query.query); 2048 } 2049 2050 return .no_match; 2051 } 2052 2053 fn finishResolveLibInput( 2054 resolved_inputs: *std.ArrayListUnmanaged(Input), 2055 path: Path, 2056 file: std.fs.File, 2057 link_mode: std.builtin.LinkMode, 2058 query: UnresolvedInput.Query, 2059 ) ResolveLibInputResult { 2060 switch (link_mode) { 2061 .static => resolved_inputs.appendAssumeCapacity(.{ .archive = .{ 2062 .path = path, 2063 .file = file, 2064 .must_link = query.must_link, 2065 .hidden = query.hidden, 2066 } }), 2067 .dynamic => resolved_inputs.appendAssumeCapacity(.{ .dso = .{ 2068 .path = path, 2069 .file = file, 2070 .needed = query.needed, 2071 .weak = query.weak, 2072 .reexport = query.reexport, 2073 } }), 2074 } 2075 return .ok; 2076 } 2077 2078 fn resolvePathInput( 2079 gpa: Allocator, 2080 arena: Allocator, 2081 /// Allocated with `gpa`. 2082 unresolved_inputs: *std.ArrayListUnmanaged(UnresolvedInput), 2083 /// Allocated with `gpa`. 2084 resolved_inputs: *std.ArrayListUnmanaged(Input), 2085 /// Allocated via `gpa`. 2086 ld_script_bytes: *std.ArrayListUnmanaged(u8), 2087 target: std.Target, 2088 pq: UnresolvedInput.PathQuery, 2089 color: std.zig.Color, 2090 ) Allocator.Error!void { 2091 switch (switch (Compilation.classifyFileExt(pq.path.sub_path)) { 2092 .static_library => try resolvePathInputLib(gpa, arena, unresolved_inputs, resolved_inputs, ld_script_bytes, target, pq, .static, color), 2093 .shared_library => try resolvePathInputLib(gpa, arena, unresolved_inputs, resolved_inputs, ld_script_bytes, target, pq, .dynamic, color), 2094 .object => { 2095 var file = pq.path.root_dir.handle.openFile(pq.path.sub_path, .{}) catch |err| 2096 fatal("failed to open object {}: {s}", .{ pq.path, @errorName(err) }); 2097 errdefer file.close(); 2098 try resolved_inputs.append(gpa, .{ .object = .{ 2099 .path = pq.path, 2100 .file = file, 2101 .must_link = pq.query.must_link, 2102 .hidden = pq.query.hidden, 2103 } }); 2104 return; 2105 }, 2106 .res => { 2107 var file = pq.path.root_dir.handle.openFile(pq.path.sub_path, .{}) catch |err| 2108 fatal("failed to open windows resource {}: {s}", .{ pq.path, @errorName(err) }); 2109 errdefer file.close(); 2110 try resolved_inputs.append(gpa, .{ .res = .{ 2111 .path = pq.path, 2112 .file = file, 2113 } }); 2114 return; 2115 }, 2116 else => fatal("{}: unrecognized file extension", .{pq.path}), 2117 }) { 2118 .ok => {}, 2119 .no_match => fatal("{}: file not found", .{pq.path}), 2120 } 2121 } 2122 2123 fn resolvePathInputLib( 2124 gpa: Allocator, 2125 arena: Allocator, 2126 /// Allocated with `gpa`. 2127 unresolved_inputs: *std.ArrayListUnmanaged(UnresolvedInput), 2128 /// Allocated with `gpa`. 2129 resolved_inputs: *std.ArrayListUnmanaged(Input), 2130 /// Allocated via `gpa`. 2131 ld_script_bytes: *std.ArrayListUnmanaged(u8), 2132 target: std.Target, 2133 pq: UnresolvedInput.PathQuery, 2134 link_mode: std.builtin.LinkMode, 2135 color: std.zig.Color, 2136 ) Allocator.Error!ResolveLibInputResult { 2137 try resolved_inputs.ensureUnusedCapacity(gpa, 1); 2138 2139 const test_path: Path = pq.path; 2140 // In the case of .so files, they might actually be "linker scripts" 2141 // that contain references to other libraries. 2142 if (pq.query.allow_so_scripts and target.ofmt == .elf and mem.endsWith(u8, test_path.sub_path, ".so")) { 2143 var file = test_path.root_dir.handle.openFile(test_path.sub_path, .{}) catch |err| switch (err) { 2144 error.FileNotFound => return .no_match, 2145 else => |e| fatal("unable to search for {s} library '{'}': {s}", .{ 2146 @tagName(link_mode), test_path, @errorName(e), 2147 }), 2148 }; 2149 errdefer file.close(); 2150 try ld_script_bytes.resize(gpa, @sizeOf(std.elf.Elf64_Ehdr)); 2151 const n = file.preadAll(ld_script_bytes.items, 0) catch |err| fatal("failed to read '{'}': {s}", .{ 2152 test_path, @errorName(err), 2153 }); 2154 elf_file: { 2155 if (n != ld_script_bytes.items.len) break :elf_file; 2156 if (!mem.eql(u8, ld_script_bytes.items[0..4], "\x7fELF")) break :elf_file; 2157 // Appears to be an ELF file. 2158 return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, pq.query); 2159 } 2160 const stat = file.stat() catch |err| 2161 fatal("failed to stat {}: {s}", .{ test_path, @errorName(err) }); 2162 const size = std.math.cast(u32, stat.size) orelse 2163 fatal("{}: linker script too big", .{test_path}); 2164 try ld_script_bytes.resize(gpa, size); 2165 const buf = ld_script_bytes.items[n..]; 2166 const n2 = file.preadAll(buf, n) catch |err| 2167 fatal("failed to read {}: {s}", .{ test_path, @errorName(err) }); 2168 if (n2 != buf.len) fatal("failed to read {}: unexpected end of file", .{test_path}); 2169 var diags = Diags.init(gpa); 2170 defer diags.deinit(); 2171 const ld_script_result = LdScript.parse(gpa, &diags, test_path, ld_script_bytes.items); 2172 if (diags.hasErrors()) { 2173 var wip_errors: std.zig.ErrorBundle.Wip = undefined; 2174 try wip_errors.init(gpa); 2175 defer wip_errors.deinit(); 2176 2177 try diags.addMessagesToBundle(&wip_errors); 2178 2179 var error_bundle = try wip_errors.toOwnedBundle(""); 2180 defer error_bundle.deinit(gpa); 2181 2182 error_bundle.renderToStdErr(color.renderOptions()); 2183 2184 std.process.exit(1); 2185 } 2186 2187 var ld_script = ld_script_result catch |err| 2188 fatal("{}: failed to parse linker script: {s}", .{ test_path, @errorName(err) }); 2189 defer ld_script.deinit(gpa); 2190 2191 try unresolved_inputs.ensureUnusedCapacity(gpa, ld_script.args.len); 2192 for (ld_script.args) |arg| { 2193 const query: UnresolvedInput.Query = .{ 2194 .needed = arg.needed or pq.query.needed, 2195 .weak = pq.query.weak, 2196 .reexport = pq.query.reexport, 2197 .preferred_mode = pq.query.preferred_mode, 2198 .search_strategy = pq.query.search_strategy, 2199 .allow_so_scripts = pq.query.allow_so_scripts, 2200 }; 2201 if (mem.startsWith(u8, arg.path, "-l")) { 2202 unresolved_inputs.appendAssumeCapacity(.{ .name_query = .{ 2203 .name = try arena.dupe(u8, arg.path["-l".len..]), 2204 .query = query, 2205 } }); 2206 } else { 2207 unresolved_inputs.appendAssumeCapacity(.{ .ambiguous_name = .{ 2208 .name = try arena.dupe(u8, arg.path), 2209 .query = query, 2210 } }); 2211 } 2212 } 2213 file.close(); 2214 return .ok; 2215 } 2216 2217 var file = test_path.root_dir.handle.openFile(test_path.sub_path, .{}) catch |err| switch (err) { 2218 error.FileNotFound => return .no_match, 2219 else => |e| fatal("unable to search for {s} library {}: {s}", .{ 2220 @tagName(link_mode), test_path, @errorName(e), 2221 }), 2222 }; 2223 errdefer file.close(); 2224 return finishResolveLibInput(resolved_inputs, test_path, file, link_mode, pq.query); 2225 } 2226 2227 pub fn openObject(path: Path, must_link: bool, hidden: bool) !Input.Object { 2228 var file = try path.root_dir.handle.openFile(path.sub_path, .{}); 2229 errdefer file.close(); 2230 return .{ 2231 .path = path, 2232 .file = file, 2233 .must_link = must_link, 2234 .hidden = hidden, 2235 }; 2236 } 2237 2238 pub fn openDso(path: Path, needed: bool, weak: bool, reexport: bool) !Input.Dso { 2239 var file = try path.root_dir.handle.openFile(path.sub_path, .{}); 2240 errdefer file.close(); 2241 return .{ 2242 .path = path, 2243 .file = file, 2244 .needed = needed, 2245 .weak = weak, 2246 .reexport = reexport, 2247 }; 2248 } 2249 2250 pub fn openObjectInput(diags: *Diags, path: Path) error{LinkFailure}!Input { 2251 return .{ .object = openObject(path, false, false) catch |err| { 2252 return diags.failParse(path, "failed to open {}: {s}", .{ path, @errorName(err) }); 2253 } }; 2254 } 2255 2256 pub fn openArchiveInput(diags: *Diags, path: Path, must_link: bool, hidden: bool) error{LinkFailure}!Input { 2257 return .{ .archive = openObject(path, must_link, hidden) catch |err| { 2258 return diags.failParse(path, "failed to open {}: {s}", .{ path, @errorName(err) }); 2259 } }; 2260 } 2261 2262 pub fn openDsoInput(diags: *Diags, path: Path, needed: bool, weak: bool, reexport: bool) error{LinkFailure}!Input { 2263 return .{ .dso = openDso(path, needed, weak, reexport) catch |err| { 2264 return diags.failParse(path, "failed to open {}: {s}", .{ path, @errorName(err) }); 2265 } }; 2266 } 2267 2268 fn stripLibPrefixAndSuffix(path: []const u8, target: std.Target) ?struct { []const u8, std.builtin.LinkMode } { 2269 const prefix = target.libPrefix(); 2270 const static_suffix = target.staticLibSuffix(); 2271 const dynamic_suffix = target.dynamicLibSuffix(); 2272 const basename = fs.path.basename(path); 2273 const unlibbed = if (mem.startsWith(u8, basename, prefix)) basename[prefix.len..] else return null; 2274 if (mem.endsWith(u8, unlibbed, static_suffix)) return .{ 2275 unlibbed[0 .. unlibbed.len - static_suffix.len], .static, 2276 }; 2277 if (mem.endsWith(u8, unlibbed, dynamic_suffix)) return .{ 2278 unlibbed[0 .. unlibbed.len - dynamic_suffix.len], .dynamic, 2279 }; 2280 return null; 2281 } 2282 2283 /// Returns true if and only if there is at least one input of type object, 2284 /// archive, or Windows resource file. 2285 pub fn anyObjectInputs(inputs: []const Input) bool { 2286 return countObjectInputs(inputs) != 0; 2287 } 2288 2289 /// Returns the number of inputs of type object, archive, or Windows resource file. 2290 pub fn countObjectInputs(inputs: []const Input) usize { 2291 var count: usize = 0; 2292 for (inputs) |input| switch (input) { 2293 .dso, .dso_exact => continue, 2294 .res, .object, .archive => count += 1, 2295 }; 2296 return count; 2297 } 2298 2299 /// Returns the first input of type object or archive. 2300 pub fn firstObjectInput(inputs: []const Input) ?Input.Object { 2301 for (inputs) |input| switch (input) { 2302 .object, .archive => |obj| return obj, 2303 .res, .dso, .dso_exact => continue, 2304 }; 2305 return null; 2306 }