#include "process.pub" #include "frame.pri" #include "symtab.pri" #include "symbol.h" #include "hostcore.h" #include <a.out.h> #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 <setjmp.h> static WaitList waitlist; int kill(int,int); const int STOPPED=0, RUNNING=1, EXITED=2, DIED=3; const REGADDR = USRADR - 18 * 4; Behavs HostCore::behavetype() { return behavs(); } char *HostCore::eventname() { return SignalName(event()); } char *HostCore::stop() { ::kill(pid, SIGSTOP); return 0; } int HostCore::event() { return cursig; } long HostCore::regaddr() { return REGADDR; } long HostCore::scratchaddr() { return 0x2000; } 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; } #define SUNBPT 0x4e4f /* Trap #15 */ char *HostCore::laybpt(Trap *t) { Cslfd *cp = peek(t->stmt->range.lo, 0); if (!cp) return "text not readable (probably shared)"; t->saved = cp->sht; return poke(t->stmt->range.lo, SUNBPT, 2); } 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::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 = N_TXTADDR(bsdcore.c_aouthdr) + bsdcore.c_tsize; startdata = N_DATADDR(bsdcore.c_aouthdr); 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(); } 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); cursig = SIGSTOP; } stabfd = ::open(stabpath(),0); if( stabfd<0 ) { if( online() ) { ::kill(pid, SIGSTOP); ptrace(PTRACE_DETACH, 1); } return SysErr( "symbol tables: " ); } stabfstat(); BsdSymTab *bsdsp = new BsdSymTab(this, stabfd, _symtab); _symtab = bsdsp; _symtab->read(); // Can't find endtext from U structure in 4.0 release, this is a kludge endtext = bsdsp->endtext(); 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; // Can't find endtext from U structure in 4.0 release, this is a kludge BsdSymTab *bsdsp = (BsdSymTab *)_symtab; endtext = bsdsp->endtext(); 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 += N_TXTOFF(bsdcore.c_aouthdr) - N_TXTADDR(bsdcore.c_aouthdr); 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; } int HostCore::instack(long curfp, long prevfp ) { return ((curfp&0xf8000000)==0x08000000) && ((curfp&0xff000000)!=0x0f000000) && (curfp>prevfp); } int HostCore::fpvalid(long fp) { return (instack(fp, 0x08000000) && fp < 0x0f000000); } 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); } 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; } const short M68K_TRAP0=0x4E40, M68K_RTS = 0x4E75; char *HostCore::dostep(long lo, long hi, int sstep) { char *error = 0; long fp0, time0, time(long); time0 = ::time(0L); fp0 = fp(); for(;;){ if( hi && isM68KJSB(peek(pc())->sht) ) { 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 || pc()<lo || pc()>=hi || (fp()>fp0 && peek(pc())->sht != M68K_RTS) ) return 0; if( ::time(0L) > time0+STEPWAIT ) return sf("single step timeout (%d secs)",STEPWAIT); } } 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; } 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; }