Skip to content

Commit ca10995

Browse files
cezarbbbjchecahi
andcommitted
export symbols: support macos/windows(32/64)
Co-authored-by: Jesus Checa <101630491+jchecahi@users.noreply.github.com>
1 parent 98594f4 commit ca10995

2 files changed

Lines changed: 103 additions & 19 deletions

File tree

  • compiler/rustc_codegen_ssa/src/back
  • tests/run-make/cdylib-export-c-library-symbols

compiler/rustc_codegen_ssa/src/back/link.rs

Lines changed: 76 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ use rustc_session::{Session, filesearch};
4747
use rustc_span::Symbol;
4848
use rustc_target::spec::crt_objects::CrtObjects;
4949
use rustc_target::spec::{
50-
BinaryFormat, Cc, CfgAbi, Env, LinkOutputKind, LinkSelfContainedComponents,
50+
Arch, BinaryFormat, Cc, CfgAbi, Env, LinkOutputKind, LinkSelfContainedComponents,
5151
LinkSelfContainedDefault, LinkerFeatures, LinkerFlavor, LinkerFlavorCli, Lld, Os, RelocModel,
5252
RelroLevel, SanitizerSet, SplitDebuginfo,
5353
};
@@ -2413,6 +2413,69 @@ fn add_rpath_args(
24132413
}
24142414
}
24152415

