@@ -38,9 +38,9 @@ use rustc_session::{filesearch, Session};
3838use rustc_span:: symbol:: Symbol ;
3939use rustc_target:: spec:: crt_objects:: CrtObjects ;
4040use 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 ,
4444} ;
4545use tempfile:: Builder as TempFileBuilder ;
4646use tracing:: { debug, info, warn} ;
@@ -2404,6 +2404,8 @@ fn add_order_independent_options(
24042404 // Take care of the flavors and CLI options requesting the `lld` linker.
24052405 add_lld_args ( cmd, sess, flavor, self_contained_components) ;
24062406
2407+ add_apple_link_args ( cmd, sess, flavor) ;
2408+
24072409 let apple_sdk_root = add_apple_sdk ( cmd, sess, flavor) ;
24082410
24092411 add_link_script ( cmd, sess, tmpdir, crate_type) ;
@@ -2956,6 +2958,133 @@ pub(crate) fn are_upstream_rust_objects_already_included(sess: &Session) -> bool
29562958 }
29572959}
29582960
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+
29593088fn add_apple_sdk ( cmd : & mut dyn Linker , sess : & Session , flavor : LinkerFlavor ) -> Option < PathBuf > {
29603089 let arch = & sess. target . arch ;
29613090 let os = & sess. target . os ;
0 commit comments