It is currently Sat, 04 Feb 2023 11:40:08 GMT



 
Author Message
 Signal handler for unaligned access: emulate and continue?
A week or so ago, I posted some questions about how to handle bus
error signals in SunOS 4.1.3 by patching up the destination register
of the load with a magic value and continuing execution.  I'm posting
this followup to summarize the responses I got, and to raise one
additional question:

  If I want to change the value of the faulting context's %o1..%o7 from
  within the signal, how can I do so?  I know these registers are probably
  stored somewhere, but under SunOS 4.1.x, the signal handling context doesn't
  provide access to these registers.  All of the other registers are fairly
  easy to modify, but I can't seem to find a good way to modify the
  %o1 to %o7 registers (%o0 is provided in the signal handling context,
  however).

  Following the suggestion of one of the responders, I'm considering
  patching the sigcontext up so that it resumes execution in a fixup routine.
  The routine could load the magic cookie into the desired %o reg before
  "returning" to the instruction after the faulting LD. The problem is
  how to do this without scribbling over any _other_ registers..  If
  the %o registers are stored somewhere that's accessible from a signal
  handler, then I could avoid this problem.

If anyone knows how to modify the values of %o1..%o7 from within a signal
handler, please let me know.

Special thanks to the people who responded:
  Mikael Pettersson <m...@ida.liu.se>
  rich...@atheist.tamu.edu (Richard Henderson)
  morgan.herring...@nosun.West.Sun.COM (Morgan Herrington)
  Chris Torek <to...@elf.bsdi.com>
  Casper....@Holland.Sun.COM (Casper Dik)
  Mark Dapoz <m...@bsc.no>

