From 059f18855f18f9dd76f74031381e04dd131da528 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Mon, 7 Oct 2024 00:52:02 +0200 Subject: [PATCH] std.Target: Make DynamicLinker.standard() much stricter. Its semantics are now documented in terms of DynamicLinker.kind(os.tag). The idea here is two-fold: * The term "standard" actually means something; we shouldn't return a valid dynamic linker path for a triple for which it hasn't *actually* been standardized. That's just incorrect. For example, previously, this function would happily return a path for x86_64-linux-androideabi, csky-macos-gnu, or aarch64-hurd-msvc, and other such obvious nonsense. * Callers that use the return value from this function to do host probing (such as std.zig.system.detectAbiAndDynamicLinker()) can now do so with greater confidence because DynamicLinker.standard() will eagerly reject nonsensical target triples. --- lib/std/Target.zig | 432 +++++++++++++++++++++++++++------------------ 1 file changed, 256 insertions(+), 176 deletions(-) diff --git a/lib/std/Target.zig b/lib/std/Target.zig index 1d037d864ecbae361da0c56badcd8eb93881ebe2..ec08879990526aa22c8321037bd59be9d12355c6 100644 --- a/lib/std/Target.zig +++ b/lib/std/Target.zig @@ -2054,209 +2054,237 @@ pub const DynamicLinker = struct { }; } + /// 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 => init("ld.so.1"), // Fuchsia is unusual in that `DT_INTERP` is just a basename. + .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 => init("/system/runtime_loader"), + .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), - if (abi == .gnuilp32) "_ilp32" else "", + switch (abi) { + .gnu => "", + .gnuilp32 => "_ilp32", + else => return none, + }, }), - .x86 => init("/lib/ld.so.1"), - .x86_64 => initFmt("/lib/ld-{s}.so.1", .{if (abi == .gnux32) "x32" else "x86-64"}), + .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, + }}), - // These are unsupported by Hurd/glibc. - .amdgcn, - .arc, - .arm, - .armeb, - .thumb, - .thumbeb, - .avr, - .bpfel, - .bpfeb, - .csky, - .hexagon, - .kalimba, - .lanai, - .loongarch32, - .loongarch64, - .m68k, - .mips, - .mipsel, - .mips64, - .mips64el, - .msp430, - .nvptx, - .nvptx64, - .powerpc, - .powerpcle, - .powerpc64, - .powerpc64le, - .propeller1, - .propeller2, - .riscv32, - .riscv64, - .s390x, - .sparc, - .sparc64, - .spirv, - .spirv32, - .spirv64, - .spu_2, - .ve, - .wasm32, - .wasm64, - .xcore, - .xtensa, - => none, + else => none, }, .linux => if (abi.isAndroid()) - initFmt("/system/bin/linker{s}", .{if (ptrBitWidth_cpu_abi(cpu, abi) == 64) "64" else ""}) - else if (abi.isMusl()) switch (cpu.arch) { .arm, - .armeb, .thumb, - .thumbeb, + => if (abi == .androideabi) init("/system/bin/linker") else none, + .aarch64, - .aarch64_be, - .loongarch64, - .m68k, - .powerpc, - .powerpc64, - .powerpc64le, - .riscv32, .riscv64, - .s390x, .x86, .x86_64, - => |arch| initFmt("/lib/ld-musl-{s}{s}.so.1", .{ - switch (arch) { - .thumb => "arm", - .thumbeb => "armeb", - .x86 => "i386", - .x86_64 => if (abi == .muslx32) "x32" else "x86_64", - else => @tagName(arch), - }, - switch (arch) { - .arm, .armeb, .thumb, .thumbeb => if (abi.floatAbi() == .hard) "hf" else "", - .aarch64, .aarch64_be => if (abi == .gnuilp32) "_ilp32" else "", - .riscv32, .riscv64 => if (std.Target.riscv.featureSetHas(cpu.features, .d)) - "" - else if (std.Target.riscv.featureSetHas(cpu.features, .f)) - "-sp" - else - "-sf", - else => if (abi.floatAbi() == .soft) "-sf" else "", + => 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, }, }), - // The naming scheme for MIPS is a bit irregular. + .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}{s}.so.1", .{ - if (arch.isMIPS64()) "64" else "", // TODO: `n32` ABI support in LLVM 20. - if (mips.featureSetHas(cpu.features, if (arch.isMIPS64()) .mips64r6 else .mips32r6)) "r6" else "", - if (arch.endian() == .little) "el" else "", - if (abi.floatAbi() == .soft) "-sf" else "", + => |arch| initFmt("/lib/ld-musl-mips{s}{s}{s}.so.1", .{ + // TODO: `n32` ABI support in LLVM 20. + switch (abi) { + .musl => "64", + else => return none, + }, + if (mips.featureSetHas(cpu.features, .mips64r6)) "r6" else "", + if (arch == .mips64el) "el" else "", }), - // These are unsupported by musl. - .amdgcn, - .arc, - .avr, - .csky, - .bpfel, - .bpfeb, - .hexagon, - .kalimba, - .lanai, - .loongarch32, - .msp430, - .nvptx, - .nvptx64, - .powerpcle, - .propeller1, - .propeller2, - .sparc, - .sparc64, - .spirv, - .spirv32, - .spirv64, - .spu_2, - .ve, - .wasm32, - .wasm64, - .xcore, - .xtensa, - => none, + .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 => init("/lib/ld-linux-arc.so.2"), + .arc => if (abi == .gnu) init("/lib/ld-linux-arc.so.2") else none, // TODO: OABI support (`/lib/ld-linux.so.2`). .arm, .armeb, .thumb, .thumbeb, - => initFmt("/lib/ld-linux{s}.so.3", .{if (abi.floatAbi() == .hard) "-armhf" else ""}), + => 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), - if (abi == .gnuilp32) "_ilp32" else "", + switch (abi) { + .gnu => "", + .gnuilp32 => "_ilp32", + else => return none, + }, }), // TODO: `-be` architecture support. - .csky => initFmt("/lib/ld-linux-cskyv2{s}.so.1", .{if (abi.floatAbi() == .hard) "-hf" else ""}), + .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 => "lp64d", + else => return none, }}), - .m68k => init("/lib/ld.so.1"), + .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) { - .gnuabin32 => "32", .gnuabi64 => "64", - else => "", + .gnuabin32 => "32", + else => return none, }, if (mips.featureSetHas(cpu.features, .nan2008)) "-linux-mipsn8" else "", }), - .powerpc => init("/lib/ld.so.1"), - // TODO: ELFv2 ABI opt-in support. - .powerpc64 => init("/lib64/ld64.so.1"), - .powerpc64le => init("/lib64/ld64.so.2"), + .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| initFmt("/lib/ld-linux-{s}-{s}{s}.so.1", .{ - @tagName(arch), + => |arch| if (abi == .gnu) initFmt("/lib/ld-linux-{s}{s}.so.1", .{ switch (arch) { - .riscv32 => "ilp32", - .riscv64 => "lp64", + .riscv32 => "riscv32-ilp32", + .riscv64 => "riscv64-lp64", else => unreachable, }, if (riscv.featureSetHas(cpu.features, .d)) @@ -2265,78 +2293,130 @@ pub const DynamicLinker = struct { "f" else "", - }), + }) else none, - .s390x => init("/lib/ld64.so.1"), + .s390x => if (abi == .gnu) init("/lib/ld64.so.1") else none, - .sparc => init("/lib/ld-linux.so.2"), - .sparc64 => init("/lib64/ld-linux.so.2"), + .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 => init("/lib/ld-linux.so.2"), - .x86_64 => init(if (abi == .gnux32) "/libx32/ld-linux-x32.so.2" else "/lib64/ld-linux-x86-64.so.2"), + .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 => init("/lib/ld.so.1"), + .xtensa => if (abi == .gnu) init("/lib/ld.so.1") else none, - // These are unsupported by glibc. - .amdgcn, - .avr, - .bpfeb, - .bpfel, - .hexagon, - .kalimba, - .lanai, - .loongarch32, - .msp430, - .nvptx, - .nvptx64, - .powerpcle, - .propeller1, - .propeller2, - .spirv, - .spirv32, - .spirv64, - .spu_2, - .ve, - .wasm32, - .wasm64, - .xcore, - => none, + else => none, } else none, // Not a known Linux libc. - .serenity => init("/usr/lib/Loader.so"), + .serenity => switch (cpu.arch) { + .aarch64, + .riscv64, + .x86_64, + => init("/usr/lib/Loader.so"), + else => none, + }, - .dragonfly => initFmt("{s}/libexec/ld-elf.so.2", .{ + .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 => initFmt("{s}/libexec/ld-elf.so.1", .{ - if (os.version_range.semver.isAtLeast(.{ .major = 6, .minor = 0, .patch = 0 }) orelse false) - "" - else - "/usr", - }), + .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 => init("/libexec/ld.elf_so"), + .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 => init("/usr/libexec/ld.so"), + .openbsd => switch (cpu.arch) { + .arm, + .thumb, + .aarch64, + .mips64, + .mips64el, + .powerpc, + .powerpc64, + .riscv64, + .sparc64, + .x86, + .x86_64, + => init("/usr/libexec/ld.so"), + else => none, + }, - .bridgeos, + .bridgeos => if (cpu.arch == .aarch64) init("/usr/lib/dyld") else none, .driverkit, .ios, .macos, .tvos, .visionos, .watchos, - => init("/usr/lib/dyld"), + => switch (cpu.arch) { + .aarch64, + .x86_64, + => init("/usr/lib/dyld"), + else => none, + }, .illumos, .solaris, - => initFmt("/lib/{s}ld.so.1", .{if (ptrBitWidth_cpu_abi(cpu, abi) == 64) "64/" else ""}), + => 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. -- 2.54.0