In the CPU register values, the content of register
RA is also interesting: this is the address of the signal trampoline. An
x/20i $ra command typed in
gdb would show it, but I cannot
reproduce it here because of the copyright SGI holds on it. There are several things to note
about this signal trampoline.
First, dumping it would show that it does not start at
RA but a few instructions before. If you
happen to run the above test program, you can see the whole IRIX 6.5 signal trampoline by
x/32i $ra-100. This suggests two things: there are several entry points to the
signal trampoline, or the signal trampoline is invoked before the signal handler.
It is easy to find out what is happening: we just have to set a breakpoint at the beginning of
the signal trampoline and run the test program again (do not pay attention to the file names,
gdb is obviously confused):
(gdb) b *$ra-100 Breakpoint 2 at 0xfadce9c: file iconv_converter.c, line 21556. (gdb) r The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /tmp/signal10 Program received signal SIGHUP, Hangup. 0xfa44cc0 in kill () at regcomp.c:575 575 regcomp.c: No such file or directory. (gdb) c Continuing. Breakpoint 2, <signal handler called> (gdb)
We do break at the beginning of the signal trampoline. The signal trampoline is therefore
executed before the signal handler, it calls the signal handler, and then
Another thing to note is that the signal trampoline is not on the user stack. In fact, on IRIX,
gdb is kind enough to gives us its symbol name when we dump it using
x/32i $ra-100: _sigtramp.
We can check that this symbol is defined in
$ nm -D libc.so.1 | grep sigtramp 0faee444 A _sigtramp
On IRIX, the signal trampoline is therefore provided by the
libc. This is another big difference from NetBSD.
The IRIX signal trampoline is also quite big compared to NetBSD's native one. Copyrights forbid
us to reproduce it, but at least we can tell what it does (and this will probably say more to
most readers). The signal frame is called
sf here, it is as defined earlier.
- The signal trampoline holds extra bytes on the stack (48 on IRIX 6, 24 on IRIX 5) for the signal frame.
- It checks if the higher bit of
A0is set. By trying with and without
SA_SIGINFOhere, we discover that this bit is set when
- If so, it stores
- If not, stores
- Finds the address of
errno, and stores it in
sf.isf_uep(IRIX 6 only). This is done by looking up the Global Offset Table and assuming that the
errnoaddrsymbol is at a fixed offset from the signal trampoline.
- Invokes the signal handler
sf.isf_errno(IRIX 6 only)
sigreturn(sf.isf_scp, sf.isf_ucp, sf.isf_signo)on IRIX 6 and
sigreturn(sf.isf_scp, sf.isf_ucp)on IRIX 5.
This is where we discover that
sf.isf_errno holds the
errno value. We
also discover that the kernel transmits the information that we use a struct
irix_ucontext instead of a struct
irix_sigcontext by setting the higher
A0 on signal handler invocation.
irix_sigreturn can discover if the context is to be restored from a
irix_ucontext instead of a struct
NULL. sf.isf_ucp then points to the struct
Now that we know everything about the signal trampoline. What should we do with it? Well, just use it. In sys/compat/irix/irix_signal.c:irix_sendsig() instead of returning to the signal handler, with the return address set to a signal trampoline on the stack (which is a NetBSD flavor signal delivery), we can return to the signal trampoline and let it do all the work for us. This behavior is much closer to the way signals are handled on IRIX.
The only problem is that the kernel does not have the signal trampoline location. The signal
trampoline is in a userland program, and we do not have any simple way of digging it out. It is
not possible to assume that we can find it at a hard coded place in user memory, since it will
To learn the signal trampoline address, we can run a shell command such as:
$ nm -D libc.so.1 |grep _sigtramp 0faee444 A _sigtramp
If a shell command can do it, the kernel can do it. A first idea could be to find the address
the same way we do it from the shell: by looking up the symbol table. This means loading the
ELF section containing the symbol table, then walking through the table looking for our symbol,
This method could work, but it is still extremely weak. First, looking up the symbol is time-consuming. Second, it means importing into the kernel a lot of user level code, thus increasing kernel size and increasing the odds of creating kernel bugs. Third, it is not trivial to find The Right Place for this symbol check in the kernel sources. Doing it that way would be a nasty hack.