wintrust: Use path in WIN_TRUST_SUBJECT_FILE structure rather than assuming a path...
[wine] / dlls / dbghelp / stack.c
1 /*
2  * Stack walking
3  *
4  * Copyright 1995 Alexandre Julliard
5  * Copyright 1996 Eric Youngdale
6  * Copyright 1999 Ove Kåven
7  * Copyright 2004 Eric Pouech
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23
24 #include "config.h"
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <assert.h>
29
30 #include "ntstatus.h"
31 #define WIN32_NO_STATUS
32 #include "dbghelp_private.h"
33 #include "winternl.h"
34 #include "wine/winbase16.h"
35 #include "wine/debug.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
38
39 enum st_mode {stm_start, stm_32bit, stm_16bit, stm_done};
40
41 static const char* wine_dbgstr_addr(const ADDRESS* addr)
42 {
43     if (!addr) return "(null)";
44     switch (addr->Mode)
45     {
46     case AddrModeFlat:
47         return wine_dbg_sprintf("flat<%08x>", addr->Offset);
48     case AddrMode1616:
49         return wine_dbg_sprintf("1616<%04x:%04x>", addr->Segment, addr->Offset);
50     case AddrMode1632:
51         return wine_dbg_sprintf("1632<%04x:%08x>", addr->Segment, addr->Offset);
52     case AddrModeReal:
53         return wine_dbg_sprintf("real<%04x:%04x>", addr->Segment, addr->Offset);
54     default:
55         return "unknown";
56     }
57 }
58
59 static BOOL CALLBACK read_mem(HANDLE hProcess, DWORD addr, void* buffer,
60                               DWORD size, LPDWORD nread)
61 {
62     SIZE_T      r;
63     if (!ReadProcessMemory(hProcess, (void*)addr, buffer, size, &r)) return FALSE;
64     if (nread) *nread = r;
65     return TRUE;
66 }
67
68 static BOOL CALLBACK read_mem64(HANDLE hProcess, DWORD64 addr, void* buffer,
69                                 DWORD size, LPDWORD nread)
70 {
71     SIZE_T      r;
72     if (!ReadProcessMemory(hProcess, (void*)(DWORD_PTR)addr, buffer, size, &r)) return FALSE;
73     if (nread) *nread = r;
74     return TRUE;
75 }
76
77 /* indexes in Reserved array */
78 #define __CurrentMode     0
79 #define __CurrentSwitch   1
80 #define __NextSwitch      2
81
82 #define curr_mode   (frame->Reserved[__CurrentMode])
83 #define curr_switch (frame->Reserved[__CurrentSwitch])
84 #define next_switch (frame->Reserved[__NextSwitch])
85
86 struct stack_walk_callback
87 {
88     HANDLE      hProcess;
89     HANDLE      hThread;
90     BOOL        is32;
91     union
92     {
93         struct
94         {
95             PREAD_PROCESS_MEMORY_ROUTINE        f_read_mem;
96             PTRANSLATE_ADDRESS_ROUTINE          f_xlat_adr;
97             PFUNCTION_TABLE_ACCESS_ROUTINE      f_tabl_acs;
98             PGET_MODULE_BASE_ROUTINE            f_modl_bas;
99         } s32;
100         struct
101         {
102             PREAD_PROCESS_MEMORY_ROUTINE64      f_read_mem;
103             PTRANSLATE_ADDRESS_ROUTINE64        f_xlat_adr;
104             PFUNCTION_TABLE_ACCESS_ROUTINE64    f_tabl_acs;
105             PGET_MODULE_BASE_ROUTINE64          f_modl_bas;
106         } s64;
107     } u;
108 };
109
110 static inline void addr_32to64(const ADDRESS* addr32, ADDRESS64* addr64)
111 {
112     addr64->Offset = (ULONG64)addr32->Offset;
113     addr64->Segment = addr32->Segment;
114     addr64->Mode = addr32->Mode;
115 }
116
117 static inline void addr_64to32(const ADDRESS64* addr64, ADDRESS* addr32)
118 {
119     addr32->Offset = (ULONG)addr64->Offset;
120     addr32->Segment = addr64->Segment;
121     addr32->Mode = addr64->Mode;
122 }
123
124 static inline BOOL sw_read_mem(struct stack_walk_callback* cb, DWORD addr, void* ptr, DWORD sz)
125 {
126     if (cb->is32)
127         return cb->u.s32.f_read_mem(cb->hProcess, addr, ptr, sz, NULL);
128     else
129         return cb->u.s64.f_read_mem(cb->hProcess, addr, ptr, sz, NULL);
130 }
131
132 static inline DWORD sw_xlat_addr(struct stack_walk_callback* cb, ADDRESS* addr)
133 {
134     if (addr->Mode == AddrModeFlat) return addr->Offset;
135     if (cb->is32) return cb->u.s32.f_xlat_adr(cb->hProcess, cb->hThread, addr);
136     if (cb->u.s64.f_xlat_adr)
137     {
138         ADDRESS64       addr64;
139
140         addr_32to64(addr, &addr64);
141         return cb->u.s64.f_xlat_adr(cb->hProcess, cb->hThread, &addr64);
142     }
143     return addr_to_linear(cb->hProcess, cb->hThread, addr);
144 }
145
146 static inline void* sw_tabl_acs(struct stack_walk_callback* cb, DWORD addr)
147 {
148     if (cb->is32)
149         return cb->u.s32.f_tabl_acs(cb->hProcess, addr);
150     else
151         return cb->u.s64.f_tabl_acs(cb->hProcess, addr);
152 }
153
154 static inline DWORD sw_modl_bas(struct stack_walk_callback* cb, DWORD addr)
155 {
156     if (cb->is32)
157         return cb->u.s32.f_modl_bas(cb->hProcess, addr);
158     else
159         return cb->u.s64.f_modl_bas(cb->hProcess, addr);
160 }
161
162 static BOOL stack_walk(struct stack_walk_callback* cb, LPSTACKFRAME frame)
163 {
164     STACK32FRAME        frame32;
165     STACK16FRAME        frame16;
166     char                ch;
167     ADDRESS             tmp;
168     DWORD               p;
169     WORD                val;
170     BOOL                do_switch;
171
172     /* sanity check */
173     if (curr_mode >= stm_done) return FALSE;
174
175     TRACE("Enter: PC=%s Frame=%s Return=%s Stack=%s Mode=%s cSwitch=%08x nSwitch=%08x\n",
176           wine_dbgstr_addr(&frame->AddrPC),
177           wine_dbgstr_addr(&frame->AddrFrame),
178           wine_dbgstr_addr(&frame->AddrReturn),
179           wine_dbgstr_addr(&frame->AddrStack), 
180           curr_mode == stm_start ? "start" : (curr_mode == stm_16bit ? "16bit" : "32bit"),
181           curr_switch, next_switch);
182
183     if (curr_mode == stm_start)
184     {
185         THREAD_BASIC_INFORMATION info;
186
187         if ((frame->AddrPC.Mode == AddrModeFlat) &&
188             (frame->AddrFrame.Mode != AddrModeFlat))
189         {
190             WARN("Bad AddrPC.Mode / AddrFrame.Mode combination\n");
191             goto done_err;
192         }
193
194         /* Init done */
195         curr_mode = (frame->AddrPC.Mode == AddrModeFlat) ? 
196             stm_32bit : stm_16bit;
197
198         /* cur_switch holds address of WOW32Reserved field in TEB in debuggee
199          * address space
200          */
201         if (NtQueryInformationThread(cb->hThread, ThreadBasicInformation, &info,
202                                      sizeof(info), NULL) == STATUS_SUCCESS)
203         {
204             curr_switch = (unsigned long)info.TebBaseAddress + FIELD_OFFSET(TEB, WOW32Reserved);
205             if (!sw_read_mem(cb, curr_switch, &next_switch, sizeof(next_switch)))
206             {
207                 WARN("Can't read TEB:WOW32Reserved\n");
208                 goto done_err;
209             }
210             if (curr_mode == stm_16bit)
211             {
212                 if (!sw_read_mem(cb, next_switch, &frame32, sizeof(frame32)))
213                 {
214                     WARN("Bad stack frame 0x%08x\n", next_switch);
215                     goto done_err;
216                 }
217                 curr_switch = (DWORD)frame32.frame16;
218                 tmp.Mode    = AddrMode1616;
219                 tmp.Segment = SELECTOROF(curr_switch);
220                 tmp.Offset  = OFFSETOF(curr_switch);
221                 if (!sw_read_mem(cb, sw_xlat_addr(cb, &tmp), &ch, sizeof(ch)))
222                     curr_switch = 0xFFFFFFFF;
223             }
224             else
225             {
226                 tmp.Mode    = AddrMode1616;
227                 tmp.Segment = SELECTOROF(next_switch);
228                 tmp.Offset  = OFFSETOF(next_switch);
229                 p = sw_xlat_addr(cb, &tmp);
230                 if (!sw_read_mem(cb, p, &frame16, sizeof(frame16)))
231                 {
232                     WARN("Bad stack frame 0x%08x\n", p);
233                     goto done_err;
234                 }
235                 curr_switch = (DWORD)frame16.frame32;
236
237                 if (!sw_read_mem(cb, curr_switch, &ch, sizeof(ch)))
238                     curr_switch = 0xFFFFFFFF;
239             }
240         }
241         else
242             /* FIXME: this will allow to work when we're not attached to a live target, 
243              * but the 16 <=> 32 switch facility won't be available.
244              */
245             curr_switch = 0;
246         frame->AddrReturn.Mode = frame->AddrStack.Mode = (curr_mode == stm_16bit) ? AddrMode1616 : AddrModeFlat;
247         /* don't set up AddrStack on first call. Either the caller has set it up, or
248          * we will get it in the next frame
249          */
250         memset(&frame->AddrBStore, 0, sizeof(frame->AddrBStore));
251     }
252     else
253     {
254         if (frame->AddrFrame.Offset == 0) goto done_err;
255         if (frame->AddrFrame.Mode == AddrModeFlat)
256         {
257             assert(curr_mode == stm_32bit);
258             do_switch = curr_switch && frame->AddrFrame.Offset >= curr_switch;
259         }
260         else
261         {
262             assert(curr_mode == stm_16bit);
263             do_switch = curr_switch && 
264                 frame->AddrFrame.Segment == SELECTOROF(curr_switch) &&
265                 frame->AddrFrame.Offset >= OFFSETOF(curr_switch);
266         }
267            
268         if (do_switch)
269         {
270             if (curr_mode == stm_16bit)
271             {
272                 if (!sw_read_mem(cb, next_switch, &frame32, sizeof(frame32)))
273                 {
274                     WARN("Bad stack frame 0x%08x\n", next_switch);
275                     goto done_err;
276                 }
277
278                 frame->AddrPC.Mode        = AddrModeFlat;
279                 frame->AddrPC.Segment     = 0;
280                 frame->AddrPC.Offset      = frame32.retaddr;
281                 frame->AddrFrame.Mode     = AddrModeFlat;
282                 frame->AddrFrame.Segment  = 0;
283                 frame->AddrFrame.Offset   = frame32.ebp;
284
285                 frame->AddrStack.Mode     = AddrModeFlat;
286                 frame->AddrStack.Segment  = 0;
287                 frame->AddrReturn.Mode    = AddrModeFlat;
288                 frame->AddrReturn.Segment = 0;
289
290                 next_switch = curr_switch;
291                 tmp.Mode    = AddrMode1616;
292                 tmp.Segment = SELECTOROF(next_switch);
293                 tmp.Offset  = OFFSETOF(next_switch);
294                 p = sw_xlat_addr(cb, &tmp);
295
296                 if (!sw_read_mem(cb, p, &frame16, sizeof(frame16)))
297                 {
298                     WARN("Bad stack frame 0x%08x\n", p);
299                     goto done_err;
300                 }
301                 curr_switch = (DWORD)frame16.frame32;
302                 curr_mode = stm_32bit;
303                 if (!sw_read_mem(cb, curr_switch, &ch, sizeof(ch)))
304                     curr_switch = 0;
305             }
306             else
307             {
308                 tmp.Mode    = AddrMode1616;
309                 tmp.Segment = SELECTOROF(next_switch);
310                 tmp.Offset  = OFFSETOF(next_switch);
311                 p = sw_xlat_addr(cb, &tmp);
312
313                 if (!sw_read_mem(cb, p, &frame16, sizeof(frame16)))
314                 {
315                     WARN("Bad stack frame 0x%08x\n", p);
316                     goto done_err;
317                 }
318
319                 TRACE("Got a 16 bit stack switch:"
320                       "\n\tframe32: %08lx"
321                       "\n\tedx:%08x ecx:%08x ebp:%08x"
322                       "\n\tds:%04x es:%04x fs:%04x gs:%04x"
323                       "\n\tcall_from_ip:%08x module_cs:%04x relay=%08x"
324                       "\n\tentry_ip:%04x entry_point:%08x"
325                       "\n\tbp:%04x ip:%04x cs:%04x\n",
326                       (unsigned long)frame16.frame32,
327                       frame16.edx, frame16.ecx, frame16.ebp,
328                       frame16.ds, frame16.es, frame16.fs, frame16.gs,
329                       frame16.callfrom_ip, frame16.module_cs, frame16.relay,
330                       frame16.entry_ip, frame16.entry_point,
331                       frame16.bp, frame16.ip, frame16.cs);
332
333                       
334                 frame->AddrPC.Mode       = AddrMode1616;
335                 frame->AddrPC.Segment    = frame16.cs;
336                 frame->AddrPC.Offset     = frame16.ip;
337
338                 frame->AddrFrame.Mode    = AddrMode1616;
339                 frame->AddrFrame.Segment = SELECTOROF(next_switch);
340                 frame->AddrFrame.Offset  = frame16.bp;
341
342                 frame->AddrStack.Mode    = AddrMode1616;
343                 frame->AddrStack.Segment = SELECTOROF(next_switch);
344
345                 frame->AddrReturn.Mode    = AddrMode1616;
346                 frame->AddrReturn.Segment = frame16.cs;
347
348                 next_switch = curr_switch;
349                 if (!sw_read_mem(cb, next_switch, &frame32, sizeof(frame32)))
350                 {
351                     WARN("Bad stack frame 0x%08x\n", next_switch);
352                     goto done_err;
353                 }
354                 curr_switch = (DWORD)frame32.frame16;
355                 tmp.Mode    = AddrMode1616;
356                 tmp.Segment = SELECTOROF(curr_switch);
357                 tmp.Offset  = OFFSETOF(curr_switch);
358
359                 if (!sw_read_mem(cb, sw_xlat_addr(cb, &tmp), &ch, sizeof(ch)))
360                     curr_switch = 0;
361                 curr_mode = stm_16bit;
362             }
363         }
364         else
365         {
366             frame->AddrPC = frame->AddrReturn;
367             if (curr_mode == stm_16bit)
368             {
369                 frame->AddrStack.Offset = frame->AddrFrame.Offset + 2 * sizeof(WORD);
370                 /* "pop up" previous BP value */
371                 if (!sw_read_mem(cb, sw_xlat_addr(cb, &frame->AddrFrame),
372                                  &val, sizeof(WORD)))
373                     goto done_err;
374                 frame->AddrFrame.Offset = val;
375             }
376             else
377             {
378                 frame->AddrStack.Offset = frame->AddrFrame.Offset + 2 * sizeof(DWORD);
379                 /* "pop up" previous EBP value */
380                 if (!sw_read_mem(cb, frame->AddrFrame.Offset, 
381                                  &frame->AddrFrame.Offset, sizeof(DWORD)))
382                     goto done_err;
383             }
384         }
385     }
386
387     if (curr_mode == stm_16bit)
388     {
389         int     i;
390
391         p = sw_xlat_addr(cb, &frame->AddrFrame);
392         if (!sw_read_mem(cb, p + sizeof(WORD), &val, sizeof(WORD)))
393             goto done_err;
394         frame->AddrReturn.Offset = val;
395         /* get potential cs if a far call was used */
396         if (!sw_read_mem(cb, p + 2 * sizeof(WORD), &val, sizeof(WORD)))
397             goto done_err;
398         if (frame->AddrFrame.Offset & 1)
399             frame->AddrReturn.Segment = val; /* far call assumed */
400         else
401         {
402             /* not explicitly marked as far call, 
403              * but check whether it could be anyway
404              */
405             if ((val & 7) == 7 && val != frame->AddrReturn.Segment)
406             {
407                 LDT_ENTRY       le;
408
409                 if (GetThreadSelectorEntry(cb->hThread, val, &le) &&
410                     (le.HighWord.Bits.Type & 0x08)) /* code segment */
411                 {
412                     /* it is very uncommon to push a code segment cs as
413                      * a parameter, so this should work in most cases 
414                      */
415                     frame->AddrReturn.Segment = val;
416                 }
417             }
418         }
419         frame->AddrFrame.Offset &= ~1;
420         /* we "pop" parameters as 16 bit entities... of course, this won't
421          * work if the parameter is in fact bigger than 16bit, but
422          * there's no way to know that here
423          */
424         for (i = 0; i < sizeof(frame->Params) / sizeof(frame->Params[0]); i++)
425         {
426             sw_read_mem(cb, p + (2 + i) * sizeof(WORD), &val, sizeof(val));
427             frame->Params[i] = val;
428         }
429     }
430     else
431     {
432         if (!sw_read_mem(cb, frame->AddrFrame.Offset + sizeof(DWORD),
433                          &frame->AddrReturn.Offset, sizeof(DWORD)))
434         {
435             WARN("Cannot read new frame offset %08x\n", frame->AddrFrame.Offset + (int)sizeof(DWORD));
436             goto done_err;
437         }
438         sw_read_mem(cb, frame->AddrFrame.Offset + 2 * sizeof(DWORD),
439                     frame->Params, sizeof(frame->Params));
440     }
441
442     frame->Far = TRUE;
443     frame->Virtual = TRUE;
444     p = sw_xlat_addr(cb, &frame->AddrPC);
445     if (p && sw_modl_bas(cb, p))
446         frame->FuncTableEntry = sw_tabl_acs(cb, p);
447     else
448         frame->FuncTableEntry = NULL;
449
450     TRACE("Leave: PC=%s Frame=%s Return=%s Stack=%s Mode=%s cSwitch=%08x nSwitch=%08x FuncTable=%p\n",
451           wine_dbgstr_addr(&frame->AddrPC),
452           wine_dbgstr_addr(&frame->AddrFrame),
453           wine_dbgstr_addr(&frame->AddrReturn),
454           wine_dbgstr_addr(&frame->AddrStack), 
455           curr_mode == stm_start ? "start" : (curr_mode == stm_16bit ? "16bit" : "32bit"),
456           curr_switch, next_switch, frame->FuncTableEntry);
457
458     return TRUE;
459 done_err:
460     curr_mode = stm_done;
461     return FALSE;
462 }
463
464 /***********************************************************************
465  *              StackWalk (DBGHELP.@)
466  */
467 BOOL WINAPI StackWalk(DWORD MachineType, HANDLE hProcess, HANDLE hThread,
468                       LPSTACKFRAME frame, PVOID ctx,
469                       PREAD_PROCESS_MEMORY_ROUTINE f_read_mem,
470                       PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine,
471                       PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine,
472                       PTRANSLATE_ADDRESS_ROUTINE f_xlat_adr)
473 {
474     struct stack_walk_callback  swcb;
475
476     TRACE("(%d, %p, %p, %p, %p, %p, %p, %p, %p)\n",
477           MachineType, hProcess, hThread, frame, ctx,
478           f_read_mem, FunctionTableAccessRoutine,
479           GetModuleBaseRoutine, f_xlat_adr);
480
481     if (MachineType != IMAGE_FILE_MACHINE_I386)
482     {
483         SetLastError(ERROR_INVALID_PARAMETER);
484         return FALSE;
485     }
486
487     swcb.hProcess = hProcess;
488     swcb.hThread = hThread;
489     swcb.is32 = TRUE;
490     /* sigh... MS isn't even consistent in the func prototypes */
491     swcb.u.s32.f_read_mem = (f_read_mem) ? f_read_mem : read_mem;
492     swcb.u.s32.f_xlat_adr = (f_xlat_adr) ? f_xlat_adr : addr_to_linear;
493     swcb.u.s32.f_tabl_acs = (FunctionTableAccessRoutine) ? FunctionTableAccessRoutine : SymFunctionTableAccess;
494     swcb.u.s32.f_modl_bas = (GetModuleBaseRoutine) ? GetModuleBaseRoutine : SymGetModuleBase;
495
496     return stack_walk(&swcb, frame);
497 }
498
499
500 /***********************************************************************
501  *              StackWalk64 (DBGHELP.@)
502  */
503 BOOL WINAPI StackWalk64(DWORD MachineType, HANDLE hProcess, HANDLE hThread,
504                         LPSTACKFRAME64 frame64, PVOID ctx,
505                         PREAD_PROCESS_MEMORY_ROUTINE64 f_read_mem,
506                         PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
507                         PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
508                         PTRANSLATE_ADDRESS_ROUTINE64 f_xlat_adr)
509 {
510     struct stack_walk_callback  swcb;
511     STACKFRAME                  frame32;
512     BOOL                        ret;
513
514     TRACE("(%d, %p, %p, %p, %p, %p, %p, %p, %p)\n",
515           MachineType, hProcess, hThread, frame64, ctx,
516           f_read_mem, FunctionTableAccessRoutine,
517           GetModuleBaseRoutine, f_xlat_adr);
518
519     if (MachineType != IMAGE_FILE_MACHINE_I386)
520     {
521         SetLastError(ERROR_INVALID_PARAMETER);
522         return FALSE;
523     }
524
525     addr_64to32(&frame64->AddrPC,     &frame32.AddrPC);
526     addr_64to32(&frame64->AddrReturn, &frame32.AddrReturn);
527     addr_64to32(&frame64->AddrFrame,  &frame32.AddrFrame);
528     addr_64to32(&frame64->AddrStack,  &frame32.AddrStack);
529     addr_64to32(&frame64->AddrBStore, &frame32.AddrBStore);
530     frame32.FuncTableEntry = frame64->FuncTableEntry; /* FIXME */
531     frame32.Far = frame64->Far;
532     frame32.Virtual = frame64->Virtual;
533     frame32.Reserved[0] = (ULONG)frame64->Reserved[0];
534     frame32.Reserved[1] = (ULONG)frame64->Reserved[1];
535     frame32.Reserved[2] = (ULONG)frame64->Reserved[2];
536     /* we don't handle KdHelp */
537
538     swcb.hProcess = hProcess;
539     swcb.hThread = hThread;
540     swcb.is32 = FALSE;
541     /* sigh... MS isn't even consistent in the func prototypes */
542     swcb.u.s64.f_read_mem = (f_read_mem) ? f_read_mem : read_mem64;
543     swcb.u.s64.f_xlat_adr = f_xlat_adr;
544     swcb.u.s64.f_tabl_acs = (FunctionTableAccessRoutine) ? FunctionTableAccessRoutine : SymFunctionTableAccess64;
545     swcb.u.s64.f_modl_bas = (GetModuleBaseRoutine) ? GetModuleBaseRoutine : SymGetModuleBase64;
546
547     ret = stack_walk(&swcb, &frame32);
548
549     addr_32to64(&frame32.AddrPC,     &frame64->AddrPC);
550     addr_32to64(&frame32.AddrReturn, &frame64->AddrReturn);
551     addr_32to64(&frame32.AddrFrame,  &frame64->AddrFrame);
552     addr_32to64(&frame32.AddrStack,  &frame64->AddrStack);
553     addr_32to64(&frame32.AddrBStore, &frame64->AddrBStore);
554     frame64->FuncTableEntry = frame32.FuncTableEntry; /* FIXME */
555     frame64->Params[0] = frame32.Params[0];
556     frame64->Params[1] = frame32.Params[1];
557     frame64->Params[2] = frame32.Params[2];
558     frame64->Params[3] = frame32.Params[3];
559     frame64->Far = frame32.Far;
560     frame64->Virtual = frame32.Virtual;
561     frame64->Reserved[0] = frame32.Reserved[0];
562     frame64->Reserved[1] = frame32.Reserved[1];
563     frame64->Reserved[2] = frame32.Reserved[2];
564     /* we don't handle KdHelp */
565     frame64->KdHelp.Thread = 0xC000FADE;
566     frame64->KdHelp.ThCallbackStack = 0x10;
567     frame64->KdHelp.ThCallbackBStore = 0;
568     frame64->KdHelp.NextCallback = 0;
569     frame64->KdHelp.FramePointer = 0;
570     frame64->KdHelp.KiCallUserMode = 0xD000DAFE;
571     frame64->KdHelp.KeUserCallbackDispatcher = 0xE000F000;
572     frame64->KdHelp.SystemRangeStart = 0xC0000000;
573     frame64->KdHelp.Reserved[0] /* KiUserExceptionDispatcher */ = 0xE0005000;
574
575     return ret;
576 }
577
578 /******************************************************************
579  *              SymRegisterFunctionEntryCallback (DBGHELP.@)
580  *
581  *
582  */
583 BOOL WINAPI SymRegisterFunctionEntryCallback(HANDLE hProc,
584                                              PSYMBOL_FUNCENTRY_CALLBACK cb, PVOID user)
585 {
586     FIXME("(%p %p %p): stub!\n", hProc, cb, user);
587     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
588     return FALSE;
589 }
590
591 /******************************************************************
592  *              SymRegisterFunctionEntryCallback64 (DBGHELP.@)
593  *
594  *
595  */
596 BOOL WINAPI SymRegisterFunctionEntryCallback64(HANDLE hProc,
597                                                PSYMBOL_FUNCENTRY_CALLBACK64 cb,
598                                                ULONG64 user)
599 {
600     FIXME("(%p %p %s): stub!\n", hProc, cb, wine_dbgstr_longlong(user));
601     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
602     return FALSE;
603 }