Most everyone pointed out that the proper way to resume execution at
the next instruction after the faulting instruction is to set both the
sc_npc and sc_pc fields of the sigcontext struct (in case the faulting
instruction was in the delay slot of a branch that's already executed):

  sc->sc_pc = sc->sc_npc;
  sc->npc += 4;

Several people pointed out that signals were very expensive, and was I
sure that I wanted to do this.  However, I don't expect the case that
generates the signal to ever occur in practice, so for my application,
this does what I want with effectively no runtime cost (versus the
three instructions if I include an explicit tag test in the
instruction stream).

For altering the value of the destination register, several people
sent me some code that handles unaligned accesses for various OSs,
including NetBSD and Solaris.  However, Mikael Petersson went far
above and beyond the call: he actually went and wrote code to do what
I want for both Solaris 2.4 and SunOS 4.1.x (except for the %o1..%o7
case for SunOS, discussed above).  I've included his message with the
code below.

Thanks to everyone who responded!

  -Jeff

===========================================================================
From: Mikael Pettersson <m...@ida.liu.se>
Date: Sun, 4 Jun 1995 19:13:55 +0200
To: jd...@cs.washington.edu
Subject: Re: Signal handler for unaligned access: emulate and continue?

In article <D9Go26....@beaver.cs.washington.edu> you write:

Strange, assigning scp->sc_o0 works for me.

I'm pretty sure that the correct way is: PC := nPC; nPC += 4.
(This is what I derived from experiments, and it's what the
SPARC Architecture Manual (V8) says on page 73.)
One needs to consider the cases where the LD is in the delay slot
of a branch, or when a branch follows the LD.

Is it just an "uncommon" but legal case to have a fixnum here,
or is it an error? If the former, are you sure that using traps
really is less expensive that explicit tests?

BTW, why a magic value? Wouldn't it be better to, say, set the
overflow bit in the PSR and do a BVS after the load?

I hacked together some code this weekend to do what you asked for.
It's included below.

The first version is for Solaris 2.4. I have tried it for
all the integer registers, and it seems to work just fine.
/usr/include/sys/regset.h warns that mcontext->gwins might be
non-NULL. I don't know when that might happen, so the code for
that case is just guesswork.

The second verson is for SunOS 4.1.3. I didn't bother with the
case where the target is one of %g2..%g7. (One would probably
have to write a few lines of assembly to move a given value to
a given %g-reg.) The cases for %g1/%o0/%l0..%l7/%i0..%i7
have worked fine in my tests. Dealing with %o1..%o7 appears to
be problematical, as /usr/include/sys/signal.h is too vague in
its description of the sc_wbcnt, sc_spbuf and sc_wbuf fields.
I don't know what to do in that case, but one might be able to
patch the sigcontext so that the thread is resumed in a fix-up
routine. This routine could load the magic cookie into the
desired %o reg before "returning" to the instruction after the
faulting LD. The problem is how to do this without scribbling
over any _other_ registers..

You may freely quote this letter when you followup on your
news article.

Cheers,
        /Mikael

==== cut here ====
/* alignfix5.c -- for Solaris 2.4
 * Hacked by Mikael Pettersson (m...@ida.liu.se), June 4, 1995.
 */
#include <siginfo.h>
#include <ucontext.h>     /* includes <sys/regset.h> and <sys/ucontext.h> */
#include <signal.h>
#include <stdio.h>
#define MAGIC_COOKIE    0x11223344

void handler(int signo, siginfo_t *siginfo, void *ptr_ucontext)
{
    ucontext_t *ucontext = (ucontext_t*)ptr_ucontext;
    mcontext_t *mcontext;
    unsigned insn;
    unsigned rd;

    if( signo != SIGBUS ) {
        printf("handler: signo == %d != SIGBUS\n", signo);
        exit(1);
    }
    if( !siginfo ) {
        printf("handler: siginfo == NULL\n");
        exit(1);
    }
    if( siginfo->si_code != BUS_ADRALN ) {
        printf("handler: SIGBUS code == %d != BUS_ARDALN\n", siginfo->si_code);
        exit(1);
    }
    mcontext = &ucontext->uc_mcontext;
    insn = *(unsigned*)mcontext->gregs[REG_PC];
    printf("handler: signo == SIGBUS, code == ADRALN, addr == 0x%X\n",
           siginfo->si_addr);
    printf("         uc_flags == 0x%X, PC == 0x%X, insn == 0x%X, nPC == 0x%X\n",
           ucontext->uc_flags, mcontext->gregs[REG_PC],
           insn, mcontext->gregs[REG_nPC]);
    /* verify that this was a load word (LD) insn */
    if( (insn & 0xC1F80000) != 0xC0000000 ) {
        printf("handler: faulting insn wasn't LD\n");
        exit(1);
    }
    /* set the destination register to the magic cookie */
    rd = (insn >> 25) & 0x1F;
    printf("handler: rd == %%%c%u\n", "goli"[(rd >> 3)], rd & 7);
    if( rd <= 15 ) {
        /* A %g or %o reg. These are in mcontext->gregs. */
        if( rd > 0 ) /* cannot assign %g0 */
            mcontext->gregs[(REG_G1 - 1) + rd] = MAGIC_COOKIE;
    } else {
        /* A %l or %i reg. Find and patch the current window. */
        struct rwindow *rwin;
        if( mcontext->gwins ) { /* when does this happen? (UNTESTED) */
            /* The windows are in a separate area. */
            unsigned cwp = mcontext->gregs[REG_PSR] & 0x1F;
            printf("handler: gwins->wbcnt == %d, cwp == %u\n",
                   mcontext->gwins->wbcnt, cwp);
            /* need to check that cwp < mcontext->gwins->wbcnt ?? */
            rwin = &mcontext->gwins->wbuf[cwp];
        } else {
            /* The windows are on the stack. %sp is current one */
            rwin = (struct rwindow*)mcontext->gregs[REG_SP];
            printf("handler: %%sp == 0x%X\n", (unsigned)rwin);
        }
        ((unsigned*)rwin)[rd - 16] = MAGIC_COOKIE;
    }
    /* skip to next instruction */
    mcontext->gregs[REG_PC] = mcontext->gregs[REG_nPC];
    mcontext->gregs[REG_nPC] += 4;
    /* tell kernel to restore cpu state (unnecessary?) */
    ucontext->uc_flags |= UC_CPU;

void catch(void)
{
    struct sigaction act;
    act.sa_sigaction = handler;
    sigemptyset(&act.sa_mask);
    act.sa_flags = SA_SIGINFO;
    if( sigaction(SIGBUS, &act, 0) ) {
        perror("sigaction");
        exit(1);
    }

unsigned load(char *addr)
{
    return *(unsigned*)addr;

void tryload(char *addr)
{
    printf("ld [0x%X] == 0x%X\n", (unsigned)addr, load(addr));

unsigned blob[2] = { 0x89abcdef, 0xfedcba98 };

int main(void)
{
    catch();
    tryload((char*)blob + 0);   /* ok */
    tryload((char*)blob + 1);   /* trap */
    tryload((char*)blob + 2);   /* trap */
    tryload((char*)blob + 3);   /* trap */
    tryload((char*)blob + 4);   /* ok */
    return 0;
==== cut here ====
/* alignfix4.c -- for SunOS 4.1.3
 * Hacked by Mikael Pettersson (m...@ida.liu.se), June 4, 1995.
 */
#include <signal.h>
#include <stdio.h>
#define MAGIC_COOKIE    0x11223344

void handler(signo, code, scp, addr)
    int signo, code;
    struct sigcontext *scp;
    char *addr;
{
    unsigned insn;
    unsigned rd;

    if( signo != SIGBUS ) {
        printf("handler: signo == %d != SIGBUS\n", signo);
        exit(1);
    }
    if( BUS_CODE(code) != BUS_ALIGN ) {
        printf("handler: BUS_CODE(code) == %d != BUS_ALIGN\n", BUS_CODE(code));
        exit(1);
    }
    insn = *(unsigned*)scp->sc_pc;
    printf("handler: signo == SIGBUS, code == BUS_ALIGN, addr = 0x%X\n",
           addr);
    printf("         PC == 0x%X, insn == 0x%X, nPC == 0x%X, wbcnt == %d\n",
           scp->sc_pc, insn, scp->sc_npc, scp->sc_wbcnt);
    /* verify that this was a load word (LD) insn */
    if( (insn & 0xC1F80000) != 0xC0000000 ) {
        printf("handler: faulting insn wasn't LD\n");
        exit(1);
    }
    /* set the destination register to the magic cookie */
    rd = (insn >> 25) & 0x1F;
    printf("handler: rd == %%%c%u\n", "goli"[(rd >> 3)], rd & 7);
    if( rd == 0 )               /* %g0 */
        ;
    else if( rd == 1 )          /* %g1 */
        scp->sc_g1 = MAGIC_COOKIE;
    else if( rd <= 7 ) {     /* %g2..%g7 */
        /* Would need to write a few lines of assembly to deal with this case */
        printf("handler: assignment to %%g2..%%g7 N.Y.I.\n");
        exit(1);
    } else if( rd == 8 )        /* %o0 */
        scp->sc_o0 = MAGIC_COOKIE;
    else if( rd >= 16 )              /* %l0..%l7, %i0..%i7 */
        ((unsigned*)scp->sc_sp)[rd - 16] = MAGIC_COOKIE;
    else {                      /* %o1..%o7 */
        /* I have no idea what to do here */
        printf("handler: assignment to %%o1..%%o7 N.Y.I.\n");
        exit(1);
    }
    /* skip to next instruction */
    scp->sc_pc = scp->sc_npc;
    scp->sc_npc += 4;

void catch()
{
    struct sigvec vec;
    vec.sv_handler = handler;
    vec.sv_mask = 0;
    vec.sv_flags = 0;
    if( sigvec(SIGBUS, &vec, (struct sigvec*)0) ) {
        perror("sigvec");
        exit(1);
    }

unsigned load(addr)
    char *addr;
{
    return *(unsigned*)addr;

void tryload(addr)
    char *addr;
{
    printf("ld [0x%X] == 0x%X\n", (unsigned)addr, load(addr));

unsigned blob[2] = { 0x89abcdef, 0xfedcba98 };

int main()
{
    catch();
    tryload((char*)blob + 0);   /* ok */
    tryload((char*)blob + 1);   /* trap */
    tryload((char*)blob + 2);   /* trap */
    tryload((char*)blob + 3);   /* trap */
    tryload((char*)blob + 4);   /* ok */
    return 0;
==== cut here ====
--
Mikael Pettersson                                | Email: m...@ida.liu.se
Department of Computer and Information Science   | Phone: +46 13282683
Linkoping University, S-581 83 Linkoping, SWEDEN | Fax  : +46 13282666

===========================================================================

-- Jeff

------------------------------------------------------------------------------
Jeffrey Dean (jd...@cs.washington.edu)         Graduate Student, Cecil Project
Dept. of Computer Science & Engineering               University of Washington
              http://www.**-**.com/
------------------------------------------------------------------------------



 Sun, 23 Nov 1997 03:00:00 GMT   
 
   [ 1 post ] 

Similar Threads

1. Signal handler for unaligned access: emulate and continue?

2. Signal handlers inside signal handlers

3. accessing state from signal handler

4. sig_atomic_t: from signal handler: set or access?

5. Linux signal() semantics - can BSD signal() be emulated?

6. Threads performance - allow signal handler to not call handler


 
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group.
Designed by ST Software