| /* |
| * File hash.c - generate hash tables for Wine debugger symbols |
| * |
| * Copyright (C) 1993, Eric Youngdale. |
| * |
| * 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 "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, local = -1; |
| |
| num = DEBUG_GSV_Helper(name, lineno, value, NUMDBGV, bp_flag); |
| if (!num && (name[0] != '_')) |
| { |
| char buffer[512]; |
| |
| 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; |
| local = num; |
| num++; |
| } |
| |
| if (num == 0) { |
| return FALSE; |
| } else if (!DEBUG_InteractiveP || num == 1) { |
| i = 0; |
| } else { |
| char buffer[256]; |
| |
| 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); |
| if (i == local) { |
| struct name_hash*func; |
| unsigned int ebp; |
| unsigned int eip; |
| |
| if (DEBUG_GetCurrentFrame(&func, &eip, &ebp)) |
| DEBUG_Printf(DBG_CHN_MESG, "local variable of %s in %s\n", func->name, func->sourcefile); |
| else |
| DEBUG_Printf(DBG_CHN_MESG, "local variable\n"); |
| } else { |
| DEBUG_PrintAddress( &value[i].addr, DEBUG_GetSelectorType(value[i].addr.seg), TRUE); |
| DEBUG_Printf(DBG_CHN_MESG, "\n"); |
| } |
| } |
| do { |
| i = 0; |
| if (DEBUG_ReadLine("=> ", buffer, sizeof(buffer), FALSE, FALSE)) |
| { |
| i = atoi(buffer); |
| 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; |
| } |