2416+
fn strip_numeric_suffix<'a>(base: &'a str, suffix: impl AsRef<str>, fallback: &'a str) -> &'a str {
2417+
if suffix.as_ref().parse::<u32>().is_ok() { base } else { fallback }
2418+
}
2419+
2420+
fn undecorate_c_symbol<'a>(
2421+
name: &'a str,
2422+
sess: &Session,
2423+
kind: SymbolExportKind,
2424+
) -> Option<&'a str> {
2425+
match sess.target.binary_format {
2426+
BinaryFormat::MachO => {
2427+
// Mach-O: strip the leading underscore that all external symbols have.
2428+
// The Darwin linker's export_symbols will add it back.
2429+
name.strip_prefix('_')
2430+
}
2431+
BinaryFormat::Coff => {
2432+
// MSVC C++ mangled names start with '?' and use a completely different
2433+
// decorating scheme that includes '@@' as structural delimiters.
2434+
// They must not be subjected to C calling-convention undecoration.
2435+
if name.starts_with('?') {
2436+
return Some(name);
2437+
}
2438+
Some(match sess.target.arch {
2439+
Arch::X86 => {
2440+
// COFF 32-bit: strip calling-convention decorations.
2441+
if let Some(rest) = name.strip_prefix('@') {
2442+
// fastcall: @foo@N -> foo
2443+
rest.rsplit_once('@')
2444+
.map(|(base, suffix)| strip_numeric_suffix(base, suffix, name))
2445+
.unwrap_or(name)
2446+
} else if let Some(stripped) = name.strip_prefix('_') {
2447+
if let Some((base, suffix)) = stripped.rsplit_once('@') {
2448+
// stdcall: _foo@N -> foo
2449+
strip_numeric_suffix(base, suffix, stripped)
2450+
} else {
2451+
// cdecl: _foo -> foo
2452+
stripped
2453+
}
2454+
} else {
2455+
// vectorcall: foo@@N -> foo
2456+
name.rsplit_once("@@")
2457+
.map(|(base, suffix)| strip_numeric_suffix(base, suffix, name))
2458+
.unwrap_or(name)
2459+
}
2460+
}
2461+
Arch::X86_64 => {
2462+
// COFF 64-bit: vectorcall mangling (foo@@N -> foo) also applies on x86_64.
2463+
name.rsplit_once("@@")
2464+
.map(|(base, suffix)| strip_numeric_suffix(base, suffix, name))
2465+
.unwrap_or(name)
2466+
}
2467+
Arch::Arm64EC if kind == SymbolExportKind::Text => {
2468+
// Arm64EC: `#` prefix distinguishes ARM64EC text symbols from x64 thunks.
2469+
name.strip_prefix('#').unwrap_or(name)
2470+
}
2471+
_ => name,
2472+
})
2473+
}
2474+
// ELF: no decoration
2475+
_ => Some(name),
2476+
}
2477+
}
2478+
24162479
fn add_c_staticlib_symbols(
24172480
sess: &Session,
24182481
lib: &NativeLib,
@@ -2454,7 +2517,14 @@ fn add_c_staticlib_symbols(
24542517
}
24552518

24562519
for symbol in object.symbols() {
2457-
if symbol.scope() != object::SymbolScope::Dynamic {
2520+
// The `object` crate returns `Dynamic` for ELF/Mach-O global symbols,
2521+
// but always returns `Linkage` for COFF external symbols.
2522+
// Accept both for COFF (Windows and UEFI).
2523+
let scope = symbol.scope();
2524+
if scope != object::SymbolScope::Dynamic
2525+
&& !(sess.target.binary_format == BinaryFormat::Coff
2526+
&& scope == object::SymbolScope::Linkage)
2527+
{
24582528
continue;
24592529
}
24602530

@@ -2469,9 +2539,10 @@ fn add_c_staticlib_symbols(
24692539
_ => continue,
24702540
};
24712541

2472-
// FIXME:The symbol mangle rules are slightly different in Windows(32-bit) and Apple.
2473-
// Need to be resolved.
2474-
out.push((name.to_string(), export_kind));
2542+
let Some(undecorated) = undecorate_c_symbol(name, sess, export_kind) else {
2543+
continue;
2544+
};
2545+
out.push((undecorated.to_string(), export_kind));
24752546
}
24762547
}
24772548

tests/run-make/cdylib-export-c-library-symbols/rmake.rs

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,54 @@
11
//@ ignore-nvptx64
22
//@ ignore-wasm
33
//@ ignore-cross-compile
4-
// FIXME:The symbol mangle rules are slightly different in Windows(32-bit) and Apple.
5-
// Need to be resolved.
6-
//@ ignore-windows
7-
//@ ignore-apple
84
// Reason: the compiled binary is executed
95

106
use run_make_support::{
11-
build_native_static_lib, cc, dynamic_lib_name, is_aix, is_darwin, llvm_nm, rustc,
7+
build_native_static_lib, cc, dynamic_lib_name, is_aix, is_darwin, is_windows, is_windows_msvc,
8+
llvm_ar, llvm_nm, llvm_readobj, rfs, rustc, static_lib_name,
129
};
1310

1411
fn main() {
15-
cc().input("foo.c").arg("-c").out_exe("foo.o").run();
16-
build_native_static_lib("foo");
12+
let obj_file = if is_windows_msvc() { "foo.obj" } else { "foo.o" };
13+
cc().input("foo.c").arg("-c").arg("-fno-lto").out_exe(obj_file).run();
14+
llvm_ar().obj_to_ar().output_input(&static_lib_name("foo"), obj_file).run();
1715

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

20-
let out = llvm_nm()
21-
.input(dynamic_lib_name("foo"))
22-
.run()
23-
.assert_stdout_not_contains_regex("T *my_function");
18+
if is_darwin() {
19+
llvm_nm().input(dynamic_lib_name("foo")).run().assert_stdout_not_contains("T _my_function");
20+
} else if is_windows() {
21+
llvm_readobj()
22+
.arg("--coff-exports")
23+
.input(dynamic_lib_name("foo"))
24+
.run()
25+
.assert_stdout_not_contains("my_function");
26+
} else {
27+
llvm_nm().input(dynamic_lib_name("foo")).run().assert_stdout_not_contains("T my_function");
28+
}
29+
30+
rfs::remove_file(dynamic_lib_name("foo"));
2431

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

2734
if is_darwin() {
28-
let out = llvm_nm()
35+
llvm_nm()
2936
.input(dynamic_lib_name("foo_export"))
3037
.run()
3138
.assert_stdout_contains("T _my_function");
39+
} else if is_windows() {
40+
llvm_readobj()
41+
.arg("--coff-exports")
42+
.input(dynamic_lib_name("foo_export"))
43+
.run()
44+
.assert_stdout_contains("my_function");
3245
} else if is_aix() {
33-
let out = llvm_nm()
46+
llvm_nm()
3447
.input(dynamic_lib_name("foo_export"))
3548
.run()
3649
.assert_stdout_contains("T .my_function");
3750
} else {
38-
let out = llvm_nm()
51+
llvm_nm()
3952
.input(dynamic_lib_name("foo_export"))
4053
.run()
4154
.assert_stdout_contains("T my_function");

0 commit comments

Comments
 (0)