| /* | |
| * File dbghelp.c - generic routines (process) for dbghelp DLL | |
| * | |
| * Copyright (C) 2004, Eric Pouech | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
| */ | |
| #include "config.h" | |
| #include "dbghelp_private.h" | |
| #include "winerror.h" | |
| #include "psapi.h" | |
| #include "wine/debug.h" | |
| #include "wdbgexts.h" | |
| #include "winnls.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(dbghelp); | |
| /* TODO | |
| * - support for symbols' types is still partly missing | |
| * + C++ support | |
| * + we should store the underlying type for an enum in the symt_enum struct | |
| * + for enums, we store the names & values (associated to the enum type), | |
| * but those values are not directly usable from a debugger (that's why, I | |
| * assume, that we have also to define constants for enum values, as | |
| * Codeview does BTW. | |
| * + SymEnumTypes should only return *user* defined types (UDT, typedefs...) not | |
| * all the types stored/used in the modules (like char*) | |
| * - SymGetLine{Next|Prev} don't work as expected (they don't seem to work across | |
| * functions, and even across function blocks...). Basically, for *Next* to work | |
| * it requires an address after the prolog of the func (the base address of the | |
| * func doesn't work) | |
| * - most options (dbghelp_options) are not used (loading lines...) | |
| * - in symbol lookup by name, we don't use RE everywhere we should. Moreover, when | |
| * we're supposed to use RE, it doesn't make use of our hash tables. Therefore, | |
| * we could use hash if name isn't a RE, and fall back to a full search when we | |
| * get a full RE | |
| * - msc: | |
| * + we should add parameters' types to the function's signature | |
| * while processing a function's parameters | |
| * + add support for function-less labels (as MSC seems to define them) | |
| * + C++ management | |
| * - stabs: | |
| * + when, in a same module, the same definition is used in several compilation | |
| * units, we get several definitions of the same object (especially | |
| * struct/union). we should find a way not to duplicate them | |
| * + in some cases (dlls/user/dialog16.c DIALOG_GetControl16), the same static | |
| * global variable is defined several times (at different scopes). We are | |
| * getting several of those while looking for a unique symbol. Part of the | |
| * issue is that we don't give a scope to a static variable inside a function | |
| * + C++ management | |
| */ | |
| unsigned dbghelp_options = SYMOPT_UNDNAME; | |
| HANDLE hMsvcrt = NULL; | |
| /*********************************************************************** | |
| * DllMain (DEBUGHLP.@) | |
| */ | |
| BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) | |
| { | |
| switch (fdwReason) | |
| { | |
| case DLL_PROCESS_ATTACH: break; | |
| case DLL_PROCESS_DETACH: | |
| if (hMsvcrt) FreeLibrary(hMsvcrt); | |
| break; | |
| case DLL_THREAD_ATTACH: break; | |
| case DLL_THREAD_DETACH: break; | |
| default: break; | |
| } | |
| return TRUE; | |
| } | |
| static struct process* process_first /* = NULL */; | |
| /****************************************************************** | |
| * process_find_by_handle | |
| * | |
| */ | |
| struct process* process_find_by_handle(HANDLE hProcess) | |
| { | |
| struct process* p; | |
| for (p = process_first; p && p->handle != hProcess; p = p->next); | |
| if (!p) SetLastError(ERROR_INVALID_HANDLE); | |
| return p; | |
| } | |
| /****************************************************************** | |
| * validate_addr64 (internal) | |
| * | |
| */ | |
| BOOL validate_addr64(DWORD64 addr) | |
| { | |
| if (addr >> 32) | |
| { | |
| FIXME("Unsupported address %s\n", wine_dbgstr_longlong(addr)); | |
| SetLastError(ERROR_INVALID_PARAMETER); | |
| return FALSE; | |
| } | |
| return TRUE; | |
| } | |
| /****************************************************************** | |
| * fetch_buffer | |
| * | |
| * Ensures process' internal buffer is large enough. | |
| */ | |
| void* fetch_buffer(struct process* pcs, unsigned size) | |
| { | |
| if (size > pcs->buffer_size) | |
| { | |
| if (pcs->buffer) | |
| pcs->buffer = HeapReAlloc(GetProcessHeap(), 0, pcs->buffer, size); | |
| else | |
| pcs->buffer = HeapAlloc(GetProcessHeap(), 0, size); | |
| pcs->buffer_size = (pcs->buffer) ? size : 0; | |
| } | |
| return pcs->buffer; | |
| } | |
| /****************************************************************** | |
| * SymSetSearchPathW (DBGHELP.@) | |
| * | |
| */ | |
| BOOL WINAPI SymSetSearchPathW(HANDLE hProcess, PCWSTR searchPath) | |
| { | |
| struct process* pcs = process_find_by_handle(hProcess); | |
| if (!pcs) return FALSE; | |
| if (!searchPath) return FALSE; | |
| HeapFree(GetProcessHeap(), 0, pcs->search_path); | |
| pcs->search_path = lstrcpyW(HeapAlloc(GetProcessHeap(), 0, | |
| (lstrlenW(searchPath) + 1) * sizeof(WCHAR)), | |
| searchPath); | |
| return TRUE; | |
| } | |
| /****************************************************************** | |
| * SymSetSearchPath (DBGHELP.@) | |
| * | |
| */ | |
| BOOL WINAPI SymSetSearchPath(HANDLE hProcess, PCSTR searchPath) | |
| { | |
| BOOL ret = FALSE; | |
| unsigned len; | |
| WCHAR* sp; | |
| len = MultiByteToWideChar(CP_ACP, 0, searchPath, -1, NULL, 0); | |
| if ((sp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)))) | |
| { | |
| MultiByteToWideChar(CP_ACP, 0, searchPath, -1, sp, len); | |
| ret = SymSetSearchPathW(hProcess, sp); | |
| HeapFree(GetProcessHeap(), 0, sp); | |
| } | |
| return ret; | |
| } | |
| /*********************************************************************** | |
| * SymGetSearchPathW (DBGHELP.@) | |
| */ | |
| BOOL WINAPI SymGetSearchPathW(HANDLE hProcess, PWSTR szSearchPath, | |
| DWORD SearchPathLength) | |
| { | |
| struct process* pcs = process_find_by_handle(hProcess); | |
| if (!pcs) return FALSE; | |
| lstrcpynW(szSearchPath, pcs->search_path, SearchPathLength); | |
| return TRUE; | |
| } | |
| /*********************************************************************** | |
| * SymGetSearchPath (DBGHELP.@) | |
| */ | |
| BOOL WINAPI SymGetSearchPath(HANDLE hProcess, PSTR szSearchPath, | |
| DWORD SearchPathLength) | |
| { | |
| WCHAR* buffer = HeapAlloc(GetProcessHeap(), 0, SearchPathLength * sizeof(WCHAR)); | |
| BOOL ret = FALSE; | |
| if (buffer) | |
| { | |
| ret = SymGetSearchPathW(hProcess, buffer, SearchPathLength); | |
| if (ret) | |
| WideCharToMultiByte(CP_ACP, 0, buffer, SearchPathLength, | |
| szSearchPath, SearchPathLength, NULL, NULL); | |
| HeapFree(GetProcessHeap(), 0, buffer); | |
| } | |
| return ret; | |
| } | |
| /****************************************************************** | |
| * invade_process | |
| * | |
| * SymInitialize helper: loads in dbghelp all known (and loaded modules) | |
| * this assumes that hProcess is a handle on a valid process | |
| */ | |
| static BOOL WINAPI process_invade_cb(PCSTR name, ULONG base, ULONG size, PVOID user) | |
| { | |
| char tmp[MAX_PATH]; | |
| HANDLE hProcess = (HANDLE)user; | |
| if (!GetModuleFileNameExA(hProcess, (HMODULE)base, | |
| tmp, sizeof(tmp))) | |
| lstrcpynA(tmp, name, sizeof(tmp)); | |
| SymLoadModule(hProcess, 0, tmp, name, base, size); | |
| return TRUE; | |
| } | |
| /****************************************************************** | |
| * check_live_target | |
| * | |
| */ | |
| static BOOL check_live_target(struct process* pcs) | |
| { | |
| if (!GetProcessId(pcs->handle)) return FALSE; | |
| if (GetEnvironmentVariableA("DBGHELP_NOLIVE", NULL, 0)) return FALSE; | |
| elf_read_wine_loader_dbg_info(pcs); | |
| return TRUE; | |
| } | |
| /****************************************************************** | |
| * SymInitializeW (DBGHELP.@) | |
| * | |
| * The initialisation of a dbghelp's context. | |
| * Note that hProcess doesn't need to be a valid process handle (except | |
| * when fInvadeProcess is TRUE). | |
| * Since, we're also allow to load ELF (pure) libraries and Wine ELF libraries | |
| * containing PE (and NE) module(s), here's how we handle it: | |
| * - we load every module (ELF, NE, PE) passed in SymLoadModule | |
| * - in fInvadeProcess (in SymInitialize) is TRUE, we set up what is called ELF | |
| * synchronization: hProcess should be a valid process handle, and we hook | |
| * ourselves on hProcess's loaded ELF-modules, and keep this list in sync with | |
| * our internal ELF modules representation (loading / unloading). This way, | |
| * we'll pair every loaded builtin PE module with its ELF counterpart (and | |
| * access its debug information). | |
| * - if fInvadeProcess (in SymInitialize) is FALSE, we check anyway if the | |
| * hProcess refers to a running process. We use some heuristics here, so YMMV. | |
| * If we detect a live target, then we get the same handling as if | |
| * fInvadeProcess is TRUE (except that the modules are not loaded). Otherwise, | |
| * we won't be able to make the peering between a builtin PE module and its ELF | |
| * counterpart. Hence we won't be able to provide the requested debug | |
| * information. We'll however be able to load native PE modules (and their | |
| * debug information) without any trouble. | |
| * Note also that this scheme can be intertwined with the deferred loading | |
| * mechanism (ie only load the debug information when we actually need it). | |
| */ | |
| BOOL WINAPI SymInitializeW(HANDLE hProcess, PCWSTR UserSearchPath, BOOL fInvadeProcess) | |
| { | |
| struct process* pcs; | |
| TRACE("(%p %s %u)\n", hProcess, debugstr_w(UserSearchPath), fInvadeProcess); | |
| if (process_find_by_handle(hProcess)) | |
| FIXME("what to do ??\n"); | |
| pcs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pcs)); | |
| if (!pcs) return FALSE; | |
| pcs->handle = hProcess; | |
| if (UserSearchPath) | |
| { | |
| pcs->search_path = lstrcpyW(HeapAlloc(GetProcessHeap(), 0, | |
| (lstrlenW(UserSearchPath) + 1) * sizeof(WCHAR)), | |
| UserSearchPath); | |
| } | |
| else | |
| { | |
| unsigned size; | |
| unsigned len; | |
| static const WCHAR sym_path[] = {'_','N','T','_','S','Y','M','B','O','L','_','P','A','T','H',0}; | |
| static const WCHAR alt_sym_path[] = {'_','N','T','_','A','L','T','E','R','N','A','T','E','_','S','Y','M','B','O','L','_','P','A','T','H',0}; | |
| pcs->search_path = HeapAlloc(GetProcessHeap(), 0, (len = MAX_PATH) * sizeof(WCHAR)); | |
| while ((size = GetCurrentDirectoryW(len, pcs->search_path)) >= len) | |
| pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (len *= 2) * sizeof(WCHAR)); | |
| pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (size + 1) * sizeof(WCHAR)); | |
| len = GetEnvironmentVariableW(sym_path, NULL, 0); | |
| if (len) | |
| { | |
| pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (size + 1 + len + 1) * sizeof(WCHAR)); | |
| pcs->search_path[size] = ';'; | |
| GetEnvironmentVariableW(sym_path, pcs->search_path + size + 1, len); | |
| size += 1 + len; | |
| } | |
| len = GetEnvironmentVariableW(alt_sym_path, NULL, 0); | |
| if (len) | |
| { | |
| pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, (size + 1 + len + 1) * sizeof(WCHAR)); | |
| pcs->search_path[size] = ';'; | |
| GetEnvironmentVariableW(alt_sym_path, pcs->search_path + size + 1, len); | |
| size += 1 + len; | |
| } | |
| } | |
| pcs->lmodules = NULL; | |
| pcs->dbg_hdr_addr = 0; | |
| pcs->next = process_first; | |
| process_first = pcs; | |
| if (check_live_target(pcs)) | |
| { | |
| if (fInvadeProcess) | |
| EnumerateLoadedModules(hProcess, process_invade_cb, (void*)hProcess); | |
| elf_synchronize_module_list(pcs); | |
| } | |
| else if (fInvadeProcess) | |
| { | |
| SymCleanup(hProcess); | |
| SetLastError(ERROR_INVALID_PARAMETER); | |
| return FALSE; | |
| } | |
| return TRUE; | |
| } | |
| /****************************************************************** | |
| * SymInitialize (DBGHELP.@) | |
| * | |
| * | |
| */ | |
| BOOL WINAPI SymInitialize(HANDLE hProcess, PCSTR UserSearchPath, BOOL fInvadeProcess) | |
| { | |
| WCHAR* sp = NULL; | |
| BOOL ret; | |
| if (UserSearchPath) | |
| { | |
| unsigned len; | |
| len = MultiByteToWideChar(CP_ACP, 0, UserSearchPath, -1, NULL, 0); | |
| sp = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); | |
| MultiByteToWideChar(CP_ACP, 0, UserSearchPath, -1, sp, len); | |
| } | |
| ret = SymInitializeW(hProcess, sp, fInvadeProcess); | |
| HeapFree(GetProcessHeap(), 0, sp); | |
| return ret; | |
| } | |
| /****************************************************************** | |
| * SymCleanup (DBGHELP.@) | |
| * | |
| */ | |
| BOOL WINAPI SymCleanup(HANDLE hProcess) | |
| { | |
| struct process** ppcs; | |
| struct process* next; | |
| for (ppcs = &process_first; *ppcs; ppcs = &(*ppcs)->next) | |
| { | |
| if ((*ppcs)->handle == hProcess) | |
| { | |
| while ((*ppcs)->lmodules) module_remove(*ppcs, (*ppcs)->lmodules); | |
| HeapFree(GetProcessHeap(), 0, (*ppcs)->search_path); | |
| next = (*ppcs)->next; | |
| HeapFree(GetProcessHeap(), 0, *ppcs); | |
| *ppcs = next; | |
| return TRUE; | |
| } | |
| } | |
| return FALSE; | |
| } | |
| /****************************************************************** | |
| * SymSetOptions (DBGHELP.@) | |
| * | |
| */ | |
| DWORD WINAPI SymSetOptions(DWORD opts) | |
| { | |
| struct process* pcs; | |
| for (pcs = process_first; pcs; pcs = pcs->next) | |
| { | |
| pcs_callback(pcs, CBA_SET_OPTIONS, &opts); | |
| } | |
| return dbghelp_options = opts; | |
| } | |
| /****************************************************************** | |
| * SymGetOptions (DBGHELP.@) | |
| * | |
| */ | |
| DWORD WINAPI SymGetOptions(void) | |
| { | |
| return dbghelp_options; | |
| } | |
| /****************************************************************** | |
| * SymSetParentWindow (DBGHELP.@) | |
| * | |
| */ | |
| BOOL WINAPI SymSetParentWindow(HWND hwnd) | |
| { | |
| /* Save hwnd so it can be used as parent window */ | |
| FIXME("(%p): stub\n", hwnd); | |
| return TRUE; | |
| } | |
| /****************************************************************** | |
| * SymSetContext (DBGHELP.@) | |
| * | |
| */ | |
| BOOL WINAPI SymSetContext(HANDLE hProcess, PIMAGEHLP_STACK_FRAME StackFrame, | |
| PIMAGEHLP_CONTEXT Context) | |
| { | |
| struct process* pcs = process_find_by_handle(hProcess); | |
| if (!pcs) return FALSE; | |
| if (pcs->ctx_frame.ReturnOffset == StackFrame->ReturnOffset && | |
| pcs->ctx_frame.FrameOffset == StackFrame->FrameOffset && | |
| pcs->ctx_frame.StackOffset == StackFrame->StackOffset) | |
| { | |
| TRACE("Setting same frame {rtn=%s frm=%s stk=%s}\n", | |
| wine_dbgstr_longlong(pcs->ctx_frame.ReturnOffset), | |
| wine_dbgstr_longlong(pcs->ctx_frame.FrameOffset), | |
| wine_dbgstr_longlong(pcs->ctx_frame.StackOffset)); | |
| pcs->ctx_frame.InstructionOffset = StackFrame->InstructionOffset; | |
| SetLastError(ERROR_ACCESS_DENIED); /* latest MSDN says ERROR_SUCCESS */ | |
| return FALSE; | |
| } | |
| pcs->ctx_frame = *StackFrame; | |
| /* MSDN states that Context is not (no longer?) used */ | |
| return TRUE; | |
| } | |
| /****************************************************************** | |
| * reg_cb64to32 (internal) | |
| * | |
| * Registered callback for converting information from 64 bit to 32 bit | |
| */ | |
| static BOOL CALLBACK reg_cb64to32(HANDLE hProcess, ULONG action, ULONG64 data, ULONG64 user) | |
| { | |
| PSYMBOL_REGISTERED_CALLBACK cb32 = (PSYMBOL_REGISTERED_CALLBACK)(DWORD)(user >> 32); | |
| DWORD user32 = (DWORD)user; | |
| void* data32; | |
| IMAGEHLP_DEFERRED_SYMBOL_LOAD64* idsl64; | |
| IMAGEHLP_DEFERRED_SYMBOL_LOAD idsl; | |
| switch (action) | |
| { | |
| case CBA_DEBUG_INFO: | |
| case CBA_DEFERRED_SYMBOL_LOAD_CANCEL: | |
| case CBA_SET_OPTIONS: | |
| case CBA_SYMBOLS_UNLOADED: | |
| data32 = (void*)(DWORD)data; | |
| break; | |
| case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE: | |
| case CBA_DEFERRED_SYMBOL_LOAD_FAILURE: | |
| case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL: | |
| case CBA_DEFERRED_SYMBOL_LOAD_START: | |
| idsl64 = (IMAGEHLP_DEFERRED_SYMBOL_LOAD64*)(DWORD)data; | |
| if (!validate_addr64(idsl64->BaseOfImage)) | |
| return FALSE; | |
| idsl.SizeOfStruct = sizeof(idsl); | |
| idsl.BaseOfImage = (DWORD)idsl64->BaseOfImage; | |
| idsl.CheckSum = idsl64->CheckSum; | |
| idsl.TimeDateStamp = idsl64->TimeDateStamp; | |
| memcpy(idsl.FileName, idsl64->FileName, sizeof(idsl.FileName)); | |
| idsl.Reparse = idsl64->Reparse; | |
| data32 = &idsl; | |
| break; | |
| case CBA_DUPLICATE_SYMBOL: | |
| case CBA_EVENT: | |
| case CBA_READ_MEMORY: | |
| default: | |
| FIXME("No mapping for action %u\n", action); | |
| return FALSE; | |
| } | |
| return cb32(hProcess, action, (PVOID)data32, (PVOID)user32); | |
| } | |
| /****************************************************************** | |
| * pcs_callback (internal) | |
| */ | |
| BOOL pcs_callback(const struct process* pcs, ULONG action, void* data) | |
| { | |
| TRACE("%p %u %p\n", pcs, action, data); | |
| if (!pcs->reg_cb) return FALSE; | |
| if (!pcs->reg_is_unicode) | |
| { | |
| IMAGEHLP_DEFERRED_SYMBOL_LOAD64 idsl; | |
| IMAGEHLP_DEFERRED_SYMBOL_LOADW64* idslW; | |
| switch (action) | |
| { | |
| case CBA_DEBUG_INFO: | |
| case CBA_DEFERRED_SYMBOL_LOAD_CANCEL: | |
| case CBA_SET_OPTIONS: | |
| case CBA_SYMBOLS_UNLOADED: | |
| break; | |
| case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE: | |
| case CBA_DEFERRED_SYMBOL_LOAD_FAILURE: | |
| case CBA_DEFERRED_SYMBOL_LOAD_PARTIAL: | |
| case CBA_DEFERRED_SYMBOL_LOAD_START: | |
| idslW = (IMAGEHLP_DEFERRED_SYMBOL_LOADW64*)(DWORD)data; | |
| idsl.SizeOfStruct = sizeof(idsl); | |
| idsl.BaseOfImage = idslW->BaseOfImage; | |
| idsl.CheckSum = idslW->CheckSum; | |
| idsl.TimeDateStamp = idslW->TimeDateStamp; | |
| WideCharToMultiByte(CP_ACP, 0, idslW->FileName, -1, | |
| idsl.FileName, sizeof(idsl.FileName), NULL, NULL); | |
| idsl.Reparse = idslW->Reparse; | |
| data = &idsl; | |
| break; | |
| case CBA_DUPLICATE_SYMBOL: | |
| case CBA_EVENT: | |
| case CBA_READ_MEMORY: | |
| default: | |
| FIXME("No mapping for action %u\n", action); | |
| return FALSE; | |
| } | |
| } | |
| return pcs->reg_cb(pcs->handle, action, (ULONG64)(DWORD_PTR)data, pcs->reg_user); | |
| } | |
| /****************************************************************** | |
| * sym_register_cb | |
| * | |
| * Helper for registering a callback. | |
| */ | |
| static BOOL sym_register_cb(HANDLE hProcess, | |
| PSYMBOL_REGISTERED_CALLBACK64 cb, | |
| DWORD64 user, BOOL unicode) | |
| { | |
| struct process* pcs = process_find_by_handle(hProcess); | |
| if (!pcs) return FALSE; | |
| pcs->reg_cb = cb; | |
| pcs->reg_is_unicode = unicode; | |
| pcs->reg_user = user; | |
| return TRUE; | |
| } | |
| /*********************************************************************** | |
| * SymRegisterCallback (DBGHELP.@) | |
| */ | |
| BOOL WINAPI SymRegisterCallback(HANDLE hProcess, | |
| PSYMBOL_REGISTERED_CALLBACK CallbackFunction, | |
| PVOID UserContext) | |
| { | |
| DWORD64 tmp = ((ULONGLONG)(DWORD)CallbackFunction << 32) | (DWORD)UserContext; | |
| TRACE("(%p, %p, %p)\n", | |
| hProcess, CallbackFunction, UserContext); | |
| return sym_register_cb(hProcess, reg_cb64to32, tmp, FALSE); | |
| } | |
| /*********************************************************************** | |
| * SymRegisterCallback64 (DBGHELP.@) | |
| */ | |
| BOOL WINAPI SymRegisterCallback64(HANDLE hProcess, | |
| PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction, | |
| ULONG64 UserContext) | |
| { | |
| TRACE("(%p, %p, %s)\n", | |
| hProcess, CallbackFunction, wine_dbgstr_longlong(UserContext)); | |
| return sym_register_cb(hProcess, CallbackFunction, UserContext, FALSE); | |
| } | |
| /*********************************************************************** | |
| * SymRegisterCallbackW64 (DBGHELP.@) | |
| */ | |
| BOOL WINAPI SymRegisterCallbackW64(HANDLE hProcess, | |
| PSYMBOL_REGISTERED_CALLBACK64 CallbackFunction, | |
| ULONG64 UserContext) | |
| { | |
| TRACE("(%p, %p, %s)\n", | |
| hProcess, CallbackFunction, wine_dbgstr_longlong(UserContext)); | |
| return sym_register_cb(hProcess, CallbackFunction, UserContext, TRUE); | |
| } | |
| /* This is imagehlp version not dbghelp !! */ | |
| static API_VERSION api_version = { 4, 0, 2, 0 }; | |
| /*********************************************************************** | |
| * ImagehlpApiVersion (DBGHELP.@) | |
| */ | |
| LPAPI_VERSION WINAPI ImagehlpApiVersion(VOID) | |
| { | |
| return &api_version; | |
| } | |
| /*********************************************************************** | |
| * ImagehlpApiVersionEx (DBGHELP.@) | |
| */ | |
| LPAPI_VERSION WINAPI ImagehlpApiVersionEx(LPAPI_VERSION AppVersion) | |
| { | |
| if (!AppVersion) return NULL; | |
| AppVersion->MajorVersion = api_version.MajorVersion; | |
| AppVersion->MinorVersion = api_version.MinorVersion; | |
| AppVersion->Revision = api_version.Revision; | |
| AppVersion->Reserved = api_version.Reserved; | |
| return AppVersion; | |
| } | |
| /****************************************************************** | |
| * ExtensionApiVersion (DBGHELP.@) | |
| */ | |
| LPEXT_API_VERSION WINAPI ExtensionApiVersion(void) | |
| { | |
| static EXT_API_VERSION eav = {5, 5, 5, 0}; | |
| return &eav; | |
| } | |
| /****************************************************************** | |
| * WinDbgExtensionDllInit (DBGHELP.@) | |
| */ | |
| void WINAPI WinDbgExtensionDllInit(PWINDBG_EXTENSION_APIS lpExtensionApis, | |
| unsigned short major, unsigned short minor) | |
| { | |
| } |