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