Skip to content

Commit 72c7b70

Browse files
committed
Auto merge of #80838 - nagisa:nagisa/stack-probe-type, r=cuviper
Target stack-probe support configurable finely This adds capability to configure the target's stack probe support in a more precise manner than just on/off. In particular now we allow choosing between always inline-asm, always call or either one of those depending on the LLVM version. Note that this removes the ability to turn off the generation of the stack-probe attribute. This is valid to replace it with inline-asm for all targets because `probe-stack="inline-asm"` will not generate any machine code on targets that do not currently support stack probes. This makes support for stack probes on targets that don't have any right now automatic with LLVM upgrades in the future. (This is valid to do based on the fact that clang unconditionally sets this attribute when `-fstack-clash-protection` is used, AFAICT) cc #77885 r? `@cuviper`
2 parents 85e355e + 1b15ec6 commit 72c7b70

35 files changed

+205
-87
lines changed

compiler/rustc_codegen_llvm/src/attributes.rs

+26-19
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use rustc_middle::ty::query::Providers;
1313
use rustc_middle::ty::{self, TyCtxt};
1414
use rustc_session::config::{OptLevel, SanitizerSet};
1515
use rustc_session::Session;
16+
use rustc_target::spec::StackProbeType;
1617

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

100101
fn set_probestack(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) {
101-
// Only use stack probes if the target specification indicates that we
102-
// should be using stack probes
103-
if !cx.sess().target.stack_probes {
104-
return;
105-
}
106-
107102
// Currently stack probes seem somewhat incompatible with the address
108103
// sanitizer and thread sanitizer. With asan we're already protected from
109104
// stack overflow anyway so we don't really need stack probes regardless.
@@ -127,19 +122,31 @@ fn set_probestack(cx: &CodegenCx<'ll, '_>, llfn: &'ll Value) {
127122
return;
128123
}
129124

130-
llvm::AddFunctionAttrStringValue(
131-
llfn,
132-
llvm::AttributePlace::Function,
133-
const_cstr!("probe-stack"),
134-
if llvm_util::get_version() < (11, 0, 1) {
135-
// Flag our internal `__rust_probestack` function as the stack probe symbol.
136-
// This is defined in the `compiler-builtins` crate for each architecture.
137-
const_cstr!("__rust_probestack")
138-
} else {
139-
// On LLVM 11+, emit inline asm for stack probes instead of a function call.
140-
const_cstr!("inline-asm")
141-
},
142-
);
125+
let attr_value = match cx.sess().target.stack_probes {
126+
StackProbeType::None => None,
127+
// Request LLVM to generate the probes inline. If the given LLVM version does not support
128+
// this, no probe is generated at all (even if the attribute is specified).
129+
StackProbeType::Inline => Some(const_cstr!("inline-asm")),
130+
// Flag our internal `__rust_probestack` function as the stack probe symbol.
131+
// This is defined in the `compiler-builtins` crate for each architecture.
132+
StackProbeType::Call => Some(const_cstr!("__rust_probestack")),
133+
// Pick from the two above based on the LLVM version.
134+
StackProbeType::InlineOrCall { min_llvm_version_for_inline } => {
135+
if llvm_util::get_version() < min_llvm_version_for_inline {
136+
Some(const_cstr!("__rust_probestack"))
137+
} else {
138+
Some(const_cstr!("inline-asm"))
139+
}
140+
}
141+
};
142+
if let Some(attr_value) = attr_value {
143+
llvm::AddFunctionAttrStringValue(
144+
llfn,
145+
llvm::AttributePlace::Function,
146+
const_cstr!("probe-stack"),
147+
attr_value,
148+
);
149+
}
143150
}
144151

