Skip to content

Commit 9e37d1b

Browse files
committed
Pass SDK version to ld64 when linking
This is an important property of the binary that we were previously setting incorrectly, and is a big step towards making binaries linked with cc and ld64 equal.
1 parent 97632ad commit 9e37d1b

File tree

5 files changed

+90
-34
lines changed

5 files changed

+90
-34
lines changed

compiler/rustc_codegen_ssa/messages.ftl

+8
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ codegen_ssa_apple_sdk_error_missing_developer_dir =
4545
- `{$sdkroot}`
4646
- `{$sdkroot_bare}`
4747
48+
codegen_ssa_apple_sdk_error_missing_mac_catalyst_version =
49+
failed to find {$version} in SDK version map key `macOS_iOSMac`.
50+
51+
This is probably a bug in your SDK.
52+
4853
codegen_ssa_apple_sdk_error_missing_sdk_settings =
4954
failed finding `SDKSettings.json` or `SDKSettings.plist` in `{$sdkroot}`.
5055
@@ -62,6 +67,9 @@ codegen_ssa_apple_sdk_error_missing_xcode_select =
6267
6368
Consider using `sudo xcode-select --switch path/to/Xcode.app` or `sudo xcode-select --reset` to select a valid path.
6469
70+
codegen_ssa_apple_sdk_error_must_have_when_using_ld64 =
71+
must know the SDK when linking with ld64
72+
6573
codegen_ssa_apple_sdk_error_not_sdk_path =
6674
failed parsing SDK at `{$path}`.
6775

compiler/rustc_codegen_ssa/src/apple.rs

+37-1
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,6 @@ pub(crate) struct SDKSettings {
281281
canonical_name: String,
282282

283283
#[serde(rename = "Version")]
284-
#[allow(dead_code)]
285284
version: AppleOSVersion,
286285

287286
#[serde(rename = "MaximumDeploymentTarget")]
@@ -293,6 +292,13 @@ pub(crate) struct SDKSettings {
293292
/// Optional to support `SDKSettings.json` converted from an older `SDKSettings.plist`.
294293
#[serde(rename = "SupportedTargets")]
295294
supported_targets: Option<BTreeMap<String, SupportedTargets>>,
295+
296+
/// Optional to support `SDKSettings.json` converted from an older `SDKSettings.plist`.
297+
///
298+
/// Could in general be useful in the future for building "zippered" binaries, see:
299+
/// <https://github.com/rust-lang/rust/issues/131216>
300+
#[serde(rename = "VersionMap")]
301+
version_map: Option<BTreeMap<String, BTreeMap<AppleOSVersion, AppleOSVersion>>>,
296302
}
297303

298304
impl SDKSettings {
@@ -326,6 +332,7 @@ impl SDKSettings {
326332
},
327333
default_properties: DefaultProperties::default(),
328334
supported_targets: None,
335+
version_map: None,
329336
})
330337
}
331338

@@ -371,6 +378,35 @@ impl SDKSettings {
371378
.unwrap_or(Path::new("/System/iOSSupport"))
372379
}
373380

