Auto merge of #127060 - Oneirical:testificate, r=jieyouxu
Migrate `symbol-visibility` `run-make` test to rmake Part of #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
This commit is contained in:
commit
70591dc15d
5 changed files with 188 additions and 124 deletions
|
@ -117,6 +117,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
|
|||
"ignore-watchos",
|
||||
"ignore-windows",
|
||||
"ignore-windows-gnu",
|
||||
"ignore-windows-msvc",
|
||||
"ignore-x32",
|
||||
"ignore-x86",
|
||||
"ignore-x86_64",
|
||||
|
|
|
@ -166,11 +166,19 @@ pub struct CompletedProcess {
|
|||
|
||||
impl CompletedProcess {
|
||||
#[must_use]
|
||||
#[track_caller]
|
||||
pub fn stdout_utf8(&self) -> String {
|
||||
String::from_utf8(self.output.stdout.clone()).expect("stdout is not valid UTF-8")
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[track_caller]
|
||||
pub fn invalid_stdout_utf8(&self) -> String {
|
||||
String::from_utf8_lossy(&self.output.stdout.clone()).to_string()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[track_caller]
|
||||
pub fn stderr_utf8(&self) -> String {
|
||||
String::from_utf8(self.output.stderr.clone()).expect("stderr is not valid UTF-8")
|
||||
}
|
||||
|
|
|
@ -49,7 +49,6 @@ run-make/split-debuginfo/Makefile
|
|||
run-make/stable-symbol-names/Makefile
|
||||
run-make/staticlib-dylib-linkage/Makefile
|
||||
run-make/symbol-mangling-hashed/Makefile
|
||||
run-make/symbol-visibility/Makefile
|
||||
run-make/sysroot-crates-are-unstable/Makefile
|
||||
run-make/thumb-none-cortex-m/Makefile
|
||||
run-make/thumb-none-qemu/Makefile
|
||||
|
|
|
@ -1,123 +0,0 @@
|
|||
# ignore-cross-compile
|
||||
include ../tools.mk
|
||||
|
||||
# ignore-windows-msvc
|
||||
|
||||
NM=nm -D
|
||||
CDYLIB_NAME=liba_cdylib.so
|
||||
RDYLIB_NAME=liba_rust_dylib.so
|
||||
PROC_MACRO_NAME=liba_proc_macro.so
|
||||
EXE_NAME=an_executable
|
||||
COMBINED_CDYLIB_NAME=libcombined_rlib_dylib.so
|
||||
|
||||
ifeq ($(UNAME),Darwin)
|
||||
NM=nm -gU
|
||||
CDYLIB_NAME=liba_cdylib.dylib
|
||||
RDYLIB_NAME=liba_rust_dylib.dylib
|
||||
PROC_MACRO_NAME=liba_proc_macro.dylib
|
||||
EXE_NAME=an_executable
|
||||
COMBINED_CDYLIB_NAME=libcombined_rlib_dylib.dylib
|
||||
endif
|
||||
|
||||
ifdef IS_WINDOWS
|
||||
NM=nm -g
|
||||
CDYLIB_NAME=liba_cdylib.dll.a
|
||||
RDYLIB_NAME=liba_rust_dylib.dll.a
|
||||
PROC_MACRO_NAME=liba_proc_macro.dll
|
||||
EXE_NAME=an_executable.exe
|
||||
COMBINED_CDYLIB_NAME=libcombined_rlib_dylib.dll.a
|
||||
endif
|
||||
|
||||
# `grep` regex for symbols produced by either `legacy` or `v0` mangling
|
||||
RE_ANY_RUST_SYMBOL="_ZN.*h.*E\|_R[a-zA-Z0-9_]+"
|
||||
|
||||
all:
|
||||
$(RUSTC) -Zshare-generics=no an_rlib.rs
|
||||
$(RUSTC) -Zshare-generics=no a_cdylib.rs
|
||||
$(RUSTC) -Zshare-generics=no a_rust_dylib.rs
|
||||
$(RUSTC) -Zshare-generics=no a_proc_macro.rs
|
||||
$(RUSTC) -Zshare-generics=no an_executable.rs
|
||||
$(RUSTC) -Zshare-generics=no a_cdylib.rs --crate-name combined_rlib_dylib --crate-type=rlib,cdylib
|
||||
|
||||
# Check that a cdylib exports its public #[no_mangle] functions
|
||||
[ "$$($(NM) $(TMPDIR)/$(CDYLIB_NAME) | grep -v __imp_ | grep -c public_c_function_from_cdylib)" -eq "1" ]
|
||||
# Check that a cdylib exports the public #[no_mangle] functions of dependencies
|
||||
[ "$$($(NM) $(TMPDIR)/$(CDYLIB_NAME) | grep -v __imp_ | grep -c public_c_function_from_rlib)" -eq "1" ]
|
||||
# Check that a cdylib DOES NOT export any public Rust functions
|
||||
[ "$$($(NM) $(TMPDIR)/$(CDYLIB_NAME) | grep -v __imp_ | grep -c $(RE_ANY_RUST_SYMBOL))" -eq "0" ]
|
||||
|
||||
# Check that a Rust dylib exports its monomorphic functions
|
||||
[ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -v __imp_ | grep -c public_c_function_from_rust_dylib)" -eq "1" ]
|
||||
[ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -v __imp_ | grep -c public_rust_function_from_rust_dylib)" -eq "1" ]
|
||||
# Check that a Rust dylib does not export generics if -Zshare-generics=no
|
||||
[ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -v __imp_ | grep -c public_generic_function_from_rust_dylib)" -eq "0" ]
|
||||
|
||||
|
||||
# Check that a Rust dylib exports the monomorphic functions from its dependencies
|
||||
[ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -v __imp_ | grep -c public_c_function_from_rlib)" -eq "1" ]
|
||||
[ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -v __imp_ | grep -c public_rust_function_from_rlib)" -eq "1" ]
|
||||
# Check that a Rust dylib does not export generics if -Zshare-generics=no
|
||||
[ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -v __imp_ | grep -c public_generic_function_from_rlib)" -eq "0" ]
|
||||
|
||||
# Check that a proc macro exports its public #[no_mangle] functions
|
||||
# FIXME(#99978) avoid exporting #[no_mangle] symbols for proc macros
|
||||
[ "$$($(NM) $(TMPDIR)/$(CDYLIB_NAME) | grep -v __imp_ | grep -c public_c_function_from_cdylib)" -eq "1" ]
|
||||
# Check that a proc macro exports the public #[no_mangle] functions of dependencies
|
||||
[ "$$($(NM) $(TMPDIR)/$(CDYLIB_NAME) | grep -v __imp_ | grep -c public_c_function_from_rlib)" -eq "1" ]
|
||||
# Check that a proc macro DOES NOT export any public Rust functions
|
||||
[ "$$($(NM) $(TMPDIR)/$(CDYLIB_NAME) | grep -v __imp_ | grep -c $(RE_ANY_RUST_SYMBOL))" -eq "0" ]
|
||||
|
||||
# FIXME(nbdd0121): This is broken in MinGW, see https://github.com/rust-lang/rust/pull/95604#issuecomment-1101564032
|
||||
ifndef IS_WINDOWS
|
||||
# Check that an executable does not export any dynamic symbols
|
||||
[ "$$($(NM) $(TMPDIR)/$(EXE_NAME) | grep -v __imp_ | grep -c public_c_function_from_rlib)" -eq "0" ]
|
||||
[ "$$($(NM) $(TMPDIR)/$(EXE_NAME) | grep -v __imp_ | grep -c public_rust_function_from_exe)" -eq "0" ]
|
||||
endif
|
||||
|
||||
|
||||
# Check the combined case, where we generate a cdylib and an rlib in the same
|
||||
# compilation session:
|
||||
# Check that a cdylib exports its public #[no_mangle] functions
|
||||
[ "$$($(NM) $(TMPDIR)/$(COMBINED_CDYLIB_NAME) | grep -v __imp_ | grep -c public_c_function_from_cdylib)" -eq "1" ]
|
||||
# Check that a cdylib exports the public #[no_mangle] functions of dependencies
|
||||
[ "$$($(NM) $(TMPDIR)/$(COMBINED_CDYLIB_NAME) | grep -v __imp_ | grep -c public_c_function_from_rlib)" -eq "1" ]
|
||||
# Check that a cdylib DOES NOT export any public Rust functions
|
||||
[ "$$($(NM) $(TMPDIR)/$(COMBINED_CDYLIB_NAME) | grep -v __imp_ | grep -c $(RE_ANY_RUST_SYMBOL))" -eq "0" ]
|
||||
|
||||
|
||||
$(RUSTC) -Zshare-generics=yes an_rlib.rs
|
||||
$(RUSTC) -Zshare-generics=yes a_cdylib.rs
|
||||
$(RUSTC) -Zshare-generics=yes a_rust_dylib.rs
|
||||
$(RUSTC) -Zshare-generics=yes a_proc_macro.rs
|
||||
$(RUSTC) -Zshare-generics=yes an_executable.rs
|
||||
|
||||
# Check that a cdylib exports its public #[no_mangle] functions
|
||||
[ "$$($(NM) $(TMPDIR)/$(CDYLIB_NAME) | grep -v __imp_ | grep -c public_c_function_from_cdylib)" -eq "1" ]
|
||||
# Check that a cdylib exports the public #[no_mangle] functions of dependencies
|
||||
[ "$$($(NM) $(TMPDIR)/$(CDYLIB_NAME) | grep -v __imp_ | grep -c public_c_function_from_rlib)" -eq "1" ]
|
||||
# Check that a cdylib DOES NOT export any public Rust functions
|
||||
[ "$$($(NM) $(TMPDIR)/$(CDYLIB_NAME) | grep -v __imp_ | grep -c $(RE_ANY_RUST_SYMBOL))" -eq "0" ]
|
||||
|
||||
# Check that a Rust dylib exports its monomorphic functions, including generics this time
|
||||
[ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -v __imp_ | grep -c public_c_function_from_rust_dylib)" -eq "1" ]
|
||||
[ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -v __imp_ | grep -c public_rust_function_from_rust_dylib)" -eq "1" ]
|
||||
[ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -v __imp_ | grep -c public_generic_function_from_rust_dylib)" -eq "1" ]
|
||||
|
||||
# Check that a Rust dylib exports the monomorphic functions from its dependencies
|
||||
[ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -v __imp_ | grep -c public_c_function_from_rlib)" -eq "1" ]
|
||||
[ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -v __imp_ | grep -c public_rust_function_from_rlib)" -eq "1" ]
|
||||
[ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -v __imp_ | grep -c public_generic_function_from_rlib)" -eq "1" ]
|
||||
|
||||
# Check that a proc macro exports its public #[no_mangle] functions
|
||||
# FIXME(#99978) avoid exporting #[no_mangle] symbols for proc macros
|
||||
[ "$$($(NM) $(TMPDIR)/$(CDYLIB_NAME) | grep -v __imp_ | grep -c public_c_function_from_cdylib)" -eq "1" ]
|
||||
# Check that a proc macro exports the public #[no_mangle] functions of dependencies
|
||||
[ "$$($(NM) $(TMPDIR)/$(CDYLIB_NAME) | grep -v __imp_ | grep -c public_c_function_from_rlib)" -eq "1" ]
|
||||
# Check that a proc macro DOES NOT export any public Rust functions
|
||||
[ "$$($(NM) $(TMPDIR)/$(CDYLIB_NAME) | grep -v __imp_ | grep -c $(RE_ANY_RUST_SYMBOL))" -eq "0" ]
|
||||
|
||||
ifndef IS_WINDOWS
|
||||
# Check that an executable does not export any dynamic symbols
|
||||
[ "$$($(NM) $(TMPDIR)/$(EXE_NAME) | grep -v __imp_ | grep -c public_c_function_from_rlib)" -eq "0" ]
|
||||
[ "$$($(NM) $(TMPDIR)/$(EXE_NAME) | grep -v __imp_ | grep -c public_rust_function_from_exe)" -eq "0" ]
|
||||
endif
|
179
tests/run-make/symbol-visibility/rmake.rs
Normal file
179
tests/run-make/symbol-visibility/rmake.rs
Normal file
|
@ -0,0 +1,179 @@
|
|||
// Dynamic libraries on Rust used to export a very high amount of symbols,
|
||||
// going as far as filling the output with mangled names and generic function
|
||||
// names. After the rework of #38117, this test checks that no mangled Rust symbols
|
||||
// are exported, and that generics are only shown if explicitely requested.
|
||||
// See https://github.com/rust-lang/rust/issues/37530
|
||||
|
||||
//@ ignore-windows-msvc
|
||||
|
||||
//FIXME(Oneirical): This currently uses llvm-nm for symbol detection. However,
|
||||
// the custom Rust-based solution of #128314 may prove to be an interesting alternative.
|
||||
|
||||
use run_make_support::{bin_name, dynamic_lib_name, is_darwin, is_windows, llvm_nm, regex, rustc};
|
||||
|
||||
fn main() {
|
||||
let cdylib_name = dynamic_lib_name("a_cdylib");
|
||||
let rdylib_name = dynamic_lib_name("a_rust_dylib");
|
||||
let exe_name = bin_name("an_executable");
|
||||
let combined_cdylib_name = dynamic_lib_name("combined_rlib_dylib");
|
||||
rustc().arg("-Zshare-generics=no").input("an_rlib.rs").run();
|
||||
rustc().arg("-Zshare-generics=no").input("a_cdylib.rs").run();
|
||||
rustc().arg("-Zshare-generics=no").input("a_rust_dylib.rs").run();
|
||||
rustc().arg("-Zshare-generics=no").input("a_proc_macro.rs").run();
|
||||
rustc().arg("-Zshare-generics=no").input("an_executable.rs").run();
|
||||
rustc()
|
||||
.arg("-Zshare-generics=no")
|
||||
.input("a_cdylib.rs")
|
||||
.crate_name("combined_rlib_dylib")
|
||||
.crate_type("rlib,cdylib")
|
||||
.run();
|
||||
|
||||
// Check that a cdylib exports its public #[no_mangle] functions
|
||||
symbols_check(&cdylib_name, SymbolCheckType::StrSymbol("public_c_function_from_cdylib"), true);
|
||||
// Check that a cdylib exports the public #[no_mangle] functions of dependencies
|
||||
symbols_check(&cdylib_name, SymbolCheckType::StrSymbol("public_c_function_from_rlib"), true);
|
||||
// Check that a cdylib DOES NOT export any public Rust functions
|
||||
symbols_check(&cdylib_name, SymbolCheckType::AnyRustSymbol, false);
|
||||
|
||||
// Check that a Rust dylib exports its monomorphic functions
|
||||
symbols_check(
|
||||
&rdylib_name,
|
||||
SymbolCheckType::StrSymbol("public_c_function_from_rust_dylib"),
|
||||
true,
|
||||
);
|
||||
symbols_check(
|
||||
&rdylib_name,
|
||||
SymbolCheckType::StrSymbol("public_rust_function_from_rust_dylib"),
|
||||
true,
|
||||
);
|
||||
// Check that a Rust dylib does not export generics if -Zshare-generics=no
|
||||
symbols_check(
|
||||
&rdylib_name,
|
||||
SymbolCheckType::StrSymbol("public_generic_function_from_rust_dylib"),
|
||||
false,
|
||||
);
|
||||
|
||||
// Check that a Rust dylib exports the monomorphic functions from its dependencies
|
||||
symbols_check(&rdylib_name, SymbolCheckType::StrSymbol("public_c_function_from_rlib"), true);
|
||||
symbols_check(&rdylib_name, SymbolCheckType::StrSymbol("public_rust_function_from_rlib"), true);
|
||||
// Check that a Rust dylib does not export generics if -Zshare-generics=no
|
||||
symbols_check(
|
||||
&rdylib_name,
|
||||
SymbolCheckType::StrSymbol("public_generic_function_from_rlib"),
|
||||
false,
|
||||
);
|
||||
|
||||
// FIXME(nbdd0121): This is broken in MinGW, see https://github.com/rust-lang/rust/pull/95604#issuecomment-1101564032
|
||||
// if is_windows() {
|
||||
// // Check that an executable does not export any dynamic symbols
|
||||
// symbols_check(&exe_name, SymbolCheckType::StrSymbol("public_c_function_from_rlib")
|
||||
//, false);
|
||||
// symbols_check(
|
||||
// &exe_name,
|
||||
// SymbolCheckType::StrSymbol("public_rust_function_from_exe"),
|
||||
// false,
|
||||
// );
|
||||
// }
|
||||
|
||||
// Check the combined case, where we generate a cdylib and an rlib in the same
|
||||
// compilation session:
|
||||
// Check that a cdylib exports its public #[no_mangle] functions
|
||||
symbols_check(
|
||||
&combined_cdylib_name,
|
||||
SymbolCheckType::StrSymbol("public_c_function_from_cdylib"),
|
||||
true,
|
||||
);
|
||||
// Check that a cdylib exports the public #[no_mangle] functions of dependencies
|
||||
symbols_check(
|
||||
&combined_cdylib_name,
|
||||
SymbolCheckType::StrSymbol("public_c_function_from_rlib"),
|
||||
true,
|
||||
);
|
||||
// Check that a cdylib DOES NOT export any public Rust functions
|
||||
symbols_check(&combined_cdylib_name, SymbolCheckType::AnyRustSymbol, false);
|
||||
|
||||
rustc().arg("-Zshare-generics=yes").input("an_rlib.rs").run();
|
||||
rustc().arg("-Zshare-generics=yes").input("a_cdylib.rs").run();
|
||||
rustc().arg("-Zshare-generics=yes").input("a_rust_dylib.rs").run();
|
||||
rustc().arg("-Zshare-generics=yes").input("an_executable.rs").run();
|
||||
|
||||
// Check that a cdylib exports its public #[no_mangle] functions
|
||||
symbols_check(&cdylib_name, SymbolCheckType::StrSymbol("public_c_function_from_cdylib"), true);
|
||||
// Check that a cdylib exports the public #[no_mangle] functions of dependencies
|
||||
symbols_check(&cdylib_name, SymbolCheckType::StrSymbol("public_c_function_from_rlib"), true);
|
||||
// Check that a cdylib DOES NOT export any public Rust functions
|
||||
symbols_check(&cdylib_name, SymbolCheckType::AnyRustSymbol, false);
|
||||
|
||||
// Check that a Rust dylib exports its monomorphic functions, including generics this time
|
||||
symbols_check(
|
||||
&rdylib_name,
|
||||
SymbolCheckType::StrSymbol("public_c_function_from_rust_dylib"),
|
||||
true,
|
||||
);
|
||||
symbols_check(
|
||||
&rdylib_name,
|
||||
SymbolCheckType::StrSymbol("public_rust_function_from_rust_dylib"),
|
||||
true,
|
||||
);
|
||||
symbols_check(
|
||||
&rdylib_name,
|
||||
SymbolCheckType::StrSymbol("public_generic_function_from_rust_dylib"),
|
||||
true,
|
||||
);
|
||||
|
||||
// Check that a Rust dylib exports the monomorphic functions from its dependencies
|
||||
symbols_check(&rdylib_name, SymbolCheckType::StrSymbol("public_c_function_from_rlib"), true);
|
||||
symbols_check(&rdylib_name, SymbolCheckType::StrSymbol("public_rust_function_from_rlib"), true);
|
||||
symbols_check(
|
||||
&rdylib_name,
|
||||
SymbolCheckType::StrSymbol("public_generic_function_from_rlib"),
|
||||
true,
|
||||
);
|
||||
|
||||
// FIXME(nbdd0121): This is broken in MinGW, see https://github.com/rust-lang/rust/pull/95604#issuecomment-1101564032
|
||||
// if is_windows() {
|
||||
// // Check that an executable does not export any dynamic symbols
|
||||
// symbols_check(&exe_name, SymbolCheckType::StrSymbol("public_c_function_from_rlib")
|
||||
//, false);
|
||||
// symbols_check(
|
||||
// &exe_name,
|
||||
// SymbolCheckType::StrSymbol("public_rust_function_from_exe"),
|
||||
// false,
|
||||
// );
|
||||
// }
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn symbols_check(path: &str, symbol_check_type: SymbolCheckType, exists_once: bool) {
|
||||
let mut nm = llvm_nm();
|
||||
if is_windows() {
|
||||
nm.arg("--extern-only");
|
||||
} else if is_darwin() {
|
||||
nm.arg("--extern-only").arg("--defined-only");
|
||||
} else {
|
||||
nm.arg("--dynamic");
|
||||
}
|
||||
let out = nm.input(path).run().stdout_utf8();
|
||||
assert_eq!(
|
||||
out.lines()
|
||||
.filter(|&line| !line.contains("__imp_") && has_symbol(line, symbol_check_type))
|
||||
.count()
|
||||
== 1,
|
||||
exists_once
|
||||
);
|
||||
}
|
||||
|
||||
fn has_symbol(line: &str, symbol_check_type: SymbolCheckType) -> bool {
|
||||
if let SymbolCheckType::StrSymbol(expected) = symbol_check_type {
|
||||
line.contains(expected)
|
||||
} else {
|
||||
let regex = regex::Regex::new(r#"_ZN.*h.*E\|_R[a-zA-Z0-9_]+"#).unwrap();
|
||||
regex.is_match(line)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum SymbolCheckType {
|
||||
StrSymbol(&'static str),
|
||||
AnyRustSymbol,
|
||||
}
|
Loading…
Add table
Reference in a new issue