]>
Commit | Line | Data |
---|---|---|
c295e0f8 XL |
1 | //! A module for searching for libraries |
2 | ||
353b0b11 | 3 | use rustc_fs_util::try_canonicalize; |
487cf647 | 4 | use smallvec::{smallvec, SmallVec}; |
85aaf69f | 5 | use std::env; |
c34b1796 | 6 | use std::fs; |
c34b1796 | 7 | use std::path::{Path, PathBuf}; |
1a4d82fc | 8 | |
5099ac24 | 9 | use crate::search_paths::{PathKind, SearchPath}; |
dfeec247 | 10 | use rustc_fs_util::fix_windows_verbatim_for_gcc; |
1a4d82fc | 11 | |
c34b1796 | 12 | #[derive(Copy, Clone)] |
1a4d82fc JJ |
13 | pub enum FileMatch { |
14 | FileMatches, | |
15 | FileDoesntMatch, | |
16 | } | |
970d7e83 | 17 | |
532ac7d7 | 18 | #[derive(Clone)] |
1a4d82fc | 19 | pub struct FileSearch<'a> { |
0731742a XL |
20 | sysroot: &'a Path, |
21 | triple: &'a str, | |
22 | search_paths: &'a [SearchPath], | |
23 | tlib_path: &'a SearchPath, | |
24 | kind: PathKind, | |
223e47cc LB |
25 | } |
26 | ||
1a4d82fc | 27 | impl<'a> FileSearch<'a> { |
0731742a XL |
28 | pub fn search_paths(&self) -> impl Iterator<Item = &'a SearchPath> { |
29 | let kind = self.kind; | |
dfeec247 XL |
30 | self.search_paths |
31 | .iter() | |
0731742a XL |
32 | .filter(move |sp| sp.kind.matches(kind)) |
33 | .chain(std::iter::once(self.tlib_path)) | |
223e47cc LB |
34 | } |
35 | ||
c34b1796 | 36 | pub fn get_lib_path(&self) -> PathBuf { |
1a4d82fc JJ |
37 | make_target_lib_path(self.sysroot, self.triple) |
38 | } | |
223e47cc | 39 | |
f035d41b XL |
40 | pub fn get_self_contained_lib_path(&self) -> PathBuf { |
41 | self.get_lib_path().join("self-contained") | |
42 | } | |
43 | ||
dfeec247 XL |
44 | pub fn new( |
45 | sysroot: &'a Path, | |
46 | triple: &'a str, | |
5869c6ff | 47 | search_paths: &'a [SearchPath], |
dfeec247 XL |
48 | tlib_path: &'a SearchPath, |
49 | kind: PathKind, | |
50 | ) -> FileSearch<'a> { | |
1a4d82fc | 51 | debug!("using sysroot = {}, triple = {}", sysroot.display(), triple); |
dfeec247 | 52 | FileSearch { sysroot, triple, search_paths, tlib_path, kind } |
223e47cc | 53 | } |
1a4d82fc | 54 | |
c295e0f8 | 55 | /// Returns just the directories within the search paths. |
0731742a | 56 | pub fn search_path_dirs(&self) -> Vec<PathBuf> { |
dfeec247 | 57 | self.search_paths().map(|sp| sp.dir.to_path_buf()).collect() |
1a4d82fc | 58 | } |
223e47cc LB |
59 | } |
60 | ||
0731742a | 61 | pub fn make_target_lib_path(sysroot: &Path, target_triple: &str) -> PathBuf { |
17df50a5 | 62 | let rustlib_path = rustc_target::target_rustlib_path(sysroot, target_triple); |
a2a8927a | 63 | PathBuf::from_iter([sysroot, Path::new(&rustlib_path), Path::new("lib")]) |
223e47cc LB |
64 | } |
65 | ||
487cf647 FG |
66 | #[cfg(unix)] |
67 | fn current_dll_path() -> Result<PathBuf, String> { | |
68 | use std::ffi::{CStr, OsStr}; | |
69 | use std::os::unix::prelude::*; | |
70 | ||
353b0b11 | 71 | #[cfg(not(target_os = "aix"))] |
487cf647 FG |
72 | unsafe { |
73 | let addr = current_dll_path as usize as *mut _; | |
74 | let mut info = std::mem::zeroed(); | |
75 | if libc::dladdr(addr, &mut info) == 0 { | |
76 | return Err("dladdr failed".into()); | |
77 | } | |
78 | if info.dli_fname.is_null() { | |
79 | return Err("dladdr returned null pointer".into()); | |
80 | } | |
81 | let bytes = CStr::from_ptr(info.dli_fname).to_bytes(); | |
82 | let os = OsStr::from_bytes(bytes); | |
83 | Ok(PathBuf::from(os)) | |
84 | } | |
353b0b11 FG |
85 | |
86 | #[cfg(target_os = "aix")] | |
87 | unsafe { | |
88 | // On AIX, the symbol `current_dll_path` references a function descriptor. | |
89 | // A function descriptor is consisted of (See https://reviews.llvm.org/D62532) | |
90 | // * The address of the entry point of the function. | |
91 | // * The TOC base address for the function. | |
92 | // * The environment pointer. | |
93 | // The function descriptor is in the data section. | |
94 | let addr = current_dll_path as u64; | |
95 | let mut buffer = vec![std::mem::zeroed::<libc::ld_info>(); 64]; | |
96 | loop { | |
97 | if libc::loadquery( | |
98 | libc::L_GETINFO, | |
99 | buffer.as_mut_ptr() as *mut i8, | |
100 | (std::mem::size_of::<libc::ld_info>() * buffer.len()) as u32, | |
101 | ) >= 0 | |
102 | { | |
103 | break; | |
104 | } else { | |
105 | if std::io::Error::last_os_error().raw_os_error().unwrap() != libc::ENOMEM { | |
106 | return Err("loadquery failed".into()); | |
107 | } | |
108 | buffer.resize(buffer.len() * 2, std::mem::zeroed::<libc::ld_info>()); | |
109 | } | |
110 | } | |
111 | let mut current = buffer.as_mut_ptr() as *mut libc::ld_info; | |
112 | loop { | |
113 | let data_base = (*current).ldinfo_dataorg as u64; | |
114 | let data_end = data_base + (*current).ldinfo_datasize; | |
115 | if (data_base..data_end).contains(&addr) { | |
116 | let bytes = CStr::from_ptr(&(*current).ldinfo_filename[0]).to_bytes(); | |
117 | let os = OsStr::from_bytes(bytes); | |
118 | return Ok(PathBuf::from(os)); | |
119 | } | |
120 | if (*current).ldinfo_next == 0 { | |
121 | break; | |
122 | } | |
123 | current = | |
124 | (current as *mut i8).offset((*current).ldinfo_next as isize) as *mut libc::ld_info; | |
125 | } | |
126 | return Err(format!("current dll's address {} is not in the load map", addr)); | |
127 | } | |
487cf647 FG |
128 | } |
129 | ||
130 | #[cfg(windows)] | |
131 | fn current_dll_path() -> Result<PathBuf, String> { | |
132 | use std::ffi::OsString; | |
133 | use std::io; | |
134 | use std::os::windows::prelude::*; | |
487cf647 | 135 | |
353b0b11 FG |
136 | use windows::{ |
137 | core::PCWSTR, | |
138 | Win32::Foundation::HINSTANCE, | |
139 | Win32::System::LibraryLoader::{ | |
140 | GetModuleFileNameW, GetModuleHandleExW, GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, | |
141 | }, | |
487cf647 FG |
142 | }; |
143 | ||
353b0b11 | 144 | let mut module = HINSTANCE::default(); |
487cf647 | 145 | unsafe { |
353b0b11 | 146 | GetModuleHandleExW( |
487cf647 | 147 | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, |
353b0b11 | 148 | PCWSTR(current_dll_path as *mut u16), |
487cf647 | 149 | &mut module, |
353b0b11 FG |
150 | ) |
151 | } | |
152 | .ok() | |
153 | .map_err(|e| e.to_string())?; | |
154 | ||
155 | let mut filename = vec![0; 1024]; | |
156 | let n = unsafe { GetModuleFileNameW(module, &mut filename) } as usize; | |
157 | if n == 0 { | |
158 | return Err(format!("GetModuleFileNameW failed: {}", io::Error::last_os_error())); | |
159 | } | |
160 | if n >= filename.capacity() { | |
161 | return Err(format!("our buffer was too small? {}", io::Error::last_os_error())); | |
487cf647 | 162 | } |
353b0b11 FG |
163 | |
164 | filename.truncate(n); | |
165 | ||
166 | Ok(OsString::from_wide(&filename).into()) | |
487cf647 FG |
167 | } |
168 | ||
169 | pub fn sysroot_candidates() -> SmallVec<[PathBuf; 2]> { | |
170 | let target = crate::config::host_triple(); | |
171 | let mut sysroot_candidates: SmallVec<[PathBuf; 2]> = | |
172 | smallvec![get_or_default_sysroot().expect("Failed finding sysroot")]; | |
353b0b11 | 173 | let path = current_dll_path().and_then(|s| try_canonicalize(s).map_err(|e| e.to_string())); |
487cf647 FG |
174 | if let Ok(dll) = path { |
175 | // use `parent` twice to chop off the file name and then also the | |
176 | // directory containing the dll which should be either `lib` or `bin`. | |
177 | if let Some(path) = dll.parent().and_then(|p| p.parent()) { | |
178 | // The original `path` pointed at the `rustc_driver` crate's dll. | |
179 | // Now that dll should only be in one of two locations. The first is | |
180 | // in the compiler's libdir, for example `$sysroot/lib/*.dll`. The | |
181 | // other is the target's libdir, for example | |
182 | // `$sysroot/lib/rustlib/$target/lib/*.dll`. | |
183 | // | |
184 | // We don't know which, so let's assume that if our `path` above | |
185 | // ends in `$target` we *could* be in the target libdir, and always | |
186 | // assume that we may be in the main libdir. | |
187 | sysroot_candidates.push(path.to_owned()); | |
188 | ||
189 | if path.ends_with(target) { | |
190 | sysroot_candidates.extend( | |
191 | path.parent() // chop off `$target` | |
192 | .and_then(|p| p.parent()) // chop off `rustlib` | |
193 | .and_then(|p| p.parent()) // chop off `lib` | |
194 | .map(|s| s.to_owned()), | |
195 | ); | |
196 | } | |
197 | } | |
198 | } | |
199 | ||
200 | return sysroot_candidates; | |
201 | } | |
202 | ||
c295e0f8 | 203 | /// This function checks if sysroot is found using env::args().next(), and if it |
487cf647 FG |
204 | /// is not found, finds sysroot from current rustc_driver dll. |
205 | pub fn get_or_default_sysroot() -> Result<PathBuf, String> { | |
9c376795 | 206 | // Follow symlinks. If the resolved path is relative, make it absolute. |
3dfed10e | 207 | fn canonicalize(path: PathBuf) -> PathBuf { |
353b0b11 | 208 | let path = try_canonicalize(&path).unwrap_or(path); |
3dfed10e XL |
209 | // See comments on this target function, but the gist is that |
210 | // gcc chokes on verbatim paths which fs::canonicalize generates | |
211 | // so we try to avoid those kinds of paths. | |
212 | fix_windows_verbatim_for_gcc(&path) | |
1a4d82fc JJ |
213 | } |
214 | ||
487cf647 | 215 | fn default_from_rustc_driver_dll() -> Result<PathBuf, String> { |
9c376795 | 216 | let dll = current_dll_path().map(|s| canonicalize(s))?; |
487cf647 FG |
217 | |
218 | // `dll` will be in one of the following two: | |
219 | // - compiler's libdir: $sysroot/lib/*.dll | |
220 | // - target's libdir: $sysroot/lib/rustlib/$target/lib/*.dll | |
221 | // | |
222 | // use `parent` twice to chop off the file name and then also the | |
223 | // directory containing the dll | |
224 | let dir = dll.parent().and_then(|p| p.parent()).ok_or(format!( | |
225 | "Could not move 2 levels upper using `parent()` on {}", | |
226 | dll.display() | |
227 | ))?; | |
228 | ||
229 | // if `dir` points target's dir, move up to the sysroot | |
353b0b11 | 230 | let mut sysroot_dir = if dir.ends_with(crate::config::host_triple()) { |
487cf647 FG |
231 | dir.parent() // chop off `$target` |
232 | .and_then(|p| p.parent()) // chop off `rustlib` | |
353b0b11 | 233 | .and_then(|p| p.parent()) // chop off `lib` |
487cf647 | 234 | .map(|s| s.to_owned()) |
353b0b11 FG |
235 | .ok_or_else(|| { |
236 | format!("Could not move 3 levels upper using `parent()` on {}", dir.display()) | |
237 | })? | |
487cf647 | 238 | } else { |
353b0b11 FG |
239 | dir.to_owned() |
240 | }; | |
241 | ||
242 | // On multiarch linux systems, there will be multiarch directory named | |
243 | // with the architecture(e.g `x86_64-linux-gnu`) under the `lib` directory. | |
244 | // Which cause us to mistakenly end up in the lib directory instead of the sysroot directory. | |
245 | if sysroot_dir.ends_with("lib") { | |
246 | sysroot_dir = | |
247 | sysroot_dir.parent().map(|real_sysroot| real_sysroot.to_owned()).ok_or_else( | |
248 | || format!("Could not move to parent path of {}", sysroot_dir.display()), | |
249 | )? | |
5869c6ff | 250 | } |
353b0b11 FG |
251 | |
252 | Ok(sysroot_dir) | |
5869c6ff XL |
253 | } |
254 | ||
255 | // Use env::args().next() to get the path of the executable without | |
256 | // following symlinks/canonicalizing any component. This makes the rustc | |
257 | // binary able to locate Rust libraries in systems using content-addressable | |
258 | // storage (CAS). | |
259 | fn from_env_args_next() -> Option<PathBuf> { | |
260 | match env::args_os().next() { | |
261 | Some(first_arg) => { | |
262 | let mut p = PathBuf::from(first_arg); | |
263 | ||
264 | // Check if sysroot is found using env::args().next() only if the rustc in argv[0] | |
265 | // is a symlink (see #79253). We might want to change/remove it to conform with | |
266 | // https://www.gnu.org/prep/standards/standards.html#Finding-Program-Files in the | |
267 | // future. | |
268 | if fs::read_link(&p).is_err() { | |
269 | // Path is not a symbolic link or does not exist. | |
270 | return None; | |
271 | } | |
272 | ||
17df50a5 | 273 | // Pop off `bin/rustc`, obtaining the suspected sysroot. |
5869c6ff XL |
274 | p.pop(); |
275 | p.pop(); | |
17df50a5 XL |
276 | // Look for the target rustlib directory in the suspected sysroot. |
277 | let mut rustlib_path = rustc_target::target_rustlib_path(&p, "dummy"); | |
278 | rustlib_path.pop(); // pop off the dummy target. | |
9ffffee4 | 279 | rustlib_path.exists().then_some(p) |
5869c6ff XL |
280 | } |
281 | None => None, | |
3dfed10e | 282 | } |
223e47cc | 283 | } |
5869c6ff | 284 | |
487cf647 | 285 | Ok(from_env_args_next().unwrap_or(default_from_rustc_driver_dll()?)) |
223e47cc | 286 | } |