authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-02-17 19:26:32-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-02-17 19:26:32-05:00
log5a4e8c779a713d7b07cec262b07afd6883684c98
treeef48c71ed261bcb0ff675e62b11b84b049c667fe
parentb53afc510c763f5da57bc654a6cc42a233304938
signaturelock-open Commit is signed but in an unrecognized format.

smarter detectNativeDynamicLinker logic

The current target's ABI cannot be relied on for this. For example, we may build the zig compiler for target riscv64-linux-musl and provide a tarball for users to download. A user could then run that zig compiler on riscv64-linux-gnu. This use case is well-defined and supported by Zig. But that means that we must detect the system ABI here rather than relying on `std.Target.current`.

1 files changed, 77 insertions(+), 20 deletions(-)

src-self-hosted/libc_installation.zig+77-20
......@@ -526,6 +526,9 @@ fn ccPrintFileName(
526526
527527 var it = std.mem.tokenize(exec_res.stdout, "\n\r");
528528 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;
529532 switch (want_dirname) {
530533 .full_path => return std.mem.dupeZ(allocator, u8, line),
531534 .only_dir => {
......@@ -536,26 +539,74 @@ fn ccPrintFileName(
536539}
537540
538541/// Caller owns returned memory.
539pub fn detectNativeDynamicLinker(allocator: *Allocator) error{OutOfMemory, TargetHasNoDynamicLinker, UnknownDynamicLinkerPath}![:0]u8 {
542pub fn detectNativeDynamicLinker(allocator: *Allocator) error{
543 OutOfMemory,
544 TargetHasNoDynamicLinker,
545 UnknownDynamicLinkerPath,
546}![:0]u8 {
540547 if (!comptime Target.current.hasDynamicLinker()) {
541548 return error.TargetHasNoDynamicLinker;
542549 }
543550
544 const standard_ld_path = try std.Target.current.getStandardDynamicLinkerPath(allocator);
545 var standard_ld_path_resource: ?[:0]u8 = standard_ld_path; // Set to null to avoid freeing it.
546 defer if (standard_ld_path_resource) |s| allocator.free(s);
551 // The current target's ABI cannot be relied on for this. For example, we may build the zig
552 // compiler for target riscv64-linux-musl and provide a tarball for users to download.
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`.
547556
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 }
549566
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.
550599 {
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.
553600 const lib_paths = try std.process.getSelfExeSharedLibPaths(allocator);
554601 defer allocator.free(lib_paths);
555602
603 // This is O(N^M) but typical case here is N=2 and M=10.
556604 for (lib_paths) |lib_path| {
557 if (std.mem.endsWith(u8, lib_path, standard_ld_basename)) {
558 return std.mem.dupeZ(allocator, u8, lib_path);
605 for (ld_info_list.toSlice()) |ld_info| {
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 }
559610 }
560611 }
561612 }
......@@ -563,17 +614,23 @@ pub fn detectNativeDynamicLinker(allocator: *Allocator) error{OutOfMemory, Targe
563614 // If Zig is statically linked, such as via distributed binary static builds, the above
564615 // trick won't work. What are we left with? Try to run the system C compiler and get
565616 // it to tell us the dynamic linker path.
566 return ccPrintFileName(allocator, standard_ld_basename, .full_path) catch |err| switch (err) {
567 error.OutOfMemory => return error.OutOfMemory,
568 error.LibCRuntimeNotFound,
569 error.CCompilerExitCode,
570 error.CCompilerCrashed,
571 error.UnableToSpawnCCompiler,
572 => {
573 standard_ld_path_resource = null; // Prevent freeing standard_ld_path.
574 return standard_ld_path;
575 },
576 };
617 // TODO: instead of this, look at the shared libs of /usr/bin/env.
618 for (ld_info_list.toSlice()) |ld_info| {
619 const standard_ld_basename = fs.path.basename(ld_info.ld_path);
620
621 const full_ld_path = ccPrintFileName(allocator, standard_ld_basename, .full_path) catch |err| switch (err) {
622 error.OutOfMemory => return error.OutOfMemory,
623 error.LibCRuntimeNotFound,
624 error.CCompilerExitCode,
625 error.CCompilerCrashed,
626 error.UnableToSpawnCCompiler,
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);
577634}
578635
579636const Search = struct {