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:
commit
7aef3a0f6f
@ -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();
|
||||
|
@ -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) {}
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user