145152
pub fn llvm_target_features(sess: &Session) -> impl Iterator<Item = &str> {

compiler/rustc_target/src/spec/i386_apple_ios.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::apple_sdk_base::{opts, Arch};
2-
use crate::spec::{Target, TargetOptions};
2+
use crate::spec::{StackProbeType, Target, TargetOptions};
33

44
pub fn target() -> Target {
55
let base = opts("ios", Arch::I386);
@@ -10,6 +10,10 @@ pub fn target() -> Target {
1010
f64:32:64-f80:128-n8:16:32-S128"
1111
.to_string(),
1212
arch: "x86".to_string(),
13-
options: TargetOptions { max_atomic_width: Some(64), stack_probes: true, ..base },
13+
options: TargetOptions {
14+
max_atomic_width: Some(64),
15+
stack_probes: StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) },
16+
..base
17+
},
1418
}
1519
}

compiler/rustc_target/src/spec/i686_apple_darwin.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
use crate::spec::{LinkerFlavor, Target, TargetOptions};
1+
use crate::spec::{LinkerFlavor, StackProbeType, Target, TargetOptions};
22

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

1212
// Clang automatically chooses a more specific target based on

compiler/rustc_target/src/spec/i686_linux_android.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::spec::Target;
1+
use crate::spec::{StackProbeType, Target};
22

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

