╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ link.zig ─────────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭ find file ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ ╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │ │ │http│ ssh │ │ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
2420 .weak = query.weak, 2421 .reexport = query.reexport, 2422 } }), 2423 } 2424 return .ok; 2425 } 2426 2427 fn resolvePathInput( 2428 gpa: Allocator, 2429 arena: Allocator, 2430 io: Io, 2431 /// Allocated with `gpa`. 2432 unresolved_inputs: *std.ArrayList(UnresolvedInput), 2433 /// Allocated with `gpa`. 2434 resolved_inputs: *std.ArrayList(Input), 2435 /// Allocated via `gpa`. 2436 ld_script_bytes: *std.ArrayList(u8), 2437 /// Allocated via `gpa`. 2438 archive_dedup: *ArchiveDedupMap, 2439 target: *const std.Target, 2440 pq: UnresolvedInput.PathQuery, 2441 color: std.zig.Color, 2442 ) Allocator.Error!?ResolveLibInputResult { 2443 switch (Compilation.classifyFileExt(pq.path.sub_path)) { 2444 .static_library => return try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, archive_dedup, target, pq, .static, color), 2445 .shared_library => return try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, archive_dedup, target, pq, .dynamic, color), 2446 .object => { 2447 var file = pq.path.root_dir.handle.openFile(io, pq.path.sub_path, .{}) catch |err| 2448 fatal("failed to open object {f}: {s}", .{ pq.path, @errorName(err) }); 2449 errdefer file.close(io); 2450 try resolved_inputs.append(gpa, .{ .object = .{ 2451 .path = pq.path, 2452 .file = file, 2453 .must_link = pq.query.must_link, 2454 .hidden = pq.query.hidden, 2455 } }); 2456 return null; 2457 }, 2458 .res => { 2459 var file = pq.path.root_dir.handle.openFile(io, pq.path.sub_path, .{}) catch |err| 2460 fatal("failed to open windows resource {f}: {s}", .{ pq.path, @errorName(err) }); 2461 errdefer file.close(io); 2462 try resolved_inputs.append(gpa, .{ .res = .{ 2463 .path = pq.path, 2464 .file = file, 2465 } }); 2466 return null; 2467 }, 2468 else => fatal("{f}: unrecognized file extension", .{pq.path}), 2469 } 2470 } 2471 2472 fn resolvePathInputLib( 2473 gpa: Allocator, 2474 arena: Allocator, 2475 io: Io, 2476 /// Allocated with `gpa`. 2477 unresolved_inputs: *std.ArrayList(UnresolvedInput), 2478 /// Allocated with `gpa`. 2479 resolved_inputs: *std.ArrayList(Input), 2480 /// Allocated via `gpa`. 2481 ld_script_bytes: *std.ArrayList(u8), 2482 /// Allocated via `gpa`. 2483 archive_dedup: *ArchiveDedupMap, 2484 target: *const std.Target, 2485 pq: UnresolvedInput.PathQuery, 2486 link_mode: std.lang.LinkMode, 2487 color: std.zig.Color, 2488 ) Allocator.Error!ResolveLibInputResult { 2489 try resolved_inputs.ensureUnusedCapacity(gpa, 1); 2490 try archive_dedup.ensureUnusedCapacity(gpa, 1); 2491 2492 const test_path: Path = pq.path; 2493 // In the case of shared libraries, they might actually be "linker scripts" 2494 // that contain references to other libraries. 2495 if (pq.query.allow_so_scripts and target.ofmt == .elf and switch (Compilation.classifyFileExt(test_path.sub_path)) { 2496 .static_library, .shared_library => true, 2497 else => false, 2498 }) { 2499 var file = test_path.root_dir.handle.openFile(io, test_path.sub_path, .{}) catch |err| switch (err) { 2500 error.FileNotFound => return .no_match, 2501 else => |e| fatal("unable to search for {t} library '{f}': {t}", .{ 2502 link_mode, std.fmt.alt(test_path, .formatEscapeChar), e, 2503 }), 2504 }; 2505 errdefer file.close(io); 2506 try ld_script_bytes.resize(gpa, @max(std.elf.MAGIC.len, std.elf.ARMAG.len)); 2507 const n = file.readPositionalAll(io, ld_script_bytes.items, 0) catch |err| 2508 fatal("failed to read '{f}': {t}", .{ std.fmt.alt(test_path, .formatEscapeChar), err }); 2509 const buf = ld_script_bytes.items[0..n]; 2510 if (mem.startsWith(u8, buf, std.elf.MAGIC) or 2511 mem.startsWith(u8, buf, std.elf.ARMAG) or 2512 mem.startsWith(u8, buf, std.elf.ARMAG_THIN)) 2513 { 2514 // Appears to be an ELF or archive file. 2515 return finishResolveLibInput(io, resolved_inputs, archive_dedup, test_path, file, link_mode, pq.query); 2516 } 2517 const stat = file.stat(io) catch |err| 2518 fatal("failed to stat {f}: {t}", .{ test_path, err }); 2519 const size = std.math.cast(u32, stat.size) orelse 2520 fatal("{f}: linker script too big", .{test_path}); 2521 try ld_script_bytes.resize(gpa, size); 2522 const buf2 = ld_script_bytes.items[n..]; 2523 const n2 = file.readPositionalAll(io, buf2, n) catch |err| 2524 fatal("failed to read {f}: {t}", .{ test_path, err }); 2525 if (n2 != buf2.len) fatal("failed to read {f}: unexpected end of file", .{test_path}); 2526 2527 // This `Io` is only used for a mutex, and we know we aren't doing anything async/concurrent. 2528 var threaded: Io.Threaded = .init_single_threaded; 2529 defer threaded.deinit(); 2530 var diags: Diags = .init(gpa, threaded.io()); 2531 defer diags.deinit(); 2532 2533 const ld_script_result = LdScript.parse(gpa, &diags, test_path, ld_script_bytes.items); 2534 if (diags.hasErrors()) { 2535 var wip_errors: std.zig.ErrorBundle.Wip = undefined; 2536 try wip_errors.init(gpa); 2537 defer wip_errors.deinit(); 2538 2539 try diags.addMessagesToBundle(&wip_errors, null); 2540 2541 var error_bundle = try wip_errors.toOwnedBundle(""); 2542 defer error_bundle.deinit(gpa); 2543 2544 error_bundle.renderToStderr(io, .{}, color) catch {}; 2545 std.process.exit(1); 2546 } 2547 2548 var ld_script = ld_script_result catch |err| 2549 fatal("{f}: failed to parse linker script: {t}", .{ test_path, err }); 2550 defer ld_script.deinit(gpa); 2551 2552 try unresolved_inputs.ensureUnusedCapacity(gpa, ld_script.args.len); 2553 for (ld_script.args) |arg| { 2554 const query: UnresolvedInput.Query = .{ 2555 .needed = arg.needed or pq.query.needed, 2556 .weak = pq.query.weak, 2557 .reexport = pq.query.reexport, 2558 .preferred_mode = pq.query.preferred_mode, 2559 .search_strategy = pq.query.search_strategy, 2560 .allow_so_scripts = pq.query.allow_so_scripts, 2561 }; 2562 if (mem.startsWith(u8, arg.path, "-l")) { 2563 unresolved_inputs.appendAssumeCapacity(.{ .name_query = .{ 2564 .name = try arena.dupe(u8, arg.path["-l".len..]), 2565 .query = query, 2566 } }); 2567 } else { 2568 unresolved_inputs.appendAssumeCapacity(.{ .ambiguous_name = .{ 2569 .name = try arena.dupe(u8, arg.path), 2570 .query = query, 2571 } }); 2572 } 2573 } 2574 file.close(io); 2575 return .ok; 2576 } 2577 2578 var file = test_path.root_dir.handle.openFile(io, test_path.sub_path, .{}) catch |err| switch (err) { 2579 error.FileNotFound => return .no_match, 2580 else => |e| fatal("unable to search for {s} library {f}: {s}", .{ 2581 @tagName(link_mode), test_path, @errorName(e), 2582 }), 2583 }; 2584 errdefer file.close(io); 2585 return finishResolveLibInput(io, resolved_inputs, archive_dedup, test_path, file, link_mode, pq.query); 2586 } 2587 2588 pub fn openObject(io: Io, path: Path, must_link: bool, hidden: bool) !Input.Object { 2589 var file = try path.root_dir.handle.openFile(io, path.sub_path, .{}); 2590 errdefer file.close(io); 2591 return .{ 2592 .path = path, 2593 .file = file, 2594 .must_link = must_link, 2595 .hidden = hidden, 2596 }; 2597 } 2598 2599 pub fn openDso(io: Io, path: Path, needed: bool, weak: bool, reexport: bool) !Input.Dso { 2600 var file = try path.root_dir.handle.openFile(io, path.sub_path, .{}); 2601 errdefer file.close(io); 2602 return .{ 2603 .path = path, 2604 .file = file, 2605 .needed = needed, 2606 .weak = weak, 2607 .reexport = reexport, 2608 }; 2609 } 2610 2611 pub fn openObjectInput(io: Io, diags: *Diags, path: Path) error{AlreadyReported}!Input { 2612 return .{ .object = openObject(io, path, false, false) catch |err| { 2613 return diags.failParse(path, "failed to open {f}: {s}", .{ path, @errorName(err) }); 2614 } }; 2615 } 2616 2617 pub fn openArchiveInput(io: Io, diags: *Diags, path: Path, must_link: bool, hidden: bool) error{AlreadyReported}!Input { 2618 return .{ .archive = openObject(io, path, must_link, hidden) catch |err| { 2619 return diags.failParse(path, "failed to open {f}: {s}", .{ path, @errorName(err) }); 2620 } }; 2621 } 2622 2623 pub fn openDsoInput(io: Io, diags: *Diags, path: Path, needed: bool, weak: bool, reexport: bool) error{AlreadyReported}!Input { 2624 return .{ .dso = openDso(io, path, needed, weak, reexport) catch |err| { 2625 return diags.failParse(path, "failed to open {f}: {s}", .{ path, @errorName(err) }); 2626 } }; 2627 } 2628 2629 /// Returns true if and only if there is at least one input of type object, 2630 /// archive, or Windows resource file. 2631 pub fn anyObjectInputs(inputs: []const Input) bool { 2632 return countObjectInputs(inputs) != 0; 2633 } 2634 2635 /// Returns the number of inputs of type object, archive, or Windows resource file. 2636 pub fn countObjectInputs(inputs: []const Input) usize { 2637 var count: usize = 0; 2638 for (inputs) |input| switch (input) { 2639 .dso, .dso_exact => continue, 2640 .res, .object, .archive => count += 1, 2641 }; 2642 return count; 2643 } 2644 2645 /// Returns the first input of type object or archive. 2646 pub fn firstObjectInput(inputs: []const Input) ?Input.Object { 2647 for (inputs) |input| switch (input) { 2648 .object, .archive => |obj| return obj, 2649 .res, .dso, .dso_exact => continue, 2650 }; 2651 return null; 2652 } 2653