| ... | @@ -526,6 +526,9 @@ fn ccPrintFileName( | ... | @@ -526,6 +526,9 @@ fn ccPrintFileName( |
| 526 | | 526 | |
| 527 | var it = std.mem.tokenize(exec_res.stdout, "\n\r"); | 527 | var it = std.mem.tokenize(exec_res.stdout, "\n\r"); |
| 528 | const line = it.next() orelse return error.LibCRuntimeNotFound; | 528 | const line = it.next() orelse return error.LibCRuntimeNotFound; |
| | 529 | // When this command fails, it returns exit code 0 and duplicates the input file name. |
| | 530 | // So we detect failure by checking if the output matches exactly the input. |
| | 531 | if (std.mem.eql(u8, line, o_file)) return error.LibCRuntimeNotFound; |
| 529 | switch (want_dirname) { | 532 | switch (want_dirname) { |
| 530 | .full_path => return std.mem.dupeZ(allocator, u8, line), | 533 | .full_path => return std.mem.dupeZ(allocator, u8, line), |
| 531 | .only_dir => { | 534 | .only_dir => { |
| ... | @@ -536,26 +539,74 @@ fn ccPrintFileName( | ... | @@ -536,26 +539,74 @@ fn ccPrintFileName( |
| 536 | } | 539 | } |
| 537 | | 540 | |
| 538 | /// Caller owns returned memory. | 541 | /// Caller owns returned memory. |
| 539 | pub fn detectNativeDynamicLinker(allocator: *Allocator) error{OutOfMemory, TargetHasNoDynamicLinker, UnknownDynamicLinkerPath}![:0]u8 { | 542 | pub fn detectNativeDynamicLinker(allocator: *Allocator) error{ |
| | 543 | OutOfMemory, |
| | 544 | TargetHasNoDynamicLinker, |
| | 545 | UnknownDynamicLinkerPath, |
| | 546 | }![:0]u8 { |
| 540 | if (!comptime Target.current.hasDynamicLinker()) { | 547 | if (!comptime Target.current.hasDynamicLinker()) { |
| 541 | return error.TargetHasNoDynamicLinker; | 548 | return error.TargetHasNoDynamicLinker; |
| 542 | } | 549 | } |
| 543 | | 550 | |
| 544 | const standard_ld_path = try std.Target.current.getStandardDynamicLinkerPath(allocator); | 551 | // The current target's ABI cannot be relied on for this. For example, we may build the zig |
| 545 | var standard_ld_path_resource: ?[:0]u8 = standard_ld_path; // Set to null to avoid freeing it. | 552 | // compiler for target riscv64-linux-musl and provide a tarball for users to download. |
| 546 | defer if (standard_ld_path_resource) |s| allocator.free(s); | 553 | // A user could then run that zig compiler on riscv64-linux-gnu. This use case is well-defined |
| | 554 | // and supported by Zig. But that means that we must detect the system ABI here rather than |
| | 555 | // relying on `std.Target.current`. |
| 547 | | 556 | |
| 548 | const standard_ld_basename = fs.path.basename(standard_ld_path); | 557 | const LdInfo = struct { |
| | 558 | ld_path: []u8, |
| | 559 | abi: Target.Abi, |
| | 560 | }; |
| | 561 | var ld_info_list = std.ArrayList(LdInfo).init(allocator); |
| | 562 | defer { |
| | 563 | for (ld_info_list.toSlice()) |ld_info| allocator.free(ld_info.ld_path); |
| | 564 | ld_info_list.deinit(); |
| | 565 | } |
| 549 | | 566 | |
| | 567 | const all_abis = comptime blk: { |
| | 568 | const fields = std.meta.fields(Target.Abi); |
| | 569 | var array: [fields.len]Target.Abi = undefined; |
| | 570 | inline for (fields) |field, i| { |
| | 571 | array[i] = @field(Target.Abi, field.name); |
| | 572 | } |
| | 573 | break :blk array; |
| | 574 | }; |
| | 575 | for (all_abis) |abi| { |
| | 576 | // This may be a nonsensical parameter. We detect this with error.UnknownDynamicLinkerPath and |
| | 577 | // skip adding it to `ld_info_list`. |
| | 578 | const target: Target = .{ |
| | 579 | .Cross = .{ |
| | 580 | .arch = Target.current.getArch(), |
| | 581 | .os = Target.current.getOs(), |
| | 582 | .abi = abi, |
| | 583 | .cpu_features = Target.current.getArch().getBaselineCpuFeatures(), |
| | 584 | }, |
| | 585 | }; |
| | 586 | const standard_ld_path = target.getStandardDynamicLinkerPath(allocator) catch |err| switch (err) { |
| | 587 | error.OutOfMemory => return error.OutOfMemory, |
| | 588 | error.UnknownDynamicLinkerPath, error.TargetHasNoDynamicLinker => continue, |
| | 589 | }; |
| | 590 | errdefer allocator.free(standard_ld_path); |
| | 591 | try ld_info_list.append(.{ |
| | 592 | .ld_path = standard_ld_path, |
| | 593 | .abi = abi, |
| | 594 | }); |
| | 595 | } |
| | 596 | |
| | 597 | // Best case scenario: the zig compiler is dynamically linked, and we can iterate |
| | 598 | // over our own shared objects and find a dynamic linker. |
| 550 | { | 599 | { |
| 551 | // Best case scenario: the current executable is dynamically linked, and we can iterate | | |
| 552 | // over our own shared objects and find a dynamic linker. | | |
| 553 | const lib_paths = try std.process.getSelfExeSharedLibPaths(allocator); | 600 | const lib_paths = try std.process.getSelfExeSharedLibPaths(allocator); |
| 554 | defer allocator.free(lib_paths); | 601 | defer allocator.free(lib_paths); |
| 555 | | 602 | |
| | 603 | // This is O(N^M) but typical case here is N=2 and M=10. |
| 556 | for (lib_paths) |lib_path| { | 604 | for (lib_paths) |lib_path| { |
| 557 | if (std.mem.endsWith(u8, lib_path, standard_ld_basename)) { | 605 | for (ld_info_list.toSlice()) |ld_info| { |
| 558 | return std.mem.dupeZ(allocator, u8, lib_path); | 606 | const standard_ld_basename = fs.path.basename(ld_info.ld_path); |
| | 607 | if (std.mem.endsWith(u8, lib_path, standard_ld_basename)) { |
| | 608 | return std.mem.dupeZ(allocator, u8, lib_path); |
| | 609 | } |
| 559 | } | 610 | } |
| 560 | } | 611 | } |
| 561 | } | 612 | } |
| ... | @@ -563,17 +614,23 @@ pub fn detectNativeDynamicLinker(allocator: *Allocator) error{OutOfMemory, Targe | ... | @@ -563,17 +614,23 @@ pub fn detectNativeDynamicLinker(allocator: *Allocator) error{OutOfMemory, Targe |
| 563 | // If Zig is statically linked, such as via distributed binary static builds, the above | 614 | // If Zig is statically linked, such as via distributed binary static builds, the above |
| 564 | // trick won't work. What are we left with? Try to run the system C compiler and get | 615 | // trick won't work. What are we left with? Try to run the system C compiler and get |
| 565 | // it to tell us the dynamic linker path. | 616 | // it to tell us the dynamic linker path. |
| 566 | return ccPrintFileName(allocator, standard_ld_basename, .full_path) catch |err| switch (err) { | 617 | // TODO: instead of this, look at the shared libs of /usr/bin/env. |
| 567 | error.OutOfMemory => return error.OutOfMemory, | 618 | for (ld_info_list.toSlice()) |ld_info| { |
| 568 | error.LibCRuntimeNotFound, | 619 | const standard_ld_basename = fs.path.basename(ld_info.ld_path); |
| 569 | error.CCompilerExitCode, | 620 | |
| 570 | error.CCompilerCrashed, | 621 | const full_ld_path = ccPrintFileName(allocator, standard_ld_basename, .full_path) catch |err| switch (err) { |
| 571 | error.UnableToSpawnCCompiler, | 622 | error.OutOfMemory => return error.OutOfMemory, |
| 572 | => { | 623 | error.LibCRuntimeNotFound, |
| 573 | standard_ld_path_resource = null; // Prevent freeing standard_ld_path. | 624 | error.CCompilerExitCode, |
| 574 | return standard_ld_path; | 625 | error.CCompilerCrashed, |
| 575 | }, | 626 | error.UnableToSpawnCCompiler, |
| 576 | }; | 627 | => continue, |
| | 628 | }; |
| | 629 | return full_ld_path; |
| | 630 | } |
| | 631 | |
| | 632 | // Finally, we fall back on the standard path. |
| | 633 | return Target.current.getStandardDynamicLinkerPath(allocator); |
| 577 | } | 634 | } |
| 578 | | 635 | |
| 579 | const Search = struct { | 636 | const Search = struct { |