CVS: winex/dlls/dbghelp coff.c, NONE, 1.1 dbghelp.c, 1.4, 1.5 dbghelp.spec, 1.4, 1.5 dbghelp_private.h, NONE, 1.1 dwarf.c, NONE, 1.1 dwarf.h, NONE, 1.1 elf_module.c, NONE, 1.1 image.c, NONE, 1.1 Makefile.in, 1.1, 1.2 memory.c, NONE, 1.1 minidump.c, NONE, 1.1 module.c, NONE, 1.1 msc.c, NONE, 1.1 path.c, NONE, 1.1 pe_module.c, NONE, 1.1 source.c, NONE, 1.1 stabs.c, NONE, 1.1 stack.c, NONE, 1.1 storage.c, NONE, 1.1 symbol.c, NONE, 1.1 type.c, NONE, 1.1 wdbgexts.h, NONE, 1.1
[email protected] 30 Aug 2007 14:16:01 -0000
| Newsgroups | gmane.comp.emulators.winex.cvs |
|---|---|
| Message-ID | <[email protected]> |
Subject: winex/dlls/dbghelp coff.c,NONE,1.1 dbghelp.c,1.4,1.5 dbghelp.spec,1.4,1.5 dbghelp_private.h,NONE,1.1 dwarf.c,NONE,1.1 dwarf.h,NONE,1.1 elf_module.c,NONE,1.1 image.c,NONE,1.1 Makefile.in,1.1,1.2 memory.c,NONE,1.1 minidump.c,NONE,1.1 module.c,NONE,1.1 msc.c,NONE,1.1 path.c,NONE,1.1 pe_module.c,NONE,1.1 source.c,NONE,1.1 stabs.c,NONE,1.1 stack.c,NONE,1.1 storage.c,NONE,1.1 symbol.c,NONE,1.1 type.c,NONE,1.1 wdbgexts.h,NONE,1.1Update of /var/lib/cvsd/cvsroot/winex/dlls/dbghelp
In directory agravaine:/tmp/cvs-serv18353/dlls/dbghelp
Modified Files:
dbghelp.c dbghelp.spec Makefile.in
Added Files:
coff.c dbghelp_private.h dwarf.c dwarf.h elf_module.c image.c
memory.c minidump.c module.c msc.c path.c pe_module.c source.c
stabs.c stack.c storage.c symbol.c type.c wdbgexts.h
Log Message:
- importing the initial version of dbghelp from winehq 0.9.41. This version will not build at the moment... build fixes coming in a few moments.
--- NEW FILE: coff.c ---
/*
* Read VC++ debug information from COFF and eventually
* from PDB files.
*
* Copyright (C) 1996, Eric Youngdale.
* Copyright (C) 1999-2000, Ulrich Weigand.
* 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
*/
/*
* Note - this handles reading debug information for 32 bit applications
* that run under Windows-NT for example. I doubt that this would work well
* for 16 bit applications, but I don't think it really matters since the
* file format is different, and we should never get in here in such cases.
*
* TODO:
* Get 16 bit CV stuff working.
* Add symbol size to internal symbol table.
*/
#include "config.h"
#include "wine/port.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifndef PATH_MAX
#define PATH_MAX MAX_PATH
#endif
#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "winternl.h"
#include "wine/exception.h"
#include "wine/debug.h"
#include "dbghelp_private.h"
#include "wine/mscvpdb.h"
WINE_DEFAULT_DEBUG_CHANNEL(dbghelp_coff);
/*========================================================================
* Process COFF debug information.
*/
struct CoffFile
{
unsigned int startaddr;
unsigned int endaddr;
struct symt_compiland* compiland;
int linetab_offset;
int linecnt;
struct symt** entries;
int neps;
int neps_alloc;
};
struct CoffFileSet
{
struct CoffFile* files;
int nfiles;
int nfiles_alloc;
};
static const char* coff_get_name(const IMAGE_SYMBOL* coff_sym,
const char* coff_strtab)
{
static char namebuff[9];
const char* nampnt;
if (coff_sym->N.Name.Short)
{
memcpy(namebuff, coff_sym->N.ShortName, 8);
namebuff[8] = '\0';
nampnt = &namebuff[0];
}
else
{
nampnt = coff_strtab + coff_sym->N.Name.Long;
}
if (nampnt[0] == '_') nampnt++;
return nampnt;
}
static int coff_add_file(struct CoffFileSet* coff_files, struct module* module,
const char* filename)
{
struct CoffFile* file;
if (coff_files->nfiles + 1 >= coff_files->nfiles_alloc)
{
coff_files->nfiles_alloc += 10;
coff_files->files = (coff_files->files) ?
HeapReAlloc(GetProcessHeap(), 0, coff_files->files,
coff_files->nfiles_alloc * sizeof(struct CoffFile)) :
HeapAlloc(GetProcessHeap(), 0,
coff_files->nfiles_alloc * sizeof(struct CoffFile));
}
file = coff_files->files + coff_files->nfiles;
file->startaddr = 0xffffffff;
file->endaddr = 0;
file->compiland = symt_new_compiland(module, 0,
source_new(module, NULL, filename));
file->linetab_offset = -1;
file->linecnt = 0;
file->entries = NULL;
file->neps = file->neps_alloc = 0;
return coff_files->nfiles++;
}
static void coff_add_symbol(struct CoffFile* coff_file, struct symt* sym)
{
if (coff_file->neps + 1 >= coff_file->neps_alloc)
{
coff_file->neps_alloc += 10;
coff_file->entries = (coff_file->entries) ?
HeapReAlloc(GetProcessHeap(), 0, coff_file->entries,
coff_file->neps_alloc * sizeof(struct symt*)) :
HeapAlloc(GetProcessHeap(), 0,
coff_file->neps_alloc * sizeof(struct symt*));
}
coff_file->entries[coff_file->neps++] = sym;
}
BOOL coff_process_info(const struct msc_debug_info* msc_dbg)
{
const IMAGE_AUX_SYMBOL* aux;
const IMAGE_COFF_SYMBOLS_HEADER* coff;
const IMAGE_LINENUMBER* coff_linetab;
const IMAGE_LINENUMBER* linepnt;
const char* coff_strtab;
const IMAGE_SYMBOL* coff_sym;
const IMAGE_SYMBOL* coff_symbols;
struct CoffFileSet coff_files;
int curr_file_idx = -1;
unsigned int i;
int j;
int k;
int l;
int linetab_indx;
const char* nampnt;
int naux;
BOOL ret = FALSE;
DWORD addr;
TRACE("Processing COFF symbols...\n");
assert(sizeof(IMAGE_SYMBOL) == IMAGE_SIZEOF_SYMBOL);
assert(sizeof(IMAGE_LINENUMBER) == IMAGE_SIZEOF_LINENUMBER);
coff_files.files = NULL;
coff_files.nfiles = coff_files.nfiles_alloc = 0;
coff = (const IMAGE_COFF_SYMBOLS_HEADER*)msc_dbg->root;
coff_symbols = (const IMAGE_SYMBOL*)((const char *)coff + coff->LvaToFirstSymbol);
coff_linetab = (const IMAGE_LINENUMBER*)((const char *)coff + coff->LvaToFirstLinenumber);
coff_strtab = (const char*)(coff_symbols + coff->NumberOfSymbols);
linetab_indx = 0;
for (i = 0; i < coff->NumberOfSymbols; i++)
{
coff_sym = coff_symbols + i;
naux = coff_sym->NumberOfAuxSymbols;
if (coff_sym->StorageClass == IMAGE_SYM_CLASS_FILE)
{
curr_file_idx = coff_add_file(&coff_files, msc_dbg->module,
(const char*)(coff_sym + 1));
TRACE("New file %s\n", (const char*)(coff_sym + 1));
i += naux;
continue;
}
if (curr_file_idx < 0)
{
assert(coff_files.nfiles == 0 && coff_files.nfiles_alloc == 0);
curr_file_idx = coff_add_file(&coff_files, msc_dbg->module, "<none>");
TRACE("New file <none>\n");
}
/*
* This guy marks the size and location of the text section
* for the current file. We need to keep track of this so
* we can figure out what file the different global functions
* go with.
*/
if (coff_sym->StorageClass == IMAGE_SYM_CLASS_STATIC &&
naux != 0 && coff_sym->Type == 0 && coff_sym->SectionNumber == 1)
{
aux = (const IMAGE_AUX_SYMBOL*) (coff_sym + 1);
if (coff_files.files[curr_file_idx].linetab_offset != -1)
{
/*
* Save this so we can still get the old name.
*/
const char* fn;
fn = source_get(msc_dbg->module,
coff_files.files[curr_file_idx].compiland->source);
TRACE("Duplicating sect from %s: %x %x %x %d %d\n",
fn, aux->Section.Length,
aux->Section.NumberOfRelocations,
aux->Section.NumberOfLinenumbers,
aux->Section.Number, aux->Section.Selection);
TRACE("More sect %d %s %08x %d %d %d\n",
coff_sym->SectionNumber,
coff_get_name(coff_sym, coff_strtab),
coff_sym->Value, coff_sym->Type,
coff_sym->StorageClass, coff_sym->NumberOfAuxSymbols);
/*
* Duplicate the file entry. We have no way to describe
* multiple text sections in our current way of handling things.
*/
coff_add_file(&coff_files, msc_dbg->module, fn);
}
else
{
TRACE("New text sect from %s: %x %x %x %d %d\n",
source_get(msc_dbg->module, coff_files.files[curr_file_idx].compiland->source),
aux->Section.Length,
aux->Section.NumberOfRelocations,
aux->Section.NumberOfLinenumbers,
aux->Section.Number, aux->Section.Selection);
}
if (coff_files.files[curr_file_idx].startaddr > coff_sym->Value)
{
coff_files.files[curr_file_idx].startaddr = coff_sym->Value;
}
if (coff_files.files[curr_file_idx].endaddr < coff_sym->Value + aux->Section.Length)
{
coff_files.files[curr_file_idx].endaddr = coff_sym->Value + aux->Section.Length;
}
coff_files.files[curr_file_idx].linetab_offset = linetab_indx;
coff_files.files[curr_file_idx].linecnt = aux->Section.NumberOfLinenumbers;
linetab_indx += aux->Section.NumberOfLinenumbers;
i += naux;
continue;
}
if (coff_sym->StorageClass == IMAGE_SYM_CLASS_STATIC && naux == 0 &&
coff_sym->SectionNumber == 1)
{
DWORD base = msc_dbg->sectp[coff_sym->SectionNumber - 1].VirtualAddress;
/*
* This is a normal static function when naux == 0.
* Just register it. The current file is the correct
* one in this instance.
*/
nampnt = coff_get_name(coff_sym, coff_strtab);
TRACE("\tAdding static symbol %s\n", nampnt);
/* FIXME: was adding symbol to this_file ??? */
coff_add_symbol(&coff_files.files[curr_file_idx],
&symt_new_function(msc_dbg->module,
coff_files.files[curr_file_idx].compiland,
nampnt,
msc_dbg->module->module.BaseOfImage + base + coff_sym->Value,
0 /* FIXME */,
NULL /* FIXME */)->symt);
i += naux;
continue;
}
if (coff_sym->StorageClass == IMAGE_SYM_CLASS_EXTERNAL &&
ISFCN(coff_sym->Type) && coff_sym->SectionNumber > 0)
{
struct symt_compiland* compiland = NULL;
DWORD base = msc_dbg->sectp[coff_sym->SectionNumber - 1].VirtualAddress;
nampnt = coff_get_name(coff_sym, coff_strtab);
TRACE("%d: %s %s\n",
i, wine_dbgstr_longlong(msc_dbg->module->module.BaseOfImage + base + coff_sym->Value),
nampnt);
TRACE("\tAdding global symbol %s (sect=%s)\n",
nampnt, msc_dbg->sectp[coff_sym->SectionNumber - 1].Name);
/*
* Now we need to figure out which file this guy belongs to.
*/
for (j = 0; j < coff_files.nfiles; j++)
{
if (coff_files.files[j].startaddr <= base + coff_sym->Value
&& coff_files.files[j].endaddr > base + coff_sym->Value)
{
compiland = coff_files.files[j].compiland;
break;
}
}
if (j < coff_files.nfiles)
{
coff_add_symbol(&coff_files.files[j],
&symt_new_function(msc_dbg->module, compiland, nampnt,
msc_dbg->module->module.BaseOfImage + base + coff_sym->Value,
0 /* FIXME */, NULL /* FIXME */)->symt);
}
else
{
symt_new_function(msc_dbg->module, NULL, nampnt,
msc_dbg->module->module.BaseOfImage + base + coff_sym->Value,
0 /* FIXME */, NULL /* FIXME */);
}
i += naux;
continue;
}
if (coff_sym->StorageClass == IMAGE_SYM_CLASS_EXTERNAL &&
coff_sym->SectionNumber > 0)
{
DWORD base = msc_dbg->sectp[coff_sym->SectionNumber - 1].VirtualAddress;
/*
* Similar to above, but for the case of data symbols.
* These aren't treated as entrypoints.
*/
nampnt = coff_get_name(coff_sym, coff_strtab);
TRACE("%d: %s %s\n",
i, wine_dbgstr_longlong(msc_dbg->module->module.BaseOfImage + base + coff_sym->Value),
nampnt);
TRACE("\tAdding global data symbol %s\n", nampnt);
/*
* Now we need to figure out which file this guy belongs to.
*/
symt_new_global_variable(msc_dbg->module, NULL, nampnt, TRUE /* FIXME */,
msc_dbg->module->module.BaseOfImage + base + coff_sym->Value,
0 /* FIXME */, NULL /* FIXME */);
i += naux;
continue;
}
if (coff_sym->StorageClass == IMAGE_SYM_CLASS_STATIC && naux == 0)
{
/*
* Ignore these. They don't have anything to do with
* reality.
*/
i += naux;
continue;
}
TRACE("Skipping unknown entry '%s' %d %d %d\n",
coff_get_name(coff_sym, coff_strtab),
coff_sym->StorageClass, coff_sym->SectionNumber, naux);
/*
* For now, skip past the aux entries.
*/
i += naux;
}
if (coff_files.files != NULL)
{
/*
* OK, we now should have a list of files, and we should have a list
* of entrypoints. We need to sort the entrypoints so that we are
* able to tie the line numbers with the given functions within the
* file.
*/
for (j = 0; j < coff_files.nfiles; j++)
{
if (coff_files.files[j].entries != NULL)
{
qsort(coff_files.files[j].entries, coff_files.files[j].neps,
sizeof(struct symt*), symt_cmp_addr);
}
}
/*
* Now pick apart the line number tables, and attach the entries
* to the given functions.
*/
for (j = 0; j < coff_files.nfiles; j++)
{
l = 0;
if (coff_files.files[j].neps != 0)
{
for (k = 0; k < coff_files.files[j].linecnt; k++)
{
linepnt = coff_linetab + coff_files.files[j].linetab_offset + k;
/*
* If we have spilled onto the next entrypoint, then
* bump the counter..
*/
for (;;)
{
if (l+1 >= coff_files.files[j].neps) break;
symt_get_info(coff_files.files[j].entries[l+1], TI_GET_ADDRESS, &addr);
if (((msc_dbg->module->module.BaseOfImage + linepnt->Type.VirtualAddress) < addr))
break;
l++;
}
if (coff_files.files[j].entries[l+1]->tag == SymTagFunction)
{
/*
* Add the line number. This is always relative to the
* start of the function, so we need to subtract that offset
* first.
*/
symt_get_info(coff_files.files[j].entries[l+1], TI_GET_ADDRESS, &addr);
symt_add_func_line(msc_dbg->module, (struct symt_function*)coff_files.files[j].entries[l+1],
coff_files.files[j].compiland->source, linepnt->Linenumber,
msc_dbg->module->module.BaseOfImage + linepnt->Type.VirtualAddress - addr);
}
}
}
}
for (j = 0; j < coff_files.nfiles; j++)
{
HeapFree(GetProcessHeap(), 0, coff_files.files[j].entries);
}
HeapFree(GetProcessHeap(), 0, coff_files.files);
msc_dbg->module->module.SymType = SymCoff;
/* FIXME: we could have a finer grain here */
msc_dbg->module->module.LineNumbers = TRUE;
msc_dbg->module->module.GlobalSymbols = TRUE;
msc_dbg->module->module.TypeInfo = FALSE;
msc_dbg->module->module.SourceIndexed = TRUE;
msc_dbg->module->module.Publics = TRUE;
ret = TRUE;
}
return ret;
}
Index: dbghelp.c
===================================================================
RCS file: /var/lib/cvsd/cvsroot/winex/dlls/dbghelp/dbghelp.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -d -r1.4 -r1.5
--- dbghelp.c 31 Jul 2007 19:50:48 -0000 1.4
+++ dbghelp.c 30 Aug 2007 14:15:59 -0000 1.5
@@ -1,44 +1,636 @@
+/*
+ * 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 "winternl.h"
+#include "wdbgexts.h"
+#include "winnls.h"
+WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
-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;
-typedef BOOL (CALLBACK *PENUMLOADED_MODULES_CALLBACK) (PSTR, ULONG, ULONG,
- PVOID);
+/***********************************************************************
+ * 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 */;
-BOOL WINAPI EnumerateLoadedModules (HANDLE hProcess,
- PENUMLOADED_MODULES_CALLBACK pFunc,
- PVOID pData)
+/******************************************************************
+ * process_find_by_handle
+ *
+ */
+struct process* process_find_by_handle(HANDLE hProcess)
{
- FIXME ("(%p, %p, %p): stub!\n", (void *)hProcess, pFunc, pData);
- return FALSE;
+ struct process* p;
+
+ for (p = process_first; p && p->handle != hProcess; p = p->next);
+ if (!p) SetLastError(ERROR_INVALID_HANDLE);
+ return p;
}
-BOOL WINAPI SymInitialize(HANDLE hProcess,
- LPCSTR UserSearchPath,
- BOOL fInvadeProcess)
+/******************************************************************
+ * validate_addr64 (internal)
+ *
+ */
+BOOL validate_addr64(DWORD64 addr)
{
- FIXME("stub! {hProcess = %p, UserSearchPath = %p, fInvadeProcess = %s}\n", (void *)hProcess, UserSearchPath, fInvadeProcess ? "TRUE" : "FALSE");
+ if (addr >> 32)
+ {
+ FIXME("Unsupported address %s\n", wine_dbgstr_longlong(addr));
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
return TRUE;
}
-BOOL WINAPI SymCleanup(HANDLE hProcess){
- FIXME("stub! {hProcess = %p}\n", (void *)hProcess);
+/******************************************************************
+ * 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, LPWSTR 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, LPSTR 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(char* name, DWORD base, DWORD size, void* 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)
{
- FIXME ("stub!\n");
- return 0;
+ return dbghelp_options;
}
-DWORD WINAPI SymSetOptions (DWORD SymOptions)
+/******************************************************************
+ * 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)
{
- FIXME ("(0x%lx)L stub!\n", SymOptions);
- return 0;
}
Index: dbghelp.spec
===================================================================
RCS file: /var/lib/cvsd/cvsroot/winex/dlls/dbghelp/dbghelp.spec,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -d -r1.4 -r1.5
--- dbghelp.spec 31 Jul 2007 19:50:48 -0000 1.4
+++ dbghelp.spec 30 Aug 2007 14:15:59 -0000 1.5
@@ -1,129 +1,126 @@
-name dbghelp
-type win32
-
@ stub DbgHelpCreateUserDump
@ stub DbgHelpCreateUserDumpW
-@ stub EnumDirTree
-@ stub EnumDirTreeW
+@ stdcall EnumDirTree(long str str ptr ptr ptr)
+@ stdcall EnumDirTreeW(long wstr wstr ptr ptr ptr)
@ stdcall EnumerateLoadedModules(long ptr ptr)
-@ stub EnumerateLoadedModules64
-@ stub EnumerateLoadedModulesW64
-@ stub ExtensionApiVersion
-@ stub FindDebugInfoFile
-@ stub FindDebugInfoFileEx
+@ stdcall EnumerateLoadedModules64(long ptr ptr)
+@ stdcall EnumerateLoadedModulesW64(long ptr ptr)
+@ stdcall ExtensionApiVersion()
+@ stdcall FindDebugInfoFile(str str ptr)
+@ stdcall FindDebugInfoFileEx(str str ptr ptr ptr)
@ stub FindDebugInfoFileExW
-@ stub FindExecutableImage
-@ stub FindExecutableImageEx
-@ stub FindExecutableImageExW
+@ stdcall FindExecutableImage(str str str)
+@ stdcall FindExecutableImageEx(str str ptr ptr ptr)
+@ stdcall FindExecutableImageExW(wstr wstr ptr ptr ptr)
@ stub FindFileInPath
@ stub FindFileInSearchPath
-@ stub GetTimestampForLoadedLibrary
-@ stub ImageDirectoryEntryToData
+@ stdcall GetTimestampForLoadedLibrary(long)
+@ stdcall ImageDirectoryEntryToData(ptr long long ptr) ntdll.RtlImageDirectoryEntryToData
@ stub ImageDirectoryEntryToDataEx
-@ stub ImageNtHeader
-@ stub ImageRvaToSection
-@ stub ImageRvaToVa
-@ stub ImagehlpApiVersion
-@ stub ImagehlpApiVersionEx
-@ stub MakeSureDirectoryPathExists
-@ stub MapDebugInformation
-@ stub MiniDumpReadDumpStream
-@ stub MiniDumpWriteDump
-@ stub SearchTreeForFile
-@ stub SearchTreeForFileW
-@ stub StackWalk
-@ stub StackWalk64
+@ stdcall ImageNtHeader(ptr) ntdll.RtlImageNtHeader
+@ stdcall ImageRvaToSection(ptr ptr long) ntdll.RtlImageRvaToSection
+@ stdcall ImageRvaToVa(ptr ptr long ptr) ntdll.RtlImageRvaToVa
+@ stdcall ImagehlpApiVersion()
+@ stdcall ImagehlpApiVersionEx(ptr)
+@ stdcall MakeSureDirectoryPathExists(str)
+@ stdcall MapDebugInformation(long str str long)
+@ stdcall MiniDumpReadDumpStream(ptr long ptr ptr ptr)
+@ stdcall MiniDumpWriteDump(ptr long ptr long long long long)
+@ stdcall SearchTreeForFile(str str ptr)
+@ stdcall SearchTreeForFileW(wstr wstr ptr)
+@ stdcall StackWalk(long long long ptr ptr ptr ptr ptr ptr)
+@ stdcall StackWalk64(long long long ptr ptr ptr ptr ptr ptr)
@ stub SymAddSymbol
@ stub SymAddSymbolW
-@ stdcall SymCleanup(ptr) SymCleanup
-@ stub SymEnumLines
+@ stdcall SymCleanup(long)
+@ stdcall SymEnumLines(ptr double str str ptr ptr)
@ stub SymEnumLinesW
-@ stub SymEnumSourceFiles
+@ stdcall SymEnumSourceFiles(ptr double str ptr ptr)
@ stub SymEnumSourceFilesW
@ stub SymEnumSym
-@ stub SymEnumSymbols
-@ stub SymEnumSymbolsW
+@ stdcall SymEnumSymbols(ptr double str ptr ptr)
+@ stdcall SymEnumSymbolsW(ptr double wstr ptr ptr)
@ stub SymEnumSymbolsForAddr
@ stub SymEnumSymbolsForAddrW
-@ stub SymEnumTypes
-@ stub SymEnumTypesW
-@ stub SymEnumerateModules
-@ stub SymEnumerateModules64
-@ stub SymEnumerateModulesW64
-@ stub SymEnumerateSymbols
+@ stdcall SymEnumTypes(ptr double ptr ptr)
+@ stdcall SymEnumTypesW(ptr double ptr ptr)
+@ stdcall SymEnumerateModules(long ptr ptr)
+@ stdcall SymEnumerateModules64(long ptr ptr)
+@ stdcall SymEnumerateModulesW64(long ptr ptr)
+@ stdcall SymEnumerateSymbols(long long ptr ptr)
@ stub SymEnumerateSymbols64
@ stub SymEnumerateSymbolsW
@ stub SymEnumerateSymbolsW64
@ stub SymFindDebugInfoFile
@ stub SymFindDebugInfoFileW
-@ stub SymFindFileInPath
-@ stub SymFindFileInPathW
-@ stub SymFromAddr
-@ stub SymFromAddrW
+@ stdcall SymFindFileInPath(long str str ptr long long long ptr ptr ptr)
+@ stdcall SymFindFileInPathW(long wstr wstr ptr long long long ptr ptr ptr)
+@ stdcall SymFromAddr(ptr double ptr ptr)
+@ stdcall SymFromAddrW(ptr double ptr ptr)
@ stub SymFromIndex
@ stub SymFromIndexW
-@ stub SymFromName
+@ stdcall SymFromName(long str ptr)
@ stub SymFromNameW
@ stub SymFromToken
@ stub SymFromTokenW
-@ stub SymFunctionTableAccess
-@ stub SymFunctionTableAccess64
+@ stdcall SymFunctionTableAccess(long long)
+@ stdcall SymFunctionTableAccess64(long double)
@ stub SymGetFileLineOffsets64
@ stub SymGetHomeDirectory
@ stub SymGetHomeDirectoryW
-@ stub SymGetLineFromAddr
-@ stub SymGetLineFromAddr64
-@ stub SymGetLineFromAddrW64
+@ stdcall SymGetLineFromAddr(long long ptr ptr)
+@ stdcall SymGetLineFromAddr64(long double ptr ptr)
+@ stdcall SymGetLineFromAddrW64(long double ptr ptr)
@ stub SymGetLineFromName
@ stub SymGetLineFromName64
-@ stub SymGetLineNext
-@ stub SymGetLineNext64
+@ stdcall SymGetLineNext(long ptr)
+@ stdcall SymGetLineNext64(long ptr)
@ stub SymGetLineNextW64
-@ stub SymGetLinePrev
-@ stub SymGetLinePrev64
+@ stdcall SymGetLinePrev(long ptr)
+@ stdcall SymGetLinePrev64(long ptr)
@ stub SymGetLinePrevW64
-@ stub SymGetModuleBase
-@ stub SymGetModuleBase64
-@ stub SymGetModuleInfo
-@ stub SymGetModuleInfo64
-@ stub SymGetModuleInfoW
-@ stub SymGetModuleInfoW64
+@ stdcall SymGetModuleBase(long long)
+@ stdcall SymGetModuleBase64(long double)
+@ stdcall SymGetModuleInfo(long long ptr)
+@ stdcall SymGetModuleInfo64(long double ptr)
+@ stdcall SymGetModuleInfoW(long long ptr)
+@ stdcall SymGetModuleInfoW64(long double ptr)
@ stub SymGetOmapBlockBase
@ stdcall SymGetOptions()
@ stub SymGetScope
@ stub SymGetScopeW
-@ stub SymGetSearchPath
-@ stub SymGetSearchPathW
+@ stdcall SymGetSearchPath(long ptr long)
+@ stdcall SymGetSearchPathW(long ptr long)
@ stub SymGetSourceFileFromToken
@ stub SymGetSourceFileFromTokenW
-@ stub SymGetSourceFileToken
-@ stub SymGetSourceFileTokenW
+@ stdcall SymGetSourceFileToken(ptr double str ptr ptr)
+@ stdcall SymGetSourceFileTokenW(ptr double wstr ptr ptr)
@ stub SymGetSourceFileW
@ stub SymGetSourceVarFromToken
@ stub SymGetSourceVarFromTokenW
-@ stub SymGetSymFromAddr
-@ stub SymGetSymFromAddr64
-@ stub SymGetSymFromName
+@ stdcall SymGetSymFromAddr(long long ptr ptr)
+@ stdcall SymGetSymFromAddr64(long double ptr ptr)
+@ stdcall SymGetSymFromName(long str ptr)
@ stub SymGetSymFromName64
-@ stub SymGetSymNext
+@ stdcall SymGetSymNext(long ptr)
@ stub SymGetSymNext64
-@ stub SymGetSymPrev
+@ stdcall SymGetSymPrev(long ptr)
@ stub SymGetSymPrev64
@ stub SymGetSymbolFile
@ stub SymGetSymbolFileW
-@ stub SymGetTypeFromName
+@ stdcall SymGetTypeFromName(ptr double str ptr)
@ stub SymGetTypeFromNameW
-@ stub SymGetTypeInfo
+@ stdcall SymGetTypeInfo(ptr double long long ptr)
@ stub SymGetTypeInfoEx
-@ stdcall SymInitialize(ptr ptr long) SymInitialize
-@ stub SymInitializeW
-@ stub SymLoadModule
-@ stub SymLoadModule64
-@ stub SymLoadModuleEx
-@ stub SymLoadModuleExW
-@ stub SymMatchFileName
-@ stub SymMatchFileNameW
-@ stub SymMatchString
+@ stdcall SymInitialize(long str long)
+@ stdcall SymInitializeW(long wstr long)
+@ stdcall SymLoadModule(long long str str long long)
+@ stdcall SymLoadModule64(long long str str double long)
+@ stdcall SymLoadModuleEx(long long str str double long ptr long)
+@ stdcall SymLoadModuleExW(long long wstr wstr double long ptr long)
+@ stdcall SymMatchFileName(str str ptr ptr)
+@ stdcall SymMatchFileNameW(wstr wstr ptr ptr)
+@ stdcall SymMatchString(str str long)
@ stub SymMatchStringA
@ stub SymMatchStringW
@ stub SymNext
@@ -131,20 +128,20 @@
@ stub SymPrev
@ stub SymPrevW
@ stub SymRefreshModuleList
-@ stub SymRegisterCallback
-@ stub SymRegisterCallback64
-@ stub SymRegisterCallbackW64
-@ stub SymRegisterFunctionEntryCallback
-@ stub SymRegisterFunctionEntryCallback64
-@ stub SymSearch
-@ stub SymSearchW
-@ stub SymSetContext
+@ stdcall SymRegisterCallback(long ptr ptr)
+@ stdcall SymRegisterCallback64(long ptr double)
+@ stdcall SymRegisterCallbackW64(long ptr double)
+@ stdcall SymRegisterFunctionEntryCallback(ptr ptr ptr)
+@ stdcall SymRegisterFunctionEntryCallback64(ptr ptr double)
+@ stdcall SymSearch(long double long long str double ptr ptr long)
+@ stdcall SymSearchW(long double long long wstr double ptr ptr long)
+@ stdcall SymSetContext(long ptr ptr)
@ stub SymSetHomeDirectory
@ stub SymSetHomeDirectoryW
@ stdcall SymSetOptions(long)
-@ stub SymSetParentWindow
-@ stub SymSetSearchPath
-@ stub SymSetSearchPathW
+@ stdcall SymSetParentWindow(long)
+@ stdcall SymSetSearchPath(long str)
+@ stdcall SymSetSearchPathW(long wstr)
@ stub SymSetSymWithAddr64
@ stub SymSrvDeltaName
@ stub SymSrvDeltaNameW
@@ -163,14 +160,14 @@
@ stub SymSrvStoreSupplement
@ stub SymSrvStoreSupplementW
# @ stub SymSetSymWithAddr64 no longer present ??
-@ stub SymUnDName
+@ stdcall SymUnDName(ptr str long)
@ stub SymUnDName64
-@ stub SymUnloadModule
-@ stub SymUnloadModule64
-@ stub UnDecorateSymbolName
+@ stdcall SymUnloadModule(long long)
+@ stdcall SymUnloadModule64(long double)
+@ stdcall UnDecorateSymbolName(str str long long)
@ stub UnDecorateSymbolNameW
-@ stub UnmapDebugInformation
-@ stub WinDbgExtensionDllInit
+@ stdcall UnmapDebugInformation(ptr)
+@ stdcall WinDbgExtensionDllInit(ptr long long)
#@ stub block
#@ stub chksym
#@ stub dbghelp
--- NEW FILE: dbghelp_private.h ---
/*
* File dbghelp_private.h - dbghelp internal definitions
*
* Copyright (C) 1995, Alexandre Julliard
* Copyright (C) 1996, Eric Youngdale.
* Copyright (C) 1999-2000, Ulrich Weigand.
* Copyright (C) 2004-2007, 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 <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "winver.h"
#include "dbghelp.h"
#include "objbase.h"
#include "oaidl.h"
#include "winnls.h"
#include "wine/unicode.h"
#include "cvconst.h"
/* #define USE_STATS */
struct pool /* poor's man */
{
struct pool_arena* first;
unsigned arena_size;
};
void pool_init(struct pool* a, unsigned arena_size);
void pool_destroy(struct pool* a);
void* pool_alloc(struct pool* a, unsigned len);
char* pool_strdup(struct pool* a, const char* str);
struct vector
{
void** buckets;
unsigned elt_size;
unsigned shift;
unsigned num_elts;
unsigned num_buckets;
unsigned buckets_allocated;
};
void vector_init(struct vector* v, unsigned elt_sz, unsigned bucket_sz);
unsigned vector_length(const struct vector* v);
void* vector_at(const struct vector* v, unsigned pos);
void* vector_add(struct vector* v, struct pool* pool);
struct sparse_array
{
struct vector key2index;
struct vector elements;
};
void sparse_array_init(struct sparse_array* sa, unsigned elt_sz, unsigned bucket_sz);
void* sparse_array_find(const struct sparse_array* sa, unsigned long idx);
void* sparse_array_add(struct sparse_array* sa, unsigned long key, struct pool* pool);
unsigned sparse_array_length(const struct sparse_array* sa);
struct hash_table_elt
{
const char* name;
struct hash_table_elt* next;
};
struct hash_table
{
unsigned num_elts;
unsigned num_buckets;
struct hash_table_elt** buckets;
struct pool* pool;
};
void hash_table_init(struct pool* pool, struct hash_table* ht,
unsigned num_buckets);
void hash_table_destroy(struct hash_table* ht);
void hash_table_add(struct hash_table* ht, struct hash_table_elt* elt);
void* hash_table_find(const struct hash_table* ht, const char* name);
unsigned hash_table_hash(const char* name, unsigned num_buckets);
struct hash_table_iter
{
const struct hash_table* ht;
struct hash_table_elt* element;
int index;
int last;
};
void hash_table_iter_init(const struct hash_table* ht,
struct hash_table_iter* hti, const char* name);
void* hash_table_iter_up(struct hash_table_iter* hti);
#define GET_ENTRY(__i, __t, __f) \
((__t*)((char*)(__i) - (unsigned int)(&((__t*)0)->__f)))
extern unsigned dbghelp_options;
/* some more Wine extensions */
#define SYMOPT_WINE_WITH_ELF_MODULES 0x40000000
enum location_kind {loc_error, /* reg is the error code */
loc_absolute, /* offset is the location */
loc_register, /* reg is the location */
loc_regrel, /* [reg+offset] is the location */
loc_user, /* value is debug information dependent,
reg & offset can be used ad libidem */
};
enum location_error {loc_err_internal = -1, /* internal while computing */
loc_err_too_complex = -2, /* couldn't compute location (even at runtime) */
loc_err_out_of_scope = -3, /* variable isn't available at current address */
loc_err_cant_read = -4, /* couldn't read memory at given address */
};
struct location
{
unsigned kind : 8,
reg;
unsigned long offset;
};
struct symt
{
enum SymTagEnum tag;
};
struct symt_ht
{
struct symt symt;
struct hash_table_elt hash_elt; /* if global symbol or type */
};
/* lexical tree */
struct symt_block
{
struct symt symt;
unsigned long address;
unsigned long size;
struct symt* container; /* block, or func */
struct vector vchildren; /* sub-blocks & local variables */
};
struct symt_compiland
{
struct symt symt;
unsigned long address;
unsigned source;
struct vector vchildren; /* global variables & functions */
};
struct symt_data
{
struct symt symt;
struct hash_table_elt hash_elt; /* if global symbol */
enum DataKind kind;
struct symt* container;
struct symt* type;
union /* depends on kind */
{
/* DataIs{Global, FileStatic}:
* loc.kind is loc_absolute
* loc.offset is address
* DataIs{Local,Param}:
* with loc.kind
* loc_absolute not supported
* loc_register location is in register loc.reg
* loc_regrel location is at address loc.reg + loc.offset
* >= loc_user ask debug info provider for resolution
*/
struct location var;
/* DataIs{Member} (all values are in bits, not bytes) */
struct
{
long offset;
unsigned long length;
} member;
/* DataIsConstant */
VARIANT value;
} u;
};
struct symt_function
{
struct symt symt;
struct hash_table_elt hash_elt; /* if global symbol */
unsigned long address;
struct symt* container; /* compiland */
struct symt* type; /* points to function_signature */
unsigned long size;
struct vector vlines;
struct vector vchildren; /* locals, params, blocks, start/end, labels */
};
struct symt_function_point
{
struct symt symt; /* either SymTagFunctionDebugStart, SymTagFunctionDebugEnd, SymTagLabel */
struct symt_function* parent;
struct location loc;
const char* name; /* for labels */
};
struct symt_public
{
struct symt symt;
struct hash_table_elt hash_elt;
struct symt* container; /* compiland */
unsigned long address;
unsigned long size;
unsigned in_code : 1,
is_function : 1;
};
struct symt_thunk
{
struct symt symt;
struct hash_table_elt hash_elt;
struct symt* container; /* compiland */
unsigned long address;
unsigned long size;
THUNK_ORDINAL ordinal; /* FIXME: doesn't seem to be accessible */
};
/* class tree */
struct symt_array
{
struct symt symt;
int start;
int end;
struct symt* base_type;
struct symt* index_type;
};
struct symt_basic
{
struct symt symt;
struct hash_table_elt hash_elt;
enum BasicType bt;
unsigned long size;
};
struct symt_enum
{
struct symt symt;
const char* name;
struct vector vchildren;
};
struct symt_function_signature
{
struct symt symt;
struct symt* rettype;
struct vector vchildren;
enum CV_call_e call_conv;
};
struct symt_function_arg_type
{
struct symt symt;
struct symt* arg_type;
struct symt* container;
};
struct symt_pointer
{
struct symt symt;
struct symt* pointsto;
};
struct symt_typedef
{
struct symt symt;
struct hash_table_elt hash_elt;
struct symt* type;
};
struct symt_udt
{
struct symt symt;
struct hash_table_elt hash_elt;
enum UdtKind kind;
int size;
struct vector vchildren;
};
enum module_type
{
DMT_UNKNOWN, /* for lookup, not actually used for a module */
DMT_ELF, /* a real ELF shared module */
DMT_PE, /* a native or builtin PE module */
DMT_PDB, /* PDB file */
};
struct process;
struct module
{
IMAGEHLP_MODULEW64 module;
/* ANSI copy of module.ModuleName for efficiency */
char module_name[MAX_PATH];
struct module* next;
enum module_type type : 16;
unsigned short is_virtual : 1;
/* specific information for debug types */
struct elf_module_info* elf_info;
struct dwarf2_module_info_s*dwarf2_info;
/* memory allocation pool */
struct pool pool;
/* symbols & symbol tables */
int sortlist_valid;
unsigned num_sorttab; /* number of symbols with addresses */
struct symt_ht** addr_sorttab;
struct hash_table ht_symbols;
void (*loc_compute)(struct process* pcs,
const struct module* module,
const struct symt_function* func,
struct location* loc);
/* types */
struct hash_table ht_types;
struct vector vtypes;
/* source files */
unsigned sources_used;
unsigned sources_alloc;
char* sources;
};
struct process
{
struct process* next;
HANDLE handle;
WCHAR* search_path;
PSYMBOL_REGISTERED_CALLBACK64 reg_cb;
BOOL reg_is_unicode;
DWORD64 reg_user;
struct module* lmodules;
unsigned long dbg_hdr_addr;
IMAGEHLP_STACK_FRAME ctx_frame;
unsigned buffer_size;
void* buffer;
};
struct line_info
{
unsigned long is_first : 1,
is_last : 1,
is_source_file : 1,
line_number;
union
{
unsigned long pc_offset; /* if is_source_file isn't set */
unsigned source_file; /* if is_source_file is set */
} u;
};
struct module_pair
{
struct process* pcs;
struct module* requested; /* in: to module_get_debug() */
struct module* effective; /* out: module with debug info */
};
enum pdb_kind {PDB_JG, PDB_DS};
struct pdb_lookup
{
const char* filename;
DWORD age;
enum pdb_kind kind;
union
{
struct
{
DWORD timestamp;
struct PDB_JG_TOC* toc;
} jg;
struct
{
GUID guid;
struct PDB_DS_TOC* toc;
} ds;
} u;
};
/* dbghelp.c */
extern struct process* process_find_by_handle(HANDLE hProcess);
extern HANDLE hMsvcrt;
extern BOOL validate_addr64(DWORD64 addr);
extern BOOL pcs_callback(const struct process* pcs, ULONG action, void* data);
extern void* fetch_buffer(struct process* pcs, unsigned size);
/* elf_module.c */
#define ELF_NO_MAP ((const void*)0xffffffff)
typedef BOOL (*elf_enum_modules_cb)(const WCHAR*, unsigned long addr, void* user);
extern BOOL elf_enum_modules(HANDLE hProc, elf_enum_modules_cb, void*);
extern BOOL elf_fetch_file_info(const WCHAR* name, DWORD* base, DWORD* size, DWORD* checksum);
struct elf_file_map;
extern BOOL elf_load_debug_info(struct module* module, struct elf_file_map* fmap);
extern struct module*
elf_load_module(struct process* pcs, const WCHAR* name, unsigned long);
extern BOOL elf_read_wine_loader_dbg_info(struct process* pcs);
extern BOOL elf_synchronize_module_list(struct process* pcs);
struct elf_thunk_area;
extern int elf_is_in_thunk_area(unsigned long addr, const struct elf_thunk_area* thunks);
extern DWORD WINAPI addr_to_linear(HANDLE hProcess, HANDLE hThread, ADDRESS* addr);
/* module.c */
extern const WCHAR S_ElfW[];
extern const WCHAR S_WineLoaderW[];
extern const WCHAR S_WinePThreadW[];
extern const WCHAR S_WineKThreadW[];
extern const WCHAR S_SlashW[];
extern struct module*
module_find_by_addr(const struct process* pcs, unsigned long addr,
enum module_type type);
extern struct module*
module_find_by_name(const struct process* pcs,
const WCHAR* name);
extern struct module*
module_find_by_nameA(const struct process* pcs,
const char* name);
extern struct module*
module_is_already_loaded(const struct process* pcs,
const WCHAR* imgname);
extern BOOL module_get_debug(struct module_pair*);
extern struct module*
module_new(struct process* pcs, const WCHAR* name,
enum module_type type, BOOL virtual,
unsigned long addr, unsigned long size,
unsigned long stamp, unsigned long checksum);
extern struct module*
module_get_container(const struct process* pcs,
const struct module* inner);
extern struct module*
module_get_containee(const struct process* pcs,
const struct module* inner);
extern enum module_type
module_get_type_by_name(const WCHAR* name);
extern void module_reset_debug_info(struct module* module);
extern BOOL module_remove(struct process* pcs,
struct module* module);
extern void module_set_module(struct module* module, const WCHAR* name);
/* msc.c */
extern BOOL pe_load_debug_directory(const struct process* pcs,
struct module* module,
const BYTE* mapping,
const IMAGE_SECTION_HEADER* sectp, DWORD nsect,
const IMAGE_DEBUG_DIRECTORY* dbg, int nDbg);
extern BOOL pdb_fetch_file_info(struct pdb_lookup* pdb_lookup);
/* pe_module.c */
extern BOOL pe_load_nt_header(HANDLE hProc, DWORD base, IMAGE_NT_HEADERS* nth);
extern struct module*
pe_load_native_module(struct process* pcs, const WCHAR* name,
HANDLE hFile, DWORD base, DWORD size);
extern struct module*
pe_load_builtin_module(struct process* pcs, const WCHAR* name,
DWORD base, DWORD size);
extern BOOL pe_load_debug_info(const struct process* pcs,
struct module* module);
/* source.c */
extern unsigned source_new(struct module* module, const char* basedir, const char* source);
extern const char* source_get(const struct module* module, unsigned idx);
/* stabs.c */
extern BOOL stabs_parse(struct module* module, unsigned long load_offset,
const void* stabs, int stablen,
const char* strs, int strtablen);
/* dwarf.c */
extern BOOL dwarf2_parse(struct module* module, unsigned long load_offset,
const struct elf_thunk_area* thunks,
const unsigned char* debug, unsigned int debug_size,
const unsigned char* abbrev, unsigned int abbrev_size,
const unsigned char* str, unsigned int str_size,
const unsigned char* line, unsigned int line_size,
const unsigned char* loclist, unsigned int loclist_size);
/* symbol.c */
extern const char* symt_get_name(const struct symt* sym);
extern int symt_cmp_addr(const void* p1, const void* p2);
extern void copy_symbolW(SYMBOL_INFOW* siw, const SYMBOL_INFO* si);
extern struct symt_ht*
symt_find_nearest(struct module* module, DWORD addr);
extern struct symt_compiland*
symt_new_compiland(struct module* module, unsigned long address,
unsigned src_idx);
extern struct symt_public*
symt_new_public(struct module* module,
struct symt_compiland* parent,
const char* typename,
unsigned long address, unsigned size,
BOOL in_code, BOOL is_func);
extern struct symt_data*
symt_new_global_variable(struct module* module,
struct symt_compiland* parent,
const char* name, unsigned is_static,
unsigned long addr, unsigned long size,
struct symt* type);
extern struct symt_function*
symt_new_function(struct module* module,
struct symt_compiland* parent,
const char* name,
unsigned long addr, unsigned long size,
struct symt* type);
extern BOOL symt_normalize_function(struct module* module,
struct symt_function* func);
extern void symt_add_func_line(struct module* module,
struct symt_function* func,
unsigned source_idx, int line_num,
unsigned long offset);
extern struct symt_data*
symt_add_func_local(struct module* module,
struct symt_function* func,
enum DataKind dt, const struct location* loc,
struct symt_block* block,
struct symt* type, const char* name);
extern struct symt_block*
symt_open_func_block(struct module* module,
struct symt_function* func,
struct symt_block* block,
unsigned pc, unsigned len);
extern struct symt_block*
symt_close_func_block(struct module* module,
struct symt_function* func,
struct symt_block* block, unsigned pc);
extern struct symt_function_point*
symt_add_function_point(struct module* module,
struct symt_function* func,
enum SymTagEnum point,
const struct location* loc,
const char* name);
extern BOOL symt_fill_func_line_info(const struct module* module,
const struct symt_function* func,
DWORD addr, IMAGEHLP_LINE* line);
extern BOOL symt_get_func_line_next(const struct module* module, PIMAGEHLP_LINE line);
extern struct symt_thunk*
symt_new_thunk(struct module* module,
struct symt_compiland* parent,
const char* name, THUNK_ORDINAL ord,
unsigned long addr, unsigned long size);
extern struct symt_data*
symt_new_constant(struct module* module,
struct symt_compiland* parent,
const char* name, struct symt* type,
const VARIANT* v);
/* type.c */
extern void symt_init_basic(struct module* module);
extern BOOL symt_get_info(const struct symt* type,
IMAGEHLP_SYMBOL_TYPE_INFO req, void* pInfo);
extern struct symt_basic*
symt_new_basic(struct module* module, enum BasicType,
const char* typename, unsigned size);
extern struct symt_udt*
symt_new_udt(struct module* module, const char* typename,
unsigned size, enum UdtKind kind);
extern BOOL symt_set_udt_size(struct module* module,
struct symt_udt* type, unsigned size);
extern BOOL symt_add_udt_element(struct module* module,
struct symt_udt* udt_type,
const char* name,
struct symt* elt_type, unsigned offset,
unsigned size);
extern struct symt_enum*
symt_new_enum(struct module* module, const char* typename);
extern BOOL symt_add_enum_element(struct module* module,
struct symt_enum* enum_type,
const char* name, int value);
extern struct symt_array*
symt_new_array(struct module* module, int min, int max,
struct symt* base, struct symt* index);
extern struct symt_function_signature*
symt_new_function_signature(struct module* module,
struct symt* ret_type,
enum CV_call_e call_conv);
extern BOOL symt_add_function_signature_parameter(struct module* module,
struct symt_function_signature* sig,
struct symt* param);
extern struct symt_pointer*
symt_new_pointer(struct module* module,
struct symt* ref_type);
extern struct symt_typedef*
symt_new_typedef(struct module* module, struct symt* ref,
const char* name);
--- NEW FILE: dwarf.c ---
/*
* File dwarf.c - read dwarf2 information from the ELF modules
*
* Copyright (C) 2005, Raphael Junqueira
* Copyright (C) 2006, 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
[...2188 lines suppressed...]
comp_unit_stream = (const dwarf2_comp_unit_stream_t*) comp_unit_cursor;
comp_unit.length = *(unsigned long*) comp_unit_stream->length;
comp_unit.version = *(unsigned short*) comp_unit_stream->version;
comp_unit.abbrev_offset = *(unsigned long*) comp_unit_stream->abbrev_offset;
comp_unit.word_size = *(unsigned char*) comp_unit_stream->word_size;
dwarf2_parse_compilation_unit(section, &comp_unit, module,
thunks, comp_unit_cursor, load_offset);
comp_unit_cursor += comp_unit.length + sizeof(unsigned);
}
module->module.SymType = SymDia;
module->module.CVSig = 'D' | ('W' << 8) | ('A' << 16) | ('R' << 24);
/* FIXME: we could have a finer grain here */
module->module.GlobalSymbols = TRUE;
module->module.TypeInfo = TRUE;
module->module.SourceIndexed = TRUE;
module->module.Publics = TRUE;
return TRUE;
}
--- NEW FILE: dwarf.h ---
/*
* dwarf2 definitions
*
* Copyright (C) 2005, Raphael Junqueira
*
* 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
*/
typedef struct
{
unsigned char length[4];
unsigned char version[2];
unsigned char abbrev_offset[4];
unsigned char word_size[1];
} dwarf2_comp_unit_stream_t;
typedef struct
{
unsigned long length;
unsigned short version;
unsigned long abbrev_offset;
unsigned char word_size;
} dwarf2_comp_unit_t;
typedef struct
{
unsigned int length;
unsigned short version;
unsigned int prologue_length;
unsigned char min_insn_length;
unsigned char default_is_stmt;
int line_base;
unsigned char line_range;
unsigned char opcode_base;
} dwarf2_line_info_t;
typedef enum dwarf_tag_e
{
DW_TAG_padding = 0x00,
DW_TAG_array_type = 0x01,
DW_TAG_class_type = 0x02,
DW_TAG_entry_point = 0x03,
DW_TAG_enumeration_type = 0x04,
DW_TAG_formal_parameter = 0x05,
DW_TAG_imported_declaration = 0x08,
DW_TAG_label = 0x0a,
DW_TAG_lexical_block = 0x0b,
DW_TAG_member = 0x0d,
DW_TAG_pointer_type = 0x0f,
DW_TAG_reference_type = 0x10,
DW_TAG_compile_unit = 0x11,
DW_TAG_string_type = 0x12,
DW_TAG_structure_type = 0x13,
DW_TAG_subroutine_type = 0x15,
DW_TAG_typedef = 0x16,
DW_TAG_union_type = 0x17,
DW_TAG_unspecified_parameters = 0x18,
DW_TAG_variant = 0x19,
DW_TAG_common_block = 0x1a,
DW_TAG_common_inclusion = 0x1b,
DW_TAG_inheritance = 0x1c,
DW_TAG_inlined_subroutine = 0x1d,
DW_TAG_module = 0x1e,
DW_TAG_ptr_to_member_type = 0x1f,
DW_TAG_set_type = 0x20,
DW_TAG_subrange_type = 0x21,
DW_TAG_with_stmt = 0x22,
DW_TAG_access_declaration = 0x23,
DW_TAG_base_type = 0x24,
DW_TAG_catch_block = 0x25,
DW_TAG_const_type = 0x26,
DW_TAG_constant = 0x27,
DW_TAG_enumerator = 0x28,
DW_TAG_file_type = 0x29,
DW_TAG_friend = 0x2a,
DW_TAG_namelist = 0x2b,
DW_TAG_namelist_item = 0x2c,
DW_TAG_packed_type = 0x2d,
DW_TAG_subprogram = 0x2e,
DW_TAG_template_type_param = 0x2f,
DW_TAG_template_value_param = 0x30,
DW_TAG_thrown_type = 0x31,
DW_TAG_try_block = 0x32,
DW_TAG_variant_part = 0x33,
DW_TAG_variable = 0x34,
DW_TAG_volatile_type = 0x35,
/** extensions */
DW_TAG_MIPS_loop = 0x4081,
DW_TAG_format_label = 0x4101,
DW_TAG_function_template = 0x4102,
DW_TAG_class_template = 0x4103
} dwarf_tag_t;
typedef enum dwarf_attribute_e
{
DW_AT_sibling = 0x01,
DW_AT_location = 0x02,
DW_AT_name = 0x03,
DW_AT_ordering = 0x09,
DW_AT_subscr_data = 0x0a,
DW_AT_byte_size = 0x0b,
DW_AT_bit_offset = 0x0c,
DW_AT_bit_size = 0x0d,
DW_AT_element_list = 0x0f,
DW_AT_stmt_list = 0x10,
DW_AT_low_pc = 0x11,
DW_AT_high_pc = 0x12,
DW_AT_language = 0x13,
DW_AT_member = 0x14,
DW_AT_discr = 0x15,
DW_AT_discr_value = 0x16,
DW_AT_visibility = 0x17,
DW_AT_import = 0x18,
DW_AT_string_length = 0x19,
DW_AT_common_reference = 0x1a,
DW_AT_comp_dir = 0x1b,
DW_AT_const_value = 0x1c,
DW_AT_containing_type = 0x1d,
DW_AT_default_value = 0x1e,
DW_AT_inline = 0x20,
DW_AT_is_optional = 0x21,
DW_AT_lower_bound = 0x22,
DW_AT_producer = 0x25,
DW_AT_prototyped = 0x27,
DW_AT_return_addr = 0x2a,
DW_AT_start_scope = 0x2c,
DW_AT_stride_size = 0x2e,
DW_AT_upper_bound = 0x2f,
DW_AT_abstract_origin = 0x31,
DW_AT_accessibility = 0x32,
DW_AT_address_class = 0x33,
DW_AT_artificial = 0x34,
DW_AT_base_types = 0x35,
DW_AT_calling_convention = 0x36,
DW_AT_count = 0x37,
DW_AT_data_member_location = 0x38,
DW_AT_decl_column = 0x39,
DW_AT_decl_file = 0x3a,
DW_AT_decl_line = 0x3b,
DW_AT_declaration = 0x3c,
DW_AT_discr_list = 0x3d,
DW_AT_encoding = 0x3e,
DW_AT_external = 0x3f,
DW_AT_frame_base = 0x40,
DW_AT_friend = 0x41,
DW_AT_identifier_case = 0x42,
DW_AT_macro_info = 0x43,
DW_AT_namelist_items = 0x44,
DW_AT_priority = 0x45,
DW_AT_segment = 0x46,
DW_AT_specification = 0x47,
DW_AT_static_link = 0x48,
DW_AT_type = 0x49,
DW_AT_use_location = 0x4a,
DW_AT_variable_parameter = 0x4b,
DW_AT_virtuality = 0x4c,
DW_AT_vtable_elem_location = 0x4d,
DW_AT_ranges = 0x55,
/* extensions */
DW_AT_MIPS_fde = 0x2001,
DW_AT_MIPS_loop_begin = 0x2002,
DW_AT_MIPS_tail_loop_begin = 0x2003,
DW_AT_MIPS_epilog_begin = 0x2004,
DW_AT_MIPS_loop_unroll_factor = 0x2005,
DW_AT_MIPS_software_pipeline_depth = 0x2006,
DW_AT_MIPS_linkage_name = 0x2007,
DW_AT_MIPS_stride = 0x2008,
DW_AT_MIPS_abstract_name = 0x2009,
DW_AT_MIPS_clone_origin = 0x200a,
DW_AT_MIPS_has_inlines = 0x200b,
DW_AT_sf_names = 0x2101,
DW_AT_src_info = 0x2102,
DW_AT_mac_info = 0x2103,
DW_AT_src_coords = 0x2104,
DW_AT_body_begin = 0x2105,
DW_AT_body_end = 0x2106
} dwarf_attribute_t;
typedef enum dwarf_form_e
{
DW_FORM_addr = 0x01,
DW_FORM_block2 = 0x03,
DW_FORM_block4 = 0x04,
DW_FORM_data2 = 0x05,
DW_FORM_data4 = 0x06,
DW_FORM_data8 = 0x07,
DW_FORM_string = 0x08,
DW_FORM_block = 0x09,
DW_FORM_block1 = 0x0a,
DW_FORM_data1 = 0x0b,
DW_FORM_flag = 0x0c,
DW_FORM_sdata = 0x0d,
DW_FORM_strp = 0x0e,
DW_FORM_udata = 0x0f,
DW_FORM_ref_addr = 0x10,
DW_FORM_ref1 = 0x11,
DW_FORM_ref2 = 0x12,
DW_FORM_ref4 = 0x13,
DW_FORM_ref8 = 0x14,
DW_FORM_ref_udata = 0x15,
DW_FORM_indirect = 0x16
} dwarf_form_t;
/** type encoding */
typedef enum dwarf_type_e
{
DW_ATE_void = 0x0,
DW_ATE_address = 0x1,
DW_ATE_boolean = 0x2,
DW_ATE_complex_float = 0x3,
DW_ATE_float = 0x4,
DW_ATE_signed = 0x5,
DW_ATE_signed_char = 0x6,
DW_ATE_unsigned = 0x7,
DW_ATE_unsigned_char = 0x8
} dwarf_type_t;
typedef enum dwarf_operation_e
{
DW_OP_addr = 0x03,
DW_OP_deref = 0x06,
DW_OP_const1u = 0x08,
DW_OP_const1s = 0x09,
DW_OP_const2u = 0x0a,
DW_OP_const2s = 0x0b,
DW_OP_const4u = 0x0c,
DW_OP_const4s = 0x0d,
DW_OP_const8u = 0x0e,
DW_OP_const8s = 0x0f,
DW_OP_constu = 0x10,
DW_OP_consts = 0x11,
DW_OP_dup = 0x12,
DW_OP_drop = 0x13,
DW_OP_over = 0x14,
DW_OP_pick = 0x15,
DW_OP_swap = 0x16,
DW_OP_rot = 0x17,
DW_OP_xderef = 0x18,
DW_OP_abs = 0x19,
DW_OP_and = 0x1a,
DW_OP_div = 0x1b,
DW_OP_minus = 0x1c,
DW_OP_mod = 0x1d,
DW_OP_mul = 0x1e,
DW_OP_neg = 0x1f,
DW_OP_not = 0x20,
DW_OP_or = 0x21,
DW_OP_plus = 0x22,
DW_OP_plus_uconst = 0x23,
DW_OP_shl = 0x24,
DW_OP_shr = 0x25,
DW_OP_shra = 0x26,
DW_OP_xor = 0x27,
DW_OP_bra = 0x28,
DW_OP_eq = 0x29,
DW_OP_ge = 0x2a,
DW_OP_gt = 0x2b,
DW_OP_le = 0x2c,
DW_OP_lt = 0x2d,
DW_OP_ne = 0x2e,
DW_OP_skip = 0x2f,
DW_OP_lit0 = 0x30,
DW_OP_lit1 = 0x31,
DW_OP_lit2 = 0x32,
DW_OP_lit3 = 0x33,
DW_OP_lit4 = 0x34,
DW_OP_lit5 = 0x35,
DW_OP_lit6 = 0x36,
DW_OP_lit7 = 0x37,
DW_OP_lit8 = 0x38,
DW_OP_lit9 = 0x39,
DW_OP_lit10 = 0x3a,
DW_OP_lit11 = 0x3b,
DW_OP_lit12 = 0x3c,
DW_OP_lit13 = 0x3d,
DW_OP_lit14 = 0x3e,
DW_OP_lit15 = 0x3f,
DW_OP_lit16 = 0x40,
DW_OP_lit17 = 0x41,
DW_OP_lit18 = 0x42,
DW_OP_lit19 = 0x43,
DW_OP_lit20 = 0x44,
DW_OP_lit21 = 0x45,
DW_OP_lit22 = 0x46,
DW_OP_lit23 = 0x47,
DW_OP_lit24 = 0x48,
DW_OP_lit25 = 0x49,
DW_OP_lit26 = 0x4a,
DW_OP_lit27 = 0x4b,
DW_OP_lit28 = 0x4c,
DW_OP_lit29 = 0x4d,
DW_OP_lit30 = 0x4e,
DW_OP_lit31 = 0x4f,
DW_OP_reg0 = 0x50,
DW_OP_reg1 = 0x51,
DW_OP_reg2 = 0x52,
DW_OP_reg3 = 0x53,
DW_OP_reg4 = 0x54,
DW_OP_reg5 = 0x55,
DW_OP_reg6 = 0x56,
DW_OP_reg7 = 0x57,
DW_OP_reg8 = 0x58,
DW_OP_reg9 = 0x59,
DW_OP_reg10 = 0x5a,
DW_OP_reg11 = 0x5b,
DW_OP_reg12 = 0x5c,
DW_OP_reg13 = 0x5d,
DW_OP_reg14 = 0x5e,
DW_OP_reg15 = 0x5f,
DW_OP_reg16 = 0x60,
DW_OP_reg17 = 0x61,
DW_OP_reg18 = 0x62,
DW_OP_reg19 = 0x63,
DW_OP_reg20 = 0x64,
DW_OP_reg21 = 0x65,
DW_OP_reg22 = 0x66,
DW_OP_reg23 = 0x67,
DW_OP_reg24 = 0x68,
DW_OP_reg25 = 0x69,
DW_OP_reg26 = 0x6a,
DW_OP_reg27 = 0x6b,
DW_OP_reg28 = 0x6c,
DW_OP_reg29 = 0x6d,
DW_OP_reg30 = 0x6e,
DW_OP_reg31 = 0x6f,
DW_OP_breg0 = 0x70,
DW_OP_breg1 = 0x71,
DW_OP_breg2 = 0x72,
DW_OP_breg3 = 0x73,
DW_OP_breg4 = 0x74,
DW_OP_breg5 = 0x75,
DW_OP_breg6 = 0x76,
DW_OP_breg7 = 0x77,
DW_OP_breg8 = 0x78,
DW_OP_breg9 = 0x79,
DW_OP_breg10 = 0x7a,
DW_OP_breg11 = 0x7b,
DW_OP_breg12 = 0x7c,
DW_OP_breg13 = 0x7d,
DW_OP_breg14 = 0x7e,
DW_OP_breg15 = 0x7f,
DW_OP_breg16 = 0x80,
DW_OP_breg17 = 0x81,
DW_OP_breg18 = 0x82,
DW_OP_breg19 = 0x83,
DW_OP_breg20 = 0x84,
DW_OP_breg21 = 0x85,
DW_OP_breg22 = 0x86,
DW_OP_breg23 = 0x87,
DW_OP_breg24 = 0x88,
DW_OP_breg25 = 0x89,
DW_OP_breg26 = 0x8a,
DW_OP_breg27 = 0x8b,
DW_OP_breg28 = 0x8c,
DW_OP_breg29 = 0x8d,
DW_OP_breg30 = 0x8e,
DW_OP_breg31 = 0x8f,
DW_OP_regx = 0x90,
DW_OP_fbreg = 0x91,
DW_OP_bregx = 0x92,
DW_OP_piece = 0x93,
DW_OP_deref_size = 0x94,
DW_OP_xderef_size = 0x95,
DW_OP_nop = 0x96
} dwarf_operation_t;
enum dwarf_calling_convention
{
DW_CC_normal = 0x1,
DW_CC_program = 0x2,
DW_CC_nocall = 0x3
};
#define DW_CC_lo_user 0x40
#define DW_CC_hi_user 0xff
#define DW_LNS_extended_op 0x00
#define DW_LNS_copy 0x01
#define DW_LNS_advance_pc 0x02
#define DW_LNS_advance_line 0x03
#define DW_LNS_set_file 0x04
#define DW_LNS_set_column 0x05
#define DW_LNS_negate_stmt 0x06
#define DW_LNS_set_basic_block 0x07
#define DW_LNS_const_add_pc 0x08
#define DW_LNS_fixed_advance_pc 0x09
#define DW_LNE_end_sequence 0x01
#define DW_LNE_set_address 0x02
#define DW_LNE_define_file 0x03
--- NEW FILE: elf_module.c ---
/*
* File elf.c - processing of ELF files
*
* Copyright (C) 1996, Eric Youngdale.
* 1999-2007 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
[...1622 lines suppressed...]
{
return FALSE;
}
struct module* elf_load_module(struct process* pcs, const WCHAR* name, unsigned long addr)
{
return NULL;
}
BOOL elf_load_debug_info(struct module* module, struct elf_file_map* fmap)
{
return FALSE;
}
int elf_is_in_thunk_area(unsigned long addr,
const struct elf_thunk_area* thunks)
{
return -1;
}
#endif /* __ELF__ */
--- NEW FILE: image.c ---
/*
* File image.c - managing images
*
* 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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "dbghelp_private.h"
#include "winternl.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
/***********************************************************************
* GetTimestampForLoadedLibrary (DBGHELP.@)
*/
DWORD WINAPI GetTimestampForLoadedLibrary(HMODULE Module)
{
IMAGE_NT_HEADERS* nth = RtlImageNtHeader(Module);
return (nth) ? nth->FileHeader.TimeDateStamp : 0;
}
/***********************************************************************
* MapDebugInformation (DBGHELP.@)
*/
PIMAGE_DEBUG_INFORMATION WINAPI MapDebugInformation(HANDLE FileHandle, LPSTR FileName,
LPSTR SymbolPath, DWORD ImageBase)
{
FIXME("(%p, %s, %s, 0x%08x): stub\n", FileHandle, FileName, SymbolPath, ImageBase);
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return NULL;
}
/***********************************************************************
* UnmapDebugInformation (DBGHELP.@)
*/
BOOL WINAPI UnmapDebugInformation(PIMAGE_DEBUG_INFORMATION DebugInfo)
{
FIXME("(%p): stub\n", DebugInfo);
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return FALSE;
}
Index: Makefile.in
===================================================================
RCS file: /var/lib/cvsd/cvsroot/winex/dlls/dbghelp/Makefile.in,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- Makefile.in 30 Mar 2007 19:52:41 -0000 1.1
+++ Makefile.in 30 Aug 2007 14:15:59 -0000 1.2
@@ -2,11 +2,29 @@
TOPOBJDIR = ../..
SRCDIR = @srcdir@
VPATH = @srcdir@
-MODULE = dbghelp
-
-LDDLLFLAGS = @LDDLLFLAGS@
-SYMBOLFILE = $(MODULE).tmp.o
+MODULE = dbghelp.dll
+IMPORTLIB = libdbghelp.$(IMPLIBEXT)
+IMPORTS = psapi kernel32 ntdll
-C_SRCS = dbghelp.c
+C_SRCS = \
+ coff.c \
+ dbghelp.c \
+ dwarf.c \
+ elf_module.c \
+ image.c \
+ memory.c \
+ minidump.c \
+ module.c \
+ msc.c \
+ path.c \
+ pe_module.c \
+ source.c \
+ stabs.c \
+ stack.c \
+ storage.c \
+ symbol.c \
+ type.c
@MAKE_DLL_RULES@
+
+@DEPENDENCIES@ # everything below this line is overwritten by make depend
--- NEW FILE: memory.c ---
/*
* File memory.c - managing memory
*
* 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 <assert.h>
#include "dbghelp_private.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
/******************************************************************
* addr_to_linear
*
* converts an address into its linear value
*/
DWORD WINAPI addr_to_linear(HANDLE hProcess, HANDLE hThread, ADDRESS* addr)
{
LDT_ENTRY le;
switch (addr->Mode)
{
case AddrMode1616:
if (GetThreadSelectorEntry(hThread, addr->Segment, &le))
return (le.HighWord.Bits.BaseHi << 24) +
(le.HighWord.Bits.BaseMid << 16) + le.BaseLow + LOWORD(addr->Offset);
break;
case AddrMode1632:
if (GetThreadSelectorEntry(hThread, addr->Segment, &le))
return (le.HighWord.Bits.BaseHi << 24) +
(le.HighWord.Bits.BaseMid << 16) + le.BaseLow + addr->Offset;
break;
case AddrModeReal:
return (DWORD)(LOWORD(addr->Segment) << 4) + addr->Offset;
case AddrModeFlat:
return addr->Offset;
default:
FIXME("Unsupported (yet) mode (%x)\n", addr->Mode);
return 0;
}
FIXME("Failed to linearize address %04x:%08x (mode %x)\n",
addr->Segment, addr->Offset, addr->Mode);
return 0;
}
--- NEW FILE: minidump.c ---
/*
* File minidump.c - management of dumps (read & write)
*
* Copyright (C) 2004-2005, 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 <time.h>
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
#include "ntstatus.h"
#define WIN32_NO_STATUS
#include "dbghelp_private.h"
#include "winternl.h"
#include "psapi.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
struct dump_memory
{
ULONG base;
ULONG size;
ULONG rva;
};
struct dump_module
{
unsigned is_elf;
ULONG base;
ULONG size;
DWORD timestamp;
DWORD checksum;
WCHAR name[MAX_PATH];
};
struct dump_context
{
/* process & thread information */
HANDLE hProcess;
DWORD pid;
void* pcs_buffer;
SYSTEM_PROCESS_INFORMATION* spi;
/* module information */
struct dump_module* module;
unsigned num_module;
/* exception information */
/* output information */
MINIDUMP_TYPE type;
HANDLE hFile;
RVA rva;
struct dump_memory* mem;
unsigned num_mem;
/* callback information */
MINIDUMP_CALLBACK_INFORMATION* cb;
};
/******************************************************************
* fetch_process_info
*
* reads system wide process information, and make spi point to the record
* for process of id 'pid'
*/
static BOOL fetch_process_info(struct dump_context* dc)
{
ULONG buf_size = 0x1000;
NTSTATUS nts;
dc->pcs_buffer = NULL;
if (!(dc->pcs_buffer = HeapAlloc(GetProcessHeap(), 0, buf_size))) return FALSE;
for (;;)
{
nts = NtQuerySystemInformation(SystemProcessInformation,
dc->pcs_buffer, buf_size, NULL);
if (nts != STATUS_INFO_LENGTH_MISMATCH) break;
dc->pcs_buffer = HeapReAlloc(GetProcessHeap(), 0, dc->pcs_buffer,
buf_size *= 2);
if (!dc->pcs_buffer) return FALSE;
}
if (nts == STATUS_SUCCESS)
{
dc->spi = dc->pcs_buffer;
for (;;)
{
if (dc->spi->dwProcessID == dc->pid) return TRUE;
if (!dc->spi->dwOffset) break;
dc->spi = (SYSTEM_PROCESS_INFORMATION*)
((char*)dc->spi + dc->spi->dwOffset);
}
}
HeapFree(GetProcessHeap(), 0, dc->pcs_buffer);
dc->pcs_buffer = NULL;
dc->spi = NULL;
return FALSE;
}
static void fetch_thread_stack(struct dump_context* dc, const void* teb_addr,
const CONTEXT* ctx, MINIDUMP_MEMORY_DESCRIPTOR* mmd)
{
NT_TIB tib;
if (ReadProcessMemory(dc->hProcess, teb_addr, &tib, sizeof(tib), NULL))
{
#ifdef __i386__
/* limiting the stack dumping to the size actually used */
if (ctx->Esp)
mmd->StartOfMemoryRange = (ctx->Esp - 4);
else
mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
#elif defined(__powerpc__)
if (ctx->Iar)
mmd->StartOfMemoryRange = ctx->Iar - 4;
else
mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
#elif defined(__x86_64__)
if (ctx->Rsp)
mmd->StartOfMemoryRange = (ctx->Rsp - 8);
else
mmd->StartOfMemoryRange = (ULONG_PTR)tib.StackLimit;
#else
#error unsupported CPU
#endif
mmd->Memory.DataSize = (ULONG_PTR)tib.StackBase - mmd->StartOfMemoryRange;
}
}
/******************************************************************
* fetch_thread_info
*
* fetches some information about thread of id 'tid'
*/
static BOOL fetch_thread_info(struct dump_context* dc, int thd_idx,
const MINIDUMP_EXCEPTION_INFORMATION* except,
MINIDUMP_THREAD* mdThd, CONTEXT* ctx)
{
DWORD tid = dc->spi->ti[thd_idx].dwThreadID;
HANDLE hThread;
THREAD_BASIC_INFORMATION tbi;
memset(ctx, 0, sizeof(*ctx));
mdThd->ThreadId = dc->spi->ti[thd_idx].dwThreadID;
mdThd->SuspendCount = 0;
mdThd->Teb = 0;
mdThd->Stack.StartOfMemoryRange = 0;
mdThd->Stack.Memory.DataSize = 0;
mdThd->Stack.Memory.Rva = 0;
mdThd->ThreadContext.DataSize = 0;
mdThd->ThreadContext.Rva = 0;
mdThd->PriorityClass = dc->spi->ti[thd_idx].dwBasePriority; /* FIXME */
mdThd->Priority = dc->spi->ti[thd_idx].dwCurrentPriority;
if ((hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, tid)) == NULL)
{
FIXME("Couldn't open thread %u (%u)\n",
dc->spi->ti[thd_idx].dwThreadID, GetLastError());
return FALSE;
}
if (NtQueryInformationThread(hThread, ThreadBasicInformation,
&tbi, sizeof(tbi), NULL) == STATUS_SUCCESS)
{
mdThd->Teb = (ULONG_PTR)tbi.TebBaseAddress;
if (tbi.ExitStatus == STILL_ACTIVE)
{
if (tid != GetCurrentThreadId() &&
(mdThd->SuspendCount = SuspendThread(hThread)) != (DWORD)-1)
{
mdThd->SuspendCount--;
ctx->ContextFlags = CONTEXT_FULL;
if (!GetThreadContext(hThread, ctx))
memset(ctx, 0, sizeof(*ctx));
fetch_thread_stack(dc, tbi.TebBaseAddress, ctx, &mdThd->Stack);
ResumeThread(hThread);
}
else if (tid == GetCurrentThreadId() && except)
{
CONTEXT lctx, *pctx;
if (except->ClientPointers)
{
EXCEPTION_POINTERS ep;
ReadProcessMemory(dc->hProcess, except->ExceptionPointers,
&ep, sizeof(ep), NULL);
ReadProcessMemory(dc->hProcess, ep.ContextRecord,
&ctx, sizeof(ctx), NULL);
pctx = &lctx;
}
else pctx = except->ExceptionPointers->ContextRecord;
fetch_thread_stack(dc, tbi.TebBaseAddress, pctx, &mdThd->Stack);
}
}
}
CloseHandle(hThread);
return TRUE;
}
/******************************************************************
* add_module
*
* Add a module to a dump context
*/
static BOOL add_module(struct dump_context* dc, const WCHAR* name,
DWORD base, DWORD size, DWORD timestamp, DWORD checksum,
BOOL is_elf)
{
if (!dc->module)
dc->module = HeapAlloc(GetProcessHeap(), 0,
++dc->num_module * sizeof(*dc->module));
else
dc->module = HeapReAlloc(GetProcessHeap(), 0, dc->module,
++dc->num_module * sizeof(*dc->module));
if (!dc->module) return FALSE;
if (is_elf ||
!GetModuleFileNameExW(dc->hProcess, (HMODULE)base,
dc->module[dc->num_module - 1].name,
sizeof(dc->module[dc->num_module - 1].name) / sizeof(WCHAR)))
lstrcpynW(dc->module[dc->num_module - 1].name, name,
sizeof(dc->module[dc->num_module - 1].name) / sizeof(WCHAR));
dc->module[dc->num_module - 1].base = base;
dc->module[dc->num_module - 1].size = size;
dc->module[dc->num_module - 1].timestamp = timestamp;
dc->module[dc->num_module - 1].checksum = checksum;
dc->module[dc->num_module - 1].is_elf = is_elf;
return TRUE;
}
/******************************************************************
* fetch_pe_module_info_cb
*
* Callback for accumulating in dump_context a PE modules set
*/
static BOOL WINAPI fetch_pe_module_info_cb(WCHAR* name, DWORD64 base, DWORD size,
void* user)
{
struct dump_context* dc = (struct dump_context*)user;
IMAGE_NT_HEADERS nth;
if (!validate_addr64(base)) return FALSE;
if (pe_load_nt_header(dc->hProcess, base, &nth))
add_module((struct dump_context*)user, name, base, size,
nth.FileHeader.TimeDateStamp, nth.OptionalHeader.CheckSum,
FALSE);
return TRUE;
}
/******************************************************************
* fetch_elf_module_info_cb
*
* Callback for accumulating in dump_context an ELF modules set
*/
static BOOL fetch_elf_module_info_cb(const WCHAR* name, unsigned long base,
void* user)
{
struct dump_context* dc = (struct dump_context*)user;
DWORD rbase, size, checksum;
/* FIXME: there's no relevant timestamp on ELF modules */
/* NB: if we have a non-null base from the live-target use it (whenever
* the ELF module is relocatable or not). If we have a null base (ELF
* module isn't relocatable) then grab its base address from ELF file
*/
if (!elf_fetch_file_info(name, &rbase, &size, &checksum))
size = checksum = 0;
add_module(dc, name, base ? base : rbase, size, 0 /* FIXME */, checksum, TRUE);
return TRUE;
}
static void fetch_module_info(struct dump_context* dc)
{
EnumerateLoadedModulesW64(dc->hProcess, fetch_pe_module_info_cb, dc);
/* Since we include ELF modules in a separate stream from the regular PE ones,
* we can always include those ELF modules (they don't eat lots of space)
* And it's always a good idea to have a trace of the loaded ELF modules for
* a given application in a post mortem debugging condition.
*/
elf_enum_modules(dc->hProcess, fetch_elf_module_info_cb, dc);
}
/******************************************************************
* add_memory_block
*
* Add a memory block to be dumped in a minidump
* If rva is non 0, it's the rva in the minidump where has to be stored
* also the rva of the memory block when written (this allows to reference
* a memory block from outside the list of memory blocks).
*/
static void add_memory_block(struct dump_context* dc, ULONG64 base, ULONG size, ULONG rva)
{
if (dc->mem)
dc->mem = HeapReAlloc(GetProcessHeap(), 0, dc->mem,
++dc->num_mem * sizeof(*dc->mem));
else
dc->mem = HeapAlloc(GetProcessHeap(), 0, ++dc->num_mem * sizeof(*dc->mem));
if (dc->mem)
{
dc->mem[dc->num_mem - 1].base = base;
dc->mem[dc->num_mem - 1].size = size;
dc->mem[dc->num_mem - 1].rva = rva;
}
else dc->num_mem = 0;
}
/******************************************************************
* writeat
*
* Writes a chunk of data at a given position in the minidump
*/
static void writeat(struct dump_context* dc, RVA rva, const void* data, unsigned size)
{
DWORD written;
SetFilePointer(dc->hFile, rva, NULL, FILE_BEGIN);
WriteFile(dc->hFile, data, size, &written, NULL);
}
/******************************************************************
* append
*
* writes a new chunk of data to the minidump, increasing the current
* rva in dc
*/
static void append(struct dump_context* dc, void* data, unsigned size)
{
writeat(dc, dc->rva, data, size);
dc->rva += size;
}
/******************************************************************
* dump_exception_info
*
* Write in File the exception information from pcs
*/
static void dump_exception_info(struct dump_context* dc,
const MINIDUMP_EXCEPTION_INFORMATION* except)
{
MINIDUMP_EXCEPTION_STREAM mdExcpt;
EXCEPTION_RECORD rec, *prec;
CONTEXT ctx, *pctx;
int i;
mdExcpt.ThreadId = except->ThreadId;
mdExcpt.__alignment = 0;
if (except->ClientPointers)
{
EXCEPTION_POINTERS ep;
ReadProcessMemory(dc->hProcess,
except->ExceptionPointers, &ep, sizeof(ep), NULL);
ReadProcessMemory(dc->hProcess,
ep.ExceptionRecord, &rec, sizeof(rec), NULL);
ReadProcessMemory(dc->hProcess,
ep.ContextRecord, &ctx, sizeof(ctx), NULL);
prec = &rec;
pctx = &ctx;
}
else
{
prec = except->ExceptionPointers->ExceptionRecord;
pctx = except->ExceptionPointers->ContextRecord;
}
mdExcpt.ExceptionRecord.ExceptionCode = prec->ExceptionCode;
mdExcpt.ExceptionRecord.ExceptionFlags = prec->ExceptionFlags;
mdExcpt.ExceptionRecord.ExceptionRecord = (DWORD_PTR)prec->ExceptionRecord;
mdExcpt.ExceptionRecord.ExceptionAddress = (DWORD_PTR)prec->ExceptionAddress;
mdExcpt.ExceptionRecord.NumberParameters = prec->NumberParameters;
mdExcpt.ExceptionRecord.__unusedAlignment = 0;
for (i = 0; i < mdExcpt.ExceptionRecord.NumberParameters; i++)
mdExcpt.ExceptionRecord.ExceptionInformation[i] = (DWORD_PTR)prec->ExceptionInformation[i];
mdExcpt.ThreadContext.DataSize = sizeof(*pctx);
mdExcpt.ThreadContext.Rva = dc->rva + sizeof(mdExcpt);
append(dc, &mdExcpt, sizeof(mdExcpt));
append(dc, pctx, sizeof(*pctx));
}
/******************************************************************
* dump_modules
*
* Write in File the modules from pcs
*/
static void dump_modules(struct dump_context* dc, BOOL dump_elf)
{
MINIDUMP_MODULE mdModule;
MINIDUMP_MODULE_LIST mdModuleList;
char tmp[1024];
MINIDUMP_STRING* ms = (MINIDUMP_STRING*)tmp;
ULONG i, nmod;
RVA rva_base;
DWORD flags_out;
for (i = nmod = 0; i < dc->num_module; i++)
{
if ((dc->module[i].is_elf && dump_elf) ||
(!dc->module[i].is_elf && !dump_elf))
nmod++;
}
mdModuleList.NumberOfModules = 0;
/* reserve space for mdModuleList
* FIXME: since we don't support 0 length arrays, we cannot use the
* size of mdModuleList
* FIXME: if we don't ask for all modules in cb, we'll get a hole in the file
*/
rva_base = dc->rva;
dc->rva += sizeof(mdModuleList.NumberOfModules) + sizeof(mdModule) * nmod;
for (i = 0; i < dc->num_module; i++)
{
if ((dc->module[i].is_elf && !dump_elf) ||
(!dc->module[i].is_elf && dump_elf))
continue;
flags_out = ModuleWriteModule | ModuleWriteMiscRecord | ModuleWriteCvRecord;
if (dc->type & MiniDumpWithDataSegs)
flags_out |= ModuleWriteDataSeg;
if (dc->type & MiniDumpWithProcessThreadData)
flags_out |= ModuleWriteTlsData;
if (dc->type & MiniDumpWithCodeSegs)
flags_out |= ModuleWriteCodeSegs;
ms->Length = (lstrlenW(dc->module[i].name) + 1) * sizeof(WCHAR);
if (sizeof(ULONG) + ms->Length > sizeof(tmp))
FIXME("Buffer overflow!!!\n");
lstrcpyW(ms->Buffer, dc->module[i].name);
if (dc->cb)
{
MINIDUMP_CALLBACK_INPUT cbin;
MINIDUMP_CALLBACK_OUTPUT cbout;
cbin.ProcessId = dc->pid;
cbin.ProcessHandle = dc->hProcess;
cbin.CallbackType = ModuleCallback;
cbin.u.Module.FullPath = ms->Buffer;
cbin.u.Module.BaseOfImage = dc->module[i].base;
cbin.u.Module.SizeOfImage = dc->module[i].size;
cbin.u.Module.CheckSum = dc->module[i].checksum;
cbin.u.Module.TimeDateStamp = dc->module[i].timestamp;
memset(&cbin.u.Module.VersionInfo, 0, sizeof(cbin.u.Module.VersionInfo));
cbin.u.Module.CvRecord = NULL;
cbin.u.Module.SizeOfCvRecord = 0;
cbin.u.Module.MiscRecord = NULL;
cbin.u.Module.SizeOfMiscRecord = 0;
cbout.u.ModuleWriteFlags = flags_out;
if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
continue;
flags_out &= cbout.u.ModuleWriteFlags;
}
if (flags_out & ModuleWriteModule)
{
mdModule.BaseOfImage = dc->module[i].base;
mdModule.SizeOfImage = dc->module[i].size;
mdModule.CheckSum = dc->module[i].checksum;
mdModule.TimeDateStamp = dc->module[i].timestamp;
mdModule.ModuleNameRva = dc->rva;
ms->Length -= sizeof(WCHAR);
append(dc, ms, sizeof(ULONG) + ms->Length + sizeof(WCHAR));
memset(&mdModule.VersionInfo, 0, sizeof(mdModule.VersionInfo)); /* FIXME */
mdModule.CvRecord.DataSize = 0; /* FIXME */
mdModule.CvRecord.Rva = 0; /* FIXME */
mdModule.MiscRecord.DataSize = 0; /* FIXME */
mdModule.MiscRecord.Rva = 0; /* FIXME */
mdModule.Reserved0 = 0; /* FIXME */
mdModule.Reserved1 = 0; /* FIXME */
writeat(dc,
rva_base + sizeof(mdModuleList.NumberOfModules) +
mdModuleList.NumberOfModules++ * sizeof(mdModule),
&mdModule, sizeof(mdModule));
}
}
writeat(dc, rva_base, &mdModuleList.NumberOfModules,
sizeof(mdModuleList.NumberOfModules));
}
/******************************************************************
* dump_system_info
*
* Dumps into File the information about the system
*/
static void dump_system_info(struct dump_context* dc)
{
MINIDUMP_SYSTEM_INFO mdSysInfo;
SYSTEM_INFO sysInfo;
OSVERSIONINFOW osInfo;
DWORD written;
ULONG slen;
GetSystemInfo(&sysInfo);
osInfo.dwOSVersionInfoSize = sizeof(osInfo);
GetVersionExW(&osInfo);
mdSysInfo.ProcessorArchitecture = sysInfo.u.s.wProcessorArchitecture;
mdSysInfo.ProcessorLevel = sysInfo.wProcessorLevel;
mdSysInfo.ProcessorRevision = sysInfo.wProcessorRevision;
mdSysInfo.u.s.NumberOfProcessors = sysInfo.dwNumberOfProcessors;
mdSysInfo.u.s.ProductType = VER_NT_WORKSTATION; /* FIXME */
mdSysInfo.MajorVersion = osInfo.dwMajorVersion;
mdSysInfo.MinorVersion = osInfo.dwMinorVersion;
mdSysInfo.BuildNumber = osInfo.dwBuildNumber;
mdSysInfo.PlatformId = osInfo.dwPlatformId;
mdSysInfo.CSDVersionRva = dc->rva + sizeof(mdSysInfo);
mdSysInfo.u1.Reserved1 = 0;
memset(&mdSysInfo.Cpu, 0, sizeof(mdSysInfo.Cpu));
append(dc, &mdSysInfo, sizeof(mdSysInfo));
slen = lstrlenW(osInfo.szCSDVersion) * sizeof(WCHAR);
WriteFile(dc->hFile, &slen, sizeof(slen), &written, NULL);
WriteFile(dc->hFile, osInfo.szCSDVersion, slen, &written, NULL);
dc->rva += sizeof(ULONG) + slen;
}
/******************************************************************
* dump_threads
*
* Dumps into File the information about running threads
*/
static void dump_threads(struct dump_context* dc,
const MINIDUMP_EXCEPTION_INFORMATION* except)
{
MINIDUMP_THREAD mdThd;
MINIDUMP_THREAD_LIST mdThdList;
unsigned i;
RVA rva_base;
DWORD flags_out;
CONTEXT ctx;
mdThdList.NumberOfThreads = 0;
rva_base = dc->rva;
dc->rva += sizeof(mdThdList.NumberOfThreads) +
dc->spi->dwThreadCount * sizeof(mdThd);
for (i = 0; i < dc->spi->dwThreadCount; i++)
{
fetch_thread_info(dc, i, except, &mdThd, &ctx);
flags_out = ThreadWriteThread | ThreadWriteStack | ThreadWriteContext |
ThreadWriteInstructionWindow;
if (dc->type & MiniDumpWithProcessThreadData)
flags_out |= ThreadWriteThreadData;
if (dc->type & MiniDumpWithThreadInfo)
flags_out |= ThreadWriteThreadInfo;
if (dc->cb)
{
MINIDUMP_CALLBACK_INPUT cbin;
MINIDUMP_CALLBACK_OUTPUT cbout;
cbin.ProcessId = dc->pid;
cbin.ProcessHandle = dc->hProcess;
cbin.CallbackType = ThreadCallback;
cbin.u.Thread.ThreadId = dc->spi->ti[i].dwThreadID;
cbin.u.Thread.ThreadHandle = 0; /* FIXME */
memcpy(&cbin.u.Thread.Context, &ctx, sizeof(CONTEXT));
cbin.u.Thread.SizeOfContext = sizeof(CONTEXT);
cbin.u.Thread.StackBase = mdThd.Stack.StartOfMemoryRange;
cbin.u.Thread.StackEnd = mdThd.Stack.StartOfMemoryRange +
mdThd.Stack.Memory.DataSize;
cbout.u.ThreadWriteFlags = flags_out;
if (!dc->cb->CallbackRoutine(dc->cb->CallbackParam, &cbin, &cbout))
continue;
flags_out &= cbout.u.ThreadWriteFlags;
}
if (flags_out & ThreadWriteThread)
{
if (ctx.ContextFlags && (flags_out & ThreadWriteContext))
{
mdThd.ThreadContext.Rva = dc->rva;
mdThd.ThreadContext.DataSize = sizeof(CONTEXT);
append(dc, &ctx, sizeof(CONTEXT));
}
if (mdThd.Stack.Memory.DataSize && (flags_out & ThreadWriteStack))
{
add_memory_block(dc, mdThd.Stack.StartOfMemoryRange,
mdThd.Stack.Memory.DataSize,
rva_base + sizeof(mdThdList.NumberOfThreads) +
mdThdList.NumberOfThreads * sizeof(mdThd) +
FIELD_OFFSET(MINIDUMP_THREAD, Stack.Memory.Rva));
}
writeat(dc,
rva_base + sizeof(mdThdList.NumberOfThreads) +
mdThdList.NumberOfThreads * sizeof(mdThd),
&mdThd, sizeof(mdThd));
mdThdList.NumberOfThreads++;
}
if (ctx.ContextFlags && (flags_out & ThreadWriteInstructionWindow))
{
/* FIXME: - Native dbghelp also dumps 0x80 bytes around EIP
* - also crop values across module boundaries,
* - and don't make it i386 dependent
*/
/* add_memory_block(dc, ctx.Eip - 0x80, ctx.Eip + 0x80, 0); */
}
}
writeat(dc, rva_base,
&mdThdList.NumberOfThreads, sizeof(mdThdList.NumberOfThreads));
}
/******************************************************************
* dump_memory_info
*
* dumps information about the memory of the process (stack of the threads)
*/
static void dump_memory_info(struct dump_context* dc)
{
MINIDUMP_MEMORY_LIST mdMemList;
MINIDUMP_MEMORY_DESCRIPTOR mdMem;
DWORD written;
unsigned i, pos, len;
RVA rva_base;
char tmp[1024];
mdMemList.NumberOfMemoryRanges = dc->num_mem;
append(dc, &mdMemList.NumberOfMemoryRanges,
sizeof(mdMemList.NumberOfMemoryRanges));
rva_base = dc->rva;
dc->rva += mdMemList.NumberOfMemoryRanges * sizeof(mdMem);
for (i = 0; i < dc->num_mem; i++)
{
mdMem.StartOfMemoryRange = dc->mem[i].base;
mdMem.Memory.Rva = dc->rva;
mdMem.Memory.DataSize = dc->mem[i].size;
SetFilePointer(dc->hFile, dc->rva, NULL, FILE_BEGIN);
for (pos = 0; pos < dc->mem[i].size; pos += sizeof(tmp))
{
len = min(dc->mem[i].size - pos, sizeof(tmp));
if (ReadProcessMemory(dc->hProcess,
(void*)(ULONG)(dc->mem[i].base + pos),
tmp, len, NULL))
WriteFile(dc->hFile, tmp, len, &written, NULL);
}
dc->rva += mdMem.Memory.DataSize;
writeat(dc, rva_base + i * sizeof(mdMem), &mdMem, sizeof(mdMem));
if (dc->mem[i].rva)
{
writeat(dc, dc->mem[i].rva, &mdMem.Memory.Rva, sizeof(mdMem.Memory.Rva));
}
}
}
static void dump_misc_info(struct dump_context* dc)
{
MINIDUMP_MISC_INFO mmi;
mmi.SizeOfInfo = sizeof(mmi);
mmi.Flags1 = MINIDUMP_MISC1_PROCESS_ID;
mmi.ProcessId = dc->pid;
/* FIXME: create/user/kernel time */
append(dc, &mmi, sizeof(mmi));
}
/******************************************************************
* MiniDumpWriteDump (DEBUGHLP.@)
*
*/
BOOL WINAPI MiniDumpWriteDump(HANDLE hProcess, DWORD pid, HANDLE hFile,
MINIDUMP_TYPE DumpType,
PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
PMINIDUMP_CALLBACK_INFORMATION CallbackParam)
{
MINIDUMP_HEADER mdHead;
MINIDUMP_DIRECTORY mdDir;
DWORD i, nStreams, idx_stream;
struct dump_context dc;
dc.hProcess = hProcess;
dc.hFile = hFile;
dc.pid = pid;
dc.module = NULL;
dc.num_module = 0;
dc.cb = CallbackParam;
dc.type = DumpType;
dc.mem = NULL;
dc.num_mem = 0;
dc.rva = 0;
if (!fetch_process_info(&dc)) return FALSE;
fetch_module_info(&dc);
/* 1) init */
nStreams = 6 + (ExceptionParam ? 1 : 0) +
(UserStreamParam ? UserStreamParam->UserStreamCount : 0);
if (DumpType & MiniDumpWithDataSegs)
FIXME("NIY MiniDumpWithDataSegs\n");
if (DumpType & MiniDumpWithFullMemory)
FIXME("NIY MiniDumpWithFullMemory\n");
if (DumpType & MiniDumpWithHandleData)
FIXME("NIY MiniDumpWithHandleData\n");
if (DumpType & MiniDumpFilterMemory)
FIXME("NIY MiniDumpFilterMemory\n");
if (DumpType & MiniDumpScanMemory)
FIXME("NIY MiniDumpScanMemory\n");
/* 2) write header */
mdHead.Signature = MINIDUMP_SIGNATURE;
mdHead.Version = MINIDUMP_VERSION;
mdHead.NumberOfStreams = nStreams;
mdHead.StreamDirectoryRva = sizeof(mdHead);
mdHead.u.TimeDateStamp = time(NULL);
mdHead.Flags = DumpType;
append(&dc, &mdHead, sizeof(mdHead));
/* 3) write stream directories */
dc.rva += nStreams * sizeof(mdDir);
idx_stream = 0;
/* 3.1) write data stream directories */
mdDir.StreamType = ThreadListStream;
mdDir.Location.Rva = dc.rva;
dump_threads(&dc, ExceptionParam);
mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva;
writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
&mdDir, sizeof(mdDir));
mdDir.StreamType = ModuleListStream;
mdDir.Location.Rva = dc.rva;
dump_modules(&dc, FALSE);
mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva;
writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
&mdDir, sizeof(mdDir));
mdDir.StreamType = 0xfff0; /* FIXME: this is part of MS reserved streams */
mdDir.Location.Rva = dc.rva;
dump_modules(&dc, TRUE);
mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva;
writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
&mdDir, sizeof(mdDir));
mdDir.StreamType = MemoryListStream;
mdDir.Location.Rva = dc.rva;
dump_memory_info(&dc);
mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva;
writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
&mdDir, sizeof(mdDir));
mdDir.StreamType = SystemInfoStream;
mdDir.Location.Rva = dc.rva;
dump_system_info(&dc);
mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva;
writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
&mdDir, sizeof(mdDir));
mdDir.StreamType = MiscInfoStream;
mdDir.Location.Rva = dc.rva;
dump_misc_info(&dc);
mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva;
writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
&mdDir, sizeof(mdDir));
/* 3.2) write exception information (if any) */
if (ExceptionParam)
{
mdDir.StreamType = ExceptionStream;
mdDir.Location.Rva = dc.rva;
dump_exception_info(&dc, ExceptionParam);
mdDir.Location.DataSize = dc.rva - mdDir.Location.Rva;
writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
&mdDir, sizeof(mdDir));
}
/* 3.3) write user defined streams (if any) */
if (UserStreamParam)
{
for (i = 0; i < UserStreamParam->UserStreamCount; i++)
{
mdDir.StreamType = UserStreamParam->UserStreamArray[i].Type;
mdDir.Location.DataSize = UserStreamParam->UserStreamArray[i].BufferSize;
mdDir.Location.Rva = dc.rva;
writeat(&dc, mdHead.StreamDirectoryRva + idx_stream++ * sizeof(mdDir),
&mdDir, sizeof(mdDir));
append(&dc, UserStreamParam->UserStreamArray[i].Buffer,
UserStreamParam->UserStreamArray[i].BufferSize);
}
}
HeapFree(GetProcessHeap(), 0, dc.pcs_buffer);
HeapFree(GetProcessHeap(), 0, dc.mem);
HeapFree(GetProcessHeap(), 0, dc.module);
return TRUE;
}
/******************************************************************
* MiniDumpReadDumpStream (DEBUGHLP.@)
*
*
*/
BOOL WINAPI MiniDumpReadDumpStream(void* base, ULONG str_idx,
PMINIDUMP_DIRECTORY* pdir,
void** stream, ULONG* size)
{
MINIDUMP_HEADER* mdHead = (MINIDUMP_HEADER*)base;
if (mdHead->Signature == MINIDUMP_SIGNATURE)
{
MINIDUMP_DIRECTORY* dir;
int i;
dir = (MINIDUMP_DIRECTORY*)((char*)base + mdHead->StreamDirectoryRva);
for (i = 0; i < mdHead->NumberOfStreams; i++, dir++)
{
if (dir->StreamType == str_idx)
{
*pdir = dir;
*stream = (char*)base + dir->Location.Rva;
*size = dir->Location.DataSize;
return TRUE;
}
}
}
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
--- NEW FILE: module.c ---
/*
* File module.c - module handling for the wine debugger
*
* Copyright (C) 1993, Eric Youngdale.
* 2000-2007, 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
[...965 lines suppressed...]
/******************************************************************
* module_reset_debug_info
* Removes any debug information linked to a given module.
*/
void module_reset_debug_info(struct module* module)
{
module->sortlist_valid = TRUE;
module->addr_sorttab = NULL;
hash_table_destroy(&module->ht_symbols);
module->ht_symbols.num_buckets = 0;
module->ht_symbols.buckets = NULL;
hash_table_destroy(&module->ht_types);
module->ht_types.num_buckets = 0;
module->ht_types.buckets = NULL;
module->vtypes.num_elts = 0;
hash_table_destroy(&module->ht_symbols);
module->sources_used = module->sources_alloc = 0;
module->sources = NULL;
}
--- NEW FILE: msc.c ---
/*
* File msc.c - read VC++ debug information from COFF and eventually
* from PDB files.
*
* Copyright (C) 1996, Eric Youngdale.
* Copyright (C) 1999-2000, Ulrich Weigand.
* Copyright (C) 2004-2006, 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
[...2508 lines suppressed...]
WORD cdwParams; /* # bytes in params/4 */
WORD cbProlog : 8; /* # bytes in prolog */
WORD cbRegs : 3; /* # regs saved */
WORD fHasSEH : 1; /* TRUE if SEH in func */
WORD fUseBP : 1; /* TRUE if EBP has been allocated */
WORD reserved : 1; /* reserved for future use */
WORD cbFrame : 2; /* frame type */
} FPO_DATA;
#endif
}
__EXCEPT_PAGE_FAULT
{
ERR("Got a page fault while loading symbols\n");
ret = FALSE;
}
__ENDTRY
return ret;
}
--- NEW FILE: path.c ---
/*
* File path.c - managing path in debugging environments
*
* 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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "dbghelp_private.h"
#include "winnls.h"
#include "winternl.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
static inline BOOL is_sep(char ch) {return ch == '/' || ch == '\\';}
static inline BOOL is_sepW(WCHAR ch) {return ch == '/' || ch == '\\';}
static inline const char* file_name(const char* str)
{
const char* p;
for (p = str + strlen(str) - 1; p >= str && !is_sep(*p); p--);
return p + 1;
}
static inline const WCHAR* file_nameW(const WCHAR* str)
{
const WCHAR* p;
for (p = str + strlenW(str) - 1; p >= str && !is_sepW(*p); p--);
return p + 1;
}
/******************************************************************
* FindDebugInfoFile (DBGHELP.@)
*
*/
HANDLE WINAPI FindDebugInfoFile(PCSTR FileName, PCSTR SymbolPath, PSTR DebugFilePath)
{
HANDLE h;
h = CreateFileA(DebugFilePath, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE)
{
if (!SearchPathA(SymbolPath, file_name(FileName), NULL, MAX_PATH, DebugFilePath, NULL))
return NULL;
h = CreateFileA(DebugFilePath, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
}
return (h == INVALID_HANDLE_VALUE) ? NULL : h;
}
/******************************************************************
* FindDebugInfoFileEx (DBGHELP.@)
*
*/
HANDLE WINAPI FindDebugInfoFileEx(PCSTR FileName, PCSTR SymbolPath,
PSTR DebugFilePath,
PFIND_DEBUG_FILE_CALLBACK Callback,
PVOID CallerData)
{
FIXME("(%s %s %p %p %p): stub\n",
FileName, SymbolPath, DebugFilePath, Callback, CallerData);
return NULL;
}
/******************************************************************
* FindExecutableImageExW (DBGHELP.@)
*
*/
HANDLE WINAPI FindExecutableImageExW(PCWSTR FileName, PCWSTR SymbolPath, PWSTR ImageFilePath,
PFIND_EXE_FILE_CALLBACKW Callback, void* user)
{
HANDLE h;
if (Callback) FIXME("Unsupported callback yet\n");
if (!SearchPathW(SymbolPath, FileName, NULL, MAX_PATH, ImageFilePath, NULL))
return NULL;
h = CreateFileW(ImageFilePath, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
return (h == INVALID_HANDLE_VALUE) ? NULL : h;
}
/******************************************************************
* FindExecutableImageEx (DBGHELP.@)
*
*/
HANDLE WINAPI FindExecutableImageEx(PCSTR FileName, PCSTR SymbolPath, PSTR ImageFilePath,
PFIND_EXE_FILE_CALLBACK Callback, void* user)
{
HANDLE h;
if (Callback) FIXME("Unsupported callback yet\n");
if (!SearchPathA(SymbolPath, FileName, NULL, MAX_PATH, ImageFilePath, NULL))
return NULL;
h = CreateFileA(ImageFilePath, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
return (h == INVALID_HANDLE_VALUE) ? NULL : h;
}
/******************************************************************
* FindExecutableImage (DBGHELP.@)
*
*/
HANDLE WINAPI FindExecutableImage(PCSTR FileName, PCSTR SymbolPath, PSTR ImageFilePath)
{
return FindExecutableImageEx(FileName, SymbolPath, ImageFilePath, NULL, NULL);
}
/***********************************************************************
* MakeSureDirectoryPathExists (DBGHELP.@)
*/
BOOL WINAPI MakeSureDirectoryPathExists(LPCSTR DirPath)
{
char path[MAX_PATH];
const char *p = DirPath;
int n;
if (p[0] && p[1] == ':') p += 2;
while (*p == '\\') p++; /* skip drive root */
while ((p = strchr(p, '\\')) != NULL)
{
n = p - DirPath + 1;
memcpy(path, DirPath, n);
path[n] = '\0';
if( !CreateDirectoryA(path, NULL) &&
(GetLastError() != ERROR_ALREADY_EXISTS))
return FALSE;
p++;
}
if (GetLastError() == ERROR_ALREADY_EXISTS)
SetLastError(ERROR_SUCCESS);
return TRUE;
}
/******************************************************************
* SymMatchFileNameW (DBGHELP.@)
*
*/
BOOL WINAPI SymMatchFileNameW(WCHAR* file, WCHAR* match,
WCHAR** filestop, WCHAR** matchstop)
{
WCHAR* fptr;
WCHAR* mptr;
TRACE("(%s %s %p %p)\n",
debugstr_w(file), debugstr_w(match), filestop, matchstop);
fptr = file + strlenW(file) - 1;
mptr = match + strlenW(match) - 1;
while (fptr >= file && mptr >= match)
{
if (toupperW(*fptr) != toupperW(*mptr) && !(is_sepW(*fptr) && is_sepW(*mptr)))
break;
fptr--; mptr--;
}
if (filestop) *filestop = fptr;
if (matchstop) *matchstop = mptr;
return mptr == match - 1;
}
/******************************************************************
* SymMatchFileName (DBGHELP.@)
*
*/
BOOL WINAPI SymMatchFileName(char* file, char* match,
char** filestop, char** matchstop)
{
char* fptr;
char* mptr;
TRACE("(%s %s %p %p)\n", file, match, filestop, matchstop);
fptr = file + strlen(file) - 1;
mptr = match + strlen(match) - 1;
while (fptr >= file && mptr >= match)
{
if (toupper(*fptr) != toupper(*mptr) && !(is_sep(*fptr) && is_sep(*mptr)))
break;
fptr--; mptr--;
}
if (filestop) *filestop = fptr;
if (matchstop) *matchstop = mptr;
return mptr == match - 1;
}
static BOOL do_searchW(const WCHAR* file, WCHAR* buffer, BOOL recurse,
PENUMDIRTREE_CALLBACKW cb, void* user)
{
HANDLE h;
WIN32_FIND_DATAW fd;
unsigned pos;
BOOL found = FALSE;
static const WCHAR S_AllW[] = {'*','.','*','\0'};
static const WCHAR S_DotW[] = {'.','\0'};
static const WCHAR S_DotDotW[] = {'.','\0'};
pos = strlenW(buffer);
if (buffer[pos - 1] != '\\') buffer[pos++] = '\\';
strcpyW(buffer + pos, S_AllW);
if ((h = FindFirstFileW(buffer, &fd)) == INVALID_HANDLE_VALUE)
return FALSE;
/* doc doesn't specify how the tree is enumerated...
* doing a depth first based on, but may be wrong
*/
do
{
if (!strcmpW(fd.cFileName, S_DotW) || !strcmpW(fd.cFileName, S_DotDotW)) continue;
strcpyW(buffer + pos, fd.cFileName);
if (recurse && (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
found = do_searchW(file, buffer, TRUE, cb, user);
else if (SymMatchFileNameW(buffer, (WCHAR*)file, NULL, NULL))
{
if (!cb || cb(buffer, user)) found = TRUE;
}
} while (!found && FindNextFileW(h, &fd));
if (!found) buffer[--pos] = '\0';
FindClose(h);
return found;
}
/***********************************************************************
* SearchTreeForFileW (DBGHELP.@)
*/
BOOL WINAPI SearchTreeForFileW(PCWSTR root, PCWSTR file, PWSTR buffer)
{
TRACE("(%s, %s, %p)\n",
debugstr_w(root), debugstr_w(file), buffer);
strcpyW(buffer, root);
return do_searchW(file, buffer, TRUE, NULL, NULL);
}
/***********************************************************************
* SearchTreeForFile (DBGHELP.@)
*/
BOOL WINAPI SearchTreeForFile(PCSTR root, PCSTR file, PSTR buffer)
{
WCHAR rootW[MAX_PATH];
WCHAR fileW[MAX_PATH];
WCHAR bufferW[MAX_PATH];
BOOL ret;
MultiByteToWideChar(CP_ACP, 0, root, -1, rootW, MAX_PATH);
MultiByteToWideChar(CP_ACP, 0, file, -1, fileW, MAX_PATH);
ret = SearchTreeForFileW(rootW, fileW, bufferW);
if (ret)
WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, MAX_PATH, NULL, NULL);
return ret;
}
/******************************************************************
* EnumDirTreeW (DBGHELP.@)
*
*
*/
BOOL WINAPI EnumDirTreeW(HANDLE hProcess, PCWSTR root, PCWSTR file,
LPWSTR buffer, PENUMDIRTREE_CALLBACKW cb, PVOID user)
{
TRACE("(%p %s %s %p %p %p)\n",
hProcess, debugstr_w(root), debugstr_w(file), buffer, cb, user);
strcpyW(buffer, root);
return do_searchW(file, buffer, TRUE, cb, user);
}
/******************************************************************
* EnumDirTree (DBGHELP.@)
*
*
*/
struct enum_dir_treeWA
{
PENUMDIRTREE_CALLBACK cb;
void* user;
char name[MAX_PATH];
};
static BOOL CALLBACK enum_dir_treeWA(LPCWSTR name, PVOID user)
{
struct enum_dir_treeWA* edt = user;
WideCharToMultiByte(CP_ACP, 0, name, -1, edt->name, MAX_PATH, NULL, NULL);
return edt->cb(edt->name, edt->user);
}
BOOL WINAPI EnumDirTree(HANDLE hProcess, PCSTR root, PCSTR file,
LPSTR buffer, PENUMDIRTREE_CALLBACK cb, PVOID user)
{
WCHAR rootW[MAX_PATH];
WCHAR fileW[MAX_PATH];
WCHAR bufferW[MAX_PATH];
struct enum_dir_treeWA edt;
BOOL ret;
edt.cb = cb;
edt.user = user;
MultiByteToWideChar(CP_ACP, 0, root, -1, rootW, MAX_PATH);
MultiByteToWideChar(CP_ACP, 0, file, -1, fileW, MAX_PATH);
if ((ret = EnumDirTreeW(hProcess, rootW, fileW, bufferW, enum_dir_treeWA, &edt)))
WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, MAX_PATH, NULL, NULL);
return ret;
}
struct sffip
{
enum module_type kind;
/* pe: id -> DWORD:timestamp
* two -> size of image (from PE header)
* pdb: id -> PDB signature
* I think either DWORD:timestamp or GUID:guid depending on PDB version
* two -> PDB age ???
* elf: id -> DWORD:CRC 32 of ELF image (Wine only)
*/
PVOID id;
DWORD two;
DWORD three;
DWORD flags;
PFINDFILEINPATHCALLBACKW cb;
void* user;
};
/* checks that buffer (as found by matching the name) matches the info
* (information is based on file type)
* returns TRUE when file is found, FALSE to continue searching
* (NB this is the opposite conventions as for SymFindFileInPathProc)
*/
static BOOL CALLBACK sffip_cb(LPCWSTR buffer, void* user)
{
struct sffip* s = (struct sffip*)user;
DWORD size, checksum;
/* FIXME: should check that id/two/three match the file pointed
* by buffer
*/
switch (s->kind)
{
case DMT_PE:
{
HANDLE hFile, hMap;
void* mapping;
DWORD timestamp;
timestamp = ~(DWORD_PTR)s->id;
size = ~s->two;
hFile = CreateFileW(buffer, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) return FALSE;
if ((hMap = CreateFileMappingW(hFile, NULL, PAGE_READONLY, 0, 0, NULL)) != NULL)
{
if ((mapping = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0)) != NULL)
{
IMAGE_NT_HEADERS* nth = RtlImageNtHeader(mapping);
timestamp = nth->FileHeader.TimeDateStamp;
size = nth->OptionalHeader.SizeOfImage;
UnmapViewOfFile(mapping);
}
CloseHandle(hMap);
}
CloseHandle(hFile);
if (timestamp != (DWORD_PTR)s->id || size != s->two)
{
WARN("Found %s, but wrong size or timestamp\n", debugstr_w(buffer));
return FALSE;
}
}
break;
case DMT_ELF:
if (elf_fetch_file_info(buffer, 0, &size, &checksum))
{
if (checksum != (DWORD_PTR)s->id)
{
WARN("Found %s, but wrong checksums: %08x %08lx\n",
debugstr_w(buffer), checksum, (DWORD_PTR)s->id);
return FALSE;
}
}
else
{
WARN("Couldn't read %s\n", debugstr_w(buffer));
return FALSE;
}
break;
case DMT_PDB:
{
struct pdb_lookup pdb_lookup;
char fn[MAX_PATH];
WideCharToMultiByte(CP_ACP, 0, buffer, -1, fn, MAX_PATH, NULL, NULL);
pdb_lookup.filename = fn;
if (!pdb_fetch_file_info(&pdb_lookup)) return FALSE;
switch (pdb_lookup.kind)
{
case PDB_JG:
if (s->flags & SSRVOPT_GUIDPTR)
{
WARN("Found %s, but wrong PDB version\n", debugstr_w(buffer));
return FALSE;
}
if (pdb_lookup.u.jg.timestamp != (DWORD_PTR)s->id)
{
WARN("Found %s, but wrong signature: %08x %08lx\n",
debugstr_w(buffer), pdb_lookup.u.jg.timestamp, (DWORD_PTR)s->id);
return FALSE;
}
break;
case PDB_DS:
if (!(s->flags & SSRVOPT_GUIDPTR))
{
WARN("Found %s, but wrong PDB version\n", debugstr_w(buffer));
return FALSE;
}
if (memcmp(&pdb_lookup.u.ds.guid, (GUID*)s->id, sizeof(GUID)))
{
WARN("Found %s, but wrong GUID: %s %s\n",
debugstr_w(buffer), debugstr_guid(&pdb_lookup.u.ds.guid),
debugstr_guid((GUID*)s->id));
return FALSE;
}
break;
}
if (pdb_lookup.age != s->two)
{
WARN("Found %s, but wrong age: %08x %08x\n",
debugstr_w(buffer), pdb_lookup.age, s->two);
return FALSE;
}
}
break;
default:
FIXME("What the heck??\n");
return FALSE;
}
/* yes, EnumDirTree/do_search and SymFindFileInPath callbacks use the opposite
* convention to stop/continue enumeration. sigh.
*/
return !(s->cb)((WCHAR*)buffer, s->user);
}
/******************************************************************
* SymFindFileInPathW (DBGHELP.@)
*
*/
BOOL WINAPI SymFindFileInPathW(HANDLE hProcess, PCWSTR searchPath, PCWSTR full_path,
PVOID id, DWORD two, DWORD three, DWORD flags,
LPWSTR buffer, PFINDFILEINPATHCALLBACKW cb,
PVOID user)
{
struct sffip s;
struct process* pcs = process_find_by_handle(hProcess);
WCHAR tmp[MAX_PATH];
WCHAR* ptr;
const WCHAR* filename;
TRACE("(hProcess = %p, searchPath = '%s', full_path = '%s', id = %p, two = 0x%08x, three = 0x%08x, flags = 0x%08x, buffer = %p, cb = %p, user = %p)\n",
hProcess, debugstr_w(searchPath), debugstr_w(full_path),
id, two, three, flags, buffer, cb, user);
if (!pcs) return FALSE;
if (!searchPath) searchPath = pcs->search_path;
s.id = id;
s.two = two;
s.three = three;
s.flags = flags;
s.cb = cb;
s.user = user;
filename = file_nameW(full_path);
s.kind = module_get_type_by_name(filename);
/* first check full path to file */
if (sffip_cb(full_path, &s))
{
strcpyW(buffer, full_path);
return TRUE;
}
while (searchPath)
{
ptr = strchrW(searchPath, ';');
if (ptr)
{
memcpy(tmp, searchPath, (ptr - searchPath) * sizeof(WCHAR));
tmp[ptr - searchPath] = 0;
searchPath = ptr + 1;
}
else
{
strcpyW(tmp, searchPath);
searchPath = NULL;
}
if (do_searchW(filename, tmp, FALSE, sffip_cb, &s))
{
strcpyW(buffer, tmp);
return TRUE;
}
}
return FALSE;
}
/******************************************************************
* SymFindFileInPath (DBGHELP.@)
*
*/
BOOL WINAPI SymFindFileInPath(HANDLE hProcess, PCSTR searchPath, PCSTR full_path,
PVOID id, DWORD two, DWORD three, DWORD flags,
LPSTR buffer, PFINDFILEINPATHCALLBACK cb,
PVOID user)
{
WCHAR searchPathW[MAX_PATH];
WCHAR full_pathW[MAX_PATH];
WCHAR bufferW[MAX_PATH];
struct enum_dir_treeWA edt;
BOOL ret;
/* a PFINDFILEINPATHCALLBACK and a PENUMDIRTREE_CALLBACK have actually the
* same signature & semantics, hence we can reuse the EnumDirTree W->A
* conversion helper
*/
edt.cb = cb;
edt.user = user;
if (searchPath)
MultiByteToWideChar(CP_ACP, 0, searchPath, -1, searchPathW, MAX_PATH);
MultiByteToWideChar(CP_ACP, 0, full_path, -1, full_pathW, MAX_PATH);
if ((ret = SymFindFileInPathW(hProcess, searchPath ? searchPathW : NULL, full_pathW,
id, two, three, flags,
bufferW, enum_dir_treeWA, &edt)))
WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, MAX_PATH, NULL, NULL);
return ret;
}
--- NEW FILE: pe_module.c ---
/*
* File pe_module.c - handle PE module information
*
* Copyright (C) 1996, Eric Youngdale.
* Copyright (C) 1999-2000, Ulrich Weigand.
* Copyright (C) 2004-2007, 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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "dbghelp_private.h"
#include "winternl.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
/******************************************************************
* pe_load_stabs
*
* look for stabs information in PE header (it's how the mingw compiler provides
* its debugging information)
*/
static BOOL pe_load_stabs(const struct process* pcs, struct module* module,
const void* mapping, IMAGE_NT_HEADERS* nth)
{
IMAGE_SECTION_HEADER* section;
int i, stabsize = 0, stabstrsize = 0;
unsigned int stabs = 0, stabstr = 0;
BOOL ret = FALSE;
section = (IMAGE_SECTION_HEADER*)
((char*)&nth->OptionalHeader + nth->FileHeader.SizeOfOptionalHeader);
for (i = 0; i < nth->FileHeader.NumberOfSections; i++, section++)
{
if (!strcasecmp((const char*)section->Name, ".stab"))
{
stabs = section->VirtualAddress;
stabsize = section->SizeOfRawData;
}
else if (!strncasecmp((const char*)section->Name, ".stabstr", 8))
{
stabstr = section->VirtualAddress;
stabstrsize = section->SizeOfRawData;
}
}
if (stabstrsize && stabsize)
{
ret = stabs_parse(module,
module->module.BaseOfImage - nth->OptionalHeader.ImageBase,
RtlImageRvaToVa(nth, (void*)mapping, stabs, NULL),
stabsize,
RtlImageRvaToVa(nth, (void*)mapping, stabstr, NULL),
stabstrsize);
}
return ret;
}
static BOOL CALLBACK dbg_match(const char* file, void* user)
{
/* accept first file */
return FALSE;
}
/******************************************************************
* pe_load_dbg_file
*
* loads a .dbg file
*/
static BOOL pe_load_dbg_file(const struct process* pcs, struct module* module,
const char* dbg_name, DWORD timestamp)
{
char tmp[MAX_PATH];
HANDLE hFile = INVALID_HANDLE_VALUE, hMap = 0;
const BYTE* dbg_mapping = NULL;
const IMAGE_SEPARATE_DEBUG_HEADER* hdr;
const IMAGE_DEBUG_DIRECTORY* dbg;
BOOL ret = FALSE;
WINE_TRACE("Processing DBG file %s\n", dbg_name);
if (SymFindFileInPath(pcs->handle, NULL, dbg_name, NULL, 0, 0, 0, tmp, dbg_match, NULL) &&
(hFile = CreateFileA(tmp, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE &&
((hMap = CreateFileMappingW(hFile, NULL, PAGE_READONLY, 0, 0, NULL)) != 0) &&
((dbg_mapping = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0)) != NULL))
{
hdr = (const IMAGE_SEPARATE_DEBUG_HEADER*)dbg_mapping;
if (hdr->TimeDateStamp != timestamp)
{
WINE_ERR("Warning - %s has incorrect internal timestamp\n",
dbg_name);
/*
* Well, sometimes this happens to DBG files which ARE REALLY the
* right .DBG files but nonetheless this check fails. Anyway,
* WINDBG (debugger for Windows by Microsoft) loads debug symbols
* which have incorrect timestamps.
*/
}
if (hdr->Signature == IMAGE_SEPARATE_DEBUG_SIGNATURE)
{
/* section headers come immediately after debug header */
const IMAGE_SECTION_HEADER *sectp =
(const IMAGE_SECTION_HEADER*)(hdr + 1);
/* and after that and the exported names comes the debug directory */
dbg = (const IMAGE_DEBUG_DIRECTORY*)
(dbg_mapping + sizeof(*hdr) +
hdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER) +
hdr->ExportedNamesSize);
ret = pe_load_debug_directory(pcs, module, dbg_mapping, sectp,
hdr->NumberOfSections, dbg,
hdr->DebugDirectorySize / sizeof(*dbg));
}
else
ERR("Wrong signature in .DBG file %s\n", debugstr_a(tmp));
}
else
WINE_ERR("-Unable to peruse .DBG file %s (%s)\n", dbg_name, debugstr_a(tmp));
if (dbg_mapping) UnmapViewOfFile(dbg_mapping);
if (hMap) CloseHandle(hMap);
if (hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile);
return ret;
}
/******************************************************************
* pe_load_msc_debug_info
*
* Process MSC debug information in PE file.
*/
static BOOL pe_load_msc_debug_info(const struct process* pcs,
struct module* module,
const void* mapping, IMAGE_NT_HEADERS* nth)
{
BOOL ret = FALSE;
const IMAGE_DATA_DIRECTORY* dir;
const IMAGE_DEBUG_DIRECTORY*dbg = NULL;
int nDbg;
/* Read in debug directory */
dir = nth->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_DEBUG;
nDbg = dir->Size / sizeof(IMAGE_DEBUG_DIRECTORY);
if (!nDbg) return FALSE;
dbg = RtlImageRvaToVa(nth, (void*)mapping, dir->VirtualAddress, NULL);
/* Parse debug directory */
if (nth->FileHeader.Characteristics & IMAGE_FILE_DEBUG_STRIPPED)
{
/* Debug info is stripped to .DBG file */
const IMAGE_DEBUG_MISC* misc = (const IMAGE_DEBUG_MISC*)
((const char*)mapping + dbg->PointerToRawData);
if (nDbg != 1 || dbg->Type != IMAGE_DEBUG_TYPE_MISC ||
misc->DataType != IMAGE_DEBUG_MISC_EXENAME)
{
WINE_ERR("-Debug info stripped, but no .DBG file in module %s\n",
debugstr_w(module->module.ModuleName));
}
else
{
ret = pe_load_dbg_file(pcs, module, (const char*)misc->Data, nth->FileHeader.TimeDateStamp);
}
}
else
{
const IMAGE_SECTION_HEADER *sectp = (const IMAGE_SECTION_HEADER*)((const char*)&nth->OptionalHeader + nth->FileHeader.SizeOfOptionalHeader);
/* Debug info is embedded into PE module */
ret = pe_load_debug_directory(pcs, module, mapping, sectp,
nth->FileHeader.NumberOfSections, dbg, nDbg);
}
return ret;
}
/***********************************************************************
* pe_load_export_debug_info
*/
static BOOL pe_load_export_debug_info(const struct process* pcs,
struct module* module,
const void* mapping, IMAGE_NT_HEADERS* nth)
{
unsigned int i;
const IMAGE_EXPORT_DIRECTORY* exports;
DWORD base = module->module.BaseOfImage;
DWORD size;
if (dbghelp_options & SYMOPT_NO_PUBLICS) return TRUE;
#if 0
/* Add start of DLL (better use the (yet unimplemented) Exe SymTag for this) */
/* FIXME: module.ModuleName isn't correctly set yet if it's passed in SymLoadModule */
symt_new_public(module, NULL, module->module.ModuleName, base, 1,
TRUE /* FIXME */, TRUE /* FIXME */);
#endif
/* Add entry point */
symt_new_public(module, NULL, "EntryPoint",
base + nth->OptionalHeader.AddressOfEntryPoint, 1,
TRUE, TRUE);
#if 0
/* FIXME: we'd better store addresses linked to sections rather than
absolute values */
IMAGE_SECTION_HEADER* section;
/* Add start of sections */
section = (IMAGE_SECTION_HEADER*)
((char*)&nth->OptionalHeader + nth->FileHeader.SizeOfOptionalHeader);
for (i = 0; i < nth->FileHeader.NumberOfSections; i++, section++)
{
symt_new_public(module, NULL, section->Name,
RtlImageRvaToVa(nth, (void*)mapping, section->VirtualAddress, NULL),
1, TRUE /* FIXME */, TRUE /* FIXME */);
}
#endif
/* Add exported functions */
if ((exports = RtlImageDirectoryEntryToData((void*)mapping, FALSE,
IMAGE_DIRECTORY_ENTRY_EXPORT, &size)))
{
const WORD* ordinals = NULL;
const DWORD_PTR* functions = NULL;
const DWORD* names = NULL;
unsigned int j;
char buffer[16];
functions = RtlImageRvaToVa(nth, (void*)mapping, exports->AddressOfFunctions, NULL);
ordinals = RtlImageRvaToVa(nth, (void*)mapping, exports->AddressOfNameOrdinals, NULL);
names = RtlImageRvaToVa(nth, (void*)mapping, exports->AddressOfNames, NULL);
for (i = 0; i < exports->NumberOfNames; i++)
{
if (!names[i]) continue;
symt_new_public(module, NULL,
RtlImageRvaToVa(nth, (void*)mapping, names[i], NULL),
base + functions[ordinals[i]],
1, TRUE /* FIXME */, TRUE /* FIXME */);
}
for (i = 0; i < exports->NumberOfFunctions; i++)
{
if (!functions[i]) continue;
/* Check if we already added it with a name */
for (j = 0; j < exports->NumberOfNames; j++)
if ((ordinals[j] == i) && names[j]) break;
if (j < exports->NumberOfNames) continue;
snprintf(buffer, sizeof(buffer), "%d", i + exports->Base);
symt_new_public(module, NULL, buffer, base + (DWORD)functions[i], 1,
TRUE /* FIXME */, TRUE /* FIXME */);
}
}
/* no real debug info, only entry points */
if (module->module.SymType == SymDeferred)
module->module.SymType = SymExport;
return TRUE;
}
/******************************************************************
* pe_load_debug_info
*
*/
BOOL pe_load_debug_info(const struct process* pcs, struct module* module)
{
BOOL ret = FALSE;
HANDLE hFile;
HANDLE hMap;
void* mapping;
IMAGE_NT_HEADERS* nth;
hFile = CreateFileW(module->module.LoadedImageName, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) return ret;
if ((hMap = CreateFileMappingW(hFile, NULL, PAGE_READONLY, 0, 0, NULL)) != 0)
{
if ((mapping = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0)) != NULL)
{
nth = RtlImageNtHeader(mapping);
if (!(dbghelp_options & SYMOPT_PUBLICS_ONLY))
{
ret = pe_load_stabs(pcs, module, mapping, nth) ||
pe_load_msc_debug_info(pcs, module, mapping, nth);
/* if we still have no debug info (we could only get SymExport at this
* point), then do the SymExport except if we have an ELF container,
* in which case we'll rely on the export's on the ELF side
*/
}
/* FIXME shouldn't we check that? if (!module_get_debug(pcs, module))l */
if (pe_load_export_debug_info(pcs, module, mapping, nth) && !ret)
ret = TRUE;
UnmapViewOfFile(mapping);
}
CloseHandle(hMap);
}
CloseHandle(hFile);
return ret;
}
/******************************************************************
* pe_load_native_module
*
*/
struct module* pe_load_native_module(struct process* pcs, const WCHAR* name,
HANDLE hFile, DWORD base, DWORD size)
{
struct module* module = NULL;
BOOL opened = FALSE;
HANDLE hMap;
WCHAR loaded_name[MAX_PATH];
loaded_name[0] = '\0';
if (!hFile)
{
assert(name);
if ((hFile = FindExecutableImageExW(name, pcs->search_path, loaded_name, NULL, NULL)) == NULL)
return NULL;
opened = TRUE;
}
else if (name) strcpyW(loaded_name, name);
else if (dbghelp_options & SYMOPT_DEFERRED_LOADS)
FIXME("Trouble ahead (no module name passed in deferred mode)\n");
if ((hMap = CreateFileMappingW(hFile, NULL, PAGE_READONLY, 0, 0, NULL)) != NULL)
{
void* mapping;
if ((mapping = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0)) != NULL)
{
IMAGE_NT_HEADERS* nth = RtlImageNtHeader(mapping);
if (nth)
{
if (!base) base = nth->OptionalHeader.ImageBase;
if (!size) size = nth->OptionalHeader.SizeOfImage;
module = module_new(pcs, loaded_name, DMT_PE, FALSE, base, size,
nth->FileHeader.TimeDateStamp,
nth->OptionalHeader.CheckSum);
if (module)
{
if (dbghelp_options & SYMOPT_DEFERRED_LOADS)
module->module.SymType = SymDeferred;
else
pe_load_debug_info(pcs, module);
}
}
UnmapViewOfFile(mapping);
}
CloseHandle(hMap);
}
if (opened) CloseHandle(hFile);
return module;
}
/******************************************************************
* pe_load_nt_header
*
*/
BOOL pe_load_nt_header(HANDLE hProc, DWORD base, IMAGE_NT_HEADERS* nth)
{
IMAGE_DOS_HEADER dos;
return ReadProcessMemory(hProc, (char*)base, &dos, sizeof(dos), NULL) &&
dos.e_magic == IMAGE_DOS_SIGNATURE &&
ReadProcessMemory(hProc, (char*)(base + dos.e_lfanew),
nth, sizeof(*nth), NULL) &&
nth->Signature == IMAGE_NT_SIGNATURE;
}
/******************************************************************
* pe_load_builtin_module
*
*/
struct module* pe_load_builtin_module(struct process* pcs, const WCHAR* name,
DWORD base, DWORD size)
{
struct module* module = NULL;
if (base && pcs->dbg_hdr_addr)
{
IMAGE_NT_HEADERS nth;
if (pe_load_nt_header(pcs->handle, base, &nth))
{
if (!size) size = nth.OptionalHeader.SizeOfImage;
module = module_new(pcs, name, DMT_PE, FALSE, base, size,
nth.FileHeader.TimeDateStamp,
nth.OptionalHeader.CheckSum);
}
}
return module;
}
--- NEW FILE: source.c ---
/*
* File source.c - source files management
*
* 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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "dbghelp_private.h"
#include "wine/debug.h"
#ifdef HAVE_REGEX_H
# include <regex.h>
#endif
WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
/******************************************************************
* source_find
*
* check whether a source file has already been stored
*/
static unsigned source_find(const struct module* module, const char* name)
{
char* ptr = module->sources;
while (*ptr)
{
if (strcmp(ptr, name) == 0) return ptr - module->sources;
ptr += strlen(ptr) + 1;
}
return (unsigned)-1;
}
/******************************************************************
* source_new
*
* checks if source exists. if not, add it
*/
unsigned source_new(struct module* module, const char* base, const char* name)
{
unsigned ret;
const char* full;
char* tmp = NULL;
if (!name) return (unsigned)-1;
if (!base || *name == '/')
full = name;
else
{
unsigned bsz = strlen(base);
tmp = HeapAlloc(GetProcessHeap(), 0, bsz + 1 + strlen(name) + 1);
if (!tmp) return (unsigned)-1;
full = tmp;
strcpy(tmp, base);
if (tmp[bsz - 1] != '/') tmp[bsz++] = '/';
strcpy(&tmp[bsz], name);
}
if (!module->sources || (ret = source_find(module, full)) == (unsigned)-1)
{
int len = strlen(full) + 1;
if (module->sources_used + len + 1 > module->sources_alloc)
{
/* Alloc by block of 256 bytes */
module->sources_alloc = (module->sources_used + len + 1 + 255) & ~255;
if (!module->sources)
module->sources = HeapAlloc(GetProcessHeap(), 0, module->sources_alloc);
else
module->sources = HeapReAlloc(GetProcessHeap(), 0, module->sources,
module->sources_alloc);
}
ret = module->sources_used;
memcpy(module->sources + module->sources_used, full, len);
module->sources_used += len;
module->sources[module->sources_used] = '\0';
}
HeapFree(GetProcessHeap(), 0, tmp);
return ret;
}
/******************************************************************
* source_get
*
* returns a stored source file name
*/
const char* source_get(const struct module* module, unsigned idx)
{
if (idx == -1) return "";
assert(module->sources);
return module->sources + idx;
}
/******************************************************************
* SymEnumSourceFiles (DBGHELP.@)
*
*/
BOOL WINAPI SymEnumSourceFiles(HANDLE hProcess, ULONG64 ModBase, PCSTR Mask,
PSYM_ENUMSOURCEFILES_CALLBACK cbSrcFiles,
PVOID UserContext)
{
struct module_pair pair;
SOURCEFILE sf;
char* ptr;
if (!cbSrcFiles) return FALSE;
pair.pcs = process_find_by_handle(hProcess);
if (!pair.pcs) return FALSE;
if (ModBase)
{
pair.requested = module_find_by_addr(pair.pcs, ModBase, DMT_UNKNOWN);
if (!module_get_debug(&pair)) return FALSE;
}
else
{
if (Mask[0] == '!')
{
pair.requested = module_find_by_nameA(pair.pcs, Mask + 1);
if (!module_get_debug(&pair)) return FALSE;
}
else
{
FIXME("Unsupported yet (should get info from current context)\n");
return FALSE;
}
}
if (!pair.effective->sources) return FALSE;
for (ptr = pair.effective->sources; *ptr; ptr += strlen(ptr) + 1)
{
/* FIXME: not using Mask */
sf.ModBase = ModBase;
sf.FileName = ptr;
if (!cbSrcFiles(&sf, UserContext)) break;
}
return TRUE;
}
/******************************************************************
* SymEnumLines (DBGHELP.@)
*
*/
BOOL WINAPI SymEnumLines(HANDLE hProcess, ULONG64 base, PCSTR compiland,
PCSTR srcfile, PSYM_ENUMLINES_CALLBACK cb, PVOID user)
{
struct module_pair pair;
struct hash_table_iter hti;
struct symt_ht* sym;
regex_t re;
struct line_info* dli;
void* ptr;
SRCCODEINFO sci;
const char* file;
if (!cb) return FALSE;
if (!(dbghelp_options & SYMOPT_LOAD_LINES)) return TRUE;
if (regcomp(&re, srcfile, REG_NOSUB))
{
FIXME("Couldn't compile %s\n", srcfile);
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
pair.pcs = process_find_by_handle(hProcess);
if (!pair.pcs) return FALSE;
if (compiland) FIXME("Unsupported yet (filtering on compiland %s)\n", compiland);
pair.requested = module_find_by_addr(pair.pcs, base, DMT_UNKNOWN);
if (!module_get_debug(&pair)) return FALSE;
sci.SizeOfStruct = sizeof(sci);
sci.ModBase = base;
hash_table_iter_init(&pair.effective->ht_symbols, &hti, NULL);
while ((ptr = hash_table_iter_up(&hti)))
{
int i;
sym = GET_ENTRY(ptr, struct symt_ht, hash_elt);
if (sym->symt.tag != SymTagFunction) continue;
sci.FileName[0] = '\0';
for (i=0; i<vector_length(&((struct symt_function*)sym)->vlines); i++)
{
dli = vector_at(&((struct symt_function*)sym)->vlines, i);
if (dli->is_source_file)
{
file = source_get(pair.effective, dli->u.source_file);
if (regexec(&re, file, 0, NULL, 0) != 0) file = "";
strcpy(sci.FileName, file);
}
else if (sci.FileName[0])
{
sci.Key = dli;
sci.Obj[0] = '\0'; /* FIXME */
sci.LineNumber = dli->line_number;
sci.Address = dli->u.pc_offset;
if (!cb(&sci, user)) break;
}
}
}
return TRUE;
}
/******************************************************************
* SymGetSourceFileToken (DBGHELP.@)
*
*/
BOOL WINAPI SymGetSourceFileToken(HANDLE hProcess, ULONG64 base,
PCSTR src, PVOID* token, DWORD* size)
{
FIXME("%p %s %s %p %p: stub!\n",
hProcess, wine_dbgstr_longlong(base), debugstr_a(src), token, size);
SetLastError(ERROR_NOT_SUPPORTED);
return FALSE;
}
/******************************************************************
* SymGetSourceFileTokenW (DBGHELP.@)
*
*/
BOOL WINAPI SymGetSourceFileTokenW(HANDLE hProcess, ULONG64 base,
PCWSTR src, PVOID* token, DWORD* size)
{
FIXME("%p %s %s %p %p: stub!\n",
hProcess, wine_dbgstr_longlong(base), debugstr_w(src), token, size);
SetLastError(ERROR_NOT_SUPPORTED);
return FALSE;
}
--- NEW FILE: stabs.c ---
/*
* File stabs.c - read stabs information from the modules
*
* Copyright (C) 1996, Eric Youngdale.
* 1999-2005, 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
[...1491 lines suppressed...]
}
stabbuff[0] = '\0';
TRACE("0x%02x %lx %s\n",
stab_ptr->n_type, stab_ptr->n_value, debugstr_a(strs + stab_ptr->n_un.n_strx));
}
module->module.SymType = SymDia;
module->module.CVSig = 'S' | ('T' << 8) | ('A' << 16) | ('B' << 24);
/* FIXME: we could have a finer grain here */
module->module.LineNumbers = TRUE;
module->module.GlobalSymbols = TRUE;
module->module.TypeInfo = TRUE;
module->module.SourceIndexed = TRUE;
module->module.Publics = TRUE;
done:
HeapFree(GetProcessHeap(), 0, stabbuff);
stabs_free_includes();
HeapFree(GetProcessHeap(), 0, pending.vars);
return ret;
}
--- NEW FILE: stack.c ---
/*
* Stack walking
*
* Copyright 1995 Alexandre Julliard
* Copyright 1996 Eric Youngdale
* Copyright 1999 Ove KÃ¥ven
* Copyright 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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "ntstatus.h"
#define WIN32_NO_STATUS
#include "dbghelp_private.h"
#include "winternl.h"
#include "wine/winbase16.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
enum st_mode {stm_start, stm_32bit, stm_16bit, stm_done};
static const char* wine_dbgstr_addr(const ADDRESS* addr)
{
if (!addr) return "(null)";
switch (addr->Mode)
{
case AddrModeFlat:
return wine_dbg_sprintf("flat<%08x>", addr->Offset);
case AddrMode1616:
return wine_dbg_sprintf("1616<%04x:%04x>", addr->Segment, addr->Offset);
case AddrMode1632:
return wine_dbg_sprintf("1632<%04x:%08x>", addr->Segment, addr->Offset);
case AddrModeReal:
return wine_dbg_sprintf("real<%04x:%04x>", addr->Segment, addr->Offset);
default:
return "unknown";
}
}
static BOOL CALLBACK read_mem(HANDLE hProcess, DWORD addr, void* buffer,
DWORD size, LPDWORD nread)
{
SIZE_T r;
if (!ReadProcessMemory(hProcess, (void*)addr, buffer, size, &r)) return FALSE;
if (nread) *nread = r;
return TRUE;
}
static BOOL CALLBACK read_mem64(HANDLE hProcess, DWORD64 addr, void* buffer,
DWORD size, LPDWORD nread)
{
SIZE_T r;
if (!ReadProcessMemory(hProcess, (void*)(DWORD_PTR)addr, buffer, size, &r)) return FALSE;
if (nread) *nread = r;
return TRUE;
}
/* indexes in Reserved array */
#define __CurrentMode 0
#define __CurrentSwitch 1
#define __NextSwitch 2
#define curr_mode (frame->Reserved[__CurrentMode])
#define curr_switch (frame->Reserved[__CurrentSwitch])
#define next_switch (frame->Reserved[__NextSwitch])
struct stack_walk_callback
{
HANDLE hProcess;
HANDLE hThread;
BOOL is32;
union
{
struct
{
PREAD_PROCESS_MEMORY_ROUTINE f_read_mem;
PTRANSLATE_ADDRESS_ROUTINE f_xlat_adr;
PFUNCTION_TABLE_ACCESS_ROUTINE f_tabl_acs;
PGET_MODULE_BASE_ROUTINE f_modl_bas;
} s32;
struct
{
PREAD_PROCESS_MEMORY_ROUTINE64 f_read_mem;
PTRANSLATE_ADDRESS_ROUTINE64 f_xlat_adr;
PFUNCTION_TABLE_ACCESS_ROUTINE64 f_tabl_acs;
PGET_MODULE_BASE_ROUTINE64 f_modl_bas;
} s64;
} u;
};
static inline void addr_32to64(const ADDRESS* addr32, ADDRESS64* addr64)
{
addr64->Offset = (ULONG64)addr32->Offset;
addr64->Segment = addr32->Segment;
addr64->Mode = addr32->Mode;
}
static inline void addr_64to32(const ADDRESS64* addr64, ADDRESS* addr32)
{
addr32->Offset = (ULONG)addr64->Offset;
addr32->Segment = addr64->Segment;
addr32->Mode = addr64->Mode;
}
static inline BOOL sw_read_mem(struct stack_walk_callback* cb, DWORD addr, void* ptr, DWORD sz)
{
if (cb->is32)
return cb->u.s32.f_read_mem(cb->hProcess, addr, ptr, sz, NULL);
else
return cb->u.s64.f_read_mem(cb->hProcess, addr, ptr, sz, NULL);
}
static inline DWORD sw_xlat_addr(struct stack_walk_callback* cb, ADDRESS* addr)
{
if (addr->Mode == AddrModeFlat) return addr->Offset;
if (cb->is32) return cb->u.s32.f_xlat_adr(cb->hProcess, cb->hThread, addr);
if (cb->u.s64.f_xlat_adr)
{
ADDRESS64 addr64;
addr_32to64(addr, &addr64);
return cb->u.s64.f_xlat_adr(cb->hProcess, cb->hThread, &addr64);
}
return addr_to_linear(cb->hProcess, cb->hThread, addr);
}
static inline void* sw_tabl_acs(struct stack_walk_callback* cb, DWORD addr)
{
if (cb->is32)
return cb->u.s32.f_tabl_acs(cb->hProcess, addr);
else
return cb->u.s64.f_tabl_acs(cb->hProcess, addr);
}
static inline DWORD sw_modl_bas(struct stack_walk_callback* cb, DWORD addr)
{
if (cb->is32)
return cb->u.s32.f_modl_bas(cb->hProcess, addr);
else
return cb->u.s64.f_modl_bas(cb->hProcess, addr);
}
static BOOL stack_walk(struct stack_walk_callback* cb, LPSTACKFRAME frame)
{
STACK32FRAME frame32;
STACK16FRAME frame16;
char ch;
ADDRESS tmp;
DWORD p;
WORD val;
BOOL do_switch;
/* sanity check */
if (curr_mode >= stm_done) return FALSE;
TRACE("Enter: PC=%s Frame=%s Return=%s Stack=%s Mode=%s cSwitch=%08x nSwitch=%08x\n",
wine_dbgstr_addr(&frame->AddrPC),
wine_dbgstr_addr(&frame->AddrFrame),
wine_dbgstr_addr(&frame->AddrReturn),
wine_dbgstr_addr(&frame->AddrStack),
curr_mode == stm_start ? "start" : (curr_mode == stm_16bit ? "16bit" : "32bit"),
curr_switch, next_switch);
if (curr_mode == stm_start)
{
THREAD_BASIC_INFORMATION info;
if ((frame->AddrPC.Mode == AddrModeFlat) &&
(frame->AddrFrame.Mode != AddrModeFlat))
{
WARN("Bad AddrPC.Mode / AddrFrame.Mode combination\n");
goto done_err;
}
/* Init done */
curr_mode = (frame->AddrPC.Mode == AddrModeFlat) ?
stm_32bit : stm_16bit;
/* cur_switch holds address of WOW32Reserved field in TEB in debuggee
* address space
*/
if (NtQueryInformationThread(cb->hThread, ThreadBasicInformation, &info,
sizeof(info), NULL) == STATUS_SUCCESS)
{
curr_switch = (unsigned long)info.TebBaseAddress + FIELD_OFFSET(TEB, WOW32Reserved);
if (!sw_read_mem(cb, curr_switch, &next_switch, sizeof(next_switch)))
{
WARN("Can't read TEB:WOW32Reserved\n");
goto done_err;
}
if (curr_mode == stm_16bit)
{
if (!sw_read_mem(cb, next_switch, &frame32, sizeof(frame32)))
{
WARN("Bad stack frame 0x%08x\n", next_switch);
goto done_err;
}
curr_switch = (DWORD)frame32.frame16;
tmp.Mode = AddrMode1616;
tmp.Segment = SELECTOROF(curr_switch);
tmp.Offset = OFFSETOF(curr_switch);
if (!sw_read_mem(cb, sw_xlat_addr(cb, &tmp), &ch, sizeof(ch)))
curr_switch = 0xFFFFFFFF;
}
else
{
tmp.Mode = AddrMode1616;
tmp.Segment = SELECTOROF(next_switch);
tmp.Offset = OFFSETOF(next_switch);
p = sw_xlat_addr(cb, &tmp);
if (!sw_read_mem(cb, p, &frame16, sizeof(frame16)))
{
WARN("Bad stack frame 0x%08x\n", p);
goto done_err;
}
curr_switch = (DWORD)frame16.frame32;
if (!sw_read_mem(cb, curr_switch, &ch, sizeof(ch)))
curr_switch = 0xFFFFFFFF;
}
}
else
/* FIXME: this will allow to work when we're not attached to a live target,
* but the 16 <=> 32 switch facility won't be available.
*/
curr_switch = 0;
frame->AddrReturn.Mode = frame->AddrStack.Mode = (curr_mode == stm_16bit) ? AddrMode1616 : AddrModeFlat;
/* don't set up AddrStack on first call. Either the caller has set it up, or
* we will get it in the next frame
*/
memset(&frame->AddrBStore, 0, sizeof(frame->AddrBStore));
}
else
{
if (frame->AddrFrame.Offset == 0) goto done_err;
if (frame->AddrFrame.Mode == AddrModeFlat)
{
assert(curr_mode == stm_32bit);
do_switch = curr_switch && frame->AddrFrame.Offset >= curr_switch;
}
else
{
assert(curr_mode == stm_16bit);
do_switch = curr_switch &&
frame->AddrFrame.Segment == SELECTOROF(curr_switch) &&
frame->AddrFrame.Offset >= OFFSETOF(curr_switch);
}
if (do_switch)
{
if (curr_mode == stm_16bit)
{
if (!sw_read_mem(cb, next_switch, &frame32, sizeof(frame32)))
{
WARN("Bad stack frame 0x%08x\n", next_switch);
goto done_err;
}
frame->AddrPC.Mode = AddrModeFlat;
frame->AddrPC.Segment = 0;
frame->AddrPC.Offset = frame32.retaddr;
frame->AddrFrame.Mode = AddrModeFlat;
frame->AddrFrame.Segment = 0;
frame->AddrFrame.Offset = frame32.ebp;
frame->AddrStack.Mode = AddrModeFlat;
frame->AddrStack.Segment = 0;
frame->AddrReturn.Mode = AddrModeFlat;
frame->AddrReturn.Segment = 0;
next_switch = curr_switch;
tmp.Mode = AddrMode1616;
tmp.Segment = SELECTOROF(next_switch);
tmp.Offset = OFFSETOF(next_switch);
p = sw_xlat_addr(cb, &tmp);
if (!sw_read_mem(cb, p, &frame16, sizeof(frame16)))
{
WARN("Bad stack frame 0x%08x\n", p);
goto done_err;
}
curr_switch = (DWORD)frame16.frame32;
curr_mode = stm_32bit;
if (!sw_read_mem(cb, curr_switch, &ch, sizeof(ch)))
curr_switch = 0;
}
else
{
tmp.Mode = AddrMode1616;
tmp.Segment = SELECTOROF(next_switch);
tmp.Offset = OFFSETOF(next_switch);
p = sw_xlat_addr(cb, &tmp);
if (!sw_read_mem(cb, p, &frame16, sizeof(frame16)))
{
WARN("Bad stack frame 0x%08x\n", p);
goto done_err;
}
TRACE("Got a 16 bit stack switch:"
"\n\tframe32: %08lx"
"\n\tedx:%08x ecx:%08x ebp:%08x"
"\n\tds:%04x es:%04x fs:%04x gs:%04x"
"\n\tcall_from_ip:%08x module_cs:%04x relay=%08x"
"\n\tentry_ip:%04x entry_point:%08x"
"\n\tbp:%04x ip:%04x cs:%04x\n",
(unsigned long)frame16.frame32,
frame16.edx, frame16.ecx, frame16.ebp,
frame16.ds, frame16.es, frame16.fs, frame16.gs,
frame16.callfrom_ip, frame16.module_cs, frame16.relay,
frame16.entry_ip, frame16.entry_point,
frame16.bp, frame16.ip, frame16.cs);
frame->AddrPC.Mode = AddrMode1616;
frame->AddrPC.Segment = frame16.cs;
frame->AddrPC.Offset = frame16.ip;
frame->AddrFrame.Mode = AddrMode1616;
frame->AddrFrame.Segment = SELECTOROF(next_switch);
frame->AddrFrame.Offset = frame16.bp;
frame->AddrStack.Mode = AddrMode1616;
frame->AddrStack.Segment = SELECTOROF(next_switch);
frame->AddrReturn.Mode = AddrMode1616;
frame->AddrReturn.Segment = frame16.cs;
next_switch = curr_switch;
if (!sw_read_mem(cb, next_switch, &frame32, sizeof(frame32)))
{
WARN("Bad stack frame 0x%08x\n", next_switch);
goto done_err;
}
curr_switch = (DWORD)frame32.frame16;
tmp.Mode = AddrMode1616;
tmp.Segment = SELECTOROF(curr_switch);
tmp.Offset = OFFSETOF(curr_switch);
if (!sw_read_mem(cb, sw_xlat_addr(cb, &tmp), &ch, sizeof(ch)))
curr_switch = 0;
curr_mode = stm_16bit;
}
}
else
{
frame->AddrPC = frame->AddrReturn;
if (curr_mode == stm_16bit)
{
frame->AddrStack.Offset = frame->AddrFrame.Offset + 2 * sizeof(WORD);
/* "pop up" previous BP value */
if (!sw_read_mem(cb, sw_xlat_addr(cb, &frame->AddrFrame),
&val, sizeof(WORD)))
goto done_err;
frame->AddrFrame.Offset = val;
}
else
{
frame->AddrStack.Offset = frame->AddrFrame.Offset + 2 * sizeof(DWORD);
/* "pop up" previous EBP value */
if (!sw_read_mem(cb, frame->AddrFrame.Offset,
&frame->AddrFrame.Offset, sizeof(DWORD)))
goto done_err;
}
}
}
if (curr_mode == stm_16bit)
{
int i;
p = sw_xlat_addr(cb, &frame->AddrFrame);
if (!sw_read_mem(cb, p + sizeof(WORD), &val, sizeof(WORD)))
goto done_err;
frame->AddrReturn.Offset = val;
/* get potential cs if a far call was used */
if (!sw_read_mem(cb, p + 2 * sizeof(WORD), &val, sizeof(WORD)))
goto done_err;
if (frame->AddrFrame.Offset & 1)
frame->AddrReturn.Segment = val; /* far call assumed */
else
{
/* not explicitly marked as far call,
* but check whether it could be anyway
*/
if ((val & 7) == 7 && val != frame->AddrReturn.Segment)
{
LDT_ENTRY le;
if (GetThreadSelectorEntry(cb->hThread, val, &le) &&
(le.HighWord.Bits.Type & 0x08)) /* code segment */
{
/* it is very uncommon to push a code segment cs as
* a parameter, so this should work in most cases
*/
frame->AddrReturn.Segment = val;
}
}
}
frame->AddrFrame.Offset &= ~1;
/* we "pop" parameters as 16 bit entities... of course, this won't
* work if the parameter is in fact bigger than 16bit, but
* there's no way to know that here
*/
for (i = 0; i < sizeof(frame->Params) / sizeof(frame->Params[0]); i++)
{
sw_read_mem(cb, p + (2 + i) * sizeof(WORD), &val, sizeof(val));
frame->Params[i] = val;
}
}
else
{
if (!sw_read_mem(cb, frame->AddrFrame.Offset + sizeof(DWORD),
&frame->AddrReturn.Offset, sizeof(DWORD)))
{
WARN("Cannot read new frame offset %08x\n", frame->AddrFrame.Offset + (int)sizeof(DWORD));
goto done_err;
}
sw_read_mem(cb, frame->AddrFrame.Offset + 2 * sizeof(DWORD),
frame->Params, sizeof(frame->Params));
}
frame->Far = TRUE;
frame->Virtual = TRUE;
p = sw_xlat_addr(cb, &frame->AddrPC);
if (p && sw_modl_bas(cb, p))
frame->FuncTableEntry = sw_tabl_acs(cb, p);
else
frame->FuncTableEntry = NULL;
TRACE("Leave: PC=%s Frame=%s Return=%s Stack=%s Mode=%s cSwitch=%08x nSwitch=%08x FuncTable=%p\n",
wine_dbgstr_addr(&frame->AddrPC),
wine_dbgstr_addr(&frame->AddrFrame),
wine_dbgstr_addr(&frame->AddrReturn),
wine_dbgstr_addr(&frame->AddrStack),
curr_mode == stm_start ? "start" : (curr_mode == stm_16bit ? "16bit" : "32bit"),
curr_switch, next_switch, frame->FuncTableEntry);
return TRUE;
done_err:
curr_mode = stm_done;
return FALSE;
}
/***********************************************************************
* StackWalk (DBGHELP.@)
*/
BOOL WINAPI StackWalk(DWORD MachineType, HANDLE hProcess, HANDLE hThread,
LPSTACKFRAME frame, LPVOID ctx,
PREAD_PROCESS_MEMORY_ROUTINE f_read_mem,
PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine,
PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine,
PTRANSLATE_ADDRESS_ROUTINE f_xlat_adr)
{
struct stack_walk_callback swcb;
TRACE("(%d, %p, %p, %p, %p, %p, %p, %p, %p)\n",
MachineType, hProcess, hThread, frame, ctx,
f_read_mem, FunctionTableAccessRoutine,
GetModuleBaseRoutine, f_xlat_adr);
if (MachineType != IMAGE_FILE_MACHINE_I386)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
swcb.hProcess = hProcess;
swcb.hThread = hThread;
swcb.is32 = TRUE;
/* sigh... MS isn't even consistent in the func prototypes */
swcb.u.s32.f_read_mem = (f_read_mem) ? f_read_mem : read_mem;
swcb.u.s32.f_xlat_adr = (f_xlat_adr) ? f_xlat_adr : addr_to_linear;
swcb.u.s32.f_tabl_acs = (FunctionTableAccessRoutine) ? FunctionTableAccessRoutine : SymFunctionTableAccess;
swcb.u.s32.f_modl_bas = (GetModuleBaseRoutine) ? GetModuleBaseRoutine : SymGetModuleBase;
return stack_walk(&swcb, frame);
}
/***********************************************************************
* StackWalk64 (DBGHELP.@)
*/
BOOL WINAPI StackWalk64(DWORD MachineType, HANDLE hProcess, HANDLE hThread,
LPSTACKFRAME64 frame64, LPVOID ctx,
PREAD_PROCESS_MEMORY_ROUTINE64 f_read_mem,
PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine,
PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine,
PTRANSLATE_ADDRESS_ROUTINE64 f_xlat_adr)
{
struct stack_walk_callback swcb;
STACKFRAME frame32;
BOOL ret;
TRACE("(%d, %p, %p, %p, %p, %p, %p, %p, %p)\n",
MachineType, hProcess, hThread, frame64, ctx,
f_read_mem, FunctionTableAccessRoutine,
GetModuleBaseRoutine, f_xlat_adr);
if (MachineType != IMAGE_FILE_MACHINE_I386)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
addr_64to32(&frame64->AddrPC, &frame32.AddrPC);
addr_64to32(&frame64->AddrReturn, &frame32.AddrReturn);
addr_64to32(&frame64->AddrFrame, &frame32.AddrFrame);
addr_64to32(&frame64->AddrStack, &frame32.AddrStack);
addr_64to32(&frame64->AddrBStore, &frame32.AddrBStore);
frame32.FuncTableEntry = frame64->FuncTableEntry; /* FIXME */
frame32.Far = frame64->Far;
frame32.Virtual = frame64->Virtual;
frame32.Reserved[0] = (ULONG)frame64->Reserved[0];
frame32.Reserved[1] = (ULONG)frame64->Reserved[1];
frame32.Reserved[2] = (ULONG)frame64->Reserved[2];
/* we don't handle KdHelp */
swcb.hProcess = hProcess;
swcb.hThread = hThread;
swcb.is32 = FALSE;
/* sigh... MS isn't even consistent in the func prototypes */
swcb.u.s64.f_read_mem = (f_read_mem) ? f_read_mem : read_mem64;
swcb.u.s64.f_xlat_adr = f_xlat_adr;
swcb.u.s64.f_tabl_acs = (FunctionTableAccessRoutine) ? FunctionTableAccessRoutine : SymFunctionTableAccess64;
swcb.u.s64.f_modl_bas = (GetModuleBaseRoutine) ? GetModuleBaseRoutine : SymGetModuleBase64;
ret = stack_walk(&swcb, &frame32);
addr_32to64(&frame32.AddrPC, &frame64->AddrPC);
addr_32to64(&frame32.AddrReturn, &frame64->AddrReturn);
addr_32to64(&frame32.AddrFrame, &frame64->AddrFrame);
addr_32to64(&frame32.AddrStack, &frame64->AddrStack);
addr_32to64(&frame32.AddrBStore, &frame64->AddrBStore);
frame64->FuncTableEntry = frame32.FuncTableEntry; /* FIXME */
frame64->Params[0] = (ULONG)frame32.Params[0];
frame64->Params[1] = (ULONG)frame32.Params[1];
frame64->Params[2] = (ULONG)frame32.Params[2];
frame64->Params[3] = (ULONG)frame32.Params[3];
frame64->Far = frame32.Far;
frame64->Virtual = frame32.Virtual;
frame64->Reserved[0] = (ULONG)frame32.Reserved[0];
frame64->Reserved[1] = (ULONG)frame32.Reserved[1];
frame64->Reserved[2] = (ULONG)frame32.Reserved[2];
/* we don't handle KdHelp */
frame64->KdHelp.Thread = 0xC000FADE;
frame64->KdHelp.ThCallbackStack = 0x10;
frame64->KdHelp.ThCallbackBStore = 0;
frame64->KdHelp.NextCallback = 0;
frame64->KdHelp.FramePointer = 0;
frame64->KdHelp.KiCallUserMode = 0xD000DAFE;
frame64->KdHelp.KeUserCallbackDispatcher = 0xE000F000;
frame64->KdHelp.SystemRangeStart = 0xC0000000;
frame64->KdHelp.Reserved[0] /* KiUserExceptionDispatcher */ = 0xE0005000;
return ret;
}
/******************************************************************
* SymRegisterFunctionEntryCallback (DBGHELP.@)
*
*
*/
BOOL WINAPI SymRegisterFunctionEntryCallback(HANDLE hProc,
PSYMBOL_FUNCENTRY_CALLBACK cb, PVOID user)
{
FIXME("(%p %p %p): stub!\n", hProc, cb, user);
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return FALSE;
}
/******************************************************************
* SymRegisterFunctionEntryCallback64 (DBGHELP.@)
*
*
*/
BOOL WINAPI SymRegisterFunctionEntryCallback64(HANDLE hProc,
PSYMBOL_FUNCENTRY_CALLBACK64 cb,
ULONG64 user)
{
FIXME("(%p %p %s): stub!\n", hProc, cb, wine_dbgstr_longlong(user));
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return FALSE;
}
--- NEW FILE: storage.c ---
/*
* Various storage structures (pool allocation, vector, hash table)
*
* Copyright (C) 1993, Eric Youngdale.
* 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 <assert.h>
#include <stdlib.h>
#include "wine/debug.h"
#include "dbghelp_private.h"
#ifdef USE_STATS
#include <math.h>
#endif
WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
struct pool_arena
{
struct pool_arena* next;
char* current;
};
void pool_init(struct pool* a, unsigned arena_size)
{
a->arena_size = arena_size;
a->first = NULL;
}
void pool_destroy(struct pool* pool)
{
struct pool_arena* arena;
struct pool_arena* next;
#ifdef USE_STATS
unsigned alloc, used, num;
alloc = used = num = 0;
arena = pool->first;
while (arena)
{
alloc += pool->arena_size;
used += arena->current - (char*)arena;
num++;
arena = arena->next;
}
if (alloc == 0) alloc = 1; /* avoid division by zero */
FIXME("STATS: pool %p has allocated %u kbytes, used %u kbytes in %u arenas,\n"
"\t\t\t\tnon-allocation ratio: %.2f%%\n",
pool, alloc >> 10, used >> 10, num, 100.0 - (float)used / (float)alloc * 100.0);
#endif
arena = pool->first;
while (arena)
{
next = arena->next;
HeapFree(GetProcessHeap(), 0, arena);
arena = next;
}
pool_init(pool, 0);
}
void* pool_alloc(struct pool* pool, unsigned len)
{
struct pool_arena* arena;
void* ret;
len = (len + 3) & ~3; /* round up size on DWORD boundary */
assert(sizeof(struct pool_arena) + len <= pool->arena_size && len);
for (arena = pool->first; arena; arena = arena->next)
{
if ((char*)arena + pool->arena_size - arena->current >= len)
{
ret = arena->current;
arena->current += len;
return ret;
}
}
arena = HeapAlloc(GetProcessHeap(), 0, pool->arena_size);
if (!arena) {FIXME("OOM\n");return NULL;}
ret = (char*)arena + sizeof(*arena);
arena->next = pool->first;
pool->first = arena;
arena->current = (char*)ret + len;
return ret;
}
char* pool_strdup(struct pool* pool, const char* str)
{
char* ret;
if ((ret = pool_alloc(pool, strlen(str) + 1))) strcpy(ret, str);
return ret;
}
void vector_init(struct vector* v, unsigned esz, unsigned bucket_sz)
{
v->buckets = NULL;
/* align size on DWORD boundaries */
v->elt_size = (esz + 3) & ~3;
switch (bucket_sz)
{
case 2: v->shift = 1; break;
case 4: v->shift = 2; break;
case 8: v->shift = 3; break;
case 16: v->shift = 4; break;
case 32: v->shift = 5; break;
case 64: v->shift = 6; break;
case 128: v->shift = 7; break;
case 256: v->shift = 8; break;
case 512: v->shift = 9; break;
case 1024: v->shift = 10; break;
default: assert(0);
}
v->num_buckets = 0;
v->buckets_allocated = 0;
v->num_elts = 0;
}
unsigned vector_length(const struct vector* v)
{
return v->num_elts;
}
void* vector_at(const struct vector* v, unsigned pos)
{
unsigned o;
if (pos >= v->num_elts) return NULL;
o = pos & ((1 << v->shift) - 1);
return (char*)v->buckets[pos >> v->shift] + o * v->elt_size;
}
void* vector_add(struct vector* v, struct pool* pool)
{
unsigned ncurr = v->num_elts++;
/* check that we don't wrap around */
assert(v->num_elts > ncurr);
if (ncurr == (v->num_buckets << v->shift))
{
if(v->num_buckets == v->buckets_allocated)
{
/* Double the bucket cache, so it scales well with big vectors.*/
unsigned new_reserved;
void* new;
new_reserved = 2*v->buckets_allocated;
if(new_reserved == 0) new_reserved = 1;
/* Don't even try to resize memory.
Pool datastructure is very inefficient with reallocs. */
new = pool_alloc(pool, new_reserved * sizeof(void*));
memcpy(new, v->buckets, v->buckets_allocated * sizeof(void*));
v->buckets = new;
v->buckets_allocated = new_reserved;
}
v->buckets[v->num_buckets] = pool_alloc(pool, v->elt_size << v->shift);
return v->buckets[v->num_buckets++];
}
return vector_at(v, ncurr);
}
/* We construct the sparse array as two vectors (of equal size)
* The first vector (key2index) is the lookup table between the key and
* an index in the second vector (elements)
* When inserting an element, it's always appended in second vector (and
* never moved in memory later on), only the first vector is reordered
*/
struct key2index
{
unsigned long key;
unsigned index;
};
void sparse_array_init(struct sparse_array* sa, unsigned elt_sz, unsigned bucket_sz)
{
vector_init(&sa->key2index, sizeof(struct key2index), bucket_sz);
vector_init(&sa->elements, elt_sz, bucket_sz);
}
/******************************************************************
* sparse_array_lookup
*
* Returns the first index which key is >= at passed key
*/
static struct key2index* sparse_array_lookup(const struct sparse_array* sa,
unsigned long key, unsigned* idx)
{
struct key2index* pk2i;
unsigned low, high;
if (!sa->elements.num_elts)
{
*idx = 0;
return NULL;
}
high = sa->elements.num_elts;
pk2i = vector_at(&sa->key2index, high - 1);
if (pk2i->key < key)
{
*idx = high;
return NULL;
}
if (pk2i->key == key)
{
*idx = high - 1;
return pk2i;
}
low = 0;
pk2i = vector_at(&sa->key2index, low);
if (pk2i->key >= key)
{
*idx = 0;
return pk2i;
}
/* now we have: sa(lowest key) < key < sa(highest key) */
while (low < high)
{
*idx = (low + high) / 2;
pk2i = vector_at(&sa->key2index, *idx);
if (pk2i->key > key) high = *idx;
else if (pk2i->key < key) low = *idx + 1;
else return pk2i;
}
/* binary search could return exact item, we search for highest one
* below the key
*/
if (pk2i->key < key)
pk2i = vector_at(&sa->key2index, ++(*idx));
return pk2i;
}
void* sparse_array_find(const struct sparse_array* sa, unsigned long key)
{
unsigned idx;
struct key2index* pk2i;
if ((pk2i = sparse_array_lookup(sa, key, &idx)) && pk2i->key == key)
return vector_at(&sa->elements, pk2i->index);
return NULL;
}
void* sparse_array_add(struct sparse_array* sa, unsigned long key,
struct pool* pool)
{
unsigned idx, i;
struct key2index* pk2i;
struct key2index* to;
pk2i = sparse_array_lookup(sa, key, &idx);
if (pk2i && pk2i->key == key)
{
FIXME("re adding an existing key\n");
return NULL;
}
to = vector_add(&sa->key2index, pool);
if (pk2i)
{
/* we need to shift vector's content... */
/* let's do it brute force... (FIXME) */
assert(sa->key2index.num_elts >= 2);
for (i = sa->key2index.num_elts - 1; i > idx; i--)
{
pk2i = vector_at(&sa->key2index, i - 1);
*to = *pk2i;
to = pk2i;
}
}
to->key = key;
to->index = sa->elements.num_elts;
return vector_add(&sa->elements, pool);
}
unsigned sparse_array_length(const struct sparse_array* sa)
{
return sa->elements.num_elts;
}
unsigned hash_table_hash(const char* name, unsigned num_buckets)
{
unsigned hash = 0;
while (*name)
{
hash += *name++;
hash += (hash << 10);
hash ^= (hash >> 6);
}
hash += (hash << 3);
hash ^= (hash >> 11);
hash += (hash << 15);
return hash % num_buckets;
}
void hash_table_init(struct pool* pool, struct hash_table* ht, unsigned num_buckets)
{
ht->num_elts = 0;
ht->num_buckets = num_buckets;
ht->pool = pool;
ht->buckets = NULL;
}
void hash_table_destroy(struct hash_table* ht)
{
#if defined(USE_STATS)
int i;
unsigned len;
unsigned min = 0xffffffff, max = 0, sq = 0;
struct hash_table_elt* elt;
double mean, variance;
for (i = 0; i < ht->num_buckets; i++)
{
for (len = 0, elt = ht->buckets[i]; elt; elt = elt->next) len++;
if (len < min) min = len;
if (len > max) max = len;
sq += len * len;
}
mean = (double)ht->num_elts / ht->num_buckets;
variance = (double)sq / ht->num_buckets - mean * mean;
FIXME("STATS: elts[num:%-4u size:%u mean:%f] buckets[min:%-4u variance:%+f max:%-4u]\n",
ht->num_elts, ht->num_buckets, mean, min, variance, max);
#if 1
for (i = 0; i < ht->num_buckets; i++)
{
for (len = 0, elt = ht->buckets[i]; elt; elt = elt->next) len++;
if (len == max)
{
FIXME("Longuest bucket:\n");
for (elt = ht->buckets[i]; elt; elt = elt->next)
FIXME("\t%s\n", elt->name);
break;
}
}
#endif
#endif
}
void hash_table_add(struct hash_table* ht, struct hash_table_elt* elt)
{
unsigned hash = hash_table_hash(elt->name, ht->num_buckets);
struct hash_table_elt** p;
if (!ht->buckets)
{
ht->buckets = pool_alloc(ht->pool, ht->num_buckets * sizeof(struct hash_table_elt*));
assert(ht->buckets);
memset(ht->buckets, 0, ht->num_buckets * sizeof(struct hash_table_elt*));
}
/* in some cases, we need to get back the symbols of same name in the order
* in which they've been inserted. So insert new elements at the end of the list.
*/
for (p = &ht->buckets[hash]; *p; p = &((*p)->next));
*p = elt;
elt->next = NULL;
ht->num_elts++;
}
void* hash_table_find(const struct hash_table* ht, const char* name)
{
unsigned hash = hash_table_hash(name, ht->num_buckets);
struct hash_table_elt* elt;
if(!ht->buckets) return NULL;
for (elt = ht->buckets[hash]; elt; elt = elt->next)
if (!strcmp(name, elt->name)) return elt;
return NULL;
}
void hash_table_iter_init(const struct hash_table* ht,
struct hash_table_iter* hti, const char* name)
{
hti->ht = ht;
if (name)
{
hti->last = hash_table_hash(name, ht->num_buckets);
hti->index = hti->last - 1;
}
else
{
hti->last = ht->num_buckets - 1;
hti->index = -1;
}
hti->element = NULL;
}
void* hash_table_iter_up(struct hash_table_iter* hti)
{
if(!hti->ht->buckets) return NULL;
if (hti->element) hti->element = hti->element->next;
while (!hti->element && hti->index < hti->last)
hti->element = hti->ht->buckets[++hti->index];
return hti->element;
}
--- NEW FILE: symbol.c ---
/*
* File symbol.c - management of symbols (lexical tree)
*
* Copyright (C) 1993, Eric Youngdale.
* 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
[...1614 lines suppressed...]
wine_dbgstr_longlong(Address), EnumSymbolsCallback,
UserContext, Options);
sew.ctx = UserContext;
sew.cb = EnumSymbolsCallback;
sew.sym_info = (PSYMBOL_INFOW)sew.buffer;
if (Mask)
{
unsigned len = WideCharToMultiByte(CP_ACP, 0, Mask, -1, NULL, 0, NULL, NULL);
maskA = HeapAlloc(GetProcessHeap(), 0, len);
if (!maskA) return FALSE;
WideCharToMultiByte(CP_ACP, 0, Mask, -1, maskA, len, NULL, NULL);
}
ret = SymSearch(hProcess, BaseOfDll, Index, SymTag, maskA, Address,
sym_enumW, &sew, Options);
HeapFree(GetProcessHeap(), 0, maskA);
return ret;
}
--- NEW FILE: type.c ---
/*
* File types.c - management of types (hierarchical tree)
*
* Copyright (C) 1997, Eric Youngdale.
* 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
*
* Note: This really doesn't do much at the moment, but it forms the framework
* upon which full support for datatype handling will eventually be built.
*/
#include "config.h"
#include <stdlib.h>
#include <stdarg.h>
#include <assert.h>
#include "windef.h"
#include "winbase.h"
#include "winnls.h"
#include "wine/debug.h"
#include "dbghelp_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
WINE_DECLARE_DEBUG_CHANNEL(dbghelp_symt);
static const char* symt_get_tag_str(DWORD tag)
{
switch (tag)
{
case SymTagNull: return "SymTagNull";
case SymTagExe: return "SymTagExe";
case SymTagCompiland: return "SymTagCompiland";
case SymTagCompilandDetails: return "SymTagCompilandDetails";
case SymTagCompilandEnv: return "SymTagCompilandEnv";
case SymTagFunction: return "SymTagFunction";
case SymTagBlock: return "SymTagBlock";
case SymTagData: return "SymTagData";
case SymTagAnnotation: return "SymTagAnnotation";
case SymTagLabel: return "SymTagLabel";
case SymTagPublicSymbol: return "SymTagPublicSymbol";
case SymTagUDT: return "SymTagUDT";
case SymTagEnum: return "SymTagEnum";
case SymTagFunctionType: return "SymTagFunctionType";
case SymTagPointerType: return "SymTagPointerType";
case SymTagArrayType: return "SymTagArrayType";
case SymTagBaseType: return "SymTagBaseType";
case SymTagTypedef: return "SymTagTypedef,";
case SymTagBaseClass: return "SymTagBaseClass";
case SymTagFriend: return "SymTagFriend";
case SymTagFunctionArgType: return "SymTagFunctionArgType,";
case SymTagFuncDebugStart: return "SymTagFuncDebugStart,";
case SymTagFuncDebugEnd: return "SymTagFuncDebugEnd";
case SymTagUsingNamespace: return "SymTagUsingNamespace,";
case SymTagVTableShape: return "SymTagVTableShape";
case SymTagVTable: return "SymTagVTable";
case SymTagCustom: return "SymTagCustom";
case SymTagThunk: return "SymTagThunk";
case SymTagCustomType: return "SymTagCustomType";
case SymTagManagedType: return "SymTagManagedType";
case SymTagDimension: return "SymTagDimension";
default: return "---";
}
}
const char* symt_get_name(const struct symt* sym)
{
switch (sym->tag)
{
/* lexical tree */
case SymTagData: return ((const struct symt_data*)sym)->hash_elt.name;
case SymTagFunction: return ((const struct symt_function*)sym)->hash_elt.name;
case SymTagPublicSymbol: return ((const struct symt_public*)sym)->hash_elt.name;
case SymTagBaseType: return ((const struct symt_basic*)sym)->hash_elt.name;
case SymTagLabel: return ((const struct symt_function_point*)sym)->name;
case SymTagThunk: return ((const struct symt_thunk*)sym)->hash_elt.name;
/* hierarchy tree */
case SymTagEnum: return ((const struct symt_enum*)sym)->name;
case SymTagTypedef: return ((const struct symt_typedef*)sym)->hash_elt.name;
case SymTagUDT: return ((const struct symt_udt*)sym)->hash_elt.name;
default:
FIXME("Unsupported sym-tag %s\n", symt_get_tag_str(sym->tag));
/* fall through */
case SymTagArrayType:
case SymTagPointerType:
case SymTagFunctionType:
return NULL;
}
}
static struct symt* symt_find_type_by_name(const struct module* module,
enum SymTagEnum sym_tag,
const char* typename)
{
void* ptr;
struct symt_ht* type;
struct hash_table_iter hti;
assert(typename);
assert(module);
hash_table_iter_init(&module->ht_types, &hti, typename);
while ((ptr = hash_table_iter_up(&hti)))
{
type = GET_ENTRY(ptr, struct symt_ht, hash_elt);
if ((sym_tag == SymTagNull || type->symt.tag == sym_tag) &&
type->hash_elt.name && !strcmp(type->hash_elt.name, typename))
return &type->symt;
}
SetLastError(ERROR_INVALID_NAME); /* FIXME ?? */
return NULL;
}
static void symt_add_type(struct module* module, struct symt* symt)
{
struct symt** p;
p = vector_add(&module->vtypes, &module->pool);
assert(p);
*p = symt;
}
struct symt_basic* symt_new_basic(struct module* module, enum BasicType bt,
const char* typename, unsigned size)
{
struct symt_basic* sym;
if (typename)
{
sym = (struct symt_basic*)symt_find_type_by_name(module, SymTagBaseType,
typename);
if (sym && sym->bt == bt && sym->size == size)
return sym;
}
if ((sym = pool_alloc(&module->pool, sizeof(*sym))))
{
sym->symt.tag = SymTagBaseType;
if (typename)
{
sym->hash_elt.name = pool_strdup(&module->pool, typename);
hash_table_add(&module->ht_types, &sym->hash_elt);
} else sym->hash_elt.name = NULL;
sym->bt = bt;
sym->size = size;
symt_add_type(module, &sym->symt);
}
return sym;
}
struct symt_udt* symt_new_udt(struct module* module, const char* typename,
unsigned size, enum UdtKind kind)
{
struct symt_udt* sym;
TRACE_(dbghelp_symt)("Adding udt %s:%s\n",
debugstr_w(module->module.ModuleName), typename);
if ((sym = pool_alloc(&module->pool, sizeof(*sym))))
{
sym->symt.tag = SymTagUDT;
sym->kind = kind;
sym->size = size;
if (typename)
{
sym->hash_elt.name = pool_strdup(&module->pool, typename);
hash_table_add(&module->ht_types, &sym->hash_elt);
} else sym->hash_elt.name = NULL;
vector_init(&sym->vchildren, sizeof(struct symt*), 8);
symt_add_type(module, &sym->symt);
}
return sym;
}
BOOL symt_set_udt_size(struct module* module, struct symt_udt* udt, unsigned size)
{
assert(udt->symt.tag == SymTagUDT);
if (vector_length(&udt->vchildren) != 0)
{
if (udt->size != size)
FIXME_(dbghelp_symt)("Changing size for %s from %u to %u\n",
udt->hash_elt.name, udt->size, size);
return TRUE;
}
udt->size = size;
return TRUE;
}
/******************************************************************
* symt_add_udt_element
*
* add an element to a udt (struct, class, union)
* the size & offset parameters are expressed in bits (not bytes) so that
* we can mix in the single call bytes aligned elements (regular fields) and
* the others (bit fields)
*/
BOOL symt_add_udt_element(struct module* module, struct symt_udt* udt_type,
const char* name, struct symt* elt_type,
unsigned offset, unsigned size)
{
struct symt_data* m;
struct symt** p;
assert(udt_type->symt.tag == SymTagUDT);
TRACE_(dbghelp_symt)("Adding %s to UDT %s\n", name, udt_type->hash_elt.name);
if (name)
{
int i;
for (i=0; i<vector_length(&udt_type->vchildren); i++)
{
m = *(struct symt_data**)vector_at(&udt_type->vchildren, i);
assert(m);
assert(m->symt.tag == SymTagData);
if (strcmp(m->hash_elt.name, name) == 0)
return TRUE;
}
}
if ((m = pool_alloc(&module->pool, sizeof(*m))) == NULL) return FALSE;
memset(m, 0, sizeof(*m));
m->symt.tag = SymTagData;
m->hash_elt.name = name ? pool_strdup(&module->pool, name) : "";
m->hash_elt.next = NULL;
m->kind = DataIsMember;
m->container = &udt_type->symt;
m->type = elt_type;
m->u.member.offset = offset;
m->u.member.length = ((offset & 7) || (size & 7)) ? size : 0;
p = vector_add(&udt_type->vchildren, &module->pool);
*p = &m->symt;
return TRUE;
}
struct symt_enum* symt_new_enum(struct module* module, const char* typename)
{
struct symt_enum* sym;
if ((sym = pool_alloc(&module->pool, sizeof(*sym))))
{
sym->symt.tag = SymTagEnum;
sym->name = (typename) ? pool_strdup(&module->pool, typename) : NULL;
vector_init(&sym->vchildren, sizeof(struct symt*), 8);
}
return sym;
}
BOOL symt_add_enum_element(struct module* module, struct symt_enum* enum_type,
const char* name, int value)
{
struct symt_data* e;
struct symt** p;
assert(enum_type->symt.tag == SymTagEnum);
e = pool_alloc(&module->pool, sizeof(*e));
if (e == NULL) return FALSE;
e->symt.tag = SymTagData;
e->hash_elt.name = pool_strdup(&module->pool, name);
e->hash_elt.next = NULL;
e->kind = DataIsConstant;
e->container = &enum_type->symt;
/* CV defines the underlying type for the enumeration */
e->type = &symt_new_basic(module, btInt, "int", 4)->symt;
e->u.value.n1.n2.vt = VT_I4;
e->u.value.n1.n2.n3.lVal = value;
p = vector_add(&enum_type->vchildren, &module->pool);
if (!p) return FALSE; /* FIXME we leak e */
*p = &e->symt;
return TRUE;
}
struct symt_array* symt_new_array(struct module* module, int min, int max,
struct symt* base, struct symt* index)
{
struct symt_array* sym;
if ((sym = pool_alloc(&module->pool, sizeof(*sym))))
{
sym->symt.tag = SymTagArrayType;
sym->start = min;
sym->end = max;
sym->base_type = base;
sym->index_type = index;
symt_add_type(module, &sym->symt);
}
return sym;
}
struct symt_function_signature* symt_new_function_signature(struct module* module,
struct symt* ret_type,
enum CV_call_e call_conv)
{
struct symt_function_signature* sym;
if ((sym = pool_alloc(&module->pool, sizeof(*sym))))
{
sym->symt.tag = SymTagFunctionType;
sym->rettype = ret_type;
vector_init(&sym->vchildren, sizeof(struct symt*), 4);
sym->call_conv = call_conv;
symt_add_type(module, &sym->symt);
}
return sym;
}
BOOL symt_add_function_signature_parameter(struct module* module,
struct symt_function_signature* sig_type,
struct symt* param)
{
struct symt** p;
struct symt_function_arg_type* arg;
assert(sig_type->symt.tag == SymTagFunctionType);
arg = pool_alloc(&module->pool, sizeof(*arg));
if (!arg) return FALSE;
arg->symt.tag = SymTagFunctionArgType;
arg->arg_type = param;
arg->container = &sig_type->symt;
p = vector_add(&sig_type->vchildren, &module->pool);
if (!p) return FALSE; /* FIXME we leak arg */
*p = &arg->symt;
return TRUE;
}
struct symt_pointer* symt_new_pointer(struct module* module, struct symt* ref_type)
{
struct symt_pointer* sym;
if ((sym = pool_alloc(&module->pool, sizeof(*sym))))
{
sym->symt.tag = SymTagPointerType;
sym->pointsto = ref_type;
symt_add_type(module, &sym->symt);
}
return sym;
}
struct symt_typedef* symt_new_typedef(struct module* module, struct symt* ref,
const char* name)
{
struct symt_typedef* sym;
if ((sym = pool_alloc(&module->pool, sizeof(*sym))))
{
sym->symt.tag = SymTagTypedef;
sym->type = ref;
sym->hash_elt.name = pool_strdup(&module->pool, name);
hash_table_add(&module->ht_types, &sym->hash_elt);
symt_add_type(module, &sym->symt);
}
return sym;
}
/******************************************************************
* SymEnumTypes (DBGHELP.@)
*
*/
BOOL WINAPI SymEnumTypes(HANDLE hProcess, ULONG64 BaseOfDll,
PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback,
PVOID UserContext)
{
struct module_pair pair;
char buffer[sizeof(SYMBOL_INFO) + 256];
SYMBOL_INFO* sym_info = (SYMBOL_INFO*)buffer;
const char* tmp;
struct symt* type;
DWORD64 size;
int i;
TRACE("(%p %s %p %p)\n",
hProcess, wine_dbgstr_longlong(BaseOfDll), EnumSymbolsCallback,
UserContext);
if (!(pair.pcs = process_find_by_handle(hProcess))) return FALSE;
pair.requested = module_find_by_addr(pair.pcs, BaseOfDll, DMT_UNKNOWN);
if (!module_get_debug(&pair)) return FALSE;
sym_info->SizeOfStruct = sizeof(SYMBOL_INFO);
sym_info->MaxNameLen = sizeof(buffer) - sizeof(SYMBOL_INFO);
for (i=0; i<vector_length(&pair.effective->vtypes); i++)
{
type = *(struct symt**)vector_at(&pair.effective->vtypes, i);
sym_info->TypeIndex = (DWORD)type;
sym_info->info = 0; /* FIXME */
symt_get_info(type, TI_GET_LENGTH, &size);
sym_info->Size = size;
sym_info->ModBase = pair.requested->module.BaseOfImage;
sym_info->Flags = 0; /* FIXME */
sym_info->Value = 0; /* FIXME */
sym_info->Address = 0; /* FIXME */
sym_info->Register = 0; /* FIXME */
sym_info->Scope = 0; /* FIXME */
sym_info->Tag = type->tag;
tmp = symt_get_name(type);
if (tmp)
{
sym_info->NameLen = min(strlen(tmp),sym_info->MaxNameLen-1);
memcpy(sym_info->Name, tmp, sym_info->NameLen);
sym_info->Name[sym_info->NameLen] = '\0';
}
else
sym_info->Name[sym_info->NameLen = 0] = '\0';
if (!EnumSymbolsCallback(sym_info, sym_info->Size, UserContext)) break;
}
return TRUE;
}
struct enum_types_AtoW
{
char buffer[sizeof(SYMBOL_INFOW) + 256 * sizeof(WCHAR)];
void* user;
PSYM_ENUMERATESYMBOLS_CALLBACKW callback;
};
BOOL CALLBACK enum_types_AtoW(PSYMBOL_INFO si, ULONG addr, PVOID _et)
{
struct enum_types_AtoW* et = _et;
SYMBOL_INFOW* siW = (SYMBOL_INFOW*)et->buffer;
copy_symbolW(siW, si);
return et->callback(siW, addr, et->user);
}
/******************************************************************
* SymEnumTypesW (DBGHELP.@)
*
*/
BOOL WINAPI SymEnumTypesW(HANDLE hProcess, ULONG64 BaseOfDll,
PSYM_ENUMERATESYMBOLS_CALLBACKW EnumSymbolsCallback,
PVOID UserContext)
{
struct enum_types_AtoW et;
et.callback = EnumSymbolsCallback;
et.user = UserContext;
return SymEnumTypes(hProcess, BaseOfDll, enum_types_AtoW, &et);
}
/******************************************************************
* symt_get_info
*
* Retrieves information about a symt (either symbol or type)
*/
BOOL symt_get_info(const struct symt* type, IMAGEHLP_SYMBOL_TYPE_INFO req,
void* pInfo)
{
unsigned len;
if (!type) return FALSE;
/* helper to typecast pInfo to its expected type (_t) */
#define X(_t) (*((_t*)pInfo))
switch (req)
{
case TI_FINDCHILDREN:
{
const struct vector* v;
struct symt** pt;
unsigned i;
TI_FINDCHILDREN_PARAMS* tifp = pInfo;
switch (type->tag)
{
case SymTagUDT: v = &((const struct symt_udt*)type)->vchildren; break;
case SymTagEnum: v = &((const struct symt_enum*)type)->vchildren; break;
case SymTagFunctionType: v = &((const struct symt_function_signature*)type)->vchildren; break;
case SymTagFunction: v = &((const struct symt_function*)type)->vchildren; break;
default:
FIXME("Unsupported sym-tag %s for find-children\n",
symt_get_tag_str(type->tag));
return FALSE;
}
for (i = 0; i < tifp->Count; i++)
{
if (!(pt = vector_at(v, tifp->Start + i))) return FALSE;
tifp->ChildId[i] = (DWORD)*pt;
}
}
break;
case TI_GET_ADDRESS:
switch (type->tag)
{
case SymTagData:
switch (((const struct symt_data*)type)->kind)
{
case DataIsGlobal:
case DataIsFileStatic:
X(ULONG64) = ((const struct symt_data*)type)->u.var.offset;
break;
default: return FALSE;
}
break;
case SymTagFunction:
X(ULONG64) = ((const struct symt_function*)type)->address;
break;
case SymTagPublicSymbol:
X(ULONG64) = ((const struct symt_public*)type)->address;
break;
case SymTagFuncDebugStart:
case SymTagFuncDebugEnd:
case SymTagLabel:
X(ULONG64) = ((const struct symt_function_point*)type)->parent->address +
((const struct symt_function_point*)type)->loc.offset;
break;
case SymTagThunk:
X(ULONG64) = ((const struct symt_thunk*)type)->address;
break;
case SymTagCompiland:
X(ULONG64) = ((const struct symt_compiland*)type)->address;
break;
default:
FIXME("Unsupported sym-tag %s for get-address\n",
symt_get_tag_str(type->tag));
return FALSE;
}
break;
case TI_GET_BASETYPE:
switch (type->tag)
{
case SymTagBaseType:
X(DWORD) = ((const struct symt_basic*)type)->bt;
break;
case SymTagEnum:
X(DWORD) = btInt;
break;
default:
return FALSE;
}
break;
case TI_GET_BITPOSITION:
if (type->tag == SymTagData &&
((const struct symt_data*)type)->kind == DataIsMember &&
((const struct symt_data*)type)->u.member.length != 0)
X(DWORD) = ((const struct symt_data*)type)->u.member.offset & 7;
else return FALSE;
break;
case TI_GET_CHILDRENCOUNT:
switch (type->tag)
{
case SymTagUDT:
X(DWORD) = vector_length(&((const struct symt_udt*)type)->vchildren);
break;
case SymTagEnum:
X(DWORD) = vector_length(&((const struct symt_enum*)type)->vchildren);
break;
case SymTagFunctionType:
X(DWORD) = vector_length(&((const struct symt_function_signature*)type)->vchildren);
break;
case SymTagFunction:
X(DWORD) = vector_length(&((const struct symt_function*)type)->vchildren);
break;
case SymTagPointerType: /* MS does it that way */
case SymTagArrayType: /* MS does it that way */
case SymTagThunk: /* MS does it that way */
X(DWORD) = 0;
break;
default:
FIXME("Unsupported sym-tag %s for get-children-count\n",
symt_get_tag_str(type->tag));
/* fall through */
case SymTagData:
case SymTagPublicSymbol:
case SymTagBaseType:
return FALSE;
}
break;
case TI_GET_COUNT:
switch (type->tag)
{
case SymTagArrayType:
X(DWORD) = ((const struct symt_array*)type)->end -
((const struct symt_array*)type)->start + 1;
break;
case SymTagFunctionType:
/* this seems to be wrong for (future) C++ methods, where 'this' parameter
* should be included in this value (and not in GET_CHILDREN_COUNT)
*/
X(DWORD) = vector_length(&((const struct symt_function_signature*)type)->vchildren);
break;
default: return FALSE;
}
break;
case TI_GET_DATAKIND:
if (type->tag != SymTagData) return FALSE;
X(DWORD) = ((const struct symt_data*)type)->kind;
break;
case TI_GET_LENGTH:
switch (type->tag)
{
case SymTagBaseType:
X(DWORD64) = ((const struct symt_basic*)type)->size;
break;
case SymTagFunction:
X(DWORD64) = ((const struct symt_function*)type)->size;
break;
case SymTagPointerType:
X(DWORD64) = sizeof(void*);
break;
case SymTagUDT:
X(DWORD64) = ((const struct symt_udt*)type)->size;
break;
case SymTagEnum:
X(DWORD64) = sizeof(int); /* FIXME: should be size of base-type of enum !!! */
break;
case SymTagData:
if (((const struct symt_data*)type)->kind != DataIsMember ||
!((const struct symt_data*)type)->u.member.length)
return FALSE;
X(DWORD64) = ((const struct symt_data*)type)->u.member.length;
break;
case SymTagArrayType:
if (!symt_get_info(((const struct symt_array*)type)->base_type,
TI_GET_LENGTH, pInfo))
return FALSE;
X(DWORD64) *= ((const struct symt_array*)type)->end -
((const struct symt_array*)type)->start + 1;
break;
case SymTagPublicSymbol:
X(DWORD64) = ((const struct symt_public*)type)->size;
break;
case SymTagTypedef:
return symt_get_info(((const struct symt_typedef*)type)->type, TI_GET_LENGTH, pInfo);
case SymTagThunk:
X(DWORD64) = ((const struct symt_thunk*)type)->size;
break;
default:
FIXME("Unsupported sym-tag %s for get-length\n",
symt_get_tag_str(type->tag));
/* fall through */
case SymTagFunctionType:
return 0;
}
break;
case TI_GET_LEXICALPARENT:
switch (type->tag)
{
case SymTagBlock:
X(DWORD) = (DWORD)((const struct symt_block*)type)->container;
break;
case SymTagData:
X(DWORD) = (DWORD)((const struct symt_data*)type)->container;
break;
case SymTagFunction:
X(DWORD) = (DWORD)((const struct symt_function*)type)->container;
break;
case SymTagThunk:
X(DWORD) = (DWORD)((const struct symt_thunk*)type)->container;
break;
case SymTagFunctionArgType:
X(DWORD) = (DWORD)((const struct symt_function_arg_type*)type)->container;
break;
default:
FIXME("Unsupported sym-tag %s for get-lexical-parent\n",
symt_get_tag_str(type->tag));
return FALSE;
}
break;
case TI_GET_NESTED:
switch (type->tag)
{
case SymTagUDT:
case SymTagEnum:
X(DWORD) = 0;
break;
default:
return FALSE;
}
break;
case TI_GET_OFFSET:
switch (type->tag)
{
case SymTagData:
switch (((const struct symt_data*)type)->kind)
{
case DataIsParam:
case DataIsLocal:
X(ULONG) = ((const struct symt_data*)type)->u.var.offset;
break;
case DataIsMember:
X(ULONG) = ((const struct symt_data*)type)->u.member.offset >> 3;
break;
default:
FIXME("Unknown kind (%u) for get-offset\n",
((const struct symt_data*)type)->kind);
return FALSE;
}
break;
default:
FIXME("Unsupported sym-tag %s for get-offset\n",
symt_get_tag_str(type->tag));
return FALSE;
}
break;
case TI_GET_SYMNAME:
{
const char* name = symt_get_name(type);
if (!name) return FALSE;
len = MultiByteToWideChar(CP_ACP, 0, name, -1, NULL, 0);
X(WCHAR*) = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
if (!X(WCHAR*)) return FALSE;
MultiByteToWideChar(CP_ACP, 0, name, -1, X(WCHAR*), len);
}
break;
case TI_GET_SYMTAG:
X(DWORD) = type->tag;
break;
case TI_GET_TYPE:
case TI_GET_TYPEID:
switch (type->tag)
{
/* hierarchical => hierarchical */
case SymTagArrayType:
X(DWORD) = (DWORD)((const struct symt_array*)type)->base_type;
break;
case SymTagPointerType:
X(DWORD) = (DWORD)((const struct symt_pointer*)type)->pointsto;
break;
case SymTagFunctionType:
X(DWORD) = (DWORD)((const struct symt_function_signature*)type)->rettype;
break;
case SymTagTypedef:
X(DWORD) = (DWORD)((const struct symt_typedef*)type)->type;
break;
/* lexical => hierarchical */
case SymTagData:
X(DWORD) = (DWORD)((const struct symt_data*)type)->type;
break;
case SymTagFunction:
X(DWORD) = (DWORD)((const struct symt_function*)type)->type;
break;
/* FIXME: should also work for enums */
case SymTagFunctionArgType:
X(DWORD) = (DWORD)((const struct symt_function_arg_type*)type)->arg_type;
break;
default:
FIXME("Unsupported sym-tag %s for get-type\n",
symt_get_tag_str(type->tag));
case SymTagPublicSymbol:
case SymTagThunk:
return FALSE;
}
break;
case TI_GET_UDTKIND:
if (type->tag != SymTagUDT) return FALSE;
X(DWORD) = ((const struct symt_udt*)type)->kind;
break;
case TI_GET_VALUE:
if (type->tag != SymTagData || ((const struct symt_data*)type)->kind != DataIsConstant)
return FALSE;
X(VARIANT) = ((const struct symt_data*)type)->u.value;
break;
case TI_GET_CALLING_CONVENTION:
if (type->tag != SymTagFunctionType) return FALSE;
if (((const struct symt_function_signature*)type)->call_conv == -1)
{
FIXME("No support for calling convention for this signature\n");
X(DWORD) = CV_CALL_FAR_C; /* FIXME */
}
else X(DWORD) = ((const struct symt_function_signature*)type)->call_conv;
break;
case TI_GET_ARRAYINDEXTYPEID:
if (type->tag != SymTagArrayType) return FALSE;
X(DWORD) = (DWORD)((const struct symt_array*)type)->index_type;
break;
#undef X
case TI_GET_ADDRESSOFFSET:
case TI_GET_CLASSPARENTID:
case TI_GET_SYMINDEX:
case TI_GET_THISADJUST:
case TI_GET_VIRTUALBASECLASS:
case TI_GET_VIRTUALBASEPOINTEROFFSET:
case TI_GET_VIRTUALTABLESHAPEID:
case TI_IS_EQUIV_TO:
FIXME("Unsupported GetInfo request (%u)\n", req);
return FALSE;
default:
FIXME("Unknown GetInfo request (%u)\n", req);
return FALSE;
}
return TRUE;
}
/******************************************************************
* SymGetTypeInfo (DBGHELP.@)
*
*/
BOOL WINAPI SymGetTypeInfo(HANDLE hProcess, DWORD64 ModBase,
ULONG TypeId, IMAGEHLP_SYMBOL_TYPE_INFO GetType,
PVOID pInfo)
{
struct module_pair pair;
pair.pcs = process_find_by_handle(hProcess);
if (!pair.pcs) return FALSE;
pair.requested = module_find_by_addr(pair.pcs, ModBase, DMT_UNKNOWN);
if (!module_get_debug(&pair))
{
FIXME("Someone didn't properly set ModBase (%s)\n", wine_dbgstr_longlong(ModBase));
return FALSE;
}
return symt_get_info((struct symt*)TypeId, GetType, pInfo);
}
/******************************************************************
* SymGetTypeFromName (DBGHELP.@)
*
*/
BOOL WINAPI SymGetTypeFromName(HANDLE hProcess, ULONG64 BaseOfDll,
PCSTR Name, PSYMBOL_INFO Symbol)
{
struct process* pcs = process_find_by_handle(hProcess);
struct module* module;
struct symt* type;
if (!pcs) return FALSE;
module = module_find_by_addr(pcs, BaseOfDll, DMT_UNKNOWN);
if (!module) return FALSE;
type = symt_find_type_by_name(module, SymTagNull, Name);
if (!type) return FALSE;
Symbol->TypeIndex = (DWORD)type;
return TRUE;
}
--- NEW FILE: wdbgexts.h ---
/*
* File wdbgexts.h: definition of windbg extensions
* (dbghelp.dll is seen as a windbg extension)
*
* Copyright (C) 2005, 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
*/
typedef struct EXT_API_VERSION
{
USHORT MajorVersion;
USHORT MinorVersion;
USHORT Revision;
USHORT Reserved;
} EXT_API_VERSION, *LPEXT_API_VERSION;
typedef void (*PWINDBG_OUTPUT_ROUTINE)(PCSTR, ...);
typedef ULONG_PTR (WINAPI *PWINDBG_GET_EXPRESSION)(PCSTR);
typedef void (WINAPI *PWINDBG_GET_SYMBOL)(void*, char*, ULONG_PTR*);
typedef ULONG (WINAPI *PWINDBG_DISASM)(ULONG_PTR*, PCSTR, ULONG);
typedef ULONG (WINAPI *PWINDBG_CHECK_CONTROL_C)(void);
typedef ULONG (WINAPI *PWINDBG_READ_PROCESS_MEMORY_ROUTINE)(ULONG_PTR, void*, ULONG, PULONG);
typedef ULONG (WINAPI *PWINDBG_WRITE_PROCESS_MEMORY_ROUTINE)(ULONG_PTR, const void*, ULONG, PULONG);
typedef ULONG (WINAPI *PWINDBG_GET_THREAD_CONTEXT_ROUTINE)(ULONG, PCONTEXT, ULONG);
typedef ULONG (WINAPI *PWINDBG_SET_THREAD_CONTEXT_ROUTINE)(ULONG, PCONTEXT, ULONG);
typedef ULONG (WINAPI *PWINDBG_IOCTL_ROUTINE)(USHORT, void*);
typedef struct _EXTSTACKTRACE
{
ULONG FramePointer;
ULONG ProgramCounter;
ULONG ReturnAddress;
ULONG Args[4];
} EXTSTACKTRACE, *PEXTSTACKTRACE;
typedef ULONG (WINAPI *PWINDBG_STACKTRACE_ROUTINE)(ULONG, ULONG, ULONG, PEXTSTACKTRACE, ULONG);
typedef struct _WINDBG_EXTENSION_APIS
{
ULONG nSize;
PWINDBG_OUTPUT_ROUTINE lpOutputRoutine;
PWINDBG_GET_EXPRESSION lpGetExpressionRoutine;
PWINDBG_GET_SYMBOL lpGetSymbolRoutine;
PWINDBG_DISASM lpDisasmRoutine;
PWINDBG_CHECK_CONTROL_C lpCheckControlCRoutine;
PWINDBG_READ_PROCESS_MEMORY_ROUTINE lpReadProcessMemoryRoutine;
PWINDBG_WRITE_PROCESS_MEMORY_ROUTINE lpWriteProcessMemoryRoutine;
PWINDBG_GET_THREAD_CONTEXT_ROUTINE lpGetThreadContextRoutine;
PWINDBG_SET_THREAD_CONTEXT_ROUTINE lpSetThreadContextRoutine;
PWINDBG_IOCTL_ROUTINE lpIoctlRoutine;
PWINDBG_STACKTRACE_ROUTINE lpStackTraceRoutine;
} WINDBG_EXTENSION_APIS, *PWINDBG_EXTENSION_APIS;