Change:
Try this, it might fix an error like or macOS targets are tricky because LLVM behaves differently depending on the MACOSXDEPLOYMENTTARGET environment variable, or a version specified in the target triple you tell LLVM to use. However, another component also uses the deployment target information to customise its output. That is the linker, from Xcode / the Command Line Tools. Rustc's default deployment target is 10.7. It only passes this to LLVM, and not to . When you invoke rustc using , it does what it should be doing by default, because it allows that env var to pass through to (aka ). When you do not provide the environment variable, it results in LLVM using 10.7 but using a much, much newer one.[^1] I believe this to be a bug in its own right -- you would expect rustc's default deployment target to apply to both the compiler and the Apple linker, but it does not. Solution: set if it is not already present in the environment for the invocation that ultimately calls to create a finished binary. [^1]: it's not 12.0! It's different, somehow even newer! See the table. I found this when compiling on an M1/aarch64 Monterey machine. It involves the technique . The cause of the error is that with the 'much, much newer' deployment target that uses by default, the linker transforms this into something completely different, and it prevents linking to the static function pointer, something that works with any deployment target set. But without further ado, this fails with a linker error if you build it in debug mode on macOS 12.0, using Xcode 13 or the Command Line Tools. It is very similar to in curl-rust. The error message is a bit fragile, it seems to depend on whether you are compiling a finished binary or some intermediate crate. When compiling a crate that depends on , you get this: But with this repro or in the curl-rust repo, you get this (very similar in spirit): There appear to be some changes around this recently. If you compile the equivalent C code, you actually get the exact same problem. Using ld from Apple LLVM 13.0.0 on a Monterey machine, linking a with If you tell clang to link the static in the section instead, then it doesn't work at all, it doesn't run before main. Clearly the "API" is to use the well-known DATA,modinitfunc, and the only guarantee is that it will execute that function before main. The above particular Rust code not compiling is therefore not really a rustc bug in its own right. Every platform has its own way of doing this, and "newer macOS" is just another variation that needs to be . Hacky platform-specific linker section stuff is almost certainly out of scope for stable/guaranteed behaviour. To do this correctly I think you would need a that always* has access to a env variable, i.e. cargo should set the env var to 10.7 if it is not already set. Then you could set some cfgs in to determine which link section to add when the targetos is macos. That solution also works for informing , the only difference being might get it from cargo. : This happens in beta/nightly-2021-10-26 too. ld version: <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
label O-macos A-linkage
I ran into this, but only with the ld64 that comes with Xcode 13.1 not with Xcode 13. Are you sure you can reproduce it with Xcode 13 as well?
Oh, indeed, I am running 13.1. I don't know about Xcode 13.
Thanks so much for this! Took a few hours of debugging before I finally ran into this page, solved it for me
claim
Thanks for reporting this. I had an issue when compiling in macOS Monterey But following the suggestion on the issue fixed it!
We've worked around this issue in the curl crate so that we won't trigger this bug -- should now compile on Monterey without needing to use the environment variable workaround. Though this bug still ought to be fixed.
I'm no longer having this issue, even without updating curl to 0.4.41.
Are you sure that's not just the absence of a lockfile, with cargo just fetching latest curl from

Discussion:
base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI | SanitizerSet::THREAD; base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-arch".to_string(), "arm64".to_string()]); <ins> base.link_env.extend(super::apple_base::macos_link_env("arm64")); </ins> base.link_env_remove.extend(super::apple_base::macos_link_env_remove()); // Clang automatically chooses a more specific target based on // MACOSX_DEPLOYMENT_TARGET.  To enable cross-language LTO to work // correctly, we do too. <del> let arch = "aarch64"; let llvm_target = super::apple_base::macos_llvm_target(&arch); </del> <ins> let llvm_target = super::apple_base::macos_llvm_target("arm64"); </ins> Target { llvm_target, pointer_width: 64, data_layout: "e-m:o-i64:64-i128:128-n32:64-S128".to_string(), <del> arch: arch.to_string(), </del> <ins> arch: "aarch64".to_string(), </ins> options: TargetOptions { mcount: "u{1}mcount".to_string(), frame_pointer: FramePointer::NonLeaf,

// warnings about the usage of ELF TLS. // // Here we detect what version is being requested, defaulting to 10.7. ELF <del> // TLS is flagged as enabled if it looks to be supported. let version = macos_deployment_target(); </del> <ins> // TLS is flagged as enabled if it looks to be supported. The architecture // only matters for default deployment target which is 11.0 for ARM64 and // 10.7 for everything else. let has_elf_tls = macos_deployment_target("x86_64") >= (10, 7); </ins> TargetOptions { os: os.to_string(),

has_rpath: true, dll_suffix: ".dylib".to_string(), archive_format: "darwin".to_string(), <del> has_elf_tls: version >= (10, 7), </del> <ins> has_elf_tls, </ins> abi_return_struct_as_int: true, emit_debug_gdb_scripts: false, eh_frame_header: false,

.and_then(|(a, b)| a.parse::<u32>().and_then(|a| b.parse::<u32>().map(|b| (a, b))).ok()) } <del> fn macos_deployment_target() -> (u32, u32) { deployment_target("MACOSX_DEPLOYMENT_TARGET").unwrap_or((10, 7)) </del> <ins> fn macos_default_deployment_target(arch: &str) -> (u32, u32) { if arch == "arm64" { (11, 0) } else { (10, 7) } } fn macos_deployment_target(arch: &str) -> (u32, u32) { deployment_target("MACOSX_DEPLOYMENT_TARGET") .unwrap_or_else(|| macos_default_deployment_target(arch)) </ins> } pub fn macos_llvm_target(arch: &str) -> String { <del> let (major, minor) = macos_deployment_target(); </del> <ins> let (major, minor) = macos_deployment_target(arch); </ins> format!("{}-apple-macosx{}.{}.0", arch, major, minor) } <ins> pub fn macos_link_env(arch: &str) -> Vec<(String, String)> { // Use the default deployment target for linking just as with the LLVM target if not // specified via MACOSX_DEPLOYMENT_TARGET, otherwise the system linker would use its // default which varies with Xcode version. if env::var("MACOSX_DEPLOYMENT_TARGET").is_err() { let default = macos_default_deployment_target(arch); vec![("MACOSX_DEPLOYMENT_TARGET".to_string(), format!("{}.{}", default.0, default.1))] } else { vec![] } } </ins> pub fn macos_link_env_remove() -> Vec<String> { let mut env_remove = Vec::with_capacity(2); // Remove the `SDKROOT` environment variable if it's clearly set for the wrong platform, which

base.cpu = "yonah".to_string(); base.max_atomic_width = Some(64); base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m32".to_string()]); <ins> base.link_env.extend(super::apple_base::macos_link_env("i686")); </ins> base.link_env_remove.extend(super::apple_base::macos_link_env_remove()); // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved base.stack_probes = StackProbeType::Call;

LinkerFlavor::Gcc, vec!["-m64".to_string(), "-arch".to_string(), "x86_64".to_string()], ); <ins> base.link_env.extend(super::apple_base::macos_link_env("x86_64")); </ins> base.link_env_remove.extend(super::apple_base::macos_link_env_remove()); // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved base.stack_probes = StackProbeType::Call;