Skip to content

Commit d635392

Browse files
committed
A heuristic diagnostic to try to help prevent issues related to old GNU ld bugs like issue 61539.
1 parent 5f1d6c4 commit d635392

File tree

1 file changed

+98
-0
lines changed
  • src/librustc_codegen_ssa/back

1 file changed

+98
-0
lines changed

src/librustc_codegen_ssa/back/link.rs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,101 @@ fn link_staticlib<'a, B: ArchiveBuilder<'a>>(sess: &'a Session,
460460
}
461461
}
462462

463+
464+
/// rust-lang/rust#61539: bugs in older versions of GNU `ld` cause problems that
465+
/// are readily exposed under our default setting of disabling PLT (PR
466+
/// rust-lang/rust#54592). Heuristically detect and warn about this.
467+
fn check_for_buggy_ld_version(sess: &Session,
468+
program_name: &Path,
469+
flavor: LinkerFlavor,
470+
crate_type: config::CrateType) {
471+
debug!("check_for_buggy_ld_version: \
472+
program_name: {:?} flavor: {:?} crate_type: {:?}",
473+
program_name, flavor, crate_type);
474+
475+
match crate_type {
476+
config::CrateType::Dylib |
477+
config::CrateType::ProcMacro => (),
478+
479+
// FIXME: should we include CrateType::Cdylib in the warning? It is not
480+
// clear why we haven't seen it there.
481+
config::CrateType::Cdylib => return,
482+
483+
// Static objects won't run into this (unless they load a dynamic
484+
// object, which this heuristic is not attempting to detect).
485+
config::CrateType::Executable |
486+
config::CrateType::Rlib |
487+
config::CrateType::Staticlib => return,
488+
};
489+
490+
let mut version_cmd = Command::new(program_name);
491+
match flavor {
492+
LinkerFlavor::Gcc => {
493+
// run `gcc -v -Xlinker --version` to query gcc for version info of underlying linker
494+
version_cmd.args(&["-v", "-Xlinker", "--version"]);
495+
}
496+
LinkerFlavor::Ld => {
497+
// run `ld --version`
498+
version_cmd.args(&["--version"]);
499+
}
500+
LinkerFlavor::Msvc |
501+
LinkerFlavor::Em |
502+
LinkerFlavor::Lld(..) |
503+
LinkerFlavor::PtxLinker => {
504+
// Not GNU ld, so don't bother inspecting version.
505+
return;
506+
}
507+
}
508+
let version_check_invocation = format!("{:?}", &version_cmd);
509+
debug!("check_for_buggy_ld_version version_check_invocation: {:?}",
510+
version_check_invocation);
511+
let output = match version_cmd.output() {
512+
Err(_) => {
513+
sess.warn(&format!("Linker version inspection failed to execute: `{}`",
514+
version_check_invocation));
515+
return;
516+
}
517+
Ok(output) => output,
518+
};
519+
let out = String::from_utf8_lossy(&output.stdout);
520+
521+
debug!("check_for_buggy_ld_version out: {:?}", out);
522+
523+
let parse = |first_line: &str| -> Option<(u32, u32)> {
524+
let suffix = first_line.splitn(2, "GNU ld version ").last()?;
525+
let version_components: Vec<_> = suffix.split('.').collect();
526+
let major: u32 = version_components.get(0)?.parse().ok()?;
527+
let minor: u32 = version_components.get(1)?.parse().ok()?;
528+
Some((major, minor))
529+
};
530+
531+
let first_line = out.lines().next();
532+
533+
debug!("check_for_buggy_ld_version first_line: {:?}", first_line);
534+
535+
let (major, minor) = match first_line.and_then(parse) {
536+
None => {
537+
sess.warn(&format!("Linker version inspection failed to parse: `{}`, output: {}",
538+
version_check_invocation, out));
539+
return;
540+
}
541+
Some(version) => version,
542+
};
543+
debug!("check_for_buggy_ld_version (major, minor): {:?}", (major, minor));
544+
545+
let is_old_buggy_version = major < 2 || (major == 2 && minor < 29);
546+
547+
if is_old_buggy_version {
548+
let diag = sess.diagnostic();
549+
diag.warn(&format!("Using linker `{}` with Rust dynamic libraries has known bugs.",
550+
first_line.unwrap()));
551+
diag.note_without_error(
552+
"Consider upgrading to GNU ld version 2.29 or newer, or using a different linker.");
553+
diag.note_without_error(
554+
"For more information, see https://github.com/rust-lang/rust/issues/61539");
555+
}
556+
}
557+
463558
// Create a dynamic library or executable
464559
//
465560
// This will invoke the system linker/cc to create the resulting file. This
@@ -476,6 +571,9 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>(sess: &'a Session,
476571
// The invocations of cc share some flags across platforms
477572
let (pname, mut cmd) = get_linker(sess, &linker, flavor);
478573

574+
// rust-lang/rust#61539: heuristically inspect ld version to warn about bugs
575+
check_for_buggy_ld_version(sess, &pname, flavor, crate_type);
576+
479577
if let Some(args) = sess.target.target.options.pre_link_args.get(&flavor) {
480578
cmd.args(args);
481579
}

0 commit comments

Comments
 (0)