/* * spy IO management */ #define _KMEMUSER /* yuk */ #include #include "spyCommon.h" #include "spyBase.h" #include "spyIO.h" #include #include #include #include #include #include #include #include #include #include #include ulong_t spyTrace; /* Debug trace flags */ int spyTraceIndent; /* Debug trace indentation - not thread safe */ static int spyTraceFile = 1; /* Debug trace fd */ /* Register sets: * * The format of the register set is determined by the target process * architecture/ABI. The caller may request we copy or widen this * format - we reject 64->32 bit conversion requests. */ /* We use the procfs format (gregset_t, fpregset_t) buffers and must * translate them to pthread library jmpbuf[]s. * Kernel contexts are larger because they must save _all_ registers * but pthreads only needs the callee preserved ones. O32 (mips1/mips2) jmp_buf[] [02..13] sp, pc, v0, s0..s8, [14..25] f21,f20,f23,f22,f25,f24,f27,f26,f29,f28,f31,f30 [25] sr N32/N64 (mips3/mips4) jmp_buf[] [00..11] sp, pc, v0, s0..s8, N32 [12,14,15,16,17] f20, f22, f24, f26, f28 N64 [12..17] f24..f29, [18..19] f30, f31, [20..21] sr, gp */ /* Map jmpbuf[] to gregset_t * Either 32 or 64 bit registers. * jbXlate maps jmpbuf[...] to gregset[...] */ static int jbRegXlate32[_JBLEN] = { /* _MIPS_SIM_ABI32 */ 0, 0, CTX_SP, CTX_EPC, CTX_V0, CTX_S0, CTX_S1, CTX_S2, CTX_S3, CTX_S4, CTX_S5, CTX_S6, CTX_S7, CTX_S8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* No CTX_SR for mips2, CTX_GP is callee saved */ }; static int jbRegXlate64[_JBLEN] = { /* _MIPS_SIM_NABI32, _MIPS_SIM_ABI64 */ CTX_SP, CTX_EPC, CTX_V0, CTX_S0, CTX_S1, CTX_S2, CTX_S3, CTX_S4, CTX_S5, CTX_S6, CTX_S7, CTX_S8, 0, 0, 0, 0, 0, 0, 0, 0, CTX_SR, CTX_GP }; /* Map jmpbuf[] to fpregset_t * Format depends on ABI. */ #define FPREGSTART 12 static int jbFPRegXlateO32[_JBLEN - FPREGSTART] = { /* _MIPS_SIM_ABI32 */ 0, 0, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 }; static int jbFPRegXlateN32[_JBLEN - FPREGSTART] = { /* _MIPS_SIM_NABI32 */ 20, 0, 22, 24, 26, 28, 30, 31 }; static int jbFPRegXlateN64[_JBLEN - FPREGSTART] = { /* _MIPS_SIM_ABI64 */ 24, 25, 26, 27, 28, 29, 30, 31 }; /* Extract gregs data from jmpbuf. */ int ioXJBGregs(spyProc_t* p, int destFormat, caddr_t buf, void* outp) { typedef irix5_gregset_t irix5_32_gregset_t; int j; TEnter( TIO, ("ioXJBGregs(0x%p, 0x%p, 0x%p)\n", p, buf, outp) ); if (p->sp_abi != SP_O32 && destFormat == SP_O32) { TReturn (EINVAL); } /* Very nasty - jmpbuf to gregset copier */ # define XLATEGREGS(src, dst) \ { \ __uint ## src ## _t* jbuf = (__uint ## src ## _t*)buf; \ __uint ## dst ## _t* greg = (__uint ## dst ## _t*)outp; \ \ memset(greg, 0, sizeof(irix5_ ## dst ## _gregset_t)); \ for (j = 0; j < _JBLEN; j++) { \ if (jbRegXlate ## src [j]) { \ greg[jbRegXlate ## src [j]] = jbuf[j]; \ } \ } \ } /* There are two formats for the general jmpbuf regs. * We just copy straight or widen to 64. */ if (destFormat == SP_O32) { XLATEGREGS(32, 32); /* 32 bit copy */ } else if (p->sp_abi == SP_O32) { XLATEGREGS(32, 64); /* 32 to 64 bit copy */ } else { XLATEGREGS(64, 64); /* 64 bit copy */ } # undef XLATEGREGS TReturn (0); } /* Extract fpregs data from jmpbuf. */ int ioXJBFPregs(spyProc_t* p, int destFormat, caddr_t buf, void* outp) { typedef irix5_fpregset_t irix5_32_fpregset_t; int r; int j; TEnter( TIO, ("ioXJBFPregs(0x%p, 0x%p, 0x%p)\n", p, buf, outp) ); if (p->sp_abi != SP_O32 && destFormat == SP_O32) { TReturn (EINVAL); } /* Even nastier - jmpbuf to fpregset copier */ # define XLATEFPREGS(bits, tgt) \ { \ __uint ## bits ## _t* jbuf = (__uint ## bits ## _t*)buf; \ __uint ## bits ## _t* freg = ((irix5_ ## bits ## _fpregset_t*)outp) \ ->fp_r.fp_regs; \ int* xlate = jbFPRegXlate ## tgt; \ \ memset(freg, 0, sizeof(irix5_ ## bits ## _fpregset_t)); \ for (j = FPREGSTART, r = 0; j < _JBLEN; j++, r++) { \ if (xlate[r]) { \ freg[xlate[r]] = jbuf[j]; \ } \ } \ } /* ABIs differ in jmpbuf layout for fp regs. * Don't do widening, just copy as the process ABI dictates. */ switch (p->sp_abi) { case SP_O32 : XLATEFPREGS(32, O32); /* 32 bit copy */ break; case SP_N32 : XLATEFPREGS(64, N32); /* 64 bit copy, n32 */ break; case SP_N64 : XLATEFPREGS(64, N64); /* 64 bit copy, n64 */ break; } # undef XLATEFPREGS TReturn (0); } /* Insert gregs data to jmpbuf. */ int ioPlaceGregsJB(spyProc_t* p, int srcFormat, void* inp, caddr_t buf, off64_t addr) { int j; size_t size; TEnter( TIO, ("ioPlaceGregsJB(0x%p, 0x%p, %#llx)\n", p, inp, addr) ); if (srcFormat != SP_O32 && p->sp_abi == SP_O32) { TReturn (EINVAL); } /* gregset to jmpbuf copier */ # define XLATEGREGS(src, dst) \ { \ __uint ## src ## _t* greg = (__uint ## src ## _t*)inp; \ __uint ## dst ## _t* jbuf = (__uint ## dst ## _t*)buf; \ \ size = _JBLEN * sizeof(*jbuf); /* side effect */ \ for (j = 0; j < _JBLEN; j++) { \ if (jbRegXlate ## dst [j]) { \ jbuf[j] = greg[jbRegXlate ## dst [j]]; \ } \ } \ } /* There are two formats for the general jmpbuf regs. * We just copy straight or widen to 64 in a local buffer. */ if (p->sp_abi == SP_O32) { XLATEGREGS(32, 32); /* 32 bit copy */ } else if (srcFormat == SP_O32) { XLATEGREGS(32, 64); /* 32 to 64 bit copy */ } else { XLATEGREGS(64, 64); /* 64 bit copy */ } # undef XLATEGREGS /* Write the regs data to the target. */ TReturn (ioPlaceBytes(p, addr, buf, size)); } /* Insert fpregs data to jmpbuf. */ int ioPlaceFPregsJB(spyProc_t* p, int srcFormat, void* inp, caddr_t buf, off64_t addr) { typedef irix5_fpregset_t irix5_32_fpregset_t; int r; int j; size_t size; TEnter( TIO, ("ioPlaceFPregsJB(0x%p, 0x%p, %#llx)\n", p, inp, addr) ); if (srcFormat != SP_O32 && p->sp_abi == SP_O32) { TReturn (EINVAL); } /* fpregset to jmpbuf copier */ # define XLATEFPREGS(bits, tgt) \ { \ __uint ## bits ## _t* freg = ((irix5_ ## bits ## _fpregset_t*)inp) \ ->fp_r.fp_regs; \ __uint ## bits ## _t* jbuf = (__uint ## bits ## _t*)buf; \ int* xlate = jbFPRegXlate ## tgt; \ \ size = _JBLEN * sizeof(*jbuf); /* side effect */ \ for (j = FPREGSTART, r = 0; j < _JBLEN; j++, r++) { \ if (xlate[r]) { \ jbuf[j] = freg[xlate[r]]; \ } \ } \ } /* ABIs differ in jmpbuf layout for fp regs. * Don't do widening, just copy as the process ABI dictates. */ if (p->sp_abi == SP_O32) { XLATEFPREGS(32, O32); /* 32 bit copy, O32 */ } else if (srcFormat == SP_O32) { switch (p->sp_abi) { case SP_N32 : XLATEFPREGS(32, N32); /* 32 bit copy, n32 */ break; case SP_N64 : XLATEFPREGS(32, N64); /* 32 bit copy, n64 */ break; } } else { switch (p->sp_abi) { case SP_N32 : XLATEFPREGS(64, N32); /* 64 bit copy, n32 */ break; case SP_N64 : XLATEFPREGS(64, N64); /* 64 bit copy, n64 */ break; } } # undef XLATEFPREGS /* Write the regs data to the target. */ TReturn (ioPlaceBytes(p, addr, buf, size)); } void ioXPointer(spyProc_t* p, caddr_t buf, off64_t* outp) { switch (p->sp_abi) { case SP_O32 : case SP_N32 : *outp = (off64_t)(*(__uint32_t*)buf); break; case SP_N64 : *outp = (off64_t)(*(__uint64_t*)buf); break; } TTrace( TIO, ("ioXPointer(0x%p) %#llx\n", p, *outp) ); } /* ARGSUSED */ void ioXInt(spyProc_t* p, caddr_t buf, uint_t* outp) { *outp = *(uint_t*)buf; TTrace( TIO, ("ioXInt(0x%p) %#x\n", p, *outp) ); } /* ARGSUSED */ void ioXShort(spyProc_t* p, caddr_t buf, ushort_t* outp) { *outp = *(ushort_t*)buf; TTrace( TIO, ("ioXShort(0x%p) %#x\n", p, *outp) ); } int ioFetchPointer(spyProc_t* p, off64_t addr, off64_t* out) { off64_t buf; int e; TEnter( TIO, ("ioFetchPointer(0x%p, %#llx)\n", p, addr) ); if (e = ioFetchBytes(p, addr, (caddr_t)&buf, IOPTRSIZE(p))) { TReturn (e); } ioXPointer(p, (caddr_t)&buf, out); TReturn (0); } int ioFetchInt(spyProc_t* p, off64_t addr, uint_t* out) { uint_t buf; int e; TEnter( TIO, ("ioFetchInt(0x%p, %#llx)\n", p, addr) ); if (e = ioFetchBytes(p, addr, (caddr_t)&buf, 4)) { TReturn (e); } ioXInt(p, (caddr_t)&buf, out); TReturn (0); } int ioFetchShort(spyProc_t* p, off64_t addr, ushort_t* out) { ushort_t buf; int e; TEnter( TIO, ("ioFetchShort(0x%p, %#llx)\n", p, addr) ); if (e = ioFetchBytes(p, addr, (caddr_t)&buf, 2)) { TReturn (e); } ioXShort(p, (caddr_t)&buf, out); TReturn (0); } /* Read bytes using call back or procfs */ int ioFetchBytes(spyProc_t* p, off64_t addr, caddr_t out, size_t size) { ssize_t e; TEnter( TIO, ("ioFetchBytes(0x%p, %#llx, %#lx)\n", p, addr, size)); if (!SP_LIVE(p)) { TReturn (SPC(spc_memory_read, p, 0, addr, out, size)); } if ((e = pread64(p->sp_procfd, out, size, addr)) != size) { TReturn ((e == -1) ? errno : EAGAIN); } TReturn (0); } /* Read thread specific bytes using call back or procfs */ int iotFetchBytes(spyProc_t* p, tid_t t, off64_t addr, caddr_t out, size_t size) { prthreadctl_t ptc; prio_t pi; TEnter( TIO, ("iotFetchBytes(0x%p, %#x, %#llx, %#lx)\n", p, t, addr, size)); if (!SP_LIVE(p)) { TReturn (SPC(spc_memory_read, p, t, addr, out, size)); } ptc.pt_tid = t; ptc.pt_flags = PTFD_EQL; ptc.pt_cmd = PIOCREAD; /* always use native abi */ ptc.pt_data = (caddr_t)π pi.pi_offset = addr; pi.pi_base = out; pi.pi_len = (ssize_t)size; TTrace( TPR, ("iotFetchBytes: procfs(%d, %#x:READ, 0x%p)\n", p->sp_procfd, ptc.pt_tid, &ptc) ); if (ioctl(p->sp_procfd, PIOCTHREAD, &ptc) != 0) { TReturn (errno); } TReturn (0); } /* Fetching general register values using spy call back * No translation is necessary */ int iotFetchGregs(spyProc_t* p, tid_t t, caddr_t out) { void* regs = alloca(sizeof(irix5_64_mcontext_t)); void* addr; size_t size; int e; TEnter( TIO, ("iotFetchGregs(0x%p, %#x)\n", p, t) ); if (e = SPC(spc_register_read, p, t, regs, 0, 0)) { TReturn (e); } switch (p->sp_abi > IONATIVE ? p->sp_abi : IONATIVE) { case SP_O32 : size = sizeof(irix5_gregset_t); addr = &((irix5_mcontext_t*)regs)->gregs; break; case SP_N32 : case SP_N64 : size = sizeof(irix5_64_gregset_t); addr = &((irix5_64_mcontext_t*)regs)->gregs; break; } memcpy(out, addr, size); TReturn (0); } /* Fetching floating point register values using spy call back * No translation is necessary */ int iotFetchFPregs(spyProc_t* p, tid_t t, caddr_t out) { void* regs = alloca(sizeof(irix5_64_mcontext_t)); void* addr; size_t size; int e; TEnter( TIO, ("iotFetchFPregs(0x%p, %#x)\n", p, t) ); if (e = SPC(spc_register_read, p, t, regs, 0, 0)) { TReturn (e); } switch (p->sp_abi > IONATIVE ? p->sp_abi : IONATIVE) { case SP_O32 : size = sizeof(irix5_fpregset_t); addr = &((irix5_mcontext_t*)regs)->fpregs; break; case SP_N32 : case SP_N64 : size = sizeof(irix5_64_fpregset_t); addr = &((irix5_64_mcontext_t*)regs)->fpregs; break; } memcpy(out, addr, size); TReturn (0); } /* Write bytes using procfs */ int ioPlaceBytes(spyProc_t* p, off64_t addr, caddr_t out, size_t size) { ssize_t e; TEnter( TIO, ("ioPlaceBytes(0x%p, %#llx, %#lx)\n", p, addr, size)); if (!SP_LIVE(p)) { TReturn (EACCES); } if ((e = pwrite64(p->sp_procfd, out, size, addr)) != size) { TReturn ((e == -1) ? errno : EAGAIN); } TReturn (0); } char* ioToken(char* first, char** rest) { # define WHITESPACE " \t\n" size_t len; first += strspn(first, WHITESPACE); if ((len = strcspn(first, WHITESPACE)) == 0) { return (0); } first[len] = '\0'; *rest = first + len + 1; return (first); } int ioListScan(spyProc_t* p, off64_t qHead, int (*func)(spyProc_t*, off64_t, void*), void* arg) { off64_t qNext = qHead; int e; TEnter( TIO, ("ioListScan(0x%p, %#llx, 0x%p, 0x%p)\n", p, qHead, func, arg)); while (!(e = ioFetchPointer(p, qNext, &qNext)) && qNext != qHead) { if (e = func(p, qNext, arg)) { TReturn (e); } } TReturn (e); } void ioSetTrace(char* flags) { char* file; int fd; if ((file = getenv("SPY_TRACE_FILE")) && (fd = open(file, O_APPEND|O_CREAT|O_WRONLY, 0777)) >= 0) { spyTraceFile = fd; } if (!flags) { return; } spyTrace = strtol(flags, 0, 0); } void ioTrace(char* fmt, ...) { char buf[256]; int cnt; va_list va; va_start(va, fmt); cnt = vsprintf(buf, fmt, va); va_end(va); write(spyTraceFile, buf, cnt); }