about summary refs log tree commit diff
diff options
context:
space:
mode:
authorBen Kimock <kimockb@gmail.com>2024-03-18 18:47:41 -0400
committerBen Kimock <kimockb@gmail.com>2024-03-20 23:36:05 -0400
commit2f6fb234de5a12fd314ec91737c1f3079f023d8c (patch)
tree2ebe3f58f89687baf3f59e9cf5f2f7818e5656ed
parent82717ab877099db6854c6176de90943c14393f6d (diff)
downloadrust-2f6fb234de5a12fd314ec91737c1f3079f023d8c.tar.gz
rust-2f6fb234de5a12fd314ec91737c1f3079f023d8c.zip
Add a test
-rw-r--r--Cargo.lock39
-rw-r--r--Cargo.toml9
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/block.rs12
-rw-r--r--compiler/rustc_middle/src/ty/instance.rs6
-rw-r--r--src/tools/run-make-support/Cargo.toml1
-rw-r--r--src/tools/run-make-support/src/lib.rs1
-rw-r--r--tests/run-make/compiler-builtins/rmake.rs142
7 files changed, 189 insertions, 21 deletions
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,11 +2623,22 @@ 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"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -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",
@@ -4882,6 +4894,17 @@ dependencies = [
 ]
 
 [[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"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -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
+        );
+    }
+}