Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Target stack-probe support configurable finely #80838

Merged
merged 2 commits into from
Jan 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 26 additions & 19 deletions compiler/rustc_codegen_llvm/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, TyCtxt};
use rustc_session::config::{OptLevel, SanitizerSet};
use rustc_session::Session;
use rustc_target::spec::StackProbeType;

use crate::attributes;
use crate::llvm::AttributePlace::Function;
Expand Down Expand Up @@ -98,12 +99,6 @@ fn set_instrument_function(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) {
}

fn set_probestack(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) {
// Only use stack probes if the target specification indicates that we
// should be using stack probes
if !cx.sess().target.stack_probes {
return;
}

// Currently stack probes seem somewhat incompatible with the address
// sanitizer and thread sanitizer. With asan we're already protected from
// stack overflow anyway so we don't really need stack probes regardless.
Expand All @@ -127,19 +122,31 @@ fn set_probestack(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) {
return;
}

llvm::AddFunctionAttrStringValue(
llfn,
llvm::AttributePlace::Function,
const_cstr!("probe-stack"),
if llvm_util::get_version() < (11, 0, 1) {
// Flag our internal `__rust_probestack` function as the stack probe symbol.
// This is defined in the `compiler-builtins` crate for each architecture.
const_cstr!("__rust_probestack")
} else {
// On LLVM 11+, emit inline asm for stack probes instead of a function call.
const_cstr!("inline-asm")
},
);
let attr_value = match cx.sess().target.stack_probes {
StackProbeType::None => None,
// Request LLVM to generate the probes inline. If the given LLVM version does not support
// this, no probe is generated at all (even if the attribute is specified).
StackProbeType::Inline => Some(const_cstr!("inline-asm")),
// Flag our internal `__rust_probestack` function as the stack probe symbol.
// This is defined in the `compiler-builtins` crate for each architecture.
StackProbeType::Call => Some(const_cstr!("__rust_probestack")),
// Pick from the two above based on the LLVM version.
StackProbeType::InlineOrCall { min_llvm_version_for_inline } => {
if llvm_util::get_version() < min_llvm_version_for_inline {
Some(const_cstr!("__rust_probestack"))
} else {
Some(const_cstr!("inline-asm"))
}
}
};
if let Some(attr_value) = attr_value {
llvm::AddFunctionAttrStringValue(
llfn,
llvm::AttributePlace::Function,
const_cstr!("probe-stack"),
attr_value,
);
}
}

pub fn llvm_target_features(sess: &Session) -> impl Iterator<Item = &str> {
Expand Down
8 changes: 6 additions & 2 deletions compiler/rustc_target/src/spec/i386_apple_ios.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::apple_sdk_base::{opts, Arch};
use crate::spec::{Target, TargetOptions};
use crate::spec::{StackProbeType, Target, TargetOptions};

pub fn target() -> Target {
let base = opts("ios", Arch::I386);
Expand All @@ -10,6 +10,10 @@ pub fn target() -> Target {
f64:32:64-f80:128-n8:16:32-S128"
.to_string(),
arch: "x86".to_string(),
options: TargetOptions { max_atomic_width: Some(64), stack_probes: true, ..base },
options: TargetOptions {
max_atomic_width: Some(64),
stack_probes: StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) },
..base
},
}
}
4 changes: 2 additions & 2 deletions compiler/rustc_target/src/spec/i686_apple_darwin.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use crate::spec::{LinkerFlavor, Target, TargetOptions};
use crate::spec::{LinkerFlavor, StackProbeType, Target, TargetOptions};

pub fn target() -> Target {
let mut base = super::apple_base::opts("macos");
base.cpu = "yonah".to_string();
base.max_atomic_width = Some(64);
base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m32".to_string()]);
base.link_env_remove.extend(super::apple_base::macos_link_env_remove());
base.stack_probes = true;
base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) };
base.eliminate_frame_pointer = false;

// Clang automatically chooses a more specific target based on
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_target/src/spec/i686_linux_android.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::spec::Target;
use crate::spec::{StackProbeType, Target};

// See https://developer.android.com/ndk/guides/abis.html#x86
// for target ABI requirements.
Expand All @@ -11,7 +11,7 @@ pub fn target() -> Target {
// http://developer.android.com/ndk/guides/abis.html#x86
base.cpu = "pentiumpro".to_string();
base.features = "+mmx,+sse,+sse2,+sse3,+ssse3".to_string();
base.stack_probes = true;
base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) };

