1//! A module for searching for libraries
23use std::path::{Path, PathBuf};
4use std::{env, fs};
56use rustc_fs_util::try_canonicalize;
7use rustc_target::spec::Target;
89use crate::search_paths::{PathKind, SearchPath};
1011#[derive(#[automatically_derived]
impl ::core::clone::Clone for FileSearch {
#[inline]
fn clone(&self) -> FileSearch {
FileSearch {
cli_search_paths: ::core::clone::Clone::clone(&self.cli_search_paths),
tlib_path: ::core::clone::Clone::clone(&self.tlib_path),
}
}
}Clone)]
12pub struct FileSearch {
13 cli_search_paths: Vec<SearchPath>,
14 tlib_path: SearchPath,
15}
1617impl FileSearch {
18pub fn cli_search_paths<'b>(&'b self, kind: PathKind) -> impl Iterator<Item = &'b SearchPath> {
19self.cli_search_paths.iter().filter(move |sp| sp.kind.matches(kind))
20 }
2122pub fn search_paths<'b>(&'b self, kind: PathKind) -> impl Iterator<Item = &'b SearchPath> {
23self.cli_search_paths
24 .iter()
25 .filter(move |sp| sp.kind.matches(kind))
26 .chain(std::iter::once(&self.tlib_path))
27 }
2829pub fn new(cli_search_paths: &[SearchPath], tlib_path: &SearchPath, target: &Target) -> Self {
30let this = FileSearch {
31 cli_search_paths: cli_search_paths.to_owned(),
32 tlib_path: tlib_path.clone(),
33 };
34this.refine(&["lib", &target.staticlib_prefix, &target.dll_prefix])
35 }
36// Produce a new file search from this search that has a smaller set of candidates.
37fn refine(mut self, allowed_prefixes: &[&str]) -> FileSearch {
38self.cli_search_paths
39 .iter_mut()
40 .for_each(|search_paths| search_paths.files.retain(allowed_prefixes));
41self.tlib_path.files.retain(allowed_prefixes);
4243self44 }
45}
4647pub fn make_target_lib_path(sysroot: &Path, target_triple: &str) -> PathBuf {
48let rustlib_path = rustc_target::relative_target_rustlib_path(sysroot, target_triple);
49sysroot.join(rustlib_path).join("lib")
50}
5152/// Returns a path to the target's `bin` folder within its `rustlib` path in the sysroot. This is
53/// where binaries are usually installed, e.g. the self-contained linkers, lld-wrappers, LLVM tools,
54/// etc.
55pub fn make_target_bin_path(sysroot: &Path, target_triple: &str) -> PathBuf {
56let rustlib_path = rustc_target::relative_target_rustlib_path(sysroot, target_triple);
57sysroot.join(rustlib_path).join("bin")
58}
5960#[cfg(unix)]
61fn current_dll_path() -> Result<PathBuf, String> {
62use std::sync::OnceLock;
6364// This is somewhat expensive relative to other work when compiling `fn main() {}` as `dladdr`
65 // needs to iterate over the symbol table of librustc_driver.so until it finds a match.
66 // As such cache this to avoid recomputing if we try to get the sysroot in multiple places.
67static CURRENT_DLL_PATH: OnceLock<Result<PathBuf, String>> = OnceLock::new();
68CURRENT_DLL_PATH69 .get_or_init(|| {
70use std::ffi::{CStr, OsStr};
71use std::os::unix::prelude::*;
7273#[cfg(not(target_os = "aix"))]
74unsafe {
75let addr = current_dll_pathas fn() -> Result<PathBuf, String> as *mut _;
76let mut info = std::mem::zeroed();
77if libc::dladdr(addr, &mut info) == 0 {
78return Err("dladdr failed".into());
79 }
80#[cfg(target_os = "cygwin")]
81let fname_ptr = info.dli_fname.as_ptr();
82#[cfg(not(target_os = "cygwin"))]
83let fname_ptr = {
84if !!info.dli_fname.is_null() {
{
::core::panicking::panic_fmt(format_args!("dli_fname cannot be null"));
}
};assert!(!info.dli_fname.is_null(), "dli_fname cannot be null");
85info.dli_fname
86 };
87let bytes = CStr::from_ptr(fname_ptr).to_bytes();
88let os = OsStr::from_bytes(bytes);
89try_canonicalize(Path::new(os)).map_err(|e| e.to_string())
90 }
9192#[cfg(target_os = "aix")]
93unsafe {
94// On AIX, the symbol `current_dll_path` references a function descriptor.
95 // A function descriptor is consisted of (See https://reviews.llvm.org/D62532)
96 // * The address of the entry point of the function.
97 // * The TOC base address for the function.
98 // * The environment pointer.
99 // The function descriptor is in the data section.
100let addr = current_dll_path as u64;
101let mut buffer = vec![std::mem::zeroed::<libc::ld_info>(); 64];
102loop {
103if libc::loadquery(
104 libc::L_GETINFO,
105 buffer.as_mut_ptr() as *mut u8,
106 (size_of::<libc::ld_info>() * buffer.len()) as u32,
107 ) >= 0
108{
109break;
110 } else {
111if std::io::Error::last_os_error().raw_os_error().unwrap() != libc::ENOMEM {
112return Err("loadquery failed".into());
113 }
114 buffer.resize(buffer.len() * 2, std::mem::zeroed::<libc::ld_info>());
115 }
116 }
117let mut current = buffer.as_mut_ptr() as *mut libc::ld_info;
118loop {
119let data_base = (*current).ldinfo_dataorg as u64;
120let data_end = data_base + (*current).ldinfo_datasize;
121if (data_base..data_end).contains(&addr) {
122let bytes = CStr::from_ptr(&(*current).ldinfo_filename[0]).to_bytes();
123let os = OsStr::from_bytes(bytes);
124return try_canonicalize(Path::new(os)).map_err(|e| e.to_string());
125 }
126if (*current).ldinfo_next == 0 {
127break;
128 }
129 current = (current as *mut i8).offset((*current).ldinfo_next as isize)
130as *mut libc::ld_info;
131 }
132return Err(format!("current dll's address {} is not in the load map", addr));
133 }
134 })
135 .clone()
136}
137138#[cfg(windows)]
139fn current_dll_path() -> Result<PathBuf, String> {
140use std::ffi::OsString;
141use std::io;
142use std::os::windows::prelude::*;
143144use windows::Win32::Foundation::HMODULE;
145use windows::Win32::System::LibraryLoader::{
146 GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, GetModuleFileNameW, GetModuleHandleExW,
147 };
148use windows::core::PCWSTR;
149150let mut module = HMODULE::default();
151unsafe {
152 GetModuleHandleExW(
153 GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
154 PCWSTR(
155 current_dll_path as fn() -> Result<std::path::PathBuf, std::string::String>
156as *mut u16,
157 ),
158&mut module,
159 )
160 }
161 .map_err(|e| e.to_string())?;
162163let mut filename = vec![0; 1024];
164let n = unsafe { GetModuleFileNameW(Some(module), &mut filename) } as usize;
165if n == 0 {
166return Err(format!("GetModuleFileNameW failed: {}", io::Error::last_os_error()));
167 }
168if n >= filename.capacity() {
169return Err(format!("our buffer was too small? {}", io::Error::last_os_error()));
170 }
171172 filename.truncate(n);
173174let path = try_canonicalize(OsString::from_wide(&filename)).map_err(|e| e.to_string())?;
175176// See comments on this target function, but the gist is that
177 // gcc chokes on verbatim paths which fs::canonicalize generates
178 // so we try to avoid those kinds of paths.
179Ok(rustc_fs_util::fix_windows_verbatim_for_gcc(&path))
180}
181182#[cfg(target_os = "wasi")]
183fn current_dll_path() -> Result<PathBuf, String> {
184Err("current_dll_path is not supported on WASI".to_string())
185}
186187/// This function checks if sysroot is found using env::args().next(), and if it
188/// is not found, finds sysroot from current rustc_driver dll.
189pub(crate) fn default_sysroot() -> PathBuf {
190fn default_from_rustc_driver_dll() -> Result<PathBuf, String> {
191let dll = current_dll_path()?;
192193// `dll` will be in one of the following two:
194 // - compiler's libdir: $sysroot/lib/*.dll
195 // - target's libdir: $sysroot/lib/rustlib/$target/lib/*.dll
196 //
197 // use `parent` twice to chop off the file name and then also the
198 // directory containing the dll
199let dir = dll.parent().and_then(|p| p.parent()).ok_or_else(|| {
200::alloc::__export::must_use({
::alloc::fmt::format(format_args!("Could not move 2 levels upper using `parent()` on {0}",
dll.display()))
})format!("Could not move 2 levels upper using `parent()` on {}", dll.display())201 })?;
202203// if `dir` points to target's dir, move up to the sysroot
204let mut sysroot_dir = if dir.ends_with(crate::config::host_tuple()) {
205dir.parent() // chop off `$target`
206.and_then(|p| p.parent()) // chop off `rustlib`
207.and_then(|p| p.parent()) // chop off `lib`
208.map(|s| s.to_owned())
209 .ok_or_else(|| {
210::alloc::__export::must_use({
::alloc::fmt::format(format_args!("Could not move 3 levels upper using `parent()` on {0}",
dir.display()))
})format!("Could not move 3 levels upper using `parent()` on {}", dir.display())211 })?
212} else {
213dir.to_owned()
214 };
215216// On multiarch linux systems, there will be multiarch directory named
217 // with the architecture(e.g `x86_64-linux-gnu`) under the `lib` directory.
218 // Which cause us to mistakenly end up in the lib directory instead of the sysroot directory.
219if sysroot_dir.ends_with("lib") {
220sysroot_dir =
221sysroot_dir.parent().map(|real_sysroot| real_sysroot.to_owned()).ok_or_else(
222 || ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("Could not move to parent path of {0}",
sysroot_dir.display()))
})format!("Could not move to parent path of {}", sysroot_dir.display()),
223 )?
224}
225226Ok(sysroot_dir)
227 }
228229// Use env::args().next() to get the path of the executable without
230 // following symlinks/canonicalizing any component. This makes the rustc
231 // binary able to locate Rust libraries in systems using content-addressable
232 // storage (CAS).
233fn from_env_args_next() -> Option<PathBuf> {
234let mut p = PathBuf::from(env::args_os().next()?);
235236// Check if sysroot is found using env::args().next() only if the rustc in argv[0]
237 // is a symlink (see #79253). We might want to change/remove it to conform with
238 // https://www.gnu.org/prep/standards/standards.html#Finding-Program-Files in the
239 // future.
240if fs::read_link(&p).is_err() {
241// Path is not a symbolic link or does not exist.
242return None;
243 }
244245// Pop off `bin/rustc`, obtaining the suspected sysroot.
246p.pop();
247p.pop();
248// Look for the target rustlib directory in the suspected sysroot.
249let mut rustlib_path = rustc_target::relative_target_rustlib_path(&p, "dummy");
250rustlib_path.pop(); // pop off the dummy target.
251rustlib_path.exists().then_some(p)
252 }
253254from_env_args_next()
255 .unwrap_or_else(|| default_from_rustc_driver_dll().expect("Failed finding sysroot"))
256}