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 | 438 +++++++++++++++++++++++++++------------------ 1 file changed, 259 insertions(+), 179 deletions(-) diff --git a/lib/std/Target.zig b/lib/std/Target.zig index 1d037d864e..ec08879990 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 ""}) + 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, + .loongarch64, // TODO: `-sp` and `-sf` ABI support in LLVM 20. .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 "", - }, - }), + => |arch| if (abi == .musl) initFmt("/lib/ld-musl-{s}.so.1", .{@tagName(arch)}) else none, - // The naming scheme for MIPS is a bit irregular. .mips, .mipsel, - .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", .{ + if (mips.featureSetHas(cpu.features, .mips32r6)) "r6" else "", + if (arch == .mipsel) "el" else "", + switch (abi) { + .musleabi => "-sf", + .musleabihf => "", + else => return none, + }, }), - // 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, + .mips64, + .mips64el, + => |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 "", + }), + + .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 ""}), - - .loongarch64 => initFmt("/lib64/ld-linux-loongarch-{s}.so.1", .{switch (abi) { - .gnuf32 => "lp64f", - .gnusf => "lp64s", - else => "lp64d", + .csky => initFmt("/lib/ld-linux-cskyv2{s}.so.1", .{switch (abi) { + .gnueabi => "", + .gnueabihf => "-hf", + else => return none, }}), - .m68k => init("/lib/ld.so.1"), + .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) { - .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.