Target {
llvm_target: "i686-linux-android".to_string(),
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_target/src/spec/i686_unknown_freebsd.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::spec::{LinkerFlavor, Target};
use crate::spec::{LinkerFlavor, StackProbeType, Target};

pub fn target() -> Target {
let mut base = super::freebsd_base::opts();
Expand All @@ -7,7 +7,7 @@ pub fn target() -> Target {
let pre_link_args = base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap();
pre_link_args.push("-m32".to_string());
pre_link_args.push("-Wl,-znotext".to_string());
base.stack_probes = true;
base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) };

Target {
llvm_target: "i686-unknown-freebsd".to_string(),
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_target/src/spec/i686_unknown_haiku.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use crate::spec::{LinkerFlavor, Target};
use crate::spec::{LinkerFlavor, StackProbeType, Target};

pub fn target() -> Target {
let mut base = super::haiku_base::opts();
base.cpu = "pentium4".to_string();
base.max_atomic_width = Some(64);
base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m32".to_string()]);
base.stack_probes = true;
base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) };

Target {
llvm_target: "i686-unknown-haiku".to_string(),
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_target/src/spec/i686_unknown_linux_gnu.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use crate::spec::{LinkerFlavor, Target};
use crate::spec::{LinkerFlavor, StackProbeType, Target};

pub fn target() -> Target {
let mut base = super::linux_gnu_base::opts();
base.cpu = "pentium4".to_string();
base.max_atomic_width = Some(64);
base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string());
base.stack_probes = true;
base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) };

Target {
llvm_target: "i686-unknown-linux-gnu".to_string(),
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_target/src/spec/i686_unknown_linux_musl.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use crate::spec::{LinkerFlavor, Target};
use crate::spec::{LinkerFlavor, StackProbeType, Target};

pub fn target() -> Target {
let mut base = super::linux_musl_base::opts();
base.cpu = "pentium4".to_string();
base.max_atomic_width = Some(64);
base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string());
base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-Wl,-melf_i386".to_string());
base.stack_probes = true;
base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) };

// The unwinder used by i686-unknown-linux-musl, the LLVM libunwind
// implementation, apparently relies on frame pointers existing... somehow.
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_target/src/spec/i686_unknown_netbsd.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use crate::spec::{LinkerFlavor, Target, TargetOptions};
use crate::spec::{LinkerFlavor, StackProbeType, Target, TargetOptions};

pub fn target() -> Target {
let mut base = super::netbsd_base::opts();
base.cpu = "pentium4".to_string();
base.max_atomic_width = Some(64);
base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string());
base.stack_probes = true;
base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) };

Target {
llvm_target: "i686-unknown-netbsdelf".to_string(),
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_target/src/spec/i686_unknown_openbsd.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use crate::spec::{LinkerFlavor, Target};
use crate::spec::{LinkerFlavor, StackProbeType, Target};

pub fn target() -> Target {
let mut base = super::openbsd_base::opts();
base.cpu = "pentium4".to_string();
base.max_atomic_width = Some(64);
base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string());
base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-fuse-ld=lld".to_string());
base.stack_probes = true;
base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) };

Target {
llvm_target: "i686-unknown-openbsd".to_string(),
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_target/src/spec/i686_wrs_vxworks.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use crate::spec::{LinkerFlavor, Target};
use crate::spec::{LinkerFlavor, StackProbeType, Target};

pub fn target() -> Target {
let mut base = super::vxworks_base::opts();
base.cpu = "pentium4".to_string();
base.max_atomic_width = Some(64);
base.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap().push("-m32".to_string());
base.stack_probes = true;
base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) };

