Skip to content

Commit a027b01

Browse files
authored
Rollup merge of #98998 - workingjubilee:naked-means-no-clothes-enforcement-technology, r=Amanieu
Remove branch target prologues from `#[naked] fn` This patch hacks around #98768 for now via injecting appropriate attributes into the LLVMIR we emit for naked functions. I intend to pursue this upstream so that these attributes can be removed in general, but it's slow going wading through C++ for me.
2 parents 5ccdf1f + 530b5da commit a027b01

File tree

7 files changed

+56
-1
lines changed

7 files changed

+56
-1
lines changed

compiler/rustc_codegen_llvm/src/attributes.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,12 @@ pub fn from_fn_attrs<'ll, 'tcx>(
299299
}
300300
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
301301
to_add.push(AttributeKind::Naked.create_attr(cx.llcx));
302+
// HACK(jubilee): "indirect branch tracking" works by attaching prologues to functions.
303+
// And it is a module-level attribute, so the alternative is pulling naked functions into new LLVM modules.
304+
// Otherwise LLVM's "naked" functions come with endbr prefixes per https://github.com/rust-lang/rust/issues/98768
305+
to_add.push(AttributeKind::NoCfCheck.create_attr(cx.llcx));
306+
// Need this for AArch64.
307+
to_add.push(llvm::CreateAttrStringValue(cx.llcx, "branch-target-enforcement", "false"));
302308
}
303309
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) {
304310
// apply to return place instead of function (unlike all other attributes applied in this function)

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ pub enum AttributeKind {
191191
StackProtect = 32,
192192
NoUndef = 33,
193193
SanitizeMemTag = 34,
194+
NoCfCheck = 35,
194195
}
195196

196197
/// LLVMIntPredicate

compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ enum LLVMRustAttribute {
8484
StackProtect = 32,
8585
NoUndef = 33,
8686
SanitizeMemTag = 34,
87+
NoCfCheck = 35,
8788
};
8889

8990
typedef struct OpaqueRustString *RustStringRef;

compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,8 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) {
176176
return Attribute::NoAlias;
177177
case NoCapture:
178178
return Attribute::NoCapture;
179+
case NoCfCheck:
180+
return Attribute::NoCfCheck;
179181
case NoInline:
180182
return Attribute::NoInline;
181183
case NonNull:
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// compile-flags: -C no-prepopulate-passes -Zbranch-protection=bti
2+
// assembly-output: emit-asm
3+
// needs-asm-support
4+
// only-aarch64
5+
6+
#![crate_type = "lib"]
7+
#![feature(naked_functions)]
8+
use std::arch::asm;
9+
10+
// The problem at hand: Rust has adopted a fairly strict meaning for "naked functions",
11+
// meaning "no prologue whatsoever, no, really, not one instruction."
12+
// Unfortunately, aarch64's "branch target identification" works via hints at landing sites.
13+
// LLVM implements this via making sure of that, even for functions with the naked attribute.
14+
// So, we must emit an appropriate instruction instead!
15+
#[no_mangle]
16+
#[naked]
17+
pub unsafe extern "C" fn _hlt() -> ! {
18+
// CHECK-NOT: hint #34
19+
// CHECK: hlt #0x1
20+
asm!("hlt #1", options(noreturn))
21+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// compile-flags: -C no-prepopulate-passes -Zcf-protection=full
2+
// assembly-output: emit-asm
3+
// needs-asm-support
4+
// only-x86_64
5+
6+
#![crate_type = "lib"]
7+
#![feature(naked_functions)]
8+
use std::arch::asm;
9+
10+
// The problem at hand: Rust has adopted a fairly strict meaning for "naked functions",
11+
// meaning "no prologue whatsoever, no, really, not one instruction."
12+
// Unfortunately, x86's control-flow enforcement, specifically indirect branch protection,
13+
// works by using an instruction for each possible landing site,
14+
// and LLVM implements this via making sure of that.
15+
#[no_mangle]
16+
#[naked]
17+
pub unsafe extern "sysv64" fn will_halt() -> ! {
18+
// CHECK-NOT: endbr{{32|64}}
19+
// CHECK: hlt
20+
asm!("hlt", options(noreturn))
21+
}
22+
23+
// what about aarch64?
24+
// "branch-protection"=false

src/test/codegen/naked-noinline.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,4 @@ pub unsafe fn g() {
2828
f();
2929
}
3030

31-
// CHECK: attributes [[ATTR]] = { naked noinline{{.*}} }
31+
// CHECK: attributes [[ATTR]] = { naked{{.*}}noinline{{.*}} }

0 commit comments

Comments
 (0)