From 16350526d8d8b514327c62b904d26f74937a1b23 Mon Sep 17 00:00:00 2001 From: Jimmy Brush Date: Thu, 8 Feb 2018 07:48:16 -0500 Subject: [PATCH 1/5] pass correct pie args to gcc linker When linking with gcc, run gcc -v to see if --enable-default-pie is compiled in. If it is, pass -no-pie when necessary to disable pie. Otherwise, pass -pie when necessary to enable it. Fixes #48032 and fixes #35061 --- src/librustc_trans/back/link.rs | 66 ++++++++++++++++++++++++++----- src/librustc_trans/back/linker.rs | 10 +++++ 2 files changed, 66 insertions(+), 10 deletions(-) diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index f050edcd513b9..a84ac5cb8bc2f 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -897,16 +897,33 @@ fn link_args(cmd: &mut Linker, let used_link_args = &trans.crate_info.link_args; - if crate_type == config::CrateTypeExecutable && - t.options.position_independent_executables { - let empty_vec = Vec::new(); - let args = sess.opts.cg.link_args.as_ref().unwrap_or(&empty_vec); - let more_args = &sess.opts.cg.link_arg; - let mut args = args.iter().chain(more_args.iter()).chain(used_link_args.iter()); - - if get_reloc_model(sess) == llvm::RelocMode::PIC - && !sess.crt_static() && !args.any(|x| *x == "-static") { - cmd.position_independent_executable(); + if crate_type == config::CrateTypeExecutable { + let mut position_independent_executable = false; + + if t.options.position_independent_executables { + let empty_vec = Vec::new(); + let args = sess.opts.cg.link_args.as_ref().unwrap_or(&empty_vec); + let more_args = &sess.opts.cg.link_arg; + let mut args = args.iter().chain(more_args.iter()).chain(used_link_args.iter()); + + if get_reloc_model(sess) == llvm::RelocMode::PIC + && !sess.crt_static() && !args.any(|x| *x == "-static") { + position_independent_executable = true; + } + } + + // Check to see if gcc defaults to generating a position independent + // executable. If so, tell it when to disable pie. Otherwise, tell it + // when to enable it. We can't do both because older versions of gcc + // don't understand -no-pie and will blow up. + if is_pie_default(sess) { + if !position_independent_executable { + cmd.no_position_independent_executable(); + } + } else { + if position_independent_executable { + cmd.position_independent_executable(); + } } } @@ -1421,3 +1438,32 @@ fn is_full_lto_enabled(sess: &Session) -> bool { Lto::ThinLocal => false, } } + +fn is_pie_default(sess: &Session) -> bool { + match sess.linker_flavor() { + LinkerFlavor::Gcc => { + let (_, mut cmd, envs) = get_linker(sess); + // This will set PATH on windows + cmd.envs(envs); + cmd.arg("-v"); + + info!("{:?}", &cmd); + + let output = cmd.command() + .stdout(Stdio::piped()).stderr(Stdio::piped()) + .spawn() + .unwrap() + .wait_with_output() + .unwrap(); + + let ret = String::from_utf8_lossy(&output.stderr) + .contains("--enable-default-pie"); + + info!("gcc {} compiled with --enable-default-pie", + if ret { "IS" } else { "is NOT" }); + + ret + }, + _ => false, + } +} diff --git a/src/librustc_trans/back/linker.rs b/src/librustc_trans/back/linker.rs index aa29c3cc12058..7e7811c56c74e 100644 --- a/src/librustc_trans/back/linker.rs +++ b/src/librustc_trans/back/linker.rs @@ -105,6 +105,7 @@ pub trait Linker { fn add_object(&mut self, path: &Path); fn gc_sections(&mut self, keep_metadata: bool); fn position_independent_executable(&mut self); + fn no_position_independent_executable(&mut self); fn partial_relro(&mut self); fn full_relro(&mut self); fn optimize(&mut self); @@ -179,6 +180,7 @@ impl<'a> Linker for GccLinker<'a> { fn output_filename(&mut self, path: &Path) { self.cmd.arg("-o").arg(path); } fn add_object(&mut self, path: &Path) { self.cmd.arg(path); } fn position_independent_executable(&mut self) { self.cmd.arg("-pie"); } + fn no_position_independent_executable(&mut self) { self.cmd.arg("-no-pie"); } fn partial_relro(&mut self) { self.linker_arg("-z,relro"); } fn full_relro(&mut self) { self.linker_arg("-z,relro,-z,now"); } fn build_static_executable(&mut self) { self.cmd.arg("-static"); } @@ -439,6 +441,10 @@ impl<'a> Linker for MsvcLinker<'a> { // noop } + fn no_position_independent_executable(&mut self) { + // noop + } + fn partial_relro(&mut self) { // noop } @@ -647,6 +653,10 @@ impl<'a> Linker for EmLinker<'a> { // noop } + fn no_position_independent_executable(&mut self) { + // noop + } + fn partial_relro(&mut self) { // noop } From 8a72f589e5f8d628a5dcfcedbdd8eec117fbe2a2 Mon Sep 17 00:00:00 2001 From: Jimmy Brush Date: Sat, 10 Feb 2018 13:16:59 -0500 Subject: [PATCH 2/5] pass correct pie args to gcc linker 2 Recent versions of gcc default to creating a position independent executable and must be explicitly told not to with the -no-pie argument. Old versions of gcc don't understand -no-pie and will throw an error. Check for that case and retry without -no-pie. This is safe because these old versions of gcc should never default to creating a position independent executable. --- src/librustc_trans/back/link.rs | 66 ++++++++++++--------------------- 1 file changed, 23 insertions(+), 43 deletions(-) diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index a84ac5cb8bc2f..d15450212ae37 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -652,9 +652,6 @@ fn link_natively(sess: &Session, prog = time(sess.time_passes(), "running linker", || { exec_linker(sess, &mut cmd, tmpdir) }); - if !retry_on_segfault || i > 3 { - break - } let output = match prog { Ok(ref output) => output, Err(_) => break, @@ -665,6 +662,26 @@ fn link_natively(sess: &Session, let mut out = output.stderr.clone(); out.extend(&output.stdout); let out = String::from_utf8_lossy(&out); + + // Check to see if the link failed with "unrecognized command line option: + // '-no-pie'". If so, reperform the link step without the -no-pie option. This + // is safe because if the linker doesn't support -no-pie then it should not + // default to linking executables as pie. Different versions of gcc seem to + // use different quotes in the error message so don't check for them. + if out.contains("unrecognized command line option") && out.contains("-no-pie") { + info!("linker output: {:?}", out); + warn!("Linker does not support -no-pie command line option. Retrying without."); + for arg in cmd.take_args() { + if arg.to_string_lossy() != "-no-pie" { + cmd.arg(arg); + } + } + info!("{:?}", &cmd); + continue; + } + if !retry_on_segfault || i > 3 { + break + } let msg_segv = "clang: error: unable to execute command: Segmentation fault: 11"; let msg_bus = "clang: error: unable to execute command: Bus error: 10"; if !(out.contains(msg_segv) || out.contains(msg_bus)) { @@ -912,18 +929,10 @@ fn link_args(cmd: &mut Linker, } } - // Check to see if gcc defaults to generating a position independent - // executable. If so, tell it when to disable pie. Otherwise, tell it - // when to enable it. We can't do both because older versions of gcc - // don't understand -no-pie and will blow up. - if is_pie_default(sess) { - if !position_independent_executable { - cmd.no_position_independent_executable(); - } + if position_independent_executable { + cmd.position_independent_executable(); } else { - if position_independent_executable { - cmd.position_independent_executable(); - } + cmd.no_position_independent_executable(); } } @@ -1438,32 +1447,3 @@ fn is_full_lto_enabled(sess: &Session) -> bool { Lto::ThinLocal => false, } } - -fn is_pie_default(sess: &Session) -> bool { - match sess.linker_flavor() { - LinkerFlavor::Gcc => { - let (_, mut cmd, envs) = get_linker(sess); - // This will set PATH on windows - cmd.envs(envs); - cmd.arg("-v"); - - info!("{:?}", &cmd); - - let output = cmd.command() - .stdout(Stdio::piped()).stderr(Stdio::piped()) - .spawn() - .unwrap() - .wait_with_output() - .unwrap(); - - let ret = String::from_utf8_lossy(&output.stderr) - .contains("--enable-default-pie"); - - info!("gcc {} compiled with --enable-default-pie", - if ret { "IS" } else { "is NOT" }); - - ret - }, - _ => false, - } -} From f0e9af1c5509ecd57c2398085b639abfb15f4ba1 Mon Sep 17 00:00:00 2001 From: Jimmy Brush Date: Sun, 11 Feb 2018 10:50:18 -0500 Subject: [PATCH 3/5] verify passed -no-pie arg before retrying failed link --- src/librustc_trans/back/command.rs | 4 ++++ src/librustc_trans/back/link.rs | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/librustc_trans/back/command.rs b/src/librustc_trans/back/command.rs index 3b765a493e0e7..0bccef1e62a8e 100644 --- a/src/librustc_trans/back/command.rs +++ b/src/librustc_trans/back/command.rs @@ -109,6 +109,10 @@ impl Command { // extensions + pub fn get_args(&self) -> &[OsString] { + &self.args + } + pub fn take_args(&mut self) -> Vec { mem::replace(&mut self.args, Vec::new()) } diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index d15450212ae37..6f8b425ad56dc 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -668,7 +668,9 @@ fn link_natively(sess: &Session, // is safe because if the linker doesn't support -no-pie then it should not // default to linking executables as pie. Different versions of gcc seem to // use different quotes in the error message so don't check for them. - if out.contains("unrecognized command line option") && out.contains("-no-pie") { + if out.contains("unrecognized command line option") && + out.contains("-no-pie") && + cmd.get_args().iter().any(|e| e.to_string_lossy() == "-no-pie") { info!("linker output: {:?}", out); warn!("Linker does not support -no-pie command line option. Retrying without."); for arg in cmd.take_args() { From c8def9222be5079585e15b2902039d371b7527ce Mon Sep 17 00:00:00 2001 From: Jimmy Brush Date: Mon, 12 Feb 2018 13:32:55 -0500 Subject: [PATCH 4/5] handle -no-pie error from clang --- src/librustc_trans/back/link.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index 6f8b425ad56dc..0a3e6265c1b7e 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -664,11 +664,13 @@ fn link_natively(sess: &Session, let out = String::from_utf8_lossy(&out); // Check to see if the link failed with "unrecognized command line option: - // '-no-pie'". If so, reperform the link step without the -no-pie option. This - // is safe because if the linker doesn't support -no-pie then it should not - // default to linking executables as pie. Different versions of gcc seem to - // use different quotes in the error message so don't check for them. - if out.contains("unrecognized command line option") && + // '-no-pie'" for gcc or "unknown argument: '-no-pie'" for clang. If so, + // reperform the link step without the -no-pie option. This is safe because + // if the linker doesn't support -no-pie then it should not default to + // linking executables as pie. Different versions of gcc seem to use + // different quotes in the error message so don't check for them. + if (out.contains("unrecognized command line option") || + out.contains("unknown argument")) && out.contains("-no-pie") && cmd.get_args().iter().any(|e| e.to_string_lossy() == "-no-pie") { info!("linker output: {:?}", out); From ab9cae1ba192afcc35e8bd6a5fddcd1445d05da7 Mon Sep 17 00:00:00 2001 From: Jimmy Brush Date: Tue, 13 Feb 2018 22:09:02 -0500 Subject: [PATCH 5/5] only pass -no-pie if linker_is_gnu --- src/librustc_trans/back/link.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index 0a3e6265c1b7e..8be4dc48d5122 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -669,7 +669,8 @@ fn link_natively(sess: &Session, // if the linker doesn't support -no-pie then it should not default to // linking executables as pie. Different versions of gcc seem to use // different quotes in the error message so don't check for them. - if (out.contains("unrecognized command line option") || + if sess.target.target.options.linker_is_gnu && + (out.contains("unrecognized command line option") || out.contains("unknown argument")) && out.contains("-no-pie") && cmd.get_args().iter().any(|e| e.to_string_lossy() == "-no-pie") { @@ -936,7 +937,12 @@ fn link_args(cmd: &mut Linker, if position_independent_executable { cmd.position_independent_executable(); } else { - cmd.no_position_independent_executable(); + // recent versions of gcc can be configured to generate position + // independent executables by default. We have to pass -no-pie to + // explicitly turn that off. + if sess.target.target.options.linker_is_gnu { + cmd.no_position_independent_executable(); + } } }