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)



The following reply was made to PR lib/57638; it has been noted by GNATS.

From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
To: Martin Husemann <martin%NetBSD.org@localhost>, Nick Hudson <skrll%NetBSD.org@localhost>,
	matthew green <mrg%NetBSD.org@localhost>
Cc: gnats-bugs%NetBSD.org@localhost, netbsd-bugs%NetBSD.org@localhost
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 <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=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+--
 



Home | Main Index | Thread Index | Old Index