Auto merge of #129369 - madsmtm:apple-cc-linker-pass-target, r=jieyouxu

Pass deployment target when linking with CC on Apple targets

This PR effectively implements what's also being considered in the `cc` crate [here](https://github.com/rust-lang/cc-rs/issues/1030#issuecomment-2051020649), that is:
- When linking macOS targets with CC, pass the `-mmacosx-version-min=.` option to specify the desired deployment target. Also, no longer pass `-m32`/`-m64`, these are redundant since we already pass `-arch`.
- When linking with CC on iOS, tvOS, watchOS and visionOS, only pass `-target` (we assume for these targets that CC forwards to Clang).

This is required to get the linker to emit the correct `LC_BUILD_VERSION` of the final binary. See https://github.com/rust-lang/rust/issues/129432 for more motivation behind this change.

r? compiler

CC `@BlackHoleFox`
This commit is contained in:
bors 2024-09-12 06:57:38 +00:00
commit 7c7372b6a1
5 changed files with 38 additions and 16 deletions

View File

@ -168,7 +168,26 @@ fn pre_link_args(os: &'static str, arch: Arch, abi: TargetAbi) -> LinkArgs {
["-platform_version".into(), platform_name, min_version, sdk_version].into_iter(), ["-platform_version".into(), platform_name, min_version, sdk_version].into_iter(),
); );
if abi != TargetAbi::MacCatalyst { // 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 // CC forwards the `-arch` to the linker, so we use the same value
// here intentionally. // here intentionally.
add_link_args( add_link_args(
@ -176,6 +195,15 @@ fn pre_link_args(os: &'static str, arch: Arch, abi: TargetAbi) -> LinkArgs {
LinkerFlavor::Darwin(Cc::Yes, Lld::No), LinkerFlavor::Darwin(Cc::Yes, Lld::No),
&["-arch", arch.ld_arch()], &["-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 { } else {
add_link_args_iter( add_link_args_iter(
&mut args, &mut args,

View File

@ -1,10 +1,8 @@
use crate::spec::base::apple::{base, Arch, TargetAbi}; use crate::spec::base::apple::{base, Arch, TargetAbi};
use crate::spec::{Cc, FramePointer, LinkerFlavor, Lld, Target, TargetOptions}; use crate::spec::{FramePointer, Target, TargetOptions};
pub(crate) fn target() -> Target { pub(crate) fn target() -> Target {
let (mut opts, llvm_target, arch) = base("macos", Arch::I686, TargetAbi::Normal); let (opts, llvm_target, arch) = base("macos", Arch::I686, TargetAbi::Normal);
opts.add_pre_link_args(LinkerFlavor::Darwin(Cc::Yes, Lld::No), &["-m32"]);
Target { Target {
llvm_target, llvm_target,
metadata: crate::spec::TargetMetadata { metadata: crate::spec::TargetMetadata {

View File

@ -1,9 +1,8 @@
use crate::spec::base::apple::{base, Arch, TargetAbi}; use crate::spec::base::apple::{base, Arch, TargetAbi};
use crate::spec::{Cc, FramePointer, LinkerFlavor, Lld, SanitizerSet, Target, TargetOptions}; use crate::spec::{FramePointer, SanitizerSet, Target, TargetOptions};
pub(crate) fn target() -> Target { pub(crate) fn target() -> Target {
let (mut opts, llvm_target, arch) = base("macos", Arch::X86_64, TargetAbi::Normal); let (opts, llvm_target, arch) = base("macos", Arch::X86_64, TargetAbi::Normal);
opts.add_pre_link_args(LinkerFlavor::Darwin(Cc::Yes, Lld::No), &["-m64"]);
Target { Target {
llvm_target, llvm_target,
metadata: crate::spec::TargetMetadata { metadata: crate::spec::TargetMetadata {

View File

@ -1,11 +1,10 @@
use crate::spec::base::apple::{base, Arch, TargetAbi}; use crate::spec::base::apple::{base, Arch, TargetAbi};
use crate::spec::{Cc, FramePointer, LinkerFlavor, Lld, SanitizerSet, Target, TargetOptions}; use crate::spec::{FramePointer, SanitizerSet, Target, TargetOptions};
pub(crate) fn target() -> Target { pub(crate) fn target() -> Target {
let (mut opts, llvm_target, arch) = base("macos", Arch::X86_64h, TargetAbi::Normal); let (mut opts, llvm_target, arch) = base("macos", Arch::X86_64h, TargetAbi::Normal);
opts.max_atomic_width = Some(128); opts.max_atomic_width = Some(128);
opts.frame_pointer = FramePointer::Always; opts.frame_pointer = FramePointer::Always;
opts.add_pre_link_args(LinkerFlavor::Darwin(Cc::Yes, Lld::No), &["-m64"]);
opts.supported_sanitizers = opts.supported_sanitizers =
SanitizerSet::ADDRESS | SanitizerSet::CFI | SanitizerSet::LEAK | SanitizerSet::THREAD; SanitizerSet::ADDRESS | SanitizerSet::CFI | SanitizerSet::LEAK | SanitizerSet::THREAD;

View File

@ -81,9 +81,8 @@ fn main() {
rustc().env(env_var, example_version).run(); rustc().env(env_var, example_version).run();
minos("libfoo.dylib", example_version); minos("libfoo.dylib", example_version);
// FIXME(madsmtm): Deployment target is not currently passed properly to linker rustc().env_remove(env_var).run();
// rustc().env_remove(env_var).run(); minos("libfoo.dylib", default_version);
// minos("libfoo.dylib", default_version);
// Test with ld64 instead // Test with ld64 instead
@ -110,9 +109,8 @@ fn main() {
rustc().env(env_var, example_version).run(); rustc().env(env_var, example_version).run();
minos("foo", example_version); minos("foo", example_version);
// FIXME(madsmtm): Deployment target is not currently passed properly to linker rustc().env_remove(env_var).run();
// rustc().env_remove(env_var).run(); minos("foo", default_version);
// minos("foo", default_version);
} }
// Test with ld64 instead // Test with ld64 instead