Skip to content

improve filesearch::get_or_default_sysroot #103660

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

Merged
merged 1 commit into from
Nov 5, 2022
Merged
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
@@ -3646,7 +3646,6 @@ dependencies = [
name = "rustc_interface"
version = "0.0.0"
dependencies = [
"libc",
"libloading",
"rustc-rayon",
"rustc-rayon-core",
@@ -3689,7 +3688,6 @@ dependencies = [
"rustc_ty_utils",
"smallvec",
"tracing",
"winapi",
]

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

[[package]]
3 changes: 2 additions & 1 deletion compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
@@ -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(),
6 changes: 0 additions & 6 deletions compiler/rustc_interface/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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" }

97 changes: 2 additions & 95 deletions compiler/rustc_interface/src/util.rs
Original file line number Diff line number Diff line change
@@ -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};
@@ -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,
@@ -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
7 changes: 7 additions & 0 deletions compiler/rustc_session/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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
@@ -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
}
};
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;
@@ -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);
@@ -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())
}
}

@@ -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
@@ -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);
79 changes: 5 additions & 74 deletions src/tools/clippy/src/driver.rs
Original file line number Diff line number Diff line change
@@ -23,8 +23,8 @@ use std::borrow::Cow;
use std::env;
use std::ops::Deref;
use std::panic;
use std::path::{Path, PathBuf};
use std::process::{exit, Command};
use std::path::Path;
use std::process::exit;
use std::sync::LazyLock;

/// If a command-line option matches `find_arg`, then apply the predicate `pred` on its value. If
@@ -209,83 +209,21 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
interface::try_print_query_stack(&handler, num_frames);
}

fn toolchain_path(home: Option<String>, toolchain: Option<String>) -> Option<PathBuf> {
home.and_then(|home| {
toolchain.map(|toolchain| {
let mut path = PathBuf::from(home);
path.push("toolchains");
path.push(toolchain);
path
})
})
}

