wine/debugger/hash.c
Alexandre Julliard 767e6f6f9d Release 980809
Sat Aug  8 19:11:46 1998  Marcus Meissner <marcus@jet.franken.de>

 	* [*/*]
	Added some missing WINAPI and some missing prototypes for
	functions.

	* [controls/static.c]
	Got rid of the MODULE32_LookupHMODULE error showing up for every
	message box.

	* [windows/winproc.c]
	WM_NOTIFY 16->32 mapping (AOL Instant Messenger or however it is called).

	* [misc/winsock.c]
	hostent/servent/protoent should not use the same static buffers.
	(has broken nt3.15 finger.exe which does hp=gethostbyname(), then
	getservbyname("finger","tcp") and the references hp->h_addr_list[0]).

Sat Aug  8 13:21:24 1998  Alexandre Julliard  <julliard@lrc.epfl.ch>

	* [include/server.h] [tools/make_requests] [server/request.c]
	  [server/trace.c]
	Automated part of the client/server request code generation.
	Added tracing of client/server communication.

	* [scheduler/*.c] [server/process.c]
	Added support for server-side handles.

	* [scheduler/thread.c]
	Added DLL_THREAD_ATTACH/DETACH notifications.

	* [configure.in]
	Added check for -lsocket.

	* [windows/winproc.c]
	Return the thunk address in WINPROC_GetProc if the function types
	don't match.

Sat Aug  8 02:44:04 1998  Douglas Ridgway  <ridgway@winehq.com>

	* [windows/winproc.c][windows/win.c][windows/message.c]  
	Documentation for CallWindowProc, SetWindowLong, DispatchMessage,
	WaitMessage, GetMessage, and PeekMessage.

Sat Aug  8 01:00:00 1998  Juergen Schmied <juergen.schmied@metronet.de>

	* [controls/commctrl.c][controls/widgets.c][include/builtin32.h]
	  [include/commctrl.h][relay32/builtin32.c][relay32/comctl32.spec]
	  [tools/build.c] [relay32/shell32.spec]
	Added the functionality of the LibMain function. The common 
	controls are properly initialized now.

	* [controls/treeview.c][memory/atom.c][scheduler/thread.c][windows/class.c]
	  [windows/msgbox.c][windows/win.c]
	Put TRACE in, put SetLastError() in.

	* [include/interfaces.h]
	Added IClassFactory::LockServer.

	* [include/ole2.h]
	Added struct for LPOLEMENUGROUPWIDTHS32, HOLEMENU32.

	* [include/shell.h][include/shlobj.h][misc/shell.c][ole/folders.c]
	Reorganized and many structs and classes (IShellBrowser,IShellView)
	added. shell32.dll should work in many cases now.
	Started SHGetFileInfoA implementeation, rewrote SHGetPathFromIDList32A.
	New Shell32LibMain started ShellIconCache Implementation.

	* [misc/shellord.c]
	Rewrote ILCombine, ILGetSize
	New stubs SHFind_InitMenuPopup, FileMenu_Create, ShellExecuteEx,
	SHSetInstanceExplorer, SHGetInstanceExplorer, SHFreeUnusedLibraries.

	* [include/winerror.h]
	Class and window related error codes added.

	* [memory/heap.c]
	Changed lstrlen32A to strlen to get rid of milions of TRACE lines.

	* [misc/ddeml.c]
	First lines for DdeCreateStringHandle32A / DdeFreeStringHandle32.

	* [misc/network.c][relay32/mpr.spec]
	Fixed some bugs, changed ordinals.

	* [windows/class.c]
	Workarounds for incorrect hInstance handling. Fixes parts of
	MSWord95/Excel95 and Money95.

Thu Aug  6 21:05:35 1998  Eric Kohl <ekohl@abo.rhein-zeitung.de>

	* [windows/nonclient.c][misc/tweak.c][include/tweak.h]
	  [documentation/win95look]
	Removed some tweak variables. They are no longer needed.

	* [files/dos_fs.c]
	Added check for null pointer in DOSFS_GetDevice().

	* [controls/tooltips.c][include/commctrl.h]
	Improved tooltips.

	* [controls/status.c][include/commctrl.h]
	Cleaned up code and added tooltip support.

	* [controls/toolbar.c][include/commctrl.h]
	Added tooltip support.

	* [documentation/common_controls]
	Updated.

Thu Aug  6 00:05:22 1998  Uwe Bonnes  <bon@elektron.ikp.physik.tu-darmstadt.de>

	* [include/ver.h] [misc/ver.c]
	Write VIF_BUFFTOOSMALL, not VIF_BUFTOSMALL.

	* [debugger/hash.c] [debugger/stabs.c]
	Make debug output more friendly for posting.

	* [files/file.c]
	Partial implementation of OF_SHARE_EXCLUSIVE.
	Needed for Quicklogic/QuickChip (InstallShield).

	* [files/profile.c]
	When a cached-only entry is found, return it.

	* [graphics/x11drv/xfont.c]
	Accept a space as delimiter for a fontname and inhibit overrun
	(Make xplasim.ex from the Phillips Coolrunner CPLD suite proceed).

	* [miscemu/main.c]
	Delay setting IF1632_CallLargeStack after loading the executables.
	Stops fpgaexp.exe from the Viewlogic FPGA suite from crashing when
 	showing the Blinker error Message Box.

	* [misc/network.c]
	Make WNetGetConnection16 recognise a CDROM as a local drive.

	* [multimedia/mmsystem.c]
	Preliminary check for MCI_ALL_DEVICE_ID in MCI_Close by a FIXME.


Tue Aug 4 21:15:23 1998  James Juran <jrj120@psu.edu>

	* [ole/ole2nls.c]
	Fixed bug in CompareString32A.  strcmp() doesn't necessarily
	return -1, 0, or 1, which the previous code depended on.
	Changed name of is_punctuation to OLE2NLS_isPunctuation.
	Made NORM_IGNOREWIDTH not print a FIXME message in 
	LCMapString32A.
	Other debugging messages, documentation, and code cleanups.

	* [objects/font.c] [relay32/gdi32.spec] [include/winerror.h]
	Added stub for GetFontData32, and added GDI_ERROR constant 
	to winerror.h.

Tue Aug  4 07:44:43 1998  Ove Kaaven <ovek@arcticnet.no>

	* [multimedia/mmio.c]
	Implemented mmioSetBuffer, mmioAdvance, mmioAscend, and
	mmioCreateChunk to the best of my knowledge. But watch out,
	there's bound to be bugs in there...

	* [include/mmsystem.h] [multimedia/mmsystem.c]
	Hacked in support for 32-bit multimedia function callbacks.

	* [AUTHORS] [misc/shell.c]
	Selfishly credited my authorship. Hopefully I'm excused.

	* [include/dosexe.h] [include/module.h] [loader/dos/*]
	  [loader/module.c] [loader/task.c] [Makefile.in]
	  [configure.in] [Makefile.in]
	Added DOS EXE (MZ) loader and virtual machine. Task
	structure integration thanks to Ulrich Weigand.

	* [files/dos_fs.c]
	Work around a null pointer dereference if ioctlGetDeviceInfo-ing
	a FILE_DupUnixHandle'd file (i.e. GetStdHandle'd).

	* [include/miscemu.h] [include/winnt.h] [loader/main.c]
	  [memory/global.c] [msdos/dpmi.c] [msdos/dosmem.c]
	Added support for DOS memory images, and added
	DOSMEM_ResizeBlock() and DOSMEM_Available().

	* [msdos/int21.c]
	Added support for the DOS virtual machine, tweaked handle
	assignment to avoid stdio clashes, forced INT21_FindNext to exit
	wildcardless searches after finding one entry, added AH=7, 8, 9,
	C, 48, 49, 4A, and 7160 CL=1 (Get Short Filename), and made the
	long filename calls only respond if running with with -winver
	win95.

	* [objects/cursoricon.c]
	DestroyCursor32 and DestroyIcon32 should now free the objects
	(hopefully) correctly.

Sun Aug  2 21:42:09 1998  Huw D M Davies <daviesh@abacus.physics.ox.ac.uk>

	* [graphics/psdrv/*] [include/psdrv.h]
	Added PPD file parsing - at the moment it reads a file called
	default.ppd from the directory in which you start Wine. Page sizes
	other than A4 should now work (landscape may not). All fonts that are
	present on your printer (according to the PPD) should be available,
	providing you have the AFM files. Fonts should now be the correct size.
	Graphics is still basically lines only. See graphics/psdrv/README .

	* [misc/printdrv.c]
	Yet more Drv[Get/Set]PrinterData fixes.

Fri Jul 31 21:33:22 1998  Per Lindström <pelinstr@algonet.se>

	* [relay32/crtdll.spec] [misc/crtdll.c]
	Added stub for freopen, _findfirst, _findnext, _fstat and _read.

	* [files/directory.c]
	Modified warning message.

Wed Jul 29 11:25:28 1998  Luiz Otavio L. Zorzella  <zorzella@nr.conexware.com>

	* [objects/font.c]
	Added stub for GetFontData.

	* [multimedia/msvideo.c]
	Created this file to hold the msvideo.dll calls (and maybe also
	msvfw32.dll). 

	* [objects/cursoricon.c]
	Added search in Global Heap for cursor when trying to destroy it
	with DestroyCursor16. This test should be done in many (all?)
	other functions that use FreeResource.

	* [controls/treeview.c] [include/commctrl.h]
	Minor correction in name and addition of many placeholders for TVM
	messages in TREEVIEW_WindowProc.

	* [msdos/dpmi.c]
	Fixed a bug in DPMI_xrealloc where in a copy of a memory region
	"A" of size "a" to a region "B" of size "b", "b" bytes were being
	copied, instead of "a", as the new version does. This both
	increases speed, as well as avoids segfaults.
1998-08-09 12:47:43 +00:00

1373 lines
32 KiB
C

/*
* File hash.c - generate hash tables for Wine debugger symbols
*
* Copyright (C) 1993, Eric Youngdale.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <sys/types.h>
#include <neexe.h>
#include "module.h"
#include "process.h"
#include "selectors.h"
#include "debugger.h"
#include "toolhelp.h"
#include "xmalloc.h"
#define NR_NAME_HASH 16384
#ifndef PATH_MAX
#define PATH_MAX _MAX_PATH
#endif
static char * reg_name[] =
{
"eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi"
};
struct name_hash
{
struct name_hash * next; /* Used to look up within name hash */
char * name;
char * sourcefile;
int n_locals;
int locals_alloc;
WineLocals * local_vars;
int n_lines;
int lines_alloc;
WineLineNo * linetab;
DBG_ADDR addr;
unsigned short flags;
unsigned short breakpoint_offset;
unsigned int symbol_size;
};
static BOOL32 DEBUG_GetStackSymbolValue( const char * name, DBG_ADDR *addr );
static int sortlist_valid = FALSE;
static int sorttab_nsym;
static struct name_hash ** addr_sorttab = NULL;
static struct name_hash * name_hash_table[NR_NAME_HASH];
static unsigned int name_hash( const char * name )
{
unsigned int hash = 0;
unsigned int tmp;
const char * p;
p = name;
while (*p)
{
hash = (hash << 4) + *p++;
if( (tmp = (hash & 0xf0000000)) )
{
hash ^= tmp >> 24;
}
hash &= ~tmp;
}
return hash % NR_NAME_HASH;
}
int
DEBUG_cmp_sym(const void * p1, const void * p2)
{
struct name_hash ** name1 = (struct name_hash **) p1;
struct name_hash ** name2 = (struct name_hash **) p2;
if( ((*name1)->flags & SYM_INVALID) != 0 )
{
return -1;
}
if( ((*name2)->flags & SYM_INVALID) != 0 )
{
return 1;
}
if( (*name1)->addr.seg > (*name2)->addr.seg )
{
return 1;
}
if( (*name1)->addr.seg < (*name2)->addr.seg )
{
return -1;
}
if( (*name1)->addr.off > (*name2)->addr.off )
{
return 1;
}
if( (*name1)->addr.off < (*name2)->addr.off )
{
return -1;
}
return 0;
}
/***********************************************************************
* DEBUG_ResortSymbols
*
* Rebuild sorted list of symbols.
*/
static
void
DEBUG_ResortSymbols()
{
struct name_hash *nh;
int nsym = 0;
int i;
for(i=0; i<NR_NAME_HASH; i++)
{
for (nh = name_hash_table[i]; nh; nh = nh->next)
{
nsym++;
}
}
sorttab_nsym = nsym;
if( nsym == 0 )
{
return;
}
addr_sorttab = (struct name_hash **) xrealloc(addr_sorttab,
nsym * sizeof(struct name_hash *));
nsym = 0;
for(i=0; i<NR_NAME_HASH; i++)
{
for (nh = name_hash_table[i]; nh; nh = nh->next)
{
addr_sorttab[nsym++] = nh;
}
}
qsort(addr_sorttab, nsym,
sizeof(struct name_hash *), DEBUG_cmp_sym);
sortlist_valid = TRUE;
}
/***********************************************************************
* DEBUG_AddSymbol
*
* Add a symbol to the table.
*/
struct name_hash *
DEBUG_AddSymbol( const char * name, const DBG_ADDR *addr, const char * source,
int flags)
{
struct name_hash * new;
struct name_hash *nh;
static char prev_source[PATH_MAX] = {'\0', };
static char * prev_duped_source = NULL;
char * c;
int hash;
hash = name_hash(name);
for (nh = name_hash_table[hash]; nh; nh = nh->next)
{
if( ((nh->flags & SYM_INVALID) != 0) && strcmp(name, nh->name) == 0 )
{
nh->addr.off = addr->off;
nh->addr.seg = addr->seg;
if( nh->addr.type == NULL && addr->type != NULL )
{
nh->addr.type = addr->type;
}
nh->flags &= ~SYM_INVALID;
return nh;
}
if (nh->addr.seg == addr->seg &&
nh->addr.off == addr->off &&
strcmp(name, nh->name) == 0 )
{
return nh;
}
}
/*
* First see if we already have an entry for this symbol. If so
* return it, so we don't end up with duplicates.
*/
new = (struct name_hash *) xmalloc(sizeof(struct name_hash));
new->addr = *addr;
new->name = xstrdup(name);
if( source != NULL )
{
/*
* This is an enhancement to reduce memory consumption. The idea
* is that we duplicate a given string only once. This is a big
* win if there are lots of symbols defined in a given source file.
*/
if( strcmp(source, prev_source) == 0 )
{
new->sourcefile = prev_duped_source;
}
else
{
strcpy(prev_source, source);
prev_duped_source = new->sourcefile = xstrdup(source);
}
}
else
{
new->sourcefile = NULL;
}
new->n_lines = 0;
new->lines_alloc = 0;
new->linetab = NULL;
new->n_locals = 0;
new->locals_alloc = 0;
new->local_vars = NULL;
new->flags = flags;
new->next = NULL;
/* Now insert into the hash table */
new->next = name_hash_table[hash];
name_hash_table[hash] = new;
/*
* Check some heuristics based upon the file name to see whether
* we want to step through this guy or not. These are machine generated
* assembly files that are used to translate between the MS way of
* calling things and the GCC way of calling things. In general we
* always want to step through.
*/
if( source != NULL )
{
c = strrchr(source, '.');
if( c != NULL && strcmp(c, ".s") == 0 )
{
c = strrchr(source, '/');
if( c != NULL )
{
c++;
if( (strcmp(c, "callfrom16.s") == 0)
|| (strcmp(c, "callto16.s") == 0)
|| (strcmp(c, "call32.s") == 0) )
{
new->flags |= SYM_TRAMPOLINE;
}
}
}
}
sortlist_valid = FALSE;
return new;
}
BOOL32 DEBUG_Normalize(struct name_hash * nh )
{
/*
* We aren't adding any more locals or linenumbers to this function.
* Free any spare memory that we might have allocated.
*/
if( nh == NULL )
{
return TRUE;
}
if( nh->n_locals != nh->locals_alloc )
{
nh->locals_alloc = nh->n_locals;
nh->local_vars = xrealloc(nh->local_vars,
nh->locals_alloc * sizeof(WineLocals));
}
if( nh->n_lines != nh->lines_alloc )
{
nh->lines_alloc = nh->n_lines;
nh->linetab = xrealloc(nh->linetab,
nh->lines_alloc * sizeof(WineLineNo));
}
return TRUE;
}
/***********************************************************************
* DEBUG_GetSymbolValue
*
* Get the address of a named symbol.
*/
BOOL32 DEBUG_GetSymbolValue( const char * name, const int lineno,
DBG_ADDR *addr, int bp_flag )
{
char buffer[256];
struct name_hash *nh;
for(nh = name_hash_table[name_hash(name)]; nh; nh = nh->next)
{
if( (nh->flags & SYM_INVALID) != 0 )
{
continue;
}
if (!strcmp(nh->name, name)) break;
}
if (!nh && (name[0] != '_'))
{
buffer[0] = '_';
strcpy(buffer+1, name);
for(nh = name_hash_table[name_hash(buffer)]; nh; nh = nh->next)
{
if( (nh->flags & SYM_INVALID) != 0 )
{
continue;
}
if (!strcmp(nh->name, buffer)) break;
}
}
/*
* If we don't have anything here, then try and see if this
* is a local symbol to the current stack frame. No matter
* what, we have nothing more to do, so we let that function
* decide what we ultimately return.
*/
if (!nh)
{
return DEBUG_GetStackSymbolValue(name, addr);
}
return DEBUG_GetLineNumberAddr( nh, lineno, addr, bp_flag );
}
/***********************************************************************
* DEBUG_GetLineNumberAddr
*
* Get the address of a named symbol.
*/
BOOL32 DEBUG_GetLineNumberAddr( struct name_hash * nh, const int lineno,
DBG_ADDR *addr, int bp_flag )
{
int i;
if( lineno == -1 )
{
*addr = nh->addr;
if( bp_flag )
{
addr->off += nh->breakpoint_offset;
}
}
else
{
/*
* Search for the specific line number. If we don't find it,
* then return FALSE.
*/
if( nh->linetab == NULL )
{
return FALSE;
}
for(i=0; i < nh->n_lines; i++ )
{
if( nh->linetab[i].line_number == lineno )
{
*addr = nh->linetab[i].pc_offset;
return TRUE;
}
}
/*
* This specific line number not found.
*/
return FALSE;
}
return TRUE;
}
/***********************************************************************
* DEBUG_SetSymbolValue
*
* Set the address of a named symbol.
*/
BOOL32 DEBUG_SetSymbolValue( const char * name, const DBG_ADDR *addr )
{
char buffer[256];
struct name_hash *nh;
for(nh = name_hash_table[name_hash(name)]; nh; nh = nh->next)
if (!strcmp(nh->name, name)) break;
if (!nh && (name[0] != '_'))
{
buffer[0] = '_';
strcpy(buffer+1, name);
for(nh = name_hash_table[name_hash(buffer)]; nh; nh = nh->next)
if (!strcmp(nh->name, buffer)) break;
}
if (!nh) return FALSE;
nh->addr = *addr;
nh->flags &= SYM_INVALID;
DBG_FIX_ADDR_SEG( &nh->addr, DS_reg(&DEBUG_context) );
return TRUE;
}
/***********************************************************************
* DEBUG_FindNearestSymbol
*
* Find the symbol nearest to a given address.
* If ebp is specified as non-zero, it means we should dump the argument
* list into the string we return as well.
*/
const char * DEBUG_FindNearestSymbol( const DBG_ADDR *addr, int flag,
struct name_hash ** rtn,
unsigned int ebp,
struct list_id * source)
{
static char name_buffer[MAX_PATH + 256];
static char arglist[1024];
static char argtmp[256];
struct name_hash * nearest = NULL;
int mid, high, low;
unsigned int * ptr;
int lineno;
char * lineinfo, *sourcefile;
int i;
char linebuff[16];
if( rtn != NULL )
{
*rtn = NULL;
}
if( source != NULL )
{
source->sourcefile = NULL;
source->line = -1;
}
if( sortlist_valid == FALSE )
{
DEBUG_ResortSymbols();
}
if( sortlist_valid == FALSE )
{
return NULL;
}
/*
* FIXME - use the binary search that we added to
* the function DEBUG_CheckLinenoStatus. Better yet, we should
* probably keep some notion of the current function so we don't
* have to search every time.
*/
/*
* Binary search to find closest symbol.
*/
low = 0;
high = sorttab_nsym;
if( addr_sorttab[0]->addr.seg > addr->seg
|| ( addr_sorttab[0]->addr.seg == addr->seg
&& addr_sorttab[0]->addr.off > addr->off) )
{
nearest = NULL;
}
else if( addr_sorttab[high - 1]->addr.seg < addr->seg
|| ( addr_sorttab[high - 1]->addr.seg == addr->seg
&& addr_sorttab[high - 1]->addr.off < addr->off) )
{
nearest = addr_sorttab[high - 1];
}
else
{
while(1==1)
{
mid = (high + low)/2;
if( mid == low )
{
/*
* See if there are any other entries that might also
* have the same address, and would also have a line
* number table.
*/
if( mid > 0 && addr_sorttab[mid]->linetab == NULL )
{
if( (addr_sorttab[mid - 1]->addr.seg ==
addr_sorttab[mid]->addr.seg)
&& (addr_sorttab[mid - 1]->addr.off ==
addr_sorttab[mid]->addr.off)
&& (addr_sorttab[mid - 1]->linetab != NULL) )
{
mid--;
}
}
if( (mid < sorttab_nsym - 1)
&& (addr_sorttab[mid]->linetab == NULL) )
{
if( (addr_sorttab[mid + 1]->addr.seg ==
addr_sorttab[mid]->addr.seg)
&& (addr_sorttab[mid + 1]->addr.off ==
addr_sorttab[mid]->addr.off)
&& (addr_sorttab[mid + 1]->linetab != NULL) )
{
mid++;
}
}
nearest = addr_sorttab[mid];
#if 0
fprintf(stderr, "Found %x:%x when looking for %x:%x %x %s\n",
addr_sorttab[mid ]->addr.seg,
addr_sorttab[mid ]->addr.off,
addr->seg, addr->off,
addr_sorttab[mid ]->linetab,
addr_sorttab[mid ]->name);
#endif
break;
}
if( (addr_sorttab[mid]->addr.seg < addr->seg)
|| ( addr_sorttab[mid]->addr.seg == addr->seg
&& addr_sorttab[mid]->addr.off <= addr->off) )
{
low = mid;
}
else
{
high = mid;
}
}
}
if (!nearest) return NULL;
if( rtn != NULL )
{
*rtn = nearest;
}
/*
* Fill in the relevant bits to the structure so that we can
* locate the source and line for this bit of code.
*/
if( source != NULL )
{
source->sourcefile = nearest->sourcefile;
if( nearest->linetab == NULL )
{
source->line = -1;
}
else
{
source->line = nearest->linetab[0].line_number;
}
}
lineinfo = "";
lineno = -1;
/*
* Prepare to display the argument list. If ebp is specified, it is
* the framepointer for the function in question. If not specified,
* we don't want the arglist.
*/
memset(arglist, '\0', sizeof(arglist));
if( ebp != 0 )
{
for(i=0; i < nearest->n_locals; i++ )
{
/*
* If this is a register (offset == 0) or a local
* variable, we don't want to know about it.
*/
if( nearest->local_vars[i].offset <= 0 )
{
continue;
}
ptr = (unsigned int *) (ebp + nearest->local_vars[i].offset);
if( arglist[0] == '\0' )
{
arglist[0] = '(';
}
else
{
strcat(arglist, ", ");
}
sprintf(argtmp, "%s=0x%x", nearest->local_vars[i].name,
*ptr);
strcat(arglist, argtmp);
}
if( arglist[0] == '(' )
{
strcat(arglist, ")");
}
}
if( (nearest->sourcefile != NULL) && (flag == TRUE)
&& (addr->off - nearest->addr.off < 0x100000) )
{
/*
* Try and find the nearest line number to the current offset.
*/
if( nearest->linetab != NULL )
{
low = 0;
high = nearest->n_lines;
while ((high - low) > 1)
{
mid = (high + low) / 2;
if (addr->off < nearest->linetab[mid].pc_offset.off)
high = mid;
else
low = mid;
}
lineno = nearest->linetab[low].line_number;
}
if( lineno != -1 )
{
sprintf(linebuff, ":%d", lineno);
lineinfo = linebuff;
if( source != NULL )
{
source->line = lineno;
}
}
/* Remove the path from the file name */
sourcefile = strrchr( nearest->sourcefile, '/' );
if (!sourcefile) sourcefile = nearest->sourcefile;
else sourcefile++;
if (addr->off == nearest->addr.off)
sprintf( name_buffer, "%s%s [%s%s]", nearest->name,
arglist, sourcefile, lineinfo);
else
sprintf( name_buffer, "%s+0x%lx%s [%s%s]", nearest->name,
addr->off - nearest->addr.off,
arglist, sourcefile, lineinfo );
}
else
{
if (addr->off == nearest->addr.off)
sprintf( name_buffer, "%s%s", nearest->name, arglist);
else {
if (addr->seg && (nearest->addr.seg!=addr->seg))
return NULL;
else
sprintf( name_buffer, "%s+0x%lx%s", nearest->name,
addr->off - nearest->addr.off, arglist);
}
}
return name_buffer;
}
/***********************************************************************
* DEBUG_ReadSymbolTable
*
* Read a symbol file into the hash table.
*/
void DEBUG_ReadSymbolTable( const char * filename )
{
FILE * symbolfile;
DBG_ADDR addr = { 0, 0 };
int nargs;
char type;
char * cpnt;
char buffer[256];
char name[256];
if (!(symbolfile = fopen(filename, "r")))
{
fprintf( stderr, "Unable to open symbol table %s\n", filename );
return;
}
fprintf( stderr, "Reading symbols from file %s\n", filename );
while (1)
{
fgets( buffer, sizeof(buffer), symbolfile );
if (feof(symbolfile)) break;
/* Strip any text after a # sign (i.e. comments) */
cpnt = buffer;
while (*cpnt)
if(*cpnt++ == '#') { *cpnt = 0; break; }
/* Quietly ignore any lines that have just whitespace */
cpnt = buffer;
while(*cpnt)
{
if(*cpnt != ' ' && *cpnt != '\t') break;
cpnt++;
}
if (!(*cpnt) || *cpnt == '\n') continue;
nargs = sscanf(buffer, "%lx %c %s", &addr.off, &type, name);
DEBUG_AddSymbol( name, &addr, NULL, SYM_WINE );
}
fclose(symbolfile);
}
/***********************************************************************
* DEBUG_LoadEntryPoints16
*
* Load the entry points of a Win16 module into the hash table.
*/
static void DEBUG_LoadEntryPoints16( HMODULE16 hModule, NE_MODULE *pModule,
const char *name )
{
DBG_ADDR addr;
char buffer[256];
FARPROC16 address;
/* First search the resident names */
unsigned char *cpnt = (unsigned char *)pModule + pModule->name_table;
while (*cpnt)
{
cpnt += *cpnt + 1 + sizeof(WORD);
sprintf( buffer, "%s.%.*s", name, *cpnt, cpnt + 1 );
if ((address = NE_GetEntryPoint(hModule, *(WORD *)(cpnt + *cpnt + 1))))
{
addr.seg = HIWORD(address);
addr.off = LOWORD(address);
addr.type = NULL;
DEBUG_AddSymbol( buffer, &addr, NULL, SYM_WIN32 | SYM_FUNC );
}
}
/* Now search the non-resident names table */
if (!pModule->nrname_handle) return; /* No non-resident table */
cpnt = (char *)GlobalLock16( pModule->nrname_handle );
while (*cpnt)
{
cpnt += *cpnt + 1 + sizeof(WORD);
sprintf( buffer, "%s.%.*s", name, *cpnt, cpnt + 1 );
if ((address = NE_GetEntryPoint(hModule, *(WORD *)(cpnt + *cpnt + 1))))
{
addr.seg = HIWORD(address);
addr.off = LOWORD(address);
addr.type = NULL;
DEBUG_AddSymbol( buffer, &addr, NULL, SYM_WIN32 | SYM_FUNC );
}
}
}
/***********************************************************************
* DEBUG_LoadEntryPoints32
*
* Load the entry points of a Win32 module into the hash table.
*/
static void DEBUG_LoadEntryPoints32( HMODULE32 hModule, const char *name )
{
#define RVA(x) (hModule+(DWORD)(x))
DBG_ADDR addr;
char buffer[256];
int i, j;
IMAGE_SECTION_HEADER *pe_seg;
IMAGE_EXPORT_DIRECTORY *exports;
IMAGE_DATA_DIRECTORY *dir;
WORD *ordinals;
void **functions;
const char **names;
addr.seg = 0;
addr.type = NULL;
/* Add start of DLL */
addr.off = hModule;
DEBUG_AddSymbol( name, &addr, NULL, SYM_WIN32 | SYM_FUNC );
/* Add entry point */
sprintf( buffer, "%s.EntryPoint", name );
addr.off = (DWORD)RVA_PTR( hModule, OptionalHeader.AddressOfEntryPoint );
DEBUG_AddSymbol( buffer, &addr, NULL, SYM_WIN32 | SYM_FUNC );
/* Add start of sections */
pe_seg = PE_SECTIONS(hModule);
for (i = 0; i < PE_HEADER(hModule)->FileHeader.NumberOfSections; i++)
{
sprintf( buffer, "%s.%s", name, pe_seg->Name );
addr.off = RVA(pe_seg->VirtualAddress );
DEBUG_AddSymbol( buffer, &addr, NULL, SYM_WIN32 | SYM_FUNC );
pe_seg++;
}
/* Add exported functions */
dir = &PE_HEADER(hModule)->OptionalHeader.
DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
if (!dir->Size) return;
exports = (IMAGE_EXPORT_DIRECTORY *)RVA( dir->VirtualAddress );
ordinals = (WORD *)RVA( exports->AddressOfNameOrdinals );
names = (const char **)RVA( exports->AddressOfNames );
functions = (void **)RVA( exports->AddressOfFunctions );
for (i = 0; i < exports->NumberOfNames; i++)
{
if (!names[i]) continue;
sprintf( buffer, "%s.%s", name, (char *)RVA(names[i]) );
addr.off = RVA( functions[ordinals[i]] );
DEBUG_AddSymbol( buffer, &addr, NULL, SYM_WIN32 | SYM_FUNC );
}
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;
sprintf( buffer, "%s.%ld", name, i + exports->Base );
addr.off = (DWORD)RVA( functions[i] );
DEBUG_AddSymbol( buffer, &addr, NULL, SYM_WIN32 | SYM_FUNC );
}
dir = &PE_HEADER(hModule)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG];
if (dir->Size)
DEBUG_RegisterDebugInfo(hModule, name, dir->VirtualAddress, dir->Size);
#undef RVA
}
/***********************************************************************
* DEBUG_LoadEntryPoints
*
* Load the entry points of all the modules into the hash table.
*/
void DEBUG_LoadEntryPoints(void)
{
MODULEENTRY entry;
NE_MODULE *pModule;
BOOL32 ok;
WINE_MODREF *wm;
int rowcount = 3;
fprintf( stderr, " " );
for (ok = ModuleFirst(&entry); ok; ok = ModuleNext(&entry))
{
if (!(pModule = NE_GetPtr( entry.hModule ))) continue;
if ((rowcount + strlen(entry.szModule)) > 76)
{
fprintf( stderr,"\n ");
rowcount = 3;
}
fprintf( stderr, " %s", entry.szModule );
rowcount += strlen(entry.szModule) + 1;
if (!(pModule->flags & NE_FFLAGS_WIN32)) /* NE module */
DEBUG_LoadEntryPoints16( entry.hModule, pModule, entry.szModule );
}
for (wm=PROCESS_Current()->modref_list;wm;wm=wm->next)
{
if ((rowcount + strlen(wm->modname)) > 76)
{
fprintf( stderr,"\n ");
rowcount = 3;
}
fprintf( stderr, " %s", wm->modname );
rowcount += strlen(wm->modname) + 1;
DEBUG_LoadEntryPoints32( wm->module, wm->modname );
}
fprintf( stderr, "\n" );
}
void
DEBUG_AddLineNumber( struct name_hash * func, int line_num,
unsigned long offset )
{
if( func == NULL )
{
return;
}
if( func->n_lines + 1 >= func->lines_alloc )
{
func->lines_alloc += 64;
func->linetab = xrealloc(func->linetab,
func->lines_alloc * sizeof(WineLineNo));
}
func->linetab[func->n_lines].line_number = line_num;
func->linetab[func->n_lines].pc_offset.seg = func->addr.seg;
func->linetab[func->n_lines].pc_offset.off = func->addr.off + offset;
func->linetab[func->n_lines].pc_offset.type = NULL;
func->n_lines++;
}
struct wine_locals *
DEBUG_AddLocal( struct name_hash * func, int regno,
int offset,
int pc_start,
int pc_end,
char * name)
{
if( func == NULL )
{
return NULL;
}
if( func->n_locals + 1 >= func->locals_alloc )
{
func->locals_alloc += 32;
func->local_vars = xrealloc(func->local_vars,
func->locals_alloc * sizeof(WineLocals));
}
func->local_vars[func->n_locals].regno = regno;
func->local_vars[func->n_locals].offset = offset;
func->local_vars[func->n_locals].pc_start = pc_start;
func->local_vars[func->n_locals].pc_end = pc_end;
func->local_vars[func->n_locals].name = xstrdup(name);
func->local_vars[func->n_locals].type = NULL;
func->n_locals++;
return &func->local_vars[func->n_locals - 1];
}
void
DEBUG_DumpHashInfo()
{
int i;
int depth;
struct name_hash *nh;
/*
* Utility function to dump stats about the hash table.
*/
for(i=0; i<NR_NAME_HASH; i++)
{
depth = 0;
for (nh = name_hash_table[i]; nh; nh = nh->next)
{
depth++;
}
fprintf(stderr, "Bucket %d: %d\n", i, depth);
}
}
/***********************************************************************
* DEBUG_CheckLinenoStatus
*
* Find the symbol nearest to a given address.
* If ebp is specified as non-zero, it means we should dump the argument
* list into the string we return as well.
*/
int DEBUG_CheckLinenoStatus( const DBG_ADDR *addr)
{
struct name_hash * nearest = NULL;
int mid, high, low;
if( sortlist_valid == FALSE )
{
DEBUG_ResortSymbols();
}
/*
* Binary search to find closest symbol.
*/
low = 0;
high = sorttab_nsym;
if( addr_sorttab[0]->addr.seg > addr->seg
|| ( addr_sorttab[0]->addr.seg == addr->seg
&& addr_sorttab[0]->addr.off > addr->off) )
{
nearest = NULL;
}
else if( addr_sorttab[high - 1]->addr.seg < addr->seg
|| ( addr_sorttab[high - 1]->addr.seg == addr->seg
&& addr_sorttab[high - 1]->addr.off < addr->off) )
{
nearest = addr_sorttab[high - 1];
}
else
{
while(1==1)
{
mid = (high + low)/2;
if( mid == low )
{
/*
* See if there are any other entries that might also
* have the same address, and would also have a line
* number table.
*/
if( mid > 0 && addr_sorttab[mid]->linetab == NULL )
{
if( (addr_sorttab[mid - 1]->addr.seg ==
addr_sorttab[mid]->addr.seg)
&& (addr_sorttab[mid - 1]->addr.off ==
addr_sorttab[mid]->addr.off)
&& (addr_sorttab[mid - 1]->linetab != NULL) )
{
mid--;
}
}
if( (mid < sorttab_nsym - 1)
&& (addr_sorttab[mid]->linetab == NULL) )
{
if( (addr_sorttab[mid + 1]->addr.seg ==
addr_sorttab[mid]->addr.seg)
&& (addr_sorttab[mid + 1]->addr.off ==
addr_sorttab[mid]->addr.off)
&& (addr_sorttab[mid + 1]->linetab != NULL) )
{
mid++;
}
}
nearest = addr_sorttab[mid];
#if 0
fprintf(stderr, "Found %x:%x when looking for %x:%x %x %s\n",
addr_sorttab[mid ]->addr.seg,
addr_sorttab[mid ]->addr.off,
addr->seg, addr->off,
addr_sorttab[mid ]->linetab,
addr_sorttab[mid ]->name);
#endif
break;
}
if( (addr_sorttab[mid]->addr.seg < addr->seg)
|| ( addr_sorttab[mid]->addr.seg == addr->seg
&& addr_sorttab[mid]->addr.off <= addr->off) )
{
low = mid;
}
else
{
high = mid;
}
}
}
if (!nearest) return FUNC_HAS_NO_LINES;
if( nearest->flags & SYM_STEP_THROUGH )
{
/*
* This will cause us to keep single stepping until
* we get to the other side somewhere.
*/
return NOT_ON_LINENUMBER;
}
if( (nearest->flags & SYM_TRAMPOLINE) )
{
/*
* This will cause us to keep single stepping until
* we get to the other side somewhere.
*/
return FUNC_IS_TRAMPOLINE;
}
if( nearest->linetab == NULL )
{
return FUNC_HAS_NO_LINES;
}
/*
* We never want to stop on the first instruction of a function
* even if it has it's own linenumber. Let the thing keep running
* until it gets past the function prologue. We only do this if there
* is more than one line number for the function, of course.
*/
if( nearest->addr.off == addr->off && nearest->n_lines > 1 )
{
return NOT_ON_LINENUMBER;
}
if( (nearest->sourcefile != NULL)
&& (addr->off - nearest->addr.off < 0x100000) )
{
low = 0;
high = nearest->n_lines;
while ((high - low) > 1)
{
mid = (high + low) / 2;
if (addr->off < nearest->linetab[mid].pc_offset.off) high = mid;
else low = mid;
}
if (addr->off == nearest->linetab[low].pc_offset.off)
return AT_LINENUMBER;
else
return NOT_ON_LINENUMBER;
}
return FUNC_HAS_NO_LINES;
}
/***********************************************************************
* DEBUG_GetFuncInfo
*
* Find the symbol nearest to a given address.
* Returns sourcefile name and line number in a format that the listing
* handler can deal with.
*/
void
DEBUG_GetFuncInfo( struct list_id * ret, const char * filename,
const char * name)
{
char buffer[256];
char * pnt;
struct name_hash *nh;
for(nh = name_hash_table[name_hash(name)]; nh; nh = nh->next)
{
if( filename != NULL )
{
if( nh->sourcefile == NULL )
{
continue;
}
pnt = strrchr(nh->sourcefile, '/');
if( strcmp(nh->sourcefile, filename) != 0
&& (pnt == NULL || strcmp(pnt + 1, filename) != 0) )
{
continue;
}
}
if (!strcmp(nh->name, name)) break;
}
if (!nh && (name[0] != '_'))
{
buffer[0] = '_';
strcpy(buffer+1, name);
for(nh = name_hash_table[name_hash(buffer)]; nh; nh = nh->next)
{
if( filename != NULL )
{
if( nh->sourcefile == NULL )
{
continue;
}
pnt = strrchr(nh->sourcefile, '/');
if( strcmp(nh->sourcefile, filename) != 0
&& (pnt == NULL || strcmp(pnt + 1, filename) != 0) )
{
continue;
}
}
if (!strcmp(nh->name, buffer)) break;
}
}
if( !nh )
{
if( filename != NULL )
{
fprintf(stderr, "No such function %s in %s\n", name, filename);
}
else
{
fprintf(stderr, "No such function %s\n", name);
}
ret->sourcefile = NULL;
ret->line = -1;
return;
}
ret->sourcefile = nh->sourcefile;
/*
* Search for the specific line number. If we don't find it,
* then return FALSE.
*/
if( nh->linetab == NULL )
{
ret->line = -1;
}
else
{
ret->line = nh->linetab[0].line_number;
}
}
/***********************************************************************
* DEBUG_GetStackSymbolValue
*
* Get the address of a named symbol from the current stack frame.
*/
static
BOOL32 DEBUG_GetStackSymbolValue( const char * name, DBG_ADDR *addr )
{
struct name_hash * curr_func;
unsigned int ebp;
unsigned int eip;
int i;
if( DEBUG_GetCurrentFrame(&curr_func, &eip, &ebp) == FALSE )
{
return FALSE;
}
for(i=0; i < curr_func->n_locals; i++ )
{
/*
* Test the range of validity of the local variable. This
* comes up with RBRAC/LBRAC stabs in particular.
*/
if( (curr_func->local_vars[i].pc_start != 0)
&& ((eip - curr_func->addr.off)
< curr_func->local_vars[i].pc_start) )
{
continue;
}
if( (curr_func->local_vars[i].pc_end != 0)
&& ((eip - curr_func->addr.off)
> curr_func->local_vars[i].pc_end) )
{
continue;
}
if( strcmp(name, curr_func->local_vars[i].name) == 0 )
{
/*
* OK, we found it. Now figure out what to do with this.
*/
if( curr_func->local_vars[i].regno != 0 )
{
/*
* Register variable. We don't know how to treat
* this yet.
*/
return FALSE;
}
addr->seg = 0;
addr->off = ebp + curr_func->local_vars[i].offset;
addr->type = curr_func->local_vars[i].type;
return TRUE;
}
}
return FALSE;
}
int
DEBUG_InfoLocals()
{
struct name_hash * curr_func;
unsigned int ebp;
unsigned int eip;
int i;
unsigned int * ptr;
int rtn = FALSE;
if( DEBUG_GetCurrentFrame(&curr_func, &eip, &ebp) == FALSE )
{
return FALSE;
}
for(i=0; i < curr_func->n_locals; i++ )
{
/*
* Test the range of validity of the local variable. This
* comes up with RBRAC/LBRAC stabs in particular.
*/
if( (curr_func->local_vars[i].pc_start != 0)
&& ((eip - curr_func->addr.off)
< curr_func->local_vars[i].pc_start) )
{
continue;
}
if( (curr_func->local_vars[i].pc_end != 0)
&& ((eip - curr_func->addr.off)
> curr_func->local_vars[i].pc_end) )
{
continue;
}
if( curr_func->local_vars[i].offset == 0 )
{
fprintf(stderr, "%s:%s optimized into register $%s \n",
curr_func->name, curr_func->local_vars[i].name,
reg_name[curr_func->local_vars[i].regno]);
}
else
{
ptr = (unsigned int *) (ebp + curr_func->local_vars[i].offset);
fprintf(stderr, "%s:%s == 0x%8.8x\n",
curr_func->name, curr_func->local_vars[i].name,
*ptr);
}
}
rtn = TRUE;
return (rtn);
}
int
DEBUG_SetSymbolSize(struct name_hash * sym, unsigned int len)
{
sym->symbol_size = len;
return TRUE;
}
int
DEBUG_SetSymbolBPOff(struct name_hash * sym, unsigned int off)
{
sym->breakpoint_offset = off;
return TRUE;
}
int
DEBUG_GetSymbolAddr(struct name_hash * sym, DBG_ADDR * addr)
{
*addr = sym->addr;
return TRUE;
}
int DEBUG_SetLocalSymbolType(struct wine_locals * sym, struct datatype * type)
{
sym->type = type;
return TRUE;
}