Skip to content

Commit b97ae88

Browse files
BratishkaErikVexu
authored andcommitted
std.zig.system.NativeTargetInfo: look for a shebang line in /usr/bin/env, if any
1 parent 4055e60 commit b97ae88

File tree

1 file changed

+72
-29
lines changed

1 file changed

+72
-29
lines changed

lib/std/zig/system/NativeTargetInfo.zig

Lines changed: 72 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
243243
fn 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

Comments
 (0)