Skip to content

macOS deployment target defaults differ between rustc, cc-rs and clang #136113

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
Fulgen301 opened this issue Jan 27, 2025 · 3 comments
Open

macOS deployment target defaults differ between rustc, cc-rs and clang #136113

Fulgen301 opened this issue Jan 27, 2025 · 3 comments
Assignees
Labels
A-linkage Area: linking into static, shared libraries and binaries C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC O-apple Operating system: Apple (macOS, iOS, tvOS, visionOS, watchOS) T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue.

Comments

@Fulgen301
Copy link
Contributor

Fulgen301 commented Jan 27, 2025

When MACOSX_DEPLOYMENT_TARGET is not set, rustc defaults to macOS 10.12 for Intel targets and 11.0 for aarch64 targets:

"macos" => (10, 12, 0),

("macos", "aarch64", _) => (11, 0, 0),

However, this does not match the default behavior for clang, which defaults to the selected SDK versions; unless changed otherwise, a C program built with clang uses the macOS host version as deployment target. The cc-rs crate also defaults to this behavior.

This difference in behavior causes issues when a Rust program has a dependency on a C library and does not set a minimum deployment target: The Rust binary tells the linker to target 10.12 / 11.0, but as the C library requires a higher minimum deployment target, a warning is emitted and the binary ends up targeting the higher version. This was recently observed with miri, which is built in a macOS 14 GitHub Runner:

ld: object file (/Users/runner/work/miri/miri/target/release/deps/libtikv_jemalloc_sys-80d1ddce8807bb5c.rlib[11](buf_writer.pic.o)) was built for newer 'macOS' version (14.5) than being linked (11.0)

In this case, miri does not have a deployment target set, so rustc tells the linker to emit a binary targeting 10.12; however, jemalloc-sys built jemalloc using clang's defaults, so it ended up targeting 14.5.

In a perfect world, projects would always set a deployment target and the linker would actually enforce that binaries cannot depend on objects that require a higher minimum macOS version instead of emitting a warning and targeting the higher version; alas, we're stuck with a warning with a not-so-great message. However, as incomprehensible the message may be, it is still indicative of a potential bug, especially if the user told the Rust part to create a binary targeting, say, 11.0, but the project then accidentally pulls in a dependency that targets 13.0 - the resulting binary would be created for 13.0 and might not run on 11.0 at all. Unfortunately, rustc can't not set a default deployment target by itself if the user hasn't specified one - it invokes the linker and has to pass it a deployment version; though it could compute the highest minimum version of all objects files it passes to the linker.

