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) {
return;
}
$ 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/libc.so.1|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
a3
R0 00000000 ffffffe0 00000000 00000000 00000001 7fff2f30 00000000
0faee284
(...)
(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 |
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.
Acknowledgements
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.
References
NetBSD-current kernel sources
The NetBSD project, 1993-2001.
ftp://ftp.netbsd.org/pub/NetBSD/NetBSD-current/tar_files/src/sys.tar.gz
Via CVSWeb: http://cvsweb.netbsd.org/bsdweb.cgi/syssrc/
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.
