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<%08x>", addr
->Offset
);
50 return wine_dbg_sprintf("1616<%04x:%04x>", addr
->Segment
, addr
->Offset
);
52 return wine_dbg_sprintf("1632<%04x:%08x>", addr
->Segment
, addr
->Offset
);
54 return wine_dbg_sprintf("real<%04x:%04x>", addr
->Segment
, addr
->Offset
);
60 static BOOL CALLBACK
read_mem(HANDLE hProcess
, DWORD addr
, void* buffer
,
61 DWORD size
, LPDWORD nread
)
64 if (!ReadProcessMemory(hProcess
, (void*)addr
, buffer
, size
, &r
)) return FALSE
;
69 static BOOL CALLBACK
read_mem64(HANDLE hProcess
, DWORD64 addr
, void* buffer
,
70 DWORD size
, LPDWORD nread
)
73 if (!ReadProcessMemory(hProcess
, (void*)(DWORD_PTR
)addr
, buffer
, size
, &r
)) return FALSE
;
78 /* indexes in Reserved array */
79 #define __CurrentMode 0
80 #define __CurrentSwitch 1
81 #define __NextSwitch 2
83 #define curr_mode (frame->Reserved[__CurrentMode])
84 #define curr_switch (frame->Reserved[__CurrentSwitch])
85 #define next_switch (frame->Reserved[__NextSwitch])
87 struct stack_walk_callback
96 PREAD_PROCESS_MEMORY_ROUTINE f_read_mem
;
97 PTRANSLATE_ADDRESS_ROUTINE f_xlat_adr
;
98 PFUNCTION_TABLE_ACCESS_ROUTINE f_tabl_acs
;
99 PGET_MODULE_BASE_ROUTINE f_modl_bas
;
103 PREAD_PROCESS_MEMORY_ROUTINE64 f_read_mem
;
104 PTRANSLATE_ADDRESS_ROUTINE64 f_xlat_adr
;
105 PFUNCTION_TABLE_ACCESS_ROUTINE64 f_tabl_acs
;
106 PGET_MODULE_BASE_ROUTINE64 f_modl_bas
;
111 static inline void addr_32to64(const ADDRESS
* addr32
, ADDRESS64
* addr64
)
113 addr64
->Offset
= (ULONG64
)addr32
->Offset
;
114 addr64
->Segment
= addr32
->Segment
;
115 addr64
->Mode
= addr32
->Mode
;
118 static inline void addr_64to32(const ADDRESS64
* addr64
, ADDRESS
* addr32
)
120 addr32
->Offset
= (ULONG
)addr64
->Offset
;
121 addr32
->Segment
= addr64
->Segment
;
122 addr32
->Mode
= addr64
->Mode
;
125 static inline BOOL
sw_read_mem(struct stack_walk_callback
* cb
, DWORD addr
, void* ptr
, DWORD sz
)
128 return cb
->u
.s32
.f_read_mem(cb
->hProcess
, addr
, ptr
, sz
, NULL
);
130 return cb
->u
.s64
.f_read_mem(cb
->hProcess
, addr
, ptr
, sz
, NULL
);
133 static inline DWORD
sw_xlat_addr(struct stack_walk_callback
* cb
, ADDRESS
* addr
)
135 if (addr
->Mode
== AddrModeFlat
) return addr
->Offset
;
136 if (cb
->is32
) return cb
->u
.s32
.f_xlat_adr(cb
->hProcess
, cb
->hThread
, addr
);
137 if (cb
->u
.s64
.f_xlat_adr
)
141 addr_32to64(addr
, &addr64
);
142 return cb
->u
.s64
.f_xlat_adr(cb
->hProcess
, cb
->hThread
, &addr64
);
144 return addr_to_linear(cb
->hProcess
, cb
->hThread
, addr
);
147 static inline void* sw_tabl_acs(struct stack_walk_callback
* cb
, DWORD addr
)
150 return cb
->u
.s32
.f_tabl_acs(cb
->hProcess
, addr
);
152 return cb
->u
.s64
.f_tabl_acs(cb
->hProcess
, addr
);
155 static inline DWORD
sw_modl_bas(struct stack_walk_callback
* cb
, DWORD addr
)
158 return cb
->u
.s32
.f_modl_bas(cb
->hProcess
, addr
);
160 return cb
->u
.s64
.f_modl_bas(cb
->hProcess
, addr
);
163 static BOOL
stack_walk(struct stack_walk_callback
* cb
, LPSTACKFRAME frame
)
165 STACK32FRAME frame32
;
166 STACK16FRAME frame16
;
174 if (curr_mode
>= stm_done
) return FALSE
;
176 TRACE("Enter: PC=%s Frame=%s Return=%s Stack=%s Mode=%s cSwitch=%08x nSwitch=%08x\n",
177 wine_dbgstr_addr(&frame
->AddrPC
),
178 wine_dbgstr_addr(&frame
->AddrFrame
),
179 wine_dbgstr_addr(&frame
->AddrReturn
),
180 wine_dbgstr_addr(&frame
->AddrStack
),
181 curr_mode
== stm_start
? "start" : (curr_mode
== stm_16bit
? "16bit" : "32bit"),
182 curr_switch
, next_switch
);
184 if (curr_mode
== stm_start
)
186 THREAD_BASIC_INFORMATION info
;
188 if ((frame
->AddrPC
.Mode
== AddrModeFlat
) &&
189 (frame
->AddrFrame
.Mode
!= AddrModeFlat
))
191 WARN("Bad AddrPC.Mode / AddrFrame.Mode combination\n");
196 curr_mode
= (frame
->AddrPC
.Mode
== AddrModeFlat
) ?
197 stm_32bit
: stm_16bit
;
199 /* cur_switch holds address of WOW32Reserved field in TEB in debuggee
202 if (NtQueryInformationThread(cb
->hThread
, ThreadBasicInformation
, &info
,
203 sizeof(info
), NULL
) == STATUS_SUCCESS
)
205 curr_switch
= (unsigned long)info
.TebBaseAddress
+ FIELD_OFFSET(TEB
, WOW32Reserved
);
206 if (!sw_read_mem(cb
, curr_switch
, &next_switch
, sizeof(next_switch
)))
208 WARN("Can't read TEB:WOW32Reserved\n");
211 if (curr_mode
== stm_16bit
)
213 if (!sw_read_mem(cb
, next_switch
, &frame32
, sizeof(frame32
)))
215 WARN("Bad stack frame 0x%08x\n", next_switch
);
218 curr_switch
= (DWORD
)frame32
.frame16
;
219 tmp
.Mode
= AddrMode1616
;
220 tmp
.Segment
= SELECTOROF(curr_switch
);
221 tmp
.Offset
= OFFSETOF(curr_switch
);
222 if (!sw_read_mem(cb
, sw_xlat_addr(cb
, &tmp
), &ch
, sizeof(ch
)))
223 curr_switch
= 0xFFFFFFFF;
227 tmp
.Mode
= AddrMode1616
;
228 tmp
.Segment
= SELECTOROF(next_switch
);
229 tmp
.Offset
= OFFSETOF(next_switch
);
230 p
= sw_xlat_addr(cb
, &tmp
);
231 if (!sw_read_mem(cb
, p
, &frame16
, sizeof(frame16
)))
233 WARN("Bad stack frame 0x%08x\n", p
);
236 curr_switch
= (DWORD
)frame16
.frame32
;
238 if (!sw_read_mem(cb
, curr_switch
, &ch
, sizeof(ch
)))
239 curr_switch
= 0xFFFFFFFF;
243 /* FIXME: this will allow to work when we're not attached to a live target,
244 * but the 16 <=> 32 switch facility won't be available.
247 frame
->AddrReturn
.Mode
= frame
->AddrStack
.Mode
= (curr_mode
== stm_16bit
) ? AddrMode1616
: AddrModeFlat
;
248 /* don't set up AddrStack on first call. Either the caller has set it up, or
249 * we will get it in the next frame
251 memset(&frame
->AddrBStore
, 0, sizeof(frame
->AddrBStore
));
255 if (frame
->AddrFrame
.Offset
== 0) goto done_err
;
256 if (frame
->AddrFrame
.Mode
== AddrModeFlat
)
258 assert(curr_mode
== stm_32bit
);
259 do_switch
= curr_switch
&& frame
->AddrFrame
.Offset
>= curr_switch
;
263 assert(curr_mode
== stm_16bit
);
264 do_switch
= curr_switch
&&
265 frame
->AddrFrame
.Segment
== SELECTOROF(curr_switch
) &&
266 frame
->AddrFrame
.Offset
>= OFFSETOF(curr_switch
);
271 if (curr_mode
== stm_16bit
)
273 if (!sw_read_mem(cb
, next_switch
, &frame32
, sizeof(frame32
)))
275 WARN("Bad stack frame 0x%08x\n", next_switch
);
279 frame
->AddrPC
.Mode
= AddrModeFlat
;
280 frame
->AddrPC
.Segment
= 0;
281 frame
->AddrPC
.Offset
= frame32
.retaddr
;
282 frame
->AddrFrame
.Mode
= AddrModeFlat
;
283 frame
->AddrFrame
.Segment
= 0;
284 frame
->AddrFrame
.Offset
= frame32
.ebp
;
286 frame
->AddrStack
.Mode
= AddrModeFlat
;
287 frame
->AddrStack
.Segment
= 0;
288 frame
->AddrReturn
.Mode
= AddrModeFlat
;
289 frame
->AddrReturn
.Segment
= 0;
291 next_switch
= curr_switch
;
292 tmp
.Mode
= AddrMode1616
;
293 tmp
.Segment
= SELECTOROF(next_switch
);
294 tmp
.Offset
= OFFSETOF(next_switch
);
295 p
= sw_xlat_addr(cb
, &tmp
);
297 if (!sw_read_mem(cb
, p
, &frame16
, sizeof(frame16
)))
299 WARN("Bad stack frame 0x%08x\n", p
);
302 curr_switch
= (DWORD
)frame16
.frame32
;
303 curr_mode
= stm_32bit
;
304 if (!sw_read_mem(cb
, curr_switch
, &ch
, sizeof(ch
)))
309 tmp
.Mode
= AddrMode1616
;
310 tmp
.Segment
= SELECTOROF(next_switch
);
311 tmp
.Offset
= OFFSETOF(next_switch
);
312 p
= sw_xlat_addr(cb
, &tmp
);
314 if (!sw_read_mem(cb
, p
, &frame16
, sizeof(frame16
)))
316 WARN("Bad stack frame 0x%08x\n", p
);
320 TRACE("Got a 16 bit stack switch:"
322 "\n\tedx:%08x ecx:%08x ebp:%08x"
323 "\n\tds:%04x es:%04x fs:%04x gs:%04x"
324 "\n\tcall_from_ip:%08x module_cs:%04x relay=%08x"
325 "\n\tentry_ip:%04x entry_point:%08x"
326 "\n\tbp:%04x ip:%04x cs:%04x\n",
327 (unsigned long)frame16
.frame32
,
328 frame16
.edx
, frame16
.ecx
, frame16
.ebp
,
329 frame16
.ds
, frame16
.es
, frame16
.fs
, frame16
.gs
,
330 frame16
.callfrom_ip
, frame16
.module_cs
, frame16
.relay
,
331 frame16
.entry_ip
, frame16
.entry_point
,
332 frame16
.bp
, frame16
.ip
, frame16
.cs
);
335 frame
->AddrPC
.Mode
= AddrMode1616
;
336 frame
->AddrPC
.Segment
= frame16
.cs
;
337 frame
->AddrPC
.Offset
= frame16
.ip
;
339 frame
->AddrFrame
.Mode
= AddrMode1616
;
340 frame
->AddrFrame
.Segment
= SELECTOROF(next_switch
);
341 frame
->AddrFrame
.Offset
= frame16
.bp
;
343 frame
->AddrStack
.Mode
= AddrMode1616
;
344 frame
->AddrStack
.Segment
= SELECTOROF(next_switch
);
346 frame
->AddrReturn
.Mode
= AddrMode1616
;
347 frame
->AddrReturn
.Segment
= frame16
.cs
;
349 next_switch
= curr_switch
;
350 if (!sw_read_mem(cb
, next_switch
, &frame32
, sizeof(frame32
)))
352 WARN("Bad stack frame 0x%08x\n", next_switch
);
355 curr_switch
= (DWORD
)frame32
.frame16
;
356 tmp
.Mode
= AddrMode1616
;
357 tmp
.Segment
= SELECTOROF(curr_switch
);
358 tmp
.Offset
= OFFSETOF(curr_switch
);
360 if (!sw_read_mem(cb
, sw_xlat_addr(cb
, &tmp
), &ch
, sizeof(ch
)))
362 curr_mode
= stm_16bit
;
367 frame
->AddrPC
= frame
->AddrReturn
;
368 if (curr_mode
== stm_16bit
)
370 frame
->AddrStack
.Offset
= frame
->AddrFrame
.Offset
+ 2 * sizeof(WORD
);
371 /* "pop up" previous BP value */
372 if (!sw_read_mem(cb
, sw_xlat_addr(cb
, &frame
->AddrFrame
),
375 frame
->AddrFrame
.Offset
= val
;
379 frame
->AddrStack
.Offset
= frame
->AddrFrame
.Offset
+ 2 * sizeof(DWORD
);
380 /* "pop up" previous EBP value */
381 if (!sw_read_mem(cb
, frame
->AddrFrame
.Offset
,
382 &frame
->AddrFrame
.Offset
, sizeof(DWORD
)))
388 if (curr_mode
== stm_16bit
)
392 p
= sw_xlat_addr(cb
, &frame
->AddrFrame
);
393 if (!sw_read_mem(cb
, p
+ sizeof(WORD
), &val
, sizeof(WORD
)))
395 frame
->AddrReturn
.Offset
= val
;
396 /* get potential cs if a far call was used */
397 if (!sw_read_mem(cb
, p
+ 2 * sizeof(WORD
), &val
, sizeof(WORD
)))
399 if (frame
->AddrFrame
.Offset
& 1)
400 frame
->AddrReturn
.Segment
= val
; /* far call assumed */
403 /* not explicitly marked as far call,
404 * but check whether it could be anyway
406 if ((val
& 7) == 7 && val
!= frame
->AddrReturn
.Segment
)
410 if (GetThreadSelectorEntry(cb
->hThread
, val
, &le
) &&
411 (le
.HighWord
.Bits
.Type
& 0x08)) /* code segment */
413 /* it is very uncommon to push a code segment cs as
414 * a parameter, so this should work in most cases
416 frame
->AddrReturn
.Segment
= val
;
420 frame
->AddrFrame
.Offset
&= ~1;
421 /* we "pop" parameters as 16 bit entities... of course, this won't
422 * work if the parameter is in fact bigger than 16bit, but
423 * there's no way to know that here
425 for (i
= 0; i
< sizeof(frame
->Params
) / sizeof(frame
->Params
[0]); i
++)
427 sw_read_mem(cb
, p
+ (2 + i
) * sizeof(WORD
), &val
, sizeof(val
));
428 frame
->Params
[i
] = val
;
433 if (!sw_read_mem(cb
, frame
->AddrFrame
.Offset
+ sizeof(DWORD
),
434 &frame
->AddrReturn
.Offset
, sizeof(DWORD
)))
436 WARN("Cannot read new frame offset %08x\n", frame
->AddrFrame
.Offset
+ (int)sizeof(DWORD
));
439 sw_read_mem(cb
, frame
->AddrFrame
.Offset
+ 2 * sizeof(DWORD
),
440 frame
->Params
, sizeof(frame
->Params
));
444 frame
->Virtual
= TRUE
;
445 p
= sw_xlat_addr(cb
, &frame
->AddrPC
);
446 if (p
&& sw_modl_bas(cb
, p
))
447 frame
->FuncTableEntry
= sw_tabl_acs(cb
, p
);
449 frame
->FuncTableEntry
= NULL
;
451 TRACE("Leave: PC=%s Frame=%s Return=%s Stack=%s Mode=%s cSwitch=%08x nSwitch=%08x FuncTable=%p\n",
452 wine_dbgstr_addr(&frame
->AddrPC
),
453 wine_dbgstr_addr(&frame
->AddrFrame
),
454 wine_dbgstr_addr(&frame
->AddrReturn
),
455 wine_dbgstr_addr(&frame
->AddrStack
),
456 curr_mode
== stm_start
? "start" : (curr_mode
== stm_16bit
? "16bit" : "32bit"),
457 curr_switch
, next_switch
, frame
->FuncTableEntry
);
461 curr_mode
= stm_done
;
465 /***********************************************************************
466 * StackWalk (DBGHELP.@)
468 BOOL WINAPI
StackWalk(DWORD MachineType
, HANDLE hProcess
, HANDLE hThread
,
469 LPSTACKFRAME frame
, LPVOID ctx
,
470 PREAD_PROCESS_MEMORY_ROUTINE f_read_mem
,
471 PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine
,
472 PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine
,
473 PTRANSLATE_ADDRESS_ROUTINE f_xlat_adr
)
475 struct stack_walk_callback swcb
;
477 TRACE("(%d, %p, %p, %p, %p, %p, %p, %p, %p)\n",
478 MachineType
, hProcess
, hThread
, frame
, ctx
,
479 f_read_mem
, FunctionTableAccessRoutine
,
480 GetModuleBaseRoutine
, f_xlat_adr
);
482 if (MachineType
!= IMAGE_FILE_MACHINE_I386
)
484 SetLastError(ERROR_INVALID_PARAMETER
);
488 swcb
.hProcess
= hProcess
;
489 swcb
.hThread
= hThread
;
491 /* sigh... MS isn't even consistent in the func prototypes */
492 swcb
.u
.s32
.f_read_mem
= (f_read_mem
) ? f_read_mem
: read_mem
;
493 swcb
.u
.s32
.f_xlat_adr
= (f_xlat_adr
) ? f_xlat_adr
: addr_to_linear
;
494 swcb
.u
.s32
.f_tabl_acs
= (FunctionTableAccessRoutine
) ? FunctionTableAccessRoutine
: SymFunctionTableAccess
;
495 swcb
.u
.s32
.f_modl_bas
= (GetModuleBaseRoutine
) ? GetModuleBaseRoutine
: SymGetModuleBase
;
497 return stack_walk(&swcb
, frame
);
501 /***********************************************************************
502 * StackWalk64 (DBGHELP.@)
504 BOOL WINAPI
StackWalk64(DWORD MachineType
, HANDLE hProcess
, HANDLE hThread
,
505 LPSTACKFRAME64 frame64
, LPVOID ctx
,
506 PREAD_PROCESS_MEMORY_ROUTINE64 f_read_mem
,
507 PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine
,
508 PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine
,
509 PTRANSLATE_ADDRESS_ROUTINE64 f_xlat_adr
)
511 struct stack_walk_callback swcb
;
515 TRACE("(%d, %p, %p, %p, %p, %p, %p, %p, %p)\n",
516 MachineType
, hProcess
, hThread
, frame64
, ctx
,
517 f_read_mem
, FunctionTableAccessRoutine
,
518 GetModuleBaseRoutine
, f_xlat_adr
);
520 if (MachineType
!= IMAGE_FILE_MACHINE_I386
)
522 SetLastError(ERROR_INVALID_PARAMETER
);
526 addr_64to32(&frame64
->AddrPC
, &frame32
.AddrPC
);
527 addr_64to32(&frame64
->AddrReturn
, &frame32
.AddrReturn
);
528 addr_64to32(&frame64
->AddrFrame
, &frame32
.AddrFrame
);
529 addr_64to32(&frame64
->AddrStack
, &frame32
.AddrStack
);
530 addr_64to32(&frame64
->AddrBStore
, &frame32
.AddrBStore
);
531 frame32
.FuncTableEntry
= frame64
->FuncTableEntry
; /* FIXME */
532 frame32
.Far
= frame64
->Far
;
533 frame32
.Virtual
= frame64
->Virtual
;
534 frame32
.Reserved
[0] = (ULONG
)frame64
->Reserved
[0];
535 frame32
.Reserved
[1] = (ULONG
)frame64
->Reserved
[1];
536 frame32
.Reserved
[2] = (ULONG
)frame64
->Reserved
[2];
537 /* we don't handle KdHelp */
539 swcb
.hProcess
= hProcess
;
540 swcb
.hThread
= hThread
;
542 /* sigh... MS isn't even consistent in the func prototypes */
543 swcb
.u
.s64
.f_read_mem
= (f_read_mem
) ? f_read_mem
: read_mem64
;
544 swcb
.u
.s64
.f_xlat_adr
= f_xlat_adr
;
545 swcb
.u
.s64
.f_tabl_acs
= (FunctionTableAccessRoutine
) ? FunctionTableAccessRoutine
: SymFunctionTableAccess64
;
546 swcb
.u
.s64
.f_modl_bas
= (GetModuleBaseRoutine
) ? GetModuleBaseRoutine
: SymGetModuleBase64
;
548 ret
= stack_walk(&swcb
, &frame32
);
550 addr_32to64(&frame32
.AddrPC
, &frame64
->AddrPC
);
551 addr_32to64(&frame32
.AddrReturn
, &frame64
->AddrReturn
);
552 addr_32to64(&frame32
.AddrFrame
, &frame64
->AddrFrame
);
553 addr_32to64(&frame32
.AddrStack
, &frame64
->AddrStack
);
554 addr_32to64(&frame32
.AddrBStore
, &frame64
->AddrBStore
);
555 frame64
->FuncTableEntry
= frame32
.FuncTableEntry
; /* FIXME */
556 frame64
->Params
[0] = (ULONG
)frame32
.Params
[0];
557 frame64
->Params
[1] = (ULONG
)frame32
.Params
[1];
558 frame64
->Params
[2] = (ULONG
)frame32
.Params
[2];
559 frame64
->Params
[3] = (ULONG
)frame32
.Params
[3];
560 frame64
->Far
= frame32
.Far
;
561 frame64
->Virtual
= frame32
.Virtual
;
562 frame64
->Reserved
[0] = (ULONG
)frame32
.Reserved
[0];
563 frame64
->Reserved
[1] = (ULONG
)frame32
.Reserved
[1];
564 frame64
->Reserved
[2] = (ULONG
)frame32
.Reserved
[2];
565 /* we don't handle KdHelp */
566 frame64
->KdHelp
.Thread
= 0xC000FADE;
567 frame64
->KdHelp
.ThCallbackStack
= 0x10;
568 frame64
->KdHelp
.ThCallbackBStore
= 0;
569 frame64
->KdHelp
.NextCallback
= 0;
570 frame64
->KdHelp
.FramePointer
= 0;
571 frame64
->KdHelp
.KiCallUserMode
= 0xD000DAFE;
572 frame64
->KdHelp
.KeUserCallbackDispatcher
= 0xE000F000;
573 frame64
->KdHelp
.SystemRangeStart
= 0xC0000000;
574 frame64
->KdHelp
.Reserved
[0] /* KiUserExceptionDispatcher */ = 0xE0005000;
579 /******************************************************************
580 * SymRegisterFunctionEntryCallback (DBGHELP.@)
584 BOOL WINAPI
SymRegisterFunctionEntryCallback(HANDLE hProc
,
585 PSYMBOL_FUNCENTRY_CALLBACK cb
, PVOID user
)
587 FIXME("(%p %p %p): stub!\n", hProc
, cb
, user
);
588 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
592 /******************************************************************
593 * SymRegisterFunctionEntryCallback64 (DBGHELP.@)
597 BOOL WINAPI
SymRegisterFunctionEntryCallback64(HANDLE hProc
,
598 PSYMBOL_FUNCENTRY_CALLBACK64 cb
,
601 FIXME("(%p %p %s): stub!\n", hProc
, cb
, wine_dbgstr_longlong(user
));
602 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);