about summary refs log tree commit diff
path: root/compiler/rustc_session/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_session/src')
-rw-r--r--compiler/rustc_session/src/filesearch.rs122
1 files changed, 66 insertions, 56 deletions
diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs
index 50f09c57107..bdeca91eb64 100644
--- a/compiler/rustc_session/src/filesearch.rs
+++ b/compiler/rustc_session/src/filesearch.rs
@@ -60,66 +60,76 @@ pub fn make_target_bin_path(sysroot: &Path, target_triple: &str) -> PathBuf {
 
 #[cfg(unix)]
 fn current_dll_path() -> Result<PathBuf, String> {
-    use std::ffi::{CStr, OsStr};
-    use std::os::unix::prelude::*;
-
-    #[cfg(not(target_os = "aix"))]
-    unsafe {
-        let addr = current_dll_path as usize as *mut _;
-        let mut info = std::mem::zeroed();
-        if libc::dladdr(addr, &mut info) == 0 {
-            return Err("dladdr failed".into());
-        }
-        if info.dli_fname.is_null() {
-            return Err("dladdr returned null pointer".into());
-        }
-        let bytes = CStr::from_ptr(info.dli_fname).to_bytes();
-        let os = OsStr::from_bytes(bytes);
-        Ok(PathBuf::from(os))
-    }
-
-    #[cfg(target_os = "aix")]
-    unsafe {
-        // On AIX, the symbol `current_dll_path` references a function descriptor.
-        // A function descriptor is consisted of (See https://reviews.llvm.org/D62532)
-        // * The address of the entry point of the function.
-        // * The TOC base address for the function.
-        // * The environment pointer.
-        // The function descriptor is in the data section.
-        let addr = current_dll_path as u64;
-        let mut buffer = vec![std::mem::zeroed::<libc::ld_info>(); 64];
-        loop {
-            if libc::loadquery(
-                libc::L_GETINFO,
-                buffer.as_mut_ptr() as *mut u8,
-                (size_of::<libc::ld_info>() * buffer.len()) as u32,
-            ) >= 0
-            {
-                break;
-            } else {
-                if std::io::Error::last_os_error().raw_os_error().unwrap() != libc::ENOMEM {
-                    return Err("loadquery failed".into());
+    use std::sync::OnceLock;
+
+    // This is somewhat expensive relative to other work when compiling `fn main() {}` as `dladdr`
+    // needs to iterate over the symbol table of librustc_driver.so until it finds a match.
+    // As such cache this to avoid recomputing if we try to get the sysroot in multiple places.
+    static CURRENT_DLL_PATH: OnceLock<Result<PathBuf, String>> = OnceLock::new();
+    CURRENT_DLL_PATH
+        .get_or_init(|| {
+            use std::ffi::{CStr, OsStr};
+            use std::os::unix::prelude::*;
+
+            #[cfg(not(target_os = "aix"))]
+            unsafe {
+                let addr = current_dll_path as usize as *mut _;
+                let mut info = std::mem::zeroed();
+                if libc::dladdr(addr, &mut info) == 0 {
+                    return Err("dladdr failed".into());
                 }
-                buffer.resize(buffer.len() * 2, std::mem::zeroed::<libc::ld_info>());
-            }
-        }
-        let mut current = buffer.as_mut_ptr() as *mut libc::ld_info;
-        loop {
-            let data_base = (*current).ldinfo_dataorg as u64;
-            let data_end = data_base + (*current).ldinfo_datasize;
-            if (data_base..data_end).contains(&addr) {
-                let bytes = CStr::from_ptr(&(*current).ldinfo_filename[0]).to_bytes();
+                if info.dli_fname.is_null() {
+                    return Err("dladdr returned null pointer".into());
+                }
+                let bytes = CStr::from_ptr(info.dli_fname).to_bytes();
                 let os = OsStr::from_bytes(bytes);
-                return Ok(PathBuf::from(os));
+                Ok(PathBuf::from(os))
             }
-            if (*current).ldinfo_next == 0 {
-                break;
+
+            #[cfg(target_os = "aix")]
+            unsafe {
+                // On AIX, the symbol `current_dll_path` references a function descriptor.
+                // A function descriptor is consisted of (See https://reviews.llvm.org/D62532)
+                // * The address of the entry point of the function.
+                // * The TOC base address for the function.
+                // * The environment pointer.
+                // The function descriptor is in the data section.
+                let addr = current_dll_path as u64;
+                let mut buffer = vec![std::mem::zeroed::<libc::ld_info>(); 64];
+                loop {
+                    if libc::loadquery(
+                        libc::L_GETINFO,
+                        buffer.as_mut_ptr() as *mut u8,
+                        (size_of::<libc::ld_info>() * buffer.len()) as u32,
+                    ) >= 0
+                    {
+                        break;
+                    } else {
+                        if std::io::Error::last_os_error().raw_os_error().unwrap() != libc::ENOMEM {
+                            return Err("loadquery failed".into());
+                        }
+                        buffer.resize(buffer.len() * 2, std::mem::zeroed::<libc::ld_info>());
+                    }
+                }
+                let mut current = buffer.as_mut_ptr() as *mut libc::ld_info;
+                loop {
+                    let data_base = (*current).ldinfo_dataorg as u64;
+                    let data_end = data_base + (*current).ldinfo_datasize;
+                    if (data_base..data_end).contains(&addr) {
+                        let bytes = CStr::from_ptr(&(*current).ldinfo_filename[0]).to_bytes();
+                        let os = OsStr::from_bytes(bytes);
+                        return Ok(PathBuf::from(os));
+                    }
+                    if (*current).ldinfo_next == 0 {
+                        break;
+                    }
+                    current = (current as *mut i8).offset((*current).ldinfo_next as isize)
+                        as *mut libc::ld_info;
+                }
+                return Err(format!("current dll's address {} is not in the load map", addr));
             }
-            current =
-                (current as *mut i8).offset((*current).ldinfo_next as isize) as *mut libc::ld_info;
-        }
-        return Err(format!("current dll's address {} is not in the load map", addr));
-    }
+        })
+        .clone()
 }
 
 #[cfg(windows)]