I want to write simple and short assembly code in Arm Linux that wait forever.
label:
b label
That works, but takes lots of CPU time.
I thought about using nanosleep
and jump to label each X seconds, but how do I use it in assembly?
pause
syscall (suggested in comments) is not good because the process has a signal handler.
Is there simple way to do that?
Yes, you need a system call, and yes both your ideas are viable. (Although nanosleep
is limited to the max number of seconds a time_t
can represent. This may "only" be 2^31-1 on ARM Linux, which is famously about 68 years, the interval from 1970 until 32-bit time overflows in 2038)
However, Linux has a system call specifically for this, pause(2)
:
pause() causes the calling process (or thread) to sleep until a signal is delivered that either terminates the process or causes the invocation of a signal-catching function
If you have no signal handlers installed, pause can't return, you could only exit (still by hitting control-C, which delivers SIGINT), or by killing it (SIGTERM) or other normal ways.
If you do have a signal handler installed and want to keep pausing, then obviously you can just call it in a loop. (Probably best to do that instead of calling pause inside the signal handler instead of returning from it). You'll only use CPU time during that brief wakeup to run the signal handler and restart execution of the main thread, which will immediately call into the kernel for another pause.
sleep and nanosleep also return on handled signals, returning -EINTR
according to the man pages. (The libc wrapper functions might retry / restart the system call for you, but if you want to use raw kernel calls you'll have to do that yourself.)
As for how to invoke system calls, search stack overflow (e.g. google with site:stackoverflow.com
) for ARM system calls, or just google in general. Given the man page and the general method for mapping C args to registers (the calling convention), you can call anything. What is the interface for ARM system calls and where is it defined in the Linux kernel?
Pause takes no args, so you'll just need the call number (__NR_pause
) in the right register:
@ arm_pause.S - build with gcc -nostdlib arm_pause.S
#include <asm/unistd.h> // contains only C preprocessor macros, no C stuff.
.globl _start
_start:
mov r7, #__NR_pause
swi 0x0
b _start @ rerun if interrupted by a signal
(untested; I only have arm-none-eabi-gcc
installed, not arm-linux-eabi-gcc
. But confirmed that ARM Linux headers do have __NR_pause, and using inline asm to check that the syntax assembles: https://godbolt.org/z/PerGTx).
Of course there's no need to use asm for this, you could just as well have used while(1){ pause(); }
in C, although that would call the libc wrapper for the system call.