From c0639ef8e4b4bf9feadb8bea49d4fe23ce9e8017 Mon Sep 17 00:00:00 2001
From: bjorn3 <17426603+bjorn3@users.noreply.github.com>
Date: Sat, 2 Dec 2023 14:08:49 +0000
Subject: [PATCH] Mangle #[rustc_std_internal_symbol] to include the rustc
 version unless #[no_mangle] is used

---
 compiler/rustc_symbol_mangling/src/lib.rs | 37 ++++++++++++++++++++
 compiler/rustc_symbol_mangling/src/v0.rs  | 42 +++++++++++++++++++++++
 2 files changed, 79 insertions(+)

diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs
index c4558b5ce8b..c9b15151a2c 100644
--- a/compiler/rustc_symbol_mangling/src/lib.rs
+++ b/compiler/rustc_symbol_mangling/src/lib.rs
@@ -112,6 +112,8 @@ mod v0;
 pub mod errors;
 pub mod test;
 
+pub use v0::mangle_internal_symbol;
+
 /// This function computes the symbol name for the given `instance` and the
 /// given instantiating crate. That is, if you know that instance X is
 /// instantiated in crate Y, this is the symbol name this instance would have.
@@ -183,6 +185,39 @@ fn compute_symbol_name<'tcx>(
         CodegenFnAttrs::EMPTY
     };
 
+    if attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) {
+        // Items marked as #[rustc_std_internal_symbol] need to have a fixed
+        // symbol name because it is used to import items from another crate
+        // without a direct dependency. As such it is not possible to look up
+        // the mangled name for the `Instance` from the crate metadata of the
+        // defining crate.
+        // Weak lang items automatically get #[rustc_std_internal_symbol]
+        // applied by the code computing the CodegenFnAttrs.
+        // We are mangling all #[rustc_std_internal_symbol] items that don't
+        // also have #[no_mangle] as a combination of the rustc version and the
+        // unmangled linkage name. This is to ensure that if we link against a
+        // staticlib compiled by a different rustc version, we don't get symbol
+        // conflicts or even UB due to a different implementation/ABI. Rust
+        // staticlibs currently export all symbols, including those that are
+        // hidden in cdylibs.
+        // We are using the v0 symbol mangling scheme here as we need to be
+        // consistent across all crates and in some contexts the legacy symbol
+        // mangling scheme can't be used. For example both the GCC backend and
+        // Rust-for-Linux don't support some of the characters used by the
+        // legacy symbol mangling scheme.
+        let name = if tcx.is_foreign_item(def_id) {
+            if let Some(name) = attrs.link_name { name } else { tcx.item_name(def_id) }
+        } else {
+            if let Some(name) = attrs.export_name { name } else { tcx.item_name(def_id) }
+        };
+
+        if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) {
+            return name.to_string();
+        } else {
+            return v0::mangle_internal_symbol(tcx, name.as_str());
+        }
+    }
+
     // Foreign items by default use no mangling for their symbol name. There's a
     // few exceptions to this rule though:
     //
@@ -198,6 +233,8 @@ fn compute_symbol_name<'tcx>(
     //   is present we mangle everything on wasm because the demangled form will
     //   show up in the `wasm-import-name` custom attribute in LLVM IR.
     //
+    // * `#[rustc_std_internal_symbol]` mangles the symbol name in a special way
+    //   both for exports and imports through foreign items. This is handled above.
     // [1]: https://bugs.llvm.org/show_bug.cgi?id=44316
     if tcx.is_foreign_item(def_id)
         && (!tcx.sess.target.is_like_wasm
diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs
index bc3923e4b4d..dbaeae5e951 100644
--- a/compiler/rustc_symbol_mangling/src/v0.rs
+++ b/compiler/rustc_symbol_mangling/src/v0.rs
@@ -1,4 +1,5 @@
 use std::fmt::Write;
+use std::hash::Hasher;
 use std::iter;
 use std::ops::Range;
 
@@ -6,6 +7,8 @@ use rustc_abi::{ExternAbi, Integer};
 use rustc_data_structures::base_n::ToBaseN;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::intern::Interned;
+use rustc_data_structures::stable_hasher::StableHasher;
+use rustc_hashes::Hash64;
 use rustc_hir as hir;
 use rustc_hir::def::CtorKind;
 use rustc_hir::def_id::{CrateNum, DefId};
@@ -70,6 +73,45 @@ pub(super) fn mangle<'tcx>(
     std::mem::take(&mut cx.out)
 }
 
+pub fn mangle_internal_symbol<'tcx>(tcx: TyCtxt<'tcx>, item_name: &str) -> String {
+    let prefix = "_R";
+    let mut cx: SymbolMangler<'_> = SymbolMangler {
+        tcx,
+        start_offset: prefix.len(),
+        paths: FxHashMap::default(),
+        types: FxHashMap::default(),
+        consts: FxHashMap::default(),
+        binders: vec![],
+        out: String::from(prefix),
+    };
+
+    cx.path_append_ns(
+        |cx| {
+            cx.push("C");
+            cx.push_disambiguator({
+                let mut hasher = StableHasher::new();
+                // Incorporate the rustc version to ensure #[rustc_std_internal_symbol] functions
+                // get a different symbol name depending on the rustc version.
+                //
+                // RUSTC_FORCE_RUSTC_VERSION is ignored here as otherwise different we would get an
+                // abi incompatibility with the standard library.
+                hasher.write(tcx.sess.cfg_version.as_bytes());
+
+                let hash: Hash64 = hasher.finish();
+                hash.as_u64()
+            });
+            cx.push_ident("__rustc");
+            Ok(())
+        },
+        'v',
+        0,
+        item_name,
+    )
+    .unwrap();
+
+    std::mem::take(&mut cx.out)
+}
+
 pub(super) fn mangle_typeid_for_trait_ref<'tcx>(
     tcx: TyCtxt<'tcx>,
     trait_ref: ty::ExistentialTraitRef<'tcx>,