From 9061915fffe5d3f127b7a9286457a285a0df9e93 Mon Sep 17 00:00:00 2001 From: Folkert Date: Mon, 29 Jul 2024 21:54:40 +0200 Subject: [PATCH 1/2] add test for symbol visibility of `#[naked]` functions --- .../naked-symbol-visibility/a_rust_dylib.rs | 74 +++++++++++++++++++ .../run-make/naked-symbol-visibility/rmake.rs | 63 ++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 tests/run-make/naked-symbol-visibility/a_rust_dylib.rs create mode 100644 tests/run-make/naked-symbol-visibility/rmake.rs diff --git a/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs b/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs new file mode 100644 index 0000000000000..50cf4943d34c7 --- /dev/null +++ b/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs @@ -0,0 +1,74 @@ +#![feature(naked_functions, asm_const, linkage)] +#![crate_type = "dylib"] + +use std::arch::asm; + +pub trait TraitWithConst { + const COUNT: u32; +} + +struct Test; + +impl TraitWithConst for Test { + const COUNT: u32 = 1; +} + +#[no_mangle] +fn entry() { + private_vanilla_rust_function_from_rust_dylib(); + private_naked_rust_function_from_rust_dylib(); + + public_vanilla_generic_function_from_rust_dylib::(); + public_naked_generic_function_from_rust_dylib::(); +} + +extern "C" fn private_vanilla_rust_function_from_rust_dylib() -> u32 { + 42 +} + +#[no_mangle] +pub extern "C" fn public_vanilla_rust_function_from_rust_dylib() -> u32 { + 42 +} + +pub extern "C" fn public_vanilla_generic_function_from_rust_dylib() -> u32 { + T::COUNT +} + +#[linkage = "weak"] +extern "C" fn vanilla_weak_linkage() -> u32 { + 42 +} + +#[linkage = "external"] +extern "C" fn vanilla_external_linkage() -> u32 { + 42 +} + +#[naked] +extern "C" fn private_naked_rust_function_from_rust_dylib() -> u32 { + unsafe { asm!("mov rax, 42", "ret", options(noreturn)) } +} + +#[naked] +#[no_mangle] +pub extern "C" fn public_naked_rust_function_from_rust_dylib() -> u32 { + unsafe { asm!("mov rax, 42", "ret", options(noreturn)) } +} + +#[naked] +pub extern "C" fn public_naked_generic_function_from_rust_dylib() -> u32 { + unsafe { asm!("mov rax, {}", "ret", const T::COUNT, options(noreturn)) } +} + +#[naked] +#[linkage = "weak"] +extern "C" fn naked_weak_linkage() -> u32 { + unsafe { asm!("mov rax, 42", "ret", options(noreturn)) } +} + +#[naked] +#[linkage = "external"] +extern "C" fn naked_external_linkage() -> u32 { + unsafe { asm!("mov rax, 42", "ret", options(noreturn)) } +} diff --git a/tests/run-make/naked-symbol-visibility/rmake.rs b/tests/run-make/naked-symbol-visibility/rmake.rs new file mode 100644 index 0000000000000..c888e120113ef --- /dev/null +++ b/tests/run-make/naked-symbol-visibility/rmake.rs @@ -0,0 +1,63 @@ +// @only-x86_64 +use run_make_support::{dynamic_lib_name, llvm_readobj, regex, rustc}; + +fn main() { + let rdylib_name = dynamic_lib_name("a_rust_dylib"); + rustc().arg("-Zshare-generics=no").input("a_rust_dylib.rs").run(); + + // check vanilla symbols + not_exported(&rdylib_name, "private_vanilla_rust_function_from_rust_dylib"); + global_function(&rdylib_name, "public_vanilla_rust_function_from_rust_dylib"); + not_exported(&rdylib_name, "public_vanilla_generic_function_from_rust_dylib"); + + weak_function(&rdylib_name, "vanilla_weak_linkage"); + global_function(&rdylib_name, "vanilla_external_linkage"); + + // naked should mirror vanilla + not_exported(&rdylib_name, "private_naked_rust_function_from_rust_dylib"); + global_function(&rdylib_name, "public_naked_rust_function_from_rust_dylib"); + not_exported(&rdylib_name, "public_naked_generic_function_from_rust_dylib"); + + weak_function(&rdylib_name, "naked_weak_linkage"); + global_function(&rdylib_name, "naked_external_linkage"); + + // share generics should expose the generic functions + rustc().arg("-Zshare-generics=yes").input("a_rust_dylib.rs").run(); + global_function(&rdylib_name, "public_vanilla_generic_function_from_rust_dylib"); + global_function(&rdylib_name, "public_naked_generic_function_from_rust_dylib"); +} + +#[track_caller] +fn global_function(path: &str, symbol_name: &str) { + let lines = find_dynamic_symbol(path, symbol_name); + let [line] = lines.as_slice() else { + panic!("symbol {symbol_name} occurs {} times", lines.len()) + }; + + assert!(line.contains("FUNC"), "`{symbol_name}` is not a function"); + assert!(line.contains("GLOBAL"), "`{symbol_name}` is not marked as global"); +} + +#[track_caller] +fn weak_function(path: &str, symbol_name: &str) { + let lines = find_dynamic_symbol(path, symbol_name); + let [line] = lines.as_slice() else { + panic!("symbol {symbol_name} occurs {} times", lines.len()) + }; + + assert!(line.contains("FUNC"), "`{symbol_name}` is not a function"); + assert!(line.contains("WEAK"), "`{symbol_name}` is not marked as weak"); +} + +#[track_caller] +fn not_exported(path: &str, symbol_name: &str) { + assert_eq!(find_dynamic_symbol(path, symbol_name).len(), 0) +} + +fn find_dynamic_symbol<'a>(path: &str, symbol_name: &str) -> Vec { + let out = llvm_readobj().arg("--dyn-symbols").input(path).run().stdout_utf8(); + out.lines() + .filter(|&line| !line.contains("__imp_") && line.contains(symbol_name)) + .map(|line| line.to_string()) + .collect() +} From 940a1090fb36f7b78c9c346a50225cab57639764 Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 30 Jul 2024 10:28:41 +0200 Subject: [PATCH 2/2] add test that tracks that functions defined in extern blocks are not exported this maybe should change, but now at least it won't be changed unintentionally --- .../naked-symbol-visibility/a_rust_dylib.rs | 13 +++ .../run-make/naked-symbol-visibility/rmake.rs | 81 +++++++++++-------- 2 files changed, 62 insertions(+), 32 deletions(-) diff --git a/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs b/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs index 50cf4943d34c7..7e81718f76c0e 100644 --- a/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs +++ b/tests/run-make/naked-symbol-visibility/a_rust_dylib.rs @@ -72,3 +72,16 @@ extern "C" fn naked_weak_linkage() -> u32 { extern "C" fn naked_external_linkage() -> u32 { unsafe { asm!("mov rax, 42", "ret", options(noreturn)) } } + +// functions that are declared in an `extern "C"` block are currently not exported +// this maybe should change in the future, this is just tracking the current behavior +// reported in https://github.com/rust-lang/rust/issues/128071 +std::arch::global_asm! { + ".globl function_defined_in_global_asm", + "function_defined_in_global_asm:", + "ret", +} + +extern "C" { + pub fn function_defined_in_global_asm(); +} diff --git a/tests/run-make/naked-symbol-visibility/rmake.rs b/tests/run-make/naked-symbol-visibility/rmake.rs index c888e120113ef..2bc585b840dc5 100644 --- a/tests/run-make/naked-symbol-visibility/rmake.rs +++ b/tests/run-make/naked-symbol-visibility/rmake.rs @@ -1,63 +1,80 @@ // @only-x86_64 -use run_make_support::{dynamic_lib_name, llvm_readobj, regex, rustc}; +use run_make_support::object::read::{File, Object, Symbol}; +use run_make_support::object::ObjectSymbol; +use run_make_support::{dynamic_lib_name, rfs, rustc}; fn main() { let rdylib_name = dynamic_lib_name("a_rust_dylib"); rustc().arg("-Zshare-generics=no").input("a_rust_dylib.rs").run(); + let binary_data = rfs::read(&rdylib_name); + let rdylib = File::parse(&*binary_data).unwrap(); + // check vanilla symbols - not_exported(&rdylib_name, "private_vanilla_rust_function_from_rust_dylib"); - global_function(&rdylib_name, "public_vanilla_rust_function_from_rust_dylib"); - not_exported(&rdylib_name, "public_vanilla_generic_function_from_rust_dylib"); + not_exported(&rdylib, "private_vanilla_rust_function_from_rust_dylib"); + global_function(&rdylib, "public_vanilla_rust_function_from_rust_dylib"); + not_exported(&rdylib, "public_vanilla_generic_function_from_rust_dylib"); - weak_function(&rdylib_name, "vanilla_weak_linkage"); - global_function(&rdylib_name, "vanilla_external_linkage"); + weak_function(&rdylib, "vanilla_weak_linkage"); + global_function(&rdylib, "vanilla_external_linkage"); // naked should mirror vanilla - not_exported(&rdylib_name, "private_naked_rust_function_from_rust_dylib"); - global_function(&rdylib_name, "public_naked_rust_function_from_rust_dylib"); - not_exported(&rdylib_name, "public_naked_generic_function_from_rust_dylib"); + not_exported(&rdylib, "private_naked_rust_function_from_rust_dylib"); + global_function(&rdylib, "public_naked_rust_function_from_rust_dylib"); + not_exported(&rdylib, "public_naked_generic_function_from_rust_dylib"); + + weak_function(&rdylib, "naked_weak_linkage"); + global_function(&rdylib, "naked_external_linkage"); - weak_function(&rdylib_name, "naked_weak_linkage"); - global_function(&rdylib_name, "naked_external_linkage"); + // functions that are declared in an `extern "C"` block are currently not exported + // this maybe should change in the future, this is just tracking the current behavior + // reported in https://github.com/rust-lang/rust/issues/128071 + not_exported(&rdylib, "function_defined_in_global_asm"); // share generics should expose the generic functions rustc().arg("-Zshare-generics=yes").input("a_rust_dylib.rs").run(); - global_function(&rdylib_name, "public_vanilla_generic_function_from_rust_dylib"); - global_function(&rdylib_name, "public_naked_generic_function_from_rust_dylib"); + let binary_data = rfs::read(&rdylib_name); + let rdylib = File::parse(&*binary_data).unwrap(); + + global_function(&rdylib, "public_vanilla_generic_function_from_rust_dylib"); + global_function(&rdylib, "public_naked_generic_function_from_rust_dylib"); } #[track_caller] -fn global_function(path: &str, symbol_name: &str) { - let lines = find_dynamic_symbol(path, symbol_name); - let [line] = lines.as_slice() else { - panic!("symbol {symbol_name} occurs {} times", lines.len()) +fn global_function(file: &File, symbol_name: &str) { + let symbols = find_dynamic_symbol(file, symbol_name); + let [symbol] = symbols.as_slice() else { + panic!("symbol {symbol_name} occurs {} times", symbols.len()) }; - assert!(line.contains("FUNC"), "`{symbol_name}` is not a function"); - assert!(line.contains("GLOBAL"), "`{symbol_name}` is not marked as global"); + assert!(symbol.is_definition(), "`{symbol_name}` is not a function"); + assert!(symbol.is_global(), "`{symbol_name}` is not marked as global"); } #[track_caller] -fn weak_function(path: &str, symbol_name: &str) { - let lines = find_dynamic_symbol(path, symbol_name); - let [line] = lines.as_slice() else { - panic!("symbol {symbol_name} occurs {} times", lines.len()) +fn weak_function(file: &File, symbol_name: &str) { + let symbols = find_dynamic_symbol(file, symbol_name); + let [symbol] = symbols.as_slice() else { + panic!("symbol {symbol_name} occurs {} times", symbols.len()) }; - assert!(line.contains("FUNC"), "`{symbol_name}` is not a function"); - assert!(line.contains("WEAK"), "`{symbol_name}` is not marked as weak"); + assert!(symbol.is_definition(), "`{symbol_name}` is not a function"); + assert!(symbol.is_weak(), "`{symbol_name}` is not marked as weak"); } #[track_caller] -fn not_exported(path: &str, symbol_name: &str) { - assert_eq!(find_dynamic_symbol(path, symbol_name).len(), 0) +fn not_exported(file: &File, symbol_name: &str) { + assert_eq!(find_dynamic_symbol(file, symbol_name).len(), 0) } -fn find_dynamic_symbol<'a>(path: &str, symbol_name: &str) -> Vec { - let out = llvm_readobj().arg("--dyn-symbols").input(path).run().stdout_utf8(); - out.lines() - .filter(|&line| !line.contains("__imp_") && line.contains(symbol_name)) - .map(|line| line.to_string()) +fn find_dynamic_symbol<'file, 'data>( + file: &'file File<'data>, + expected: &str, +) -> Vec> { + file.dynamic_symbols() + .filter(|symbol| { + let name = symbol.name().unwrap(); + !name.contains("__imp_") && name.contains(expected) + }) .collect() }