From 18771b9329c590b97e68a7c7a264d3df4c3e0ad1 Mon Sep 17 00:00:00 2001 From: Paul Floyd Date: Thu, 22 Feb 2024 16:56:22 +0100 Subject: [PATCH] Bug 369723 - __builtin_longjmp not supported in clang/llvm on Android arm64 target Made the functions out-of-line, more like other platforms. --- NEWS | 1 + coregrind/m_libcsetjmp.c | 106 ++++++++++++++++++++++++++++++++++++++++++ include/pub_tool_libcsetjmp.h | 8 ++++ 3 files changed, 115 insertions(+) diff --git a/NEWS b/NEWS index f0ec723c2..0aa3d0eb3 100644 --- a/NEWS +++ b/NEWS @@ -26,6 +26,7 @@ are not entered into bugzilla tend to get forgotten about or ignored. 283429 ARM leak checking needs CLEAR_CALLER_SAVED_REGS 281059 Cannot connect to Oracle using valgrind +369723 __builtin_longjmp not supported in clang/llvm on Android arm64 target 390269 unhandled amd64-darwin syscall: unix:464 (openat_nocancel) 401284 False positive "Source and destination overlap in strncat" 428364 Signals inside io_uring_enter not handled diff --git a/coregrind/m_libcsetjmp.c b/coregrind/m_libcsetjmp.c index f53a22352..aa1749032 100644 --- a/coregrind/m_libcsetjmp.c +++ b/coregrind/m_libcsetjmp.c @@ -781,6 +781,112 @@ __asm__( ); #endif /* VGP_s390x_linux */ +#if defined(__clang__) && (defined(VGP_arm64_linux) || defined(VGP_arm64_freebsd)) + +// __builtin_setjmp is not implemented by the standard C library +// used on Android in current llvm-based toolchains as of NDK r19. +// +// Here is a custom implementation of Valgrind's "minimal" setjmp +// interface. Per the comment at the top of this file, we only need +// to save integer registers. +// +// Per the Procedure Call Standard for the ARM 64-bit Architecture +// document, +// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf +// Section 5.1.1. General-purpose registers: +// > +// > A subroutine invocation must preserve the contents of the +// > registers r19-r29 and SP." +// +// We also need to save and restore r30, the link register, i.e. +// the default destination that a 'ret' instruction branches to. +// +// Note that this document is phrased in terms of 'r' registers +// (e.g. "r30") because it aims to be agnostic as to A64 vs A32 +// instruction sets, but here we are targeting the A64 instruction +// set, so we are dealing with 'x' registers. + + + +__attribute__((returns_twice)) +UWord VG_MINIMAL_SETJMP(VG_MINIMAL_JMP_BUF(_env)) +{ + asm volatile( + // x9 is the first of the regular temporary registers + // per the above-mentioned Procedule Call Standard document. + // Use it as temporary to hold the value of SP, since str does + // not accept SP as operand. + " mov x9, sp \n" + // Store the general-purpose registers that we need to save + // per the above discussion. + // Note that x30 is the link register. + " stp x19, x20, [%[_env], 0] \n" + " stp x21, x22, [%[_env], 0x10] \n" + " stp x23, x24, [%[_env], 0x20] \n" + " stp x25, x26, [%[_env], 0x30] \n" + " stp x27, x28, [%[_env], 0x40] \n" + " stp x29, x30, [%[_env], 0x50] \n" + // Store the value of SP. + " str x9, [%[_env], 0x60] \n" + : + // No outputs + : + // Inputs + [_env]"r"(_env) + : + // Clobbers. + // We have used x9 as a temporary + "x9", + // We have written to memory locations + "memory"); + + // Direct invokation of setjmp always returns 0. + // The pseudo returning of the value 1 as a return from longjmp + // is implemented in longjmp. + return 0; +} + +__attribute__((noreturn)) +void VG_MINIMAL_LONGJMP(VG_MINIMAL_JMP_BUF(_env)) +{ + asm volatile( + // Loads to match the stores in the above setjmp implementation. + " ldp x19, x20, [%[_env], 0] \n" + " ldp x21, x22, [%[_env], 0x10] \n" + " ldp x23, x24, [%[_env], 0x20] \n" + " ldp x25, x26, [%[_env], 0x30] \n" + " ldp x27, x28, [%[_env], 0x40] \n" + " ldp x29, x30, [%[_env], 0x50] \n" + " ldr x9, [%[_env], 0x60] \n" + " mov sp, x9 \n" + // return as setjmp for the second time, returning 1. + // Since we have loaded the link register x30 from the saved buffer + // that was set by the above setjmp implementation, the 'ret' instruction + // here is going to return to where setjmp was called. + // Per the setjmp/longjmp contract, that pseudo second returning + // of setjmp should return the value 1. + // x0 is holds the integer return value. + " mov x0, 1 \n" + " ret \n" + : + // No outputs + : + // Inputs + [_env]"r"(_env) + : + // Clobbers. + // We have used x9 as a temporary + "x9"); + + // This statement is unreachable because the above asm statement + // unconditionally does a 'ret' instruction. The purpose of this + // statement is to silence clang warnings about this function returning + // while having the 'noreturn' attribute. + __builtin_unreachable(); +} + +#endif + /*--------------------------------------------------------------------*/ /*--- end ---*/ /*--------------------------------------------------------------------*/ diff --git a/include/pub_tool_libcsetjmp.h b/include/pub_tool_libcsetjmp.h index a3a386f80..ee9e1dc8b 100644 --- a/include/pub_tool_libcsetjmp.h +++ b/include/pub_tool_libcsetjmp.h @@ -134,6 +134,14 @@ UWord VG_MINIMAL_SETJMP(VG_MINIMAL_JMP_BUF(_env)); __attribute__((noreturn)) void VG_MINIMAL_LONGJMP(VG_MINIMAL_JMP_BUF(_env)); +#elif defined(__clang__) && (defined(VGP_arm64_linux) || defined(VGP_arm64_freebsd)) + +#define VG_MINIMAL_JMP_BUF(_name) UWord _name [13] +__attribute__((returns_twice)) +UWord VG_MINIMAL_SETJMP(VG_MINIMAL_JMP_BUF(_env)); +__attribute__((noreturn)) +void VG_MINIMAL_LONGJMP(VG_MINIMAL_JMP_BUF(_env)); + #else /* The default implementation. */ -- 2.11.4.GIT