wine/debugger/hash.c
Eric Pouech 38f2be49f0 Fixed the parsing of id1.id2 which could be either access to field id2
of struct id1, or the identifier id2 in dll id1.
Enhanced some error reporting as well as 'info local' display layout.
Minor cosmetic changes.
2001-08-15 17:40:31 +00:00

1293 lines
30 KiB
C

/*
* File hash.c - generate hash tables for Wine debugger symbols
*
* Copyright (C) 1993, Eric Youngdale.
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <sys/types.h>
#include "debugger.h"
#define NR_NAME_HASH 16384
#ifndef PATH_MAX
#define PATH_MAX _MAX_PATH
#endif
#ifdef __i386__
static char * reg_name[] =
{
"eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi"
};
static unsigned reg_ofs[] =
{
FIELD_OFFSET(CONTEXT, Eax), FIELD_OFFSET(CONTEXT, Ecx),
FIELD_OFFSET(CONTEXT, Edx), FIELD_OFFSET(CONTEXT, Ebx),
FIELD_OFFSET(CONTEXT, Esp), FIELD_OFFSET(CONTEXT, Ebp),
FIELD_OFFSET(CONTEXT, Esi), FIELD_OFFSET(CONTEXT, Edi)
};
#else
static char * reg_name[] = { NULL }; /* FIXME */
static unsigned reg_ofs[] = { 0 };
#endif
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_VALUE value;
unsigned short flags;
unsigned short breakpoint_offset;
unsigned int symbol_size;
};
static BOOL DEBUG_GetStackSymbolValue( const char * name, DBG_VALUE *value );
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)->value.addr.seg > (*name2)->value.addr.seg )
{
return 1;
}
if( (*name1)->value.addr.seg < (*name2)->value.addr.seg )
{
return -1;
}
if( (*name1)->value.addr.off > (*name2)->value.addr.off )
{
return 1;
}
if( (*name1)->value.addr.off < (*name2)->value.addr.off )
{
return -1;
}
return 0;
}
/***********************************************************************
* DEBUG_ResortSymbols
*
* Rebuild sorted list of symbols.
*/
static
void
DEBUG_ResortSymbols(void)
{
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)
{
if( (nh->flags & SYM_INVALID) == 0 )
nsym++;
else
DEBUG_Printf( DBG_CHN_MESG, "Symbol %s is invalid\n", nh->name );
}
}
sorttab_nsym = nsym;
if( nsym == 0 )
{
return;
}
addr_sorttab = (struct name_hash **) DBG_realloc(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)
{
if( (nh->flags & SYM_INVALID) == 0 )
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_VALUE *value,
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;
int hash;
assert(value->cookie == DV_TARGET || value->cookie == DV_HOST);
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 )
{
#if 0
DEBUG_Printf(DBG_CHN_MESG, "Changing address for symbol %s (%08lx:%08lx => %08lx:%08lx)\n",
name, nh->value.addr.seg, nh->value.addr.off, value->addr.seg, value->addr.off);
#endif
nh->value.addr = value->addr;
if( nh->value.type == NULL && value->type != NULL )
{
nh->value.type = value->type;
nh->value.cookie = value->cookie;
}
/* it may happen that the same symbol is defined in several compilation
* units, but the linker decides to merge it into a single instance.
* in that case, we don't clear the invalid flag for all the compilation
* units (N_GSYM), and wait to get the symbol from the symtab
*/
if ((flags & SYM_INVALID) == 0)
nh->flags &= ~SYM_INVALID;
return nh;
}
if (nh->value.addr.seg == value->addr.seg &&
nh->value.addr.off == value->addr.off &&
strcmp(name, nh->name) == 0 )
{
return nh;
}
}
#if 0
DEBUG_Printf(DBG_CHN_TRACE, "adding symbol (%s) from file '%s' at 0x%04lx:%08lx\n",
name, source, value->addr.seg, value->addr.off);
#endif
/*
* 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 *) DBG_alloc(sizeof(struct name_hash));
new->value = *value;
new->name = DBG_strdup(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 = DBG_strdup(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 ) {
int len = strlen(source);
if (len > 2 && source[len-2] == '.' && source[len-1] == 's') {
char* c = strrchr(source - 2, '/');
if (c != NULL) {
if (strcmp(c + 1, "asmrelay.s") == 0)
new->flags |= SYM_TRAMPOLINE;
}
}
}
sortlist_valid = FALSE;
return new;
}
BOOL 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 = DBG_realloc(nh->local_vars,
nh->locals_alloc * sizeof(WineLocals));
}
if( nh->n_lines != nh->lines_alloc )
{
nh->lines_alloc = nh->n_lines;
nh->linetab = DBG_realloc(nh->linetab,
nh->lines_alloc * sizeof(WineLineNo));
}
return TRUE;
}
/***********************************************************************
* DEBUG_GetSymbolValue
*
* Get the address of a named symbol.
*/
static int DEBUG_GSV_Helper(const char* name, const int lineno,
DBG_VALUE* value, int num, int bp_flag)
{
struct name_hash* nh;
int i = 0;
DBG_ADDR addr;
for (nh = name_hash_table[name_hash(name)]; nh; nh = nh->next)
{
if ((nh->flags & SYM_INVALID) != 0) continue;
if (!strcmp(nh->name, name) && DEBUG_GetLineNumberAddr( nh, lineno, &addr, bp_flag ))
{
if (i >= num) return num + 1;
value[i].addr = addr;
value[i].type = nh->value.type;
value[i].cookie = nh->value.cookie;
i++;
}
}
return i;
}
BOOL DEBUG_GetSymbolValue( const char * name, const int lineno,
DBG_VALUE *rtn, int bp_flag )
{
#define NUMDBGV 10
/* FIXME: NUMDBGV should be made variable */
DBG_VALUE value[NUMDBGV];
DBG_VALUE vtmp;
int num, i;
num = DEBUG_GSV_Helper(name, lineno, value, NUMDBGV, bp_flag);
if (!num && (name[0] != '_'))
{
char buffer[256];
assert(strlen(name) < sizeof(buffer) - 2); /* one for '_', one for '\0' */
buffer[0] = '_';
strcpy(buffer + 1, name);
num = DEBUG_GSV_Helper(buffer, lineno, value, NUMDBGV, bp_flag);
}
/* now get the local symbols if any */
if (DEBUG_GetStackSymbolValue(name, &vtmp) && num < NUMDBGV)
{
value[num] = vtmp;
num++;
}
if (num == 0) {
return FALSE;
} else if (!DEBUG_interactiveP || num == 1) {
i = 0;
} else {
char* ptr;
if (num == NUMDBGV+1) {
DEBUG_Printf(DBG_CHN_MESG, "Too many addresses for symbol '%s', limiting the first %d\n", name, NUMDBGV);
num = NUMDBGV;
}
DEBUG_Printf(DBG_CHN_MESG, "Many symbols with name '%s', choose the one you want (<cr> to abort):\n", name);
for (i = 0; i < num; i++) {
DEBUG_Printf(DBG_CHN_MESG, "[%d]: ", i + 1);
DEBUG_PrintAddress( &value[i].addr, DEBUG_GetSelectorType(value[i].addr.seg), TRUE);
DEBUG_Printf(DBG_CHN_MESG, "\n");
}
do {
ptr = readline("=> ");
if (!*ptr) return FALSE;
i = atoi(ptr);
if (i < 1 || i > num)
DEBUG_Printf(DBG_CHN_MESG, "Invalid choice %d\n", i);
} while (i < 1 || i > num);
/* The array is 0-based, but the choices are 1..n, so we have to subtract one before returning. */
i--;
}
*rtn = value[i];
return TRUE;
}
/***********************************************************************
* DEBUG_GetLineNumberAddr
*
* Get the address of a named symbol.
*/
BOOL DEBUG_GetLineNumberAddr( const struct name_hash * nh, const int lineno,
DBG_ADDR *addr, int bp_flag )
{
int i;
if( lineno == -1 )
{
*addr = nh->value.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.
*/
BOOL DEBUG_SetSymbolValue( const char * name, const DBG_VALUE *value )
{
char buffer[256];
struct name_hash *nh;
assert(value->cookie == DV_TARGET || value->cookie == DV_HOST);
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->value = *value;
nh->flags &= ~SYM_INVALID;
#ifdef __i386__
DEBUG_FixAddress( &nh->value.addr, DEBUG_context.SegDs );
#endif
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];
unsigned val;
DBG_MODULE* module;
char modbuf[256];
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]->value.addr.seg > addr->seg
|| ( addr_sorttab[0]->value.addr.seg == addr->seg
&& addr_sorttab[0]->value.addr.off > addr->off) )
{
nearest = NULL;
}
else if( addr_sorttab[high - 1]->value.addr.seg < addr->seg
|| ( addr_sorttab[high - 1]->value.addr.seg == addr->seg
&& addr_sorttab[high - 1]->value.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]->value.addr.seg ==
addr_sorttab[mid]->value.addr.seg)
&& (addr_sorttab[mid - 1]->value.addr.off ==
addr_sorttab[mid]->value.addr.off)
&& (addr_sorttab[mid - 1]->linetab != NULL) )
{
mid--;
}
}
if( (mid < sorttab_nsym - 1)
&& (addr_sorttab[mid]->linetab == NULL) )
{
if( (addr_sorttab[mid + 1]->value.addr.seg ==
addr_sorttab[mid]->value.addr.seg)
&& (addr_sorttab[mid + 1]->value.addr.off ==
addr_sorttab[mid]->value.addr.off)
&& (addr_sorttab[mid + 1]->linetab != NULL) )
{
mid++;
}
}
nearest = addr_sorttab[mid];
#if 0
DEBUG_Printf(DBG_CHN_MESG, "Found %x:%x when looking for %x:%x %x %s\n",
addr_sorttab[mid ]->value.addr.seg,
addr_sorttab[mid ]->value.addr.off,
addr->seg, addr->off,
addr_sorttab[mid ]->linetab,
addr_sorttab[mid ]->name);
#endif
break;
}
if( (addr_sorttab[mid]->value.addr.seg < addr->seg)
|| ( addr_sorttab[mid]->value.addr.seg == addr->seg
&& addr_sorttab[mid]->value.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, ", ");
}
DEBUG_READ_MEM_VERBOSE(ptr, &val, sizeof(val));
sprintf(argtmp, "%s=0x%x", nearest->local_vars[i].name, val);
strcat(arglist, argtmp);
}
if( arglist[0] == '(' )
{
strcat(arglist, ")");
}
}
module = DEBUG_FindModuleByAddr((void*)DEBUG_ToLinear(addr), DMT_UNKNOWN);
if (module) {
char* ptr = strrchr(module->module_name, '/');
if (!ptr++) ptr = module->module_name;
sprintf( modbuf, " in %s", ptr);
}
else
modbuf[0] = '\0';
if( (nearest->sourcefile != NULL) && (flag == TRUE)
&& (addr->off - nearest->value.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->value.addr.off)
sprintf( name_buffer, "%s%s [%s%s]%s", nearest->name,
arglist, sourcefile, lineinfo, modbuf);
else
sprintf( name_buffer, "%s+0x%lx%s [%s%s]%s", nearest->name,
addr->off - nearest->value.addr.off,
arglist, sourcefile, lineinfo, modbuf );
}
else
{
if (addr->off == nearest->value.addr.off)
sprintf( name_buffer, "%s%s%s", nearest->name, arglist, modbuf);
else {
if (addr->seg && (nearest->value.addr.seg!=addr->seg))
return NULL;
else
sprintf( name_buffer, "%s+0x%lx%s%s", nearest->name,
addr->off - nearest->value.addr.off, arglist, modbuf);
}
}
return name_buffer;
}
/***********************************************************************
* DEBUG_ReadSymbolTable
*
* Read a symbol file into the hash table.
*/
void DEBUG_ReadSymbolTable( const char* filename )
{
FILE * symbolfile;
DBG_VALUE value;
char type;
char * cpnt;
char buffer[256];
char name[256];
if (!(symbolfile = fopen(filename, "r")))
{
DEBUG_Printf( DBG_CHN_WARN, "Unable to open symbol table %s\n", filename );
return;
}
DEBUG_Printf( DBG_CHN_MESG, "Reading symbols from file %s\n", filename );
value.type = NULL;
value.addr.seg = 0;
value.addr.off = 0;
value.cookie = DV_TARGET;
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;
if (sscanf(buffer, "%lx %c %s", &value.addr.off, &type, name) == 3)
DEBUG_AddSymbol( name, &value, NULL, SYM_WINE );
}
fclose(symbolfile);
}
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 = DBG_realloc(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->value.addr.seg;
func->linetab[func->n_lines].pc_offset.off = func->value.addr.off + offset;
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 = DBG_realloc(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 = DBG_strdup(name);
func->local_vars[func->n_locals].type = NULL;
func->n_locals++;
return &func->local_vars[func->n_locals - 1];
}
void
DEBUG_DumpHashInfo(void)
{
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++;
}
DEBUG_Printf(DBG_CHN_MESG, "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]->value.addr.seg > addr->seg
|| ( addr_sorttab[0]->value.addr.seg == addr->seg
&& addr_sorttab[0]->value.addr.off > addr->off) )
{
nearest = NULL;
}
else if( addr_sorttab[high - 1]->value.addr.seg < addr->seg
|| ( addr_sorttab[high - 1]->value.addr.seg == addr->seg
&& addr_sorttab[high - 1]->value.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]->value.addr.seg ==
addr_sorttab[mid]->value.addr.seg)
&& (addr_sorttab[mid - 1]->value.addr.off ==
addr_sorttab[mid]->value.addr.off)
&& (addr_sorttab[mid - 1]->linetab != NULL) )
{
mid--;
}
}
if( (mid < sorttab_nsym - 1)
&& (addr_sorttab[mid]->linetab == NULL) )
{
if( (addr_sorttab[mid + 1]->value.addr.seg ==
addr_sorttab[mid]->value.addr.seg)
&& (addr_sorttab[mid + 1]->value.addr.off ==
addr_sorttab[mid]->value.addr.off)
&& (addr_sorttab[mid + 1]->linetab != NULL) )
{
mid++;
}
}
nearest = addr_sorttab[mid];
#if 0
DEBUG_Printf(DBG_CHN_MESG, "Found %x:%x when looking for %x:%x %x %s\n",
addr_sorttab[mid ]->value.addr.seg,
addr_sorttab[mid ]->value.addr.off,
addr->seg, addr->off,
addr_sorttab[mid ]->linetab,
addr_sorttab[mid ]->name);
#endif
break;
}
if( (addr_sorttab[mid]->value.addr.seg < addr->seg)
|| ( addr_sorttab[mid]->value.addr.seg == addr->seg
&& addr_sorttab[mid]->value.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->value.addr.off == addr->off && nearest->n_lines > 1 )
{
return NOT_ON_LINENUMBER;
}
if( (nearest->sourcefile != NULL)
&& (addr->off - nearest->value.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 )
{
DEBUG_Printf(DBG_CHN_MESG, "No such function %s in %s\n", name, filename);
}
else
{
DEBUG_Printf(DBG_CHN_MESG, "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
BOOL DEBUG_GetStackSymbolValue( const char * name, DBG_VALUE *value )
{
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->value.addr.off)
< curr_func->local_vars[i].pc_start) )
{
continue;
}
if( (curr_func->local_vars[i].pc_end != 0)
&& ((eip - curr_func->value.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. Point to DEBUG_context field.
*/
assert(curr_func->local_vars[i].regno - 1 < sizeof(reg_ofs)/sizeof(reg_ofs[0]));
value->addr.off = ((DWORD)&DEBUG_context) +
reg_ofs[curr_func->local_vars[i].regno - 1];
value->cookie = DV_HOST;
}
else
{
value->addr.off = ebp + curr_func->local_vars[i].offset;
value->cookie = DV_TARGET;
}
value->addr.seg = 0;
value->type = curr_func->local_vars[i].type;
return TRUE;
}
}
return FALSE;
}
int
DEBUG_InfoLocals(void)
{
struct name_hash * curr_func;
unsigned int ebp;
unsigned int eip;
int i;
unsigned int * ptr;
unsigned int val;
if( DEBUG_GetCurrentFrame(&curr_func, &eip, &ebp) == FALSE )
{
return FALSE;
}
DEBUG_Printf(DBG_CHN_MESG, "%s:\n", curr_func->name);
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->value.addr.off)
< curr_func->local_vars[i].pc_start) )
{
continue;
}
if( (curr_func->local_vars[i].pc_end != 0)
&& ((eip - curr_func->value.addr.off)
> curr_func->local_vars[i].pc_end) )
{
continue;
}
DEBUG_PrintTypeCast(curr_func->local_vars[i].type);
if( curr_func->local_vars[i].regno != 0 )
{
ptr = (unsigned int *)(((DWORD)&DEBUG_context)
+ reg_ofs[curr_func->local_vars[i].regno - 1]);
DEBUG_Printf(DBG_CHN_MESG, " %s (optimized into register $%s) == 0x%8.8x\n",
curr_func->local_vars[i].name,
reg_name[curr_func->local_vars[i].regno - 1],
*ptr);
}
else
{
DEBUG_READ_MEM_VERBOSE((void*)(ebp + curr_func->local_vars[i].offset),
&val, sizeof(val));
DEBUG_Printf(DBG_CHN_MESG, " %s == 0x%8.8x\n",
curr_func->local_vars[i].name, val);
}
}
return TRUE;
}
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->value.addr;
return TRUE;
}
int DEBUG_SetLocalSymbolType(struct wine_locals * sym, struct datatype * type)
{
sym->type = type;
return TRUE;
}