@@ -146,86 +146,78 @@ pub fn make_target_bin_path(sysroot: &Path, target_triple: &str) -> PathBuf {
146146 sysroot. join ( rustlib_path) . join ( "bin" )
147147}
148148
149+ /// Attempts to find the path to the dynamic library containing a function.
150+ ///
151+ /// SAFETY: `function` must be a valid pointer to some function.
149152#[ cfg( unix) ]
150- fn current_dll_path ( ) -> Result < PathBuf , String > {
151- use std:: sync:: OnceLock ;
153+ pub unsafe fn dll_path ( function : * mut std:: ffi:: c_void ) -> Result < PathBuf , String > {
154+ use std:: ffi:: { CStr , OsStr } ;
155+ use std:: os:: unix:: prelude:: * ;
152156
153- // This is somewhat expensive relative to other work when compiling `fn main() {}` as `dladdr`
154- // needs to iterate over the symbol table of librustc_driver.so until it finds a match.
155- // As such cache this to avoid recomputing if we try to get the sysroot in multiple places.
156- static CURRENT_DLL_PATH : OnceLock < Result < PathBuf , String > > = OnceLock :: new ( ) ;
157- CURRENT_DLL_PATH
158- . get_or_init ( || {
159- use std:: ffi:: { CStr , OsStr } ;
160- use std:: os:: unix:: prelude:: * ;
161-
162- #[ cfg( not( target_os = "aix" ) ) ]
163- unsafe {
164- let addr = current_dll_path as fn ( ) -> Result < PathBuf , String > as * mut _ ;
165- let mut info = std:: mem:: zeroed ( ) ;
166- if libc:: dladdr ( addr, & mut info) == 0 {
167- return Err ( "dladdr failed" . into ( ) ) ;
157+ #[ cfg( not( target_os = "aix" ) ) ]
158+ unsafe {
159+ let mut info = std:: mem:: zeroed ( ) ;
160+ if libc:: dladdr ( function, & mut info) == 0 {
161+ return Err ( "dladdr failed" . into ( ) ) ;
162+ }
163+ #[ cfg( target_os = "cygwin" ) ]
164+ let fname_ptr = info. dli_fname . as_ptr ( ) ;
165+ #[ cfg( not( target_os = "cygwin" ) ) ]
166+ let fname_ptr = {
167+ assert ! ( !info. dli_fname. is_null( ) , "dli_fname cannot be null" ) ;
168+ info. dli_fname
169+ } ;
170+ let bytes = CStr :: from_ptr ( fname_ptr) . to_bytes ( ) ;
171+ let os = OsStr :: from_bytes ( bytes) ;
172+ try_canonicalize ( Path :: new ( os) ) . map_err ( |e| e. to_string ( ) )
173+ }
174+
175+ #[ cfg( target_os = "aix" ) ]
176+ unsafe {
177+ // On AIX, the symbol references a function descriptor.
178+ // A function descriptor is consisted of (See https://reviews.llvm.org/D62532)
179+ // * The address of the entry point of the function.
180+ // * The TOC base address for the function.
181+ // * The environment pointer.
182+ // The function descriptor is in the data section.
183+ let addr = function as u64 ;
184+ let mut buffer = vec ! [ std:: mem:: zeroed:: <libc:: ld_info>( ) ; 64 ] ;
185+ loop {
186+ if libc:: loadquery (
187+ libc:: L_GETINFO ,
188+ buffer. as_mut_ptr ( ) as * mut libc:: c_void ,
189+ ( size_of :: < libc:: ld_info > ( ) * buffer. len ( ) ) as u32 ,
190+ ) >= 0
191+ {
192+ break ;
193+ } else {
194+ if std:: io:: Error :: last_os_error ( ) . raw_os_error ( ) . unwrap ( ) != libc:: ENOMEM {
195+ return Err ( "loadquery failed" . into ( ) ) ;
168196 }
169- #[ cfg( target_os = "cygwin" ) ]
170- let fname_ptr = info. dli_fname . as_ptr ( ) ;
171- #[ cfg( not( target_os = "cygwin" ) ) ]
172- let fname_ptr = {
173- assert ! ( !info. dli_fname. is_null( ) , "dli_fname cannot be null" ) ;
174- info. dli_fname
175- } ;
176- let bytes = CStr :: from_ptr ( fname_ptr) . to_bytes ( ) ;
197+ buffer. resize ( buffer. len ( ) * 2 , std:: mem:: zeroed :: < libc:: ld_info > ( ) ) ;
198+ }
199+ }
200+ let mut current = buffer. as_mut_ptr ( ) as * mut libc:: ld_info ;
201+ loop {
202+ let data_base = ( * current) . ldinfo_dataorg as u64 ;
203+ let data_end = data_base + ( * current) . ldinfo_datasize ;
204+ if ( data_base..data_end) . contains ( & addr) {
205+ let bytes = CStr :: from_ptr ( & ( * current) . ldinfo_filename [ 0 ] ) . to_bytes ( ) ;
177206 let os = OsStr :: from_bytes ( bytes) ;
178- try_canonicalize ( Path :: new ( os) ) . map_err ( |e| e. to_string ( ) )
207+ return try_canonicalize ( Path :: new ( os) ) . map_err ( |e| e. to_string ( ) ) ;
179208 }
180-
181- #[ cfg( target_os = "aix" ) ]
182- unsafe {
183- // On AIX, the symbol `current_dll_path` references a function descriptor.
184- // A function descriptor is consisted of (See https://reviews.llvm.org/D62532)
185- // * The address of the entry point of the function.
186- // * The TOC base address for the function.
187- // * The environment pointer.
188- // The function descriptor is in the data section.
189- let addr = current_dll_path as u64 ;
190- let mut buffer = vec ! [ std:: mem:: zeroed:: <libc:: ld_info>( ) ; 64 ] ;
191- loop {
192- if libc:: loadquery (
193- libc:: L_GETINFO ,
194- buffer. as_mut_ptr ( ) as * mut libc:: c_void ,
195- ( size_of :: < libc:: ld_info > ( ) * buffer. len ( ) ) as u32 ,
196- ) >= 0
197- {
198- break ;
199- } else {
200- if std:: io:: Error :: last_os_error ( ) . raw_os_error ( ) . unwrap ( ) != libc:: ENOMEM {
201- return Err ( "loadquery failed" . into ( ) ) ;
202- }
203- buffer. resize ( buffer. len ( ) * 2 , std:: mem:: zeroed :: < libc:: ld_info > ( ) ) ;
204- }
205- }
206- let mut current = buffer. as_mut_ptr ( ) as * mut libc:: ld_info ;
207- loop {
208- let data_base = ( * current) . ldinfo_dataorg as u64 ;
209- let data_end = data_base + ( * current) . ldinfo_datasize ;
210- if ( data_base..data_end) . contains ( & addr) {
211- let bytes = CStr :: from_ptr ( & ( * current) . ldinfo_filename [ 0 ] ) . to_bytes ( ) ;
212- let os = OsStr :: from_bytes ( bytes) ;
213- return try_canonicalize ( Path :: new ( os) ) . map_err ( |e| e. to_string ( ) ) ;
214- }
215- if ( * current) . ldinfo_next == 0 {
216- break ;
217- }
218- current = ( current as * mut i8 ) . offset ( ( * current) . ldinfo_next as isize )
219- as * mut libc:: ld_info ;
220- }
221- return Err ( format ! ( "current dll's address {} is not in the load map" , addr) ) ;
209+ if ( * current) . ldinfo_next == 0 {
210+ break ;
222211 }
223- } )
224- . clone ( )
212+ current =
213+ ( current as * mut i8 ) . offset ( ( * current) . ldinfo_next as isize ) as * mut libc:: ld_info ;
214+ }
215+ return Err ( format ! ( "current dll's address {} is not in the load map" , addr) ) ;
216+ }
225217}
226218
227219#[ cfg( windows) ]
228- fn current_dll_path ( ) -> Result < PathBuf , String > {
220+ pub unsafe fn dll_path ( function : * mut std :: ffi :: c_void ) -> Result < PathBuf , String > {
229221 use std:: ffi:: OsString ;
230222 use std:: io;
231223 use std:: os:: windows:: prelude:: * ;
@@ -240,10 +232,7 @@ fn current_dll_path() -> Result<PathBuf, String> {
240232 unsafe {
241233 GetModuleHandleExW (
242234 GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS ,
243- PCWSTR (
244- current_dll_path as fn ( ) -> Result < std:: path:: PathBuf , std:: string:: String >
245- as * mut u16 ,
246- ) ,
235+ PCWSTR ( function as * mut u16 ) ,
247236 & mut module,
248237 )
249238 }
@@ -269,8 +258,20 @@ fn current_dll_path() -> Result<PathBuf, String> {
269258}
270259
271260#[ cfg( target_os = "wasi" ) ]
261+ pub unsafe fn dll_path ( function : * mut std:: ffi:: c_void ) -> Result < PathBuf , String > {
262+ Err ( "dll_path is not supported on WASI" . to_string ( ) )
263+ }
264+
272265fn current_dll_path ( ) -> Result < PathBuf , String > {
273- Err ( "current_dll_path is not supported on WASI" . to_string ( ) )
266+ use std:: sync:: OnceLock ;
267+
268+ // This is somewhat expensive relative to other work when compiling `fn main() {}` as `dladdr`
269+ // needs to iterate over the symbol table of librustc_driver.so until it finds a match.
270+ // As such cache this to avoid recomputing if we try to get the sysroot in multiple places.
271+ static CURRENT_DLL_PATH : OnceLock < Result < PathBuf , String > > = OnceLock :: new ( ) ;
272+ CURRENT_DLL_PATH
273+ . get_or_init ( || unsafe { dll_path ( current_dll_path as fn ( ) -> _ as * mut _ ) } )
274+ . clone ( )
274275}
275276
276277/// This function checks if sysroot is found using env::args().next(), and if it
0 commit comments