Change:
After , Firefox builds fail with e.g: This happens when targeting arm64 macos or arm64 windows. Not only did the default change and now diverges from clang (causing the above error when mixing objects from clang and rust), but it's also apparently not possible to disable the feature. Note this also now reached beta. Cc:
It should be off by default. My first guess is that the problem is that , but with a true/false value, so perhaps a false value is not equivalent to an unset value. If that is the problem then there might be something deeper to investigate here, like some BTI mismatch between the Rust and C/C++ code that wasn't previously spotted. It would be interesting to know if the C/C++ code is compiled with BTI enabled. I don't have Firefox (or recent Rust) build environment on my M1 Mac so it'll take me a while to reproduce. I'll make a start, but if others can get there first and unblock this quickly, then please do! We could also revert from beta whilst we investigate. I'm not sure of the process there.
It most probably involves linker-plugin-lto. A default firefox build wouldn't enable that. I suspect it's reproducible with a simple testcase that has both C and C++ code with linker-plugin-lto enabled.
(beware of if you try to use linker-plugin-lto)
Ok, I was also able to reproduce locally with a Linux aarch64 cross-build (modulo ) and it turns out the rust llvm-ir has: while the clang llvm-ir has: llvm-ir from older versions of rust didn't have them at all.
Nor does the llvm-ir from older versions of clang — at least the one I have pre-built — so the simple idea of marking them as () to match the newer clang might cause different problems.
why do you think so? the llvm-ir that doesn't contain these flags at all links fine with the llvm-ir that does (even with behavior set to Error).
It was my own (naive) assumption, based on how BTI itself works: it's not correct to treat non-BTI machine code as BTI because if it lacks the BTI instructions it will be impossible to call any of its functions. However, that's not particularly relevant as this logic is all resolved before code generation. That leaves two candidate solutions, then: error-on-mismatch for these flags. This is nice because it catches configuration mismatches, and will be compatible with Clang and LTO. 't emit these flags at all if they aren't enabled. This most closely matches the existing (stable) behaviour but means that configuration mismatches (or compatibility issues like this one) may be missed. Given the behaviour you described, and the LTO — "Best results are achieved by using a and that are based on the exact same version of LLVM" — I think we can reasonably prefer option 1. I'll start working on that today.
Assigning priority as discussed in the of the Prioritization Working Group. label -I-prioritize +P-high
Reopening to track beta fix (proposed in ).
resolved by (on beta), on master

Discussion:
// to ensure intrinsic calls don't use it. if !sess.needs_plt() { let avoid_plt = "RtLibUseGOT0".as_ptr().cast(); <del> llvm::LLVMRustAddModuleFlag(llmod, avoid_plt, 1); </del> <ins> llvm::LLVMRustAddModuleFlag(llmod, llvm::LLVMModFlagBehavior::Warning, avoid_plt, 1); </ins> } if sess.is_sanitizer_cfi_enabled() { // FIXME(rcvalle): Add support for non canonical jump tables. let canonical_jump_tables = "CFI Canonical Jump Tables0".as_ptr().cast(); <del> // FIXME(rcvalle): Add it with Override behavior flag--LLVMRustAddModuleFlag adds it with // Warning behavior flag. Add support for specifying the behavior flag to // LLVMRustAddModuleFlag. llvm::LLVMRustAddModuleFlag(llmod, canonical_jump_tables, 1); </del> <ins> // FIXME(rcvalle): Add it with Override behavior flag. llvm::LLVMRustAddModuleFlag( llmod, llvm::LLVMModFlagBehavior::Warning, canonical_jump_tables, 1, ); </ins> } // Control Flow Guard is currently only supported by the MSVC linker on Windows.