Target {
llvm_target: "i686-unknown-linux-gnu".to_string(),
Expand Down
6 changes: 4 additions & 2 deletions compiler/rustc_target/src/spec/linux_kernel_base.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::spec::{LinkArgs, LinkerFlavor, PanicStrategy, RelocModel, RelroLevel, TargetOptions};
use crate::spec::{
LinkArgs, LinkerFlavor, PanicStrategy, RelocModel, RelroLevel, StackProbeType, TargetOptions,
};

pub fn opts() -> TargetOptions {
let mut pre_link_args = LinkArgs::new();
Expand All @@ -11,7 +13,7 @@ pub fn opts() -> TargetOptions {
env: "gnu".to_string(),
disable_redzone: true,
panic_strategy: PanicStrategy::Abort,
stack_probes: true,
stack_probes: StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) },
eliminate_frame_pointer: false,
linker_is_gnu: true,
position_independent_executables: true,
Expand Down
98 changes: 94 additions & 4 deletions compiler/rustc_target/src/spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ use crate::spec::crt_objects::{CrtObjects, CrtObjectsFallback};
use rustc_serialize::json::{Json, ToJson};
use rustc_span::symbol::{sym, Symbol};
use std::collections::BTreeMap;
use std::convert::TryFrom;
use std::ops::{Deref, DerefMut};
use std::path::{Path, PathBuf};
use std::str::FromStr;
Expand Down Expand Up @@ -479,6 +480,83 @@ macro_rules! supported_targets {
};
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum StackProbeType {
/// Don't emit any stack probes.
None,
/// It is harmless to use this option even on targets that do not have backend support for
/// stack probes as the failure mode is the same as if no stack-probe option was specified in
/// the first place.
Inline,
/// Call `__rust_probestack` whenever stack needs to be probed.
Call,
/// Use inline option for LLVM versions later than specified in `min_llvm_version_for_inline`
/// and call `__rust_probestack` otherwise.
InlineOrCall { min_llvm_version_for_inline: (u32, u32, u32) },
}

impl StackProbeType {
fn from_json(json: &Json) -> Result<Self, String> {
cuviper marked this conversation as resolved.
Show resolved Hide resolved
let object = json.as_object().ok_or_else(|| "expected a JSON object")?;
let kind = object
.get("kind")
.and_then(|o| o.as_string())
.ok_or_else(|| "expected `kind` to be a string")?;
match kind {
"none" => Ok(StackProbeType::None),
"inline" => Ok(StackProbeType::Inline),
"call" => Ok(StackProbeType::Call),
"inline-or-call" => {
let min_version = object
.get("min-llvm-version-for-inline")
.and_then(|o| o.as_array())
.ok_or_else(|| "expected `min-llvm-version-for-inline` to be an array")?;
let mut iter = min_version.into_iter().map(|v| {
let int = v.as_u64().ok_or_else(
|| "expected `min-llvm-version-for-inline` values to be integers",
)?;
u32::try_from(int)
.map_err(|_| "`min-llvm-version-for-inline` values don't convert to u32")
});
let min_llvm_version_for_inline = (
iter.next().unwrap_or(Ok(11))?,
iter.next().unwrap_or(Ok(0))?,
iter.next().unwrap_or(Ok(0))?,
);
Ok(StackProbeType::InlineOrCall { min_llvm_version_for_inline })
}
_ => Err(String::from(
"`kind` expected to be one of `inline-or-none`, `call` or `inline-or-call`",
)),
}
}
}

impl ToJson for StackProbeType {
fn to_json(&self) -> Json {
Json::Object(match self {
StackProbeType::None => {
vec![(String::from("kind"), "none".to_json())].into_iter().collect()
}
StackProbeType::Inline => {
vec![(String::from("kind"), "inline".to_json())].into_iter().collect()
}
StackProbeType::Call => {
vec![(String::from("kind"), "call".to_json())].into_iter().collect()
}
StackProbeType::InlineOrCall { min_llvm_version_for_inline } => vec![
(String::from("kind"), "inline-or-call".to_json()),
(
String::from("min-llvm-version-for-inline"),
min_llvm_version_for_inline.to_json(),
),
]
.into_iter()
.collect(),
})
}
}

supported_targets! {
("x86_64-unknown-linux-gnu", x86_64_unknown_linux_gnu),
("x86_64-unknown-linux-gnux32", x86_64_unknown_linux_gnux32),
Expand Down Expand Up @@ -926,8 +1004,8 @@ pub struct TargetOptions {
/// Whether or not crt-static is respected by the compiler (or is a no-op).
pub crt_static_respected: bool,

/// Whether or not stack probes (__rust_probestack) are enabled
pub stack_probes: bool,
/// The implementation of stack probes to use.
pub stack_probes: StackProbeType,

/// The minimum alignment for global symbols.
pub min_global_align: Option<u64>,
Expand Down Expand Up @@ -1085,7 +1163,7 @@ impl Default for TargetOptions {
crt_static_allows_dylibs: false,
crt_static_default: false,
crt_static_respected: false,
stack_probes: false,
stack_probes: StackProbeType::None,
min_global_align: None,
default_codegen_units: None,
trap_unreachable: true,
Expand Down Expand Up @@ -1361,6 +1439,18 @@ impl Target {
Some(Ok(()))
})).unwrap_or(Ok(()))
} );
($key_name:ident, StackProbeType) => ( {
let name = (stringify!($key_name)).replace("_", "-");
obj.find(&name[..]).and_then(|o| match StackProbeType::from_json(o) {
Ok(v) => {
base.$key_name = v;
Some(Ok(()))
},
Err(s) => Some(Err(
format!("`{:?}` is not a valid value for `{}`: {}", o, name, s)
)),
}).unwrap_or(Ok(()))
} );
($key_name:ident, crt_objects_fallback) => ( {
let name = (stringify!($key_name)).replace("_", "-");
obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| {
Expand Down Expand Up @@ -1516,7 +1606,7 @@ impl Target {
key!(crt_static_allows_dylibs, bool);
key!(crt_static_default, bool);
key!(crt_static_respected, bool);
key!(stack_probes, bool);
key!(stack_probes, StackProbeType)?;
key!(min_global_align, Option<u64>);
key!(default_codegen_units, Option<u64>);
key!(trap_unreachable, bool);
Expand Down
6 changes: 4 additions & 2 deletions compiler/rustc_target/src/spec/uefi_msvc_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
// the timer-interrupt. Device-drivers are required to use polling-based models. Furthermore, all
// code runs in the same environment, no process separation is supported.

use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, TargetOptions};
use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, StackProbeType, TargetOptions};

pub fn opts() -> TargetOptions {
let mut base = super::msvc_base::opts();
Expand Down Expand Up @@ -43,7 +43,9 @@ pub fn opts() -> TargetOptions {
exe_suffix: ".efi".to_string(),
allows_weak_linkage: false,
panic_strategy: PanicStrategy::Abort,
stack_probes: true,
// LLVM does not emit inline assembly because the LLVM target does not get considered as…
// "Windows".
stack_probes: StackProbeType::Call,
singlethread: true,
linker: Some("rust-lld".to_string()),
..base
Expand Down
Loading