@@ -237,7 +237,7 @@ pub fn detect(allocator: Allocator, cross_target: CrossTarget) DetectError!Nativ
237
237
238
238
/// First we attempt to use the executable's own binary. If it is dynamically
239
239
/// 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
241
241
/// we fall back to the defaults.
242
242
/// TODO Remove the Allocator requirement from this function.
243
243
fn detectAbiAndDynamicLinker (
@@ -355,37 +355,77 @@ fn detectAbiAndDynamicLinker(
355
355
return result ;
356
356
}
357
357
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
+ },
379
393
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
+ }
381
423
};
382
- defer env_file .close ();
424
+ defer elf_file .close ();
383
425
384
426
// 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 ) {
389
429
error .FileSystem ,
390
430
error .SystemResources ,
391
431
error .SymLinkLoop ,
@@ -403,7 +443,10 @@ fn detectAbiAndDynamicLinker(
403
443
error .UnexpectedEndOfFile ,
404
444
error .NameTooLong ,
405
445
// 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
+ },
407
450
};
408
451
}
409
452
0 commit comments