NetBSD-Bugs archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: lib/57638: thread local storage broken on evbarm (armv5)



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?
# HG changeset patch
# User Taylor R Campbell <riastradh%NetBSD.org@localhost>
# 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=soft, ensure stack alignment even in leaves.

The option -mtp=soft, 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>=6, 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>=6, 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 == 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 = offsets->soft_frame;
       offsets->locals_base = offsets->soft_frame;


Home | Main Index | Thread Index | Old Index