Skip to content

arch/arm: Tell the compiler which registers a system call changes. - #20873

Merged
acassis merged 2 commits into
apache:masterfrom
casaroli:arm-syscall-clobbers
Oct 23, 2026
Merged

acassis merged 2 commits into
apache:masterfrom
casaroli:arm-syscall-clobbers

Conversation

@casaroli

@casaroli casaroli commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Summary

A system call returns from dispatch_syscall() with R1-R3 and R12 of the kernel (R2 holds the return value). The sys_call macros of arch/arm/include/syscall.h gave R1-R3 to the compiler as inputs only and did not clobber R12, so the compiler could keep a value in them across the SVC.

A function with two system calls then passes a wrong parameter to the second one. On qemu-armv7a:knsh, sched_getparam(0) followed by sched_getscheduler(0) gives 0 and -1 (ESRCH). With CONFIG_LTO_FULL, posix_spawnattr_init() inlines exactly these two calls, so no program starts.

The GCC form now gives R0-R3 as in and out operands where they carry a parameter and clobbers the others and R12. The GHS form clobbers them too.

A second commit keeps the stub table in an LTO link. On armv7-a, armv7-r and armv8-r, dispatch_syscall() reads g_stublookup only in assembly, which LTO does not see, so the link fails with undefined reference to 'g_stublookup'. The assembly now gets the table as an "i" operand, so the compiler sees the reference.

Impact

arm protected and kernel builds (the code that calls the sys_call macros). The compiler saves values that it keeps across a system call; no other change.

Testing

qemu-armv7a:knsh (kernel build, Arm GNU 15.3, QEMU), a program that calls sched_getparam(0) and sched_getscheduler(0) with the macros in one function, then ostest:

master this PR
two calls getparam 0 getscheduler -1: FAIL PASS
ostest Exiting with status 0

With #20846 and CONFIG_LTO_FULL, qemu-armv7a:knsh links, the same program passes, and ostest exits with status 0.

qemu-armv7r:pnsh (protected build, armv7-r) builds; upstream QEMU cannot run it.

mps2-an500:knsh (protected build, armv7-m, QEMU): the same program passes. tools/checkpatch.sh -c -u -m -g passes. The GHS form is not built here.

@github-actions github-actions Bot added Arch: arm Issues related to ARM (32-bit) architecture Size: M The size of the change in this PR is medium labels Oct 21, 2026
@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

MemBrowse Memory Report · Explore what changed →

No memory changes detected for:


Catch memory regressions in CI with MemBrowse. Free for open source.

Comment thread arch/arm/include/syscall.h Outdated
Comment thread arch/arm/src/armv7-a/arm_syscall.c Outdated
A system call returns from dispatch_syscall() with R1-R3 and R12 of the
kernel: R2 holds the return value, and the stub function may change
R1, R3 and R12.  The sys_call macros gave R1-R3 to the compiler as
inputs only and did not clobber R12, so the compiler kept values in
them across the SVC.  With CONFIG_LTO_FULL, posix_spawnattr_init()
inlines two system calls and gives the second one the pid that it set
for the first one in R1; sched_getscheduler() then gets another pid and
fails with ESRCH, and no program starts on qemu-armv7a:knsh.

The same happens without LTO when one function makes two system calls
with the macros: on qemu-armv7a:knsh, sched_getparam(0) and then
sched_getscheduler(0) give 0 and -1 (ESRCH).

The GCC form now gives R0-R3 as in and out operands where they carry a
parameter and clobbers the others and R12, and the GHS form clobbers
them too.

Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
On armv7-a, armv7-r and armv8-r, dispatch_syscall() reads g_stublookup
only in its inline assembly.  LTO does not see that reference, so with
CONFIG_LTO_FULL the link of a protected or kernel build does not take
syscall_stublookup.o from libstubs.a, or makes the table local:

  arm_syscall.c:144:(.text.dispatch_syscall+0x24): undefined reference
  to `g_stublookup'

The assembly now gets the table as an "i" operand.  The compiler then
sees the reference: the link takes the table, and the assembly uses the
name that LTO gives to it.

Assisted-by: Claude Code:claude-opus-5-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
@casaroli
casaroli force-pushed the arm-syscall-clobbers branch from 69fec63 to be31bce Compare October 22, 2026 07:33
@acassis
acassis merged commit c188d55 into apache:master Oct 23, 2026
38 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Arch: arm Issues related to ARM (32-bit) architecture Size: M The size of the change in this PR is medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants