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

Replace MinGW library hack with heuristic controlling link mode #76167

Merged
merged 2 commits into from
Sep 3, 2020
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
119 changes: 26 additions & 93 deletions compiler/rustc_codegen_ssa/src/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1014,86 +1014,7 @@ fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLib]) {
}
}

// Because windows-gnu target is meant to be self-contained for pure Rust code it bundles
// own mingw-w64 libraries. These libraries are usually not compatible with mingw-w64
// installed in the system. This breaks many cases where Rust is mixed with other languages
// (e.g. *-sys crates).
// We prefer system mingw-w64 libraries if they are available to avoid this issue.
fn get_crt_libs_path(sess: &Session) -> Option<PathBuf> {
fn find_exe_in_path<P>(exe_name: P) -> Option<PathBuf>
where
P: AsRef<Path>,
{
for dir in env::split_paths(&env::var_os("PATH")?) {
let full_path = dir.join(&exe_name);
if full_path.is_file() {
return Some(fix_windows_verbatim_for_gcc(&full_path));
}
}
None
}

fn probe(sess: &Session) -> Option<PathBuf> {
if let (linker, LinkerFlavor::Gcc) = linker_and_flavor(&sess) {
let linker_path = if cfg!(windows) && linker.extension().is_none() {
linker.with_extension("exe")
} else {
linker
};
if let Some(linker_path) = find_exe_in_path(linker_path) {
let mingw_arch = match &sess.target.target.arch {
x if x == "x86" => "i686",
x => x,
};
let mingw_bits = &sess.target.target.target_pointer_width;
let mingw_dir = format!("{}-w64-mingw32", mingw_arch);
// Here we have path/bin/gcc but we need path/
let mut path = linker_path;
path.pop();
path.pop();
// Loosely based on Clang MinGW driver
let probe_paths = vec![
path.join(&mingw_dir).join("lib"), // Typical path
path.join(&mingw_dir).join("sys-root/mingw/lib"), // Rare path
path.join(format!(
"lib/mingw/tools/install/mingw{}/{}/lib",
&mingw_bits, &mingw_dir
)), // Chocolatey is creative
];
for probe_path in probe_paths {
if probe_path.join("crt2.o").exists() {
return Some(probe_path);
};
}
};
};
None
}

let mut system_library_path = sess.system_library_path.borrow_mut();
match &*system_library_path {
Some(Some(compiler_libs_path)) => Some(compiler_libs_path.clone()),
Some(None) => None,
None => {
let path = probe(sess);
*system_library_path = Some(path.clone());
path
}
}
}