381+
/// The version of the SDK.
382+
///
383+
/// This is needed by the linker.
384+
///
385+
/// settings["Version"] or settings["VersionMap"]["macOS_iOSMac"][settings["Version"]].
386+
pub(crate) fn sdk_version(&self, target: &Target, sdkroot: &Path) -> Result<AppleOSVersion, AppleSdkError> {
387+
if target.abi == "macabi" {
388+
let map = self
389+
.version_map
390+
.as_ref()
391+
.ok_or(AppleSdkError::SdkDoesNotSupportOS {
392+
sdkroot: sdkroot.to_owned(),
393+
os: target.os.clone(),
394+
abi: target.abi.clone(),
395+
})?
396+
.get("macOS_iOSMac")
397+
.ok_or(AppleSdkError::SdkDoesNotSupportOS {
398+
sdkroot: sdkroot.to_owned(),
399+
os: target.os.clone(),
400+
abi: target.abi.clone(),
401+
})?;
402+
map.get(&self.version).cloned().ok_or(AppleSdkError::MissingMacCatalystVersion {
403+
version: self.version.pretty().to_string(),
404+
})
405+
} else {
406+
Ok(self.version)
407+
}
408+
}
409+
374410
fn supports_target(&self, target: &Target, sdkroot: &Path) -> Result<(), AppleSdkError> {
375411
let arch = ld64_arch(target);
376412

compiler/rustc_codegen_ssa/src/back/link.rs

+38-23
Original file line numberDiff line numberDiff line change
@@ -2410,10 +2410,10 @@ fn add_order_independent_options(
24102410
// Take care of the flavors and CLI options requesting the `lld` linker.
24112411
add_lld_args(cmd, sess, flavor, self_contained_components);
24122412

2413-
add_apple_link_args(cmd, sess, flavor);
2414-
24152413
let apple_sdk_data = add_apple_sdk(cmd, sess, crate_type, flavor);
24162414

2415+
add_apple_link_args(cmd, sess, flavor, &apple_sdk_data);
2416+
24172417
add_link_script(cmd, sess, tmpdir, crate_type);
24182418

24192419
if sess.target.os == "fuchsia"
@@ -2970,7 +2970,12 @@ pub(crate) fn are_upstream_rust_objects_already_included(sess: &Session) -> bool
29702970
/// - The environment / ABI.
29712971
/// - The deployment target.
29722972
/// - The SDK version.
2973-
fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
2973+
fn add_apple_link_args(
2974+
cmd: &mut dyn Linker,
2975+
sess: &Session,
2976+
flavor: LinkerFlavor,
2977+
settings: &Option<(PathBuf, SDKSettings)>,
2978+
) {
29742979
if !sess.target.is_like_osx {
29752980
return;
29762981
}
@@ -3030,31 +3035,41 @@ fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavo
30303035
// `-[NSView wantsBestResolutionOpenGLSurface]` is `YES` when the SDK version is >= 10.15.
30313036
// <https://developer.apple.com/documentation/appkit/nsview/1414938-wantsbestresolutionopenglsurface?language=objc>
30323037
//
3033-
// We do not currently know the actual SDK version though, so we have a few options:
3034-
// 1. Use the minimum version supported by rustc.
3035-
// 2. Use the same as the deployment target.
3036-
// 3. Use an arbitary recent version.
3037-
// 4. Omit the version.
3038+
// So it is important that we pass the correct version here.
30383039
//
3039-
// The first option is too low / too conservative, and means that users will not get the
3040-
// same behaviour from a binary compiled with rustc as with one compiled by clang.
30413040
//
3042-
// The second option is similarly conservative, and also wrong since if the user specified a
3043-
// higher deployment target than the SDK they're compiling/linking with, the runtime might
3044-
// make invalid assumptions about the capabilities of the binary.
3041+
// For posterity, insufficient alternatives are listed below:
30453042
//
3046-
// The third option requires that `rustc` is periodically kept up to date with Apple's SDK
3047-
// version, and is also wrong for similar reasons as above.
3043+
// 1. Use the minimum version supported by rustc.
3044+
// Too low / too conservative, and means that users will not get the same behaviour from
3045+
// a binary compiled with rustc as with one compiled by clang.
30483046
//
3049-
// The fourth option is bad because while `ld`, `otool`, `vtool` and such understand it to
3050-
// mean "absent" or `n/a`, dyld doesn't actually understand it, and will end up interpreting
3051-
// it as 0.0, which is again too low/conservative.
3047+
// 2. Use the same as the deployment target.
3048+
// Similarly conservative, and also wrong since if the user specified a higher deployment
3049+
// target than the SDK they're compiling/linking with, the runtime might make invalid
3050+
// assumptions about the capabilities of the binary.
30523051
//
3053-
// Currently, we lie about the SDK version, and choose the second option.
3052+
// 3. Use an arbitary recent version.
3053+
// Requires that `rustc` is periodically kept up to date with Apple's SDK version, and is
3054+
// also wrong for similar reasons as above.
30543055
//
3055-
// FIXME(madsmtm): Parse the SDK version from the SDK root instead.
3056-
// <https://github.com/rust-lang/rust/issues/129432>
3057-
let sdk_version = &*min_version;
3056+
// 4. Omit the version.
3057+
// Bad because while `ld`, `otool`, `vtool` and such understand it to mean "absent" or
3058+
// `n/a`, dyld doesn't actually understand it, and will end up interpreting it as 0.0,
3059+
// which is again too low/conservative.
3060+
let AppleOSVersion { major, minor, patch } = if let Some((sdkroot, settings)) = settings {
3061+
settings.sdk_version(&sess.target, &sdkroot).unwrap_or_else(|err| {
3062+
sess.dcx().emit_err(err);
3063+
AppleOSVersion::MAX
3064+
})
3065+
} else {
3066+
// If the SDK wasn't read properly, we may have errored already, but we may also only
3067+
// have given a warning to support `zig cc` on non-macOS hosts. `ld64` requires the SDK
3068+
// version though, so we must actually error here.
3069+
sess.dcx().emit_err(errors::AppleSdkError::MustHaveWhenUsingLd64);
3070+
AppleOSVersion::MAX
3071+
};
3072+
let sdk_version = format!("{major}.{minor}.{patch}");
30583073

30593074
// From the man page for ld64 (`man ld`):
30603075
// > This is set to indicate the platform, oldest supported version of
@@ -3064,7 +3079,7 @@ fn add_apple_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavo
30643079
// Like with `-arch`, the linker can figure out the platform versions
30653080
// itself from the binaries being linked, but to be safe, we specify
30663081
// the desired versions here explicitly.
3067-
cmd.link_args(&["-platform_version", platform_name, &*min_version, sdk_version]);
3082+
cmd.link_args(&["-platform_version", platform_name, &*min_version, &*sdk_version]);
30683083
} else {
30693084
// cc == Cc::Yes
30703085
//

compiler/rustc_codegen_ssa/src/errors.rs

+6
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,9 @@ pub(crate) enum AppleSdkError {
565565
#[diag(codegen_ssa_apple_sdk_error_missing_developer_dir)]
566566
MissingDeveloperDir { dir: PathBuf, sdkroot: PathBuf, sdkroot_bare: PathBuf },
567567

568+
#[diag(codegen_ssa_apple_sdk_error_missing_mac_catalyst_version)]
569+
MissingMacCatalystVersion { version: String },
570+
568571
#[diag(codegen_ssa_apple_sdk_error_missing_sdk_settings)]
569572
MissingSDKSettings { sdkroot: PathBuf },
570573

@@ -574,6 +577,9 @@ pub(crate) enum AppleSdkError {
574577
#[diag(codegen_ssa_apple_sdk_error_missing_xcode_select)]
575578
MissingXcodeSelect { dir: PathBuf, sdkroot: PathBuf, sdkroot_bare: PathBuf },
576579

580+
#[diag(codegen_ssa_apple_sdk_error_must_have_when_using_ld64)]
581+
MustHaveWhenUsingLd64,
582+
577583
#[diag(codegen_ssa_apple_sdk_error_not_sdk_path)]
578584
NotSdkPath { sdkroot: PathBuf },
579585

tests/run-make/apple-sdk-version/rmake.rs

+1-10
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,6 @@ fn has_sdk_version(file: &str, version: &str) {
2323
}
2424

2525
fn main() {
26-
// Fetch rustc's inferred deployment target.
27-
let current_deployment_target =
28-
rustc().target(target()).print("deployment-target").run().stdout_utf8();
29-
let current_deployment_target =
30-
current_deployment_target.strip_prefix("deployment_target=").unwrap().trim();
31-
3226
// Fetch current SDK version via. xcrun.
3327
//
3428
// Assumes a standard Xcode distribution, where e.g. the macOS SDK's Mac Catalyst
@@ -87,9 +81,6 @@ fn main() {
8781
.input("foo.rs")
8882
.output(&file_name)
8983
.run();
90-
// FIXME(madsmtm): This uses the current deployment target
91-
// instead of the current SDK version like Clang does.
92-
// https://github.com/rust-lang/rust/issues/129432
93-
has_sdk_version(&file_name, current_deployment_target);
84+
has_sdk_version(&file_name, current_sdk_version);
9485
}
9586
}

0 commit comments

Comments
 (0)