1
1
//! A module for searching for libraries
2
2
3
+ use smallvec:: { smallvec, SmallVec } ;
3
4
use std:: env;
4
5
use std:: fs;
5
6
use std:: iter:: FromIterator ;
@@ -62,9 +63,99 @@ pub fn make_target_lib_path(sysroot: &Path, target_triple: &str) -> PathBuf {
62
63
PathBuf :: from_iter ( [ sysroot, Path :: new ( & rustlib_path) , Path :: new ( "lib" ) ] )
63
64
}
64
65
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
+
65
156
/// 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 > {
68
159
// Follow symlinks. If the resolved path is relative, make it absolute.
69
160
fn canonicalize ( path : PathBuf ) -> PathBuf {
70
161
let path = fs:: canonicalize ( & path) . unwrap_or ( path) ;
@@ -74,17 +165,32 @@ pub fn get_or_default_sysroot() -> PathBuf {
74
165
fix_windows_verbatim_for_gcc ( & path)
75
166
}
76
167
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 ( ) )
88
194
}
89
195
}
90
196
@@ -118,7 +224,5 @@ pub fn get_or_default_sysroot() -> PathBuf {
118
224
}
119
225
}
120
226
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 ( ) ?) )
124
228
}
0 commit comments