// 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 use run_make_support::object::read::Object; use run_make_support::{bin_name, dynamic_lib_name, is_msvc, object, regex, rfs, 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_msvc() { // 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_msvc() { // 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 binary_data = rfs::read(path); let file = object::File::parse(&*binary_data).unwrap(); let mut found: u64 = 0; for export in file.exports().unwrap() { let name = std::str::from_utf8(export.name()).unwrap(); if has_symbol(name, symbol_check_type) { found += 1; } } assert_eq!(found, exists_once as u64); } fn has_symbol(name: &str, symbol_check_type: SymbolCheckType) -> bool { if let SymbolCheckType::StrSymbol(expected) = symbol_check_type { name.contains(expected) } else { let regex = regex::Regex::new(r#"_ZN.*h.*E\|_R[a-zA-Z0-9_]+"#).unwrap(); regex.is_match(name) } } #[derive(Clone, Copy)] enum SymbolCheckType { StrSymbol(&'static str), AnyRustSymbol, }