(@madsmtm has indicated wanting to change cc-rs's defaults to match rustc's behavior, so that a Rust program building a C library via cc would not run into a deployment target mismatch; without such a change, it's too likely to be a false positive.)

@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Jan 27, 2025
@madsmtm
Copy link
Contributor

madsmtm commented Jan 27, 2025

@rustbot label O-apple
@rustbot claim

@rustbot rustbot added the O-apple Operating system: Apple (macOS, iOS, tvOS, visionOS, watchOS) label Jan 27, 2025
@saethlin saethlin added T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue. A-linkage Area: linking into static, shared libraries and binaries and removed needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. labels Jan 27, 2025
@jieyouxu jieyouxu added the C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC label Jan 27, 2025
@jyn514
Copy link
Member

jyn514 commented Jan 27, 2025

#136096 (comment)

Why should "built for newer 'macOS' version than being linked" be silenced? My instinct is that indicates libraries are being built with the wrong macos deployment target.

@ehuss I agree this indicates a mismatch. But right now, the mismatch is usually because cc-rs uses a different default than rustc, not because someone has manually set MACOSX_DEPLOYMENT_TARGET . So there will be a high incidence of unactionable warnings. I think we should silence these warnings until the defaults match to avoid alert fatigue.

That said I don't know a lot about deployment targets and I defer to @Fulgen301 and @madsmtm's judgement.

@madsmtm
Copy link
Contributor

madsmtm commented Jan 27, 2025

Yes, we should silence them by default until cc-rs is fixed (just to note: I've assigned myself to the cc-rs part, but I'm probably not gonna be doing the silencing).

bors added a commit to rust-lang-ci/rust that referenced this issue Feb 5, 2025
… r=<try>

Always set the deployment target when building std

`cc` has [a bug/feature](rust-lang/cc-rs#1171) (I guess depending on how you look at it) where the default deployment target is taken from the SDK instead of from `rustc`. This causes `compiler-builtins` to build `compiler-rt` with the wrong deployment target on iOS.

I've been meaning to change how `cc` works in this regard, but that's a lengthy process, so let's fix it in bootstrap for now.

The behaviour can be seen locally with `./x build library --set build.optimized-compiler-builtins=true` for various target triples, and then inspecting with `otool -l build/host/stage1/lib/rustlib/*/lib/libcompiler_builtins-*.rlib | rg 'minos|version'`. I have added a rmake test that ensures that we now have the same version everywhere.

Fixes rust-lang#128419
Fixes rust-lang/compiler-builtins#650
Fixes rust-lang#136523
See also rust-lang/cargo#13115, rust-lang/cc-rs#1171, rust-lang#136113
See rust-lang#133092 (comment) for a description of how the change works.

try-job: i686-gnu
try-job: x86_64-apple-1
try-job: aarch64-apple
try-job: dist-apple-various
try-job: dist-aarch64-apple
bors added a commit to rust-lang-ci/rust that referenced this issue Feb 9, 2025
… r=Mark-Simulacrum

Always set the deployment target when building std

`cc` has [a bug/feature](rust-lang/cc-rs#1171) (I guess depending on how you look at it) where the default deployment target is taken from the SDK instead of from `rustc`. This causes `compiler-builtins` to build `compiler-rt` with the wrong deployment target on iOS.

I've been meaning to change how `cc` works in this regard, but that's a lengthy process, so let's fix it in bootstrap for now.

The behaviour can be seen locally with `./x build library --set build.optimized-compiler-builtins=true` for various target triples, and then inspecting with `otool -l build/host/stage1/lib/rustlib/*/lib/libcompiler_builtins-*.rlib | rg 'minos|version'`. I have added a rmake test that ensures that we now have the same version everywhere.

Fixes rust-lang#128419
Fixes rust-lang/compiler-builtins#650
Fixes rust-lang#136523
See also rust-lang/cargo#13115, rust-lang/cc-rs#1171, rust-lang#136113
See rust-lang#133092 (comment) for a description of how the change works.

try-job: i686-gnu
try-job: x86_64-apple-1
try-job: aarch64-apple
try-job: dist-apple-various
try-job: dist-aarch64-apple
workingjubilee added a commit to workingjubilee/rustc that referenced this issue Feb 10, 2025
…t, r=Mark-Simulacrum

Always set the deployment target when building std

`cc` has [a bug/feature](rust-lang/cc-rs#1171) (I guess depending on how you look at it) where the default deployment target is taken from the SDK instead of from `rustc`. This causes `compiler-builtins` to build `compiler-rt` with the wrong deployment target on iOS.

I've been meaning to change how `cc` works in this regard, but that's a lengthy process, so let's fix it in bootstrap for now.

The behaviour can be seen locally with `./x build library --set build.optimized-compiler-builtins=true` for various target triples, and then inspecting with `otool -l build/host/stage1/lib/rustlib/*/lib/libcompiler_builtins-*.rlib | rg 'minos|version'`. I have added a rmake test that ensures that we now have the same version everywhere.

Fixes rust-lang#128419
Fixes rust-lang/compiler-builtins#650
Fixes rust-lang#136523
See also rust-lang/cargo#13115, rust-lang/cc-rs#1171, rust-lang#136113
See rust-lang#133092 (comment) for a description of how the change works.

try-job: i686-gnu
try-job: x86_64-apple-1
try-job: aarch64-apple
try-job: dist-apple-various
try-job: dist-aarch64-apple
bors added a commit to rust-lang-ci/rust that referenced this issue Feb 10, 2025
… r=<try>

Always set the deployment target when building std

`cc` has [a bug/feature](rust-lang/cc-rs#1171) (I guess depending on how you look at it) where the default deployment target is taken from the SDK instead of from `rustc`. This causes `compiler-builtins` to build `compiler-rt` with the wrong deployment target on iOS.

I've been meaning to change how `cc` works in this regard, but that's a lengthy process, so let's fix it in bootstrap for now.

The behaviour can be seen locally with `./x build library --set build.optimized-compiler-builtins=true` for various target triples, and then inspecting with `otool -l build/host/stage1/lib/rustlib/*/lib/libcompiler_builtins-*.rlib | rg 'minos|version'`. I have added a rmake test that ensures that we now have the same version everywhere.

Fixes rust-lang#128419
Fixes rust-lang/compiler-builtins#650
Fixes rust-lang#136523
See also rust-lang/cargo#13115, rust-lang/cc-rs#1171, rust-lang#136113
See rust-lang#133092 (comment) for a description of how the change works.

try-job: i686-gnu
try-job: x86_64-apple-1
try-job: aarch64-apple
try-job: dist-apple-various
try-job: dist-aarch64-apple
try-job: dist-various-2
try-job: x86_64-fuchsia
bors added a commit to rust-lang-ci/rust that referenced this issue Feb 10, 2025
… r=<try>

Always set the deployment target when building std

`cc` has [a bug/feature](rust-lang/cc-rs#1171) (I guess depending on how you look at it) where the default deployment target is taken from the SDK instead of from `rustc`. This causes `compiler-builtins` to build `compiler-rt` with the wrong deployment target on iOS.

I've been meaning to change how `cc` works in this regard, but that's a lengthy process, so let's fix it in bootstrap for now.

The behaviour can be seen locally with `./x build library --set build.optimized-compiler-builtins=true` for various target triples, and then inspecting with `otool -l build/host/stage1/lib/rustlib/*/lib/libcompiler_builtins-*.rlib | rg 'minos|version'`. I have added a rmake test that ensures that we now have the same version everywhere.

Fixes rust-lang#128419
Fixes rust-lang/compiler-builtins#650
Fixes rust-lang#136523
See also rust-lang/cargo#13115, rust-lang/cc-rs#1171, rust-lang#136113
See rust-lang#133092 (comment) for a description of how the change works.

try-job: i686-gnu-1
try-job: i686-gnu-2
try-job: x86_64-apple-1
try-job: aarch64-apple
try-job: dist-apple-various
try-job: dist-aarch64-apple
try-job: dist-various-2
try-job: x86_64-fuchsia
bors added a commit to rust-lang-ci/rust that referenced this issue Feb 10, 2025
… r=Mark-Simulacrum,jieyouxu

Always set the deployment target when building std

`cc` has [a bug/feature](rust-lang/cc-rs#1171) (I guess depending on how you look at it) where the default deployment target is taken from the SDK instead of from `rustc`. This causes `compiler-builtins` to build `compiler-rt` with the wrong deployment target on iOS.

I've been meaning to change how `cc` works in this regard, but that's a lengthy process, so let's fix it in bootstrap for now.

The behaviour can be seen locally with `./x build library --set build.optimized-compiler-builtins=true` for various target triples, and then inspecting with `otool -l build/host/stage1/lib/rustlib/*/lib/libcompiler_builtins-*.rlib | rg 'minos|version'`. I have added a rmake test that ensures that we now have the same version everywhere.

Fixes rust-lang#128419
Fixes rust-lang/compiler-builtins#650
Fixes rust-lang#136523
See also rust-lang/cargo#13115, rust-lang/cc-rs#1171, rust-lang#136113
See rust-lang#133092 (comment) for a description of how the change works.

try-job: i686-gnu-1
try-job: i686-gnu-2
try-job: x86_64-apple-1
try-job: aarch64-apple
try-job: dist-apple-various
try-job: dist-aarch64-apple
try-job: dist-various-2
try-job: x86_64-fuchsia
github-actions bot pushed a commit to rust-lang/miri that referenced this issue Feb 11, 2025
…imulacrum,jieyouxu

Always set the deployment target when building std

`cc` has [a bug/feature](rust-lang/cc-rs#1171) (I guess depending on how you look at it) where the default deployment target is taken from the SDK instead of from `rustc`. This causes `compiler-builtins` to build `compiler-rt` with the wrong deployment target on iOS.

I've been meaning to change how `cc` works in this regard, but that's a lengthy process, so let's fix it in bootstrap for now.

The behaviour can be seen locally with `./x build library --set build.optimized-compiler-builtins=true` for various target triples, and then inspecting with `otool -l build/host/stage1/lib/rustlib/*/lib/libcompiler_builtins-*.rlib | rg 'minos|version'`. I have added a rmake test that ensures that we now have the same version everywhere.

Fixes rust-lang/rust#128419
Fixes rust-lang/compiler-builtins#650
Fixes rust-lang/rust#136523
See also rust-lang/cargo#13115, rust-lang/cc-rs#1171, rust-lang/rust#136113
See rust-lang/rust#133092 (comment) for a description of how the change works.

try-job: i686-gnu-1
try-job: i686-gnu-2
try-job: x86_64-apple-1
try-job: aarch64-apple
try-job: dist-apple-various
try-job: dist-aarch64-apple
try-job: dist-various-2
try-job: x86_64-fuchsia
github-actions bot pushed a commit to rust-lang/rustc-dev-guide that referenced this issue Feb 13, 2025
…imulacrum,jieyouxu

Always set the deployment target when building std

`cc` has [a bug/feature](rust-lang/cc-rs#1171) (I guess depending on how you look at it) where the default deployment target is taken from the SDK instead of from `rustc`. This causes `compiler-builtins` to build `compiler-rt` with the wrong deployment target on iOS.

I've been meaning to change how `cc` works in this regard, but that's a lengthy process, so let's fix it in bootstrap for now.

The behaviour can be seen locally with `./x build library --set build.optimized-compiler-builtins=true` for various target triples, and then inspecting with `otool -l build/host/stage1/lib/rustlib/*/lib/libcompiler_builtins-*.rlib | rg 'minos|version'`. I have added a rmake test that ensures that we now have the same version everywhere.

Fixes rust-lang/rust#128419
Fixes rust-lang/compiler-builtins#650
Fixes rust-lang/rust#136523
See also rust-lang/cargo#13115, rust-lang/cc-rs#1171, rust-lang/rust#136113
See rust-lang/rust#133092 (comment) for a description of how the change works.

try-job: i686-gnu-1
try-job: i686-gnu-2
try-job: x86_64-apple-1
try-job: aarch64-apple
try-job: dist-apple-various
try-job: dist-aarch64-apple
try-job: dist-various-2
try-job: x86_64-fuchsia
github-merge-queue bot pushed a commit to rust-lang/rust-analyzer that referenced this issue Feb 17, 2025
…imulacrum,jieyouxu

Always set the deployment target when building std

`cc` has [a bug/feature](rust-lang/cc-rs#1171) (I guess depending on how you look at it) where the default deployment target is taken from the SDK instead of from `rustc`. This causes `compiler-builtins` to build `compiler-rt` with the wrong deployment target on iOS.

I've been meaning to change how `cc` works in this regard, but that's a lengthy process, so let's fix it in bootstrap for now.

The behaviour can be seen locally with `./x build library --set build.optimized-compiler-builtins=true` for various target triples, and then inspecting with `otool -l build/host/stage1/lib/rustlib/*/lib/libcompiler_builtins-*.rlib | rg 'minos|version'`. I have added a rmake test that ensures that we now have the same version everywhere.

Fixes rust-lang/rust#128419
Fixes rust-lang/compiler-builtins#650
Fixes rust-lang/rust#136523
See also rust-lang/cargo#13115, rust-lang/cc-rs#1171, rust-lang/rust#136113
See rust-lang/rust#133092 (comment) for a description of how the change works.

try-job: i686-gnu-1
try-job: i686-gnu-2
try-job: x86_64-apple-1
try-job: aarch64-apple
try-job: dist-apple-various
try-job: dist-aarch64-apple
try-job: dist-various-2
try-job: x86_64-fuchsia
rust-bors bot added a commit that referenced this issue Jun 12, 2025
Add `__isPlatformVersionAtLeast` and `__isOSVersionAtLeast` symbols

## Motivation

When Objective-C code uses ``@available(...)`,` Clang inserts a call to [`__isPlatformVersionAtLeast`](https://github.com/llvm/llvm-project/blob/llvmorg-20.1.0/compiler-rt/lib/builtins/os_version_check.c#L276) (`__isOSVersionAtLeast` in older Clang versions). These symbols not being available sometimes ends up causing linker errors. See the new test `tests/run-make/apple-c-available-links` for a minimal reproducer.

The workaround is to link `libclang_rt.osx.a`, see e.g. alexcrichton/curl-rust#279. But that's very difficult for users to figure out (and the backreferences to that issue indicates that people are still running into this in their own projects every so often).

For another recent example, this is preventing `rustc` from using LLVM assertions on macOS, see #62592 (comment) and #134275 (comment).

It is also a blocker for [setting the correct minimum OS version in `cc-rs`](#136113), since fixing this in `cc-rs` might end up introducing linker errors in places where we weren't before (by default, if using e.g. ``@available(macos` 10.15, *)`, the symbol usually happens to be left out, since `clang` defaults to compiling for the host macOS version, and thus things _seem_ to work - but the availability check actually compiles down to nothing, which is a huge correctness footgun for running on older OSes).

(My super secret evil agenda is also to expose some variant of ``@available`` in Rust's `std` after rust-lang/rfcs#3750 progresses further, will probably file an ACP for this later. But I believe this PR has value regardless of those future plans, since we'd be making C/Objective-C/Swift interop easier).

## Solution

Implement `__isPlatformVersionAtLeast` and `__isOSVersionAtLeast` as part of the "public ABI" that `std` exposes.

**This is insta-stable**, in the same sense that additions to `compiler-builtins` are insta-stable, though the availability of these symbols can probably be considered a "quality of implementation" detail rather than a stable promise.

I originally proposed to implement this in `compiler-builtins`, see rust-lang/compiler-builtins#794, but we discussed moving it to `std` instead ([Zulip thread](https://rust-lang.zulipchat.com/#narrow/channel/219381-t-libs/topic/Provide.20.60__isPlatformVersionAtLeast.60.20in.20.60std.60.3F/with/507880717)), which makes the implementation substantially simpler, and we avoid gnarly issues with requiring the user to link `libSystem.dylib` (since `std` unconditionally does that).

Note that this does not solve the linker errors for (pure) `#![no_std]` users, but that's _probably_ fine, if you are using ``@available`` to test the OS version on Apple platforms, you're likely also using `std` (and it is still possible to work around by linking `libclang_rt.*.a`).

A thing to note about the implementation, I've choosen to stray a bit from LLVM's upstream implementation, and not use `_availability_version_check` since [it has problems when compiling with an older SDK](llvm/llvm-project#64227). Instead, we use `sysctl kern.osproductversion` when available to still avoid the costly PList lookup in most cases, but still with a fall back to the PList lookup when that is not available (with the PList fallback being is similar to LLVM's implementation).

## Testing

Apple has a lot of different "modes" that they can run binaries in, which can be a bit difficult to find your bearings in, but I've tried to be as thorough as I could in testing them all.

Tested using roughly the equivalent of `./x test library/std -- platform_version` on the following configurations:
- macOS 14.7.3 on a Macbook Pro M2
    - `aarch64-apple-darwin`
    - `x86_64-apple-darwin` (under Rosetta)
    - `aarch64-apple-ios-macabi`
    - `x86_64-apple-ios-macabi` (under Rosetta)
    - `aarch64-apple-ios` (using Xcode's "Designed for iPad" setting)
    - `aarch64-apple-ios-sim` (in iOS Simulator, as iPhone with iOS 17.5)
    - `aarch64-apple-ios-sim` (in iOS Simulator, as iPad with iOS 18.2)
    - `aarch64-apple-tvos-sim` (in tvOS Simulator)
    - `aarch64-apple-watchos-sim` (in watchOS Simulator)
    - `aarch64-apple-ios-sim` (in visionOS simulator, using Xcode's "Designed for iPad" setting)
    - `aarch64-apple-visionos-sim` (in visionOS Simulator)
- macOS 15.3.1 VM
    - `aarch64-apple-darwin`
    - `aarch64-apple-ios-macabi`
- macOS 10.12.6 on an Intel Macbook from 2013
    - `x86_64-apple-darwin`
    - `i686-apple-darwin`
    - `x86_64-apple-ios` (in iOS Simulator)
- iOS 9.3.6 on a 1st generation iPad Mini
    - `armv7-apple-ios` with an older compiler

Along with manually inspecting the output of `version_from_sysctl()` and `version_from_plist()`, and verifying that they actually match what's expected.

I believe the only real omissions here would be:
- `aarch64-apple-ios` on a newer iPhone that has `sysctl` available (iOS 11.4 or above).
- `aarch64-apple-ios` on a Vision Pro using Xcode's "Designed for iPad" setting.

But I don't have the hardware available to test those.

`@rustbot` label O-apple A-linkage -T-compiler -A-meta -A-run-make

try-job: aarch64-apple
try-job: x86_64-apple
rust-bors bot added a commit that referenced this issue Jun 12, 2025
Add `__isPlatformVersionAtLeast` and `__isOSVersionAtLeast` symbols

## Motivation

When Objective-C code uses ``@available(...)`,` Clang inserts a call to [`__isPlatformVersionAtLeast`](https://github.com/llvm/llvm-project/blob/llvmorg-20.1.0/compiler-rt/lib/builtins/os_version_check.c#L276) (`__isOSVersionAtLeast` in older Clang versions). These symbols not being available sometimes ends up causing linker errors. See the new test `tests/run-make/apple-c-available-links` for a minimal reproducer.

The workaround is to link `libclang_rt.osx.a`, see e.g. alexcrichton/curl-rust#279. But that's very difficult for users to figure out (and the backreferences to that issue indicates that people are still running into this in their own projects every so often).

For another recent example, this is preventing `rustc` from using LLVM assertions on macOS, see #62592 (comment) and #134275 (comment).

It is also a blocker for [setting the correct minimum OS version in `cc-rs`](#136113), since fixing this in `cc-rs` might end up introducing linker errors in places where we weren't before (by default, if using e.g. ``@available(macos` 10.15, *)`, the symbol usually happens to be left out, since `clang` defaults to compiling for the host macOS version, and thus things _seem_ to work - but the availability check actually compiles down to nothing, which is a huge correctness footgun for running on older OSes).

(My super secret evil agenda is also to expose some variant of ``@available`` in Rust's `std` after rust-lang/rfcs#3750 progresses further, will probably file an ACP for this later. But I believe this PR has value regardless of those future plans, since we'd be making C/Objective-C/Swift interop easier).

## Solution

Implement `__isPlatformVersionAtLeast` and `__isOSVersionAtLeast` as part of the "public ABI" that `std` exposes.

**This is insta-stable**, in the same sense that additions to `compiler-builtins` are insta-stable, though the availability of these symbols can probably be considered a "quality of implementation" detail rather than a stable promise.

I originally proposed to implement this in `compiler-builtins`, see rust-lang/compiler-builtins#794, but we discussed moving it to `std` instead ([Zulip thread](https://rust-lang.zulipchat.com/#narrow/channel/219381-t-libs/topic/Provide.20.60__isPlatformVersionAtLeast.60.20in.20.60std.60.3F/with/507880717)), which makes the implementation substantially simpler, and we avoid gnarly issues with requiring the user to link `libSystem.dylib` (since `std` unconditionally does that).

Note that this does not solve the linker errors for (pure) `#![no_std]` users, but that's _probably_ fine, if you are using ``@available`` to test the OS version on Apple platforms, you're likely also using `std` (and it is still possible to work around by linking `libclang_rt.*.a`).

A thing to note about the implementation, I've choosen to stray a bit from LLVM's upstream implementation, and not use `_availability_version_check` since [it has problems when compiling with an older SDK](llvm/llvm-project#64227). Instead, we use `sysctl kern.osproductversion` when available to still avoid the costly PList lookup in most cases, but still with a fall back to the PList lookup when that is not available (with the PList fallback being is similar to LLVM's implementation).

## Testing

Apple has a lot of different "modes" that they can run binaries in, which can be a bit difficult to find your bearings in, but I've tried to be as thorough as I could in testing them all.

Tested using roughly the equivalent of `./x test library/std -- platform_version` on the following configurations:
- macOS 14.7.3 on a Macbook Pro M2
    - `aarch64-apple-darwin`
    - `x86_64-apple-darwin` (under Rosetta)
    - `aarch64-apple-ios-macabi`
    - `x86_64-apple-ios-macabi` (under Rosetta)
    - `aarch64-apple-ios` (using Xcode's "Designed for iPad" setting)
    - `aarch64-apple-ios-sim` (in iOS Simulator, as iPhone with iOS 17.5)
    - `aarch64-apple-ios-sim` (in iOS Simulator, as iPad with iOS 18.2)
    - `aarch64-apple-tvos-sim` (in tvOS Simulator)
    - `aarch64-apple-watchos-sim` (in watchOS Simulator)
    - `aarch64-apple-ios-sim` (in visionOS simulator, using Xcode's "Designed for iPad" setting)
    - `aarch64-apple-visionos-sim` (in visionOS Simulator)
- macOS 15.3.1 VM
    - `aarch64-apple-darwin`
    - `aarch64-apple-ios-macabi`
- macOS 10.12.6 on an Intel Macbook from 2013
    - `x86_64-apple-darwin`
    - `i686-apple-darwin`
    - `x86_64-apple-ios` (in iOS Simulator)
- iOS 9.3.6 on a 1st generation iPad Mini
    - `armv7-apple-ios` with an older compiler

Along with manually inspecting the output of `version_from_sysctl()` and `version_from_plist()`, and verifying that they actually match what's expected.

I believe the only real omissions here would be:
- `aarch64-apple-ios` on a newer iPhone that has `sysctl` available (iOS 11.4 or above).
- `aarch64-apple-ios` on a Vision Pro using Xcode's "Designed for iPad" setting.

But I don't have the hardware available to test those.

`@rustbot` label O-apple A-linkage -T-compiler -A-meta -A-run-make

try-job: aarch64-apple
try-job: x86_64-apple*
rust-bors bot added a commit that referenced this issue Jun 13, 2025
Add `__isPlatformVersionAtLeast` and `__isOSVersionAtLeast` symbols

## Motivation

When Objective-C code uses ``@available(...)`,` Clang inserts a call to [`__isPlatformVersionAtLeast`](https://github.com/llvm/llvm-project/blob/llvmorg-20.1.0/compiler-rt/lib/builtins/os_version_check.c#L276) (`__isOSVersionAtLeast` in older Clang versions). These symbols not being available sometimes ends up causing linker errors. See the new test `tests/run-make/apple-c-available-links` for a minimal reproducer.

The workaround is to link `libclang_rt.osx.a`, see e.g. alexcrichton/curl-rust#279. But that's very difficult for users to figure out (and the backreferences to that issue indicates that people are still running into this in their own projects every so often).

For another recent example, this is preventing `rustc` from using LLVM assertions on macOS, see #62592 (comment) and #134275 (comment).

It is also a blocker for [setting the correct minimum OS version in `cc-rs`](#136113), since fixing this in `cc-rs` might end up introducing linker errors in places where we weren't before (by default, if using e.g. ``@available(macos` 10.15, *)`, the symbol usually happens to be left out, since `clang` defaults to compiling for the host macOS version, and thus things _seem_ to work - but the availability check actually compiles down to nothing, which is a huge correctness footgun for running on older OSes).

(My super secret evil agenda is also to expose some variant of ``@available`` in Rust's `std` after rust-lang/rfcs#3750 progresses further, will probably file an ACP for this later. But I believe this PR has value regardless of those future plans, since we'd be making C/Objective-C/Swift interop easier).

## Solution

Implement `__isPlatformVersionAtLeast` and `__isOSVersionAtLeast` as part of the "public ABI" that `std` exposes.

**This is insta-stable**, in the same sense that additions to `compiler-builtins` are insta-stable, though the availability of these symbols can probably be considered a "quality of implementation" detail rather than a stable promise.

I originally proposed to implement this in `compiler-builtins`, see rust-lang/compiler-builtins#794, but we discussed moving it to `std` instead ([Zulip thread](https://rust-lang.zulipchat.com/#narrow/channel/219381-t-libs/topic/Provide.20.60__isPlatformVersionAtLeast.60.20in.20.60std.60.3F/with/507880717)), which makes the implementation substantially simpler, and we avoid gnarly issues with requiring the user to link `libSystem.dylib` (since `std` unconditionally does that).

Note that this does not solve the linker errors for (pure) `#![no_std]` users, but that's _probably_ fine, if you are using ``@available`` to test the OS version on Apple platforms, you're likely also using `std` (and it is still possible to work around by linking `libclang_rt.*.a`).

A thing to note about the implementation, I've choosen to stray a bit from LLVM's upstream implementation, and not use `_availability_version_check` since [it has problems when compiling with an older SDK](llvm/llvm-project#64227). Instead, we use `sysctl kern.osproductversion` when available to still avoid the costly PList lookup in most cases, but still with a fall back to the PList lookup when that is not available (with the PList fallback being is similar to LLVM's implementation).

## Testing

Apple has a lot of different "modes" that they can run binaries in, which can be a bit difficult to find your bearings in, but I've tried to be as thorough as I could in testing them all.

Tested using roughly the equivalent of `./x test library/std -- platform_version` on the following configurations:
- macOS 14.7.3 on a Macbook Pro M2
    - `aarch64-apple-darwin`
    - `x86_64-apple-darwin` (under Rosetta)
    - `aarch64-apple-ios-macabi`
    - `x86_64-apple-ios-macabi` (under Rosetta)
    - `aarch64-apple-ios` (using Xcode's "Designed for iPad" setting)
    - `aarch64-apple-ios-sim` (in iOS Simulator, as iPhone with iOS 17.5)
    - `aarch64-apple-ios-sim` (in iOS Simulator, as iPad with iOS 18.2)
    - `aarch64-apple-tvos-sim` (in tvOS Simulator)
    - `aarch64-apple-watchos-sim` (in watchOS Simulator)
    - `aarch64-apple-ios-sim` (in visionOS simulator, using Xcode's "Designed for iPad" setting)
    - `aarch64-apple-visionos-sim` (in visionOS Simulator)
- macOS 15.3.1 VM
    - `aarch64-apple-darwin`
    - `aarch64-apple-ios-macabi`
- macOS 10.12.6 on an Intel Macbook from 2013
    - `x86_64-apple-darwin`
    - `i686-apple-darwin`
    - `x86_64-apple-ios` (in iOS Simulator)
- iOS 9.3.6 on a 1st generation iPad Mini
    - `armv7-apple-ios` with an older compiler

Along with manually inspecting the output of `version_from_sysctl()` and `version_from_plist()`, and verifying that they actually match what's expected.

I believe the only real omissions here would be:
- `aarch64-apple-ios` on a newer iPhone that has `sysctl` available (iOS 11.4 or above).
- `aarch64-apple-ios` on a Vision Pro using Xcode's "Designed for iPad" setting.

But I don't have the hardware available to test those.

`@rustbot` label O-apple A-linkage -T-compiler -A-meta -A-run-make

try-job: aarch64-apple
try-job: x86_64-apple*
rust-bors bot added a commit that referenced this issue Jun 13, 2025
Add `__isPlatformVersionAtLeast` and `__isOSVersionAtLeast` symbols

## Motivation

When Objective-C code uses ``@available(...)`,` Clang inserts a call to [`__isPlatformVersionAtLeast`](https://github.com/llvm/llvm-project/blob/llvmorg-20.1.0/compiler-rt/lib/builtins/os_version_check.c#L276) (`__isOSVersionAtLeast` in older Clang versions). These symbols not being available sometimes ends up causing linker errors. See the new test `tests/run-make/apple-c-available-links` for a minimal reproducer.

The workaround is to link `libclang_rt.osx.a`, see e.g. alexcrichton/curl-rust#279. But that's very difficult for users to figure out (and the backreferences to that issue indicates that people are still running into this in their own projects every so often).

For another recent example, this is preventing `rustc` from using LLVM assertions on macOS, see #62592 (comment) and #134275 (comment).

It is also a blocker for [setting the correct minimum OS version in `cc-rs`](#136113), since fixing this in `cc-rs` might end up introducing linker errors in places where we weren't before (by default, if using e.g. ``@available(macos` 10.15, *)`, the symbol usually happens to be left out, since `clang` defaults to compiling for the host macOS version, and thus things _seem_ to work - but the availability check actually compiles down to nothing, which is a huge correctness footgun for running on older OSes).

(My super secret evil agenda is also to expose some variant of ``@available`` in Rust's `std` after rust-lang/rfcs#3750 progresses further, will probably file an ACP for this later. But I believe this PR has value regardless of those future plans, since we'd be making C/Objective-C/Swift interop easier).

## Solution

Implement `__isPlatformVersionAtLeast` and `__isOSVersionAtLeast` as part of the "public ABI" that `std` exposes.

**This is insta-stable**, in the same sense that additions to `compiler-builtins` are insta-stable, though the availability of these symbols can probably be considered a "quality of implementation" detail rather than a stable promise.

I originally proposed to implement this in `compiler-builtins`, see rust-lang/compiler-builtins#794, but we discussed moving it to `std` instead ([Zulip thread](https://rust-lang.zulipchat.com/#narrow/channel/219381-t-libs/topic/Provide.20.60__isPlatformVersionAtLeast.60.20in.20.60std.60.3F/with/507880717)), which makes the implementation substantially simpler, and we avoid gnarly issues with requiring the user to link `libSystem.dylib` (since `std` unconditionally does that).

Note that this does not solve the linker errors for (pure) `#![no_std]` users, but that's _probably_ fine, if you are using ``@available`` to test the OS version on Apple platforms, you're likely also using `std` (and it is still possible to work around by linking `libclang_rt.*.a`).

A thing to note about the implementation, I've choosen to stray a bit from LLVM's upstream implementation, and not use `_availability_version_check` since [it has problems when compiling with an older SDK](llvm/llvm-project#64227). Instead, we use `sysctl kern.osproductversion` when available to still avoid the costly PList lookup in most cases, but still with a fall back to the PList lookup when that is not available (with the PList fallback being is similar to LLVM's implementation).

## Testing

Apple has a lot of different "modes" that they can run binaries in, which can be a bit difficult to find your bearings in, but I've tried to be as thorough as I could in testing them all.

Tested using roughly the equivalent of `./x test library/std -- platform_version` on the following configurations:
- macOS 14.7.3 on a Macbook Pro M2
    - `aarch64-apple-darwin`
    - `x86_64-apple-darwin` (under Rosetta)
    - `aarch64-apple-ios-macabi`
    - `x86_64-apple-ios-macabi` (under Rosetta)
    - `aarch64-apple-ios` (using Xcode's "Designed for iPad" setting)
    - `aarch64-apple-ios-sim` (in iOS Simulator, as iPhone with iOS 17.5)
    - `aarch64-apple-ios-sim` (in iOS Simulator, as iPad with iOS 18.2)
    - `aarch64-apple-tvos-sim` (in tvOS Simulator)
    - `aarch64-apple-watchos-sim` (in watchOS Simulator)
    - `aarch64-apple-ios-sim` (in visionOS simulator, using Xcode's "Designed for iPad" setting)
    - `aarch64-apple-visionos-sim` (in visionOS Simulator)
- macOS 15.3.1 VM
    - `aarch64-apple-darwin`
    - `aarch64-apple-ios-macabi`
- macOS 10.12.6 on an Intel Macbook from 2013
    - `x86_64-apple-darwin`
    - `i686-apple-darwin`
    - `x86_64-apple-ios` (in iOS Simulator)
- iOS 9.3.6 on a 1st generation iPad Mini
    - `armv7-apple-ios` with an older compiler

Along with manually inspecting the output of `version_from_sysctl()` and `version_from_plist()`, and verifying that they actually match what's expected.

I believe the only real omissions here would be:
- `aarch64-apple-ios` on a newer iPhone that has `sysctl` available (iOS 11.4 or above).
- `aarch64-apple-ios` on a Vision Pro using Xcode's "Designed for iPad" setting.

But I don't have the hardware available to test those.

`@rustbot` label O-apple A-linkage -T-compiler -A-meta -A-run-make

try-job: aarch64-apple
try-job: x86_64-apple*
rust-bors bot added a commit that referenced this issue Jun 13, 2025
Add `__isPlatformVersionAtLeast` and `__isOSVersionAtLeast` symbols

## Motivation

When Objective-C code uses ``@available(...)`,` Clang inserts a call to [`__isPlatformVersionAtLeast`](https://github.com/llvm/llvm-project/blob/llvmorg-20.1.0/compiler-rt/lib/builtins/os_version_check.c#L276) (`__isOSVersionAtLeast` in older Clang versions). These symbols not being available sometimes ends up causing linker errors. See the new test `tests/run-make/apple-c-available-links` for a minimal reproducer.

The workaround is to link `libclang_rt.osx.a`, see e.g. alexcrichton/curl-rust#279. But that's very difficult for users to figure out (and the backreferences to that issue indicates that people are still running into this in their own projects every so often).

For another recent example, this is preventing `rustc` from using LLVM assertions on macOS, see #62592 (comment) and #134275 (comment).

It is also a blocker for [setting the correct minimum OS version in `cc-rs`](#136113), since fixing this in `cc-rs` might end up introducing linker errors in places where we weren't before (by default, if using e.g. ``@available(macos` 10.15, *)`, the symbol usually happens to be left out, since `clang` defaults to compiling for the host macOS version, and thus things _seem_ to work - but the availability check actually compiles down to nothing, which is a huge correctness footgun for running on older OSes).

(My super secret evil agenda is also to expose some variant of ``@available`` in Rust's `std` after rust-lang/rfcs#3750 progresses further, will probably file an ACP for this later. But I believe this PR has value regardless of those future plans, since we'd be making C/Objective-C/Swift interop easier).

## Solution

Implement `__isPlatformVersionAtLeast` and `__isOSVersionAtLeast` as part of the "public ABI" that `std` exposes.

**This is insta-stable**, in the same sense that additions to `compiler-builtins` are insta-stable, though the availability of these symbols can probably be considered a "quality of implementation" detail rather than a stable promise.

I originally proposed to implement this in `compiler-builtins`, see rust-lang/compiler-builtins#794, but we discussed moving it to `std` instead ([Zulip thread](https://rust-lang.zulipchat.com/#narrow/channel/219381-t-libs/topic/Provide.20.60__isPlatformVersionAtLeast.60.20in.20.60std.60.3F/with/507880717)), which makes the implementation substantially simpler, and we avoid gnarly issues with requiring the user to link `libSystem.dylib` (since `std` unconditionally does that).

Note that this does not solve the linker errors for (pure) `#![no_std]` users, but that's _probably_ fine, if you are using ``@available`` to test the OS version on Apple platforms, you're likely also using `std` (and it is still possible to work around by linking `libclang_rt.*.a`).

A thing to note about the implementation, I've choosen to stray a bit from LLVM's upstream implementation, and not use `_availability_version_check` since [it has problems when compiling with an older SDK](llvm/llvm-project#64227). Instead, we use `sysctl kern.osproductversion` when available to still avoid the costly PList lookup in most cases, but still with a fall back to the PList lookup when that is not available (with the PList fallback being is similar to LLVM's implementation).

## Testing

Apple has a lot of different "modes" that they can run binaries in, which can be a bit difficult to find your bearings in, but I've tried to be as thorough as I could in testing them all.

Tested using roughly the equivalent of `./x test library/std -- platform_version` on the following configurations:
- macOS 14.7.3 on a Macbook Pro M2
    - `aarch64-apple-darwin`
    - `x86_64-apple-darwin` (under Rosetta)
    - `aarch64-apple-ios-macabi`
    - `x86_64-apple-ios-macabi` (under Rosetta)
    - `aarch64-apple-ios` (using Xcode's "Designed for iPad" setting)
    - `aarch64-apple-ios-sim` (in iOS Simulator, as iPhone with iOS 17.5)
    - `aarch64-apple-ios-sim` (in iOS Simulator, as iPad with iOS 18.2)
    - `aarch64-apple-tvos-sim` (in tvOS Simulator)
    - `aarch64-apple-watchos-sim` (in watchOS Simulator)
    - `aarch64-apple-ios-sim` (in visionOS simulator, using Xcode's "Designed for iPad" setting)
    - `aarch64-apple-visionos-sim` (in visionOS Simulator)
- macOS 15.3.1 VM
    - `aarch64-apple-darwin`
    - `aarch64-apple-ios-macabi`
- macOS 10.12.6 on an Intel Macbook from 2013
    - `x86_64-apple-darwin`
    - `i686-apple-darwin`
    - `x86_64-apple-ios` (in iOS Simulator)
- iOS 9.3.6 on a 1st generation iPad Mini
    - `armv7-apple-ios` with an older compiler

Along with manually inspecting the output of `version_from_sysctl()` and `version_from_plist()`, and verifying that they actually match what's expected.

I believe the only real omissions here would be:
- `aarch64-apple-ios` on a newer iPhone that has `sysctl` available (iOS 11.4 or above).
- `aarch64-apple-ios` on a Vision Pro using Xcode's "Designed for iPad" setting.

But I don't have the hardware available to test those.

`@rustbot` label O-apple A-linkage -T-compiler -A-meta -A-run-make

try-job: aarch64-apple
try-job: x86_64-apple*
rust-bors bot added a commit that referenced this issue Jun 13, 2025
Add `__isPlatformVersionAtLeast` and `__isOSVersionAtLeast` symbols

## Motivation

When Objective-C code uses ``@available(...)`,` Clang inserts a call to [`__isPlatformVersionAtLeast`](https://github.com/llvm/llvm-project/blob/llvmorg-20.1.0/compiler-rt/lib/builtins/os_version_check.c#L276) (`__isOSVersionAtLeast` in older Clang versions). These symbols not being available sometimes ends up causing linker errors. See the new test `tests/run-make/apple-c-available-links` for a minimal reproducer.

The workaround is to link `libclang_rt.osx.a`, see e.g. alexcrichton/curl-rust#279. But that's very difficult for users to figure out (and the backreferences to that issue indicates that people are still running into this in their own projects every so often).

For another recent example, this is preventing `rustc` from using LLVM assertions on macOS, see #62592 (comment) and #134275 (comment).

It is also a blocker for [setting the correct minimum OS version in `cc-rs`](#136113), since fixing this in `cc-rs` might end up introducing linker errors in places where we weren't before (by default, if using e.g. ``@available(macos` 10.15, *)`, the symbol usually happens to be left out, since `clang` defaults to compiling for the host macOS version, and thus things _seem_ to work - but the availability check actually compiles down to nothing, which is a huge correctness footgun for running on older OSes).

(My super secret evil agenda is also to expose some variant of ``@available`` in Rust's `std` after rust-lang/rfcs#3750 progresses further, will probably file an ACP for this later. But I believe this PR has value regardless of those future plans, since we'd be making C/Objective-C/Swift interop easier).

## Solution

Implement `__isPlatformVersionAtLeast` and `__isOSVersionAtLeast` as part of the "public ABI" that `std` exposes.

**This is insta-stable**, in the same sense that additions to `compiler-builtins` are insta-stable, though the availability of these symbols can probably be considered a "quality of implementation" detail rather than a stable promise.

I originally proposed to implement this in `compiler-builtins`, see rust-lang/compiler-builtins#794, but we discussed moving it to `std` instead ([Zulip thread](https://rust-lang.zulipchat.com/#narrow/channel/219381-t-libs/topic/Provide.20.60__isPlatformVersionAtLeast.60.20in.20.60std.60.3F/with/507880717)), which makes the implementation substantially simpler, and we avoid gnarly issues with requiring the user to link `libSystem.dylib` (since `std` unconditionally does that).

Note that this does not solve the linker errors for (pure) `#![no_std]` users, but that's _probably_ fine, if you are using ``@available`` to test the OS version on Apple platforms, you're likely also using `std` (and it is still possible to work around by linking `libclang_rt.*.a`).

A thing to note about the implementation, I've choosen to stray a bit from LLVM's upstream implementation, and not use `_availability_version_check` since [it has problems when compiling with an older SDK](llvm/llvm-project#64227). Instead, we use `sysctl kern.osproductversion` when available to still avoid the costly PList lookup in most cases, but still with a fall back to the PList lookup when that is not available (with the PList fallback being is similar to LLVM's implementation).

## Testing

Apple has a lot of different "modes" that they can run binaries in, which can be a bit difficult to find your bearings in, but I've tried to be as thorough as I could in testing them all.

Tested using roughly the equivalent of `./x test library/std -- platform_version` on the following configurations:
- macOS 14.7.3 on a Macbook Pro M2
    - `aarch64-apple-darwin`
    - `x86_64-apple-darwin` (under Rosetta)
    - `aarch64-apple-ios-macabi`
    - `x86_64-apple-ios-macabi` (under Rosetta)
    - `aarch64-apple-ios` (using Xcode's "Designed for iPad" setting)
    - `aarch64-apple-ios-sim` (in iOS Simulator, as iPhone with iOS 17.5)
    - `aarch64-apple-ios-sim` (in iOS Simulator, as iPad with iOS 18.2)
    - `aarch64-apple-tvos-sim` (in tvOS Simulator)
    - `aarch64-apple-watchos-sim` (in watchOS Simulator)
    - `aarch64-apple-ios-sim` (in visionOS simulator, using Xcode's "Designed for iPad" setting)
    - `aarch64-apple-visionos-sim` (in visionOS Simulator)
- macOS 15.3.1 VM
    - `aarch64-apple-darwin`
    - `aarch64-apple-ios-macabi`
- macOS 10.12.6 on an Intel Macbook from 2013
    - `x86_64-apple-darwin`
    - `i686-apple-darwin`
    - `x86_64-apple-ios` (in iOS Simulator)
- iOS 9.3.6 on a 1st generation iPad Mini
    - `armv7-apple-ios` with an older compiler

Along with manually inspecting the output of `version_from_sysctl()` and `version_from_plist()`, and verifying that they actually match what's expected.

I believe the only real omissions here would be:
- `aarch64-apple-ios` on a newer iPhone that has `sysctl` available (iOS 11.4 or above).
- `aarch64-apple-ios` on a Vision Pro using Xcode's "Designed for iPad" setting.

But I don't have the hardware available to test those.

`@rustbot` label O-apple A-linkage -T-compiler -A-meta -A-run-make

try-job: aarch64-apple
try-job: x86_64-apple*
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-linkage Area: linking into static, shared libraries and binaries C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC O-apple Operating system: Apple (macOS, iOS, tvOS, visionOS, watchOS) T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-libs Relevant to the library team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

6 participants