CFGuard::Disabled => {} CFGuard::NoChecks => { // Set `cfguard=1` module flag to emit metadata only. <del> llvm::LLVMRustAddModuleFlag(llmod, "cfguard0".as_ptr() as *const _, 1) </del> <ins> llvm::LLVMRustAddModuleFlag( llmod, llvm::LLVMModFlagBehavior::Warning, "cfguard0".as_ptr() as *const _, 1, ) </ins> } CFGuard::Checks => { // Set `cfguard=2` module flag to emit metadata and checks. <del> llvm::LLVMRustAddModuleFlag(llmod, "cfguard0".as_ptr() as *const _, 2) </del> <ins> llvm::LLVMRustAddModuleFlag( llmod, llvm::LLVMModFlagBehavior::Warning, "cfguard0".as_ptr() as *const _, 2, ) </ins> } } }

llvm::LLVMRustAddModuleFlag( llmod, <ins> llvm::LLVMModFlagBehavior::Error, </ins> "branch-target-enforcement0".as_ptr().cast(), bti.into(), ); llvm::LLVMRustAddModuleFlag( llmod, <ins> llvm::LLVMModFlagBehavior::Error, </ins> "sign-return-address0".as_ptr().cast(), pac.is_some().into(), ); let pac_opts = pac.unwrap_or(PacRet { leaf: false, key: PAuthKey::A }); llvm::LLVMRustAddModuleFlag( llmod, <ins> llvm::LLVMModFlagBehavior::Error, </ins> "sign-return-address-all0".as_ptr().cast(), pac_opts.leaf.into(), ); let is_bkey = if pac_opts.key == PAuthKey::A { false } else { true }; llvm::LLVMRustAddModuleFlag( llmod, <ins> llvm::LLVMModFlagBehavior::Error, </ins> "sign-return-address-with-bkey0".as_ptr().cast(), is_bkey.into(), );

// This can be overridden using --llvm-opts -dwarf-version,N. // Android has the same issue (#22398) if let Some(version) = sess.target.dwarf_version { <del> llvm::LLVMRustAddModuleFlag(self.llmod, "Dwarf Version0".as_ptr().cast(), version) </del> <ins> llvm::LLVMRustAddModuleFlag( self.llmod, llvm::LLVMModFlagBehavior::Warning, "Dwarf Version0".as_ptr().cast(), version, ) </ins> } // Indicate that we want CodeView debug information on MSVC if sess.target.is_like_msvc { <del> llvm::LLVMRustAddModuleFlag(self.llmod, "CodeView0".as_ptr().cast(), 1) </del> <ins> llvm::LLVMRustAddModuleFlag( self.llmod, llvm::LLVMModFlagBehavior::Warning, "CodeView0".as_ptr().cast(), 1, ) </ins> } // Prevent bitcode readers from deleting the debug info. let ptr = "Debug Info Version0".as_ptr(); llvm::LLVMRustAddModuleFlag( self.llmod, <ins> llvm::LLVMModFlagBehavior::Warning, </ins> ptr.cast(), llvm::LLVMRustDebugMetadataVersion(), );

ARM = 0x01c0, } <ins> /// LLVM's Module::ModFlagBehavior, defined in llvm/include/llvm/IR/Module.h. /// /// When merging modules (e.g. during LTO), their metadata flags are combined. Conflicts are /// resolved according to the merge behaviors specified here. Flags differing only in merge /// behavior are still considered to be in conflict. /// /// In order for Rust-C LTO to work, we must specify behaviors compatible with Clang. Notably, /// 'Error' and 'Warning' cannot be mixed for a given flag. #[derive(Copy, Clone, PartialEq)] #[repr(C)] pub enum LLVMModFlagBehavior { Error = 1, Warning = 2, Require = 3, Override = 4, Append = 5, AppendUnique = 6, Max = 7, } </ins> // Consts for the LLVM CallConv type, pre-cast to usize. /// LLVM CallingConv::ID. Should we wrap this?

pub fn LLVMRustIsRustLLVM() -> bool; <del> pub fn LLVMRustAddModuleFlag(M: &Module, name: *const c_char, value: u32); </del> <ins> /// Add LLVM module flags. /// /// In order for Rust-C LTO to work, module flags must be compatible with Clang. What /// "compatible" means depends on the merge behaviors involved. pub fn LLVMRustAddModuleFlag( M: &Module, merge_behavior: LLVMModFlagBehavior, name: *const c_char, value: u32, ); </ins> pub fn LLVMRustMetadataAsValue<'a>(C: &'a Context, MD: &'a Metadata) -> &'a Value;

#endif } <del> extern "C" void LLVMRustAddModuleFlag(LLVMModuleRef M, const char *Name, uint32_t Value) { unwrap(M)->addModuleFlag(Module::Warning, Name, Value); </del> <ins> extern "C" void LLVMRustAddModuleFlag( LLVMModuleRef M, Module::ModFlagBehavior MergeBehavior, const char *Name, uint32_t Value) { unwrap(M)->addModuleFlag(MergeBehavior, Name, Value); </ins> } extern "C" LLVMValueRef LLVMRustMetadataAsValue(LLVMContextRef C, LLVMMetadataRef MD) {