@@ -60,66 +60,76 @@ pub fn make_target_bin_path(sysroot: &Path, target_triple: &str) -> PathBuf {
60
60
61
61
#[ cfg( unix) ]
62
62
fn 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 ( ) ) ;
102
80
}
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 ( ) ;
112
85
let os = OsStr :: from_bytes ( bytes) ;
113
- return Ok ( PathBuf :: from ( os) ) ;
86
+ Ok ( PathBuf :: from ( os) )
114
87
}
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) ) ;
117
130
}
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 ( )
123
133
}
124
134
125
135
#[ cfg( windows) ]
0 commit comments