/* * File hash.c - generate hash tables for Wine debugger symbols * * Copyright (C) 1993, Eric Youngdale. */ #include "config.h" #include #include #include #include #include #include "neexe.h" #include "module.h" #include "process.h" #include "selectors.h" #include "debugger.h" #include "toolhelp.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_ADDR addr; unsigned short flags; unsigned short breakpoint_offset; unsigned int symbol_size; }; static BOOL 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; inext) { if( (nh->flags & SYM_INVALID) == 0 ) nsym++; else fprintf( stderr, "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; inext) { 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_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; } /* 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->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 *) DBG_alloc(sizeof(struct name_hash)); new->addr = *addr; 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 ) { 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; } 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. */ BOOL 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. */ BOOL 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. */ BOOL 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( HMODULE 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) { 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 ); } } DEBUG_RegisterDebugInfo(hModule, name); #undef RVA } typedef struct tag_lmr{ char* module_name; BOOL is16; struct tag_lmr* next; } DBG_LoadedModuleRef; typedef struct { int rowcount; int first; const char* pfx; } DBG_LEPData; static BOOL DEBUG_LEPHelper(const char* mod_name, BOOL is16, DBG_LEPData* lep) { static DBG_LoadedModuleRef* lmr = NULL; DBG_LoadedModuleRef* p; DBG_LoadedModuleRef** pp1; DBG_LoadedModuleRef* p2; int len = strlen(mod_name); int cmp; for (p = lmr; p; p = p->next) { cmp = strcasecmp(p->module_name, mod_name); if (cmp == 0 && p->is16 == is16) return FALSE; if (cmp >= 0) break; } if (!lep->first) { if (lep->pfx) fprintf( stderr, lep->pfx ); fprintf( stderr, " " ); lep->first++; lep->rowcount = 3; } if ((lep->rowcount + len) > 76) { fprintf( stderr, "\n "); lep->rowcount = 3; } fprintf( stderr, " %s", mod_name ); lep->rowcount += len + 1; p = DBG_alloc(sizeof(*lmr)); p->module_name = DBG_strdup(mod_name); p->is16 = is16; p2 = NULL; for (pp1 = &lmr; *pp1; pp1 = &(*pp1)->next) { if (strcasecmp((*pp1)->module_name, mod_name) > 0) break; p2 = *pp1; } if (p2 == NULL) { p->next = lmr; lmr = p; } else if (*pp1 == NULL) { p->next = NULL; *pp1 = p; } else { p->next = *pp1; p2->next = p; } return TRUE; } /*********************************************************************** * DEBUG_LoadEntryPoints * * Load the entry points of all the modules into the hash table. */ int DEBUG_LoadEntryPoints(const char* pfx) { MODULEENTRY entry; NE_MODULE* pModule; BOOL ok; WINE_MODREF*wm; DBG_LEPData lep; lep.first = 0; lep.pfx = pfx; /* FIXME: we assume that a module is never removed from memory */ for (ok = ModuleFirst16(&entry); ok; ok = ModuleNext16(&entry)) { if (!(pModule = NE_GetPtr( entry.hModule ))) continue; if (!(pModule->flags & NE_FFLAGS_WIN32) && /* NE module */ DEBUG_LEPHelper( entry.szModule, TRUE, &lep )) DEBUG_LoadEntryPoints16( entry.hModule, pModule, entry.szModule ); } for (wm=PROCESS_Current()->modref_list;wm;wm=wm->next) { if (DEBUG_LEPHelper( wm->modname, FALSE, &lep )) DEBUG_LoadEntryPoints32( wm->module, wm->modname ); } if (lep.first) fprintf( stderr, "\n" ); return lep.first; } 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->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 = 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() { int i; int depth; struct name_hash *nh; /* * Utility function to dump stats about the hash table. */ for(i=0; inext) { 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 BOOL 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. */ /* FIXME: what if regno == 0 ($eax) */ if( curr_func->local_vars[i].regno != 0 ) { /* * Register variable. Point to DEBUG_context field. */ addr->seg = 0; addr->off = ((DWORD)&DEBUG_context) + reg_ofs[curr_func->local_vars[i].regno]; addr->type = curr_func->local_vars[i].type; return TRUE; } 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 ) { ptr = (unsigned int *) (((DWORD)&DEBUG_context) + reg_ofs[curr_func->local_vars[i].regno]); fprintf(stderr, "%s:%s (optimized into register $%s) == 0x%8.8x\n", curr_func->name, curr_func->local_vars[i].name, reg_name[curr_func->local_vars[i].regno], *ptr); } 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; }