Rollup merge of #71804 - petrochenkov:static-pie, r=cuviper

linker: Support `-static-pie` and `-static -shared`

This PR adds support for passing linker arguments for creating statically linked position-independent executables and "statically linked" shared libraries.

Therefore it incorporates the majority of https://github.com/rust-lang/rust/pull/70740 except for the linker rerun hack and actually flipping the "`static-pie` is supported" switch for musl targets.
This commit is contained in:
Ralf Jung 2020-05-29 21:58:24 +02:00 committed by GitHub
commit 7aef3a0f6f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 127 additions and 134 deletions

View File

@ -1194,9 +1194,10 @@ fn link_output_kind(sess: &Session, crate_type: CrateType) -> LinkOutputKind {
};
// Adjust the output kind to target capabilities.
let pic_exe_supported = sess.target.target.options.position_independent_executables;
let static_pic_exe_supported = false; // FIXME: Add this option to target specs.
let static_dylib_supported = sess.target.target.options.crt_static_allows_dylibs;
let opts = &sess.target.target.options;
let pic_exe_supported = opts.position_independent_executables;
let static_pic_exe_supported = opts.static_position_independent_executables;
let static_dylib_supported = opts.crt_static_allows_dylibs;
match kind {
LinkOutputKind::DynamicPicExe if !pic_exe_supported => LinkOutputKind::DynamicNoPicExe,
LinkOutputKind::StaticPicExe if !static_pic_exe_supported => LinkOutputKind::StaticNoPicExe,
@ -1580,16 +1581,7 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
}
// NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER
// FIXME: Support `StaticPicExe` correctly.
match link_output_kind {
LinkOutputKind::DynamicPicExe | LinkOutputKind::StaticPicExe => {
cmd.position_independent_executable()
}
LinkOutputKind::DynamicNoPicExe | LinkOutputKind::StaticNoPicExe => {
cmd.no_position_independent_executable()
}
_ => {}
}
cmd.set_output_kind(link_output_kind, out_filename);
// OBJECT-FILES-NO, AUDIT-ORDER
add_relro_args(cmd, sess);
@ -1618,17 +1610,6 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
tmpdir,
);
// NO-OPT-OUT, OBJECT-FILES-NO, AUDIT-ORDER
// FIXME: Merge with the previous `link_output_kind` match,
// and support `StaticPicExe` and `StaticDylib` correctly.
match link_output_kind {
LinkOutputKind::StaticNoPicExe | LinkOutputKind::StaticPicExe => {
cmd.build_static_executable()
}
LinkOutputKind::DynamicDylib | LinkOutputKind::StaticDylib => cmd.build_dylib(out_filename),
_ => {}
}
// OBJECT-FILES-NO, AUDIT-ORDER
if sess.opts.cg.profile_generate.enabled() {
cmd.pgo_gen();

View File

@ -17,7 +17,7 @@ use rustc_serialize::{json, Encoder};
use rustc_session::config::{self, CrateType, DebugInfo, LinkerPluginLto, Lto, OptLevel, Strip};
use rustc_session::Session;
use rustc_span::symbol::Symbol;
use rustc_target::spec::{LinkerFlavor, LldFlavor};
use rustc_target::spec::{LinkOutputKind, LinkerFlavor, LldFlavor};
/// Disables non-English messages from localized linkers.
/// Such messages may cause issues with text encoding on Windows (#35785)
@ -101,6 +101,7 @@ impl LinkerInfo {
/// MSVC linker (e.g., `link.exe`) is being used.
pub trait Linker {
fn cmd(&mut self) -> &mut Command;
fn set_output_kind(&mut self, output_kind: LinkOutputKind, out_filename: &Path);
fn link_dylib(&mut self, lib: Symbol);
fn link_rust_dylib(&mut self, lib: Symbol, path: &Path);
fn link_framework(&mut self, framework: Symbol);
@ -113,8 +114,6 @@ pub trait Linker {
fn output_filename(&mut self, path: &Path);
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 full_relro(&mut self);
fn partial_relro(&mut self);
fn no_relro(&mut self);
@ -124,8 +123,6 @@ pub trait Linker {
fn debuginfo(&mut self, strip: Strip);
fn no_crt_objects(&mut self);
fn no_default_libraries(&mut self);
fn build_dylib(&mut self, out_filename: &Path);
fn build_static_executable(&mut self);
fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType);
fn subsystem(&mut self, subsystem: &str);
fn group_start(&mut self);
@ -232,12 +229,94 @@ impl<'a> GccLinker<'a> {
let target_cpu = self.target_cpu;
self.linker_arg(&format!("-plugin-opt=mcpu={}", target_cpu));
}
fn build_dylib(&mut self, out_filename: &Path) {
// On mac we need to tell the linker to let this library be rpathed
if self.sess.target.target.options.is_like_osx {
self.cmd.arg("-dynamiclib");
self.linker_arg("-dylib");
// Note that the `osx_rpath_install_name` option here is a hack
// purely to support rustbuild right now, we should get a more
// principled solution at some point to force the compiler to pass
// the right `-Wl,-install_name` with an `@rpath` in it.
if self.sess.opts.cg.rpath || self.sess.opts.debugging_opts.osx_rpath_install_name {
self.linker_arg("-install_name");
let mut v = OsString::from("@rpath/");
v.push(out_filename.file_name().unwrap());
self.linker_arg(&v);
}
} else {
self.cmd.arg("-shared");
if self.sess.target.target.options.is_like_windows {
// The output filename already contains `dll_suffix` so
// the resulting import library will have a name in the
// form of libfoo.dll.a
let implib_name =
out_filename.file_name().and_then(|file| file.to_str()).map(|file| {
format!(
"{}{}{}",
self.sess.target.target.options.staticlib_prefix,
file,
self.sess.target.target.options.staticlib_suffix
)
});
if let Some(implib_name) = implib_name {
let implib = out_filename.parent().map(|dir| dir.join(&implib_name));
if let Some(implib) = implib {
self.linker_arg(&format!("--out-implib,{}", (*implib).to_str().unwrap()));
}
}
}
}
}
}
impl<'a> Linker for GccLinker<'a> {
fn cmd(&mut self) -> &mut Command {
&mut self.cmd
}
fn set_output_kind(&mut self, output_kind: LinkOutputKind, out_filename: &Path) {
match output_kind {
LinkOutputKind::DynamicNoPicExe => {
if !self.is_ld {
self.cmd.arg("-no-pie");
}
}
LinkOutputKind::DynamicPicExe => {
// `-pie` works for both gcc wrapper and ld.
self.cmd.arg("-pie");
}
LinkOutputKind::StaticNoPicExe => {
// `-static` works for both gcc wrapper and ld.
self.cmd.arg("-static");
if !self.is_ld {
self.cmd.arg("-no-pie");
}
}
LinkOutputKind::StaticPicExe => {
if !self.is_ld {
// Note that combination `-static -pie` doesn't work as expected
// for the gcc wrapper, `-static` in that case suppresses `-pie`.
self.cmd.arg("-static-pie");
} else {
// `--no-dynamic-linker` and `-z text` are not strictly necessary for producing
// a static pie, but currently passed because gcc and clang pass them.
// The former suppresses the `INTERP` ELF header specifying dynamic linker,
// which is otherwise implicitly injected by ld (but not lld).
// The latter doesn't change anything, only ensures that everything is pic.
self.cmd.args(&["-static", "-pie", "--no-dynamic-linker", "-z", "text"]);
}
}
LinkOutputKind::DynamicDylib => self.build_dylib(out_filename),
LinkOutputKind::StaticDylib => {
self.cmd.arg("-static");
self.build_dylib(out_filename);
}
}
}
fn link_dylib(&mut self, lib: Symbol) {
self.hint_dynamic();
self.cmd.arg(format!("-l{}", lib));
@ -262,14 +341,6 @@ impl<'a> Linker for GccLinker<'a> {
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) {
if !self.is_ld {
self.cmd.arg("-no-pie");
}
}
fn full_relro(&mut self) {
self.linker_arg("-zrelro");
self.linker_arg("-znow");
@ -280,9 +351,6 @@ impl<'a> Linker for GccLinker<'a> {
fn no_relro(&mut self) {
self.linker_arg("-znorelro");
}
fn build_static_executable(&mut self) {
self.cmd.arg("-static");
}
fn link_rust_dylib(&mut self, lib: Symbol, _path: &Path) {
self.hint_dynamic();
@ -418,47 +486,6 @@ impl<'a> Linker for GccLinker<'a> {
}
}
fn build_dylib(&mut self, out_filename: &Path) {
// On mac we need to tell the linker to let this library be rpathed
if self.sess.target.target.options.is_like_osx {
self.cmd.arg("-dynamiclib");
self.linker_arg("-dylib");
// Note that the `osx_rpath_install_name` option here is a hack
// purely to support rustbuild right now, we should get a more
// principled solution at some point to force the compiler to pass
// the right `-Wl,-install_name` with an `@rpath` in it.
if self.sess.opts.cg.rpath || self.sess.opts.debugging_opts.osx_rpath_install_name {
self.linker_arg("-install_name");
let mut v = OsString::from("@rpath/");
v.push(out_filename.file_name().unwrap());
self.linker_arg(&v);
}
} else {
self.cmd.arg("-shared");
if self.sess.target.target.options.is_like_windows {
// The output filename already contains `dll_suffix` so
// the resulting import library will have a name in the
// form of libfoo.dll.a
let implib_name =
out_filename.file_name().and_then(|file| file.to_str()).map(|file| {
format!(
"{}{}{}",
self.sess.target.target.options.staticlib_prefix,
file,
self.sess.target.target.options.staticlib_suffix
)
});
if let Some(implib_name) = implib_name {
let implib = out_filename.parent().map(|dir| dir.join(&implib_name));
if let Some(implib) = implib {
self.linker_arg(&format!("--out-implib,{}", (*implib).to_str().unwrap()));
}
}
}
}
}
fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType) {
// Symbol visibility in object files typically takes care of this.
if crate_type == CrateType::Executable
@ -582,6 +609,22 @@ impl<'a> Linker for MsvcLinker<'a> {
fn cmd(&mut self) -> &mut Command {
&mut self.cmd
}
fn set_output_kind(&mut self, output_kind: LinkOutputKind, out_filename: &Path) {
match output_kind {
LinkOutputKind::DynamicNoPicExe
| LinkOutputKind::DynamicPicExe
| LinkOutputKind::StaticNoPicExe
| LinkOutputKind::StaticPicExe => {}
LinkOutputKind::DynamicDylib | LinkOutputKind::StaticDylib => {
self.cmd.arg("/DLL");
let mut arg: OsString = "/IMPLIB:".into();
arg.push(out_filename.with_extension("dll.lib"));
self.cmd.arg(arg);
}
}
}
fn link_rlib(&mut self, lib: &Path) {
self.cmd.arg(lib);
}
@ -589,17 +632,6 @@ impl<'a> Linker for MsvcLinker<'a> {
self.cmd.arg(path);
}
fn build_dylib(&mut self, out_filename: &Path) {
self.cmd.arg("/DLL");
let mut arg: OsString = "/IMPLIB:".into();
arg.push(out_filename.with_extension("dll.lib"));
self.cmd.arg(arg);
}
fn build_static_executable(&mut self) {
// noop
}
fn gc_sections(&mut self, _keep_metadata: bool) {
// MSVC's ICF (Identical COMDAT Folding) link optimization is
// slow for Rust and thus we disable it by default when not in
@ -632,14 +664,6 @@ impl<'a> Linker for MsvcLinker<'a> {
self.cmd.arg(&format!("{}.lib", lib));
}
fn position_independent_executable(&mut self) {
// noop
}
fn no_position_independent_executable(&mut self) {
// noop
}
fn full_relro(&mut self) {
// noop
}
@ -817,6 +841,9 @@ impl<'a> Linker for EmLinker<'a> {
fn cmd(&mut self) -> &mut Command {
&mut self.cmd
}
fn set_output_kind(&mut self, _output_kind: LinkOutputKind, _out_filename: &Path) {}
fn include_path(&mut self, path: &Path) {
self.cmd.arg("-L").arg(path);
}
@ -856,14 +883,6 @@ impl<'a> Linker for EmLinker<'a> {
self.add_object(lib);
}
fn position_independent_executable(&mut self) {
// noop
}
fn no_position_independent_executable(&mut self) {
// noop
}
fn full_relro(&mut self) {
// noop
}
@ -925,14 +944,6 @@ impl<'a> Linker for EmLinker<'a> {
self.cmd.args(&["-s", "DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=[]"]);
}
fn build_dylib(&mut self, _out_filename: &Path) {
bug!("building dynamic library is unsupported on Emscripten")
}
fn build_static_executable(&mut self) {
// noop
}
fn export_symbols(&mut self, _tmpdir: &Path, crate_type: CrateType) {
let symbols = &self.info.exports[&crate_type];
@ -1031,6 +1042,18 @@ impl<'a> Linker for WasmLd<'a> {
&mut self.cmd
}
fn set_output_kind(&mut self, output_kind: LinkOutputKind, _out_filename: &Path) {
match output_kind {
LinkOutputKind::DynamicNoPicExe
| LinkOutputKind::DynamicPicExe
| LinkOutputKind::StaticNoPicExe
| LinkOutputKind::StaticPicExe => {}
LinkOutputKind::DynamicDylib | LinkOutputKind::StaticDylib => {
self.cmd.arg("--no-entry");
}
}
}
fn link_dylib(&mut self, lib: Symbol) {
self.cmd.arg("-l").sym_arg(lib);
}
@ -1059,16 +1082,12 @@ impl<'a> Linker for WasmLd<'a> {
self.cmd.arg(path);
}
fn position_independent_executable(&mut self) {}
fn full_relro(&mut self) {}
fn partial_relro(&mut self) {}
fn no_relro(&mut self) {}
fn build_static_executable(&mut self) {}
fn link_rust_dylib(&mut self, lib: Symbol, _path: &Path) {
self.cmd.arg("-l").sym_arg(lib);
}
@ -1124,10 +1143,6 @@ impl<'a> Linker for WasmLd<'a> {
fn no_default_libraries(&mut self) {}
fn build_dylib(&mut self, _out_filename: &Path) {
self.cmd.arg("--no-entry");
}
fn export_symbols(&mut self, _tmpdir: &Path, crate_type: CrateType) {
for sym in self.info.exports[&crate_type].iter() {
self.cmd.arg("--export").arg(&sym);
@ -1143,8 +1158,6 @@ impl<'a> Linker for WasmLd<'a> {
fn subsystem(&mut self, _subsystem: &str) {}
fn no_position_independent_executable(&mut self) {}
fn finalize(&mut self) {}
// Not needed for now with LLD
@ -1207,6 +1220,8 @@ impl<'a> Linker for PtxLinker<'a> {
&mut self.cmd
}
fn set_output_kind(&mut self, _output_kind: LinkOutputKind, _out_filename: &Path) {}
fn link_rlib(&mut self, path: &Path) {
self.cmd.arg("--rlib").arg(path);
}
@ -1273,16 +1288,12 @@ impl<'a> Linker for PtxLinker<'a> {
panic!("frameworks not supported")
}
fn position_independent_executable(&mut self) {}
fn full_relro(&mut self) {}
fn partial_relro(&mut self) {}
fn no_relro(&mut self) {}
fn build_static_executable(&mut self) {}
fn gc_sections(&mut self, _keep_metadata: bool) {}
fn pgo_gen(&mut self) {}
@ -1295,14 +1306,10 @@ impl<'a> Linker for PtxLinker<'a> {
self.sess.warn("Windows Control Flow Guard is not supported by this linker.");
}
fn build_dylib(&mut self, _out_filename: &Path) {}
fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType) {}
fn subsystem(&mut self, _subsystem: &str) {}
fn no_position_independent_executable(&mut self) {}
fn group_start(&mut self) {}
fn group_end(&mut self) {}

View File

@ -856,6 +856,8 @@ pub struct TargetOptions {
/// the functions in the executable are not randomized and can be used
/// during an exploit of a vulnerability in any code.
pub position_independent_executables: bool,
/// Executables that are both statically linked and position-independent are supported.
pub static_position_independent_executables: bool,
/// Determines if the target always requires using the PLT for indirect
/// library calls or not. This controls the default value of the `-Z plt` flag.
pub needs_plt: bool,
@ -1029,6 +1031,7 @@ impl Default for TargetOptions {
has_rpath: false,
no_default_libraries: true,
position_independent_executables: false,
static_position_independent_executables: false,
needs_plt: false,
relro_level: RelroLevel::None,
pre_link_objects: Default::default(),
@ -1433,6 +1436,7 @@ impl Target {
key!(has_rpath, bool);
key!(no_default_libraries, bool);
key!(position_independent_executables, bool);
key!(static_position_independent_executables, bool);
key!(needs_plt, bool);
key!(relro_level, RelroLevel)?;
key!(archive_format);
@ -1664,6 +1668,7 @@ impl ToJson for Target {
target_option_val!(has_rpath);
target_option_val!(no_default_libraries);
target_option_val!(position_independent_executables);
target_option_val!(static_position_independent_executables);
target_option_val!(needs_plt);
target_option_val!(relro_level);
target_option_val!(archive_format);