@@ -13,6 +13,16 @@ use std::{io, sync::OnceLock};
1313use super :: super :: UNPUBLISHED_OR_UPDATING ;
1414use super :: { HeaderMemoryHolder , MappingHeader , MonotonicTime } ;
1515
16+ #[ cfg( target_env = "msvc" ) ]
17+ #[ used]
18+ #[ link_section = ".drectve" ]
19+ static EXPORT_OTEL_PROCESS_CTX_V2 : [ u8 ; 28 ] = * b" /EXPORT:otel_process_ctx_v2" ;
20+
21+ #[ cfg( target_env = "gnu" ) ]
22+ #[ used]
23+ #[ link_section = ".drectve" ]
24+ static EXPORT_OTEL_PROCESS_CTX_V2 : [ u8 ; 28 ] = * b" -export:otel_process_ctx_v2" ;
25+
1626#[ no_mangle]
1727#[ allow( non_upper_case_globals) ]
1828pub static otel_process_ctx_v2: AtomicPtr < u8 > = AtomicPtr :: new ( ptr:: null_mut ( ) ) ;
@@ -107,3 +117,32 @@ fn last_error(context: &'static str) -> io::Error {
107117 let error = io:: Error :: last_os_error ( ) ;
108118 io:: Error :: new ( error. kind ( ) , format ! ( "{context}: {error}" ) )
109119}
120+
121+ #[ cfg( test) ]
122+ mod tests {
123+ use core:: { ffi:: c_void, ptr} ;
124+
125+ use super :: otel_process_ctx_v2;
126+
127+ type Handle = * mut c_void ;
128+
129+ #[ link( name = "kernel32" ) ]
130+ unsafe extern "system" {
131+ fn GetModuleHandleW ( module_name : * const u16 ) -> Handle ;
132+ fn GetProcAddress ( module : Handle , proc_name : * const u8 ) -> * mut c_void ;
133+ }
134+
135+ #[ test]
136+ fn exports_process_context_global ( ) {
137+ // SAFETY: a null module name requests the current executable's module handle.
138+ let module = unsafe { GetModuleHandleW ( ptr:: null ( ) ) } ;
139+ assert ! ( !module. is_null( ) ) ;
140+
141+ // SAFETY: module is the current executable and the symbol name is NUL-terminated.
142+ let symbol = unsafe { GetProcAddress ( module, c"otel_process_ctx_v2" . as_ptr ( ) . cast ( ) ) } ;
143+ assert_eq ! (
144+ symbol. cast_const( ) ,
145+ ptr:: from_ref( & otel_process_ctx_v2) . cast:: <c_void>( )
146+ ) ;
147+ }
148+ }
0 commit comments