4 * Copyright 1995 Alexandre Julliard
5 * Copyright 1996 Eric Youngdale
6 * Copyright 1999 Ove Kåven
7 * Copyright 2004 Eric Pouech
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.
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.
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
31 #define WIN32_NO_STATUS
32 #include "dbghelp_private.h"
34 #include "wine/winbase16.h"
35 #include "wine/debug.h"
37 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp
);
39 enum st_mode
{stm_start
, stm_32bit
, stm_16bit
, stm_done
};
41 static const char* wine_dbgstr_addr(const ADDRESS
* addr
)
43 if (!addr
) return "(null)";
47 return wine_dbg_sprintf("flat<%08x>", addr
->Offset
);
49 return wine_dbg_sprintf("1616<%04x:%04x>", addr
->Segment
, addr
->Offset
);
51 return wine_dbg_sprintf("1632<%04x:%08x>", addr
->Segment
, addr
->Offset
);
53 return wine_dbg_sprintf("real<%04x:%04x>", addr
->Segment
, addr
->Offset
);
59 static BOOL CALLBACK
read_mem(HANDLE hProcess
, DWORD addr
, void* buffer
,
60 DWORD size
, LPDWORD nread
)
63 if (!ReadProcessMemory(hProcess
, (void*)addr
, buffer
, size
, &r
)) return FALSE
;
64 if (nread
) *nread
= r
;
68 static BOOL CALLBACK
read_mem64(HANDLE hProcess
, DWORD64 addr
, void* buffer
,
69 DWORD size
, LPDWORD nread
)
72 if (!ReadProcessMemory(hProcess
, (void*)(DWORD_PTR
)addr
, buffer
, size
, &r
)) return FALSE
;
73 if (nread
) *nread
= r
;
77 /* indexes in Reserved array */
78 #define __CurrentMode 0
79 #define __CurrentSwitch 1
80 #define __NextSwitch 2
82 #define curr_mode (frame->Reserved[__CurrentMode])
83 #define curr_switch (frame->Reserved[__CurrentSwitch])
84 #define next_switch (frame->Reserved[__NextSwitch])
86 struct stack_walk_callback
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
;
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
;
110 static inline void addr_32to64(const ADDRESS
* addr32
, ADDRESS64
* addr64
)
112 addr64
->Offset
= (ULONG64
)addr32
->Offset
;
113 addr64
->Segment
= addr32
->Segment
;
114 addr64
->Mode
= addr32
->Mode
;
117 static inline void addr_64to32(const ADDRESS64
* addr64
, ADDRESS
* addr32
)
119 addr32
->Offset
= (ULONG
)addr64
->Offset
;
120 addr32
->Segment
= addr64
->Segment
;
121 addr32
->Mode
= addr64
->Mode
;
124 static inline BOOL
sw_read_mem(struct stack_walk_callback
* cb
, DWORD addr
, void* ptr
, DWORD sz
)
127 return cb
->u
.s32
.f_read_mem(cb
->hProcess
, addr
, ptr
, sz
, NULL
);
129 return cb
->u
.s64
.f_read_mem(cb
->hProcess
, addr
, ptr
, sz
, NULL
);
132 static inline DWORD
sw_xlat_addr(struct stack_walk_callback
* cb
, ADDRESS
* addr
)
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
)
140 addr_32to64(addr
, &addr64
);
141 return cb
->u
.s64
.f_xlat_adr(cb
->hProcess
, cb
->hThread
, &addr64
);
143 return addr_to_linear(cb
->hProcess
, cb
->hThread
, addr
);
146 static inline void* sw_tabl_acs(struct stack_walk_callback
* cb
, DWORD addr
)
149 return cb
->u
.s32
.f_tabl_acs(cb
->hProcess
, addr
);
151 return cb
->u
.s64
.f_tabl_acs(cb
->hProcess
, addr
);
154 static inline DWORD
sw_modl_bas(struct stack_walk_callback
* cb
, DWORD addr
)
157 return cb
->u
.s32
.f_modl_bas(cb
->hProcess
, addr
);
159 return cb
->u
.s64
.f_modl_bas(cb
->hProcess
, addr
);
162 static BOOL
stack_walk(struct stack_walk_callback
* cb
, LPSTACKFRAME frame
)
164 STACK32FRAME frame32
;
165 STACK16FRAME frame16
;
173 if (curr_mode
>= stm_done
) return FALSE
;
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
);
183 if (curr_mode
== stm_start
)
185 THREAD_BASIC_INFORMATION info
;
187 if ((frame
->AddrPC
.Mode
== AddrModeFlat
) &&
188 (frame
->AddrFrame
.Mode
!= AddrModeFlat
))
190 WARN("Bad AddrPC.Mode / AddrFrame.Mode combination\n");
195 curr_mode
= (frame
->AddrPC
.Mode
== AddrModeFlat
) ?
196 stm_32bit
: stm_16bit
;
198 /* cur_switch holds address of WOW32Reserved field in TEB in debuggee
201 if (NtQueryInformationThread(cb
->hThread
, ThreadBasicInformation
, &info
,
202 sizeof(info
), NULL
) == STATUS_SUCCESS
)
204 curr_switch
= (unsigned long)info
.TebBaseAddress
+ FIELD_OFFSET(TEB
, WOW32Reserved
);
205 if (!sw_read_mem(cb
, curr_switch
, &next_switch
, sizeof(next_switch
)))
207 WARN("Can't read TEB:WOW32Reserved\n");
210 if (curr_mode
== stm_16bit
)
212 if (!sw_read_mem(cb
, next_switch
, &frame32
, sizeof(frame32
)))
214 WARN("Bad stack frame 0x%08x\n", next_switch
);
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;
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
)))
232 WARN("Bad stack frame 0x%08x\n", p
);
235 curr_switch
= (DWORD
)frame16
.frame32
;
237 if (!sw_read_mem(cb
, curr_switch
, &ch
, sizeof(ch
)))
238 curr_switch
= 0xFFFFFFFF;
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.
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
250 memset(&frame
->AddrBStore
, 0, sizeof(frame
->AddrBStore
));
254 if (frame
->AddrFrame
.Offset
== 0) goto done_err
;
255 if (frame
->AddrFrame
.Mode
== AddrModeFlat
)
257 assert(curr_mode
== stm_32bit
);
258 do_switch
= curr_switch
&& frame
->AddrFrame
.Offset
>= curr_switch
;
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
);
270 if (curr_mode
== stm_16bit
)
272 if (!sw_read_mem(cb
, next_switch
, &frame32
, sizeof(frame32
)))
274 WARN("Bad stack frame 0x%08x\n", next_switch
);
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
;
285 frame
->AddrStack
.Mode
= AddrModeFlat
;
286 frame
->AddrStack
.Segment
= 0;
287 frame
->AddrReturn
.Mode
= AddrModeFlat
;
288 frame
->AddrReturn
.Segment
= 0;
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
);
296 if (!sw_read_mem(cb
, p
, &frame16
, sizeof(frame16
)))
298 WARN("Bad stack frame 0x%08x\n", p
);
301 curr_switch
= (DWORD
)frame16
.frame32
;
302 curr_mode
= stm_32bit
;
303 if (!sw_read_mem(cb
, curr_switch
, &ch
, sizeof(ch
)))
308 tmp
.Mode
= AddrMode1616
;
309 tmp
.Segment
= SELECTOROF(next_switch
);
310 tmp
.Offset
= OFFSETOF(next_switch
);
311 p
= sw_xlat_addr(cb
, &tmp
);
313 if (!sw_read_mem(cb
, p
, &frame16
, sizeof(frame16
)))
315 WARN("Bad stack frame 0x%08x\n", p
);
319 TRACE("Got a 16 bit stack switch:"
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
);
334 frame
->AddrPC
.Mode
= AddrMode1616
;
335 frame
->AddrPC
.Segment
= frame16
.cs
;
336 frame
->AddrPC
.Offset
= frame16
.ip
;
338 frame
->AddrFrame
.Mode
= AddrMode1616
;
339 frame
->AddrFrame
.Segment
= SELECTOROF(next_switch
);
340 frame
->AddrFrame
.Offset
= frame16
.bp
;
342 frame
->AddrStack
.Mode
= AddrMode1616
;
343 frame
->AddrStack
.Segment
= SELECTOROF(next_switch
);
345 frame
->AddrReturn
.Mode
= AddrMode1616
;
346 frame
->AddrReturn
.Segment
= frame16
.cs
;
348 next_switch
= curr_switch
;
349 if (!sw_read_mem(cb
, next_switch
, &frame32
, sizeof(frame32
)))
351 WARN("Bad stack frame 0x%08x\n", next_switch
);
354 curr_switch
= (DWORD
)frame32
.frame16
;
355 tmp
.Mode
= AddrMode1616
;
356 tmp
.Segment
= SELECTOROF(curr_switch
);
357 tmp
.Offset
= OFFSETOF(curr_switch
);
359 if (!sw_read_mem(cb
, sw_xlat_addr(cb
, &tmp
), &ch
, sizeof(ch
)))
361 curr_mode
= stm_16bit
;
366 frame
->AddrPC
= frame
->AddrReturn
;
367 if (curr_mode
== stm_16bit
)
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
),
374 frame
->AddrFrame
.Offset
= val
;
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
)))
387 if (curr_mode
== stm_16bit
)
391 p
= sw_xlat_addr(cb
, &frame
->AddrFrame
);
392 if (!sw_read_mem(cb
, p
+ sizeof(WORD
), &val
, sizeof(WORD
)))
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
)))
398 if (frame
->AddrFrame
.Offset
& 1)
399 frame
->AddrReturn
.Segment
= val
; /* far call assumed */
402 /* not explicitly marked as far call,
403 * but check whether it could be anyway
405 if ((val
& 7) == 7 && val
!= frame
->AddrReturn
.Segment
)
409 if (GetThreadSelectorEntry(cb
->hThread
, val
, &le
) &&
410 (le
.HighWord
.Bits
.Type
& 0x08)) /* code segment */
412 /* it is very uncommon to push a code segment cs as
413 * a parameter, so this should work in most cases
415 frame
->AddrReturn
.Segment
= val
;
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
424 for (i
= 0; i
< sizeof(frame
->Params
) / sizeof(frame
->Params
[0]); i
++)
426 sw_read_mem(cb
, p
+ (2 + i
) * sizeof(WORD
), &val
, sizeof(val
));
427 frame
->Params
[i
] = val
;
432 if (!sw_read_mem(cb
, frame
->AddrFrame
.Offset
+ sizeof(DWORD
),
433 &frame
->AddrReturn
.Offset
, sizeof(DWORD
)))
435 WARN("Cannot read new frame offset %08x\n", frame
->AddrFrame
.Offset
+ (int)sizeof(DWORD
));
438 sw_read_mem(cb
, frame
->AddrFrame
.Offset
+ 2 * sizeof(DWORD
),
439 frame
->Params
, sizeof(frame
->Params
));
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
);
448 frame
->FuncTableEntry
= NULL
;
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
);
460 curr_mode
= stm_done
;
464 /***********************************************************************
465 * StackWalk (DBGHELP.@)
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
)
474 struct stack_walk_callback swcb
;
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
);
481 if (MachineType
!= IMAGE_FILE_MACHINE_I386
)
483 SetLastError(ERROR_INVALID_PARAMETER
);
487 swcb
.hProcess
= hProcess
;
488 swcb
.hThread
= hThread
;
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
;
496 return stack_walk(&swcb
, frame
);
500 /***********************************************************************
501 * StackWalk64 (DBGHELP.@)
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
)
510 struct stack_walk_callback swcb
;
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
);
519 if (MachineType
!= IMAGE_FILE_MACHINE_I386
)
521 SetLastError(ERROR_INVALID_PARAMETER
);
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 */
538 swcb
.hProcess
= hProcess
;
539 swcb
.hThread
= hThread
;
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
;
547 ret
= stack_walk(&swcb
, &frame32
);
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;
578 /******************************************************************
579 * SymRegisterFunctionEntryCallback (DBGHELP.@)
583 BOOL WINAPI
SymRegisterFunctionEntryCallback(HANDLE hProc
,
584 PSYMBOL_FUNCENTRY_CALLBACK cb
, PVOID user
)
586 FIXME("(%p %p %p): stub!\n", hProc
, cb
, user
);
587 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
591 /******************************************************************
592 * SymRegisterFunctionEntryCallback64 (DBGHELP.@)
596 BOOL WINAPI
SymRegisterFunctionEntryCallback64(HANDLE hProc
,
597 PSYMBOL_FUNCENTRY_CALLBACK64 cb
,
600 FIXME("(%p %p %s): stub!\n", hProc
, cb
, wine_dbgstr_longlong(user
));
601 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);