| /* ----------------------------------------------------------------------------- |
| * stab.c |
| * |
| * This file reads stabs data and looks for various properties of a |
| * given symbol. |
| * |
| * Author(s) : David Beazley (beazley@cs.uchicago.edu) |
| * |
| * Copyright (C) 2000. The University of Chicago. |
| * |
| * 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 |
| * |
| * See the file COPYING for a complete copy of the LGPL. |
| * ----------------------------------------------------------------------------- */ |
| |
| #include "wad.h" |
| |
| static char cvs[] = "$Header$"; |
| |
| /* stabs data structure. This appears to be somewhat universal. */ |
| typedef struct Stab { |
| unsigned n_strx; /* index into file string table */ |
| unsigned char n_type; /* type flag (N_TEXT,..) */ |
| char n_other; /* used by N_SLINE stab */ |
| unsigned short n_desc; /* see stabs documentation */ |
| unsigned n_value; /* value of symbol (or sdb offset) */ |
| } Stab; |
| |
| /* stabs data types used by this module */ |
| |
| #define N_UNDF 0x0 /* undefined */ |
| #define N_FUN 0x24 /* function */ |
| #define N_OBJ 0x38 /* object file path */ |
| #define N_RSYM 0x40 /* Register symbol */ |
| #define N_SLINE 0x44 /* Source line */ |
| #define N_SO 0x64 /* Source file name */ |
| #define N_LSYM 0x80 /* Local symbol */ |
| #define N_PSYM 0xa0 /* Parameter */ |
| #define N_LBRAC 0xc0 /* Left brace */ |
| #define N_RBRAC 0xe0 /* Right brace */ |
| |
| /* ----------------------------------------------------------------------------- |
| * stabs type handler |
| * |
| * Type names are defined as N_LSYM types. We need to keep a hash table of |
| * logical type names and stabs type names. |
| * |
| * We also need to keep a hash table of stabs types. |
| * ----------------------------------------------------------------------------- */ |
| |
| typedef struct stabtype { |
| char *name; |
| char *value; |
| struct stabtype *next; |
| int visit; |
| } stabtype; |
| |
| #define HASH_SIZE 113 |
| |
| static int stab_type_init = 0; |
| static stabtype *lnames[HASH_SIZE]; /* Hash of local names */ |
| static stabtype *deadnames[HASH_SIZE]; /* Hash of dead names */ |
| |
| /* Initialize the hash table */ |
| |
| static void init_hash() { |
| int i; |
| stabtype *s, *sp = 0; |
| |
| for (i = 0; i < HASH_SIZE; i++) { |
| if (stab_type_init) { |
| /* Add stabs to dead list */ |
| s = lnames[i]; |
| sp = 0; |
| while (s) { |
| sp = s; |
| s = s->next; |
| } |
| if (sp) { |
| sp->next = deadnames[i]; |
| deadnames[i] = lnames[i]; |
| } |
| } |
| lnames[i] = 0; |
| } |
| stab_type_init = 1; |
| } |
| |
| static int thash(char *name) { |
| unsigned int h = 0; |
| int i; |
| for (i = 0; i < 8 && (*name); i++, name++) { |
| h = ((h << 7) + *name); |
| } |
| return (h % HASH_SIZE); |
| } |
| |
| /* Add a symbol to the hash */ |
| |
| static void type_add(char *name, char *value) { |
| int h; |
| stabtype *s; |
| char sc =0; |
| char *v; |
| char *vr; |
| char *split; |
| |
| if (!stab_type_init) { |
| init_hash(); |
| stab_type_init = 1; |
| } |
| |
| /* Split the "value" up into a type name and a value */ |
| |
| split = strchr(value,'='); |
| if (value[0] != '(') split = 0; |
| if (split) { |
| sc = *split; |
| v = value; |
| *split = 0; |
| vr = split+1; |
| } else { |
| v = value; |
| sc = 0; |
| vr = 0; |
| } |
| |
| h = thash(name); |
| s = lnames[h]; |
| while (s) { |
| if (strcmp(s->name,name) == 0) { |
| if (strcmp(s->value,v)) { |
| s->value = wad_string_lookup(v); |
| } |
| goto add_more; |
| } |
| s = s->next; |
| } |
| s = deadnames[h]; |
| if (!s) { |
| s = (stabtype *) wad_malloc(sizeof(stabtype)); |
| } else { |
| deadnames[h] = s->next; |
| } |
| s->name = wad_string_lookup(name); |
| s->value = wad_string_lookup(v); |
| s->next = lnames[h]; |
| s->visit = 0; |
| lnames[h] = s; |
| |
| /* Now take a look at the value. If it is contains other types, we might be able to define more stuff */ |
| add_more: |
| if (vr) { |
| /* There is a mapping to another type */ |
| type_add(v,vr); |
| } |
| } |
| |
| static |
| char *type_resolve(char *name) { |
| int h; |
| stabtype *s; |
| h = thash(name); |
| s = lnames[h]; |
| while(s) { |
| if (strcmp(s->name,name) == 0) { |
| if (!s->visit) { |
| char *c; |
| /* The visit flag is set so that we don't get in infinite loops */ |
| s->visit = 1; |
| c = type_resolve(s->value); |
| s->visit = 0; |
| return c; |
| } else { |
| return name; |
| } |
| } |
| s = s->next; |
| } |
| return name; |
| } |
| |
| /* This function tries to resolve base stabs types into a machine equivalent */ |
| static |
| int type_typecode(char *name) { |
| char *range; |
| |
| if (name[0] == '*') { |
| return WAD_TYPE_POINTER; |
| } |
| |
| range = strchr(name,';'); |
| if (!range) return WAD_TYPE_UNKNOWN; |
| range++; |
| |
| if (name[0] == 'r') { |
| /* GNU-style range specifiers */ |
| if ( |
| (strcmp(range,"0000000000000;0037777777777;") == 0) |
| ) { |
| return WAD_TYPE_UINT32; |
| } |
| if ( |
| (strcmp(range,"0020000000000;0017777777777;") == 0) |
| ) { |
| return WAD_TYPE_INT32; |
| } |
| if ( |
| (strcmp(range,"-32768;32767;") == 0) |
| ) { |
| return WAD_TYPE_INT16; |
| } |
| if ( |
| (strcmp(range,"0;65535;") == 0) |
| ) { |
| return WAD_TYPE_UINT16; |
| } |
| if ( |
| (strcmp(range,"0;127;") == 0) |
| ) { |
| return WAD_TYPE_CHAR; |
| } |
| if ( |
| (strcmp(range,"-128;127;") == 0) |
| ) { |
| return WAD_TYPE_INT8; |
| } |
| if ( |
| (strcmp(range,"0;255;") == 0) |
| ) { |
| return WAD_TYPE_UINT8; |
| } |
| if ( |
| (strcmp(range,"4;0;") == 0) |
| ) { |
| return WAD_TYPE_FLOAT; |
| } |
| if ( |
| (strcmp(range,"8;0;") == 0) |
| ) { |
| return WAD_TYPE_DOUBLE; |
| } |
| } |
| /* Traditional built-in types */ |
| if (strcmp(name,"bs4;0;32;") == 0) { |
| return WAD_TYPE_INT32; |
| } |
| if (strcmp(name,"bs2;0;16;") == 0) { |
| return WAD_TYPE_INT16; |
| } |
| if (strcmp(name,"bs1;0;8;") == 0) { |
| return WAD_TYPE_INT8; |
| } |
| if (strcmp(name,"bsc1;0;8;") == 0) { |
| return WAD_TYPE_CHAR; |
| } |
| if (strcmp(name,"bu4;0;32;") == 0) { |
| return WAD_TYPE_UINT32; |
| } |
| if (strcmp(name,"bu2;0;16;") == 0) { |
| return WAD_TYPE_UINT16; |
| } |
| if (strcmp(name,"bu1;0;8;") == 0) { |
| return WAD_TYPE_UINT8; |
| } |
| if (strcmp(name,"R1;4;") == 0) { |
| return WAD_TYPE_FLOAT; |
| } |
| if (strcmp(name,"R2;8;") == 0) { |
| return WAD_TYPE_DOUBLE; |
| } |
| return WAD_TYPE_UNKNOWN; |
| } |
| |
| static void types_print() { |
| stabtype *s; |
| int i; |
| for (i = 0; i < HASH_SIZE; i++) { |
| s = lnames[i]; |
| while (s) { |
| wad_printf("%20s %s\n", s->name, s->value); |
| s = s->next; |
| } |
| } |
| } |
| |
| void wad_stab_debug() { |
| /* types_print();*/ |
| } |
| |
| /* ----------------------------------------------------------------------------- |
| * match_stab_symbol() |
| * |
| * Match a stabs symbol name against a stab string. The stab string may contain |
| * extra information delimited by a colon which is not used in the comparsion. |
| * Returns 1 on match, 0 on mismatch. |
| * ----------------------------------------------------------------------------- */ |
| |
| static int |
| match_stab_symbol(char *symbol, char *stabtext, int slen) { |
| if (strcmp(symbol,stabtext) == 0) { |
| return 1; |
| } |
| if ((strncmp(symbol, stabtext, slen) == 0) && (*(stabtext+slen) == ':')) return 1; |
| return 0; |
| } |
| |
| static char * |
| stab_string_parm(char *str) { |
| return strchr(str,':'); |
| } |
| |
| /* ----------------------------------------------------------------------------- |
| * stab_symbol(Stab *s, char *stabstr) |
| * |
| * Process stab symbol specifier N_LSYM |
| * ----------------------------------------------------------------------------- */ |
| |
| static void |
| stab_symbol(Stab *s, char *stabstr) { |
| char *str; |
| char *pstr; |
| char name[1024]; |
| char value[65536]; |
| |
| str = stabstr+s->n_strx; |
| pstr = stab_string_parm(str); |
| if (!pstr) return; |
| |
| strncpy(name,str, pstr-str); |
| name[(int)(pstr-str)] = 0; |
| if ((pstr[1] == 't') || (pstr[1] == 'p') || (pstr[1] == 'r')) { |
| /* A stabs type definition */ |
| /* printf("stab lsym: other=%d, desc=%d, value=%d, str='%s'\n", s->n_other,s->n_desc,s->n_value, |
| stabstr+s->n_strx); */ |
| /* wad_printf("name = '%s', pstr='%s'\n", name, pstr+2); */ |
| wad_strcpy(value,pstr+2); |
| type_add(name,value); |
| } |
| } |
| |
| |
| /* ----------------------------------------------------------------------------- |
| * scan_function() |
| * |
| * Collect stabs data for a function definition. |
| * ----------------------------------------------------------------------------- */ |
| |
| static int |
| scan_function(Stab *s, char *stabstr, int ns, WadFrame *f) { |
| int i; |
| unsigned long offset; |
| int get_parms = 1; |
| int nbrace = 0; |
| |
| offset = f->pc - f->sym_base; |
| if (wad_debug_mode & DEBUG_STABS) { |
| wad_printf("---[ %s ] --------------\n", f->sym_name); |
| } |
| |
| for (i = 0; i < ns; i++,s++) { |
| if (wad_debug_mode & DEBUG_STABS) { |
| wad_printf(" %10d %10x %10d %10d %10d: '%s'\n", s->n_strx, s->n_type, s->n_other, s->n_desc, s->n_value, |
| stabstr+s->n_strx); |
| |
| } |
| |
| if ((s->n_type == N_UNDF) || (s->n_type == N_SO) || /* (s->n_type == N_FUN) || */ |
| (s->n_type == N_OBJ)) return i; |
| |
| if ((s->n_type == N_FUN) && !(strlen(stabstr+s->n_strx))) return 1; |
| |
| if (s->n_type == N_LBRAC) { |
| nbrace++; |
| get_parms = 0; |
| } |
| if (s->n_type == N_RBRAC) { |
| nbrace--; |
| if (nbrace <= 0) return i; |
| } |
| /* Local variable declaration */ |
| |
| if (s->n_type == N_LSYM) { |
| /* This might be a local variable definition */ |
| /* wad_printf("local: n_value = %d, offset = %d\n", s->n_value, offset);*/ |
| if (s->n_desc <= f->loc_line) |
| { |
| /* Okay. We can pay attention to it */ |
| char *pname; |
| char *c; |
| int len; |
| WadLocal *arg, *a; |
| pname = stabstr+s->n_strx; |
| c = strchr(pname,':'); |
| if (*(c+1) != '(') continue; |
| if (c) { |
| len = (c-pname); |
| } else { |
| len = strlen(pname); |
| } |
| /* printf("local\n"); */ |
| stab_symbol(s,stabstr); |
| a = f->debug_locals; |
| while (a) { |
| if ((strncmp(a->name,pname,len) == 0) && (strlen(a->name) == len)) { |
| /* We already saw this argument. Given a choice between a register and a stack |
| argument. We will choose the stack version */ |
| a->loc = PARM_STACK; |
| a->stack = s->n_value; |
| break; |
| } |
| a = a->next; |
| } |
| if (a) continue; /* We got an argument match. Just skip to the next stab */ |
| arg = (WadLocal *) wad_malloc(sizeof(WadLocal)); |
| { |
| char t = pname[len]; |
| pname[len] = 0; |
| arg->name = wad_string_lookup(pname); |
| pname[len] = t; |
| } |
| arg->loc = PARM_STACK; |
| arg->line = s->n_desc; |
| arg->stack = s->n_value; |
| arg->type = 0; |
| arg->next = 0; |
| { |
| char tname[128]; |
| char *t = tname; |
| |
| c+=1; |
| while ((*c) && (*c != '=')) { |
| *t++ = *c++; |
| } |
| *t = 0; |
| t = type_resolve(tname); |
| arg->type = type_typecode(t); |
| if (wad_debug_mode & DEBUG_STABS) { |
| wad_printf("type_resolve '%s' -> '%s' (%d)\n", tname, t, arg->type); |
| } |
| } |
| if (f->debug_locals) { |
| f->debug_lastlocal->next = arg; |
| f->debug_lastlocal = arg; |
| } else { |
| f->debug_locals = arg; |
| f->debug_lastlocal = arg; |
| f->debug_nlocals= 0; |
| } |
| f->debug_nlocals++; |
| } |
| } |
| |
| if (s->n_type == N_SLINE) { |
| get_parms = 0; |
| if (s->n_value <= offset) { |
| f->loc_line = s->n_desc; |
| } |
| } else if (((s->n_type == N_PSYM) || (s->n_type == N_RSYM)) && get_parms) { |
| /* Parameter counting */ |
| char *pname; |
| char *c; |
| int len; |
| WadLocal *arg; |
| pname = stabstr+s->n_strx; |
| c = strchr(pname,':'); |
| if (c) { |
| len = (c-pname); |
| } else { |
| len = strlen(pname); |
| } |
| /* Get type information */ |
| |
| stab_symbol(s,stabstr); |
| |
| /* Check if the argument was already used */ |
| /* In this case, the first stab simply identifies an argument. The second |
| one identifies its location for the debugger */ |
| |
| { |
| /* Need to do some fix up for linux here */ |
| WadLocal *a = f->debug_args; |
| while (a) { |
| if ((strncmp(a->name,pname,len) == 0) && (strlen(a->name) == len)) { |
| /* We already saw this argument. Given a choice between a register and a stack |
| argument. We will choose the stack version */ |
| |
| if (a->loc == PARM_STACK) { |
| break; |
| } |
| /* Go ahead and use the new argument */ |
| if (s->n_type == N_RSYM) { |
| a->loc = PARM_REGISTER; |
| a->reg = s->n_value; |
| } else { |
| a->loc = PARM_STACK; |
| a->stack = s->n_value; |
| } |
| break; |
| } |
| a = a->next; |
| } |
| if (a) continue; /* We got an argument match. Just skip to the next stab */ |
| } |
| |
| arg = (WadLocal *) wad_malloc(sizeof(WadLocal)); |
| { |
| char t = pname[len]; |
| pname[len] = 0; |
| arg->name = wad_string_lookup(pname); |
| pname[len] = t; |
| } |
| if (s->n_type == N_RSYM) { |
| arg->loc = PARM_REGISTER; |
| arg->reg = s->n_value; |
| arg->stack = 0; |
| } else { |
| arg->loc = PARM_STACK; |
| arg->line = s->n_desc; |
| arg->stack = s->n_value; |
| } |
| arg->type = 0; |
| arg->next = 0; |
| { |
| char tname[128]; |
| char *t = tname; |
| |
| c+=2; |
| while ((*c) && (*c != '=')) { |
| *t++ = *c++; |
| } |
| *t = 0; |
| t = type_resolve(tname); |
| arg->type = type_typecode(t); |
| if (wad_debug_mode & DEBUG_STABS) { |
| wad_printf("type_resolve '%s' -> '%s' (%d)\n", tname, t, arg->type); |
| } |
| } |
| if (f->debug_args) { |
| f->debug_lastarg->next = arg; |
| f->debug_lastarg = arg; |
| } else { |
| f->debug_args = arg; |
| f->debug_lastarg = arg; |
| f->debug_nargs= 0; |
| } |
| f->debug_nargs++; |
| } |
| } |
| return i; |
| } |
| |
| /* Given a stabs data segment (obtained somehow), this function tries to |
| collect as much information as it can about a given symbol. |
| |
| s points to the stab data. stabstr points to the stab string section, |
| ns is the size of the stab section, symbol is the item of interest, |
| and offset is the offset in the object file of the symbol |
| |
| Note: this function may recurse upon itself if there are multiple |
| stabs sections. |
| |
| Note: If a symbol corresponds to a local symbol, it's entirely possible |
| that the only stabs data we will find is a file specifier. In this case, |
| */ |
| |
| int |
| wad_search_stab(void *sp, int size, char *stabstr, WadFrame *f) { |
| Stab *s; |
| int ns; |
| int i; |
| int found = 0; |
| |
| char *file, *lastfile = 0; |
| |
| char srcfile[MAX_PATH]; |
| char objfile[MAX_PATH]; |
| |
| /* It appears to be necessary to clear the types table on each new stabs section */ |
| |
| init_hash(); |
| |
| if (!f->sym_name) return 0; |
| |
| s = (Stab *) sp; /* Stabs data */ |
| ns = size/sizeof(Stab); /* number of stabs */ |
| |
| srcfile[0] = 0; |
| objfile[0] = 0; |
| |
| for (i = 0; i < ns; i++, s++) { |
| if (wad_debug_mode & DEBUG_STABS) { |
| /* wad_printf(" %10d %10x %10d %10d %10d: '%s'\n", s->n_strx, s->n_type, s->n_other, s->n_desc, s->n_value, |
| stabstr+s->n_strx); */ |
| |
| } |
| if (s->n_type == N_LSYM) { |
| stab_symbol(s,stabstr); |
| continue; |
| } |
| if ((s->n_type == N_UNDF)) { /* && (s->n_desc >= 0)) { */ |
| /* New stabs section. We need to be a little careful here. Do a recursive |
| search of the subsection. */ |
| |
| if (wad_search_stab(s+1,s->n_desc*sizeof(Stab), stabstr, f)) { |
| return 1; |
| } |
| |
| /* On solaris, each stabs section seems to increment the stab string pointer. On Linux, |
| the linker seems to do a certain amount of optimization that results in a single |
| string table. */ |
| |
| #ifdef WAD_SOLARIS |
| stabstr += s->n_value; /* Update the string table location*/ |
| #endif |
| i += s->n_desc; |
| s += s->n_desc; |
| objfile[0] = 0; |
| srcfile[0] = 0; |
| continue; |
| } else if (s->n_type == N_SO) { |
| /* Source file specification */ |
| /* Look for directory */ |
| file = stabstr+s->n_strx; |
| if (strlen(file) && (file[strlen(file)-1] == '/')) { |
| wad_strcpy(srcfile,file); |
| } else { |
| wad_strcat(srcfile,file); |
| } |
| objfile[0] = 0; |
| /* If we have a file match, we might be looking for a local symbol. If so, |
| we'll go ahead and set the srcfile field of the frame */ |
| |
| /* We're going to check for a file match. Maybe we're looking for a local symbol */ |
| if (f->sym_file && strcmp(f->sym_file,file) == 0) { |
| found = 1; |
| } |
| lastfile = file; |
| } else if (s->n_type == N_OBJ) { |
| /* Object file specifier */ |
| if (objfile[0]) { |
| wad_strcat(objfile,"/"); |
| } |
| wad_strcat(objfile,stabstr+s->n_strx); |
| } else if (s->n_type == N_FUN) { |
| if (match_stab_symbol(f->sym_name, stabstr+s->n_strx, f->sym_nlen)) { |
| if (!f->sym_file || (strcmp(f->sym_file,lastfile) == 0)) { |
| int n; |
| /* Go find debugging information for the function */ |
| n = scan_function(s+1, stabstr, ns -i - 1, f); |
| f->loc_srcfile = wad_string_lookup(srcfile); |
| f->loc_objfile = wad_string_lookup(objfile); |
| return 1; |
| } |
| } |
| } |
| } |
| /* If found, but no other debugging information was filled in, go ahead and copy the |
| source and objfile information */ |
| |
| if ((found) && (!f->debug_check)) { |
| f->loc_srcfile = wad_string_lookup(srcfile); |
| f->loc_objfile = wad_string_lookup(objfile); |
| } |
| return found; |
| } |
| |
| |
| |
| |