[AArch64] KASAN asan.module_ctor does not inherit "frame-pointer"="all" or uwtable, emits str x30 instead of stp x29, x30
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
When targeting aarch64-unknown-none with **-Zsanitizer=kernel-address, -Cforce-frame-pointers=yes, and -Cforce-unwind-tables=y**, all user functions correctly receive **"frame-pointer"="all" + uwtable** attributes and emit **stp x29, x30** prologues.
However, **asan.module_ctor** and **asan.module_dtor** synthesised by LLVM's **missing both "frame-pointer" and uwtable** — and emit **str x30** (LR-only, no frame record).
### Reproducing Issue
**minimal_repro.rs**
```rust
#![no_std]
#![allow(missing_docs)]
use core::sync::atomic::{AtomicUsize, Ordering};
#[panic_handler]
fn panic(_: &core::panic::PanicInfo<'_>) -> ! { loop {} }
static COUNTER: AtomicUsize = AtomicUsize::new(0);
#[no_mangle]
pub extern "C" fn use_global() -> usize {
COUNTER.fetch_add(1, Ordering::Relaxed)
}
```
**Build**
```bash
rustc --edition=2021 --crate-type=lib --target=aarch64-unknown-none \
-Cpanic=abort -Copt-level=2 -Crelocation-model=static \
-Cembed-bitcode=n -Clto=n -Ccodegen-units=1 \
-Csymbol-mangling-version=v0 -Zfunction-sections=n \
-Ctarget-feature="-neon" \
-Cforce-unwind-tables=y -Zuse-sync-unwind=n \
-Zbranch-protection=pac-ret -Zfixed-x18 \
-Cforce-frame-pointers=yes \
-Zsanitizer=kernel-address \
-o repro.o --emit=obj minimal_repro.rs
```
**Check:**
```bash
llvm-objdump --mattr=+v8.3a -d repro.o
```
## Actual disassembly output
Full disassembly attached as `c`.
**`use_global` — CORRECT** (`stp x29, x30`, full frame record):
```asm
0000000000000010 :
10: 3f 23 03 d5 paciasp
14: fd 7b bf a9 stp x29, x30, [sp, #-16]! ← CORRECT
18: fd 03 00 91 mov x29, sp
1c: 08 00 00 90 adrp x8, 0x0
20: 08 01 00 91 add x8, x8, #0
24: 00 7d 5f c8 ldxr x0, [x8]
28: 09 04 00 91 add x9, x0, #1
2c: 09 7d 0a c8 stxr w10, x9, [x8]
30: aa ff ff 35 cbnz w10, 0x24
34: fd 7b c1 a8 ldp x29, x30, [sp], #16
38: bf 23 03 d5 autiasp
3c: c0 03 5f d6 ret
```
**`asan.module_ctor`** (`str x30`, no frame record):
```asm
0000000000000000 :
0: 3f 23 03 d5 paciasp
4: fe 0f 1f f8 str x30, [sp, #-16]! ← Frame pointed omitted
8: 00 00 00 90 adrp x0, 0x0
c: 00 00 00 91 add x0, x0, #0
10: 21 00 80 52 mov w1, #1
14: 00 00 00 94 bl __asan_register_globals
18: fe 07 41 f8 ldr x30, [sp], #16
1c: bf 23 03 d5 autiasp
20: c0 03 5f d6 ret
```
**`asan.module_dtor`**
```asm
0000000000000000 :
0: 3f 23 03 d5 paciasp
4: fe 0f 1f f8 str x30, [sp, #-16]! ← Frame pointed omitted
8: 00 00 00 90 adrp x0, 0x0
c: 00 00 00 91 add x0, x0, #0
10: 21 00 80 52 mov w1, #1
14: 00 00 00 94 bl __asan_unregister_globals
18: fe 07 41 f8 ldr x30, [sp], #16
1c: bf 23 03 d5 autiasp
20: c0 03 5f d6 ret
```
**disasm.s:** [disasm.txt](https://github.com/user-attachments/files/26210638/disasm.txt)
**repro.o:** [repro_obj.txt](https://github.com/user-attachments/files/26210639/repro_obj.txt)
## LLVM IR: the attribute mismatch
Comamnd used
```
rustc --edition=2021 --crate-type=lib --target=aarch64-unknown-none \
-Cpanic=abort -Copt-level=2 -Crelocation-model=static -Cembed-bitcode=n \
-Clto=n -Ccodegen-units=1 -Csymbol-mangling-version=v0 -Zfunction-sections=n \
-Ctarget-feature=-neon -Cforce-unwind-tables=y -Zuse-sync-unwind=n \
-Zbranch-protection=pac-ret -Zfixed-x18 -Cforce-frame-pointers=yes \
-Zsanitizer=kernel-address --emit=llvm-ir -o /tmp/asan-fp-repro/repro.ll \
./minimal_repro.rs
```
Full IR attached as `repro.ll`.
User functions use attribute groups `#0` and `#1`, which have `"frame-pointer"="all"` **and** `uwtable`. The synthesised `asan.module_ctor` / `asan.module_dtor` use attribute group `#2` which has **only** `nounwind`:
```llvm
; asan.module_ctor — uses #2:
define internal void @asan.module_ctor() #2 {
call void @__asan_register_globals(i64 ptrtoint (ptr @0 to i64), i64 1)
ret void
}
; asan.module_dtor — uses #2:
define internal void @asan.module_dtor() #2 {
call void @__asan_unregister_globals(i64 ptrtoint (ptr @0 to i64), i64 1)
ret void
}
; User function attributes — CORRECT (has frame-pointer + uwtable):
attributes #0 = { nofree norecurse noredzone noreturn nosync nounwind
sanitize_address memory(none) uwtable "frame-pointer"="all"
"probe-stack"="inline-asm" "target-cpu"="generic"
"target-features"="+v8a,+strict-align,+neon,+fp-armv8,-neon,-fp-armv8,+reserve-x18" }
attributes #1 = { mustprogress nofree norecurse noredzone nounwind
sanitize_address willreturn
memory(readwrite, argmem: none, inaccessiblemem: none) uwtable
"frame-pointer"="all" "probe-stack"="inline-asm"
"target-cpu"="generic"
"target-features"="+v8a,+strict-align,+neon,+fp-armv8,-neon,-fp-armv8,+reserve-x18" }
; asan.module_ctor/dtor attributes — BUG (no frame-pointer, no uwtable):
attributes #2 = { nounwind }
```
**repro.ll:** [repro_ll.txt](https://github.com/user-attachments/files/26210640/repro_ll.txt)
Since **asan.module_ctor** and **asan.module_dtor** lack the **uwtable attribute** (their **attribute group 2 = { nounwind }** has neither **uwtable nor "frame-pointer"="all")**, Backend does not emit any **.eh_frame FDE** (Frame Description Entry) for these functions. This means **no Call Frame Information** (CFI) directives are generated at all — including, critically, **no DW_CFA_AARCH64_negate_ra_state**.
#### Dwarf generated for repro.o (asan.module_ctor/asan.module_dtor)
```
00000014 0000001c 00000018 FDE cie=00000000 pc=00000000...00000010
Format: DWARF32
DW_CFA_advance_loc: 4 to 0x4
DW_CFA_AARCH64_negate_ra_state:
DW_CFA_advance_loc: 4 to 0x8
DW_CFA_def_cfa_offset: +16
DW_CFA_advance_loc: 4 to 0xc
DW_CFA_def_cfa: W29 +16
DW_CFA_offset: W30 -8
DW_CFA_offset: W29 -16
DW_CFA_nop:
DW_CFA_nop:
0x0: CFA=WSP
0x4: CFA=WSP: reg34=1
0x8: CFA=WSP+16: reg34=1
0xc: CFA=W29+16: W29=[CFA-16], W30=[CFA-8], reg34=1
```
[dwarf_dump.txt](https://github.com/user-attachments/files/26212886/dwarf_dump.txt)
How can we force asan_ctor to use a frame pointer and generate proper unwind tables so that we get DW_CFA_AARCH64_negate_ra_state?
@vitalybuka Could you take look at this?
This issue actually breaks arm64 shadow call patching and PAC aware unwinding in Linux kernel.
Contributor guide
Research direction
Start by reproducing the issue with minimal_repro.rs and inspect the generated repro.ll, comparing the user-function attribute groups with the asan.module_ctor and asan.module_dtor group. Confirm the resulting object and DWARF output in the attached disassembly and dwarf dump. Done means the synthesized functions carry the required frame-pointer and uwtable attributes and produce the expected frame record and AArch64 unwind information.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- linux
- Domain
- compilers, operating-systems
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100