@@ -28,7 +28,10 @@ macro_rules! intercept {
2828 #[ cfg( test) ]
2929 #[ test]
3030 fn symbol_64_does_not_exist( ) {
31- :: core:: assert_eq!( $crate:: macros:: symbol_exists( :: core:: stringify!( $name) ) , false ) ;
31+ :: core:: assert_eq!(
32+ $crate:: macros:: symbol_exists( :: core:: concat!( :: core:: stringify!( $name) , 64 ) ) ,
33+ false ,
34+ ) ;
3235 }
3336 }
3437 } ;
@@ -47,7 +50,7 @@ pub fn symbol_exists(name: &str) -> bool {
4750}
4851
4952macro_rules! intercept_inner {
50- ( $name: ident: $fn_sig: ty; $test_fn: item ) => {
53+ ( $name: ident: $fn_sig: ty; $test_fn: item) => {
5154 const _: $fn_sig = $name;
5255 const _: $fn_sig = $crate:: libc:: $name;
5356
@@ -66,17 +69,44 @@ macro_rules! intercept_inner {
6669 #[ expect( clippy:: allow_attributes, reason = "using allow because unused_imports may or may not fire depending on macro expansion" ) ]
6770 #[ allow( unused_imports, reason = "glob import brings types into scope for macro-generated code" ) ]
6871 use super :: * ;
72+ #[ expect(
73+ clippy:: allow_attributes,
74+ reason = "using allow because dead_code only fires for optional original symbols"
75+ ) ]
76+ #[ allow(
77+ dead_code,
78+ reason = "not every interposer forwards to its generated original function"
79+ ) ]
6980 pub unsafe fn original( ) -> $fn_sig {
70- static LAZY : std:: sync:: LazyLock <$fn_sig> = std:: sync:: LazyLock :: new( ||
71- // SAFETY: dlsym with RTLD_NEXT returns the next symbol in the dynamic linking order,
72- // and transmute converts the resulting function pointer to the expected function signature.
73- // The caller guarantees the symbol name matches the expected function signature via the macro invocation.
74- unsafe {
75- :: core:: mem:: transmute( :: libc:: dlsym(
76- :: libc:: RTLD_NEXT ,
77- :: core:: concat!( :: core:: stringify!( $name) , "\0 " ) . as_ptr( ) . cast( ) ,
81+ try_original( ) . unwrap_or_else( || {
82+ panic!( :: core:: concat!(
83+ "original symbol not found: " ,
84+ :: core:: stringify!( $name)
7885 ) )
79- } ) ;
86+ } )
87+ }
88+ pub fn try_original( ) -> :: core:: option:: Option <$fn_sig> {
89+ static LAZY : std:: sync:: LazyLock <:: core:: option:: Option <$fn_sig>> =
90+ std:: sync:: LazyLock :: new( || {
91+ // SAFETY: dlsym with RTLD_NEXT returns the next symbol in the dynamic
92+ // linking order. A non-null pointer has the signature checked by the
93+ // macro invocation.
94+ let symbol = unsafe {
95+ :: libc:: dlsym(
96+ :: libc:: RTLD_NEXT ,
97+ :: core:: concat!( :: core:: stringify!( $name) , "\0 " ) . as_ptr( ) . cast( ) ,
98+ )
99+ } ;
100+ if symbol. is_null( ) {
101+ :: core:: option:: Option :: None
102+ } else {
103+ // SAFETY: the symbol name and function signature are paired by the
104+ // macro invocation, and null was checked above.
105+ :: core:: option:: Option :: Some ( unsafe {
106+ :: core:: mem:: transmute:: <* mut :: libc:: c_void, $fn_sig>( symbol)
107+ } )
108+ }
109+ } ) ;
80110 * LAZY
81111 }
82112 $test_fn
0 commit comments