+#[cfg(unix)]
+fn current_dll_path() -> Result<PathBuf, String> {
+ use std::ffi::{CStr, OsStr};
+ use std::os::unix::prelude::*;
+
+ 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(windows)]
+fn current_dll_path() -> Result<PathBuf, String> {
+ use std::ffi::OsString;
+ use std::io;
+ use std::os::windows::prelude::*;
+ use std::ptr;
+
+ use winapi::um::libloaderapi::{
+ GetModuleFileNameW, GetModuleHandleExW, GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
+ };
+
+ unsafe {
+ let mut module = ptr::null_mut();
+ let r = GetModuleHandleExW(
+ GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
+ current_dll_path as usize as *mut _,
+ &mut module,
+ );
+ if r == 0 {
+ return Err(format!("GetModuleHandleExW failed: {}", io::Error::last_os_error()));
+ }
+ let mut space = Vec::with_capacity(1024);
+ let r = GetModuleFileNameW(module, space.as_mut_ptr(), space.capacity() as u32);
+ if r == 0 {
+ return Err(format!("GetModuleFileNameW failed: {}", io::Error::last_os_error()));
+ }
+ let r = r as usize;
+ if r >= space.capacity() {
+ return Err(format!("our buffer was too small? {}", io::Error::last_os_error()));
+ }
+ space.set_len(r);
+ let os = OsString::from_wide(&space);
+ Ok(PathBuf::from(os))
+ }
+}
+
+pub fn sysroot_candidates() -> SmallVec<[PathBuf; 2]> {
+ let target = crate::config::host_triple();
+ let mut sysroot_candidates: SmallVec<[PathBuf; 2]> =
+ smallvec![get_or_default_sysroot().expect("Failed finding sysroot")];
+ let path = current_dll_path().and_then(|s| Ok(s.canonicalize().map_err(|e| e.to_string())?));
+ if let Ok(dll) = path {
+ // use `parent` twice to chop off the file name and then also the
+ // directory containing the dll which should be either `lib` or `bin`.
+ if let Some(path) = dll.parent().and_then(|p| p.parent()) {
+ // The original `path` pointed at the `rustc_driver` crate's dll.
+ // Now that dll should only be in one of two locations. The first is
+ // in the compiler's libdir, for example `$sysroot/lib/*.dll`. The
+ // other is the target's libdir, for example
+ // `$sysroot/lib/rustlib/$target/lib/*.dll`.
+ //
+ // We don't know which, so let's assume that if our `path` above
+ // ends in `$target` we *could* be in the target libdir, and always
+ // assume that we may be in the main libdir.
+ sysroot_candidates.push(path.to_owned());
+
+ if path.ends_with(target) {
+ sysroot_candidates.extend(
+ path.parent() // chop off `$target`
+ .and_then(|p| p.parent()) // chop off `rustlib`
+ .and_then(|p| p.parent()) // chop off `lib`
+ .map(|s| s.to_owned()),
+ );
+ }
+ }
+ }
+
+ return sysroot_candidates;
+}
+