add test for symbol visibility of #[naked] functions

This commit is contained in:
Folkert 2024-07-29 21:54:40 +02:00
parent a886938671
commit 19679510c7
No known key found for this signature in database
GPG Key ID: 1F17F6FFD112B97C
2 changed files with 187 additions and 0 deletions

View File

@ -0,0 +1,89 @@
#![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();
private_naked();
public_vanilla_generic::<Test>();
public_naked_generic::<Test>();
}
extern "C" fn private_vanilla() -> u32 {
42
}
#[naked]
extern "C" fn private_naked() -> u32 {
unsafe { asm!("mov rax, 42", "ret", options(noreturn)) }
}
#[no_mangle]
pub extern "C" fn public_vanilla() -> u32 {
42
}
#[naked]
#[no_mangle]
pub extern "C" fn public_naked() -> u32 {
unsafe { asm!("mov rax, 42", "ret", options(noreturn)) }
}
pub extern "C" fn public_vanilla_generic<T: TraitWithConst>() -> u32 {
T::COUNT
}
#[naked]
pub extern "C" fn public_naked_generic<T: TraitWithConst>() -> u32 {
unsafe { asm!("mov rax, {}", "ret", const T::COUNT, options(noreturn)) }
}
#[linkage = "external"]
extern "C" fn vanilla_external_linkage() -> u32 {
42
}
#[naked]
#[linkage = "external"]
extern "C" fn naked_external_linkage() -> u32 {
unsafe { asm!("mov rax, 42", "ret", options(noreturn)) }
}
#[cfg(not(windows))]
#[linkage = "weak"]
extern "C" fn vanilla_weak_linkage() -> u32 {
42
}
#[naked]
#[cfg(not(windows))]
#[linkage = "weak"]
extern "C" fn naked_weak_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();
}

View File

@ -0,0 +1,98 @@
//@ ignore-windows
//@ only-x86_64
use run_make_support::object::read::{File, Object, Symbol};
use run_make_support::object::ObjectSymbol;
use run_make_support::targets::is_windows;
use run_make_support::{dynamic_lib_name, env_var, 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();
// naked should mirror vanilla
not_exported(&rdylib, "private_vanilla");
not_exported(&rdylib, "private_naked");
global_function(&rdylib, "public_vanilla");
global_function(&rdylib, "public_naked");
not_exported(&rdylib, "public_vanilla_generic");
not_exported(&rdylib, "public_naked_generic");
global_function(&rdylib, "vanilla_external_linkage");
global_function(&rdylib, "naked_external_linkage");
// FIXME: make this work on windows (gnu and msvc). See the PR
// https://github.com/rust-lang/rust/pull/128362 for some approaches
// that don't work
//
// #[linkage = "weak"] does not work well on windows, we get
//
// lib.def : error LNK2001: unresolved external symbol naked_weak_linkage␍
// lib.def : error LNK2001: unresolved external symbol vanilla_weak_linkage
//
// so just skip weak symbols on windows (for now)
if !is_windows() {
weak_function(&rdylib, "vanilla_weak_linkage");
weak_function(&rdylib, "naked_weak_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();
let binary_data = rfs::read(&rdylib_name);
let rdylib = File::parse(&*binary_data).unwrap();
global_function(&rdylib, "public_vanilla_generic");
global_function(&rdylib, "public_naked_generic");
}
#[track_caller]
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!(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(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!(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(file: &File, symbol_name: &str) {
assert_eq!(find_dynamic_symbol(file, symbol_name).len(), 0)
}
fn find_subsequence(haystack: &[u8], needle: &[u8]) -> bool {
haystack.windows(needle.len()).any(|window| window == needle)
}
fn find_dynamic_symbol<'file, 'data>(
file: &'file File<'data>,
expected: &str,
) -> Vec<Symbol<'data, 'file>> {
file.exports()
.unwrap()
.into_iter()
.filter(|e| find_subsequence(e.name(), expected.as_bytes()))
.filter_map(|e| file.symbol_by_name_bytes(e.name()))
.collect()
}