diff options
Diffstat (limited to 'src/ToolBox/SOS/Strike/disasmX86.cpp')
-rw-r--r-- | src/ToolBox/SOS/Strike/disasmX86.cpp | 1707 |
1 files changed, 1707 insertions, 0 deletions
diff --git a/src/ToolBox/SOS/Strike/disasmX86.cpp b/src/ToolBox/SOS/Strike/disasmX86.cpp new file mode 100644 index 0000000000..36a08d20a3 --- /dev/null +++ b/src/ToolBox/SOS/Strike/disasmX86.cpp @@ -0,0 +1,1707 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// ==++== +// + +// +// ==--== +#include "strike.h" +#include "util.h" +#include "disasm.h" +#include <dbghelp.h> + +#include "../../../inc/corhdr.h" +#include "../../../inc/cor.h" +#include "../../../inc/dacprivate.h" + + +#if defined(SOS_TARGET_X86) && defined(SOS_TARGET_AMD64) +#error This file does not support SOS targeting both X86 and AMD64 debuggees +#endif + +#if !defined(SOS_TARGET_X86) && !defined(SOS_TARGET_AMD64) +#error This file should be used to support SOS targeting either X86 or AMD64 debuggees +#endif + + +// These must be in the same order as they are used in the instruction +// encodings/same as the CONTEXT field order. +enum RegIndex +{ + EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI, + +#ifdef _TARGET_AMD64_ + R8, R9, R10, R11, R12, R13, R14, R15, +#endif // _TARGET_AMD64_ + + EIP, NONE +}; + +const int NumReg = NONE; +struct Register +{ + TADDR value; + BOOL bValid; + TADDR stack; + BOOL bOnStack; +}; + +// Find the index for a register name +inline RegIndex FindReg (___in __in_z char *ptr, __out_opt int *plen = NULL, __out_opt int *psize = NULL) +{ + struct RegName + { + RegIndex index; + PCSTR pszName; + int cchName; + int size; + }; + + static RegName rgRegNames[] = { + +#define REG(index, reg, size) { index, #reg, sizeof(#reg)-1, size } +#define REG8(index, reg) REG(index, reg, 1) +#define REG16(index, reg) REG(index, reg, 2) +#define REG32(index, reg) REG(index, reg, 4) +#define REG64(index, reg) REG(index, reg, 8) + + REG8(EAX, al), + REG8(EAX, ah), + REG8(EBX, bl), + REG8(EBX, bh), + REG8(ECX, cl), + REG8(ECX, ch), + REG8(EDX, dl), + REG8(EDX, dh), + + REG16(EAX, ax), + REG16(EBX, bx), + REG16(ECX, cx), + REG16(EDX, dx), + REG16(ESI, si), + REG16(EDI, di), + REG16(EBP, bp), + REG16(ESP, sp), + + REG32(EAX, eax), + REG32(EBX, ebx), + REG32(ECX, ecx), + REG32(EDX, edx), + REG32(ESI, esi), + REG32(EDI, edi), + REG32(EBP, ebp), + REG32(ESP, esp), + +#ifdef _TARGET_AMD64_ + + REG8(R8, r8b), + REG8(R9, r9b), + REG8(R10, r10b), + REG8(R11, r11b), + REG8(R12, r12b), + REG8(R13, r13b), + REG8(R14, r14b), + REG8(R15, r15b), + + REG16(R8, r8w), + REG16(R9, r9w), + REG16(R10, r10w), + REG16(R11, r11w), + REG16(R12, r12w), + REG16(R13, r13w), + REG16(R14, r14w), + REG16(R15, r15w), + + REG32(R8, r8d), + REG32(R9, r9d), + REG32(R10, r10d), + REG32(R11, r11d), + REG32(R12, r12d), + REG32(R13, r13d), + REG32(R14, r14d), + REG32(R15, r15d), + + REG64(EAX, rax), + REG64(EBX, rbx), + REG64(ECX, rcx), + REG64(EDX, rdx), + REG64(ESI, rsi), + REG64(EDI, rdi), + REG64(EBP, rbp), + REG64(ESP, rsp), + REG64(R8, r8), + REG64(R9, r9), + REG64(R10, r10), + REG64(R11, r11), + REG64(R12, r12), + REG64(R13, r13), + REG64(R14, r14), + REG64(R15, r15), + +#endif // _TARGET_AMD64_ + +#undef REG +#undef REG8 +#undef REG16 +#undef REG32 +#undef REG64 + + }; + + for (size_t i = 0; i < sizeof(rgRegNames)/sizeof(rgRegNames[0]); i++) + { + if (!strncmp(ptr, rgRegNames[i].pszName, rgRegNames[i].cchName)) + { + if (psize) + *psize = rgRegNames[i].size; + + if (plen) + *plen = rgRegNames[i].cchName; + + return rgRegNames[i].index; + } + } + + return NONE; +} + +// Find the value of an expression. +inline BOOL FindSrc (__in_z char *ptr, ___in Register *reg, INT_PTR &value, BOOL &bDigit) +{ + if (GetValueFromExpr (ptr, value)) + { + bDigit = TRUE; + return TRUE; + } + + BOOL bValid = FALSE; + BOOL bByRef = IsByRef (ptr); + bDigit = FALSE; + + int regnamelen; + RegIndex index = FindReg (ptr, ®namelen); + if (index != NONE) + { + if (reg[index].bValid) + { + value = reg[index].value; + ptr += regnamelen; + // TODO: consider ecx+edi*4+0x4 + if ((IsTermSep (ptr[0]) && !bByRef) + || (ptr[0] == ']' && bByRef)) + { + bValid = TRUE; + if (bByRef) + SafeReadMemory (TO_TADDR(value), &value, sizeof(void*), NULL); + } + } + } + return bValid; +} + +enum ADDRESSMODE {REG, DATA, INDIRECT, NODATA, BAD}; + +struct RegState +{ + RegIndex reg; + BOOL bFullReg; + char scale; + int namelen; +}; + +struct InstData +{ + ADDRESSMODE mode; + RegState reg[2]; + INT_PTR value; +}; + +void FindMainReg (___in __in_z char *ptr, RegState ®) +{ + int size = 0; + + reg.reg = FindReg(ptr, ®.namelen, &size); + + reg.bFullReg = (reg.reg!=NONE && sizeof(void*)==size) ? TRUE : FALSE; +} + +static void DecodeAddressIndirect (___in __in_z char *term, InstData& arg) +{ + arg.mode = BAD; + arg.value = 0; + arg.reg[0].scale = 0; + arg.reg[1].scale = 0; + + if (!IsByRef (term)) + { + return; + } + + // first part must be a reg + arg.reg[0].scale = 1; + if (term[0] == '+') + term ++; + else if (term[0] == '-') + { + term ++; + arg.reg[0].scale = -1; + } + if (isdigit(term[0])) + { + arg.reg[0].scale *= term[0]-'0'; + term ++; + } + + FindMainReg (term, arg.reg[0]); + if (arg.reg[0].reg == NONE) + return; + term += arg.reg[0].namelen; + + if (term[0] == ']') + { + // It is [reg] + arg.mode = INDIRECT; + arg.value = 0; + return; + } + + char sign = (char)((term[0] == '+')?1:-1); + term ++; + FindMainReg (term, arg.reg[1]); + if (arg.reg[1].reg != NONE) + { + // It is either [reg+reg*c] or [reg+reg*c+c] + + term += arg.reg[1].namelen; + + if (term[0] == '*') + { + term ++; + arg.reg[1].scale = sign*(term[0]-'0'); + term ++; + } + else + arg.reg[1].scale = sign; + + if (term[0] == ']') + { + // It is [reg+reg*c] + arg.mode = INDIRECT; + arg.value = 0; + return; + } + sign = (char)((term[0] == '+')?1:-1); + term ++; + } + + char *endptr; + arg.value = strtoul(term, &endptr, 16); + if (endptr[0] == ']') + { + // It is [reg+reg*c+c] + arg.value *= sign; + arg.mode = INDIRECT; + } +} + +void DecodeAddressTerm (___in __in_z char *term, InstData& arg) +{ + arg.mode = BAD; + arg.reg[0].scale = 0; + arg.reg[1].scale = 0; + arg.value = 0; + INT_PTR value; + + if (GetValueFromExpr (term, value)) + { + arg.value = value; + arg.mode = DATA; + } + else + { + FindMainReg (term, arg.reg[0]); + if (arg.reg[0].reg != NONE) + { + arg.mode = REG; + } + else + { + DecodeAddressIndirect (term, arg); + } + } +} + +// Return 0 for non-managed call. Otherwise return MD address. +TADDR MDForCall (TADDR callee) +{ + // call managed code? + JITTypes jitType; + TADDR methodDesc; + TADDR IP = callee; + TADDR gcinfoAddr; + + if (!GetCalleeSite (callee, IP)) + return 0; + + IP2MethodDesc (IP, methodDesc, jitType, gcinfoAddr); + if (methodDesc) + { + return methodDesc; + } + + // jmp stub + char line[256]; + DisasmAndClean (IP, line, 256); + char *ptr = line; + NextTerm (ptr); + NextTerm (ptr); + if (!strncmp (ptr, "jmp ", 4)) + { + // jump thunk + NextTerm (ptr); + INT_PTR value; + methodDesc = 0; + if (GetValueFromExpr (ptr, value)) + { + IP2MethodDesc (value, methodDesc, jitType, gcinfoAddr); + } + return methodDesc; + } + return 0; +} + +// Handle a call instruction. +void HandleCall(TADDR callee, Register *reg) +{ + // call managed code? + TADDR methodDesc = MDForCall (callee); + if (methodDesc) + { + DacpMethodDescData MethodDescData; + if (MethodDescData.Request(g_sos, TO_CDADDR(methodDesc)) == S_OK) + { + NameForMD_s(methodDesc, g_mdName,mdNameLen); + ExtOut(" (%S, mdToken: %p)", g_mdName, SOS_PTR(MethodDescData.MDToken)); + return; + } + } + +#ifdef _TARGET_AMD64_ + // A jump thunk? + + CONTEXT ctx = {0}; + + ctx.ContextFlags = (CONTEXT_AMD64 | CONTEXT_CONTROL | CONTEXT_INTEGER); + + for (unsigned ireg = 0; ireg < 16; ireg++) + { + if (reg[ireg].bValid) + { + *(&ctx.Rax + ireg) = reg[ireg].value; + } + } + + ctx.Rip = callee; + + CLRDATA_ADDRESS ip = 0, md = 0; + if (S_OK == g_sos->GetJumpThunkTarget(&ctx, &ip, &md)) + { + if (md) + { + DacpMethodDescData MethodDescData; + if (MethodDescData.Request(g_sos, md) == S_OK) + { + NameForMD_s(md, g_mdName,mdNameLen); + ExtOut(" (%S, mdToken: %p)", g_mdName, SOS_PTR(MethodDescData.MDToken)); + return; + } + } + + if (ip != callee) + { + return HandleCall(ip, reg); + } + } +#endif // _TARGET_AMD64_ + + // A JitHelper? + const char* name = HelperFuncName(callee); + if (name) { + ExtOut (" (JitHelp: %s)", name); + return; + } + + // call unmanaged code? + char Symbol[1024]; + if (SUCCEEDED(g_ExtSymbols->GetNameByOffset(TO_CDADDR(callee), Symbol, 1024, + NULL, NULL))) + { + if (Symbol[0] != '\0') + { + ExtOut (" (%s)", Symbol); + return; + } + } +} + +// Determine if a value is MT/MD/Obj +void HandleValue(TADDR value) +{ + // A MethodTable? + if (IsMethodTable(value)) + { + NameForMT_s (value, g_mdName,mdNameLen); + ExtOut (" (MT: %S)", g_mdName); + return; + } + + // A Managed Object? + TADDR dwMTAddr; + move_xp (dwMTAddr, value); + if (IsStringObject(value)) + { + ExtOut (" (\""); + StringObjectContent (value, TRUE); + ExtOut ("\")"); + return; + } + else if (IsMethodTable(dwMTAddr)) + { + NameForMT_s (dwMTAddr, g_mdName,mdNameLen); + ExtOut (" (Object: %S)", g_mdName); + return; + } + + // A MethodDesc? + if (IsMethodDesc(value)) + { + NameForMD_s (value, g_mdName,mdNameLen); + ExtOut (" (MD: %S)", g_mdName); + return; + } + + // A JitHelper? + const char* name = HelperFuncName(value); + if (name) { + ExtOut (" (JitHelp: %s)", name); + return; + } +} + +/**********************************************************************\ +* Routine Description: * +* * +* Unassembly a managed code. Translating managed object, * +* call. * +* * +\**********************************************************************/ +void +#ifdef _TARGET_X86_ + X86Machine::Unassembly +#elif defined(_TARGET_AMD64_) + AMD64Machine::Unassembly +#endif + (TADDR IPBegin, + TADDR IPEnd, + TADDR IPAskedFor, + TADDR GCStressCodeCopy, + GCEncodingInfo *pGCEncodingInfo, + SOSEHInfo *pEHInfo, + BOOL bSuppressLines, + BOOL bDisplayOffsets) const +{ + ULONG_PTR IP = IPBegin; + char line[1024]; + Register reg [NumReg]; + ZeroMemory (reg, sizeof(reg)); + RegIndex dest; + INT_PTR value; + BOOL bDigit; + char *ptr; + + ULONG curLine = -1; + WCHAR filename[MAX_LONGPATH]; + ULONG linenum; + + while (IP < IPEnd) + { + if (IsInterrupt()) + return; + + // Print out line numbers if needed + if (!bSuppressLines + && SUCCEEDED(GetLineByOffset(TO_CDADDR(IP), &linenum, filename, MAX_LONGPATH))) + { + if (linenum != curLine) + { + curLine = linenum; + ExtOut("\n%S @ %d:\n", filename, linenum); + } + } + + // + // Print out any GC information corresponding to the current instruction offset. + // + +#ifndef FEATURE_PAL + if (pGCEncodingInfo) + { + SIZE_T curOffset = (IP - IPBegin) + pGCEncodingInfo->hotSizeToAdd; + while ( !pGCEncodingInfo->fDoneDecoding + && pGCEncodingInfo->ofs <= curOffset) + { + ExtOut(pGCEncodingInfo->buf); + ExtOut("\n"); + SwitchToFiber(pGCEncodingInfo->pvGCTableFiber); + } + } +#endif // FEATURE_PAL + + ULONG_PTR InstrAddr = IP; + + // + // Print out any EH info corresponding to the current offset + // + if (pEHInfo) + { + pEHInfo->FormatForDisassembly(IP - IPBegin); + } + + if (IP == IPAskedFor) + { + ExtOut (">>> "); + } + + // + // Print offsets, in addition to actual address. + // + if (bDisplayOffsets) + { + ExtOut("%04x ", IP - IPBegin); + } + + DisasmAndClean (IP, line, _countof(line)); + + // look at key word + ptr = line; + NextTerm (ptr); + NextTerm (ptr); + + // + // If there is gcstress info for this method, and this is a 'hlt' + // instruction, then gcstress probably put the 'hlt' there. Look + // up the original instruction and print it instead. + // + + SSIZE_T cbIPOffset = 0; + + if ( GCStressCodeCopy + && ( !strncmp (ptr, "hlt", 3) + || !strncmp (ptr, "cli", 3) + || !strncmp (ptr, "sti", 3))) + { + // + // Compute address into saved copy of the code, and + // disassemble the original instruction + // + + ULONG_PTR OrigInstrAddr = GCStressCodeCopy + (InstrAddr - IPBegin); + ULONG_PTR OrigIP = OrigInstrAddr; + + DisasmAndClean(OrigIP, line, _countof(line)); + + // + // Increment the real IP based on the size of the unmodifed + // instruction + // + + IP = InstrAddr + (OrigIP - OrigInstrAddr); + + cbIPOffset = IP - OrigIP; + + // + // Print out real code address in place of the copy address + // + +#ifdef _WIN64 + ExtOut("%08x`%08x ", (ULONG)(InstrAddr >> 32), (ULONG)InstrAddr); +#else + ExtOut("%08x ", (ULONG)InstrAddr); +#endif + + ptr = line; + NextTerm (ptr); + + // + // Print out everything after the code address, and skip the + // instruction bytes + // + + ExtOut(ptr); + + NextTerm (ptr); + + // + // Add an indicator that this address has not executed yet + // + + ExtOut(" (gcstress)"); + } + else + { + ExtOut (line); + } + + if (!strncmp (ptr, "mov ", 4)) + { + NextTerm (ptr); + + dest = FindReg(ptr); + if (dest != NONE) + { + NextTerm (ptr); + + if (FindSrc (ptr, reg, value, bDigit)) + { + reg[dest].bValid = TRUE; + reg[dest].value = value; + // Is it a managed obj + if (bDigit) + HandleValue (reg[dest].value); + } + else + { + reg[dest].bValid = FALSE; + } + } + } + else if (!strncmp (ptr, "call ", 5)) + { + NextTerm (ptr); + if (FindSrc (ptr, reg, value, bDigit)) + { + if (bDigit) + value += cbIPOffset; + + HandleCall (value, reg); + } + + // trash EAX, ECX, EDX + reg[EAX].bValid = FALSE; + reg[ECX].bValid = FALSE; + reg[EDX].bValid = FALSE; + +#ifdef _TARGET_AMD64_ + reg[R8].bValid = FALSE; + reg[R9].bValid = FALSE; + reg[R10].bValid = FALSE; + reg[R11].bValid = FALSE; +#endif // _TARGET_AMD64_ + } + else if (!strncmp (ptr, "lea ", 4)) + { + NextTerm (ptr); + dest = FindReg(ptr); + if (dest != NONE) + { + NextTerm (ptr); + if (FindSrc (ptr, reg, value, bDigit)) + { + reg[dest].bValid = TRUE; + reg[dest].value = value; + } + else + { + reg[dest].bValid = FALSE; + } + } + } + else if (!strncmp (ptr, "push ", 5)) + { + // do not do anything + NextTerm (ptr); + if (FindSrc (ptr, reg, value, bDigit)) + { + if (bDigit) + { + HandleValue (value); + } + } + } + else + { + // assume this instruction will trash dest reg + NextTerm (ptr); + dest = FindReg(ptr); + if (dest != NONE) + reg[dest].bValid = FALSE; + } + ExtOut ("\n"); + } + + // + // Print out any "end" EH info (where the end address is the byte immediately following the last instruction) + // + if (pEHInfo) + { + pEHInfo->FormatForDisassembly(IP - IPBegin); + } +} + +// Find the real callee site. Handle JMP instruction. +// Return TRUE if we get the address, FALSE if not. +BOOL GetCalleeSite (TADDR IP, TADDR &IPCallee) +{ + while (TRUE) { + unsigned char inst[2]; + if (g_ExtData->ReadVirtual(TO_CDADDR(IP), inst, sizeof(inst), NULL) != S_OK) + { + return FALSE; + } + if (inst[0] == 0xEB) { + IP += 2+(char)inst[1]; + } + else if (inst[0] == 0xE9) { + int displace; + if (g_ExtData->ReadVirtual(TO_CDADDR(IP+1), &displace, sizeof(displace), NULL) != S_OK) + { + return FALSE; + } + else + { + IP += 5+displace; + } + } + else if (inst[0] == 0xFF && (inst[1] & 070) == 040) { + if (inst[1] == 0x25) { + DWORD displace; + if (g_ExtData->ReadVirtual(TO_CDADDR(IP+2), &displace, sizeof(displace), NULL) != S_OK) + { + return FALSE; + } + if (g_ExtData->ReadVirtual(TO_CDADDR(displace), &displace, sizeof(displace), NULL) != S_OK) + { + return FALSE; + } + else + { + IP = displace; + } + } + else + // Target for jmp is determined from register values. + return FALSE; + } + else + { + IPCallee = IP; + return TRUE; + } + } +} + +// GetFinalTarget is based on HandleCall, but avoids printing anything to the output. +// This is currently only called on x64 +eTargetType GetFinalTarget(TADDR callee, TADDR* finalMDorIP) +{ + // call managed code? + TADDR methodDesc = MDForCall (callee); + if (methodDesc) + { + DacpMethodDescData MethodDescData; + if (MethodDescData.Request(g_sos, TO_CDADDR(methodDesc)) == S_OK) + { + *finalMDorIP = methodDesc; + return ettMD; + } + } + +#ifdef _TARGET_AMD64_ + // A jump thunk? + + CONTEXT ctx = {0}; + ctx.ContextFlags = (CONTEXT_AMD64 | CONTEXT_CONTROL | CONTEXT_INTEGER); + ctx.Rip = callee; + + CLRDATA_ADDRESS ip = 0, md = 0; + if (S_OK == g_sos->GetJumpThunkTarget(&ctx, &ip, &md)) + { + if (md) + { + DacpMethodDescData MethodDescData; + if (MethodDescData.Request(g_sos, md) == S_OK) + { + *finalMDorIP = md; + return ettStub; + } + } + + if (ip != callee) + { + return GetFinalTarget(ip, finalMDorIP); + } + } +#endif // _TARGET_AMD64_ + + // A JitHelper? + const char* name = HelperFuncName(callee); + if (name) { + *finalMDorIP = callee; + return ettJitHelp; + } + + // call unmanaged code? + *finalMDorIP = callee; + return ettNative; +} + +#ifndef FEATURE_PAL + +void ExpFuncStateInit (TADDR *IPRetAddr) +{ + ULONG64 offset; + if (FAILED(g_ExtSymbols->GetOffsetByName("ntdll!KiUserExceptionDispatcher", &offset))) { + return; + } + + // test if we have a minidump for which the image is not cached anymore. this avoids + // the having the while loop below spin forever (or a very long time)... + // (Watson backend hit this a few times, and they had to institute a timeout policy + // to work around this) + SIZE_T instrs; + if (FAILED(g_ExtData->ReadVirtual(offset, &instrs, sizeof(instrs), NULL)) || instrs == 0) { + return; + } + + char line[256]; + int i = 0; + int cnt = 0; +#ifdef SOS_TARGET_X86 + // On x86 and x64 the last 3 "call" instructions in ntdll!KiUserExceptionDispatcher + // are making calls to OS APIs that take as argument the context record (and some + // of them the exception record as well) + const int cCallInstrs = 3; +#elif defined(SOS_TARGET_AMD64) + // On x64 the first "call" instruction should be considered, as well + const int cCallInstrs = 4; +#endif + + while (i < cCallInstrs) { + g_ExtControl->Disassemble (offset, 0, line, 256, NULL, &offset); + if (strstr (line, "call")) { + IPRetAddr[i++] = (TADDR)offset; + } + // if we didn't find at least one "call" in the first 500 instructions give up... + if (++cnt >= 500 && IPRetAddr[0] == 0) + break; + } +} + +#endif // FEATURE_PAL + + +/**********************************************************************\ +* Routine Description: * +* * +* This function is called to fill in a cross platform context * +* struct by looking on the stack for return addresses into * +* KiUserExceptionDispatcher * +* * +\**********************************************************************/ +BOOL +#ifdef SOS_TARGET_X86 + X86Machine::GetExceptionContext +#elif defined(SOS_TARGET_AMD64) + AMD64Machine::GetExceptionContext +#endif + (TADDR stack, + TADDR IP, + TADDR * cxrAddr, + CROSS_PLATFORM_CONTEXT * pcxr, + TADDR * exrAddr, + PEXCEPTION_RECORD exr) const +{ +#ifndef FEATURE_PAL +#ifdef SOS_TARGET_X86 + X86_CONTEXT * cxr = &pcxr->X86Context; + size_t contextSize = offsetof(CONTEXT, ExtendedRegisters); +#elif defined(SOS_TARGET_AMD64) + AMD64_CONTEXT * cxr = &pcxr->Amd64Context; + size_t contextSize = offsetof(CONTEXT, FltSave); +#endif + + static TADDR IPRetAddr[4] = {0, 0, 0, 0}; + + if (IPRetAddr[0] == 0) { + ExpFuncStateInit (IPRetAddr); + } + *cxrAddr = 0; + *exrAddr = 0; + +#ifdef SOS_TARGET_X86 + + if (IP == IPRetAddr[0]) { + *exrAddr = stack + sizeof(TADDR); + *cxrAddr = stack + 2*sizeof(TADDR); + } + else if (IP == IPRetAddr[1]) { + *cxrAddr = stack + sizeof(TADDR); + } + else if (IP == IPRetAddr[2]) { + *exrAddr = stack + sizeof(TADDR); + *cxrAddr = stack + 2*sizeof(TADDR); + } + else + return FALSE; + + if (FAILED (g_ExtData->ReadVirtual(TO_CDADDR(*cxrAddr), &stack, sizeof(stack), NULL))) + return FALSE; + *cxrAddr = stack; + + //if ((pContext->ContextFlags & CONTEXT_EXTENDED_REGISTERS) == CONTEXT_EXTENDED_REGISTERS) + // contextSize += sizeof(pContext->ExtendedRegisters); + if (FAILED (g_ExtData->ReadVirtual(TO_CDADDR(stack), cxr, (ULONG)contextSize, NULL))) { + return FALSE; + } + + if (*exrAddr) { + if (FAILED (g_ExtData->ReadVirtual(TO_CDADDR(*exrAddr), &stack, sizeof(stack), NULL))) + { + *exrAddr = 0; + return TRUE; + } + *exrAddr = stack; + size_t erSize = offsetof (EXCEPTION_RECORD, ExceptionInformation); + if (FAILED (g_ExtData->ReadVirtual(TO_CDADDR(stack), exr, (ULONG)erSize, NULL))) { + *exrAddr = 0; + return TRUE; + } + } + +#elif defined(SOS_TARGET_AMD64) + + if (IP == IPRetAddr[0] || IP == IPRetAddr[1] || IP == IPRetAddr[3]) { + *exrAddr = stack + sizeof(TADDR) + 0x4F0; + *cxrAddr = stack + sizeof(TADDR); + } else if (IP == IPRetAddr[2]) { + *cxrAddr = stack + sizeof(TADDR); + } + else { + return FALSE; + } + + if (FAILED (g_ExtData->ReadVirtual(TO_CDADDR(*cxrAddr), cxr, (ULONG)contextSize, NULL))) { + return FALSE; + } + + if (*exrAddr) { + size_t erSize = offsetof (EXCEPTION_RECORD, ExceptionInformation); + if (FAILED (g_ExtData->ReadVirtual(TO_CDADDR(*exrAddr), exr, (ULONG)erSize, NULL))) { + *exrAddr = 0; + return TRUE; + } + } + +#endif + return TRUE; +#else + return FALSE; +#endif // FEATURE_PAL +} + + +/**********************************************************************\ +* Routine Description: * +* * +* This function is called to determine if a DWORD on the stack is * +* a return address. * +* It does this by checking several bytes before the DWORD to see if * +* there is a call instruction. * +* * +\**********************************************************************/ + +void +#ifdef _TARGET_X86_ + X86Machine::IsReturnAddress +#elif defined(_TARGET_AMD64_) + AMD64Machine::IsReturnAddress +#endif + (TADDR retAddr, TADDR* whereCalled) const +{ + *whereCalled = 0; + + unsigned char spotend[6]; + move_xp (spotend, retAddr-6); + unsigned char *spot = spotend+6; + TADDR addr; + + // Note this is possible to be spoofed, but pretty unlikely + // call XXXXXXXX + if (spot[-5] == 0xE8) { + DWORD offs = 0; + move_xp (offs, retAddr-4); + *whereCalled = retAddr + (ULONG64)(LONG)(offs); + //*whereCalled = *((int*) (retAddr-4)) + retAddr; + // on WOW64 the range valid for code is almost the whole 4GB adddress space + if (g_ExtData->ReadVirtual(TO_CDADDR(*whereCalled), &addr, sizeof(addr), NULL) == S_OK) + { + TADDR callee; + if (GetCalleeSite(*whereCalled, callee)) { + *whereCalled = callee; + } + return; + } + else + *whereCalled = 0; + } + + // call [XXXXXXXX] + if (spot[-6] == 0xFF && (spot[-5] == 025)) { + DWORD offs = 0; + move_xp (offs, retAddr-4); +#ifdef _TARGET_AMD64_ + // on x64 this 32-bit is an RIP offset + addr = retAddr + (ULONG64)(LONG)(offs); +#elif defined (_TARGET_X86_) + addr = offs; +#endif + if (g_ExtData->ReadVirtual(TO_CDADDR(addr), whereCalled, sizeof(*whereCalled), NULL) == S_OK) { + move_xp (*whereCalled, addr); + //*whereCalled = **((unsigned**) (retAddr-4)); + // on WOW64 the range valid for code is almost the whole 4GB adddress space + if (g_ExtData->ReadVirtual(TO_CDADDR(*whereCalled), &addr, sizeof(addr), NULL) == S_OK) + { + TADDR callee; + if (GetCalleeSite(*whereCalled,callee)) { + *whereCalled = callee; + } + return; + } + else + *whereCalled = 0; + } + else + *whereCalled = 0; + } + + // call [REG+XX] + if (spot[-3] == 0xFF && (spot[-2] & ~7) == 0120 && (spot[-2] & 7) != 4) + { + *whereCalled = 0xFFFFFFFF; + return; + } + if (spot[-4] == 0xFF && spot[-3] == 0124) + { + *whereCalled = 0xFFFFFFFF; + return; + } + + // call [REG+XXXX] + if (spot[-6] == 0xFF && (spot[-5] & ~7) == 0220 && (spot[-5] & 7) != 4) + { + *whereCalled = 0xFFFFFFFF; + return; + } + if (spot[-7] == 0xFF && spot[-6] == 0224) + { + *whereCalled = 0xFFFFFFFF; + return; + } + + // call [REG] + if (spot[-2] == 0xFF && (spot[-1] & ~7) == 0020 && (spot[-1] & 7) != 4 && (spot[-1] & 7) != 5) + { + *whereCalled = 0xFFFFFFFF; + return; + } + + // call REG + if (spot[-2] == 0xFF && (spot[-1] & ~7) == 0320 && (spot[-1] & 7) != 4) + { + *whereCalled = 0xFFFFFFFF; + return; + } + + // There are other cases, but I don't believe they are used. + return; +} + + +#ifdef _X86_ + +/// +/// This is dead code, not called from anywhere, not linked in the final product. +/// +static BOOL DecodeLine (___in __in_z char *line, ___in __in_z const char *const inst, InstData& arg1, InstData& arg2) +{ + char *ptr = line; + if (inst[0] == '*' || !strncmp (ptr, inst, strlen (inst))) + { + arg1.mode = BAD; + arg2.mode = BAD; + NextTerm (ptr); + if (*ptr == '\0') + { + arg1.mode = NODATA; + return TRUE; + } + + DecodeAddressTerm (ptr, arg1); + NextTerm (ptr); + if (*ptr == '\0') + { + return TRUE; + } + DecodeAddressTerm (ptr, arg2); + return TRUE; + } + else + return FALSE; +} + +void PrintReg (Register *reg) +{ + ExtOut ("[EBX=%08x ESI=%08x EDI=%08x EBP=%08x ESP=%08x]\n", + reg[EBX].value, reg[ESI].value, reg[EDI].value, reg[EBP].value, + reg[ESP].value); +} + + +struct CallInfo +{ + DWORD_PTR stackPos; + DWORD_PTR retAddr; + DWORD_PTR whereCalled; +}; + +// Search for a Return address on stack. +BOOL GetNextRetAddr (DWORD_PTR stackBegin, DWORD_PTR stackEnd, + CallInfo &callInfo) +{ + for (callInfo.stackPos = stackBegin; + callInfo.stackPos <= stackEnd; + callInfo.stackPos += 4) + { + if (!SafeReadMemory (callInfo.stackPos, &callInfo.retAddr, 4, NULL)) + continue; + + g_targetMachine->IsReturnAddress(callInfo.retAddr, &callInfo.whereCalled); + if (callInfo.whereCalled) + { + return TRUE; + } + } + + return FALSE; +} + +struct FrameInfo +{ + DWORD_PTR IPStart; + DWORD_PTR Prolog; + DWORD_PTR FrameBase; // The value of ESP at the entry. + DWORD_PTR StackEnd; + DWORD_PTR argCount; + BOOL bEBPFrame; +}; + +// if a EBP frame, return TRUE if EBP has been setup +void GetFrameBaseHelper (DWORD_PTR IPBegin, DWORD_PTR IPEnd, + INT_PTR &StackChange) +{ + char line[256]; + char *ptr; + InstData arg1; + InstData arg2; + DWORD_PTR IP = IPBegin; + StackChange = 0; + while (IP < IPEnd) + { + DisasmAndClean (IP, line, 256); + ptr = line; + NextTerm (ptr); + NextTerm (ptr); + if (DecodeLine (ptr, "push ", arg1, arg2)) + { + StackChange += 4; + } + else if (DecodeLine (ptr, "pop ", arg1, arg2)) + { + StackChange -= 4; + } + else if (DecodeLine (ptr, "sub ", arg1, arg2)) + { + if (arg1.mode == REG && arg1.reg[0].reg == ESP) + { + if (arg2.mode == DATA) + StackChange -= arg2.value; + } + } + else if (DecodeLine (ptr, "add ", arg1, arg2)) + { + if (arg1.mode == REG && arg1.reg[0].reg == ESP) + { + if (arg2.mode == DATA) + StackChange += arg2.value; + } + } + else if (!strncmp (ptr, "ret", 3)) { + return; + } + } +} + +enum IPSTATE {IPPROLOG1 /*Before EBP set*/, IPPROLOG2 /*After EBP set*/, IPCODE, IPEPILOG, IPEND}; + +IPSTATE GetIpState (DWORD_PTR IP, FrameInfo* pFrame) +{ + char line[256]; + char *ptr; + + if (IP >= pFrame->IPStart && IP < pFrame->IPStart + pFrame->Prolog) + { + if (pFrame->bEBPFrame) { + DWORD_PTR pIP = pFrame->IPStart; + while (pIP < IP) { + DisasmAndClean (IP,line, 256); + ptr = line; + NextTerm (ptr); + NextTerm (ptr); + if (!strncmp (ptr, "mov ", 4)) { + NextTerm (ptr); + if (!strncmp (ptr, "ebp", 3)) { + NextTerm (ptr); + if (!strncmp (ptr, "esp", 3)) { + return IPPROLOG2; + } + } + } + else if (!strncmp (ptr, "call ", 5)) { + NextTerm (ptr); + if (strstr (ptr, "__EH_prolog")) { + return IPPROLOG2; + } + } + } + pIP = IP; + while (pIP < pFrame->IPStart + pFrame->Prolog) { + DisasmAndClean (IP,line, 256); + ptr = line; + NextTerm (ptr); + NextTerm (ptr); + if (!strncmp (ptr, "mov ", 4)) { + NextTerm (ptr); + if (!strncmp (ptr, "ebp", 3)) { + NextTerm (ptr); + if (!strncmp (ptr, "esp", 3)) { + return IPPROLOG1; + } + } + } + else if (!strncmp (ptr, "call ", 5)) { + NextTerm (ptr); + if (strstr (ptr, "__EH_prolog")) { + return IPPROLOG1; + } + } + } + + ExtOut ("Fail to find where EBP is saved\n"); + return IPPROLOG2; + } + else + { + return IPPROLOG1; + } + } + + int nline = 0; + while (1) { + DisasmAndClean (IP,line, 256); + nline ++; + ptr = line; + NextTerm (ptr); + NextTerm (ptr); + if (!strncmp (ptr, "ret", 3)) { + return (nline==1)?IPEND:IPEPILOG; + } + else if (!strncmp (ptr, "leave", 5)) { + return IPEPILOG; + } + else if (!strncmp (ptr, "call", 4)) { + return IPCODE; + } + else if (ptr[0] == 'j') { + return IPCODE; + } + } +} + +// FrameBase is the ESP value at the entry of a function. +BOOL GetFrameBase (Register callee[], FrameInfo* pFrame) +{ + //char line[256]; + //char *ptr; + INT_PTR dwpushed = 0; + //DWORD_PTR IP; + + IPSTATE IpState = GetIpState (callee[EIP].value, pFrame); + + if (pFrame->bEBPFrame) + { + if (IpState == IPEND || IpState == IPPROLOG1) { + pFrame->FrameBase = callee[ESP].value; + } + else + { + pFrame->FrameBase = callee[EBP].value+4; + } + return TRUE; + } + else + { + if (IpState == IPEND) { + pFrame->FrameBase = callee[ESP].value; + return TRUE; + } + + DWORD_PTR IPBegin, IPEnd; + if (IpState == IPEPILOG) { + IPBegin = callee[EIP].value; + IPEnd = ~0ul; + } + else if (IpState == IPPROLOG1) { + IPBegin = pFrame->IPStart; + IPEnd = callee[EIP].value; + } + else + { + IPBegin = pFrame->IPStart; + IPEnd = IPBegin + pFrame->Prolog; + } + GetFrameBaseHelper (IPBegin, IPEnd, dwpushed); + + if (IpState == IPEPILOG) { + ExtOut ("stack %d\n", dwpushed); + pFrame->FrameBase = callee[ESP].value - dwpushed; + return TRUE; + } + + CallInfo callInfo; + if (GetNextRetAddr (callee[ESP].value + dwpushed, + pFrame->StackEnd, callInfo)) + { + pFrame->FrameBase = callInfo.stackPos; + return TRUE; + } + + return FALSE; + } +} + +// caller[ESP]: the ESP value when we return to caller. +void RestoreCallerRegister (Register callee[], Register caller[], + FrameInfo *pFrame) +{ + if (pFrame->bEBPFrame) + { + if (callee[ESP].value < pFrame->FrameBase) + { + SafeReadMemory (pFrame->FrameBase-4, &caller[EBP].value, 4, NULL); + } + else + caller[EBP].value = callee[EBP].value; + } + else + caller[EBP].value = callee[EBP].value; + + caller[EBP].bValid = TRUE; + caller[ESP].value = pFrame->FrameBase + 4 + pFrame->argCount; + callee[EBP].value = pFrame->FrameBase - sizeof(void*); + SafeReadMemory (pFrame->FrameBase, &caller[EIP].value, 4, NULL); +} + +BOOL GetFrameInfoHelper (Register callee[], Register caller[], + FrameInfo *pFrame) +{ + if (GetFrameBase (callee, pFrame)) + { + RestoreCallerRegister (callee, caller, pFrame); + return TRUE; + } + else + return FALSE; +} + +// Return TRUE if Frame Info is OK, otherwise FALSE. +BOOL GetUnmanagedFrameInfo (Register callee[], Register caller[], + DumpStackFlag &DSFlag, PFPO_DATA data) +{ + FrameInfo Frame; + ULONG64 base; + g_ExtSymbols->GetModuleByOffset (callee[EIP].value, 0, NULL, &base); + Frame.IPStart = data->ulOffStart + (ULONG_PTR)base; + Frame.Prolog = data->cbProlog; + // Why do we have to do this to make it work? + if (Frame.Prolog == 1) { + Frame.Prolog = 0; + } + Frame.bEBPFrame = (data->cbFrame == FRAME_NONFPO); + Frame.StackEnd = DSFlag.end; + Frame.argCount = data->cdwParams*4; + + return GetFrameInfoHelper (callee, caller, &Frame); +} + +// offsetEBP: offset of stack position where EBP is saved. +// If EBP is not saved, *offsetEBP = -1 (~0ul); +BOOL IPReachable (DWORD_PTR IPBegin, DWORD_PTR IP, DWORD *offsetEBP) +{ + *offsetEBP = ~0ul; + return FALSE; +} + +BOOL HandleEEStub (Register callee[], Register caller[], + DumpStackFlag &DSFlag) +{ + // EEStub can only be called by IP directory. Let's look for possible caller. + CallInfo callInfo; + DWORD_PTR stackPos = callee[ESP].value; + while (stackPos < DSFlag.end) { + if (GetNextRetAddr (stackPos, + DSFlag.end, callInfo)) + { + if (callInfo.whereCalled != ~0ul) { + DWORD offsetEBP; + if (IPReachable (callInfo.whereCalled, callee[EIP].value, &offsetEBP)) { + caller[EIP].value = callInfo.retAddr; + // TODO: We may have saved EBP. + if (offsetEBP == ~0ul) { + caller[EBP].value = callee[EBP].value; + } + else + { + TADDR offs = TO_TADDR(callInfo.stackPos)-sizeof(PVOID)-offsetEBP; + SafeReadMemory (offs, &caller[EBP].value, sizeof(PVOID), NULL); + } + caller[ESP].value = callInfo.stackPos+sizeof(PVOID); + return TRUE; + } + } + stackPos = callInfo.stackPos+sizeof(PVOID); + } + else + return FALSE; + } + + return FALSE; +} + + +BOOL HandleByEpilog (Register callee[], Register caller[], + DumpStackFlag &DSFlag) +{ + return FALSE; +} + +#ifndef FEATURE_PAL +void RestoreFrameUnmanaged (Register *reg, DWORD_PTR CurIP) +{ + char line[256]; + char *ptr; + DWORD_PTR IP = CurIP; + INT_PTR value; + BOOL bDigit; + BOOL bGoodESP = true; + RegIndex dest; + + ULONG64 base; + g_ExtSymbols->GetModuleByOffset (TO_CDADDR(CurIP), 0, NULL, &base); + ULONG64 handle; + g_ExtSystem->GetCurrentProcessHandle(&handle); + PFPO_DATA data = + (PFPO_DATA)SymFunctionTableAccess((HANDLE)handle, CurIP); + DWORD_PTR IPBegin = data->ulOffStart + (ULONG_PTR)base; + + if (CurIP - IPBegin <= data->cbProlog) + { + // We are inside a prolog. + // See where we save the callee saved register. + // Also how many DWORD's we pushd + IP = IPBegin; + reg[ESP].stack = 0; + reg[ESP].bOnStack = FALSE; + reg[EBP].stack = 0; + reg[EBP].bOnStack = FALSE; + reg[ESI].stack = 0; + reg[ESI].bOnStack = FALSE; + reg[EDI].stack = 0; + reg[EDI].bOnStack = FALSE; + reg[EBX].stack = 0; + reg[EBX].bOnStack = FALSE; + + while (IP < CurIP) + { + DisasmAndClean (IP, line, 256); + ptr = line; + NextTerm (ptr); + NextTerm (ptr); + if (!strncmp (ptr, "push ", 5)) + { + reg[ESP].stack += 4; + NextTerm (ptr); + dest = FindReg(ptr); + if (dest == EBP || dest == EBX || dest == ESI || dest == EDI) + { + reg[dest].bOnStack = TRUE; + reg[dest].stack = reg[ESP].stack; + } + } + else if (!strncmp (ptr, "sub ", 4)) + { + NextTerm (ptr); + dest = FindReg(ptr); + if (dest == ESP) + { + NextTerm (ptr); + char *endptr; + reg[ESP].stack += strtoul(ptr, &endptr, 16);; + } + } + } + + DWORD_PTR baseESP = reg[ESP].value + reg[ESP].stack; + if (reg[EBP].bOnStack) + { + move_xp (reg[EBP].value, baseESP-reg[EBP].stack); + } + if (reg[EBX].bOnStack) + { + move_xp (reg[EBX].value, baseESP-reg[EBX].stack); + } + if (reg[ESI].bOnStack) + { + move_xp (reg[ESI].value, baseESP-reg[ESI].stack); + } + if (reg[EDI].bOnStack) + { + move_xp (reg[EDI].value, baseESP-reg[EDI].stack); + } + move_xp (reg[EIP].value, baseESP); + reg[ESP].value = baseESP + 4; + return; + } + + if (data->cbFrame == FRAME_NONFPO) + { + // EBP Frame + } + + // Look for epilog + while (1) + { + DisasmAndClean (IP, line, 256); + ptr = line; + NextTerm (ptr); + NextTerm (ptr); + if (!strncmp (ptr, "mov ", 4)) + { + NextTerm (ptr); + dest = FindReg(ptr); + if (dest == ESP) + { + NextTerm (ptr); + if (FindReg(ptr) == EBP) + { + // We have a EBP frame + bGoodESP = true; + reg[ESP].value = reg[EBP].value; + } + } + } + else if (!strncmp (ptr, "ret", 3)) + { + NextTerm (ptr); + // check the value on stack is a return address. + DWORD_PTR retAddr; + DWORD_PTR whereCalled; + move_xp (retAddr, reg[ESP].value); + int ESPAdjustCount = 0; + while (1) + { + g_targetMachine->IsReturnAddress(retAddr, &whereCalled); + if (whereCalled) + break; + ESPAdjustCount ++; + reg[ESP].value += 4; + move_xp (retAddr, reg[ESP].value); + } + reg[EIP].value = retAddr; + if (ESPAdjustCount) + { + ESPAdjustCount *= 4; + } + if (reg[EBX].bOnStack) + { + reg[EBX].stack += ESPAdjustCount; + move_xp (reg[EBX].value, reg[EBX].stack); + } + if (reg[ESI].bOnStack) + { + reg[ESI].stack += ESPAdjustCount; + move_xp (reg[ESI].value, reg[EBX].stack); + } + if (reg[EDI].bOnStack) + { + reg[EDI].stack += ESPAdjustCount; + move_xp (reg[EDI].value, reg[EBX].stack); + } + + reg[ESP].value += 4; + if (ptr[0] != '\0') + { + FindSrc (ptr, reg, value, bDigit); + reg[ESP].value += value; + } + break; + } + else if (!strncmp (ptr, "pop ", 4)) + { + NextTerm (ptr); + dest = FindReg(ptr); + if (dest == EBP || dest == EBX || dest == ESI || dest == EDI) + { + reg[dest].stack = reg[ESP].value; + reg[dest].bOnStack = TRUE; + } + reg[ESP].value += 4; + } + else if (!strncmp (ptr, "add ", 4)) + { + NextTerm (ptr); + dest = FindReg(ptr); + if (dest == ESP) + { + NextTerm (ptr); + FindSrc (ptr, reg, value, bDigit); + reg[ESP].value += value; + } + } + else if (!strncmp (ptr, "call ", 5)) + { + // assume we do not have a good value on ESP. + // We could go into the call and find out number of pushed args. + bGoodESP = FALSE; + } + } + + // Look for prolog +} +#endif // !FEATURE_PAL + +#elif defined(_AMD64_) + + +#endif // !_X86_ |