Skip to content

Commit

Permalink
Support for disabling the PLT on ELF targets
Browse files Browse the repository at this point in the history
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
  • Loading branch information
GabrielMajeri committed Oct 11, 2018
1 parent 2243fab commit 6009da0
Show file tree
Hide file tree
Showing 12 changed files with 89 additions and 7 deletions.
4 changes: 4 additions & 0 deletions src/librustc/session/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
24 changes: 22 additions & 2 deletions src/librustc/session/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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(
Expand Down
9 changes: 9 additions & 0 deletions src/librustc_codegen_llvm/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
7 changes: 7 additions & 0 deletions src/librustc_codegen_llvm/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
2 changes: 2 additions & 0 deletions src/librustc_codegen_llvm/declare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ fn declare_raw_fn(
attributes::unwind(llfn, false);
}

attributes::non_lazy_bind(cx.sess(), llfn);

llfn
}

Expand Down
1 change: 1 addition & 0 deletions src/librustc_codegen_llvm/llvm/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ pub enum Attribute {
SanitizeThread = 20,
SanitizeAddress = 21,
SanitizeMemory = 22,
NonLazyBind = 23,
}

/// LLVMIntPredicate
Expand Down
6 changes: 6 additions & 0 deletions src/librustc_target/spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions src/librustc_target/spec/x86_64_unknown_linux_gnux32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
2 changes: 2 additions & 0 deletions src/rustllvm/RustWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand Down
1 change: 1 addition & 0 deletions src/rustllvm/rustllvm.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ enum LLVMRustAttribute {
SanitizeThread = 20,
SanitizeAddress = 21,
SanitizeMemory = 22,
NonLazyBind = 23,
};

typedef struct OpaqueRustString *RustStringRef;
Expand Down
10 changes: 5 additions & 5 deletions src/test/codegen/naked-functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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]+}})
Expand All @@ -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]
Expand All @@ -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]
Expand All @@ -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]
Expand Down
27 changes: 27 additions & 0 deletions src/test/codegen/no-plt.rs
Original file line number Diff line number Diff line change
@@ -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"

0 comments on commit 6009da0

Please sign in to comment.