summaryrefslogtreecommitdiff
path: root/src/ToolBox/SOS/Strike/disasmX86.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ToolBox/SOS/Strike/disasmX86.cpp')
-rw-r--r--src/ToolBox/SOS/Strike/disasmX86.cpp1707
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, &regnamelen);
+ 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 &reg)
+{
+ int size = 0;
+
+ reg.reg = FindReg(ptr, &reg.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_