From 4f9acb268704a1c7f78c8c55da0d7614eac57ade Mon Sep 17 00:00:00 2001
From: Gary Guo <gary@garyguo.net>
Date: Tue, 26 Apr 2022 18:01:10 +0100
Subject: [PATCH 1/2] Use decorated names for linked_symbols on Windows

---
 compiler/rustc_codegen_ssa/src/back/link.rs   |  4 ++
 compiler/rustc_codegen_ssa/src/back/linker.rs |  2 +-
 .../src/back/symbol_export.rs                 | 72 ++++++++++++++++++-
 compiler/rustc_codegen_ssa/src/lib.rs         |  1 +
 src/test/ui/symbol-names/x86-stdcall.rs       | 13 ++++
 5 files changed, 90 insertions(+), 2 deletions(-)
 create mode 100644 src/test/ui/symbol-names/x86-stdcall.rs

diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 743f6c0e5703d..54a69fcf68110 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -1696,6 +1696,10 @@ fn add_linked_symbol_object(
     // so add an empty section.
     if file.format() == object::BinaryFormat::Coff {
         file.add_section(Vec::new(), ".text".into(), object::SectionKind::Text);
+
+        // We handle the name decoration of COFF targets in `symbol_export.rs`, so disable the
+        // default mangler in `object` crate.
+        file.set_mangling(object::write::Mangling::None);
     }
 
     for (sym, kind) in symbols.iter() {
diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs
index 6e13e0d0e43b1..0805df5dad6bf 100644
--- a/compiler/rustc_codegen_ssa/src/back/linker.rs
+++ b/compiler/rustc_codegen_ssa/src/back/linker.rs
@@ -1576,7 +1576,7 @@ pub(crate) fn linked_symbols(
     for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| {
         if info.level.is_below_threshold(export_threshold) || info.used {
             symbols.push((
-                symbol_export::symbol_name_for_instance_in_crate(tcx, symbol, cnum),
+                symbol_export::linking_symbol_name_for_instance_in_crate(tcx, symbol, cnum),
                 info.kind,
             ));
         }
diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
index 3a416c8a2b54b..f651814be7ea6 100644
--- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
+++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
@@ -12,7 +12,7 @@ use rustc_middle::middle::exported_symbols::{
 use rustc_middle::ty::query::{ExternProviders, Providers};
 use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
 use rustc_middle::ty::Instance;
-use rustc_middle::ty::{SymbolName, TyCtxt};
+use rustc_middle::ty::{self, SymbolName, TyCtxt};
 use rustc_session::config::CrateType;
 use rustc_target::spec::SanitizerSet;
 
@@ -493,6 +493,76 @@ pub fn symbol_name_for_instance_in_crate<'tcx>(
     }
 }
 
+/// This is the symbol name of the given instance as seen by the linker.
+///
+/// On 32-bit Windows symbols are decorated according to their calling conventions.
+pub fn linking_symbol_name_for_instance_in_crate<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    symbol: ExportedSymbol<'tcx>,
+    instantiating_crate: CrateNum,
+) -> String {
+    use rustc_target::abi::call::Conv;
+
+    let mut undecorated = symbol_name_for_instance_in_crate(tcx, symbol, instantiating_crate);
+
+    let target = &tcx.sess.target;
+    if !target.is_like_windows {
+        // Mach-O has a global "_" suffix and `object` crate will handle it.
+        // ELF does not have any symbol decorations.
+        return undecorated;
+    }
+
+    let x86 = match &target.arch[..] {
+        "x86" => true,
+        "x86_64" => false,
+        // Only x86/64 use symbol decorations.
+        _ => return undecorated,
+    };
+
+    let instance = match symbol {
+        ExportedSymbol::NonGeneric(def_id) | ExportedSymbol::Generic(def_id, _)
+            if tcx.is_static(def_id) =>
+        {
+            None
+        }
+        ExportedSymbol::NonGeneric(def_id) => Some(Instance::mono(tcx, def_id)),
+        ExportedSymbol::Generic(def_id, substs) => Some(Instance::new(def_id, substs)),
+        // DropGlue always use the Rust calling convention and thus follow the target's default
+        // symbol decoration scheme.
+        ExportedSymbol::DropGlue(..) => None,
+        // NoDefId always follow the target's default symbol decoration scheme.
+        ExportedSymbol::NoDefId(..) => None,
+    };
+
+    let (conv, args) = instance
+        .map(|i| {
+            tcx.fn_abi_of_instance(ty::ParamEnv::reveal_all().and((i, ty::List::empty())))
+                .unwrap_or_else(|_| bug!("fn_abi_of_instance({i:?}) failed"))
+        })
+        .map(|fnabi| (fnabi.conv, &fnabi.args[..]))
+        .unwrap_or((Conv::Rust, &[]));
+
+    // Decorate symbols with prefices, suffices and total number of bytes of arguments.
+    // Reference: https://docs.microsoft.com/en-us/cpp/build/reference/decorated-names?view=msvc-170
+    let (prefix, suffix) = match conv {
+        Conv::X86Fastcall => ("@", "@"),
+        Conv::X86Stdcall => ("_", "@"),
+        Conv::X86VectorCall => ("", "@@"),
+        _ => {
+            if x86 {
+                undecorated.insert(0, '_');
+            }
+            return undecorated;
+        }
+    };
+
+    let args_in_bytes: u64 = args
+        .iter()
+        .map(|abi| abi.layout.size.bytes().next_multiple_of(target.pointer_width as u64 / 8))
+        .sum();
+    format!("{prefix}{undecorated}{suffix}{args_in_bytes}")
+}
+
 fn wasm_import_module_map(tcx: TyCtxt<'_>, cnum: CrateNum) -> FxHashMap<DefId, String> {
     // Build up a map from DefId to a `NativeLib` structure, where
     // `NativeLib` internally contains information about
diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs
index d430800220930..05d32972dab17 100644
--- a/compiler/rustc_codegen_ssa/src/lib.rs
+++ b/compiler/rustc_codegen_ssa/src/lib.rs
@@ -7,6 +7,7 @@
 #![feature(nll)]
 #![feature(associated_type_bounds)]
 #![feature(strict_provenance)]
+#![feature(int_roundings)]
 #![recursion_limit = "256"]
 #![allow(rustc::potential_query_instability)]
 
diff --git a/src/test/ui/symbol-names/x86-stdcall.rs b/src/test/ui/symbol-names/x86-stdcall.rs
new file mode 100644
index 0000000000000..9948488c0e950
--- /dev/null
+++ b/src/test/ui/symbol-names/x86-stdcall.rs
@@ -0,0 +1,13 @@
+// build-pass
+// only-x86-windows
+#![crate_type = "cdylib"]
+#![feature(abi_vectorcall)]
+
+#[no_mangle]
+extern "stdcall" fn foo(_: bool) {}
+
+#[no_mangle]
+extern "fastcall" fn bar(_: u8) {}
+
+#[no_mangle]
+extern "vectorcall" fn baz(_: u16) {}

From 0fce0db96f5d7f3b42d25412d3989014551852ac Mon Sep 17 00:00:00 2001
From: Gary Guo <gary@garyguo.net>
Date: Thu, 28 Apr 2022 16:22:40 +0100
Subject: [PATCH 2/2] Add `@feat.00` symbol to symbols.o for COFF

---
 compiler/rustc_codegen_ssa/src/back/link.rs | 23 +++++++++++++++++++++
 src/test/run-make/issue-96498/Makefile      |  8 +++++++
 src/test/run-make/issue-96498/foo.rs        |  4 ++++
 3 files changed, 35 insertions(+)
 create mode 100644 src/test/run-make/issue-96498/Makefile
 create mode 100644 src/test/run-make/issue-96498/foo.rs

diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 54a69fcf68110..886ca9681e2b2 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -1700,6 +1700,29 @@ fn add_linked_symbol_object(
         // We handle the name decoration of COFF targets in `symbol_export.rs`, so disable the
         // default mangler in `object` crate.
         file.set_mangling(object::write::Mangling::None);
+
+        // Add feature flags to the object file. On MSVC this is optional but LLD will complain if
+        // not present.
+        let mut feature = 0;
+
+        if file.architecture() == object::Architecture::I386 {
+            // Indicate that all SEH handlers are registered in .sxdata section.
+            // We don't have generate any code, so we don't need .sxdata section but LLD still
+            // expects us to set this bit (see #96498).
+            // Reference: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
+            feature |= 1;
+        }
+
+        file.add_symbol(object::write::Symbol {
+            name: "@feat.00".into(),
+            value: feature,
+            size: 0,
+            kind: object::SymbolKind::Data,
+            scope: object::SymbolScope::Compilation,
+            weak: false,
+            section: object::write::SymbolSection::Absolute,
+            flags: object::SymbolFlags::None,
+        });
     }
 
     for (sym, kind) in symbols.iter() {
diff --git a/src/test/run-make/issue-96498/Makefile b/src/test/run-make/issue-96498/Makefile
new file mode 100644
index 0000000000000..eae6400aee43a
--- /dev/null
+++ b/src/test/run-make/issue-96498/Makefile
@@ -0,0 +1,8 @@
+# only-windows
+# needs-rust-lld
+
+-include ../../run-make-fulldeps/tools.mk
+
+# Ensure that LLD can link
+all:
+	$(RUSTC) -C linker=rust-lld foo.rs
diff --git a/src/test/run-make/issue-96498/foo.rs b/src/test/run-make/issue-96498/foo.rs
new file mode 100644
index 0000000000000..93ac3641b098c
--- /dev/null
+++ b/src/test/run-make/issue-96498/foo.rs
@@ -0,0 +1,4 @@
+#![crate_type = "cdylib"]
+
+#[no_mangle]
+extern "C" fn foo() {}