Skip to content

Commit 70591dc

Browse files
committed
Auto merge of rust-lang#127060 - Oneirical:testificate, r=jieyouxu
Migrate `symbol-visibility` `run-make` test to rmake Part of rust-lang#121876 and the associated [Google Summer of Code project](https://blog.rust-lang.org/2024/05/01/gsoc-2024-selected-projects.html). Pretty scary! - The expected number of symbols on each check has been changed slightly to reflect the differences between `llvm_readobj` and `nm`, as I think the former will print hidden symbols once and visible symbols twice, while the latter will only print visible symbols. - The original test ran the same exact checks on `cdylib` twice, for seemingly no reason. I have removed it. - This may be possible to optimize some more? `llvm_readobj` could get called only once for each library type, and the regex could avoid being created repeatedly. I am not sure if these kinds of considerations are important for a `run-make` test. Demands a Windows try-job. try-job: x86_64-mingw
2 parents e485266 + ea04b0a commit 70591dc

File tree

5 files changed

+188
-124
lines changed

5 files changed

+188
-124
lines changed

src/tools/compiletest/src/command-list.rs

+1
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
117117
"ignore-watchos",
118118
"ignore-windows",
119119
"ignore-windows-gnu",
120+
"ignore-windows-msvc",
120121
"ignore-x32",
121122
"ignore-x86",
122123
"ignore-x86_64",

src/tools/run-make-support/src/command.rs

