@@ -38,9 +38,9 @@ use rustc_session::{filesearch, Session};
38
38
use rustc_span:: symbol:: Symbol ;
39
39
use rustc_target:: spec:: crt_objects:: CrtObjects ;
40
40
use rustc_target:: spec:: {
41
- Cc , LinkOutputKind , LinkSelfContainedComponents , LinkSelfContainedDefault , LinkerFeatures ,
42
- LinkerFlavor , LinkerFlavorCli , Lld , PanicStrategy , RelocModel , RelroLevel , SanitizerSet ,
43
- SplitDebuginfo ,
41
+ current_apple_deployment_target , Cc , LinkOutputKind , LinkSelfContainedComponents ,
42
+ LinkSelfContainedDefault , LinkerFeatures , LinkerFlavor , LinkerFlavorCli , Lld , PanicStrategy ,
43
+ RelocModel , RelroLevel , SanitizerSet , SplitDebuginfo ,
44
44
} ;
45
45
use tempfile:: Builder as TempFileBuilder ;
46
46
use tracing:: { debug, info, warn} ;
@@ -2404,6 +2404,8 @@ fn add_order_independent_options(
2404
2404
// Take care of the flavors and CLI options requesting the `lld` linker.
2405
2405
add_lld_args ( cmd, sess, flavor, self_contained_components) ;
2406
2406
2407
+ add_apple_link_args ( cmd, sess, flavor) ;
2408
+
2407
2409
let apple_sdk_root = add_apple_sdk ( cmd, sess, flavor) ;
2408
2410
2409
2411
add_link_script ( cmd, sess, tmpdir, crate_type) ;
@@ -2956,6 +2958,133 @@ pub(crate) fn are_upstream_rust_objects_already_included(sess: &Session) -> bool
2956
2958
}
2957
2959
}
2958
2960
2961
+ /// We need to communicate four things to the linker on Apple/Darwin targets:
2962
+ /// - The architecture.
2963
+ /// - The operating system (and that it's an Apple platform).
2964
+ /// - The deployment target.
2965
+ /// - The environment / ABI.
2966
+ fn add_apple_link_args ( cmd : & mut dyn Linker , sess : & Session , flavor : LinkerFlavor ) {
2967
+ if !sess. target . is_like_osx {
2968
+ return ;
2969
+ }
2970
+
2971
+ // `sess.target.arch` (`target_arch`) is not detailed enough.
2972
+ let llvm_arch = sess. target . llvm_target . split_once ( '-' ) . expect ( "LLVM target must have arch" ) . 0 ;
2973
+ let target_os = & * sess. target . os ;
2974
+ let target_abi = & * sess. target . abi ;
2975
+
2976
+ // The architecture name to forward to the linker.
2977
+ //
2978
+ // Supported architecture names can be found in the source:
2979
+ // https://github.com/apple-oss-distributions/ld64/blob/ld64-951.9/src/abstraction/MachOFileAbstraction.hpp#L578-L648
2980
+ //
2981
+ // Intentially verbose to ensure that the list always matches correctly
2982
+ // with the list in the source above.
2983
+ let ld64_arch = match llvm_arch {
2984
+ "armv7k" => "armv7k" ,
2985
+ "armv7s" => "armv7s" ,
2986
+ "arm64" => "arm64" ,
2987
+ "arm64e" => "arm64e" ,
2988
+ "arm64_32" => "arm64_32" ,
2989
+ // ld64 doesn't understand i686, so fall back to i386 instead.
2990
+ //
2991
+ // Same story when linking with cc, since that ends up invoking ld64.
2992
+ "i386" | "i686" => "i386" ,
2993
+ "x86_64" => "x86_64" ,
2994
+ "x86_64h" => "x86_64h" ,
2995
+ _ => bug ! ( "unsupported architecture in Apple target: {}" , sess. target. llvm_target) ,
2996
+ } ;
2997
+
2998
+ if matches ! ( flavor, LinkerFlavor :: Darwin ( Cc :: No , _) ) {
2999
+ // From the man page for ld64 (`man ld`):
3000
+ // > The linker accepts universal (multiple-architecture) input files,
3001
+ // > but always creates a "thin" (single-architecture), standard
3002
+ // > Mach-O output file. The architecture for the output file is
3003
+ // > specified using the -arch option.
3004
+ //
3005
+ // The linker has heuristics to determine the desired architecture,
3006
+ // but to be safe, and to avoid a warning, we set the architecture
3007
+ // explicitly.
3008
+ cmd. link_args ( & [ "-arch" , ld64_arch] ) ;
3009
+
3010
+ // Man page says that ld64 supports the following platform names:
3011
+ // > - macos
3012
+ // > - ios
3013
+ // > - tvos
3014
+ // > - watchos
3015
+ // > - bridgeos
3016
+ // > - visionos
3017
+ // > - xros
3018
+ // > - mac-catalyst
3019
+ // > - ios-simulator
3020
+ // > - tvos-simulator
3021
+ // > - watchos-simulator
3022
+ // > - visionos-simulator
3023
+ // > - xros-simulator
3024
+ // > - driverkit
3025
+ let platform_name = match ( target_os, target_abi) {
3026
+ ( os, "" ) => os,
3027
+ ( "ios" , "macabi" ) => "mac-catalyst" ,
3028
+ ( "ios" , "sim" ) => "ios-simulator" ,
3029
+ ( "tvos" , "sim" ) => "tvos-simulator" ,
3030
+ ( "watchos" , "sim" ) => "watchos-simulator" ,
3031
+ ( "visionos" , "sim" ) => "visionos-simulator" ,
3032
+ _ => bug ! ( "invalid OS/ABI combination for Apple target: {target_os}, {target_abi}" ) ,
3033
+ } ;
3034
+
3035
+ let ( major, minor, patch) = current_apple_deployment_target ( & sess. target ) ;
3036
+ let min_version = format ! ( "{major}.{minor}.{patch}" ) ;
3037
+
3038
+ // Lie about the SDK version, we don't know it here
3039
+ let sdk_version = & * min_version;
3040
+
3041
+ // From the man page for ld64 (`man ld`):
3042
+ // > This is set to indicate the platform, oldest supported version of
3043
+ // > that platform that output is to be used on, and the SDK that the
3044
+ // > output was built against.
3045
+ //
3046
+ // Like with `-arch`, the linker can figure out the platform versions
3047
+ // itself from the binaries being linked, but to be safe, we specify
3048
+ // the desired versions here explicitly.
3049
+ cmd. link_args ( & [ "-platform_version" , platform_name, & * min_version, sdk_version] ) ;
3050
+ } else if matches ! ( flavor, LinkerFlavor :: Darwin ( Cc :: Yes , _) ) {
3051
+ // We'd _like_ to use `-target` everywhere, since that can uniquely
3052
+ // communicate all the required details, but that doesn't work on GCC,
3053
+ // and since we don't know whether the `cc` compiler is Clang, GCC, or
3054
+ // something else, we fall back to other options that also work on GCC
3055
+ // when compiling for macOS.
3056
+ //
3057
+ // Targets other than macOS are ill-supported by GCC (it doesn't even
3058
+ // support e.g. `-miphoneos-version-min`), so in those cases we can
3059
+ // fairly safely use `-target`. See also the following, where it is
3060
+ // made explicit that the recommendation by LLVM developers is to use
3061
+ // `-target`: <https://github.com/llvm/llvm-project/issues/88271>
3062
+ if target_os == "macos" {
3063
+ // `-arch` communicates the architecture.
3064
+ //
3065
+ // CC forwards the `-arch` to the linker, so we use the same value
3066
+ // here intentionally.
3067
+ cmd. cc_args ( & [ "-arch" , ld64_arch] ) ;
3068
+
3069
+ // The presence of `-mmacosx-version-min` makes CC default to
3070
+ // macOS, and it sets the deployment target.
3071
+ let ( major, minor, patch) = current_apple_deployment_target ( & sess. target ) ;
3072
+ // Intentionally pass this as a single argument, Clang doesn't
3073
+ // seem to like it otherwise.
3074
+ cmd. cc_arg ( & format ! ( "-mmacosx-version-min={major}.{minor}.{patch}" ) ) ;
3075
+
3076
+ // macOS has no environment, so with these two, we've told CC the
3077
+ // four desired parameters.
3078
+ //
3079
+ // We avoid `-m32`/`-m64`, as this is already encoded by `-arch`.
3080
+ } else {
3081
+ cmd. cc_args ( & [ "-target" , & sess. target . llvm_target ] ) ;
3082
+ }
3083
+ } else {
3084
+ // FIXME: Are we doing the correct thing for non-Darwin linkers?
3085
+ }
3086
+ }
3087
+
2959
3088
fn add_apple_sdk ( cmd : & mut dyn Linker , sess : & Session , flavor : LinkerFlavor ) -> Option < PathBuf > {
2960
3089
let arch = & sess. target . arch ;
2961
3090
let os = & sess. target . os ;
0 commit comments