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