about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_codegen_llvm/src/attributes.rs6
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm/ffi.rs1
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h1
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp2
-rw-r--r--src/test/assembly/aarch64-naked-fn-no-bti-prolog.rs21
-rw-r--r--src/test/assembly/x86_64-naked-fn-no-cet-prolog.rs24
-rw-r--r--src/test/codegen/naked-noinline.rs2
7 files changed, 56 insertions, 1 deletions
diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs
index 32bfa5094c3..62da99ac3fb 100644
--- a/compiler/rustc_codegen_llvm/src/attributes.rs
+++ b/compiler/rustc_codegen_llvm/src/attributes.rs
@@ -299,6 +299,12 @@ pub fn from_fn_attrs<'ll, 'tcx>(
     }
     if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) {
         to_add.push(AttributeKind::Naked.create_attr(cx.llcx));
+        // HACK(jubilee): "indirect branch tracking" works by attaching prologues to functions.
+        // And it is a module-level attribute, so the alternative is pulling naked functions into new LLVM modules.
+        // Otherwise LLVM's "naked" functions come with endbr prefixes per https://github.com/rust-lang/rust/issues/98768
+        to_add.push(AttributeKind::NoCfCheck.create_attr(cx.llcx));
+        // Need this for AArch64.
+        to_add.push(llvm::CreateAttrStringValue(cx.llcx, "branch-target-enforcement", "false"));
     }
     if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) {
         // apply to return place instead of function (unlike all other attributes applied in this function)
diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
index 0ad65e5d99b..73cedb59349 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
@@ -191,6 +191,7 @@ pub enum AttributeKind {
     StackProtect = 32,
     NoUndef = 33,
     SanitizeMemTag = 34,
+    NoCfCheck = 35,
 }
 
 /// LLVMIntPredicate
diff --git a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h
index a2b0e9b4d29..9fe84a6309b 100644
--- a/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h
+++ b/compiler/rustc_llvm/llvm-wrapper/LLVMWrapper.h
@@ -84,6 +84,7 @@ enum LLVMRustAttribute {
   StackProtect = 32,
   NoUndef = 33,
   SanitizeMemTag = 34,
+  NoCfCheck = 35,
 };
 
 typedef struct OpaqueRustString *RustStringRef;
diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
index 7ac3157e7a1..2d35ee8976e 100644
--- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
@@ -176,6 +176,8 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) {
     return Attribute::NoAlias;
   case NoCapture:
     return Attribute::NoCapture;
+  case NoCfCheck:
+    return Attribute::NoCfCheck;
   case NoInline:
     return Attribute::NoInline;
   case NonNull:
diff --git a/src/test/assembly/aarch64-naked-fn-no-bti-prolog.rs b/src/test/assembly/aarch64-naked-fn-no-bti-prolog.rs
new file mode 100644
index 00000000000..79b0bb2d7ee
--- /dev/null
+++ b/src/test/assembly/aarch64-naked-fn-no-bti-prolog.rs
@@ -0,0 +1,21 @@
+// compile-flags: -C no-prepopulate-passes -Zbranch-protection=bti
+// assembly-output: emit-asm
+// needs-asm-support
+// only-aarch64
+
+#![crate_type = "lib"]
+#![feature(naked_functions)]
+use std::arch::asm;
+
+// The problem at hand: Rust has adopted a fairly strict meaning for "naked functions",
+// meaning "no prologue whatsoever, no, really, not one instruction."
+// Unfortunately, aarch64's "branch target identification" works via hints at landing sites.
+// LLVM implements this via making sure of that, even for functions with the naked attribute.
+// So, we must emit an appropriate instruction instead!
+#[no_mangle]
+#[naked]
+pub unsafe extern "C" fn _hlt() -> ! {
+    // CHECK-NOT: hint #34
+    // CHECK: hlt #0x1
+    asm!("hlt #1", options(noreturn))
+}
diff --git a/src/test/assembly/x86_64-naked-fn-no-cet-prolog.rs b/src/test/assembly/x86_64-naked-fn-no-cet-prolog.rs
new file mode 100644
index 00000000000..bedcded731d
--- /dev/null
+++ b/src/test/assembly/x86_64-naked-fn-no-cet-prolog.rs
@@ -0,0 +1,24 @@
+// compile-flags: -C no-prepopulate-passes -Zcf-protection=full
+// assembly-output: emit-asm
+// needs-asm-support
+// only-x86_64
+
+#![crate_type = "lib"]
+#![feature(naked_functions)]
+use std::arch::asm;
+
+// The problem at hand: Rust has adopted a fairly strict meaning for "naked functions",
+// meaning "no prologue whatsoever, no, really, not one instruction."
+// Unfortunately, x86's control-flow enforcement, specifically indirect branch protection,
+// works by using an instruction for each possible landing site,
+// and LLVM implements this via making sure of that.
+#[no_mangle]
+#[naked]
+pub unsafe extern "sysv64" fn will_halt() -> ! {
+    // CHECK-NOT: endbr{{32|64}}
+    // CHECK: hlt
+    asm!("hlt", options(noreturn))
+}
+
+// what about aarch64?
+// "branch-protection"=false
diff --git a/src/test/codegen/naked-noinline.rs b/src/test/codegen/naked-noinline.rs
index 13bc139ecd0..c0ac69f4ed7 100644
--- a/src/test/codegen/naked-noinline.rs
+++ b/src/test/codegen/naked-noinline.rs
@@ -28,4 +28,4 @@ pub unsafe fn g() {
     f();
 }
 
-// CHECK: attributes [[ATTR]] = { naked noinline{{.*}} }
+// CHECK: attributes [[ATTR]] = { naked{{.*}}noinline{{.*}} }