mirror of
https://github.com/ziglang/zig.git
synced 2026-02-12 20:37:54 +00:00
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
This commit is contained in:
parent
662b5f7c60
commit
60f2f3457d
@ -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,
|
||||
|
||||
.arc,
|
||||
.avr,
|
||||
.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,
|
||||
.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,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user