+8
Original file line numberDiff line numberDiff line change
@@ -166,11 +166,19 @@ pub struct CompletedProcess {
166166

167167
impl CompletedProcess {
168168
#[must_use]
169+
#[track_caller]
169170
pub fn stdout_utf8(&self) -> String {
170171
String::from_utf8(self.output.stdout.clone()).expect("stdout is not valid UTF-8")
171172
}
172173

173174
#[must_use]
175+
#[track_caller]
176+
pub fn invalid_stdout_utf8(&self) -> String {
177+
String::from_utf8_lossy(&self.output.stdout.clone()).to_string()
178+
}
179+
180+
#[must_use]
181+
#[track_caller]
174182
pub fn stderr_utf8(&self) -> String {
175183
String::from_utf8(self.output.stderr.clone()).expect("stderr is not valid UTF-8")
176184
}

src/tools/tidy/src/allowed_run_make_makefiles.txt

-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ run-make/split-debuginfo/Makefile
4949
run-make/stable-symbol-names/Makefile
5050
run-make/staticlib-dylib-linkage/Makefile
5151
run-make/symbol-mangling-hashed/Makefile
52-
run-make/symbol-visibility/Makefile
5352
run-make/sysroot-crates-are-unstable/Makefile
5453
run-make/thumb-none-cortex-m/Makefile
5554
run-make/thumb-none-qemu/Makefile

tests/run-make/symbol-visibility/Makefile

-123
This file was deleted.
+179
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
// Dynamic libraries on Rust used to export a very high amount of symbols,
2+
// going as far as filling the output with mangled names and generic function
3+
// names. After the rework of #38117, this test checks that no mangled Rust symbols
4+
// are exported, and that generics are only shown if explicitely requested.
5+
// See https://github.com/rust-lang/rust/issues/37530
6+
7+
//@ ignore-windows-msvc
8+
9+
//FIXME(Oneirical): This currently uses llvm-nm for symbol detection. However,
10+
// the custom Rust-based solution of #128314 may prove to be an interesting alternative.
11+
12+
use run_make_support::{bin_name, dynamic_lib_name, is_darwin, is_windows, llvm_nm, regex, rustc};
13+
14+
fn main() {
15+
let cdylib_name = dynamic_lib_name("a_cdylib");
16+
let rdylib_name = dynamic_lib_name("a_rust_dylib");
17+
let exe_name = bin_name("an_executable");
18+
let combined_cdylib_name = dynamic_lib_name("combined_rlib_dylib");
19+
rustc().arg("-Zshare-generics=no").input("an_rlib.rs").run();
20+
rustc().arg("-Zshare-generics=no").input("a_cdylib.rs").run();
21+
rustc().arg("-Zshare-generics=no").input("a_rust_dylib.rs").run();
22+
rustc().arg("-Zshare-generics=no").input("a_proc_macro.rs").run();
23+
rustc().arg("-Zshare-generics=no").input("an_executable.rs").run();
24+
rustc()
25+
.arg("-Zshare-generics=no")
26+
.input("a_cdylib.rs")
27+
.crate_name("combined_rlib_dylib")
28+
.crate_type("rlib,cdylib")
29+
.run();
30+
31+
// Check that a cdylib exports its public #[no_mangle] functions
32+
symbols_check(&cdylib_name, SymbolCheckType::StrSymbol("public_c_function_from_cdylib"), true);
33+
// Check that a cdylib exports the public #[no_mangle] functions of dependencies
34+
symbols_check(&cdylib_name, SymbolCheckType::StrSymbol("public_c_function_from_rlib"), true);
35+
// Check that a cdylib DOES NOT export any public Rust functions
36+
symbols_check(&cdylib_name, SymbolCheckType::AnyRustSymbol, false);
37+
38+
// Check that a Rust dylib exports its monomorphic functions
39+
symbols_check(
40+
&rdylib_name,
41+
SymbolCheckType::StrSymbol("public_c_function_from_rust_dylib"),
42+
true,
43+
);
44+
symbols_check(
45+
&rdylib_name,
46+
SymbolCheckType::StrSymbol("public_rust_function_from_rust_dylib"),
47+
true,
48+
);
49+
// Check that a Rust dylib does not export generics if -Zshare-generics=no
50+
symbols_check(
51+
&rdylib_name,
52+
SymbolCheckType::StrSymbol("public_generic_function_from_rust_dylib"),
53+
false,
54+
);
55+
56+
// Check that a Rust dylib exports the monomorphic functions from its dependencies
57+
symbols_check(&rdylib_name, SymbolCheckType::StrSymbol("public_c_function_from_rlib"), true);
58+
symbols_check(&rdylib_name, SymbolCheckType::StrSymbol("public_rust_function_from_rlib"), true);
59+
// Check that a Rust dylib does not export generics if -Zshare-generics=no
60+
symbols_check(
61+
&rdylib_name,
62+
SymbolCheckType::StrSymbol("public_generic_function_from_rlib"),
63+
false,
64+
);
65+
66+
// FIXME(nbdd0121): This is broken in MinGW, see https://github.com/rust-lang/rust/pull/95604#issuecomment-1101564032
67+
// if is_windows() {
68+
// // Check that an executable does not export any dynamic symbols
69+
// symbols_check(&exe_name, SymbolCheckType::StrSymbol("public_c_function_from_rlib")
70+
//, false);
71+
// symbols_check(
72+
// &exe_name,
73+
// SymbolCheckType::StrSymbol("public_rust_function_from_exe"),
74+
// false,
75+
// );
76+
// }
77+
78+
// Check the combined case, where we generate a cdylib and an rlib in the same
79+
// compilation session:
80+
// Check that a cdylib exports its public #[no_mangle] functions
81+
symbols_check(
82+
&combined_cdylib_name,
83+
SymbolCheckType::StrSymbol("public_c_function_from_cdylib"),
84+
true,
85+
);
86+
// Check that a cdylib exports the public #[no_mangle] functions of dependencies
87+
symbols_check(
88+
&combined_cdylib_name,
89+
SymbolCheckType::StrSymbol("public_c_function_from_rlib"),
90+
true,
91+
);
92+
// Check that a cdylib DOES NOT export any public Rust functions
93+
symbols_check(&combined_cdylib_name, SymbolCheckType::AnyRustSymbol, false);
94+
95+
rustc().arg("-Zshare-generics=yes").input("an_rlib.rs").run();
96+
rustc().arg("-Zshare-generics=yes").input("a_cdylib.rs").run();
97+
rustc().arg("-Zshare-generics=yes").input("a_rust_dylib.rs").run();
98+
rustc().arg("-Zshare-generics=yes").input("an_executable.rs").run();
99+
100+
// Check that a cdylib exports its public #[no_mangle] functions
101+
symbols_check(&cdylib_name, SymbolCheckType::StrSymbol("public_c_function_from_cdylib"), true);
102+
// Check that a cdylib exports the public #[no_mangle] functions of dependencies
103+
symbols_check(&cdylib_name, SymbolCheckType::StrSymbol("public_c_function_from_rlib"), true);
104+
// Check that a cdylib DOES NOT export any public Rust functions
105+
symbols_check(&cdylib_name, SymbolCheckType::AnyRustSymbol, false);
106+
107+
// Check that a Rust dylib exports its monomorphic functions, including generics this time
108+
symbols_check(
109+
&rdylib_name,
110+
SymbolCheckType::StrSymbol("public_c_function_from_rust_dylib"),
111+
true,
112+
);
113+
symbols_check(
114+
&rdylib_name,
115+
SymbolCheckType::StrSymbol("public_rust_function_from_rust_dylib"),
116+
true,
117+
);
118+
symbols_check(
119+
&rdylib_name,
120+
SymbolCheckType::StrSymbol("public_generic_function_from_rust_dylib"),
121+
true,
122+
);
123+
124+
// Check that a Rust dylib exports the monomorphic functions from its dependencies
125+
symbols_check(&rdylib_name, SymbolCheckType::StrSymbol("public_c_function_from_rlib"), true);
126+
symbols_check(&rdylib_name, SymbolCheckType::StrSymbol("public_rust_function_from_rlib"), true);
127+
symbols_check(
128+
&rdylib_name,
129+
SymbolCheckType::StrSymbol("public_generic_function_from_rlib"),
130+
true,
131+
);
132+
133+
// FIXME(nbdd0121): This is broken in MinGW, see https://github.com/rust-lang/rust/pull/95604#issuecomment-1101564032
134+
// if is_windows() {
135+
// // Check that an executable does not export any dynamic symbols
136+
// symbols_check(&exe_name, SymbolCheckType::StrSymbol("public_c_function_from_rlib")
137+
//, false);
138+
// symbols_check(
139+
// &exe_name,
140+
// SymbolCheckType::StrSymbol("public_rust_function_from_exe"),
141+
// false,
142+
// );
143+
// }
144+
}
145+
146+
#[track_caller]
147+
fn symbols_check(path: &str, symbol_check_type: SymbolCheckType, exists_once: bool) {
148+
let mut nm = llvm_nm();
149+
if is_windows() {
150+
nm.arg("--extern-only");
151+
} else if is_darwin() {
152+
nm.arg("--extern-only").arg("--defined-only");
153+
} else {
154+
nm.arg("--dynamic");
155+
}
156+
let out = nm.input(path).run().stdout_utf8();
157+
assert_eq!(
158+
out.lines()
159+
.filter(|&line| !line.contains("__imp_") && has_symbol(line, symbol_check_type))
160+
.count()
161+
== 1,
162+
exists_once
163+
);
164+
}
165+
166+
fn has_symbol(line: &str, symbol_check_type: SymbolCheckType) -> bool {
167+
if let SymbolCheckType::StrSymbol(expected) = symbol_check_type {
168+
line.contains(expected)
169+
} else {
170+
let regex = regex::Regex::new(r#"_ZN.*h.*E\|_R[a-zA-Z0-9_]+"#).unwrap();
171+
regex.is_match(line)
172+
}
173+
}
174+
175+
#[derive(Clone, Copy)]
176+
enum SymbolCheckType {
177+
StrSymbol(&'static str),
178+
AnyRustSymbol,
179+
}

0 commit comments

Comments
 (0)