Re: lib/57638: thread local storage broken on evbarm (armv5)
"Taylor R Campbell via gnats" <[email protected]> Fri, 24 Jul 2026 16:15:02 +0000 (UTC)
| Newsgroups | gmane.os.netbsd.bugs |
|---|---|
| Message-ID | <[email protected]> |
The following reply was made to PR lib/57638; it has been noted by GNATS. From: Taylor R Campbell <[email protected]> To: Martin Husemann <[email protected]>, Nick Hudson <[email protected]>, matthew green <[email protected]> Cc: [email protected], [email protected] Subject: Re: lib/57638: thread local storage broken on evbarm (armv5) Date: Fri, 24 Jul 2026 16:11:31 +0000 This is a multi-part message in MIME format. --=_zuuYtksK0C8RJaF9J8AwVVP/KZSlw/L+ nick and I tracked this down to gcc generating calls to __aeabi_read_tp (read thread pointer) -- which it uses only on armv5 (or with -mtp=soft) -- with a misaligned stack pointer: 00000328 <fdef>: fdef(): 328: e52de004 push {lr} @ (str lr, [sp, #-4]!) 32c: ebfffffa bl 31c <__aeabi_read_tp@plt> 330: e59f3008 ldr r3, [pc, #8] @ 340 <fdef+0x18> 334: e79f3003 ldr r3, [pc, r3] 338: e0800003 add r0, r0, r3 33c: e49df004 pop {pc} @ (ldr pc, [sp], #4) 340: 000012a4 .word 0x000012a4 The ABI requires 8-byte stack alignment on entry to procedures, but this calls __aeabi_read_tp with only 4-byte alignment. And although __aeabi_read_tp itself doesn't care about stack alignment, if the symbol is resolved lazily, we'll take a detour through the dynamic linker, which does care about stack alignment. I filed an upstream gcc bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126394 The attached patch works around it when compiling with an ABI that uses __aeabi_read_tp (instead of, e.g., cp15, as armv>=6 use) by forcing stack alignment for leaf procedures -- so any procedures that would have saved 2n+1 registers will instead save 2n+2 registers. (Leaf procedures that don't use the stack at all are unaffected.) Shall we commit this? --=_zuuYtksK0C8RJaF9J8AwVVP/KZSlw/L+ Content-Type: text/plain; charset="ISO-8859-1"; name="pr57638-armv5tls" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="pr57638-armv5tls.patch" # HG changeset patch # User Taylor R Campbell <[email protected]> # Date 1784905905 0 # Fri Jul 24 15:11:45 2026 +0000 # Branch trunk # Node ID e31559a47a90f4fc44d6145177b4fdadb3bad67d # Parent a8ca1f8a2394ec93dffbe0921ba3fb836fefd4c9 # EXP-Topic riastradh-pr57638-armv5tls gcc/arm: For -mtp=3Dsoft, ensure stack alignment even in leaves. The option -mtp=3Dsoft, which is the default on earmv5, makes queries to the thread pointer, for access to static (`initial-exec') thread-local storage, go through the C runtime subroutine __aeabi_read_tp. (For earmv>=3D6, we use the cp15 register via a single instruction.) Thus procedures which gcc thinks of as leaf procedures that use __aeabi_read_tp are not really leaf procedures -- and even though __aeabi_read_tp itself doesn't use the stack pointer at all, resolving the symbol may take a detour through the dynamic linker, which does rely on an aligned stack pointer. Ideally, we would ensure stack alignment only for `leaf' procedures that actually use __aeabi_read_tp. But I don't know how to query that in this context. And in any case, this change only applies to subroutines that are already saving 2n+1 registers for some n, picking some arbitrary register to round it up to 2n+2. For example: int y; int * get_y(void) { return &y; } int * get_y_with_r4(void) { asm volatile("" ::: "r4"); return &y; } With this change, we get: 00000018 <get_y>: get_y(): 18: e59f0000 ldr r0, [pc] @ 20 <get_y+0x8> 1c: e12fff1e bx lr 20: 00000000 .word 0x00000000 20: R_ARM_ABS32 .bss 00000024 <get_y_with_r4>: get_y_with_r4(): 24: e92d0030 push {r4, r5} 28: e8bd0030 pop {r4, r5} 2c: e59f0000 ldr r0, [pc] @ 34 <get_y_with_r4+0x10> 30: e12fff1e bx lr 34: 00000000 .word 0x00000000 34: R_ARM_ABS32 .bss In contrast, with earmv>=3D6, get_y would be unchanged, but get_y_with_r4 would be: 00000020 <get_y_with_r4>: get_y_with_r4(): 20: e52d4004 push {r4} @ (str r4, [sp, #-4]!) 24: e59f0004 ldr r0, [pc, #4] @ 30 <get_y_with_r4+0x10> 28: e49d4004 pop {r4} @ (ldr r4, [sp], #4) 2c: e12fff1e bx lr 30: 00000000 .word 0x00000000 30: R_ARM_ABS32 .bss Note that the number of instructions hasn't changed; all that has changed is whether we save and restore one or two registers in the function prologue and epilogue. PR lib/57638: thread local storage broken on evbarm (armv5) diff -r a8ca1f8a2394 -r e31559a47a90 external/gpl3/gcc/dist/gcc/config/arm/= arm.cc --- a/external/gpl3/gcc/dist/gcc/config/arm/arm.cc Fri Jul 24 03:41:48 2026= +0000 +++ b/external/gpl3/gcc/dist/gcc/config/arm/arm.cc Fri Jul 24 15:11:45 2026= +0000 @@ -23156,7 +23156,16 @@ arm_compute_frame_layout (void) if (crtl->is_leaf && frame_size =3D=3D 0 /* However if it calls alloca(), we have a dynamically allocated block of BIGGEST_ALIGNMENT on stack, so still do stack alignment. */ - && ! cfun->calls_alloca) + && ! cfun->calls_alloca + /* If queries to the thread pointer go through __aeabi_read_tp, + we must ensure the stack has correct alignment even though we + think of this as a leaf routine. Even if __aeabi_read_tp + itself doesn't use the stack, resolving the symbol may take a + detour through a procedure call to the dynamic linker. We + should really enforce alignment only if the procedure actually + uses __aeabi_read_tp (load_tp_soft*) but I don't know how to + query that here. */ + && !TARGET_SOFT_TP) { offsets->outgoing_args =3D offsets->soft_frame; offsets->locals_base =3D offsets->soft_frame; --=_zuuYtksK0C8RJaF9J8AwVVP/KZSlw/L+--