@@ -526,6 +526,9 @@ fn ccPrintFileName(
526
526
527
527
var it = std .mem .tokenize (exec_res .stdout , "\n \r " );
528
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
532
switch (want_dirname ) {
530
533
.full_path = > return std .mem .dupeZ (allocator , u8 , line ),
531
534
.only_dir = > {
@@ -536,44 +539,98 @@ fn ccPrintFileName(
536
539
}
537
540
538
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
547
if (! comptime Target .current .hasDynamicLinker ()) {
541
548
return error .TargetHasNoDynamicLinker ;
542
549
}
543
550
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`.
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
600
const lib_paths = try std .process .getSelfExeSharedLibPaths (allocator );
554
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
604
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
+ }
559
610
}
560
611
}
561
612
}
562
613
563
614
// If Zig is statically linked, such as via distributed binary static builds, the above
564
615
// trick won't work. What are we left with? Try to run the system C compiler and get
565
616
// 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 );
577
634
}
578
635
579
636
const Search = struct {
0 commit comments