Skip to content

Commit 0013d1a

Browse files
committed
test that weak linkage is respected
1 parent 6e61c46 commit 0013d1a

File tree

3 files changed

+65
-10
lines changed

3 files changed

+65
-10
lines changed

compiler/rustc_codegen_ssa/src/codegen_attrs.rs

+7
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,13 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
561561
// a declaration, with a definition provided in global assembly.
562562
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
563563
codegen_fn_attrs.inline = InlineAttr::Never;
564+
565+
// linkage must always be some flavor of external because the actual symbol is defined in
566+
// assembly, and the function is just a declaration, not a definition. This conversion
567+
// gives the naked function the semantics of a weakly-linked function.
568+
if codegen_fn_attrs.linkage == Some(Linkage::WeakAny) {
569+
codegen_fn_attrs.linkage = Some(Linkage::ExternalWeak);
570+
}
564571
}
565572

566573
codegen_fn_attrs.optimize = attrs.iter().fold(OptimizeAttr::None, |ia, attr| {

tests/run-make/naked-symbol-visibility/a_rust_dylib.rs

+23-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#![feature(naked_functions, asm_const)]
1+
#![feature(naked_functions, asm_const, linkage)]
22
#![crate_type = "dylib"]
33

44
use std::arch::asm;
@@ -35,6 +35,16 @@ pub extern "C" fn public_vanilla_generic_function_from_rust_dylib<T: TraitWithCo
3535
T::COUNT
3636
}
3737

38+
#[linkage = "weak"]
39+
extern "C" fn vanilla_weak_linkage() -> u32 {
40+
42
41+
}
42+
43+
#[linkage = "external"]
44+
extern "C" fn vanilla_external_linkage() -> u32 {
45+
42
46+
}
47+
3848
#[naked]
3949
extern "C" fn private_naked_rust_function_from_rust_dylib() -> u32 {
4050
unsafe { asm!("mov rax, 42", "ret", options(noreturn)) }
@@ -50,3 +60,15 @@ pub extern "C" fn public_naked_rust_function_from_rust_dylib() -> u32 {
5060
pub extern "C" fn public_naked_generic_function_from_rust_dylib<T: TraitWithConst>() -> u32 {
5161
unsafe { asm!("mov rax, {}", "ret", const T::COUNT, options(noreturn)) }
5262
}
63+
64+
#[naked]
65+
#[linkage = "weak"]
66+
extern "C" fn naked_weak_linkage() -> u32 {
67+
unsafe { asm!("mov rax, 42", "ret", options(noreturn)) }
68+
}
69+
70+
#[naked]
71+
#[linkage = "external"]
72+
extern "C" fn naked_external_linkage() -> u32 {
73+
unsafe { asm!("mov rax, 42", "ret", options(noreturn)) }
74+
}

tests/run-make/naked-symbol-visibility/rmake.rs

+35-9
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,57 @@ fn main() {
77

88
// check vanilla symbols
99
not_dynamic_symbol(&rdylib_name, "private_vanilla_rust_function_from_rust_dylib");
10-
dynamic_symbol(&rdylib_name, "public_vanilla_rust_function_from_rust_dylib");
10+
global_function(&rdylib_name, "public_vanilla_rust_function_from_rust_dylib");
1111
not_dynamic_symbol(&rdylib_name, "public_vanilla_generic_function_from_rust_dylib");
1212

13+
weak_function(&rdylib_name, "vanilla_weak_linkage");
14+
global_function(&rdylib_name, "vanilla_external_linkage");
15+
1316
// naked should mirror vanilla
1417
not_dynamic_symbol(&rdylib_name, "private_naked_rust_function_from_rust_dylib");
15-
dynamic_symbol(&rdylib_name, "public_naked_rust_function_from_rust_dylib");
18+
global_function(&rdylib_name, "public_naked_rust_function_from_rust_dylib");
1619
not_dynamic_symbol(&rdylib_name, "public_naked_generic_function_from_rust_dylib");
1720

21+
weak_function(&rdylib_name, "naked_weak_linkage");
22+
global_function(&rdylib_name, "naked_external_linkage");
23+
1824
// share generics should expose the generic functions
1925
rustc().arg("-Zshare-generics=yes").input("a_rust_dylib.rs").run();
20-
dynamic_symbol(&rdylib_name, "public_vanilla_generic_function_from_rust_dylib");
21-
dynamic_symbol(&rdylib_name, "public_naked_generic_function_from_rust_dylib");
26+
global_function(&rdylib_name, "public_vanilla_generic_function_from_rust_dylib");
27+
global_function(&rdylib_name, "public_naked_generic_function_from_rust_dylib");
2228
}
2329

2430
#[track_caller]
25-
fn dynamic_symbol(path: &str, symbol_name: &str) {
26-
assert_eq!(find_dynamic_symbol(path, symbol_name), 1)
31+
fn global_function(path: &str, symbol_name: &str) {
32+
let lines = find_dynamic_symbol(path, symbol_name);
33+
let [line] = lines.as_slice() else {
34+
panic!("symbol {symbol_name} occurs {} times", lines.len())
35+
};
36+
37+
assert!(line.contains("FUNC"), "`{symbol_name}` is not a function");
38+
assert!(line.contains("GLOBAL"), "`{symbol_name}` is not marked as global");
39+
}
40+
41+
#[track_caller]
42+
fn weak_function(path: &str, symbol_name: &str) {
43+
let lines = find_dynamic_symbol(path, symbol_name);
44+
let [line] = lines.as_slice() else {
45+
panic!("symbol {symbol_name} occurs {} times", lines.len())
46+
};
47+
48+
assert!(line.contains("FUNC"), "`{symbol_name}` is not a function");
49+
assert!(line.contains("WEAK"), "`{symbol_name}` is not marked as weak");
2750
}
2851

2952
#[track_caller]
3053
fn not_dynamic_symbol(path: &str, symbol_name: &str) {
31-
assert_eq!(find_dynamic_symbol(path, symbol_name), 0)
54+
assert_eq!(find_dynamic_symbol(path, symbol_name).len(), 0)
3255
}
3356

34-
fn find_dynamic_symbol(path: &str, symbol_name: &str) -> usize {
57+
fn find_dynamic_symbol<'a>(path: &str, symbol_name: &str) -> Vec<String> {
3558
let out = llvm_readobj().arg("--dyn-symbols").input(path).run().stdout_utf8();
36-
out.lines().filter(|&line| !line.contains("__imp_") && line.contains(symbol_name)).count()
59+
out.lines()
60+
.filter(|&line| !line.contains("__imp_") && line.contains(symbol_name))
61+
.map(|line| line.to_string())
62+
.collect()
3763
}

0 commit comments

Comments
 (0)