Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Signal handler for unaligned access: emulate and continue?

0 views
Skip to first unread message

Jeffrey Dean

unread,
Jun 7, 1995, 3:00:00 AM6/7/95
to
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>
ric...@atheist.tamu.edu (Richard Henderson)
morgan.h...@nosun.West.Sun.COM (Morgan Herrington)
Chris Torek <to...@elf.bsdi.com>
Caspe...@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 <D9Go2...@beaver.cs.washington.edu> you write:
>There's a field called 'sc_o0', but changing this value in my signal
>handler doesn't seem to affect the value of %o0 that gets restored
>when execution resumes

Strange, assigning scp->sc_o0 works for me.

>2) What's the best way to get execution to resume at the instruction
>following the load (rather than re-executing the load instruction)?
>Should I just do:
>
> scp->sc_pc += sizeof(void*);
>
>To skip to the next instruction? This seems to work, but there might
>be a better way.

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.

>(In case you're wondering, this is for implementing a
>dynamically-typed language where integers are tagged with 0's in their
>low bit, and other values are pointers with their low bit tagged as
>1's: I'm trying to extract a class identifier stored in the first word
>of most objects, and the 'magic_value' is the value I want to use for
>integers).

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.cs.washington.edu/homes/jdean/index.html
------------------------------------------------------------------------------

0 new messages