329 lines
11 KiB
C
329 lines
11 KiB
C
|
/*
|
||
|
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||
|
*/
|
||
|
|
||
|
#include "config.h"
|
||
|
|
||
|
#include "dbghelp_private.h"
|
||
|
#include "winerror.h"
|
||
|
#include "psapi.h"
|
||
|
#include "wine/debug.h"
|
||
|
|
||
|
WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
|
||
|
|
||
|
/* TODO
|
||
|
* - support for symbols' types is still partly missing
|
||
|
* + debug start/stop in functions
|
||
|
* + parameters in function prototype...
|
||
|
* + C++ support
|
||
|
* - most options (dbghelp_options) are not used (loading lines, decoration,
|
||
|
* deferring reading of module symbols, public symbols...)
|
||
|
* - (un)decoration is not handled (should make winedump's code a (.a) library
|
||
|
* and link it to winedump, and potentially to msvcrt and dbghelp (check best
|
||
|
* way not to duplicate code in msvcrt & dbghelp)
|
||
|
* - msc:
|
||
|
* + handle the debug_start & debug_end information block
|
||
|
* + get rid of MSC reading FIXME:s (lots of types are not defined)
|
||
|
* + C++ management
|
||
|
* - stabs:
|
||
|
* + we should add parameters' types to the function's signature
|
||
|
* while processing a function's parameters
|
||
|
* + should generate the func debug_{start,end} statements (black magic ?)
|
||
|
* + should identify the relay code in Wine and mark it as thunk type
|
||
|
* + C++ management
|
||
|
* - implement the callback notification mechanism
|
||
|
*/
|
||
|
|
||
|
unsigned dbghelp_options = SYMOPT_UNDNAME;
|
||
|
|
||
|
/***********************************************************************
|
||
|
* DllMain (DEBUGHLP.@)
|
||
|
*/
|
||
|
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
|
||
|
{
|
||
|
switch (fdwReason)
|
||
|
{
|
||
|
case DLL_PROCESS_ATTACH: break;
|
||
|
case DLL_PROCESS_DETACH: 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;
|
||
|
}
|
||
|
|
||
|
/******************************************************************
|
||
|
* SymSetSearchPath (DBGHELP.@)
|
||
|
*
|
||
|
*/
|
||
|
BOOL WINAPI SymSetSearchPath(HANDLE hProcess, PSTR 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 = strcpy(HeapAlloc(GetProcessHeap(), 0, strlen(searchPath) + 1),
|
||
|
searchPath);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/***********************************************************************
|
||
|
* SymGetSearchPath (DBGHELP.@)
|
||
|
*/
|
||
|
BOOL WINAPI SymGetSearchPath(HANDLE hProcess, LPSTR szSearchPath,
|
||
|
DWORD SearchPathLength)
|
||
|
{
|
||
|
struct process* pcs = process_find_by_handle(hProcess);
|
||
|
if (!pcs) return FALSE;
|
||
|
|
||
|
strncpy(szSearchPath, pcs->search_path, SearchPathLength);
|
||
|
szSearchPath[SearchPathLength - 1] = '\0';
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/******************************************************************
|
||
|
* 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 process_invade(HANDLE hProcess)
|
||
|
{
|
||
|
HMODULE hMods[256];
|
||
|
char img[256], mod[256];
|
||
|
DWORD i, sz;
|
||
|
MODULEINFO mi;
|
||
|
|
||
|
if (!EnumProcessModules(hProcess, hMods, sizeof(hMods), &sz))
|
||
|
return FALSE; /* FIXME should grow hMods */
|
||
|
|
||
|
for (i = 0; i < sz / sizeof(HMODULE); i++)
|
||
|
{
|
||
|
if (!GetModuleInformation(hProcess, hMods[i], &mi, sizeof(mi)) ||
|
||
|
!GetModuleFileNameExA(hProcess, hMods[i], img, sizeof(img)) ||
|
||
|
!GetModuleBaseNameA(hProcess, hMods[i], mod, sizeof(mod)) ||
|
||
|
!SymLoadModule(hProcess, 0, img, mod, (DWORD)mi.lpBaseOfDll, mi.SizeOfImage))
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
return sz != 0;
|
||
|
}
|
||
|
|
||
|
/******************************************************************
|
||
|
* SymInitialize (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 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 SymInitialize(HANDLE hProcess, PSTR UserSearchPath, BOOL fInvadeProcess)
|
||
|
{
|
||
|
struct process* pcs;
|
||
|
|
||
|
TRACE("(%p %s %u)\n", hProcess, debugstr_a(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 = strcpy(HeapAlloc(GetProcessHeap(), 0, strlen(UserSearchPath) + 1),
|
||
|
UserSearchPath);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
unsigned size;
|
||
|
unsigned len;
|
||
|
|
||
|
pcs->search_path = HeapAlloc(GetProcessHeap(), 0, len = MAX_PATH);
|
||
|
while ((size = GetCurrentDirectoryA(len, pcs->search_path)) >= len)
|
||
|
pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, len *= 2);
|
||
|
pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, size + 1);
|
||
|
|
||
|
len = GetEnvironmentVariableA("_NT_SYMBOL_PATH", NULL, 0);
|
||
|
if (len)
|
||
|
{
|
||
|
pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, size + 1 + len + 1);
|
||
|
pcs->search_path[size] = ';';
|
||
|
GetEnvironmentVariableA("_NT_SYMBOL_PATH", pcs->search_path + size + 1, len);
|
||
|
size += 1 + len;
|
||
|
}
|
||
|
len = GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", NULL, 0);
|
||
|
if (len)
|
||
|
{
|
||
|
pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, size + 1 + len + 1);
|
||
|
pcs->search_path[size] = ';';
|
||
|
GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_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 (fInvadeProcess)
|
||
|
{
|
||
|
pcs->dbg_hdr_addr = elf_read_wine_loader_dbg_info(pcs);
|
||
|
if (pcs->dbg_hdr_addr == 0)
|
||
|
{
|
||
|
SymCleanup(hProcess);
|
||
|
return FALSE;
|
||
|
}
|
||
|
process_invade(hProcess);
|
||
|
elf_synchronize_module_list(pcs);
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/******************************************************************
|
||
|
* 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)
|
||
|
{
|
||
|
return dbghelp_options = opts;
|
||
|
}
|
||
|
|
||
|
/******************************************************************
|
||
|
* SymGetOptions (DBGHELP.@)
|
||
|
*
|
||
|
*/
|
||
|
DWORD WINAPI SymGetOptions(void)
|
||
|
{
|
||
|
return dbghelp_options;
|
||
|
}
|
||
|
|
||
|
/******************************************************************
|
||
|
* 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;
|
||
|
|
||
|
pcs->ctx_frame = *StackFrame;
|
||
|
/* MSDN states that Context is not (no longer?) used */
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/***********************************************************************
|
||
|
* SymRegisterCallback (DBGHELP.@)
|
||
|
*/
|
||
|
BOOL WINAPI SymRegisterCallback(HANDLE hProcess,
|
||
|
PSYMBOL_REGISTERED_CALLBACK CallbackFunction,
|
||
|
PVOID UserContext)
|
||
|
{
|
||
|
FIXME("(%p, %p, %p): stub\n", hProcess, CallbackFunction, UserContext);
|
||
|
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
/* 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;
|
||
|
}
|