From nobody Fri Dec 19 19:00:42 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7D865C5479D for ; Mon, 9 Jan 2023 08:46:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236810AbjAIIqD (ORCPT ); Mon, 9 Jan 2023 03:46:03 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54504 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236852AbjAIIn6 (ORCPT ); Mon, 9 Jan 2023 03:43:58 -0500 Received: from 1wt.eu (wtarreau.pck.nerim.net [62.212.114.60]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id 6342414013 for ; Mon, 9 Jan 2023 00:42:42 -0800 (PST) Received: (from willy@localhost) by pcw.home.local (8.15.2/8.15.2/Submit) id 3098gCUY027422; Mon, 9 Jan 2023 09:42:12 +0100 From: Willy Tarreau To: "Paul E. McKenney" Cc: linux-kernel@vger.kernel.org, Willy Tarreau Subject: [PATCH 03/22] tools/nolibc: support thumb mode with frame pointers on ARM Date: Mon, 9 Jan 2023 09:41:49 +0100 Message-Id: <20230109084208.27355-4-w@1wt.eu> X-Mailer: git-send-email 2.17.5 In-Reply-To: <20230109084208.27355-1-w@1wt.eu> References: <20230109084208.27355-1-w@1wt.eu> Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" In Thumb mode, register r7 is normally used to store the frame pointer. By default when optimizing at -Os there's no frame pointer so this works fine. But if no optimization is set, then build errors occur, indicating that r7 cannot not be used. It's difficult to cheat because it's the compiler that is complaining, not the assembler, so it's not even possible to report that the register was clobbered. The solution consists in saving and restoring r7 around the syscall, but this slightly inflates the code. The syscall number is passed via r6 which is never used by syscalls. The current patch adds a few macroes which do that only in Thumb mode, and which continue to directly assign the syscall number to register r7 in ARM mode. Now this always builds and works for all modes (tested on Arm, Thumbv1, Thumbv2 modes, at -Os, -O0, -O0 -fomit-frame-pointer). The code is very slightly inflated in thumb-mode without frame-pointers compared to previously (e.g. 7928 vs 7864 bytes for nolibc-test) but at least it's always operational. And it's possible to disable this mechanism by setting NOLIBC_OMIT_FRAME_POINTER. Signed-off-by: Willy Tarreau --- tools/include/nolibc/arch-arm.h | 60 ++++++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 13 deletions(-) diff --git a/tools/include/nolibc/arch-arm.h b/tools/include/nolibc/arch-ar= m.h index e4ba77b0310f..ef94df2d93d5 100644 --- a/tools/include/nolibc/arch-arm.h +++ b/tools/include/nolibc/arch-arm.h @@ -70,20 +70,44 @@ struct sys_stat_struct { * don't have to experience issues with register constraints. * - the syscall number is always specified last in order to allow to fo= rce * some registers before (gcc refuses a %-register at the last positio= n). + * - in thumb mode without -fomit-frame-pointer, r7 is also used to stor= e the + * frame pointer, and we cannot directly assign it as a register varia= ble, + * nor can we clobber it. Instead we assign the r6 register and swap it + * with r7 before calling svc, and r6 is marked as clobbered. + * We're just using any regular register which we assign to r7 after s= aving + * it. * * Also, ARM supports the old_select syscall if newselect is not available */ #define __ARCH_WANT_SYS_OLD_SELECT =20 +#if (defined(__THUMBEB__) || defined(__THUMBEL__)) && \ + !defined(NOLIBC_OMIT_FRAME_POINTER) +/* swap r6,r7 needed in Thumb mode since we can't use nor clobber r7 */ +#define _NOLIBC_SYSCALL_REG "r6" +#define _NOLIBC_THUMB_SET_R7 "eor r7, r6\neor r6, r7\neor r7, r6\n" +#define _NOLIBC_THUMB_RESTORE_R7 "mov r7, r6\n" + +#else /* we're in ARM mode */ +/* in Arm mode we can directly use r7 */ +#define _NOLIBC_SYSCALL_REG "r7" +#define _NOLIBC_THUMB_SET_R7 "" +#define _NOLIBC_THUMB_RESTORE_R7 "" + +#endif /* end THUMB */ + #define my_syscall0(num) = \ ({ = \ - register long _num __asm__ ("r7") =3D (num); \ + register long _num __asm__(_NOLIBC_SYSCALL_REG) =3D (num); \ register long _arg1 __asm__ ("r0"); \ \ __asm__ volatile ( \ + _NOLIBC_THUMB_SET_R7 \ "svc #0\n" \ - : "=3Dr"(_arg1) \ - : "r"(_num) \ + _NOLIBC_THUMB_RESTORE_R7 \ + : "=3Dr"(_arg1), "=3Dr"(_num) \ + : "r"(_arg1), \ + "r"(_num) \ : "memory", "cc", "lr" \ ); \ _arg1; \ @@ -91,12 +115,14 @@ struct sys_stat_struct { =20 #define my_syscall1(num, arg1) = \ ({ = \ - register long _num __asm__ ("r7") =3D (num); \ + register long _num __asm__(_NOLIBC_SYSCALL_REG) =3D (num); \ register long _arg1 __asm__ ("r0") =3D (long)(arg1); \ \ __asm__ volatile ( \ + _NOLIBC_THUMB_SET_R7 \ "svc #0\n" \ - : "=3Dr"(_arg1) \ + _NOLIBC_THUMB_RESTORE_R7 \ + : "=3Dr"(_arg1), "=3Dr" (_num) \ : "r"(_arg1), \ "r"(_num) \ : "memory", "cc", "lr" \ @@ -106,13 +132,15 @@ struct sys_stat_struct { =20 #define my_syscall2(num, arg1, arg2) = \ ({ = \ - register long _num __asm__ ("r7") =3D (num); \ + register long _num __asm__(_NOLIBC_SYSCALL_REG) =3D (num); \ register long _arg1 __asm__ ("r0") =3D (long)(arg1); \ register long _arg2 __asm__ ("r1") =3D (long)(arg2); \ \ __asm__ volatile ( \ + _NOLIBC_THUMB_SET_R7 \ "svc #0\n" \ - : "=3Dr"(_arg1) \ + _NOLIBC_THUMB_RESTORE_R7 \ + : "=3Dr"(_arg1), "=3Dr" (_num) \ : "r"(_arg1), "r"(_arg2), \ "r"(_num) \ : "memory", "cc", "lr" \ @@ -122,14 +150,16 @@ struct sys_stat_struct { =20 #define my_syscall3(num, arg1, arg2, arg3) = \ ({ = \ - register long _num __asm__ ("r7") =3D (num); \ + register long _num __asm__(_NOLIBC_SYSCALL_REG) =3D (num); \ register long _arg1 __asm__ ("r0") =3D (long)(arg1); \ register long _arg2 __asm__ ("r1") =3D (long)(arg2); \ register long _arg3 __asm__ ("r2") =3D (long)(arg3); \ \ __asm__ volatile ( \ + _NOLIBC_THUMB_SET_R7 \ "svc #0\n" \ - : "=3Dr"(_arg1) \ + _NOLIBC_THUMB_RESTORE_R7 \ + : "=3Dr"(_arg1), "=3Dr" (_num) \ : "r"(_arg1), "r"(_arg2), "r"(_arg3), \ "r"(_num) \ : "memory", "cc", "lr" \ @@ -139,15 +169,17 @@ struct sys_stat_struct { =20 #define my_syscall4(num, arg1, arg2, arg3, arg4) = \ ({ = \ - register long _num __asm__ ("r7") =3D (num); \ + register long _num __asm__(_NOLIBC_SYSCALL_REG) =3D (num); \ register long _arg1 __asm__ ("r0") =3D (long)(arg1); \ register long _arg2 __asm__ ("r1") =3D (long)(arg2); \ register long _arg3 __asm__ ("r2") =3D (long)(arg3); \ register long _arg4 __asm__ ("r3") =3D (long)(arg4); \ \ __asm__ volatile ( \ + _NOLIBC_THUMB_SET_R7 \ "svc #0\n" \ - : "=3Dr"(_arg1) \ + _NOLIBC_THUMB_RESTORE_R7 \ + : "=3Dr"(_arg1), "=3Dr" (_num) \ : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), \ "r"(_num) \ : "memory", "cc", "lr" \ @@ -157,7 +189,7 @@ struct sys_stat_struct { =20 #define my_syscall5(num, arg1, arg2, arg3, arg4, arg5) = \ ({ = \ - register long _num __asm__ ("r7") =3D (num); \ + register long _num __asm__(_NOLIBC_SYSCALL_REG) =3D (num); \ register long _arg1 __asm__ ("r0") =3D (long)(arg1); \ register long _arg2 __asm__ ("r1") =3D (long)(arg2); \ register long _arg3 __asm__ ("r2") =3D (long)(arg3); \ @@ -165,8 +197,10 @@ struct sys_stat_struct { register long _arg5 __asm__ ("r4") =3D (long)(arg5); \ \ __asm__ volatile ( \ + _NOLIBC_THUMB_SET_R7 \ "svc #0\n" \ - : "=3Dr" (_arg1) \ + _NOLIBC_THUMB_RESTORE_R7 \ + : "=3Dr"(_arg1), "=3Dr" (_num) \ : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), "r"(_arg5), \ "r"(_num) \ : "memory", "cc", "lr" \ --=20 2.17.5