Commit b97ae88898

Eric Joldasov <bratishkaerik@getgoogleoff.me>
2022-08-11 22:34:53
std.zig.system.NativeTargetInfo: look for a shebang line in /usr/bin/env, if any
1 parent 4055e60
Changed files (1)
lib
std
lib/std/zig/system/NativeTargetInfo.zig
@@ -237,7 +237,7 @@ pub fn detect(allocator: Allocator, cross_target: CrossTarget) DetectError!Nativ
 
 /// First we attempt to use the executable's own binary. If it is dynamically
 /// linked, then it should answer both the C ABI question and the dynamic linker question.
-/// If it is statically linked, then we try /usr/bin/env. If that does not provide the answer, then
+/// 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
 /// we fall back to the defaults.
 /// TODO Remove the Allocator requirement from this function.
 fn detectAbiAndDynamicLinker(
@@ -355,37 +355,77 @@ fn detectAbiAndDynamicLinker(
         return result;
     }
 
-    const env_file = std.fs.openFileAbsoluteZ("/usr/bin/env", .{}) catch |err| switch (err) {
-        error.NoSpaceLeft => unreachable,
-        error.NameTooLong => unreachable,
-        error.PathAlreadyExists => unreachable,
-        error.SharingViolation => unreachable,
-        error.InvalidUtf8 => unreachable,
-        error.BadPathName => unreachable,
-        error.PipeBusy => unreachable,
-        error.FileLocksNotSupported => unreachable,
-        error.WouldBlock => unreachable,
-        error.FileBusy => unreachable, // opened without write permissions
-
-        error.IsDir,
-        error.NotDir,
-        error.InvalidHandle,
-        error.AccessDenied,
-        error.NoDevice,
-        error.FileNotFound,
-        error.FileTooBig,
-        error.Unexpected,
-        => return defaultAbiAndDynamicLinker(cpu, os, cross_target),
+    const elf_file = blk: {
+        // This block looks for a shebang line in /usr/bin/env,
+        // if it finds one, then instead of using /usr/bin/env as the ELF file to examine, it uses the file it references instead,
+        // doing the same logic recursively in case it finds another shebang line.
+
+        // Since /usr/bin/env is hard-coded into the shebang line of many portable scripts, it's a
+        // reasonably reliable path to start with.
+        var file_name: []const u8 = "/usr/bin/env";
+        // #! (2) + 255 (max length of shebang line since Linux 5.1) + \n (1)
+        var buffer: [258]u8 = undefined;
+        while (true) {
+            const file = std.fs.openFileAbsolute(file_name, .{}) catch |err| switch (err) {
+                error.NoSpaceLeft => unreachable,
+                error.NameTooLong => unreachable,
+                error.PathAlreadyExists => unreachable,
+                error.SharingViolation => unreachable,
+                error.InvalidUtf8 => unreachable,
+                error.BadPathName => unreachable,
+                error.PipeBusy => unreachable,
+                error.FileLocksNotSupported => unreachable,
+                error.WouldBlock => unreachable,
+                error.FileBusy => unreachable, // opened without write permissions
+
+                error.IsDir,
+                error.NotDir,
+                error.InvalidHandle,
+                error.AccessDenied,
+                error.NoDevice,
+                error.FileNotFound,
+                error.FileTooBig,
+                error.Unexpected,
+                => |e| {
+                    std.log.warn("Encoutered error: {s}, falling back to default ABI and dynamic linker.\n", .{@errorName(e)});
+                    return defaultAbiAndDynamicLinker(cpu, os, cross_target);
+                },
 
-        else => |e| return e,
+                else => |e| return e,
+            };
+
+            const line = file.reader().readUntilDelimiter(&buffer, '\n') catch |err| switch (err) {
+                error.IsDir => unreachable, // Handled before
+                error.AccessDenied => unreachable,
+                error.WouldBlock => unreachable, // Did not request blocking mode
+                error.OperationAborted => unreachable, // Windows-only
+                error.BrokenPipe => unreachable,
+                error.ConnectionResetByPeer => unreachable,
+                error.ConnectionTimedOut => unreachable,
+                error.InputOutput => unreachable,
+                error.Unexpected => unreachable,
+
+                error.StreamTooLong,
+                error.EndOfStream,
+                error.NotOpenForReading,
+                => break :blk file,
+
+                else => |e| {
+                    file.close();
+                    return e;
+                },
+            };
+            if (!mem.startsWith(u8, line, "#!")) break :blk file;
+            var it = std.mem.tokenize(u8, line[2..], " ");
+            file.close();
+            file_name = it.next() orelse return defaultAbiAndDynamicLinker(cpu, os, cross_target);
+        }
     };
-    defer env_file.close();
+    defer elf_file.close();
 
     // If Zig is statically linked, such as via distributed binary static builds, the above
-    // trick won't work. The next thing we fall back to is the same thing, but for /usr/bin/env.
-    // Since that path is hard-coded into the shebang line of many portable scripts, it's a
-    // reasonably reliable path to check for.
-    return abiAndDynamicLinkerFromFile(env_file, cpu, os, ld_info_list, cross_target) catch |err| switch (err) {
+    // trick (block self_exe) won't work. The next thing we fall back to is the same thing, but for elf_file.
+    return abiAndDynamicLinkerFromFile(elf_file, cpu, os, ld_info_list, cross_target) catch |err| switch (err) {
         error.FileSystem,
         error.SystemResources,
         error.SymLinkLoop,
@@ -403,7 +443,10 @@ fn detectAbiAndDynamicLinker(
         error.UnexpectedEndOfFile,
         error.NameTooLong,
         // Finally, we fall back on the standard path.
-        => defaultAbiAndDynamicLinker(cpu, os, cross_target),
+        => |e| {
+            std.log.warn("Encoutered error: {s}, falling back to default ABI and dynamic linker.\n", .{@errorName(e)});
+            return defaultAbiAndDynamicLinker(cpu, os, cross_target);
+        },
     };
 }