BSD DevCenter
oreilly.comSafari Books Online.Conferences.


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

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 issuing an 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

Breakpoint 2, <signal handler called>

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 sigreturn().

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 libc itself:

$ nm -D | 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 A0 is set. By trying with and without SA_SIGINFO here, we discover that this bit is set when SA_SIGINFO is set.
  • If so, it stores A2 in sf.isf_ucp, and NULL in sf.isf_scp
  • If not, stores A2 in sf.isf_scp and sf.isf_ucp.
  • 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 errnoaddr symbol is at a fixed offset from the signal trampoline.
  • Invokes the signal handler
  • Sets errno using sf.isf_uep and sf.isf_errno (IRIX 6 only)
  • Calls 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 bit of A0 on signal handler invocation.

The function irix_sigreturn can discover if the context is to be restored from a struct irix_ucontext instead of a struct irix_sigcontext when sf.isf_scp is NULL. sf.isf_ucp then points to the struct irix_ucontext.

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 change with libc versions.

To learn the signal trampoline address, we can run a shell command such as:

$ nm -D |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, et voila.

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.

Pages: 1, 2, 3, 4

Next Pagearrow

Sponsored by: