@@ -60,66 +60,76 @@ pub fn make_target_bin_path(sysroot: &Path, target_triple: &str) -> PathBuf {
6060
6161#[ cfg( unix) ]
6262fn current_dll_path ( ) -> Result < PathBuf , String > {
63- use std:: ffi:: { CStr , OsStr } ;
64- use std:: os:: unix:: prelude:: * ;
65-
66- #[ cfg( not( target_os = "aix" ) ) ]
67- unsafe {
68- let addr = current_dll_path as usize as * mut _ ;
69- let mut info = std:: mem:: zeroed ( ) ;
70- if libc:: dladdr ( addr, & mut info) == 0 {
71- return Err ( "dladdr failed" . into ( ) ) ;
72- }
73- if info. dli_fname . is_null ( ) {
74- return Err ( "dladdr returned null pointer" . into ( ) ) ;
75- }
76- let bytes = CStr :: from_ptr ( info. dli_fname ) . to_bytes ( ) ;
77- let os = OsStr :: from_bytes ( bytes) ;
78- Ok ( PathBuf :: from ( os) )
79- }
80-
81- #[ cfg( target_os = "aix" ) ]
82- unsafe {
83- // On AIX, the symbol `current_dll_path` references a function descriptor.
84- // A function descriptor is consisted of (See https://reviews.llvm.org/D62532)
85- // * The address of the entry point of the function.
86- // * The TOC base address for the function.
87- // * The environment pointer.
88- // The function descriptor is in the data section.
89- let addr = current_dll_path as u64 ;
90- let mut buffer = vec ! [ std:: mem:: zeroed:: <libc:: ld_info>( ) ; 64 ] ;
91- loop {
92- if libc:: loadquery (
93- libc:: L_GETINFO ,
94- buffer. as_mut_ptr ( ) as * mut u8 ,
95- ( size_of :: < libc:: ld_info > ( ) * buffer. len ( ) ) as u32 ,
96- ) >= 0
97- {
98- break ;
99- } else {
100- if std:: io:: Error :: last_os_error ( ) . raw_os_error ( ) . unwrap ( ) != libc:: ENOMEM {
101- return Err ( "loadquery failed" . into ( ) ) ;
63+ use std:: sync:: OnceLock ;
64+
65+ // This is somewhat expensive relative to other work when compiling `fn main() {}` as `dladdr`
66+ // needs to iterate over the symbol table of librustc_driver.so until it finds a match.
67+ // As such cache this to avoid recomputing if we try to get the sysroot in multiple places.
68+ static CURRENT_DLL_PATH : OnceLock < Result < PathBuf , String > > = OnceLock :: new ( ) ;
69+ CURRENT_DLL_PATH
70+ . get_or_init ( || {
71+ use std:: ffi:: { CStr , OsStr } ;
72+ use std:: os:: unix:: prelude:: * ;
73+
74+ #[ cfg( not( target_os = "aix" ) ) ]
75+ unsafe {
76+ let addr = current_dll_path as usize as * mut _ ;
77+ let mut info = std:: mem:: zeroed ( ) ;
78+ if libc:: dladdr ( addr, & mut info) == 0 {
79+ return Err ( "dladdr failed" . into ( ) ) ;
10280 }
103- buffer. resize ( buffer. len ( ) * 2 , std:: mem:: zeroed :: < libc:: ld_info > ( ) ) ;
104- }
105- }
106- let mut current = buffer. as_mut_ptr ( ) as * mut libc:: ld_info ;
107- loop {
108- let data_base = ( * current) . ldinfo_dataorg as u64 ;
109- let data_end = data_base + ( * current) . ldinfo_datasize ;
110- if ( data_base..data_end) . contains ( & addr) {
111- let bytes = CStr :: from_ptr ( & ( * current) . ldinfo_filename [ 0 ] ) . to_bytes ( ) ;
81+ if info. dli_fname . is_null ( ) {
82+ return Err ( "dladdr returned null pointer" . into ( ) ) ;
83+ }
84+ let bytes = CStr :: from_ptr ( info. dli_fname ) . to_bytes ( ) ;
11285 let os = OsStr :: from_bytes ( bytes) ;
113- return Ok ( PathBuf :: from ( os) ) ;
86+ Ok ( PathBuf :: from ( os) )
11487 }
115- if ( * current) . ldinfo_next == 0 {
116- break ;
88+
89+ #[ cfg( target_os = "aix" ) ]
90+ unsafe {
91+ // On AIX, the symbol `current_dll_path` references a function descriptor.
92+ // A function descriptor is consisted of (See https://reviews.llvm.org/D62532)
93+ // * The address of the entry point of the function.
94+ // * The TOC base address for the function.
95+ // * The environment pointer.
96+ // The function descriptor is in the data section.
97+ let addr = current_dll_path as u64 ;
98+ let mut buffer = vec ! [ std:: mem:: zeroed:: <libc:: ld_info>( ) ; 64 ] ;
99+ loop {
100+ if libc:: loadquery (
101+ libc:: L_GETINFO ,
102+ buffer. as_mut_ptr ( ) as * mut u8 ,
103+ ( size_of :: < libc:: ld_info > ( ) * buffer. len ( ) ) as u32 ,
104+ ) >= 0
105+ {
106+ break ;
107+ } else {
108+ if std:: io:: Error :: last_os_error ( ) . raw_os_error ( ) . unwrap ( ) != libc:: ENOMEM {
109+ return Err ( "loadquery failed" . into ( ) ) ;
110+ }
111+ buffer. resize ( buffer. len ( ) * 2 , std:: mem:: zeroed :: < libc:: ld_info > ( ) ) ;
112+ }
113+ }
114+ let mut current = buffer. as_mut_ptr ( ) as * mut libc:: ld_info ;
115+ loop {
116+ let data_base = ( * current) . ldinfo_dataorg as u64 ;
117+ let data_end = data_base + ( * current) . ldinfo_datasize ;
118+ if ( data_base..data_end) . contains ( & addr) {
119+ let bytes = CStr :: from_ptr ( & ( * current) . ldinfo_filename [ 0 ] ) . to_bytes ( ) ;
120+ let os = OsStr :: from_bytes ( bytes) ;
121+ return Ok ( PathBuf :: from ( os) ) ;
122+ }
123+ if ( * current) . ldinfo_next == 0 {
124+ break ;
125+ }
126+ current = ( current as * mut i8 ) . offset ( ( * current) . ldinfo_next as isize )
127+ as * mut libc:: ld_info ;
128+ }
129+ return Err ( format ! ( "current dll's address {} is not in the load map" , addr) ) ;
117130 }
118- current =
119- ( current as * mut i8 ) . offset ( ( * current) . ldinfo_next as isize ) as * mut libc:: ld_info ;
120- }
121- return Err ( format ! ( "current dll's address {} is not in the load map" , addr) ) ;
122- }
131+ } )
132+ . clone ( )
123133}
124134
125135#[ cfg( windows) ]
0 commit comments