#include "process.pub" #include "frame.pri" #include "symtab.pri" #include "symbol.h" #include "hostcore.h" #include #include "asm.pri" #include "format.pub" #include "bpts.pri" #include "master.pri" SRCFILE("hostcore.c") #define TXTOFF(magic) (magic==ZMAGIC ? 0 : sizeof (struct exec)) #include static WaitList waitlist; int kill(int,int); const int STOPPED=0, RUNNING=1, EXITED=2, DIED=3; char *HostCore::read(long l,char *b,int n) { return readwrite(l,b,n,0); } char *HostCore::write(long l,char *b,int n) { return readwrite(l,b,0,n); } char *HostCore::eventname() { return SignalName(event()); } char *HostCore::pokedbl(long l,double d,int n) { return write(l,(char*)&d,n); } char *HostCore::stop() { ::kill(pid, SIGSTOP); return 0; } int HostCore::REG_AP() { return 0; } int HostCore::REG_FP() { return 14; } int HostCore::REG_PC() { return 17; } int HostCore::REG_SP() { return 15; } int HostCore::event() { return cursig; } extern int errno; int HostCore::ptrace(int cmd, int a, int d, int a2) { errno = 0; int ret = ::ptrace(cmd, pid, a, d, a2); switch (cmd) { case PTRACE_CONT: case PTRACE_KILL: case PTRACE_SINGLESTEP: case PTRACE_ATTACH: case PTRACE_DETACH: break; default: if (!errno) waitlist.wait(this, WAIT_DISCARD); break; } return ret; } char *HostCore::run() { if (cursig == SIGSTOP || cursig == SIGTRAP) cursig = 0; ptrace(PTRACE_CONT, 1, cursig); state = RUNNING; cursig = 0; return 0; } char *HostCore::poke(long l,long d,int n) { switch (n) { case 1: return write(l, 3 + (char*)&d,n); case 2: return write(l, 2 + (char*)&d,n); case 4: return write(l,(char*)&d,n); default: return "Hostcore poke error"; } } #define SUNBPT 0x4e4f /* Trap #15 */ char *HostCore::laybpt(Trap *t) { Cslfd *cp = peek(t->stmt->range.lo); if (!cp) return "text not readable (probably shared)"; t->saved = cp->sht; return poke(t->stmt->range.lo, SUNBPT, 2); } char *HostCore::liftbpt(Trap *t) { if( behavs() == ERRORED ) return 0; return poke(t->stmt->range.lo, t->saved, 2); } #define REGBIT(r) (1 << r) long HostCore::saved(Frame *f, int r, int) /* ignore size */ { if( r > 15 ) return 0; if( !(f->regsave & REGBIT(r)) ) return 0; long loc = f->regbase; while( --r >= 0 ) if( f->regsave & REGBIT(r) ) loc += 4; return loc; } Asm *HostCore::newAsm() { return new M68kAsm(this); } Behavs HostCore::behavs() { if( !online() ) return PENDING; // Confirm it is still running if ( state == EXITED || state == DIED) return ERRORED; if ( state == RUNNING ) { if (waitlist.wait(this, WAIT_POLL|WAIT_PCFIX)) { if (state != STOPPED) return ERRORED; if (!(sigmask&(1<<(cursig-1)))) { run(); return ACTIVE; } } else return ACTIVE; } switch( cursig ){ case SIGSTOP: return HALTED; case SIGTRAP: return BREAKED; default: return PENDING; } } char *HostCore::regname(int r) { if (r < 8) return sf( "d%d", r ); if (r < 15) return sf( "a%d", r - 8 ); switch( r ){ case 15: return "sp"; case 16: return "ps"; case 17: return "pc"; default: return sf( "r%d", r ); } } const REGADDR = USRADR - 18 * 4; long HostCore::regloc(int r, int sz) { if (r >= 0 && r <= 17) { long ret = 4 * r + REGADDR; if (sz && sz < 4) ret += 4 - sz; return ret; } return 0; } char *HostCore::problem() { static char buf[64]; if( !online() ) { if( corefstat() ) return "core image has gone away"; return Core::problem(); } if (state == EXITED) sprintf( buf, "exited with status %d", cursig); else if (state == DIED) sprintf( buf, "died from signal %d", cursig); return buf; } char *HostCore::readcontrol() { if( online() ) return "online readcontrol"; if( corefstat() ) return "cannot fstat core image"; int got = ::read(corefd, (char *)&bsdcore, sizeof(bsdcore)); if (got != sizeof(bsdcore)) return "cannot read bsdcore"; state = STOPPED; cursig = bsdcore.c_signo; endtext = bsdcore.c_tsize + ctob(1); startdata = ctob(stoc(ctos(btoc(endtext)))); enddata = startdata + bsdcore.c_dsize; startstack = SYSADR - bsdcore.c_ssize; return 0; } void HostCore::close() { if (online()){ if (state == RUNNING) { stop(); waitstop(WAIT_PCFIX); ptrace(PTRACE_DETACH, 1); } else if (state == STOPPED) { ::kill(pid, SIGSTOP); ptrace(PTRACE_DETACH, 1); } waitlist.remove(this); } Core::close(); } const user *U = (user *)0; char *HostCore::open() { int mode = 2; if( procpath() && !strcmp(basename(procpath()), "core") ) { while( corefd<0 && mode>=0 ) corefd = ::open(procpath(), mode--); if( corefd<0 ) return SysErr("core image: " ); if( corefstat() ) return SysErr("core image: " ); if( corestat.st_mode & S_IEXEC ) return "executable - should be dump (core)"; } else if( procpath() ) { sscanf(procpath(), "%d", &pid); _online = 1; waitlist.add(this); waitlist.wait(this, 0); endtext = ctob(ptrace(PTRACE_PEEKUSER, (int)&U->u_tsize) + 1); cursig = SIGSTOP; } stabfd = ::open(stabpath(),0); if( stabfd<0 ) { if( online() ) { ::kill(pid, SIGSTOP); ptrace(PTRACE_DETACH, 1); } return SysErr( "symbol tables: " ); } stabfstat(); _symtab = new BsdSymTab(this, stabfd, _symtab); _symtab->read(); if( online() || !procpath() ) return 0; return readcontrol(); } char *HostCore::reopen(char *newprocpath, char *newstabpath) { int compstabfd = -1; char *error = 0; int retstat; if( !online() || (newprocpath && !strcmp(basename(newprocpath), "core")) ) return "reopen core not implemented"; int opid = pid; int ostate = state; int ocursig = cursig; sscanf(newprocpath, "%d", &pid); waitlist.wait(this, 0); compstabfd = ::open(newstabpath, 0); struct stat compstabstat; if( compstabfd < 0 ) { error = "symbol table error"; goto out; } retstat = ::fstat(compstabfd, &compstabstat); ::close(compstabfd); if (retstat) error = "symbol table error"; else if( compstabstat.st_mtime != stabstat.st_mtime ) error = "symbol tables differ (modified time)"; else if( compstabstat.st_size != stabstat.st_size ) error = "symbol tables differ (file size)"; cursig = ocursig; state = ostate; if (error) { out: ::kill(pid, SIGSTOP); ptrace(PTRACE_DETACH, 1); pid = opid; return error; } // Must disconnect from the old process int npid = pid; pid = opid; switch (state) { case RUNNING: stop(); waitstop(WAIT_PCFIX); ptrace(PTRACE_DETACH, 1); break; case STOPPED: ::kill(pid, SIGSTOP); ptrace(PTRACE_DETACH, 1); break; case EXITED: case DIED: break; } pid = npid; state = STOPPED; cursig = SIGSTOP; endtext = ctob(ptrace(PTRACE_PEEKUSER, (int)&U->u_tsize) + 1); return 0; } const int REGFD = -2, DATAFD = -3, TEXTFD = -4, USERFD = -5; char *HostCore::seekto(int &fd, long &addr, int &whence) { if( !online() ) { if ( (u_long)addr >= USRADR) { addr -= USRADR + ctob(UPAGES); whence = 2; } else if ( (u_long)addr >= REGADDR) { addr -= REGADDR; fd = REGFD; } else if ( (u_long)addr < 0x2000) return "offset below text segment"; else if ( (u_long)addr <= endtext) { addr += TXTOFF(_symtab->magic()) - 0x2000; fd = stabfd; } else if ( (u_long)addr < startdata) return "offset beyond text segment"; else if ( (u_long)addr <= enddata) addr -= startdata - bsdcore.c_len; else if ( (u_long)addr < startstack) return "offset beyond data segment"; else if ( (u_long)addr < SYSADR) { addr -= SYSADR + ctob(UPAGES); whence = 2; } else return "offset in system space"; } else { if ( (u_long)addr >= USRADR) { addr -= USRADR; fd = USERFD; } else if ( (u_long)addr >= REGADDR) { addr -= REGADDR; fd = REGFD; } else if ( (u_long)addr >= SYSADR ) { extern char *DEVKMEM; if( kmemfd == -1 ) { kmemfd = ::open(DEVKMEM, 0); if (kmemfd == -1) return "can't read kernel"; } fd = kmemfd; } else if ( (u_long)addr <= endtext) fd = TEXTFD; else fd = DATAFD; } return 0; } void bcopy(char*,char*,int); char *HostCore::readwrite(long offset, char *buf, int r, int w) { int fd = corefd, whence = 0; int rv; char *msg = "core image:"; char *error = seekto(fd, offset, whence); if( error ) return sf("core image: %s", error); if( fd >= 0 ) { if( lseek(fd, offset, whence) == -1 ) return sf("lseek(%d,0x%X,%d)", fd, offset, whence); if( r ){ int got = ::read(fd, buf, r); for( int i = got; i < r; ++i ) buf[i] = 0; if( got > 0 ) return 0; } if( w && ::write(fd, buf, w) == w ) return 0; return SysErr(msg); } int wasrunning = 0; if (state == RUNNING) { stop(); if (error = waitstop(WAIT_PCFIX)) return error; if (cursig != SIGSTOP) { int savesig = cursig; /* The STOP signal still has to be eaten */ run(); waitstop(WAIT_PCFIX); cursig = savesig; } else wasrunning = 1; } if (fd == REGFD) { error = regrw(offset, buf, r, w); if (wasrunning && !error) run(); return error; } else if (fd == DATAFD) { if ( r ) rv = ptrace(PTRACE_READDATA, offset, r, (int)buf); else rv = ptrace(PTRACE_WRITEDATA, offset, w, (int)buf); } else if (fd == TEXTFD) { if ( r ) rv = ptrace(PTRACE_READTEXT, offset, r, (int)buf); else { /* Damn bug in Sun's kernel */ if (w < 4){ int tmp; ptrace(PTRACE_READTEXT, offset, 4, (int)&tmp); ::bcopy(buf, (char*)&tmp, w); if (rv = ptrace(PTRACE_WRITETEXT, offset, 4, (int)&tmp)) return "text not writable (probably shared)"; } else rv = ptrace(PTRACE_WRITETEXT, offset, w, (int)buf); } } else if (fd == USERFD) { int *ip = (int *)buf; if ( r ) { do { *ip++ = ptrace(PTRACE_PEEKUSER, offset); offset += 4; r -= 4; } while (r > 0 && errno == 0); } else { do { ptrace(PTRACE_POKEUSER, offset, *ip++); offset += 4; w -= 4; } while (w > 0 && errno == 0); } rv = errno; } if (wasrunning && !rv) run(); return rv == 0 ? 0 : "bad ptrace call"; } char *HostCore::regrw(long offset, char *buf, int r, int w) { if ( online() ) { regs rg; if ( ptrace(PTRACE_GETREGS, (int)&rg) ) return "Can't read registers"; if ( r ) ::bcopy( (char*)&rg + offset, buf, r); else { ::bcopy( buf, (char*)&rg + offset, w); ptrace(PTRACE_SETREGS, (int)&rg); } } else { if ( r ) ::bcopy( (char*)&bsdcore.c_regs + offset, buf, r); else if ( w ) ::bcopy( buf, (char*)&bsdcore.c_regs + offset, w); } return 0; } #define CYCLE 4 Cslfd *HostCore::peek(long loc, Cslfd *fail) { static i; static Cslfd *c; UCslfd u; if( read(loc, (char*)&u, 8) ) return Core::peek(loc, fail); if( !c ) c = new Cslfd[CYCLE]; Cslfd *p = c+(i++,i%=CYCLE); p->chr = u.chr; p->sht = u.sht; p->lng = u.lng; p->flterr = 0; p->flt = u.flt; p->dbl = u.dbl; return p; } char *HostCore::peekstring(long loc, char *fail) { static char buf[256]; int cnt = 255; if ( online() && (loc + 255) > SYSADR && loc < SYSADR ) cnt = SYSADR - loc; char *error = read(loc, buf, cnt); if( error ) return fail ? fail : strcpy(buf,error); return buf; } int HostCore::instack(long curfp, long prevfp ) { return ((curfp&0xf8000000)==0x08000000) && ((curfp&0xff000000)!=0x0f000000) && (curfp>prevfp); } CallStk *HostCore::callstack() // do we have to peek everything twice? { long size; long _fp = fp(); if( !instack(_fp, 0x08000000) || _fp >= 0x0f000000) return (CallStk *)0; for( size = 1; size<1000; ++size ){ long __fp = peek(_fp)->lng; if( !instack(__fp, _fp) ) break; _fp = __fp; } CallStk *c = new CallStk(size, this); _fp = fp(); long _pc = pc(); for( long i = 0; i < size; ++i ){ c->fpf[i].fp = _fp; c->fpf[i].func = (Func*) _symtab->loctosym(U_FUNC, _pc); _pc = peek(_fp+4)->lng; _fp = peek(_fp)->lng; } return c; } const short LINKA6=0x4E56, ADDLSP=0xDFFC, MOVEMLSP=0x48D7; const short LEASPREL=0x4fef, ADDQLSP=0x500f, ADDQLSPMASK=0xf12f; Frame HostCore::frameabove(long _fp) { Frame f(this); if( _fp ){ f.pc = peek(_fp+4)->lng; f.fp = peek(_fp)->lng; } else { f.pc = pc(); f.fp = fp(); } f.ap = f.fp; int callpc = peek(f.fp+4)->lng; int inst = peek(callpc)->sht; if (inst == LEASPREL) f.nargs = peek(callpc+2)->sht; else if ((inst & ADDQLSPMASK) == ADDQLSP) { f.nargs = (inst >> 9) & 7; if (f.nargs == 0) f.nargs = 8; } else f.nargs = 0; f.nargs /= 4; f.regbase = f.fp; int faddr = ((Func*)_symtab->loctosym(U_FUNC, f.pc))->range.lo; if (peek(faddr)->sht == LINKA6) { f.regbase += peek(faddr+2)->sht; faddr += 4; } if (peek(faddr)->sht == ADDLSP) { f.regbase += peek(faddr+2)->lng; faddr += 6; } if (peek(faddr)->sht == MOVEMLSP) f.regsave = peek(faddr+2)->sht; else f.regsave = 0; return f; } char *HostCore::signalmask(long mask) { sigmask = mask; return 0; } char *HostCore::exechang(long ehang) { if (!ehang) return "Must hang on exec"; return 0; } #define PSW_T 0x00008000 char *HostCore::pswT(long psw_loc, int t) { long ps; char *error = read(psw_loc, (char*) &ps, 4 ); if( error ) return error; if( t ) ps |= PSW_T; else ps &= ~PSW_T; return write(psw_loc, (char*)&ps, 4); } const short M68K_TRAP0=0x4E40, M68K_RTS = 0x4E75, M68K_UNLNKA6 = 0x4E5E; char *HostCore::popcallstack() { long regaddr, savepc; char *error = 0; int i; short saveinst; Cslfd *c; if( state != STOPPED ) return "pop callstack: process not stopped"; savepc = pc(); if( peek(savepc-2)->sht == M68K_TRAP0 ) return "pop callstack: process in system call"; c = peek(savepc, 0); if( !c ) return "cannot pop callstack"; saveinst = c->sht; // Must be careful if we are in the middle of the call sequence switch (saveinst) { case M68K_RTS: return step(); case LINKA6: goto rts; case MOVEMLSP: goto unlink; } // Sometimes the space is added after the link if( peek(savepc-2)->sht == LINKA6 ) goto rts; // Restore the registers, except the frame pointer and sp { // This is here because C++ bitches about gotos if it isn't Frame f = frameabove(0); for (i = 0; i < 14; i++) if (regaddr = saved(&f, i, 4)) regpoke(i, peek(regaddr)->lng); } // Pop the frame unlink: if( !error ) error = poke(savepc, M68K_UNLNKA6, 2); if( !error ) error = step(); if( !error ) regpoke(REG_PC(), savepc); // Return from the subroutine rts: if( !error ) error = poke(savepc, M68K_RTS, 2); if( !error ) error = step(); // Restore the instruction poke(savepc, saveinst, 2); return error; } char *HostCore::step(long lo, long hi) { return dostep(lo,hi,1); } const short M68K_BSR = 0x6100, M68K_JSR = 0x4e80; const short M68K_BSR_MSK = 0xff00, M68K_JSR_MSK = 0xffc0; char *HostCore::stepoverM68KJSB() { char *error = 0; static Trap *t; short inst; long fp0, offset; if(!t) t = new Trap(new Stmt(0,0,0),0); inst = peek(pc())->sht; /* * Determine where to put the breakpoint depending on the * addressing mode of the instruction. */ if ((inst & M68K_BSR_MSK) == M68K_BSR) { inst &= 0xff; if (inst == 0) offset = 4; else if (inst == 0xff) offset = 6; else offset = 2; } else { /* Its a JSR */ short reg, mode; reg = inst & 07; mode = (inst >> 3) & 0x7; if (mode == 7) { if (reg == 1) offset = 6; else if (reg == 0 || reg == 2) offset = 4; else goto indexed; } else if (mode == 2) offset = 2; else if (mode == 5) offset = 4; else { indexed: inst = peek(pc()+2)->sht; offset = 4; /* Full format extension */ if (inst & 0x0100) { /* Base Displacement */ if (inst & 0x0020) { if (inst & 0x0010) offset += 4; else offset += 2; } /* Outer Displacement */ if (inst & 0x0002) { if (inst & 0x0001) offset += 4; else offset += 2; } } } } t->stmt->range.lo = pc()+offset; fp0 = fp(); if(error = laybpt(t)) return error; for(;;) { error = dostep(0,0,0); if (error || fp() >= fp0) break; if( error = liftbpt(t) ) return error; if( error = dostep(0,0,1) ) return error; if( error = laybpt(t) ) return error; } if( !error ) error = liftbpt(t); else liftbpt(t); return error; } static jmp_buf saveenv; void longjmp(jmp_buf, int); int setjmp(jmp_buf); static void SigCatch(int) { longjmp(saveenv, 1); } const int STEPWAIT = 15; char *HostCore::waitstop(int flags) { int wret; SIG_TYP oldsig = ::signal(SIGALRM, (SIG_TYP)&SigCatch); int oldalarm = ::alarm(STEPWAIT); if( ::setjmp(saveenv) ){ ::alarm(oldalarm); ::signal(SIGALRM, (SIG_TYP)oldsig); ::kill(pid, SIGSTOP); return sf("timeout (%d secs)", STEPWAIT); } wret = waitlist.wait(this, flags); ::alarm(oldalarm); ::signal(SIGALRM, (SIG_TYP)oldsig); if (!wret ) return "Unexpected wait error"; if (state != STOPPED) return "Process exited"; return 0; } char *HostCore::dostep(long lo, long hi, int sstep) { char *error = 0; long fp0, time0, time(long); short inst; time0 = ::time(0L); fp0 = fp(); for(;;){ if( hi && (((inst=peek(pc())->sht) & M68K_BSR_MSK) == M68K_BSR || (inst & M68K_JSR_MSK) == M68K_JSR)) { error = stepoverM68KJSB(); goto next; } if (sstep) ptrace(PTRACE_SINGLESTEP, 1); else error = run(); if( !error ) error = waitstop(sstep ? 0 : WAIT_PCFIX); if( !error && event()!=SIGTRAP ) error = sf( "single step error. signal=%d", event() ); if( !error ) error = clrcurrsig(); next: if( error ) return error; if( !hi || (fp()>fp0 && peek(pc())->sht != M68K_RTS) || pc()=hi) return 0; if( ::time(0L) > time0+STEPWAIT ) return sf("single step timeout (%d secs)",STEPWAIT); } } char *HostCore::stepprolog() { process()->stmtstep(1); return 0; } char *HostCore::resources() { // static char buf[64]; // timeval t; // // sprintf( buf, "%d\.%du %d\.%ds", // u()->u_vm.vm_utime/50, u()->u_vm.vm_stime/50 ); // return buf; return ""; } char *HostCore::destroy() { char *error; if ( state == EXITED || state == DIED) return 0; clrcurrsig(); if (state == RUNNING) { stop(); if (error = waitstop(WAIT_PCFIX)) return error; } ptrace(PTRACE_KILL); waitlist.wait(this, WAIT_DISCARD|WAIT_POLL); state = EXITED; cursig = SIGKILL; return 0; } char *HostCore::clrcurrsig() { cursig = 0; return 0; } char *HostCore::sendsig(long sig) { if( !online() ) return "send signal: process not live"; ::kill(pid, sig); return 0; } char *HostCore::docall(long addr, int numarg) { const int CALL_SEQ=0x2000, CALL_SIZE=12, JSR=0x4eb9, ADDSP=0xdffc; char save[CALL_SIZE], *error; int i; if( state != STOPPED ) return "process not stopped"; if( !online() ) return "cannot restart dump"; for( i = 0; i < CALL_SIZE; ++i ) save[i] = peek(CALL_SEQ+i)->chr; if( ( error = poke(CALL_SEQ+0, JSR, 2) ) || ( error = poke(CALL_SEQ+2, addr, 4) ) || ( error = poke(CALL_SEQ+6, ADDSP, 2) ) || ( error = poke(CALL_SEQ+8, numarg * 4, 4) ) ) return error; if( ( error = regpoke(REG_PC(), CALL_SEQ) ) || ( error = step( CALL_SEQ, CALL_SEQ+CALL_SIZE) ) ) return error; for( i = 0; i < CALL_SIZE; ++i ) if( error = poke(CALL_SEQ+i, save[i], 1 ) ) return error; return 0; } long HostCore::apforcall(int argbytes) { regpoke(REG_SP(), sp() - argbytes ); return sp() - 8; } long HostCore::returnregloc() { return regloc(0); } Context* HostCore::newContext() { HostContext *cc = new HostContext; cc->error = 0; cc->core = this; cc->pending = 0; if( state != STOPPED ) cc->error = "context save: process not stopped"; else if( peek(pc()-2)->sht == M68K_TRAP0 ) cc->error = "context save: process in system call"; else if( cc->pending = cursig ) cc->error = clrcurrsig(); if( !cc->error ) for( int i = 0; i < 18; ++i ) cc->regs[i] = regpeek(i); return cc; } void HostContext::restore() { if( pending ) core->cursig = pending; for( int i = 0; i < 18; ++i ) if( error = core->regpoke(i, regs[i]) ) return; }