Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 79 additions & 5 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ use rustc_session::{Session, filesearch};
use rustc_span::Symbol;
use rustc_target::spec::crt_objects::CrtObjects;
use rustc_target::spec::{
BinaryFormat, Cc, CfgAbi, Env, LinkOutputKind, LinkSelfContainedComponents,
Arch, BinaryFormat, Cc, CfgAbi, Env, LinkOutputKind, LinkSelfContainedComponents,
LinkSelfContainedDefault, LinkerFeatures, LinkerFlavor, LinkerFlavorCli, Lld, Os, RelocModel,
RelroLevel, SanitizerSet, SplitDebuginfo,
};
Expand Down Expand Up @@ -2290,6 +2290,75 @@ fn add_rpath_args(
}
}

fn undecorate_c_symbol(name: &str, sess: &Session, kind: SymbolExportKind) -> String {
if sess.target.is_like_darwin {
// Mach-O: strip the leading underscore that all C symbols have.
// The Darwin linker's export_symbols will add it back.
name.strip_prefix('_').map(|s| s.to_string()).unwrap_or(name.to_string())
} else if sess.target.is_like_windows {
match sess.target.arch {
Arch::X86 => {
// COFF 32-bit: strip calling-convention decorations.
if let Some(rest) = name.strip_prefix('@') {
// fastcall: @foo@N -> foo
rest.split_once('@')
Comment thread
cezarbbb marked this conversation as resolved.
.map(|(base, suffix)| {
if suffix.parse::<u32>().is_ok() {
base.to_string()
} else {
name.to_string()
}
})
.unwrap_or(name.to_string())
} else if let Some(stripped) = name.strip_prefix('_') {
if let Some((base, suffix)) = stripped.split_once('@') {
// stdcall: _foo@N -> foo
if suffix.parse::<u32>().is_ok() {
base.to_string()
} else {
stripped.to_string()
}
} else {
// cdecl: _foo -> foo
stripped.to_string()
}
} else {
// vectorcall: foo@@N -> foo
name.split_once("@@")
Comment thread
cezarbbb marked this conversation as resolved.
.map(|(base, suffix)| {
if suffix.parse::<u32>().is_ok() {
base.to_string()
} else {
name.to_string()
}
})
.unwrap_or(name.to_string())
}
}
Arch::X86_64 => {
// COFF 64-bit: vectorcall mangling (foo@@N -> foo) also applies on x86_64.
name.split_once("@@")
.map(|(base, suffix)| {
if suffix.parse::<u32>().is_ok() {
base.to_string()
} else {
name.to_string()
}
})
.unwrap_or(name.to_string())
}
Arch::Arm64EC if kind == SymbolExportKind::Text => {
// Arm64EC: only text symbols have '#' prefix decoration
name.strip_prefix('#').map(|s| s.to_string()).unwrap_or(name.to_string())
}
_ => name.to_string(),
}
} else {
// ELF: no decoration
name.to_string()
}
}

fn add_c_staticlib_symbols(
sess: &Session,
lib: &NativeLib,
Expand Down Expand Up @@ -2331,7 +2400,13 @@ fn add_c_staticlib_symbols(
}

for symbol in object.symbols() {
if symbol.scope() != object::SymbolScope::Dynamic {
// The `object` crate returns `Dynamic` for ELF/Mach-O global symbols,
// but always returns `Linkage` for COFF external symbols.
// Accept both on Windows.
let scope = symbol.scope();
if scope != object::SymbolScope::Dynamic
&& !(sess.target.is_like_windows && scope == object::SymbolScope::Linkage)
{
continue;
}

Expand All @@ -2346,9 +2421,8 @@ fn add_c_staticlib_symbols(
_ => continue,
};

// FIXME:The symbol mangle rules are slightly different in Windows(32-bit) and Apple.
// Need to be resolved.
out.push((name.to_string(), export_kind));
let undecorated = undecorate_c_symbol(name, sess, export_kind);
out.push((undecorated, export_kind));
}
}

Expand Down
36 changes: 25 additions & 11 deletions tests/run-make/cdylib-export-c-library-symbols/rmake.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,48 @@
//@ ignore-nvptx64
//@ ignore-wasm
//@ ignore-cross-compile
// FIXME:The symbol mangle rules are slightly different in Windows(32-bit) and Apple.
// Need to be resolved.
//@ ignore-windows
//@ ignore-apple
// Reason: the compiled binary is executed

use run_make_support::{build_native_static_lib, cc, dynamic_lib_name, is_darwin, llvm_nm, rustc};
use run_make_support::{
build_native_static_lib, cc, dynamic_lib_name, is_darwin, is_windows, llvm_nm, llvm_readobj,
rfs, rustc,
};

fn main() {
cc().input("foo.c").arg("-c").out_exe("foo.o").run();
build_native_static_lib("foo");

rustc().input("foo.rs").arg("-lstatic=foo").crate_type("cdylib").run();

let out = llvm_nm()
.input(dynamic_lib_name("foo"))
.run()
.assert_stdout_not_contains_regex("T *my_function");
if is_darwin() {
llvm_nm().input(dynamic_lib_name("foo")).run().assert_stdout_not_contains("T _my_function");
} else if is_windows() {
llvm_readobj()
.arg("--coff-exports")
.input(dynamic_lib_name("foo"))
.run()
.assert_stdout_not_contains("my_function");
} else {
llvm_nm().input(dynamic_lib_name("foo")).run().assert_stdout_not_contains("T my_function");
}

rfs::remove_file(dynamic_lib_name("foo"));

rustc().input("foo_export.rs").arg("-lstatic:+export-symbols=foo").crate_type("cdylib").run();

if is_darwin() {
let out = llvm_nm()
llvm_nm()
.input(dynamic_lib_name("foo_export"))
.run()
.assert_stdout_contains("T _my_function");
} else if is_windows() {
llvm_readobj()
.arg("--coff-exports")
.input(dynamic_lib_name("foo_export"))
.run()
.assert_stdout_contains("my_function");
} else {
let out = llvm_nm()
llvm_nm()
.input(dynamic_lib_name("foo_export"))
.run()
.assert_stdout_contains("T my_function");
Expand Down
Loading