@@ -908,6 +908,44 @@ fn glibcVerFromSoFile(file: fs.File) !std.SemanticVersion {
908
908
return max_ver ;
909
909
}
910
910
911
+ /// Recursively chases the shebang line until it finds what is likely the desired ELF.
912
+ fn followElfShebangs (origin_file_name : []const u8 ) ? fs.File {
913
+ var file_name = origin_file_name ;
914
+
915
+ // #! (2) + 255 (max length of shebang line since Linux 5.1) + \n (1)
916
+ var buffer : [258 ]u8 = undefined ;
917
+ while (true ) {
918
+ const file = fs .openFileAbsolute (file_name , .{}) catch | err | switch (err ) {
919
+ error .NoSpaceLeft = > unreachable ,
920
+ error .NameTooLong = > unreachable ,
921
+ error .PathAlreadyExists = > unreachable ,
922
+ error .SharingViolation = > unreachable ,
923
+ error .InvalidUtf8 = > unreachable ,
924
+ error .BadPathName = > unreachable ,
925
+ error .PipeBusy = > unreachable ,
926
+ error .FileLocksNotSupported = > unreachable ,
927
+ error .WouldBlock = > unreachable ,
928
+ error .FileBusy = > unreachable , // opened without write permissions
929
+ error .AntivirusInterference = > unreachable , // Windows-only error
930
+ else = > return null ,
931
+ };
932
+ errdefer file .close ();
933
+
934
+ const len = preadMin (file , & buffer , 0 , buffer .len ) catch | err | switch (err ) {
935
+ error .UnexpectedEndOfFile ,
936
+ error .UnableToReadElfFile ,
937
+ = > return file ,
938
+ else = > return null ,
939
+ };
940
+ const newline = mem .indexOfScalar (u8 , buffer [0.. len ], '\n ' ) orelse return file ;
941
+ const line = buffer [0.. newline ];
942
+ if (! mem .startsWith (u8 , line , "#!" )) return file ;
943
+ var it = mem .tokenizeScalar (u8 , line [2.. ], ' ' );
944
+ file_name = it .next () orelse return null ;
945
+ file .close ();
946
+ }
947
+ }
948
+
911
949
/// In the past, this function attempted to use the executable's own binary if it was dynamically
912
950
/// linked to answer both the C ABI question and the dynamic linker question. However, this
913
951
/// could be problematic on a system that uses a RUNPATH for the compiler binary, locking
@@ -917,10 +955,9 @@ fn glibcVerFromSoFile(file: fs.File) !std.SemanticVersion {
917
955
/// mismatching will fail to run.
918
956
///
919
957
/// Therefore, this function works the same regardless of whether the compiler binary is
920
- /// dynamically or statically linked. It inspects `/usr/bin/env` as an ELF file to find the
921
- /// answer to these questions, or if there is a shebang line, then it chases the referenced
922
- /// file recursively. If that does not provide the answer, then the function falls back to
923
- /// defaults.
958
+ /// dynamically or statically linked. It first checks `/usr/bin/env` as an ELF file to find
959
+ /// the answer to these questions, or iterates PATH for another `env` if it doesn't exist at
960
+ /// the usual path.
924
961
fn detectAbiAndDynamicLinker (
925
962
cpu : Target.Cpu ,
926
963
os : Target.Os ,
@@ -983,61 +1020,24 @@ fn detectAbiAndDynamicLinker(
983
1020
// Best case scenario: the executable is dynamically linked, and we can iterate
984
1021
// over our own shared objects and find a dynamic linker.
985
1022
const elf_file = blk : {
986
- // This block looks for a shebang line in /usr/bin/env,
987
- // if it finds one, then instead of using /usr/bin/env as the ELF file to examine, it uses the file it references instead,
988
- // doing the same logic recursively in case it finds another shebang line.
989
-
990
- // Since /usr/bin/env is hard-coded into the shebang line of many portable scripts, it's a
991
- // reasonably reliable path to start with.
992
- var file_name : []const u8 = "/usr/bin/env" ;
993
- // #! (2) + 255 (max length of shebang line since Linux 5.1) + \n (1)
994
- var buffer : [258 ]u8 = undefined ;
995
- while (true ) {
996
- const file = fs .openFileAbsolute (file_name , .{}) catch | err | switch (err ) {
997
- error .NoSpaceLeft = > unreachable ,
998
- error .NameTooLong = > unreachable ,
999
- error .PathAlreadyExists = > unreachable ,
1000
- error .SharingViolation = > unreachable ,
1001
- error .InvalidUtf8 = > unreachable ,
1002
- error .BadPathName = > unreachable ,
1003
- error .PipeBusy = > unreachable ,
1004
- error .FileLocksNotSupported = > unreachable ,
1005
- error .WouldBlock = > unreachable ,
1006
- error .FileBusy = > unreachable , // opened without write permissions
1007
- error .AntivirusInterference = > unreachable , // Windows-only error
1008
-
1009
- error .IsDir ,
1010
- error .NotDir ,
1011
- error .InvalidHandle ,
1012
- error .AccessDenied ,
1013
- error .NoDevice ,
1014
- error .FileNotFound ,
1015
- error .NetworkNotFound ,
1016
- error .FileTooBig ,
1017
- error .Unexpected ,
1018
- = > | e | {
1019
- std .log .warn ("Encountered error: {s}, falling back to default ABI and dynamic linker.\n " , .{@errorName (e )});
1020
- return defaultAbiAndDynamicLinker (cpu , os , query );
1021
- },
1022
-
1023
- else = > | e | return e ,
1024
- };
1025
- errdefer file .close ();
1026
-
1027
- const len = preadMin (file , & buffer , 0 , buffer .len ) catch | err | switch (err ) {
1028
- error .UnexpectedEndOfFile ,
1029
- error .UnableToReadElfFile ,
1030
- = > break :blk file ,
1031
-
1032
- else = > | e | return e ,
1033
- };
1034
- const newline = mem .indexOfScalar (u8 , buffer [0.. len ], '\n ' ) orelse break :blk file ;
1035
- const line = buffer [0.. newline ];
1036
- if (! mem .startsWith (u8 , line , "#!" )) break :blk file ;
1037
- var it = mem .tokenizeScalar (u8 , line [2.. ], ' ' );
1038
- file_name = it .next () orelse return defaultAbiAndDynamicLinker (cpu , os , query );
1039
- file .close ();
1023
+ if (followElfShebangs ("/usr/bin/env" )) | file | {
1024
+ break :blk file ;
1040
1025
}
1026
+ // If /usr/bin/env is missing, iterate PATH to find another "env" binary.
1027
+ const PATH = std .os .getenv ("PATH" ) orelse {
1028
+ std .log .warn ("Could not find /usr/bin/env and PATH environment variable is missing, falling back to default ABI and dynamic linker.\n " , .{});
1029
+ return defaultAbiAndDynamicLinker (cpu , os , query );
1030
+ };
1031
+ var path_it = mem .tokenizeScalar (u8 , PATH , ':' );
1032
+ var path_buf : [std .os .PATH_MAX ]u8 = undefined ;
1033
+ while (path_it .next ()) | path | {
1034
+ var path_alloc = std .heap .FixedBufferAllocator .init (& path_buf );
1035
+ const path_env = std .fs .path .join (path_alloc .allocator (), &.{ path , "env" }) catch continue ;
1036
+ const env_file = followElfShebangs (path_env ) orelse continue ;
1037
+ break :blk env_file ;
1038
+ }
1039
+ std .log .warn ("Could not find env in PATH, falling back to default ABI and dynamic linker.\n " , .{});
1040
+ return defaultAbiAndDynamicLinker (cpu , os , query );
1041
1041
};
1042
1042
defer elf_file .close ();
1043
1043
@@ -1064,7 +1064,7 @@ fn detectAbiAndDynamicLinker(
1064
1064
error .NameTooLong ,
1065
1065
// Finally, we fall back on the standard path.
1066
1066
= > | e | {
1067
- std .log .warn ("Encountered error: {s}, falling back to default ABI and dynamic linker.\n " , .{@errorName (e )});
1067
+ std .log .warn ("Encountered error: {s} whilst detecting dynamic linker in env binary , falling back to default ABI and dynamic linker.\n " , .{@errorName (e )});
1068
1068
return defaultAbiAndDynamicLinker (cpu , os , query );
1069
1069
},
1070
1070
};
0 commit comments