#include "precomp.h" #pragma hdrstop #define OPCODE(x) ((WORD)((x & 0xFFFF0000) >>16)) #define ADDRESS(x) ((WORD)(x & 0x0000FFFF)) BOOL FIsDirectJump( BYTE * rgbBuffer, DWORD cbBuff, HTHDX hthd, UOFFSET uoffset, UOFFSET * lpuoffThunkDest, LPDWORD lpdwThunkSize ) { // MIPS direct (ilink) thunk looks like this: // 0x03e04025 or t0,ra,zero // 0x04110001 bgezal zero,0xc // 0x00000000 nop // 0x8fe90014 lw t1,20(ra) // 0x013f4821 addu t1,t1,ra // 0x25290018 addiu t1,t1,24 // 0x01200008 jr t1 // 0x0100f825 or ra,t0,zero // 0xdeadbeef ld t5,-16657(s5) return FALSE; } BOOL FIsIndirectJump( BYTE * rgbBuffer, DWORD cbBuff, HTHDX hthd, UOFFSET uoffset, UOFFSET * lpuoffThunkDest, LPDWORD lpdwThunkSize ) { // MIPS indirect (dll import) thunk either looks like this: // R4k // 0x00, 0x00, 0x08, 0x3C, // lui $8,IAT // 0x00, 0x00, 0x08, 0x8D, // lw $8,IAT($8) // 0x08, 0x00, 0x00, 0x01, // jr $8 // 0x00, 0x00, 0x00, 0x00, // nop (delay slot) // or like this: // R3k // 0x00, 0x00, 0x08, 0x3C, // lui $8,IAT // 0x00, 0x00, 0x08, 0x8D, // lw $8,IAT($8) // 0x00, 0x00, 0x00, 0x00, // nop (Required for R3000) // 0x08, 0x00, 0x00, 0x01, // jr $8 // 0x00, 0x00, 0x00, 0x00, // nop (delay slot) if (cbBuff >= 16) { DWORD *Inst = (DWORD *)rgbBuffer; DWORD ThunkSize = 0; if ((OPCODE(*(Inst+0)) == 0x3C08) && (OPCODE(*(Inst+1)) == 0x8D08) && (*(Inst+2) == 0x01000008) && (*(Inst+3) == 0x00000000)) { // We've got a R4K thunk. ThunkSize = 16; } else { if (cbBuff >= 20) { if ((OPCODE(*(Inst+0)) == 0x3C08) && (OPCODE(*(Inst+1)) == 0x8D08) && (*(Inst+2) == 0x00000000) && (*(Inst+3) == 0x01000008) && (*(Inst+4) == 0x00000000)) { // We've got a R3K thunk. ThunkSize = 20; } } } if (ThunkSize) { DWORD Address; Address = ADDRESS(*(Inst+0)) << 16 + ADDRESS(*(Inst+1)); if ( DbgReadMemory ( hthd->hprc, (LPCVOID)Address, lpuoffThunkDest, sizeof(UOFFSET), NULL) ) { *lpdwThunkSize = ThunkSize; return TRUE; } } } return FALSE; } BOOL FIsVCallThunk( BYTE * rgbBuffer, DWORD cbBuff, HTHDX hthd, UOFFSET uoffset, UOFFSET * lpuoffThunkDest, LPDWORD lpdwThunkSize ) { return FALSE; } BOOL FIsVTDispAdjustorThunk( BYTE * rgbBuffer, DWORD cbBuff, HTHDX hthd, UOFFSET uoffset, UOFFSET * lpuoffThunkDest, LPDWORD lpdwThunkSize ) { return FALSE; } BOOL FIsAdjustorThunk( BYTE * rgbBuffer, DWORD cbBuff, HTHDX hthd, UOFFSET uoffset, UOFFSET * lpuoffThunkDest, LPDWORD lpdwThunkSize ) { return FALSE; } BOOL GetPMFDest( HTHDX hthd, UOFFSET uThis, UOFFSET uPMF, UOFFSET *lpuOffDest ) { return FALSE; } #if 0 /* !!! HACK ALERT !!! This is a quick fix for V2 V3 should do the following or some variant 1) SAPI should return a non-null hproc when sitting at an import thunk. windbg's SH does this but dolphin returns null. 2) x86 and mips backend should mark adjustor thunks as S_THUNK32 rather than S_GPROC32 and S_GPROCMIPS 3) Then we could return CANSTEP_THUNK to DM and it would handle this nasty pseudo-emulation 4) Dolphin EE does some thunk manipulation to print out function pointers symbolicly -- Perhaps use a thunkhlp.lib to deal with all this in one place */ class REGISTERS { UOFFSET m_regs[32]; BOOL m_fValid[32]; public: REGISTERS () { int i; memset (m_regs, 0, sizeof (m_regs)); for (i = 0; i < sizeof (m_fValid) / sizeof (m_fValid[0]); i++) { m_fValid[i] = FALSE; } } UOFFSET & operator[] (int i) { if (!m_fValid[i]) { // initialize on first access. SHREG shreg; shreg.hReg = (unsigned short) (CV_M4_IntZERO + i); DHGetReg (&shreg, NULL); m_regs[i] = shreg.Byte4; m_fValid[i] = TRUE; } return (m_regs[i]); } }; #include // for PMIPS_INSTRUCTION /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * ThunkTarget * Input * hpid,htid * lpThunkAddr: Address of thunk * Output * BOOL: True if address was thunk and target was updated * target: Destination of thunk * In/Out: * regs: registers used by thunk [updated -- in case the next thunk uses them] * Factor out common code for dbcCanStep and dbcExitedFunction. *-------------------------------------------------------------------------------- */ static BOOL ThunkTarget ( HPID hpid, HTID htid, LPADDR lpThunkAddr, DWORD& target, REGISTERS& regs ) { const int MIPS_THUNK_SIZE = 10; // Largest thunk I can think of DWORD cbRead; MIPS_INSTRUCTION buffer[MIPS_THUNK_SIZE]; PMIPS_INSTRUCTION pInst = buffer; PMIPS_INSTRUCTION pInstMax; UOFFSET source; BOOL isThunk = FALSE; source = GetAddrOff (*lpThunkAddr); cbRead = DHGetDebuggeeBytes (*lpThunkAddr, sizeof (buffer), buffer); if (cbRead < sizeof(buffer)) { return(FALSE); } pInstMax = pInst + (cbRead / sizeof (buffer[0])); for (; !isThunk && (pInst < pInstMax); pInst++) { switch (pInst->j_format.Opcode) { case J_OP: target = (source & 0xf0000000) | (pInst->j_format.Target << 2); isThunk = TRUE; break; case LUI_OP: regs[pInst->i_format.Rt] = (pInst->i_format.Simmediate << 16); break; case LW_OP: { if (pInst->i_format.Rs == 29) { // SP pInst = pInstMax; // leave loop break; } UOFFSET uoff = regs[pInst->i_format.Rs]; uoff += pInst->i_format.Simmediate; ADDR addr2 = *lpThunkAddr; SE_SetAddrOff (&addr2, uoff); if (DHGetDebuggeeBytes (addr2, sizeof (regs[0]), ®s[pInst->i_format.Rt]) == 0) { ASSERT (FALSE); } } break; case ADDI_OP: case ADDIU_OP: regs[pInst->i_format.Rt] = regs[pInst->i_format.Rs] + pInst->i_format.Simmediate; break; case SPEC_OP: switch (pInst->r_format.Function) { case JR_OP: if (pInst->r_format.Rs != 31) { target = regs[pInst->r_format.Rs]; isThunk = TRUE; } pInst = pInstMax; // leave loop break; case OR_OP: regs[pInst->r_format.Rd] = regs[pInst->r_format.Rs] | regs[pInst->r_format.Rt]; break; case ADD_OP: case ADDU_OP: regs[pInst->r_format.Rd] = regs[pInst->r_format.Rs] + regs[pInst->r_format.Rt]; break; case SLL_OP: if (pInst->Long == 0 || pInst->Long == 0x21) { // nop? break; } // fall-through default: pInst = pInstMax; // leave loop break; } break; case BCOND_OP: switch (pInst->u_format.Rt) { case BGEZAL_OP: if (pInst->r_format.Rs == 0) { regs[31] = source + (pInst - buffer + 2) * sizeof (MIPS_INSTRUCTION); pInst += pInst->i_format.Simmediate; } else { pInst = pInstMax; } break; default: pInst = pInstMax; break; } break; default: pInst = pInstMax; // leave loop break; } } return(isThunk); } /* SHThunkStep Purpose : disassemble/emulate mips thunks to find final destination and test for source code availability. Arguments: hpid,htid - current process/thread srcAddr - original thunk address for the sake of prolog fudging CanStep - returned to DM to indicate how to continue destAddr - current thunk address for disassembly/emulation Returns : TRUE if a destination with source code is found */ static BOOL SHThunkStep ( HPID hpid, HTID htid, LPADDR lpSrcAddr, CANSTEP * CanStep, LPADDR lpDestAddr ) { REGISTERS regs; UOFFSET target = 0; ADDR destAddr; BOOL fStep = FALSE; if (lpDestAddr == NULL) { destAddr = *lpSrcAddr; lpDestAddr = &destAddr; } if (ADDR_IS_LI (*lpDestAddr)) { SYFixupAddr (lpDestAddr); } if (ADDR_IS_LI (*lpSrcAddr)) { SYFixupAddr (lpSrcAddr); } CanStep->Flags = CANSTEP_NO; int ThunkLimit = 8; while (ThunkTarget(hpid, htid, lpDestAddr, target, regs) && --ThunkLimit) { ASSERT(target != 0); ADDR addrTarget = {0}; ADDR_IS_OFF32 (addrTarget) = TRUE; ADDR_IS_FLAT (addrTarget) = TRUE; SE_SetAddrOff (&addrTarget, target); fStep = SYFHasSource (hpid, &addrTarget); CanStep->Flags = fStep ? CANSTEP_YES : CANSTEP_NO; if (CanStep->Flags == CANSTEP_YES) { UOFF32 uoff; CXF Cxf = {0}; ADDR_IS_OFF32 (addrTarget) = TRUE; ADDR_IS_FLAT (addrTarget) = TRUE; SE_SetAddrOff (&addrTarget, target); SYUnFixupAddr (&addrTarget); SHSetCxt (&addrTarget, SHpCXTFrompCXF (&Cxf)); if (SHIsInProlog (&Cxf.cxt)) { uoff = SHGetDebugStart (SHHPROCFrompCXT (SHpCXTFrompCXF (&Cxf))); if (uoff > Cxf.cxt.addr.addr.off) { SE_SetAddrOff (&addrTarget, uoff); SYFixupAddr (&addrTarget); target = GetAddrOff (addrTarget); } } CanStep->PrologOffset = target - GetAddrOff (*lpSrcAddr); break; } else { // check for thunk to thunk by continuing target = 0; *lpDestAddr = addrTarget; } } return fStep; } #endif