Skip to content

Commit 521d0c4

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 521d0c4

File tree

1 file changed

+66
-56
lines changed

1 file changed

+66
-56
lines changed

compiler/rustc_session/src/filesearch.rs

+66-56
Original file line numberDiff line numberDiff line change
@@ -60,66 +60,76 @@ pub fn make_target_bin_path(sysroot: &Path, target_triple: &str) -> PathBuf {
6060

6161
#[cfg(unix)]
6262
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());
63+
use std::sync::OnceLock;
64+
65+
// This is somewhat expensive relative to other work when compiling `fn main() {}` as `dladdr`
66+
// needs to iterate over the symbol table of librustc_driver.so until it finds a match.
67+
// As such cache this to avoid recomputing if we try to get the sysroot in multiple places.
68+
static CURRENT_DLL_PATH: OnceLock<Result<PathBuf, String>> = OnceLock::new();
69+
CURRENT_DLL_PATH
70+
.get_or_init(|| {
71+
use std::ffi::{CStr, OsStr};
72+
use std::os::unix::prelude::*;
73+
74+
#[cfg(not(target_os = "aix"))]
75+
unsafe {
76+
let addr = current_dll_path as usize as *mut _;
77+
let mut info = std::mem::zeroed();
78+
if libc::dladdr(addr, &mut info) == 0 {
79+
return Err("dladdr failed".into());
10280
}
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();
81+
if info.dli_fname.is_null() {
82+
return Err("dladdr returned null pointer".into());
83+
}
84+
let bytes = CStr::from_ptr(info.dli_fname).to_bytes();
11285
let os = OsStr::from_bytes(bytes);
113-
return Ok(PathBuf::from(os));
86+
Ok(PathBuf::from(os))
11487
}
115-
if (*current).ldinfo_next == 0 {
116-
break;
88+
89+
#[cfg(target_os = "aix")]
90+
unsafe {
91+
// On AIX, the symbol `current_dll_path` references a function descriptor.
92+
// A function descriptor is consisted of (See https://reviews.llvm.org/D62532)
93+
// * The address of the entry point of the function.
94+
// * The TOC base address for the function.
95+
// * The environment pointer.
96+
// The function descriptor is in the data section.
97+
let addr = current_dll_path as u64;
98+
let mut buffer = vec![std::mem::zeroed::<libc::ld_info>(); 64];
99+
loop {
100+
if libc::loadquery(
101+
libc::L_GETINFO,
102+
buffer.as_mut_ptr() as *mut u8,
103+
(size_of::<libc::ld_info>() * buffer.len()) as u32,
104+
) >= 0
105+
{
106+
break;
107+
} else {
108+
if std::io::Error::last_os_error().raw_os_error().unwrap() != libc::ENOMEM {
109+
return Err("loadquery failed".into());
110+
}
111+
buffer.resize(buffer.len() * 2, std::mem::zeroed::<libc::ld_info>());
112+
}
113+
}
114+
let mut current = buffer.as_mut_ptr() as *mut libc::ld_info;
115+
loop {
116+
let data_base = (*current).ldinfo_dataorg as u64;
117+
let data_end = data_base + (*current).ldinfo_datasize;
118+
if (data_base..data_end).contains(&addr) {
119+
let bytes = CStr::from_ptr(&(*current).ldinfo_filename[0]).to_bytes();
120+
let os = OsStr::from_bytes(bytes);
121+
return Ok(PathBuf::from(os));
122+
}
123+
if (*current).ldinfo_next == 0 {
124+
break;
125+
}
126+
current = (current as *mut i8).offset((*current).ldinfo_next as isize)
127+
as *mut libc::ld_info;
128+
}
129+
return Err(format!("current dll's address {} is not in the load map", addr));
117130
}
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-
}
131+
})
132+
.clone()
123133
}
124134

125135
#[cfg(windows)]

0 commit comments

Comments
 (0)