Skip to content

Commit 4d17358

Browse files
committed
Annotate naked unwind example and add resources
We demonstrate how to correctly unwind from a naked function. The assembler directives and boilerplate for this may be unfamiliar to people, so let's annotate this example heavily and add links to further resources. We'll use the `sysv64-unwind` ABI rather than `C-unwind` just to make things a bit more unambiguous.
1 parent d64c438 commit 4d17358

File tree

1 file changed

+49
-7
lines changed

1 file changed

+49
-7
lines changed

Diff for: src/inline-assembly.md

+49-7
Original file line numberDiff line numberDiff line change
@@ -1413,31 +1413,73 @@ r[asm.naked-rules.unwind]
14131413
```rust
14141414
# #[cfg(target_arch = "x86_64")] {
14151415
#[unsafe(naked)]
1416-
extern "C-unwind" fn naked_function() {
1416+
extern "sysv64-unwind" fn unwinding_naked() {
14171417
core::arch::naked_asm!(
1418+
// "CFI" here stands for "call frame information".
14181419
".cfi_startproc",
1420+
// The CFA (canonical frame address) is the value of `rsp`
1421+
// before the `call`, i.e. before the return address, `rip`,
1422+
// was pushed to `rsp`, so it's eight bytes higher in memory
1423+
// than `rsp` upon function entry (after `rip` has been
1424+
// pushed).
1425+
//
1426+
// This is the default, so we don't have to write it.
1427+
//".cfi_def_cfa rsp, 8",
1428+
//
1429+
// The traditional thing to do is to preserve the base
1430+
// pointer, so we'll do that.
14191431
"push rbp",
1420-
".cfi_def_cfa_offset 16",
1432+
// Since we've now extended the stack downward by 8 bytes in
1433+
// memory, we need to adjust the offset to the CFA from `rsp`
1434+
// by another 8 bytes.
1435+
".cfi_adjust_cfa_offset 8",
1436+
// We also then annotate where we've stored the caller's value
1437+
// of `rbp`, relative to the CFA, so that when unwinding into
1438+
// the caller we can find it, in case we need it to calculate
1439+
// the caller's CFA relative to it.
1440+
//
1441+
// Here, we've stored the caller's `rbp` starting 16 bytes
1442+
// below the CFA. I.e., starting from the CFA, there's first
1443+
// the `rip` (which starts 8 bytes below the CFA), then
1444+
// there's the caller's `rbp` that we just pushed.
14211445
".cfi_offset rbp, -16",
1446+
// As is traditional, we set the base pointer to the value of
1447+
// the stack pointer. This way, the base pointer stays the
1448+
// same throughout the function body.
14221449
"mov rbp, rsp",
1450+
// We can now track the offset to the CFA from the base
1451+
// pointer. This means we don't need to make any further
1452+
// adjustments until the end, as we don't change `rbp`.
14231453
".cfi_def_cfa_register rbp",
1424-
"",
1425-
"call {function}",
1426-
"",
1454+
// We can now call a function that may panic.
1455+
"call {f}",
1456+
// Upon return, we restore `rbp` in preparation for returning
1457+
// ourselves.
14271458
"pop rbp",
1459+
// Now that we've restored `rbp`, we must specify the offset
1460+
// to the CFA again in terms of `rsp`.
14281461
".cfi_def_cfa rsp, 8",
1462+
// Now we can return.
14291463
"ret",
14301464
".cfi_endproc",
1431-
function = sym function_that_panics,
1465+
f = sym may_panic,
14321466
)
14331467
}
14341468

1435-
extern "C-unwind" fn function_that_panics() {
1469+
extern "sysv64-unwind" fn may_panic() {
14361470
panic!("unwind!");
14371471
}
14381472
# }
14391473
```
14401474

1475+
> [!NOTE]
1476+
>
1477+
> For more information on the `cfi` assembler directives above, see these resources:
1478+
>
1479+
> - [Using `as` - CFI directives](https://sourceware.org/binutils/docs/as/CFI-directives.html)
1480+
> - [DWARF Debugging Information Format Version 5](https://dwarfstd.org/doc/DWARF5.pdf)
1481+
> - [ImperialViolet - CFI directives in assembly files](https://www.imperialviolet.org/2017/01/18/cfi.html)
1482+
14411483
r[asm.validity]
14421484
### Correctness and Validity
14431485

0 commit comments

Comments
 (0)