diff --git a/Cargo.lock b/Cargo.lock index 91a6ebe4446..29ebd2b3979 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -214,7 +214,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9792d37ca5173d7e7f4fe453739a0671d0557915a030a383d6b866476bbc3e71" dependencies = [ - "object", + "object 0.32.2", ] [[package]] @@ -281,7 +281,7 @@ dependencies = [ "cfg-if", "libc", "miniz_oxide", - "object", + "object 0.32.2", "rustc-demangle", ] @@ -2623,10 +2623,21 @@ dependencies = [ "memchr", "rustc-std-workspace-alloc", "rustc-std-workspace-core", - "ruzstd", + "ruzstd 0.5.0", "wasmparser", ] +[[package]] +name = "object" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7090bae93f8585aad99e595b7073c5de9ba89fbd6b4e9f0cdd7a10177273ac8" +dependencies = [ + "flate2", + "memchr", + "ruzstd 0.6.0", +] + [[package]] name = "odht" version = "0.3.1" @@ -3310,6 +3321,7 @@ dependencies = [ name = "run_make_support" version = "0.0.0" dependencies = [ + "object 0.34.0", "wasmparser", ] @@ -3621,7 +3633,7 @@ dependencies = [ "itertools 0.12.1", "libc", "measureme", - "object", + "object 0.32.2", "rustc-demangle", "rustc_ast", "rustc_attr", @@ -3657,7 +3669,7 @@ dependencies = [ "itertools 0.12.1", "jobserver", "libc", - "object", + "object 0.32.2", "pathdiff", "regex", "rustc_arena", @@ -4614,7 +4626,7 @@ name = "rustc_target" version = "0.0.0" dependencies = [ "bitflags 2.4.2", - "object", + "object 0.32.2", "rustc_abi", "rustc_data_structures", "rustc_feature", @@ -4881,6 +4893,17 @@ dependencies = [ "twox-hash", ] +[[package]] +name = "ruzstd" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5174a470eeb535a721ae9fdd6e291c2411a906b96592182d05217591d5c5cf7b" +dependencies = [ + "byteorder", + "derive_more", + "twox-hash", +] + [[package]] name = "ryu" version = "1.0.17" @@ -5184,7 +5207,7 @@ dependencies = [ "hermit-abi", "libc", "miniz_oxide", - "object", + "object 0.32.2", "panic_abort", "panic_unwind", "profiler_builtins", @@ -5501,7 +5524,7 @@ checksum = "4db52ee8fec06e119b692ef3dd2c4cf621a99204c1b8c47407870ed050305b9b" dependencies = [ "gimli", "hashbrown", - "object", + "object 0.32.2", "tracing", ] diff --git a/Cargo.toml b/Cargo.toml index 5dd315ef2f7..e12c968e205 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -66,15 +66,6 @@ exclude = [ ] [profile.release.package.compiler_builtins] -# The compiler-builtins crate cannot reference libcore, and its own CI will -# verify that this is the case. This requires, however, that the crate is built -# without overflow checks and debug assertions. Forcefully disable debug -# assertions and overflow checks here which should ensure that even if these -# assertions are enabled for libstd we won't enable them for compiler_builtins -# which should ensure we still link everything correctly. -debug-assertions = false -overflow-checks = false - # For compiler-builtins we always use a high number of codegen units. # The goal here is to place every single intrinsic into its own object # file to avoid symbol clashes with the system libgcc if possible. Note diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 09286a8ed24..8fd84d8c053 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1693,11 +1693,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { self.set_debug_loc(&mut bx, mir::SourceInfo::outermost(self.mir.span)); - let (fn_abi, fn_ptr, _instance) = common::build_langcall(&bx, None, reason.lang_item()); - let fn_ty = bx.fn_decl_backend_type(fn_abi); + let (fn_abi, fn_ptr, instance) = common::build_langcall(&bx, None, reason.lang_item()); + if is_call_from_compiler_builtins_to_upstream_monomorphization(bx.tcx(), instance) { + bx.abort(); + } else { + let fn_ty = bx.fn_decl_backend_type(fn_abi); - let llret = bx.call(fn_ty, None, Some(fn_abi), fn_ptr, &[], funclet.as_ref()); - bx.apply_attrs_to_cleanup_callsite(llret); + let llret = bx.call(fn_ty, None, Some(fn_abi), fn_ptr, &[], funclet.as_ref()); + bx.apply_attrs_to_cleanup_callsite(llret); + } bx.unreachable(); diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index 814c3629b08..b82e959844f 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -10,6 +10,7 @@ use rustc_hir::lang_items::LangItem; use rustc_index::bit_set::FiniteBitSet; use rustc_macros::HashStable; use rustc_middle::ty::normalize_erasing_regions::NormalizationError; +use rustc_span::def_id::LOCAL_CRATE; use rustc_span::Symbol; use std::assert_matches::assert_matches; @@ -168,6 +169,11 @@ impl<'tcx> Instance<'tcx> { // If this a non-generic instance, it cannot be a shared monomorphization. self.args.non_erasable_generics(tcx, self.def_id()).next()?; + // compiler_builtins cannot use upstream monomorphizations. + if tcx.is_compiler_builtins(LOCAL_CRATE) { + return None; + } + match self.def { InstanceDef::Item(def) => tcx .upstream_monomorphizations_for(def) diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml index 958aef69572..d8bb8c643d1 100644 --- a/src/tools/run-make-support/Cargo.toml +++ b/src/tools/run-make-support/Cargo.toml @@ -4,4 +4,5 @@ version = "0.0.0" edition = "2021" [dependencies] +object = "0.34.0" wasmparser = "0.118.2" diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index 674860f8413..e5e7b559c92 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -2,6 +2,7 @@ use std::env; use std::path::{Path, PathBuf}; use std::process::{Command, Output}; +pub use object; pub use wasmparser; pub fn out_dir() -> PathBuf { diff --git a/tests/run-make/compiler-builtins/rmake.rs b/tests/run-make/compiler-builtins/rmake.rs new file mode 100644 index 00000000000..e7a5e8addbe --- /dev/null +++ b/tests/run-make/compiler-builtins/rmake.rs @@ -0,0 +1,142 @@ +//! The compiler_builtins library is special. It can call functions in core, but it must not +//! require linkage against a build of core. If it ever does, building the standard library *may* +//! result in linker errors, depending on whether the linker in use applies optimizations first or +//! resolves symbols first. So the portable and safe approach is to forbid such a linkage +//! requirement entirely. +//! +//! In addition, whether compiler_builtins requires linkage against core can depend on optimization +//! settings. Turning off optimizations and enabling debug assertions tends to produce the most +//! dependence on core that is possible, so that is the configuration we test here. + +#![deny(warnings)] + +extern crate run_make_support; + +use run_make_support::object; +use run_make_support::object::read::archive::ArchiveFile; +use run_make_support::object::read::Object; +use run_make_support::object::ObjectSection; +use run_make_support::object::ObjectSymbol; +use run_make_support::object::RelocationTarget; +use run_make_support::out_dir; +use std::collections::HashSet; + +const MANIFEST: &str = r#" +[package] +name = "scratch" +version = "0.1.0" +edition = "2021" + +[lib] +path = "lib.rs""#; + +fn main() { + let target_dir = out_dir().join("target"); + let target = std::env::var("TARGET").unwrap(); + if target.starts_with("wasm") || target.starts_with("nvptx") { + // wasm and nvptx targets don't produce rlib files that object can parse. + return; + } + + println!("Testing compiler_builtins for {}", target); + + // Set up the tiniest Cargo project: An empty no_std library. Just enough to run -Zbuild-std. + let manifest_path = out_dir().join("Cargo.toml"); + std::fs::write(&manifest_path, MANIFEST.as_bytes()).unwrap(); + std::fs::write(out_dir().join("lib.rs"), b"#![no_std]").unwrap(); + + let path = std::env::var("PATH").unwrap(); + let rustc = std::env::var("RUSTC").unwrap(); + let bootstrap_cargo = std::env::var("BOOTSTRAP_CARGO").unwrap(); + let status = std::process::Command::new(bootstrap_cargo) + .args([ + "build", + "--manifest-path", + manifest_path.to_str().unwrap(), + "-Zbuild-std=core", + "--target", + &target, + ]) + .env_clear() + .env("PATH", path) + .env("RUSTC", rustc) + .env("RUSTFLAGS", "-Copt-level=0 -Cdebug-assertions=yes") + .env("CARGO_TARGET_DIR", &target_dir) + .env("RUSTC_BOOTSTRAP", "1") + .status() + .unwrap(); + + assert!(status.success()); + + let rlibs_path = target_dir.join(target).join("debug").join("deps"); + let compiler_builtins_rlib = std::fs::read_dir(rlibs_path) + .unwrap() + .find_map(|e| { + let path = e.unwrap().path(); + let file_name = path.file_name().unwrap().to_str().unwrap(); + if file_name.starts_with("libcompiler_builtins") && file_name.ends_with(".rlib") { + Some(path) + } else { + None + } + }) + .unwrap(); + + // rlib files are archives, where the archive members each a CGU, and we also have one called + // lib.rmeta which is the encoded metadata. Each of the CGUs is an object file. + let data = std::fs::read(compiler_builtins_rlib).unwrap(); + + let mut defined_symbols = HashSet::new(); + let mut undefined_relocations = HashSet::new(); + + let archive = ArchiveFile::parse(&*data).unwrap(); + for member in archive.members() { + let member = member.unwrap(); + if member.name() == b"lib.rmeta" { + continue; + } + let data = member.data(&*data).unwrap(); + let object = object::File::parse(&*data).unwrap(); + + // Record all defined symbols in this CGU. + for symbol in object.symbols() { + if !symbol.is_undefined() { + let name = symbol.name().unwrap(); + defined_symbols.insert(name); + } + } + + // Find any relocations against undefined symbols. Calls within this CGU are relocations + // against a defined symbol. + for (_offset, relocation) in object.sections().flat_map(|section| section.relocations()) { + let RelocationTarget::Symbol(symbol_index) = relocation.target() else { + continue; + }; + let symbol = object.symbol_by_index(symbol_index).unwrap(); + if symbol.is_undefined() { + let name = symbol.name().unwrap(); + undefined_relocations.insert(name); + } + } + } + + // We can have symbols in the compiler_builtins rlib that are actually from core, if they were + // monomorphized in the compiler_builtins crate. This is totally fine, because though the call + // is to a function in core, it's resolved internally. + // + // It is normal to have relocations against symbols not defined in the rlib for things like + // unwinding, or math functions provided the target's platform libraries. Finding these is not + // a problem, we want to specifically ban relocations against core which are not resolved + // internally. + undefined_relocations + .retain(|symbol| !defined_symbols.contains(symbol) && symbol.contains("core")); + + if !undefined_relocations.is_empty() { + panic!( + "compiler_builtins must not link against core, but it does. \n\ + These symbols may be undefined in a debug build of compiler_builtins:\n\ + {:?}", + undefined_relocations + ); + } +}