Commit 800864a0 authored by Eric Pouech's avatar Eric Pouech Committed by Alexandre Julliard

First shot at implementing dbghelp.

parent 7e301d8a
......@@ -3128,9 +3128,12 @@ ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $
ac_compiler_gnu=$ac_cv_c_compiler_gnu
echo "$as_me:$LINENO: checking for X" >&5
if test "x$ac_path_x_has_been_run" != xyes; then
echo "$as_me:$LINENO: checking for X" >&5
echo $ECHO_N "checking for X... $ECHO_C" >&6
ac_path_x_has_been_run=yes
# Check whether --with-x or --without-x was given.
if test "${with_x+set}" = set; then
......@@ -3223,7 +3226,7 @@ ac_x_header_dirs='
/usr/openwin/share/include'
if test "$ac_x_includes" = no; then
# Guess where to find include files, by looking for Intrinsic.h.
# Guess where to find include files, by looking for a specified header file.
# First, try using that file with no special directory specified.
cat >conftest.$ac_ext <<_ACEOF
/* confdefs.h. */
......@@ -3357,10 +3360,15 @@ else
# Update the cache value to reflect the command line values.
ac_cv_have_x="have_x=yes \
ac_x_includes=$x_includes ac_x_libraries=$x_libraries"
echo "$as_me:$LINENO: result: libraries $x_libraries, headers $x_includes" >&5
echo "${ECHO_T}libraries $x_libraries, headers $x_includes" >&6
# It might be that x_includes is empty (headers are found in the
# standard search path. Then output the corresponding message
ac_out_x_includes=$x_includes
test "x$x_includes" = x && ac_out_x_includes="in standard search path"
echo "$as_me:$LINENO: result: libraries $x_libraries, headers $ac_out_x_includes" >&5
echo "${ECHO_T}libraries $x_libraries, headers $ac_out_x_includes" >&6
fi
fi
if test "$no_x" = yes; then
# Not all programs may use this symbol, but it does not hurt to define it.
......@@ -19964,7 +19972,7 @@ MAKE_LIB_RULES=libs/Makelib.rules
MAKE_PROG_RULES=programs/Makeprog.rules
ac_config_files="$ac_config_files Make.rules dlls/Makedll.rules dlls/Maketest.rules libs/Makelib.rules programs/Makeprog.rules Makefile dlls/Makefile dlls/advapi32/Makefile dlls/advapi32/tests/Makefile dlls/amstream/Makefile dlls/avicap32/Makefile dlls/avifil32/Makefile dlls/cabinet/Makefile dlls/capi2032/Makefile dlls/cards/Makefile dlls/cfgmgr32/Makefile dlls/comcat/Makefile dlls/comctl32/Makefile dlls/comctl32/tests/Makefile dlls/commdlg/Makefile dlls/crtdll/Makefile dlls/crypt32/Makefile dlls/ctl3d/Makefile dlls/d3d8/Makefile dlls/d3d9/Makefile dlls/d3dim/Makefile dlls/d3dx8/Makefile dlls/dciman32/Makefile dlls/ddraw/Makefile dlls/ddraw/tests/Makefile dlls/devenum/Makefile dlls/dinput/Makefile dlls/dinput8/Makefile dlls/dmband/Makefile dlls/dmcompos/Makefile dlls/dmime/Makefile dlls/dmloader/Makefile dlls/dmscript/Makefile dlls/dmstyle/Makefile dlls/dmsynth/Makefile dlls/dmusic/Makefile dlls/dmusic32/Makefile dlls/dplay/Makefile dlls/dplayx/Makefile dlls/dpnhpast/Makefile dlls/dsound/Makefile dlls/dsound/tests/Makefile dlls/dswave/Makefile dlls/dxerr8/Makefile dlls/dxerr9/Makefile dlls/dxguid/Makefile dlls/gdi/Makefile dlls/gdi/tests/Makefile dlls/glu32/Makefile dlls/glut32/Makefile dlls/iccvid/Makefile dlls/icmp/Makefile dlls/ifsmgr.vxd/Makefile dlls/imagehlp/Makefile dlls/imm32/Makefile dlls/iphlpapi/Makefile dlls/iphlpapi/tests/Makefile dlls/kernel/Makefile dlls/kernel/tests/Makefile dlls/lzexpand/Makefile dlls/mapi32/Makefile dlls/mmdevldr.vxd/Makefile dlls/monodebg.vxd/Makefile dlls/mpr/Makefile dlls/msacm/Makefile dlls/msacm/imaadp32/Makefile dlls/msacm/msadp32/Makefile dlls/msacm/msg711/Makefile dlls/msacm/winemp3/Makefile dlls/msdmo/Makefile dlls/mshtml/Makefile dlls/msi/Makefile dlls/msimg32/Makefile dlls/msisys/Makefile dlls/msnet32/Makefile dlls/msrle32/Makefile dlls/msvcrt/Makefile dlls/msvcrt/tests/Makefile dlls/msvcrt20/Makefile dlls/msvcrt40/Makefile dlls/msvcrtd/Makefile dlls/msvidc32/Makefile dlls/msvideo/Makefile dlls/mswsock/Makefile dlls/netapi32/Makefile dlls/netapi32/tests/Makefile dlls/newdev/Makefile dlls/ntdll/Makefile dlls/ntdll/tests/Makefile dlls/odbc32/Makefile dlls/ole32/Makefile dlls/oleacc/Makefile dlls/oleaut32/Makefile dlls/oleaut32/tests/Makefile dlls/olecli/Makefile dlls/oledlg/Makefile dlls/olepro32/Makefile dlls/olesvr/Makefile dlls/opengl32/Makefile dlls/psapi/Makefile dlls/qcap/Makefile dlls/quartz/Makefile dlls/rasapi32/Makefile dlls/richedit/Makefile dlls/rpcrt4/Makefile dlls/rpcrt4/tests/Makefile dlls/rsabase/Makefile dlls/secur32/Makefile dlls/serialui/Makefile dlls/setupapi/Makefile dlls/shdocvw/Makefile dlls/shell32/Makefile dlls/shell32/tests/Makefile dlls/shfolder/Makefile dlls/shlwapi/Makefile dlls/shlwapi/tests/Makefile dlls/snmpapi/Makefile dlls/sti/Makefile dlls/tapi32/Makefile dlls/ttydrv/Makefile dlls/twain/Makefile dlls/unicows/Makefile dlls/url/Makefile dlls/urlmon/Makefile dlls/urlmon/tests/Makefile dlls/user/Makefile dlls/user/tests/Makefile dlls/uuid/Makefile dlls/uxtheme/Makefile dlls/vdhcp.vxd/Makefile dlls/version/Makefile dlls/vmm.vxd/Makefile dlls/vnbt.vxd/Makefile dlls/vnetbios.vxd/Makefile dlls/vtdapi.vxd/Makefile dlls/vwin32.vxd/Makefile dlls/win32s/Makefile dlls/winaspi/Makefile dlls/wined3d/Makefile dlls/winedos/Makefile dlls/wineps/Makefile dlls/wininet/Makefile dlls/wininet/tests/Makefile dlls/winmm/Makefile dlls/winmm/joystick/Makefile dlls/winmm/mcianim/Makefile dlls/winmm/mciavi/Makefile dlls/winmm/mcicda/Makefile dlls/winmm/mciseq/Makefile dlls/winmm/mciwave/Makefile dlls/winmm/midimap/Makefile dlls/winmm/tests/Makefile dlls/winmm/wavemap/Makefile dlls/winmm/winealsa/Makefile dlls/winmm/winearts/Makefile dlls/winmm/wineaudioio/Makefile dlls/winmm/winejack/Makefile dlls/winmm/winenas/Makefile dlls/winmm/wineoss/Makefile dlls/winnls/Makefile dlls/winsock/Makefile dlls/winsock/tests/Makefile dlls/winspool/Makefile dlls/winspool/tests/Makefile dlls/wintab32/Makefile dlls/wintrust/Makefile dlls/wow32/Makefile dlls/wsock32/Makefile dlls/x11drv/Makefile documentation/Makefile include/Makefile libs/Makefile libs/port/Makefile libs/unicode/Makefile libs/wine/Makefile libs/wpp/Makefile loader/Makefile programs/Makefile programs/avitools/Makefile programs/clock/Makefile programs/cmdlgtst/Makefile programs/control/Makefile programs/expand/Makefile programs/notepad/Makefile programs/progman/Makefile programs/regedit/Makefile programs/regsvr32/Makefile programs/rpcss/Makefile programs/rundll32/Makefile programs/start/Makefile programs/taskmgr/Makefile programs/uninstaller/Makefile programs/view/Makefile programs/wcmd/Makefile programs/wineboot/Makefile programs/winebrowser/Makefile programs/winecfg/Makefile programs/wineconsole/Makefile programs/winedbg/Makefile programs/winefile/Makefile programs/winemenubuilder/Makefile programs/winemine/Makefile programs/winepath/Makefile programs/winetest/Makefile programs/winevdm/Makefile programs/winhelp/Makefile programs/winver/Makefile server/Makefile tools/Makefile tools/widl/Makefile tools/winapi/Makefile tools/winebuild/Makefile tools/winedump/Makefile tools/winegcc/Makefile tools/wmc/Makefile tools/wrc/Makefile"
ac_config_files="$ac_config_files Make.rules dlls/Makedll.rules dlls/Maketest.rules libs/Makelib.rules programs/Makeprog.rules Makefile dlls/Makefile dlls/advapi32/Makefile dlls/advapi32/tests/Makefile dlls/amstream/Makefile dlls/avicap32/Makefile dlls/avifil32/Makefile dlls/cabinet/Makefile dlls/capi2032/Makefile dlls/cards/Makefile dlls/cfgmgr32/Makefile dlls/comcat/Makefile dlls/comctl32/Makefile dlls/comctl32/tests/Makefile dlls/commdlg/Makefile dlls/crtdll/Makefile dlls/crypt32/Makefile dlls/ctl3d/Makefile dlls/d3d8/Makefile dlls/d3d9/Makefile dlls/d3dim/Makefile dlls/d3dx8/Makefile dlls/dbghelp/Makefile dlls/dciman32/Makefile dlls/ddraw/Makefile dlls/ddraw/tests/Makefile dlls/devenum/Makefile dlls/dinput/Makefile dlls/dinput8/Makefile dlls/dmband/Makefile dlls/dmcompos/Makefile dlls/dmime/Makefile dlls/dmloader/Makefile dlls/dmscript/Makefile dlls/dmstyle/Makefile dlls/dmsynth/Makefile dlls/dmusic/Makefile dlls/dmusic32/Makefile dlls/dplay/Makefile dlls/dplayx/Makefile dlls/dpnhpast/Makefile dlls/dsound/Makefile dlls/dsound/tests/Makefile dlls/dswave/Makefile dlls/dxerr8/Makefile dlls/dxerr9/Makefile dlls/dxguid/Makefile dlls/gdi/Makefile dlls/gdi/tests/Makefile dlls/glu32/Makefile dlls/glut32/Makefile dlls/iccvid/Makefile dlls/icmp/Makefile dlls/ifsmgr.vxd/Makefile dlls/imagehlp/Makefile dlls/imm32/Makefile dlls/iphlpapi/Makefile dlls/iphlpapi/tests/Makefile dlls/kernel/Makefile dlls/kernel/tests/Makefile dlls/lzexpand/Makefile dlls/mapi32/Makefile dlls/mmdevldr.vxd/Makefile dlls/monodebg.vxd/Makefile dlls/mpr/Makefile dlls/msacm/Makefile dlls/msacm/imaadp32/Makefile dlls/msacm/msadp32/Makefile dlls/msacm/msg711/Makefile dlls/msacm/winemp3/Makefile dlls/msdmo/Makefile dlls/mshtml/Makefile dlls/msi/Makefile dlls/msimg32/Makefile dlls/msisys/Makefile dlls/msnet32/Makefile dlls/msrle32/Makefile dlls/msvcrt/Makefile dlls/msvcrt/tests/Makefile dlls/msvcrt20/Makefile dlls/msvcrt40/Makefile dlls/msvcrtd/Makefile dlls/msvidc32/Makefile dlls/msvideo/Makefile dlls/mswsock/Makefile dlls/netapi32/Makefile dlls/netapi32/tests/Makefile dlls/newdev/Makefile dlls/ntdll/Makefile dlls/ntdll/tests/Makefile dlls/odbc32/Makefile dlls/ole32/Makefile dlls/oleacc/Makefile dlls/oleaut32/Makefile dlls/oleaut32/tests/Makefile dlls/olecli/Makefile dlls/oledlg/Makefile dlls/olepro32/Makefile dlls/olesvr/Makefile dlls/opengl32/Makefile dlls/psapi/Makefile dlls/qcap/Makefile dlls/quartz/Makefile dlls/rasapi32/Makefile dlls/richedit/Makefile dlls/rpcrt4/Makefile dlls/rpcrt4/tests/Makefile dlls/rsabase/Makefile dlls/secur32/Makefile dlls/serialui/Makefile dlls/setupapi/Makefile dlls/shdocvw/Makefile dlls/shell32/Makefile dlls/shell32/tests/Makefile dlls/shfolder/Makefile dlls/shlwapi/Makefile dlls/shlwapi/tests/Makefile dlls/snmpapi/Makefile dlls/sti/Makefile dlls/tapi32/Makefile dlls/ttydrv/Makefile dlls/twain/Makefile dlls/unicows/Makefile dlls/url/Makefile dlls/urlmon/Makefile dlls/urlmon/tests/Makefile dlls/user/Makefile dlls/user/tests/Makefile dlls/uuid/Makefile dlls/uxtheme/Makefile dlls/vdhcp.vxd/Makefile dlls/version/Makefile dlls/vmm.vxd/Makefile dlls/vnbt.vxd/Makefile dlls/vnetbios.vxd/Makefile dlls/vtdapi.vxd/Makefile dlls/vwin32.vxd/Makefile dlls/win32s/Makefile dlls/winaspi/Makefile dlls/wined3d/Makefile dlls/winedos/Makefile dlls/wineps/Makefile dlls/wininet/Makefile dlls/wininet/tests/Makefile dlls/winmm/Makefile dlls/winmm/joystick/Makefile dlls/winmm/mcianim/Makefile dlls/winmm/mciavi/Makefile dlls/winmm/mcicda/Makefile dlls/winmm/mciseq/Makefile dlls/winmm/mciwave/Makefile dlls/winmm/midimap/Makefile dlls/winmm/tests/Makefile dlls/winmm/wavemap/Makefile dlls/winmm/winealsa/Makefile dlls/winmm/winearts/Makefile dlls/winmm/wineaudioio/Makefile dlls/winmm/winejack/Makefile dlls/winmm/winenas/Makefile dlls/winmm/wineoss/Makefile dlls/winnls/Makefile dlls/winsock/Makefile dlls/winsock/tests/Makefile dlls/winspool/Makefile dlls/winspool/tests/Makefile dlls/wintab32/Makefile dlls/wintrust/Makefile dlls/wow32/Makefile dlls/wsock32/Makefile dlls/x11drv/Makefile documentation/Makefile include/Makefile libs/Makefile libs/port/Makefile libs/unicode/Makefile libs/wine/Makefile libs/wpp/Makefile loader/Makefile programs/Makefile programs/avitools/Makefile programs/clock/Makefile programs/cmdlgtst/Makefile programs/control/Makefile programs/expand/Makefile programs/notepad/Makefile programs/progman/Makefile programs/regedit/Makefile programs/regsvr32/Makefile programs/rpcss/Makefile programs/rundll32/Makefile programs/start/Makefile programs/taskmgr/Makefile programs/uninstaller/Makefile programs/view/Makefile programs/wcmd/Makefile programs/wineboot/Makefile programs/winebrowser/Makefile programs/winecfg/Makefile programs/wineconsole/Makefile programs/winedbg/Makefile programs/winefile/Makefile programs/winemenubuilder/Makefile programs/winemine/Makefile programs/winepath/Makefile programs/winetest/Makefile programs/winevdm/Makefile programs/winhelp/Makefile programs/winver/Makefile server/Makefile tools/Makefile tools/widl/Makefile tools/winapi/Makefile tools/winebuild/Makefile tools/winedump/Makefile tools/winegcc/Makefile tools/wmc/Makefile tools/wrc/Makefile"
cat >confcache <<\_ACEOF
......@@ -20522,6 +20530,7 @@ do
"dlls/d3d9/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/d3d9/Makefile" ;;
"dlls/d3dim/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/d3dim/Makefile" ;;
"dlls/d3dx8/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/d3dx8/Makefile" ;;
"dlls/dbghelp/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/dbghelp/Makefile" ;;
"dlls/dciman32/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/dciman32/Makefile" ;;
"dlls/ddraw/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/ddraw/Makefile" ;;
"dlls/ddraw/tests/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/ddraw/tests/Makefile" ;;
......
......@@ -1510,6 +1510,7 @@ dlls/d3d8/Makefile
dlls/d3d9/Makefile
dlls/d3dim/Makefile
dlls/d3dx8/Makefile
dlls/dbghelp/Makefile
dlls/dciman32/Makefile
dlls/ddraw/Makefile
dlls/ddraw/tests/Makefile
......
......@@ -31,6 +31,7 @@ BASEDIRS = \
crypt32 \
ctl3d \
d3dim \
dbghelp \
dciman32 \
devenum \
dinput \
......@@ -243,6 +244,7 @@ SYMLINKS_SO = \
crypt32.dll.so \
ctl3d32.dll.so \
d3dim.dll.so \
dbghelp.dll.so \
dciman32.dll.so \
devenum.dll.so \
dinput.dll.so \
......@@ -443,6 +445,9 @@ d3dim.dll.so: d3dim/d3dim.dll.so
d3dx8.dll.so: d3dx8/d3dx8.dll.so
$(RM) $@ && $(LN_S) d3dx8/d3dx8.dll.so $@
dbghelp.dll.so: dbghelp/dbghelp.dll.so
$(RM) $@ && $(LN_S) dbghelp/dbghelp.dll.so $@
dciman32.dll.so: dciman32/dciman32.dll.so
$(RM) $@ && $(LN_S) dciman32/dciman32.dll.so $@
......@@ -926,6 +931,7 @@ IMPORT_LIBS = \
libd3d9 \
libd3dim \
libd3dx8 \
libdbghelp \
libdciman32 \
libddraw \
libdevenum \
......@@ -1123,6 +1129,11 @@ libd3dx8.def: d3dx8/d3dx8.spec.def
libd3dx8.a: d3dx8/d3dx8.spec.def
$(DLLTOOL) -k -l $@ -d d3dx8/d3dx8.spec.def
libdbghelp.def: dbghelp/dbghelp.spec.def
$(RM) $@ && $(LN_S) dbghelp/dbghelp.spec.def $@
libdbghelp.a: dbghelp/dbghelp.spec.def
$(DLLTOOL) -k -l $@ -d dbghelp/dbghelp.spec.def
libdciman32.def: dciman32/dciman32.spec.def
$(RM) $@ && $(LN_S) dciman32/dciman32.spec.def $@
libdciman32.a: dciman32/dciman32.spec.def
......@@ -1626,6 +1637,7 @@ d3d8/d3d8.spec.def: $(WINEBUILD)
d3d9/d3d9.spec.def: $(WINEBUILD)
d3dim/d3dim.spec.def: $(WINEBUILD)
d3dx8/d3dx8.spec.def: $(WINEBUILD)
dbghelp/dbghelp.spec.def: $(WINEBUILD)
dciman32/dciman32.spec.def: $(WINEBUILD)
ddraw/ddraw.spec.def: $(WINEBUILD)
devenum/devenum.spec.def: $(WINEBUILD)
......@@ -1747,6 +1759,7 @@ d3d8/d3d8.dll.so: d3d8
d3d9/d3d9.dll.so: d3d9
d3dim/d3dim.dll.so: d3dim
d3dx8/d3dx8.dll.so: d3dx8
dbghelp/dbghelp.dll.so: dbghelp
dciman32/dciman32.dll.so: dciman32
ddraw/ddraw.dll.so: ddraw
devenum/devenum.dll.so: devenum
......
Makefile
dbghelp.dll.dbg.c
dbghelp.spec.def
TOPSRCDIR = @top_srcdir@
TOPOBJDIR = ../..
SRCDIR = @srcdir@
VPATH = @srcdir@
MODULE = dbghelp.dll
IMPORTS = psapi kernel32 ntdll
C_SRCS = \
dbghelp.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:
/*
* File dbghelp.c - generic routines (process) for dbghelp DLL
*
* Copyright (C) 2004, Eric Pouech
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include "dbghelp_private.h"
#include "winerror.h"
#include "psapi.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
/* TODO
* - support for symbols' types is still partly missing
* + debug start/stop in functions
* + parameters in function prototype...
* + C++ support
* - most options (dbghelp_options) are not used (loading lines, decoration,
* deferring reading of module symbols, public symbols...)
* - (un)decoration is not handled (should make winedump's code a (.a) library
* and link it to winedump, and potentially to msvcrt and dbghelp (check best
* way not to duplicate code in msvcrt & dbghelp)
* - msc:
* + handle the debug_start & debug_end information block
* + get rid of MSC reading FIXME:s (lots of types are not defined)
* + C++ management
* - stabs:
* + we should add parameters' types to the function's signature
* while processing a function's parameters
* + should generate the func debug_{start,end} statements (black magic ?)
* + should identify the relay code in Wine and mark it as thunk type
* + C++ management
* - implement the callback notification mechanism
*/
unsigned dbghelp_options = SYMOPT_UNDNAME;
/***********************************************************************
* DllMain (DEBUGHLP.@)
*/
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH: break;
case DLL_PROCESS_DETACH: break;
case DLL_THREAD_ATTACH: break;
case DLL_THREAD_DETACH: break;
default: break;
}
return TRUE;
}
static struct process* process_first /* = NULL */;
/******************************************************************
* process_find_by_handle
*
*/
struct process* process_find_by_handle(HANDLE hProcess)
{
struct process* p;
for (p = process_first; p && p->handle != hProcess; p = p->next);
if (!p) SetLastError(ERROR_INVALID_HANDLE);
return p;
}
/******************************************************************
* SymSetSearchPath (DBGHELP.@)
*
*/
BOOL WINAPI SymSetSearchPath(HANDLE hProcess, PSTR searchPath)
{
struct process* pcs = process_find_by_handle(hProcess);
if (!pcs) return FALSE;
if (!searchPath) return FALSE;
HeapFree(GetProcessHeap(), 0, pcs->search_path);
pcs->search_path = strcpy(HeapAlloc(GetProcessHeap(), 0, strlen(searchPath) + 1),
searchPath);
return TRUE;
}
/***********************************************************************
* SymGetSearchPath (DBGHELP.@)
*/
BOOL WINAPI SymGetSearchPath(HANDLE hProcess, LPSTR szSearchPath,
DWORD SearchPathLength)
{
struct process* pcs = process_find_by_handle(hProcess);
if (!pcs) return FALSE;
strncpy(szSearchPath, pcs->search_path, SearchPathLength);
szSearchPath[SearchPathLength - 1] = '\0';
return TRUE;
}
/******************************************************************
* invade_process
*
* SymInitialize helper: loads in dbghelp all known (and loaded modules)
* this assumes that hProcess is a handle on a valid process
*/
static BOOL process_invade(HANDLE hProcess)
{
HMODULE hMods[256];
char img[256], mod[256];
DWORD i, sz;
MODULEINFO mi;
if (!EnumProcessModules(hProcess, hMods, sizeof(hMods), &sz))
return FALSE; /* FIXME should grow hMods */
for (i = 0; i < sz / sizeof(HMODULE); i++)
{
if (!GetModuleInformation(hProcess, hMods[i], &mi, sizeof(mi)) ||
!GetModuleFileNameExA(hProcess, hMods[i], img, sizeof(img)) ||
!GetModuleBaseNameA(hProcess, hMods[i], mod, sizeof(mod)) ||
!SymLoadModule(hProcess, 0, img, mod, (DWORD)mi.lpBaseOfDll, mi.SizeOfImage))
return FALSE;
}
return sz != 0;
}
/******************************************************************
* SymInitialize (DBGHELP.@)
*
* The initialisation of a dbghelp's context.
* Note that hProcess doesn't need to be a valid process handle (except
* when fInvadeProcess is TRUE).
* Since, we're also allow to load ELF (pure) libraries and Wine ELF libraries
* containing PE (and NE) module(s), here's how we handle it:
* - we load every module (ELF, NE, PE) passed in SymLoadModule
* - in fInvadeProcess (in SymInitialize) is TRUE, we set up what is called ELF
* synchronization: hProcess should be a valid process handle, and we hook
* ourselves on hProcess's loaded ELF-modules, and keep this list in sync with
* our internal ELF modules representation (loading / unloading). This way,
* we'll pair every loaded builtin PE module with its ELF counterpart (and
* access its debug information).
* - if fInvadeProcess (in SymInitialize) is FALSE, we won't be able to
* make the peering between a builtin PE module and its ELF counterpart, hence
* we won't be able to provide the requested debug information. We'll
* however be able to load native PE modules (and their debug information)
* without any trouble.
* Note also that this scheme can be intertwined with the deferred loading
* mechanism (ie only load the debug information when we actually need it).
*/
BOOL WINAPI SymInitialize(HANDLE hProcess, PSTR UserSearchPath, BOOL fInvadeProcess)
{
struct process* pcs;
TRACE("(%p %s %u)\n", hProcess, debugstr_a(UserSearchPath), fInvadeProcess);
if (process_find_by_handle(hProcess))
FIXME("what to do ??\n");
pcs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pcs));
if (!pcs) return FALSE;
pcs->handle = hProcess;
if (UserSearchPath)
{
pcs->search_path = strcpy(HeapAlloc(GetProcessHeap(), 0, strlen(UserSearchPath) + 1),
UserSearchPath);
}
else
{
unsigned size;
unsigned len;
pcs->search_path = HeapAlloc(GetProcessHeap(), 0, len = MAX_PATH);
while ((size = GetCurrentDirectoryA(len, pcs->search_path)) >= len)
pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, len *= 2);
pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, size + 1);
len = GetEnvironmentVariableA("_NT_SYMBOL_PATH", NULL, 0);
if (len)
{
pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, size + 1 + len + 1);
pcs->search_path[size] = ';';
GetEnvironmentVariableA("_NT_SYMBOL_PATH", pcs->search_path + size + 1, len);
size += 1 + len;
}
len = GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", NULL, 0);
if (len)
{
pcs->search_path = HeapReAlloc(GetProcessHeap(), 0, pcs->search_path, size + 1 + len + 1);
pcs->search_path[size] = ';';
GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", pcs->search_path + size + 1, len);
size += 1 + len;
}
}
pcs->lmodules = NULL;
pcs->dbg_hdr_addr = 0;
pcs->next = process_first;
process_first = pcs;
if (fInvadeProcess)
{
pcs->dbg_hdr_addr = elf_read_wine_loader_dbg_info(pcs);
if (pcs->dbg_hdr_addr == 0)
{
SymCleanup(hProcess);
return FALSE;
}
process_invade(hProcess);
elf_synchronize_module_list(pcs);
}
return TRUE;
}
/******************************************************************
* SymCleanup (DBGHELP.@)
*
*/
BOOL WINAPI SymCleanup(HANDLE hProcess)
{
struct process** ppcs;
struct process* next;
for (ppcs = &process_first; *ppcs; ppcs = &(*ppcs)->next)
{
if ((*ppcs)->handle == hProcess)
{
while ((*ppcs)->lmodules) module_remove(*ppcs, (*ppcs)->lmodules);
HeapFree(GetProcessHeap(), 0, (*ppcs)->search_path);
next = (*ppcs)->next;
HeapFree(GetProcessHeap(), 0, *ppcs);
*ppcs = next;
return TRUE;
}
}
return FALSE;
}
/******************************************************************
* SymSetOptions (DBGHELP.@)
*
*/
DWORD WINAPI SymSetOptions(DWORD opts)
{
return dbghelp_options = opts;
}
/******************************************************************
* SymGetOptions (DBGHELP.@)
*
*/
DWORD WINAPI SymGetOptions(void)
{
return dbghelp_options;
}
/******************************************************************
* SymSetContext (DBGHELP.@)
*
*/
BOOL WINAPI SymSetContext(HANDLE hProcess, PIMAGEHLP_STACK_FRAME StackFrame,
PIMAGEHLP_CONTEXT Context)
{
struct process* pcs = process_find_by_handle(hProcess);
if (!pcs) return FALSE;
pcs->ctx_frame = *StackFrame;
/* MSDN states that Context is not (no longer?) used */
return TRUE;
}
/***********************************************************************
* SymRegisterCallback (DBGHELP.@)
*/
BOOL WINAPI SymRegisterCallback(HANDLE hProcess,
PSYMBOL_REGISTERED_CALLBACK CallbackFunction,
PVOID UserContext)
{
FIXME("(%p, %p, %p): stub\n", hProcess, CallbackFunction, UserContext);
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return FALSE;
}
/* This is imagehlp version not dbghelp !! */
static API_VERSION api_version = { 4, 0, 2, 0 };
/***********************************************************************
* ImagehlpApiVersion (DBGHELP.@)
*/
LPAPI_VERSION WINAPI ImagehlpApiVersion(VOID)
{
return &api_version;
}
/***********************************************************************
* ImagehlpApiVersionEx (DBGHELP.@)
*/
LPAPI_VERSION WINAPI ImagehlpApiVersionEx(LPAPI_VERSION AppVersion)
{
if (!AppVersion) return NULL;
AppVersion->MajorVersion = api_version.MajorVersion;
AppVersion->MinorVersion = api_version.MinorVersion;
AppVersion->Revision = api_version.Revision;
AppVersion->Reserved = api_version.Reserved;
return AppVersion;
}
@ stub DbgHelpCreateUserDump
@ stub DbgHelpCreateUserDumpW
@ stdcall EnumerateLoadedModules(long ptr ptr)
@ stub EnumerateLoadedModules64
@ stub ExtensionApiVersion
@ stdcall FindDebugInfoFile(str str ptr)
@ stdcall FindDebugInfoFileEx(str str ptr ptr ptr)
@ stdcall FindExecutableImage(str str str)
@ stub FindExecutableImageEx
@ stub FindFileInPath
@ stub FindFileInSearchPath
@ stdcall GetTimestampForLoadedLibrary(long)
@ stub ImageDirectoryEntryToData
@ stub ImageDirectoryEntryToDataEx
@ 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)
@ stub MiniDumpReadDumpStream
@ stub MiniDumpWriteDump
@ stdcall SearchTreeForFile(str str str)
@ stdcall StackWalk(long long long ptr ptr ptr ptr ptr ptr)
@ stub StackWalk64
@ stdcall SymCleanup(long)
@ stdcall SymEnumSourceFiles(long long str ptr ptr)
@ stub SymEnumSym
@ stdcall SymEnumSymbols(long long str ptr ptr)
@ stdcall SymEnumTypes(long long ptr ptr)
@ stdcall SymEnumerateModules(long ptr ptr)
@ stub SymEnumerateModules64
@ stdcall SymEnumerateSymbols(long long ptr ptr)
@ stub SymEnumerateSymbols64
@ stub SymEnumerateSymbolsW
@ stub SymEnumerateSymbolsW64
@ stub SymFindFileInPath
@ stdcall SymFromAddr(long long ptr ptr)
@ stdcall SymFromName(long str ptr)
@ stdcall SymFunctionTableAccess(long long)
@ stub SymFunctionTableAccess64
@ stub SymGetFileLineOffsets64
@ stdcall SymGetLineFromAddr(long long ptr ptr)
@ stub SymGetLineFromAddr64
@ stub SymGetLineFromName
@ stub SymGetLineFromName64
@ stdcall SymGetLineNext(long ptr)
@ stub SymGetLineNext64
@ stdcall SymGetLinePrev(long ptr)
@ stub SymGetLinePrev64
@ stdcall SymGetModuleBase(long long)
@ stub SymGetModuleBase64
@ stdcall SymGetModuleInfo(long long ptr)
@ stub SymGetModuleInfo64
@ stub SymGetModuleInfoW
@ stub SymGetModuleInfoW64
@ stdcall SymGetOptions()
@ stdcall SymGetSearchPath(long str long)
@ stdcall SymGetSymFromAddr(long long ptr ptr)
@ stub SymGetSymFromAddr64
@ stdcall SymGetSymFromName(long str ptr)
@ stub SymGetSymFromName64
@ stdcall SymGetSymNext(long ptr)
@ stub SymGetSymNext64
@ stdcall SymGetSymPrev(long ptr)
@ stub SymGetSymPrev64
@ stdcall SymGetTypeFromName(long long str ptr)
@ stdcall SymGetTypeInfo(long long long long ptr)
@ stdcall SymInitialize(long str long)
@ stdcall SymLoadModule(long long str str long long)
@ stub SymLoadModule64
@ stub SymLoadModuleEx
@ stub SymMatchFileName
@ stub SymMatchString
@ stdcall SymRegisterCallback(long ptr ptr)
@ stub SymRegisterCallback64
@ stub SymRegisterFunctionEntryCallback
@ stub SymRegisterFunctionEntryCallback64
@ stdcall SymSetContext(long ptr ptr)
@ stdcall SymSetOptions(long)
@ stdcall SymSetSearchPath(long str)
@ stub SymSetSymWithAddr64
@ stdcall SymUnDName(ptr str long)
@ stub SymUnDName64
@ stdcall SymUnloadModule(long long)
@ stub SymUnloadModule64
@ stdcall UnDecorateSymbolName(str str long long)
@ stdcall UnmapDebugInformation(ptr)
@ stub WinDbgExtensionDllInit
#@ stub dbghelp
#@ stub dh
#@ stub lm
#@ stub lmi
#@ stub omap
#@ stub srcfiles
#@ stub sym
#@ stub vc7fpo
/*
* 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, Eric Pouech.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "winver.h"
#include "dbghelp.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);
/* void* pool_realloc(struct pool* a, void* p,
unsigned old_size, unsigned new_size); */
char* pool_strdup(struct pool* a, const char* str);
struct vector
{
void** buckets;
unsigned elt_size : 24, shift : 8;
unsigned num_elts : 20, num_buckets : 12;
};
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);
/*void vector_pool_normalize(struct vector* v, struct pool* pool); */
void* vector_iter_up(const struct vector* v, void* elt);
void* vector_iter_down(const struct vector* v, void* elt);
struct hash_table_elt
{
const char* name;
struct hash_table_elt* next;
};
struct hash_table
{
unsigned num_buckets;
struct hash_table_elt** buckets;
};
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;
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 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;
enum LocationType location;
union /* depends on location */
{
unsigned long address; /* used by Static, Tls, ThisRel */
int offset; /* used by RegRel */
unsigned reg_id; /* used by Enregistered */
struct
{
unsigned position;
unsigned length;
} bitfield; /* used by BitField */
#if 1
unsigned value; /* LocIsConstant */
#else
VARIANT value; /* LocIsConstant */
#endif
} u;
};
struct symt_function
{
struct symt symt;
struct hash_table_elt hash_elt; /* if global symbol */
unsigned long addr;
struct symt* container; /* compiland */
struct symt* type; /* points to function_signature */
unsigned long size;
struct vector vlines;
struct vector vchildren; /* locals, params, blocks */
};
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;
};
/* class tree */
struct symt_array
{
struct symt symt;
int start;
int end;
struct symt* basetype;
};
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 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 DbgModuleType {DMT_UNKNOWN, DMT_ELF, DMT_NE, DMT_PE};
struct module
{
IMAGEHLP_MODULE module;
struct module* next;
enum DbgModuleType type;
unsigned short elf_mark : 1;
struct tagELF_DBG_INFO* elf_dbg_info;
/* memory allocation pool */
struct pool pool;
/* symbol tables */
int sortlist_valid;
struct symt_ht** addr_sorttab;
struct hash_table ht_symbols;
/* types */
struct hash_table ht_types;
/* source files */
unsigned sources_used;
unsigned sources_alloc;
char* sources;
};
struct process
{
struct process* next;
HANDLE handle;
char* search_path;
struct module* lmodules;
unsigned long dbg_hdr_addr;
IMAGEHLP_STACK_FRAME ctx_frame;
};
/* dbghelp.c */
extern struct process* process_find_by_handle(HANDLE hProcess);
/* elf_module.c */
extern SYM_TYPE elf_load_debug_info(struct module* module);
extern struct module*
elf_load_module(struct process* pcs, const char* name);
extern unsigned long
elf_read_wine_loader_dbg_info(struct process* pcs);
extern BOOL elf_synchronize_module_list(struct process* pcs);
/* memory.c */
struct memory_access
{
BOOL (*read_mem)(HANDLE hProcess, DWORD addr, void* buf, DWORD len);
BOOL (*write_mem)(HANDLE hProcess, DWORD addr, void* buf, DWORD len);
};
extern struct memory_access mem_access;
#define read_mem(p,a,b,l) (mem_access.read_mem)((p),(a),(b),(l))
#define write_mem(p,a,b,l) (mem_access.write_mem)((p),(a),(b),(l))
extern unsigned long WINAPI addr_to_linear(HANDLE hProcess, HANDLE hThread, ADDRESS* addr);
/* module.c */
extern struct module*
module_find_by_addr(const struct process* pcs, unsigned long addr,
enum DbgModuleType type);
extern struct module*
module_find_by_name(const struct process* pcs,
const char* name, enum DbgModuleType type);
extern struct module*
module_get_debug(const struct process* pcs, struct module*);
extern struct module*
module_new(struct process* pcs, const char* name,
enum DbgModuleType type, unsigned long addr,
unsigned long size, unsigned long stamp,
unsigned long checksum);
extern BOOL module_remove(struct process* pcs,
struct module* module);
/* msc.c */
extern SYM_TYPE pe_load_debug_directory(const struct process* pcs,
struct module* module,
const BYTE* file_map,
PIMAGE_DEBUG_DIRECTORY dbg, int nDbg);
/* pe_module.c */
extern struct module*
pe_load_module(struct process* pcs, char* name,
HANDLE hFile, DWORD base, DWORD size);
extern struct module*
pe_load_module_from_pcs(struct process* pcs, const char* name,
const char* mod_name, DWORD base, DWORD size);
extern SYM_TYPE pe_load_debug_info(const struct process* pcs,
struct module* module);
/* source.c */
extern unsigned source_new(struct module* module, const char* source);
extern const char* source_get(const struct module* module, unsigned idx);
/* stabs.c */
extern SYM_TYPE stabs_parse(struct module* module, const char* addr,
unsigned long load_offset,
unsigned int staboff, int stablen,
unsigned int strtaboff, int strtablen);
/* symbol.c */
extern const char* symt_get_name(const struct symt* sym);
extern int symt_cmp_addr(const void* p1, const void* p2);
extern struct symt_compiland*
symt_new_compiland(struct module* module,
const char* filename);
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,
int regno, int offset,
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);
extern struct symt_block*
symt_close_func_block(struct module* module,
struct symt_function* func,
struct symt_block* block, unsigned pc);
/* 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, unsigned value);
extern struct symt_array*
symt_new_array(struct module* module, int min, int max,
struct symt* base);
extern struct symt_function_signature*
symt_new_function_signature(struct module* module,
struct symt* ret_type);
extern struct symt_pointer*
symt_new_pointer(struct module* module,
struct symt* ref_type);
/*
* File elf.c - processing of ELF files
*
* Copyright (C) 1996, Eric Youngdale.
* 1999-2004 Eric Pouech
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#ifndef PATH_MAX
#define PATH_MAX MAX_PATH
#endif
#include "dbghelp_private.h"
#if defined(__svr4__) || defined(__sun)
#define __ELF__
#endif
#ifdef HAVE_ELF_H
# include <elf.h>
#endif
#ifdef HAVE_SYS_ELF32_H
# include <sys/elf32.h>
#endif
#ifdef HAVE_SYS_EXEC_ELF_H
# include <sys/exec_elf.h>
#endif
#if !defined(DT_NUM)
# if defined(DT_COUNT)
# define DT_NUM DT_COUNT
# else
/* this seems to be a satisfactory value on Solaris, which doesn't support this AFAICT */
# define DT_NUM 24
# endif
#endif
#ifdef HAVE_LINK_H
# include <link.h>
#endif
#ifdef HAVE_SYS_LINK_H
# include <sys/link.h>
#endif
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
typedef struct tagELF_DBG_INFO
{
unsigned long elf_addr;
} ELF_DBG_INFO;
#ifdef __ELF__
#define ELF_INFO_DEBUG_HEADER 0x0001
#define ELF_INFO_MODULE 0x0002
struct elf_info
{
unsigned flags; /* IN one (or several) of the ELF_INFO constants */
unsigned long dbg_hdr_addr; /* OUT address of debug header (if ELF_INFO_DEBUG_HEADER is set) */
struct module* module; /* OUT loaded module (if ELF_INFO_MODULE is set) */
};
/******************************************************************
* elf_load_symtab
*
* Walk through the entire symbol table and add any symbols we find there.
* This can be used in cases where we have stripped ELF shared libraries,
* or it can be used in cases where we have data symbols for which the address
* isn't encoded in the stabs.
*
* This is all really quite easy, since we don't have to worry about line
* numbers or local data variables.
*/
static int elf_load_symtab(struct module* module, const char* addr,
unsigned long load_addr, const Elf32_Shdr* symtab,
const Elf32_Shdr* strtab)
{
int i, nsym;
const char* strp;
const char* symname;
const Elf32_Sym* symp;
struct symt_compiland* compiland = NULL;
symp = (Elf32_Sym*)(addr + symtab->sh_offset);
nsym = symtab->sh_size / sizeof(*symp);
strp = (char*)(addr + strtab->sh_offset);
for (i = 0; i < nsym; i++, symp++)
{
/* Ignore certain types of entries which really aren't of that much
* interest.
*/
if (ELF32_ST_TYPE(symp->st_info) == STT_SECTION ||
symp->st_shndx == SHN_UNDEF)
{
continue;
}
symname = strp + symp->st_name;
if (ELF32_ST_TYPE(symp->st_info) == STT_FILE)
{
compiland = symt_new_compiland(module, symname);
continue;
}
symt_new_public(module, compiland, symname,
load_addr + symp->st_value, symp->st_size,
TRUE /* FIXME */, ELF32_ST_TYPE(symp->st_info) == STT_FUNC);
}
return TRUE;
}
/******************************************************************
* elf_load_debug_info
*
* Loads the symbolic information from ELF module stored in 'filename'
* the module has been loaded at 'load_offset' address, so symbols' address
* relocation is performed
* returns
* -1 if the file cannot be found/opened
* 0 if the file doesn't contain symbolic info (or this info cannot be
* read or parsed)
* 1 on success
*/
SYM_TYPE elf_load_debug_info(struct module* module)
{
SYM_TYPE sym_type = -1;
char* addr = (char*)0xffffffff;
int fd = -1;
struct stat statbuf;
const Elf32_Ehdr* ehptr;
const Elf32_Shdr* spnt;
const char* shstrtab;
int i;
int stabsect, stabstrsect, debugsect;
int symsect, dynsect;
if (module->type != DMT_ELF || !module->elf_dbg_info)
{
ERR("Bad elf module '%s'\n", module->module.LoadedImageName);
return sym_type;
}
TRACE("%s\n", module->module.LoadedImageName);
/* check that the file exists, and that the module hasn't been loaded yet */
if (stat(module->module.LoadedImageName, &statbuf) == -1) goto leave;
if (S_ISDIR(statbuf.st_mode)) goto leave;
/*
* Now open the file, so that we can mmap() it.
*/
if ((fd = open(module->module.LoadedImageName, O_RDONLY)) == -1) goto leave;
/*
* Now mmap() the file.
*/
addr = mmap(0, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (addr == (char*)0xffffffff) goto leave;
sym_type = SymNone;
/*
* Next, we need to find a few of the internal ELF headers within
* this thing. We need the main executable header, and the section
* table.
*/
ehptr = (Elf32_Ehdr*)addr;
spnt = (Elf32_Shdr*)(addr + ehptr->e_shoff);
shstrtab = (addr + spnt[ehptr->e_shstrndx].sh_offset);
symsect = dynsect = stabsect = stabstrsect = debugsect = -1;
for (i = 0; i < ehptr->e_shnum; i++)
{
if (strcmp(shstrtab + spnt[i].sh_name, ".stab") == 0)
stabsect = i;
if (strcmp(shstrtab + spnt[i].sh_name, ".stabstr") == 0)
stabstrsect = i;
if (strcmp(shstrtab + spnt[i].sh_name, ".debug_info") == 0)
debugsect = i;
if ((strcmp(shstrtab + spnt[i].sh_name, ".symtab") == 0) &&
(spnt[i].sh_type == SHT_SYMTAB))
symsect = i;
if ((strcmp(shstrtab + spnt[i].sh_name, ".dynsym") == 0) &&
(spnt[i].sh_type == SHT_DYNSYM))
dynsect = i;
}
/* start loading dynamic symbol info (so that we can get the correct address) */
if (symsect != -1)
elf_load_symtab(module, addr, module->elf_dbg_info->elf_addr,
spnt + symsect, spnt + spnt[symsect].sh_link);
else if (dynsect != -1)
elf_load_symtab(module, addr, module->elf_dbg_info->elf_addr,
spnt + dynsect, spnt + spnt[dynsect].sh_link);
sym_type = SymExport;
if (!(dbghelp_options & SYMOPT_PUBLICS_ONLY))
{
if (stabsect != -1 && stabstrsect != -1)
{
/* OK, now just parse all of the stabs. */
sym_type = stabs_parse(module, addr, module->elf_dbg_info->elf_addr,
spnt[stabsect].sh_offset, spnt[stabsect].sh_size,
spnt[stabstrsect].sh_offset,
spnt[stabstrsect].sh_size);
if (sym_type == -1)
{
WARN("Couldn't read correctly read stabs\n");
goto leave;
}
}
else if (debugsect != -1)
{
/* Dwarf 2 debug information */
FIXME("Unsupported Dwarf2 information\n");
sym_type = SymNone;
}
}
leave:
if (addr != (char*)0xffffffff) munmap(addr, statbuf.st_size);
if (fd != -1) close(fd);
return module->module.SymType = sym_type;
}
/******************************************************************
* is_dt_flag_valid
* returns true iff the section tag is valid
*/
static unsigned is_dt_flag_valid(unsigned d_tag)
{
#ifndef DT_PROCNUM
#define DT_PROCNUM 0
#endif
#ifndef DT_EXTRANUM
#define DT_EXTRANUM 0
#endif
return (d_tag >= 0 && d_tag < DT_NUM + DT_PROCNUM + DT_EXTRANUM)
#if defined(DT_LOOS) && defined(DT_HIOS)
|| (d_tag >= DT_LOOS && d_tag < DT_HIOS)
#endif
#if defined(DT_LOPROC) && defined(DT_HIPROC)
|| (d_tag >= DT_LOPROC && d_tag < DT_HIPROC)
#endif
;
}
/******************************************************************
* elf_load_file
*
* Loads the information for ELF module stored in 'filename'
* the module has been loaded at 'load_offset' address
* returns
* -1 if the file cannot be found/opened
* 0 if the file doesn't contain symbolic info (or this info cannot be
* read or parsed)
* 1 on success
*/
static SYM_TYPE elf_load_file(struct process* pcs, const char* filename,
unsigned long load_offset, struct elf_info* elf_info)
{
static const BYTE elf_signature[4] = { ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3 };
SYM_TYPE sym_type = -1;
const char* addr = (char*)0xffffffff;
int fd = -1;
struct stat statbuf;
const Elf32_Ehdr* ehptr;
const Elf32_Shdr* spnt;
const Elf32_Phdr* ppnt;
const char* shstrtab;
int i;
DWORD delta, size;
TRACE("Processing elf file '%s' at %08lx\n", filename, load_offset);
/* check that the file exists, and that the module hasn't been loaded yet */
if (stat(filename, &statbuf) == -1) goto leave;
/* Now open the file, so that we can mmap() it. */
if ((fd = open(filename, O_RDONLY)) == -1) goto leave;
/* Now mmap() the file. */
addr = mmap(0, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (addr == (char*)-1) goto leave;
sym_type = SymNone;
/* Next, we need to find a few of the internal ELF headers within
* this thing. We need the main executable header, and the section
* table.
*/
ehptr = (Elf32_Ehdr*)addr;
if (memcmp(ehptr->e_ident, elf_signature, sizeof(elf_signature))) goto leave;
spnt = (Elf32_Shdr*)(addr + ehptr->e_shoff);
shstrtab = (addr + spnt[ehptr->e_shstrndx].sh_offset);
/* if non relocatable ELF, then remove fixed address from computation
* otherwise, all addresses are zero based
*/
delta = (load_offset == 0) ? ehptr->e_entry : 0;
/* grab size of module once loaded in memory */
ppnt = (Elf32_Phdr*)(addr + ehptr->e_phoff);
size = 0;
for (i = 0; i < ehptr->e_phnum; i++)
{
if (ppnt[i].p_type == PT_LOAD)
{
size += (ppnt[i].p_align <= 1) ? ppnt[i].p_memsz :
(ppnt[i].p_memsz + ppnt[i].p_align - 1) & ~(ppnt[i].p_align - 1);
}
}
if (elf_info->flags & ELF_INFO_DEBUG_HEADER)
{
for (i = 0; i < ehptr->e_shnum; i++)
{
if (strcmp(shstrtab + spnt[i].sh_name, ".dynamic") == 0 &&
spnt[i].sh_type == SHT_DYNAMIC)
{
Elf32_Dyn dyn;
char* ptr = (char*)spnt[i].sh_addr;
unsigned long len;
do
{
if (!ReadProcessMemory(pcs->handle, ptr, &dyn, sizeof(dyn), &len) ||
len != sizeof(dyn) || !is_dt_flag_valid(dyn.d_tag))
dyn.d_tag = DT_NULL;
ptr += sizeof(dyn);
} while (dyn.d_tag != DT_DEBUG && dyn.d_tag != DT_NULL);
if (dyn.d_tag == DT_NULL)
{
sym_type = -1;
goto leave;
}
elf_info->dbg_hdr_addr = dyn.d_un.d_ptr;
}
}
}
if (elf_info->flags & ELF_INFO_MODULE)
{
elf_info->module = module_new(pcs, filename, DMT_ELF,
(load_offset) ? load_offset : ehptr->e_entry,
size, 0, 0);
if (elf_info->module)
{
if ((elf_info->module->elf_dbg_info = HeapAlloc(GetProcessHeap(), 0, sizeof(ELF_DBG_INFO))) == NULL)
{
ERR("OOM\n");
exit(0); /* FIXME */
}
elf_info->module->elf_dbg_info->elf_addr = load_offset;
elf_info->module->module.SymType = sym_type =
(dbghelp_options & SYMOPT_DEFERRED_LOADS) ? SymDeferred :
elf_load_debug_info(elf_info->module);
elf_info->module->elf_mark = 1;
}
else sym_type = -1;
}
leave:
if (addr != (char*)0xffffffff) munmap((void*)addr, statbuf.st_size);
if (fd != -1) close(fd);
return sym_type;
}
/******************************************************************
* elf_load_file_from_path
* tries to load an ELF file from a set of paths (separated by ':')
*/
static SYM_TYPE elf_load_file_from_path(HANDLE hProcess,
const char* filename,
unsigned long load_offset,
const char* path,
struct elf_info* elf_info)
{
SYM_TYPE sym_type = -1;
char *s, *t, *fn;
char* paths = NULL;
if (!path) return sym_type;
paths = strcpy(HeapAlloc(GetProcessHeap(), 0, strlen(path) + 1), path);
for (s = paths; s && *s; s = (t) ? (t+1) : NULL)
{
t = strchr(s, ':');
if (t) *t = '\0';
fn = HeapAlloc(GetProcessHeap(), 0, strlen(filename) + 1 + strlen(s) + 1);
if (!fn) break;
strcpy(fn, s);
strcat(fn, "/");
strcat(fn, filename);
sym_type = elf_load_file(hProcess, fn, load_offset, elf_info);
HeapFree(GetProcessHeap(), 0, fn);
if (sym_type != -1) break;
s = (t) ? (t+1) : NULL;
}
HeapFree(GetProcessHeap(), 0, paths);
return sym_type;
}
/******************************************************************
* elf_search_and_load_file
*
* lookup a file in standard ELF locations, and if found, load it
*/
static SYM_TYPE elf_search_and_load_file(struct process* pcs, const char* filename,
unsigned long load_offset, struct elf_info* elf_info)
{
SYM_TYPE sym_type = -1;
struct module* module;
if (filename == NULL || *filename == '\0') return sym_type;
if ((module = module_find_by_name(pcs, filename, DMT_ELF)))
{
elf_info->module = module;
module->elf_mark = 1;
return module->module.SymType;
}
if (strstr(filename, "libstdc++")) return -1; /* We know we can't do it */
sym_type = elf_load_file(pcs, filename, load_offset, elf_info);
/* if relative pathname, try some absolute base dirs */
if (sym_type == -1 && !strchr(filename, '/'))
{
sym_type = elf_load_file_from_path(pcs, filename, load_offset,
getenv("PATH"), elf_info);
if (sym_type == -1)
sym_type = elf_load_file_from_path(pcs, filename, load_offset,
getenv("LD_LIBRARY_PATH"), elf_info);
if (sym_type == -1)
sym_type = elf_load_file_from_path(pcs, filename, load_offset,
getenv("WINEDLLPATH"), elf_info);
}
return sym_type;
}
/******************************************************************
* elf_synchronize_module_list
*
* this functions rescans the debuggee module's list and synchronizes it with
* the one from 'pcs', ie:
* - if a module is in debuggee and not in pcs, it's loaded into pcs
* - if a module is in pcs and not in debuggee, it's unloaded from pcs
*/
BOOL elf_synchronize_module_list(struct process* pcs)
{
struct r_debug dbg_hdr;
void* lm_addr;
struct link_map lm;
char bufstr[256];
struct elf_info elf_info;
struct module* module;
if (!pcs->dbg_hdr_addr) return FALSE;
if (!read_mem(pcs->handle, pcs->dbg_hdr_addr, &dbg_hdr, sizeof(dbg_hdr)) ||
dbg_hdr.r_state != RT_CONSISTENT)
return FALSE;
for (module = pcs->lmodules; module; module = module->next)
{
if (module->type == DMT_ELF) module->elf_mark = 0;
}
elf_info.flags = ELF_INFO_MODULE;
/* Now walk the linked list. In all known ELF implementations,
* the dynamic loader maintains this linked list for us. In some
* cases the first entry doesn't appear with a name, in other cases it
* does.
*/
for (lm_addr = (void*)dbg_hdr.r_map; lm_addr; lm_addr = (void*)lm.l_next)
{
if (!read_mem(pcs->handle, (ULONG)lm_addr, &lm, sizeof(lm)))
return FALSE;
if (lm.l_prev != NULL && /* skip first entry, normally debuggee itself */
lm.l_name != NULL &&
read_mem(pcs->handle, (ULONG)lm.l_name, bufstr, sizeof(bufstr)))
{
bufstr[sizeof(bufstr) - 1] = '\0';
elf_search_and_load_file(pcs, bufstr, lm.l_addr, &elf_info);
}
}
for (module = pcs->lmodules; module; module = module->next)
{
if (module->type == DMT_ELF && !module->elf_mark)
{
module_remove(pcs, module);
/* restart all over */
module = pcs->lmodules;
}
}
return TRUE;
}
/******************************************************************
* elf_read_wine_loader_dbg_info
*
* Try to find a decent wine executable which could have loader the debuggee
*/
unsigned long elf_read_wine_loader_dbg_info(struct process* pcs)
{
const char* ptr;
SYM_TYPE sym_type;
struct elf_info elf_info;
elf_info.flags = ELF_INFO_DEBUG_HEADER;
/* All binaries are loaded with WINELOADER (if run from tree) or by the
* main executable (either wine-kthread or wine-pthread)
* Note: the heuristic use to know wether we need to load wine-pthread or
* wine-kthread is not 100% safe
*/
if ((ptr = getenv("WINELOADER")))
sym_type = elf_search_and_load_file(pcs, ptr, 0, &elf_info);
else
{
if ((sym_type = elf_search_and_load_file(pcs, "wine-kthread", 0, &elf_info)) == -1)
sym_type = elf_search_and_load_file(pcs, "wine-pthread", 0, &elf_info);
}
return (sym_type < 0) ? 0 : elf_info.dbg_hdr_addr;
}
/******************************************************************
* elf_load_module
*
* loads an ELF module and stores it in process' module list
* if 'sync' is TRUE, let's find module real name and load address from
* the real loaded modules list in pcs address space
*/
struct module* elf_load_module(struct process* pcs, const char* name)
{
struct elf_info elf_info;
SYM_TYPE sym_type = -1;
const char* p;
const char* xname;
struct r_debug dbg_hdr;
void* lm_addr;
struct link_map lm;
char bufstr[256];
TRACE("(%p %s)\n", pcs, name);
elf_info.flags = ELF_INFO_MODULE;
/* do only the lookup from the filename, not the path (as we lookup module name
* in the process' loaded module list)
*/
xname = strrchr(name, '/');
if (!xname++) xname = name;
if (!read_mem(pcs->handle, pcs->dbg_hdr_addr, &dbg_hdr, sizeof(dbg_hdr)) ||
dbg_hdr.r_state != RT_CONSISTENT)
return NULL;
for (lm_addr = (void*)dbg_hdr.r_map; lm_addr; lm_addr = (void*)lm.l_next)
{
if (!read_mem(pcs->handle, (ULONG)lm_addr, &lm, sizeof(lm)))
return NULL;
if (lm.l_prev != NULL && /* skip first entry, normally debuggee itself */
lm.l_name != NULL &&
read_mem(pcs->handle, (ULONG)lm.l_name, bufstr, sizeof(bufstr)))
{
bufstr[sizeof(bufstr) - 1] = '\0';
/* memcmp is needed for matches when bufstr contains also version information
* name: libc.so, bufstr: libc.so.6.0
*/
p = strrchr(bufstr, '/');
if (!p++) p = bufstr;
if (!memcmp(p, xname, strlen(xname)))
{
sym_type = elf_search_and_load_file(pcs, bufstr, lm.l_addr, &elf_info);
break;
}
}
}
if (!lm_addr || sym_type == -1) return NULL;
assert(elf_info.module);
return elf_info.module;
}
#else /* !__ELF__ */
BOOL elf_synchronize_module_list(struct process* pcs)
{
return FALSE;
}
SYM_TYPE elf_read_wine_loader_dbg_info(struct process pcs, struct elf_info* elf_info)
{
return -1;
}
struct module* elf_load_module(struct process* pcs, const char* name)
{
return NULL;
}
SYM_TYPE elf_load_debug_info(struct module* module)
{
return -1;
}
#endif /* __ELF__ */
/*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "dbghelp_private.h"
#include "winreg.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%08lx): 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;
}
/*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include <assert.h>
#include "dbghelp_private.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
BOOL win32_read_mem(HANDLE hProcess, DWORD addr, void* buf, DWORD len)
{
return ReadProcessMemory(hProcess, (void*)addr, buf, len, NULL);
}
BOOL win32_write_mem(HANDLE hProcess, DWORD addr, void* buf, DWORD len)
{
return WriteProcessMemory(hProcess, (void*)addr, buf, len, NULL);
}
/* standard routines for reading / writing memory. Can be overriden for some
* minidump usage
*/
struct memory_access mem_access = {win32_read_mem, win32_write_mem};
/******************************************************************
* addr_to_linear
*
* converts an address into its linear value
*/
unsigned long 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:%08lx (mode %x)\n",
addr->Segment, addr->Offset, addr->Mode);
return 0;
}
/*
* File minidump.c - management of dumps (read & write)
*
* Copyright (C) 2004, Eric Pouech
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <time.h>
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
#include "dbghelp_private.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
#if 0
/* hard to see how we can generate this very easily (how to grab latest exception
* in a process ?)
*/
static void DumpException(struct process* pcs, HANDLE hFile, RVA* rva)
{
MINIDUMP_EXCEPTION_STREAM mdExcpt;
mdExcpt.ThreadId = DEBUG_CurrThread->tid;
mdExcpt.__alignment = 0;
mdExcpt.ExceptionRecord.
ULONG ExceptionCode;
ULONG ExceptionFlags;
ULONGLONG ExceptionRecord;
ULONGLONG ExceptionAddress;
ULONG NumberParameters;
ULONG __unusedAlignment;
ULONGLONG ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
}
#endif
/******************************************************************
* dump_modules
*
* Write in File the modules from pcs
*/
static void dump_modules(struct process* pcs, HANDLE hFile, RVA* rva)
{
MINIDUMP_MODULE mdModule;
MINIDUMP_MODULE_LIST mdModuleList;
DWORD written;
struct module* module = NULL;
mdModuleList.NumberOfModules = 0;
for (module = pcs->lmodules; module; module = module->next)
mdModuleList.NumberOfModules++;
WriteFile(hFile, &mdModuleList.NumberOfModules,
sizeof(mdModuleList.NumberOfModules), &written, NULL);
*rva += sizeof(mdModuleList.NumberOfModules) +
sizeof(mdModule) * mdModuleList.NumberOfModules;
for (module = pcs->lmodules; module; module = module->next)
{
mdModule.BaseOfImage = (DWORD)module->module.BaseOfImage;
mdModule.SizeOfImage = module->module.ImageSize;
mdModule.CheckSum = module->module.CheckSum;
mdModule.TimeDateStamp = module->module.TimeDateStamp;
mdModule.ModuleNameRva = *rva;
*rva += strlen(module->module.ModuleName) + 1;
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;
mdModule.Reserved1 = 0;
WriteFile(hFile, &mdModule, sizeof(mdModule), &written, NULL);
}
for (module = pcs->lmodules; module; module = module->next)
{
WriteFile(hFile, module->module.ModuleName,
strlen(module->module.ModuleName) + 1, &written, NULL);
FIXME("CV and misc records not written\n");
}
}
/******************************************************************
* dump_system_info
*
* Dumps into File the information about the system
*/
static void dump_system_info(struct process* pcs, HANDLE hFile, RVA* rva)
{
MINIDUMP_SYSTEM_INFO mdSysInfo;
SYSTEM_INFO sysInfo;
OSVERSIONINFOA osInfo;
DWORD written;
GetSystemInfo(&sysInfo);
GetVersionExA(&osInfo);
mdSysInfo.ProcessorArchitecture = sysInfo.u.s.wProcessorArchitecture;
mdSysInfo.ProcessorLevel = sysInfo.wProcessorLevel;
mdSysInfo.ProcessorRevision = sysInfo.wProcessorRevision;
mdSysInfo.Reserved0 = 0;
mdSysInfo.MajorVersion = osInfo.dwMajorVersion;
mdSysInfo.MinorVersion = osInfo.dwMinorVersion;
mdSysInfo.BuildNumber = osInfo.dwBuildNumber;
mdSysInfo.PlatformId = osInfo.dwPlatformId;
mdSysInfo.CSDVersionRva = *rva + sizeof(mdSysInfo);
mdSysInfo.Reserved1 = 0;
WriteFile(hFile, &mdSysInfo, sizeof(mdSysInfo), &written, NULL);
*rva += sizeof(mdSysInfo);
WriteFile(hFile, osInfo.szCSDVersion, strlen(osInfo.szCSDVersion) + 1,
&written, NULL);
*rva += strlen(osInfo.szCSDVersion) + 1;
}
/******************************************************************
* dump_threads
*
* Dumps into File the information about running threads
*/
static void dump_threads(struct process* pcs, HANDLE hFile, RVA* rva)
{
#if 0
MINIDUMP_THREAD mdThd;
MINIDUMP_THREAD_LIST mdThdList;
DWORD written;
DBG_THREAD* thd;
mdThdList.NumberOfThreads = pcs->num_threads;
WriteFile(hFile, &mdThdList.NumberOfThreads, sizeof(mdThdList.NumberOfThreads),
&written, NULL);
*rva += sizeof(mdThdList.NumberOfThreads) +
mdThdList.NumberOfThreads * sizeof(mdThd);
for (thd = pcs->threads; thd; thd = thd->next)
{
mdThd.ThreadId = thd->tid;
mdThd.SuspendCount = 0; /* FIXME */
mdThd.PriorityClass = 0; /* FIXME */
mdThd.Priority = 0; /* FIXME */
mdThd.Teb = 0; /* FIXME */
mdThd.Stack.StartOfMemoryRange = 0; /* FIXME */
mdThd.Stack.Memory.DataSize = 0; /* FIXME */
mdThd.Stack.Memory.Rva = 0; /* FIXME */
mdThd.ThreadContext.DataSize = 0;/* FIXME */
mdThd.ThreadContext.Rva = 0; /* FIXME */
WriteFile(hFile, &mdThd, sizeof(mdThd), &written, NULL);
FIXME("Stack & thread context not written\n");
}
#endif
}
/******************************************************************
* MiniDumpWriteDump (DEBUGHLP.@)
*
*
*/
BOOL WINAPI MiniDumpWriteDump(HANDLE hProcess, DWORD ProcessId, HANDLE hFile,
MINIDUMP_TYPE DumpType,
PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
PMINIDUMP_CALLBACK_INFORMATION CallbackParam)
{
struct process* pcs;
MINIDUMP_HEADER mdHead;
MINIDUMP_DIRECTORY mdDir;
DWORD currRva, written;
DWORD i, nStream, addStream;
RVA rva;
pcs = process_find_by_handle(hProcess);
if (!pcs) return FALSE; /* FIXME: should try to load it ??? */
/* 1) init */
nStream = UserStreamParam ? UserStreamParam->UserStreamCount : 0;
addStream = 0;
if (DumpType & MiniDumpNormal)
addStream += 3; /* sure ? thread stack back trace */
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 */
rva = sizeof(mdHead);
mdHead.Signature = MINIDUMP_SIGNATURE;
mdHead.Version = MINIDUMP_VERSION;
mdHead.NumberOfStreams = nStream + addStream;
mdHead.StreamDirectoryRva = rva;
mdHead.u.TimeDateStamp = time(NULL);
mdHead.Flags = DumpType;
WriteFile(hFile, &mdHead, sizeof(mdHead), &written, NULL);
/* 3) write stream directories */
rva += (nStream + addStream) * sizeof(mdDir);
/* 3.1) write data stream directories */
currRva = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
SetFilePointer(hFile, rva, NULL, FILE_BEGIN);
mdDir.StreamType = ModuleListStream;
mdDir.Location.Rva = rva;
dump_modules(pcs, hFile, &rva);
mdDir.Location.DataSize = SetFilePointer(hFile, 0, NULL, FILE_CURRENT) - mdDir.Location.Rva;
SetFilePointer(hFile, currRva, NULL, FILE_BEGIN);
WriteFile(hFile, &mdDir, sizeof(mdDir), &written, NULL);
currRva = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
SetFilePointer(hFile, rva, NULL, FILE_BEGIN);
mdDir.StreamType = ThreadListStream;
mdDir.Location.Rva = rva;
dump_threads(pcs, hFile, &rva);
mdDir.Location.DataSize = SetFilePointer(hFile, 0, NULL, FILE_CURRENT) - mdDir.Location.Rva;
SetFilePointer(hFile, currRva, NULL, FILE_BEGIN);
WriteFile(hFile, &mdDir, sizeof(mdDir), &written, NULL);
currRva = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
SetFilePointer(hFile, rva, NULL, FILE_BEGIN);
mdDir.StreamType = SystemInfoStream;
mdDir.Location.Rva = rva;
dump_system_info(pcs, hFile, &rva);
mdDir.Location.DataSize = SetFilePointer(hFile, 0, NULL, FILE_CURRENT) - mdDir.Location.Rva;
SetFilePointer(hFile, currRva, NULL, FILE_BEGIN);
WriteFile(hFile, &mdDir, sizeof(mdDir), &written, NULL);
/* 3.2) write user define stream */
for (i = 0; i < nStream; i++)
{
mdDir.StreamType = UserStreamParam->UserStreamArray[i].Type;
mdDir.Location.DataSize = UserStreamParam->UserStreamArray[i].BufferSize;
mdDir.Location.Rva = rva;
WriteFile(hFile, &mdDir, sizeof(mdDir), &written, NULL);
currRva = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
SetFilePointer(hFile, rva, NULL, FILE_BEGIN);
WriteFile(hFile,
UserStreamParam->UserStreamArray[i].Buffer,
UserStreamParam->UserStreamArray[i].BufferSize,
&written, NULL);
rva += UserStreamParam->UserStreamArray[i].BufferSize;
SetFilePointer(hFile, currRva, NULL, FILE_BEGIN);
}
return TRUE;
}
/*
* File module.c - module handling for the wine debugger
*
* Copyright (C) 1993, Eric Youngdale.
* 2000-2004, Eric Pouech
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "dbghelp_private.h"
#include "psapi.h"
#include "winreg.h"
#include "winternl.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
/***********************************************************************
* Creates and links a new module to a process
*/
struct module* module_new(struct process* pcs, const char* name,
enum DbgModuleType type,
unsigned long mod_addr, unsigned long size,
unsigned long stamp, unsigned long checksum)
{
struct module* module;
const char* ptr;
if (!(module = HeapAlloc(GetProcessHeap(), 0, sizeof(*module))))
return NULL;
memset(module, 0, sizeof(*module));
module->next = pcs->lmodules;
pcs->lmodules = module;
TRACE("=> %s %08lx-%08lx %s\n",
type == DMT_ELF ? "ELF" : (type == DMT_PE ? "PE" : "---"),
mod_addr, mod_addr + size, name);
pool_init(&module->pool, 65536);
module->module.SizeOfStruct = sizeof(module->module);
module->module.BaseOfImage = mod_addr;
module->module.ImageSize = size;
for (ptr = name + strlen(name) - 1;
*ptr != '/' && *ptr != '\\' && ptr >= name;
ptr--);
if (ptr < name || *ptr == '/' || *ptr == '\\') ptr++;
strncpy(module->module.ModuleName, ptr, sizeof(module->module.ModuleName));
module->module.ModuleName[sizeof(module->module.ModuleName) - 1] = '\0';
module->module.ImageName[0] = '\0';
strncpy(module->module.LoadedImageName, name,
sizeof(module->module.LoadedImageName));
module->module.LoadedImageName[sizeof(module->module.LoadedImageName) - 1] = '\0';
module->module.SymType = SymNone;
module->module.NumSyms = 0;
module->module.TimeDateStamp = stamp;
module->module.CheckSum = checksum;
module->type = type;
module->sortlist_valid = FALSE;
module->addr_sorttab = NULL;
/* FIXME: this seems a bit too high (on a per module basis)
* need some statistics about this
*/
hash_table_init(&module->pool, &module->ht_symbols, 4096);
hash_table_init(&module->pool, &module->ht_types, 4096);
module->sources_used = 0;
module->sources_alloc = 0;
module->sources = 0;
return module;
}
/***********************************************************************
* module_find_by_name
*
*/
struct module* module_find_by_name(const struct process* pcs,
const char* name, enum DbgModuleType type)
{
struct module* module;
if (type == DMT_UNKNOWN)
{
if ((module = module_find_by_name(pcs, name, DMT_PE)) ||
(module = module_find_by_name(pcs, name, DMT_ELF)))
return module;
}
else
{
for (module = pcs->lmodules; module; module = module->next)
{
if (type == module->type && !strcasecmp(name, module->module.LoadedImageName))
return module;
}
for (module = pcs->lmodules; module; module = module->next)
{
if (type == module->type && !strcasecmp(name, module->module.ModuleName))
return module;
}
}
SetLastError(ERROR_INVALID_NAME);
return NULL;
}
/***********************************************************************
* module_has_container
*
*/
static struct module* module_get_container(const struct process* pcs,
const struct module* inner)
{
struct module* module;
for (module = pcs->lmodules; module; module = module->next)
{
if (module != inner &&
module->module.BaseOfImage <= inner->module.BaseOfImage &&
module->module.BaseOfImage + module->module.ImageSize >=
inner->module.BaseOfImage + inner->module.ImageSize)
return module;
}
return NULL;
}
/******************************************************************
* module_get_debug
*
* get the debug information from a module:
* - if the module's type is deferred, then force loading of debug info (and return
* the module itself)
* - if the module has no debug info and has an ELF container, then return the ELF
* container (and also force the ELF container's debug info loading if deferred)
* - otherwise return the module itself if it has some debug info
*/
struct module* module_get_debug(const struct process* pcs, struct module* module)
{
if (!module) return NULL;
switch (module->module.SymType)
{
case -1: break;
case SymNone:
module = module_get_container(pcs, module);
if (!module || module->module.SymType != SymDeferred) break;
/* fall through */
case SymDeferred:
switch (module->type)
{
case DMT_ELF:
elf_load_debug_info(module);
break;
case DMT_PE:
pe_load_debug_info(pcs, module);
break;
default: break;
}
break;
default: break;
}
return (module && module->module.SymType > SymNone) ? module : NULL;
}
/***********************************************************************
* module_find_by_addr
*
* either the addr where module is loaded, or any address inside the
* module
*/
struct module* module_find_by_addr(const struct process* pcs, unsigned long addr,
enum DbgModuleType type)
{
struct module* module;
if (type == DMT_UNKNOWN)
{
if ((module = module_find_by_addr(pcs, addr, DMT_PE)) ||
(module = module_find_by_addr(pcs, addr, DMT_ELF)))
return module;
}
else
{
for (module = pcs->lmodules; module; module = module->next)
{
if (type == module->type && addr >= module->module.BaseOfImage &&
addr < module->module.BaseOfImage + module->module.ImageSize)
return module;
}
}
SetLastError(ERROR_INVALID_ADDRESS);
return module;
}
/***********************************************************************
* SymLoadModule (DBGHELP.@)
*/
DWORD WINAPI SymLoadModule(HANDLE hProcess, HANDLE hFile, char* ImageName,
char* ModuleName, DWORD BaseOfDll, DWORD SizeOfDll)
{
struct process* pcs;
struct module* module = NULL;
TRACE("(%p %p %s %s %08lx %08lx)\n",
hProcess, hFile, debugstr_a(ImageName), debugstr_a(ModuleName),
BaseOfDll, SizeOfDll);
pcs = process_find_by_handle(hProcess);
if (!pcs) return FALSE;
if (!(module = pe_load_module(pcs, ImageName, hFile, BaseOfDll, SizeOfDll)))
{
unsigned len = strlen(ImageName);
if (!strcmp(ImageName + len - 3, ".so") &&
(module = elf_load_module(pcs, ImageName))) goto done;
if ((module = pe_load_module_from_pcs(pcs, ImageName, ModuleName, BaseOfDll, SizeOfDll)))
goto done;
WARN("Couldn't locate %s\n", ImageName);
return 0;
}
done:
/* by default pe_load_module fills module.ModuleName from a derivation
* of ImageName. Overwrite it, if we have better information
*/
if (ModuleName)
{
strncpy(module->module.ModuleName, ModuleName,
sizeof(module->module.ModuleName));
module->module.ModuleName[sizeof(module->module.ModuleName) - 1] = '\0';
}
strncpy(module->module.ImageName, ImageName, sizeof(module->module.ImageName));
module->module.ImageName[sizeof(module->module.ImageName) - 1] = '\0';
/* force transparent ELF loading / unloading */
if (module->type != DMT_ELF) elf_synchronize_module_list(pcs);
return module->module.BaseOfImage;
}
/******************************************************************
* module_remove
*
*/
BOOL module_remove(struct process* pcs, struct module* module)
{
struct module** p;
TRACE("%s (%p)\n", module->module.ModuleName, module);
hash_table_destroy(&module->ht_symbols);
hash_table_destroy(&module->ht_types);
HeapFree(GetProcessHeap(), 0, (char*)module->sources);
HeapFree(GetProcessHeap(), 0, module->addr_sorttab);
pool_destroy(&module->pool);
for (p = &pcs->lmodules; *p; p = &(*p)->next)
{
if (*p == module)
{
*p = module->next;
HeapFree(GetProcessHeap(), 0, module);
return TRUE;
}
}
FIXME("This shouldn't happen\n");
return FALSE;
}
/******************************************************************
* SymUnloadModule (DBGHELP.@)
*
*/
BOOL WINAPI SymUnloadModule(HANDLE hProcess, DWORD BaseOfDll)
{
struct process* pcs;
struct module* module;
pcs = process_find_by_handle(hProcess);
if (!pcs) return FALSE;
module = module_find_by_addr(pcs, BaseOfDll, DMT_UNKNOWN);
if (!module) return FALSE;
return module_remove(pcs, module);
}
/******************************************************************
* SymEnumerateModules (DBGHELP.@)
*
*/
BOOL WINAPI SymEnumerateModules(HANDLE hProcess,
PSYM_ENUMMODULES_CALLBACK EnumModulesCallback,
PVOID UserContext)
{
struct process* pcs = process_find_by_handle(hProcess);
struct module* module;
if (!pcs) return FALSE;
for (module = pcs->lmodules; module; module = module->next)
{
if (module->type != DMT_PE) continue;
if (!EnumModulesCallback(module->module.ModuleName,
module->module.BaseOfImage, UserContext))
break;
}
return TRUE;
}
/******************************************************************
* EnumerateLoadedModules (DBGHELP.@)
*
*/
BOOL WINAPI EnumerateLoadedModules(HANDLE hProcess,
PENUMLOADED_MODULES_CALLBACK EnumLoadedModulesCallback,
PVOID UserContext)
{
HMODULE* hMods;
char img[256], mod[256];
DWORD i, sz;
MODULEINFO mi;
hMods = HeapAlloc(GetProcessHeap(), 0, sz);
if (!hMods) return FALSE;
if (!EnumProcessModules(hProcess, hMods, 256 * sizeof(hMods[0]), &sz))
{
/* hProcess should also be a valid process handle !! */
FIXME("If this happens, bump the number in mod\n");
HeapFree(GetProcessHeap(), 0, hMods);
return FALSE;
}
sz /= sizeof(HMODULE);
for (i = 0; i < sz; i++)
{
if (!GetModuleInformation(hProcess, hMods[i], &mi, sizeof(mi)) ||
!GetModuleFileNameExA(hProcess, hMods[i], img, sizeof(img)) ||
!GetModuleBaseNameA(hProcess, hMods[i], mod, sizeof(mod)))
break;
EnumLoadedModulesCallback(mod, (DWORD)mi.lpBaseOfDll, mi.SizeOfImage,
UserContext);
}
HeapFree(GetProcessHeap(), 0, hMods);
return sz != 0 && i == sz;
}
/******************************************************************
* SymGetModuleInfo (DBGHELP.@)
*
*/
BOOL WINAPI SymGetModuleInfo(HANDLE hProcess, DWORD dwAddr,
PIMAGEHLP_MODULE ModuleInfo)
{
struct process* pcs = process_find_by_handle(hProcess);
struct module* module;
if (!pcs) return FALSE;
if (ModuleInfo->SizeOfStruct < sizeof(*ModuleInfo)) return FALSE;
module = module_find_by_addr(pcs, dwAddr, DMT_UNKNOWN);
if (!module) return FALSE;
*ModuleInfo = module->module;
if (module->module.SymType <= SymNone)
{
module = module_get_container(pcs, module);
if (module && module->module.SymType > SymNone)
ModuleInfo->SymType = module->module.SymType;
}
return TRUE;
}
/***********************************************************************
* SymGetModuleBase (IMAGEHLP.@)
*/
DWORD WINAPI SymGetModuleBase(HANDLE hProcess, DWORD dwAddr)
{
struct process* pcs = process_find_by_handle(hProcess);
struct module* module;
if (!pcs) return 0;
module = module_find_by_addr(pcs, dwAddr, DMT_UNKNOWN);
if (!module) return 0;
return module->module.BaseOfImage;
}
/*
* 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, Eric Pouech.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* 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 "winreg.h"
#include "winternl.h"
#include "wine/exception.h"
#include "wine/debug.h"
#include "excpt.h"
#include "dbghelp_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(dbghelp_msc);
#define MAX_PATHNAME_LEN 1024
typedef struct
{
DWORD from;
DWORD to;
} OMAP_DATA;
struct msc_debug_info
{
struct module* module;
int nsect;
PIMAGE_SECTION_HEADER sectp;
int nomap;
OMAP_DATA* omapp;
const BYTE* root;
};
/*========================================================================
* Debug file access helper routines
*/
static WINE_EXCEPTION_FILTER(page_fault)
{
if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
return EXCEPTION_EXECUTE_HANDLER;
return EXCEPTION_CONTINUE_SEARCH;
}
/*========================================================================
* 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, 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;
}
static SYM_TYPE 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;
SYM_TYPE sym_type = SymNone;
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*)((unsigned int)coff +
coff->LvaToFirstSymbol);
coff_linetab = (const IMAGE_LINENUMBER*)((unsigned int)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,
(char*)(coff_sym + 1));
TRACE("New file %s\n", (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: %lx %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 %08lx %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: %lx %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: %lx %s\n",
i, 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: %lx %s\n",
i, 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);
}
}
}
}
sym_type = SymCoff;
for (j = 0; j < coff_files.nfiles; j++)
{
if (coff_files.files[j].entries != NULL)
{
HeapFree(GetProcessHeap(), 0, coff_files.files[j].entries);
}
}
HeapFree(GetProcessHeap(), 0, coff_files.files);
}
return sym_type;
}
/*========================================================================
* Process CodeView type information.
*/
union codeview_type
{
struct
{
unsigned short int len;
short int id;
} generic;
struct
{
unsigned short int len;
short int id;
short int attribute;
short int datatype;
unsigned char variant[1];
} pointer;
struct
{
unsigned short int len;
short int id;
unsigned int datatype;
unsigned int attribute;
unsigned char variant[1];
} pointer32;
struct
{
unsigned short int len;
short int id;
unsigned char nbits;
unsigned char bitoff;
unsigned short type;
} bitfield;
struct
{
unsigned short int len;
short int id;
unsigned int type;
unsigned char nbits;
unsigned char bitoff;
} bitfield32;
struct
{
unsigned short int len;
short int id;
short int elemtype;
short int idxtype;
unsigned short int arrlen; /* numeric leaf */
#if 0
unsigned char name[1];
#endif
} array;
struct
{
unsigned short int len;
short int id;
unsigned int elemtype;
unsigned int idxtype;
unsigned short int arrlen; /* numeric leaf */
#if 0
unsigned char name[1];
#endif
} array32;
struct
{
unsigned short int len;
short int id;
short int n_element;
short int fieldlist;
short int property;
short int derived;
short int vshape;
unsigned short int structlen; /* numeric leaf */
#if 0
unsigned char name[1];
#endif
} structure;
struct
{
unsigned short int len;
short int id;
short int n_element;
short int property;
unsigned int fieldlist;
unsigned int derived;
unsigned int vshape;
unsigned short int structlen; /* numeric leaf */
#if 0
unsigned char name[1];
#endif
} structure32;
struct
{
unsigned short int len;
short int id;
short int count;
short int fieldlist;
short int property;
unsigned short int un_len; /* numeric leaf */
#if 0
unsigned char name[1];
#endif
} t_union;
struct
{
unsigned short int len;
short int id;
short int count;
short int property;
unsigned int fieldlist;
unsigned short int un_len; /* numeric leaf */
#if 0
unsigned char name[1];
#endif
} t_union32;
struct
{
unsigned short int len;
short int id;
short int count;
short int type;
short int field;
short int property;
unsigned char name[1];
} enumeration;
struct
{
unsigned short int len;
short int id;
short int count;
short int property;
unsigned int type;
unsigned int field;
unsigned char name[1];
} enumeration32;
struct
{
unsigned short int len;
short int id;
unsigned char list[1];
} fieldlist;
};
union codeview_fieldtype
{
struct
{
short int id;
} generic;
struct
{
short int id;
short int type;
short int attribute;
unsigned short int offset; /* numeric leaf */
} bclass;
struct
{
short int id;
short int attribute;
unsigned int type;
unsigned short int offset; /* numeric leaf */
} bclass32;
struct
{
short int id;
short int btype;
short int vbtype;
short int attribute;
unsigned short int vbpoff; /* numeric leaf */
#if 0
unsigned short int vboff; /* numeric leaf */
#endif
} vbclass;
struct
{
short int id;
short int attribute;
unsigned int btype;
unsigned int vbtype;
unsigned short int vbpoff; /* numeric leaf */
#if 0
unsigned short int vboff; /* numeric leaf */
#endif
} vbclass32;
struct
{
short int id;
short int attribute;
unsigned short int value; /* numeric leaf */
#if 0
unsigned char name[1];
#endif
} enumerate;
struct
{
short int id;
short int type;
unsigned char name[1];
} friendfcn;
struct
{
short int id;
short int _pad0;
unsigned int type;
unsigned char name[1];
} friendfcn32;
struct
{
short int id;
short int type;
short int attribute;
unsigned short int offset; /* numeric leaf */
#if 0
unsigned char name[1];
#endif
} member;
struct
{
short int id;
short int attribute;
unsigned int type;
unsigned short int offset; /* numeric leaf */
#if 0
unsigned char name[1];
#endif
} member32;
struct
{
short int id;
short int type;
short int attribute;
unsigned char name[1];
} stmember;
struct
{
short int id;
short int attribute;
unsigned int type;
unsigned char name[1];
} stmember32;
struct
{
short int id;
short int count;
short int mlist;
unsigned char name[1];
} method;
struct
{
short int id;
short int count;
unsigned int mlist;
unsigned char name[1];
} method32;
struct
{
short int id;
short int index;
unsigned char name[1];
} nesttype;
struct
{
short int id;
short int _pad0;
unsigned int index;
unsigned char name[1];
} nesttype32;
struct
{
short int id;
short int type;
} vfunctab;
struct
{
short int id;
short int _pad0;
unsigned int type;
} vfunctab32;
struct
{
short int id;
short int type;
} friendcls;
struct
{
short int id;
short int _pad0;
unsigned int type;
} friendcls32;
struct
{
short int id;
short int attribute;
short int type;
unsigned char name[1];
} onemethod;
struct
{
short int id;
short int attribute;
short int type;
unsigned int vtab_offset;
unsigned char name[1];
} onemethod_virt;
struct
{
short int id;
short int attribute;
unsigned int type;
unsigned char name[1];
} onemethod32;
struct
{
short int id;
short int attribute;
unsigned int type;
unsigned int vtab_offset;
unsigned char name[1];
} onemethod32_virt;
struct
{
short int id;
short int type;
unsigned int offset;
} vfuncoff;
struct
{
short int id;
short int _pad0;
unsigned int type;
unsigned int offset;
} vfuncoff32;
struct
{
short int id;
short int attribute;
short int index;
unsigned char name[1];
} nesttypeex;
struct
{
short int id;
short int attribute;
unsigned int index;
unsigned char name[1];
} nesttypeex32;
struct
{
short int id;
short int attribute;
unsigned int type;
unsigned char name[1];
} membermodify;
};
/*
* This covers the basic datatypes that VC++ seems to be using these days.
* 32 bit mode only. There are additional numbers for the pointers in 16
* bit mode. There are many other types listed in the documents, but these
* are apparently not used by the compiler, or represent pointer types
* that are not used.
*/
#define T_NOTYPE 0x0000 /* Notype */
#define T_ABS 0x0001 /* Abs */
#define T_VOID 0x0003 /* Void */
#define T_CHAR 0x0010 /* signed char */
#define T_SHORT 0x0011 /* short */
#define T_LONG 0x0012 /* long */
#define T_QUAD 0x0013 /* long long */
#define T_UCHAR 0x0020 /* unsigned char */
#define T_USHORT 0x0021 /* unsigned short */
#define T_ULONG 0x0022 /* unsigned long */
#define T_UQUAD 0x0023 /* unsigned long long */
#define T_REAL32 0x0040 /* float */
#define T_REAL64 0x0041 /* double */
#define T_RCHAR 0x0070 /* real char */
#define T_WCHAR 0x0071 /* wide char */
#define T_INT4 0x0074 /* int */
#define T_UINT4 0x0075 /* unsigned int */
#define T_32PVOID 0x0403 /* 32 bit near pointer to void */
#define T_32PCHAR 0x0410 /* 16:32 near pointer to signed char */
#define T_32PSHORT 0x0411 /* 16:32 near pointer to short */
#define T_32PLONG 0x0412 /* 16:32 near pointer to int */
#define T_32PQUAD 0x0413 /* 16:32 near pointer to long long */
#define T_32PUCHAR 0x0420 /* 16:32 near pointer to unsigned char */
#define T_32PUSHORT 0x0421 /* 16:32 near pointer to unsigned short */
#define T_32PULONG 0x0422 /* 16:32 near pointer to unsigned int */
#define T_32PUQUAD 0x0423 /* 16:32 near pointer to long long */
#define T_32PREAL32 0x0440 /* 16:32 near pointer to float */
#define T_32PREAL64 0x0441 /* 16:32 near pointer to float */
#define T_32PRCHAR 0x0470 /* 16:32 near pointer to real char */
#define T_32PWCHAR 0x0471 /* 16:32 near pointer to real char */
#define T_32PINT4 0x0474 /* 16:32 near pointer to int */
#define T_32PUINT4 0x0475 /* 16:32 near pointer to unsigned int */
#define LF_MODIFIER 0x0001
#define LF_POINTER 0x0002
#define LF_ARRAY 0x0003
#define LF_CLASS 0x0004
#define LF_STRUCTURE 0x0005
#define LF_UNION 0x0006
#define LF_ENUM 0x0007
#define LF_PROCEDURE 0x0008
#define LF_MFUNCTION 0x0009
#define LF_VTSHAPE 0x000a
#define LF_COBOL0 0x000b
#define LF_COBOL1 0x000c
#define LF_BARRAY 0x000d
#define LF_LABEL 0x000e
#define LF_NULL 0x000f
#define LF_NOTTRAN 0x0010
#define LF_DIMARRAY 0x0011
#define LF_VFTPATH 0x0012
#define LF_PRECOMP 0x0013
#define LF_ENDPRECOMP 0x0014
#define LF_OEM 0x0015
#define LF_TYPESERVER 0x0016
#define LF_MODIFIER_32 0x1001 /* variants with new 32-bit type indices */
#define LF_POINTER_32 0x1002
#define LF_ARRAY_32 0x1003
#define LF_CLASS_32 0x1004
#define LF_STRUCTURE_32 0x1005
#define LF_UNION_32 0x1006
#define LF_ENUM_32 0x1007
#define LF_PROCEDURE_32 0x1008
#define LF_MFUNCTION_32 0x1009
#define LF_COBOL0_32 0x100a
#define LF_BARRAY_32 0x100b
#define LF_DIMARRAY_32 0x100c
#define LF_VFTPATH_32 0x100d
#define LF_PRECOMP_32 0x100e
#define LF_OEM_32 0x100f
#define LF_SKIP 0x0200
#define LF_ARGLIST 0x0201
#define LF_DEFARG 0x0202
#define LF_LIST 0x0203
#define LF_FIELDLIST 0x0204
#define LF_DERIVED 0x0205
#define LF_BITFIELD 0x0206
#define LF_METHODLIST 0x0207
#define LF_DIMCONU 0x0208
#define LF_DIMCONLU 0x0209
#define LF_DIMVARU 0x020a
#define LF_DIMVARLU 0x020b
#define LF_REFSYM 0x020c
#define LF_SKIP_32 0x1200 /* variants with new 32-bit type indices */
#define LF_ARGLIST_32 0x1201
#define LF_DEFARG_32 0x1202
#define LF_FIELDLIST_32 0x1203
#define LF_DERIVED_32 0x1204
#define LF_BITFIELD_32 0x1205
#define LF_METHODLIST_32 0x1206
#define LF_DIMCONU_32 0x1207
#define LF_DIMCONLU_32 0x1208
#define LF_DIMVARU_32 0x1209
#define LF_DIMVARLU_32 0x120a
#define LF_BCLASS 0x0400
#define LF_VBCLASS 0x0401
#define LF_IVBCLASS 0x0402
#define LF_ENUMERATE 0x0403
#define LF_FRIENDFCN 0x0404
#define LF_INDEX 0x0405
#define LF_MEMBER 0x0406
#define LF_STMEMBER 0x0407
#define LF_METHOD 0x0408
#define LF_NESTTYPE 0x0409
#define LF_VFUNCTAB 0x040a
#define LF_FRIENDCLS 0x040b
#define LF_ONEMETHOD 0x040c
#define LF_VFUNCOFF 0x040d
#define LF_NESTTYPEEX 0x040e
#define LF_MEMBERMODIFY 0x040f
#define LF_BCLASS_32 0x1400 /* variants with new 32-bit type indices */
#define LF_VBCLASS_32 0x1401
#define LF_IVBCLASS_32 0x1402
#define LF_FRIENDFCN_32 0x1403
#define LF_INDEX_32 0x1404
#define LF_MEMBER_32 0x1405
#define LF_STMEMBER_32 0x1406
#define LF_METHOD_32 0x1407
#define LF_NESTTYPE_32 0x1408
#define LF_VFUNCTAB_32 0x1409
#define LF_FRIENDCLS_32 0x140a
#define LF_ONEMETHOD_32 0x140b
#define LF_VFUNCOFF_32 0x140c
#define LF_NESTTYPEEX_32 0x140d
#define LF_NUMERIC 0x8000 /* numeric leaf types */
#define LF_CHAR 0x8000
#define LF_SHORT 0x8001
#define LF_USHORT 0x8002
#define LF_LONG 0x8003
#define LF_ULONG 0x8004
#define LF_REAL32 0x8005
#define LF_REAL64 0x8006
#define LF_REAL80 0x8007
#define LF_REAL128 0x8008
#define LF_QUADWORD 0x8009
#define LF_UQUADWORD 0x800a
#define LF_REAL48 0x800b
#define LF_COMPLEX32 0x800c
#define LF_COMPLEX64 0x800d
#define LF_COMPLEX80 0x800e
#define LF_COMPLEX128 0x800f
#define LF_VARSTRING 0x8010
#define MAX_BUILTIN_TYPES 0x480
static struct symt* cv_basic_types[MAX_BUILTIN_TYPES];
static unsigned int num_cv_defined_types = 0;
static struct symt** cv_defined_types = NULL;
#define SymTagCVBitField (SymTagMax + 0x100)
struct codeview_bitfield
{
struct symt symt;
unsigned subtype;
unsigned bitposition;
unsigned bitlength;
};
static struct codeview_bitfield* cv_bitfields;
static unsigned num_cv_bitfields;
static unsigned used_cv_bitfields;
static void codeview_init_basic_types(struct module* module)
{
/*
* These are the common builtin types that are used by VC++.
*/
cv_basic_types[T_NOTYPE] = NULL;
cv_basic_types[T_ABS] = NULL;
cv_basic_types[T_VOID] = &symt_new_basic(module, btVoid, "void", 0)->symt;
cv_basic_types[T_CHAR] = &symt_new_basic(module, btChar, "char", 1)->symt;
cv_basic_types[T_SHORT] = &symt_new_basic(module, btInt, "short int", 2)->symt;
cv_basic_types[T_LONG] = &symt_new_basic(module, btInt, "long int", 4)->symt;
cv_basic_types[T_QUAD] = &symt_new_basic(module, btInt, "long long int", 8)->symt;
cv_basic_types[T_UCHAR] = &symt_new_basic(module, btUInt, "unsignd char", 1)->symt;
cv_basic_types[T_USHORT] = &symt_new_basic(module, btUInt, "unsigned short", 2)->symt;
cv_basic_types[T_ULONG] = &symt_new_basic(module, btUInt, "unsigned long", 4)->symt;
cv_basic_types[T_UQUAD] = &symt_new_basic(module, btUInt, "unsigned long long", 8)->symt;
cv_basic_types[T_REAL32] = &symt_new_basic(module, btFloat, "float", 4)->symt;
cv_basic_types[T_REAL64] = &symt_new_basic(module, btFloat, "double", 8)->symt;
cv_basic_types[T_RCHAR] = &symt_new_basic(module, btInt, "signed char", 1)->symt;
cv_basic_types[T_WCHAR] = &symt_new_basic(module, btWChar, "wchar_t", 2)->symt;
cv_basic_types[T_INT4] = &symt_new_basic(module, btInt, "INT4", 4)->symt;
cv_basic_types[T_UINT4] = &symt_new_basic(module, btUInt, "UINT4", 4)->symt;
cv_basic_types[T_32PVOID] = &symt_new_pointer(module, cv_basic_types[T_VOID])->symt;
cv_basic_types[T_32PCHAR] = &symt_new_pointer(module, cv_basic_types[T_CHAR])->symt;
cv_basic_types[T_32PSHORT] = &symt_new_pointer(module, cv_basic_types[T_SHORT])->symt;
cv_basic_types[T_32PLONG] = &symt_new_pointer(module, cv_basic_types[T_LONG])->symt;
cv_basic_types[T_32PQUAD] = &symt_new_pointer(module, cv_basic_types[T_QUAD])->symt;
cv_basic_types[T_32PUCHAR] = &symt_new_pointer(module, cv_basic_types[T_UCHAR])->symt;
cv_basic_types[T_32PUSHORT] = &symt_new_pointer(module, cv_basic_types[T_USHORT])->symt;
cv_basic_types[T_32PULONG] = &symt_new_pointer(module, cv_basic_types[T_ULONG])->symt;
cv_basic_types[T_32PUQUAD] = &symt_new_pointer(module, cv_basic_types[T_UQUAD])->symt;
cv_basic_types[T_32PREAL32] = &symt_new_pointer(module, cv_basic_types[T_REAL32])->symt;
cv_basic_types[T_32PREAL64] = &symt_new_pointer(module, cv_basic_types[T_REAL64])->symt;
cv_basic_types[T_32PRCHAR] = &symt_new_pointer(module, cv_basic_types[T_RCHAR])->symt;
cv_basic_types[T_32PWCHAR] = &symt_new_pointer(module, cv_basic_types[T_WCHAR])->symt;
cv_basic_types[T_32PINT4] = &symt_new_pointer(module, cv_basic_types[T_INT4])->symt;
cv_basic_types[T_32PUINT4] = &symt_new_pointer(module, cv_basic_types[T_UINT4])->symt;
}
static int numeric_leaf(int* value, const unsigned short int* leaf)
{
unsigned short int type = *leaf++;
int length = 2;
if (type < LF_NUMERIC)
{
*value = type;
}
else
{
switch (type)
{
case LF_CHAR:
length += 1;
*value = *(char*)leaf;
break;
case LF_SHORT:
length += 2;
*value = *(short*)leaf;
break;
case LF_USHORT:
length += 2;
*value = *(unsigned short*)leaf;
break;
case LF_LONG:
length += 4;
*value = *(int*)leaf;
break;
case LF_ULONG:
length += 4;
*value = *(unsigned int*)leaf;
break;
case LF_QUADWORD:
case LF_UQUADWORD:
length += 8;
*value = 0; /* FIXME */
break;
case LF_REAL32:
length += 4;
*value = 0; /* FIXME */
break;
case LF_REAL48:
length += 6;
*value = 0; /* FIXME */
break;
case LF_REAL64:
length += 8;
*value = 0; /* FIXME */
break;
case LF_REAL80:
length += 10;
*value = 0; /* FIXME */
break;
case LF_REAL128:
length += 16;
*value = 0; /* FIXME */
break;
case LF_COMPLEX32:
length += 4;
*value = 0; /* FIXME */
break;
case LF_COMPLEX64:
length += 8;
*value = 0; /* FIXME */
break;
case LF_COMPLEX80:
length += 10;
*value = 0; /* FIXME */
break;
case LF_COMPLEX128:
length += 16;
*value = 0; /* FIXME */
break;
case LF_VARSTRING:
length += 2 + *leaf;
*value = 0; /* FIXME */
break;
default:
FIXME("Unknown numeric leaf type %04x\n", type);
*value = 0;
break;
}
}
return length;
}
static const char* terminate_string(const unsigned char* name)
{
static char symname[256];
int namelen = name[0];
assert(namelen >= 0 && namelen < 256);
memcpy(symname, name + 1, namelen);
symname[namelen] = '\0';
return (!*symname || strcmp(symname, "__unnamed") == 0) ? NULL : symname;
}
static struct symt* codeview_get_type(unsigned int typeno, BOOL allow_special)
{
struct symt* symt = NULL;
/*
* Convert Codeview type numbers into something we can grok internally.
* Numbers < 0x1000 are all fixed builtin types. Numbers from 0x1000 and
* up are all user defined (structs, etc).
*/
if (typeno < 0x1000)
{
if (typeno < MAX_BUILTIN_TYPES)
symt = cv_basic_types[typeno];
}
else
{
if (typeno - 0x1000 < num_cv_defined_types)
symt = cv_defined_types[typeno - 0x1000];
}
if (!allow_special && symt && symt->tag == SymTagCVBitField)
FIXME("bitfields are only handled for UDTs\n");
return symt;
}
static int codeview_add_type(unsigned int typeno, struct symt* dt)
{
while (typeno - 0x1000 >= num_cv_defined_types)
{
num_cv_defined_types += 0x100;
if (cv_defined_types)
cv_defined_types = (struct symt**)
HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cv_defined_types,
num_cv_defined_types * sizeof(struct symt*));
else
cv_defined_types = (struct symt**)
HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
num_cv_defined_types * sizeof(struct symt*));
if (cv_defined_types == NULL) return FALSE;
}
cv_defined_types[typeno - 0x1000] = dt;
return TRUE;
}
static void codeview_clear_type_table(void)
{
if (cv_defined_types) HeapFree(GetProcessHeap(), 0, cv_defined_types);
cv_defined_types = NULL;
num_cv_defined_types = 0;
if (cv_bitfields) HeapFree(GetProcessHeap(), 0, cv_bitfields);
cv_bitfields = NULL;
num_cv_bitfields = used_cv_bitfields = 0;
}
static int codeview_add_type_pointer(struct module* module, unsigned int typeno,
unsigned int datatype)
{
struct symt* symt = &symt_new_pointer(module, codeview_get_type(datatype, FALSE))->symt;
return codeview_add_type(typeno, symt);
}
static int codeview_add_type_array(struct module* module,
unsigned int typeno, const char* name,
unsigned int elemtype, unsigned int arr_len)
{
struct symt* symt;
struct symt* elem = codeview_get_type(elemtype, FALSE);
DWORD arr_max = 0;
if (elem)
{
DWORD elem_size;
symt_get_info(elem, TI_GET_LENGTH, &elem_size);
if (elem_size) arr_max = arr_len / elem_size;
}
symt = &symt_new_array(module, 0, arr_max, elem)->symt;
return codeview_add_type(typeno, symt);
}
static int codeview_add_type_bitfield(unsigned int typeno, unsigned int bitoff,
unsigned int nbits, unsigned int basetype)
{
if (used_cv_bitfields >= num_cv_bitfields)
{
num_cv_bitfields *= 2;
if (cv_bitfields)
cv_bitfields = HeapReAlloc(GetProcessHeap(), 0, cv_bitfields,
num_cv_bitfields * sizeof(struct codeview_bitfield));
else
cv_bitfields = HeapAlloc(GetProcessHeap(), 0,
num_cv_bitfields * sizeof(struct codeview_bitfield));
if (!cv_bitfields) return 0;
}
cv_bitfields[used_cv_bitfields].symt.tag = SymTagCVBitField;
cv_bitfields[used_cv_bitfields].subtype = basetype;
cv_bitfields[used_cv_bitfields].bitposition = bitoff;
cv_bitfields[used_cv_bitfields].bitlength = nbits;
return codeview_add_type(typeno, &cv_bitfields[used_cv_bitfields++].symt);
}
static int codeview_add_type_enum_field_list(struct module* module,
unsigned int typeno,
const unsigned char* list, int len)
{
struct symt_enum* symt;
const unsigned char* ptr = list;
symt = symt_new_enum(module, NULL);
while (ptr - list < len)
{
union codeview_fieldtype* type = (union codeview_fieldtype*)ptr;
if (*ptr >= 0xf0) /* LF_PAD... */
{
ptr += *ptr & 0x0f;
continue;
}
switch (type->generic.id)
{
case LF_ENUMERATE:
{
int value, vlen = numeric_leaf(&value, &type->enumerate.value);
unsigned char* name = (unsigned char*)&type->enumerate.value + vlen;
symt_add_enum_element(module, symt, terminate_string(name), value);
ptr += 2 + 2 + vlen + (1 + name[0]);
break;
}
default:
FIXME("Unhandled type %04x in ENUM field list\n", type->generic.id);
return FALSE;
}
}
return codeview_add_type(typeno, &symt->symt);
}
static int codeview_add_type_struct_field_list(struct module* module,
unsigned int typeno,
const unsigned char* list, int len)
{
struct symt_udt* symt;
const unsigned char* ptr = list;
symt = symt_new_udt(module, NULL, 0, UdtStruct /* don't care */);
while (ptr - list < len)
{
const union codeview_fieldtype* type = (const union codeview_fieldtype*)ptr;
if (*ptr >= 0xf0) /* LF_PAD... */
{
ptr +=* ptr & 0x0f;
continue;
}
switch (type->generic.id)
{
case LF_BCLASS:
{
int offset, olen = numeric_leaf(&offset, &type->bclass.offset);
/* FIXME: ignored for now */
ptr += 2 + 2 + 2 + olen;
break;
}
case LF_BCLASS_32:
{
int offset, olen = numeric_leaf(&offset, &type->bclass32.offset);
/* FIXME: ignored for now */
ptr += 2 + 2 + 4 + olen;
break;
}
case LF_VBCLASS:
case LF_IVBCLASS:
{
int vbpoff, vbplen = numeric_leaf(&vbpoff, &type->vbclass.vbpoff);
const unsigned short int* p_vboff = (const unsigned short int*)((char*)&type->vbclass.vbpoff + vbpoff);
int vpoff, vplen = numeric_leaf(&vpoff, p_vboff);
/* FIXME: ignored for now */
ptr += 2 + 2 + 2 + 2 + vbplen + vplen;
break;
}
case LF_VBCLASS_32:
case LF_IVBCLASS_32:
{
int vbpoff, vbplen = numeric_leaf(&vbpoff, &type->vbclass32.vbpoff);
const unsigned short int* p_vboff = (const unsigned short int*)((char*)&type->vbclass32.vbpoff + vbpoff);
int vpoff, vplen = numeric_leaf(&vpoff, p_vboff);
/* FIXME: ignored for now */
ptr += 2 + 2 + 4 + 4 + vbplen + vplen;
break;
}
case LF_MEMBER:
{
int offset, olen = numeric_leaf(&offset, &type->member.offset);
const unsigned char* name = (const unsigned char*)&type->member.offset + olen;
struct symt* subtype = codeview_get_type(type->member.type, TRUE);
if (!subtype || subtype->tag != SymTagCVBitField)
{
DWORD elem_size = 0;
if (subtype) symt_get_info(subtype, TI_GET_LENGTH, &elem_size);
symt_add_udt_element(module, symt, terminate_string(name),
subtype, offset << 3, elem_size << 3);
}
else
{
struct codeview_bitfield* cvbf = (struct codeview_bitfield*)subtype;
symt_add_udt_element(module, symt, terminate_string(name),
codeview_get_type(cvbf->subtype, FALSE),
cvbf->bitposition, cvbf->bitlength);
}
ptr += 2 + 2 + 2 + olen + (1 + name[0]);
break;
}
case LF_MEMBER_32:
{
int offset, olen = numeric_leaf(&offset, &type->member32.offset);
const unsigned char* name = (const unsigned char*)&type->member32.offset + olen;
struct symt* subtype = codeview_get_type(type->member32.type, TRUE);
if (!subtype || subtype->tag != SymTagCVBitField)
{
DWORD elem_size = 0;
if (subtype) symt_get_info(subtype, TI_GET_LENGTH, &elem_size);
symt_add_udt_element(module, symt, terminate_string(name),
subtype, offset << 3, elem_size << 3);
}
else
{
struct codeview_bitfield* cvbf = (struct codeview_bitfield*)subtype;
symt_add_udt_element(module, symt, terminate_string(name),
codeview_get_type(cvbf->subtype, FALSE),
cvbf->bitposition, cvbf->bitlength);
}
ptr += 2 + 2 + 4 + olen + (1 + name[0]);
break;
}
case LF_STMEMBER:
/* FIXME: ignored for now */
ptr += 2 + 2 + 2 + (1 + type->stmember.name[0]);
break;
case LF_STMEMBER_32:
/* FIXME: ignored for now */
ptr += 2 + 4 + 2 + (1 + type->stmember32.name[0]);
break;
case LF_METHOD:
/* FIXME: ignored for now */
ptr += 2 + 2 + 2 + (1 + type->method.name[0]);
break;
case LF_METHOD_32:
/* FIXME: ignored for now */
ptr += 2 + 2 + 4 + (1 + type->method32.name[0]);
break;
case LF_NESTTYPE:
/* FIXME: ignored for now */
ptr += 2 + 2 + (1 + type->nesttype.name[0]);
break;
case LF_NESTTYPE_32:
/* FIXME: ignored for now */
ptr += 2 + 2 + 4 + (1 + type->nesttype32.name[0]);
break;
case LF_VFUNCTAB:
/* FIXME: ignored for now */
ptr += 2 + 2;
break;
case LF_VFUNCTAB_32:
/* FIXME: ignored for now */
ptr += 2 + 2 + 4;
break;
case LF_ONEMETHOD:
/* FIXME: ignored for now */
switch ((type->onemethod.attribute >> 2) & 7)
{
case 4: case 6: /* (pure) introducing virtual method */
ptr += 2 + 2 + 2 + 4 + (1 + type->onemethod_virt.name[0]);
break;
default:
ptr += 2 + 2 + 2 + (1 + type->onemethod.name[0]);
break;
}
break;
case LF_ONEMETHOD_32:
/* FIXME: ignored for now */
switch ((type->onemethod32.attribute >> 2) & 7)
{
case 4: case 6: /* (pure) introducing virtual method */
ptr += 2 + 2 + 4 + 4 + (1 + type->onemethod32_virt.name[0]);
break;
default:
ptr += 2 + 2 + 4 + (1 + type->onemethod32.name[0]);
break;
}
break;
default:
FIXME("Unhandled type %04x in STRUCT field list\n", type->generic.id);
return FALSE;
}
}
return codeview_add_type(typeno, &symt->symt);
}
static int codeview_add_type_enum(struct module* module, unsigned int typeno,
const char* name, unsigned int fieldlist)
{
struct symt_enum* symt = symt_new_enum(module, name);
struct symt* list = codeview_get_type(fieldlist, FALSE);
/* FIXME: this is rather ugly !!! */
if (list) symt->vchildren = ((struct symt_enum*)list)->vchildren;
return codeview_add_type(typeno, &symt->symt);
}
static int codeview_add_type_struct(struct module* module, unsigned int typeno,
const char* name, int structlen,
unsigned int fieldlist, enum UdtKind kind)
{
struct symt_udt* symt = symt_new_udt(module, name, structlen, kind);
struct symt* list = codeview_get_type(fieldlist, FALSE);
/* FIXME: this is rather ugly !!! */
if (list) symt->vchildren = ((struct symt_udt*)list)->vchildren;
return codeview_add_type(typeno, &symt->symt);
}
static int codeview_parse_type_table(struct module* module, const char* table, int len)
{
unsigned int curr_type = 0x1000;
const char* ptr = table;
while (ptr - table < len)
{
const union codeview_type* type = (const union codeview_type*)ptr;
int retv = TRUE;
switch (type->generic.id)
{
case LF_POINTER:
retv = codeview_add_type_pointer(module, curr_type,
type->pointer.datatype);
break;
case LF_POINTER_32:
retv = codeview_add_type_pointer(module, curr_type,
type->pointer32.datatype);
break;
case LF_ARRAY:
{
int arrlen, alen = numeric_leaf(&arrlen, &type->array.arrlen);
const unsigned char* name = (const unsigned char*)&type->array.arrlen + alen;
retv = codeview_add_type_array(module, curr_type, terminate_string(name),
type->array.elemtype, arrlen);
break;
}
case LF_ARRAY_32:
{
int arrlen, alen = numeric_leaf(&arrlen, &type->array32.arrlen);
const unsigned char* name = (const unsigned char*)&type->array32.arrlen + alen;
retv = codeview_add_type_array(module, curr_type, terminate_string(name),
type->array32.elemtype,
type->array32.arrlen);
break;
}
/* a bitfields is a CodeView specific data type which represent a bitfield
* in a structure or a class. For now, we store it in a SymTag-like type
* (so that the rest of the process is seamless), but check at udt inclusion
* type for its presence
*/
case LF_BITFIELD:
retv = codeview_add_type_bitfield(curr_type, type->bitfield.bitoff,
type->bitfield.nbits,
type->bitfield.type);
break;
case LF_BITFIELD_32:
retv = codeview_add_type_bitfield(curr_type, type->bitfield32.bitoff,
type->bitfield32.nbits,
type->bitfield32.type);
break;
case LF_FIELDLIST:
case LF_FIELDLIST_32:
{
/*
* A 'field list' is a CodeView-specific data type which doesn't
* directly correspond to any high-level data type. It is used
* to hold the collection of members of a struct, class, union
* or enum type. The actual definition of that type will follow
* later, and refer to the field list definition record.
*
* As we don't have a field list type ourselves, we look ahead
* in the field list to try to find out whether this field list
* will be used for an enum or struct type, and create a dummy
* type of the corresponding sort. Later on, the definition of
* the 'real' type will copy the member / enumeration data.
*/
const char* list = type->fieldlist.list;
int len = (ptr + type->generic.len + 2) - list;
if (((union codeview_fieldtype*)list)->generic.id == LF_ENUMERATE)
retv = codeview_add_type_enum_field_list(module, curr_type, list, len);
else
retv = codeview_add_type_struct_field_list(module, curr_type, list, len);
break;
}
case LF_STRUCTURE:
case LF_CLASS:
{
int structlen, slen = numeric_leaf(&structlen, &type->structure.structlen);
const unsigned char* name = (const unsigned char*)&type->structure.structlen + slen;
retv = codeview_add_type_struct(module, curr_type, terminate_string(name),
structlen, type->structure.fieldlist,
type->generic.id == LF_CLASS ? UdtClass : UdtStruct);
break;
}
case LF_STRUCTURE_32:
case LF_CLASS_32:
{
int structlen, slen = numeric_leaf(&structlen, &type->structure32.structlen);
const unsigned char* name = (const unsigned char*)&type->structure32.structlen + slen;
retv = codeview_add_type_struct(module, curr_type, terminate_string(name),
structlen, type->structure32.fieldlist,
type->generic.id == LF_CLASS ? UdtClass : UdtStruct);
break;
}
case LF_UNION:
{
int un_len, ulen = numeric_leaf(&un_len, &type->t_union.un_len);
const unsigned char* name = (const unsigned char*)&type->t_union.un_len + ulen;
retv = codeview_add_type_struct(module, curr_type, terminate_string(name),
un_len, type->t_union.fieldlist, UdtUnion);
break;
}
case LF_UNION_32:
{
int un_len, ulen = numeric_leaf(&un_len, &type->t_union32.un_len);
const unsigned char* name = (const unsigned char*)&type->t_union32.un_len + ulen;
retv = codeview_add_type_struct(module, curr_type, terminate_string(name),
un_len, type->t_union32.fieldlist, UdtUnion);
break;
}
case LF_ENUM:
retv = codeview_add_type_enum(module, curr_type, terminate_string(type->enumeration.name),
type->enumeration.field);
break;
case LF_ENUM_32:
retv = codeview_add_type_enum(module, curr_type, terminate_string(type->enumeration32.name),
type->enumeration32.field);
break;
default:
FIXME("Unhandled leaf %x\n", type->generic.id);
break;
}
if (!retv)
return FALSE;
curr_type++;
ptr += type->generic.len + 2;
}
return TRUE;
}
/*========================================================================
* Process CodeView line number information.
*/
union any_size
{
const char* c;
const short* s;
const int* i;
const unsigned int* ui;
};
struct startend
{
unsigned int start;
unsigned int end;
};
struct codeview_linetab
{
unsigned int nline;
unsigned int segno;
unsigned int start;
unsigned int end;
struct symt_compiland* compiland;
const unsigned short* linetab;
const unsigned int* offtab;
};
static struct codeview_linetab* codeview_snarf_linetab(struct module* module,
const char* linetab, int size)
{
int file_segcount;
char filename[PATH_MAX];
const unsigned int* filetab;
char* fn;
int i;
int k;
struct codeview_linetab* lt_hdr;
unsigned int* lt_ptr;
int nfile;
int nseg;
union any_size pnt;
union any_size pnt2;
struct startend* start;
int this_seg;
struct symt_compiland* compiland;
/*
* Now get the important bits.
*/
pnt.c = linetab;
nfile = *pnt.s++;
nseg = *pnt.s++;
filetab = (const unsigned int*) pnt.c;
/*
* Now count up the number of segments in the file.
*/
nseg = 0;
for (i = 0; i < nfile; i++)
{
pnt2.c = linetab + filetab[i];
nseg += *pnt2.s;
}
/*
* Next allocate the header we will be returning.
* There is one header for each segment, so that we can reach in
* and pull bits as required.
*/
lt_hdr = (struct codeview_linetab*)
HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (nseg + 1) * sizeof(*lt_hdr));
if (lt_hdr == NULL)
{
goto leave;
}
/*
* Now fill the header we will be returning, one for each segment.
* Note that this will basically just contain pointers into the existing
* line table, and we do not actually copy any additional information
* or allocate any additional memory.
*/
this_seg = 0;
for (i = 0; i < nfile; i++)
{
/*
* Get the pointer into the segment information.
*/
pnt2.c = linetab + filetab[i];
file_segcount = *pnt2.s;
pnt2.ui++;
lt_ptr = (unsigned int*) pnt2.c;
start = (struct startend*)(lt_ptr + file_segcount);
/*
* Now snarf the filename for all of the segments for this file.
*/
fn = (unsigned char*)(start + file_segcount);
memset(filename, 0, sizeof(filename));
memcpy(filename, fn + 1, *fn);
compiland = symt_new_compiland(module, filename);
for (k = 0; k < file_segcount; k++, this_seg++)
{
pnt2.c = linetab + lt_ptr[k];
lt_hdr[this_seg].start = start[k].start;
lt_hdr[this_seg].end = start[k].end;
lt_hdr[this_seg].compiland = compiland;
lt_hdr[this_seg].segno = *pnt2.s++;
lt_hdr[this_seg].nline = *pnt2.s++;
lt_hdr[this_seg].offtab = pnt2.ui;
lt_hdr[this_seg].linetab = (unsigned short*)(pnt2.ui + lt_hdr[this_seg].nline);
}
}
leave:
return lt_hdr;
}
/*========================================================================
* Process CodeView symbol information.
*/
union codeview_symbol
{
struct
{
short int len;
short int id;
} generic;
struct
{
short int len;
short int id;
unsigned int offset;
unsigned short seg;
unsigned short symtype;
unsigned char namelen;
unsigned char name[1];
} data;
struct
{
short int len;
short int id;
unsigned int symtype;
unsigned int offset;
unsigned short seg;
unsigned char namelen;
unsigned char name[1];
} data32;
struct
{
short int len;
short int id;
unsigned int pparent;
unsigned int pend;
unsigned int next;
unsigned int offset;
unsigned short segment;
unsigned short thunk_len;
unsigned char thtype;
unsigned char namelen;
unsigned char name[1];
} thunk;
struct
{
short int len;
short int id;
unsigned int pparent;
unsigned int pend;
unsigned int next;
unsigned int proc_len;
unsigned int debug_start;
unsigned int debug_end;
unsigned int offset;
unsigned short segment;
unsigned short proctype;
unsigned char flags;
unsigned char namelen;
unsigned char name[1];
} proc;
struct
{
short int len;
short int id;
unsigned int pparent;
unsigned int pend;
unsigned int next;
unsigned int proc_len;
unsigned int debug_start;
unsigned int debug_end;
unsigned int proctype;
unsigned int offset;
unsigned short segment;
unsigned char flags;
unsigned char namelen;
unsigned char name[1];
} proc32;
struct
{
short int len; /* Total length of this entry */
short int id; /* Always S_BPREL32 */
unsigned int offset; /* Stack offset relative to BP */
unsigned short symtype;
unsigned char namelen;
unsigned char name[1];
} stack;
struct
{
short int len; /* Total length of this entry */
short int id; /* Always S_BPREL32 */
unsigned int offset; /* Stack offset relative to BP */
unsigned int symtype;
unsigned char namelen;
unsigned char name[1];
} stack32;
};
#define S_COMPILE 0x0001
#define S_REGISTER 0x0002
#define S_CONSTANT 0x0003
#define S_UDT 0x0004
#define S_SSEARCH 0x0005
#define S_END 0x0006
#define S_SKIP 0x0007
#define S_CVRESERVE 0x0008
#define S_OBJNAME 0x0009
#define S_ENDARG 0x000a
#define S_COBOLUDT 0x000b
#define S_MANYREG 0x000c
#define S_RETURN 0x000d
#define S_ENTRYTHIS 0x000e
#define S_BPREL 0x0200
#define S_LDATA 0x0201
#define S_GDATA 0x0202
#define S_PUB 0x0203
#define S_LPROC 0x0204
#define S_GPROC 0x0205
#define S_THUNK 0x0206
#define S_BLOCK 0x0207
#define S_WITH 0x0208
#define S_LABEL 0x0209
#define S_CEXMODEL 0x020a
#define S_VFTPATH 0x020b
#define S_REGREL 0x020c
#define S_LTHREAD 0x020d
#define S_GTHREAD 0x020e
#define S_PROCREF 0x0400
#define S_DATAREF 0x0401
#define S_ALIGN 0x0402
#define S_LPROCREF 0x0403
#define S_REGISTER_32 0x1001 /* Variants with new 32-bit type indices */
#define S_CONSTANT_32 0x1002
#define S_UDT_32 0x1003
#define S_COBOLUDT_32 0x1004
#define S_MANYREG_32 0x1005
#define S_BPREL_32 0x1006
#define S_LDATA_32 0x1007
#define S_GDATA_32 0x1008
#define S_PUB_32 0x1009
#define S_LPROC_32 0x100a
#define S_GPROC_32 0x100b
#define S_VFTTABLE_32 0x100c
#define S_REGREL_32 0x100d
#define S_LTHREAD_32 0x100e
#define S_GTHREAD_32 0x100f
static unsigned int codeview_map_offset(const struct msc_debug_info* msc_dbg,
unsigned int offset)
{
int nomap = msc_dbg->nomap;
const OMAP_DATA* omapp = msc_dbg->omapp;
int i;
if (!nomap || !omapp) return offset;
/* FIXME: use binary search */
for (i = 0; i < nomap - 1; i++)
if (omapp[i].from <= offset && omapp[i+1].from > offset)
return !omapp[i].to ? 0 : omapp[i].to + (offset - omapp[i].from);
return 0;
}
static const struct codeview_linetab*
codeview_get_linetab(const struct codeview_linetab* linetab,
unsigned seg, unsigned offset)
{
/*
* Check whether we have line number information
*/
if (linetab)
{
for (; linetab->linetab; linetab++)
if (linetab->segno == seg &&
linetab->start <= offset && linetab->end > offset)
break;
if (!linetab->linetab) linetab = NULL;
}
return linetab;
}
static unsigned codeview_get_address(const struct msc_debug_info* msc_dbg,
unsigned seg, unsigned offset)
{
int nsect = msc_dbg->nsect;
const IMAGE_SECTION_HEADER* sectp = msc_dbg->sectp;
if (!seg || seg > nsect) return 0;
return msc_dbg->module->module.BaseOfImage +
codeview_map_offset(msc_dbg, sectp[seg-1].VirtualAddress + offset);
}
static void codeview_add_func_linenum(struct module* module,
struct symt_function* func,
const struct codeview_linetab* linetab,
unsigned offset, unsigned size)
{
unsigned int i;
if (!linetab) return;
for (i = 0; i < linetab->nline; i++)
{
if (linetab->offtab[i] >= offset && linetab->offtab[i] < offset + size)
{
symt_add_func_line(module, func, linetab->compiland->source,
linetab->linetab[i], linetab->offtab[i] - offset);
}
}
}
static int codeview_snarf(const struct msc_debug_info* msc_dbg, const BYTE* root,
int offset, int size,
struct codeview_linetab* linetab)
{
struct symt_function* curr_func = NULL;
int i, length;
char symname[PATH_MAX];
const struct codeview_linetab* flt;
/*
* Loop over the different types of records and whenever we
* find something we are interested in, record it and move on.
*/
for (i = offset; i < size; i += length)
{
const union codeview_symbol* sym = (const union codeview_symbol*)(root + i);
length = sym->generic.len + 2;
switch (sym->generic.id)
{
/*
* Global and local data symbols. We don't associate these
* with any given source file.
*/
case S_GDATA:
case S_LDATA:
memcpy(symname, sym->data.name, sym->data.namelen);
symname[sym->data.namelen] = '\0';
flt = codeview_get_linetab(linetab, sym->data.seg, sym->data.offset);
/* global data should be the only one of type global var...
* the other ones sound different
* FIXME
*/
symt_new_global_variable(msc_dbg->module,
flt ? flt->compiland : NULL,
symname, sym->generic.id == S_GDATA,
codeview_get_address(msc_dbg, sym->data.seg, sym->data.offset),
0,
codeview_get_type(sym->data.symtype, FALSE));
break;
case S_GDATA_32:
case S_LDATA_32:
memcpy(symname, sym->data32.name, sym->data32.namelen);
symname[sym->data32.namelen] = '\0';
flt = codeview_get_linetab(linetab, sym->data32.seg, sym->data32.offset);
/* global data should be the only one of type global var...
* the other ones sound different
* FIXME
*/
symt_new_global_variable(msc_dbg->module, flt ? flt->compiland : NULL,
symname, sym->generic.id == S_GDATA_32,
codeview_get_address(msc_dbg, sym->data32.seg, sym->data32.offset),
0,
codeview_get_type(sym->data32.symtype, FALSE));
break;
case S_PUB: /* FIXME is this really a 'data' structure ?? */
memcpy(symname, sym->data.name, sym->data.namelen);
symname[sym->data.namelen] = '\0';
flt = codeview_get_linetab(linetab, sym->data.seg, sym->data.offset);
symt_new_public(msc_dbg->module, flt ? flt->compiland : NULL,
symname,
codeview_get_address(msc_dbg, sym->data.seg, sym->data.offset),
0, TRUE /* FIXME */, TRUE /* FIXME */);
break;
case S_PUB_32: /* FIXME is this really a 'data32' structure ?? */
memcpy(symname, sym->data32.name, sym->data32.namelen);
symname[sym->data32.namelen] = '\0';
flt = codeview_get_linetab(linetab, sym->data32.seg, sym->data32.offset);
symt_new_public(msc_dbg->module, flt ? flt->compiland : NULL,
symname,
codeview_get_address(msc_dbg, sym->data32.seg, sym->data32.offset),
0, TRUE /* FIXME */, TRUE /* FIXME */);
break;
/*
* Sort of like a global function, but it just points
* to a thunk, which is a stupid name for what amounts to
* a PLT slot in the normal jargon that everyone else uses.
*/
case S_THUNK:
memcpy(symname, sym->thunk.name, sym->thunk.namelen);
symname[sym->thunk.namelen] = '\0';
flt = codeview_get_linetab(linetab, sym->thunk.segment, sym->thunk.offset);
symt_new_function(msc_dbg->module, flt ? flt->compiland : NULL,
symname,
codeview_get_address(msc_dbg, sym->thunk.segment, sym->thunk.offset),
sym->thunk.thunk_len,
codeview_get_type(sym->thunk.thtype, FALSE));
break;
/*
* Global and static functions.
*/
case S_GPROC:
case S_LPROC:
if (curr_func) symt_normalize_function(msc_dbg->module, curr_func);
memcpy(symname, sym->proc.name, sym->proc.namelen);
symname[sym->proc.namelen] = '\0';
flt = codeview_get_linetab(linetab, sym->proc.segment, sym->proc.offset);
curr_func = symt_new_function(msc_dbg->module,
flt ? flt->compiland : NULL, symname,
codeview_get_address(msc_dbg, sym->proc.segment, sym->proc.offset),
sym->proc.proc_len,
codeview_get_type(sym->proc.proctype, FALSE));
codeview_add_func_linenum(msc_dbg->module, curr_func, flt,
sym->proc.offset, sym->proc.proc_len);
/* DEBUG_SetSymbolBPOff(curr_func, sym->proc.debug_start); */
break;
case S_GPROC_32:
case S_LPROC_32:
if (curr_func) symt_normalize_function(msc_dbg->module, curr_func);
memcpy(symname, sym->proc32.name, sym->proc32.namelen);
symname[sym->proc32.namelen] = '\0';
flt = codeview_get_linetab(linetab, sym->proc32.segment, sym->proc32.offset);
curr_func = symt_new_function(msc_dbg->module,
flt ? flt->compiland : NULL, symname,
codeview_get_address(msc_dbg, sym->proc32.segment, sym->proc32.offset),
sym->proc32.proc_len,
codeview_get_type(sym->proc32.proctype, FALSE));
codeview_add_func_linenum(msc_dbg->module, curr_func, flt,
sym->proc32.offset, sym->proc32.proc_len);
/* DEBUG_SetSymbolBPOff(curr_func, sym->proc32.debug_start); */
break;
/*
* Function parameters and stack variables.
*/
case S_BPREL:
memcpy(symname, sym->stack.name, sym->stack.namelen);
symname[sym->stack.namelen] = '\0';
symt_add_func_local(msc_dbg->module, curr_func, 0, sym->stack.offset,
NULL, codeview_get_type(sym->stack.symtype, FALSE),
symname);
break;
case S_BPREL_32:
memcpy(symname, sym->stack32.name, sym->stack32.namelen);
symname[sym->stack32.namelen] = '\0';
symt_add_func_local(msc_dbg->module, curr_func, 0, sym->stack32.offset,
NULL, codeview_get_type(sym->stack32.symtype, FALSE),
symname);
break;
/*
* These are special, in that they are always followed by an
* additional length-prefixed string which is *not* included
* into the symbol length count. We need to skip it.
*/
case S_PROCREF:
case S_DATAREF:
case S_LPROCREF:
{
LPBYTE name = (LPBYTE)sym + length;
length += (*name + 1 + 3) & ~3;
}
break;
default:
FIXME("Unsupported id %x\n", sym->generic.id);
}
}
if (curr_func) symt_normalize_function(msc_dbg->module, curr_func);
if (linetab) HeapFree(GetProcessHeap(), 0, linetab);
return TRUE;
}
/*========================================================================
* Process PDB file.
*/
#pragma pack(1)
typedef struct _PDB_FILE
{
DWORD size;
DWORD unknown;
} PDB_FILE,* PPDB_FILE;
typedef struct _PDB_HEADER
{
CHAR ident[40];
DWORD signature;
DWORD blocksize;
WORD freelist;
WORD total_alloc;
PDB_FILE toc;
WORD toc_block[1];
} PDB_HEADER, *PPDB_HEADER;
typedef struct _PDB_TOC
{
DWORD nFiles;
PDB_FILE file[ 1 ];
} PDB_TOC, *PPDB_TOC;
typedef struct _PDB_ROOT
{
DWORD version;
DWORD TimeDateStamp;
DWORD unknown;
DWORD cbNames;
CHAR names[1];
} PDB_ROOT, *PPDB_ROOT;
typedef struct _PDB_TYPES_OLD
{
DWORD version;
WORD first_index;
WORD last_index;
DWORD type_size;
WORD file;
WORD pad;
} PDB_TYPES_OLD, *PPDB_TYPES_OLD;
typedef struct _PDB_TYPES
{
DWORD version;
DWORD type_offset;
DWORD first_index;
DWORD last_index;
DWORD type_size;
WORD file;
WORD pad;
DWORD hash_size;
DWORD hash_base;
DWORD hash_offset;
DWORD hash_len;
DWORD search_offset;
DWORD search_len;
DWORD unknown_offset;
DWORD unknown_len;
} PDB_TYPES, *PPDB_TYPES;
typedef struct _PDB_SYMBOL_RANGE
{
WORD segment;
WORD pad1;
DWORD offset;
DWORD size;
DWORD characteristics;
WORD index;
WORD pad2;
} PDB_SYMBOL_RANGE, *PPDB_SYMBOL_RANGE;
typedef struct _PDB_SYMBOL_RANGE_EX
{
WORD segment;
WORD pad1;
DWORD offset;
DWORD size;
DWORD characteristics;
WORD index;
WORD pad2;
DWORD timestamp;
DWORD unknown;
} PDB_SYMBOL_RANGE_EX, *PPDB_SYMBOL_RANGE_EX;
typedef struct _PDB_SYMBOL_FILE
{
DWORD unknown1;
PDB_SYMBOL_RANGE range;
WORD flag;
WORD file;
DWORD symbol_size;
DWORD lineno_size;
DWORD unknown2;
DWORD nSrcFiles;
DWORD attribute;
CHAR filename[1];
} PDB_SYMBOL_FILE, *PPDB_SYMBOL_FILE;
typedef struct _PDB_SYMBOL_FILE_EX
{
DWORD unknown1;
PDB_SYMBOL_RANGE_EX range;
WORD flag;
WORD file;
DWORD symbol_size;
DWORD lineno_size;
DWORD unknown2;
DWORD nSrcFiles;
DWORD attribute;
DWORD reserved[2];
CHAR filename[1];
} PDB_SYMBOL_FILE_EX, *PPDB_SYMBOL_FILE_EX;
typedef struct _PDB_SYMBOL_SOURCE
{
WORD nModules;
WORD nSrcFiles;
WORD table[1];
} PDB_SYMBOL_SOURCE, *PPDB_SYMBOL_SOURCE;
typedef struct _PDB_SYMBOL_IMPORT
{
DWORD unknown1;
DWORD unknown2;
DWORD TimeDateStamp;
DWORD nRequests;
CHAR filename[1];
} PDB_SYMBOL_IMPORT, *PPDB_SYMBOL_IMPORT;
typedef struct _PDB_SYMBOLS_OLD
{
WORD hash1_file;
WORD hash2_file;
WORD gsym_file;
WORD pad;
DWORD module_size;
DWORD offset_size;
DWORD hash_size;
DWORD srcmodule_size;
} PDB_SYMBOLS_OLD, *PPDB_SYMBOLS_OLD;
typedef struct _PDB_SYMBOLS
{
DWORD signature;
DWORD version;
DWORD unknown;
DWORD hash1_file;
DWORD hash2_file;
DWORD gsym_file;
DWORD module_size;
DWORD offset_size;
DWORD hash_size;
DWORD srcmodule_size;
DWORD pdbimport_size;
DWORD resvd[5];
} PDB_SYMBOLS, *PPDB_SYMBOLS;
#pragma pack()
static void* pdb_read(const BYTE* image, const WORD* block_list, int size)
{
const PDB_HEADER* pdb = (const PDB_HEADER*)image;
int i, nBlocks;
BYTE* buffer;
if (!size) return NULL;
nBlocks = (size + pdb->blocksize - 1) / pdb->blocksize;
buffer = HeapAlloc(GetProcessHeap(), 0, nBlocks * pdb->blocksize);
for (i = 0; i < nBlocks; i++)
memcpy(buffer + i * pdb->blocksize,
image + block_list[i] * pdb->blocksize, pdb->blocksize);
return buffer;
}
static void* pdb_read_file(const BYTE* image, const PDB_TOC* toc, DWORD fileNr)
{
const PDB_HEADER* pdb = (const PDB_HEADER*)image;
const WORD* block_list;
DWORD i;
if (!toc || fileNr >= toc->nFiles) return NULL;
block_list = (const WORD*) &toc->file[toc->nFiles];
for (i = 0; i < fileNr; i++)
block_list += (toc->file[i].size + pdb->blocksize - 1) / pdb->blocksize;
return pdb_read(image, block_list, toc->file[fileNr].size);
}
static void pdb_free(void* buffer)
{
HeapFree(GetProcessHeap(), 0, buffer);
}
static void pdb_convert_types_header(PDB_TYPES* types, const BYTE* image)
{
memset(types, 0, sizeof(PDB_TYPES));
if (!image) return;
if (*(DWORD*)image < 19960000) /* FIXME: correct version? */
{
/* Old version of the types record header */
const PDB_TYPES_OLD* old = (const PDB_TYPES_OLD*)image;
types->version = old->version;
types->type_offset = sizeof(PDB_TYPES_OLD);
types->type_size = old->type_size;
types->first_index = old->first_index;
types->last_index = old->last_index;
types->file = old->file;
}
else
{
/* New version of the types record header */
*types = *(const PDB_TYPES*)image;
}
}
static void pdb_convert_symbols_header(PDB_SYMBOLS* symbols,
int* header_size, const BYTE* image)
{
memset(symbols, 0, sizeof(PDB_SYMBOLS));
if (!image) return;
if (*(DWORD*)image != 0xffffffff)
{
/* Old version of the symbols record header */
const PDB_SYMBOLS_OLD* old = (const PDB_SYMBOLS_OLD*)image;
symbols->version = 0;
symbols->module_size = old->module_size;
symbols->offset_size = old->offset_size;
symbols->hash_size = old->hash_size;
symbols->srcmodule_size = old->srcmodule_size;
symbols->pdbimport_size = 0;
symbols->hash1_file = old->hash1_file;
symbols->hash2_file = old->hash2_file;
symbols->gsym_file = old->gsym_file;
*header_size = sizeof(PDB_SYMBOLS_OLD);
}
else
{
/* New version of the symbols record header */
*symbols = *(const PDB_SYMBOLS*)image;
*header_size = sizeof(PDB_SYMBOLS);
}
}
static const char* get_last_sep(const char* str)
{
char* a;
if ((a = strrchr(str, '/'))) str = a;
return (a = strrchr(str, '\\')) ? a : str;
}
static HANDLE open_pdb_file(const struct process* pcs, struct module* module,
char* filename)
{
HANDLE h;
char dbg_file_path[MAX_PATH];
h = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE)
{
h = FindDebugInfoFile(filename, pcs->search_path, dbg_file_path);
if (h == NULL)
{
const char* p;
const char* q;
strcpy(dbg_file_path, module->module.LoadedImageName);
if ((p = get_last_sep(dbg_file_path)))
{
if ((q = get_last_sep(filename))) q++; else q = filename;
strcpy((char*)p + 1, q);
h = CreateFileA(dbg_file_path, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
}
}
}
return (h == INVALID_HANDLE_VALUE) ? NULL : h;
}
static SYM_TYPE pdb_process_file(const struct process* pcs,
const struct msc_debug_info* msc_dbg,
char* filename, DWORD timestamp)
{
SYM_TYPE sym_type = -1;
HANDLE hFile, hMap = NULL;
char* image = NULL;
PDB_HEADER* pdb = NULL;
PDB_TOC* toc = NULL;
PDB_ROOT* root = NULL;
char* types_image = NULL;
char* symbols_image = NULL;
PDB_TYPES types;
PDB_SYMBOLS symbols;
int header_size = 0;
char* modimage;
char* file;
TRACE("Processing PDB file %s\n", filename);
/*
* Open and map() .PDB file
*/
if ((hFile = open_pdb_file(pcs, msc_dbg->module, filename)) == NULL ||
((hMap = CreateFileMappingA(hFile, NULL, PAGE_READONLY, 0, 0, NULL)) == NULL) ||
((image = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0)) == NULL))
{
ERR("-Unable to peruse .PDB file %s\n", filename);
goto leave;
}
/*
* Read in TOC and well-known files
*/
pdb = (PPDB_HEADER)image;
toc = pdb_read(image, pdb->toc_block, pdb->toc.size);
root = pdb_read_file(image, toc, 1);
types_image = pdb_read_file(image, toc, 2);
symbols_image = pdb_read_file(image, toc, 3);
pdb_convert_types_header(&types, types_image);
pdb_convert_symbols_header(&symbols, &header_size, symbols_image);
if (!root)
{
ERR("-Unable to get root from .PDB file %s\n", filename);
goto leave;
}
/*
* Check for unknown versions
*/
switch (root->version)
{
case 19950623: /* VC 4.0 */
case 19950814:
case 19960307: /* VC 5.0 */
case 19970604: /* VC 6.0 */
break;
default:
ERR("-Unknown root block version %ld\n", root->version);
}
switch (types.version)
{
case 19950410: /* VC 4.0 */
case 19951122:
case 19961031: /* VC 5.0 / 6.0 */
break;
default:
ERR("-Unknown type info version %ld\n", types.version);
}
switch (symbols.version)
{
case 0: /* VC 4.0 */
case 19960307: /* VC 5.0 */
case 19970606: /* VC 6.0 */
break;
default:
ERR("-Unknown symbol info version %ld\n", symbols.version);
}
/* Check .PDB time stamp */
if (root->TimeDateStamp != timestamp)
{
ERR("-Wrong time stamp of .PDB file %s (0x%08lx, 0x%08lx)\n",
filename, root->TimeDateStamp, timestamp);
}
/* Read type table */
codeview_parse_type_table(msc_dbg->module, types_image + types.type_offset, types.type_size);
/* Read type-server .PDB imports */
if (symbols.pdbimport_size)
{
/* FIXME */
ERR("-Type server .PDB imports ignored!\n");
}
/* Read global symbol table */
modimage = pdb_read_file(image, toc, symbols.gsym_file);
if (modimage)
{
codeview_snarf(msc_dbg, modimage, 0, toc->file[symbols.gsym_file].size, NULL);
pdb_free(modimage);
}
/* Read per-module symbol / linenumber tables */
file = symbols_image + header_size;
while (file - symbols_image < header_size + symbols.module_size)
{
int file_nr, file_index, symbol_size, lineno_size;
char* file_name;
if (symbols.version < 19970000)
{
PDB_SYMBOL_FILE *sym_file = (PDB_SYMBOL_FILE*) file;
file_nr = sym_file->file;
file_name = sym_file->filename;
file_index = sym_file->range.index;
symbol_size = sym_file->symbol_size;
lineno_size = sym_file->lineno_size;
}
else
{
PDB_SYMBOL_FILE_EX *sym_file = (PDB_SYMBOL_FILE_EX*) file;
file_nr = sym_file->file;
file_name = sym_file->filename;
file_index = sym_file->range.index;
symbol_size = sym_file->symbol_size;
lineno_size = sym_file->lineno_size;
}
modimage = pdb_read_file(image, toc, file_nr);
if (modimage)
{
struct codeview_linetab* linetab = NULL;
if (lineno_size)
linetab = codeview_snarf_linetab(msc_dbg->module, modimage + symbol_size, lineno_size);
if (symbol_size)
codeview_snarf(msc_dbg, modimage, sizeof(DWORD), symbol_size, linetab);
pdb_free(modimage);
}
file_name += strlen(file_name) + 1;
file = (char*)((DWORD)(file_name + strlen(file_name) + 1 + 3) & ~3);
}
sym_type = SymCv;
leave:
/* Cleanup */
codeview_clear_type_table();
if (symbols_image) pdb_free(symbols_image);
if (types_image) pdb_free(types_image);
if (root) pdb_free(root);
if (toc) pdb_free(toc);
if (image) UnmapViewOfFile(image);
if (hMap) CloseHandle(hMap);
if (hFile) CloseHandle(hFile);
return sym_type;
}
/*========================================================================
* Process CodeView debug information.
*/
#define CODEVIEW_NB09_SIG ('N' | ('B' << 8) | ('0' << 16) | ('9' << 24))
#define CODEVIEW_NB10_SIG ('N' | ('B' << 8) | ('1' << 16) | ('0' << 24))
#define CODEVIEW_NB11_SIG ('N' | ('B' << 8) | ('1' << 16) | ('1' << 24))
typedef struct _CODEVIEW_HEADER
{
DWORD dwSignature;
DWORD lfoDirectory;
} CODEVIEW_HEADER,* PCODEVIEW_HEADER;
typedef struct _CODEVIEW_PDB_DATA
{
DWORD timestamp;
DWORD unknown;
CHAR name[1];
} CODEVIEW_PDB_DATA, *PCODEVIEW_PDB_DATA;
typedef struct _CV_DIRECTORY_HEADER
{
WORD cbDirHeader;
WORD cbDirEntry;
DWORD cDir;
DWORD lfoNextDir;
DWORD flags;
} CV_DIRECTORY_HEADER, *PCV_DIRECTORY_HEADER;
typedef struct _CV_DIRECTORY_ENTRY
{
WORD subsection;
WORD iMod;
DWORD lfo;
DWORD cb;
} CV_DIRECTORY_ENTRY, *PCV_DIRECTORY_ENTRY;
#define sstAlignSym 0x125
#define sstSrcModule 0x127
static SYM_TYPE codeview_process_info(const struct process* pcs,
const struct msc_debug_info* msc_dbg)
{
const CODEVIEW_HEADER* cv = (const CODEVIEW_HEADER*)msc_dbg->root;
SYM_TYPE sym_type = -1;
switch (cv->dwSignature)
{
case CODEVIEW_NB09_SIG:
case CODEVIEW_NB11_SIG:
{
const CV_DIRECTORY_HEADER* hdr = (const CV_DIRECTORY_HEADER*)(msc_dbg->root + cv->lfoDirectory);
const CV_DIRECTORY_ENTRY* ent;
const CV_DIRECTORY_ENTRY* prev;
const CV_DIRECTORY_ENTRY* next;
unsigned int i;
codeview_init_basic_types(msc_dbg->module);
ent = (const CV_DIRECTORY_ENTRY*)((const BYTE*)hdr + hdr->cbDirHeader);
for (i = 0; i < hdr->cDir; i++, ent = next)
{
next = (i == hdr->cDir-1)? NULL :
(const CV_DIRECTORY_ENTRY*)((const BYTE*)ent + hdr->cbDirEntry);
prev = (i == 0)? NULL :
(const CV_DIRECTORY_ENTRY*)((const BYTE*)ent - hdr->cbDirEntry);
if (ent->subsection == sstAlignSym)
{
/*
* Check the next and previous entry. If either is a
* sstSrcModule, it contains the line number info for
* this file.
*
* FIXME: This is not a general solution!
*/
struct codeview_linetab* linetab = NULL;
if (next && next->iMod == ent->iMod && next->subsection == sstSrcModule)
linetab = codeview_snarf_linetab(msc_dbg->module, msc_dbg->root + next->lfo, next->cb);
if (prev && prev->iMod == ent->iMod && prev->subsection == sstSrcModule)
linetab = codeview_snarf_linetab(msc_dbg->module, msc_dbg->root + prev->lfo, prev->cb);
codeview_snarf(msc_dbg, msc_dbg->root + ent->lfo, sizeof(DWORD),
ent->cb, linetab);
}
}
sym_type = SymCv;
break;
}
case CODEVIEW_NB10_SIG:
{
PCODEVIEW_PDB_DATA pdb = (PCODEVIEW_PDB_DATA)(cv + 1);
codeview_init_basic_types(msc_dbg->module);
sym_type = pdb_process_file(pcs, msc_dbg, pdb->name, pdb->timestamp);
break;
}
default:
ERR("Unknown CODEVIEW signature %08lX in module %s\n",
cv->dwSignature, msc_dbg->module->module.ModuleName);
break;
}
return sym_type;
}
/*========================================================================
* Process debug directory.
*/
SYM_TYPE pe_load_debug_directory(const struct process* pcs, struct module* module,
const BYTE* mapping, PIMAGE_DEBUG_DIRECTORY dbg,
int nDbg)
{
SYM_TYPE sym_type;
int i;
struct msc_debug_info msc_dbg;
IMAGE_NT_HEADERS* nth = RtlImageNtHeader((void*)mapping);
msc_dbg.module = module;
msc_dbg.nsect = nth->FileHeader.NumberOfSections;
msc_dbg.sectp = (PIMAGE_SECTION_HEADER)((char*)&nth->OptionalHeader + nth->FileHeader.SizeOfOptionalHeader);
msc_dbg.nomap = 0;
msc_dbg.omapp = NULL;
__TRY
{
sym_type = -1;
/* First, watch out for OMAP data */
for (i = 0; i < nDbg; i++)
{
if (dbg[i].Type == IMAGE_DEBUG_TYPE_OMAP_FROM_SRC)
{
msc_dbg.nomap = dbg[i].SizeOfData / sizeof(OMAP_DATA);
msc_dbg.omapp = (OMAP_DATA*)(mapping + dbg[i].PointerToRawData);
break;
}
}
/* Now, try to parse CodeView debug info */
for (i = 0; i < nDbg; i++)
{
if (dbg[i].Type == IMAGE_DEBUG_TYPE_CODEVIEW)
{
msc_dbg.root = mapping + dbg[i].PointerToRawData;
sym_type = codeview_process_info(pcs, &msc_dbg);
if (sym_type == SymCv) goto done;
}
}
/* If not found, try to parse COFF debug info */
for (i = 0; i < nDbg; i++)
{
if (dbg[i].Type == IMAGE_DEBUG_TYPE_COFF)
{
msc_dbg.root = mapping + dbg[i].PointerToRawData;
sym_type = coff_process_info(&msc_dbg);
if (sym_type == SymCoff) goto done;
}
}
done:
#if 0
/* FIXME: this should be supported... this is the debug information for
* functions compiled without a frame pointer (FPO = frame pointer omission)
* the associated data helps finding out the relevant information
*/
for (i = 0; i < nDbg; i++)
if (dbg[i].Type == IMAGE_DEBUG_TYPE_FPO)
DEBUG_Printf("This guy has FPO information\n");
#define FRAME_FPO 0
#define FRAME_TRAP 1
#define FRAME_TSS 2
typedef struct _FPO_DATA
{
DWORD ulOffStart; /* offset 1st byte of function code */
DWORD cbProcSize; /* # bytes in function */
DWORD cdwLocals; /* # bytes in locals/4 */
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;
#else
;
#endif
}
__EXCEPT(page_fault)
{
ERR("Got a page fault while loading symbols\n");
sym_type = -1;
}
__ENDTRY
return sym_type;
}
/*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "dbghelp_private.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
/******************************************************************
* FindDebugInfoFile (DBGHELP.@)
*
*/
HANDLE WINAPI FindDebugInfoFile(PSTR FileName, PSTR 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)
{
const char* p = strrchr(FileName, '/');
if (!p) p = FileName;
if (!SearchPathA(SymbolPath, p, 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(PSTR FileName, PSTR 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;
}
/******************************************************************
* FindExecutableImage (DBGHELP.@)
*
*/
HANDLE WINAPI FindExecutableImage(PSTR FileName, PSTR SymbolPath, PSTR ImageFilePath)
{
HANDLE h;
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;
}
/***********************************************************************
* MakeSureDirectoryPathExists (DBGHELP.@)
*/
BOOL WINAPI MakeSureDirectoryPathExists(LPCSTR DirPath)
{
if (CreateDirectoryA(DirPath, NULL)) return TRUE;
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
SetLastError(ERROR_SUCCESS);
return TRUE;
}
return FALSE;
}
/***********************************************************************
* SearchTreeForFile (DBGHELP.@)
*/
BOOL WINAPI SearchTreeForFile(LPSTR RootPath, LPSTR InputPathName,
LPSTR OutputPathBuffer)
{
FIXME("(%s, %s, %s): stub\n",
debugstr_a(RootPath), debugstr_a(InputPathName),
debugstr_a(OutputPathBuffer));
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return FALSE;
}
/*
* File pe_module.c - handle PE module information
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "dbghelp_private.h"
#include "winreg.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 SYM_TYPE 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;
SYM_TYPE sym_type = SymNone;
section = (IMAGE_SECTION_HEADER*)
((char*)&nth->OptionalHeader + nth->FileHeader.SizeOfOptionalHeader);
for (i = 0; i < nth->FileHeader.NumberOfSections; i++, section++)
{
if (!strcasecmp(section->Name, ".stab"))
{
stabs = section->VirtualAddress;
stabsize = section->SizeOfRawData;
}
else if (!strncasecmp(section->Name, ".stabstr", 8))
{
stabstr = section->VirtualAddress;
stabstrsize = section->SizeOfRawData;
}
}
if (stabstrsize && stabsize)
{
sym_type = stabs_parse(module, mapping, module->module.BaseOfImage,
stabs, stabsize, stabstr, stabstrsize);
}
return sym_type;
}
/******************************************************************
* pe_load_dbg_file
*
* loads a .dbg file
*/
static SYM_TYPE pe_load_dbg_file(const struct process* pcs, struct module* module,
char* dbg_name, DWORD timestamp)
{
char tmp[MAX_PATH];
HANDLE hFile, hMap = 0;
const BYTE* dbg_mapping = NULL;
PIMAGE_SEPARATE_DEBUG_HEADER hdr;
PIMAGE_DEBUG_DIRECTORY dbg;
SYM_TYPE sym_type = -1;
WINE_TRACE("Processing DBG file %s\n", dbg_name);
if ((hFile = FindDebugInfoFile(dbg_name, pcs->search_path, tmp)) != NULL &&
((hMap = CreateFileMappingA(hFile, NULL, PAGE_READONLY, 0, 0, NULL)) != 0) &&
((dbg_mapping = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0)) != NULL))
{
hdr = (PIMAGE_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.
*/
}
dbg = (PIMAGE_DEBUG_DIRECTORY)
(dbg_mapping + sizeof(*hdr) +
hdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER) +
hdr->ExportedNamesSize);
sym_type = pe_load_debug_directory(pcs, module, dbg_mapping, dbg,
hdr->DebugDirectorySize / sizeof(*dbg));
}
else
{
WINE_ERR("-Unable to peruse .DBG file %s (%s)\n",
dbg_name, debugstr_a(tmp));
}
if (dbg_mapping) UnmapViewOfFile((void*)dbg_mapping);
if (hMap) CloseHandle(hMap);
if (hFile != NULL) CloseHandle(hFile);
return sym_type;
}
/******************************************************************
* pe_load_msc_debug_info
*
* Process MSC debug information in PE file.
*/
static SYM_TYPE pe_load_msc_debug_info(const struct process* pcs,
struct module* module,
const void* mapping, IMAGE_NT_HEADERS* nth)
{
SYM_TYPE sym_type = -1;
PIMAGE_DATA_DIRECTORY dir;
PIMAGE_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 sym_type;
dbg = (PIMAGE_DEBUG_DIRECTORY)((char*)mapping + dir->VirtualAddress);
/* Parse debug directory */
if (nth->FileHeader.Characteristics & IMAGE_FILE_DEBUG_STRIPPED)
{
/* Debug info is stripped to .DBG file */
PIMAGE_DEBUG_MISC misc = (PIMAGE_DEBUG_MISC)
((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",
module->module.ModuleName);
}
else
{
sym_type = pe_load_dbg_file(pcs, module, misc->Data, nth->FileHeader.TimeDateStamp);
}
}
else
{
/* Debug info is embedded into PE module */
sym_type = pe_load_debug_directory(pcs, module, mapping, dbg, nDbg);
}
return sym_type;
}
/***********************************************************************
* pe_load_export_debug_info
*/
static SYM_TYPE pe_load_export_debug_info(const struct process* pcs,
struct module* module,
const void* mapping, IMAGE_NT_HEADERS* nth)
{
char buffer[512];
unsigned int i;
IMAGE_DATA_DIRECTORY* dir;
DWORD base = module->module.BaseOfImage;
ADDRESS addr;
addr.Mode = AddrModeFlat;
addr.Segment = 0;
#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, 0,
TRUE /* FIXME */, TRUE /* FIXME */);
#endif
/* Add entry point */
snprintf(buffer, sizeof(buffer), "%s.EntryPoint", module->module.ModuleName);
symt_new_public(module, NULL, buffer,
base + nth->OptionalHeader.AddressOfEntryPoint, 0,
TRUE /* FIXME */, TRUE /* FIXME */);
#if 0
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++)
{
snprintf(buffer, sizeof(buffer), "%s.%s",
module->module.ModuleName, section->Name);
symt_new_public(module, NULL, buffer, base + section->VirtualAddress, 0,
TRUE /* FIXME */, TRUE /* FIXME */);
}
#endif
/* Add exported functions */
if ((dir = RtlImageDirectoryEntryToData((void*)mapping, TRUE,
IMAGE_DIRECTORY_ENTRY_EXPORT, NULL)))
{
IMAGE_EXPORT_DIRECTORY* exports;
WORD* ordinals = NULL;
void** functions = NULL;
DWORD* names = NULL;
unsigned int j;
exports = (void*)((char*)mapping + dir->VirtualAddress);
functions = (void*)((char*)mapping + exports->AddressOfFunctions);
ordinals = (void*)((char*)mapping + exports->AddressOfNameOrdinals);
names = (void*)((char*)mapping + exports->AddressOfNames);
for (i = 0; i < exports->NumberOfNames; i++)
{
if (!names[i]) continue;
snprintf(buffer, sizeof(buffer), "%s.%s",
module->module.ModuleName, (char*)base + names[i]);
symt_new_public(module, NULL, buffer,
base + (DWORD)functions[ordinals[i]], 0,
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), "%s.%ld",
module->module.ModuleName, i + exports->Base);
symt_new_public(module, NULL, buffer, base + (DWORD)functions[i], 0,
TRUE /* FIXME */, TRUE /* FIXME */);
}
}
/* no real debug info, only entry points */
return module->module.SymType = SymExport;
}
/******************************************************************
* pe_load_debug_info
*
*/
SYM_TYPE pe_load_debug_info(const struct process* pcs, struct module* module)
{
SYM_TYPE sym_type = -1;
HANDLE hFile;
HANDLE hMap;
void* mapping;
IMAGE_NT_HEADERS* nth;
hFile = CreateFileA(module->module.LoadedImageName, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) return -1;
if ((hMap = CreateFileMappingA(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))
{
sym_type = pe_load_stabs(pcs, module, mapping, nth);
if (sym_type <= SymNone)
sym_type = 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
*/
}
if (sym_type <= SymNone && !module_get_debug(pcs, module))
sym_type = pe_load_export_debug_info(pcs, module, mapping, nth);
UnmapViewOfFile(mapping);
}
CloseHandle(hMap);
}
CloseHandle(hFile);
module->module.SymType = (sym_type >= SymNone) ? sym_type : SymNone;
return sym_type;
}
/******************************************************************
* pe_load_module
*
*/
struct module* pe_load_module(struct process* pcs, char* name,
HANDLE hFile, DWORD base, DWORD size)
{
struct module* module = NULL;
BOOL opened = FALSE;
HANDLE hMap;
void* mapping;
char loaded_name[MAX_PATH];
loaded_name[0] = '\0';
if (!hFile)
{
if (!name)
{
/* FIXME SetLastError */
return NULL;
}
if ((hFile = FindExecutableImage(name, NULL, loaded_name)) == NULL)
return NULL;
opened = TRUE;
}
else if (name) strcpy(loaded_name, name);
else if (dbghelp_options & SYMOPT_DEFERRED_LOADS)
FIXME("Trouble ahead (no module name passed in deferred mode)\n");
if ((hMap = CreateFileMappingA(hFile, NULL, PAGE_READONLY, 0, 0, NULL)) != 0)
{
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, base, size,
nth->FileHeader.TimeDateStamp,
nth->OptionalHeader.CheckSum);
if (module)
{
module->module.SymType = (dbghelp_options & SYMOPT_DEFERRED_LOADS) ?
SymDeferred : pe_load_debug_info(pcs, module);
}
}
UnmapViewOfFile(mapping);
}
CloseHandle(hMap);
}
if (opened) CloseHandle(hFile);
return module;
}
/******************************************************************
* pe_load_module_from_pcs
*
*/
struct module* pe_load_module_from_pcs(struct process* pcs, const char* name,
const char* mod_name, DWORD base, DWORD size)
{
struct module* module;
const char* ptr;
if ((module = module_find_by_name(pcs, name, DMT_PE))) return module;
if (mod_name) ptr = mod_name;
else
{
for (ptr = name + strlen(name) - 1; ptr >= name; ptr--)
{
if (*ptr == '/' || *ptr == '\\')
{
ptr++;
break;
}
}
}
if (ptr && (module = module_find_by_name(pcs, ptr, DMT_PE))) return module;
if (base && pcs->dbg_hdr_addr)
{
IMAGE_DOS_HEADER dos;
IMAGE_NT_HEADERS nth;
if (read_mem(pcs->handle, base, &dos, sizeof(dos)) &&
dos.e_magic == IMAGE_DOS_SIGNATURE &&
read_mem(pcs->handle, base + dos.e_lfanew, &nth, sizeof(nth)) &&
nth.Signature == IMAGE_NT_SIGNATURE)
{
if (!size) size = nth.OptionalHeader.SizeOfImage;
module = module_new(pcs, name, DMT_PE, base, size,
nth.FileHeader.TimeDateStamp, nth.OptionalHeader.CheckSum);
}
}
return module;
}
/*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "dbghelp_private.h"
#include "wine/debug.h"
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* name)
{
int len;
unsigned ret;
if (!name) return (unsigned)-1;
if (module->sources && (ret = source_find(module, name)) != (unsigned)-1)
return ret;
len = strlen(name) + 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;
strcpy(module->sources + module->sources_used, name);
module->sources_used += len;
module->sources[module->sources_used] = '\0';
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, ULONG ModBase, LPSTR Mask,
PSYM_ENUMSOURCFILES_CALLBACK cbSrcFiles,
void* UserContext)
{
struct process* pcs;
struct module* module;
SOURCEFILE sf;
char* ptr;
if (!cbSrcFiles) return FALSE;
pcs = process_find_by_handle(hProcess);
if (!pcs) return FALSE;
if (ModBase)
{
module = module_find_by_addr(pcs, ModBase, DMT_UNKNOWN);
if (!(module = module_get_debug(pcs, module))) return FALSE;
}
else
{
if (Mask[0] == '!')
{
module = module_find_by_name(pcs, Mask + 1, DMT_UNKNOWN);
if (!(module = module_get_debug(pcs, module))) return FALSE;
}
else
{
FIXME("Unsupported yet (should get info from current context)\n");
return FALSE;
}
}
if (!module->sources) return FALSE;
for (ptr = module->sources; *ptr; ptr += strlen(ptr) + 1)
{
/* FIXME: not using Mask */
sf.ModBase = ModBase;
sf.FileName = ptr;
if (!cbSrcFiles(&sf, UserContext)) break;
}
return TRUE;
}
/*
* File stabs.c - read stabs information from the modules
*
* Copyright (C) 1996, Eric Youngdale.
* 1999-2004, Eric Pouech
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*
* Maintenance Information
* -----------------------
*
* For documentation on the stabs format see for example
* The "stabs" debug format
* by Julia Menapace, Jim Kingdon, David Mackenzie
* of Cygnus Support
* available (hopefully) from http:\\sources.redhat.com\gdb\onlinedocs
*/
#include "config.h"
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <stdio.h>
#ifndef PATH_MAX
#define PATH_MAX MAX_PATH
#endif
#include <assert.h>
#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "winreg.h"
#include "winnls.h"
#include "dbghelp_private.h"
#if defined(__svr4__) || defined(__sun)
#define __ELF__
#endif
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(dbghelp_stabs);
#ifndef N_UNDF
#define N_UNDF 0x00
#endif
#define N_GSYM 0x20
#define N_FUN 0x24
#define N_STSYM 0x26
#define N_LCSYM 0x28
#define N_MAIN 0x2a
#define N_ROSYM 0x2c
#define N_OPT 0x3c
#define N_RSYM 0x40
#define N_SLINE 0x44
#define N_SO 0x64
#define N_LSYM 0x80
#define N_BINCL 0x82
#define N_SOL 0x84
#define N_PSYM 0xa0
#define N_EINCL 0xa2
#define N_LBRAC 0xc0
#define N_EXCL 0xc2
#define N_RBRAC 0xe0
struct stab_nlist
{
union
{
char* n_name;
struct stab_nlist* n_next;
long n_strx;
} n_un;
unsigned char n_type;
char n_other;
short n_desc;
unsigned long n_value;
};
static void stab_strcpy(char* dest, int sz, const char* source)
{
/*
* A strcpy routine that stops when we hit the ':' character.
* Faster than copying the whole thing, and then nuking the
* ':'.
*/
while (*source != '\0' && *source != ':' && sz-- > 0)
*dest++ = *source++;
*dest = '\0';
assert(sz > 0);
}
typedef struct
{
char* name;
unsigned long value;
struct symt** vector;
int nrofentries;
} include_def;
#define MAX_INCLUDES 5120
static include_def* include_defs = NULL;
static int num_include_def = 0;
static int num_alloc_include_def = 0;
static int cu_include_stack[MAX_INCLUDES];
static int cu_include_stk_idx = 0;
static struct symt** cu_vector = NULL;
static int cu_nrofentries = 0;
static struct symt_basic* stabs_basic[36];
static int stabs_new_include(const char* file, unsigned long val)
{
if (num_include_def == num_alloc_include_def)
{
num_alloc_include_def += 256;
if (!include_defs)
include_defs = HeapAlloc(GetProcessHeap(), 0,
sizeof(include_defs[0]) * num_alloc_include_def);
else
include_defs = HeapReAlloc(GetProcessHeap(), 0, include_defs,
sizeof(include_defs[0]) * num_alloc_include_def);
memset(include_defs + num_include_def, 0, sizeof(include_defs[0]) * 256);
}
include_defs[num_include_def].name = strcpy(HeapAlloc(GetProcessHeap(), 0, strlen(file) + 1), file);
include_defs[num_include_def].value = val;
include_defs[num_include_def].vector = NULL;
include_defs[num_include_def].nrofentries = 0;
return num_include_def++;
}
static int stabs_find_include(const char* file, unsigned long val)
{
int i;
for (i = 0; i < num_include_def; i++)
{
if (val == include_defs[i].value &&
strcmp(file, include_defs[i].name) == 0)
return i;
}
return -1;
}
static int stabs_add_include(int idx)
{
assert(idx >= 0);
cu_include_stk_idx++;
/* if this happens, just bump MAX_INCLUDES */
/* we could also handle this as another dynarray */
assert(cu_include_stk_idx < MAX_INCLUDES);
cu_include_stack[cu_include_stk_idx] = idx;
return cu_include_stk_idx;
}
static void stabs_reset_includes(void)
{
/*
* The struct symt:s that we would need to use are reset when
* we start a new file. (at least the ones in filenr == 0)
*/
cu_include_stk_idx = 0;/* keep 0 as index for the .c file itself */
memset(cu_vector, 0, sizeof(cu_vector[0]) * cu_nrofentries);
}
static void stabs_free_includes(void)
{
int i;
stabs_reset_includes();
for (i = 0; i < num_include_def; i++)
{
HeapFree(GetProcessHeap(), 0, include_defs[i].name);
HeapFree(GetProcessHeap(), 0, include_defs[i].vector);
}
HeapFree(GetProcessHeap(), 0, include_defs);
include_defs = NULL;
num_include_def = 0;
num_alloc_include_def = 0;
HeapFree(GetProcessHeap(), 0, cu_vector);
cu_vector = NULL;
cu_nrofentries = 0;
}
static struct symt** stabs_find_ref(long filenr, long subnr)
{
struct symt** ret;
/* FIXME: I could perhaps create a dummy include_def for each compilation
* unit which would allow not to handle those two cases separately
*/
if (filenr == 0)
{
if (cu_nrofentries <= subnr)
{
if (!cu_vector)
cu_vector = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
sizeof(cu_vector[0]) * (subnr+1));
else
cu_vector = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
cu_vector, sizeof(cu_vector[0]) * (subnr+1));
cu_nrofentries = subnr + 1;
}
ret = &cu_vector[subnr];
}
else
{
include_def* idef;
assert(filenr <= cu_include_stk_idx);
idef = &include_defs[cu_include_stack[filenr]];
if (idef->nrofentries <= subnr)
{
if (!idef->vector)
idef->vector = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
sizeof(idef->vector[0]) * (subnr+1));
else
idef->vector = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
idef->vector, sizeof(idef->vector[0]) * (subnr+1));
idef->nrofentries = subnr + 1;
}
ret = &idef->vector[subnr];
}
TRACE("(%ld,%ld) => %p (%p)\n", filenr, subnr, ret, *ret);
return ret;
}
static struct symt** stabs_read_type_enum(const char** x)
{
long filenr, subnr;
if (**x == '(')
{
(*x)++; /* '(' */
filenr = strtol(*x, (char**)x, 10); /* <int> */
(*x)++; /* ',' */
subnr = strtol(*x, (char**)x, 10); /* <int> */
(*x)++; /* ')' */
}
else
{
filenr = 0;
subnr = strtol(*x, (char**)x, 10); /* <int> */
}
return stabs_find_ref(filenr, subnr);
}
#define PTS_DEBUG
struct ParseTypedefData
{
const char* ptr;
char buf[1024];
int idx;
struct module* module;
#ifdef PTS_DEBUG
struct PTS_Error
{
const char* ptr;
unsigned line;
} errors[16];
int err_idx;
#endif
};
#ifdef PTS_DEBUG
static void stabs_pts_push(struct ParseTypedefData* ptd, unsigned line)
{
assert(ptd->err_idx < sizeof(ptd->errors) / sizeof(ptd->errors[0]));
ptd->errors[ptd->err_idx].line = line;
ptd->errors[ptd->err_idx].ptr = ptd->ptr;
ptd->err_idx++;
}
#define PTS_ABORTIF(ptd, t) do { if (t) { stabs_pts_push((ptd), __LINE__); return -1;} } while (0)
#else
#define PTS_ABORTIF(ptd, t) do { if (t) return -1; } while (0)
#endif
static int stabs_get_basic(struct ParseTypedefData* ptd, unsigned basic, struct symt** symt)
{
PTS_ABORTIF(ptd, basic >= sizeof(stabs_basic) / sizeof(stabs_basic[0]));
if (!stabs_basic[basic])
{
switch (basic)
{
case 1: stabs_basic[basic] = symt_new_basic(ptd->module, btInt, "int", 4); break;
case 2: stabs_basic[basic] = symt_new_basic(ptd->module, btChar, "char", 1); break;
case 3: stabs_basic[basic] = symt_new_basic(ptd->module, btInt, "short int", 2); break;
case 4: stabs_basic[basic] = symt_new_basic(ptd->module, btInt, "long int", 4); break;
case 5: stabs_basic[basic] = symt_new_basic(ptd->module, btUInt, "unsigned char", 1); break;
case 6: stabs_basic[basic] = symt_new_basic(ptd->module, btInt, "signed char", 1); break;
case 7: stabs_basic[basic] = symt_new_basic(ptd->module, btUInt, "unsigned short int", 2); break;
case 8: stabs_basic[basic] = symt_new_basic(ptd->module, btUInt, "unsigned int", 4); break;
case 9: stabs_basic[basic] = symt_new_basic(ptd->module, btUInt, "unsigned", 2); break;
case 10: stabs_basic[basic] = symt_new_basic(ptd->module, btUInt, "unsigned long int", 2); break;
case 11: stabs_basic[basic] = symt_new_basic(ptd->module, btVoid, "void", 0); break;
case 12: stabs_basic[basic] = symt_new_basic(ptd->module, btFloat, "float", 4); break;
case 13: stabs_basic[basic] = symt_new_basic(ptd->module, btFloat, "double", 8); break;
case 14: stabs_basic[basic] = symt_new_basic(ptd->module, btFloat, "long double", 12); break;
case 15: stabs_basic[basic] = symt_new_basic(ptd->module, btInt, "integer", 4); break;
case 16: stabs_basic[basic] = symt_new_basic(ptd->module, btBool, "bool", 1); break;
/* case 17: short real */
/* case 18: real */
case 25: stabs_basic[basic] = symt_new_basic(ptd->module, btComplex, "float complex", 8); break;
case 26: stabs_basic[basic] = symt_new_basic(ptd->module, btComplex, "double complex", 16); break;
case 30: stabs_basic[basic] = symt_new_basic(ptd->module, btWChar, "wchar_t", 2); break;
case 31: stabs_basic[basic] = symt_new_basic(ptd->module, btInt, "long long int", 8); break;
case 32: stabs_basic[basic] = symt_new_basic(ptd->module, btUInt, "long long unsigned", 8); break;
/* starting at 35 are wine extensions (especially for R implementation) */
case 35: stabs_basic[basic] = symt_new_basic(ptd->module, btComplex, "long double complex", 24); break;
default: PTS_ABORTIF(ptd, 1);
}
}
*symt = &stabs_basic[basic]->symt;
return 0;
}
static int stabs_pts_read_type_def(struct ParseTypedefData* ptd,
const char* typename, struct symt** dt);
static int stabs_pts_read_id(struct ParseTypedefData* ptd)
{
const char* first = ptd->ptr;
unsigned int len;
PTS_ABORTIF(ptd, (ptd->ptr = strchr(ptd->ptr, ':')) == NULL);
len = ptd->ptr - first;
PTS_ABORTIF(ptd, len >= sizeof(ptd->buf) - ptd->idx);
memcpy(ptd->buf + ptd->idx, first, len);
ptd->buf[ptd->idx + len] = '\0';
ptd->idx += len + 1;
ptd->ptr++; /* ':' */
return 0;
}
static int stabs_pts_read_number(struct ParseTypedefData* ptd, long* v)
{
char* last;
*v = strtol(ptd->ptr, &last, 10);
PTS_ABORTIF(ptd, last == ptd->ptr);
ptd->ptr = last;
return 0;
}
static int stabs_pts_read_type_reference(struct ParseTypedefData* ptd,
long* filenr, long* subnr)
{
if (*ptd->ptr == '(')
{
/* '(' <int> ',' <int> ')' */
ptd->ptr++;
PTS_ABORTIF(ptd, stabs_pts_read_number(ptd, filenr) == -1);
PTS_ABORTIF(ptd, *ptd->ptr++ != ',');
PTS_ABORTIF(ptd, stabs_pts_read_number(ptd, subnr) == -1);
PTS_ABORTIF(ptd, *ptd->ptr++ != ')');
}
else
{
*filenr = 0;
PTS_ABORTIF(ptd, stabs_pts_read_number(ptd, subnr) == -1);
}
return 0;
}
struct pts_range_value
{
unsigned long long val;
int sign;
};
static int stabs_pts_read_range_value(struct ParseTypedefData* ptd, struct pts_range_value* prv)
{
char* last;
switch (*ptd->ptr)
{
case '0':
while (*ptd->ptr == '0') ptd->ptr++;
if (*ptd->ptr >= '1' && *ptd->ptr <= '7')
{
switch (ptd->ptr[1])
{
case '0':
PTS_ABORTIF(ptd, ptd->ptr[0] != '1');
prv->sign = -1;
prv->val = 0;
while (isdigit(*ptd->ptr)) prv->val = (prv->val << 3) + *ptd->ptr++ - '0';
break;
case '7':
prv->sign = 1;
prv->val = 0;
while (isdigit(*ptd->ptr)) prv->val = (prv->val << 3) + *ptd->ptr++ - '0';
break;
default: PTS_ABORTIF(ptd, 1); break;
}
} else prv->sign = 0;
break;
case '-':
prv->sign = -1;
prv->val = strtoull(++ptd->ptr, &last, 10);
ptd->ptr = last;
break;
case '+':
default:
prv->sign = 1;
prv->val = strtoull(ptd->ptr, &last, 10);
ptd->ptr = last;
break;
}
return 0;
}
static int stabs_pts_read_range(struct ParseTypedefData* ptd, const char* typename,
struct symt** dt)
{
struct symt* ref;
struct pts_range_value lo;
struct pts_range_value hi;
unsigned size;
enum BasicType bt;
int i;
unsigned long long v;
/* type ';' <int> ';' <int> ';' */
PTS_ABORTIF(ptd, stabs_pts_read_type_def(ptd, NULL, &ref) == -1);
PTS_ABORTIF(ptd, *ptd->ptr++ != ';'); /* ';' */
PTS_ABORTIF(ptd, stabs_pts_read_range_value(ptd, &lo) == -1);
PTS_ABORTIF(ptd, *ptd->ptr++ != ';'); /* ';' */
PTS_ABORTIF(ptd, stabs_pts_read_range_value(ptd, &hi) == -1);
PTS_ABORTIF(ptd, *ptd->ptr++ != ';'); /* ';' */
/* basically, we don't use ref... in some cases, for example, float is declared
* as a derivated type of int... which won't help us... so we guess the types
* from the various formats
*/
if (lo.sign == 0 && hi.sign < 0)
{
bt = btUInt;
size = hi.val;
}
else if (lo.sign < 0 && hi.sign == 0)
{
bt = btUInt;
size = lo.val;
}
else if (lo.sign > 0 && hi.sign == 0)
{
bt = btFloat;
size = lo.val;
}
else if (lo.sign < 0 && hi.sign > 0)
{
v = 1 << 7;
for (i = 7; i < 64; i += 8)
{
if (lo.val == v && hi.val == v - 1)
{
bt = btInt;
size = (i + 1) / 8;
break;
}
v <<= 8;
}
PTS_ABORTIF(ptd, i >= 64);
}
else if (lo.sign == 0 && hi.sign > 0)
{
if (hi.val == 127) /* specific case for char... */
{
bt = btChar;
size = 1;
}
else
{
v = 1;
for (i = 8; i <= 64; i += 8)
{
v <<= 8;
if (hi.val + 1 == v)
{
bt = btUInt;
size = (i + 1) / 8;
break;
}
}
PTS_ABORTIF(ptd, i > 64);
}
}
else PTS_ABORTIF(ptd, 1);
*dt = &symt_new_basic(ptd->module, bt, typename, size)->symt;
return 0;
}
static inline int stabs_pts_read_method_info(struct ParseTypedefData* ptd)
{
struct symt* dt;
char* tmp;
char mthd;
do
{
/* get type of return value */
PTS_ABORTIF(ptd, stabs_pts_read_type_def(ptd, NULL, &dt) == -1);
if (*ptd->ptr == ';') ptd->ptr++;
/* get types of parameters */
if (*ptd->ptr == ':')
{
PTS_ABORTIF(ptd, !(tmp = strchr(ptd->ptr + 1, ';')));
ptd->ptr = tmp + 1;
}
PTS_ABORTIF(ptd, !(*ptd->ptr >= '0' && *ptd->ptr <= '9'));
ptd->ptr++;
PTS_ABORTIF(ptd, !(ptd->ptr[0] >= 'A' && *ptd->ptr <= 'D'));
mthd = *++ptd->ptr;
PTS_ABORTIF(ptd, mthd != '.' && mthd != '?' && mthd != '*');
ptd->ptr++;
if (mthd == '*')
{
long int ofs;
struct symt* dt;
PTS_ABORTIF(ptd, stabs_pts_read_number(ptd, &ofs) == -1);
PTS_ABORTIF(ptd, *ptd->ptr++ != ';');
PTS_ABORTIF(ptd, stabs_pts_read_type_def(ptd, NULL, &dt) == -1);
PTS_ABORTIF(ptd, *ptd->ptr++ != ';');
}
} while (*ptd->ptr != ';');
ptd->ptr++;
return 0;
}
static inline int stabs_pts_read_aggregate(struct ParseTypedefData* ptd,
struct symt_udt* sdt)
{
long sz, ofs;
struct symt* adt;
struct symt* dt = NULL;
int idx;
int doadd;
PTS_ABORTIF(ptd, stabs_pts_read_number(ptd, &sz) == -1);
doadd = symt_set_udt_size(ptd->module, sdt, sz);
if (*ptd->ptr == '!') /* C++ inheritence */
{
long num_classes;
ptd->ptr++;
PTS_ABORTIF(ptd, stabs_pts_read_number(ptd, &num_classes) == -1);
PTS_ABORTIF(ptd, *ptd->ptr++ != ',');
while (--num_classes >= 0)
{
ptd->ptr += 2; /* skip visibility and inheritence */
PTS_ABORTIF(ptd, stabs_pts_read_number(ptd, &ofs) == -1);
PTS_ABORTIF(ptd, *ptd->ptr++ != ',');
PTS_ABORTIF(ptd, stabs_pts_read_type_def(ptd, NULL, &adt) == -1);
if (doadd)
{
char tmp[256];
WCHAR* name;
DWORD size;
symt_get_info(adt, TI_GET_SYMNAME, &name);
strcmp(tmp, "__inherited_class_");
WideCharToMultiByte(CP_ACP, 0, name, -1,
tmp + strlen(tmp), sizeof(tmp) - strlen(tmp),
NULL, NULL);
HeapFree(GetProcessHeap(), 0, name);
/* FIXME: TI_GET_LENGTH will not always work, especially when adt
* has just been seen as a forward definition and not the real stuff
* yet.
* As we don't use much the size of members in structs, this may not
* be much of a problem
*/
symt_get_info(adt, TI_GET_LENGTH, &size);
symt_add_udt_element(ptd->module, sdt, tmp, adt, ofs, size * 8);
}
PTS_ABORTIF(ptd, *ptd->ptr++ != ';');
}
}
/* if the structure has already been filled, just redo the parsing
* but don't store results into the struct
* FIXME: there's a quite ugly memory leak in there...
*/
/* Now parse the individual elements of the structure/union. */
while (*ptd->ptr != ';')
{
/* agg_name : type ',' <int:offset> ',' <int:size> */
idx = ptd->idx;
if (ptd->ptr[0] == '$' && ptd->ptr[1] == 'v')
{
long x;
if (ptd->ptr[2] == 'f')
{
/* C++ virtual method table */
ptd->ptr += 3;
stabs_read_type_enum(&ptd->ptr);
PTS_ABORTIF(ptd, *ptd->ptr++ != ':');
PTS_ABORTIF(ptd, stabs_pts_read_type_def(ptd, NULL, &dt) == -1);
PTS_ABORTIF(ptd, *ptd->ptr++ != ',');
PTS_ABORTIF(ptd, stabs_pts_read_number(ptd, &x) == -1);
PTS_ABORTIF(ptd, *ptd->ptr++ != ';');
ptd->idx = idx;
continue;
}
else if (ptd->ptr[2] == 'b')
{
ptd->ptr += 3;
PTS_ABORTIF(ptd, stabs_pts_read_type_def(ptd, NULL, &dt) == -1);
PTS_ABORTIF(ptd, *ptd->ptr++ != ':');
PTS_ABORTIF(ptd, stabs_pts_read_type_def(ptd, NULL, &dt) == -1);
PTS_ABORTIF(ptd, *ptd->ptr++ != ',');
PTS_ABORTIF(ptd, stabs_pts_read_number(ptd, &x) == -1);
PTS_ABORTIF(ptd, *ptd->ptr++ != ';');
ptd->idx = idx;
continue;
}
}
PTS_ABORTIF(ptd, stabs_pts_read_id(ptd) == -1);
/* Ref. TSDF R2.130 Section 7.4. When the field name is a method name
* it is followed by two colons rather than one.
*/
if (*ptd->ptr == ':')
{
ptd->ptr++;
stabs_pts_read_method_info(ptd);
ptd->idx = idx;
continue;
}
else
{
/* skip C++ member protection /0 /1 or /2 */
if (*ptd->ptr == '/') ptd->ptr += 2;
}
PTS_ABORTIF(ptd, stabs_pts_read_type_def(ptd, NULL, &adt) == -1);
switch (*ptd->ptr++)
{
case ',':
PTS_ABORTIF(ptd, stabs_pts_read_number(ptd, &ofs) == -1);
PTS_ABORTIF(ptd, *ptd->ptr++ != ',');
PTS_ABORTIF(ptd, stabs_pts_read_number(ptd, &sz) == -1);
PTS_ABORTIF(ptd, *ptd->ptr++ != ';');
if (doadd) symt_add_udt_element(ptd->module, sdt, ptd->buf + idx, adt, ofs, sz);
break;
case ':':
{
char* tmp;
/* method parameters... terminated by ';' */
PTS_ABORTIF(ptd, !(tmp = strchr(ptd->ptr, ';')));
ptd->ptr = tmp + 1;
}
break;
default:
PTS_ABORTIF(ptd, TRUE);
}
ptd->idx = idx;
}
PTS_ABORTIF(ptd, *ptd->ptr++ != ';');
if (*ptd->ptr == '~')
{
ptd->ptr++;
PTS_ABORTIF(ptd, *ptd->ptr++ != '%');
PTS_ABORTIF(ptd, stabs_pts_read_type_def(ptd, NULL, &dt) == -1);
PTS_ABORTIF(ptd, *ptd->ptr++ != ';');
}
return 0;
}
static inline int stabs_pts_read_enum(struct ParseTypedefData* ptd,
struct symt_enum* edt)
{
long value;
int idx;
while (*ptd->ptr != ';')
{
idx = ptd->idx;
PTS_ABORTIF(ptd, stabs_pts_read_id(ptd) == -1);
PTS_ABORTIF(ptd, stabs_pts_read_number(ptd, &value) == -1);
PTS_ABORTIF(ptd, *ptd->ptr++ != ',');
symt_add_enum_element(ptd->module, edt, ptd->buf + idx, value);
ptd->idx = idx;
}
ptd->ptr++;
return 0;
}
static inline int stabs_pts_read_array(struct ParseTypedefData* ptd,
struct symt** adt)
{
long lo, hi;
struct symt* rdt;
/* ar<typeinfo_nodef>;<int>;<int>;<typeinfo> */
PTS_ABORTIF(ptd, *ptd->ptr++ != 'r');
/* FIXME: range type is lost, always assume int */
PTS_ABORTIF(ptd, stabs_pts_read_type_def(ptd, NULL, &rdt) == -1);
PTS_ABORTIF(ptd, *ptd->ptr++ != ';'); /* ';' */
PTS_ABORTIF(ptd, stabs_pts_read_number(ptd, &lo) == -1);
PTS_ABORTIF(ptd, *ptd->ptr++ != ';'); /* ';' */
PTS_ABORTIF(ptd, stabs_pts_read_number(ptd, &hi) == -1);
PTS_ABORTIF(ptd, *ptd->ptr++ != ';'); /* ';' */
PTS_ABORTIF(ptd, stabs_pts_read_type_def(ptd, NULL, &rdt) == -1);
*adt = &symt_new_array(ptd->module, lo, hi, rdt)->symt;
return 0;
}
static int stabs_pts_read_type_def(struct ParseTypedefData* ptd, const char* typename,
struct symt** ret_dt)
{
int idx;
long sz = -1;
struct symt* new_dt = NULL; /* newly created data type */
struct symt* ref_dt; /* referenced data type (pointer...) */
long filenr1, subnr1, tmp;
/* things are a bit complicated because of the way the typedefs are stored inside
* the file, because addresses can change when realloc is done, so we must call
* over and over stabs_find_ref() to keep the correct values around
*/
PTS_ABORTIF(ptd, stabs_pts_read_type_reference(ptd, &filenr1, &subnr1) == -1);
while (*ptd->ptr == '=')
{
ptd->ptr++;
PTS_ABORTIF(ptd, new_dt != btNoType);
/* first handle attribute if any */
switch (*ptd->ptr)
{
case '@':
if (*++ptd->ptr == 's')
{
ptd->ptr++;
if (stabs_pts_read_number(ptd, &sz) == -1)
{
ERR("Not an attribute... NIY\n");
ptd->ptr -= 2;
return -1;
}
PTS_ABORTIF(ptd, *ptd->ptr++ != ';');
}
break;
}
/* then the real definitions */
switch (*ptd->ptr++)
{
case '*':
case '&':
PTS_ABORTIF(ptd, stabs_pts_read_type_def(ptd, NULL, &ref_dt) == -1);
new_dt = &symt_new_pointer(ptd->module, ref_dt)->symt;
break;
case 'k': /* 'const' modifier */
case 'B': /* 'volatile' modifier */
/* just kinda ignore the modifier, I guess -gmt */
PTS_ABORTIF(ptd, stabs_pts_read_type_def(ptd, typename, &new_dt) == -1);
break;
case '(':
ptd->ptr--;
PTS_ABORTIF(ptd, stabs_pts_read_type_def(ptd, typename, &new_dt) == -1);
break;
case 'a':
PTS_ABORTIF(ptd, stabs_pts_read_array(ptd, &new_dt) == -1);
break;
case 'r':
PTS_ABORTIF(ptd, stabs_pts_read_range(ptd, typename, &new_dt) == -1);
assert(!*stabs_find_ref(filenr1, subnr1));
*stabs_find_ref(filenr1, subnr1) = new_dt;
break;
case 'f':
PTS_ABORTIF(ptd, stabs_pts_read_type_def(ptd, NULL, &ref_dt) == -1);
new_dt = &symt_new_function_signature(ptd->module, ref_dt)->symt;
break;
case 'e':
new_dt = &symt_new_enum(ptd->module, typename)->symt;
PTS_ABORTIF(ptd, stabs_pts_read_enum(ptd, (struct symt_enum*)new_dt) == -1);
break;
case 's':
case 'u':
{
struct symt_udt* udt;
enum UdtKind kind = (ptd->ptr[-1] == 's') ? UdtStruct : UdtUnion;
/* udt can have been already defined in a forward definition */
udt = (struct symt_udt*)*stabs_find_ref(filenr1, subnr1);
if (!udt)
{
udt = symt_new_udt(ptd->module, typename, 0, kind);
/* we need to set it here, because a struct can hold a pointer
* to itself
*/
new_dt = *stabs_find_ref(filenr1, subnr1) = &udt->symt;
}
else
{
if (udt->symt.tag != SymTagUDT)
{
ERR("Forward declaration (%p/%s) is not an aggregate (%u)\n",
udt, symt_get_name(&udt->symt), udt->symt.tag);
return -1;
}
/* should check typename is the same too */
new_dt = &udt->symt;
}
PTS_ABORTIF(ptd, stabs_pts_read_aggregate(ptd, udt) == -1);
}
break;
case 'x':
idx = ptd->idx;
tmp = *ptd->ptr++;
PTS_ABORTIF(ptd, stabs_pts_read_id(ptd) == -1);
switch (tmp)
{
case 'e':
new_dt = &symt_new_enum(ptd->module, ptd->buf + idx)->symt;
break;
case 's':
new_dt = &symt_new_udt(ptd->module, ptd->buf + idx, 0, UdtStruct)->symt;
break;
case 'u':
new_dt = &symt_new_udt(ptd->module, ptd->buf + idx, 0, UdtUnion)->symt;
break;
default:
return -1;
}
ptd->idx = idx;
break;
case '-':
{
PTS_ABORTIF(ptd, stabs_pts_read_number(ptd, &tmp) == -1);
PTS_ABORTIF(ptd, stabs_get_basic(ptd, tmp, &new_dt) == -1);
PTS_ABORTIF(ptd, *ptd->ptr++ != ';');
}
break;
case '#':
if (*ptd->ptr == '#')
{
ptd->ptr++;
PTS_ABORTIF(ptd, stabs_pts_read_type_def(ptd, NULL, &ref_dt) == -1);
new_dt = &symt_new_function_signature(ptd->module, ref_dt)->symt;
}
else
{
struct symt* cls_dt;
struct symt* pmt_dt;
PTS_ABORTIF(ptd, stabs_pts_read_type_def(ptd, NULL, &cls_dt) == -1);
PTS_ABORTIF(ptd, *ptd->ptr++ != ',');
PTS_ABORTIF(ptd, stabs_pts_read_type_def(ptd, NULL, &ref_dt) == -1);
new_dt = &symt_new_function_signature(ptd->module, ref_dt)->symt;
while (*ptd->ptr == ',')
{
ptd->ptr++;
PTS_ABORTIF(ptd, stabs_pts_read_type_def(ptd, NULL, &pmt_dt) == -1);
}
}
break;
case 'R':
{
long type, len, unk;
int basic;
PTS_ABORTIF(ptd, stabs_pts_read_number(ptd, &type) == -1);
PTS_ABORTIF(ptd, *ptd->ptr++ != ';'); /* ';' */
PTS_ABORTIF(ptd, stabs_pts_read_number(ptd, &len) == -1);
PTS_ABORTIF(ptd, *ptd->ptr++ != ';'); /* ';' */
PTS_ABORTIF(ptd, stabs_pts_read_number(ptd, &unk) == -1);
PTS_ABORTIF(ptd, *ptd->ptr++ != ';'); /* ';' */
switch (type) /* see stabs_get_basic for the details */
{
case 1: basic = 12; break;
case 2: basic = 13; break;
case 3: basic = 25; break;
case 4: basic = 26; break;
case 5: basic = 35; break;
case 6: basic = 14; break;
default: PTS_ABORTIF(ptd, 1);
}
PTS_ABORTIF(ptd, stabs_get_basic(ptd, basic, &new_dt) == -1);
}
break;
default:
ERR("Unknown type '%c'\n", ptd->ptr[-1]);
return -1;
}
}
if (!new_dt)
{
/* is it a forward declaration that has been filled ? */
new_dt = *stabs_find_ref(filenr1, subnr1);
/* if not, this should be void (which is defined as a ref to itself, but we
* don't correctly catch it)
*/
if (!new_dt && typename)
{
new_dt = &symt_new_basic(ptd->module, btVoid, typename, 0)->symt;
PTS_ABORTIF(ptd, strcmp(typename, "void"));
}
}
*stabs_find_ref(filenr1, subnr1) = *ret_dt = new_dt;
TRACE("Adding (%ld,%ld) %s\n", filenr1, subnr1, typename);
return 0;
}
static int stabs_parse_typedef(struct module* module, const char* ptr,
const char* typename)
{
struct ParseTypedefData ptd;
struct symt* dt;
int ret = -1;
/* check for already existing definition */
TRACE("%s\n", ptr);
ptd.module = module;
ptd.idx = 0;
#ifdef PTS_DEBUG
ptd.err_idx = 0;
#endif
for (ptd.ptr = ptr - 1; ;)
{
ptd.ptr = strchr(ptd.ptr + 1, ':');
if (ptd.ptr == NULL || *++ptd.ptr != ':') break;
}
if (ptd.ptr)
{
if (*ptd.ptr != '(') ptd.ptr++;
/* most of type definitions take one char, except Tt */
if (*ptd.ptr != '(') ptd.ptr++;
ret = stabs_pts_read_type_def(&ptd, typename, &dt);
}
if (ret == -1 || *ptd.ptr)
{
#ifdef PTS_DEBUG
int i;
TRACE("Failure on %s\n", ptr);
if (ret == -1)
{
for (i = 0; i < ptd.err_idx; i++)
{
TRACE("[%d]: line %d => %s\n",
i, ptd.errors[i].line, ptd.errors[i].ptr);
}
}
else
TRACE("[0]: => %s\n", ptd.ptr);
#else
ERR("Failure on %s at %s\n", ptr, ptd.ptr);
#endif
return FALSE;
}
return TRUE;
}
static struct symt* stabs_parse_type(const char* stab)
{
const char* c = stab - 1;
/*
* Look through the stab definition, and figure out what struct symt
* this represents. If we have something we know about, assign the
* type.
* According to "The \"stabs\" debug format" (Rev 2.130) the name may be
* a C++ name and contain double colons e.g. foo::bar::baz:t5=*6.
*/
do
{
if ((c = strchr(c + 1, ':')) == NULL) return NULL;
} while (*++c == ':');
/*
* The next characters say more about the type (i.e. data, function, etc)
* of symbol. Skip them. (C++ for example may have Tt).
* Actually this is a very weak description; I think Tt is the only
* multiple combination we should see.
*/
while (*c && *c != '(' && !isdigit(*c))
c++;
/*
* The next is either an integer or a (integer,integer).
* The stabs_read_type_enum() takes care that stab_types is large enough.
*/
return *stabs_read_type_enum(&c);
}
struct pending_loc_var
{
char name[256];
struct symt* type;
unsigned offset;
unsigned regno;
};
static
struct symt_public* lookup_public(const struct module* module,
const struct symt_compiland* compiland,
const char* name)
{
unsigned nfind = 0;
struct symt_public* found = NULL;
struct symt_public* xfound = NULL;
struct symt_public* sym;
const char* xname;
const char* tmp;
void* ptr;
struct hash_table_iter hti;
const char* in_src;
const char* out_src;
if (compiland && compiland->symt.tag == SymTagCompiland)
in_src = source_get(module, compiland->source);
else in_src = NULL;
hash_table_iter_init(&module->ht_symbols, &hti, name);
while ((ptr = hash_table_iter_up(&hti)))
{
sym = GET_ENTRY(ptr, struct symt_public, hash_elt);
if (sym->symt.tag == SymTagPublicSymbol)
{
xname = symt_get_name(&sym->symt);
if (!xname || strcmp(xname, name)) continue;
if (sym->container &&
sym->container->tag == SymTagCompiland)
out_src = source_get(module, ((struct symt_compiland*)sym->container)->source);
else out_src = NULL;
xfound = sym;
if ((in_src && !out_src) || (!in_src && out_src)) continue;
if (in_src)
{
if (strcmp(in_src, out_src) || (tmp = strrchr(in_src, '/')) == NULL ||
strcmp(tmp + 1, out_src))
continue;
}
/* we continue once found to insure uniqueness of public symbol's name */
if (nfind++)
{
FIXME("More than one public symbol (%s) in %s: [%u] %p {%lx,%lx} in %s\n",
name, in_src, nfind, sym, sym->address, sym->size, out_src);
}
else found = sym;
}
}
if (!nfind)
{
if (xfound) found = xfound;
else FIXME("Couldn't locate %s in public symbols\n", name);
}
if (found)
{
if (found->container &&
found->container->tag == SymTagCompiland)
out_src = source_get(module, ((struct symt_compiland*)found->container)->source);
else out_src = NULL;
TRACE("Found for %s in %s: %p {%lx,%lx} in %s\n",
name, in_src, found, found->address, found->size, out_src);
}
return found;
}
SYM_TYPE stabs_parse(struct module* module, const char* addr,
unsigned long load_offset, unsigned int staboff, int stablen,
unsigned int strtaboff, int strtablen)
{
struct symt_function* curr_func = NULL;
struct symt_block* block = NULL;
struct symt_public* public;
struct symt_compiland* compiland = NULL;
char currpath[PATH_MAX];
int i, j;
int nstab;
const char* ptr;
char* stabbuff;
unsigned int stabbufflen;
const struct stab_nlist* stab_ptr;
const char* strs;
int strtabinc;
char symname[4096];
unsigned incl[32];
int incl_stk = -1;
int source_idx = -1;
struct pending_loc_var* pending_vars = NULL;
unsigned num_pending_vars = 0;
unsigned num_allocated_pending_vars = 0;
nstab = stablen / sizeof(struct stab_nlist);
stab_ptr = (struct stab_nlist*)(addr + staboff);
strs = (char*)(addr + strtaboff);
memset(currpath, 0, sizeof(currpath));
memset(stabs_basic, 0, sizeof(stabs_basic));
/*
* Allocate a buffer into which we can build stab strings for cases
* where the stab is continued over multiple lines.
*/
stabbufflen = 65536;
stabbuff = HeapAlloc(GetProcessHeap(), 0, stabbufflen);
strtabinc = 0;
stabbuff[0] = '\0';
for (i = 0; i < nstab; i++, stab_ptr++)
{
ptr = strs + stab_ptr->n_un.n_strx;
if (ptr[strlen(ptr) - 1] == '\\')
{
/*
* Indicates continuation. Append this to the buffer, and go onto the
* next record. Repeat the process until we find a stab without the
* '/' character, as this indicates we have the whole thing.
*/
unsigned len = strlen(ptr);
if (strlen(stabbuff) + len > stabbufflen)
{
stabbufflen += 65536;
stabbuff = HeapReAlloc(GetProcessHeap(), 0, stabbuff, stabbufflen);
}
strncat(stabbuff, ptr, len - 1);
continue;
}
else if (stabbuff[0] != '\0')
{
strcat(stabbuff, ptr);
ptr = stabbuff;
}
if (strchr(ptr, '=') != NULL)
{
/*
* The stabs aren't in writable memory, so copy it over so we are
* sure we can scribble on it.
*/
if (ptr != stabbuff)
{
strcpy(stabbuff, ptr);
ptr = stabbuff;
}
stab_strcpy(symname, sizeof(symname), ptr);
if (!stabs_parse_typedef(module, ptr, symname))
{
/* skip this definition */
stabbuff[0] = '\0';
continue;
}
}
#if 0
const char* defs[] = {"","","","", /* 00 */
"","","","", /* 08 */
"","","","", /* 10 */
"","","","", /* 18 */
"gsym","","fun","stsym", /* 20 */
"lcsym","main","rosym","", /* 28 */
"","","","", /* 30 */
"","","opt","", /* 38 */
"rsym","","sline","", /* 40 */
"","","","", /* 48 */
"","","","", /* 50 */
"","","","", /* 58 */
"","","so","", /* 60 */
"","","","", /* 68 */
"","","","", /* 70 */
"","","","", /* 78 */
"lsym","bincl","sol","", /* 80 */
"","","","", /* 88 */
"","","","", /* 90 */
"","","","", /* 98 */
"psym","eincl","","", /* a0 */
"","","","", /* a8 */
"","","","", /* b0 */
"","","","", /* b8 */
"lbrac","excl","","", /* c0 */
"","","","", /* c8 */
"","","","", /* d0 */
"","","","", /* d8 */
"rbrac","","","", /* e0 */
};
FIXME("Got %s<%u> %u/%lu (%s)\n",
defs[stab_ptr->n_type / 2], stab_ptr->n_type, stab_ptr->n_desc, stab_ptr->n_value, debugstr_a(ptr));
#endif
switch (stab_ptr->n_type)
{
case N_GSYM:
/*
* These are useless with ELF. They have no value, and you have to
* read the normal symbol table to get the address. Thus we
* ignore them, and when we process the normal symbol table
* we should do the right thing.
*
* With a.out or mingw, they actually do make some amount of sense.
*/
stab_strcpy(symname, sizeof(symname), ptr);
#ifdef __ELF__
if ((public = lookup_public(module, compiland, symname)))
symt_new_global_variable(module, compiland, symname, TRUE /* FIXME */,
public->address, public->size,
stabs_parse_type(ptr));
#else
symt_new_global_variable(module, symname, TRUE /* FIXME */,
load_offset + stab_ptr->n_value, 0,
stabs_parse_type(ptr));
#endif
break;
case N_LCSYM:
case N_STSYM:
/* These are static symbols and BSS symbols. */
stab_strcpy(symname, sizeof(symname), ptr);
symt_new_global_variable(module, compiland, symname, TRUE /* FIXME */,
load_offset + stab_ptr->n_value, 0,
stabs_parse_type(ptr));
break;
case N_LBRAC:
block = symt_open_func_block(module, curr_func, block,
stab_ptr->n_value);
for (j = 0; j < num_pending_vars; j++)
{
symt_add_func_local(module, curr_func, pending_vars[j].regno,
pending_vars[j].offset,
block, pending_vars[j].type, pending_vars[j].name);
}
num_pending_vars = 0;
break;
case N_RBRAC:
block = symt_close_func_block(module, curr_func, block,
stab_ptr->n_value);
break;
case N_PSYM:
/* These are function parameters. */
if (curr_func != NULL)
{
stab_strcpy(symname, sizeof(symname), ptr);
symt_add_func_local(module, curr_func, 0, stab_ptr->n_value,
NULL, stabs_parse_type(ptr), symname);
}
break;
case N_RSYM:
/* These are registers (as local variables) */
if (curr_func != NULL)
{
unsigned reg;
if (num_pending_vars == num_allocated_pending_vars)
{
num_allocated_pending_vars += 8;
if (!pending_vars)
pending_vars = HeapAlloc(GetProcessHeap(), 0,
num_allocated_pending_vars * sizeof(pending_vars[0]));
else
pending_vars = HeapReAlloc(GetProcessHeap(), 0, pending_vars,
num_allocated_pending_vars * sizeof(pending_vars[0]));
}
switch (stab_ptr->n_value)
{
case 0: reg = CV_REG_EAX; break;
case 1: reg = CV_REG_ECX; break;
case 2: reg = CV_REG_EDX; break;
case 3: reg = CV_REG_EBX; break;
case 4: reg = CV_REG_ESP; break;
case 5: reg = CV_REG_EBP; break;
case 6: reg = CV_REG_ESI; break;
case 7: reg = CV_REG_EDI; break;
case 11:
case 12:
case 13:
case 14:
case 15:
case 16:
case 17:
case 18:
case 19: reg = CV_REG_ST0 + stab_ptr->n_value - 12; break;
default:
FIXME("Unknown register value (%lu)\n", stab_ptr->n_value);
reg = CV_REG_NONE;
break;
}
stab_strcpy(pending_vars[num_pending_vars].name,
sizeof(pending_vars[num_pending_vars].name), ptr);
pending_vars[num_pending_vars].type = stabs_parse_type(ptr);
pending_vars[num_pending_vars].offset = 0;
pending_vars[num_pending_vars].regno = reg;
num_pending_vars++;
}
break;
case N_LSYM:
/* These are local variables */
if (curr_func != NULL)
{
if (num_pending_vars == num_allocated_pending_vars)
{
num_allocated_pending_vars += 8;
if (!pending_vars)
pending_vars = HeapAlloc(GetProcessHeap(), 0,
num_allocated_pending_vars * sizeof(pending_vars[0]));
else
pending_vars = HeapReAlloc(GetProcessHeap(), 0, pending_vars,
num_allocated_pending_vars * sizeof(pending_vars[0]));
}
stab_strcpy(pending_vars[num_pending_vars].name,
sizeof(pending_vars[num_pending_vars].name), ptr);
pending_vars[num_pending_vars].type = stabs_parse_type(ptr);
pending_vars[num_pending_vars].offset = stab_ptr->n_value;
pending_vars[num_pending_vars].regno = 0;
num_pending_vars++;
}
break;
case N_SLINE:
/*
* This is a line number. These are always relative to the start
* of the function (N_FUN), and this makes the lookup easier.
*/
if (curr_func != NULL)
{
assert(source_idx >= 0);
#ifdef __ELF__
symt_add_func_line(module, curr_func, source_idx,
stab_ptr->n_desc, stab_ptr->n_value);
#else
/*
* This isn't right. The order of the stabs is different under
* a.out, and as a result we would end up attaching the line
* number to the wrong function.
*/
symt_add_func_line(module, curr_func, source_idx,
stab_ptr->n_desc,
stab_ptr->n_value - curr_func->addr.off);
#endif
}
break;
case N_FUN:
/* First, clean up the previous function we were working on. */
symt_normalize_function(module, curr_func);
/*
* For now, just declare the various functions. Later
* on, we will add the line number information and the
* local symbols.
*/
/*
* Copy the string to a temp buffer so we
* can kill everything after the ':'. We do
* it this way because otherwise we end up dirtying
* all of the pages related to the stabs, and that
* sucks up swap space like crazy.
*/
stab_strcpy(symname, sizeof(symname), ptr);
if (*symname)
{
struct symt_function_signature* func_type;
func_type = symt_new_function_signature(module,
stabs_parse_type(ptr));
#ifdef __ELF__
if ((public = lookup_public(module, compiland, symname)))
curr_func = symt_new_function(module, compiland, symname,
public->address, public->size,
stabs_parse_type(ptr));
#else
curr_func = symt_new_function(module, compiland, symname,
load_offset + stab_ptr->n_value, 0,
func_type);
#endif
}
else
{
/* some GCC seem to use a N_FUN "" to mark the end of a function */
curr_func = NULL;
}
break;
case N_SO:
/*
* This indicates a new source file. Append the records
* together, to build the correct path name.
*/
if (*ptr == '\0') /* end of N_SO file */
{
/* Nuke old path. */
currpath[0] = '\0';
symt_normalize_function(module, curr_func);
curr_func = NULL;
source_idx = -1;
incl_stk = -1;
assert(block == NULL);
compiland = NULL;
}
else
{
if (*ptr != '/')
strcat(currpath, ptr);
else
strcpy(currpath, ptr);
stabs_reset_includes();
compiland = symt_new_compiland(module, currpath);
source_idx = source_new(module, currpath);
}
break;
case N_SOL:
strcpy(currpath, ptr);
source_idx = source_new(module, currpath);
break;
case N_UNDF:
strs += strtabinc;
strtabinc = stab_ptr->n_value;
symt_normalize_function(module, curr_func);
curr_func = NULL;
break;
case N_OPT:
/* Ignore this. We don't care what it points to. */
break;
case N_BINCL:
stabs_add_include(stabs_new_include(ptr, stab_ptr->n_value));
assert(incl_stk < (int)(sizeof(incl) / sizeof(incl[0])) - 1);
source_idx = incl[++incl_stk] = source_new(module, ptr);
break;
case N_EINCL:
assert(incl_stk > 0);
source_idx = incl[--incl_stk];
break;
case N_EXCL:
stabs_add_include(stabs_find_include(ptr, stab_ptr->n_value));
break;
case N_MAIN:
/* Always ignore these. GCC doesn't even generate them. */
break;
default:
ERR("Unknown stab type 0x%02x\n", stab_ptr->n_type);
break;
}
stabbuff[0] = '\0';
TRACE("0x%02x %lx %s\n",
stab_ptr->n_type, stab_ptr->n_value, strs + stab_ptr->n_un.n_strx);
}
HeapFree(GetProcessHeap(), 0, stabbuff);
stabs_free_includes();
if (pending_vars) HeapFree(GetProcessHeap(), 0, pending_vars);
return SymSym;
}
/*
* Stack walking
*
* Copyright 1995 Alexandre Julliard
* Copyright 1996 Eric Youngdale
* Copyright 1999 Ove Kven
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "dbghelp_private.h"
#include "winreg.h"
#include "ntstatus.h"
#include "thread.h" /* FIXME: must be included before winternl.h */
#include "winternl.h"
#include "wine/debug.h"
#include "stackframe.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<%08lx>", addr->Offset);
case AddrMode1616:
return wine_dbg_sprintf("1616<%04x:%04lx>", addr->Segment, addr->Offset);
case AddrMode1632:
return wine_dbg_sprintf("1632<%04x:%08lx>", addr->Segment, addr->Offset);
case AddrModeReal:
return wine_dbg_sprintf("real<%04x:%04lx>", addr->Segment, addr->Offset);
default:
return "unknown";
}
}
/* 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])
/***********************************************************************
* 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)
{
CONTEXT* ctx = (CONTEXT*)_ctx;
STACK32FRAME frame32;
STACK16FRAME frame16;
char ch;
ADDRESS tmp;
DWORD p;
WORD val;
BOOL do_switch;
TRACE("(%ld, %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;
}
/* sanity check */
if (curr_mode >= stm_done) return FALSE;
if (!f_read_mem) f_read_mem = ReadProcessMemory;
if (!f_xlat_adr) f_xlat_adr = addr_to_linear;
TRACE("Enter: PC=%s Frame=%s Return=%s Stack=%s Mode=%s cSwitch=%08lx nSwitch=%08lx\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;
/* Init done */
curr_mode = (frame->AddrPC.Mode == AddrModeFlat) ?
stm_32bit : stm_16bit;
/* Get the current ESP (don't know if this is valid) */
if (ctx)
{
frame->AddrStack.Segment = 0;
frame->AddrStack.Offset = ctx->Esp;
frame->AddrStack.Mode = AddrModeFlat;
}
/* cur_switch holds address of curr_stack's field in TEB in debuggee
* address space
*/
if (NtQueryInformationThread(hThread, ThreadBasicInformation, &info,
sizeof(info), NULL) != STATUS_SUCCESS)
goto done_err;
curr_switch = (unsigned long)info.TebBaseAddress + FIELD_OFFSET(TEB, cur_stack);
if (!f_read_mem(hProcess, (void*)curr_switch, &next_switch,
sizeof(next_switch), NULL))
{
WARN("Can't read TEB:cur_stack\n");
goto done_err;
}
if (curr_mode == stm_16bit)
{
if (!f_read_mem(hProcess, (void*)next_switch, &frame32,
sizeof(frame32), NULL))
{
WARN("Bad stack frame 0x%08lx\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 (!f_read_mem(hProcess, (void*)f_xlat_adr(hProcess, hThread, &tmp),
&ch, sizeof(ch), NULL))
curr_switch = 0xFFFFFFFF;
frame->AddrReturn.Mode = frame->AddrStack.Mode = AddrMode1616;
/* "pop up" previous BP value */
if (!f_read_mem(hProcess, (void*)frame->AddrFrame.Offset,
&val, sizeof(WORD), NULL))
goto done_err;
frame->AddrFrame.Offset = val;
}
else
{
tmp.Mode = AddrMode1616;
tmp.Segment = SELECTOROF(next_switch);
tmp.Offset = OFFSETOF(next_switch);
p = f_xlat_adr(hProcess, hThread, &tmp);
if (!f_read_mem(hProcess, (void*)p, &frame16, sizeof(frame16), NULL))
{
WARN("Bad stack frame 0x%08lx\n", p);
goto done_err;
}
curr_switch = (DWORD)frame16.frame32;
if (!f_read_mem(hProcess, (void*)curr_switch, &ch, sizeof(ch), NULL))
curr_switch = 0xFFFFFFFF;
frame->AddrReturn.Mode = frame->AddrStack.Mode = AddrModeFlat;
/* "pop up" previous EBP value */
if (!f_read_mem(hProcess, (void*)frame->AddrFrame.Offset,
&frame->AddrFrame.Offset, sizeof(DWORD), NULL))
goto done_err;
}
}
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 = OFFSETOF(curr_switch) &&
frame->AddrFrame.Segment == SELECTOROF(curr_switch) &&
frame->AddrFrame.Offset >= OFFSETOF(curr_switch);
}
if (do_switch)
{
if (curr_mode == stm_16bit)
{
if (!f_read_mem(hProcess, (void*)next_switch, &frame32,
sizeof(frame32), NULL))
{
WARN("Bad stack frame 0x%08lx\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 = f_xlat_adr(hProcess, hThread, &tmp);
if (!f_read_mem(hProcess, (void*)p, &frame16, sizeof(frame16), NULL))
{
WARN("Bad stack frame 0x%08lx\n", p);
goto done_err;
}
curr_switch = (DWORD)frame16.frame32;
curr_mode = stm_32bit;
if (!f_read_mem(hProcess, (void*)curr_switch, &ch, sizeof(ch), NULL))
curr_switch = 0xFFFFFFFF;
}
else
{
tmp.Mode = AddrMode1616;
tmp.Segment = SELECTOROF(next_switch);
tmp.Offset = OFFSETOF(next_switch);
p = f_xlat_adr(hProcess, hThread, &tmp);
if (!f_read_mem(hProcess, (void*)p, &frame16, sizeof(frame16), NULL))
{
WARN("Bad stack frame 0x%08lx\n", p);
goto done_err;
}
TRACE("Got a 16 bit stack switch:"
"\n\tframe32: %08lx"
"\n\tedx:%08lx ecx:%08lx ebp:%08lx"
"\n\tds:%04x es:%04x fs:%04x gs:%04x"
"\n\tcall_from_ip:%08lx module_cs:%04lx relay=%08lx"
"\n\tentry_ip:%04x entry_point:%08lx"
"\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 (!f_read_mem(hProcess, (void*)next_switch, &frame32, sizeof(frame32),
NULL))
{
WARN("Bad stack frame 0x%08lx\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 (!f_read_mem(hProcess, (void*)f_xlat_adr(hProcess, hThread, &tmp),
&ch, sizeof(ch), NULL))
curr_switch = 0xFFFFFFFF;
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 (!f_read_mem(hProcess,
(void*)f_xlat_adr(hProcess, hThread, &frame->AddrFrame),
&val, sizeof(WORD), NULL))
goto done_err;
frame->AddrFrame.Offset = val;
}
else
{
frame->AddrStack.Offset = frame->AddrFrame.Offset + 2 * sizeof(DWORD);
/* "pop up" previous EBP value */
if (!f_read_mem(hProcess, (void*)frame->AddrFrame.Offset,
&frame->AddrFrame.Offset, sizeof(DWORD), NULL))
goto done_err;
}
}
}
if (curr_mode == stm_16bit)
{
int i;
p = f_xlat_adr(hProcess, hThread, &frame->AddrFrame);
if (!f_read_mem(hProcess, (void*)(p + sizeof(WORD)), &val, sizeof(WORD), NULL))
goto done_err;
frame->AddrReturn.Offset = val;
/* get potential cs if a far call was used */
if (!f_read_mem(hProcess, (void*)(p + 2 * sizeof(WORD)),
&val, sizeof(WORD), NULL))
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(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" paramaters 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++)
{
f_read_mem(hProcess, (void*)(p + (2 + i) * sizeof(WORD)),
&val, sizeof(val), NULL);
frame->Params[i] = val;
}
}
else
{
if (!f_read_mem(hProcess,
(void*)(frame->AddrFrame.Offset + sizeof(DWORD)),
&frame->AddrReturn.Offset, sizeof(DWORD), NULL))
goto done_err;
f_read_mem(hProcess,
(void*)(frame->AddrFrame.Offset + 2 * sizeof(DWORD)),
frame->Params, sizeof(frame->Params), NULL);
}
frame->Far = FALSE;
frame->Virtual = FALSE;
TRACE("Leave: PC=%s Frame=%s Return=%s Stack=%s Mode=%s cSwitch=%08lx nSwitch=%08lx\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);
return TRUE;
done_err:
curr_mode = stm_done;
return FALSE;
}
/*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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;
for (alloc = used = num = 0, arena = pool->first; arena; arena = arena->next)
{
alloc += pool->arena_size;
used += arena->current - (char*)arena;
num++;
}
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
for (arena = pool->first; arena; arena = next)
{
next = arena->next;
HeapFree(GetProcessHeap(), 0, arena);
}
pool_init(pool, 0);
}
void* pool_alloc(struct pool* pool, unsigned len)
{
struct pool_arena** parena;
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 (parena = &pool->first; *parena; parena = &(*parena)->next)
{
if ((char*)(*parena) + pool->arena_size - (*parena)->current >= len)
{
ret = (*parena)->current;
(*parena)->current += len;
return ret;
}
}
arena = HeapAlloc(GetProcessHeap(), 0, pool->arena_size);
if (!arena) {FIXME("OOM\n");return NULL;}
*parena = arena;
ret = (char*)arena + sizeof(*arena);
arena->next = NULL;
arena->current = (char*)ret + len;
return ret;
}
static struct pool_arena* pool_is_last(struct pool* pool, void* p, unsigned old_size)
{
struct pool_arena* arena;
for (arena = pool->first; arena; arena = arena->next)
{
if (arena->current == (char*)p + old_size) return arena;
}
return NULL;
}
void* pool_realloc(struct pool* pool, void* p, unsigned old_size, unsigned new_size)
{
struct pool_arena* arena;
void* new;
if ((arena = pool_is_last(pool, p, old_size)) &&
(char*)p + new_size <= (char*)arena + pool->arena_size)
{
arena->current = (char*)p + new_size;
return p;
}
if ((new = pool_alloc(pool, new_size)) && old_size)
memcpy(new, p, min(old_size, new_size));
return new;
}
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->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))
{
v->buckets = pool_realloc(pool, v->buckets,
v->num_buckets * sizeof(void*),
(v->num_buckets + 1) * sizeof(void*));
v->buckets[v->num_buckets] = pool_alloc(pool, v->elt_size << v->shift);
return v->buckets[v->num_buckets++];
}
return vector_at(v, ncurr);
}
static unsigned vector_position(const struct vector* v, const void* elt)
{
int i;
for (i = 0; i < v->num_buckets; i++)
{
if (v->buckets[i] <= elt &&
(const char*)elt < (const char*)v->buckets[i] + (v->elt_size << v->shift))
{
return (i << v->shift) +
((const char*)elt - (const char*)v->buckets[i]) / v->elt_size;
}
}
assert(0);
}
void* vector_iter_up(const struct vector* v, void* elt)
{
unsigned pos;
if (!elt) return vector_at(v, 0);
pos = vector_position(v, elt) + 1;
if (pos >= vector_length(v)) return NULL;
return vector_at(v, pos);
}
void* vector_iter_down(const struct vector* v, void* elt)
{
unsigned pos;
if (!elt) return vector_at(v, vector_length(v) - 1);
pos = vector_position(v, elt);
if (pos == 0) return NULL;
return vector_at(v, pos - 1);
}
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->buckets = pool_alloc(pool, num_buckets * sizeof(struct hash_table_elt*));
assert(ht->buckets);
ht->num_buckets = num_buckets;
memset(ht->buckets, 0, num_buckets * sizeof(struct hash_table_elt*));
}
void hash_table_destroy(struct hash_table* ht)
{
#if defined(USE_STATS)
int i;
unsigned len;
unsigned num = 0, 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;
num += len;
sq += len * len;
}
mean = (double)num / 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",
num, 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);
elt->next = ht->buckets[hash];
ht->buckets[hash] = elt;
}
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;
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->element) hti->element = hti->element->next;
while (!hti->element && hti->index < hti->last)
hti->element = hti->ht->buckets[++hti->index];
return hti->element;
}
/*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <sys/types.h>
#include <assert.h>
#include <regex.h>
#include "wine/debug.h"
#include "dbghelp_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
WINE_DECLARE_DEBUG_CHANNEL(dbghelp_symtype);
#define DLIT_OFFSET 0x00
#define DLIT_FIRST 0x01
#define DLIT_LAST 0x02
#define DLIT_SOURCEFILE 0x04
struct line_info
{
unsigned long cookie : 3,
line_number;
union
{
unsigned long pc_offset;
unsigned source_file;
} u;
};
inline static int cmp_addr(DWORD a1, DWORD a2)
{
if (a1 > a2) return 1;
if (a1 < a2) return -1;
return 0;
}
inline static int cmp_sorttab_addr(const struct module* module, int idx, DWORD addr)
{
DWORD ref;
symt_get_info(&module->addr_sorttab[idx]->symt, TI_GET_ADDRESS, &ref);
return cmp_addr(ref, addr);
}
int symt_cmp_addr(const void* p1, const void* p2)
{
struct symt* sym1 = *(struct symt**)p1;
struct symt* sym2 = *(struct symt**)p2;
DWORD a1, a2;
symt_get_info(sym1, TI_GET_ADDRESS, &a1);
symt_get_info(sym2, TI_GET_ADDRESS, &a2);
return cmp_addr(a1, a2);
}
struct symt_compiland* symt_new_compiland(struct module* module, const char* name)
{
struct symt_compiland* sym;
TRACE_(dbghelp_symtype)("Adding compiland symbol %s:%s\n",
module->module.ModuleName, name);
if ((sym = pool_alloc(&module->pool, sizeof(*sym))))
{
sym->symt.tag = SymTagCompiland;
sym->source = source_new(module, name);
vector_init(&sym->vchildren, sizeof(struct symt*), 32);
}
return sym;
}
struct symt_public* symt_new_public(struct module* module,
struct symt_compiland* compiland,
const char* name,
unsigned long address, unsigned size,
BOOL in_code, BOOL is_func)
{
struct symt_public* sym;
struct symt** p;
TRACE_(dbghelp_symtype)("Adding public symbol %s:%s @%lx\n",
module->module.ModuleName, name, address);
if ((sym = pool_alloc(&module->pool, sizeof(*sym))))
{
sym->symt.tag = SymTagPublicSymbol;
sym->hash_elt.name = pool_strdup(&module->pool, name);
hash_table_add(&module->ht_symbols, &sym->hash_elt);
module->sortlist_valid = FALSE;
sym->container = compiland ? &compiland->symt : NULL;
sym->address = address;
sym->size = size;
sym->in_code = in_code;
sym->is_function = is_func;
if (compiland)
{
p = vector_add(&compiland->vchildren, &module->pool);
*p = &sym->symt;
}
}
return sym;
}
struct symt_data* symt_new_global_variable(struct module* module,
struct symt_compiland* compiland,
const char* name, unsigned is_static,
unsigned long addr, unsigned long size,
struct symt* type)
{
struct symt_data* sym;
struct symt** p;
TRACE_(dbghelp_symtype)("Adding global symbol %s:%s @%lx %p\n",
module->module.ModuleName, name, addr, type);
if ((sym = pool_alloc(&module->pool, sizeof(*sym))))
{
sym->symt.tag = SymTagData;
sym->hash_elt.name = pool_strdup(&module->pool, name);
hash_table_add(&module->ht_symbols, &sym->hash_elt);
module->sortlist_valid = FALSE;
sym->kind = is_static ? DataIsFileStatic : DataIsGlobal;
sym->container = compiland ? &compiland->symt : NULL;
sym->type = type;
sym->location = LocIsStatic; /* FIXME */
sym->u.address = addr;
if (compiland)
{
p = vector_add(&compiland->vchildren, &module->pool);
*p = &sym->symt;
}
}
return sym;
}
struct symt_function* symt_new_function(struct module* module,
struct symt_compiland* compiland,
const char* name,
unsigned long addr, unsigned long size,
struct symt* type)
{
struct symt_function* sym;
struct symt** p;
TRACE_(dbghelp_symtype)("Adding global function %s:%s @%lx-%lx\n",
module->module.ModuleName, name, addr, addr + size - 1);
if ((sym = pool_alloc(&module->pool, sizeof(*sym))))
{
sym->symt.tag = SymTagFunction;
sym->hash_elt.name = pool_strdup(&module->pool, name);
hash_table_add(&module->ht_symbols, &sym->hash_elt);
module->sortlist_valid = FALSE;
sym->container = &compiland->symt;
sym->addr = addr;
sym->type = type;
sym->size = size;
sym->addr = addr;
vector_init(&sym->vlines, sizeof(struct line_info), 64);
vector_init(&sym->vchildren, sizeof(struct symt*), 8);
if (compiland)
{
p = vector_add(&compiland->vchildren, &module->pool);
*p = &sym->symt;
}
}
return sym;
}
void symt_add_func_line(struct module* module, struct symt_function* func,
unsigned source_idx, int line_num, unsigned long offset)
{
struct line_info* dli;
BOOL last_matches = FALSE;
if (func == NULL || !(dbghelp_options & SYMOPT_LOAD_LINES)) return;
TRACE_(dbghelp_symtype)("(%p)%s:%lx %s:%u\n",
func, func->hash_elt.name, offset,
source_get(module, source_idx), line_num);
assert(func->symt.tag == SymTagFunction);
dli = NULL;
while ((dli = vector_iter_down(&func->vlines, dli)))
{
if (dli->cookie & DLIT_SOURCEFILE)
{
last_matches = (source_idx == dli->u.source_file);
break;
}
}
if (!last_matches)
{
/* we shouldn't have line changes on first line of function */
dli = vector_add(&func->vlines, &module->pool);
dli->cookie = DLIT_SOURCEFILE;
dli->line_number = 0;
dli->u.source_file = source_idx;
}
dli = vector_add(&func->vlines, &module->pool);
dli->cookie = DLIT_OFFSET;
dli->line_number = line_num;
dli->u.pc_offset = func->addr + offset;
}
struct symt_data* symt_add_func_local(struct module* module,
struct symt_function* func,
int regno, int offset,
struct symt_block* block,
struct symt* type, const char* name)
{
struct symt_data* locsym;
struct symt** p;
assert(func);
assert(func->symt.tag == SymTagFunction);
TRACE_(dbghelp_symtype)("Adding local symbol (%s:%s): %s %p\n",
module->module.ModuleName, func->hash_elt.name,
name, type);
locsym = pool_alloc(&module->pool, sizeof(*locsym));
locsym->symt.tag = SymTagData;
locsym->hash_elt.name = pool_strdup(&module->pool, name);
locsym->hash_elt.next = NULL;
locsym->kind = DataIsLocal;
locsym->container = &block->symt;
locsym->type = type;
if (regno)
{
locsym->location = LocIsEnregistered;
locsym->u.reg_id = regno;
}
else
{
locsym->location = LocIsRegRel;
locsym->u.reg_id = CV_REG_EBP;
locsym->u.offset = offset;
}
if (block)
p = vector_add(&block->vchildren, &module->pool);
else
p = vector_add(&func->vchildren, &module->pool);
*p = &locsym->symt;
return locsym;
}
struct symt_block* symt_open_func_block(struct module* module,
struct symt_function* func,
struct symt_block* parent_block,
unsigned pc)
{
struct symt_block* block;
struct symt** p;
assert(func);
assert(func->symt.tag == SymTagFunction);
assert(!parent_block || parent_block->symt.tag == SymTagBlock);
block = pool_alloc(&module->pool, sizeof(*block));
block->symt.tag = SymTagBlock;
block->address = func->addr + pc;
block->size = 0;
block->container = parent_block ? &parent_block->symt : &func->symt;
vector_init(&block->vchildren, sizeof(struct symt*), 4);
if (parent_block)
p = vector_add(&parent_block->vchildren, &module->pool);
else
p = vector_add(&func->vchildren, &module->pool);
*p = &block->symt;
return block;
}
struct symt_block* symt_close_func_block(struct module* module,
struct symt_function* func,
struct symt_block* block, unsigned pc)
{
assert(func->symt.tag == SymTagFunction);
block->size = func->addr + pc - block->address;
return (block->container->tag == SymTagBlock) ?
GET_ENTRY(block->container, struct symt_block, symt) : NULL;
}
BOOL symt_normalize_function(struct module* module, struct symt_function* func)
{
unsigned len;
struct line_info* dli;
if (!func) return TRUE;
/* We aren't adding any more locals or line numbers to this function.
* Free any spare memory that we might have allocated.
*/
assert(func->symt.tag == SymTagFunction);
/* EPP vector_pool_normalize(&func->vlines, &module->pool); */
/* EPP vector_pool_normalize(&func->vchildren, &module->pool); */
len = vector_length(&func->vlines);
if (len--)
{
dli = vector_at(&func->vlines, 0); dli->cookie |= DLIT_FIRST;
dli = vector_at(&func->vlines, len); dli->cookie |= DLIT_LAST;
}
return TRUE;
}
/* expect sym_info->MaxNameLen to be set before being called */
static void symt_fill_sym_info(const struct module* module,
const struct symt* sym, SYMBOL_INFO* sym_info)
{
const char* name;
sym_info->TypeIndex = (DWORD)sym;
sym_info->info = 0; /* TBD */
symt_get_info(sym, TI_GET_LENGTH, &sym_info->Size);
sym_info->ModBase = module->module.BaseOfImage;
sym_info->Flags = 0;
switch (sym->tag)
{
case SymTagData:
{
struct symt_data* data = (struct symt_data*)sym;
switch (data->location)
{
case LocIsEnregistered:
sym_info->Flags |= SYMFLAG_REGISTER;
sym_info->Register = data->u.reg_id;
sym_info->Address = 0;
break;
case LocIsRegRel:
sym_info->Flags |=
((data->u.offset < 0) ? SYMFLAG_LOCAL : SYMFLAG_PARAMETER) |
SYMFLAG_FRAMEREL;
sym_info->Register = data->u.reg_id;
sym_info->Address = data->u.offset;
break;
case LocIsStatic:
symt_get_info(sym, TI_GET_ADDRESS, &sym_info->Address);
sym_info->Register = 0;
break;
case LocIsConstant:
sym_info->Flags |= SYMFLAG_VALUEPRESENT;
sym_info->Value = data->u.value;
break;
default:
FIXME("Unhandled loc (%u) in sym data\n", data->location);
}
}
break;
case SymTagPublicSymbol:
sym_info->Flags |= SYMFLAG_EXPORT;
symt_get_info(sym, TI_GET_ADDRESS, &sym_info->Address);
break;
case SymTagFunction:
sym_info->Flags |= SYMFLAG_FUNCTION;
symt_get_info(sym, TI_GET_ADDRESS, &sym_info->Address);
break;
default:
symt_get_info(sym, TI_GET_ADDRESS, &sym_info->Address);
sym_info->Register = 0;
break;
}
sym_info->Scope = 0; /* FIXME */
sym_info->Tag = sym->tag;
name = symt_get_name(sym);
sym_info->NameLen = strlen(name) + 1;
if (sym_info->MaxNameLen)
{
strncpy(sym_info->Name, name, min(sym_info->NameLen, sym_info->MaxNameLen));
sym_info->Name[sym_info->MaxNameLen - 1] = '\0';
}
TRACE_(dbghelp_symtype)("%p => %s %lu %lx\n",
sym, sym_info->Name, sym_info->Size, sym_info->Address);
}
static BOOL symt_enum_module(struct module* module, const char* mask,
PSYM_ENUMERATESYMBOLS_CALLBACK cb, PVOID user)
{
char buffer[sizeof(SYMBOL_INFO) + 256];
SYMBOL_INFO* sym_info = (SYMBOL_INFO*)buffer;
void* ptr;
struct symt_ht* sym = NULL;
struct hash_table_iter hti;
regex_t preg;
assert(mask);
assert(mask[0] != '!');
regcomp(&preg, mask, REG_NOSUB);
hash_table_iter_init(&module->ht_symbols, &hti, NULL);
while ((ptr = hash_table_iter_up(&hti)))
{
sym = GET_ENTRY(ptr, struct symt_ht, hash_elt);
/* FIXME: this is not true, we should only drop the public
* symbol iff no other one is found
*/
if ((dbghelp_options & SYMOPT_AUTO_PUBLICS) &&
sym->symt.tag == SymTagPublicSymbol) continue;
if (sym->hash_elt.name &&
regexec(&preg, sym->hash_elt.name, 0, NULL, 0) == 0)
{
sym_info->SizeOfStruct = sizeof(SYMBOL_INFO);
sym_info->MaxNameLen = sizeof(buffer) - sizeof(SYMBOL_INFO);
symt_fill_sym_info(module, &sym->symt, sym_info);
if (!cb(sym_info, sym_info->Size, user)) break;
}
}
regfree(&preg);
return sym ? FALSE : TRUE;
}
/***********************************************************************
* resort_symbols
*
* Rebuild sorted list of symbols for a module.
*/
static BOOL resort_symbols(struct module* module)
{
int nsym = 0;
void* ptr;
struct symt_ht* sym;
struct hash_table_iter hti;
hash_table_iter_init(&module->ht_symbols, &hti, NULL);
while ((ptr = hash_table_iter_up(&hti)))
nsym++;
if (!(module->module.NumSyms = nsym)) return FALSE;
if (module->addr_sorttab)
module->addr_sorttab = HeapReAlloc(GetProcessHeap(), 0,
module->addr_sorttab,
nsym * sizeof(struct symt_ht*));
else
module->addr_sorttab = HeapAlloc(GetProcessHeap(), 0,
nsym * sizeof(struct symt_ht*));
if (!module->addr_sorttab) return FALSE;
nsym = 0;
hash_table_iter_init(&module->ht_symbols, &hti, NULL);
while ((ptr = hash_table_iter_up(&hti)))
{
sym = GET_ENTRY(ptr, struct symt_ht, hash_elt);
assert(sym);
module->addr_sorttab[nsym++] = sym;
}
qsort(module->addr_sorttab, nsym, sizeof(struct symt_ht*), symt_cmp_addr);
return module->sortlist_valid = TRUE;
}
/* assume addr is in module */
static int symt_find_nearest(struct module* module, DWORD addr)
{
int mid, high, low;
if (!module->sortlist_valid && !resort_symbols(module)) return -1;
/*
* Binary search to find closest symbol.
*/
low = 0;
high = module->module.NumSyms;
while (high > low + 1)
{
mid = (high + low) / 2;
if (cmp_sorttab_addr(module, mid, addr) < 0)
low = mid;
else
high = mid;
}
if (low != high && high != module->module.NumSyms &&
cmp_sorttab_addr(module, high, addr) <= 0)
low = high;
/* If found symbol is a public symbol, check if there are any other entries that
* might also have the same address, but would get better information
*/
if (module->addr_sorttab[low]->symt.tag == SymTagPublicSymbol)
{
DWORD ref;
symt_get_info(&module->addr_sorttab[low]->symt, TI_GET_ADDRESS, &ref);
if (low > 0 &&
module->addr_sorttab[low - 1]->symt.tag != SymTagPublicSymbol &&
!cmp_sorttab_addr(module, low - 1, ref))
low--;
else if (low < module->module.NumSyms - 1 &&
module->addr_sorttab[low + 1]->symt.tag != SymTagPublicSymbol &&
!cmp_sorttab_addr(module, low + 1, ref))
low++;
}
return low;
}
static BOOL symt_enum_locals_helper(struct process* pcs, struct module* module,
regex_t* preg, PSYM_ENUMERATESYMBOLS_CALLBACK cb,
PVOID user, SYMBOL_INFO* sym_info,
struct vector* v)
{
struct symt** plsym = NULL;
struct symt* lsym = NULL;
DWORD pc = pcs->ctx_frame.InstructionOffset;
while ((plsym = vector_iter_up(v, plsym)))
{
lsym = *plsym;
switch (lsym->tag)
{
case SymTagBlock:
{
struct symt_block* block = (struct symt_block*)lsym;
if (pc < block->address || block->address + block->size <= pc)
continue;
if (!symt_enum_locals_helper(pcs, module, preg, cb, user,
sym_info, &block->vchildren))
return FALSE;
}
break;
case SymTagData:
if (regexec(preg, symt_get_name(lsym), 0, NULL, 0) == 0)
{
symt_fill_sym_info(module, lsym, sym_info);
if (!cb(sym_info, sym_info->Size, user))
return FALSE;
}
break;
default:
FIXME("Unknown type: %u (%x)\n", lsym->tag, lsym->tag);
assert(0);
}
}
return TRUE;
}
static BOOL symt_enum_locals(struct process* pcs, const char* mask,
PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback,
PVOID UserContext)
{
struct module* module;
struct symt_ht* sym;
char buffer[sizeof(SYMBOL_INFO) + 256];
SYMBOL_INFO* sym_info = (SYMBOL_INFO*)buffer;
DWORD pc = pcs->ctx_frame.InstructionOffset;
int idx;
sym_info->SizeOfStruct = sizeof(*sym_info);
sym_info->MaxNameLen = sizeof(buffer) - sizeof(SYMBOL_INFO);
module = module_find_by_addr(pcs, pc, DMT_UNKNOWN);
if (!(module = module_get_debug(pcs, module))) return FALSE;
if ((idx = symt_find_nearest(module, pc)) == -1) return FALSE;
sym = module->addr_sorttab[idx];
if (sym->symt.tag == SymTagFunction)
{
BOOL ret;
regex_t preg;
regcomp(&preg, mask ? mask : ".*", REG_NOSUB);
ret = symt_enum_locals_helper(pcs, module, &preg, EnumSymbolsCallback,
UserContext, sym_info,
&((struct symt_function*)sym)->vchildren);
regfree(&preg);
return ret;
}
symt_fill_sym_info(module, &sym->symt, sym_info);
return EnumSymbolsCallback(sym_info, sym_info->Size, UserContext);
}
/******************************************************************
* SymEnumSymbols (DBGHELP.@)
*
*/
BOOL WINAPI SymEnumSymbols(HANDLE hProcess, ULONG BaseOfDll, PCSTR Mask,
PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback,
PVOID UserContext)
{
struct process* pcs = process_find_by_handle(hProcess);
struct module* module;
TRACE("(%p %08lx %s %p %p)\n",
hProcess, BaseOfDll, debugstr_a(Mask), EnumSymbolsCallback, UserContext);
if (!pcs) return FALSE;
if (BaseOfDll == 0)
{
if (Mask && Mask[0] == '!')
{
if (!Mask[1])
{
/* FIXME: is this really what's intended ??? */
for (module = pcs->lmodules; module; module = module->next)
{
if (module->module.SymType != SymNone &&
!symt_enum_module(module, ".*", EnumSymbolsCallback, UserContext))
break;
}
return TRUE;
}
module = module_find_by_name(pcs, &Mask[1], DMT_UNKNOWN);
Mask++;
}
else return symt_enum_locals(pcs, Mask, EnumSymbolsCallback, UserContext);
}
else
{
module = module_find_by_addr(pcs, BaseOfDll, DMT_UNKNOWN);
if (Mask && Mask[0] == '!')
{
if (!Mask[1] ||
strcmp(&Mask[1], module->module.ModuleName))
{
FIXME("Strange call mode\n");
return FALSE;
}
Mask = ".*";
}
else if (!Mask) Mask = ".*";
}
if ((module = module_get_debug(pcs, module)))
symt_enum_module(module, Mask, EnumSymbolsCallback, UserContext);
return TRUE;
}
struct sym_enumerate
{
void* ctx;
PSYM_ENUMSYMBOLS_CALLBACK cb;
};
static BOOL CALLBACK sym_enumerate_cb(PSYMBOL_INFO syminfo, ULONG size, void* ctx)
{
struct sym_enumerate* se = (struct sym_enumerate*)ctx;
return (se->cb)(syminfo->Name, syminfo->Address, syminfo->Size, se->ctx);
}
/***********************************************************************
* SymEnumerateSymbols (DBGHELP.@)
*/
BOOL WINAPI SymEnumerateSymbols(HANDLE hProcess, DWORD BaseOfDll,
PSYM_ENUMSYMBOLS_CALLBACK EnumSymbolsCallback,
PVOID UserContext)
{
struct sym_enumerate se;
se.ctx = UserContext;
se.cb = EnumSymbolsCallback;
return SymEnumSymbols(hProcess, BaseOfDll, NULL, sym_enumerate_cb, &se);
}
/******************************************************************
* SymFromAddr (DBGHELP.@)
*
*/
BOOL WINAPI SymFromAddr(HANDLE hProcess, DWORD Address,
DWORD* Displacement, PSYMBOL_INFO Symbol)
{
struct process* pcs = process_find_by_handle(hProcess);
struct module* module;
struct symt_ht* sym;
int idx;
if (!pcs) return FALSE;
module = module_find_by_addr(pcs, Address, DMT_UNKNOWN);
if (!(module = module_get_debug(pcs, module))) return FALSE;
if ((idx = symt_find_nearest(module, Address)) == -1) return FALSE;
sym = module->addr_sorttab[idx];
symt_fill_sym_info(module, &sym->symt, Symbol);
if (Displacement) *Displacement = Address - Symbol->Address;
return TRUE;
}
/******************************************************************
* SymGetSymFromAddr (DBGHELP.@)
*
*/
BOOL WINAPI SymGetSymFromAddr(HANDLE hProcess, DWORD Address,
PDWORD Displacement, PIMAGEHLP_SYMBOL Symbol)
{
char buffer[sizeof(SYMBOL_INFO) + 256];
SYMBOL_INFO*si = (SYMBOL_INFO*)buffer;
size_t len;
if (Symbol->SizeOfStruct < sizeof(*Symbol)) return FALSE;
si->SizeOfStruct = sizeof(*si);
si->MaxNameLen = 256;
if (!SymFromAddr(hProcess, Address, Displacement, si))
return FALSE;
Symbol->Address = si->Address;
Symbol->Size = si->Size;
Symbol->Flags = si->Flags;
len = min(Symbol->MaxNameLength, si->MaxNameLen);
strncpy(Symbol->Name, si->Name, len);
Symbol->Name[len - 1] = '\0';
return TRUE;
}
/******************************************************************
* SymFromName (DBGHELP.@)
*
*/
BOOL WINAPI SymFromName(HANDLE hProcess, LPSTR Name, PSYMBOL_INFO Symbol)
{
struct process* pcs = process_find_by_handle(hProcess);
struct module* module;
struct hash_table_iter hti;
void* ptr;
struct symt_ht* sym = NULL;
TRACE("(%p, %s, %p)\n", hProcess, Name, Symbol);
if (!pcs) return FALSE;
if (Symbol->SizeOfStruct < sizeof(*Symbol)) return FALSE;
for (module = pcs->lmodules; module; module = module->next)
{
if (module->module.SymType != SymNone)
{
if (module->module.SymType == SymDeferred)
{
struct module* xmodule = module_get_debug(pcs, module);
if (!xmodule) continue;
module = xmodule;
}
hash_table_iter_init(&module->ht_symbols, &hti, Name);
while ((ptr = hash_table_iter_up(&hti)))
{
sym = GET_ENTRY(ptr, struct symt_ht, hash_elt);
if (!strcmp(sym->hash_elt.name, Name))
{
symt_fill_sym_info(module, &sym->symt, Symbol);
return TRUE;
}
}
}
}
return FALSE;
}
/***********************************************************************
* SymGetSymFromName (DBGHELP.@)
*/
BOOL WINAPI SymGetSymFromName(HANDLE hProcess, LPSTR Name, PIMAGEHLP_SYMBOL Symbol)
{
char buffer[sizeof(SYMBOL_INFO) + 256];
SYMBOL_INFO*si = (SYMBOL_INFO*)buffer;
size_t len;
if (Symbol->SizeOfStruct < sizeof(*Symbol)) return FALSE;
si->SizeOfStruct = sizeof(*si);
si->MaxNameLen = 256;
if (!SymFromName(hProcess, Name, si)) return FALSE;
Symbol->Address = si->Address;
Symbol->Size = si->Size;
Symbol->Flags = si->Flags;
len = min(Symbol->MaxNameLength, si->MaxNameLen);
strncpy(Symbol->Name, si->Name, len);
Symbol->Name[len - 1] = '\0';
return TRUE;
}
/******************************************************************
* fill_line_info
*
* fills information about a file
*/
static BOOL fill_line_info(struct module* module, struct symt_function* func,
DWORD addr, IMAGEHLP_LINE* line)
{
struct line_info* dli = NULL;
BOOL found = FALSE;
assert(func->symt.tag == SymTagFunction);
while ((dli = vector_iter_down(&func->vlines, dli)))
{
if (!(dli->cookie & DLIT_SOURCEFILE))
{
if (found || dli->u.pc_offset > addr) continue;
line->LineNumber = dli->line_number;
line->Address = dli->u.pc_offset;
line->Key = dli;
found = TRUE;
continue;
}
if (found)
{
line->FileName = (char*)source_get(module, dli->u.source_file);
return TRUE;
}
}
return FALSE;
}
/***********************************************************************
* SymGetSymNext (DBGHELP.@)
*/
BOOL WINAPI SymGetSymNext(HANDLE hProcess, PIMAGEHLP_SYMBOL Symbol)
{
/* algo:
* get module from Symbol.Address
* get index in module.addr_sorttab of Symbol.Address
* increment index
* if out of module bounds, move to next module in process address space
*/
FIXME("(%p, %p): stub\n", hProcess, Symbol);
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return FALSE;
}
/***********************************************************************
* SymGetSymPrev (DBGHELP.@)
*/
BOOL WINAPI SymGetSymPrev(HANDLE hProcess, PIMAGEHLP_SYMBOL Symbol)
{
FIXME("(%p, %p): stub\n", hProcess, Symbol);
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return FALSE;
}
/******************************************************************
* SymGetLineFromAddr (DBGHELP.@)
*
*/
BOOL WINAPI SymGetLineFromAddr(HANDLE hProcess, DWORD dwAddr,
PDWORD pdwDisplacement, PIMAGEHLP_LINE Line)
{
struct process* pcs = process_find_by_handle(hProcess);
struct module* module;
int idx;
TRACE("%p %08lx %p %p\n", hProcess, dwAddr, pdwDisplacement, Line);
if (Line->SizeOfStruct < sizeof(*Line)) return FALSE;
if (!pcs) return FALSE;
module = module_find_by_addr(pcs, dwAddr, DMT_UNKNOWN);
if (!(module = module_get_debug(pcs, module))) return FALSE;
if ((idx = symt_find_nearest(module, dwAddr)) == -1) return FALSE;
if (module->addr_sorttab[idx]->symt.tag != SymTagFunction) return FALSE;
if (!fill_line_info(module,
(struct symt_function*)module->addr_sorttab[idx],
dwAddr, Line)) return FALSE;
if (pdwDisplacement) *pdwDisplacement = dwAddr - Line->Address;
return TRUE;
}
/******************************************************************
* SymGetLinePrev (DBGHELP.@)
*
*/
BOOL WINAPI SymGetLinePrev(HANDLE hProcess, PIMAGEHLP_LINE Line)
{
struct process* pcs = process_find_by_handle(hProcess);
struct module* module;
struct line_info* li;
BOOL in_search = FALSE;
TRACE("(%p %p)\n", hProcess, Line);
if (Line->SizeOfStruct < sizeof(*Line)) return FALSE;
if (!pcs) return FALSE;
module = module_find_by_addr(pcs, Line->Address, DMT_UNKNOWN);
if (!(module = module_get_debug(pcs, module))) return FALSE;
if (Line->Key == 0) return FALSE;
li = (struct line_info*)Line->Key;
/* things are a bit complicated because when we encounter a DLIT_SOURCEFILE
* element we have to go back until we find the prev one to get the real
* source file name for the DLIT_OFFSET element just before
* the first DLIT_SOURCEFILE
*/
while (!(li->cookie & DLIT_FIRST))
{
li--;
if (!(li->cookie & DLIT_SOURCEFILE))
{
Line->LineNumber = li->line_number;
Line->Address = li->u.pc_offset;
Line->Key = li;
if (!in_search) return TRUE;
}
else
{
if (in_search)
{
Line->FileName = (char*)source_get(module, li->u.source_file);
return TRUE;
}
in_search = TRUE;
}
}
SetLastError(ERROR_NO_MORE_ITEMS); /* FIXME */
return FALSE;
}
/******************************************************************
* SymGetLineNext (DBGHELP.@)
*
*/
BOOL WINAPI SymGetLineNext(HANDLE hProcess, PIMAGEHLP_LINE Line)
{
struct process* pcs = process_find_by_handle(hProcess);
struct module* module;
struct line_info* li;
TRACE("(%p %p)\n", hProcess, Line);
if (Line->SizeOfStruct < sizeof(*Line)) return FALSE;
if (!pcs) return FALSE;
module = module_find_by_addr(pcs, Line->Address, DMT_UNKNOWN);
if (!(module = module_get_debug(pcs, module))) return FALSE;
if (Line->Key == 0) return FALSE;
li = (struct line_info*)Line->Key;
while (!(li->cookie & DLIT_LAST))
{
li++;
if (!(li->cookie & DLIT_SOURCEFILE))
{
Line->LineNumber = li->line_number;
Line->Address = li->u.pc_offset;
Line->Key = li;
return TRUE;
}
Line->FileName = (char*)source_get(module, li->u.source_file);
}
SetLastError(ERROR_NO_MORE_ITEMS); /* FIXME */
return FALSE;
}
/***********************************************************************
* SymFunctionTableAccess (DBGHELP.@)
*/
PVOID WINAPI SymFunctionTableAccess(HANDLE hProcess, DWORD AddrBase)
{
FIXME("(%p, 0x%08lx): stub\n", hProcess, AddrBase);
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return FALSE;
}
/***********************************************************************
* SymUnDName (DBGHELP.@)
*/
BOOL WINAPI SymUnDName(PIMAGEHLP_SYMBOL sym, LPSTR UnDecName, DWORD UnDecNameLength)
{
FIXME("(%p %s %lu): stub\n", sym, UnDecName, UnDecNameLength);
return UnDecorateSymbolName(sym->Name, UnDecName, UnDecNameLength,
UNDNAME_COMPLETE);
}
/***********************************************************************
* UnDecorateSymbolName (DBGHELP.@)
*/
DWORD WINAPI UnDecorateSymbolName(LPCSTR DecoratedName, LPSTR UnDecoratedName,
DWORD UndecoratedLength, DWORD Flags)
{
FIXME("(%s, %p, %ld, 0x%08lx): stub\n",
debugstr_a(DecoratedName), UnDecoratedName, UndecoratedLength, Flags);
strncpy(UnDecoratedName, DecoratedName, UndecoratedLength);
UnDecoratedName[UndecoratedLength - 1] = '\0';
return TRUE;
}
/*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 "winreg.h"
#include "winnls.h"
#include "wine/debug.h"
#include "dbghelp_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);
WINE_DECLARE_DEBUG_CHANNEL(dbghelp_symtype);
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 ((struct symt_data*)sym)->hash_elt.name;
case SymTagFunction: return ((struct symt_function*)sym)->hash_elt.name;
case SymTagPublicSymbol: return ((struct symt_public*)sym)->hash_elt.name;
case SymTagBaseType: return ((struct symt_basic*)sym)->hash_elt.name;
/* hierarchy tree */
case SymTagEnum: return ((struct symt_enum*)sym)->name;
case SymTagTypedef: return ((struct symt_typedef*)sym)->hash_elt.name;
case SymTagUDT: return ((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(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;
}
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;
}
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_symtype)("Adding udt %s:%s\n", 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);
}
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_symtype)("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_symtype)("Adding %s to UDT %s\n", name, udt_type->hash_elt.name);
p = NULL;
while ((p = vector_iter_up(&udt_type->vchildren, p)))
{
m = (struct symt_data*)*p;
assert(m);
assert(m->symt.tag == SymTagData);
if (m->hash_elt.name[0] == name[0] && 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 = pool_strdup(&module->pool, name);
m->hash_elt.next = NULL;
m->kind = DataIsMember;
m->container = &udt_type->symt;
m->type = elt_type;
if (!(offset & 7) && !(size & 7))
{
m->location = LocIsThisRel;
m->u.offset = offset >> 3;
/* we could check that elt_type's size is actually size */
}
else
{
m->location = LocIsBitField;
m->u.bitfield.position = offset;
m->u.bitfield.length = size;
}
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 = pool_strdup(&module->pool, typename);
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, unsigned 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->location = LocIsConstant;
e->u.value = value; /* FIXME: use variant */
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_array* sym;
if ((sym = pool_alloc(&module->pool, sizeof(*sym))))
{
sym->symt.tag = SymTagArrayType;
sym->start = min;
sym->end = max;
sym->basetype = base;
}
return sym;
}
struct symt_function_signature* symt_new_function_signature(struct module* module,
struct symt* ret_type)
{
struct symt_function_signature* sym;
if ((sym = pool_alloc(&module->pool, sizeof(*sym))))
{
sym->symt.tag = SymTagFunctionType;
sym->rettype = ret_type;
}
return sym;
}
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;
}
return sym;
}
/******************************************************************
* SymEnumTypes (DBGHELP.@)
*
*/
BOOL WINAPI SymEnumTypes(HANDLE hProcess, unsigned long BaseOfDll,
PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback,
void* UserContext)
{
struct process* pcs;
struct module* module;
struct symt_ht* type;
void* ptr;
char buffer[sizeof(SYMBOL_INFO) + 256];
SYMBOL_INFO* sym_info = (SYMBOL_INFO*)buffer;
struct hash_table_iter hti;
const char* tmp;
TRACE("(%p %08lx %p %p)\n",
hProcess, BaseOfDll, EnumSymbolsCallback, UserContext);
pcs = process_find_by_handle(hProcess);
if (!pcs) return FALSE;
module = module_find_by_addr(pcs, BaseOfDll, DMT_UNKNOWN);
if (!(module = module_get_debug(pcs, module))) return FALSE;
sym_info->SizeOfStruct = sizeof(SYMBOL_INFO);
sym_info->MaxNameLen = sizeof(buffer) - sizeof(SYMBOL_INFO);
hash_table_iter_init(&module->ht_types, &hti, NULL);
while ((ptr = hash_table_iter_up(&hti)))
{
type = GET_ENTRY(ptr, struct symt_ht, hash_elt);
sym_info->TypeIndex = (DWORD)type;
sym_info->info = 0; /* FIXME */
symt_get_info(&type->symt, TI_GET_LENGTH, &sym_info->Size);
sym_info->ModBase = module->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->symt.tag;
tmp = symt_get_name(&type->symt);
sym_info->NameLen = strlen(tmp) + 1;
strncpy(sym_info->Name, tmp, min(sym_info->NameLen, sym_info->MaxNameLen));
sym_info->Name[sym_info->MaxNameLen - 1] = '\0';
if (!EnumSymbolsCallback(sym_info, sym_info->Size, UserContext)) break;
}
return TRUE;
}
/******************************************************************
* symt_get_info
*
* Retrieves inforamtion 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 = &((struct symt_udt*)type)->vchildren; break;
case SymTagEnum: v = &((struct symt_enum*)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 (((struct symt_data*)type)->kind)
{
case DataIsGlobal:
case DataIsFileStatic:
X(DWORD) = ((struct symt_data*)type)->u.address;
break;
default: return FALSE;
}
break;
case SymTagFunction:
X(DWORD) = ((struct symt_function*)type)->addr;
break;
case SymTagPublicSymbol:
X(DWORD) = ((struct symt_public*)type)->address;
break;
default:
FIXME("Unsupported sym-tag %s for get-address\n", symt_get_tag_str(type->tag));
return FALSE;
}
break;
#if 0
/* this is wrong, we should return the type of the index, not the
* type of the array[0]
*/
case TI_GET_ARRAYINDEXTYPEID:
if (type->tag != SymTagArrayType) return FALSE;
X(DWORD) = (DWORD)((struct symt_array*)type)->basetype;
break;
#endif
case TI_GET_BASETYPE:
if (type->tag != SymTagBaseType) return FALSE;
X(DWORD) = ((struct symt_basic*)type)->bt;
break;
case TI_GET_BITPOSITION:
if (type->tag != SymTagData || ((struct symt_data*)type)->location != LocIsBitField)
return FALSE;
X(DWORD) = ((struct symt_data*)type)->u.bitfield.position;
break;
case TI_GET_CHILDRENCOUNT:
switch (type->tag)
{
case SymTagUDT:
X(DWORD) = vector_length(&((struct symt_udt*)type)->vchildren);
break;
case SymTagEnum:
X(DWORD) = vector_length(&((struct symt_enum*)type)->vchildren);
break;
default:
FIXME("Unsupported sym-tag %s for get-children-count\n", symt_get_tag_str(type->tag));
/* fall through */
case SymTagPublicSymbol:
case SymTagPointerType:
case SymTagBaseType:
return FALSE;
}
break;
case TI_GET_COUNT:
if (type->tag != SymTagArrayType) return FALSE;
X(DWORD) = ((struct symt_array*)type)->end -
((struct symt_array*)type)->start;
break;
case TI_GET_DATAKIND:
if (type->tag != SymTagData) return FALSE;
X(DWORD) = ((struct symt_data*)type)->kind;
break;
case TI_GET_LENGTH:
switch (type->tag)
{
case SymTagBaseType:
X(DWORD) = ((struct symt_basic*)type)->size;
break;
case SymTagFunction:
X(DWORD) = ((struct symt_function*)type)->size;
break;
case SymTagPointerType:
X(DWORD) = sizeof(void*);
break;
case SymTagUDT:
X(DWORD) = ((struct symt_udt*)type)->size;
break;
case SymTagEnum:
X(DWORD) = sizeof(int);
break;
case SymTagData:
if (((struct symt_data*)type)->location == LocIsBitField)
X(DWORD) = ((struct symt_data*)type)->u.bitfield.length;
else
return symt_get_info(((struct symt_data*)type)->type,
TI_GET_LENGTH, pInfo);
break;
case SymTagArrayType:
if (!symt_get_info(((struct symt_array*)type)->basetype,
TI_GET_LENGTH, pInfo))
return FALSE;
X(DWORD) *= ((struct symt_array*)type)->end -
((struct symt_array*)type)->start;
break;
case SymTagPublicSymbol:
X(DWORD) = ((struct symt_public*)type)->size;
break;
default:
FIXME("Unsupported sym-tag %s for get-size\n", symt_get_tag_str(type->tag));
return 0;
}
break;
case TI_GET_LEXICALPARENT:
switch (type->tag)
{
case SymTagBlock:
X(DWORD) = (DWORD)((struct symt_block*)type)->container;
break;
case SymTagData:
X(DWORD) = (DWORD)((struct symt_data*)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_OFFSET:
switch (type->tag)
{
case SymTagData:
switch (((struct symt_data*)type)->location)
{
case LocIsRegRel:
case LocIsThisRel:
X(ULONG) = ((struct symt_data*)type)->u.offset;
break;
case LocIsConstant:
X(ULONG) = 0; /* FIXME ???? */
break;
default:
FIXME("Unknown location (%u) for get-offset\n",
((struct symt_data*)type)->location);
break;
}
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:
switch (type->tag)
{
case SymTagArrayType:
X(DWORD) = (DWORD)((struct symt_array*)type)->basetype;
break;
case SymTagPointerType:
X(DWORD) = (DWORD)((struct symt_pointer*)type)->pointsto;
break;
case SymTagFunctionType:
X(DWORD) = (DWORD)((struct symt_function_signature*)type)->rettype;
break;
case SymTagData:
X(DWORD) = (DWORD)((struct symt_data*)type)->type;
break;
default:
FIXME("Unsupported sym-tag %s for get-type\n", symt_get_tag_str(type->tag));
return FALSE;
}
break;
case TI_GET_TYPEID:
X(DWORD) = (DWORD)type;
break;
case TI_GET_UDTKIND:
if (type->tag != SymTagUDT) return FALSE;
X(DWORD) = ((struct symt_udt*)type)->kind;
break;
#undef X
case TI_GET_ADDRESSOFFSET:
case TI_GET_ARRAYINDEXTYPEID:
case TI_GET_CALLING_CONVENTION:
case TI_GET_CLASSPARENTID:
case TI_GET_NESTED:
case TI_GET_SYMINDEX:
case TI_GET_THISADJUST:
case TI_GET_VALUE:
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;
}
return TRUE;
}
/******************************************************************
* SymGetTypeInfo (DBGHELP.@)
*
*/
BOOL WINAPI SymGetTypeInfo(HANDLE hProcess, unsigned long ModBase,
ULONG TypeId, IMAGEHLP_SYMBOL_TYPE_INFO GetType,
PVOID pInfo)
{
struct process* pcs = process_find_by_handle(hProcess);
if (!pcs) return FALSE;
#if 0
struct module* module;
module = module_find_by_addr(pcs, ModBase, DMT_UNKNOWN);
if (!(module = module_get_debug(pcs, module))) return FALSE;
#endif
return symt_get_info((struct symt*)TypeId, GetType, pInfo);
}
/******************************************************************
* SymGetTypeFromName (DBGHELP.@)
*
*/
BOOL WINAPI SymGetTypeFromName(HANDLE hProcess, unsigned long BaseOfDll,
LPSTR 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;
}
......@@ -44,6 +44,7 @@ WINDOWS_INCLUDES = \
control.h \
cpl.h \
custcntl.h \
cvconst.h \
d3d.h \
d3d8.h \
d3d8caps.h \
......
/*
* File cvconst.h - MS debug information
*
* Copyright (C) 2004, Eric Pouech
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* information in this file is highly derivated from MSDN DIA information pages */
/* symbols & types enumeration */
enum SymTagEnum
{
SymTagNull,
SymTagExe,
SymTagCompiland,
SymTagCompilandDetails,
SymTagCompilandEnv,
SymTagFunction,
SymTagBlock,
SymTagData,
SymTagAnnotation,
SymTagLabel,
SymTagPublicSymbol,
SymTagUDT,
SymTagEnum,
SymTagFunctionType,
SymTagPointerType,
SymTagArrayType,
SymTagBaseType,
SymTagTypedef,
SymTagBaseClass,
SymTagFriend,
SymTagFunctionArgType,
SymTagFuncDebugStart,
SymTagFuncDebugEnd,
SymTagUsingNamespace,
SymTagVTableShape,
SymTagVTable,
SymTagCustom,
SymTagThunk,
SymTagCustomType,
SymTagManagedType,
SymTagDimension,
SymTagMax
};
enum BasicType
{
btNoType = 0,
btVoid = 1,
btChar = 2,
btWChar = 3,
btInt = 6,
btUInt = 7,
btFloat = 8,
btBCD = 9,
btBool = 10,
btLong = 13,
btULong = 14,
btCurrency = 25,
btDate = 26,
btVariant = 27,
btComplex = 28,
btBit = 29,
btBSTR = 30,
btHresult = 31,
};
/* kind of UDT */
enum UdtKind
{
UdtStruct,
UdtClass,
UdtUnion
};
/* where a SymTagData is */
enum LocationType
{
LocIsNull,
LocIsStatic,
LocIsTLS,
LocIsRegRel,
LocIsThisRel,
LocIsEnregistered,
LocIsBitField,
LocIsSlot,
LocIsIlRel,
LocInMetaData,
LocIsConstant
};
/* kind of SymTagData */
enum DataKind
{
DataIsUnknown,
DataIsLocal,
DataIsStaticLocal,
DataIsParam,
DataIsObjectPtr,
DataIsFileStatic,
DataIsGlobal,
DataIsMember,
DataIsStaticMember,
DataIsConstant
};
/* values for registers (on different CPUs) */
enum CV_HREG_e
{
/* those values are common to all supported CPUs (and CPU independant) */
CV_ALLREG_ERR = 30000,
CV_ALLREG_TEB = 30001,
CV_ALLREG_TIMER = 30002,
CV_ALLREG_EFAD1 = 30003,
CV_ALLREG_EFAD2 = 30004,
CV_ALLREG_EFAD3 = 30005,
CV_ALLREG_VFRAME = 30006,
CV_ALLREG_HANDLE = 30007,
CV_ALLREG_PARAMS = 30008,
CV_ALLREG_LOCALS = 30009,
/* Intel x86 CPU */
CV_REG_NONE = 0,
CV_REG_AL = 1,
CV_REG_CL = 2,
CV_REG_DL = 3,
CV_REG_BL = 4,
CV_REG_AH = 5,
CV_REG_CH = 6,
CV_REG_DH = 7,
CV_REG_BH = 8,
CV_REG_AX = 9,
CV_REG_CX = 10,
CV_REG_DX = 11,
CV_REG_BX = 12,
CV_REG_SP = 13,
CV_REG_BP = 14,
CV_REG_SI = 15,
CV_REG_DI = 16,
CV_REG_EAX = 17,
CV_REG_ECX = 18,
CV_REG_EDX = 19,
CV_REG_EBX = 20,
CV_REG_ESP = 21,
CV_REG_EBP = 22,
CV_REG_ESI = 23,
CV_REG_EDI = 24,
CV_REG_ES = 25,
CV_REG_CS = 26,
CV_REG_SS = 27,
CV_REG_DS = 28,
CV_REG_FS = 29,
CV_REG_GS = 30,
CV_REG_IP = 31,
CV_REG_FLAGS = 32,
CV_REG_EIP = 33,
CV_REG_EFLAGS = 34,
/* <pcode> */
CV_REG_TEMP = 40,
CV_REG_TEMPH = 41,
CV_REG_QUOTE = 42,
CV_REG_PCDR3 = 43, /* this includes PCDR4 to PCDR7 */
CV_REG_CR0 = 80, /* this includes CR1 to CR4 */
CV_REG_DR0 = 90, /* this includes DR1 to DR7 */
/* </pcode> */
CV_REG_GDTR = 110,
CV_REG_GDTL = 111,
CV_REG_IDTR = 112,
CV_REG_IDTL = 113,
CV_REG_LDTR = 114,
CV_REG_TR = 115,
CV_REG_PSEUDO1 = 116, /* this includes Pseudo02 to Pseuso09 */
CV_REG_ST0 = 128, /* this includes ST1 to ST7 */
CV_REG_CTRL = 136,
CV_REG_STAT = 137,
CV_REG_TAG = 138,
CV_REG_FPIP = 139,
CV_REG_FPCS = 140,
CV_REG_FPDO = 141,
CV_REG_FPDS = 142,
CV_REG_ISEM = 143,
CV_REG_FPEIP = 144,
CV_REG_FPEDO = 145,
CV_REG_MM0 = 146, /* this includes MM1 to MM7 */
CV_REG_XMM0 = 154, /* this includes XMM1 to XMM7 */
CV_REG_XMM00 = 162,
CV_REG_XMM0L = 194, /* this includes XMM1L to XMM7L */
CV_REG_XMM0H = 202, /* this includes XMM1H to XMM7H */
CV_REG_MXCSR = 211,
CV_REG_EDXEAX = 212,
CV_REG_EMM0L = 220,
CV_REG_EMM0H = 228,
CV_REG_MM00 = 236,
CV_REG_MM01 = 237,
CV_REG_MM10 = 238,
CV_REG_MM11 = 239,
CV_REG_MM20 = 240,
CV_REG_MM21 = 241,
CV_REG_MM30 = 242,
CV_REG_MM31 = 243,
CV_REG_MM40 = 244,
CV_REG_MM41 = 245,
CV_REG_MM50 = 246,
CV_REG_MM51 = 247,
CV_REG_MM60 = 248,
CV_REG_MM61 = 249,
CV_REG_MM70 = 250,
CV_REG_MM71 = 251,
/* Motorola 68K CPU */
CV_R68_D0 = 0, /* this includes D1 to D7 too */
CV_R68_A0 = 8, /* this includes A1 to A7 too */
CV_R68_CCR = 16,
CV_R68_SR = 17,
CV_R68_USP = 18,
CV_R68_MSP = 19,
CV_R68_SFC = 20,
CV_R68_DFC = 21,
CV_R68_CACR = 22,
CV_R68_VBR = 23,
CV_R68_CAAR = 24,
CV_R68_ISP = 25,
CV_R68_PC = 26,
CV_R68_FPCR = 28,
CV_R68_FPSR = 29,
CV_R68_FPIAR = 30,
CV_R68_FP0 = 32, /* this includes FP1 to FP7 */
CV_R68_MMUSR030 = 41,
CV_R68_MMUSR = 42,
CV_R68_URP = 43,
CV_R68_DTT0 = 44,
CV_R68_DTT1 = 45,
CV_R68_ITT0 = 46,
CV_R68_ITT1 = 47,
CV_R68_PSR = 51,
CV_R68_PCSR = 52,
CV_R68_VAL = 53,
CV_R68_CRP = 54,
CV_R68_SRP = 55,
CV_R68_DRP = 56,
CV_R68_TC = 57,
CV_R68_AC = 58,
CV_R68_SCC = 59,
CV_R68_CAL = 60,
CV_R68_TT0 = 61,
CV_R68_TT1 = 62,
CV_R68_BAD0 = 64, /* this includes BAD1 to BAD7 */
CV_R68_BAC0 = 72, /* this includes BAC1 to BAC7 */
/* MIPS 4000 CPU */
CV_M4_NOREG = CV_REG_NONE,
CV_M4_IntZERO = 10,
CV_M4_IntAT = 11,
CV_M4_IntV0 = 12,
CV_M4_IntV1 = 13,
CV_M4_IntA0 = 14, /* this includes IntA1 to IntA3 */
CV_M4_IntT0 = 18, /* this includes IntT1 to IntT7 */
CV_M4_IntS0 = 26, /* this includes IntS1 to IntS7 */
CV_M4_IntT8 = 34,
CV_M4_IntT9 = 35,
CV_M4_IntKT0 = 36,
CV_M4_IntKT1 = 37,
CV_M4_IntGP = 38,
CV_M4_IntSP = 39,
CV_M4_IntS8 = 40,
CV_M4_IntRA = 41,
CV_M4_IntLO = 42,
CV_M4_IntHI = 43,
CV_M4_Fir = 50,
CV_M4_Psr = 51,
CV_M4_FltF0 = 60, /* this includes FltF1 to Flt31 */
CV_M4_FltFsr = 92,
/* Alpha AXP CPU */
CV_ALPHA_NOREG = CV_REG_NONE,
CV_ALPHA_FltF0 = 10, /* this includes FltF1 to FltF31 */
CV_ALPHA_IntV0 = 42,
CV_ALPHA_IntT0 = 43, /* this includes T1 to T7 */
CV_ALPHA_IntS0 = 51, /* this includes S1 to S5 */
CV_ALPHA_IntFP = 57,
CV_ALPHA_IntA0 = 58, /* this includes A1 to A5 */
CV_ALPHA_IntT8 = 64,
CV_ALPHA_IntT9 = 65,
CV_ALPHA_IntT10 = 66,
CV_ALPHA_IntT11 = 67,
CV_ALPHA_IntRA = 68,
CV_ALPHA_IntT12 = 69,
CV_ALPHA_IntAT = 70,
CV_ALPHA_IntGP = 71,
CV_ALPHA_IntSP = 72,
CV_ALPHA_IntZERO = 73,
CV_ALPHA_Fpcr = 74,
CV_ALPHA_Fir = 75,
CV_ALPHA_Psr = 76,
CV_ALPHA_FltFsr = 77,
CV_ALPHA_SoftFpcr = 78,
/* Motorola & IBM PowerPC CPU */
CV_PPC_GPR0 = 1, /* this includes GPR1 to GPR31 */
CV_PPC_CR = 33,
CV_PPC_CR0 = 34, /* this includes CR1 to CR7 */
CV_PPC_FPR0 = 42, /* this includes FPR1 to FPR31 */
CV_PPC_FPSCR = 74,
CV_PPC_MSR = 75,
CV_PPC_SR0 = 76, /* this includes SR1 to SR15 */
/* some PPC registers missing */
/* Hitachi SH3 CPU */
CV_SH3_NOREG = CV_REG_NONE,
CV_SH3_IntR0 = 10, /* this include R1 to R13 */
CV_SH3_IntFp = 24,
CV_SH3_IntSp = 25,
CV_SH3_Gbr = 38,
CV_SH3_Pr = 39,
CV_SH3_Mach = 40,
CV_SH3_Macl = 41,
CV_SH3_Pc = 50,
CV_SH3_Sr = 51,
CV_SH3_BarA = 60,
CV_SH3_BasrA = 61,
CV_SH3_BamrA = 62,
CV_SH3_BbrA = 63,
CV_SH3_BarB = 64,
CV_SH3_BasrB = 65,
CV_SH3_BamrB = 66,
CV_SH3_BbrB = 67,
CV_SH3_BdrB = 68,
CV_SH3_BdmrB = 69,
CV_SH3_Brcr = 70,
CV_SH_Fpscr = 75,
CV_SH_Fpul = 76,
CV_SH_FpR0 = 80, /* this includes FpR1 to FpR15 */
CV_SH_XFpR0 = 96, /* this includes XFpR1 to XXFpR15 */
/* ARM CPU */
CV_ARM_NOREG = CV_REG_NONE,
CV_ARM_R0 = 10, /* this includes R1 to R12 */
CV_ARM_SP = 23,
CV_ARM_LR = 24,
CV_ARM_PC = 25,
CV_ARM_CPSR = 26,
/* Intel IA64 CPU */
CV_IA64_NOREG = CV_REG_NONE,
CV_IA64_Br0 = 512, /* this includes Br1 to Br7 */
CV_IA64_P0 = 704, /* this includes P1 to P63 */
CV_IA64_Preds = 768,
CV_IA64_IntH0 = 832, /* this includes H1 to H15 */
CV_IA64_Ip = 1016,
CV_IA64_Umask = 1017,
CV_IA64_Cfm = 1018,
CV_IA64_Psr = 1019,
CV_IA64_Nats = 1020,
CV_IA64_Nats2 = 1021,
CV_IA64_Nats3 = 1022,
CV_IA64_IntR0 = 1024, /* this includes R1 to R127 */
CV_IA64_FltF0 = 2048, /* this includes FltF1 to FltF127 */
/* some IA64 registers missing */
/* TriCore CPU */
CV_TRI_NOREG = CV_REG_NONE,
CV_TRI_D0 = 10, /* includes D1 to D15 */
CV_TRI_A0 = 26, /* includes A1 to A15 */
CV_TRI_E0 = 42,
CV_TRI_E2 = 43,
CV_TRI_E4 = 44,
CV_TRI_E6 = 45,
CV_TRI_E8 = 46,
CV_TRI_E10 = 47,
CV_TRI_E12 = 48,
CV_TRI_E14 = 49,
CV_TRI_EA0 = 50,
CV_TRI_EA2 = 51,
CV_TRI_EA4 = 52,
CV_TRI_EA6 = 53,
CV_TRI_EA8 = 54,
CV_TRI_EA10 = 55,
CV_TRI_EA12 = 56,
CV_TRI_EA14 = 57,
/* some TriCode registers missing */
/* AM33 (and the likes) CPU */
CV_AM33_NOREG = CV_REG_NONE,
CV_AM33_E0 = 10, /* this includes E1 to E7 */
CV_AM33_A0 = 20, /* this includes A1 to A3 */
CV_AM33_D0 = 30, /* this includes D1 to D3 */
CV_AM33_FS0 = 40, /* this includes FS1 to FS31 */
/* Mitsubishi M32R CPU */
CV_M32R_NOREG = CV_REG_NONE,
CV_M32R_R0 = 10, /* this includes R1 to R11 */
CV_M32R_R12 = 22,
CV_M32R_R13 = 23,
CV_M32R_R14 = 24,
CV_M32R_R15 = 25,
CV_M32R_PSW = 26,
CV_M32R_CBR = 27,
CV_M32R_SPI = 28,
CV_M32R_SPU = 29,
CV_M32R_SPO = 30,
CV_M32R_BPC = 31,
CV_M32R_ACHI = 32,
CV_M32R_ACLO = 33,
CV_M32R_PC = 34,
} CV_HREG_e;
......@@ -205,6 +205,12 @@ typedef struct _IMAGEHLP_DUPLICATE_SYMBOL
#define SYMOPT_WILD_UNDERSCORE 0x00000800
#define SYMOPT_USE_DEFAULTS 0x00001000
#define SYMOPT_INCLUDE_32BIT_MODULES 0x00002000
#define SYMOPT_PUBLICS_ONLY 0x00004000
#define SYMOPT_NO_PUBLICS 0x00008000
#define SYMOPT_AUTO_PUBLICS 0x00010000
#define SYMOPT_NO_IMAGE_SEARCH 0x00020000
#define SYMOPT_SECURE 0x00040000
#define SYMOPT_NO_PROMPTS 0x00080000
#define SYMOPT_DEBUG 0x80000000
......@@ -235,7 +241,7 @@ typedef struct _DBGHELP_MODLOAD_DATA
} MODLOAD_DATA, *PMODLOAD_DATA;
/*************************
* now DBGHELP *
* MiniDUMP *
*************************/
/* DebugHelp */
......@@ -523,7 +529,6 @@ typedef struct _MINIDUMP_THREAD_LIST
MINIDUMP_THREAD Threads[1]; /* FIXME: no support of 0 sized array */
} MINIDUMP_THREAD_LIST, *PMINIDUMP_THREAD_LIST;
/*************************
* MODULE handling *
*************************/
......@@ -535,7 +540,7 @@ extern BOOL WINAPI EnumerateLoadedModules(HANDLE hProcess,
typedef BOOL (CALLBACK *PSYM_ENUMMODULES_CALLBACK)(PSTR ModuleName, DWORD BaseOfDll,
PVOID UserContext);
extern BOOL WINAPI SymEnumerateModules(HANDLE hProcess,
PSYM_ENUMMODULES_CALLBACK EnumModulesCallback,
PSYM_ENUMMODULES_CALLBACK EnumModulesCallback,
PVOID UserContext);
extern BOOL WINAPI SymGetModuleInfo(HANDLE hProcess, DWORD dwAddr,
PIMAGEHLP_MODULE ModuleInfo);
......@@ -562,12 +567,26 @@ extern BOOL WINAPI SymUnloadModule(HANDLE hProcess, DWORD BaseOfDll);
#define IMAGEHLP_SYMBOL_INFO_CONSTANT SYMF_CONSTANT /* 0x100 */
#define IMAGEHLP_SYMBOL_FUNCTION SYMF_FUNCTION /* 0x800 */
#define SYMFLAG_VALUEPRESENT 0x00000001
#define SYMFLAG_REGISTER 0x00000008
#define SYMFLAG_REGREL 0x00000010
#define SYMFLAG_FRAMEREL 0x00000020
#define SYMFLAG_PARAMETER 0x00000040
#define SYMFLAG_LOCAL 0x00000080
#define SYMFLAG_CONSTANT 0x00000100
#define SYMFLAG_EXPORT 0x00000200
#define SYMFLAG_FORWARDER 0x00000400
#define SYMFLAG_FUNCTION 0x00000800
#define SYMFLAG_VIRTUAL 0x00001000
#define SYMFLAG_THUNK 0x00002000
#define SYMFLAG_TLSREL 0x00004000
typedef struct _SYMBOL_INFO
{
ULONG SizeOfStruct;
ULONG TypeIndex;
ULONG Reserved[2];
ULONG Reserved2;
ULONG info;
ULONG Size;
ULONG ModBase;
ULONG Flags;
......@@ -607,6 +626,9 @@ typedef enum _IMAGEHLP_SYMBOL_TYPE_INFO
TI_GET_LEXICALPARENT,
TI_GET_ADDRESS,
TI_GET_THISADJUST,
TI_GET_UDTKIND,
TI_IS_EQUIV_TO,
TI_GET_CALLING_CONVENTION,
} IMAGEHLP_SYMBOL_TYPE_INFO;
typedef struct _TI_FINDCHILDREN_PARAMS
......@@ -616,6 +638,24 @@ typedef struct _TI_FINDCHILDREN_PARAMS
ULONG ChildId[1];
} TI_FINDCHILDREN_PARAMS;
#define UNDNAME_COMPLETE (0x0000)
#define UNDNAME_NO_LEADING_UNDERSCORES (0x0001)
#define UNDNAME_NO_MS_KEYWORDS (0x0002)
#define UNDNAME_NO_FUNCTION_RETURNS (0x0004)
#define UNDNAME_NO_ALLOCATION_MODEL (0x0008)
#define UNDNAME_NO_ALLOCATION_LANGUAGE (0x0010)
#define UNDNAME_NO_MS_THISTYPE (0x0020)
#define UNDNAME_NO_CV_THISTYPE (0x0040)
#define UNDNAME_NO_THISTYPE (0x0060)
#define UNDNAME_NO_ACCESS_SPECIFIERS (0x0080)
#define UNDNAME_NO_THROW_SIGNATURES (0x0100)
#define UNDNAME_NO_MEMBER_TYPE (0x0200)
#define UNDNAME_NO_RETURN_UDT_MODEL (0x0400)
#define UNDNAME_32_BIT_DECODE (0x0800)
#define UNDNAME_NAME_ONLY (0x1000)
#define UNDNAME_NO_ARGUMENTS (0x2000)
#define UNDNAME_NO_SPECIAL_SYMS (0x4000)
BOOL WINAPI SymGetTypeInfo(HANDLE hProcess, DWORD ModBase, ULONG TypeId,
IMAGEHLP_SYMBOL_TYPE_INFO GetType, PVOID);
typedef BOOL (CALLBACK *PSYM_ENUMERATESYMBOLS_CALLBACK)(PSYMBOL_INFO pSymInfo,
......@@ -623,11 +663,26 @@ typedef BOOL (CALLBACK *PSYM_ENUMERATESYMBOLS_CALLBACK)(PSYMBOL_INFO pSymInfo,
BOOL WINAPI SymEnumTypes(HANDLE hProcess, DWORD BaseOfDll,
PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback,
PVOID UserContext);
BOOL WINAPI SymFromAddr(HANDLE hProcess, DWORD addr, DWORD* displacement,
SYMBOL_INFO* sym_info);
BOOL WINAPI SymFromName(HANDLE hProcess, LPSTR Name, PSYMBOL_INFO Symbol);
BOOL WINAPI SymGetTypeFromName(HANDLE hProcess, DWORD BaseOfDll, LPSTR Name,
PSYMBOL_INFO Symbol);
BOOL WINAPI SymEnumSymbols(HANDLE hProcess, ULONG BaseOfDll, PCSTR Mask,
PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback,
PVOID UserContext);
typedef BOOL (CALLBACK *PSYM_ENUMSYMBOLS_CALLBACK)(PSTR SymbolName, DWORD SymbolAddress,
ULONG SymbolSize, PVOID UserContext);
BOOL WINAPI SymEnumerateSymbols(HANDLE hProcess, DWORD BaseOfDll,
PSYM_ENUMSYMBOLS_CALLBACK EnumSymbolsCallback,
PVOID UserContext);
typedef BOOL (CALLBACK *PSYMBOL_REGISTERED_CALLBACK)(HANDLE hProcess, ULONG ActionCode,
PVOID CallbackData, PVOID UserContext);
BOOL WINAPI SymRegisterCallback(HANDLE hProcess,
PSYMBOL_REGISTERED_CALLBACK CallbackFunction,
PVOID UserContext);
DWORD WINAPI UnDecorateSymbolName(LPCSTR DecoratedName, LPSTR UnDecoratedName,
DWORD UndecoratedLength, DWORD Flags);
/*************************
* Source Files *
......@@ -638,6 +693,10 @@ typedef BOOL (CALLBACK *PSYM_ENUMSOURCFILES_CALLBACK)(PSOURCEFILE pSourceFile,
BOOL WINAPI SymEnumSourceFiles(HANDLE hProcess, ULONG ModBase, LPSTR Mask,
PSYM_ENUMSOURCFILES_CALLBACK cbSrcFiles,
PVOID UserContext);
BOOL WINAPI SymGetLineFromAddr(HANDLE hProcess, DWORD dwAddr,
PDWORD pdwDisplacement, PIMAGEHLP_LINE Line);
BOOL WINAPI SymGetLinePrev(HANDLE hProcess, PIMAGEHLP_LINE Line);
BOOL WINAPI SymGetLineNext(HANDLE hProcess, PIMAGEHLP_LINE Line);
/*************************
* File & image handling *
......@@ -680,6 +739,116 @@ BOOL WINAPI SymSetContext(HANDLE hProcess, PIMAGEHLP_STACK_FRAME StackFrame,
PIMAGEHLP_CONTEXT Context);
/*************************
* Stack management *
*************************/
typedef struct _KDHELP
{
DWORD Thread;
DWORD ThCallbackStack;
DWORD NextCallback;
DWORD FramePointer;
DWORD KiCallUserMode;
DWORD KeUserCallbackDispatcher;
DWORD SystemRangeStart;
} KDHELP, *PKDHELP;
typedef struct _STACKFRAME
{
ADDRESS AddrPC;
ADDRESS AddrReturn;
ADDRESS AddrFrame;
ADDRESS AddrStack;
PVOID FuncTableEntry;
DWORD Params[4];
BOOL Far;
BOOL Virtual;
DWORD Reserved[3];
KDHELP KdHelp;
} STACKFRAME, *LPSTACKFRAME;
typedef BOOL (CALLBACK *PREAD_PROCESS_MEMORY_ROUTINE)
(HANDLE hProcess, LPCVOID lpBaseAddress, PVOID lpBuffer,
DWORD nSize, PDWORD lpNumberOfBytesRead);
typedef PVOID (CALLBACK *PFUNCTION_TABLE_ACCESS_ROUTINE)
(HANDLE hProcess, DWORD AddrBase);
typedef DWORD (CALLBACK *PGET_MODULE_BASE_ROUTINE)
(HANDLE hProcess, DWORD ReturnAddress);
typedef DWORD (CALLBACK *PTRANSLATE_ADDRESS_ROUTINE)
(HANDLE hProcess, HANDLE hThread, LPADDRESS lpaddr);
BOOL WINAPI StackWalk(DWORD MachineType, HANDLE hProcess, HANDLE hThread,
LPSTACKFRAME StackFrame, PVOID ContextRecord,
PREAD_PROCESS_MEMORY_ROUTINE ReadMemoryRoutine,
PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine,
PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine,
PTRANSLATE_ADDRESS_ROUTINE TranslateAddress);
PVOID WINAPI SymFunctionTableAccess(HANDLE hProcess, DWORD AddrBase);
/*************************
* Version, global stuff *
*************************/
typedef struct API_VERSION
{
USHORT MajorVersion;
USHORT MinorVersion;
USHORT Revision;
USHORT Reserved;
} API_VERSION, *LPAPI_VERSION;
LPAPI_VERSION WINAPI ImagehlpApiVersion(void);
LPAPI_VERSION WINAPI ImagehlpApiVersionEx(LPAPI_VERSION AppVersion);
typedef struct _IMAGE_DEBUG_INFORMATION
{
LIST_ENTRY List;
DWORD ReservedSize;
PVOID ReservedMappedBase;
USHORT ReservedMachine;
USHORT ReservedCharacteristics;
DWORD ReservedCheckSum;
DWORD ImageBase;
DWORD SizeOfImage;
DWORD ReservedNumberOfSections;
PIMAGE_SECTION_HEADER ReservedSections;
DWORD ReservedExportedNamesSize;
PSTR ReservedExportedNames;
DWORD ReservedNumberOfFunctionTableEntries;
PIMAGE_FUNCTION_ENTRY ReservedFunctionTableEntries;
DWORD ReservedLowestFunctionStartingAddress;
DWORD ReservedHighestFunctionEndingAddress;
DWORD ReservedNumberOfFpoTableEntries;
PFPO_DATA ReservedFpoTableEntries;
DWORD SizeOfCoffSymbols;
PIMAGE_COFF_SYMBOLS_HEADER CoffSymbols;
DWORD ReservedSizeOfCodeViewSymbols;
PVOID ReservedCodeViewSymbols;
PSTR ImageFilePath;
PSTR ImageFileName;
PSTR ReservedDebugFilePath;
DWORD ReservedTimeDateStamp;
BOOL ReservedRomImage;
PIMAGE_DEBUG_DIRECTORY ReservedDebugDirectory;
DWORD ReservedNumberOfDebugDirectories;
DWORD ReservedOriginalFunctionTableBaseAddress;
DWORD Reserved[ 2 ];
} IMAGE_DEBUG_INFORMATION, *PIMAGE_DEBUG_INFORMATION;
PIMAGE_DEBUG_INFORMATION WINAPI MapDebugInformation(HANDLE FileHandle, PSTR FileName,
PSTR SymbolPath, DWORD ImageBase);
BOOL WINAPI UnmapDebugInformation(PIMAGE_DEBUG_INFORMATION DebugInfo);
DWORD WINAPI SymGetOptions(void);
DWORD WINAPI SymSetOptions(DWORD);
#ifdef __cplusplus
} /* extern "C" */
#endif /* defined(__cplusplus) */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment