From 071f0422c6dd897bc61891f96d4448df44d8069f Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 28 May 2020 22:12:31 +0300 Subject: [PATCH] linker: Add a linker rerun hack for gcc versions not supporting -static-pie --- src/librustc_codegen_ssa/back/link.rs | 65 +++++++++++++++++--- src/librustc_target/spec/tests/tests_impl.rs | 5 ++ 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/src/librustc_codegen_ssa/back/link.rs b/src/librustc_codegen_ssa/back/link.rs index 9a7c4907754b0..b322e9a8fef1c 100644 --- a/src/librustc_codegen_ssa/back/link.rs +++ b/src/librustc_codegen_ssa/back/link.rs @@ -12,7 +12,7 @@ use rustc_session::utils::NativeLibKind; /// need out of the shared crate context before we get rid of it. use rustc_session::{filesearch, Session}; use rustc_span::symbol::Symbol; -use rustc_target::spec::crt_objects::CrtObjectsFallback; +use rustc_target::spec::crt_objects::{CrtObjects, CrtObjectsFallback}; use rustc_target::spec::{LinkOutputKind, LinkerFlavor, LldFlavor}; use rustc_target::spec::{PanicStrategy, RelocModel, RelroLevel}; @@ -25,16 +25,10 @@ use crate::{looks_like_rust_object_file, CodegenResults, CrateInfo, METADATA_FIL use cc::windows_registry; use tempfile::{Builder as TempFileBuilder, TempDir}; -use std::ascii; -use std::char; -use std::env; use std::ffi::OsString; -use std::fmt; -use std::fs; -use std::io; use std::path::{Path, PathBuf}; use std::process::{ExitStatus, Output, Stdio}; -use std::str; +use std::{ascii, char, env, fmt, fs, io, mem, str}; pub fn remove(sess: &Session, path: &Path) { if let Err(e) = fs::remove_file(path) { @@ -543,6 +537,61 @@ fn link_natively<'a, B: ArchiveBuilder<'a>>( continue; } + // Detect '-static-pie' used with an older version of gcc or clang not supporting it. + // Fallback from '-static-pie' to '-static' in that case. + if sess.target.target.options.linker_is_gnu + && flavor != LinkerFlavor::Ld + && (out.contains("unrecognized command line option") + || out.contains("unknown argument")) + && (out.contains("-static-pie") || out.contains("--no-dynamic-linker")) + && cmd.get_args().iter().any(|e| e.to_string_lossy() == "-static-pie") + { + info!("linker output: {:?}", out); + warn!( + "Linker does not support -static-pie command line option. Retrying with -static instead." + ); + // Mirror `add_(pre,post)_link_objects` to replace CRT objects. + let fallback = crt_objects_fallback(sess, crate_type); + let opts = &sess.target.target.options; + let pre_objects = + if fallback { &opts.pre_link_objects_fallback } else { &opts.pre_link_objects }; + let post_objects = + if fallback { &opts.post_link_objects_fallback } else { &opts.post_link_objects }; + let get_objects = |objects: &CrtObjects, kind| { + objects + .get(&kind) + .iter() + .copied() + .flatten() + .map(|obj| get_object_file_path(sess, obj).into_os_string()) + .collect::>() + }; + let pre_objects_static_pie = get_objects(pre_objects, LinkOutputKind::StaticPicExe); + let post_objects_static_pie = get_objects(post_objects, LinkOutputKind::StaticPicExe); + let mut pre_objects_static = get_objects(pre_objects, LinkOutputKind::StaticNoPicExe); + let mut post_objects_static = get_objects(post_objects, LinkOutputKind::StaticNoPicExe); + // Assume that we know insertion positions for the replacement arguments from replaced + // arguments, which is true for all supported targets. + assert!(pre_objects_static.is_empty() || !pre_objects_static_pie.is_empty()); + assert!(post_objects_static.is_empty() || !post_objects_static_pie.is_empty()); + for arg in cmd.take_args() { + if arg.to_string_lossy() == "-static-pie" { + // Replace the output kind. + cmd.arg("-static"); + } else if pre_objects_static_pie.contains(&arg) { + // Replace the pre-link objects (replace the first and remove the rest). + cmd.args(mem::take(&mut pre_objects_static)); + } else if post_objects_static_pie.contains(&arg) { + // Replace the post-link objects (replace the first and remove the rest). + cmd.args(mem::take(&mut post_objects_static)); + } else { + cmd.arg(arg); + } + } + info!("{:?}", &cmd); + continue; + } + // Here's a terribly awful hack that really shouldn't be present in any // compiler. Here an environment variable is supported to automatically // retry the linker invocation if the linker looks like it segfaulted. diff --git a/src/librustc_target/spec/tests/tests_impl.rs b/src/librustc_target/spec/tests/tests_impl.rs index 4cf186bdd7c1a..fb3f912ffde1a 100644 --- a/src/librustc_target/spec/tests/tests_impl.rs +++ b/src/librustc_target/spec/tests/tests_impl.rs @@ -39,5 +39,10 @@ impl Target { assert_eq!(self.options.lld_flavor, LldFlavor::Link); } } + assert!( + (self.options.pre_link_objects_fallback.is_empty() + && self.options.post_link_objects_fallback.is_empty()) + || self.options.crt_objects_fallback.is_some() + ); } }