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"
35 #include "wine/winbase16.h"
36 #include "wine/debug.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(dbghelp
);
40 enum st_mode
{stm_start
, stm_32bit
, stm_16bit
, stm_done
};
42 static const char* wine_dbgstr_addr(const ADDRESS
* addr
)
44 if (!addr
) return "(null)";
48 return wine_dbg_sprintf("flat<%08lx>", addr
->Offset
);
50 return wine_dbg_sprintf("1616<%04x:%04lx>", addr
->Segment
, addr
->Offset
);
52 return wine_dbg_sprintf("1632<%04x:%08lx>", addr
->Segment
, addr
->Offset
);
54 return wine_dbg_sprintf("real<%04x:%04lx>", addr
->Segment
, addr
->Offset
);
60 static BOOL CALLBACK
read_mem(HANDLE hProcess
, DWORD addr
, void* buffer
,
61 DWORD size
, LPDWORD nread
)
63 return ReadProcessMemory(hProcess
, (void*)addr
, buffer
, size
, nread
);
66 static BOOL CALLBACK
read_mem64(HANDLE hProcess
, DWORD64 addr
, void* buffer
,
67 DWORD size
, LPDWORD nread
)
69 return ReadProcessMemory(hProcess
, (void*)(DWORD_PTR
)addr
, buffer
, size
, nread
);
72 /* indexes in Reserved array */
73 #define __CurrentMode 0
74 #define __CurrentSwitch 1
75 #define __NextSwitch 2
77 #define curr_mode (frame->Reserved[__CurrentMode])
78 #define curr_switch (frame->Reserved[__CurrentSwitch])
79 #define next_switch (frame->Reserved[__NextSwitch])
81 struct stack_walk_callback
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
;
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
;
105 static inline void addr_32to64(const ADDRESS
* addr32
, ADDRESS64
* addr64
)
107 addr64
->Offset
= (ULONG64
)addr32
->Offset
;
108 addr64
->Segment
= addr32
->Segment
;
109 addr64
->Mode
= addr32
->Mode
;
112 static inline void addr_64to32(const ADDRESS64
* addr64
, ADDRESS
* addr32
)
114 addr32
->Offset
= (ULONG
)addr64
->Offset
;
115 addr32
->Segment
= addr64
->Segment
;
116 addr32
->Mode
= addr64
->Mode
;
119 static inline BOOL
sw_read_mem(struct stack_walk_callback
* cb
, DWORD addr
, void* ptr
, DWORD sz
)
122 return cb
->u
.s32
.f_read_mem(cb
->hProcess
, addr
, ptr
, sz
, NULL
);
124 return cb
->u
.s64
.f_read_mem(cb
->hProcess
, addr
, ptr
, sz
, NULL
);
127 static inline DWORD
sw_xlat_addr(struct stack_walk_callback
* cb
, ADDRESS
* addr
)
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
)
135 addr_32to64(addr
, &addr64
);
136 return cb
->u
.s64
.f_xlat_adr(cb
->hProcess
, cb
->hThread
, &addr64
);
138 return addr_to_linear(cb
->hProcess
, cb
->hThread
, addr
);
141 static inline void* sw_tabl_acs(struct stack_walk_callback
* cb
, DWORD addr
)
144 return cb
->u
.s32
.f_tabl_acs(cb
->hProcess
, addr
);
146 return cb
->u
.s64
.f_tabl_acs(cb
->hProcess
, addr
);
149 static inline DWORD
sw_modl_bas(struct stack_walk_callback
* cb
, DWORD addr
)
152 return cb
->u
.s32
.f_modl_bas(cb
->hProcess
, addr
);
154 return cb
->u
.s64
.f_modl_bas(cb
->hProcess
, addr
);
157 static BOOL
stack_walk(struct stack_walk_callback
* cb
, LPSTACKFRAME frame
)
159 STACK32FRAME frame32
;
160 STACK16FRAME frame16
;
168 if (curr_mode
>= stm_done
) return FALSE
;
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
);
178 if (curr_mode
== stm_start
)
180 THREAD_BASIC_INFORMATION info
;
182 if ((frame
->AddrPC
.Mode
== AddrModeFlat
) &&
183 (frame
->AddrFrame
.Mode
!= AddrModeFlat
))
185 WARN("Bad AddrPC.Mode / AddrFrame.Mode combination\n");
190 curr_mode
= (frame
->AddrPC
.Mode
== AddrModeFlat
) ?
191 stm_32bit
: stm_16bit
;
193 /* cur_switch holds address of WOW32Reserved field in TEB in debuggee
196 if (NtQueryInformationThread(cb
->hThread
, ThreadBasicInformation
, &info
,
197 sizeof(info
), NULL
) == STATUS_SUCCESS
)
199 curr_switch
= (unsigned long)info
.TebBaseAddress
+ FIELD_OFFSET(TEB
, WOW32Reserved
);
200 if (!sw_read_mem(cb
, curr_switch
, &next_switch
, sizeof(next_switch
)))
202 WARN("Can't read TEB:WOW32Reserved\n");
205 if (curr_mode
== stm_16bit
)
207 if (!sw_read_mem(cb
, next_switch
, &frame32
, sizeof(frame32
)))
209 WARN("Bad stack frame 0x%08lx\n", next_switch
);
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;
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
)))
227 WARN("Bad stack frame 0x%08lx\n", p
);
230 curr_switch
= (DWORD
)frame16
.frame32
;
232 if (!sw_read_mem(cb
, curr_switch
, &ch
, sizeof(ch
)))
233 curr_switch
= 0xFFFFFFFF;
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.
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
245 memset(&frame
->AddrBStore
, 0, sizeof(frame
->AddrBStore
));
249 if (frame
->AddrFrame
.Offset
== 0) goto done_err
;
250 if (frame
->AddrFrame
.Mode
== AddrModeFlat
)
252 assert(curr_mode
== stm_32bit
);
253 do_switch
= curr_switch
&& frame
->AddrFrame
.Offset
>= curr_switch
;
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
);
265 if (curr_mode
== stm_16bit
)
267 if (!sw_read_mem(cb
, next_switch
, &frame32
, sizeof(frame32
)))
269 WARN("Bad stack frame 0x%08lx\n", next_switch
);
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
;
280 frame
->AddrStack
.Mode
= AddrModeFlat
;
281 frame
->AddrStack
.Segment
= 0;
282 frame
->AddrReturn
.Mode
= AddrModeFlat
;
283 frame
->AddrReturn
.Segment
= 0;
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
);
291 if (!sw_read_mem(cb
, p
, &frame16
, sizeof(frame16
)))
293 WARN("Bad stack frame 0x%08lx\n", p
);
296 curr_switch
= (DWORD
)frame16
.frame32
;
297 curr_mode
= stm_32bit
;
298 if (!sw_read_mem(cb
, curr_switch
, &ch
, sizeof(ch
)))
303 tmp
.Mode
= AddrMode1616
;
304 tmp
.Segment
= SELECTOROF(next_switch
);
305 tmp
.Offset
= OFFSETOF(next_switch
);
306 p
= sw_xlat_addr(cb
, &tmp
);
308 if (!sw_read_mem(cb
, p
, &frame16
, sizeof(frame16
)))
310 WARN("Bad stack frame 0x%08lx\n", p
);
314 TRACE("Got a 16 bit stack switch:"
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
);
329 frame
->AddrPC
.Mode
= AddrMode1616
;
330 frame
->AddrPC
.Segment
= frame16
.cs
;
331 frame
->AddrPC
.Offset
= frame16
.ip
;
333 frame
->AddrFrame
.Mode
= AddrMode1616
;
334 frame
->AddrFrame
.Segment
= SELECTOROF(next_switch
);
335 frame
->AddrFrame
.Offset
= frame16
.bp
;
337 frame
->AddrStack
.Mode
= AddrMode1616
;
338 frame
->AddrStack
.Segment
= SELECTOROF(next_switch
);
340 frame
->AddrReturn
.Mode
= AddrMode1616
;
341 frame
->AddrReturn
.Segment
= frame16
.cs
;
343 next_switch
= curr_switch
;
344 if (!sw_read_mem(cb
, next_switch
, &frame32
, sizeof(frame32
)))
346 WARN("Bad stack frame 0x%08lx\n", next_switch
);
349 curr_switch
= (DWORD
)frame32
.frame16
;
350 tmp
.Mode
= AddrMode1616
;
351 tmp
.Segment
= SELECTOROF(curr_switch
);
352 tmp
.Offset
= OFFSETOF(curr_switch
);
354 if (!sw_read_mem(cb
, sw_xlat_addr(cb
, &tmp
), &ch
, sizeof(ch
)))
356 curr_mode
= stm_16bit
;
361 frame
->AddrPC
= frame
->AddrReturn
;
362 if (curr_mode
== stm_16bit
)
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
),
369 frame
->AddrFrame
.Offset
= val
;
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
)))
382 if (curr_mode
== stm_16bit
)
386 p
= sw_xlat_addr(cb
, &frame
->AddrFrame
);
387 if (!sw_read_mem(cb
, p
+ sizeof(WORD
), &val
, sizeof(WORD
)))
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
)))
393 if (frame
->AddrFrame
.Offset
& 1)
394 frame
->AddrReturn
.Segment
= val
; /* far call assumed */
397 /* not explicitly marked as far call,
398 * but check whether it could be anyway
400 if ((val
& 7) == 7 && val
!= frame
->AddrReturn
.Segment
)
404 if (GetThreadSelectorEntry(cb
->hThread
, val
, &le
) &&
405 (le
.HighWord
.Bits
.Type
& 0x08)) /* code segment */
407 /* it is very uncommon to push a code segment cs as
408 * a parameter, so this should work in most cases
410 frame
->AddrReturn
.Segment
= val
;
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
419 for (i
= 0; i
< sizeof(frame
->Params
) / sizeof(frame
->Params
[0]); i
++)
421 sw_read_mem(cb
, p
+ (2 + i
) * sizeof(WORD
), &val
, sizeof(val
));
422 frame
->Params
[i
] = val
;
427 if (!sw_read_mem(cb
, frame
->AddrFrame
.Offset
+ sizeof(DWORD
),
428 &frame
->AddrReturn
.Offset
, sizeof(DWORD
)))
430 WARN("Cannot read new frame offset %08lx\n", frame
->AddrFrame
.Offset
+ sizeof(DWORD
));
433 sw_read_mem(cb
, frame
->AddrFrame
.Offset
+ 2 * sizeof(DWORD
),
434 frame
->Params
, sizeof(frame
->Params
));
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
);
443 frame
->FuncTableEntry
= NULL
;
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
);
455 curr_mode
= stm_done
;
459 /***********************************************************************
460 * StackWalk (DBGHELP.@)
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
)
469 struct stack_walk_callback swcb
;
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
);
476 if (MachineType
!= IMAGE_FILE_MACHINE_I386
)
478 SetLastError(ERROR_INVALID_PARAMETER
);
482 swcb
.hProcess
= hProcess
;
483 swcb
.hThread
= hThread
;
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
;
491 return stack_walk(&swcb
, frame
);
495 /***********************************************************************
496 * StackWalk64 (DBGHELP.@)
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
)
505 struct stack_walk_callback swcb
;
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
);
514 if (MachineType
!= IMAGE_FILE_MACHINE_I386
)
516 SetLastError(ERROR_INVALID_PARAMETER
);
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 */
533 swcb
.hProcess
= hProcess
;
534 swcb
.hThread
= hThread
;
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
;
542 ret
= stack_walk(&swcb
, &frame32
);
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;
573 /******************************************************************
574 * SymRegisterFunctionEntryCallback (DBGHELP.@)
578 BOOL WINAPI
SymRegisterFunctionEntryCallback(HANDLE hProc
,
579 PSYMBOL_FUNCENTRY_CALLBACK cb
, PVOID user
)
581 FIXME("(%p %p %p): stub!\n", hProc
, cb
, user
);
582 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
586 /******************************************************************
587 * SymRegisterFunctionEntryCallback64 (DBGHELP.@)
591 BOOL WINAPI
SymRegisterFunctionEntryCallback64(HANDLE hProc
,
592 PSYMBOL_FUNCENTRY_CALLBACK64 cb
,
595 FIXME("(%p %p %s): stub!\n", hProc
, cb
, wine_dbgstr_longlong(user
));
596 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);