rustc_target/spec/base/apple/
mod.rs

1use std::borrow::Cow;
2use std::env;
3
4use crate::spec::{
5    Cc, DebuginfoKind, FloatAbi, FramePointer, LinkerFlavor, Lld, RustcAbi, SplitDebuginfo,
6    StackProbeType, StaticCow, TargetOptions, cvs,
7};
8
9#[cfg(test)]
10mod tests;
11
12use Arch::*;
13#[allow(non_camel_case_types)]
14#[derive(Copy, Clone, PartialEq)]
15pub(crate) enum Arch {
16    Armv7k,
17    Armv7s,
18    Arm64,
19    Arm64e,
20    Arm64_32,
21    I386,
22    I686,
23    X86_64,
24    X86_64h,
25}
26
27impl Arch {
28    fn target_name(self) -> &'static str {
29        match self {
30            Armv7k => "armv7k",
31            Armv7s => "armv7s",
32            Arm64 => "arm64",
33            Arm64e => "arm64e",
34            Arm64_32 => "arm64_32",
35            I386 => "i386",
36            I686 => "i686",
37            X86_64 => "x86_64",
38            X86_64h => "x86_64h",
39        }
40    }
41
42    pub(crate) fn target_arch(self) -> Cow<'static, str> {
43        Cow::Borrowed(match self {
44            Armv7k | Armv7s => "arm",
45            Arm64 | Arm64e | Arm64_32 => "aarch64",
46            I386 | I686 => "x86",
47            X86_64 | X86_64h => "x86_64",
48        })
49    }
50
51    fn target_cpu(self, abi: TargetAbi) -> &'static str {
52        match self {
53            Armv7k => "cortex-a8",
54            Armv7s => "swift", // iOS 10 is only supported on iPhone 5 or higher.
55            Arm64 => match abi {
56                TargetAbi::Normal => "apple-a7",
57                TargetAbi::Simulator => "apple-a12",
58                TargetAbi::MacCatalyst => "apple-a12",
59            },
60            Arm64e => "apple-a12",
61            Arm64_32 => "apple-s4",
62            // Only macOS 10.12+ is supported, which means
63            // all x86_64/x86 CPUs must be running at least penryn
64            // https://github.com/llvm/llvm-project/blob/01f924d0e37a5deae51df0d77e10a15b63aa0c0f/clang/lib/Driver/ToolChains/Arch/X86.cpp#L79-L82
65            I386 | I686 => "penryn",
66            X86_64 => "penryn",
67            // Note: `core-avx2` is slightly more advanced than `x86_64h`, see
68            // comments (and disabled features) in `x86_64h_apple_darwin` for
69            // details. It is a higher baseline then `penryn` however.
70            X86_64h => "core-avx2",
71        }
72    }
73
74    fn stack_probes(self) -> StackProbeType {
75        match self {
76            Armv7k | Armv7s => StackProbeType::None,
77            Arm64 | Arm64e | Arm64_32 | I386 | I686 | X86_64 | X86_64h => StackProbeType::Inline,
78        }
79    }
80}
81
82#[derive(Copy, Clone, PartialEq)]
83pub(crate) enum TargetAbi {
84    Normal,
85    Simulator,
86    MacCatalyst,
87}
88
89impl TargetAbi {
90    fn target_abi(self) -> &'static str {
91        match self {
92            Self::Normal => "",
93            Self::MacCatalyst => "macabi",
94            Self::Simulator => "sim",
95        }
96    }
97}
98
99/// Get the base target options, unversioned LLVM target and `target_arch` from the three
100/// things that uniquely identify Rust's Apple targets: The OS, the architecture, and the ABI.
101pub(crate) fn base(
102    os: &'static str,
103    arch: Arch,
104    abi: TargetAbi,
105) -> (TargetOptions, StaticCow<str>, StaticCow<str>) {
106    let mut opts = TargetOptions {
107        abi: abi.target_abi().into(),
108        llvm_floatabi: Some(FloatAbi::Hard),
109        os: os.into(),
110        cpu: arch.target_cpu(abi).into(),
111        link_env_remove: link_env_remove(os),
112        vendor: "apple".into(),
113        linker_flavor: LinkerFlavor::Darwin(Cc::Yes, Lld::No),
114        // macOS has -dead_strip, which doesn't rely on function_sections
115        function_sections: false,
116        dynamic_linking: true,
117        families: cvs!["unix"],
118        is_like_osx: true,
119        // LLVM notes that macOS 10.11+ and iOS 9+ default
120        // to v4, so we do the same.
121        // https://github.com/llvm/llvm-project/blob/378778a0d10c2f8d5df8ceff81f95b6002984a4b/clang/lib/Driver/ToolChains/Darwin.cpp#L1203
122        default_dwarf_version: 4,
123        frame_pointer: FramePointer::Always,
124        has_rpath: true,
125        dll_suffix: ".dylib".into(),
126        archive_format: "darwin".into(),
127        // Thread locals became available with iOS 8 and macOS 10.7,
128        // and both are far below our minimum.
129        has_thread_local: true,
130        abi_return_struct_as_int: true,
131        emit_debug_gdb_scripts: false,
132        eh_frame_header: false,
133        stack_probes: arch.stack_probes(),
134
135        debuginfo_kind: DebuginfoKind::DwarfDsym,
136        // The historical default for macOS targets is to run `dsymutil` which
137        // generates a packed version of debuginfo split from the main file.
138        split_debuginfo: SplitDebuginfo::Packed,
139        supported_split_debuginfo: Cow::Borrowed(&[
140            SplitDebuginfo::Packed,
141            SplitDebuginfo::Unpacked,
142            SplitDebuginfo::Off,
143        ]),
144
145        // This environment variable is pretty magical but is intended for
146        // producing deterministic builds. This was first discovered to be used
147        // by the `ar` tool as a way to control whether or not mtime entries in
148        // the archive headers were set to zero or not. It appears that
149        // eventually the linker got updated to do the same thing and now reads
150        // this environment variable too in recent versions.
151        //
152        // For some more info see the commentary on #47086
153        link_env: Cow::Borrowed(&[(Cow::Borrowed("ZERO_AR_DATE"), Cow::Borrowed("1"))]),
154
155        ..Default::default()
156    };
157    if matches!(arch, Arch::I386 | Arch::I686) {
158        // All Apple x86-32 targets have SSE2.
159        opts.rustc_abi = Some(RustcAbi::X86Sse2);
160    }
161    (opts, unversioned_llvm_target(os, arch, abi), arch.target_arch())
162}
163
164/// Generate part of the LLVM target triple.
165///
166/// See `rustc_codegen_ssa::back::versioned_llvm_target` for the full triple passed to LLVM and
167/// Clang.
168fn unversioned_llvm_target(os: &str, arch: Arch, abi: TargetAbi) -> StaticCow<str> {
169    let arch = arch.target_name();
170    // Convert to the "canonical" OS name used by LLVM:
171    // https://github.com/llvm/llvm-project/blob/llvmorg-18.1.8/llvm/lib/TargetParser/Triple.cpp#L236-L282
172    let os = match os {
173        "macos" => "macosx",
174        "ios" => "ios",
175        "watchos" => "watchos",
176        "tvos" => "tvos",
177        "visionos" => "xros",
178        _ => unreachable!("tried to get LLVM target OS for non-Apple platform"),
179    };
180    let environment = match abi {
181        TargetAbi::Normal => "",
182        TargetAbi::MacCatalyst => "-macabi",
183        TargetAbi::Simulator => "-simulator",
184    };
185    format!("{arch}-apple-{os}{environment}").into()
186}
187
188fn link_env_remove(os: &'static str) -> StaticCow<[StaticCow<str>]> {
189    // Apple platforms only officially support macOS as a host for any compilation.
190    //
191    // If building for macOS, we go ahead and remove any erroneous environment state
192    // that's only applicable to cross-OS compilation. Always leave anything for the
193    // host OS alone though.
194    if os == "macos" {
195        let mut env_remove = Vec::with_capacity(2);
196        // Remove the `SDKROOT` environment variable if it's clearly set for the wrong platform, which
197        // may occur when we're linking a custom build script while targeting iOS for example.
198        if let Ok(sdkroot) = env::var("SDKROOT") {
199            if sdkroot.contains("iPhoneOS.platform")
200                || sdkroot.contains("iPhoneSimulator.platform")
201                || sdkroot.contains("AppleTVOS.platform")
202                || sdkroot.contains("AppleTVSimulator.platform")
203                || sdkroot.contains("WatchOS.platform")
204                || sdkroot.contains("WatchSimulator.platform")
205                || sdkroot.contains("XROS.platform")
206                || sdkroot.contains("XRSimulator.platform")
207            {
208                env_remove.push("SDKROOT".into())
209            }
210        }
211        // Additionally, `IPHONEOS_DEPLOYMENT_TARGET` must not be set when using the Xcode linker at
212        // "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld",
213        // although this is apparently ignored when using the linker at "/usr/bin/ld".
214        env_remove.push("IPHONEOS_DEPLOYMENT_TARGET".into());
215        env_remove.push("TVOS_DEPLOYMENT_TARGET".into());
216        env_remove.push("XROS_DEPLOYMENT_TARGET".into());
217        env_remove.into()
218    } else {
219        // Otherwise if cross-compiling for a different OS/SDK (including Mac Catalyst), remove any part
220        // of the linking environment that's wrong and reversed.
221        cvs!["MACOSX_DEPLOYMENT_TARGET"]
222    }
223}