BSD DevCenter
oreilly.comSafari Books Online.Conferences.


IRIX Binary Compatibility, Part 4
Pages: 1, 2, 3, 4

I could not imagine that it was done in such an ugly way in the IRIX kernel. There must have existed some way for the program to inform the kernel of the signal trampoline address.

After some investigation, this appears to be true: Each time the sigaction(2) system call is invoked on IRIX, the libc gives the signal trampoline address as a fourth argument to sigaction(4):

/* signal10.c -- a sigaction tester */
#include <stdio.h>
#include <unistd.h>
#include <signal.h>

void func (int, siginfo_t *, void *);

int main (int argc, char** argv) {
        struct sigaction sa;
        sigset_t ss;

        bzero(&ss, sizeof(ss));

        sa.sa_handler = NULL;
        sa.sa_mask = ss;
        sa.sa_flags = 0;
        sa.sa_sigaction = (void *)func;

        if (sigaction(SIGHUP, &sa, NULL)) {
                printf("signal() failed\n");
                return -1;
        printf("sigactionl() successful. Now sleeping\n");
        kill(getpid(), 1);
        return 0;

void func (int sig,siginfo_t *si, void *v) {

$ gdb ./signal10
(gdb) b sigaction
Breakpoint 1 at 0x400b90
(gdb) r
Starting program: ./signal10
Breakpoint 1 at 0xfa40df0: file sigaction.c, line 25.

Breakpoint 1, _sigaction () at sigaction.c:26
(gdb) x/5i $pc
0xfa40df0 <_sigaction+12>:      lw      $v0,-31524($gp)
0xfa40df4 <_sigaction+16>:      addiu   $sp,$sp,-32
0xfa40df8 <_sigaction+20>:      sw      $ra,28($sp)
0xfa40dfc <_sigaction+24>:      lw      $v0,0($v0)
0xfa40e00 <_sigaction+28>:      sw      $gp,24($sp)

No system call here. In fact the sigaction() function in libc calls another libc function. By tracing in gdb with the stepi command, we can discover it. The other way is to think that the name could be related:

$ nm -D /usr/lib/|grep sigaction
0fa40d94 A _ksigaction
0fa40dd4 A _sigaction
0fa40d94 W ksigaction
0fa40dd4 W sigaction

If in gdb we attempt a break on ksigaction...

$ gdb ./signal10
(gdb) b ksigaction
Breakpoint 1 at 0xfa40da4: file ksigaction.s, line 23.
(gdb) r
Starting program: ./signal10

Breakpoint 1, _ksigaction () at ksigaction.s:23
23      ksigaction.s: No such file or directoryCurrent language:  auto;
currently asm
(gdb) x/4i $pc
0xfa40da4 <_ksigaction>:        li      $v0,1162
0xfa40da8 <_ksigaction+4>:      syscall
0xfa40dac <_ksigaction+8>:      bnez    $a3,0xfa40dbc <_ksigaction+24>
0xfa40db0 <ksigaction+12>:     nop
(gdb) info reg
          zero       at       v0       v1       a0       a1       a2       
 R0   00000000 ffffffe0 00000000 00000000 00000001 7fff2f30 00000000 

(gdb) x/4i $a3
0xfaee284 <_sigtramp>:  lui     $t0,0x8000
0xfaee288 <_sigtramp+4>:        addiu   $sp,$sp,-48
0xfaee28c <_sigtramp+8>:        and     $t0,$a0,$t0
0xfaee290 <_sigtramp+12>:       sw      $a0,36($sp)

We've got it. A3 holds the fourth argument to the system call. Once we know where the signal trampoline is, our only problem is to remember it between the time we are given it by sigaction(2) and the time we have to use it on signal delivery. We cannot use a kernel global variable since we may work with several processes which use different libc, and hence different sigramp addresses. We must store a per process view of this information.

This is done using a field of the struct proc. Each process is described by a proc structure, which is defined in sys/sys/proc.h. In this structure, we find various information such as the process credentials, a pointer to the process' struct emul, the process pid, and so on. There is a p_emuldata field, which is a pointer to emulation specific data. Each emulation subsystem is free to use it to store per process emulation specific data.

We just have to define a struct irix_emuldata with an appropriate field to store the signal trampoline address. Then we must allocate this structure at process creation time and set the struct proc p_emuldata field pointing to it. At process termination time we must de-allocate the struct irix_emuldata, to avoid memory leak in the kernel. To do this we need a hook at fork, exec and exit time. The emulation subsystem provides a way of doing this without having to provide emulation specific versions of these system calls: in struct emul, we have 3 fields named e_proc_exec, e_proc_fork, and e_proc_exit, which point to 3 functions invoked on exec, fork and exit.

Thus, we can define the 3 functions: irix_e_proc_exec(), irix_e_proc_fork(), and irix_e_proc_exit(), fill the fields in the emul_irix_o32 and emul_irix_n32 struct emul (if you don't remember them go back to article 2 of the series), and we have enough support for allocating and freeing our irix_emuldata structures.

Related Reading

C Pocket Reference
By Peter Prinz, Ulla Kirch-Prinz

Once all of this was done, we achieve a very good level of compatibility with IRIX signal delivery. Additionally this makes the code in irix_sendsig() and irix_sigreturn() much simpler, since it does not have to handle most of the signal frame anymore: the signal trampoline does this. We are even able to successfully emulate signal delivery for binaries such as autocad which provide their own signal trampoline.

One other interesting thing to note is that since that code was written, Jason Thorpe implemented signal trampolines provided by libc for NetBSD native processes, thus adopting the same scheme IRIX used.

The libc provided signal trampoline was adopted in NetBSD because it removes the need to execute code on the stack. Memory pages mapped on the stack can therefore be made non executable (the Memory Management Unit of all modern CPU are able to enforce such rules), and we are able to fix a whole class of security problems. With a non executable stack, it is not possible anymore to exploit a buffer overflow on a local variable by executing some user-supplied code stored on the stack.

In future articles we will move on to multi-threading support in IRIX binaries.


I would like to thank David Brownlee, Hubert Feyrer, and John Kloss for reviewing this paper, and of course all the NetBSD developers that spent some time answering my kernel questions on the project's mailing lists.


NetBSD-current kernel sources
The NetBSD project, 1993-2001.
Via CVSWeb:

NetBSD man pages
The NetBSD project, 1993-2001.

System V Release 4 Application Binary Interface: MIPS processor supplement
The Santa Cruz Operation, Inc, 1990-1996.

Linux Compatibility on BSD for the PPC Platform
Emmanuel Dreyfus, 2001

Emmanuel Dreyfus is a system and network administrator in Paris, France, and is currently a developer for NetBSD.

Return to the BSD DevCenter.

Sponsored by: