mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-12 06:53:05 +00:00
Support for disabling the PLT on ELF targets
Disable the PLT where possible to improve performance for indirect calls into shared libraries. This optimization is enabled by default where possible. - Add the `NonLazyBind` attribute to `rustllvm`: This attribute informs LLVM to skip PLT calls in codegen. - Disable PLT unconditionally: Apply the `NonLazyBind` attribute on every function. - Only enable no-plt when full relro is enabled: Ensures we only enable it when we have linker support. - Add `-Z plt` as a compiler option
This commit is contained in:
parent
2243fabd8f
commit
6009da0794
@ -1387,6 +1387,10 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
|
||||
"output a json file with profiler results"),
|
||||
emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED],
|
||||
"emits a section containing stack size metadata"),
|
||||
plt: Option<bool> = (None, parse_opt_bool, [TRACKED],
|
||||
"whether to use the PLT when calling into shared libraries;
|
||||
only has effect for PIC code on systems with ELF binaries
|
||||
(default: PLT is disabled if full relro is enabled)"),
|
||||
}
|
||||
|
||||
pub fn default_lib_output() -> CrateType {
|
||||
|
@ -40,8 +40,7 @@ use syntax::parse::{self, ParseSess};
|
||||
use syntax_pos::{MultiSpan, Span};
|
||||
use util::profiling::SelfProfiler;
|
||||
|
||||
use rustc_target::spec::PanicStrategy;
|
||||
use rustc_target::spec::{Target, TargetTriple};
|
||||
use rustc_target::spec::{PanicStrategy, RelroLevel, Target, TargetTriple};
|
||||
use rustc_data_structures::flock;
|
||||
use jobserver::Client;
|
||||
|
||||
@ -984,6 +983,27 @@ impl Session {
|
||||
pub fn edition(&self) -> Edition {
|
||||
self.opts.edition
|
||||
}
|
||||
|
||||
/// True if we cannot skip the PLT for shared library calls.
|
||||
pub fn needs_plt(&self) -> bool {
|
||||
// Check if the current target usually needs PLT to be enabled.
|
||||
// The user can use the command line flag to override it.
|
||||
let needs_plt = self.target.target.options.needs_plt;
|
||||
|
||||
let dbg_opts = &self.opts.debugging_opts;
|
||||
|
||||
let relro_level = dbg_opts.relro_level
|
||||
.unwrap_or(self.target.target.options.relro_level);
|
||||
|
||||
// Only enable this optimization by default if full relro is also enabled.
|
||||
// In this case, lazy binding was already unavailable, so nothing is lost.
|
||||
// This also ensures `-Wl,-z,now` is supported by the linker.
|
||||
let full_relro = RelroLevel::Full == relro_level;
|
||||
|
||||
// If user didn't explicitly forced us to use / skip the PLT,
|
||||
// then try to skip it where possible.
|
||||
dbg_opts.plt.unwrap_or(needs_plt || !full_relro)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_session(
|
||||
|
@ -138,6 +138,15 @@ pub fn apply_target_cpu_attr(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) {
|
||||
target_cpu.as_c_str());
|
||||
}
|
||||
|
||||
/// Sets the `NonLazyBind` LLVM attribute on a given function,
|
||||
/// assuming the codegen options allow skipping the PLT.
|
||||
pub fn non_lazy_bind(sess: &Session, llfn: &'ll Value) {
|
||||
// Don't generate calls through PLT if it's not necessary
|
||||
if !sess.needs_plt() {
|
||||
Attribute::NonLazyBind.apply_llfn(Function, llfn);
|
||||
}
|
||||
}
|
||||
|
||||
/// Composite function which sets LLVM attributes for function depending on its AST (#[attribute])
|
||||
/// attributes.
|
||||
pub fn from_fn_attrs(
|
||||
|
@ -208,6 +208,13 @@ pub unsafe fn create_module(
|
||||
llvm::LLVMRustSetModulePIELevel(llmod);
|
||||
}
|
||||
|
||||
// If skipping the PLT is enabled, we need to add some module metadata
|
||||
// to ensure intrinsic calls don't use it.
|
||||
if !sess.needs_plt() {
|
||||
let avoid_plt = "RtLibUseGOT\0".as_ptr() as *const _;
|
||||
llvm::LLVMRustAddModuleFlag(llmod, avoid_plt, 1);
|
||||
}
|
||||
|
||||
llmod
|
||||
}
|
||||
|
||||
|
@ -104,6 +104,8 @@ fn declare_raw_fn(
|
||||
attributes::unwind(llfn, false);
|
||||
}
|
||||
|
||||
attributes::non_lazy_bind(cx.sess(), llfn);
|
||||
|
||||
llfn
|
||||
}
|
||||
|
||||
|
@ -122,6 +122,7 @@ pub enum Attribute {
|
||||
SanitizeThread = 20,
|
||||
SanitizeAddress = 21,
|
||||
SanitizeMemory = 22,
|
||||
NonLazyBind = 23,
|
||||
}
|
||||
|
||||
/// LLVMIntPredicate
|
||||
|
@ -576,6 +576,9 @@ 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,
|
||||
/// 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,
|
||||
/// Either partial, full, or off. Full RELRO makes the dynamic linker
|
||||
/// resolve all symbols at startup and marks the GOT read-only before
|
||||
/// starting the program, preventing overwriting the GOT.
|
||||
@ -720,6 +723,7 @@ impl Default for TargetOptions {
|
||||
has_rpath: false,
|
||||
no_default_libraries: true,
|
||||
position_independent_executables: false,
|
||||
needs_plt: false,
|
||||
relro_level: RelroLevel::None,
|
||||
pre_link_objects_exe: Vec::new(),
|
||||
pre_link_objects_exe_crt: Vec::new(),
|
||||
@ -1009,6 +1013,7 @@ impl Target {
|
||||
key!(has_rpath, bool);
|
||||
key!(no_default_libraries, bool);
|
||||
key!(position_independent_executables, bool);
|
||||
key!(needs_plt, bool);
|
||||
try!(key!(relro_level, RelroLevel));
|
||||
key!(archive_format);
|
||||
key!(allow_asm, bool);
|
||||
@ -1217,6 +1222,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!(needs_plt);
|
||||
target_option_val!(relro_level);
|
||||
target_option_val!(archive_format);
|
||||
target_option_val!(allow_asm);
|
||||
|
@ -17,6 +17,9 @@ pub fn target() -> TargetResult {
|
||||
base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-mx32".to_string());
|
||||
base.stack_probes = true;
|
||||
base.has_elf_tls = false;
|
||||
// BUG(GabrielMajeri): disabling the PLT on x86_64 Linux with x32 ABI
|
||||
// breaks code gen. See LLVM bug 36743
|
||||
base.needs_plt = true;
|
||||
|
||||
Ok(Target {
|
||||
llvm_target: "x86_64-unknown-linux-gnux32".to_string(),
|
||||
|
@ -178,6 +178,8 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) {
|
||||
return Attribute::SanitizeAddress;
|
||||
case SanitizeMemory:
|
||||
return Attribute::SanitizeMemory;
|
||||
case NonLazyBind:
|
||||
return Attribute::NonLazyBind;
|
||||
}
|
||||
report_fatal_error("bad AttributeKind");
|
||||
}
|
||||
|
@ -97,6 +97,7 @@ enum LLVMRustAttribute {
|
||||
SanitizeThread = 20,
|
||||
SanitizeAddress = 21,
|
||||
SanitizeMemory = 22,
|
||||
NonLazyBind = 23,
|
||||
};
|
||||
|
||||
typedef struct OpaqueRustString *RustStringRef;
|
||||
|
@ -15,7 +15,7 @@
|
||||
#![crate_type = "lib"]
|
||||
#![feature(naked_functions)]
|
||||
|
||||
// CHECK: Function Attrs: naked uwtable
|
||||
// CHECK: Function Attrs: naked
|
||||
// CHECK-NEXT: define void @naked_empty()
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
@ -24,7 +24,7 @@ pub fn naked_empty() {
|
||||
// CHECK-NEXT: ret void
|
||||
}
|
||||
|
||||
// CHECK: Function Attrs: naked uwtable
|
||||
// CHECK: Function Attrs: naked
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
// CHECK-NEXT: define void @naked_with_args(i{{[0-9]+}})
|
||||
@ -35,7 +35,7 @@ pub fn naked_with_args(a: isize) {
|
||||
// CHECK: ret void
|
||||
}
|
||||
|
||||
// CHECK: Function Attrs: naked uwtable
|
||||
// CHECK: Function Attrs: naked
|
||||
// CHECK-NEXT: define i{{[0-9]+}} @naked_with_return()
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
@ -45,7 +45,7 @@ pub fn naked_with_return() -> isize {
|
||||
0
|
||||
}
|
||||
|
||||
// CHECK: Function Attrs: naked uwtable
|
||||
// CHECK: Function Attrs: naked
|
||||
// CHECK-NEXT: define i{{[0-9]+}} @naked_with_args_and_return(i{{[0-9]+}})
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
@ -57,7 +57,7 @@ pub fn naked_with_args_and_return(a: isize) -> isize {
|
||||
a
|
||||
}
|
||||
|
||||
// CHECK: Function Attrs: naked uwtable
|
||||
// CHECK: Function Attrs: naked
|
||||
// CHECK-NEXT: define void @naked_recursive()
|
||||
#[no_mangle]
|
||||
#[naked]
|
||||
|
27
src/test/codegen/no-plt.rs
Normal file
27
src/test/codegen/no-plt.rs
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// compile-flags: -C relocation-model=pic -Z plt=no
|
||||
|
||||
#![crate_type = "lib"]
|
||||
|
||||
// We need a function which is normally called through the PLT.
|
||||
extern "C" {
|
||||
// CHECK: Function Attrs: nounwind nonlazybind
|
||||
fn getenv(name: *const u8) -> *mut u8;
|
||||
}
|
||||
|
||||
// Ensure the function gets referenced.
|
||||
pub unsafe fn call_through_plt() -> *mut u8 {
|
||||
getenv(b"\0".as_ptr())
|
||||
}
|
||||
|
||||
// Ensure intrinsics also skip the PLT
|
||||
// CHECK: !"RtLibUseGOT"
|
Loading…
Reference in New Issue
Block a user