1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4
5// ==++==
6//
7
8//
9// ==--==
10#include "strike.h"
11#include "util.h"
12#include "disasm.h"
13#include <dbghelp.h>
14
15#include "../../../inc/corhdr.h"
16#include "../../../inc/cor.h"
17#include "../../../inc/dacprivate.h"
18
19
20#if defined(SOS_TARGET_X86) && defined(SOS_TARGET_AMD64)
21#error This file does not support SOS targeting both X86 and AMD64 debuggees
22#endif
23
24#if !defined(SOS_TARGET_X86) && !defined(SOS_TARGET_AMD64)
25#error This file should be used to support SOS targeting either X86 or AMD64 debuggees
26#endif
27
28
29// These must be in the same order as they are used in the instruction
30// encodings/same as the CONTEXT field order.
31enum RegIndex
32{
33 EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI,
34
35#ifdef _TARGET_AMD64_
36 R8, R9, R10, R11, R12, R13, R14, R15,
37#endif // _TARGET_AMD64_
38
39 EIP, NONE
40};
41
42const int NumReg = NONE;
43struct Register
44{
45 TADDR value;
46 BOOL bValid;
47 TADDR stack;
48 BOOL bOnStack;
49};
50
51// Find the index for a register name
52inline RegIndex FindReg (___in __in_z char *ptr, __out_opt int *plen = NULL, __out_opt int *psize = NULL)
53{
54 struct RegName
55 {
56 RegIndex index;
57 PCSTR pszName;
58 int cchName;
59 int size;
60 };
61
62 static RegName rgRegNames[] = {
63
64#define REG(index, reg, size) { index, #reg, sizeof(#reg)-1, size }
65#define REG8(index, reg) REG(index, reg, 1)
66#define REG16(index, reg) REG(index, reg, 2)
67#define REG32(index, reg) REG(index, reg, 4)
68#define REG64(index, reg) REG(index, reg, 8)
69
70 REG8(EAX, al),
71 REG8(EAX, ah),
72 REG8(EBX, bl),
73 REG8(EBX, bh),
74 REG8(ECX, cl),
75 REG8(ECX, ch),
76 REG8(EDX, dl),
77 REG8(EDX, dh),
78
79 REG16(EAX, ax),
80 REG16(EBX, bx),
81 REG16(ECX, cx),
82 REG16(EDX, dx),
83 REG16(ESI, si),
84 REG16(EDI, di),
85 REG16(EBP, bp),
86 REG16(ESP, sp),
87
88 REG32(EAX, eax),
89 REG32(EBX, ebx),
90 REG32(ECX, ecx),
91 REG32(EDX, edx),
92 REG32(ESI, esi),
93 REG32(EDI, edi),
94 REG32(EBP, ebp),
95 REG32(ESP, esp),
96
97#ifdef _TARGET_AMD64_
98
99 REG8(R8, r8b),
100 REG8(R9, r9b),
101 REG8(R10, r10b),
102 REG8(R11, r11b),
103 REG8(R12, r12b),
104 REG8(R13, r13b),
105 REG8(R14, r14b),
106 REG8(R15, r15b),
107
108 REG16(R8, r8w),
109 REG16(R9, r9w),
110 REG16(R10, r10w),
111 REG16(R11, r11w),
112 REG16(R12, r12w),
113 REG16(R13, r13w),
114 REG16(R14, r14w),
115 REG16(R15, r15w),
116
117 REG32(R8, r8d),
118 REG32(R9, r9d),
119 REG32(R10, r10d),
120 REG32(R11, r11d),
121 REG32(R12, r12d),
122 REG32(R13, r13d),
123 REG32(R14, r14d),
124 REG32(R15, r15d),
125
126 REG64(EAX, rax),
127 REG64(EBX, rbx),
128 REG64(ECX, rcx),
129 REG64(EDX, rdx),
130 REG64(ESI, rsi),
131 REG64(EDI, rdi),
132 REG64(EBP, rbp),
133 REG64(ESP, rsp),
134 REG64(R8, r8),
135 REG64(R9, r9),
136 REG64(R10, r10),
137 REG64(R11, r11),
138 REG64(R12, r12),
139 REG64(R13, r13),
140 REG64(R14, r14),
141 REG64(R15, r15),
142
143#endif // _TARGET_AMD64_
144
145#undef REG
146#undef REG8
147#undef REG16
148#undef REG32
149#undef REG64
150
151 };
152
153 for (size_t i = 0; i < sizeof(rgRegNames)/sizeof(rgRegNames[0]); i++)
154 {
155 if (!strncmp(ptr, rgRegNames[i].pszName, rgRegNames[i].cchName))
156 {
157 if (psize)
158 *psize = rgRegNames[i].size;
159
160 if (plen)
161 *plen = rgRegNames[i].cchName;
162
163 return rgRegNames[i].index;
164 }
165 }
166
167 return NONE;
168}
169
170// Find the value of an expression.
171inline BOOL FindSrc (__in_z char *ptr, ___in Register *reg, INT_PTR &value, BOOL &bDigit)
172{
173 if (GetValueFromExpr (ptr, value))
174 {
175 bDigit = TRUE;
176 return TRUE;
177 }
178
179 BOOL bValid = FALSE;
180 BOOL bByRef = IsByRef (ptr);
181 bDigit = FALSE;
182
183 int regnamelen;
184 RegIndex index = FindReg (ptr, &regnamelen);
185 if (index != NONE)
186 {
187 if (reg[index].bValid)
188 {
189 value = reg[index].value;
190 ptr += regnamelen;
191 // TODO: consider ecx+edi*4+0x4
192 if ((IsTermSep (ptr[0]) && !bByRef)
193 || (ptr[0] == ']' && bByRef))
194 {
195 bValid = TRUE;
196 if (bByRef)
197 SafeReadMemory (TO_TADDR(value), &value, sizeof(void*), NULL);
198 }
199 }
200 }
201 return bValid;
202}
203
204enum ADDRESSMODE {REG, DATA, INDIRECT, NODATA, BAD};
205
206struct RegState
207{
208 RegIndex reg;
209 BOOL bFullReg;
210 char scale;
211 int namelen;
212};
213
214struct InstData
215{
216 ADDRESSMODE mode;
217 RegState reg[2];
218 INT_PTR value;
219};
220
221void FindMainReg (___in __in_z char *ptr, RegState &reg)
222{
223 int size = 0;
224
225 reg.reg = FindReg(ptr, &reg.namelen, &size);
226
227 reg.bFullReg = (reg.reg!=NONE && sizeof(void*)==size) ? TRUE : FALSE;
228}
229
230static void DecodeAddressIndirect (___in __in_z char *term, InstData& arg)
231{
232 arg.mode = BAD;
233 arg.value = 0;
234 arg.reg[0].scale = 0;
235 arg.reg[1].scale = 0;
236
237 if (!IsByRef (term))
238 {
239 return;
240 }
241
242 // first part must be a reg
243 arg.reg[0].scale = 1;
244 if (term[0] == '+')
245 term ++;
246 else if (term[0] == '-')
247 {
248 term ++;
249 arg.reg[0].scale = -1;
250 }
251 if (isdigit(term[0]))
252 {
253 arg.reg[0].scale *= term[0]-'0';
254 term ++;
255 }
256
257 FindMainReg (term, arg.reg[0]);
258 if (arg.reg[0].reg == NONE)
259 return;
260 term += arg.reg[0].namelen;
261
262 if (term[0] == ']')
263 {
264 // It is [reg]
265 arg.mode = INDIRECT;
266 arg.value = 0;
267 return;
268 }
269
270 char sign = (char)((term[0] == '+')?1:-1);
271 term ++;
272 FindMainReg (term, arg.reg[1]);
273 if (arg.reg[1].reg != NONE)
274 {
275 // It is either [reg+reg*c] or [reg+reg*c+c]
276
277 term += arg.reg[1].namelen;
278
279 if (term[0] == '*')
280 {
281 term ++;
282 arg.reg[1].scale = sign*(term[0]-'0');
283 term ++;
284 }
285 else
286 arg.reg[1].scale = sign;
287
288 if (term[0] == ']')
289 {
290 // It is [reg+reg*c]
291 arg.mode = INDIRECT;
292 arg.value = 0;
293 return;
294 }
295 sign = (char)((term[0] == '+')?1:-1);
296 term ++;
297 }
298
299 char *endptr;
300 arg.value = strtoul(term, &endptr, 16);
301 if (endptr[0] == ']')
302 {
303 // It is [reg+reg*c+c]
304 arg.value *= sign;
305 arg.mode = INDIRECT;
306 }
307}
308
309void DecodeAddressTerm (___in __in_z char *term, InstData& arg)
310{
311 arg.mode = BAD;
312 arg.reg[0].scale = 0;
313 arg.reg[1].scale = 0;
314 arg.value = 0;
315 INT_PTR value;
316
317 if (GetValueFromExpr (term, value))
318 {
319 arg.value = value;
320 arg.mode = DATA;
321 }
322 else
323 {
324 FindMainReg (term, arg.reg[0]);
325 if (arg.reg[0].reg != NONE)
326 {
327 arg.mode = REG;
328 }
329 else
330 {
331 DecodeAddressIndirect (term, arg);
332 }
333 }
334}
335
336// Return 0 for non-managed call. Otherwise return MD address.
337TADDR MDForCall (TADDR callee)
338{
339 // call managed code?
340 JITTypes jitType;
341 TADDR methodDesc;
342 TADDR IP = callee;
343 TADDR gcinfoAddr;
344
345 if (!GetCalleeSite (callee, IP))
346 return 0;
347
348 IP2MethodDesc (IP, methodDesc, jitType, gcinfoAddr);
349 if (methodDesc)
350 {
351 return methodDesc;
352 }
353
354 // jmp stub
355 char line[256];
356 DisasmAndClean (IP, line, 256);
357 char *ptr = line;
358 NextTerm (ptr);
359 NextTerm (ptr);
360 if (!strncmp (ptr, "jmp ", 4))
361 {
362 // jump thunk
363 NextTerm (ptr);
364 INT_PTR value;
365 methodDesc = 0;
366 if (GetValueFromExpr (ptr, value))
367 {
368 IP2MethodDesc (value, methodDesc, jitType, gcinfoAddr);
369 }
370 return methodDesc;
371 }
372 return 0;
373}
374
375// Handle a call instruction.
376void HandleCall(TADDR callee, Register *reg)
377{
378 // call managed code?
379 TADDR methodDesc = MDForCall (callee);
380 if (methodDesc)
381 {
382 DacpMethodDescData MethodDescData;
383 if (MethodDescData.Request(g_sos, TO_CDADDR(methodDesc)) == S_OK)
384 {
385 NameForMD_s(methodDesc, g_mdName,mdNameLen);
386 ExtOut(" (%S, mdToken: %p)", g_mdName, SOS_PTR(MethodDescData.MDToken));
387 return;
388 }
389 }
390
391#ifdef _TARGET_AMD64_
392 // A jump thunk?
393
394 CONTEXT ctx = {0};
395
396 ctx.ContextFlags = (CONTEXT_AMD64 | CONTEXT_CONTROL | CONTEXT_INTEGER);
397
398 for (unsigned ireg = 0; ireg < 16; ireg++)
399 {
400 if (reg[ireg].bValid)
401 {
402 *(&ctx.Rax + ireg) = reg[ireg].value;
403 }
404 }
405
406 ctx.Rip = callee;
407
408 CLRDATA_ADDRESS ip = 0, md = 0;
409 if (S_OK == g_sos->GetJumpThunkTarget(&ctx, &ip, &md))
410 {
411 if (md)
412 {
413 DacpMethodDescData MethodDescData;
414 if (MethodDescData.Request(g_sos, md) == S_OK)
415 {
416 NameForMD_s(md, g_mdName,mdNameLen);
417 ExtOut(" (%S, mdToken: %p)", g_mdName, SOS_PTR(MethodDescData.MDToken));
418 return;
419 }
420 }
421
422 if (ip != callee)
423 {
424 return HandleCall(ip, reg);
425 }
426 }
427#endif // _TARGET_AMD64_
428
429 // A JitHelper?
430 const char* name = HelperFuncName(callee);
431 if (name) {
432 ExtOut (" (JitHelp: %s)", name);
433 return;
434 }
435
436 // call unmanaged code?
437 char Symbol[1024];
438 if (SUCCEEDED(g_ExtSymbols->GetNameByOffset(TO_CDADDR(callee), Symbol, 1024,
439 NULL, NULL)))
440 {
441 if (Symbol[0] != '\0')
442 {
443 ExtOut (" (%s)", Symbol);
444 return;
445 }
446 }
447}
448
449// Determine if a value is MT/MD/Obj
450void HandleValue(TADDR value)
451{
452 // A MethodTable?
453 if (IsMethodTable(value))
454 {
455 NameForMT_s (value, g_mdName,mdNameLen);
456 ExtOut (" (MT: %S)", g_mdName);
457 return;
458 }
459
460 // A Managed Object?
461 TADDR dwMTAddr;
462 move_xp (dwMTAddr, value);
463 if (IsStringObject(value))
464 {
465 ExtOut (" (\"");
466 StringObjectContent (value, TRUE);
467 ExtOut ("\")");
468 return;
469 }
470 else if (IsMethodTable(dwMTAddr))
471 {
472 NameForMT_s (dwMTAddr, g_mdName,mdNameLen);
473 ExtOut (" (Object: %S)", g_mdName);
474 return;
475 }
476
477 // A MethodDesc?
478 if (IsMethodDesc(value))
479 {
480 NameForMD_s (value, g_mdName,mdNameLen);
481 ExtOut (" (MD: %S)", g_mdName);
482 return;
483 }
484
485 // A JitHelper?
486 const char* name = HelperFuncName(value);
487 if (name) {
488 ExtOut (" (JitHelp: %s)", name);
489 return;
490 }
491}
492
493/**********************************************************************\
494* Routine Description: *
495* *
496* Unassembly a managed code. Translating managed object, *
497* call. *
498* *
499\**********************************************************************/
500void
501#ifdef _TARGET_X86_
502 X86Machine::Unassembly
503#elif defined(_TARGET_AMD64_)
504 AMD64Machine::Unassembly
505#endif
506 (TADDR IPBegin,
507 TADDR IPEnd,
508 TADDR IPAskedFor,
509 TADDR GCStressCodeCopy,
510 GCEncodingInfo *pGCEncodingInfo,
511 SOSEHInfo *pEHInfo,
512 BOOL bSuppressLines,
513 BOOL bDisplayOffsets) const
514{
515 ULONG_PTR IP = IPBegin;
516 char line[1024];
517 Register reg [NumReg];
518 ZeroMemory (reg, sizeof(reg));
519 RegIndex dest;
520 INT_PTR value;
521 BOOL bDigit;
522 char *ptr;
523
524 ULONG curLine = -1;
525 WCHAR filename[MAX_LONGPATH];
526 ULONG linenum;
527
528 while (IP < IPEnd)
529 {
530 if (IsInterrupt())
531 return;
532
533 // Print out line numbers if needed
534 if (!bSuppressLines
535 && SUCCEEDED(GetLineByOffset(TO_CDADDR(IP), &linenum, filename, MAX_LONGPATH)))
536 {
537 if (linenum != curLine)
538 {
539 curLine = linenum;
540 ExtOut("\n%S @ %d:\n", filename, linenum);
541 }
542 }
543
544 //
545 // Print out any GC information corresponding to the current instruction offset.
546 //
547
548#ifndef FEATURE_PAL
549 if (pGCEncodingInfo)
550 {
551 SIZE_T curOffset = (IP - IPBegin) + pGCEncodingInfo->hotSizeToAdd;
552 while ( !pGCEncodingInfo->fDoneDecoding
553 && pGCEncodingInfo->ofs <= curOffset)
554 {
555 ExtOut(pGCEncodingInfo->buf);
556 ExtOut("\n");
557 SwitchToFiber(pGCEncodingInfo->pvGCTableFiber);
558 }
559 }
560#endif // FEATURE_PAL
561
562 ULONG_PTR InstrAddr = IP;
563
564 //
565 // Print out any EH info corresponding to the current offset
566 //
567 if (pEHInfo)
568 {
569 pEHInfo->FormatForDisassembly(IP - IPBegin);
570 }
571
572 if (IP == IPAskedFor)
573 {
574 ExtOut (">>> ");
575 }
576
577 //
578 // Print offsets, in addition to actual address.
579 //
580 if (bDisplayOffsets)
581 {
582 ExtOut("%04x ", IP - IPBegin);
583 }
584
585 DisasmAndClean (IP, line, _countof(line));
586
587 // look at key word
588 ptr = line;
589 NextTerm (ptr);
590 NextTerm (ptr);
591
592 //
593 // If there is gcstress info for this method, and this is a 'hlt'
594 // instruction, then gcstress probably put the 'hlt' there. Look
595 // up the original instruction and print it instead.
596 //
597
598 SSIZE_T cbIPOffset = 0;
599
600 if ( GCStressCodeCopy
601 && ( !strncmp (ptr, "hlt", 3)
602 || !strncmp (ptr, "cli", 3)
603 || !strncmp (ptr, "sti", 3)))
604 {
605 //
606 // Compute address into saved copy of the code, and
607 // disassemble the original instruction
608 //
609
610 ULONG_PTR OrigInstrAddr = GCStressCodeCopy + (InstrAddr - IPBegin);
611 ULONG_PTR OrigIP = OrigInstrAddr;
612
613 DisasmAndClean(OrigIP, line, _countof(line));
614
615 //
616 // Increment the real IP based on the size of the unmodifed
617 // instruction
618 //
619
620 IP = InstrAddr + (OrigIP - OrigInstrAddr);
621
622 cbIPOffset = IP - OrigIP;
623
624 //
625 // Print out real code address in place of the copy address
626 //
627
628#ifdef _WIN64
629 ExtOut("%08x`%08x ", (ULONG)(InstrAddr >> 32), (ULONG)InstrAddr);
630#else
631 ExtOut("%08x ", (ULONG)InstrAddr);
632#endif
633
634 ptr = line;
635 NextTerm (ptr);
636
637 //
638 // Print out everything after the code address, and skip the
639 // instruction bytes
640 //
641
642 ExtOut(ptr);
643
644 NextTerm (ptr);
645
646 //
647 // Add an indicator that this address has not executed yet
648 //
649
650 ExtOut(" (gcstress)");
651 }
652 else
653 {
654 ExtOut (line);
655 }
656
657 if (!strncmp (ptr, "mov ", 4))
658 {
659 NextTerm (ptr);
660
661 dest = FindReg(ptr);
662 if (dest != NONE)
663 {
664 NextTerm (ptr);
665
666 if (FindSrc (ptr, reg, value, bDigit))
667 {
668 reg[dest].bValid = TRUE;
669 reg[dest].value = value;
670 // Is it a managed obj
671 if (bDigit)
672 HandleValue (reg[dest].value);
673 }
674 else
675 {
676 reg[dest].bValid = FALSE;
677 }
678 }
679 }
680 else if (!strncmp (ptr, "call ", 5))
681 {
682 NextTerm (ptr);
683 if (FindSrc (ptr, reg, value, bDigit))
684 {
685 if (bDigit)
686 value += cbIPOffset;
687
688 HandleCall (value, reg);
689 }
690
691 // trash EAX, ECX, EDX
692 reg[EAX].bValid = FALSE;
693 reg[ECX].bValid = FALSE;
694 reg[EDX].bValid = FALSE;
695
696#ifdef _TARGET_AMD64_
697 reg[R8].bValid = FALSE;
698 reg[R9].bValid = FALSE;
699 reg[R10].bValid = FALSE;
700 reg[R11].bValid = FALSE;
701#endif // _TARGET_AMD64_
702 }
703 else if (!strncmp (ptr, "lea ", 4))
704 {
705 NextTerm (ptr);
706 dest = FindReg(ptr);
707 if (dest != NONE)
708 {
709 NextTerm (ptr);
710 if (FindSrc (ptr, reg, value, bDigit))
711 {
712 reg[dest].bValid = TRUE;
713 reg[dest].value = value;
714 }
715 else
716 {
717 reg[dest].bValid = FALSE;
718 }
719 }
720 }
721 else if (!strncmp (ptr, "push ", 5))
722 {
723 // do not do anything
724 NextTerm (ptr);
725 if (FindSrc (ptr, reg, value, bDigit))
726 {
727 if (bDigit)
728 {
729 HandleValue (value);
730 }
731 }
732 }
733 else
734 {
735 // assume this instruction will trash dest reg
736 NextTerm (ptr);
737 dest = FindReg(ptr);
738 if (dest != NONE)
739 reg[dest].bValid = FALSE;
740 }
741 ExtOut ("\n");
742 }
743
744 //
745 // Print out any "end" EH info (where the end address is the byte immediately following the last instruction)
746 //
747 if (pEHInfo)
748 {
749 pEHInfo->FormatForDisassembly(IP - IPBegin);
750 }
751}
752
753// Find the real callee site. Handle JMP instruction.
754// Return TRUE if we get the address, FALSE if not.
755BOOL GetCalleeSite (TADDR IP, TADDR &IPCallee)
756{
757 while (TRUE) {
758 unsigned char inst[2];
759 if (g_ExtData->ReadVirtual(TO_CDADDR(IP), inst, sizeof(inst), NULL) != S_OK)
760 {
761 return FALSE;
762 }
763 if (inst[0] == 0xEB) {
764 IP += 2+(char)inst[1];
765 }
766 else if (inst[0] == 0xE9) {
767 int displace;
768 if (g_ExtData->ReadVirtual(TO_CDADDR(IP+1), &displace, sizeof(displace), NULL) != S_OK)
769 {
770 return FALSE;
771 }
772 else
773 {
774 IP += 5+displace;
775 }
776 }
777 else if (inst[0] == 0xFF && (inst[1] & 070) == 040) {
778 if (inst[1] == 0x25) {
779 DWORD displace;
780 if (g_ExtData->ReadVirtual(TO_CDADDR(IP+2), &displace, sizeof(displace), NULL) != S_OK)
781 {
782 return FALSE;
783 }
784 if (g_ExtData->ReadVirtual(TO_CDADDR(displace), &displace, sizeof(displace), NULL) != S_OK)
785 {
786 return FALSE;
787 }
788 else
789 {
790 IP = displace;
791 }
792 }
793 else
794 // Target for jmp is determined from register values.
795 return FALSE;
796 }
797 else
798 {
799 IPCallee = IP;
800 return TRUE;
801 }
802 }
803}
804
805// GetFinalTarget is based on HandleCall, but avoids printing anything to the output.
806// This is currently only called on x64
807eTargetType GetFinalTarget(TADDR callee, TADDR* finalMDorIP)
808{
809 // call managed code?
810 TADDR methodDesc = MDForCall (callee);
811 if (methodDesc)
812 {
813 DacpMethodDescData MethodDescData;
814 if (MethodDescData.Request(g_sos, TO_CDADDR(methodDesc)) == S_OK)
815 {
816 *finalMDorIP = methodDesc;
817 return ettMD;
818 }
819 }
820
821#ifdef _TARGET_AMD64_
822 // A jump thunk?
823
824 CONTEXT ctx = {0};
825 ctx.ContextFlags = (CONTEXT_AMD64 | CONTEXT_CONTROL | CONTEXT_INTEGER);
826 ctx.Rip = callee;
827
828 CLRDATA_ADDRESS ip = 0, md = 0;
829 if (S_OK == g_sos->GetJumpThunkTarget(&ctx, &ip, &md))
830 {
831 if (md)
832 {
833 DacpMethodDescData MethodDescData;
834 if (MethodDescData.Request(g_sos, md) == S_OK)
835 {
836 *finalMDorIP = md;
837 return ettStub;
838 }
839 }
840
841 if (ip != callee)
842 {
843 return GetFinalTarget(ip, finalMDorIP);
844 }
845 }
846#endif // _TARGET_AMD64_
847
848 // A JitHelper?
849 const char* name = HelperFuncName(callee);
850 if (name) {
851 *finalMDorIP = callee;
852 return ettJitHelp;
853 }
854
855 // call unmanaged code?
856 *finalMDorIP = callee;
857 return ettNative;
858}
859
860#ifndef FEATURE_PAL
861
862void ExpFuncStateInit (TADDR *IPRetAddr)
863{
864 ULONG64 offset;
865 if (FAILED(g_ExtSymbols->GetOffsetByName("ntdll!KiUserExceptionDispatcher", &offset))) {
866 return;
867 }
868
869 // test if we have a minidump for which the image is not cached anymore. this avoids
870 // the having the while loop below spin forever (or a very long time)...
871 // (Watson backend hit this a few times, and they had to institute a timeout policy
872 // to work around this)
873 SIZE_T instrs;
874 if (FAILED(g_ExtData->ReadVirtual(offset, &instrs, sizeof(instrs), NULL)) || instrs == 0) {
875 return;
876 }
877
878 char line[256];
879 int i = 0;
880 int cnt = 0;
881#ifdef SOS_TARGET_X86
882 // On x86 and x64 the last 3 "call" instructions in ntdll!KiUserExceptionDispatcher
883 // are making calls to OS APIs that take as argument the context record (and some
884 // of them the exception record as well)
885 const int cCallInstrs = 3;
886#elif defined(SOS_TARGET_AMD64)
887 // On x64 the first "call" instruction should be considered, as well
888 const int cCallInstrs = 4;
889#endif
890
891 while (i < cCallInstrs) {
892 g_ExtControl->Disassemble (offset, 0, line, 256, NULL, &offset);
893 if (strstr (line, "call")) {
894 IPRetAddr[i++] = (TADDR)offset;
895 }
896 // if we didn't find at least one "call" in the first 500 instructions give up...
897 if (++cnt >= 500 && IPRetAddr[0] == 0)
898 break;
899 }
900}
901
902#endif // FEATURE_PAL
903
904
905/**********************************************************************\
906* Routine Description: *
907* *
908* This function is called to fill in a cross platform context *
909* struct by looking on the stack for return addresses into *
910* KiUserExceptionDispatcher *
911* *
912\**********************************************************************/
913BOOL
914#ifdef SOS_TARGET_X86
915 X86Machine::GetExceptionContext
916#elif defined(SOS_TARGET_AMD64)
917 AMD64Machine::GetExceptionContext
918#endif
919 (TADDR stack,
920 TADDR IP,
921 TADDR * cxrAddr,
922 CROSS_PLATFORM_CONTEXT * pcxr,
923 TADDR * exrAddr,
924 PEXCEPTION_RECORD exr) const
925{
926#ifndef FEATURE_PAL
927#ifdef SOS_TARGET_X86
928 X86_CONTEXT * cxr = &pcxr->X86Context;
929 size_t contextSize = offsetof(CONTEXT, ExtendedRegisters);
930#elif defined(SOS_TARGET_AMD64)
931 AMD64_CONTEXT * cxr = &pcxr->Amd64Context;
932 size_t contextSize = offsetof(CONTEXT, FltSave);
933#endif
934
935 static TADDR IPRetAddr[4] = {0, 0, 0, 0};
936
937 if (IPRetAddr[0] == 0) {
938 ExpFuncStateInit (IPRetAddr);
939 }
940 *cxrAddr = 0;
941 *exrAddr = 0;
942
943#ifdef SOS_TARGET_X86
944
945 if (IP == IPRetAddr[0]) {
946 *exrAddr = stack + sizeof(TADDR);
947 *cxrAddr = stack + 2*sizeof(TADDR);
948 }
949 else if (IP == IPRetAddr[1]) {
950 *cxrAddr = stack + sizeof(TADDR);
951 }
952 else if (IP == IPRetAddr[2]) {
953 *exrAddr = stack + sizeof(TADDR);
954 *cxrAddr = stack + 2*sizeof(TADDR);
955 }
956 else
957 return FALSE;
958
959 if (FAILED (g_ExtData->ReadVirtual(TO_CDADDR(*cxrAddr), &stack, sizeof(stack), NULL)))
960 return FALSE;
961 *cxrAddr = stack;
962
963 //if ((pContext->ContextFlags & CONTEXT_EXTENDED_REGISTERS) == CONTEXT_EXTENDED_REGISTERS)
964 // contextSize += sizeof(pContext->ExtendedRegisters);
965 if (FAILED (g_ExtData->ReadVirtual(TO_CDADDR(stack), cxr, (ULONG)contextSize, NULL))) {
966 return FALSE;
967 }
968
969 if (*exrAddr) {
970 if (FAILED (g_ExtData->ReadVirtual(TO_CDADDR(*exrAddr), &stack, sizeof(stack), NULL)))
971 {
972 *exrAddr = 0;
973 return TRUE;
974 }
975 *exrAddr = stack;
976 size_t erSize = offsetof (EXCEPTION_RECORD, ExceptionInformation);
977 if (FAILED (g_ExtData->ReadVirtual(TO_CDADDR(stack), exr, (ULONG)erSize, NULL))) {
978 *exrAddr = 0;
979 return TRUE;
980 }
981 }
982
983#elif defined(SOS_TARGET_AMD64)
984
985 if (IP == IPRetAddr[0] || IP == IPRetAddr[1] || IP == IPRetAddr[3]) {
986 *exrAddr = stack + sizeof(TADDR) + 0x4F0;
987 *cxrAddr = stack + sizeof(TADDR);
988 } else if (IP == IPRetAddr[2]) {
989 *cxrAddr = stack + sizeof(TADDR);
990 }
991 else {
992 return FALSE;
993 }
994
995 if (FAILED (g_ExtData->ReadVirtual(TO_CDADDR(*cxrAddr), cxr, (ULONG)contextSize, NULL))) {
996 return FALSE;
997 }
998
999 if (*exrAddr) {
1000 size_t erSize = offsetof (EXCEPTION_RECORD, ExceptionInformation);
1001 if (FAILED (g_ExtData->ReadVirtual(TO_CDADDR(*exrAddr), exr, (ULONG)erSize, NULL))) {
1002 *exrAddr = 0;
1003 return TRUE;
1004 }
1005 }
1006
1007#endif
1008 return TRUE;
1009#else
1010 return FALSE;
1011#endif // FEATURE_PAL
1012}
1013
1014
1015/**********************************************************************\
1016* Routine Description: *
1017* *
1018* This function is called to determine if a DWORD on the stack is *
1019* a return address. *
1020* It does this by checking several bytes before the DWORD to see if *
1021* there is a call instruction. *
1022* *
1023\**********************************************************************/
1024
1025void
1026#ifdef _TARGET_X86_
1027 X86Machine::IsReturnAddress
1028#elif defined(_TARGET_AMD64_)
1029 AMD64Machine::IsReturnAddress
1030#endif
1031 (TADDR retAddr, TADDR* whereCalled) const
1032{
1033 *whereCalled = 0;
1034
1035 unsigned char spotend[6];
1036 move_xp (spotend, retAddr-6);
1037 unsigned char *spot = spotend+6;
1038 TADDR addr;
1039
1040 // Note this is possible to be spoofed, but pretty unlikely
1041 // call XXXXXXXX
1042 if (spot[-5] == 0xE8) {
1043 DWORD offs = 0;
1044 move_xp (offs, retAddr-4);
1045 *whereCalled = retAddr + (ULONG64)(LONG)(offs);
1046 //*whereCalled = *((int*) (retAddr-4)) + retAddr;
1047 // on WOW64 the range valid for code is almost the whole 4GB address space
1048 if (g_ExtData->ReadVirtual(TO_CDADDR(*whereCalled), &addr, sizeof(addr), NULL) == S_OK)
1049 {
1050 TADDR callee;
1051 if (GetCalleeSite(*whereCalled, callee)) {
1052 *whereCalled = callee;
1053 }
1054 return;
1055 }
1056 else
1057 *whereCalled = 0;
1058 }
1059
1060 // call [XXXXXXXX]
1061 if (spot[-6] == 0xFF && (spot[-5] == 025)) {
1062 DWORD offs = 0;
1063 move_xp (offs, retAddr-4);
1064#ifdef _TARGET_AMD64_
1065 // on x64 this 32-bit is an RIP offset
1066 addr = retAddr + (ULONG64)(LONG)(offs);
1067#elif defined (_TARGET_X86_)
1068 addr = offs;
1069#endif
1070 if (g_ExtData->ReadVirtual(TO_CDADDR(addr), whereCalled, sizeof(*whereCalled), NULL) == S_OK) {
1071 move_xp (*whereCalled, addr);
1072 //*whereCalled = **((unsigned**) (retAddr-4));
1073 // on WOW64 the range valid for code is almost the whole 4GB address space
1074 if (g_ExtData->ReadVirtual(TO_CDADDR(*whereCalled), &addr, sizeof(addr), NULL) == S_OK)
1075 {
1076 TADDR callee;
1077 if (GetCalleeSite(*whereCalled,callee)) {
1078 *whereCalled = callee;
1079 }
1080 return;
1081 }
1082 else
1083 *whereCalled = 0;
1084 }
1085 else
1086 *whereCalled = 0;
1087 }
1088
1089 // call [REG+XX]
1090 if (spot[-3] == 0xFF && (spot[-2] & ~7) == 0120 && (spot[-2] & 7) != 4)
1091 {
1092 *whereCalled = 0xFFFFFFFF;
1093 return;
1094 }
1095 if (spot[-4] == 0xFF && spot[-3] == 0124)
1096 {
1097 *whereCalled = 0xFFFFFFFF;
1098 return;
1099 }
1100
1101 // call [REG+XXXX]
1102 if (spot[-6] == 0xFF && (spot[-5] & ~7) == 0220 && (spot[-5] & 7) != 4)
1103 {
1104 *whereCalled = 0xFFFFFFFF;
1105 return;
1106 }
1107 if (spot[-7] == 0xFF && spot[-6] == 0224)
1108 {
1109 *whereCalled = 0xFFFFFFFF;
1110 return;
1111 }
1112
1113 // call [REG]
1114 if (spot[-2] == 0xFF && (spot[-1] & ~7) == 0020 && (spot[-1] & 7) != 4 && (spot[-1] & 7) != 5)
1115 {
1116 *whereCalled = 0xFFFFFFFF;
1117 return;
1118 }
1119
1120 // call REG
1121 if (spot[-2] == 0xFF && (spot[-1] & ~7) == 0320 && (spot[-1] & 7) != 4)
1122 {
1123 *whereCalled = 0xFFFFFFFF;
1124 return;
1125 }
1126
1127 // There are other cases, but I don't believe they are used.
1128 return;
1129}
1130
1131
1132#ifdef _X86_
1133
1134///
1135/// This is dead code, not called from anywhere, not linked in the final product.
1136///
1137static BOOL DecodeLine (___in __in_z char *line, ___in __in_z const char *const inst, InstData& arg1, InstData& arg2)
1138{
1139 char *ptr = line;
1140 if (inst[0] == '*' || !strncmp (ptr, inst, strlen (inst)))
1141 {
1142 arg1.mode = BAD;
1143 arg2.mode = BAD;
1144 NextTerm (ptr);
1145 if (*ptr == '\0')
1146 {
1147 arg1.mode = NODATA;
1148 return TRUE;
1149 }
1150
1151 DecodeAddressTerm (ptr, arg1);
1152 NextTerm (ptr);
1153 if (*ptr == '\0')
1154 {
1155 return TRUE;
1156 }
1157 DecodeAddressTerm (ptr, arg2);
1158 return TRUE;
1159 }
1160 else
1161 return FALSE;
1162}
1163
1164void PrintReg (Register *reg)
1165{
1166 ExtOut ("[EBX=%08x ESI=%08x EDI=%08x EBP=%08x ESP=%08x]\n",
1167 reg[EBX].value, reg[ESI].value, reg[EDI].value, reg[EBP].value,
1168 reg[ESP].value);
1169}
1170
1171
1172struct CallInfo
1173{
1174 DWORD_PTR stackPos;
1175 DWORD_PTR retAddr;
1176 DWORD_PTR whereCalled;
1177};
1178
1179// Search for a Return address on stack.
1180BOOL GetNextRetAddr (DWORD_PTR stackBegin, DWORD_PTR stackEnd,
1181 CallInfo &callInfo)
1182{
1183 for (callInfo.stackPos = stackBegin;
1184 callInfo.stackPos <= stackEnd;
1185 callInfo.stackPos += 4)
1186 {
1187 if (!SafeReadMemory (callInfo.stackPos, &callInfo.retAddr, 4, NULL))
1188 continue;
1189
1190 g_targetMachine->IsReturnAddress(callInfo.retAddr, &callInfo.whereCalled);
1191 if (callInfo.whereCalled)
1192 {
1193 return TRUE;
1194 }
1195 }
1196
1197 return FALSE;
1198}
1199
1200struct FrameInfo
1201{
1202 DWORD_PTR IPStart;
1203 DWORD_PTR Prolog;
1204 DWORD_PTR FrameBase; // The value of ESP at the entry.
1205 DWORD_PTR StackEnd;
1206 DWORD_PTR argCount;
1207 BOOL bEBPFrame;
1208};
1209
1210// if a EBP frame, return TRUE if EBP has been setup
1211void GetFrameBaseHelper (DWORD_PTR IPBegin, DWORD_PTR IPEnd,
1212 INT_PTR &StackChange)
1213{
1214 char line[256];
1215 char *ptr;
1216 InstData arg1;
1217 InstData arg2;
1218 DWORD_PTR IP = IPBegin;
1219 StackChange = 0;
1220 while (IP < IPEnd)
1221 {
1222 DisasmAndClean (IP, line, 256);
1223 ptr = line;
1224 NextTerm (ptr);
1225 NextTerm (ptr);
1226 if (DecodeLine (ptr, "push ", arg1, arg2))
1227 {
1228 StackChange += 4;
1229 }
1230 else if (DecodeLine (ptr, "pop ", arg1, arg2))
1231 {
1232 StackChange -= 4;
1233 }
1234 else if (DecodeLine (ptr, "sub ", arg1, arg2))
1235 {
1236 if (arg1.mode == REG && arg1.reg[0].reg == ESP)
1237 {
1238 if (arg2.mode == DATA)
1239 StackChange -= arg2.value;
1240 }
1241 }
1242 else if (DecodeLine (ptr, "add ", arg1, arg2))
1243 {
1244 if (arg1.mode == REG && arg1.reg[0].reg == ESP)
1245 {
1246 if (arg2.mode == DATA)
1247 StackChange += arg2.value;
1248 }
1249 }
1250 else if (!strncmp (ptr, "ret", 3)) {
1251 return;
1252 }
1253 }
1254}
1255
1256enum IPSTATE {IPPROLOG1 /*Before EBP set*/, IPPROLOG2 /*After EBP set*/, IPCODE, IPEPILOG, IPEND};
1257
1258IPSTATE GetIpState (DWORD_PTR IP, FrameInfo* pFrame)
1259{
1260 char line[256];
1261 char *ptr;
1262
1263 if (IP >= pFrame->IPStart && IP < pFrame->IPStart + pFrame->Prolog)
1264 {
1265 if (pFrame->bEBPFrame) {
1266 DWORD_PTR pIP = pFrame->IPStart;
1267 while (pIP < IP) {
1268 DisasmAndClean (IP,line, 256);
1269 ptr = line;
1270 NextTerm (ptr);
1271 NextTerm (ptr);
1272 if (!strncmp (ptr, "mov ", 4)) {
1273 NextTerm (ptr);
1274 if (!strncmp (ptr, "ebp", 3)) {
1275 NextTerm (ptr);
1276 if (!strncmp (ptr, "esp", 3)) {
1277 return IPPROLOG2;
1278 }
1279 }
1280 }
1281 else if (!strncmp (ptr, "call ", 5)) {
1282 NextTerm (ptr);
1283 if (strstr (ptr, "__EH_prolog")) {
1284 return IPPROLOG2;
1285 }
1286 }
1287 }
1288 pIP = IP;
1289 while (pIP < pFrame->IPStart + pFrame->Prolog) {
1290 DisasmAndClean (IP,line, 256);
1291 ptr = line;
1292 NextTerm (ptr);
1293 NextTerm (ptr);
1294 if (!strncmp (ptr, "mov ", 4)) {
1295 NextTerm (ptr);
1296 if (!strncmp (ptr, "ebp", 3)) {
1297 NextTerm (ptr);
1298 if (!strncmp (ptr, "esp", 3)) {
1299 return IPPROLOG1;
1300 }
1301 }
1302 }
1303 else if (!strncmp (ptr, "call ", 5)) {
1304 NextTerm (ptr);
1305 if (strstr (ptr, "__EH_prolog")) {
1306 return IPPROLOG1;
1307 }
1308 }
1309 }
1310
1311 ExtOut ("Fail to find where EBP is saved\n");
1312 return IPPROLOG2;
1313 }
1314 else
1315 {
1316 return IPPROLOG1;
1317 }
1318 }
1319
1320 int nline = 0;
1321 while (1) {
1322 DisasmAndClean (IP,line, 256);
1323 nline ++;
1324 ptr = line;
1325 NextTerm (ptr);
1326 NextTerm (ptr);
1327 if (!strncmp (ptr, "ret", 3)) {
1328 return (nline==1)?IPEND:IPEPILOG;
1329 }
1330 else if (!strncmp (ptr, "leave", 5)) {
1331 return IPEPILOG;
1332 }
1333 else if (!strncmp (ptr, "call", 4)) {
1334 return IPCODE;
1335 }
1336 else if (ptr[0] == 'j') {
1337 return IPCODE;
1338 }
1339 }
1340}
1341
1342// FrameBase is the ESP value at the entry of a function.
1343BOOL GetFrameBase (Register callee[], FrameInfo* pFrame)
1344{
1345 //char line[256];
1346 //char *ptr;
1347 INT_PTR dwpushed = 0;
1348 //DWORD_PTR IP;
1349
1350 IPSTATE IpState = GetIpState (callee[EIP].value, pFrame);
1351
1352 if (pFrame->bEBPFrame)
1353 {
1354 if (IpState == IPEND || IpState == IPPROLOG1) {
1355 pFrame->FrameBase = callee[ESP].value;
1356 }
1357 else
1358 {
1359 pFrame->FrameBase = callee[EBP].value+4;
1360 }
1361 return TRUE;
1362 }
1363 else
1364 {
1365 if (IpState == IPEND) {
1366 pFrame->FrameBase = callee[ESP].value;
1367 return TRUE;
1368 }
1369
1370 DWORD_PTR IPBegin, IPEnd;
1371 if (IpState == IPEPILOG) {
1372 IPBegin = callee[EIP].value;
1373 IPEnd = ~0ul;
1374 }
1375 else if (IpState == IPPROLOG1) {
1376 IPBegin = pFrame->IPStart;
1377 IPEnd = callee[EIP].value;
1378 }
1379 else
1380 {
1381 IPBegin = pFrame->IPStart;
1382 IPEnd = IPBegin + pFrame->Prolog;
1383 }
1384 GetFrameBaseHelper (IPBegin, IPEnd, dwpushed);
1385
1386 if (IpState == IPEPILOG) {
1387 ExtOut ("stack %d\n", dwpushed);
1388 pFrame->FrameBase = callee[ESP].value - dwpushed;
1389 return TRUE;
1390 }
1391
1392 CallInfo callInfo;
1393 if (GetNextRetAddr (callee[ESP].value + dwpushed,
1394 pFrame->StackEnd, callInfo))
1395 {
1396 pFrame->FrameBase = callInfo.stackPos;
1397 return TRUE;
1398 }
1399
1400 return FALSE;
1401 }
1402}
1403
1404// caller[ESP]: the ESP value when we return to caller.
1405void RestoreCallerRegister (Register callee[], Register caller[],
1406 FrameInfo *pFrame)
1407{
1408 if (pFrame->bEBPFrame)
1409 {
1410 if (callee[ESP].value < pFrame->FrameBase)
1411 {
1412 SafeReadMemory (pFrame->FrameBase-4, &caller[EBP].value, 4, NULL);
1413 }
1414 else
1415 caller[EBP].value = callee[EBP].value;
1416 }
1417 else
1418 caller[EBP].value = callee[EBP].value;
1419
1420 caller[EBP].bValid = TRUE;
1421 caller[ESP].value = pFrame->FrameBase + 4 + pFrame->argCount;
1422 callee[EBP].value = pFrame->FrameBase - sizeof(void*);
1423 SafeReadMemory (pFrame->FrameBase, &caller[EIP].value, 4, NULL);
1424}
1425
1426BOOL GetFrameInfoHelper (Register callee[], Register caller[],
1427 FrameInfo *pFrame)
1428{
1429 if (GetFrameBase (callee, pFrame))
1430 {
1431 RestoreCallerRegister (callee, caller, pFrame);
1432 return TRUE;
1433 }
1434 else
1435 return FALSE;
1436}
1437
1438// Return TRUE if Frame Info is OK, otherwise FALSE.
1439BOOL GetUnmanagedFrameInfo (Register callee[], Register caller[],
1440 DumpStackFlag &DSFlag, PFPO_DATA data)
1441{
1442 FrameInfo Frame;
1443 ULONG64 base;
1444 g_ExtSymbols->GetModuleByOffset (callee[EIP].value, 0, NULL, &base);
1445 Frame.IPStart = data->ulOffStart + (ULONG_PTR)base;
1446 Frame.Prolog = data->cbProlog;
1447 // Why do we have to do this to make it work?
1448 if (Frame.Prolog == 1) {
1449 Frame.Prolog = 0;
1450 }
1451 Frame.bEBPFrame = (data->cbFrame == FRAME_NONFPO);
1452 Frame.StackEnd = DSFlag.end;
1453 Frame.argCount = data->cdwParams*4;
1454
1455 return GetFrameInfoHelper (callee, caller, &Frame);
1456}
1457
1458// offsetEBP: offset of stack position where EBP is saved.
1459// If EBP is not saved, *offsetEBP = -1 (~0ul);
1460BOOL IPReachable (DWORD_PTR IPBegin, DWORD_PTR IP, DWORD *offsetEBP)
1461{
1462 *offsetEBP = ~0ul;
1463 return FALSE;
1464}
1465
1466BOOL HandleEEStub (Register callee[], Register caller[],
1467 DumpStackFlag &DSFlag)
1468{
1469 // EEStub can only be called by IP directory. Let's look for possible caller.
1470 CallInfo callInfo;
1471 DWORD_PTR stackPos = callee[ESP].value;
1472 while (stackPos < DSFlag.end) {
1473 if (GetNextRetAddr (stackPos,
1474 DSFlag.end, callInfo))
1475 {
1476 if (callInfo.whereCalled != ~0ul) {
1477 DWORD offsetEBP;
1478 if (IPReachable (callInfo.whereCalled, callee[EIP].value, &offsetEBP)) {
1479 caller[EIP].value = callInfo.retAddr;
1480 // TODO: We may have saved EBP.
1481 if (offsetEBP == ~0ul) {
1482 caller[EBP].value = callee[EBP].value;
1483 }
1484 else
1485 {
1486 TADDR offs = TO_TADDR(callInfo.stackPos)-sizeof(PVOID)-offsetEBP;
1487 SafeReadMemory (offs, &caller[EBP].value, sizeof(PVOID), NULL);
1488 }
1489 caller[ESP].value = callInfo.stackPos+sizeof(PVOID);
1490 return TRUE;
1491 }
1492 }
1493 stackPos = callInfo.stackPos+sizeof(PVOID);
1494 }
1495 else
1496 return FALSE;
1497 }
1498
1499 return FALSE;
1500}
1501
1502
1503BOOL HandleByEpilog (Register callee[], Register caller[],
1504 DumpStackFlag &DSFlag)
1505{
1506 return FALSE;
1507}
1508
1509#ifndef FEATURE_PAL
1510void RestoreFrameUnmanaged (Register *reg, DWORD_PTR CurIP)
1511{
1512 char line[256];
1513 char *ptr;
1514 DWORD_PTR IP = CurIP;
1515 INT_PTR value;
1516 BOOL bDigit;
1517 BOOL bGoodESP = true;
1518 RegIndex dest;
1519
1520 ULONG64 base;
1521 g_ExtSymbols->GetModuleByOffset (TO_CDADDR(CurIP), 0, NULL, &base);
1522 ULONG64 handle;
1523 g_ExtSystem->GetCurrentProcessHandle(&handle);
1524 PFPO_DATA data =
1525 (PFPO_DATA)SymFunctionTableAccess((HANDLE)handle, CurIP);
1526 DWORD_PTR IPBegin = data->ulOffStart + (ULONG_PTR)base;
1527
1528 if (CurIP - IPBegin <= data->cbProlog)
1529 {
1530 // We are inside a prolog.
1531 // See where we save the callee saved register.
1532 // Also how many DWORD's we pushd
1533 IP = IPBegin;
1534 reg[ESP].stack = 0;
1535 reg[ESP].bOnStack = FALSE;
1536 reg[EBP].stack = 0;
1537 reg[EBP].bOnStack = FALSE;
1538 reg[ESI].stack = 0;
1539 reg[ESI].bOnStack = FALSE;
1540 reg[EDI].stack = 0;
1541 reg[EDI].bOnStack = FALSE;
1542 reg[EBX].stack = 0;
1543 reg[EBX].bOnStack = FALSE;
1544
1545 while (IP < CurIP)
1546 {
1547 DisasmAndClean (IP, line, 256);
1548 ptr = line;
1549 NextTerm (ptr);
1550 NextTerm (ptr);
1551 if (!strncmp (ptr, "push ", 5))
1552 {
1553 reg[ESP].stack += 4;
1554 NextTerm (ptr);
1555 dest = FindReg(ptr);
1556 if (dest == EBP || dest == EBX || dest == ESI || dest == EDI)
1557 {
1558 reg[dest].bOnStack = TRUE;
1559 reg[dest].stack = reg[ESP].stack;
1560 }
1561 }
1562 else if (!strncmp (ptr, "sub ", 4))
1563 {
1564 NextTerm (ptr);
1565 dest = FindReg(ptr);
1566 if (dest == ESP)
1567 {
1568 NextTerm (ptr);
1569 char *endptr;
1570 reg[ESP].stack += strtoul(ptr, &endptr, 16);;
1571 }
1572 }
1573 }
1574
1575 DWORD_PTR baseESP = reg[ESP].value + reg[ESP].stack;
1576 if (reg[EBP].bOnStack)
1577 {
1578 move_xp (reg[EBP].value, baseESP-reg[EBP].stack);
1579 }
1580 if (reg[EBX].bOnStack)
1581 {
1582 move_xp (reg[EBX].value, baseESP-reg[EBX].stack);
1583 }
1584 if (reg[ESI].bOnStack)
1585 {
1586 move_xp (reg[ESI].value, baseESP-reg[ESI].stack);
1587 }
1588 if (reg[EDI].bOnStack)
1589 {
1590 move_xp (reg[EDI].value, baseESP-reg[EDI].stack);
1591 }
1592 move_xp (reg[EIP].value, baseESP);
1593 reg[ESP].value = baseESP + 4;
1594 return;
1595 }
1596
1597 if (data->cbFrame == FRAME_NONFPO)
1598 {
1599 // EBP Frame
1600 }
1601
1602 // Look for epilog
1603 while (1)
1604 {
1605 DisasmAndClean (IP, line, 256);
1606 ptr = line;
1607 NextTerm (ptr);
1608 NextTerm (ptr);
1609 if (!strncmp (ptr, "mov ", 4))
1610 {
1611 NextTerm (ptr);
1612 dest = FindReg(ptr);
1613 if (dest == ESP)
1614 {
1615 NextTerm (ptr);
1616 if (FindReg(ptr) == EBP)
1617 {
1618 // We have a EBP frame
1619 bGoodESP = true;
1620 reg[ESP].value = reg[EBP].value;
1621 }
1622 }
1623 }
1624 else if (!strncmp (ptr, "ret", 3))
1625 {
1626 NextTerm (ptr);
1627 // check the value on stack is a return address.
1628 DWORD_PTR retAddr;
1629 DWORD_PTR whereCalled;
1630 move_xp (retAddr, reg[ESP].value);
1631 int ESPAdjustCount = 0;
1632 while (1)
1633 {
1634 g_targetMachine->IsReturnAddress(retAddr, &whereCalled);
1635 if (whereCalled)
1636 break;
1637 ESPAdjustCount ++;
1638 reg[ESP].value += 4;
1639 move_xp (retAddr, reg[ESP].value);
1640 }
1641 reg[EIP].value = retAddr;
1642 if (ESPAdjustCount)
1643 {
1644 ESPAdjustCount *= 4;
1645 }
1646 if (reg[EBX].bOnStack)
1647 {
1648 reg[EBX].stack += ESPAdjustCount;
1649 move_xp (reg[EBX].value, reg[EBX].stack);
1650 }
1651 if (reg[ESI].bOnStack)
1652 {
1653 reg[ESI].stack += ESPAdjustCount;
1654 move_xp (reg[ESI].value, reg[EBX].stack);
1655 }
1656 if (reg[EDI].bOnStack)
1657 {
1658 reg[EDI].stack += ESPAdjustCount;
1659 move_xp (reg[EDI].value, reg[EBX].stack);
1660 }
1661
1662 reg[ESP].value += 4;
1663 if (ptr[0] != '\0')
1664 {
1665 FindSrc (ptr, reg, value, bDigit);
1666 reg[ESP].value += value;
1667 }
1668 break;
1669 }
1670 else if (!strncmp (ptr, "pop ", 4))
1671 {
1672 NextTerm (ptr);
1673 dest = FindReg(ptr);
1674 if (dest == EBP || dest == EBX || dest == ESI || dest == EDI)
1675 {
1676 reg[dest].stack = reg[ESP].value;
1677 reg[dest].bOnStack = TRUE;
1678 }
1679 reg[ESP].value += 4;
1680 }
1681 else if (!strncmp (ptr, "add ", 4))
1682 {
1683 NextTerm (ptr);
1684 dest = FindReg(ptr);
1685 if (dest == ESP)
1686 {
1687 NextTerm (ptr);
1688 FindSrc (ptr, reg, value, bDigit);
1689 reg[ESP].value += value;
1690 }
1691 }
1692 else if (!strncmp (ptr, "call ", 5))
1693 {
1694 // assume we do not have a good value on ESP.
1695 // We could go into the call and find out number of pushed args.
1696 bGoodESP = FALSE;
1697 }
1698 }
1699
1700 // Look for prolog
1701}
1702#endif // !FEATURE_PAL
1703
1704#elif defined(_AMD64_)
1705
1706
1707#endif // !_X86_
1708