[libc-commits] [libc] Force to inline syscall_impl on all platforms (PR #186849)

Jakob Koschel via libc-commits libc-commits at lists.llvm.org
Tue Mar 24 03:57:26 PDT 2026


https://github.com/jakos-sec updated https://github.com/llvm/llvm-project/pull/186849

>From 3f6f48c19cedb60c40bb38dd2b953fef24441e29 Mon Sep 17 00:00:00 2001
From: Jakob Koschel <jakobkoschel at google.com>
Date: Mon, 16 Mar 2026 17:35:25 +0000
Subject: [PATCH 1/2] Force to inline syscall_impl on all platforms

With currently only LIBC_INLINE, we just hint the compiler to inline
the function which however in practice is not always the case.

Since we added `[[gnu::always_inline]]` on linux/x86_64 it makes sense
to do it on all platforms consistently.
---
 .../__support/OSUtil/darwin/aarch64/syscall.h | 25 +++++++++++--------
 .../__support/OSUtil/linux/aarch64/syscall.h  | 25 +++++++++++--------
 libc/src/__support/OSUtil/linux/arm/syscall.h | 25 +++++++++++--------
 .../src/__support/OSUtil/linux/i386/syscall.h | 24 ++++++++++--------
 .../__support/OSUtil/linux/riscv/syscall.h    | 25 +++++++++++--------
 5 files changed, 74 insertions(+), 50 deletions(-)

diff --git a/libc/src/__support/OSUtil/darwin/aarch64/syscall.h b/libc/src/__support/OSUtil/darwin/aarch64/syscall.h
index dc98c07a8ba33..8e273f487fd75 100644
--- a/libc/src/__support/OSUtil/darwin/aarch64/syscall.h
+++ b/libc/src/__support/OSUtil/darwin/aarch64/syscall.h
@@ -47,46 +47,51 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-LIBC_INLINE long syscall_impl(long number) {
+[[gnu::always_inline]] LIBC_INLINE long syscall_impl(long number) {
   REGISTER_DECL_0;
   SYSCALL_INSTR(REGISTER_CONSTRAINT_0);
   return x0;
 }
 
-LIBC_INLINE long syscall_impl(long number, long arg1) {
+[[gnu::always_inline]] LIBC_INLINE long syscall_impl(long number, long arg1) {
   REGISTER_DECL_1;
   SYSCALL_INSTR(REGISTER_CONSTRAINT_1);
   return x0;
 }
 
-LIBC_INLINE long syscall_impl(long number, long arg1, long arg2) {
+[[gnu::always_inline]] LIBC_INLINE long syscall_impl(long number, long arg1,
+                                                     long arg2) {
   REGISTER_DECL_2;
   SYSCALL_INSTR(REGISTER_CONSTRAINT_2);
   return x0;
 }
 
-LIBC_INLINE long syscall_impl(long number, long arg1, long arg2, long arg3) {
+[[gnu::always_inline]] LIBC_INLINE long syscall_impl(long number, long arg1,
+                                                     long arg2, long arg3) {
   REGISTER_DECL_3;
   SYSCALL_INSTR(REGISTER_CONSTRAINT_3);
   return x0;
 }
 
-LIBC_INLINE long syscall_impl(long number, long arg1, long arg2, long arg3,
-                              long arg4) {
+[[gnu::always_inline]] LIBC_INLINE long
+syscall_impl(long number, long arg1, long arg2, long arg3, long arg4) {
   REGISTER_DECL_4;
   SYSCALL_INSTR(REGISTER_CONSTRAINT_4);
   return x0;
 }
 
-LIBC_INLINE long syscall_impl(long number, long arg1, long arg2, long arg3,
-                              long arg4, long arg5) {
+[[gnu::always_inline]] LIBC_INLINE long syscall_impl(long number, long arg1,
+                                                     long arg2, long arg3,
+                                                     long arg4, long arg5) {
   REGISTER_DECL_5;
   SYSCALL_INSTR(REGISTER_CONSTRAINT_5);
   return x0;
 }
 
-LIBC_INLINE long syscall_impl(long number, long arg1, long arg2, long arg3,
-                              long arg4, long arg5, long arg6) {
+[[gnu::always_inline]] LIBC_INLINE long syscall_impl(long number, long arg1,
+                                                     long arg2, long arg3,
+                                                     long arg4, long arg5,
+                                                     long arg6) {
   REGISTER_DECL_6;
   SYSCALL_INSTR(REGISTER_CONSTRAINT_6);
   return x0;
diff --git a/libc/src/__support/OSUtil/linux/aarch64/syscall.h b/libc/src/__support/OSUtil/linux/aarch64/syscall.h
index f28392de5dced..c211c0e4cf11a 100644
--- a/libc/src/__support/OSUtil/linux/aarch64/syscall.h
+++ b/libc/src/__support/OSUtil/linux/aarch64/syscall.h
@@ -45,46 +45,51 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-LIBC_INLINE long syscall_impl(long number) {
+[[gnu::always_inline]] LIBC_INLINE long syscall_impl(long number) {
   REGISTER_DECL_0;
   SYSCALL_INSTR(REGISTER_CONSTRAINT_0);
   return x0;
 }
 
-LIBC_INLINE long syscall_impl(long number, long arg1) {
+[[gnu::always_inline]] LIBC_INLINE long syscall_impl(long number, long arg1) {
   REGISTER_DECL_1;
   SYSCALL_INSTR(REGISTER_CONSTRAINT_1);
   return x0;
 }
 
-LIBC_INLINE long syscall_impl(long number, long arg1, long arg2) {
+[[gnu::always_inline]] LIBC_INLINE long syscall_impl(long number, long arg1,
+                                                     long arg2) {
   REGISTER_DECL_2;
   SYSCALL_INSTR(REGISTER_CONSTRAINT_2);
   return x0;
 }
 
-LIBC_INLINE long syscall_impl(long number, long arg1, long arg2, long arg3) {
+[[gnu::always_inline]] LIBC_INLINE long syscall_impl(long number, long arg1,
+                                                     long arg2, long arg3) {
   REGISTER_DECL_3;
   SYSCALL_INSTR(REGISTER_CONSTRAINT_3);
   return x0;
 }
 
-LIBC_INLINE long syscall_impl(long number, long arg1, long arg2, long arg3,
-                              long arg4) {
+[[gnu::always_inline]] LIBC_INLINE long
+syscall_impl(long number, long arg1, long arg2, long arg3, long arg4) {
   REGISTER_DECL_4;
   SYSCALL_INSTR(REGISTER_CONSTRAINT_4);
   return x0;
 }
 
-LIBC_INLINE long syscall_impl(long number, long arg1, long arg2, long arg3,
-                              long arg4, long arg5) {
+[[gnu::always_inline]] LIBC_INLINE long syscall_impl(long number, long arg1,
+                                                     long arg2, long arg3,
+                                                     long arg4, long arg5) {
   REGISTER_DECL_5;
   SYSCALL_INSTR(REGISTER_CONSTRAINT_5);
   return x0;
 }
 
-LIBC_INLINE long syscall_impl(long number, long arg1, long arg2, long arg3,
-                              long arg4, long arg5, long arg6) {
+[[gnu::always_inline]] LIBC_INLINE long syscall_impl(long number, long arg1,
+                                                     long arg2, long arg3,
+                                                     long arg4, long arg5,
+                                                     long arg6) {
   REGISTER_DECL_6;
   SYSCALL_INSTR(REGISTER_CONSTRAINT_6);
   return x0;
diff --git a/libc/src/__support/OSUtil/linux/arm/syscall.h b/libc/src/__support/OSUtil/linux/arm/syscall.h
index d1058c84281fe..252c8c80249b3 100644
--- a/libc/src/__support/OSUtil/linux/arm/syscall.h
+++ b/libc/src/__support/OSUtil/linux/arm/syscall.h
@@ -63,46 +63,51 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-LIBC_INLINE long syscall_impl(long number) {
+[[gnu::always_inline]] LIBC_INLINE long syscall_impl(long number) {
   REGISTER_DECL_0;
   SYSCALL_INSTR(REGISTER_CONSTRAINT_0);
   return r0;
 }
 
-LIBC_INLINE long syscall_impl(long number, long arg1) {
+[[gnu::always_inline]] LIBC_INLINE long syscall_impl(long number, long arg1) {
   REGISTER_DECL_1;
   SYSCALL_INSTR(REGISTER_CONSTRAINT_1);
   return r0;
 }
 
-LIBC_INLINE long syscall_impl(long number, long arg1, long arg2) {
+[[gnu::always_inline]] LIBC_INLINE long syscall_impl(long number, long arg1,
+                                                     long arg2) {
   REGISTER_DECL_2;
   SYSCALL_INSTR(REGISTER_CONSTRAINT_2);
   return r0;
 }
 
-LIBC_INLINE long syscall_impl(long number, long arg1, long arg2, long arg3) {
+[[gnu::always_inline]] LIBC_INLINE long syscall_impl(long number, long arg1,
+                                                     long arg2, long arg3) {
   REGISTER_DECL_3;
   SYSCALL_INSTR(REGISTER_CONSTRAINT_3);
   return r0;
 }
 
-LIBC_INLINE long syscall_impl(long number, long arg1, long arg2, long arg3,
-                              long arg4) {
+[[gnu::always_inline]] LIBC_INLINE long
+syscall_impl(long number, long arg1, long arg2, long arg3, long arg4) {
   REGISTER_DECL_4;
   SYSCALL_INSTR(REGISTER_CONSTRAINT_4);
   return r0;
 }
 
-LIBC_INLINE long syscall_impl(long number, long arg1, long arg2, long arg3,
-                              long arg4, long arg5) {
+[[gnu::always_inline]] LIBC_INLINE long syscall_impl(long number, long arg1,
+                                                     long arg2, long arg3,
+                                                     long arg4, long arg5) {
   REGISTER_DECL_5;
   SYSCALL_INSTR(REGISTER_CONSTRAINT_5);
   return r0;
 }
 
-LIBC_INLINE long syscall_impl(long number, long arg1, long arg2, long arg3,
-                              long arg4, long arg5, long arg6) {
+[[gnu::always_inline]] LIBC_INLINE long syscall_impl(long number, long arg1,
+                                                     long arg2, long arg3,
+                                                     long arg4, long arg5,
+                                                     long arg6) {
   REGISTER_DECL_6;
   SYSCALL_INSTR(REGISTER_CONSTRAINT_6);
   return r0;
diff --git a/libc/src/__support/OSUtil/linux/i386/syscall.h b/libc/src/__support/OSUtil/linux/i386/syscall.h
index 88d7f2fb2c49f..76fb83cbb35a8 100644
--- a/libc/src/__support/OSUtil/linux/i386/syscall.h
+++ b/libc/src/__support/OSUtil/linux/i386/syscall.h
@@ -14,19 +14,20 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-LIBC_INLINE long syscall_impl(long num) {
+[[gnu::always_inline]] LIBC_INLINE long syscall_impl(long num) {
   long ret;
   LIBC_INLINE_ASM("int $128" : "=a"(ret) : "a"(num) : "memory");
   return ret;
 }
 
-LIBC_INLINE long syscall_impl(long num, long arg1) {
+[[gnu::always_inline]] LIBC_INLINE long syscall_impl(long num, long arg1) {
   long ret;
   LIBC_INLINE_ASM("int $128" : "=a"(ret) : "a"(num), "b"(arg1) : "memory");
   return ret;
 }
 
-LIBC_INLINE long syscall_impl(long num, long arg1, long arg2) {
+[[gnu::always_inline]] LIBC_INLINE long syscall_impl(long num, long arg1,
+                                                     long arg2) {
   long ret;
   LIBC_INLINE_ASM("int $128"
                   : "=a"(ret)
@@ -35,7 +36,8 @@ LIBC_INLINE long syscall_impl(long num, long arg1, long arg2) {
   return ret;
 }
 
-LIBC_INLINE long syscall_impl(long num, long arg1, long arg2, long arg3) {
+[[gnu::always_inline]] LIBC_INLINE long syscall_impl(long num, long arg1,
+                                                     long arg2, long arg3) {
   long ret;
   LIBC_INLINE_ASM("int $128"
                   : "=a"(ret)
@@ -44,8 +46,8 @@ LIBC_INLINE long syscall_impl(long num, long arg1, long arg2, long arg3) {
   return ret;
 }
 
-LIBC_INLINE long syscall_impl(long num, long arg1, long arg2, long arg3,
-                              long arg4) {
+[[gnu::always_inline]] LIBC_INLINE long
+syscall_impl(long num, long arg1, long arg2, long arg3, long arg4) {
   long ret;
   LIBC_INLINE_ASM("int $128"
                   : "=a"(ret)
@@ -54,8 +56,8 @@ LIBC_INLINE long syscall_impl(long num, long arg1, long arg2, long arg3,
   return ret;
 }
 
-LIBC_INLINE long syscall_impl(long num, long arg1, long arg2, long arg3,
-                              long arg4, long arg5) {
+[[gnu::always_inline]] LIBC_INLINE long
+syscall_impl(long num, long arg1, long arg2, long arg3, long arg4, long arg5) {
   long ret;
   LIBC_INLINE_ASM("int $128"
                   : "=a"(ret)
@@ -65,8 +67,10 @@ LIBC_INLINE long syscall_impl(long num, long arg1, long arg2, long arg3,
   return ret;
 }
 
-LIBC_INLINE long syscall_impl(long num, long arg1, long arg2, long arg3,
-                              long arg4, long arg5, long arg6) {
+[[gnu::always_inline]] LIBC_INLINE long syscall_impl(long num, long arg1,
+                                                     long arg2, long arg3,
+                                                     long arg4, long arg5,
+                                                     long arg6) {
   long ret;
   LIBC_INLINE_ASM(R"(
     push %[arg6]
diff --git a/libc/src/__support/OSUtil/linux/riscv/syscall.h b/libc/src/__support/OSUtil/linux/riscv/syscall.h
index e460e9bb56e67..1fabc400b1c70 100644
--- a/libc/src/__support/OSUtil/linux/riscv/syscall.h
+++ b/libc/src/__support/OSUtil/linux/riscv/syscall.h
@@ -45,46 +45,51 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
-LIBC_INLINE long syscall_impl(long number) {
+[[gnu::always_inline]] LIBC_INLINE long syscall_impl(long number) {
   REGISTER_DECL_0;
   SYSCALL_INSTR(REGISTER_CONSTRAINT_0);
   return a0;
 }
 
-LIBC_INLINE long syscall_impl(long number, long arg1) {
+[[gnu::always_inline]] LIBC_INLINE long syscall_impl(long number, long arg1) {
   REGISTER_DECL_1;
   SYSCALL_INSTR(REGISTER_CONSTRAINT_1);
   return a0;
 }
 
-LIBC_INLINE long syscall_impl(long number, long arg1, long arg2) {
+[[gnu::always_inline]] LIBC_INLINE long syscall_impl(long number, long arg1,
+                                                     long arg2) {
   REGISTER_DECL_2;
   SYSCALL_INSTR(REGISTER_CONSTRAINT_2);
   return a0;
 }
 
-LIBC_INLINE long syscall_impl(long number, long arg1, long arg2, long arg3) {
+[[gnu::always_inline]] LIBC_INLINE long syscall_impl(long number, long arg1,
+                                                     long arg2, long arg3) {
   REGISTER_DECL_3;
   SYSCALL_INSTR(REGISTER_CONSTRAINT_3);
   return a0;
 }
 
-LIBC_INLINE long syscall_impl(long number, long arg1, long arg2, long arg3,
-                              long arg4) {
+[[gnu::always_inline]] LIBC_INLINE long
+syscall_impl(long number, long arg1, long arg2, long arg3, long arg4) {
   REGISTER_DECL_4;
   SYSCALL_INSTR(REGISTER_CONSTRAINT_4);
   return a0;
 }
 
-LIBC_INLINE long syscall_impl(long number, long arg1, long arg2, long arg3,
-                              long arg4, long arg5) {
+[[gnu::always_inline]] LIBC_INLINE long syscall_impl(long number, long arg1,
+                                                     long arg2, long arg3,
+                                                     long arg4, long arg5) {
   REGISTER_DECL_5;
   SYSCALL_INSTR(REGISTER_CONSTRAINT_5);
   return a0;
 }
 
-LIBC_INLINE long syscall_impl(long number, long arg1, long arg2, long arg3,
-                              long arg4, long arg5, long arg6) {
+[[gnu::always_inline]] LIBC_INLINE long syscall_impl(long number, long arg1,
+                                                     long arg2, long arg3,
+                                                     long arg4, long arg5,
+                                                     long arg6) {
   REGISTER_DECL_6;
   SYSCALL_INSTR(REGISTER_CONSTRAINT_6);
   return a0;

>From 3d6e076e019e818272881099af4ab9c069359344 Mon Sep 17 00:00:00 2001
From: Jakob Koschel <jakobkoschel at google.com>
Date: Tue, 24 Mar 2026 10:57:05 +0000
Subject: [PATCH 2/2] add comment for reason of gnu::always_inline on
 syscall_impl

---
 libc/src/__support/OSUtil/linux/x86_64/syscall.h | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libc/src/__support/OSUtil/linux/x86_64/syscall.h b/libc/src/__support/OSUtil/linux/x86_64/syscall.h
index f069d46185d0f..25ff5faf081b1 100644
--- a/libc/src/__support/OSUtil/linux/x86_64/syscall.h
+++ b/libc/src/__support/OSUtil/linux/x86_64/syscall.h
@@ -16,6 +16,11 @@
 
 namespace LIBC_NAMESPACE_DECL {
 
+// In order for SHSTK (CET ShadowStack) to work, we are required to force
+// inlining the syscall_impl, since we cannot return from an untracked call
+// after enabling support throught the system call.
+// For consistency, we do this consistently on all platforms, but can split it
+// into force-inlined and regular inlined functions in the future if necessary.
 [[gnu::always_inline]] LIBC_INLINE long syscall_impl(long __number) {
   long retcode;
   LIBC_INLINE_ASM("syscall"



More information about the libc-commits mailing list