Rollup merge of #130435 - madsmtm:move-apple-link-args, r=petrochenkov
Move Apple linker args from `rustc_target` to `rustc_codegen_ssa` They are dependent on the deployment target and SDK version, but having these in `rustc_target` makes it hard to introduce that dependency. Part of the work needed to do https://github.com/rust-lang/rust/issues/118204, see https://github.com/rust-lang/rust/pull/129342 for some discussion. Tested using: ```console ./x test tests/run-make/apple-deployment-target --target="aarch64-apple-darwin,aarch64-apple-ios,aarch64-apple-ios-macabi,aarch64-apple-ios-sim,aarch64-apple-tvos,aarch64-apple-tvos-sim,aarch64-apple-visionos,aarch64-apple-visionos-sim,aarch64-apple-watchos,aarch64-apple-watchos-sim,arm64_32-apple-watchos,armv7k-apple-watchos,armv7s-apple-ios,x86_64-apple-darwin,x86_64-apple-ios,x86_64-apple-ios-macabi,x86_64-apple-tvos,x86_64-apple-watchos-sim,x86_64h-apple-darwin" IPHONEOS_DEPLOYMENT_TARGET=10.0 ./x test tests/run-make/apple-deployment-target --target=i386-apple-ios ``` `arm64e-apple-darwin` and `arm64e-apple-ios` have not been tested, see https://github.com/rust-lang/rust/issues/130085, neither is `i686-apple-darwin`, since that requires using an x86_64 macbook, and I currently can't get mine to work, see https://github.com/rust-lang/rust/issues/130434. CC `@petrochenkov`
This commit is contained in:
commit
f9cd81f3d9
@ -40,7 +40,7 @@
|
|||||||
use rustc_target::spec::{
|
use rustc_target::spec::{
|
||||||
Cc, LinkOutputKind, LinkSelfContainedComponents, LinkSelfContainedDefault, LinkerFeatures,
|
Cc, LinkOutputKind, LinkSelfContainedComponents, LinkSelfContainedDefault, LinkerFeatures,
|
||||||
LinkerFlavor, LinkerFlavorCli, Lld, PanicStrategy, RelocModel, RelroLevel, SanitizerSet,
|
LinkerFlavor, LinkerFlavorCli, Lld, PanicStrategy, RelocModel, RelroLevel, SanitizerSet,
|
||||||
SplitDebuginfo,
|
SplitDebuginfo, current_apple_deployment_target,
|
||||||
};
|
};
|
||||||
use tempfile::Builder as TempFileBuilder;
|
use tempfile::Builder as TempFileBuilder;
|
||||||
use tracing::{debug, info, warn};
|
use tracing::{debug, info, warn};
|
||||||
@ -2405,6 +2405,8 @@ fn add_order_independent_options(
|
|||||||
// Take care of the flavors and CLI options requesting the `lld` linker.
|
// Take care of the flavors and CLI options requesting the `lld` linker.
|
||||||
add_lld_args(cmd, sess, flavor, self_contained_components);
|
add_lld_args(cmd, sess, flavor, self_contained_components);
|
||||||
|
|
||||||
|
add_apple_link_args(cmd, sess, flavor);
|
||||||
|
|
||||||
let apple_sdk_root = add_apple_sdk(cmd, sess, flavor);
|
let apple_sdk_root = add_apple_sdk(cmd, sess, flavor);
|
||||||
|
|
||||||
add_link_script(cmd, sess, tmpdir, crate_type);
|
add_link_script(cmd, sess, tmpdir, crate_type);
|
||||||
@ -2957,6 +2959,135 @@ pub(crate) fn are_upstream_rust_objects_already_included(sess: &Session) -> bool
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// We need to communicate four things to the linker on Apple/Darwin targets:
|
||||||
|
/// - The architecture.
|
||||||
|
/// - The operating system (and that it's an Apple platform).
|
||||||
|
/// - The deployment target.
|
||||||
|
/// - The environment / ABI.
|
||||||
|
fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
|
||||||
|
if !sess.target.is_like_osx {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let LinkerFlavor::Darwin(cc, _) = flavor else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// `sess.target.arch` (`target_arch`) is not detailed enough.
|
||||||
|
let llvm_arch = sess.target.llvm_target.split_once('-').expect("LLVM target must have arch").0;
|
||||||
|
let target_os = &*sess.target.os;
|
||||||
|
let target_abi = &*sess.target.abi;
|
||||||
|
|
||||||
|
// The architecture name to forward to the linker.
|
||||||
|
//
|
||||||
|
// Supported architecture names can be found in the source:
|
||||||
|
// https://github.com/apple-oss-distributions/ld64/blob/ld64-951.9/src/abstraction/MachOFileAbstraction.hpp#L578-L648
|
||||||
|
//
|
||||||
|
// Intentially verbose to ensure that the list always matches correctly
|
||||||
|
// with the list in the source above.
|
||||||
|
let ld64_arch = match llvm_arch {
|
||||||
|
"armv7k" => "armv7k",
|
||||||
|
"armv7s" => "armv7s",
|
||||||
|
"arm64" => "arm64",
|
||||||
|
"arm64e" => "arm64e",
|
||||||
|
"arm64_32" => "arm64_32",
|
||||||
|
// ld64 doesn't understand i686, so fall back to i386 instead.
|
||||||
|
//
|
||||||
|
// Same story when linking with cc, since that ends up invoking ld64.
|
||||||
|
"i386" | "i686" => "i386",
|
||||||
|
"x86_64" => "x86_64",
|
||||||
|
"x86_64h" => "x86_64h",
|
||||||
|
_ => bug!("unsupported architecture in Apple target: {}", sess.target.llvm_target),
|
||||||
|
};
|
||||||
|
|
||||||
|
if cc == Cc::No {
|
||||||
|
// From the man page for ld64 (`man ld`):
|
||||||
|
// > The linker accepts universal (multiple-architecture) input files,
|
||||||
|
// > but always creates a "thin" (single-architecture), standard
|
||||||
|
// > Mach-O output file. The architecture for the output file is
|
||||||
|
// > specified using the -arch option.
|
||||||
|
//
|
||||||
|
// The linker has heuristics to determine the desired architecture,
|
||||||
|
// but to be safe, and to avoid a warning, we set the architecture
|
||||||
|
// explicitly.
|
||||||
|
cmd.link_args(&["-arch", ld64_arch]);
|
||||||
|
|
||||||
|
// Man page says that ld64 supports the following platform names:
|
||||||
|
// > - macos
|
||||||
|
// > - ios
|
||||||
|
// > - tvos
|
||||||
|
// > - watchos
|
||||||
|
// > - bridgeos
|
||||||
|
// > - visionos
|
||||||
|
// > - xros
|
||||||
|
// > - mac-catalyst
|
||||||
|
// > - ios-simulator
|
||||||
|
// > - tvos-simulator
|
||||||
|
// > - watchos-simulator
|
||||||
|
// > - visionos-simulator
|
||||||
|
// > - xros-simulator
|
||||||
|
// > - driverkit
|
||||||
|
let platform_name = match (target_os, target_abi) {
|
||||||
|
(os, "") => os,
|
||||||
|
("ios", "macabi") => "mac-catalyst",
|
||||||
|
("ios", "sim") => "ios-simulator",
|
||||||
|
("tvos", "sim") => "tvos-simulator",
|
||||||
|
("watchos", "sim") => "watchos-simulator",
|
||||||
|
("visionos", "sim") => "visionos-simulator",
|
||||||
|
_ => bug!("invalid OS/ABI combination for Apple target: {target_os}, {target_abi}"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let (major, minor, patch) = current_apple_deployment_target(&sess.target);
|
||||||
|
let min_version = format!("{major}.{minor}.{patch}");
|
||||||
|
|
||||||
|
// Lie about the SDK version, we don't know it here
|
||||||
|
let sdk_version = &*min_version;
|
||||||
|
|
||||||
|
// From the man page for ld64 (`man ld`):
|
||||||
|
// > This is set to indicate the platform, oldest supported version of
|
||||||
|
// > that platform that output is to be used on, and the SDK that the
|
||||||
|
// > output was built against.
|
||||||
|
//
|
||||||
|
// Like with `-arch`, the linker can figure out the platform versions
|
||||||
|
// itself from the binaries being linked, but to be safe, we specify
|
||||||
|
// the desired versions here explicitly.
|
||||||
|
cmd.link_args(&["-platform_version", platform_name, &*min_version, sdk_version]);
|
||||||
|
} else {
|
||||||
|
// cc == Cc::Yes
|
||||||
|
// We'd _like_ to use `-target` everywhere, since that can uniquely
|
||||||
|
// communicate all the required details, but that doesn't work on GCC,
|
||||||
|
// and since we don't know whether the `cc` compiler is Clang, GCC, or
|
||||||
|
// something else, we fall back to other options that also work on GCC
|
||||||
|
// when compiling for macOS.
|
||||||
|
//
|
||||||
|
// Targets other than macOS are ill-supported by GCC (it doesn't even
|
||||||
|
// support e.g. `-miphoneos-version-min`), so in those cases we can
|
||||||
|
// fairly safely use `-target`. See also the following, where it is
|
||||||
|
// made explicit that the recommendation by LLVM developers is to use
|
||||||
|
// `-target`: <https://github.com/llvm/llvm-project/issues/88271>
|
||||||
|
if target_os == "macos" {
|
||||||
|
// `-arch` communicates the architecture.
|
||||||
|
//
|
||||||
|
// CC forwards the `-arch` to the linker, so we use the same value
|
||||||
|
// here intentionally.
|
||||||
|
cmd.cc_args(&["-arch", ld64_arch]);
|
||||||
|
|
||||||
|
// The presence of `-mmacosx-version-min` makes CC default to
|
||||||
|
// macOS, and it sets the deployment target.
|
||||||
|
let (major, minor, patch) = current_apple_deployment_target(&sess.target);
|
||||||
|
// Intentionally pass this as a single argument, Clang doesn't
|
||||||
|
// seem to like it otherwise.
|
||||||
|
cmd.cc_arg(&format!("-mmacosx-version-min={major}.{minor}.{patch}"));
|
||||||
|
|
||||||
|
// macOS has no environment, so with these two, we've told CC the
|
||||||
|
// four desired parameters.
|
||||||
|
//
|
||||||
|
// We avoid `-m32`/`-m64`, as this is already encoded by `-arch`.
|
||||||
|
} else {
|
||||||
|
cmd.cc_args(&["-target", &sess.target.llvm_target]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) -> Option<PathBuf> {
|
fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) -> Option<PathBuf> {
|
||||||
let arch = &sess.target.arch;
|
let arch = &sess.target.arch;
|
||||||
let os = &sess.target.os;
|
let os = &sess.target.os;
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
use std::num::ParseIntError;
|
use std::num::ParseIntError;
|
||||||
|
|
||||||
use crate::spec::{
|
use crate::spec::{
|
||||||
Cc, DebuginfoKind, FramePointer, LinkArgs, LinkerFlavor, Lld, SplitDebuginfo, StackProbeType,
|
Cc, DebuginfoKind, FramePointer, LinkerFlavor, Lld, SplitDebuginfo, StackProbeType, StaticCow,
|
||||||
StaticCow, Target, TargetOptions, add_link_args, add_link_args_iter, cvs,
|
Target, TargetOptions, cvs,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -40,25 +40,6 @@ fn target_name(self) -> &'static str {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The architecture name to forward to the linker.
|
|
||||||
fn ld_arch(self) -> &'static str {
|
|
||||||
// Supported architecture names can be found in the source:
|
|
||||||
// https://github.com/apple-oss-distributions/ld64/blob/ld64-951.9/src/abstraction/MachOFileAbstraction.hpp#L578-L648
|
|
||||||
match self {
|
|
||||||
Armv7k => "armv7k",
|
|
||||||
Armv7s => "armv7s",
|
|
||||||
Arm64 => "arm64",
|
|
||||||
Arm64e => "arm64e",
|
|
||||||
Arm64_32 => "arm64_32",
|
|
||||||
// ld64 doesn't understand i686, so fall back to i386 instead
|
|
||||||
//
|
|
||||||
// Same story when linking with cc, since that ends up invoking ld64.
|
|
||||||
I386 | I686 => "i386",
|
|
||||||
X86_64 => "x86_64",
|
|
||||||
X86_64h => "x86_64h",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn target_arch(self) -> Cow<'static, str> {
|
pub(crate) fn target_arch(self) -> Cow<'static, str> {
|
||||||
Cow::Borrowed(match self {
|
Cow::Borrowed(match self {
|
||||||
Armv7k | Armv7s => "arm",
|
Armv7k | Armv7s => "arm",
|
||||||
@ -116,104 +97,6 @@ fn target_abi(self) -> &'static str {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pre_link_args(os: &'static str, arch: Arch, abi: TargetAbi) -> LinkArgs {
|
|
||||||
// From the man page for ld64 (`man ld`):
|
|
||||||
// > The linker accepts universal (multiple-architecture) input files,
|
|
||||||
// > but always creates a "thin" (single-architecture), standard Mach-O
|
|
||||||
// > output file. The architecture for the output file is specified using
|
|
||||||
// > the -arch option.
|
|
||||||
//
|
|
||||||
// The linker has heuristics to determine the desired architecture, but to
|
|
||||||
// be safe, and to avoid a warning, we set the architecture explicitly.
|
|
||||||
let mut args =
|
|
||||||
TargetOptions::link_args(LinkerFlavor::Darwin(Cc::No, Lld::No), &["-arch", arch.ld_arch()]);
|
|
||||||
|
|
||||||
// From the man page for ld64 (`man ld`):
|
|
||||||
// > This is set to indicate the platform, oldest supported version of
|
|
||||||
// > that platform that output is to be used on, and the SDK that the
|
|
||||||
// > output was built against. platform [...] may be one of the following
|
|
||||||
// > strings:
|
|
||||||
// > - macos
|
|
||||||
// > - ios
|
|
||||||
// > - tvos
|
|
||||||
// > - watchos
|
|
||||||
// > - bridgeos
|
|
||||||
// > - visionos
|
|
||||||
// > - xros
|
|
||||||
// > - mac-catalyst
|
|
||||||
// > - ios-simulator
|
|
||||||
// > - tvos-simulator
|
|
||||||
// > - watchos-simulator
|
|
||||||
// > - visionos-simulator
|
|
||||||
// > - xros-simulator
|
|
||||||
// > - driverkit
|
|
||||||
//
|
|
||||||
// Like with `-arch`, the linker can figure out the platform versions
|
|
||||||
// itself from the binaries being linked, but to be safe, we specify the
|
|
||||||
// desired versions here explicitly.
|
|
||||||
let platform_name: StaticCow<str> = match abi {
|
|
||||||
TargetAbi::Normal => os.into(),
|
|
||||||
TargetAbi::Simulator => format!("{os}-simulator").into(),
|
|
||||||
TargetAbi::MacCatalyst => "mac-catalyst".into(),
|
|
||||||
};
|
|
||||||
let min_version: StaticCow<str> = {
|
|
||||||
let (major, minor, patch) = deployment_target(os, arch, abi);
|
|
||||||
format!("{major}.{minor}.{patch}").into()
|
|
||||||
};
|
|
||||||
// Lie about the SDK version, we don't know it here
|
|
||||||
let sdk_version = min_version.clone();
|
|
||||||
add_link_args_iter(
|
|
||||||
&mut args,
|
|
||||||
LinkerFlavor::Darwin(Cc::No, Lld::No),
|
|
||||||
["-platform_version".into(), platform_name, min_version, sdk_version].into_iter(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// We need to communicate four things to the C compiler to be able to link:
|
|
||||||
// - The architecture.
|
|
||||||
// - The operating system (and that it's an Apple platform).
|
|
||||||
// - The deployment target.
|
|
||||||
// - The environment / ABI.
|
|
||||||
//
|
|
||||||
// We'd like to use `-target` everywhere, since that can uniquely
|
|
||||||
// communicate all of these, but that doesn't work on GCC, and since we
|
|
||||||
// don't know whether the `cc` compiler is Clang, GCC, or something else,
|
|
||||||
// we fall back to other options that also work on GCC when compiling for
|
|
||||||
// macOS.
|
|
||||||
//
|
|
||||||
// Targets other than macOS are ill-supported by GCC (it doesn't even
|
|
||||||
// support e.g. `-miphoneos-version-min`), so in those cases we can fairly
|
|
||||||
// safely use `-target`. See also the following, where it is made explicit
|
|
||||||
// that the recommendation by LLVM developers is to use `-target`:
|
|
||||||
// <https://github.com/llvm/llvm-project/issues/88271>
|
|
||||||
if os == "macos" {
|
|
||||||
// `-arch` communicates the architecture.
|
|
||||||
//
|
|
||||||
// CC forwards the `-arch` to the linker, so we use the same value
|
|
||||||
// here intentionally.
|
|
||||||
add_link_args(&mut args, LinkerFlavor::Darwin(Cc::Yes, Lld::No), &[
|
|
||||||
"-arch",
|
|
||||||
arch.ld_arch(),
|
|
||||||
]);
|
|
||||||
// The presence of `-mmacosx-version-min` makes CC default to macOS,
|
|
||||||
// and it sets the deployment target.
|
|
||||||
let (major, minor, patch) = deployment_target(os, arch, abi);
|
|
||||||
let opt = format!("-mmacosx-version-min={major}.{minor}.{patch}").into();
|
|
||||||
add_link_args_iter(&mut args, LinkerFlavor::Darwin(Cc::Yes, Lld::No), [opt].into_iter());
|
|
||||||
// macOS has no environment, so with these two, we've told CC all the
|
|
||||||
// desired parameters.
|
|
||||||
//
|
|
||||||
// We avoid `-m32`/`-m64`, as this is already encoded by `-arch`.
|
|
||||||
} else {
|
|
||||||
add_link_args_iter(
|
|
||||||
&mut args,
|
|
||||||
LinkerFlavor::Darwin(Cc::Yes, Lld::No),
|
|
||||||
["-target".into(), llvm_target(os, arch, abi)].into_iter(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
args
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the base target options, LLVM target and `target_arch` from the three
|
/// Get the base target options, LLVM target and `target_arch` from the three
|
||||||
/// things that uniquely identify Rust's Apple targets: The OS, the
|
/// things that uniquely identify Rust's Apple targets: The OS, the
|
||||||
/// architecture, and the ABI.
|
/// architecture, and the ABI.
|
||||||
@ -232,7 +115,6 @@ pub(crate) fn base(
|
|||||||
// macOS has -dead_strip, which doesn't rely on function_sections
|
// macOS has -dead_strip, which doesn't rely on function_sections
|
||||||
function_sections: false,
|
function_sections: false,
|
||||||
dynamic_linking: true,
|
dynamic_linking: true,
|
||||||
pre_link_args: pre_link_args(os, arch, abi),
|
|
||||||
families: cvs!["unix"],
|
families: cvs!["unix"],
|
||||||
is_like_osx: true,
|
is_like_osx: true,
|
||||||
// LLVM notes that macOS 10.11+ and iOS 9+ default
|
// LLVM notes that macOS 10.11+ and iOS 9+ default
|
||||||
|
Loading…
Reference in New Issue
Block a user