Skip to content

Commit 2a6268d

Browse files
authored
[libc][AArch64] Add an AArch64 setjmp/longjmp. (#101177)
Previously, building libc for AArch64 in `LLVM_LIBC_FULL_BUILD` mode would fail because no implementation of setjmp/longjmp was available. This was the only obstacle, so now a full AArch64 build of libc is possible. This implementation automatically supports PAC and BTI if compiled with the appropriate options. I would have liked to do the same for MTE stack tagging, but as far as I can see there's currently no predefined macro that allows detection of `-fsanitize=memtag-stack`, so I've left that one as a TODO. AAPCS64 delegates the x18 register to individual platform ABIs, and allows them to choose what it's used for, which may or may not require setjmp and longjmp to save and restore it. To accommodate this, I've introduced a libc configuration option. The default is on, because the only use of x18 I've so far encountered uses it to store information specific to the current stack frame (so longjmp does need to restore it), and this is also safe behavior in the default situation where the platform ABI specifies no use of x18 and it becomes a temporary register (restoring it to its previous value is no worse than any _other_ way for a function call to clobber it). But if a platform ABI needs to use x18 in a way that requires longjmp to leave it alone, they can turn the option off.
1 parent fa84297 commit 2a6268d

File tree

7 files changed

+228
-0
lines changed

7 files changed

+228
-0
lines changed

libc/config/config.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,5 +82,11 @@
8282
"value": "LIBC_QSORT_QUICK_SORT",
8383
"doc": "Configures sorting algorithm for qsort and qsort_r. Values accepted are LIBC_QSORT_QUICK_SORT, LIBC_QSORT_HEAP_SORT."
8484
}
85+
},
86+
"setjmp": {
87+
"LIBC_CONF_SETJMP_AARCH64_RESTORE_PLATFORM_REGISTER": {
88+
"value": true,
89+
"doc": "Make setjmp save the value of x18, and longjmp restore it. The AArch64 ABI delegates this register to platform ABIs, which can choose whether to make it caller-saved."
90+
}
8591
}
8692
}

libc/config/linux/aarch64/entrypoints.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,10 @@ if(LLVM_LIBC_FULL_BUILD)
728728
# sched.h entrypoints
729729
libc.src.sched.__sched_getcpucount
730730

731+
# setjmp.h entrypoints
732+
libc.src.setjmp.longjmp
733+
libc.src.setjmp.setjmp
734+
731735
# stdio.h entrypoints
732736
libc.src.stdio.clearerr
733737
libc.src.stdio.clearerr_unlocked

libc/docs/configure.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ to learn about the defaults for your platform and target.
4747
* **"scanf" options**
4848
- ``LIBC_CONF_SCANF_DISABLE_FLOAT``: Disable parsing floating point values in scanf and friends.
4949
- ``LIBC_CONF_SCANF_DISABLE_INDEX_MODE``: Disable index mode in the scanf format string.
50+
* **"setjmp" options**
51+
- ``LIBC_CONF_SETJMP_AARCH64_RESTORE_PLATFORM_REGISTER``: Make setjmp save the value of x18, and longjmp restore it. The AArch64 ABI delegates this register to platform ABIs, which can choose whether to make it caller-saved.
5052
* **"string" options**
5153
- ``LIBC_CONF_MEMSET_X86_USE_SOFTWARE_PREFETCHING``: Inserts prefetch for write instructions (PREFETCHW) for memset on x86 to recover performance when hardware prefetcher is disabled.
5254
- ``LIBC_CONF_STRING_UNSAFE_WIDE_READ``: Read more than a byte at a time to perform byte-string operations like strlen.

