Commit 48cef8d5be

Andrew Kelley <andrew@ziglang.org>
2020-03-01 03:31:03
refactor std.zig.system.NativeTargetInfo functions
to make them a bit easier to test.
1 parent c903b76
Changed files (1)
lib
std
lib/std/zig/system.zig
@@ -399,17 +399,38 @@ pub const NativeTargetInfo = struct {
             return result;
         }
 
+        const env_file = std.fs.openFileAbsoluteC("/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.IsDir,
+            error.NotDir,
+            error.AccessDenied,
+            error.NoDevice,
+            error.FileNotFound,
+            error.FileTooBig,
+            error.Unexpected,
+            => return defaultAbiAndDynamicLinker(cpu, os, cross_target),
+
+            else => |e| return e,
+        };
+        defer env_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 abiAndDynamicLinkerFromUsrBinEnv(cpu, os, ld_info_list, cross_target) catch |err| switch (err) {
+        return abiAndDynamicLinkerFromFile(env_file, cpu, os, ld_info_list, cross_target) catch |err| switch (err) {
             error.FileSystem,
             error.SystemResources,
             error.SymLinkLoop,
             error.ProcessFdQuotaExceeded,
             error.SystemFdQuotaExceeded,
-            error.DeviceBusy,
             => |e| return e,
 
             error.UnableToReadElfFile,
@@ -418,7 +439,6 @@ pub const NativeTargetInfo = struct {
             error.InvalidElfEndian,
             error.InvalidElfFile,
             error.InvalidElfMagic,
-            error.UsrBinEnvNotAvailable,
             error.Unexpected,
             error.UnexpectedEndOfFile,
             error.NameTooLong,
@@ -461,32 +481,15 @@ pub const NativeTargetInfo = struct {
         };
     }
 
-    fn abiAndDynamicLinkerFromUsrBinEnv(
+    pub fn abiAndDynamicLinkerFromFile(
+        file: fs.File,
         cpu: Target.Cpu,
         os: Target.Os,
         ld_info_list: []const LdInfo,
         cross_target: CrossTarget,
     ) !NativeTargetInfo {
-        const env_file = std.fs.openFileAbsoluteC("/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.IsDir => return error.UsrBinEnvNotAvailable,
-            error.NotDir => return error.UsrBinEnvNotAvailable,
-            error.AccessDenied => return error.UsrBinEnvNotAvailable,
-            error.NoDevice => return error.UsrBinEnvNotAvailable,
-            error.FileNotFound => return error.UsrBinEnvNotAvailable,
-            error.FileTooBig => return error.UsrBinEnvNotAvailable,
-
-            else => |e| return e,
-        };
         var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 align(@alignOf(elf.Elf64_Ehdr)) = undefined;
-        _ = try preadFull(env_file, &hdr_buf, 0, hdr_buf.len);
+        _ = try preadFull(file, &hdr_buf, 0, hdr_buf.len);
         const hdr32 = @ptrCast(*elf.Elf32_Ehdr, &hdr_buf);
         const hdr64 = @ptrCast(*elf.Elf64_Ehdr, &hdr_buf);
         if (!mem.eql(u8, hdr32.e_ident[0..4], "\x7fELF")) return error.InvalidElfMagic;
@@ -526,7 +529,7 @@ pub const NativeTargetInfo = struct {
             // Reserve some bytes so that we can deref the 64-bit struct fields
             // even when the ELF file is 32-bits.
             const ph_reserve: usize = @sizeOf(elf.Elf64_Phdr) - @sizeOf(elf.Elf32_Phdr);
-            const ph_read_byte_len = try preadFull(env_file, ph_buf[0 .. ph_buf.len - ph_reserve], phoff, phentsize);
+            const ph_read_byte_len = try preadFull(file, ph_buf[0 .. ph_buf.len - ph_reserve], phoff, phentsize);
             var ph_buf_i: usize = 0;
             while (ph_buf_i < ph_read_byte_len and ph_i < phnum) : ({
                 ph_i += 1;
@@ -541,7 +544,7 @@ pub const NativeTargetInfo = struct {
                         const p_offset = elfInt(is_64, need_bswap, ph32.p_offset, ph64.p_offset);
                         const p_filesz = elfInt(is_64, need_bswap, ph32.p_filesz, ph64.p_filesz);
                         if (p_filesz > result.dynamic_linker.buffer.len) return error.NameTooLong;
-                        _ = try preadFull(env_file, result.dynamic_linker.buffer[0..p_filesz], p_offset, p_filesz);
+                        _ = try preadFull(file, result.dynamic_linker.buffer[0..p_filesz], p_offset, p_filesz);
                         // PT_INTERP includes a null byte in p_filesz.
                         const len = p_filesz - 1;
                         // dynamic_linker.max_byte is "max", not "len".
@@ -573,7 +576,7 @@ pub const NativeTargetInfo = struct {
                             // even when the ELF file is 32-bits.
                             const dyn_reserve: usize = @sizeOf(elf.Elf64_Dyn) - @sizeOf(elf.Elf32_Dyn);
                             const dyn_read_byte_len = try preadFull(
-                                env_file,
+                                file,
                                 dyn_buf[0 .. dyn_buf.len - dyn_reserve],
                                 dyn_off,
                                 dyn_size,
@@ -617,14 +620,14 @@ pub const NativeTargetInfo = struct {
                 var sh_buf: [16 * @sizeOf(elf.Elf64_Shdr)]u8 align(@alignOf(elf.Elf64_Shdr)) = undefined;
                 if (sh_buf.len < shentsize) return error.InvalidElfFile;
 
-                _ = try preadFull(env_file, &sh_buf, str_section_off, shentsize);
+                _ = try preadFull(file, &sh_buf, str_section_off, shentsize);
                 const shstr32 = @ptrCast(*elf.Elf32_Shdr, @alignCast(@alignOf(elf.Elf32_Shdr), &sh_buf));
                 const shstr64 = @ptrCast(*elf.Elf64_Shdr, @alignCast(@alignOf(elf.Elf64_Shdr), &sh_buf));
                 const shstrtab_off = elfInt(is_64, need_bswap, shstr32.sh_offset, shstr64.sh_offset);
                 const shstrtab_size = elfInt(is_64, need_bswap, shstr32.sh_size, shstr64.sh_size);
                 var strtab_buf: [4096:0]u8 = undefined;
                 const shstrtab_len = std.math.min(shstrtab_size, strtab_buf.len);
-                const shstrtab_read_len = try preadFull(env_file, &strtab_buf, shstrtab_off, shstrtab_len);
+                const shstrtab_read_len = try preadFull(file, &strtab_buf, shstrtab_off, shstrtab_len);
                 const shstrtab = strtab_buf[0..shstrtab_read_len];
 
                 const shnum = elfInt(is_64, need_bswap, hdr32.e_shnum, hdr64.e_shnum);
@@ -634,7 +637,7 @@ pub const NativeTargetInfo = struct {
                     // even when the ELF file is 32-bits.
                     const sh_reserve: usize = @sizeOf(elf.Elf64_Shdr) - @sizeOf(elf.Elf32_Shdr);
                     const sh_read_byte_len = try preadFull(
-                        env_file,
+                        file,
                         sh_buf[0 .. sh_buf.len - sh_reserve],
                         shoff,
                         shentsize,
@@ -667,7 +670,7 @@ pub const NativeTargetInfo = struct {
 
                 if (dynstr) |ds| {
                     const strtab_len = std.math.min(ds.size, strtab_buf.len);
-                    const strtab_read_len = try preadFull(env_file, &strtab_buf, ds.offset, shstrtab_len);
+                    const strtab_read_len = try preadFull(file, &strtab_buf, ds.offset, shstrtab_len);
                     const strtab = strtab_buf[0..strtab_read_len];
                     // TODO this pointer cast should not be necessary
                     const rpath_list = mem.toSliceConst(u8, @ptrCast([*:0]u8, strtab[rpoff..].ptr));
@@ -763,7 +766,7 @@ pub const NativeTargetInfo = struct {
         };
     }
 
-    const LdInfo = struct {
+    pub const LdInfo = struct {
         ld: DynamicLinker,
         abi: Target.Abi,
     };