1616
Target {
1717
llvm_target: "i686-linux-android".to_string(),

compiler/rustc_target/src/spec/i686_unknown_freebsd.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::spec::{LinkerFlavor, Target};
1+
use crate::spec::{LinkerFlavor, StackProbeType, Target};
22

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

1212
Target {
1313
llvm_target: "i686-unknown-freebsd".to_string(),

compiler/rustc_target/src/spec/i686_unknown_haiku.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
use crate::spec::{LinkerFlavor, Target};
1+
use crate::spec::{LinkerFlavor, StackProbeType, Target};
22

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

1010
Target {
1111
llvm_target: "i686-unknown-haiku".to_string(),

compiler/rustc_target/src/spec/i686_unknown_linux_gnu.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
use crate::spec::{LinkerFlavor, Target};
1+
use crate::spec::{LinkerFlavor, StackProbeType, Target};
22

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

1010
Target {
1111
llvm_target: "i686-unknown-linux-gnu".to_string(),

compiler/rustc_target/src/spec/i686_unknown_linux_musl.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
use crate::spec::{LinkerFlavor, Target};
1+
use crate::spec::{LinkerFlavor, StackProbeType, Target};
22

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

1111
// The unwinder used by i686-unknown-linux-musl, the LLVM libunwind
1212
// implementation, apparently relies on frame pointers existing... somehow.

compiler/rustc_target/src/spec/i686_unknown_netbsd.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
use crate::spec::{LinkerFlavor, Target, TargetOptions};
1+
use crate::spec::{LinkerFlavor, StackProbeType, Target, TargetOptions};
22

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

1010
Target {
1111
llvm_target: "i686-unknown-netbsdelf".to_string(),

compiler/rustc_target/src/spec/i686_unknown_openbsd.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
use crate::spec::{LinkerFlavor, Target};
1+
use crate::spec::{LinkerFlavor, StackProbeType, Target};
22

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

1111
Target {
1212
llvm_target: "i686-unknown-openbsd".to_string(),

compiler/rustc_target/src/spec/i686_wrs_vxworks.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
use crate::spec::{LinkerFlavor, Target};
1+
use crate::spec::{LinkerFlavor, StackProbeType, Target};
22

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

1010
Target {
1111
llvm_target: "i686-unknown-linux-gnu".to_string(),

compiler/rustc_target/src/spec/linux_kernel_base.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use crate::spec::{LinkArgs, LinkerFlavor, PanicStrategy, RelocModel, RelroLevel, TargetOptions};
1+
use crate::spec::{
2+
LinkArgs, LinkerFlavor, PanicStrategy, RelocModel, RelroLevel, StackProbeType, TargetOptions,
3+
};
24

35
pub fn opts() -> TargetOptions {
46
let mut pre_link_args = LinkArgs::new();
@@ -11,7 +13,7 @@ pub fn opts() -> TargetOptions {
1113
env: "gnu".to_string(),
1214
disable_redzone: true,
1315
panic_strategy: PanicStrategy::Abort,
14-
stack_probes: true,
16+
stack_probes: StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) },
1517
eliminate_frame_pointer: false,
1618
linker_is_gnu: true,
1719
position_independent_executables: true,

compiler/rustc_target/src/spec/mod.rs

+94-4
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ use crate::spec::crt_objects::{CrtObjects, CrtObjectsFallback};
4040
use rustc_serialize::json::{Json, ToJson};
4141
use rustc_span::symbol::{sym, Symbol};
4242
use std::collections::BTreeMap;
43+
use std::convert::TryFrom;
4344
use std::ops::{Deref, DerefMut};
4445
use std::path::{Path, PathBuf};
4546
use std::str::FromStr;
@@ -479,6 +480,83 @@ macro_rules! supported_targets {
479480
};
480481
}
481482

483+
#[derive(Clone, Debug, PartialEq, Eq)]
484+
pub enum StackProbeType {
485+
/// Don't emit any stack probes.
486+
None,
487+
/// It is harmless to use this option even on targets that do not have backend support for
488+
/// stack probes as the failure mode is the same as if no stack-probe option was specified in
489+
/// the first place.
490+
Inline,
491+
/// Call `__rust_probestack` whenever stack needs to be probed.
492+
Call,
493+
/// Use inline option for LLVM versions later than specified in `min_llvm_version_for_inline`
494+
/// and call `__rust_probestack` otherwise.
495+
InlineOrCall { min_llvm_version_for_inline: (u32, u32, u32) },
496+
}
497+
498+
impl StackProbeType {
499+
fn from_json(json: &Json) -> Result<Self, String> {
500+
let object = json.as_object().ok_or_else(|| "expected a JSON object")?;
501+
let kind = object
502+
.get("kind")
503+
.and_then(|o| o.as_string())
504+
.ok_or_else(|| "expected `kind` to be a string")?;
505+
match kind {
506+
"none" => Ok(StackProbeType::None),
507+
"inline" => Ok(StackProbeType::Inline),
508+
"call" => Ok(StackProbeType::Call),
509+
"inline-or-call" => {
510+
let min_version = object
511+
.get("min-llvm-version-for-inline")
512+
.and_then(|o| o.as_array())
513+
.ok_or_else(|| "expected `min-llvm-version-for-inline` to be an array")?;
514+
let mut iter = min_version.into_iter().map(|v| {
515+
let int = v.as_u64().ok_or_else(
516+
|| "expected `min-llvm-version-for-inline` values to be integers",
517+
)?;
518+
u32::try_from(int)
519+
.map_err(|_| "`min-llvm-version-for-inline` values don't convert to u32")
520+
});
521+
let min_llvm_version_for_inline = (
522+
iter.next().unwrap_or(Ok(11))?,
523+
iter.next().unwrap_or(Ok(0))?,
524+
iter.next().unwrap_or(Ok(0))?,
525+
);
526+
Ok(StackProbeType::InlineOrCall { min_llvm_version_for_inline })
527+
}
528+
_ => Err(String::from(
529+
"`kind` expected to be one of `inline-or-none`, `call` or `inline-or-call`",
530+
)),
531+
}
532+
}
533+
}
534+
535+
impl ToJson for StackProbeType {
536+
fn to_json(&self) -> Json {
537+
Json::Object(match self {
538+
StackProbeType::None => {
539+
vec![(String::from("kind"), "none".to_json())].into_iter().collect()
540+
}
541+
StackProbeType::Inline => {
542+
vec![(String::from("kind"), "inline".to_json())].into_iter().collect()
543+
}
544+
StackProbeType::Call => {
545+
vec![(String::from("kind"), "call".to_json())].into_iter().collect()
546+
}
547+
StackProbeType::InlineOrCall { min_llvm_version_for_inline } => vec![
548+
(String::from("kind"), "inline-or-call".to_json()),
549+
(
550+
String::from("min-llvm-version-for-inline"),
551+
min_llvm_version_for_inline.to_json(),
552+
),
553+
]
554+
.into_iter()
555+
.collect(),
556+
})
557+
}
558+
}
559+
482560
supported_targets! {
483561
("x86_64-unknown-linux-gnu", x86_64_unknown_linux_gnu),
484562
("x86_64-unknown-linux-gnux32", x86_64_unknown_linux_gnux32),
@@ -926,8 +1004,8 @@ pub struct TargetOptions {
9261004
/// Whether or not crt-static is respected by the compiler (or is a no-op).
9271005
pub crt_static_respected: bool,
9281006

929-
/// Whether or not stack probes (__rust_probestack) are enabled
930-
pub stack_probes: bool,
1007+
/// The implementation of stack probes to use.
1008+
pub stack_probes: StackProbeType,
9311009

9321010
/// The minimum alignment for global symbols.
9331011
pub min_global_align: Option<u64>,
@@ -1085,7 +1163,7 @@ impl Default for TargetOptions {
10851163
crt_static_allows_dylibs: false,
10861164
crt_static_default: false,
10871165
crt_static_respected: false,
1088-
stack_probes: false,
1166+
stack_probes: StackProbeType::None,
10891167
min_global_align: None,
10901168
default_codegen_units: None,
10911169
trap_unreachable: true,
@@ -1361,6 +1439,18 @@ impl Target {
13611439
Some(Ok(()))
13621440
})).unwrap_or(Ok(()))
13631441
} );
1442+
($key_name:ident, StackProbeType) => ( {
1443+
let name = (stringify!($key_name)).replace("_", "-");
1444+
obj.find(&name[..]).and_then(|o| match StackProbeType::from_json(o) {
1445+
Ok(v) => {
1446+
base.$key_name = v;
1447+
Some(Ok(()))
1448+
},
1449+
Err(s) => Some(Err(
1450+
format!("`{:?}` is not a valid value for `{}`: {}", o, name, s)
1451+
)),
1452+
}).unwrap_or(Ok(()))
1453+
} );
13641454
($key_name:ident, crt_objects_fallback) => ( {
13651455
let name = (stringify!($key_name)).replace("_", "-");
13661456
obj.find(&name[..]).and_then(|o| o.as_string().and_then(|s| {
@@ -1516,7 +1606,7 @@ impl Target {
15161606
key!(crt_static_allows_dylibs, bool);
15171607
key!(crt_static_default, bool);
15181608
key!(crt_static_respected, bool);
1519-
key!(stack_probes, bool);
1609+
key!(stack_probes, StackProbeType)?;
15201610
key!(min_global_align, Option<u64>);
15211611
key!(default_codegen_units, Option<u64>);
15221612
key!(trap_unreachable, bool);

compiler/rustc_target/src/spec/uefi_msvc_base.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
// the timer-interrupt. Device-drivers are required to use polling-based models. Furthermore, all
1010
// code runs in the same environment, no process separation is supported.
1111

12-
use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, TargetOptions};
12+
use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, StackProbeType, TargetOptions};
1313

1414
pub fn opts() -> TargetOptions {
1515
let mut base = super::msvc_base::opts();
@@ -43,7 +43,9 @@ pub fn opts() -> TargetOptions {
4343
exe_suffix: ".efi".to_string(),
4444
allows_weak_linkage: false,
4545
panic_strategy: PanicStrategy::Abort,
46-
stack_probes: true,
46+
// LLVM does not emit inline assembly because the LLVM target does not get considered as…
47+
// "Windows".
48+
stack_probes: StackProbeType::Call,
4749
singlethread: true,
4850
linker: Some("rust-lld".to_string()),
4951
..base

0 commit comments

Comments
 (0)