@@ -237,7 +237,7 @@ pub fn detect(allocator: Allocator, cross_target: CrossTarget) DetectError!Nativ
237237
238238/// First we attempt to use the executable's own binary. If it is dynamically
239239/// linked, then it should answer both the C ABI question and the dynamic linker question.
240- /// If it is statically linked, then we try /usr/bin/env. If that does not provide the answer, then
240+ /// If it is statically linked, then we try /usr/bin/env (or the file it references in shebang) . If that does not provide the answer, then
241241/// we fall back to the defaults.
242242/// TODO Remove the Allocator requirement from this function.
243243fn detectAbiAndDynamicLinker (
@@ -355,37 +355,77 @@ fn detectAbiAndDynamicLinker(
355355 return result ;
356356 }
357357
358- const env_file = std .fs .openFileAbsoluteZ ("/usr/bin/env" , .{}) catch | err | switch (err ) {
359- error .NoSpaceLeft = > unreachable ,
360- error .NameTooLong = > unreachable ,
361- error .PathAlreadyExists = > unreachable ,
362- error .SharingViolation = > unreachable ,
363- error .InvalidUtf8 = > unreachable ,
364- error .BadPathName = > unreachable ,
365- error .PipeBusy = > unreachable ,
366- error .FileLocksNotSupported = > unreachable ,
367- error .WouldBlock = > unreachable ,
368- error .FileBusy = > unreachable , // opened without write permissions
369-
370- error .IsDir ,
371- error .NotDir ,
372- error .InvalidHandle ,
373- error .AccessDenied ,
374- error .NoDevice ,
375- error .FileNotFound ,
376- error .FileTooBig ,
377- error .Unexpected ,
378- = > return defaultAbiAndDynamicLinker (cpu , os , cross_target ),
358+ const elf_file = blk : {
359+ // This block looks for a shebang line in /usr/bin/env,
360+ // if it finds one, then instead of using /usr/bin/env as the ELF file to examine, it uses the file it references instead,
361+ // doing the same logic recursively in case it finds another shebang line.
362+
363+ // Since /usr/bin/env is hard-coded into the shebang line of many portable scripts, it's a
364+ // reasonably reliable path to start with.
365+ var file_name : []const u8 = "/usr/bin/env" ;
366+ // #! (2) + 255 (max length of shebang line since Linux 5.1) + \n (1)
367+ var buffer : [258 ]u8 = undefined ;
368+ while (true ) {
369+ const file = std .fs .openFileAbsolute (file_name , .{}) catch | err | switch (err ) {
370+ error .NoSpaceLeft = > unreachable ,
371+ error .NameTooLong = > unreachable ,
372+ error .PathAlreadyExists = > unreachable ,
373+ error .SharingViolation = > unreachable ,
374+ error .InvalidUtf8 = > unreachable ,
375+ error .BadPathName = > unreachable ,
376+ error .PipeBusy = > unreachable ,
377+ error .FileLocksNotSupported = > unreachable ,
378+ error .WouldBlock = > unreachable ,
379+ error .FileBusy = > unreachable , // opened without write permissions
380+
381+ error .IsDir ,
382+ error .NotDir ,
383+ error .InvalidHandle ,
384+ error .AccessDenied ,
385+ error .NoDevice ,
386+ error .FileNotFound ,
387+ error .FileTooBig ,
388+ error .Unexpected ,
389+ = > | e | {
390+ std .log .warn ("Encoutered error: {s}, falling back to default ABI and dynamic linker.\n " , .{@errorName (e )});
391+ return defaultAbiAndDynamicLinker (cpu , os , cross_target );
392+ },
379393
380- else = > | e | return e ,
394+ else = > | e | return e ,
395+ };
396+
397+ const line = file .reader ().readUntilDelimiter (& buffer , '\n ' ) catch | err | switch (err ) {
398+ error .IsDir = > unreachable , // Handled before
399+ error .AccessDenied = > unreachable ,
400+ error .WouldBlock = > unreachable , // Did not request blocking mode
401+ error .OperationAborted = > unreachable , // Windows-only
402+ error .BrokenPipe = > unreachable ,
403+ error .ConnectionResetByPeer = > unreachable ,
404+ error .ConnectionTimedOut = > unreachable ,
405+ error .InputOutput = > unreachable ,
406+ error .Unexpected = > unreachable ,
407+
408+ error .StreamTooLong ,
409+ error .EndOfStream ,
410+ error .NotOpenForReading ,
411+ = > break :blk file ,
412+
413+ else = > | e | {
414+ file .close ();
415+ return e ;
416+ },
417+ };
418+ if (! mem .startsWith (u8 , line , "#!" )) break :blk file ;
419+ var it = std .mem .tokenize (u8 , line [2.. ], " " );
420+ file .close ();
421+ file_name = it .next () orelse return defaultAbiAndDynamicLinker (cpu , os , cross_target );
422+ }
381423 };
382- defer env_file .close ();
424+ defer elf_file .close ();
383425
384426 // If Zig is statically linked, such as via distributed binary static builds, the above
385- // trick won't work. The next thing we fall back to is the same thing, but for /usr/bin/env.
386- // Since that path is hard-coded into the shebang line of many portable scripts, it's a
387- // reasonably reliable path to check for.
388- return abiAndDynamicLinkerFromFile (env_file , cpu , os , ld_info_list , cross_target ) catch | err | switch (err ) {
427+ // trick (block self_exe) won't work. The next thing we fall back to is the same thing, but for elf_file.
428+ return abiAndDynamicLinkerFromFile (elf_file , cpu , os , ld_info_list , cross_target ) catch | err | switch (err ) {
389429 error .FileSystem ,
390430 error .SystemResources ,
391431 error .SymLinkLoop ,
@@ -403,7 +443,10 @@ fn detectAbiAndDynamicLinker(
403443 error .UnexpectedEndOfFile ,
404444 error .NameTooLong ,
405445 // Finally, we fall back on the standard path.
406- = > defaultAbiAndDynamicLinker (cpu , os , cross_target ),
446+ = > | e | {
447+ std .log .warn ("Encoutered error: {s}, falling back to default ABI and dynamic linker.\n " , .{@errorName (e )});
448+ return defaultAbiAndDynamicLinker (cpu , os , cross_target );
449+ },
407450 };
408451}
409452
0 commit comments