Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve filesearch::get_or_default_sysroot #103660

Merged
merged 1 commit into from
Nov 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3646,7 +3646,6 @@ dependencies = [
name = "rustc_interface"
version = "0.0.0"
dependencies = [
"libc",
"libloading",
"rustc-rayon",
"rustc-rayon-core",
Expand Down Expand Up @@ -3689,7 +3688,6 @@ dependencies = [
"rustc_ty_utils",
"smallvec",
"tracing",
"winapi",
]

[[package]]
Expand Down Expand Up @@ -4109,6 +4107,7 @@ name = "rustc_session"
version = "0.0.0"
dependencies = [
"getopts",
"libc",
"rustc_ast",
"rustc_data_structures",
"rustc_errors",
Expand All @@ -4120,7 +4119,9 @@ dependencies = [
"rustc_serialize",
"rustc_span",
"rustc_target",
"smallvec",
"tracing",
"winapi",
]

[[package]]
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1153,7 +1153,8 @@ fn link_sanitizer_runtime(sess: &Session, linker: &mut dyn Linker, name: &str) {
if path.exists() {
return session_tlib;
} else {
let default_sysroot = filesearch::get_or_default_sysroot();
let default_sysroot =
filesearch::get_or_default_sysroot().expect("Failed finding sysroot");
let default_tlib = filesearch::make_target_lib_path(
&default_sysroot,
sess.opts.target_triple.triple(),
Expand Down
6 changes: 0 additions & 6 deletions compiler/rustc_interface/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,6 @@ rustc_resolve = { path = "../rustc_resolve" }
rustc_trait_selection = { path = "../rustc_trait_selection" }
rustc_ty_utils = { path = "../rustc_ty_utils" }

[target.'cfg(unix)'.dependencies]
libc = "0.2"

[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["libloaderapi"] }

[dev-dependencies]
rustc_target = { path = "../rustc_target" }

Expand Down
97 changes: 2 additions & 95 deletions compiler/rustc_interface/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use rustc_session as session;
use rustc_session::config::CheckCfg;
use rustc_session::config::{self, CrateType};
use rustc_session::config::{ErrorOutputType, Input, OutputFilenames};
use rustc_session::filesearch::sysroot_candidates;
use rustc_session::lint::{self, BuiltinLintDiagnostics, LintBuffer};
use rustc_session::parse::CrateConfig;
use rustc_session::{early_error, filesearch, output, Session};
Expand Down Expand Up @@ -78,7 +79,7 @@ pub fn create_session(

let bundle = match rustc_errors::fluent_bundle(
sopts.maybe_sysroot.clone(),
sysroot_candidates(),
sysroot_candidates().to_vec(),
sopts.unstable_opts.translate_lang.clone(),
sopts.unstable_opts.translate_additional_ftl.as_deref(),
sopts.unstable_opts.translate_directionality_markers,
Expand Down Expand Up @@ -273,100 +274,6 @@ fn get_rustc_path_inner(bin_path: &str) -> Option<PathBuf> {
})
}

fn sysroot_candidates() -> Vec<PathBuf> {
let target = session::config::host_triple();
let mut sysroot_candidates = vec![filesearch::get_or_default_sysroot()];
let path = current_dll_path().and_then(|s| s.canonicalize().ok());
if let Some(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;

#[cfg(unix)]
fn current_dll_path() -> Option<PathBuf> {
use std::ffi::{CStr, OsStr};
use std::os::unix::prelude::*;

unsafe {
let addr = current_dll_path as usize as *mut _;
let mut info = mem::zeroed();
if libc::dladdr(addr, &mut info) == 0 {
info!("dladdr failed");
return None;
}
if info.dli_fname.is_null() {
info!("dladdr returned null pointer");
return None;
}
let bytes = CStr::from_ptr(info.dli_fname).to_bytes();
let os = OsStr::from_bytes(bytes);
Some(PathBuf::from(os))
}
}

#[cfg(windows)]
fn current_dll_path() -> Option<PathBuf> {
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 {
info!("GetModuleHandleExW failed: {}", io::Error::last_os_error());
return None;
}
let mut space = Vec::with_capacity(1024);
let r = GetModuleFileNameW(module, space.as_mut_ptr(), space.capacity() as u32);
if r == 0 {
info!("GetModuleFileNameW failed: {}", io::Error::last_os_error());
return None;
}
let r = r as usize;
if r >= space.capacity() {
info!("our buffer was too small? {}", io::Error::last_os_error());
return None;
}
space.set_len(r);
let os = OsString::from_wide(&space);
Some(PathBuf::from(os))
}
}
}

fn get_codegen_sysroot(maybe_sysroot: &Option<PathBuf>, backend_name: &str) -> MakeBackendFn {
// For now we only allow this function to be called once as it'll dlopen a
// few things, which seems to work best if we only do that once. In
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_session/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,10 @@ rustc_span = { path = "../rustc_span" }
rustc_fs_util = { path = "../rustc_fs_util" }
rustc_ast = { path = "../rustc_ast" }
rustc_lint_defs = { path = "../rustc_lint_defs" }
smallvec = "1.8.1"

[target.'cfg(unix)'.dependencies]
libc = "0.2"

[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["libloaderapi"] }
2 changes: 1 addition & 1 deletion compiler/rustc_session/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2431,7 +2431,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
let sysroot = match &sysroot_opt {
Some(s) => s,
None => {
tmp_buf = crate::filesearch::get_or_default_sysroot();
tmp_buf = crate::filesearch::get_or_default_sysroot().expect("Failed finding sysroot");
&tmp_buf
}
};
Expand Down
136 changes: 120 additions & 16 deletions compiler/rustc_session/src/filesearch.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! A module for searching for libraries

use smallvec::{smallvec, SmallVec};
use std::env;
use std::fs;
use std::iter::FromIterator;
Expand Down Expand Up @@ -62,9 +63,99 @@ pub fn make_target_lib_path(sysroot: &Path, target_triple: &str) -> PathBuf {
PathBuf::from_iter([sysroot, Path::new(&rustlib_path), Path::new("lib")])
}

#[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;
}

/// This function checks if sysroot is found using env::args().next(), and if it
/// is not found, uses env::current_exe() to imply sysroot.
pub fn get_or_default_sysroot() -> PathBuf {
/// is not found, finds sysroot from current rustc_driver dll.
pub fn get_or_default_sysroot() -> Result<PathBuf, String> {
// Follow symlinks. If the resolved path is relative, make it absolute.
fn canonicalize(path: PathBuf) -> PathBuf {
let path = fs::canonicalize(&path).unwrap_or(path);
Expand All @@ -74,17 +165,32 @@ pub fn get_or_default_sysroot() -> PathBuf {
fix_windows_verbatim_for_gcc(&path)
}

// Use env::current_exe() to get the path of the executable following
// symlinks/canonicalizing components.
fn from_current_exe() -> PathBuf {
match env::current_exe() {
Ok(exe) => {
let mut p = canonicalize(exe);
p.pop();
p.pop();
p
}
Err(e) => panic!("failed to get current_exe: {e}"),
fn default_from_rustc_driver_dll() -> Result<PathBuf, String> {
let dll = current_dll_path().and_then(|s| Ok(canonicalize(s)))?;

// `dll` will be in one of the following two:
// - compiler's libdir: $sysroot/lib/*.dll
// - target's libdir: $sysroot/lib/rustlib/$target/lib/*.dll
//
// use `parent` twice to chop off the file name and then also the
// directory containing the dll
let dir = dll.parent().and_then(|p| p.parent()).ok_or(format!(
"Could not move 2 levels upper using `parent()` on {}",
dll.display()
))?;

// if `dir` points target's dir, move up to the sysroot
if dir.ends_with(crate::config::host_triple()) {
dir.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())
.ok_or(format!(
"Could not move 3 levels upper using `parent()` on {}",
dir.display()
))
} else {
Ok(dir.to_owned())
}
}

Expand Down Expand Up @@ -118,7 +224,5 @@ pub fn get_or_default_sysroot() -> PathBuf {
}
}

// Check if sysroot is found using env::args().next(), and if is not found,
// use env::current_exe() to imply sysroot.
from_env_args_next().unwrap_or_else(from_current_exe)
Ok(from_env_args_next().unwrap_or(default_from_rustc_driver_dll()?))
}
2 changes: 1 addition & 1 deletion compiler/rustc_session/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1280,7 +1280,7 @@ pub fn build_session(

let sysroot = match &sopts.maybe_sysroot {
Some(sysroot) => sysroot.clone(),
None => filesearch::get_or_default_sysroot(),
None => filesearch::get_or_default_sysroot().expect("Failed finding sysroot"),
};

let target_cfg = config::build_target_config(&sopts, target_override, &sysroot);
Expand Down
Loading