#[allow(clippy::too_many_lines)]
pub fn main() {
rustc_driver::init_rustc_env_logger();
LazyLock::force(&ICE_HOOK);
exit(rustc_driver::catch_with_exit_code(move || {
let mut orig_args: Vec<String> = env::args().collect();

// Get the sysroot, looking from most specific to this invocation to the least:
// - command line
// - runtime environment
// - SYSROOT
// - RUSTUP_HOME, MULTIRUST_HOME, RUSTUP_TOOLCHAIN, MULTIRUST_TOOLCHAIN
// - sysroot from rustc in the path
// - compile-time environment
// - SYSROOT
// - RUSTUP_HOME, MULTIRUST_HOME, RUSTUP_TOOLCHAIN, MULTIRUST_TOOLCHAIN
let sys_root_arg = arg_value(&orig_args, "--sysroot", |_| true);
let have_sys_root_arg = sys_root_arg.is_some();
let sys_root = sys_root_arg
.map(PathBuf::from)
.or_else(|| std::env::var("SYSROOT").ok().map(PathBuf::from))
.or_else(|| {
let home = std::env::var("RUSTUP_HOME")
.or_else(|_| std::env::var("MULTIRUST_HOME"))
.ok();
let toolchain = std::env::var("RUSTUP_TOOLCHAIN")
.or_else(|_| std::env::var("MULTIRUST_TOOLCHAIN"))
.ok();
toolchain_path(home, toolchain)
})
.or_else(|| {
Command::new("rustc")
.arg("--print")
.arg("sysroot")
.output()
.ok()
.and_then(|out| String::from_utf8(out.stdout).ok())
.map(|s| PathBuf::from(s.trim()))
})
.or_else(|| option_env!("SYSROOT").map(PathBuf::from))
.or_else(|| {
let home = option_env!("RUSTUP_HOME")
.or(option_env!("MULTIRUST_HOME"))
.map(ToString::to_string);
let toolchain = option_env!("RUSTUP_TOOLCHAIN")
.or(option_env!("MULTIRUST_TOOLCHAIN"))
.map(ToString::to_string);
toolchain_path(home, toolchain)
})
.map(|pb| pb.to_string_lossy().to_string())
.expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust");

// make "clippy-driver --rustc" work like a subcommand that passes further args to "rustc"
// for example `clippy-driver --rustc --version` will print the rustc version that clippy-driver
// uses
if let Some(pos) = orig_args.iter().position(|arg| arg == "--rustc") {
orig_args.remove(pos);
orig_args[0] = "rustc".to_string();

// if we call "rustc", we need to pass --sysroot here as well
let mut args: Vec<String> = orig_args.clone();
if !have_sys_root_arg {
args.extend(vec!["--sysroot".into(), sys_root]);
};

return rustc_driver::RunCompiler::new(&args, &mut DefaultCallbacks).run();
return rustc_driver::RunCompiler::new(&orig_args, &mut DefaultCallbacks).run();
}

if orig_args.iter().any(|a| a == "--version" || a == "-V") {
@@ -308,14 +246,6 @@ pub fn main() {
exit(0);
}

// this conditional check for the --sysroot flag is there so users can call
// `clippy_driver` directly
// without having to pass --sysroot or anything
let mut args: Vec<String> = orig_args.clone();
if !have_sys_root_arg {
args.extend(vec!["--sysroot".into(), sys_root]);
};

let mut no_deps = false;
let clippy_args_var = env::var("CLIPPY_ARGS").ok();
let clippy_args = clippy_args_var
@@ -344,10 +274,11 @@ pub fn main() {

let clippy_enabled = !cap_lints_allow && (!no_deps || in_primary_package);
if clippy_enabled {
let mut args: Vec<String> = orig_args.clone();
args.extend(clippy_args);
rustc_driver::RunCompiler::new(&args, &mut ClippyCallbacks { clippy_args_var }).run()
} else {
rustc_driver::RunCompiler::new(&args, &mut RustcCallbacks { clippy_args_var }).run()
rustc_driver::RunCompiler::new(&orig_args, &mut RustcCallbacks { clippy_args_var }).run()
}
}))
}
70 changes: 11 additions & 59 deletions src/tools/miri/src/bin/miri.rs
Original file line number Diff line number Diff line change
@@ -216,76 +216,28 @@ fn init_late_loggers(tcx: TyCtxt<'_>) {
}
}

/// Returns the "default sysroot" that Miri will use for host things if no `--sysroot` flag is set.
/// Should be a compile-time constant.
fn host_sysroot() -> Option<String> {
if option_env!("RUSTC_STAGE").is_some() {
// This is being built as part of rustc, and gets shipped with rustup.
// We can rely on the sysroot computation in librustc_session.
return None;
}
// For builds outside rustc, we need to ensure that we got a sysroot
// that gets used as a default. The sysroot computation in librustc_session would
// end up somewhere in the build dir (see `get_or_default_sysroot`).
// Taken from PR <https://github.com/Manishearth/rust-clippy/pull/911>.
let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
Some(match (home, toolchain) {
(Some(home), Some(toolchain)) => {
// Check that at runtime, we are still in this toolchain (if there is any toolchain).
if let Some(toolchain_runtime) =
env::var_os("RUSTUP_TOOLCHAIN").or_else(|| env::var_os("MULTIRUST_TOOLCHAIN"))
{
if toolchain_runtime != toolchain {
show_error!(
"This Miri got built with local toolchain `{toolchain}`, but now is being run under a different toolchain. \n\
Make sure to run Miri in the toolchain it got built with, e.g. via `cargo +{toolchain} miri`."
)
}
}
format!("{home}/toolchains/{toolchain}")
}
_ => option_env!("RUST_SYSROOT")
.unwrap_or_else(|| {
show_error!(
"To build Miri without rustup, set the `RUST_SYSROOT` env var at build time",
)
})
.to_owned(),
})
}

/// Execute a compiler with the given CLI arguments and callbacks.
fn run_compiler(
mut args: Vec<String>,
target_crate: bool,
callbacks: &mut (dyn rustc_driver::Callbacks + Send),
) -> ! {
// Make sure we use the right default sysroot. The default sysroot is wrong,
// because `get_or_default_sysroot` in `librustc_session` bases that on `current_exe`.
//
// Make sure we always call `host_sysroot` as that also does some sanity-checks
// of the environment we were built in and whether it matches what we are running in.
let host_default_sysroot = host_sysroot();
// Now see if we even need to set something.
let sysroot_flag = "--sysroot";
if !args.iter().any(|e| e == sysroot_flag) {
// No sysroot was set, let's see if we have a custom default we want to configure.
let default_sysroot = if target_crate {
if target_crate {
// Miri needs a custom sysroot for target crates.
// If no `--sysroot` is given, the `MIRI_SYSROOT` env var is consulted to find where
// that sysroot lives, and that is passed to rustc.
let sysroot_flag = "--sysroot";
if !args.iter().any(|e| e == sysroot_flag) {
// Using the built-in default here would be plain wrong, so we *require*
// the env var to make sure things make sense.
Some(env::var("MIRI_SYSROOT").unwrap_or_else(|_| {
let miri_sysroot = env::var("MIRI_SYSROOT").unwrap_or_else(|_| {
show_error!(
"Miri was invoked in 'target' mode without `MIRI_SYSROOT` or `--sysroot` being set"
)
}))
} else {
host_default_sysroot
};
if let Some(sysroot) = default_sysroot {
// We need to overwrite the default that librustc_session would compute.
)
});

args.push(sysroot_flag.to_owned());
args.push(sysroot);
args.push(miri_sysroot);
}
}