2

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.

frijid
  • 21
  • 3
  • Apple engineers on various mailing lists and forums (sorry, no link) have said that they consider the system library interface to be the supported interface. Syscalls are not. They don't bother to maintain source or binary compatibility at that interface. In some cases, the syscalls take different arguments than the corresponding library wrapper functions. I don't know if that explains the problem you're seeing, but what you describe of the `sigaction()` wrapper sounds like it could be. – Ken Thomases Dec 29 '14 at 02:58
  • Yes, if that is true, then that could very well be my problem. I was using this list of sys calls as my reference: http://www.opensource.apple.com/source/xnu/xnu-1504.3.12/bsd/kern/syscalls.master?txt According to that, the sys call signatures do match the those in the library, but perhaps that isn't true. – frijid Dec 29 '14 at 03:30
  • You are not supposed to use `syscall` directly, never. Its purpose is to test new system call. `syscall.h` constants are guarded by `__APPLE_API_PRIVATE`. In the manual for `syscall` things are stated such `This function is useful for testing new system calls that do not have entries in the C library.` and in the BUGS section I read `There is no way to simulate system calls that have multiple return values such as pipe(2).`. The later means (clearly to me) that library wrappers did many (strange) things... – Jean-Baptiste Yunès Dec 30 '14 at 10:28
  • I'm not sure that particular entry in the man page is prohibiting the use of syscall, but rather a suggestion as to why you might want to use it. To be clear, when I talk about "syscall" in my post, I mean pushing the syscall number into eax & calling the OS via int $0x80 - not using the syscall() library function (though I'm sure syscall() simply turns around & does what I'm doing anyway). Aside from my current problem with sigaction, my program is pure assembly - I am not linking with the C library, but rather making direct system calls to the OS via int $0x80. – frijid Dec 30 '14 at 15:17
  • I solved the BSD sigaction problem. As mentioned earlier, there are multiple sigaction syscalls on BSD. I was using 0x2e, which wasn't working. I disassembled the sigaction library function (whic was working) & as it turns out, all it is doing is calling the other sigaction syscall - 0x1a0. So I modified my code to use 0x1a0 & sigaction is now working! At this point, I have sigaction working in Linux & BSD, but not OS X. Unfortunately, OS X only has one sigaction syscall, so the problem there isn't going to be that easy. – frijid Dec 31 '14 at 06:08

0 Answers0