rustc_session/
filesearch.rs1use std::path::{Path, PathBuf};
4use std::{env, fs};
5
6use rustc_fs_util::try_canonicalize;
7use rustc_target::spec::Target;
8
9use crate::search_paths::{PathKind, SearchPath};
10
11#[derive(Clone)]
12pub struct FileSearch {
13    cli_search_paths: Vec<SearchPath>,
14    tlib_path: SearchPath,
15}
16
17impl FileSearch {
18    pub fn cli_search_paths<'b>(&'b self, kind: PathKind) -> impl Iterator<Item = &'b SearchPath> {
19        self.cli_search_paths.iter().filter(move |sp| sp.kind.matches(kind))
20    }
21
22    pub fn search_paths<'b>(&'b self, kind: PathKind) -> impl Iterator<Item = &'b SearchPath> {
23        self.cli_search_paths
24            .iter()
25            .filter(move |sp| sp.kind.matches(kind))
26            .chain(std::iter::once(&self.tlib_path))
27    }
28
29    pub fn new(cli_search_paths: &[SearchPath], tlib_path: &SearchPath, target: &Target) -> Self {
30        let this = FileSearch {
31            cli_search_paths: cli_search_paths.to_owned(),
32            tlib_path: tlib_path.clone(),
33        };
34        this.refine(&["lib", &target.staticlib_prefix, &target.dll_prefix])
35    }
36    fn refine(mut self, allowed_prefixes: &[&str]) -> FileSearch {
38        self.cli_search_paths
39            .iter_mut()
40            .for_each(|search_paths| search_paths.files.retain(allowed_prefixes));
41        self.tlib_path.files.retain(allowed_prefixes);
42
43        self
44    }
45}
46
47pub fn make_target_lib_path(sysroot: &Path, target_triple: &str) -> PathBuf {
48    let rustlib_path = rustc_target::relative_target_rustlib_path(sysroot, target_triple);
49    sysroot.join(rustlib_path).join("lib")
50}
51
52pub fn make_target_bin_path(sysroot: &Path, target_triple: &str) -> PathBuf {
56    let rustlib_path = rustc_target::relative_target_rustlib_path(sysroot, target_triple);
57    sysroot.join(rustlib_path).join("bin")
58}
59
60#[cfg(unix)]
61fn current_dll_path() -> Result<PathBuf, String> {
62    use std::sync::OnceLock;
63
64    static CURRENT_DLL_PATH: OnceLock<Result<PathBuf, String>> = OnceLock::new();
68    CURRENT_DLL_PATH
69        .get_or_init(|| {
70            use std::ffi::{CStr, OsStr};
71            use std::os::unix::prelude::*;
72
73            #[cfg(not(target_os = "aix"))]
74            unsafe {
75                let addr = current_dll_path as usize as *mut _;
76                let mut info = std::mem::zeroed();
77                if libc::dladdr(addr, &mut info) == 0 {
78                    return Err("dladdr failed".into());
79                }
80                #[cfg(target_os = "cygwin")]
81                let fname_ptr = info.dli_fname.as_ptr();
82                #[cfg(not(target_os = "cygwin"))]
83                let fname_ptr = {
84                    assert!(!info.dli_fname.is_null(), "dli_fname cannot be null");
85                    info.dli_fname
86                };
87                let bytes = CStr::from_ptr(fname_ptr).to_bytes();
88                let os = OsStr::from_bytes(bytes);
89                try_canonicalize(Path::new(os)).map_err(|e| e.to_string())
90            }
91
92            #[cfg(target_os = "aix")]
93            unsafe {
94                let addr = current_dll_path as u64;
101                let mut buffer = vec![std::mem::zeroed::<libc::ld_info>(); 64];
102                loop {
103                    if 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                    {
109                        break;
110                    } else {
111                        if std::io::Error::last_os_error().raw_os_error().unwrap() != libc::ENOMEM {
112                            return Err("loadquery failed".into());
113                        }
114                        buffer.resize(buffer.len() * 2, std::mem::zeroed::<libc::ld_info>());
115                    }
116                }
117                let mut current = buffer.as_mut_ptr() as *mut libc::ld_info;
118                loop {
119                    let data_base = (*current).ldinfo_dataorg as u64;
120                    let data_end = data_base + (*current).ldinfo_datasize;
121                    if (data_base..data_end).contains(&addr) {
122                        let bytes = CStr::from_ptr(&(*current).ldinfo_filename[0]).to_bytes();
123                        let os = OsStr::from_bytes(bytes);
124                        return try_canonicalize(Path::new(os)).map_err(|e| e.to_string());
125                    }
126                    if (*current).ldinfo_next == 0 {
127                        break;
128                    }
129                    current = (current as *mut i8).offset((*current).ldinfo_next as isize)
130                        as *mut libc::ld_info;
131                }
132                return Err(format!("current dll's address {} is not in the load map", addr));
133            }
134        })
135        .clone()
136}
137
138#[cfg(windows)]
139fn current_dll_path() -> Result<PathBuf, String> {
140    use std::ffi::OsString;
141    use std::io;
142    use std::os::windows::prelude::*;
143
144    use windows::Win32::Foundation::HMODULE;
145    use windows::Win32::System::LibraryLoader::{
146        GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, GetModuleFileNameW, GetModuleHandleExW,
147    };
148    use windows::core::PCWSTR;
149
150    let mut module = HMODULE::default();
151    unsafe {
152        GetModuleHandleExW(
153            GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
154            PCWSTR(current_dll_path as *mut u16),
155            &mut module,
156        )
157    }
158    .map_err(|e| e.to_string())?;
159
160    let mut filename = vec![0; 1024];
161    let n = unsafe { GetModuleFileNameW(Some(module), &mut filename) } as usize;
162    if n == 0 {
163        return Err(format!("GetModuleFileNameW failed: {}", io::Error::last_os_error()));
164    }
165    if n >= filename.capacity() {
166        return Err(format!("our buffer was too small? {}", io::Error::last_os_error()));
167    }
168
169    filename.truncate(n);
170
171    let path = try_canonicalize(OsString::from_wide(&filename)).map_err(|e| e.to_string())?;
172
173    Ok(rustc_fs_util::fix_windows_verbatim_for_gcc(&path))
177}
178
179#[cfg(target_os = "wasi")]
180fn current_dll_path() -> Result<PathBuf, String> {
181    Err("current_dll_path is not supported on WASI".to_string())
182}
183
184pub(crate) fn default_sysroot() -> PathBuf {
187    fn default_from_rustc_driver_dll() -> Result<PathBuf, String> {
188        let dll = current_dll_path()?;
189
190        let dir = dll.parent().and_then(|p| p.parent()).ok_or_else(|| {
197            format!("Could not move 2 levels upper using `parent()` on {}", dll.display())
198        })?;
199
200        let mut sysroot_dir = if dir.ends_with(crate::config::host_tuple()) {
202            dir.parent() .and_then(|p| p.parent()) .and_then(|p| p.parent()) .map(|s| s.to_owned())
206                .ok_or_else(|| {
207                    format!("Could not move 3 levels upper using `parent()` on {}", dir.display())
208                })?
209        } else {
210            dir.to_owned()
211        };
212
213        if sysroot_dir.ends_with("lib") {
217            sysroot_dir =
218                sysroot_dir.parent().map(|real_sysroot| real_sysroot.to_owned()).ok_or_else(
219                    || format!("Could not move to parent path of {}", sysroot_dir.display()),
220                )?
221        }
222
223        Ok(sysroot_dir)
224    }
225
226    fn from_env_args_next() -> Option<PathBuf> {
231        let mut p = PathBuf::from(env::args_os().next()?);
232
233        if fs::read_link(&p).is_err() {
238            return None;
240        }
241
242        p.pop();
244        p.pop();
245        let mut rustlib_path = rustc_target::relative_target_rustlib_path(&p, "dummy");
247        rustlib_path.pop(); rustlib_path.exists().then_some(p)
249    }
250
251    from_env_args_next()
252        .unwrap_or_else(|| default_from_rustc_driver_dll().expect("Failed finding sysroot"))
253}