Commit 60f2f3457d

Andrew Kelley <andrew@ziglang.org>
2020-02-27 22:38:10
getStandardDynamicLinkerPath renamed and no allocator
* `std.Target.getStandardDynamicLinkerPath` => `std.Target.standardDynamicLinkerPath` * it now takes a pointer to fixed size array rather than an allocator * `std.zig.system.NativeTargetInfo.detect` now supports reading PT_INTERP from /usr/bin/env
1 parent 662b5f7
Changed files (3)
lib
src-self-hosted
lib/std/zig/system.zig
@@ -167,7 +167,15 @@ pub const NativePaths = struct {
 
 pub const NativeTargetInfo = struct {
     target: Target,
-    dynamic_linker: ?[:0]u8,
+
+    /// Contains the memory used to store the dynamic linker path. This field should
+    /// not be used directly. See `dynamicLinker` and `setDynamicLinker`. This field
+    /// exists so that this API requires no allocator.
+    dynamic_linker_buffer: [255]u8 = undefined,
+
+    /// Used to construct the dynamic linker path. This field should not be used
+    /// directly. See `dynamicLinker` and `setDynamicLinker`.
+    dynamic_linker_max: ?u8 = null,
 
     pub const DetectError = error{
         OutOfMemory,
@@ -181,6 +189,7 @@ pub const NativeTargetInfo = struct {
 
     /// Detects the native CPU model & features, operating system & version, and C ABI & dynamic linker.
     /// On Linux, this is additionally responsible for detecting the native glibc version when applicable.
+    /// TODO Remove the allocator requirement from this.
     pub fn detect(allocator: *Allocator) DetectError!NativeTargetInfo {
         const arch = Target.current.cpu.arch;
         const os_tag = Target.current.os.tag;
@@ -188,8 +197,7 @@ pub const NativeTargetInfo = struct {
         // TODO Detect native CPU model & features. Until that is implemented we hard code baseline.
         const cpu = Target.Cpu.baseline(arch);
 
-        // TODO Detect native operating system version. Until that is implemented we use the minimum version
-        // of the default range.
+        // TODO Detect native operating system version. Until that is implemented we use the default range.
         const os = Target.Os.defaultVersionRange(os_tag);
 
         return detectAbiAndDynamicLinker(allocator, cpu, os);
@@ -201,6 +209,21 @@ pub const NativeTargetInfo = struct {
         self.* = undefined;
     }
 
+    /// The returned memory has the same lifetime as the `NativeTargetInfo`.
+    pub fn dynamicLinker(self: *const NativeTargetInfo) ?[]const u8 {
+        const m = self.dynamic_linker_max orelse return null;
+        return self.dynamic_linker_buffer[0 .. m + 1];
+    }
+
+    pub fn setDynamicLinker(self: *NativeTargetInfo, dl_or_null: ?[]const u8) void {
+        if (dl_or_null) |dl| {
+            mem.copy(u8, &self.dynamic_linker_buffer, dl);
+            self.dynamic_linker_max = @intCast(u8, dl.len - 1);
+        } else {
+            self.dynamic_linker_max = null;
+        }
+    }
+
     /// 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
@@ -245,10 +268,11 @@ pub const NativeTargetInfo = struct {
                 .os = os,
                 .abi = abi,
             };
-            const standard_ld_path = target.getStandardDynamicLinkerPath(allocator) catch |err| switch (err) {
-                error.OutOfMemory => return error.OutOfMemory,
-                error.UnknownDynamicLinkerPath, error.TargetHasNoDynamicLinker => continue,
-            };
+            var buf: [255]u8 = undefined;
+            const standard_ld_path = if (target.standardDynamicLinkerPath(&buf)) |s|
+                try mem.dupe(allocator, u8, s)
+            else
+                continue;
             errdefer allocator.free(standard_ld_path);
             try ld_info_list.append(.{
                 .ld_path = standard_ld_path,
@@ -294,28 +318,29 @@ pub const NativeTargetInfo = struct {
                 }
             }
 
-            return NativeTargetInfo{
+            var result: NativeTargetInfo = .{
                 .target = .{
                     .cpu = cpu,
                     .os = os_adjusted,
                     .abi = found_ld_info.abi,
                 },
-                .dynamic_linker = try mem.dupeZ(allocator, u8, found_ld_path),
             };
+            result.setDynamicLinker(found_ld_path);
+            return result;
         }
 
         // 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(allocator, cpu, os) catch |err| switch (err) {
-            error.OutOfMemory => return error.OutOfMemory,
-            error.FileSystem => return error.FileSystem,
-            error.SystemResources => return error.SystemResources,
-            error.SymLinkLoop => return error.SymLinkLoop,
-            error.ProcessFdQuotaExceeded => return error.ProcessFdQuotaExceeded,
-            error.SystemFdQuotaExceeded => return error.SystemFdQuotaExceeded,
-            error.DeviceBusy => return error.DeviceBusy,
+        return abiAndDynamicLinkerFromUsrBinEnv(cpu, os) catch |err| switch (err) {
+            error.FileSystem,
+            error.SystemResources,
+            error.SymLinkLoop,
+            error.ProcessFdQuotaExceeded,
+            error.SystemFdQuotaExceeded,
+            error.DeviceBusy,
+            => |e| return e,
 
             error.UnableToReadElfFile,
             error.ElfNotADynamicExecutable,
@@ -327,6 +352,8 @@ pub const NativeTargetInfo = struct {
             error.InvalidElfMagic,
             error.UsrBinEnvNotAvailable,
             error.Unexpected,
+            error.UnexpectedEndOfFile,
+            error.NameTooLong,
             // Finally, we fall back on the standard path.
             => defaultAbiAndDynamicLinker(allocator, cpu, os),
         };
@@ -362,11 +389,7 @@ pub const NativeTargetInfo = struct {
         };
     }
 
-    fn abiAndDynamicLinkerFromUsrBinEnv(
-        allocator: *Allocator,
-        cpu: Target.Cpu,
-        os: Target.Os,
-    ) !NativeTargetInfo {
+    fn abiAndDynamicLinkerFromUsrBinEnv(cpu: Target.Cpu, os: Target.Os) !NativeTargetInfo {
         const env_file = std.fs.openFileAbsoluteC("/usr/bin/env", .{}) catch |err| switch (err) {
             error.NoSpaceLeft => unreachable,
             error.NameTooLong => unreachable,
@@ -409,6 +432,14 @@ pub const NativeTargetInfo = struct {
         const phnum = elfInt(is_64, need_bswap, hdr32.e_phnum, hdr64.e_phnum);
         const shstrndx = elfInt(is_64, need_bswap, hdr32.e_shstrndx, hdr64.e_shstrndx);
 
+        var result: NativeTargetInfo = .{
+            .target = .{
+                .cpu = cpu,
+                .os = os,
+                .abi = Target.Abi.default(cpu.arch, os),
+            },
+        };
+
         const ph_total_size = std.math.mul(u32, phentsize, phnum) catch |err| switch (err) {
             error.Overflow => return error.InvalidElfProgramHeaders,
         };
@@ -430,7 +461,22 @@ pub const NativeTargetInfo = struct {
                 const p_type = elfInt(is_64, need_bswap, ph32.p_type, ph64.p_type);
                 switch (p_type) {
                     elf.PT_INTERP => {
-                        std.debug.warn("found PT_INTERP\n", .{});
+                        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);
+                        var interp_buf: [255]u8 = undefined;
+                        if (p_filesz > interp_buf.len) return error.NameTooLong;
+                        var read_offset: usize = 0;
+                        while (true) {
+                            const len = try wrapRead(env_file.pread(
+                                interp_buf[read_offset .. p_filesz - read_offset],
+                                p_offset + read_offset,
+                            ));
+                            if (len == 0) return error.UnexpectedEndOfFile;
+                            read_offset += len;
+                            if (read_offset == p_filesz) break;
+                        }
+                        // PT_INTERP includes a null byte in p_filesz.
+                        result.setDynamicLinker(interp_buf[0 .. p_filesz - 1]);
                     },
                     elf.PT_DYNAMIC => {
                         std.debug.warn("found PT_DYNAMIC\n", .{});
@@ -440,7 +486,7 @@ pub const NativeTargetInfo = struct {
             }
         }
 
-        return error.OutOfMemory; // TODO
+        return result;
     }
 
     fn wrapRead(res: std.os.ReadError!usize) !usize {
@@ -457,18 +503,17 @@ pub const NativeTargetInfo = struct {
     }
 
     fn defaultAbiAndDynamicLinker(allocator: *Allocator, cpu: Target.Cpu, os: Target.Os) !NativeTargetInfo {
-        const target: Target = .{
-            .cpu = cpu,
-            .os = os,
-            .abi = Target.Abi.default(cpu.arch, os),
-        };
-        return @as(NativeTargetInfo, .{
-            .target = target,
-            .dynamic_linker = target.getStandardDynamicLinkerPath(allocator) catch |err| switch (err) {
-                error.OutOfMemory => return error.OutOfMemory,
-                error.UnknownDynamicLinkerPath, error.TargetHasNoDynamicLinker => null,
+        var result: NativeTargetInfo = .{
+            .target = .{
+                .cpu = cpu,
+                .os = os,
+                .abi = Target.Abi.default(cpu.arch, os),
             },
-        });
+        };
+        if (result.target.standardDynamicLinkerPath(&result.dynamic_linker_buffer)) |s| {
+            result.dynamic_linker_max = @intCast(u8, s.len - 1);
+        }
+        return result;
     }
 };
 
lib/std/target.zig
@@ -1084,65 +1084,59 @@ pub const Target = struct {
         }
     }
 
-    /// Caller owns returned memory.
-    pub fn getStandardDynamicLinkerPath(
-        self: Target,
-        allocator: *mem.Allocator,
-    ) error{
-        OutOfMemory,
-        UnknownDynamicLinkerPath,
-        TargetHasNoDynamicLinker,
-    }![:0]u8 {
-        const a = allocator;
+    /// The result will be a slice of `buffer`, pointing at position 0.
+    /// A return value of `null` means the concept of a dynamic linker is not meaningful for that target.
+    pub fn standardDynamicLinkerPath(self: Target, buffer: *[255]u8) ?[]u8 {
+        const S = struct {
+            fn print(b: *[255]u8, comptime fmt: []const u8, args: var) []u8 {
+                return std.fmt.bufPrint(b, fmt, args) catch unreachable;
+            }
+            fn copy(b: *[255]u8, s: []const u8) []u8 {
+                mem.copy(u8, b, s);
+                return b[0..s.len];
+            }
+        };
+        const print = S.print;
+        const copy = S.copy;
+
         if (self.isAndroid()) {
-            return mem.dupeZ(a, u8, if (self.cpu.arch.ptrBitWidth() == 64)
-                "/system/bin/linker64"
-            else
-                "/system/bin/linker");
+            const suffix = if (self.cpu.arch.ptrBitWidth() == 64) "64" else "";
+            return print(buffer, "/system/bin/linker{}", .{suffix});
         }
 
         if (self.isMusl()) {
-            var result = try std.Buffer.init(allocator, "/lib/ld-musl-");
-            defer result.deinit();
-
-            var is_arm = false;
-            switch (self.cpu.arch) {
-                .arm, .thumb => {
-                    try result.append("arm");
-                    is_arm = true;
-                },
-                .armeb, .thumbeb => {
-                    try result.append("armeb");
-                    is_arm = true;
-                },
-                else => |arch| try result.append(@tagName(arch)),
-            }
-            if (is_arm and self.getFloatAbi() == .hard) {
-                try result.append("hf");
-            }
-            try result.append(".so.1");
-            return result.toOwnedSlice();
+            const is_arm = switch (self.cpu.arch) {
+                .arm, .armeb, .thumb, .thumbeb => true,
+                else => false,
+            };
+            const arch_part = switch (self.cpu.arch) {
+                .arm, .thumb => "arm",
+                .armeb, .thumbeb => "armeb",
+                else => |arch| @tagName(arch),
+            };
+            const arch_suffix = if (is_arm and self.getFloatAbi() == .hard) "hf" else "";
+            return print(buffer, "/lib/ld-musl-{}{}.so.1", .{ arch_part, arch_suffix });
         }
 
         switch (self.os.tag) {
-            .freebsd => return mem.dupeZ(a, u8, "/libexec/ld-elf.so.1"),
-            .netbsd => return mem.dupeZ(a, u8, "/libexec/ld.elf_so"),
-            .dragonfly => return mem.dupeZ(a, u8, "/libexec/ld-elf.so.2"),
+            .freebsd => return copy(buffer, "/libexec/ld-elf.so.1"),
+            .netbsd => return copy(buffer, "/libexec/ld.elf_so"),
+            .dragonfly => return copy(buffer, "/libexec/ld-elf.so.2"),
             .linux => switch (self.cpu.arch) {
                 .i386,
                 .sparc,
                 .sparcel,
-                => return mem.dupeZ(a, u8, "/lib/ld-linux.so.2"),
+                => return copy(buffer, "/lib/ld-linux.so.2"),
 
-                .aarch64 => return mem.dupeZ(a, u8, "/lib/ld-linux-aarch64.so.1"),
-                .aarch64_be => return mem.dupeZ(a, u8, "/lib/ld-linux-aarch64_be.so.1"),
-                .aarch64_32 => return mem.dupeZ(a, u8, "/lib/ld-linux-aarch64_32.so.1"),
+                .aarch64 => return copy(buffer, "/lib/ld-linux-aarch64.so.1"),
+                .aarch64_be => return copy(buffer, "/lib/ld-linux-aarch64_be.so.1"),
+                .aarch64_32 => return copy(buffer, "/lib/ld-linux-aarch64_32.so.1"),
 
                 .arm,
                 .armeb,
                 .thumb,
                 .thumbeb,
-                => return mem.dupeZ(a, u8, switch (self.getFloatAbi()) {
+                => return copy(buffer, switch (self.getFloatAbi()) {
                     .hard => "/lib/ld-linux-armhf.so.3",
                     else => "/lib/ld-linux.so.3",
                 }),
@@ -1159,29 +1153,35 @@ pub const Target = struct {
                     };
                     const is_nan_2008 = mips.featureSetHas(self.cpu.features, .nan2008);
                     const loader = if (is_nan_2008) "ld-linux-mipsn8.so.1" else "ld.so.1";
-                    return std.fmt.allocPrint0(a, "/lib{}/{}", .{ lib_suffix, loader });
+                    return print(buffer, "/lib{}/{}", .{ lib_suffix, loader });
                 },
 
-                .powerpc => return mem.dupeZ(a, u8, "/lib/ld.so.1"),
-                .powerpc64, .powerpc64le => return mem.dupeZ(a, u8, "/lib64/ld64.so.2"),
-                .s390x => return mem.dupeZ(a, u8, "/lib64/ld64.so.1"),
-                .sparcv9 => return mem.dupeZ(a, u8, "/lib64/ld-linux.so.2"),
-                .x86_64 => return mem.dupeZ(a, u8, switch (self.abi) {
+                .powerpc => return copy(buffer, "/lib/ld.so.1"),
+                .powerpc64, .powerpc64le => return copy(buffer, "/lib64/ld64.so.2"),
+                .s390x => return copy(buffer, "/lib64/ld64.so.1"),
+                .sparcv9 => return copy(buffer, "/lib64/ld-linux.so.2"),
+                .x86_64 => return copy(buffer, switch (self.abi) {
                     .gnux32 => "/libx32/ld-linux-x32.so.2",
                     else => "/lib64/ld-linux-x86-64.so.2",
                 }),
 
-                .riscv32 => return mem.dupeZ(a, u8, "/lib/ld-linux-riscv32-ilp32.so.1"),
-                .riscv64 => return mem.dupeZ(a, u8, "/lib/ld-linux-riscv64-lp64.so.1"),
+                .riscv32 => return copy(buffer, "/lib/ld-linux-riscv32-ilp32.so.1"),
+                .riscv64 => return copy(buffer, "/lib/ld-linux-riscv64-lp64.so.1"),
 
+                // Architectures in this list have been verified as not having a standard
+                // dynamic linker path.
                 .wasm32,
                 .wasm64,
-                => return error.TargetHasNoDynamicLinker,
+                .bpfel,
+                .bpfeb,
+                .nvptx,
+                .nvptx64,
+                => return null,
 
+                // TODO go over each item in this list and either move it to the above list, or
+                // implement the standard dynamic linker path code for it.
                 .arc,
                 .avr,
-                .bpfel,
-                .bpfeb,
                 .hexagon,
                 .msp430,
                 .r600,
@@ -1189,8 +1189,6 @@ pub const Target = struct {
                 .tce,
                 .tcele,
                 .xcore,
-                .nvptx,
-                .nvptx64,
                 .le32,
                 .le64,
                 .amdil,
@@ -1204,9 +1202,25 @@ pub const Target = struct {
                 .lanai,
                 .renderscript32,
                 .renderscript64,
-                => return error.UnknownDynamicLinkerPath,
+                => return null,
             },
 
+            // Operating systems in this list have been verified as not having a standard
+            // dynamic linker path.
+            .freestanding,
+            .ios,
+            .tvos,
+            .watchos,
+            .macosx,
+            .uefi,
+            .windows,
+            .emscripten,
+            .other,
+            .wasi,
+            => return null,
+
+            // TODO go over each item in this list and either move it to the above list, or
+            // implement the standard dynamic linker path code for it.
             .ananas,
             .cloudabi,
             .fuchsia,
@@ -1230,19 +1244,7 @@ pub const Target = struct {
             .amdpal,
             .hermit,
             .hurd,
-            => return error.UnknownDynamicLinkerPath,
-
-            .freestanding,
-            .ios,
-            .tvos,
-            .watchos,
-            .macosx,
-            .uefi,
-            .windows,
-            .emscripten,
-            .other,
-            .wasi,
-            => return error.TargetHasNoDynamicLinker,
+            => return null,
         }
     }
 };
src-self-hosted/stage2.zig
@@ -1157,9 +1157,9 @@ fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8)
         if (cross_target.os_tag == null) {
             adjusted_target.os = detected_info.target.os;
 
-            if (detected_info.dynamic_linker) |dl| {
+            if (detected_info.dynamicLinker()) |dl| {
                 have_native_dl = true;
-                dynamic_linker_ptr.* = dl.ptr;
+                dynamic_linker_ptr.* = try mem.dupeZ(std.heap.c_allocator, u8, dl);
             }
             if (cross_target.abi == null) {
                 adjusted_target.abi = detected_info.target.abi;
@@ -1169,13 +1169,11 @@ fn crossTargetToTarget(cross_target: CrossTarget, dynamic_linker_ptr: *?[*:0]u8)
         }
     }
     if (!have_native_dl) {
-        dynamic_linker_ptr.* = adjusted_target.getStandardDynamicLinkerPath(
-            std.heap.c_allocator,
-        ) catch |err| switch (err) {
-            error.TargetHasNoDynamicLinker => null,
-            error.UnknownDynamicLinkerPath => null,
-            else => |e| return e,
-        };
+        var buf: [255]u8 = undefined;
+        dynamic_linker_ptr.* = if (adjusted_target.standardDynamicLinkerPath(&buf)) |s|
+            try mem.dupeZ(std.heap.c_allocator, u8, s)
+        else
+            null;
     }
     return adjusted_target;
 }