fn get_object_file_path(sess: &Session, name: &str, self_contained: bool) -> PathBuf {
// prefer system {,dll}crt2.o libs, see get_crt_libs_path comment for more details
if sess.opts.debugging_opts.link_self_contained.is_none()
&& sess.target.target.llvm_target.contains("windows-gnu")
{
if let Some(compiler_libs_path) = get_crt_libs_path(sess) {
let file_path = compiler_libs_path.join(name);
if file_path.exists() {
return file_path;
}
}
}
let fs = sess.target_filesearch(PathKind::Native);
let file_path = fs.get_lib_path().join(name);
if file_path.exists() {
Expand Down Expand Up @@ -1286,10 +1207,32 @@ fn link_output_kind(sess: &Session, crate_type: CrateType) -> LinkOutputKind {
}
}

// Returns true if linker is located within sysroot
fn detect_self_contained_mingw(sess: &Session) -> bool {
let (linker, _) = linker_and_flavor(&sess);
// Assume `-C linker=rust-lld` as self-contained mode
if linker == Path::new("rust-lld") {
return true;
}
let linker_with_extension = if cfg!(windows) && linker.extension().is_none() {
linker.with_extension("exe")
} else {
linker
};
for dir in env::split_paths(&env::var_os("PATH").unwrap_or_default()) {
let full_path = dir.join(&linker_with_extension);
// If linker comes from sysroot assume self-contained mode
if full_path.is_file() && !full_path.starts_with(&sess.sysroot) {
return false;
}
}
true
}

/// Whether we link to our own CRT objects instead of relying on gcc to pull them.
/// We only provide such support for a very limited number of targets.
fn crt_objects_fallback(sess: &Session, crate_type: CrateType) -> bool {
if let Some(self_contained) = sess.opts.debugging_opts.link_self_contained {
if let Some(self_contained) = sess.opts.cg.link_self_contained {
return self_contained;
}

Expand All @@ -1298,10 +1241,10 @@ fn crt_objects_fallback(sess: &Session, crate_type: CrateType) -> bool {
// based on host and linker path, for example.
// (https://github.com/rust-lang/rust/pull/71769#issuecomment-626330237).
Some(CrtObjectsFallback::Musl) => sess.crt_static(Some(crate_type)),
// FIXME: Find some heuristic for "native mingw toolchain is available",
// likely based on `get_crt_libs_path` (https://github.com/rust-lang/rust/pull/67429).
Some(CrtObjectsFallback::Mingw) => {
sess.host == sess.target.target && sess.target.target.target_vendor != "uwp"
sess.host == sess.target.target
&& sess.target.target.target_vendor != "uwp"
&& detect_self_contained_mingw(&sess)
}
// FIXME: Figure out cases in which WASM needs to link with a native toolchain.
Some(CrtObjectsFallback::Wasm) => true,
Expand Down Expand Up @@ -1498,16 +1441,6 @@ fn link_local_crate_native_libs_and_dependent_crate_libs<'a, B: ArchiveBuilder<'

/// Add sysroot and other globally set directories to the directory search list.
fn add_library_search_dirs(cmd: &mut dyn Linker, sess: &Session, self_contained: bool) {
// Prefer system mingw-w64 libs, see get_crt_libs_path comment for more details.
if sess.opts.debugging_opts.link_self_contained.is_none()
&& cfg!(windows)
&& sess.target.target.llvm_target.contains("windows-gnu")
{
if let Some(compiler_libs_path) = get_crt_libs_path(sess) {
cmd.include_path(&compiler_libs_path);
}
}

// The default library location, we need this to find the runtime.
// The location of crates will be determined as needed.
let lib_path = sess.target_filesearch(PathKind::All).get_lib_path();
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_interface/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,7 @@ fn test_codegen_options_tracking_hash() {
// `link_arg` is omitted because it just forwards to `link_args`.
untracked!(link_args, vec![String::from("abc"), String::from("def")]);
untracked!(link_dead_code, Some(true));
untracked!(link_self_contained, Some(true));
untracked!(linker, Some(PathBuf::from("linker")));
untracked!(linker_flavor, Some(LinkerFlavor::Gcc));
untracked!(no_stack_check, true);
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_session/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,9 @@ options! {CodegenOptions, CodegenSetter, basic_codegen_options,
"extra arguments to append to the linker invocation (space separated)"),
link_dead_code: Option<bool> = (None, parse_opt_bool, [UNTRACKED],
"keep dead code at link time (useful for code coverage) (default: no)"),
link_self_contained: Option<bool> = (None, parse_opt_bool, [UNTRACKED],
"control whether to link Rust provided C objects/libraries or rely
on C toolchain installed in the system"),
linker: Option<PathBuf> = (None, parse_opt_pathbuf, [UNTRACKED],
"system linker to link outputs with"),
linker_flavor: Option<LinkerFlavor> = (None, parse_linker_flavor, [UNTRACKED],
Expand Down Expand Up @@ -894,9 +897,6 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
"keep hygiene data after analysis (default: no)"),
link_native_libraries: bool = (true, parse_bool, [UNTRACKED],
"link native libraries in the linker invocation (default: yes)"),
link_self_contained: Option<bool> = (None, parse_opt_bool, [TRACKED],
"control whether to link Rust provided C objects/libraries or rely
on C toolchain installed in the system"),
link_only: bool = (false, parse_bool, [TRACKED],
"link the `.rlink` file generated by `-Z no-link` (default: no)"),
llvm_time_trace: bool = (false, parse_bool, [UNTRACKED],
Expand Down
20 changes: 16 additions & 4 deletions src/doc/rustc/src/codegen-options/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,13 @@ incremental builds the default is 256 which allows caching to be more granular.

## control-flow-guard

This flag controls whether LLVM enables the Windows [Control Flow
Guard](https://docs.microsoft.com/en-us/windows/win32/secbp/control-flow-guard)
platform security feature. This flag is currently ignored for non-Windows targets.
This flag controls whether LLVM enables the Windows [Control Flow
Guard](https://docs.microsoft.com/en-us/windows/win32/secbp/control-flow-guard)
platform security feature. This flag is currently ignored for non-Windows targets.
It takes one of the following values:

* `y`, `yes`, `on`, `checks`, or no value: enable Control Flow Guard.
* `nochecks`: emit Control Flow Guard metadata without runtime enforcement checks (this
* `nochecks`: emit Control Flow Guard metadata without runtime enforcement checks (this
should only be used for testing purposes as it does not provide security enforcement).
* `n`, `no`, `off`: do not enable Control Flow Guard (the default).

Expand Down Expand Up @@ -200,6 +200,18 @@ the following values:
An example of when this flag might be useful is when trying to construct code coverage
metrics.

## link-self-contained

On targets that support it this flag controls whether the linker will use libraries and objects
shipped with Rust instead or those in the system.
It takes one of the following values:

* no value: rustc will use heuristic to disable self-contained mode if system has necessary tools.
* `y`, `yes`, `on`: use only libraries/objects shipped with Rust.
* `n`, `no`, or `off`: rely on the user or the linker to provide non-Rust libraries/objects.

This allows overriding cases when detection fails or user wants to use shipped libraries.

## linker

This flag controls which linker `rustc` invokes to link your code. It takes a
Expand Down