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

For dylib crates, warn about GNU ld <=2.28 #66839

Closed
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
133 changes: 133 additions & 0 deletions src/librustc_codegen_ssa/back/link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,136 @@ fn link_staticlib<'a, B: ArchiveBuilder<'a>>(sess: &'a Session,
}
}


/// rust-lang/rust#61539: bugs in older versions of GNU `ld` cause problems that
/// are readily exposed under our default setting of disabling PLT (PR
/// rust-lang/rust#54592). Heuristically detect and warn about this.
fn check_for_buggy_ld_version(sess: &Session,
program_name: &Path,
flavor: LinkerFlavor,
crate_type: config::CrateType) {
debug!("check_for_buggy_ld_version: \
program_name: {:?} flavor: {:?} crate_type: {:?}",
program_name, flavor, crate_type);

match crate_type {
// This is the one case that we fire on, because it is the one place we
// know of where using the output in a "supported" fashion (*) can
// trigger the bug in old GNU ld versions.
//
// (*) Of course this raises the question of how much support do we give
// Rust dylibs in the first place
config::CrateType::Dylib => (),

// FIXME: should we include CrateType::Cdylib in the warning? It is not
// clear why we haven't seen it there.
config::CrateType::Cdylib => return,

// We deliberately do not include CrateType::ProcMacro in the warning,
// as it would cause too many false-positives (and ot actually observe
// the known bugs in that context, you would have to be using the
// geneated dylib in an unsupported fashion anyway).
config::CrateType::ProcMacro => return,

// Static objects won't run into this (unless they load a dynamic
// object, which this heuristic is not attempting to detect).
config::CrateType::Executable |
config::CrateType::Rlib |
config::CrateType::Staticlib => return,
};

let mut version_cmd = Command::new(program_name);
match flavor {
LinkerFlavor::Gcc => {
// run `gcc -v -Xlinker --version` to query gcc for version info of underlying linker
version_cmd.args(&["-v", "-Xlinker", "--version"]);
}
LinkerFlavor::Ld => {
// run `ld --version`
version_cmd.args(&["--version"]);
}
LinkerFlavor::Msvc |
LinkerFlavor::Em |
LinkerFlavor::Lld(..) |
LinkerFlavor::PtxLinker => {
// Not GNU ld, so don't bother inspecting version.
return;
}
}
let version_check_invocation = format!("{:?}", &version_cmd);
debug!("check_for_buggy_ld_version version_check_invocation: {:?}",
version_check_invocation);
let output = match version_cmd.output() {
Err(_) => {
sess.warn(&format!("Linker version inspection failed to execute: `{}`",
version_check_invocation));
return;
}
Ok(output) => output,
};
let out = String::from_utf8_lossy(&output.stdout);

debug!("check_for_buggy_ld_version out: {:?}", out);

let first_line = match out.lines().next() {
Some(line) => line,
None => {
sess.warn(&format!("Linker version inspection had no lines of output: `{}`",
version_check_invocation));
return;
}
};
debug!("check_for_buggy_ld_version first_line: {:?}", first_line);

if !first_line.contains("GNU ld") {
// If we cannot find "GNU ld" in the version string, then assume that
// this is not actually GNU ld; no need for warning.
return;
}

let version_suffix_start = match first_line.find(" 2.") {
None => {
// if we cannot find ` 2.`, then assume that this an ld version that
// does not have the bug; no need for warning.
return;
}
Some(space_version_start) => {
// skip the space character so we are looking at "2.x.y-zzz"
space_version_start + 1
}
};
let version_suffix = &first_line[version_suffix_start..];
debug!("check_for_buggy_ld_version version_suffix: {:?}", version_suffix);

let parse = |suffix: &str| -> Option<(u32, u32)> {
let mut components = suffix.split('.');
let major: u32 = components.next()?.parse().ok()?;
let minor: u32 = components.next()?.parse().ok()?;
Some((major, minor))
};
let (major, minor) = match parse(version_suffix) {
None => {
sess.warn(&format!("Linker version inspection failed to parse: `{}`, output: {}",
version_check_invocation, out));
return;
}
Some(version) => version,
};
debug!("check_for_buggy_ld_version (major, minor): {:?}", (major, minor));

let is_old_buggy_version = major < 2 || (major == 2 && minor < 29);

if is_old_buggy_version {
let diag = sess.diagnostic();
diag.warn(&format!("Using linker `{}` with Rust dynamic libraries has known bugs.",
first_line));
diag.note_without_error(
"Consider upgrading to GNU ld version 2.29 or newer, or using a different linker.");
diag.note_without_error(
"For more information, see https://github.com/rust-lang/rust/issues/61539");
}
}

// Create a dynamic library or executable
//
// This will invoke the system linker/cc to create the resulting file. This
Expand All @@ -476,6 +606,9 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(sess: &'a Session,
// The invocations of cc share some flags across platforms
let (pname, mut cmd) = get_linker(sess, &linker, flavor);

// rust-lang/rust#61539: heuristically inspect ld version to warn about bugs
check_for_buggy_ld_version(sess, &pname, flavor, crate_type);

if let Some(args) = sess.target.target.options.pre_link_args.get(&flavor) {
cmd.args(args);
}
Expand Down