11//! A module for searching for libraries
22
3+ use smallvec:: { smallvec, SmallVec } ;
34use std:: env;
45use std:: fs;
56use std:: iter:: FromIterator ;
@@ -62,9 +63,99 @@ pub fn make_target_lib_path(sysroot: &Path, target_triple: &str) -> PathBuf {
6263 PathBuf :: from_iter ( [ sysroot, Path :: new ( & rustlib_path) , Path :: new ( "lib" ) ] )
6364}
6465
66+ #[ cfg( unix) ]
67+ fn current_dll_path ( ) -> Result < PathBuf , String > {
68+ use std:: ffi:: { CStr , OsStr } ;
69+ use std:: os:: unix:: prelude:: * ;
70+
71+ unsafe {
72+ let addr = current_dll_path as usize as * mut _ ;
73+ let mut info = std:: mem:: zeroed ( ) ;
74+ if libc:: dladdr ( addr, & mut info) == 0 {
75+ return Err ( "dladdr failed" . into ( ) ) ;
76+ }
77+ if info. dli_fname . is_null ( ) {
78+ return Err ( "dladdr returned null pointer" . into ( ) ) ;
79+ }
80+ let bytes = CStr :: from_ptr ( info. dli_fname ) . to_bytes ( ) ;
81+ let os = OsStr :: from_bytes ( bytes) ;
82+ Ok ( PathBuf :: from ( os) )
83+ }
84+ }
85+
86+ #[ cfg( windows) ]
87+ fn current_dll_path ( ) -> Result < PathBuf , String > {
88+ use std:: ffi:: OsString ;
89+ use std:: io;
90+ use std:: os:: windows:: prelude:: * ;
91+ use std:: ptr;
92+
93+ use winapi:: um:: libloaderapi:: {
94+ GetModuleFileNameW , GetModuleHandleExW , GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS ,
95+ } ;
96+
97+ unsafe {
98+ let mut module = ptr:: null_mut ( ) ;
99+ let r = GetModuleHandleExW (
100+ GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS ,
101+ current_dll_path as usize as * mut _ ,
102+ & mut module,
103+ ) ;
104+ if r == 0 {
105+ return Err ( format ! ( "GetModuleHandleExW failed: {}" , io:: Error :: last_os_error( ) ) ) ;
106+ }
107+ let mut space = Vec :: with_capacity ( 1024 ) ;
108+ let r = GetModuleFileNameW ( module, space. as_mut_ptr ( ) , space. capacity ( ) as u32 ) ;
109+ if r == 0 {
110+ return Err ( format ! ( "GetModuleFileNameW failed: {}" , io:: Error :: last_os_error( ) ) ) ;
111+ }
112+ let r = r as usize ;
113+ if r >= space. capacity ( ) {
114+ return Err ( format ! ( "our buffer was too small? {}" , io:: Error :: last_os_error( ) ) ) ;
115+ }
116+ space. set_len ( r) ;
117+ let os = OsString :: from_wide ( & space) ;
118+ Ok ( PathBuf :: from ( os) )
119+ }
120+ }
121+
122+ pub fn sysroot_candidates ( ) -> SmallVec < [ PathBuf ; 2 ] > {
123+ let target = crate :: config:: host_triple ( ) ;
124+ let mut sysroot_candidates: SmallVec < [ PathBuf ; 2 ] > =
125+ smallvec ! [ get_or_default_sysroot( ) . expect( "Failed finding sysroot" ) ] ;
126+ let path = current_dll_path ( ) . and_then ( |s| Ok ( s. canonicalize ( ) . map_err ( |e| e. to_string ( ) ) ?) ) ;
127+ if let Ok ( dll) = path {
128+ // use `parent` twice to chop off the file name and then also the
129+ // directory containing the dll which should be either `lib` or `bin`.
130+ if let Some ( path) = dll. parent ( ) . and_then ( |p| p. parent ( ) ) {
131+ // The original `path` pointed at the `rustc_driver` crate's dll.
132+ // Now that dll should only be in one of two locations. The first is
133+ // in the compiler's libdir, for example `$sysroot/lib/*.dll`. The
134+ // other is the target's libdir, for example
135+ // `$sysroot/lib/rustlib/$target/lib/*.dll`.
136+ //
137+ // We don't know which, so let's assume that if our `path` above
138+ // ends in `$target` we *could* be in the target libdir, and always
139+ // assume that we may be in the main libdir.
140+ sysroot_candidates. push ( path. to_owned ( ) ) ;
141+
142+ if path. ends_with ( target) {
143+ sysroot_candidates. extend (
144+ path. parent ( ) // chop off `$target`
145+ . and_then ( |p| p. parent ( ) ) // chop off `rustlib`
146+ . and_then ( |p| p. parent ( ) ) // chop off `lib`
147+ . map ( |s| s. to_owned ( ) ) ,
148+ ) ;
149+ }
150+ }
151+ }
152+
153+ return sysroot_candidates;
154+ }
155+
65156/// This function checks if sysroot is found using env::args().next(), and if it
66- /// is not found, uses env::current_exe() to imply sysroot .
67- pub fn get_or_default_sysroot ( ) -> PathBuf {
157+ /// is not found, finds sysroot from current rustc_driver dll .
158+ pub fn get_or_default_sysroot ( ) -> Result < PathBuf , String > {
68159 // Follow symlinks. If the resolved path is relative, make it absolute.
69160 fn canonicalize ( path : PathBuf ) -> PathBuf {
70161 let path = fs:: canonicalize ( & path) . unwrap_or ( path) ;
@@ -74,17 +165,32 @@ pub fn get_or_default_sysroot() -> PathBuf {
74165 fix_windows_verbatim_for_gcc ( & path)
75166 }
76167
77- // Use env::current_exe() to get the path of the executable following
78- // symlinks/canonicalizing components.
79- fn from_current_exe ( ) -> PathBuf {
80- match env:: current_exe ( ) {
81- Ok ( exe) => {
82- let mut p = canonicalize ( exe) ;
83- p. pop ( ) ;
84- p. pop ( ) ;
85- p
86- }
87- Err ( e) => panic ! ( "failed to get current_exe: {e}" ) ,
168+ fn default_from_rustc_driver_dll ( ) -> Result < PathBuf , String > {
169+ let dll = current_dll_path ( ) . and_then ( |s| Ok ( canonicalize ( s) ) ) ?;
170+
171+ // `dll` will be in one of the following two:
172+ // - compiler's libdir: $sysroot/lib/*.dll
173+ // - target's libdir: $sysroot/lib/rustlib/$target/lib/*.dll
174+ //
175+ // use `parent` twice to chop off the file name and then also the
176+ // directory containing the dll
177+ let dir = dll. parent ( ) . and_then ( |p| p. parent ( ) ) . ok_or ( format ! (
178+ "Could not move 2 levels upper using `parent()` on {}" ,
179+ dll. display( )
180+ ) ) ?;
181+
182+ // if `dir` points target's dir, move up to the sysroot
183+ if dir. ends_with ( crate :: config:: host_triple ( ) ) {
184+ dir. parent ( ) // chop off `$target`
185+ . and_then ( |p| p. parent ( ) ) // chop off `rustlib`
186+ . and_then ( |p| p. parent ( ) ) // chop off `lib`
187+ . map ( |s| s. to_owned ( ) )
188+ . ok_or ( format ! (
189+ "Could not move 3 levels upper using `parent()` on {}" ,
190+ dir. display( )
191+ ) )
192+ } else {
193+ Ok ( dir. to_owned ( ) )
88194 }
89195 }
90196
@@ -118,7 +224,5 @@ pub fn get_or_default_sysroot() -> PathBuf {
118224 }
119225 }
120226
121- // Check if sysroot is found using env::args().next(), and if is not found,
122- // use env::current_exe() to imply sysroot.
123- from_env_args_next ( ) . unwrap_or_else ( from_current_exe)
227+ Ok ( from_env_args_next ( ) . unwrap_or ( default_from_rustc_driver_dll ( ) ?) )
124228}
0 commit comments