about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/session/config.rs31
-rw-r--r--src/librustc_back/lib.rs42
-rw-r--r--src/librustc_back/target/bitrig_base.rs3
-rw-r--r--src/librustc_back/target/dragonfly_base.rs3
-rw-r--r--src/librustc_back/target/freebsd_base.rs3
-rw-r--r--src/librustc_back/target/haiku_base.rs3
-rw-r--r--src/librustc_back/target/linux_base.rs3
-rw-r--r--src/librustc_back/target/mod.rs21
-rw-r--r--src/librustc_back/target/netbsd_base.rs3
-rw-r--r--src/librustc_back/target/openbsd_base.rs3
-rw-r--r--src/librustc_back/target/powerpc64_unknown_linux_gnu.rs6
-rw-r--r--src/librustc_trans/back/link.rs16
-rw-r--r--src/librustc_trans/back/linker.rs20
13 files changed, 143 insertions, 14 deletions
diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index 4b41572c1a1..5661c412302 100644
--- a/src/librustc/session/config.rs
+++ b/src/librustc/session/config.rs
@@ -19,7 +19,7 @@ pub use self::DebugInfoLevel::*;
 use session::{early_error, early_warn, Session};
 use session::search_paths::SearchPaths;
 
-use rustc_back::{LinkerFlavor, PanicStrategy};
+use rustc_back::{LinkerFlavor, PanicStrategy, RelroLevel};
 use rustc_back::target::Target;
 use lint;
 use middle::cstore;
