sig.c - vx32 - Local 9vx git repository for patches.
 (HTM) git clone git://r-36.net/vx32
 (DIR) Log
 (DIR) Files
 (DIR) Refs
       ---
       sig.c (10170B)
       ---
            1 #include <sys/signal.h>
            2 #include <ucontext.h>
            3 #include <stdio.h>
            4 #include <stdlib.h>
            5 #include <assert.h>
            6 #include <string.h>
            7 #include <signal.h>
            8 
            9 #include "vx32.h"
           10 #include "vx32impl.h"
           11 
           12 static void sighandler(int signo, siginfo_t *si, void *ucontext)
           13 {
           14         if (vx32_sighandler(signo, si, ucontext)){
           15                 // Back into the fire.
           16                 return;
           17         }
           18         
           19         // vx32_sighandler thought that vx32 wasn't
           20         // running or otherwise didn't know what to do.
           21         // TODO: If there was a handler registered before us, call it?
           22         // If the signal is not important, ignore it.
           23         if (signo == SIGVTALRM || signo == SIGALRM)
           24                 return;
           25         
           26         // Otherwise, deregister the handler and let the signal
           27         // happen again -- this time we'll crash and get a core dump.
           28         vxprint("Unregistering signal handler %d\n", signo);
           29         signal(signo, SIG_DFL);
           30 }
           31 
           32 // vx32_sighandler saves execution context, finds emu,
           33 // and calls vxemu_sighandler.  If we're here, the fs segment
           34 // register pointed at a valid emu, but we still might not have
           35 // been executing actual vx32 translations.
           36 // Trapeip is 0xffffffff if the other segment registers 
           37 // suggest we weren't inside vxrun_setup ... vxrun_cleanup.
           38 int vxemu_sighandler(vxemu *emu, uint32_t trapeip)
           39 {
           40         if (vx32_debugxlate)
           41                 vxprint("vxemu_sighandler %p %#x\n", emu, trapeip);
           42 
           43         if (emu->cpu_trap == 0)
           44                 return VXSIG_ERROR;
           45         
           46         // Not in vx32 code: assume registers saved already and trap.
           47         if (trapeip == 0xffffffff)
           48                 return VXSIG_TRAP;
           49         
           50         if (emu->ininst) {
           51                 // In the middle of translating an instruction,
           52                 // so the registers are already saved.
           53                 // The signal hander knows the exact fault address,
           54                 // which may be a bit after emu->ininst.
           55                 // In fact, the fault address may be one or two
           56                 // instructions past emu->cpu.eip, and a real 
           57                 // processor would not throw the trap until it
           58                 // actually got to the unreadable instruction,
           59                 // but there's not really any harm in trapping now instead.
           60                 return VXSIG_TRAP;
           61         }
           62 
           63         // Single-stepping?  Use the original trap code.
           64         if (emu->cpu_trap == VXTRAP_SINGLESTEP && emu->saved_trap){
           65                 emu->cpu_trap = emu->saved_trap;
           66                 emu->saved_trap = 0;
           67         }
           68 
           69         // In vx32 runtime code (rts.S, run32/64.S), the registers are in flux.
           70         // Single-step until we can get out; then they'll be safe to look at.
           71         // This only makes sense if the trap is an external trap like a timer.
           72         char *eip = (char*)(uintptr_t)trapeip;
           73         if ((emu->cpu_trap&VXTRAP_CATEGORY) != VXTRAP_CPU){
           74                 // The check for run32/64.S doesn't look for the entire file.
           75                 // Instead it looks for anywhere in the file except
           76                 // vxrun_cleanup.  If we single-step out of vxrun_cleanup,
           77                 // then vx32_sighandler will get a single-step trap when
           78                 // the vx32 segment register doesn't point at emu and 
           79                 // won't know what to do.  If we're in vxrun_cleanup, then
           80                 // all the cpu registers are known to be saved.
           81                 extern char vx_rts_S_start[], vx_rts_S_end[];
           82                 extern char vx_run_S_start[];
           83                 if ((vx_rts_S_start <= eip && eip < vx_rts_S_end)
           84                 ||  (vx_run_S_start <= eip && eip < (char*)vxrun_cleanup)){
           85                 SingleStep:
           86                         if(++emu->nsinglestep > 500){
           87                                 // Give up: something is wrong.
           88                                 vxprint("vx32: single-stepping but stuck\n");
           89                                 return VXSIG_ERROR;
           90                         }
           91                         emu->saved_trap = emu->cpu_trap;
           92                         emu->cpu_trap = 0;
           93                         return VXSIG_SINGLESTEP;
           94                 }
           95         }
           96         emu->nsinglestep = 0;
           97 
           98         // In translated code buffer?  Find original eip.
           99         if ((char*)emu->codebuf <= eip && eip < (char*)emu->codefree){
          100                 // Binary search for the translated chunk in which the trap occurred.
          101                 // NB: frags appear in the opposite order as the code itself.
          102                 uint32_t *codetab = emu->codetab;
          103                 unsigned lofrag = 0;
          104                 unsigned hifrag = (uint32_t*)emu->codetop - codetab - 1;
          105                 while (hifrag > lofrag) {
          106                         unsigned midfrag = (lofrag + hifrag) / 2;
          107                         uint32_t mideip = codetab[midfrag];
          108                         if (trapeip >= mideip)
          109                                 hifrag = midfrag;
          110                         else
          111                                 lofrag = midfrag + 1;
          112                 }
          113                 struct vxfrag *frag = (vxfrag*)(intptr_t)codetab[lofrag];
          114 
          115                 // The eip is known to be in the translation buffer,
          116                 // but the buffer contains both fragment headers and
          117                 // fragment code.  Make sure it's not in a fragment header.
          118                 if (trapeip < (uint32_t)(intptr_t)FRAGCODE(frag))
          119                         return VXSIG_ERROR;
          120                 unsigned ofs = trapeip - (uint32_t)(intptr_t)FRAGCODE(frag);
          121 
          122                 // The very first instruction in each fragment is the one that
          123                 // restores ebx.  It is not included in the table, because it 
          124                 // doesn't correspond to any original source instruction.
          125                 // If we're there, pretend we're at the first instruction in the
          126                 // fragment but don't save ebx -- it's already saved.
          127                 if (ofs < frag->insn[0].dstofs) {
          128                         emu->cpu.eip = frag->eip + frag->insn[0].srcofs;
          129                         return VXSIG_TRAP | (VXSIG_SAVE_ALL & ~VXSIG_SAVE_EBX);
          130                 }
          131 
          132                 // Binary search through this fragment's instruction table
          133                 // for the actual faulting translated instruction,
          134                 // and compute the corresponding EIP in the original vx32 code.
          135                 unsigned ninsn = frag->ninsn;
          136                 unsigned loinsn = 0;
          137                 unsigned hiinsn = ninsn - 1;
          138                 while (hiinsn > loinsn) {
          139                         unsigned midinsn = (loinsn + hiinsn + 1) / 2;
          140                         unsigned midofs = frag->insn[midinsn].dstofs;
          141                         if (ofs >= midofs)
          142                                 loinsn = midinsn;
          143                         else
          144                                 hiinsn = midinsn - 1;
          145                 }
          146                 struct vxinsn *insn = &frag->insn[loinsn];
          147                 
          148                 if (ofs < insn->dstofs) {
          149                         // How did that happen?
          150                         // We checked for ofs < frag->insn[0].dstofs above.
          151                         assert(0);
          152                 }
          153                 emu->cpu.eip = frag->eip + insn->srcofs;
          154                 
          155                 // At the beginning of a translated instruction (before the
          156                 // translation has begun to execute) all registers are valid.
          157                 if (ofs == insn->dstofs)
          158                         return VXSIG_TRAP | VXSIG_SAVE_ALL;
          159                 
          160                 // But some translations end up being more than one instruction,
          161                 // and we have to handle those specially, if they've executed 
          162                 // only some of the instructions.
          163                 int r;
          164                 switch (insn->itype) {
          165                 default:
          166                         assert(0);
          167 
          168                 case VXI_JUMP:
          169                 case VXI_ENDFRAG:
          170                         // Direct jumps don't trash any registers while
          171                         // making their way into vxrun_lookup_backpatch.
          172                         return VXSIG_TRAP | VXSIG_SAVE_ALL;
          173 
          174                 case VXI_CALL:
          175                         // Call pushes a return address onto the stack
          176                         // as the first instruction of the translation.
          177                         // We can't just pop it back off, because we would
          178                         // still need to restore the value that got overwritten.
          179                         // The target eip is stored in the word at byte 26 in
          180                         // the translation.
          181                         // Call does not trash any registers on the way 
          182                         // into vxrun_lookup_backpatch.
          183                         emu->cpu.eip = *(uint32_t*)(FRAGCODE(frag)+insn->dstofs+26);
          184                         return VXSIG_TRAP | VXSIG_SAVE_ALL;
          185 
          186                 case VXI_JUMPIND:
          187                         // Indirect jumps save ebx as their first instruction,
          188                         // and then they trash it.  Since we're not at the first
          189                         // instruction (see above), it might or might not be 
          190                         // trashed but is definitely already saved.  
          191                         return VXSIG_TRAP | (VXSIG_SAVE_ALL & ~VXSIG_SAVE_EBX);
          192 
          193                 case VXI_CALLIND:
          194                         // Indirect call is like indirect jump except that it
          195                         // pushes a return address onto the stack in the
          196                         // instruction that ends at dstlen-5.  Until that point,
          197                         // only ebx has been trashed (but saved) and the stack
          198                         // is unmodified.  At that point, though, the stack is now
          199                         // modified and we must commit the instruction:
          200                         // ebx contains the target eip.
          201                         r = VXSIG_TRAP | (VXSIG_SAVE_ALL & ~VXSIG_SAVE_EBX);
          202                         if (ofs >= insn->dstofs + insn->dstlen - 5)
          203                                 r |= VXSIG_SAVE_EBX_AS_EIP;
          204                         return r;
          205                         
          206                 case VXI_RETURN:
          207                 case VXI_RETURN_IMM:;
          208                         // Return is like indirect jump, but we have to treat it
          209                         // as committed once the pop ebx happens.
          210                         // Pop ebx happens at byte 7 of the translation.
          211                         r = VXSIG_TRAP | (VXSIG_SAVE_ALL & ~VXSIG_SAVE_EBX);
          212                         if (ofs - insn->dstofs > 7) {
          213                                 r |= VXSIG_SAVE_EBX_AS_EIP;
          214                         
          215                                 if (insn->itype == VXI_RETURN_IMM) {
          216                                         // Return immediate is like return but if we've committed
          217                                         // to popping ebx we also have to commit to popping
          218                                         // the immediate number of bytes off the stack.
          219                                         // The immediate is a 32-bit word at byte 10 of
          220                                         // the translation, but it was originally only 16 bits
          221                                         // in the original return instruction, so it's okay to
          222                                         // truncate.
          223                                         r |= VXSIG_ADD_COUNT_TO_ESP;
          224                                         r |= *(uint16_t*)(FRAGCODE(frag)+insn->dstofs+10) << VXSIG_COUNT_SHIFT;
          225                                 }
          226                         }
          227                         return r;
          228 
          229                 case VXI_TRAP:
          230                         // Traps save eax as their first instruction and then
          231                         // they trash it.  Since we're not at the first instruction
          232                         // (see above), it might or might not be trashed but
          233                         // is definitely already saved.
          234                         return VXSIG_TRAP | (VXSIG_SAVE_ALL & ~VXSIG_SAVE_EAX);
          235                 
          236                 case VXI_LOOP:
          237                 case VXI_LOOPZ:
          238                 case VXI_LOOPNZ:
          239                         // The first instruction in the loop translation decrements ecx.
          240                         // The rest figure out whether to jump.
          241                         // We can back out by re-incrementing ecx.
          242                         // Untested (most loops translate into actual loop instructions).
          243                         return VXSIG_TRAP | VXSIG_SAVE_ALL | VXSIG_INC_ECX;
          244                 }
          245         }
          246 
          247         // Let's see.
          248         // The fs segment register pointed at a vxemu, so we're in vxproc_run.
          249         // The eip is not in rts.S, nor in run32/64.S.
          250         // The eip is not in a fragment translation.
          251         // We're not translating an instruction (emu->ininst == nil).
          252         // That pretty much means we're in some interstitial instruction.
          253         // Assume the registers are already saved.
          254         return VXSIG_TRAP;
          255 }
          256 
          257 int vx32_siginit(void)
          258 {
          259         stack_t ss;
          260         void *stk;
          261         struct sigaction sa;
          262         
          263         // See if there's already an alternate signal stack.
          264         if (sigaltstack(NULL, &ss) < 0)
          265                 return -1;
          266 
          267         assert(!(ss.ss_flags & SS_ONSTACK));
          268         if (ss.ss_flags & SS_DISABLE) {
          269                 // Allocate an alternate signal stack.
          270                 ss.ss_size = 64*1024;
          271                 stk = malloc(ss.ss_size);
          272                 if (stk == NULL)
          273                         return -1;
          274                 ss.ss_flags = 0;
          275                 ss.ss_sp = stk;
          276                 if (sigaltstack(&ss, NULL) < 0) {
          277                         free(stk);
          278                         return -1;
          279                 }
          280         }
          281         
          282         // Register our signal handler.
          283         memset(&sa, 0, sizeof sa);
          284         sa.sa_handler = (void*)sighandler;
          285         sa.sa_flags = SA_ONSTACK | SA_SIGINFO;
          286         if (sigemptyset(&sa.sa_mask) < 0)
          287                 return -1;
          288         if (sigaction(SIGSEGV, &sa, NULL) < 0)
          289                 return -1;
          290         if (sigaction(SIGBUS, &sa, NULL) < 0)
          291                 return -1;
          292         if (sigaction(SIGFPE, &sa, NULL) < 0)
          293                 return -1;
          294         if (sigaction(SIGVTALRM, &sa, NULL) < 0)
          295                 return -1;
          296         if (sigaction(SIGTRAP, &sa, NULL) < 0)
          297                 return -1;
          298         return 0;
          299 }
          300