[PATCH] libc: arm: fix setjmp abi non-conformance
Richard Earnshaw
Richard.Earnshaw@foss.arm.com
Tue Dec 13 15:54:34 GMT 2022
On 13/12/2022 14:51, Victor L. Do Nascimento wrote:
> As per the arm Procedure Call Standard for the Arm Architecture
> section 6.1.2 [1], VFP registers s16-s31 (d8-d15, q4-q7) must be
> preserved across subroutine calls.
>
> The current setjmp/longjmp implementations preserve only the core
> registers, with the jump buffer size too small to store the required
> co-processor registers.
>
> In accordance with the C Library ABI for the Arm Architecture
> section 6.11 [2], this patch sets _JBTYPE to long long adjusting
> _JBLEN to 20.
>
> It also emits vfp load/store instructions depending on architectural
> support, predicated at compile time on ACLE feature-test macros.
>
Pushed.
Would you mind writing a short entry for the top-level NEWS file, as
this is technically an ABI break. Just point out that the size and
alignment of jmp_buf has been changed to conform with the ABI and to fix
a bug with saving floating-point registers.
R.
> [1] https://github.com/ARM-software/abi-aa/blob/main/aapcs32/aapcs32.rst
> [2] https://github.com/ARM-software/abi-aa/blob/main/clibabi32/clibabi32.rst
> ---
> COPYING.NEWLIB | 2 +-
> newlib/libc/include/machine/setjmp.h | 8 ++-
> newlib/libc/machine/arm/setjmp.S | 74 +++++++++++++++-------------
> 3 files changed, 46 insertions(+), 38 deletions(-)
>
> diff --git a/COPYING.NEWLIB b/COPYING.NEWLIB
> index 2d1473639..d54ed293d 100644
> --- a/COPYING.NEWLIB
> +++ b/COPYING.NEWLIB
> @@ -762,7 +762,7 @@ SUCH DAMAGE.
>
> (35) - Arm Ltd
>
> - Copyright (c) 2009-2018 Arm Ltd
> + Copyright (c) 2009-2022 Arm Ltd
> All rights reserved.
>
> Redistribution and use in source and binary forms, with or without
> diff --git a/newlib/libc/include/machine/setjmp.h b/newlib/libc/include/machine/setjmp.h
> index 53878a03d..29b76cec1 100644
> --- a/newlib/libc/include/machine/setjmp.h
> +++ b/newlib/libc/include/machine/setjmp.h
> @@ -12,9 +12,13 @@ _BEGIN_STD_C
> #if defined(__arm__) || defined(__thumb__)
> /*
> * All callee preserved registers:
> - * v1 - v7, fp, ip, sp, lr, f4, f5, f6, f7
> + * core registers:
> + * r4 - r10, fp, sp, lr
> + * VFP registers (architectural support dependent):
> + * d8 - d15
> */
> -#define _JBLEN 23
> +#define _JBLEN 20
> +#define _JBTYPE long long
> #endif
>
> #if defined(__aarch64__)
> diff --git a/newlib/libc/machine/arm/setjmp.S b/newlib/libc/machine/arm/setjmp.S
> index 21d6ff9e7..4cf0a8e3f 100644
> --- a/newlib/libc/machine/arm/setjmp.S
> +++ b/newlib/libc/machine/arm/setjmp.S
> @@ -27,34 +27,34 @@
> The interworking scheme expects functions to use a BX instruction
> to return control to their parent. Since we need this code to work
> in both interworked and non-interworked environments as well as with
> - older processors which do not have the BX instruction we do the
> + older processors which do not have the BX instruction we do the
> following:
> Test the return address.
> If the bottom bit is clear perform an "old style" function exit.
> (We know that we are in ARM mode and returning to an ARM mode caller).
> Otherwise use the BX instruction to perform the function exit.
>
> - We know that we will never attempt to perform the BX instruction on
> - an older processor, because that kind of processor will never be
> - interworked, and a return address with the bottom bit set will never
> + We know that we will never attempt to perform the BX instruction on
> + an older processor, because that kind of processor will never be
> + interworked, and a return address with the bottom bit set will never
> be generated.
>
> In addition, we do not actually assemble the BX instruction as this would
> require us to tell the assembler that the processor is an ARM7TDMI and
> it would store this information in the binary. We want this binary to be
> able to be linked with binaries compiled for older processors however, so
> - we do not want such information stored there.
> + we do not want such information stored there.
>
> If we are running using the APCS-26 convention however, then we never
> - test the bottom bit, because this is part of the processor status.
> - Instead we just do a normal return, since we know that we cannot be
> + test the bottom bit, because this is part of the processor status.
> + Instead we just do a normal return, since we know that we cannot be
> returning to a Thumb caller - the Thumb does not support APCS-26.
> -
> - Function entry is much simpler. If we are compiling for the Thumb we
> +
> + Function entry is much simpler. If we are compiling for the Thumb we
> just switch into ARM mode and then drop through into the rest of the
> function. The function exit code will take care of the restore to
> Thumb mode.
> -
> +
> For Thumb-2 do everything in Thumb mode. */
>
> .syntax unified
> @@ -115,15 +115,15 @@ SYM (longjmp):
> #else
> #define RET tst lr, #1; \
> moveq pc, lr ; \
> -.word 0xe12fff1e /* bx lr */
> +.inst 0xe12fff1e /* bx lr */
> #endif
>
> #ifdef __thumb2__
> -.macro COND where when
> +.macro COND where when
> i\where \when
> .endm
> #else
> -.macro COND where when
> +.macro COND where when
> .endm
> #endif
>
> @@ -140,7 +140,7 @@ SYM (longjmp):
> .macro PROLOGUE name
> .code 16
> bx pc
> - nop
> + nop
> .code 32
> SYM (.arm_start_of.\name):
> .endm
> @@ -149,7 +149,7 @@ SYM (.arm_start_of.\name):
> .macro PROLOGUE name
> .endm
> #endif
> -
> +
> .macro FUNC_START name
> .text
> .align 2
> @@ -164,61 +164,65 @@ SYM (\name):
> RET
> SIZE (\name)
> .endm
> -
> +
> /* --------------------------------------------------------------------
> - int setjmp (jmp_buf);
> + int setjmp (jmp_buf);
> -------------------------------------------------------------------- */
> -
> +
> FUNC_START setjmp
>
> /* Save all the callee-preserved registers into the jump buffer. */
> #ifdef __thumb2__
> mov ip, sp
> - stmea a1!, { v1-v7, fp, ip, lr }
> + stmia r0!, { r4-r10, fp, ip, lr }
> #else
> - stmea a1!, { v1-v7, fp, ip, sp, lr }
> + stmia r0!, { r4-r10, fp, sp, lr }
> +#endif
> +#if defined __ARM_FP || defined __ARM_FEATURE_MVE
> + vstm r0, { d8-d15 }
> #endif
> -
> +
> #if 0 /* Simulator does not cope with FP instructions yet. */
> #ifndef __SOFTFP__
> /* Save the floating point registers. */
> sfmea f4, 4, [a1]
> #endif
> -#endif
> +#endif
> /* When setting up the jump buffer return 0. */
> - mov a1, #0
> + mov r0, #0
>
> FUNC_END setjmp
> -
> +
> /* --------------------------------------------------------------------
> volatile void longjmp (jmp_buf, int);
> -------------------------------------------------------------------- */
> -
> +
> FUNC_START longjmp
>
> /* If we have stack extension code it ought to be handled here. */
> -
> +
> /* Restore the registers, retrieving the state when setjmp() was called. */
> #ifdef __thumb2__
> - ldmfd a1!, { v1-v7, fp, ip, lr }
> + ldmia r0!, { r4-r10, fp, ip, lr }
> mov sp, ip
> #else
> - ldmfd a1!, { v1-v7, fp, ip, sp, lr }
> + ldmia r0!, { r4-r10, fp, sp, lr }
> +#endif
> +#if defined __ARM_FP || defined __ARM_FEATURE_MVE
> + vldm r0, { d8-d15 }
> #endif
> -
> +
> #if 0 /* Simulator does not cope with FP instructions yet. */
> #ifndef __SOFTFP__
> /* Restore floating point registers as well. */
> lfmfd f4, 4, [a1]
> #endif
> -#endif
> +#endif
> /* Put the return value into the integer result register.
> - But if it is zero then return 1 instead. */
> - movs a1, a2
> -#ifdef __thumb2__
> + But if it is zero then return 1 instead. */
> + movs r0, r1
> it eq
> -#endif
> - moveq a1, #1
> + moveq r0, #1
>
> FUNC_END longjmp
> #endif
More information about the Newlib
mailing list