@@ -654,6 +654,8 @@ macro_rules! options {
             Some("a number");
         pub const parse_panic_strategy: Option<&'static str> =
             Some("either `panic` or `abort`");
+        pub const parse_relro_level: Option<&'static str> =
+            Some("one of: `full`, `partial`, or `off`");
         pub const parse_sanitizer: Option<&'static str> =
             Some("one of: `address`, `leak`, `memory` or `thread`");
         pub const parse_linker_flavor: Option<&'static str> =
@@ -665,7 +667,7 @@ macro_rules! options {
     #[allow(dead_code)]
     mod $mod_set {
         use super::{$struct_name, Passes, SomePasses, AllPasses, Sanitizer};
-        use rustc_back::{LinkerFlavor, PanicStrategy};
+        use rustc_back::{LinkerFlavor, PanicStrategy, RelroLevel};
 
         $(
             pub fn $opt(cg: &mut $struct_name, v: Option<&str>) -> bool {
@@ -786,6 +788,19 @@ macro_rules! options {
             true
         }
 
+        fn parse_relro_level(slot: &mut Option<RelroLevel>, v: Option<&str>) -> bool {
+            match v {
+                Some(s) => {
+                    match s.parse::<RelroLevel>() {
+                        Ok(level) => *slot = Some(level),
+                        _ => return false
+                    }
+                },
+                _ => return false
+            }
+            true
+        }
+
         fn parse_sanitizer(slote: &mut Option<Sanitizer>, v: Option<&str>) -> bool {
             match v {
                 Some("address") => *slote = Some(Sanitizer::Address),
@@ -1043,6 +1058,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
         "extra arguments to prepend to the linker invocation (space separated)"),
     profile: bool = (false, parse_bool, [TRACKED],
                      "insert profiling code"),
+    relro_level: Option<RelroLevel> = (None, parse_relro_level, [TRACKED],
+        "choose which RELRO level to use"),
 }
 
 pub fn default_lib_output() -> CrateType {
@@ -1776,7 +1793,7 @@ mod dep_tracking {
     use super::{Passes, CrateType, OptLevel, DebugInfoLevel,
                 OutputTypes, Externs, ErrorOutputType, Sanitizer};
     use syntax::feature_gate::UnstableFeatures;
-    use rustc_back::PanicStrategy;
+    use rustc_back::{PanicStrategy, RelroLevel};
 
     pub trait DepTrackingHash {
         fn hash(&self, hasher: &mut DefaultHasher, error_format: ErrorOutputType);
@@ -1818,11 +1835,13 @@ mod dep_tracking {
     impl_dep_tracking_hash_via_hash!(Option<String>);
     impl_dep_tracking_hash_via_hash!(Option<(String, u64)>);
     impl_dep_tracking_hash_via_hash!(Option<PanicStrategy>);
+    impl_dep_tracking_hash_via_hash!(Option<RelroLevel>);
     impl_dep_tracking_hash_via_hash!(Option<lint::Level>);
     impl_dep_tracking_hash_via_hash!(Option<PathBuf>);
     impl_dep_tracking_hash_via_hash!(Option<cstore::NativeLibraryKind>);
     impl_dep_tracking_hash_via_hash!(CrateType);
     impl_dep_tracking_hash_via_hash!(PanicStrategy);
+    impl_dep_tracking_hash_via_hash!(RelroLevel);
     impl_dep_tracking_hash_via_hash!(Passes);
     impl_dep_tracking_hash_via_hash!(OptLevel);
     impl_dep_tracking_hash_via_hash!(DebugInfoLevel);
@@ -1904,7 +1923,7 @@ mod tests {
     use std::path::PathBuf;
     use std::rc::Rc;
     use super::{OutputType, OutputTypes, Externs};
-    use rustc_back::PanicStrategy;
+    use rustc_back::{PanicStrategy, RelroLevel};
     use syntax::symbol::Symbol;
 
     fn optgroups() -> getopts::Options {
@@ -2582,5 +2601,9 @@ mod tests {
         opts = reference.clone();
         opts.debugging_opts.mir_opt_level = 3;
         assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
+
+        opts = reference.clone();
+        opts.debugging_opts.relro_level = Some(RelroLevel::Full);
+        assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
     }
 }
diff --git a/src/librustc_back/lib.rs b/src/librustc_back/lib.rs
index c776f28ecd0..55b39f22670 100644
--- a/src/librustc_back/lib.rs
+++ b/src/librustc_back/lib.rs
@@ -47,6 +47,8 @@ pub mod target;
 pub mod slice;
 pub mod dynamic_lib;
 
+use std::str::FromStr;
+
 use serialize::json::{Json, ToJson};
 
 macro_rules! linker_flavor {
@@ -114,3 +116,43 @@ impl ToJson for PanicStrategy {
         }
     }
 }
+
+#[derive(Clone, Copy, Debug, PartialEq, Hash, RustcEncodable, RustcDecodable)]
+pub enum RelroLevel {
+    Full,
+    Partial,
+    Off,
+}
+
+impl RelroLevel {
+    pub fn desc(&self) -> &str {
+        match *self {
+            RelroLevel::Full => "full",
+            RelroLevel::Partial => "partial",
+            RelroLevel::Off => "off",
+        }
+    }
+}
+
+impl FromStr for RelroLevel {
+    type Err = ();
+
+    fn from_str(s: &str) -> Result<RelroLevel, ()> {
+        match s {
+            "full" => Ok(RelroLevel::Full),
+            "partial" => Ok(RelroLevel::Partial),
+            "off" => Ok(RelroLevel::Off),
+            _ => Err(()),
+        }
+    }
+}
+
+impl ToJson for RelroLevel {
+    fn to_json(&self) -> Json {
+        match *self {
+            RelroLevel::Full => "full".to_json(),
+            RelroLevel::Partial => "partial".to_json(),
+            RelroLevel::Off => "off".to_json(),
+        }
+    }
+}
diff --git a/src/librustc_back/target/bitrig_base.rs b/src/librustc_back/target/bitrig_base.rs
index 5c4e01886a4..45ceb2d5a60 100644
--- a/src/librustc_back/target/bitrig_base.rs
+++ b/src/librustc_back/target/bitrig_base.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use target::TargetOptions;
+use target::{TargetOptions, RelroLevel};
 use std::default::Default;
 
 pub fn opts() -> TargetOptions {
@@ -19,6 +19,7 @@ pub fn opts() -> TargetOptions {
         linker_is_gnu: true,
         has_rpath: true,
         position_independent_executables: true,
+        relro_level: RelroLevel::Full,
 
         .. Default::default()
     }
diff --git a/src/librustc_back/target/dragonfly_base.rs b/src/librustc_back/target/dragonfly_base.rs
index e44cd393289..21dca99aa50 100644
--- a/src/librustc_back/target/dragonfly_base.rs
+++ b/src/librustc_back/target/dragonfly_base.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 use LinkerFlavor;
-use target::{LinkArgs, TargetOptions};
+use target::{LinkArgs, TargetOptions, RelroLevel};
 use std::default::Default;
 
 pub fn opts() -> TargetOptions {
@@ -33,6 +33,7 @@ pub fn opts() -> TargetOptions {
         has_rpath: true,
         pre_link_args: args,
         position_independent_executables: true,
+        relro_level: RelroLevel::Full,
         exe_allocation_crate: super::maybe_jemalloc(),
         .. Default::default()
     }
diff --git a/src/librustc_back/target/freebsd_base.rs b/src/librustc_back/target/freebsd_base.rs
index e44cd393289..21dca99aa50 100644
--- a/src/librustc_back/target/freebsd_base.rs
+++ b/src/librustc_back/target/freebsd_base.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 use LinkerFlavor;
-use target::{LinkArgs, TargetOptions};
+use target::{LinkArgs, TargetOptions, RelroLevel};
 use std::default::Default;
 
 pub fn opts() -> TargetOptions {
@@ -33,6 +33,7 @@ pub fn opts() -> TargetOptions {
         has_rpath: true,
         pre_link_args: args,
         position_independent_executables: true,
+        relro_level: RelroLevel::Full,
         exe_allocation_crate: super::maybe_jemalloc(),
         .. Default::default()
     }
diff --git a/src/librustc_back/target/haiku_base.rs b/src/librustc_back/target/haiku_base.rs
index 8e7f463563c..21410dcd412 100644
--- a/src/librustc_back/target/haiku_base.rs
+++ b/src/librustc_back/target/haiku_base.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-use target::TargetOptions;
+use target::{TargetOptions, RelroLevel};
 use std::default::Default;
 
 pub fn opts() -> TargetOptions {
@@ -18,6 +18,7 @@ pub fn opts() -> TargetOptions {
         executables: true,
         has_rpath: false,
         target_family: Some("unix".to_string()),
+        relro_level: RelroLevel::Full,
         linker_is_gnu: true,
         no_integrated_as: true,
         .. Default::default()
diff --git a/src/librustc_back/target/linux_base.rs b/src/librustc_back/target/linux_base.rs
index 722d2fa16ef..52f700ac751 100644
--- a/src/librustc_back/target/linux_base.rs
+++ b/src/librustc_back/target/linux_base.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 use LinkerFlavor;
-use target::{LinkArgs, TargetOptions};
+use target::{LinkArgs, TargetOptions, RelroLevel};
 use std::default::Default;
 
 pub fn opts() -> TargetOptions {
@@ -36,6 +36,7 @@ pub fn opts() -> TargetOptions {
         has_rpath: true,
         pre_link_args: args,
         position_independent_executables: true,
+        relro_level: RelroLevel::Full,
         exe_allocation_crate: super::maybe_jemalloc(),
         has_elf_tls: true,
         .. Default::default()
diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs
index edbbcf6f0b6..0dbfdb4d809 100644
--- a/src/librustc_back/target/mod.rs
+++ b/src/librustc_back/target/mod.rs
@@ -50,7 +50,7 @@ use std::default::Default;
 use std::io::prelude::*;
 use syntax::abi::{Abi, lookup as lookup_abi};
 
-use {LinkerFlavor, PanicStrategy};
+use {LinkerFlavor, PanicStrategy, RelroLevel};
 
 mod android_base;
 mod apple_base;
@@ -367,6 +367,10 @@ 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,
+    /// 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.
+    pub relro_level: RelroLevel,
     /// Format that archives should be emitted in. This affects whether we use
     /// LLVM to assemble an archive or fall back to the system linker, and
     /// currently only "gnu" is used to fall into LLVM. Unknown strings cause
@@ -454,6 +458,7 @@ impl Default for TargetOptions {
             has_rpath: false,
             no_default_libraries: true,
             position_independent_executables: false,
+            relro_level: RelroLevel::Off,
             pre_link_objects_exe: Vec::new(),
             pre_link_objects_dll: Vec::new(),
             post_link_objects: Vec::new(),
@@ -580,6 +585,18 @@ impl Target {
                 Some(Ok(()))
             })).unwrap_or(Ok(()))
             } );
+            ($key_name:ident, RelroLevel) => ( {
+                let name = (stringify!($key_name)).replace("_", "-");
+                obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| {
+                    match s.parse::<RelroLevel>() {
+                        Ok(level) => base.options.$key_name = level,
+                        _ => return Some(Err(format!("'{}' is not a valid value for \
+                                                      relro-level. Use 'full', 'partial, or 'off'.",
+                                                      s))),
+                    }
+                    Some(Ok(()))
+                })).unwrap_or(Ok(()))
+            } );
             ($key_name:ident, list) => ( {
                 let name = (stringify!($key_name)).replace("_", "-");
                 obj.find(&name[..]).map(|o| o.as_array()
@@ -683,6 +700,7 @@ impl Target {
         key!(has_rpath, bool);
         key!(no_default_libraries, bool);
         key!(position_independent_executables, bool);
+        try!(key!(relro_level, RelroLevel));
         key!(archive_format);
         key!(allow_asm, bool);
         key!(custom_unwind_resume, bool);
@@ -870,6 +888,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!(relro_level);
         target_option_val!(archive_format);
         target_option_val!(allow_asm);
         target_option_val!(custom_unwind_resume);
diff --git a/src/librustc_back/target/netbsd_base.rs b/src/librustc_back/target/netbsd_base.rs
index 63245fcae76..1cb31137193 100644
--- a/src/librustc_back/target/netbsd_base.rs
+++ b/src/librustc_back/target/netbsd_base.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 use LinkerFlavor;
-use target::{LinkArgs, TargetOptions};
+use target::{LinkArgs, TargetOptions, RelroLevel};
 use std::default::Default;
 
 pub fn opts() -> TargetOptions {
@@ -33,6 +33,7 @@ pub fn opts() -> TargetOptions {
         has_rpath: true,
         pre_link_args: args,
         position_independent_executables: true,
+        relro_level: RelroLevel::Full,
         .. Default::default()
     }
 }
diff --git a/src/librustc_back/target/openbsd_base.rs b/src/librustc_back/target/openbsd_base.rs
index 051028d5c4a..a5f8e7ae5f9 100644
--- a/src/librustc_back/target/openbsd_base.rs
+++ b/src/librustc_back/target/openbsd_base.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 use LinkerFlavor;
-use target::{LinkArgs, TargetOptions};
+use target::{LinkArgs, TargetOptions, RelroLevel};
 use std::default::Default;
 
 pub fn opts() -> TargetOptions {
@@ -34,6 +34,7 @@ pub fn opts() -> TargetOptions {
         is_like_openbsd: true,
         pre_link_args: args,
         position_independent_executables: true,
+        relro_level: RelroLevel::Full,
         .. Default::default()
     }
 }
diff --git a/src/librustc_back/target/powerpc64_unknown_linux_gnu.rs b/src/librustc_back/target/powerpc64_unknown_linux_gnu.rs
index 718a79a685e..7b038ac0073 100644
--- a/src/librustc_back/target/powerpc64_unknown_linux_gnu.rs
+++ b/src/librustc_back/target/powerpc64_unknown_linux_gnu.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 use LinkerFlavor;
-use target::{Target, TargetResult};
+use target::{Target, TargetResult, RelroLevel};
 
 pub fn target() -> TargetResult {
     let mut base = super::linux_base::opts();
@@ -17,6 +17,10 @@ pub fn target() -> TargetResult {
     base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m64".to_string());
     base.max_atomic_width = Some(64);
 
+    // ld.so in at least RHEL6 on ppc64 has a bug related to BIND_NOW, so only enable partial RELRO
+    // for now. https://github.com/rust-lang/rust/pull/43170#issuecomment-315411474
+    base.relro_level = RelroLevel::Partial;
+
     // see #36994
     base.exe_allocation_crate = None;
 
diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs
index 6cbdae76277..5e85771217b 100644
--- a/src/librustc_trans/back/link.rs
+++ b/src/librustc_trans/back/link.rs
@@ -27,7 +27,7 @@ use rustc::dep_graph::{DepKind, DepNode};
 use rustc::hir::def_id::CrateNum;
 use rustc::hir::svh::Svh;
 use rustc_back::tempdir::TempDir;
-use rustc_back::PanicStrategy;
+use rustc_back::{PanicStrategy, RelroLevel};
 use rustc_incremental::IncrementalHashesMap;
 use context::get_reloc_model;
 use llvm;
@@ -1029,6 +1029,20 @@ fn link_args(cmd: &mut Linker,
         }
     }
 
+    let relro_level = match sess.opts.debugging_opts.relro_level {
+        Some(level) => level,
+        None => t.options.relro_level,
+    };
+    match relro_level {
+        RelroLevel::Full => {
+            cmd.full_relro();
+        },
+        RelroLevel::Partial => {
+            cmd.partial_relro();
+        },
+        RelroLevel::Off => {},
+    }
+
     // Pass optimization flags down to the linker.
     cmd.optimize();
 
diff --git a/src/librustc_trans/back/linker.rs b/src/librustc_trans/back/linker.rs
index 0b15886083a..89ebfd0d254 100644
--- a/src/librustc_trans/back/linker.rs
+++ b/src/librustc_trans/back/linker.rs
@@ -104,6 +104,8 @@ pub trait Linker {
     fn add_object(&mut self, path: &Path);
     fn gc_sections(&mut self, keep_metadata: bool);
     fn position_independent_executable(&mut self);
+    fn partial_relro(&mut self);
+    fn full_relro(&mut self);
     fn optimize(&mut self);
     fn debuginfo(&mut self);
     fn no_default_libraries(&mut self);
@@ -175,6 +177,8 @@ impl<'a> Linker for GccLinker<'a> {
     fn output_filename(&mut self, path: &Path) { self.cmd.arg("-o").arg(path); }
     fn add_object(&mut self, path: &Path) { self.cmd.arg(path); }
     fn position_independent_executable(&mut self) { self.cmd.arg("-pie"); }
+    fn partial_relro(&mut self) { self.linker_arg("-z,relro"); }
+    fn full_relro(&mut self) { self.linker_arg("-z,relro,-z,now"); }
     fn args(&mut self, args: &[String]) { self.cmd.args(args); }
 
     fn link_rust_dylib(&mut self, lib: &str, _path: &Path) {
@@ -428,6 +432,14 @@ impl<'a> Linker for MsvcLinker<'a> {
         // noop
     }
 
+    fn partial_relro(&mut self) {
+        // noop
+    }
+
+    fn full_relro(&mut self) {
+        // noop
+    }
+
     fn no_default_libraries(&mut self) {
         // Currently we don't pass the /NODEFAULTLIB flag to the linker on MSVC
         // as there's been trouble in the past of linking the C++ standard
@@ -595,6 +607,14 @@ impl<'a> Linker for EmLinker<'a> {
         // noop
     }
 
+    fn partial_relro(&mut self) {
+        // noop
+    }
+
+    fn full_relro(&mut self) {
+        // noop
+    }
+
     fn args(&mut self, args: &[String]) {
         self.cmd.args(args);
     }