libc/include/llvm-libc-types/jmp_buf.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ typedef struct {
3535
#elif defined(__arm__)
3636
// r4, r5, r6, r7, r8, r9, r10, r11, r12, lr
3737
long opaque[10];
38+
#elif defined(__aarch64__)
39+
long opaque[14]; // x19-x29, lr, sp, optional x18
40+
#if __ARM_FP
41+
long fopaque[8]; // d8-d15
42+
#endif
3843
#else
3944
#error "__jmp_buf not available for your target architecture."
4045
#endif
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
if(LIBC_CONF_SETJMP_AARCH64_RESTORE_PLATFORM_REGISTER)
2+
list(APPEND setjmp_config_options "-DLIBC_COPT_SETJMP_AARCH64_RESTORE_PLATFORM_REGISTER")
3+
endif()
4+
if(setjmp_config_options)
5+
list(PREPEND setjmp_config_options "COMPILE_OPTIONS")
6+
endif()
7+
8+
add_entrypoint_object(
9+
setjmp
10+
SRCS
11+
setjmp.cpp
12+
HDRS
13+
../setjmp_impl.h
14+
DEPENDS
15+
libc.include.setjmp
16+
${setjmp_config_options}
17+
)
18+
19+
add_entrypoint_object(
20+
longjmp
21+
SRCS
22+
longjmp.cpp
23+
HDRS
24+
../longjmp.h
25+
DEPENDS
26+
libc.include.setjmp
27+
${setjmp_config_options}
28+
)

libc/src/setjmp/aarch64/longjmp.cpp

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
//===-- Implementation of longjmp for AArch64 -----------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "src/setjmp/longjmp.h"
10+
#include "src/__support/common.h"
11+
#include "src/__support/macros/config.h"
12+
13+
namespace LIBC_NAMESPACE_DECL {
14+
15+
// TODO: if MTE stack tagging is in use (-fsanitize=memtag-stack), we need to
16+
// iterate over the region between the old and new values of sp, using STG or
17+
// ST2G instructions to clear the memory tags on the invalidated region of the
18+
// stack. But this requires a means of finding out that we're in that mode, and
19+
// as far as I can see there isn't currently a predefined macro for that.
20+
//
21+
// (__ARM_FEATURE_MEMORY_TAGGING only indicates whether the target architecture
22+
// supports the MTE instructions, not whether the compiler is configured to use
23+
// them.)
24+
25+
[[gnu::naked]] LLVM_LIBC_FUNCTION(void, longjmp, (__jmp_buf * buf, int val)) {
26+
// If BTI branch protection is in use, the compiler will automatically insert
27+
// a BTI here, so we don't need to make any extra effort to do so.
28+
29+
// If PAC branch protection is in use, there's no need to sign the return
30+
// address at the start of longjmp, because we're not going to use it anyway!
31+
32+
asm(
33+
// Reload the callee-saved GPRs, including fp and lr.
34+
R"(
35+
ldp x19, x20, [x0, #0*16]
36+
ldp x21, x22, [x0, #1*16]
37+
ldp x23, x24, [x0, #2*16]
38+
ldp x25, x26, [x0, #3*16]
39+
ldp x27, x28, [x0, #4*16]
40+
ldp x29, x30, [x0, #5*16]
41+
)"
42+
43+
#if LIBC_COPT_SETJMP_AARCH64_RESTORE_PLATFORM_REGISTER
44+
// Reload the stack pointer, and the platform register x18.
45+
R"(
46+
ldp x2, x18, [x0, #6*16]
47+
mov sp, x2
48+
)"
49+
#else
50+
// Reload just the stack pointer.
51+
R"(
52+
ldr x2, [x0, #6*16]
53+
mov sp, x2
54+
)"
55+
#endif
56+
57+
#if __ARM_FP
58+
// Reload the callee-saved FP registers.
59+
R"(
60+
ldp d8, d9, [x0, #7*16]
61+
ldp d10, d11, [x0, #8*16]
62+
ldp d12, d13, [x0, #9*16]
63+
ldp d14, d15, [x0, #10*16]
64+
)"
65+
#endif
66+
67+
// Calculate the return value.
68+
R"(
69+
cmp w1, #0
70+
cinc w0, w1, eq
71+
)"
72+
73+
#if __ARM_FEATURE_PAC_DEFAULT & 1
74+
// Authenticate the return address using the PAC A key.
75+
R"(
76+
autiasp
77+
)"
78+
#elif __ARM_FEATURE_PAC_DEFAULT & 2
79+
// Authenticate the return address using the PAC B key.
80+
R"(
81+
autibsp
82+
)"
83+
#endif
84+
85+
R"(
86+
ret
87+
)");
88+
}
89+
90+
} // namespace LIBC_NAMESPACE_DECL

libc/src/setjmp/aarch64/setjmp.cpp

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
//===-- Implementation of setjmp for AArch64 ------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "src/__support/common.h"
10+
#include "src/__support/macros/config.h"
11+
#include "src/setjmp/setjmp_impl.h"
12+
13+
namespace LIBC_NAMESPACE_DECL {
14+
15+
[[gnu::naked]] LLVM_LIBC_FUNCTION(int, setjmp, (__jmp_buf * buf)) {
16+
// If BTI branch protection is in use, the compiler will automatically insert
17+
// a BTI here, so we don't need to make any extra effort to do so.
18+
19+
asm(
20+
#if __ARM_FEATURE_PAC_DEFAULT & 1
21+
// Sign the return address using the PAC A key.
22+
R"(
23+
paciasp
24+
)"
25+
#elif __ARM_FEATURE_PAC_DEFAULT & 2
26+
// Sign the return address using the PAC B key.
27+
R"(
28+
pacibsp
29+
)"
30+
#endif
31+
32+
// Store all the callee-saved GPRs, including fp (x29) and also lr (x30).
33+
// Of course lr isn't normally callee-saved (the call instruction itself
34+
// can't help clobbering it), but we certainly need to save it for this
35+
// purpose.
36+
R"(
37+
stp x19, x20, [x0, #0*16]
38+
stp x21, x22, [x0, #1*16]
39+
stp x23, x24, [x0, #2*16]
40+
stp x25, x26, [x0, #3*16]
41+
stp x27, x28, [x0, #4*16]
42+
stp x29, x30, [x0, #5*16]
43+
)"
44+
45+
#if LIBC_COPT_SETJMP_AARCH64_RESTORE_PLATFORM_REGISTER
46+
// Store the stack pointer, and the platform register x18.
47+
R"(
48+
add x1, sp, #0
49+
stp x1, x18, [x0, #6*16]
50+
)"
51+
#else
52+
// Store just the stack pointer.
53+
R"(
54+
add x1, sp, #0
55+
str x1, [x0, #6*16]
56+
)"
57+
#endif
58+
59+
#if __ARM_FP
60+
// Store the callee-saved FP registers. AAPCS64 only requires the low 64
61+
// bits of v8-v15 to be preserved, i.e. each of d8,...,d15.
62+
R"(
63+
stp d8, d9, [x0, #7*16]
64+
stp d10, d11, [x0, #8*16]
65+
stp d12, d13, [x0, #9*16]
66+
stp d14, d15, [x0, #10*16]
67+
)"
68+
#endif
69+
70+
// Set up return value of zero.
71+
R"(
72+
mov x0, #0
73+
)"
74+
75+
#if (__ARM_FEATURE_PAC_DEFAULT & 7) == 5
76+
// Authenticate the return address using the PAC A key, since the
77+
// compilation options ask for PAC protection even on leaf functions.
78+
R"(
79+
autiasp
80+
)"
81+
#elif (__ARM_FEATURE_PAC_DEFAULT & 7) == 6
82+
// Same, but using the PAC B key.
83+
R"(
84+
autibsp
85+
)"
86+
#endif
87+
88+
R"(
89+
ret
90+
)");
91+
}
92+
93+
} // namespace LIBC_NAMESPACE_DECL

0 commit comments

Comments
 (0)