struct DynamicLinker [src]
Fields
buffer: [255]u8Contains the memory used to store the dynamic linker path. This field
should not be used directly. See get and set. This field exists so
that this API requires no allocator.
len: u8Used to construct the dynamic linker path. This field should not be used
directly. See get and set.
Members
Source
pub const DynamicLinker = struct {
/// Contains the memory used to store the dynamic linker path. This field
/// should not be used directly. See `get` and `set`. This field exists so
/// that this API requires no allocator.
buffer: [255]u8,
/// Used to construct the dynamic linker path. This field should not be used
/// directly. See `get` and `set`.
len: u8,
pub const none: DynamicLinker = .{ .buffer = undefined, .len = 0 };
/// Asserts that the length is less than or equal to 255 bytes.
pub fn init(maybe_path: ?[]const u8) DynamicLinker {
var dl: DynamicLinker = undefined;
dl.set(maybe_path);
return dl;
}
pub fn initFmt(comptime fmt_str: []const u8, args: anytype) !DynamicLinker {
var dl: DynamicLinker = undefined;
try dl.setFmt(fmt_str, args);
return dl;
}
/// The returned memory has the same lifetime as the `DynamicLinker`.
pub fn get(dl: *const DynamicLinker) ?[]const u8 {
return if (dl.len > 0) dl.buffer[0..dl.len] else null;
}
/// Asserts that the length is less than or equal to 255 bytes.
pub fn set(dl: *DynamicLinker, maybe_path: ?[]const u8) void {
const path = maybe_path orelse "";
@memcpy(dl.buffer[0..path.len], path);
dl.len = @intCast(path.len);
}
/// Asserts that the length is less than or equal to 255 bytes.
pub fn setFmt(dl: *DynamicLinker, comptime fmt_str: []const u8, args: anytype) !void {
dl.len = @intCast((try std.fmt.bufPrint(&dl.buffer, fmt_str, args)).len);
}
pub fn eql(lhs: DynamicLinker, rhs: DynamicLinker) bool {
return std.mem.eql(u8, lhs.buffer[0..lhs.len], rhs.buffer[0..rhs.len]);
}
pub const Kind = enum {
/// No dynamic linker.
none,
/// Dynamic linker path is determined by the arch/OS components.
arch_os,
/// Dynamic linker path is determined by the arch/OS/ABI components.
arch_os_abi,
};
pub fn kind(os: Os.Tag) Kind {
return switch (os) {
.fuchsia,
.haiku,
.serenity,
.dragonfly,
.freebsd,
.netbsd,
.openbsd,
.driverkit,
.ios,
.macos,
.tvos,
.visionos,
.watchos,
.illumos,
.solaris,
=> .arch_os,
.hurd,
.linux,
=> .arch_os_abi,
.freestanding,
.other,
.contiki,
.elfiamcu,
.hermit,
.aix,
.plan9,
.rtems,
.zos,
.uefi,
.windows,
.emscripten,
.wasi,
.amdhsa,
.amdpal,
.cuda,
.mesa3d,
.nvcl,
.opencl,
.opengl,
.vulkan,
.ps3,
.ps4,
.ps5,
=> .none,
};
}
/// The strictness of this function depends on the value of `kind(os.tag)`:
///
/// * `.none`: Ignores all arguments and just returns `none`.
/// * `.arch_os`: Ignores `abi` and returns the dynamic linker matching `cpu` and `os`.
/// * `.arch_os_abi`: Returns the dynamic linker matching `cpu`, `os`, and `abi`.
///
/// In the case of `.arch_os` in particular, callers should be aware that a valid dynamic linker
/// being returned only means that the `cpu` + `os` combination represents a platform that
/// actually exists and which has an established dynamic linker path that does not change with
/// the ABI; it does not necessarily mean that `abi` makes any sense at all for that platform.
/// The responsibility for determining whether `abi` is valid in this case rests with the
/// caller. `Abi.default()` can be used to pick a best-effort default ABI for such platforms.
pub fn standard(cpu: Cpu, os: Os, abi: Abi) DynamicLinker {
return switch (os.tag) {
.fuchsia => switch (cpu.arch) {
.aarch64,
.riscv64,
.x86_64,
=> init("ld.so.1"), // Fuchsia is unusual in that `DT_INTERP` is just a basename.
else => none,
},
.haiku => switch (cpu.arch) {
.arm,
.thumb,
.aarch64,
.m68k,
.powerpc,
.riscv64,
.sparc64,
.x86,
.x86_64,
=> init("/system/runtime_loader"),
else => none,
},
.hurd => switch (cpu.arch) {
.aarch64,
.aarch64_be,
=> |arch| initFmt("/lib/ld-{s}{s}.so.1", .{
@tagName(arch),
switch (abi) {
.gnu => "",
.gnuilp32 => "_ilp32",
else => return none,
},
}),
.x86 => if (abi == .gnu) init("/lib/ld.so.1") else none,
.x86_64 => initFmt("/lib/ld-{s}.so.1", .{switch (abi) {
.gnu => "x86-64",
.gnux32 => "x32",
else => return none,
}}),
else => none,
},
.linux => if (abi.isAndroid())
switch (cpu.arch) {
.arm,
.thumb,
=> if (abi == .androideabi) init("/system/bin/linker") else none,
.aarch64,
.riscv64,
.x86,
.x86_64,
=> if (abi == .android) initFmt("/system/bin/linker{s}", .{
if (ptrBitWidth_cpu_abi(cpu, abi) == 64) "64" else "",
}) else none,
else => none,
}
else if (abi.isMusl())
switch (cpu.arch) {
.arm,
.armeb,
.thumb,
.thumbeb,
=> |arch| initFmt("/lib/ld-musl-arm{s}{s}.so.1", .{
if (arch == .armeb or arch == .thumbeb) "eb" else "",
switch (abi) {
.musleabi => "",
.musleabihf => "hf",
else => return none,
},
}),
.aarch64,
.aarch64_be,
.loongarch64, // TODO: `-sp` and `-sf` ABI support in LLVM 20.
.m68k,
.powerpc64,
.powerpc64le,
.s390x,
=> |arch| if (abi == .musl) initFmt("/lib/ld-musl-{s}.so.1", .{@tagName(arch)}) else none,
.mips,
.mipsel,
=> |arch| initFmt("/lib/ld-musl-mips{s}{s}{s}.so.1", .{
if (mips.featureSetHas(cpu.features, .mips32r6)) "r6" else "",
if (arch == .mipsel) "el" else "",
switch (abi) {
.musleabi => "-sf",
.musleabihf => "",
else => return none,
},
}),
.mips64,
.mips64el,
=> |arch| initFmt("/lib/ld-musl-mips{s}{s}{s}.so.1", .{
switch (abi) {
.muslabi64 => "64",
.muslabin32 => "n32",
else => return none,
},
if (mips.featureSetHas(cpu.features, .mips64r6)) "r6" else "",
if (arch == .mips64el) "el" else "",
}),
.powerpc => initFmt("/lib/ld-musl-powerpc{s}.so.1", .{switch (abi) {
.musleabi => "-sf",
.musleabihf => "",
else => return none,
}}),
.riscv32,
.riscv64,
=> |arch| if (abi == .musl) initFmt("/lib/ld-musl-{s}{s}.so.1", .{
@tagName(arch),
if (riscv.featureSetHas(cpu.features, .d))
""
else if (riscv.featureSetHas(cpu.features, .f))
"-sp"
else
"-sf",
}) else none,
.x86 => if (abi == .musl) init("/lib/ld-musl-i386.so.1") else none,
.x86_64 => initFmt("/lib/ld-musl-{s}.so.1", .{switch (abi) {
.musl => "x86_64",
.muslx32 => "x32",
else => return none,
}}),
else => none,
}
else if (abi.isGnu())
switch (cpu.arch) {
// TODO: `eb` architecture support.
// TODO: `700` ABI support.
.arc => if (abi == .gnu) init("/lib/ld-linux-arc.so.2") else none,
.arm,
.armeb,
.thumb,
.thumbeb,
=> initFmt("/lib/ld-linux{s}.so.3", .{switch (abi) {
.gnueabi => "",
.gnueabihf => "-armhf",
else => return none,
}}),
.aarch64,
.aarch64_be,
=> |arch| initFmt("/lib/ld-linux-{s}{s}.so.1", .{
@tagName(arch),
switch (abi) {
.gnu => "",
.gnuilp32 => "_ilp32",
else => return none,
},
}),
// TODO: `-be` architecture support.
.csky => initFmt("/lib/ld-linux-cskyv2{s}.so.1", .{switch (abi) {
.gnueabi => "",
.gnueabihf => "-hf",
else => return none,
}}),
.loongarch64 => initFmt("/lib64/ld-linux-loongarch-{s}.so.1", .{switch (abi) {
.gnu => "lp64d",
.gnuf32 => "lp64f",
.gnusf => "lp64s",
else => return none,
}}),
.m68k => if (abi == .gnu) init("/lib/ld.so.1") else none,
.mips,
.mipsel,
=> switch (abi) {
.gnueabi,
.gnueabihf,
=> initFmt("/lib/ld{s}.so.1", .{
if (mips.featureSetHas(cpu.features, .nan2008)) "-linux-mipsn8" else "",
}),
else => none,
},
.mips64,
.mips64el,
=> initFmt("/lib{s}/ld{s}.so.1", .{
switch (abi) {
.gnuabi64 => "64",
.gnuabin32 => "32",
else => return none,
},
if (mips.featureSetHas(cpu.features, .nan2008)) "-linux-mipsn8" else "",
}),
.powerpc => switch (abi) {
.gnueabi,
.gnueabihf,
=> init("/lib/ld.so.1"),
else => none,
},
// TODO: ELFv2 ABI (`/lib64/ld64.so.2`) opt-in support.
.powerpc64 => if (abi == .gnu) init("/lib64/ld64.so.1") else none,
.powerpc64le => if (abi == .gnu) init("/lib64/ld64.so.2") else none,
.riscv32,
.riscv64,
=> |arch| if (abi == .gnu) initFmt("/lib/ld-linux-{s}{s}.so.1", .{
switch (arch) {
.riscv32 => "riscv32-ilp32",
.riscv64 => "riscv64-lp64",
else => unreachable,
},
if (riscv.featureSetHas(cpu.features, .d))
"d"
else if (riscv.featureSetHas(cpu.features, .f))
"f"
else
"",
}) else none,
.s390x => if (abi == .gnu) init("/lib/ld64.so.1") else none,
.sparc => if (abi == .gnu) init("/lib/ld-linux.so.2") else none,
.sparc64 => if (abi == .gnu) init("/lib64/ld-linux.so.2") else none,
.x86 => if (abi == .gnu) init("/lib/ld-linux.so.2") else none,
.x86_64 => switch (abi) {
.gnu => init("/lib64/ld-linux-x86-64.so.2"),
.gnux32 => init("/libx32/ld-linux-x32.so.2"),
else => none,
},
.xtensa => if (abi == .gnu) init("/lib/ld.so.1") else none,
else => none,
}
else
none, // Not a known Linux libc.
.serenity => switch (cpu.arch) {
.aarch64,
.riscv64,
.x86_64,
=> init("/usr/lib/Loader.so"),
else => none,
},
.dragonfly => if (cpu.arch == .x86_64) initFmt("{s}/libexec/ld-elf.so.2", .{
if (os.version_range.semver.isAtLeast(.{ .major = 3, .minor = 8, .patch = 0 }) orelse false)
""
else
"/usr",
}) else none,
.freebsd => switch (cpu.arch) {
.arm,
.armeb,
.thumb,
.thumbeb,
.aarch64,
.mips,
.mipsel,
.mips64,
.mips64el,
.powerpc,
.powerpc64,
.powerpc64le,
.riscv64,
.sparc64,
.x86,
.x86_64,
=> initFmt("{s}/libexec/ld-elf.so.1", .{
if (os.version_range.semver.isAtLeast(.{ .major = 6, .minor = 0, .patch = 0 }) orelse false)
""
else
"/usr",
}),
else => none,
},
.netbsd => switch (cpu.arch) {
.arm,
.armeb,
.thumb,
.thumbeb,
.aarch64,
.aarch64_be,
.m68k,
.mips,
.mipsel,
.mips64,
.mips64el,
.powerpc,
.riscv64,
.sparc,
.sparc64,
.x86,
.x86_64,
=> init("/libexec/ld.elf_so"),
else => none,
},
.openbsd => switch (cpu.arch) {
.arm,
.thumb,
.aarch64,
.mips64,
.mips64el,
.powerpc,
.powerpc64,
.riscv64,
.sparc64,
.x86,
.x86_64,
=> init("/usr/libexec/ld.so"),
else => none,
},
.driverkit,
.ios,
.macos,
.tvos,
.visionos,
.watchos,
=> switch (cpu.arch) {
.aarch64,
.x86_64,
=> init("/usr/lib/dyld"),
else => none,
},
.illumos,
.solaris,
=> switch (cpu.arch) {
.sparc,
.sparc64,
.x86,
.x86_64,
=> initFmt("/lib/{s}ld.so.1", .{if (ptrBitWidth_cpu_abi(cpu, .none) == 64) "64/" else ""}),
else => none,
},
// Operating systems in this list have been verified as not having a standard
// dynamic linker path.
.freestanding,
.other,
.contiki,
.elfiamcu,
.hermit,
.aix,
.plan9,
.rtems,
.zos,
.uefi,
.windows,
.emscripten,
.wasi,
.amdhsa,
.amdpal,
.cuda,
.mesa3d,
.nvcl,
.opencl,
.opengl,
.vulkan,
=> none,
// 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.
.ps3,
.ps4,
.ps5,
=> none,
} catch unreachable;
}
}