diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index c532b5ee56f47d2d908cf442795842f84b0182e5..d8c36f81da32427bfb86d2775babd1dced494f60 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -1387,6 +1387,10 @@ fn parse_cross_lang_lto(slot: &mut CrossLangLto, v: Option<&str>) -> bool { "output a json file with profiler results"), emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED], "emits a section containing stack size metadata"), + plt: Option = (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 { diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 3c209a4324675a5b0681375b7f9b75869f0d2ae0..10a506da4eab44c6cffbc8da7f5405ad357afffd 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -40,8 +40,7 @@ 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 @@ pub fn rust_2018(&self) -> bool { 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( diff --git a/src/librustc_codegen_llvm/attributes.rs b/src/librustc_codegen_llvm/attributes.rs index 51380db5b23df295c4f1125e5d12a3ec8f7d4c22..c871816c49c58fe73e81f2c22cf9327f165b61ad 100644 --- a/src/librustc_codegen_llvm/attributes.rs +++ b/src/librustc_codegen_llvm/attributes.rs @@ -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( diff --git a/src/librustc_codegen_llvm/context.rs b/src/librustc_codegen_llvm/context.rs index 9547f4a190e7896d721cda285efb8217b05fc674..fdbace4365af13be9dfb5edd6d72bfe8f83f4d7c 100644 --- a/src/librustc_codegen_llvm/context.rs +++ b/src/librustc_codegen_llvm/context.rs @@ -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 } diff --git a/src/librustc_codegen_llvm/declare.rs b/src/librustc_codegen_llvm/declare.rs index 7141c9ece89d7180ea0c7f85f22e1fb29afbf8e1..26969e24f0883503d9178699bd9ba32377bc351b 100644 --- a/src/librustc_codegen_llvm/declare.rs +++ b/src/librustc_codegen_llvm/declare.rs @@ -104,6 +104,8 @@ fn declare_raw_fn( attributes::unwind(llfn, false); } + attributes::non_lazy_bind(cx.sess(), llfn); + llfn } diff --git a/src/librustc_codegen_llvm/llvm/ffi.rs b/src/librustc_codegen_llvm/llvm/ffi.rs index 8485db4210cb0b691fb2cfcbe7b6987838bf6f96..c9f51efdc50957bbb01e080c50f5165d27ae2f2c 100644 --- a/src/librustc_codegen_llvm/llvm/ffi.rs +++ b/src/librustc_codegen_llvm/llvm/ffi.rs @@ -122,6 +122,7 @@ pub enum Attribute { SanitizeThread = 20, SanitizeAddress = 21, SanitizeMemory = 22, + NonLazyBind = 23, } /// LLVMIntPredicate diff --git a/src/librustc_target/spec/mod.rs b/src/librustc_target/spec/mod.rs index 3f1e8ee55286b6bfd682894aa3bbbc5e29c9cb13..9c0f945326d148006c8f9cd54e7412d4ea9d92eb 100644 --- a/src/librustc_target/spec/mod.rs +++ b/src/librustc_target/spec/mod.rs @@ -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 @@ fn default() -> 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 @@ pub fn from_json(obj: Json) -> TargetResult { 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 @@ fn to_json(&self) -> Json { 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); diff --git a/src/librustc_target/spec/x86_64_unknown_linux_gnux32.rs b/src/librustc_target/spec/x86_64_unknown_linux_gnux32.rs index 72b5bd27c7dfe3b35f1069e0fdb87362bd87eb3f..fd61067ba5168cdd575c6ef826d11ef3e34b8ad0 100644 --- a/src/librustc_target/spec/x86_64_unknown_linux_gnux32.rs +++ b/src/librustc_target/spec/x86_64_unknown_linux_gnux32.rs @@ -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(), diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index f1ab1d4ddfa476d8fb0006583f4a3b7521eac4e6..2b1bf1c0290812c228099457d3b57df54c6f8cda 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -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"); } diff --git a/src/rustllvm/rustllvm.h b/src/rustllvm/rustllvm.h index 1070068b99845f86c72e6cc839a4e0d90f2da995..b6fa9a2fa950809b5b52dd42bea14f10e1e112fa 100644 --- a/src/rustllvm/rustllvm.h +++ b/src/rustllvm/rustllvm.h @@ -97,6 +97,7 @@ enum LLVMRustAttribute { SanitizeThread = 20, SanitizeAddress = 21, SanitizeMemory = 22, + NonLazyBind = 23, }; typedef struct OpaqueRustString *RustStringRef; diff --git a/src/test/codegen/naked-functions.rs b/src/test/codegen/naked-functions.rs index aab5f1bfb4f863825d67206ef1efd378688bbe0b..2cf8ce00bfb5d7d8366a67be1584d6f8f4c24c7a 100644 --- a/src/test/codegen/naked-functions.rs +++ b/src/test/codegen/naked-functions.rs @@ -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] diff --git a/src/test/codegen/no-plt.rs b/src/test/codegen/no-plt.rs new file mode 100644 index 0000000000000000000000000000000000000000..8f302e57902c23ca16688753614c2481cff9bff4 --- /dev/null +++ b/src/test/codegen/no-plt.rs @@ -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 or the MIT license +// , 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"