Skip to content

Commit 715ea3b

Browse files
committed
Cache current_dll_path output
Computing the current dll path 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.
1 parent 5d85a71 commit 715ea3b

File tree

1 file changed

+65
-56
lines changed

1 file changed

+65
-56
lines changed

compiler/rustc_session/src/filesearch.rs

+65-56
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! A module for searching for libraries
22
33
use std::path::{Path, PathBuf};
4+
use std::sync::OnceLock;
45
use std::{env, fs};
56

67
use rustc_fs_util::{fix_windows_verbatim_for_gcc, try_canonicalize};
@@ -60,66 +61,74 @@ pub fn make_target_bin_path(sysroot: &Path, target_triple: &str) -> PathBuf {
6061

6162
#[cfg(unix)]
6263
fn current_dll_path() -> Result<PathBuf, String> {
63-
use std::ffi::{CStr, OsStr};
64-
use std::os::unix::prelude::*;
65-
66-
#[cfg(not(target_os = "aix"))]
67-
unsafe {
68-
let addr = current_dll_path as usize as *mut _;
69-
let mut info = std::mem::zeroed();
70-
if libc::dladdr(addr, &mut info) == 0 {
71-
return Err("dladdr failed".into());
72-
}
73-
if info.dli_fname.is_null() {
74-
return Err("dladdr returned null pointer".into());
75-
}
76-
let bytes = CStr::from_ptr(info.dli_fname).to_bytes();
77-
let os = OsStr::from_bytes(bytes);
78-
Ok(PathBuf::from(os))
79-
}
80-
81-
#[cfg(target_os = "aix")]
82-
unsafe {
83-
// On AIX, the symbol `current_dll_path` references a function descriptor.
84-
// A function descriptor is consisted of (See https://reviews.llvm.org/D62532)
85-
// * The address of the entry point of the function.
86-
// * The TOC base address for the function.
87-
// * The environment pointer.
88-
// The function descriptor is in the data section.
89-
let addr = current_dll_path as u64;
90-
let mut buffer = vec![std::mem::zeroed::<libc::ld_info>(); 64];
91-
loop {
92-
if libc::loadquery(
93-
libc::L_GETINFO,
94-
buffer.as_mut_ptr() as *mut u8,
95-
(size_of::<libc::ld_info>() * buffer.len()) as u32,
96-
) >= 0
97-
{
98-
break;
99-
} else {
100-
if std::io::Error::last_os_error().raw_os_error().unwrap() != libc::ENOMEM {
101-
return Err("loadquery failed".into());
64+
// 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.
67+
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());
10279
}
103-
buffer.resize(buffer.len() * 2, std::mem::zeroed::<libc::ld_info>());
104-
}
105-
}
106-
let mut current = buffer.as_mut_ptr() as *mut libc::ld_info;
107-
loop {
108-
let data_base = (*current).ldinfo_dataorg as u64;
109-
let data_end = data_base + (*current).ldinfo_datasize;
110-
if (data_base..data_end).contains(&addr) {
111-
let bytes = CStr::from_ptr(&(*current).ldinfo_filename[0]).to_bytes();
80+
if info.dli_fname.is_null() {
81+
return Err("dladdr returned null pointer".into());
82+
}
83+
let bytes = CStr::from_ptr(info.dli_fname).to_bytes();
11284
let os = OsStr::from_bytes(bytes);
113-
return Ok(PathBuf::from(os));
85+
Ok(PathBuf::from(os))
11486
}
115-
if (*current).ldinfo_next == 0 {
116-
break;
87+
88+
#[cfg(target_os = "aix")]
89+
unsafe {
90+
// On AIX, the symbol `current_dll_path` references a function descriptor.
91+
// A function descriptor is consisted of (See https://reviews.llvm.org/D62532)
92+
// * The address of the entry point of the function.
93+
// * The TOC base address for the function.
94+
// * The environment pointer.
95+
// The function descriptor is in the data section.
96+
let addr = current_dll_path as u64;
97+
let mut buffer = vec![std::mem::zeroed::<libc::ld_info>(); 64];
98+
loop {
99+
if libc::loadquery(
100+
libc::L_GETINFO,
101+
buffer.as_mut_ptr() as *mut u8,
102+
(size_of::<libc::ld_info>() * buffer.len()) as u32,
103+
) >= 0
104+
{
105+
break;
106+
} else {
107+
if std::io::Error::last_os_error().raw_os_error().unwrap() != libc::ENOMEM {
108+
return Err("loadquery failed".into());
109+
}
110+
buffer.resize(buffer.len() * 2, std::mem::zeroed::<libc::ld_info>());
111+
}
112+
}
113+
let mut current = buffer.as_mut_ptr() as *mut libc::ld_info;
114+
loop {
115+
let data_base = (*current).ldinfo_dataorg as u64;
116+
let data_end = data_base + (*current).ldinfo_datasize;
117+
if (data_base..data_end).contains(&addr) {
118+
let bytes = CStr::from_ptr(&(*current).ldinfo_filename[0]).to_bytes();
119+
let os = OsStr::from_bytes(bytes);
120+
return Ok(PathBuf::from(os));
121+
}
122+
if (*current).ldinfo_next == 0 {
123+
break;
124+
}
125+
current = (current as *mut i8).offset((*current).ldinfo_next as isize)
126+
as *mut libc::ld_info;
127+
}
128+
return Err(format!("current dll's address {} is not in the load map", addr));
117129
}
118-
current =
119-
(current as *mut i8).offset((*current).ldinfo_next as isize) as *mut libc::ld_info;
120-
}
121-
return Err(format!("current dll's address {} is not in the load map", addr));
122-
}
130+
})
131+
.clone()
123132
}
124133

125134
#[cfg(windows)]

0 commit comments

Comments
 (0)