I'm trying to implement a timer routine in 32-bit assembler on OS X Lion using sigaction() & setitimer() system calls. The idea is to set a timer using setitimer() & then have the generated alarm signal invoke a handler function previously setup through sigaction(). I have such a mechanism working on Linux, but cannot seem to get it working on OS X. I know the system call convention is different between OS X & Linux, and that OS X has a 16 byte alignment requirement. Despite compensating for these, I'm still not able to get it working (usually a "Bus error: 10" error). Thinking I did something wrong with the alignment, I wrote a simple C program that does what I want & then used clang 3.2 to generate the assembly code. Then I modified the machine-generated assembly by replacing the calls to sigaction() & setitimer() with the appropriate system & int $0x80 calls, as well as stack alignment instructions. The resulting program still doesn't work.
Here is the C program, sigaction.c, that I used to generate the assembly. Note that I commented out the printf & sleep stuff so the resulting assembly code would be easier to read:
//#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
struct sigaction action;
void handler(int arg) {
// printf("HERE!\n");
}
int main() {
action.__sigaction_u.__sa_handler = handler;
action.sa_mask = 0;
action.sa_flags = 0;
// printf("sigaction size: %d\n", sizeof(action));
int fd = sigaction(14, &action, 0);
struct itimerval timer;
timer.it_interval.tv_sec = 1;
timer.it_interval.tv_usec = 0;
timer.it_value.tv_sec = 1;
timer.it_value.tv_usec = 0;
// printf("itimerval size: %d\n", sizeof(timer));
fd = setitimer(0, &timer, 0);
while (1) {
// sleep(60);
}
return 0;
}
Here is the assembly code generated using "clang -arch i386 -S sigaction.c" on the above file:
.section __TEXT,__text,regular,pure_instructions
.globl _handler
.align 4, 0x90
_handler: ## @handler
## BB#0:
pushl %ebp
movl %esp, %ebp
pushl %eax
movl 8(%ebp), %eax
movl %eax, -4(%ebp)
addl $4, %esp
popl %ebp
ret
.globl _main
.align 4, 0x90
_main: ## @main
## BB#0:
pushl %ebp
movl %esp, %ebp
pushl %esi
subl $52, %esp
calll L1$pb
L1$pb:
popl %eax
movl $14, %ecx
movl L_action$non_lazy_ptr-L1$pb(%eax), %edx
movl $0, %esi
leal _handler-L1$pb(%eax), %eax
movl $0, -8(%ebp)
movl %eax, (%edx)
movl $0, 4(%edx)
movl $0, 8(%edx)
movl $14, (%esp)
movl %edx, 4(%esp)
movl $0, 8(%esp)
movl %esi, -36(%ebp) ## 4-byte Spill
movl %ecx, -40(%ebp) ## 4-byte Spill
calll _sigaction
movl $0, %ecx
leal -32(%ebp), %edx
movl %eax, -12(%ebp)
movl $1, -32(%ebp)
movl $0, -28(%ebp)
movl $1, -24(%ebp)
movl $0, -20(%ebp)
movl $0, (%esp)
movl %edx, 4(%esp)
movl $0, 8(%esp)
movl %ecx, -44(%ebp) ## 4-byte Spill
calll _setitimer
movl %eax, -12(%ebp)
LBB1_1: ## =>This Inner Loop Header: Depth=1
jmp LBB1_1
.comm _action,12,2 ## @action
.section __IMPORT,__pointers,non_lazy_symbol_pointers
L_action$non_lazy_ptr:
.indirect_symbol _action
.long 0
.subsections_via_symbols
If I compile the assembly code using "clang -arch i386 sigaction.s -o sigaction", debug it using lldb & place a breakpoint in the handler function, the handler function is indeed called every second. so I know the assembly code is correct (ditto for the C code).
Now if I replace the call to sigaction() with:
# calll _sigaction
movl $0x2e, %eax
subl $0x04, %esp
int $0x80
addl $0x04, %esp
and the call to setitimer() with:
# calll _setitimer
movl $0x53, %eax
subl $0x04, %esp
int $0x80
addl $0x04, %esp
the assembly code no longer works, and generates the same "Bus error: 10" that my hand-coded assembly code does.
I've tried removing the subl/addl instructions that I'm using to align the stack as well as changing the values to make sure the stack is aligned on 16-byte boundaries, but nothing seems to work. I either get the bus error, a segmentation fault, or the code just hangs without calling the handler function.
One thing I did notice during debugging is that the sigaction call appears to have a lengthy wrapper around the underlying system call. If you disassemble both functions from within lldb, you will see sigaction() has a lengthy wrapper but setitimer does not. Not sure this means anything, but perhaps the sigaction() wrapper is massaging the data before passing it along. I tried debugging that code, but haven't found anything definitive yet.
If anyone knows how to get the above assembly code working by replacing the sigaction() & setitimer() functions with the appropriate system calls, it would be greatly appreciated. I can then take those changes & apply them to my hand-coded routines.
Thanks.
Update: I stripped down my hand-written assembly code to a manageable size & was able to get it working using the sigaction() & setitimer() library calls, but still haven't figured out why the syscalls don't work. Here's the code (timer.s):
.globl _main
.data
.set ITIMER_REAL, 0x00
.set SIGALRM, 0x0e
.set SYS_SIGACTION, 0x2e
.set SYS_SETITIMER, 0x53
.set TRAP, 0x80
itimerval:
interval_tv_sec:
.long 0
interval_tv_usec:
.long 0
value_tv_sec:
.long 0
value_tv_usec:
.long 0
sigaction:
sa_handler:
.long handler
sa_mask:
.long 0
sa_flags:
.long 0
.text
handler:
pushl %ebp
movl %esp, %ebp
movl %ebp, %esp
popl %ebp
ret
_main:
pushl %ebp
movl %esp, %ebp
subl $0x0c, %esp
movl $SIGALRM, %ebx
movl $sigaction, %ecx
movl $0x00, %edx
pushl %edx
pushl %ecx
pushl %ebx
# subl $0x04, %esp
call _sigaction
# movl $SYS_SIGACTION, %eax
# int $0x80
addl $0x0c, %esp
# addl $0x10, %esp
movl $ITIMER_REAL, %ebx
movl $0x01, interval_tv_sec # Successive calls every 1 second
movl $0x00, interval_tv_usec
movl $0x01, value_tv_sec # Initial call in 1 second
movl $0x00, value_tv_usec
movl $itimerval, %ecx
movl $0x00, %edx
pushl %edx
pushl %ecx
pushl %ebx
# subl $0x04, %esp
call _setitimer
# movl $SYS_SETITIMER, %eax
# int $0x80
addl $0x0c, %esp
# addl $0x10, %esp
loop:
jmp loop
When compiled with "clang -arch i386 timer.s -o timer" & debugged with lldb, the handler routine is called every second. I left my efforts at making the code work with syscalls in the code - they are commented out around the sigaction() & setitimer() calls. If for no other reason than to educate myself (and others), I would still like to get the sys call version working if possible, and if not, understand the reason why it doesn't work.
Thanks again.
Update 2: I got the setitimer syscall working. Here's the modified code:
pushl %edx
pushl %ecx
pushl %ebx
subl $0x04, %esp
movl $SYS_SETITIMER, %eax
int $0x80
addl $0x10, %esp
But the same edits do not work for the sigaction sys call, which leads me back to my original conclusion - the sigaction() library function is doing something extra before making the actual syscall. This snippet from dtruss seems to suggest the same:
With sigaction() syscall (not working):
sigaction(0xE, 0x2030, 0x0) = 0 0
setitimer(0x0, 0x2020, 0x0) = 0 0
With sigaction() library call (working):
sigaction(0xE, 0xBFFFFC40, 0x0) = 0 0
setitimer(0x0, 0x2028, 0x0) = 0 0
As you can see, the 2nd argument is different between the two versions. It seems the address of the sigaction structure (0x2030) is passed directly when using the syscall, but something else is passed when using the library call. I'm guessing that the "something else" is generated in the sigaction() library function.
Update 3: I discovered that the same exact problem exists on FreeBSD 9.1. The setitimer syscall works, but the sigaction syscall does not. Like OS X, the sigaction() library call does work.
BSD has a few sigaction syscalls - so far, I've only tried the same one I was using in OS X - 0x2e. Perhaps one of the other sigaction syscalls will work. Knowing that BSD has the same behavior will make this easier to track down, as I can pull the C source code. Plus this opens the problem up to a much wider group of people who may already know what the problem is.
Based on my understanding of how syscalls work coupled with the fact that sigaction does work on Linux, I can't help but to think I am doing something wrong in my code. However, the fact that replacing the int $0x80 call with the sigaction() library function causes my code to work seems to contradict this. There is an entire chapter on assembly language programming in the FreeBSD developer manual, as well as a section on making system calls, so what I'm doing should be possible:
https://www.freebsd.org/doc/en_US.ISO8859-1/books/developers-handbook/x86-system-calls.html
Unless someone can point out what, if anything, I am doing wrong, I think the next step is for me to look at the BSD sources for sigaction(). As I mentioned previously, I looked at the disassembled version of sigaction on OS X & found it to be quite lengthy compared to other syscalls & filled with magic numbers. Hopefully looking at the C code will make clear what it's doing that causes it to work. In the end, it could be something as simple as passing in the wrong sigaction struct (there are several of them) or failing to set some bit somewhere.