]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Adds VG_(describe_addr)() for generating symbolic descriptions of a
authorJeremy Fitzhardinge <jeremy@valgrind.org>
Tue, 14 Oct 2003 21:55:10 +0000 (21:55 +0000)
committerJeremy Fitzhardinge <jeremy@valgrind.org>
Tue, 14 Oct 2003 21:55:10 +0000 (21:55 +0000)
memory address, based on which variables are in scope at the time and
their types.

As part of this change, I restructured the symbol table parsing code,
by splitting the stabs and dwarf-specific parts into their own files.
I also added a new set of vg_symtypes.[ch] files which contains the
type system code and the core of the VG_(describe_addr)().

I've only implemented the stabs type parser.  I have not yet implemented
the DWARF2 parser.  It looks well-defined but complex.

The only skin which uses this is Helgrind at the moment.

git-svn-id: svn://svn.valgrind.org/valgrind/trunk@1926

coregrind/Makefile.am
coregrind/vg_symtab2.c
coregrind/vg_symtab2.h [new file with mode: 0644]
coregrind/vg_symtab_dwarf.c [new file with mode: 0644]
coregrind/vg_symtab_stabs.c [new file with mode: 0644]
coregrind/vg_symtypes.c [new file with mode: 0644]
coregrind/vg_symtypes.h [new file with mode: 0644]
helgrind/hg_main.c
include/vg_skin.h

index 6582ac27af50e2cf6b0fda0bcb3e73690f025141..a3efb56e9ed75e432c487b37f11e567c10d75688 100644 (file)
@@ -58,6 +58,9 @@ valgrind_so_SOURCES = \
        vg_signals.c \
        vg_startup.S \
        vg_symtab2.c \
+       vg_symtab_dwarf.c \
+       vg_symtab_stabs.c \
+       vg_symtypes.c \
        vg_syscalls.c \
        vg_syscall.S \
        vg_to_ucode.c \
@@ -84,6 +87,8 @@ lib_replace_malloc_a_SOURCES = vg_replace_malloc.c
 noinst_HEADERS = \
         vg_include.h            \
         vg_constants.h          \
+       vg_symtab2.h            \
+       vg_symtypes.h           \
        vg_unsafe.h
 
 MANUAL_DEPS = $(noinst_HEADERS) $(include_HEADERS) .in_place/libpthread.so.0
index 34b460f000b3508f39d458fc97e51d5df82298e2..51e97a58af56f02939436412ef7070a731f0cb61 100644 (file)
 */
 
 #include "vg_include.h"
+#include "vg_symtypes.h"
+#include "vg_symtab2.h"
 
 #include <elf.h>          /* ELF defns                      */
-#include <a.out.h>        /* stabs defns                    */
 
 
 /* Majorly rewritten Sun 3 Feb 02 to enable loading symbols from
 */
 
 
-/*------------------------------------------------------------*/
-/*--- Structs n stuff                                      ---*/
-/*------------------------------------------------------------*/
-
-/* A structure to hold an ELF symbol (very crudely). */
-typedef 
-   struct { 
-      Addr addr;   /* lowest address of entity */
-      UInt size;   /* size in bytes */
-      Int  nmoff;  /* offset of name in this SegInfo's str tab */
-   }
-   RiSym;
-
-/* Line count at which overflow happens, due to line numbers being stored as
- * shorts in `struct nlist' in a.out.h. */
-#define LINENO_OVERFLOW (1 << (sizeof(short) * 8))
-
-#define LINENO_BITS     20
-#define LOC_SIZE_BITS  (32 - LINENO_BITS)
-#define MAX_LINENO     ((1 << LINENO_BITS) - 1)
-
-/* Unlikely to have any lines with instruction ranges > 4096 bytes */
-#define MAX_LOC_SIZE   ((1 << LOC_SIZE_BITS) - 1)
-
-/* Number used to detect line number overflows;  if one line is 60000-odd
- * smaller than the previous, is was probably an overflow.  
- */
-#define OVERFLOW_DIFFERENCE     (LINENO_OVERFLOW - 5000)
-
-/* A structure to hold addr-to-source info for a single line.  There can be a
- * lot of these, hence the dense packing. */
-typedef
-   struct {
-      /* Word 1 */
-      Addr   addr;                  /* lowest address for this line */
-      /* Word 2 */
-      UShort size:LOC_SIZE_BITS;    /* byte size; we catch overflows of this */
-      UInt   lineno:LINENO_BITS;    /* source line number, or zero */
-      /* Word 3 */
-      UInt   fnmoff;                /* source filename; offset in this 
-                                       SegInfo's str tab */
-   }
-   RiLoc;
-
-
-/* A structure which contains information pertaining to one mapped
-   text segment. (typedef in vg_skin.h) */
-struct _SegInfo {
-   struct _SegInfo* next;
-   /* Description of the mapped segment. */
-   Addr   start;
-   UInt   size;
-   UChar* filename; /* in mallocville */
-   UInt   foffset;
-   /* An expandable array of symbols. */
-   RiSym* symtab;
-   UInt   symtab_used;
-   UInt   symtab_size;
-   /* An expandable array of locations. */
-   RiLoc* loctab;
-   UInt   loctab_used;
-   UInt   loctab_size;
-   /* An expandable array of characters -- the string table. */
-   Char*  strtab;
-   UInt   strtab_used;
-   UInt   strtab_size;
-   /* offset    is what we need to add to symbol table entries
-      to get the real location of that symbol in memory.
-      For executables, offset is zero.  
-      For .so's, offset == base_addr.
-      This seems like a giant kludge to me.
-   */
-   UInt   offset;
-
-   /* Bounds of data, BSS, PLT and GOT, so that skins can see what
-      section an address is in */
-   Addr          plt_start;
-   UInt   plt_size;
-   Addr   got_start;
-   UInt   got_size;
-   Addr   data_start;
-   UInt   data_size;
-   Addr   bss_start;
-   UInt   bss_size;
-};
-
 
 static void freeSegInfo ( SegInfo* si )
 {
+   struct strchunk *chunk, *next;
    vg_assert(si != NULL);
    if (si->filename) VG_(arena_free)(VG_AR_SYMTAB, si->filename);
    if (si->symtab)   VG_(arena_free)(VG_AR_SYMTAB, si->symtab);
    if (si->loctab)   VG_(arena_free)(VG_AR_SYMTAB, si->loctab);
-   if (si->strtab)   VG_(arena_free)(VG_AR_SYMTAB, si->strtab);
+   if (si->scopetab) VG_(arena_free)(VG_AR_SYMTAB, si->scopetab);
+
+   for(chunk = si->strchunks; chunk != NULL; chunk = next) {
+      next = chunk->next;
+      VG_(arena_free)(VG_AR_SYMTAB, chunk);
+   }
    VG_(arena_free)(VG_AR_SYMTAB, si);
 }
 
@@ -144,23 +65,28 @@ static void freeSegInfo ( SegInfo* si )
 /*------------------------------------------------------------*/
 
 /* Add a str to the string table, including terminating zero, and
-   return offset of the string in vg_strtab.  Unless it's been seen
-   recently, in which case we find the old index and return that.
-   This avoids the most egregious duplications. */
+   return pointer to the string in vg_strtab.  Unless it's been seen
+   recently, in which case we find the old pointer and return that.
+   This avoids the most egregious duplications. 
 
-static
-Int addStr ( SegInfo* si, Char* str )
+   JSGF: changed from returning an index to a pointer, and changed to
+   a chunking memory allocator rather than reallocating, so the
+   pointers are stable.
+*/
+
+Char *VG_(addStr) ( SegInfo* si, Char* str, Int len )
 {
-#  define EMPTY    0xffffffff
+#  define EMPTY    NULL
 #  define NN       5
    
    /* prevN[0] has the most recent, prevN[NN-1] the least recent */
-   static UInt     prevN[NN] = { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY };
+   static Char     *prevN[NN] = { EMPTY, EMPTY, EMPTY, EMPTY, EMPTY };
    static SegInfo* curr_si = NULL;
+   struct strchunk *chunk;
+   Int   i, space_needed;
 
-   Char* new_tab;
-   Int   space_needed, i;
-   UInt  new_sz, u;
+   if (len == -1)
+      len = VG_(strlen)(str);
 
    /* Avoid gratuitous duplication:  if we saw `str' within the last NN,
     * within this segment, return that index.  Saves about 200KB in glibc,
@@ -168,8 +94,9 @@ Int addStr ( SegInfo* si, Char* str )
    if (curr_si == si) {
       for (i = NN-1; i >= 0; i--) {
          if (EMPTY != prevN[i] 
-             && NULL != si->strtab
-             && 0 == VG_(strcmp)(str, &si->strtab[prevN[i]])) {
+             && NULL != si->strchunks
+             && 0 == VG_(strncmp)(str, prevN[i], len) &&
+            prevN[i+len] == '\0') {
             return prevN[i];
          }
       }
@@ -179,33 +106,28 @@ Int addStr ( SegInfo* si, Char* str )
       for (i = 0; i < NN; i++) prevN[i] = EMPTY;
    }
    /* Shuffle prevous ones along, put new one in. */
-   for (i = NN-1; i > 0; i--) prevN[i] = prevN[i-1];
-   prevN[0] = si->strtab_used;
+   for (i = NN-1; i > 0; i--)
+      prevN[i] = prevN[i-1];
 
 #  undef EMPTY
 
-   space_needed = 1 + VG_(strlen)(str);
+   space_needed = 1 + len;
 
-   if (si->strtab_used + space_needed > si->strtab_size) {
-      new_sz = 2 * si->strtab_size;
-      if (new_sz == 0) new_sz = 5000;
-      new_tab = VG_(arena_malloc)(VG_AR_SYMTAB, new_sz);
-      if (si->strtab != NULL) {
-         for (u = 0; u < si->strtab_used; u++)
-            new_tab[u] = si->strtab[u];
-         VG_(arena_free)(VG_AR_SYMTAB, si->strtab);
-      }
-      si->strtab      = new_tab;
-      si->strtab_size = new_sz;
+   if (si->strchunks == NULL || 
+       (si->strchunks->strtab_used + space_needed) > STRCHUNKSIZE) {
+      chunk = VG_(arena_malloc)(VG_AR_SYMTAB, sizeof(*chunk));
+      chunk->strtab_used = 0;
+      chunk->next = si->strchunks;
+      si->strchunks = chunk;
    }
+   chunk = si->strchunks;
 
-   for (i = 0; i < space_needed; i++)
-      si->strtab[si->strtab_used+i] = str[i];
+   prevN[0] = &chunk->strtab[chunk->strtab_used];
+   VG_(memcpy)(prevN[0], str, len);
+   chunk->strtab[chunk->strtab_used+len] = '\0';
+   chunk->strtab_used += space_needed;
 
-   si->strtab_used += space_needed;
-   vg_assert(si->strtab_used <= si->strtab_size);
-
-   return si->strtab_used - space_needed;
+   return prevN[0];
 }
 
 /* Add a symbol to the symbol table. */
@@ -269,21 +191,24 @@ void addLoc ( SegInfo* si, RiLoc* loc )
 
 /* Top-level place to call to add a source-location mapping entry. */
 
-static 
-void addLineInfo ( SegInfo* si,
-                   Int      fnmoff,
-                   Addr     this,
-                   Addr     next,
-                   Int      lineno,
-                   Int      entry /* only needed for debug printing */
-                 )
+void VG_(addLineInfo) ( SegInfo* si,
+                       Char*    filename,
+                       Addr     this,
+                       Addr     next,
+                       Int      lineno,
+                       Int      entry /* only needed for debug printing */
+                      )
 {
+   static const Bool debug = False;
    RiLoc loc;
    Int size = next - this;
 
    /* Ignore zero-sized locs */
    if (this == next) return;
 
+   if (debug)
+      VG_(printf)("  src %s line %d %p-%p\n", filename, lineno, this, next);
+
    /* Maximum sanity checking.  Some versions of GNU as do a shabby
     * job with stabs entries; if anything looks suspicious, revert to
     * a size of 1.  This should catch the instruction of interest
@@ -331,22 +256,78 @@ void addLineInfo ( SegInfo* si,
    loc.addr      = this;
    loc.size      = (UShort)size;
    loc.lineno    = lineno;
-   loc.fnmoff    = fnmoff;
+   loc.filename  = filename;
 
    if (0) VG_(message)(Vg_DebugMsg, 
                       "addLoc: addr %p, size %d, line %d, file %s",
-                      this,size,lineno,&si->strtab[fnmoff]);
+                      this,size,lineno,filename);
 
    addLoc ( si, &loc );
 }
 
+static __inline__
+void addScopeRange ( SegInfo* si, ScopeRange *range )
+{
+   Int    new_sz, i;
+   ScopeRange* new_tab;
+
+   /* Zero-sized scopes should have been ignored earlier */
+   vg_assert(range->size > 0);
+
+   if (si->scopetab_used == si->scopetab_size) {
+      new_sz = 2 * si->scopetab_size;
+      if (new_sz == 0) new_sz = 500;
+      new_tab = VG_(arena_malloc)(VG_AR_SYMTAB, new_sz * sizeof(*new_tab) );
+      if (si->scopetab != NULL) {
+         for (i = 0; i < si->scopetab_used; i++)
+            new_tab[i] = si->scopetab[i];
+         VG_(arena_free)(VG_AR_SYMTAB, si->scopetab);
+      }
+      si->scopetab = new_tab;
+      si->scopetab_size = new_sz;
+   }
+
+   si->scopetab[si->scopetab_used] = *range;
+   si->scopetab_used++;
+   vg_assert(si->scopetab_used <= si->scopetab_size);
+}
+
+
+/* Top-level place to call to add a source-location mapping entry. */
+
+void VG_(addScopeInfo) ( SegInfo* si,
+                        Addr     this,
+                        Addr     next,
+                        Scope    *scope)
+{
+   static const Bool debug = False;
+   Int size = next - this;
+   ScopeRange range;
+
+   /* Ignore zero-sized scopes */
+   if (this == next) {
+      if (debug)
+        VG_(printf)("ignoring zero-sized range, scope %p at %p\n", scope, this);
+      return;
+   }
+
+   if (debug)
+      VG_(printf)("adding scope range %p-%p (size=%d)  scope %p (%d)\n",
+                 this, next, next-this, scope, scope->depth);
+
+   range.addr    = this;
+   range.size    = size;
+   range.scope   = scope;
+
+   addScopeRange ( si, &range );
+}
+
 /*------------------------------------------------------------*/
 /*--- Helpers                                              ---*/
 /*------------------------------------------------------------*/
 
 /* Non-fatal -- use vg_panic if terminal. */
-static 
-void vg_symerr ( Char* msg )
+void VG_(symerr) ( Char* msg )
 {
    if (VG_(clo_verbosity) > 1)
       VG_(message)(Vg_UserMsg,"%s", msg );
@@ -361,7 +342,7 @@ void printSym ( SegInfo* si, Int i )
                i,
                si->symtab[i].addr, 
                si->symtab[i].addr + si->symtab[i].size - 1, si->symtab[i].size,
-                &si->strtab[si->symtab[i].nmoff] );
+              si->symtab[i].name );
 }
 
 
@@ -403,6 +384,49 @@ void safeCopy ( UChar* dst, UInt maxlen, UChar* src )
 /*--- Canonicalisers                                       ---*/
 /*------------------------------------------------------------*/
 
+static void shellsort(void *base, UInt nmemb, UInt sz, Int (*compare)(void *a, void *b))
+{
+   /* Magic numbers due to Janet Incerpi and Robert Sedgewick. */
+   static const Int incs[16] = { 1, 3, 7, 21, 48, 112, 336, 861, 1968,
+                                4592, 13776, 33936, 86961, 198768, 
+                                463792, 1391376 };
+   Int lo = 0;
+   Int hi = nmemb-1;
+   Int bigN;
+   Int hp;
+   Char *cp = (Char *)base;
+   Char tmp[sz];
+
+   bigN = hi - lo + 1;
+   if (bigN < 2)
+      return;
+   hp = 0;
+   while (hp < 16 && incs[hp] < bigN)
+      hp++;
+   hp--;
+   vg_assert(0 <= hp && hp < 16);
+
+   for (; hp >= 0; hp--) {
+      Int i, h;
+      
+      h = incs[hp];
+      i = lo + h;
+      for (i = lo+h; i <= hi; i++) {
+        Int j;
+        VG_(memcpy)(&tmp, &cp[i * sz], sz);
+
+         j = i;
+         while ((*compare)((void *)&cp[(j-h) * sz], (void *)&tmp) > 0) {
+           VG_(memcpy)(&cp[j * sz], &cp[(j-h) * sz], sz);
+            j = j - h;
+            if (j <= (lo + h - 1))
+              break;
+         }
+        VG_(memcpy)(&cp[j * sz], &tmp, sz);
+      }
+   }
+}
+
 /* Sort the symtab by starting address, and emit warnings if any
    symbols have overlapping address ranges.  We use that old chestnut,
    shellsort.  Mash the table around so as to establish the property
@@ -410,42 +434,26 @@ void safeCopy ( UChar* dst, UInt maxlen, UChar* src )
    facilitates using binary search to map addresses to symbols when we
    come to query the table.
 */
+static Int compare_RiSym(void *va, void *vb) {
+   RiSym *a = (RiSym *)va;
+   RiSym *b = (RiSym *)vb;
+   
+   return a->addr - b->addr;
+}
+
 static 
 void canonicaliseSymtab ( SegInfo* si )
 {
-   /* Magic numbers due to Janet Incerpi and Robert Sedgewick. */
-   Int   incs[16] = { 1, 3, 7, 21, 48, 112, 336, 861, 1968,
-                      4592, 13776, 33936, 86961, 198768, 
-                      463792, 1391376 };
-   Int   lo = 0;
-   Int   hi = si->symtab_used-1;
-   Int   i, j, h, bigN, hp, n_merged, n_truncated;
-   RiSym v;
+   Int   i, j, n_merged, n_truncated;
    Addr  s1, s2, e1, e2;
 
 #  define SWAP(ty,aa,bb) \
       do { ty tt = (aa); (aa) = (bb); (bb) = tt; } while (0)
 
-   bigN = hi - lo + 1; if (bigN < 2) return;
-   hp = 0; while (hp < 16 && incs[hp] < bigN) hp++; hp--;
-   vg_assert(0 <= hp && hp < 16);
+   if (si->symtab_used == 0)
+      return;
 
-   for (; hp >= 0; hp--) {
-      h = incs[hp];
-      i = lo + h;
-      while (1) {
-         if (i > hi) break;
-         v = si->symtab[i];
-         j = i;
-         while (si->symtab[j-h].addr > v.addr) {
-            si->symtab[j] = si->symtab[j-h];
-            j = j - h;
-            if (j <= (lo + h - 1)) break;
-         }
-         si->symtab[j] = v;
-         i++;
-      }
-   }
+   shellsort(si->symtab, si->symtab_used, sizeof(*si->symtab), compare_RiSym);
 
   cleanup_more:
  
@@ -462,8 +470,8 @@ void canonicaliseSymtab ( SegInfo* si )
              && si->symtab[i].size   == si->symtab[i+1].size) {
             n_merged++;
             /* merge the two into one */
-            if (VG_(strlen)(&si->strtab[si->symtab[i].nmoff]
-                > VG_(strlen)(&si->strtab[si->symtab[i+1].nmoff])) {
+            if (VG_(strlen)(si->symtab[i].name
+                > VG_(strlen)(si->symtab[i+1].name)) {
                si->symtab[si->symtab_used++] = si->symtab[i];
             } else {
                si->symtab[si->symtab_used++] = si->symtab[i+1];
@@ -551,6 +559,74 @@ void canonicaliseSymtab ( SegInfo* si )
 #  undef SWAP
 }
 
+/* Sort the scope range table by starting address.  Mash the table
+   around so as to establish the property that addresses are in order
+   and the ranges do not overlap.  This facilitates using binary
+   search to map addresses to scopes when we come to query the
+   table.
+*/
+static Int compare_ScopeRange(void *va, void *vb) {
+   ScopeRange *a = (ScopeRange *)va;
+   ScopeRange *b = (ScopeRange *)vb;
+   
+   return a->addr - b->addr;
+}
+
+static 
+void canonicaliseScopetab ( SegInfo* si )
+{
+   Int i,j;
+
+   if (si->scopetab_used == 0)
+      return;
+
+   /* Sort by start address. */
+   shellsort(si->scopetab, si->scopetab_used, sizeof(*si->scopetab), 
+            compare_ScopeRange);
+
+   /* If two adjacent entries overlap, truncate the first. */
+   for (i = 0; i < si->scopetab_used-1; i++) {
+      if (si->scopetab[i].addr + si->scopetab[i].size > si->scopetab[i+1].addr) {
+         Int new_size = si->scopetab[i+1].addr - si->scopetab[i].addr;
+
+         if (new_size < 0)
+            si->scopetab[i].size = 0;
+         else
+           si->scopetab[i].size = new_size;
+      }
+   }
+
+   /* Zap any zero-sized entries resulting from the truncation
+      process. */
+   j = 0;
+   for (i = 0; i < si->scopetab_used; i++) {
+      if (si->scopetab[i].size > 0) {
+         si->scopetab[j] = si->scopetab[i];
+         j++;
+      }
+   }
+   si->scopetab_used = j;
+
+   /* Ensure relevant postconditions hold. */
+   for (i = 0; i < si->scopetab_used-1; i++) {
+      /* 
+      VG_(printf)("%d   (%d) %d 0x%x\n", 
+                   i, si->scopetab[i+1].confident, 
+                   si->scopetab[i+1].size, si->scopetab[i+1].addr );
+      */
+      /* No zero-sized symbols. */
+      vg_assert(si->scopetab[i].size > 0);
+      /* In order. */
+      if (si->scopetab[i].addr >= si->scopetab[i+1].addr)
+        VG_(printf)("si->scopetab[%d] = %p,size=%d [%d] = %p,size=%d\n",
+                    i, si->scopetab[i].addr, si->scopetab[i].size,
+                    i+1, si->scopetab[i+1].addr, si->scopetab[i+1].size);
+      vg_assert(si->scopetab[i].addr < si->scopetab[i+1].addr);
+      /* No overlaps. */
+      vg_assert(si->scopetab[i].addr + si->scopetab[i].size - 1
+                < si->scopetab[i+1].addr);
+   }
+}
 
 
 /* Sort the location table by starting address.  Mash the table around
@@ -558,43 +634,26 @@ void canonicaliseSymtab ( SegInfo* si )
    ranges do not overlap.  This facilitates using binary search to map
    addresses to locations when we come to query the table.  
 */
+static Int compare_RiLoc(void *va, void *vb) {
+   RiLoc *a = (RiLoc *)va;
+   RiLoc *b = (RiLoc *)vb;
+
+   return a->addr - b->addr;
+}
+
 static 
 void canonicaliseLoctab ( SegInfo* si )
 {
-   /* Magic numbers due to Janet Incerpi and Robert Sedgewick. */
-   Int   incs[16] = { 1, 3, 7, 21, 48, 112, 336, 861, 1968,
-                      4592, 13776, 33936, 86961, 198768, 
-                      463792, 1391376 };
-   Int   lo = 0;
-   Int   hi = si->loctab_used-1;
-   Int   i, j, h, bigN, hp;
-   RiLoc v;
+   Int   i, j;
 
 #  define SWAP(ty,aa,bb) \
       do { ty tt = (aa); (aa) = (bb); (bb) = tt; } while (0);
 
-   /* Sort by start address. */
-
-   bigN = hi - lo + 1; if (bigN < 2) return;
-   hp = 0; while (hp < 16 && incs[hp] < bigN) hp++; hp--;
-   vg_assert(0 <= hp && hp < 16);
+   if (si->loctab_used == 0)
+      return;
 
-   for (; hp >= 0; hp--) {
-      h = incs[hp];
-      i = lo + h;
-      while (1) {
-         if (i > hi) break;
-         v = si->loctab[i];
-         j = i;
-         while (si->loctab[j-h].addr > v.addr) {
-            si->loctab[j] = si->loctab[j-h];
-            j = j - h;
-            if (j <= (lo + h - 1)) break;
-         }
-         si->loctab[j] = v;
-         i++;
-      }
-   }
+   /* Sort by start address. */
+   shellsort(si->loctab, si->loctab_used, sizeof(*si->loctab), compare_RiLoc);
 
    /* If two adjacent entries overlap, truncate the first. */
    for (i = 0; i < ((Int)si->loctab_used)-1; i++) {
@@ -644,732 +703,6 @@ void canonicaliseLoctab ( SegInfo* si )
 }
 
 
-/*------------------------------------------------------------*/
-/*--- Read STABS format debug info.                        ---*/
-/*------------------------------------------------------------*/
-
-/* Stabs entry types, from:
- *   The "stabs" debug format
- *   Menapace, Kingdon and MacKenzie
- *   Cygnus Support
- */
-typedef enum { N_GSYM  = 32,    /* Global symbol                    */
-               N_FUN   = 36,    /* Function start or end            */
-               N_STSYM = 38,    /* Data segment file-scope variable */
-               N_LCSYM = 40,    /* BSS segment file-scope variable  */
-               N_RSYM  = 64,    /* Register variable                */
-               N_SLINE = 68,    /* Source line number               */
-               N_SO    = 100,   /* Source file path and name        */
-               N_LSYM  = 128,   /* Stack variable or type           */
-               N_SOL   = 132,   /* Include file name                */
-               N_LBRAC = 192,   /* Start of lexical block           */
-               N_RBRAC = 224    /* End   of lexical block           */
-             } stab_types;
-      
-
-/* Read stabs-format debug info.  This is all rather horrible because
-   stabs is a underspecified, kludgy hack.
-*/
-static
-void read_debuginfo_stabs ( SegInfo* si,
-                            UChar* stabC,   Int stab_sz, 
-                            UChar* stabstr, Int stabstr_sz )
-{
-   Int    i;
-   Int    curr_filenmoff;
-   Addr   curr_fn_stabs_addr = (Addr)NULL;
-   Addr   curr_fnbaseaddr    = (Addr)NULL;
-   Char  *curr_file_name, *curr_fn_name;
-   Int    n_stab_entries;
-   Int    prev_lineno = 0, lineno = 0;
-   Int    lineno_overflows = 0;
-   Bool   same_file = True;
-   struct nlist* stab = (struct nlist*)stabC;
-
-   /* Ok.  It all looks plausible.  Go on and read debug data. 
-         stab kinds: 100   N_SO     a source file name
-                      68   N_SLINE  a source line number
-                      36   N_FUN    start of a function
-
-      In this loop, we maintain a current file name, updated as 
-      N_SO/N_SOLs appear, and a current function base address, 
-      updated as N_FUNs appear.  Based on that, address ranges for 
-      N_SLINEs are calculated, and stuffed into the line info table.
-
-      Finding the instruction address range covered by an N_SLINE is
-      complicated;  see the N_SLINE case below.
-   */
-   curr_filenmoff     = addStr(si,"???");
-   curr_file_name     = curr_fn_name = (Char*)NULL;
-
-   n_stab_entries = stab_sz/(int)sizeof(struct nlist);
-
-   for (i = 0; i < n_stab_entries; i++) {
-#     if 0
-      VG_(printf) ( "   %2d  ", i );
-      VG_(printf) ( "type=0x%x   othr=%d   desc=%d   value=0x%x   strx=%d  %s",
-                    stab[i].n_type, stab[i].n_other, stab[i].n_desc, 
-                    (int)stab[i].n_value,
-                    (int)stab[i].n_un.n_strx, 
-                    stabstr + stab[i].n_un.n_strx );
-      VG_(printf)("\n");
-#     endif
-
-      Char *no_fn_name = "???";
-
-      switch (stab[i].n_type) {
-         UInt next_addr;
-
-         /* Two complicated things here:
-         *
-          * 1. the n_desc field in 'struct n_list' in a.out.h is only
-          *    16-bits, which gives a maximum of 65535 lines.  We handle
-          *    files bigger than this by detecting heuristically
-          *    overflows -- if the line count goes from 65000-odd to
-          *    0-odd within the same file, we assume it's an overflow.
-          *    Once we switch files, we zero the overflow count.
-          *
-          * 2. To compute the instr address range covered by a single
-          *    line, find the address of the next thing and compute the
-          *    difference.  The approach used depends on what kind of
-          *    entry/entries follow...
-          */
-         case N_SLINE: {
-            Int this_addr = (UInt)stab[i].n_value;
-
-            /* Although stored as a short, neg values really are >
-             * 32768, hence the UShort cast.  Then we use an Int to
-             * handle overflows. */
-            prev_lineno = lineno;
-            lineno      = (Int)((UShort)stab[i].n_desc);
-
-            if (prev_lineno > lineno + OVERFLOW_DIFFERENCE && same_file) {
-               VG_(message)(Vg_DebugMsg, 
-                  "Line number overflow detected (%d --> %d) in %s", 
-                  prev_lineno, lineno, curr_file_name);
-               lineno_overflows++;
-            }
-            same_file = True;
-
-            LOOP:
-            if (i+1 >= n_stab_entries) {
-               /* If it's the last entry, just guess the range is
-                * four; can't do any better */
-               next_addr = this_addr + 4;
-            } else {    
-               switch (stab[i+1].n_type) {
-                  /* Easy, common case: use address of next entry */
-                  case N_SLINE: case N_SO:
-                     next_addr = (UInt)stab[i+1].n_value;
-                     break;
-
-                  /* Boring one: skip, look for something more useful. */
-                  case N_RSYM: case N_LSYM: case N_LBRAC: case N_RBRAC: 
-                  case N_STSYM: case N_LCSYM: case N_GSYM:
-                     i++;
-                     goto LOOP;
-                     
-                  /* If end-of-this-fun entry, use its address.
-                   * If start-of-next-fun entry, find difference between start
-                   *   of current function and start of next function to work
-                   *   it out.
-                   */
-                  case N_FUN: 
-                     if ('\0' == * (stabstr + stab[i+1].n_un.n_strx) ) {
-                        next_addr = (UInt)stab[i+1].n_value;
-                     } else {
-                        next_addr = 
-                            (UInt)stab[i+1].n_value - curr_fn_stabs_addr;
-                     }
-                     break;
-
-                  /* N_SOL should be followed by an N_SLINE which can
-                     be used */
-                  case N_SOL:
-                     if (i+2 < n_stab_entries && N_SLINE == stab[i+2].n_type) {
-                        next_addr = (UInt)stab[i+2].n_value;
-                        break;
-                     } else {
-                        VG_(printf)("unhandled N_SOL stabs case: %d %d %d", 
-                                    stab[i+1].n_type, i, n_stab_entries);
-                        VG_(core_panic)("unhandled N_SOL stabs case");
-                     }
-
-                  default:
-                     VG_(printf)("unhandled (other) stabs case: %d %d", 
-                                 stab[i+1].n_type,i);
-                     /* VG_(core_panic)("unhandled (other) stabs case"); */
-                     next_addr = this_addr + 4;
-                     break;
-               }
-            }
-            
-            addLineInfo ( si, curr_filenmoff, curr_fnbaseaddr + this_addr, 
-                          curr_fnbaseaddr + next_addr,
-                          lineno + lineno_overflows * LINENO_OVERFLOW, i);
-            break;
-         }
-
-         case N_FUN: {
-            if ('\0' != (stabstr + stab[i].n_un.n_strx)[0] ) {
-               /* N_FUN with a name -- indicates the start of a fn.  */
-               curr_fn_stabs_addr = (Addr)stab[i].n_value;
-               curr_fnbaseaddr = si->offset + curr_fn_stabs_addr;
-               curr_fn_name = stabstr + stab[i].n_un.n_strx;
-            } else {
-               curr_fn_name = no_fn_name;
-            }
-            break;
-         }
-
-         case N_SOL:
-            if (lineno_overflows != 0) {
-               VG_(message)(Vg_UserMsg, 
-                            "Warning: file %s is very big (> 65535 lines) "
-                            "Line numbers and annotation for this file might "
-                            "be wrong.  Sorry",
-                            curr_file_name);
-            }
-            /* fall through! */
-         case N_SO: 
-            lineno_overflows = 0;
-
-         /* seems to give lots of locations in header files */
-         /* case 130: */ /* BINCL */
-         { 
-            UChar* nm = stabstr + stab[i].n_un.n_strx;
-            UInt len = VG_(strlen)(nm);
-            
-            if (len > 0 && nm[len-1] != '/') {
-               curr_filenmoff = addStr ( si, nm );
-               curr_file_name = stabstr + stab[i].n_un.n_strx;
-            }
-            else
-               if (len == 0)
-                  curr_filenmoff = addStr ( si, "?1\0" );
-
-            break;
-         }
-
-#        if 0
-         case 162: /* EINCL */
-            curr_filenmoff = addStr ( si, "?2\0" );
-            break;
-#        endif
-
-         default:
-            break;
-      }
-   } /* for (i = 0; i < stab_sz/(int)sizeof(struct nlist); i++) */
-}
-
-
-/*------------------------------------------------------------*/
-/*--- Read DWARF2 format debug info.                       ---*/
-/*------------------------------------------------------------*/
-
-/* Structure found in the .debug_line section.  */
-typedef struct
-{
-  UChar li_length          [4];
-  UChar li_version         [2];
-  UChar li_prologue_length [4];
-  UChar li_min_insn_length [1];
-  UChar li_default_is_stmt [1];
-  UChar li_line_base       [1];
-  UChar li_line_range      [1];
-  UChar li_opcode_base     [1];
-}
-DWARF2_External_LineInfo;
-
-typedef struct
-{
-  UInt   li_length;
-  UShort li_version;
-  UInt   li_prologue_length;
-  UChar  li_min_insn_length;
-  UChar  li_default_is_stmt;
-  Int    li_line_base;
-  UChar  li_line_range;
-  UChar  li_opcode_base;
-}
-DWARF2_Internal_LineInfo;
-
-/* Line number opcodes.  */
-enum dwarf_line_number_ops
-  {
-    DW_LNS_extended_op = 0,
-    DW_LNS_copy = 1,
-    DW_LNS_advance_pc = 2,
-    DW_LNS_advance_line = 3,
-    DW_LNS_set_file = 4,
-    DW_LNS_set_column = 5,
-    DW_LNS_negate_stmt = 6,
-    DW_LNS_set_basic_block = 7,
-    DW_LNS_const_add_pc = 8,
-    DW_LNS_fixed_advance_pc = 9,
-    /* DWARF 3.  */
-    DW_LNS_set_prologue_end = 10,
-    DW_LNS_set_epilogue_begin = 11,
-    DW_LNS_set_isa = 12
-  };
-
-/* Line number extended opcodes.  */
-enum dwarf_line_number_x_ops
-  {
-    DW_LNE_end_sequence = 1,
-    DW_LNE_set_address = 2,
-    DW_LNE_define_file = 3
-  };
-
-typedef struct State_Machine_Registers
-{
-  /* Information for the last statement boundary.
-   * Needed to calculate statement lengths. */
-  Addr  last_address;
-  UInt  last_file;
-  UInt  last_line;
-
-  Addr  address;
-  UInt  file;
-  UInt  line;
-  UInt  column;
-  Int   is_stmt;
-  Int   basic_block;
-  Int   end_sequence;
-  /* This variable hold the number of the last entry seen
-     in the File Table.  */
-  UInt  last_file_entry;
-} SMR;
-
-
-static 
-UInt read_leb128 ( UChar* data, Int* length_return, Int sign )
-{
-  UInt   result = 0;
-  UInt   num_read = 0;
-  Int    shift = 0;
-  UChar  byte;
-
-  do
-    {
-      byte = * data ++;
-      num_read ++;
-
-      result |= (byte & 0x7f) << shift;
-
-      shift += 7;
-
-    }
-  while (byte & 0x80);
-
-  if (length_return != NULL)
-    * length_return = num_read;
-
-  if (sign && (shift < 32) && (byte & 0x40))
-    result |= -1 << shift;
-
-  return result;
-}
-
-
-static SMR state_machine_regs;
-
-static 
-void reset_state_machine ( Int is_stmt )
-{
-  if (0) VG_(printf)("smr.a := %p (reset)\n", 0 );
-  state_machine_regs.last_address = 0;
-  state_machine_regs.last_file = 1;
-  state_machine_regs.last_line = 1;
-  state_machine_regs.address = 0;
-  state_machine_regs.file = 1;
-  state_machine_regs.line = 1;
-  state_machine_regs.column = 0;
-  state_machine_regs.is_stmt = is_stmt;
-  state_machine_regs.basic_block = 0;
-  state_machine_regs.end_sequence = 0;
-  state_machine_regs.last_file_entry = 0;
-}
-
-/* Handled an extend line op.  Returns true if this is the end
-   of sequence.  */
-static 
-int process_extended_line_op( SegInfo *si, UInt** fnames, 
-                              UChar* data, Int is_stmt, Int pointer_size)
-{
-  UChar   op_code;
-  Int     bytes_read;
-  UInt    len;
-  UChar * name;
-  Addr    adr;
-
-  len = read_leb128 (data, & bytes_read, 0);
-  data += bytes_read;
-
-  if (len == 0)
-    {
-      VG_(message)(Vg_UserMsg,
-         "badly formed extended line op encountered!\n");
-      return bytes_read;
-    }
-
-  len += bytes_read;
-  op_code = * data ++;
-
-  if (0) VG_(printf)("dwarf2: ext OPC: %d\n", op_code);
-
-  switch (op_code)
-    {
-    case DW_LNE_end_sequence:
-      if (0) VG_(printf)("1001: si->o %p, smr.a %p\n", 
-                         si->offset, state_machine_regs.address );
-      state_machine_regs.end_sequence = 1; /* JRS: added for compliance
-         with spec; is pointless due to reset_state_machine below 
-      */
-      if (state_machine_regs.is_stmt) {
-         if (state_machine_regs.last_address)
-             addLineInfo (si, (*fnames)[state_machine_regs.last_file], 
-                       si->offset + state_machine_regs.last_address, 
-                       si->offset + state_machine_regs.address, 
-                       state_machine_regs.last_line, 0);
-      }
-      reset_state_machine (is_stmt);
-      break;
-
-    case DW_LNE_set_address:
-      /* XXX: Pointer size could be 8 */
-      vg_assert(pointer_size == 4);
-      adr = *((Addr *)data);
-      if (0) VG_(printf)("smr.a := %p\n", adr );
-      state_machine_regs.address = adr;
-      break;
-
-    case DW_LNE_define_file:
-      ++ state_machine_regs.last_file_entry;
-      name = data;
-      if (*fnames == NULL)
-        *fnames = VG_(arena_malloc)(VG_AR_SYMTAB, sizeof (UInt) * 2);
-      else
-        *fnames = VG_(arena_realloc)(
-                     VG_AR_SYMTAB, *fnames, /*alignment*/4,
-                     sizeof(UInt) 
-                        * (state_machine_regs.last_file_entry + 1));
-      (*fnames)[state_machine_regs.last_file_entry] = addStr (si,name);
-      data += VG_(strlen) ((char *) data) + 1;
-      read_leb128 (data, & bytes_read, 0);
-      data += bytes_read;
-      read_leb128 (data, & bytes_read, 0);
-      data += bytes_read;
-      read_leb128 (data, & bytes_read, 0);
-      break;
-
-    default:
-      break;
-    }
-
-  return len;
-}
-
-
-static
-void read_debuginfo_dwarf2 ( SegInfo* si, UChar* dwarf2, Int dwarf2_sz )
-{
-  DWARF2_External_LineInfo * external;
-  DWARF2_Internal_LineInfo   info;
-  UChar *            standard_opcodes;
-  UChar *            data = dwarf2;
-  UChar *            end  = dwarf2 + dwarf2_sz;
-  UChar *            end_of_sequence;
-  UInt  *            fnames = NULL;
-
-  /* Fails due to gcc padding ...
-  vg_assert(sizeof(DWARF2_External_LineInfo)
-            == sizeof(DWARF2_Internal_LineInfo));
-  */
-
-  while (data < end)
-    {
-      external = (DWARF2_External_LineInfo *) data;
-
-      /* Check the length of the block.  */
-      info.li_length = * ((UInt *)(external->li_length));
-
-      if (info.li_length == 0xffffffff)
-       {
-         vg_symerr("64-bit DWARF line info is not supported yet.");
-         break;
-       }
-
-      if (info.li_length + sizeof (external->li_length) > (UInt)dwarf2_sz)
-       {
-        vg_symerr("DWARF line info appears to be corrupt "
-                  "- the section is too small");
-         return;
-       }
-
-      /* Check its version number.  */
-      info.li_version = * ((UShort *) (external->li_version));
-      if (info.li_version != 2)
-       {
-         vg_symerr("Only DWARF version 2 line info "
-                   "is currently supported.");
-         return;
-       }
-
-      info.li_prologue_length = * ((UInt *) (external->li_prologue_length));
-      info.li_min_insn_length = * ((UChar *)(external->li_min_insn_length));
-
-      info.li_default_is_stmt = True; 
-         /* WAS: = * ((UChar *)(external->li_default_is_stmt)); */
-         /* Josef Weidendorfer (20021021) writes:
-
-            It seems to me that the Intel Fortran compiler generates
-            bad DWARF2 line info code: It sets "is_stmt" of the state
-            machine in the the line info reader to be always
-            false. Thus, there is never a statement boundary generated
-            and therefore never a instruction range/line number
-            mapping generated for valgrind.
-
-            Please have a look at the DWARF2 specification, Ch. 6.2
-            (x86.ddj.com/ftp/manuals/tools/dwarf.pdf).  Perhaps I
-            understand this wrong, but I don't think so.
-
-            I just had a look at the GDB DWARF2 reader...  They
-            completely ignore "is_stmt" when recording line info ;-)
-            That's the reason "objdump -S" works on files from the the
-            intel fortran compiler.  
-         */
-
-
-      /* JRS: changed (UInt*) to (UChar*) */
-      info.li_line_base       = * ((UChar *)(external->li_line_base));
-
-      info.li_line_range      = * ((UChar *)(external->li_line_range));
-      info.li_opcode_base     = * ((UChar *)(external->li_opcode_base)); 
-
-      if (0) VG_(printf)("dwarf2: line base: %d, range %d, opc base: %d\n",
-                 info.li_line_base, info.li_line_range, info.li_opcode_base);
-
-      /* Sign extend the line base field.  */
-      info.li_line_base <<= 24;
-      info.li_line_base >>= 24;
-
-      end_of_sequence = data + info.li_length 
-                             + sizeof (external->li_length);
-
-      reset_state_machine (info.li_default_is_stmt);
-
-      /* Read the contents of the Opcodes table.  */
-      standard_opcodes = data + sizeof (* external);
-
-      /* Read the contents of the Directory table.  */
-      data = standard_opcodes + info.li_opcode_base - 1;
-
-      if (* data == 0) 
-       {
-       }
-      else
-       {
-         /* We ignore the directory table, since gcc gives the entire
-            path as part of the filename */
-         while (* data != 0)
-           {
-             data += VG_(strlen) ((char *) data) + 1;
-           }
-       }
-
-      /* Skip the NUL at the end of the table.  */
-      if (*data != 0) {
-         vg_symerr("can't find NUL at end of DWARF2 directory table");
-         return;
-      }
-      data ++;
-
-      /* Read the contents of the File Name table.  */
-      if (* data == 0)
-       {
-       }
-      else
-       {
-         while (* data != 0)
-           {
-             UChar * name;
-             Int bytes_read;
-
-             ++ state_machine_regs.last_file_entry;
-             name = data;
-             /* Since we don't have realloc (0, ....) == malloc (...)
-               semantics, we need to malloc the first time. */
-
-             if (fnames == NULL)
-               fnames = VG_(arena_malloc)(VG_AR_SYMTAB, sizeof (UInt) * 2);
-             else
-               fnames = VG_(arena_realloc)(VG_AR_SYMTAB, fnames, /*alignment*/4,
-                           sizeof(UInt) 
-                              * (state_machine_regs.last_file_entry + 1));
-             data += VG_(strlen) ((Char *) data) + 1;
-             fnames[state_machine_regs.last_file_entry] = addStr (si,name);
-
-             read_leb128 (data, & bytes_read, 0);
-             data += bytes_read;
-             read_leb128 (data, & bytes_read, 0);
-             data += bytes_read;
-             read_leb128 (data, & bytes_read, 0);
-             data += bytes_read;
-           }
-       }
-
-      /* Skip the NUL at the end of the table.  */
-      if (*data != 0) {
-         vg_symerr("can't find NUL at end of DWARF2 file name table");
-         return;
-      }
-      data ++;
-
-      /* Now display the statements.  */
-
-      while (data < end_of_sequence)
-       {
-         UChar op_code;
-         Int           adv;
-         Int           bytes_read;
-
-         op_code = * data ++;
-
-        if (0) VG_(printf)("dwarf2: OPC: %d\n", op_code);
-
-         if (op_code >= info.li_opcode_base)
-           {
-             Int advAddr;
-             op_code -= info.li_opcode_base;
-             adv      = (op_code / info.li_line_range) 
-                           * info.li_min_insn_length;
-             advAddr = adv;
-             state_machine_regs.address += adv;
-             if (0) VG_(printf)("smr.a += %p\n", adv );
-             adv = (op_code % info.li_line_range) + info.li_line_base;
-             if (0) VG_(printf)("1002: si->o %p, smr.a %p\n", 
-                                si->offset, state_machine_regs.address );
-             state_machine_regs.line += adv;
-
-            if (state_machine_regs.is_stmt) {
-                /* only add a statement if there was a previous boundary */
-                if (state_machine_regs.last_address) 
-                    addLineInfo (si, fnames[state_machine_regs.last_file], 
-                             si->offset + state_machine_regs.last_address, 
-                              si->offset + state_machine_regs.address, 
-                              state_machine_regs.last_line, 0);
-                state_machine_regs.last_address = state_machine_regs.address;
-                state_machine_regs.last_file = state_machine_regs.file;
-                state_machine_regs.last_line = state_machine_regs.line;
-            }
-           }
-         else switch (op_code)
-           {
-           case DW_LNS_extended_op:
-             data += process_extended_line_op (
-                        si, &fnames, data, 
-                        info.li_default_is_stmt, sizeof (Addr));
-             break;
-
-           case DW_LNS_copy:
-             if (0) VG_(printf)("1002: si->o %p, smr.a %p\n", 
-                                si->offset, state_machine_regs.address );
-            if (state_machine_regs.is_stmt) {
-                /* only add a statement if there was a previous boundary */
-                if (state_machine_regs.last_address) 
-                    addLineInfo (si, fnames[state_machine_regs.last_file], 
-                              si->offset + state_machine_regs.last_address, 
-                              si->offset + state_machine_regs.address,
-                              state_machine_regs.last_line , 0);
-                state_machine_regs.last_address = state_machine_regs.address;
-                state_machine_regs.last_file = state_machine_regs.file;
-                state_machine_regs.last_line = state_machine_regs.line;
-            }
-             state_machine_regs.basic_block = 0; /* JRS added */
-             break;
-
-           case DW_LNS_advance_pc:
-             adv = info.li_min_insn_length 
-                      * read_leb128 (data, & bytes_read, 0);
-             data += bytes_read;
-             state_machine_regs.address += adv;
-             if (0) VG_(printf)("smr.a += %p\n", adv );
-             break;
-
-           case DW_LNS_advance_line:
-             adv = read_leb128 (data, & bytes_read, 1);
-             data += bytes_read;
-             state_machine_regs.line += adv;
-             break;
-
-           case DW_LNS_set_file:
-             adv = read_leb128 (data, & bytes_read, 0);
-             data += bytes_read;
-             state_machine_regs.file = adv;
-             break;
-
-           case DW_LNS_set_column:
-             adv = read_leb128 (data, & bytes_read, 0);
-             data += bytes_read;
-             state_machine_regs.column = adv;
-             break;
-
-           case DW_LNS_negate_stmt:
-             adv = state_machine_regs.is_stmt;
-             adv = ! adv;
-             state_machine_regs.is_stmt = adv;
-             break;
-
-           case DW_LNS_set_basic_block:
-             state_machine_regs.basic_block = 1;
-             break;
-
-           case DW_LNS_const_add_pc:
-             adv = (((255 - info.li_opcode_base) / info.li_line_range)
-                    * info.li_min_insn_length);
-             state_machine_regs.address += adv;
-             if (0) VG_(printf)("smr.a += %p\n", adv );
-             break;
-
-           case DW_LNS_fixed_advance_pc:
-             /* XXX: Need something to get 2 bytes */
-             adv = *((UShort *)data);
-             data += 2;
-             state_machine_regs.address += adv;
-             if (0) VG_(printf)("smr.a += %p\n", adv );
-             break;
-
-           case DW_LNS_set_prologue_end:
-             break;
-
-           case DW_LNS_set_epilogue_begin:
-             break;
-
-           case DW_LNS_set_isa:
-             adv = read_leb128 (data, & bytes_read, 0);
-             data += bytes_read;
-             break;
-
-           default:
-             {
-               int j;
-               for (j = standard_opcodes[op_code - 1]; j > 0 ; --j)
-                 {
-                   read_leb128 (data, &bytes_read, 0);
-                   data += bytes_read;
-                 }
-             }
-             break;
-           }
-       }
-      VG_(arena_free)(VG_AR_SYMTAB, fnames);
-      fnames = NULL;
-    }
-}
-
-
 /*------------------------------------------------------------*/
 /*--- Read info from a .so/exe file.                       ---*/
 /*------------------------------------------------------------*/
@@ -1405,14 +738,14 @@ Bool vg_read_lib_symbols ( SegInfo* si )
 
    i = VG_(stat)(si->filename, &stat_buf);
    if (i != 0) {
-      vg_symerr("Can't stat .so/.exe (to determine its size)?!");
+      VG_(symerr)("Can't stat .so/.exe (to determine its size)?!");
       return False;
    }
    n_oimage = stat_buf.st_size;
 
    fd = VG_(open)(si->filename, VKI_O_RDONLY, 0);
    if (fd == -1) {
-      vg_symerr("Can't open .so/.exe to read symbols?!");
+      VG_(symerr)("Can't open .so/.exe to read symbols?!");
       return False;
    }
 
@@ -1450,7 +783,7 @@ Bool vg_read_lib_symbols ( SegInfo* si )
    }
 
    if (!ok) {
-      vg_symerr("Invalid ELF header, or missing stringtab/sectiontab.");
+      VG_(symerr)("Invalid ELF header, or missing stringtab/sectiontab.");
       VG_(munmap) ( (void*)oimage, n_oimage );
       return False;
    }
@@ -1460,7 +793,7 @@ Bool vg_read_lib_symbols ( SegInfo* si )
       bss memory.  Also computes correct symbol offset value for this
       ELF file. */
    if (ehdr->e_phoff + ehdr->e_phnum*sizeof(Elf32_Phdr) > n_oimage) {
-      vg_symerr("ELF program header is beyond image end?!");
+      VG_(symerr)("ELF program header is beyond image end?!");
       VG_(munmap) ( (void*)oimage, n_oimage );
       return False;
    }
@@ -1485,7 +818,7 @@ Bool vg_read_lib_symbols ( SegInfo* si )
         }
 
         if (o_phdr->p_vaddr < prev_addr) {
-           vg_symerr("ELF Phdrs are out of order!?");
+           VG_(symerr)("ELF Phdrs are out of order!?");
            VG_(munmap) ( (void*)oimage, n_oimage );
            return False;
         }
@@ -1529,7 +862,7 @@ Bool vg_read_lib_symbols ( SegInfo* si )
           ehdr->e_shoff, ehdr->e_shnum, sizeof(Elf32_Shdr), n_oimage );
 
    if (ehdr->e_shoff + ehdr->e_shnum*sizeof(Elf32_Shdr) > n_oimage) {
-      vg_symerr("ELF section header is beyond image end?!");
+      VG_(symerr)("ELF section header is beyond image end?!");
       VG_(munmap) ( (void*)oimage, n_oimage );
       return False;
    }
@@ -1613,10 +946,10 @@ Bool vg_read_lib_symbols ( SegInfo* si )
       }
 
       if (o_strtab == NULL || o_symtab == NULL) {
-         vg_symerr("   object doesn't have a symbol table");
+         VG_(symerr)("   object doesn't have a symbol table");
       }
       if (o_dynstr == NULL || o_dynsym == NULL) {
-         vg_symerr("   object doesn't have a dynamic symbol table");
+         VG_(symerr)("   object doesn't have a dynamic symbol table");
       }
 
       while (True) {
@@ -1766,15 +1099,15 @@ Bool vg_read_lib_symbols ( SegInfo* si )
                Char* t0 = o_symtab[i].st_name 
                              ? (Char*)(o_strtab+o_symtab[i].st_name) 
                              : (Char*)"NONAME";
-               Int nmoff = addStr ( si, t0 );
-               vg_assert(nmoff >= 0 
+               Char *name = VG_(addStr) ( si, t0, -1 );
+               vg_assert(name != NULL
                          /* && 0==VG_(strcmp)(t0,&vg_strtab[nmoff]) */ );
                vg_assert( (Int)o_symtab[i].st_value >= 0);
               /* VG_(printf)("%p + %d:   %p %s\n", si->start, 
                           (Int)o_symtab[i].st_value, sym_addr,  t0 ); */
                sym.addr  = sym_addr;
                sym.size  = o_symtab[i].st_size;
-               sym.nmoff = nmoff;
+               sym.name  = name;
                addSym ( si, &sym );
            }
          }
@@ -1810,7 +1143,7 @@ Bool vg_read_lib_symbols ( SegInfo* si )
    }
 
    if ((stab == NULL || stabstr == NULL) && dwarf2 == NULL) {
-      vg_symerr("   object doesn't have any debug info");
+      VG_(symerr)("   object doesn't have any debug info");
       VG_(munmap) ( (void*)oimage, n_oimage );
       return False;
    }
@@ -1818,24 +1151,24 @@ Bool vg_read_lib_symbols ( SegInfo* si )
    if ( stab_sz + (UChar*)stab > n_oimage + (UChar*)oimage
         || stabstr_sz + (UChar*)stabstr 
            > n_oimage + (UChar*)oimage ) {
-      vg_symerr("   ELF (stabs) debug data is beyond image end?!");
+      VG_(symerr)("   ELF (stabs) debug data is beyond image end?!");
       VG_(munmap) ( (void*)oimage, n_oimage );
       return False;
    }
 
    if ( dwarf2_sz + (UChar*)dwarf2 > n_oimage + (UChar*)oimage ) {
-      vg_symerr("   ELF (dwarf2) debug data is beyond image end?!");
+      VG_(symerr)("   ELF (dwarf2) debug data is beyond image end?!");
       VG_(munmap) ( (void*)oimage, n_oimage );
       return False;
    }
 
    /* Looks plausible.  Go on and read debug data. */
    if (stab != NULL && stabstr != NULL) {
-      read_debuginfo_stabs ( si, stab, stab_sz, stabstr, stabstr_sz );
+      VG_(read_debuginfo_stabs) ( si, stab, stab_sz, stabstr, stabstr_sz );
    }
 
    if (dwarf2 != NULL) {
-      read_debuginfo_dwarf2 ( si, dwarf2, dwarf2_sz );
+      VG_(read_debuginfo_dwarf2) ( si, dwarf2, dwarf2_sz );
    }
 
    /* Last, but not least, heave the oimage back overboard. */
@@ -1910,8 +1243,11 @@ void VG_(read_seg_symbols) ( Addr start, UInt size,
    si->symtab_size = si->symtab_used = 0;
    si->loctab = NULL;
    si->loctab_size = si->loctab_used = 0;
-   si->strtab = NULL;
-   si->strtab_size = si->strtab_used = 0;
+   si->strchunks = NULL;
+   si->scopetab = NULL;
+   si->scopetab_size = si->scopetab_used = 0;
+
+   si->stab_typetab = NULL;
 
    si->plt_start  = si->plt_size = 0;
    si->got_start  = si->got_size = 0;
@@ -1934,6 +1270,7 @@ void VG_(read_seg_symbols) ( Addr start, UInt size,
 
       canonicaliseSymtab ( si );
       canonicaliseLoctab ( si );
+      canonicaliseScopetab ( si );
    }
    VGP_POPCC(VgpReadSyms);
 }
@@ -1981,7 +1318,7 @@ void VG_(unload_symbols) ( Addr start, UInt length )
 
    VG_(message)(Vg_UserMsg, 
                 "discard syms in %s due to munmap()", 
-                curr->filename ? curr->filename : (UChar*)"???");
+                curr->filename ? curr->filename : (Char *)"???");
 
    vg_assert(prev == NULL || prev->next == curr);
 
@@ -2040,9 +1377,8 @@ static Addr reverse_search_one_symtab ( SegInfo* si,
    UInt i;
    for (i = 0; i < si->symtab_used; i++) {
       if (0) 
-         VG_(printf)("%p %s\n",  si->symtab[i].addr, 
-                     &si->strtab[si->symtab[i].nmoff]);
-      if (0 == VG_(strcmp)(name, &si->strtab[si->symtab[i].nmoff]))
+         VG_(printf)("%p %s\n",  si->symtab[i].addr, si->symtab[i].name);
+      if (0 == VG_(strcmp)(name, si->symtab[i].name))
          return si->symtab[i].addr;
    }
    return (Addr)NULL;
@@ -2130,6 +1466,58 @@ static void search_all_loctabs ( Addr ptr, /*OUT*/SegInfo** psi,
 }
 
 
+/* Find a scope-table index containing the specified pointer, or -1
+   if not found.  Binary search.  */
+
+static Int search_one_scopetab ( SegInfo* si, Addr ptr )
+{
+   Addr a_mid_lo, a_mid_hi;
+   Int  mid, 
+        lo = 0, 
+        hi = si->scopetab_used-1;
+   while (True) {
+      /* current unsearched space is from lo to hi, inclusive. */
+      if (lo > hi) return -1; /* not found */
+      mid      = (lo + hi) / 2;
+      a_mid_lo = si->scopetab[mid].addr;
+      a_mid_hi = ((Addr)si->scopetab[mid].addr) + si->scopetab[mid].size - 1;
+
+      if (ptr < a_mid_lo) { hi = mid-1; continue; } 
+      if (ptr > a_mid_hi) { lo = mid+1; continue; }
+      vg_assert(ptr >= a_mid_lo && ptr <= a_mid_hi);
+      return mid;
+   }
+}
+
+
+/* Search all scopetabs that we know about to locate ptr.  If found, set
+   *psi to the relevant SegInfo, and *locno to the scopetab entry number
+   within that.  If not found, *psi is set to NULL.
+*/
+static void search_all_scopetabs ( Addr ptr,
+                                  /*OUT*/SegInfo** psi,
+                                  /*OUT*/Int* scopeno )
+{
+   Int      scno;
+   SegInfo* si;
+
+   VGP_PUSHCC(VgpSearchSyms);
+
+   for (si = segInfo; si != NULL; si = si->next) {
+      if (si->start <= ptr && ptr < si->start+si->size) {
+         scno = search_one_scopetab ( si, ptr );
+         if (scno == -1) goto not_found;
+         *scopeno = scno;
+         *psi = si;
+         VGP_POPCC(VgpSearchSyms);
+         return;
+      }
+   }
+  not_found:
+   *psi = NULL;
+   VGP_POPCC(VgpSearchSyms);
+}
+
 /* The whole point of this whole big deal: map a code address to a
    plausible symbol name.  Returns False if no idea; otherwise True.
    Caller supplies buf and nbuf.  If demangle is False, don't do
@@ -2147,10 +1535,10 @@ Bool get_fnname ( Bool demangle, Addr a, Char* buf, Int nbuf,
    if (si == NULL) 
       return False;
    if (demangle) {
-      VG_(demangle) ( & si->strtab[si->symtab[sno].nmoff], buf, nbuf );
+      VG_(demangle) ( si->symtab[sno].name, buf, nbuf );
    } else {
       VG_(strncpy_safely) 
-         ( buf, & si->strtab[si->symtab[sno].nmoff], nbuf );
+         ( buf, si->symtab[sno].name, nbuf );
    }
 
    offset = a - si->symtab[sno].addr;
@@ -2250,7 +1638,7 @@ Bool VG_(get_filename)( Addr a, Char* filename, Int n_filename )
    search_all_loctabs ( a, &si, &locno );
    if (si == NULL) 
       return False;
-   VG_(strncpy_safely)(filename, & si->strtab[si->loctab[locno].fnmoff]
+   VG_(strncpy_safely)(filename, si->loctab[locno].filename
                        n_filename);
    return True;
 }
@@ -2280,13 +1668,181 @@ Bool VG_(get_filename_linenum)( Addr a,
    search_all_loctabs ( a, &si, &locno );
    if (si == NULL) 
       return False;
-   VG_(strncpy_safely)(filename, & si->strtab[si->loctab[locno].fnmoff]
+   VG_(strncpy_safely)(filename, si->loctab[locno].filename
                        n_filename);
    *lineno = si->loctab[locno].lineno;
 
    return True;
 }
 
+#ifndef TEST
+
+/* return a pointer to a register (now for 5 other impossible things
+   before breakfast) */
+static UInt *regaddr(ThreadId tid, Int regno)
+{
+   UInt *ret = 0;
+
+   if (VG_(is_running_thread)(tid)) {
+      Int idx;
+
+      switch(regno) {
+      case R_EAX:      idx = VGOFF_(m_eax); break;
+      case R_ECX:      idx = VGOFF_(m_ecx); break;
+      case R_EDX:      idx = VGOFF_(m_edx); break;
+      case R_EBX:      idx = VGOFF_(m_ebx); break;
+      case R_ESP:      idx = VGOFF_(m_esp); break;
+      case R_EBP:      idx = VGOFF_(m_ebp); break;
+      case R_ESI:      idx = VGOFF_(m_esi); break;
+      case R_EDI:      idx = VGOFF_(m_edi); break;
+      default:         
+        idx = -1;
+        break;
+      }
+      if (idx != -1)
+        ret = &VG_(baseBlock)[idx];
+   } else {
+      ThreadState *tst = &VG_(threads)[tid];
+
+      switch(regno) {
+      case R_EAX:      ret = &tst->m_eax; break;
+      case R_ECX:      ret = &tst->m_ecx; break;
+      case R_EDX:      ret = &tst->m_edx; break;
+      case R_EBX:      ret = &tst->m_ebx; break;
+      case R_ESP:      ret = &tst->m_esp; break;
+      case R_EBP:      ret = &tst->m_ebp; break;
+      case R_ESI:      ret = &tst->m_esi; break;
+      case R_EDI:      ret = &tst->m_edi; break;
+      default: 
+        break;
+      }
+   }          
+
+   if (ret == 0) {
+      Char file[100];
+      Int line;
+      Addr eip = VG_(get_EIP)(tid);
+
+      if (!VG_(get_filename_linenum)(eip, file, sizeof(file), &line))
+        file[0] = 0;
+      VG_(printf)("mysterious register %d used at %p %s:%d\n",
+                 regno, eip, file, line);
+   }
+
+   return ret;
+}
+
+/* Get a list of all variables in scope, working out from the directly
+   current one */
+Variable *VG_(get_scope_variables)(ThreadId tid)
+{
+   static const Bool debug = False;
+   Variable *list, *end;
+   Addr eip;
+   SegInfo *si;
+   Int scopeidx;
+   Scope *scope;
+   Int distance;
+   static const Int maxsyms = 1000;
+   Int nsyms = maxsyms;
+
+   list = end = NULL;
+
+   eip = VG_(get_EIP)(tid);
+   
+   search_all_scopetabs(eip, &si, &scopeidx);
+
+   if (debug)
+      VG_(printf)("eip=%p si=%p (%s; offset=%p) scopeidx=%d\n", 
+                 eip, si, si ? si->filename : (Char *)"???",
+                 si ? si->offset : 0x99999, scopeidx);
+
+   if (si == NULL)
+      return NULL;             /* nothing in scope (should use global scope at least) */
+
+   if (debug) {
+      ScopeRange *sr = &si->scopetab[scopeidx];
+      Char file[100];
+      Int line;
+
+      if (!VG_(get_filename_linenum)(sr->addr, file, sizeof(file), &line))
+        file[0] = 0;
+
+      VG_(printf)("found scope range %p: eip=%p (%s:%d) size=%d scope=%p\n",
+                 sr, sr->addr, file, line, sr->size, sr->scope);
+   }
+
+   distance = 0;
+   for(scope = si->scopetab[scopeidx].scope; scope != NULL; scope = scope->outer, distance++) {
+      UInt i;
+
+      for(i = 0; i < scope->nsyms; i++) {
+        Sym *sym = &scope->syms[i];
+        Variable *v;
+
+        if (nsyms-- == 0) {
+           VG_(printf)("max %d syms reached\n", maxsyms);
+           return list;
+        }
+           
+        v = VG_(arena_malloc)(VG_AR_SYMTAB, sizeof(*v));
+
+        v->next = NULL;
+        v->distance = distance;
+        v->type = VG_(st_basetype)(sym->type, False);
+        v->name = VG_(arena_strdup)(VG_AR_SYMTAB, sym->name);
+        v->container = NULL;
+        v->size = VG_(st_sizeof)(sym->type);
+
+        if (debug && 0)
+           VG_(printf)("sym->name=%s sym->kind=%d offset=%d\n", sym->name, sym->kind, sym->offset);
+        switch(sym->kind) {
+           UInt reg;
+
+        case SyGlobal:
+        case SyStatic:
+           if (sym->addr == 0) {
+              /* XXX lookup value */
+           }
+           v->valuep = sym->addr;
+           break;
+
+        case SyReg:
+           v->valuep = (Addr)regaddr(tid, sym->regno);
+           break;
+
+        case SyEBPrel:
+        case SyESPrel:
+           reg = *regaddr(tid, sym->kind == SyESPrel ? R_ESP : R_EBP);
+           if (debug)
+              VG_(printf)("reg=%p+%d=%p\n", reg, sym->offset, reg+sym->offset);
+           v->valuep = (Addr)(reg + sym->offset);
+           break;
+
+        case SyType:
+           VG_(core_panic)("unexpected typedef in scope");
+        }
+
+        if (v->valuep == 0) {
+           /* not interesting or useful */
+           VG_(arena_free)(VG_AR_SYMTAB, v);
+           continue;
+        }
+
+        /* append to end of list */
+        if (list == NULL)
+           list = end = v;
+        else {
+           end->next = v;
+           end = v;
+        }
+      }  
+   }
+
+   return list;
+}
+#endif /* TEST */
+
 /* Print into buf info on code address, function name and filename */
 Char* VG_(describe_eip)(Addr eip, Char* buf, Int n_buf)
 {
diff --git a/coregrind/vg_symtab2.h b/coregrind/vg_symtab2.h
new file mode 100644 (file)
index 0000000..bf2f588
--- /dev/null
@@ -0,0 +1,168 @@
+
+#ifndef _VG_SYMTYPE_H
+#define _VG_SYMTYPE_H
+
+/*------------------------------------------------------------*/
+/*--- Structs n stuff                                      ---*/
+/*------------------------------------------------------------*/
+
+#include "vg_symtypes.h"
+
+/* A structure to hold an ELF symbol (very crudely). */
+typedef 
+   struct { 
+      Addr addr;   /* lowest address of entity */
+      UInt size;   /* size in bytes */
+      Char *name;  /* name */
+   }
+   RiSym;
+
+/* Line count at which overflow happens, due to line numbers being stored as
+ * shorts in `struct nlist' in a.out.h. */
+#define LINENO_OVERFLOW (1 << (sizeof(short) * 8))
+
+#define LINENO_BITS     20
+#define LOC_SIZE_BITS  (32 - LINENO_BITS)
+#define MAX_LINENO     ((1 << LINENO_BITS) - 1)
+
+/* Unlikely to have any lines with instruction ranges > 4096 bytes */
+#define MAX_LOC_SIZE   ((1 << LOC_SIZE_BITS) - 1)
+
+/* Number used to detect line number overflows;  if one line is 60000-odd
+ * smaller than the previous, is was probably an overflow.  
+ */
+#define OVERFLOW_DIFFERENCE     (LINENO_OVERFLOW - 5000)
+
+/* A structure to hold addr-to-source info for a single line.  There can be a
+ * lot of these, hence the dense packing. */
+typedef
+   struct {
+      /* Word 1 */
+      Addr   addr;                  /* lowest address for this line */
+      /* Word 2 */
+      UShort size:LOC_SIZE_BITS;    /* byte size; we catch overflows of this */
+      UInt   lineno:LINENO_BITS;    /* source line number, or zero */
+      /* Word 3 */
+      Char*  filename;                /* source filename */
+   }
+   RiLoc;
+
+
+/* A structure to hold a set of variables in a particular scope */
+typedef struct _Scope Scope;   /* a set of symbols in one scope */
+typedef struct _Sym Sym;       /* a single symbol */
+typedef struct _ScopeRange ScopeRange; /* a range of code addreses a scope covers */
+
+typedef enum {
+      SyESPrel,                        /* on the stack (relative to ESP) */
+      SyEBPrel,                        /* on the stack (relative to EBP) */
+      SyReg,                   /* in a register */
+      SyType,                  /* a type definition */
+      SyStatic,                        /* a static variable */
+      SyGlobal,                        /* a global variable (XXX any different to static
+                                  in an outer scope?) */
+} SyKind;
+
+struct _Sym {
+   SymType     *type;          /* type */
+   Char                *name;          /* name */
+   SyKind      kind;           /* kind of symbol */
+
+   /* a value, depending on kind */
+   union {
+      Int      offset;         /* offset on stack (-ve -> ebp; +ve -> esp) */
+      Int      regno;          /* register number */
+      Addr     addr;           /* static or global address */
+   };
+};
+
+struct _Scope {
+   Scope       *outer;         /* outer (containing) scope */
+   UInt                nsyms;          /* number of symbols in this scope */
+   UInt                depth;          /* depth of scope */
+   Sym         *syms;          /* the symbols */
+};
+
+/* A structure to map a scope to a range of code addresses; scopes may
+   be broken into multiple ranges (before and after a nested scope) */
+struct _ScopeRange {
+   Addr                addr;                   /* start address of this scope */
+   Int         size;                   /* length of scope */
+   Scope       *scope;                 /* symbols in scope */
+};
+
+#define STRCHUNKSIZE   (64*1024)
+
+/* A structure which contains information pertaining to one mapped
+   text segment. (typedef in vg_skin.h) */
+struct _SegInfo {
+   struct _SegInfo* next;
+   /* Description of the mapped segment. */
+   Addr   start;
+   UInt   size;
+   Char*  filename; /* in mallocville */
+   UInt   foffset;
+   /* An expandable array of symbols. */
+   RiSym* symtab;
+   UInt   symtab_used;
+   UInt   symtab_size;
+   /* An expandable array of locations. */
+   RiLoc* loctab;
+   UInt   loctab_used;
+   UInt   loctab_size;
+   /* An expandable array of scope ranges. */
+   ScopeRange *scopetab;
+   UInt        scopetab_used;
+   UInt        scopetab_size;
+
+   /* Expandable arrays of characters -- the string table.
+      Pointers into this are stable (the arrays are not reallocated)
+    */
+   struct strchunk {
+      UInt   strtab_used;
+      struct strchunk *next;
+      Char   strtab[STRCHUNKSIZE];
+   }      *strchunks;
+
+   /* offset    is what we need to add to symbol table entries
+      to get the real location of that symbol in memory.
+   */
+   UInt   offset;
+
+   /* Bounds of data, BSS, PLT and GOT, so that skins can see what
+      section an address is in */
+   Addr          plt_start;
+   UInt   plt_size;
+   Addr   got_start;
+   UInt   got_size;
+   Addr   data_start;
+   UInt   data_size;
+   Addr   bss_start;
+   UInt   bss_size;
+
+   /* data used by stabs parser */
+   struct _StabTypeTab *stab_typetab;
+};
+
+Char *VG_(addStr) ( SegInfo* si, Char* str, Int len );
+void VG_(addScopeInfo) ( SegInfo* si, Addr this, Addr next, Scope *scope);
+void VG_(addLineInfo) ( SegInfo* si, Char* filename, Addr this, Addr next, Int lineno, Int entry);
+
+/* Non-fatal -- use vg_panic if terminal. */
+void VG_(symerr) ( Char* msg );
+
+/* --------------------
+   Stabs reader
+   -------------------- */
+
+void VG_(read_debuginfo_stabs) ( SegInfo* si,
+                                UChar* stabC,   Int stab_sz, 
+                                UChar* stabstr, Int stabstr_sz );
+
+
+/* --------------------
+   DWARF2 reader
+   -------------------- */
+void VG_(read_debuginfo_dwarf2) ( SegInfo* si, UChar* dwarf2, Int dwarf2_sz );
+
+#endif /* _VG_SYMTYPE_H */
diff --git a/coregrind/vg_symtab_dwarf.c b/coregrind/vg_symtab_dwarf.c
new file mode 100644 (file)
index 0000000..3894a93
--- /dev/null
@@ -0,0 +1,511 @@
+/* --------------------------------------------------------------------------------
+   DWARF2-specific core file handling
+   -------------------------------------------------------------------------------- */
+
+#include "vg_include.h"
+#include "vg_symtab2.h"
+
+
+/*------------------------------------------------------------*/
+/*--- Read DWARF2 format debug info.                       ---*/
+/*------------------------------------------------------------*/
+
+/* Structure found in the .debug_line section.  */
+typedef struct
+{
+  UChar li_length          [4];
+  UChar li_version         [2];
+  UChar li_prologue_length [4];
+  UChar li_min_insn_length [1];
+  UChar li_default_is_stmt [1];
+  UChar li_line_base       [1];
+  UChar li_line_range      [1];
+  UChar li_opcode_base     [1];
+}
+DWARF2_External_LineInfo;
+
+typedef struct
+{
+  UInt   li_length;
+  UShort li_version;
+  UInt   li_prologue_length;
+  UChar  li_min_insn_length;
+  UChar  li_default_is_stmt;
+  Int    li_line_base;
+  UChar  li_line_range;
+  UChar  li_opcode_base;
+}
+DWARF2_Internal_LineInfo;
+
+/* Line number opcodes.  */
+enum dwarf_line_number_ops
+  {
+    DW_LNS_extended_op = 0,
+    DW_LNS_copy = 1,
+    DW_LNS_advance_pc = 2,
+    DW_LNS_advance_line = 3,
+    DW_LNS_set_file = 4,
+    DW_LNS_set_column = 5,
+    DW_LNS_negate_stmt = 6,
+    DW_LNS_set_basic_block = 7,
+    DW_LNS_const_add_pc = 8,
+    DW_LNS_fixed_advance_pc = 9,
+    /* DWARF 3.  */
+    DW_LNS_set_prologue_end = 10,
+    DW_LNS_set_epilogue_begin = 11,
+    DW_LNS_set_isa = 12
+  };
+
+/* Line number extended opcodes.  */
+enum dwarf_line_number_x_ops
+  {
+    DW_LNE_end_sequence = 1,
+    DW_LNE_set_address = 2,
+    DW_LNE_define_file = 3
+  };
+
+typedef struct State_Machine_Registers
+{
+  /* Information for the last statement boundary.
+   * Needed to calculate statement lengths. */
+  Addr  last_address;
+  UInt  last_file;
+  UInt  last_line;
+
+  Addr  address;
+  UInt  file;
+  UInt  line;
+  UInt  column;
+  Int   is_stmt;
+  Int   basic_block;
+  Int   end_sequence;
+  /* This variable hold the number of the last entry seen
+     in the File Table.  */
+  UInt  last_file_entry;
+} SMR;
+
+
+static 
+UInt read_leb128 ( UChar* data, Int* length_return, Int sign )
+{
+  UInt   result = 0;
+  UInt   num_read = 0;
+  Int    shift = 0;
+  UChar  byte;
+
+  do
+    {
+      byte = * data ++;
+      num_read ++;
+
+      result |= (byte & 0x7f) << shift;
+
+      shift += 7;
+
+    }
+  while (byte & 0x80);
+
+  if (length_return != NULL)
+    * length_return = num_read;
+
+  if (sign && (shift < 32) && (byte & 0x40))
+    result |= -1 << shift;
+
+  return result;
+}
+
+
+static SMR state_machine_regs;
+
+static 
+void reset_state_machine ( Int is_stmt )
+{
+  if (0) VG_(printf)("smr.a := %p (reset)\n", 0 );
+  state_machine_regs.last_address = 0;
+  state_machine_regs.last_file = 1;
+  state_machine_regs.last_line = 1;
+  state_machine_regs.address = 0;
+  state_machine_regs.file = 1;
+  state_machine_regs.line = 1;
+  state_machine_regs.column = 0;
+  state_machine_regs.is_stmt = is_stmt;
+  state_machine_regs.basic_block = 0;
+  state_machine_regs.end_sequence = 0;
+  state_machine_regs.last_file_entry = 0;
+}
+
+/* Handled an extend line op.  Returns true if this is the end
+   of sequence.  */
+static 
+int process_extended_line_op( SegInfo *si, Char*** fnames, 
+                              UChar* data, Int is_stmt, Int pointer_size)
+{
+  UChar   op_code;
+  Int     bytes_read;
+  UInt    len;
+  UChar * name;
+  Addr    adr;
+
+  len = read_leb128 (data, & bytes_read, 0);
+  data += bytes_read;
+
+  if (len == 0)
+    {
+      VG_(message)(Vg_UserMsg,
+         "badly formed extended line op encountered!\n");
+      return bytes_read;
+    }
+
+  len += bytes_read;
+  op_code = * data ++;
+
+  if (0) VG_(printf)("dwarf2: ext OPC: %d\n", op_code);
+
+  switch (op_code)
+    {
+    case DW_LNE_end_sequence:
+      if (0) VG_(printf)("1001: si->o %p, smr.a %p\n", 
+                         si->offset, state_machine_regs.address );
+      state_machine_regs.end_sequence = 1; /* JRS: added for compliance
+         with spec; is pointless due to reset_state_machine below 
+      */
+      if (state_machine_regs.is_stmt) {
+        if (state_machine_regs.last_address)
+           VG_(addLineInfo) (si, (*fnames)[state_machine_regs.last_file], 
+                             si->offset + state_machine_regs.last_address, 
+                             si->offset + state_machine_regs.address, 
+                             state_machine_regs.last_line, 0);
+      }
+      reset_state_machine (is_stmt);
+      break;
+
+    case DW_LNE_set_address:
+      /* XXX: Pointer size could be 8 */
+      vg_assert(pointer_size == 4);
+      adr = *((Addr *)data);
+      if (0) VG_(printf)("smr.a := %p\n", adr );
+      state_machine_regs.address = adr;
+      break;
+
+    case DW_LNE_define_file:
+      ++ state_machine_regs.last_file_entry;
+      name = data;
+      if (*fnames == NULL)
+        *fnames = VG_(arena_malloc)(VG_AR_SYMTAB, sizeof (UInt) * 2);
+      else
+        *fnames = VG_(arena_realloc)(
+                     VG_AR_SYMTAB, *fnames, /*alignment*/4,
+                     sizeof(UInt) 
+                        * (state_machine_regs.last_file_entry + 1));
+      (*fnames)[state_machine_regs.last_file_entry] = VG_(addStr) (si,name, -1);
+      data += VG_(strlen) ((char *) data) + 1;
+      read_leb128 (data, & bytes_read, 0);
+      data += bytes_read;
+      read_leb128 (data, & bytes_read, 0);
+      data += bytes_read;
+      read_leb128 (data, & bytes_read, 0);
+      break;
+
+    default:
+      break;
+    }
+
+  return len;
+}
+
+
+void VG_(read_debuginfo_dwarf2) ( SegInfo* si, UChar* dwarf2, Int dwarf2_sz )
+{
+  DWARF2_External_LineInfo * external;
+  DWARF2_Internal_LineInfo   info;
+  UChar *            standard_opcodes;
+  UChar *            data = dwarf2;
+  UChar *            end  = dwarf2 + dwarf2_sz;
+  UChar *            end_of_sequence;
+  Char  **           fnames = NULL;
+
+  /* Fails due to gcc padding ...
+  vg_assert(sizeof(DWARF2_External_LineInfo)
+            == sizeof(DWARF2_Internal_LineInfo));
+  */
+
+  while (data < end)
+    {
+      external = (DWARF2_External_LineInfo *) data;
+
+      /* Check the length of the block.  */
+      info.li_length = * ((UInt *)(external->li_length));
+
+      if (info.li_length == 0xffffffff)
+       {
+         VG_(symerr)("64-bit DWARF line info is not supported yet.");
+         break;
+       }
+
+      if (info.li_length + sizeof (external->li_length) > dwarf2_sz)
+       {
+        VG_(symerr)("DWARF line info appears to be corrupt "
+                  "- the section is too small");
+         return;
+       }
+
+      /* Check its version number.  */
+      info.li_version = * ((UShort *) (external->li_version));
+      if (info.li_version != 2)
+       {
+         VG_(symerr)("Only DWARF version 2 line info "
+                   "is currently supported.");
+         return;
+       }
+
+      info.li_prologue_length = * ((UInt *) (external->li_prologue_length));
+      info.li_min_insn_length = * ((UChar *)(external->li_min_insn_length));
+
+      info.li_default_is_stmt = True; 
+         /* WAS: = * ((UChar *)(external->li_default_is_stmt)); */
+         /* Josef Weidendorfer (20021021) writes:
+
+            It seems to me that the Intel Fortran compiler generates
+            bad DWARF2 line info code: It sets "is_stmt" of the state
+            machine in the the line info reader to be always
+            false. Thus, there is never a statement boundary generated
+            and therefore never a instruction range/line number
+            mapping generated for valgrind.
+
+            Please have a look at the DWARF2 specification, Ch. 6.2
+            (x86.ddj.com/ftp/manuals/tools/dwarf.pdf).  Perhaps I
+            understand this wrong, but I don't think so.
+
+            I just had a look at the GDB DWARF2 reader...  They
+            completely ignore "is_stmt" when recording line info ;-)
+            That's the reason "objdump -S" works on files from the the
+            intel fortran compiler.  
+         */
+
+
+      /* JRS: changed (UInt*) to (UChar*) */
+      info.li_line_base       = * ((UChar *)(external->li_line_base));
+
+      info.li_line_range      = * ((UChar *)(external->li_line_range));
+      info.li_opcode_base     = * ((UChar *)(external->li_opcode_base)); 
+
+      if (0) VG_(printf)("dwarf2: line base: %d, range %d, opc base: %d\n",
+                 info.li_line_base, info.li_line_range, info.li_opcode_base);
+
+      /* Sign extend the line base field.  */
+      info.li_line_base <<= 24;
+      info.li_line_base >>= 24;
+
+      end_of_sequence = data + info.li_length 
+                             + sizeof (external->li_length);
+
+      reset_state_machine (info.li_default_is_stmt);
+
+      /* Read the contents of the Opcodes table.  */
+      standard_opcodes = data + sizeof (* external);
+
+      /* Read the contents of the Directory table.  */
+      data = standard_opcodes + info.li_opcode_base - 1;
+
+      if (* data == 0) 
+       {
+       }
+      else
+       {
+         /* We ignore the directory table, since gcc gives the entire
+            path as part of the filename */
+         while (* data != 0)
+           {
+             data += VG_(strlen) ((char *) data) + 1;
+           }
+       }
+
+      /* Skip the NUL at the end of the table.  */
+      if (*data != 0) {
+         VG_(symerr)("can't find NUL at end of DWARF2 directory table");
+         return;
+      }
+      data ++;
+
+      /* Read the contents of the File Name table.  */
+      if (* data == 0)
+       {
+       }
+      else
+       {
+         while (* data != 0)
+           {
+             UChar * name;
+             Int bytes_read;
+
+             ++ state_machine_regs.last_file_entry;
+             name = data;
+             /* Since we don't have realloc (0, ....) == malloc (...)
+               semantics, we need to malloc the first time. */
+
+             if (fnames == NULL)
+               fnames = VG_(arena_malloc)(VG_AR_SYMTAB, sizeof (UInt) * 2);
+             else
+               fnames = VG_(arena_realloc)(VG_AR_SYMTAB, fnames, /*alignment*/4,
+                           sizeof(UInt) 
+                              * (state_machine_regs.last_file_entry + 1));
+             data += VG_(strlen) ((Char *) data) + 1;
+             fnames[state_machine_regs.last_file_entry] = VG_(addStr) (si,name, -1);
+
+             read_leb128 (data, & bytes_read, 0);
+             data += bytes_read;
+             read_leb128 (data, & bytes_read, 0);
+             data += bytes_read;
+             read_leb128 (data, & bytes_read, 0);
+             data += bytes_read;
+           }
+       }
+
+      /* Skip the NUL at the end of the table.  */
+      if (*data != 0) {
+         VG_(symerr)("can't find NUL at end of DWARF2 file name table");
+         return;
+      }
+      data ++;
+
+      /* Now display the statements.  */
+
+      while (data < end_of_sequence)
+       {
+         UChar op_code;
+         Int           adv;
+         Int           bytes_read;
+
+         op_code = * data ++;
+
+        if (0) VG_(printf)("dwarf2: OPC: %d\n", op_code);
+
+         if (op_code >= info.li_opcode_base)
+           {
+             Int advAddr;
+             op_code -= info.li_opcode_base;
+             adv      = (op_code / info.li_line_range) 
+                           * info.li_min_insn_length;
+             advAddr = adv;
+             state_machine_regs.address += adv;
+             if (0) VG_(printf)("smr.a += %p\n", adv );
+             adv = (op_code % info.li_line_range) + info.li_line_base;
+             if (0) VG_(printf)("1002: si->o %p, smr.a %p\n", 
+                                si->offset, state_machine_regs.address );
+             state_machine_regs.line += adv;
+
+            if (state_machine_regs.is_stmt) {
+                /* only add a statement if there was a previous boundary */
+                if (state_machine_regs.last_address) 
+                    VG_(addLineInfo) (si, fnames[state_machine_regs.last_file], 
+                                      si->offset + state_machine_regs.last_address, 
+                                      si->offset + state_machine_regs.address, 
+                                      state_machine_regs.last_line, 0);
+                state_machine_regs.last_address = state_machine_regs.address;
+                state_machine_regs.last_file = state_machine_regs.file;
+                state_machine_regs.last_line = state_machine_regs.line;
+            }
+           }
+         else switch (op_code)
+           {
+           case DW_LNS_extended_op:
+             data += process_extended_line_op (
+                        si, &fnames, data, 
+                        info.li_default_is_stmt, sizeof (Addr));
+             break;
+
+           case DW_LNS_copy:
+             if (0) VG_(printf)("1002: si->o %p, smr.a %p\n", 
+                                si->offset, state_machine_regs.address );
+            if (state_machine_regs.is_stmt) {
+               /* only add a statement if there was a previous boundary */
+               if (state_machine_regs.last_address) 
+                    VG_(addLineInfo) (si, fnames[state_machine_regs.last_file], 
+                                      si->offset + state_machine_regs.last_address, 
+                                      si->offset + state_machine_regs.address,
+                                      state_machine_regs.last_line, 0);
+                state_machine_regs.last_address = state_machine_regs.address;
+                state_machine_regs.last_file = state_machine_regs.file;
+                state_machine_regs.last_line = state_machine_regs.line;
+            }
+             state_machine_regs.basic_block = 0; /* JRS added */
+             break;
+
+           case DW_LNS_advance_pc:
+             adv = info.li_min_insn_length 
+                      * read_leb128 (data, & bytes_read, 0);
+             data += bytes_read;
+             state_machine_regs.address += adv;
+             if (0) VG_(printf)("smr.a += %p\n", adv );
+             break;
+
+           case DW_LNS_advance_line:
+             adv = read_leb128 (data, & bytes_read, 1);
+             data += bytes_read;
+             state_machine_regs.line += adv;
+             break;
+
+           case DW_LNS_set_file:
+             adv = read_leb128 (data, & bytes_read, 0);
+             data += bytes_read;
+             state_machine_regs.file = adv;
+             break;
+
+           case DW_LNS_set_column:
+             adv = read_leb128 (data, & bytes_read, 0);
+             data += bytes_read;
+             state_machine_regs.column = adv;
+             break;
+
+           case DW_LNS_negate_stmt:
+             adv = state_machine_regs.is_stmt;
+             adv = ! adv;
+             state_machine_regs.is_stmt = adv;
+             break;
+
+           case DW_LNS_set_basic_block:
+             state_machine_regs.basic_block = 1;
+             break;
+
+           case DW_LNS_const_add_pc:
+             adv = (((255 - info.li_opcode_base) / info.li_line_range)
+                    * info.li_min_insn_length);
+             state_machine_regs.address += adv;
+             if (0) VG_(printf)("smr.a += %p\n", adv );
+             break;
+
+           case DW_LNS_fixed_advance_pc:
+             /* XXX: Need something to get 2 bytes */
+             adv = *((UShort *)data);
+             data += 2;
+             state_machine_regs.address += adv;
+             if (0) VG_(printf)("smr.a += %p\n", adv );
+             break;
+
+           case DW_LNS_set_prologue_end:
+             break;
+
+           case DW_LNS_set_epilogue_begin:
+             break;
+
+           case DW_LNS_set_isa:
+             adv = read_leb128 (data, & bytes_read, 0);
+             data += bytes_read;
+             break;
+
+           default:
+             {
+               int j;
+               for (j = standard_opcodes[op_code - 1]; j > 0 ; --j)
+                 {
+                   read_leb128 (data, &bytes_read, 0);
+                   data += bytes_read;
+                 }
+             }
+             break;
+           }
+       }
+      VG_(arena_free)(VG_AR_SYMTAB, fnames);
+      fnames = NULL;
+    }
+}
diff --git a/coregrind/vg_symtab_stabs.c b/coregrind/vg_symtab_stabs.c
new file mode 100644 (file)
index 0000000..6ecfeac
--- /dev/null
@@ -0,0 +1,1570 @@
+/* --------------------------------------------------------------------------------
+   Stabs-specific core file handling
+   -------------------------------------------------------------------------------- */
+
+#include "vg_include.h"
+#include "vg_symtab2.h"
+
+#include <a.out.h>        /* stabs defns                    */
+
+/*------------------------------------------------------------*/
+/*--- Read STABS format debug info.                        ---*/
+/*------------------------------------------------------------*/
+
+/* Stabs entry types, from:
+ *   The "stabs" debug format
+ *   Menapace, Kingdon and MacKenzie
+ *   Cygnus Support
+ */
+typedef enum { N_UNDEF = 0,    /* undefined symbol, new stringtab  */
+              N_GSYM  = 32,    /* Global symbol                    */
+               N_FUN   = 36,    /* Function start or end            */
+               N_STSYM = 38,    /* Data segment file-scope variable */
+               N_LCSYM = 40,    /* BSS segment file-scope variable  */
+               N_RSYM  = 64,    /* Register variable                */
+               N_SLINE = 68,    /* Source line number               */
+               N_SO    = 100,   /* Source file path and name        */
+               N_LSYM  = 128,   /* Stack variable or type           */
+              N_BINCL = 130,   /* Beginning of an include file     */
+               N_SOL   = 132,   /* Include file name                */
+              N_PSYM  = 160,   /* Function parameter               */
+              N_EINCL = 162,   /* End of an include file           */
+               N_LBRAC = 192,   /* Start of lexical block           */
+              N_EXCL  = 194,   /* Placeholder for an include file  */
+               N_RBRAC = 224    /* End   of lexical block           */
+             } stab_types;
+      
+
+/* stabs use a two-dimentional numbering scheme for types: the type
+   number is either of the form name:N or name:(M,N); name may be
+   empty.  N is the type number within a file context; M is the file
+   number (an object may have multiple files by inclusion).
+*/
+
+typedef struct _StabType {
+   Char                *str;           /* string as it appears in file */
+   SymType     *type;          /* our type info */
+} StabType;
+
+typedef struct _StabFile {
+   StabType    *types;
+   Int         ntypes;
+   UInt                fileidx;        /* for reference, idx of creation */
+} StabFile;
+
+typedef struct _StabTypeTab {
+   StabFile    **files;
+   Int         nfiles;
+
+   /* List of structure tag names, used for mapping them to actual
+      definitions of the structures.  There should really be one of
+      these per object and a global one to cope with cross-object
+      references. */
+   struct structlist {
+      Char             *name;
+      Bool             isstruct; /* struct (or union) */
+      SymType          *type;  /* reference */
+      struct structlist *next;
+   }           *structlist;
+
+#define HEADER_HASHSZ  53
+   struct header {
+      Char             *filename;      /* header file name */
+      StabFile         *types;         /* types for that header */
+      UInt             instance;       /* instance */
+      struct header    *next;
+   }           *headerhash[HEADER_HASHSZ];
+} StabTypeTab;
+
+
+static UInt header_hash(Char *filename, UInt instance)
+{
+   Char *cp;
+   UInt hash = 0;
+
+   for(cp = filename; *cp; cp++) {
+      hash += *cp;
+      hash = (hash << 17) | (hash >> (32-17));
+   }
+   hash += instance;
+
+   return hash % HEADER_HASHSZ;
+}
+
+/* Look up a struct/union tag name in table, and return reference to
+   existing type, or create a new tag entry.
+   XXX make this a proper data structure
+*/
+static SymType *structRef(StabTypeTab *tab, SymType *def, Bool isstruct, Char *name)
+{
+   static const Bool debug = False;
+   struct structlist *sl;
+   SymType *ty;
+   static Int warnlen = 0;
+   Int len = 0;
+
+   for(sl = tab->structlist; sl != NULL; sl = sl->next) {
+      len++;
+
+      if (isstruct == sl->isstruct && VG_(strcmp)(name, sl->name) == 0) {
+        if (debug)
+           VG_(printf)("found %s ref for %s\n",
+                       isstruct ? "struct" : "union", name);
+        return sl->type;
+      }
+   }
+
+   if (debug && (len > warnlen*2)) {
+      warnlen = len;
+      VG_(printf)("struct ref list reached %d entries\n", len);
+   }
+
+   sl = VG_(arena_malloc)(VG_AR_SYMTAB, sizeof(*sl));
+   if (isstruct)
+      ty = VG_(st_mkstruct)(def, 0, 0);
+   else
+      ty = VG_(st_mkunion)(def, 0, 0);
+
+   VG_(st_setname)(ty, name);
+   sl->isstruct = isstruct;
+   sl->type = ty;
+   sl->name = name;
+   sl->next = tab->structlist;
+   tab->structlist = sl;
+
+   if (debug)
+      VG_(printf)("created %s ref for %s = %p\n",
+                 isstruct ? "struct" : "union", name, ty);
+
+   return ty;
+}
+
+/* Add a structural defintion for a struct/union reference */
+static SymType *structDef(StabTypeTab *tab, SymType *def, Bool isstruct, Char *name)
+{
+   static const Bool debug = False;
+   SymType *ref = structRef(tab, NULL, isstruct, name);
+
+   if (debug)
+      VG_(printf)("defining %s ref for %s %p -> %p\n",
+                 isstruct ? "struct" : "union", name, ref, def);
+
+   def = VG_(st_mktypedef)(ref, name, VG_(st_basetype)(def, False));
+   VG_(st_setname)(def, name);
+   return def;
+}
+
+static StabFile *getStabFile(StabTypeTab *tab, Int file, StabFile *set)
+{
+   StabFile *sf;
+   file++;                     /* file == -1 -> no file */
+
+   if (file < 0)
+      return NULL;
+
+   if (file >= tab->nfiles) {
+      UInt i;
+      StabFile **n = VG_(arena_malloc)(VG_AR_SYMTAB, (file+1) * sizeof(*n));
+
+      for(i = 0; i <= file; i++) {
+        if (i < tab->nfiles)
+           n[i] = tab->files[i];
+        else {
+           n[i] = NULL;
+        }
+      }
+
+      if (tab->files != NULL)
+        VG_(arena_free)(VG_AR_SYMTAB, tab->files);
+
+      tab->files = n;
+      tab->nfiles = file+1;
+   }
+
+   if (set != NULL)
+      tab->files[file] = set;
+
+   if (tab->files[file] == NULL) {
+      sf = VG_(arena_malloc)(VG_AR_SYMTAB, sizeof(*sf));
+      tab->files[file] = sf;
+      sf->types = NULL;
+      sf->ntypes = 0;
+      sf->fileidx = file - 1;  /* compensate for file++ above */
+   }
+
+   sf = tab->files[file];
+   
+   return sf;
+}
+
+/* add a new index for a file */
+static void addFileAlias(StabTypeTab *tab, Char *filename, UInt instance, Int idx)
+{
+   static const Bool debug = False;
+   struct header *hp;
+
+   for(hp = tab->headerhash[header_hash(filename, instance)]; hp != NULL; hp = hp->next) {
+      if (hp->instance == instance && VG_(strcmp)(filename, hp->filename) == 0) {
+        if (debug)
+           VG_(printf)("adding alias for \"%s\"/%d fileidx %d to fileidx %d\n",
+                       filename, instance, idx, hp->types->fileidx);
+        getStabFile(tab, idx, hp->types);
+        return;
+      }
+   }
+
+   VG_(printf)("Couldn't find previous reference to \"%s\"/%d for fileidx %d\n", 
+              filename, instance, idx);
+}
+
+static void addHeader(StabTypeTab *tab, Char *filename, UInt instance, Int idx)
+{
+   static const Bool debug = False;
+   struct header *hp, **bucket;
+   
+   if (debug)
+      VG_(printf)("adding new header %s/%d fileidx %d\n", filename, instance, idx);
+
+   bucket = &tab->headerhash[header_hash(filename, instance)];
+
+   hp = VG_(arena_malloc)(VG_AR_SYMTAB, sizeof(*hp));
+   hp->filename = filename;
+   hp->instance = instance;
+   hp->types = getStabFile(tab, idx, NULL);
+   hp->next = *bucket;
+   *bucket = hp;
+}
+
+static void clearStabFiles(StabTypeTab *tab)
+{
+   VG_(arena_free)(VG_AR_SYMTAB, tab->files);
+
+   tab->files = NULL;
+   tab->nfiles = 0;
+}
+
+static StabType *getStabType(StabTypeTab *tab, Int file, Int sym)
+{
+   StabFile *sf;
+   
+   sf = getStabFile(tab, file, NULL);
+
+   if (sf == NULL || sym < 0)
+      return NULL;
+
+   if (sym >= sf->ntypes) {
+      UInt i;
+      StabType *n = VG_(arena_malloc)(VG_AR_SYMTAB, (sym+1) * sizeof(*n));
+
+      for(i = 0; i <= sym; i++) {
+        if (i < sf->ntypes)
+           n[i] = sf->types[i];
+        else {
+           n[i].str = NULL;
+           n[i].type = NULL;
+        }
+      }
+
+      if (sf->types != NULL)
+        VG_(arena_free)(VG_AR_SYMTAB, sf->types);
+
+      sf->types = n;
+      sf->ntypes = sym+1;
+   }
+
+   return &sf->types[sym];
+}
+
+static inline Bool isdigit(Char c, Int base, Int *v)
+{
+   switch(base) {
+   case 10:
+   case 0:
+      *v = c - '0';
+      return c >= '0' && c <= '9';
+
+   case 8:
+      *v = c - '0';
+      return c >= '0' && c <= '7';
+
+   case 16:
+      if (c >= '0' && c <= '9') {
+        *v = c - '0';
+        return True;
+      }
+      if (c >= 'a' && c <= 'f') {
+        *v = c - 'a';
+        return True;
+      }
+      if (c >= 'A' && c <= 'F') {
+        *v = c - 'F';
+        return True;
+      }
+      return False;
+   }
+
+   return False;
+}
+
+static inline Int getbase(Char **pp)
+{
+   Char *p = *pp;
+   Int base = 10;
+
+   if (p[0] == '0') {
+      if (p[1] == 'x') {
+        base = 16;
+        p += 2;
+      } else {
+        base = 8;
+        p++;
+      }
+   }
+   *pp = p;
+
+   return base;
+}
+
+static Int atoi(Char **pp, Int base)
+{
+   Char *p = *pp;
+   Int ret = 0;
+   Int v;
+   Bool neg = False;
+
+   if (*p == '-') {
+      neg = True;
+      p++;
+   }
+
+   if (base == 0)
+      base = getbase(&p);
+
+   while(isdigit(*p, base, &v)) {
+      ret *= base;
+      ret += v;
+      p++;
+   }
+
+   *pp = p;
+   if (neg)
+      ret = -ret;
+   return ret;
+}
+
+static UInt atou(Char **pp, Int base)
+{
+   Char *p = *pp;
+   UInt ret = 0;
+   Int v;
+
+   if (base == 0)
+      base = getbase(&p);
+
+   while(isdigit(*p, base, &v)) {
+      ret *= base;
+      ret += v;
+      p++;
+   }
+
+   *pp = p;
+   return ret;
+}
+
+/* Skip to end of name (':'); "::" in the name indicate nesting, and
+   are included within the name */
+static Char *nested_name(Char *p) {
+   while(*p && (p[0] != ':' || p[1] == ':')) {
+      if (*p == ':')
+        p++;
+      p++;
+   }
+
+   return p;
+}
+
+/* updates pp to point to after parsed typeref */
+static void parse_typeref(Char **pp, Int *filep, Int *symp)
+{
+   Char *p = *pp;
+   Int file, sym;
+
+   file = sym = *filep = *symp = -1;
+
+   if (*p == '(') {
+      p++;
+      file = atoi(&p, 10);
+      if (*p++ != ',')
+        return;
+      sym = atoi(&p, 10);
+      if (*p++ != ')')
+        return;
+   } else if (VG_(isdigit)(*p)) {
+      sym = atoi(&p, 10);
+   }
+   
+   *pp = p;
+   *filep = file;
+   *symp = sym;
+}
+
+static void stab_resolve(SymType *st, void *data)
+{
+   static const Bool debug = False;
+   Char *str = (Char *)data;
+   vg_assert(!VG_(st_isresolved)(st));
+
+   if (debug)
+      VG_(printf)("stab_resolve: failing to do anything useful with symtype %p=%s\n", 
+                 st, str);
+}
+
+/* Top level of recursive descent parser for stab type information.
+   This only extracts the information needed by vg_symtypes.c, which
+   is just structure shapes, pointers and arrays.  It is still
+   necessary to parse everything else, because there's no way to skip
+   it to get to the interesting bits.  Also, new types can be
+   introduced anywhere, so we need to scan it all to pick them up. */
+static SymType *stabtype_parser(SegInfo *si, SymType *def, Char **pp)
+{
+   static const Bool debug = False;
+   Char *p = *pp;
+   Char t;
+   SymType *type;
+   StabTypeTab *tab = si->stab_typetab;
+
+/* make sure *p == 'c' and skip over it */
+#define EXPECT(c, msg)                                                                 \
+   do {                                                                                        \
+      if (p == NULL || *p++ != c) {                                                    \
+        VG_(printf)("\n @@ expected '%c' at %s (remains=\"%s\")\n", c, msg, p);        \
+        return NULL;                                                                   \
+      }                                                                                        \
+   } while(0)
+
+/* return a pointer to just after the next ch after (and including) ptr */
+#define SKIPPAST(ptr, ch, msg)                                                         \
+   ({                                                                                  \
+      Char *__zz_charptr = VG_(strchr)((ptr), (ch));                                   \
+      if (__zz_charptr == NULL) {                                                      \
+        VG_(printf)("\n @@ expected '%c' at %s (ptr=\"%s\")\n", (ch), (msg), (ptr));   \
+        return NULL;                                                                   \
+      }                                                                                        \
+      __zz_charptr+1;                                                                  \
+   })
+
+   t = *p++;
+
+   if (0 && debug)
+      VG_(printf)("stabtype_parser: parsing '%c' remains=\"%s\"\n", t, p);
+
+   switch(t) {
+   case '(':
+   case '0' ... '9': {         /* reference (and perhaps definition) */
+      SymType *symtype;
+      Int file, sym;
+      Char *prev;
+
+      p--;
+      prev = p;
+
+      parse_typeref(&p, &file, &sym);
+
+      {
+        /* keep stabtype reference local, because the stabtype table
+           can be rearranged by new insertions, invalidating this
+           pointer; so copy the bits we need and don't hold onto the
+           pointer. */
+        StabType *stabtype = getStabType(tab, file, sym);
+
+        if (stabtype == NULL) {
+           VG_(printf)(" @@ bad type ref: %s\n", prev);
+           return NULL;
+        }
+
+        if (stabtype->type == NULL) {
+           stabtype->type = VG_(st_mkunresolved)(def, stab_resolve, NULL);
+           if (debug)
+              VG_(printf)("making (%d,%d) %p unresolved\n", file, sym, stabtype->type);
+        }
+
+        symtype = stabtype->type;
+      }
+
+      if (*p == '=') {
+        /* a type definition */
+        p++;
+
+        if (VG_(st_isresolved)(symtype)) {
+           /* a redefinition; clear the old type out */
+           StabType *stabtype = getStabType(tab, file, sym);
+
+           symtype = stabtype->type = VG_(st_mkunresolved)(NULL, stab_resolve, NULL);
+           if (debug)
+              VG_(printf)("creating new type %p for definition (%d,%d)\n",
+                          symtype, file, sym);
+        } else
+           VG_(st_unresolved_setdata)(symtype, stab_resolve, p);
+
+        if (debug)
+           VG_(printf)("defining type %p (%d,%d) = %s\n", symtype, file, sym, p);
+
+        /* skip type attributes */
+        while(*p == '@')
+           p = SKIPPAST(p+1, ';', "type attrib");
+
+        prev = p;
+
+        type = stabtype_parser(si, symtype, &p);
+        if (debug)
+           VG_(printf)("parsed definition: type=%p symtype=%p\n", type, symtype);
+
+        if (type != symtype) {
+           StabType *stabtype = getStabType(tab, file, sym);
+
+           vg_assert(stabtype->type != NULL);
+           if (0) {
+              /* XXX bogus */
+              vg_assert(!VG_(st_isresolved)(stabtype->type));
+              VG_(arena_free)(VG_AR_SYMTAB, stabtype->type); /* XXX proper free method? */
+           }
+           stabtype->type = type;
+        } else if (!VG_(st_isresolved)(type)) {
+           /* If type is defined in terms of itself, and is
+              therefore not resolved, it is void */
+           if (debug)
+              VG_(printf)("type %p is defined in terms of self - making void\n", type);
+           type = VG_(st_mkvoid)(type);
+        }
+      } else {
+        /* just a type reference */
+        type = symtype;
+        if ((0 || debug) && !VG_(st_isresolved)(type))
+           VG_(printf)("type %p (%d,%d) is unresolved\n", type, file, sym);
+        if ((0 || debug) && VG_(st_isresolved)(type))
+           VG_(printf)("reference (%d,%d) -> %p\n", file, sym, type);
+      }
+      break;
+   }
+
+   case '-': {                 /* -ve types for builtins? */
+      Int n;
+      p--;
+      n = atoi(&p, 0);
+      switch(n) {
+      case -16:        type = VG_(st_mkbool)(def, sizeof(int)); break;
+
+      default:
+        VG_(printf)(" @@ unrecognized negative type %d\n", n);
+        type = NULL;
+        break;
+      }
+      EXPECT(';', "negative builtin type");
+      break;
+   }
+
+   case 't': {                 /* typedef: 't' TYPE */
+      SymType *td = stabtype_parser(si, NULL, &p);
+      type = VG_(st_mktypedef)(def, NULL, td);
+      break;
+   }
+
+   case 'R': {                 /* FP type: 'R' FP-TYPE ';' BYTES ';' (extra) ';' */
+      Int fptype, bytes;
+
+      fptype = atoi(&p, 0);
+      EXPECT(';', "FP-TYPE");
+      bytes = atoi(&p, 0);
+      EXPECT(';', "FP-TYPE bytes");
+      atoi(&p, 0);
+      EXPECT(';', "FP-TYPE extra");
+      
+      type = VG_(st_mkfloat)(def, bytes * 8);
+      break;
+   }
+
+   case 'r': {                 /* range: 'r' TYPE ';' MIN ';' MAX ';' */
+      Int min, max;
+      SymType *rtype = stabtype_parser(si, NULL, &p);
+
+      EXPECT(';', "range TYPE");
+
+      /* MIN and MAX are: (INTEGER | 'A' OFFSET | 'T' OFFSET | 'a' REGNO | 't' REGNO | 'J')
+        only expect INTEGER for now (no way to represent the rest yet, and no need so far)
+       */
+      min = atoi(&p, 0);
+      EXPECT(';', "range MIN");
+      max = atoi(&p, 0);
+      EXPECT(';', "range MAX");
+
+      if (debug && 0)
+        VG_(printf)("range: rtype=%p def=%p min=%d max=%d remains = \"%s\"\n", 
+                    rtype, def, min, max, p);
+      
+      if (rtype == def) {
+        if (debug)
+           VG_(printf)("type %p is subrange of self - making int\n", def);
+        type = VG_(st_mkint)(def, sizeof(int), False);
+      } else if (min > max && max == 0) {
+        if (debug)
+           VG_(printf)("type %p has backwards range %d - %d: making float\n", 
+                       def, min, max);
+        type = VG_(st_mkfloat)(def, min);
+      } else
+        type = VG_(st_mkrange)(def, rtype, min, max);
+
+      vg_assert(VG_(st_isresolved)(type));
+      break;
+   }
+
+   case '&':                   /* reference */
+   case '*': {                 /* pointer */
+      /* '*' TYPE */
+      type = stabtype_parser(si, NULL, &p);
+      type = VG_(st_mkpointer)(def, type);
+      break;
+   }
+
+   case 'x': {                 /* reference to undefined type */
+      /* 'x' ('s' | 'u' | 'e') NAME ':' */
+      Int brac = 0;            /* < > brackets in type */
+      Char kind = *p++;                /* get kind */
+      Char *name = p;
+
+      /* name is delimited by : except for :: within <> */
+      for(;;) {
+        if (*p == '<')
+           brac++;
+        if (*p == '>')
+           brac--;
+        if (*p == ':') {
+           if (brac && p[1] == ':')
+              p += 1;
+           else
+              break;
+        }
+        p++;
+      }
+      EXPECT(':', "struct/union/enum ref");
+
+      name = VG_(addStr)(si, name, p-1-name);
+
+      switch (kind) {
+      case 's':                        /* struct */
+      case 'u':                        /* union */
+        type = structRef(tab, def, kind == 's', name);
+        break;
+
+      case 'e':                        /* enum */
+        type = VG_(st_mkenum)(def, 0);
+        break;
+
+      default:
+        VG_(printf)(" @@ unexpected type ref %c\n", p[-1]);
+        return NULL;
+      };
+
+      break;
+   }
+
+   case 'P':                   /* packed array */
+   case 'a': {                 /* array */
+      /* a IDX-TYPE TYPE */
+      SymType *idxtype;
+      SymType *artype;
+
+      idxtype = stabtype_parser(si, NULL, &p);
+      artype = stabtype_parser(si, NULL, &p);
+      
+      type = VG_(st_mkarray)(def, idxtype, artype);
+
+      break;
+   }
+
+   case 'e': {                 /* enum */
+      /* 'e' ( NAME ':' N ',' )* ';' */
+
+      type = VG_(st_mkenum)(def, 0);
+
+      /* don't really care about tags; just skip them */
+      while(*p != ';') {
+        p = SKIPPAST(p, ':', "enum tag NAME");
+        p = SKIPPAST(p, ',', "enum tag N");
+      }
+      p++;                     /* skip ';' */
+
+      break;
+   }
+
+   case 'u':                   /* union */
+   case 's': {                 /* struct */
+      /* Gad.  Here we go:
+
+        's' SIZE
+               ( '!' NBASE ',' ( VIRT PUB OFF ',' BASE-TYPE ){NBASE} )?
+
+               ( NAME ( ':' ( '/' [0-9] )? TYPE ',' OFFSET ( ',' SIZE )?
+                      | '::' ( METHOD-TYPE ':' MANGLE-ARGS ';' 
+                             PROT QUAL ( '.' | '*' VIRT | '?' ) )+
+                      )
+                 ';'
+               )*
+
+               ( '~%' FIRST-BASE-CLASS )?
+        ';'
+      */
+      UInt size;
+      Bool method = False;
+
+      size = atou(&p, 0);
+      type = (t == 's' ? VG_(st_mkstruct) : VG_(st_mkunion))(def, size, 0);
+
+      if (*p == '!') {
+        /* base classes */
+        Int nbase;
+
+        p++;
+        nbase = atoi(&p, 0);
+        EXPECT(',', "class base class count");
+        while(nbase--) {
+           p++;                /* VIRT flag */
+           p++;                /* PUB flag */
+           atoi(&p, 0);        /* offset */
+           EXPECT(',', "class base class ref");
+           stabtype_parser(si, NULL, &p);
+
+           if (*p == ';')      /* who eats this? */
+              p++;
+        }
+      }
+        
+      while(*p != ';') {
+        Char *end;
+        Char *name;
+        UInt off, sz;
+        SymType *fieldty;
+
+        end = SKIPPAST(p, ':', "struct/union field NAME");
+        end--;                 /* want to point to (first) ':' */
+        if (end[1] == ':') {
+           /* c++ method names end in :: */
+           method = True;
+
+           if (VG_(strncmp)(p, "op$", 3) == 0) {
+              /* According to stabs.info, operators are named
+                 ( "op$::" OP '.' ), where OP is +=, etc.  Current
+                 gcc doesn't seem to use this; operators just
+                 appear as "operator==::" */
+              end = SKIPPAST(end, '.', "op$ name");
+           }
+           name = VG_(addStr)(si, p, end-p);
+           p = end+2;
+        } else {
+           name = VG_(addStr)(si, p, end-p);
+           p = end+1;
+        }
+
+        if (method) {
+           /* don't care about methods, but we still have to crunch
+              through this goo */
+           fieldty = NULL;
+           off = sz = 0;
+
+           do {
+              stabtype_parser(si, NULL, &p);   /* METHOD-TYPE */
+
+              EXPECT(':', "struct method MANGLE-ARGS");
+              p = SKIPPAST(p, ';', "struct method MANGLE-ARGS");
+              
+              p += 1;          /* skip PROT */
+              if (*p >= 'A' && *p <= 'Z')
+                 p++;          /* skip QUAL (if present) */
+
+              switch(*p++) {
+              case '*':        /* VIRT: VTAB-IDX ';' OVERRIDE-CLASS ';' */
+                 atoi(&p, 0);  /* skip VTAB-IDX */
+                 EXPECT(';', "struct method vtab idx");
+                 stabtype_parser(si, NULL, &p);        /* skip OVERRIDE-CLASS */
+                 EXPECT(';', "struct method vtab override");
+                 break;
+
+              default:
+                 VG_(printf)(" @@ struct method unexpected member-type '%c' \"%s\" remains\n",
+                             p[-1], p);
+                 /* FALLTHROUGH */
+              case '?':
+              case '.':
+                 break;
+              }
+           } while (*p != ';');
+        } else {
+           if (*p == '/') {
+              /* c++ visibility spec: '/' PROT */
+              p += 2;
+           }
+
+           fieldty = stabtype_parser(si, NULL, &p);
+
+           if (*p == ':') {
+              /* static member; don't care (it will appear later) */
+              fieldty = NULL;
+              off = sz = 0;
+
+              p = SKIPPAST(p, ';', "struct static member");
+              p--;             /* point at ';' */
+           } else {
+              EXPECT(',', "struct TYPE");
+
+              off = atou(&p, 0);
+
+              if (*p == ',') {
+                 EXPECT(',', "struct OFFSET");
+                 sz = atou(&p, 0);
+              } else {
+                 /* sometimes the size is missing and assumed to be a
+                    pointer (in bits) */
+                 sz = sizeof(void *) * 8;
+              }
+           }
+        }
+
+        if (fieldty != NULL)
+           VG_(st_addfield)(type, name, fieldty, off, sz);
+
+        EXPECT(';', "struct field end");
+      }
+      p++;                     /* skip final ';' */
+
+      /* one final C++ surprise */
+      if (*p == '~') {
+        /* "~%" FIRST-BASE-CLASS ';' */
+        p++;
+        EXPECT('%', "struct first base");
+        stabtype_parser(si, NULL, &p); /* skip FIRST-BASE-CLASS */
+        EXPECT(';', "struct first base semi");
+      }
+
+      break;
+   }
+
+   case 'f':                   /* function */
+      /* 'f' TYPE */
+      type = VG_(st_mkvoid)(def); /* approximate functions as void */
+      stabtype_parser(si, NULL, &p);
+      break;
+
+   case '#':                   /* method */
+      /* '#' ( '#' RET-TYPE | 
+              CLASS-TYPE ',' RET-TYPE ',' ( ARG-TYPE ( ',' ARG-TYPE )* )? )
+         ';'
+      */
+      type = VG_(st_mkvoid)(def);      /* methods are really void */
+
+      if (*p == '#') {
+        p++;                   /* skip '#' */
+        stabtype_parser(si, NULL, &p); /* RET-TYPE */
+      } else {
+        stabtype_parser(si, NULL, &p); /* CLASS-TYPE */
+        EXPECT(',', "method CLASS-TYPE");
+
+        stabtype_parser(si, NULL, &p); /* RET-TYPE */
+        EXPECT(',', "method RET-TYPE");
+
+        while (*p != ';') {
+           stabtype_parser(si, NULL, &p);
+           if (*p == ',')
+              p++;
+           else if (*p != ';')
+              VG_(printf)(" @@ method ARG-TYPE list unexpected '%c'\n", *p);
+        }
+      }
+
+      EXPECT(';', "method definition");
+      break;
+
+   default:
+      VG_(printf)(" @@ don't know what type '%c' is\n", t);
+      type = NULL;
+      break;
+   }
+#undef EXPECT
+#undef SKIPPAST
+
+   if (type == NULL)
+      VG_(printf)(" @@ parsing %s gave NULL type (%s remains)\n", *pp, p);
+
+   *pp = p;
+
+   return type;
+}
+
+/* parse a symbol reference: NAME ':' DESC TYPE */
+static Bool initSym(SegInfo *si, Sym *sym, stab_types kind, Char **namep, Int val)
+{
+   static const Bool debug = False;
+   Char *name = *namep;
+   Char *ty;
+   Int len;
+   Bool isTypedef = False;
+   Bool isStruct = False;
+   SymType *base;
+
+   if (debug && 0)
+      VG_(printf)("initSym(si=%p, tab=%p, sym=%p, kind=%d, name=%p \"%s\", val=%d)\n",
+                 si, si->stab_typetab, sym, kind, name, name, val);
+
+   ty = nested_name(name);
+
+   len = ty - name;
+
+   if (debug) {
+      Char buf[len+1];
+      VG_(strncpy_safely)(buf, name, len+1);
+      VG_(printf)("\ninitSym name=\"%s\" type=%s\n", buf, ty+1);
+   }
+
+   if (*ty != ':') {
+      /* no type info */
+      sym->type = VG_(st_mkvoid)(NULL);
+   } else {
+      ty++;                    /* skip ':' */
+
+      /* chew through an initial sequence of
+        type descriptor type describers */
+      for(;;) {
+        switch(*ty) {
+        case 'a': case 'b': case 'c': case 'C':
+        case 'd': case 'D': case 'f': case 'F':
+        case 'G': case 'i': case 'I': case 'J':
+        case 'L': case 'm': case 'p': case 'P':
+        case 'Q': case 'R': case 'r': case 'S':
+        case 's': case 'v': case 'V': case 'x':
+        case 'X':
+           break;
+
+        case 'T':                      /* struct/union/enum */
+           isStruct = True;
+           break;
+
+        case 't':              /* typedef handled within stabtype_parser */
+           isTypedef = True;
+           /* FALLTHROUGH */
+        case '(': case '-': case '0' ... '9': /* type reference */
+        default:
+           goto out;
+        }
+        ty++;
+      }
+
+     out:
+      sym->type = stabtype_parser(si, NULL, &ty);
+      base = VG_(st_basetype)(sym->type, False);
+
+      if (isStruct && (VG_(st_isstruct)(base) || VG_(st_isunion)(base))) {
+        Char *sname = VG_(addStr)(si, name, len);
+        structDef(si->stab_typetab, base, VG_(st_isstruct)(base), sname);
+      }
+
+      if (isTypedef) {
+        Char *tname = VG_(addStr)(si, name, len);
+        vg_assert(sym->type != base);
+        if (debug)
+           VG_(printf)(" typedef %p \"%s\"\n", sym->type, tname);
+        VG_(st_setname)(sym->type, tname);
+        VG_(st_setname)(base, tname);
+      }
+   }
+   *namep = ty;
+
+   switch(kind) {
+   case N_STSYM:
+   case N_LCSYM:
+      sym->kind = SyStatic;
+      sym->addr = si->offset + (Addr)val;
+      break;
+
+   case N_PSYM:
+      sym->kind = SyEBPrel;    /* +ve offset off EBP (erk, or ESP if no frame pointer) */
+      sym->offset = val;
+      break;
+
+   case N_LSYM:
+      if (val < 0)
+        sym->kind = SyEBPrel;  /* -ve off EBP when there's a frame pointer */
+      else
+        sym->kind = SyESPrel;  /* +ve off ESP when there's no frame pointer */
+      sym->offset = val;
+      break;
+
+   case N_RSYM:
+      sym->kind = SyReg;
+      sym->regno = val;
+      break;
+
+   case N_GSYM:
+      sym->kind = SyGlobal;
+      sym->addr = 0;           /* XXX should really look up global address */
+      break;
+
+   default:
+      VG_(core_panic)("bad sym kind");
+   }
+
+   if (debug)
+      VG_(printf)("  %s = type=%p\n", (isStruct || isTypedef) ? "skipping" : "adding", sym->type);
+   
+   if (isStruct || isTypedef) {
+      return True;             /* skip */
+   } else {
+      sym->name = VG_(addStr)(si, name, len);
+      return False;            /* don't skip */
+   }
+}
+
+/* list of unbound symbols for next scope */
+struct symlist {
+   Sym sym;
+   struct symlist *next;
+};
+
+/* XXX TODO: make sure added syms are unique.  A lot of syms added to
+   the global scope are not.  On the other hand, skipping type
+   definitions helps a lot. */
+static Scope *addSymsToScope(Scope *sc, struct symlist *list, Int nsyms, Scope *outer)
+{
+   static const Bool debug = False;
+   Int j;
+   struct symlist *n;
+   Int base;
+
+   if (sc == NULL) {
+      sc = VG_(arena_malloc)(VG_AR_SYMTAB, sizeof(*sc));
+      sc->syms = VG_(arena_malloc)(VG_AR_SYMTAB, sizeof(*sc->syms) * nsyms);
+      sc->nsyms = nsyms;
+      base = 0;
+      sc->outer = outer;
+      if (outer == NULL)
+        sc->depth = 0;
+      else
+        sc->depth = outer->depth+1;
+   } else {
+      Sym *s = VG_(arena_malloc)(VG_AR_SYMTAB, sizeof(*s) * (sc->nsyms + nsyms));
+
+      VG_(memcpy)(s, sc->syms, sc->nsyms * sizeof(*s));
+      VG_(arena_free)(VG_AR_SYMTAB, sc->syms);
+      sc->syms = s;
+      base = sc->nsyms;
+      sc->nsyms += nsyms;
+   }
+
+   /* bind any unbound syms to new scope */
+   for(j = 0; j < nsyms; j++, list = n) {
+      if (debug)
+        VG_(printf)("   adding (%p) %s to scope %p depth %d\n", 
+                    list->sym.name, list->sym.name, sc, sc->depth);
+      n = list->next;
+      sc->syms[base+j] = list->sym;
+      VG_(arena_free)(VG_AR_SYMTAB, list);
+   }
+   vg_assert(list == NULL);
+
+   return sc;
+}
+
+/* Read stabs-format debug info.  This is all rather horrible because
+   stabs is a underspecified, kludgy hack.
+*/
+void VG_(read_debuginfo_stabs) ( SegInfo* si,
+                                UChar* stabC,   Int stab_sz, 
+                                UChar* stabstr, Int stabstr_sz )
+{
+   static const Bool debug = False;
+   Int    i;
+   Int    n_stab_entries;
+   struct nlist* stab = (struct nlist*)stabC;
+   UChar *next_stabstr = NULL;
+   /* state for various things */
+   struct {
+      Addr     start;          /* start address */
+      Addr     end;            /* end address */
+      Char     *name;          /* name */
+      Char     *filename;      /* source file name */
+      Int      line;           /* first line */
+   } func = { 0, 0, NULL, NULL, -1 };
+   struct {
+      Char     *name;
+      Bool     same;
+   } file = { NULL, True };
+   struct {
+      Int      prev;           /* prev line */
+      Int      no;             /* current line */
+      Int      ovf;            /* line wrap */
+      Addr     addr;           /* start of this line */
+      Bool     first;          /* first line in function */
+      Bool     jump;           /* was a jump from prev line (inline?) */
+   } line = { 0, 0, 0, 0, False };
+   struct {
+      Scope    *scope;         /* current scope */
+      struct symlist *symlist; /* unbound symbols */
+      Int      nsyms;          /* number of unbound scopes */
+      Addr     addr;           /* start of range */
+      Int      depth;
+   } scope = { NULL, NULL, 0, 0 };
+   Scope *global;
+   Int fileidx = 0;
+   StabTypeTab *tab;
+
+   if (si->stab_typetab == NULL) {
+      si->stab_typetab = VG_(arena_malloc)(VG_AR_SYMTAB, sizeof(StabTypeTab));
+      VG_(memset)(si->stab_typetab, 0, sizeof(StabTypeTab));
+   }
+   tab = si->stab_typetab;
+
+   /* Ok.  It all looks plausible.  Go on and read debug data. 
+         stab kinds: 100   N_SO     a source file name
+                      68   N_SLINE  a source line number
+                      36   N_FUN    start of a function
+
+      In this loop, we maintain a current file name, updated as 
+      N_SO/N_SOLs appear, and a current function base address, 
+      updated as N_FUNs appear.  Based on that, address ranges for 
+      N_SLINEs are calculated, and stuffed into the line info table.
+
+      Finding the instruction address range covered by an N_SLINE is
+      complicated;  see the N_SLINE case below.
+   */
+   file.name     = VG_(addStr)(si,"???", -1);
+
+   n_stab_entries = stab_sz/(int)sizeof(struct nlist);
+
+   /* empty initial file-wide scope */
+   global = addSymsToScope(NULL, NULL, 0, NULL);
+   scope.scope = global;
+
+   for (i = 0; i < n_stab_entries; i++) {
+      const struct nlist *st = &stab[i];
+      Char *no_fn_name = "???";
+      Char *string;
+
+      if (debug && 1) {
+        VG_(printf) ( "%2d  type=%d   othr=%d   desc=%d   value=0x%x   strx=%d  %s\n", i,
+                      st->n_type, st->n_other, st->n_desc, 
+                      (int)st->n_value,
+                      (int)st->n_un.n_strx, 
+                      stabstr + st->n_un.n_strx );
+      }
+
+      /* handle continued string stabs */
+      {
+        static const Bool contdebug = False;
+        Int buflen = 0;
+        Int idx = 0;
+        Char *buf = NULL;
+        Int len;
+        Bool continuing = False;
+        UInt stringidx;
+
+        stringidx = st->n_un.n_strx;
+        string = stabstr + stringidx;
+        len = VG_(strlen)(string);
+
+        while(string && len > 0 && (continuing || string[len-1] == '\\')) {
+           /* Gak, we have a continuation. Skip forward through
+              subsequent stabs to gather all the parts of the
+              continuation.  Increment i, but keep st pointing at
+              current stab. */
+
+           continuing = string[len-1] == '\\';
+
+           /* remove trailing \ */
+           while(string[len-1] == '\\' && len > 0)
+              len--;
+
+           if (contdebug)
+              VG_(printf)("found extension string: \"%s\" len=%d(%c) idx=%d buflen=%d\n", 
+                          string, len, string[len-1], idx, buflen);
+
+           /* XXX this is silly.  The si->strtab should have a way of
+              appending to the last added string... */
+           if ((idx + len) >= buflen) {
+              Char *n;
+              
+              if (buflen == 0)
+                 buflen = 16;
+              while((idx + len) >= buflen)
+                 buflen *= 2;
+              n = VG_(arena_malloc)(VG_AR_SYMTAB, buflen);
+              VG_(memcpy)(n, buf, idx);
+              
+              if (buf != NULL)
+                 VG_(arena_free)(VG_AR_SYMTAB, buf);
+              buf = n;
+           }
+
+           VG_(memcpy)(&buf[idx], string, len);
+           idx += len;
+           if (contdebug) {
+              buf[idx] = '\0';
+              VG_(printf)("working buf=\"%s\"\n", buf);
+           }
+
+           i++;
+           if (i >= n_stab_entries)
+              break;
+
+           if (stab[i].n_un.n_strx) {
+              string = stabstr + stab[i].n_un.n_strx;
+              len = VG_(strlen)(string);
+           } else {
+              string = NULL;
+              len = 0;
+           }
+        }
+
+        if (buf != NULL) {
+           i--;                        /* overstepped */
+           string = VG_(addStr)(si, buf, idx);
+           VG_(arena_free)(VG_AR_SYMTAB, buf);
+           if (contdebug)
+              VG_(printf)("made composite: \"%s\"\n", string);
+        }
+      }
+
+      switch(st->n_type) {
+         case N_UNDEF:
+           /* new string table base */
+           if (next_stabstr != NULL) {
+              stabstr_sz -= next_stabstr - stabstr;
+              stabstr = next_stabstr;
+              if (stabstr_sz <= 0) {
+                 VG_(printf)(" @@ bad stabstr size %d\n", stabstr_sz);
+                 return;
+              }
+           }
+           next_stabstr = stabstr + st->n_value;
+           break;
+
+         case N_BINCL: {
+           fileidx++;
+           addHeader(tab, stabstr + st->n_un.n_strx, st->n_value, fileidx);
+
+           if (debug)
+              VG_(printf)("BINCL: pushed %s fileidx=%d\n", 
+                          stabstr + st->n_un.n_strx, fileidx);
+           break;
+        }
+
+         case N_EINCL:
+           break;
+
+         case N_EXCL:
+           ++fileidx;
+
+           addFileAlias(tab, stabstr + st->n_un.n_strx, st->n_value, fileidx);
+
+           if (debug) {
+              VG_(printf)("reference to excluded include file %s; fileidx=%d\n",
+                          stabstr + st->n_un.n_strx, fileidx);
+           }
+           break;
+
+        case N_SOL:            /* sub-source (include) file */
+           if (line.ovf != 0) 
+              VG_(message)(Vg_UserMsg, 
+                            "Warning: file %s is very big (> 65535 lines) "
+                            "Line numbers and annotation for this file might "
+                            "be wrong.  Sorry",
+                            file.name);
+           /* FALLTHROUGH */
+
+        case N_SO: {           /* new source file */
+           UChar *nm = string;
+           UInt len = VG_(strlen)(nm);
+           Addr addr = func.start + st->n_value;
+
+           if (line.addr != 0) {
+              /* finish off previous line */
+              VG_(addLineInfo)(si, file.name, line.addr,
+                               addr, line.no + line.ovf * LINENO_OVERFLOW, i);
+           }
+
+           /* reset line state */
+           line.ovf = 0;           
+           line.addr = 0;
+           line.prev = 0;
+           line.no = 0;
+           line.jump = True;
+
+           if (len > 0 && nm[len-1] != '/') {
+              file.name = VG_(addStr)(si, nm, -1);
+              if (debug)
+                 VG_(printf)("new source: %s\n", file.name);
+              if (st->n_type == N_SO) {
+                 fileidx = 0;
+                 clearStabFiles(tab);
+              }
+           } else if (len == 0)
+              file.name = VG_(addStr)(si, "?1\0", -1);
+
+           if (func.start != 0)
+              line.jump = True;
+           break;
+        }
+
+        case N_SLINE: {        /* line info */
+           Addr addr = func.start + st->n_value;
+
+           if (line.addr != 0) {
+              /* there was a previous */
+              VG_(addLineInfo)(si, file.name, line.addr,
+                               addr, line.no + line.ovf * LINENO_OVERFLOW, i);
+           }
+
+           line.addr = addr;
+           line.prev = line.no;
+           line.no = (Int)((UShort)st->n_desc);
+
+           if (line.prev > line.no + OVERFLOW_DIFFERENCE && file.same) {
+               VG_(message)(Vg_DebugMsg, 
+                  "Line number overflow detected (%d --> %d) in %s", 
+                  line.prev, line.no, file.name);
+               line.ovf++;
+            }
+            file.same = True;
+
+           /* This is pretty horrible.  If this is the first line of
+              the function, then bind any unbound symbols to the arg
+              scope, since they're probably arguments. */
+           if (line.first) {
+              line.first = False;
+              
+              if (scope.nsyms != 0) {
+                 addSymsToScope(scope.scope, scope.symlist, scope.nsyms, NULL);
+                 scope.symlist = NULL;
+                 scope.nsyms = 0;
+              }
+
+              /* remember first line of function */
+              if (func.start != 0) {
+                 func.filename = file.name;
+                 func.line = line.no;
+              }
+           } else if (func.start != 0 && (line.no < func.line || func.filename != file.name)) {
+              /* If we're suddenly in code before the function starts
+                 or in a different file, then it seems like its
+                 probably some inlined code.  Should do something
+                 useful with this information. */
+              //VG_(printf)("possible inline?\n");
+              line.jump = True;
+           }
+           break;
+        }
+
+        case N_FUN: {          /* function start/end */
+           Addr addr = 0;      /* end address for prev line/scope */
+           Bool newfunc = False;
+
+           if (scope.nsyms != 0) {
+              /* clean up any unbound symbols */
+              addSymsToScope(scope.scope, scope.symlist, scope.nsyms, NULL);
+              scope.symlist = NULL;
+              scope.nsyms = 0;
+           }
+           
+           /* if this the end of the function or we haven't
+              previously finished the previous function... */
+           if (*string == '\0' || func.start != 0) {
+              /* end of function */
+              newfunc = False;
+              line.first = False;
+
+              /* end line at end of function */
+              addr = func.start + st->n_value;
+
+              if (debug)
+                 VG_(printf)("ending func %s at %p\n", func.name, addr);
+
+              /* now between functions */
+              func.name = no_fn_name;
+              func.start = 0;
+
+              if (scope.addr != 0) {
+                 /* finish any previous scope range */
+                 VG_(addScopeInfo)(si, scope.addr, addr, scope.scope);
+              }
+
+              /* tidy up arg scope */
+              /* XXX LEAK: free scope if it or any of its inner scopes was
+                 never added to a scope range  */
+
+              if (scope.scope->depth == 0) {
+                 VG_(message)(Vg_UserMsg,
+                              "It seems there's more scopes closed than opened...\n");
+                 break;
+              }
+
+              scope.scope = scope.scope->outer;
+              scope.addr = addr;
+              scope.addr = 0;
+           }
+
+           if (*string != '\0') {
+              /* new function */
+              newfunc = True;
+              line.first = True;
+
+              /* line ends at start of next function */
+              addr = si->offset + st->n_value;
+
+              func.start = addr;
+              func.name = string;
+              
+              if (debug)
+                 VG_(printf)("\nnew func %s at %p\n", func.name, func.start);
+
+           }
+
+           if (line.addr) {
+              VG_(addLineInfo)(si, file.name, line.addr,
+                               addr, line.no + line.ovf * LINENO_OVERFLOW, i);
+              line.addr = 0;
+           }
+
+           if (scope.addr) {
+              /* finish any previous scope range */
+              VG_(addScopeInfo)(si, scope.addr, addr, scope.scope);
+           }
+
+           if (newfunc) {
+              /* make little wrapper scope for args */
+              Scope *sc;
+              if (scope.addr) {
+                 /* finish any previous scope range */
+                 VG_(addScopeInfo)(si, scope.addr, addr, scope.scope);
+              }
+
+              sc = addSymsToScope(NULL, scope.symlist, scope.nsyms, scope.scope);
+              scope.scope = sc;
+              scope.nsyms = 0;
+              scope.symlist = NULL;
+              scope.addr = addr;
+           }
+           break;
+        }
+
+        case N_LBRAC: {
+           /* open new scope */
+           Scope *sc;
+           Addr addr = func.start + st->n_value;
+
+           if (scope.addr) {
+              /* end previous range */
+              VG_(addScopeInfo)(si, scope.addr, addr, scope.scope);
+           }
+
+           scope.addr = addr;
+
+           if (debug) {
+              static const Char indent[]=
+                 "                                        "
+                 "                                        ";
+              Int idx;
+
+              idx = sizeof(indent)-1 - (scope.depth * 2);
+              scope.depth++;
+              VG_(printf)("%s{\n", &indent[idx >= 0 ? idx : 0]);
+           }
+           /* add unbound syms to scope */
+           sc = addSymsToScope(NULL, scope.symlist, scope.nsyms, scope.scope);
+           scope.scope = sc;
+           scope.nsyms = 0;
+           scope.symlist = NULL;
+
+           break;
+        }
+
+      case N_RBRAC: {
+        /* close scope */
+        Addr addr = func.start + st->n_value;
+
+        if (scope.nsyms != 0) {
+           /* If there's any unbound symbols, tidy them up */
+           addSymsToScope(scope.scope, scope.symlist, scope.nsyms, NULL);
+           scope.symlist = NULL;
+           scope.nsyms = 0;
+        }
+
+        vg_assert(scope.addr != 0);
+        VG_(addScopeInfo)(si, scope.addr, addr, scope.scope);
+        
+        /* XXX LEAK: free scope if it or any of its inner scopes was
+           never added to a scope range  */
+
+        if (scope.scope->depth == 0) {
+           /* complain */
+           VG_(message)(Vg_UserMsg, "It seems there's more scopes closed than opened...\n");
+           break;
+        }
+
+        scope.scope = scope.scope->outer;
+        scope.addr = addr;
+        if (debug) {
+           static const Char indent[]=
+              "                                        "
+              "                                        ";
+           Int idx;
+
+           scope.depth--;
+           idx = sizeof(indent)-1 - (scope.depth * 2);
+           VG_(printf)("%s}\n", &indent[idx >= 0 ? idx : 0]);
+        }
+
+        break;
+      }
+
+      case N_GSYM:             /* global variable */
+      case N_STSYM:            /* static in data segment */
+      case N_LCSYM:            /* static in bss segment */
+      case N_PSYM:             /* function parameter */
+      case N_LSYM:             /* stack variable */
+      case N_RSYM: {           /* register variable */
+        Char *cp = string;
+        Int val = st->n_value;
+
+        /* a single string can have multiple definitions nested in it */
+        while(*cp != '\0') {
+           struct symlist *s = VG_(arena_malloc)(VG_AR_SYMTAB, sizeof(*s));
+        
+           if (initSym(si, &s->sym, st->n_type, &cp, val)) {
+              VG_(arena_free)(VG_AR_SYMTAB, s); /* not interesting */
+           } else {
+              s->next = scope.symlist;
+              scope.symlist = s;
+              scope.nsyms++;
+           }
+           switch(*cp) {
+           case '\0':          /* all done */
+              break;
+
+           case '0' ... '9':   /* symbol */
+           case 'A' ... 'Z':
+           case 'a' ... 'z':
+           case '_':
+              break;
+
+           case ' ': case ':': /* nameless type */
+              break;
+
+           default:
+              VG_(printf)(" @@ unlikely looking definition in unparsed remains \"%s\"\n", cp);
+              break;
+           }
+        }
+        break;
+      }
+      }
+   }
+   
+   if (scope.nsyms != 0)
+      addSymsToScope(scope.scope, scope.symlist, scope.nsyms, NULL);
+}
diff --git a/coregrind/vg_symtypes.c b/coregrind/vg_symtypes.c
new file mode 100644 (file)
index 0000000..e430470
--- /dev/null
@@ -0,0 +1,1025 @@
+/*
+ * Extract type information from debug information
+ */
+
+#include "vg_include.h"
+#include "vg_symtypes.h"
+
+typedef enum {
+   TyUnknown,                  /* unknown type */
+   TyUnresolved,               /* unresolved type */
+   TyError,                    /* error type */
+
+   TyVoid,                     /* void */
+
+   TyInt,                      /* integer */
+   TyBool,                     /* boolean */
+   TyChar,                     /* character */
+   TyFloat,                    /* float */
+
+   TyRange,                    /* type subrange */
+
+   TyEnum,                     /* enum */
+
+   TyPointer,                  /* pointer */
+   TyArray,                    /* array */
+   TyStruct,                   /* structure/class */
+   TyUnion,                    /* union */
+
+   TyTypedef                   /* typedef */
+} TyKind;
+
+static const Char *ppkind(TyKind k)
+{
+   switch(k) {
+#define S(x)   case x:         return #x
+      S(TyUnknown);
+      S(TyUnresolved);
+      S(TyError);
+      S(TyVoid);
+      S(TyInt);
+      S(TyBool);
+      S(TyChar);
+      S(TyRange);
+      S(TyFloat);
+      S(TyEnum);
+      S(TyPointer);
+      S(TyArray);
+      S(TyStruct);
+      S(TyUnion);
+      S(TyTypedef);
+#undef S
+   default:
+      return "Ty???";
+   }
+}
+
+/* struct/union field */
+typedef struct _StField {
+   UInt                offset;         /* offset into structure (0 for union) (in bits) */
+   UInt                size;           /* size (in bits) */
+   SymType     *type;          /* type of element */
+   Char        *name;                  /* name of element */
+} StField;
+
+/* enum tag */
+typedef struct _EnTag {
+   const Char   *name;         /* name */
+   UInt                val;            /* value */
+} EnTag;
+
+struct _SymType {
+   TyKind      kind;                   /* type descriminator */
+   UInt                size;                   /* sizeof(type) */
+   Char                *name;                  /* useful name */
+
+   union {
+      /* TyInt,TyBool,TyChar */
+      struct {
+        Bool           issigned;       /* signed or not */
+      } t_scalar;
+
+      /* TyFloat */
+      struct {
+        Bool           isdouble;       /* is double prec */
+      } t_float;
+
+      /* TyRange */
+      struct {
+        Int            min;
+        Int            max;
+        SymType        *type;
+      } t_range;
+
+      /* TyPointer */
+      struct {
+        SymType        *type;          /* *type */
+      } t_pointer;
+
+      /* TyArray */
+      struct {
+        SymType        *idxtype;
+        SymType        *type;
+      } t_array;
+
+      /* TyEnum */
+      struct {
+        UInt           ntag;           /* number of tags */
+        EnTag          *tags;          /* tags */
+      } t_enum;
+
+      /* TyStruct, TyUnion */
+      struct {
+        UInt           nfield;         /* number of fields */
+        UInt           nfieldalloc;    /* number of fields allocated */
+        StField        *fields;        /* fields */
+      } t_struct;
+
+      /* TyTypedef */
+      struct {
+        SymType        *type;          /* type */
+      } t_typedef;
+
+      /* TyUnresolved - reference to unresolved type */
+      struct {
+        /* some kind of symtab reference */
+        SymResolver    *resolver;      /* symtab reader's resolver */
+        void           *data;          /* data for resolver */
+      } t_unresolved;
+   };
+};
+
+
+Bool VG_(st_isstruct)(SymType *ty)
+{
+   return ty->kind == TyStruct;
+}
+
+Bool VG_(st_isunion)(SymType *ty)
+{
+   return ty->kind == TyUnion;
+}
+
+Bool VG_(st_isenum)(SymType *ty)
+{
+   return ty->kind == TyEnum;
+}
+
+static inline SymType *alloc(SymType *st)
+{
+   if (st == NULL) {
+      st = VG_(arena_malloc)(VG_AR_SYMTAB, sizeof(*st));
+      st->kind = TyUnknown;
+      st->name = NULL;
+   }
+
+   return st;
+}
+
+static void resolve(SymType *st)
+{
+   if (st->kind != TyUnresolved)
+      return;
+
+   (*st->t_unresolved.resolver)(st, st->t_unresolved.data);
+
+   if (st->kind == TyUnresolved)
+      st->kind = TyError;
+}
+
+SymType *VG_(st_mkunresolved)(SymType *st, SymResolver *resolver, void *data)
+{
+   st = alloc(st);
+   
+   vg_assert(st->kind == TyUnresolved || st->kind == TyUnknown);
+
+   st->kind = TyUnresolved;
+   st->size = 0;
+   st->t_unresolved.resolver = resolver;
+   st->t_unresolved.data = data;
+
+   return st;
+}
+
+void VG_(st_unresolved_setdata)(SymType *st, SymResolver *resolver, void *data)
+{
+   if (st->kind != TyUnresolved)
+      return;
+
+   st->t_unresolved.resolver = resolver;
+   st->t_unresolved.data = data;
+}
+
+Bool VG_(st_isresolved)(SymType *st)
+{
+   return st->kind != TyUnresolved;
+}
+
+void VG_(st_setname)(SymType *st, Char *name)
+{
+   if (st->name != NULL)
+      st->name = name;
+}
+
+SymType *VG_(st_mkvoid)(SymType *st)
+{
+   st = alloc(st);
+
+   vg_assert(st->kind == TyUnresolved || st->kind == TyUnknown);
+   
+   st->kind = TyVoid;
+   st->size = 1;               /* for address calculations */
+   st->name = "void";
+   return st;
+}
+
+SymType *VG_(st_mkint)(SymType *st, UInt size, Bool isSigned)
+{
+   st = alloc(st);
+
+   vg_assert(st->kind == TyUnresolved || st->kind == TyUnknown);
+   
+   st->kind = TyInt;
+   st->size = size;
+   st->t_scalar.issigned = isSigned;
+
+   return st;
+}
+
+SymType *VG_(st_mkfloat)(SymType *st, UInt size)
+{
+   st = alloc(st);
+
+   vg_assert(st->kind == TyUnresolved || st->kind == TyUnknown);
+   
+   st->kind = TyFloat;
+   st->size = size;
+   st->t_scalar.issigned = True;
+
+   return st;
+}
+
+SymType *VG_(st_mkbool)(SymType *st, UInt size)
+{
+   st = alloc(st);
+
+   vg_assert(st->kind == TyUnresolved || st->kind == TyUnknown);
+   
+   st->kind = TyBool;
+   st->size = size;
+
+   return st;
+}
+
+
+SymType *VG_(st_mkpointer)(SymType *st, SymType *ptr)
+{
+   st = alloc(st);
+
+   vg_assert(st->kind == TyUnresolved || st->kind == TyUnknown);
+
+   st->kind = TyPointer;
+   st->size = sizeof(void *);
+   st->t_pointer.type = ptr;
+
+   return st;
+}
+
+SymType *VG_(st_mkrange)(SymType *st, SymType *ty, Int min, Int max)
+{
+   st = alloc(st);
+
+   vg_assert(st->kind == TyUnresolved || st->kind == TyUnknown);
+
+   st->kind = TyRange;
+   st->size = 0;               /* ? */
+   st->t_range.type = ty;
+   st->t_range.min = min;
+   st->t_range.max = max;
+
+   return st;
+}
+
+SymType *VG_(st_mkstruct)(SymType *st, UInt size, UInt nfields)
+{
+   st = alloc(st);
+
+   vg_assert(st->kind == TyUnresolved || st->kind == TyUnknown || st->kind == TyStruct);
+
+   vg_assert(st->kind != TyStruct || st->t_struct.nfield == 0);
+
+   st->kind = TyStruct;
+   st->size = size;
+   st->t_struct.nfield = 0;
+   st->t_struct.nfieldalloc = nfields;
+   if (nfields != 0)
+      st->t_struct.fields = VG_(arena_malloc)(VG_AR_SYMTAB, sizeof(StField) * nfields);
+   else
+      st->t_struct.fields = NULL;
+   
+   return st;
+}
+
+SymType *VG_(st_mkunion)(SymType *st, UInt size, UInt nfields)
+{
+   st = alloc(st);
+
+   vg_assert(st->kind == TyUnresolved || st->kind == TyUnknown || st->kind == TyUnion);
+
+   vg_assert(st->kind != TyUnion || st->t_struct.nfield == 0);
+
+   st->kind = TyUnion;
+   st->size = size;
+   st->t_struct.nfield = 0;
+   st->t_struct.nfieldalloc = nfields;
+   if (nfields != 0)
+      st->t_struct.fields = VG_(arena_malloc)(VG_AR_SYMTAB, sizeof(StField) * nfields);
+   else
+      st->t_struct.fields = NULL;
+
+   return st;
+}
+
+void VG_(st_addfield)(SymType *st, Char *name, SymType *type, UInt off, UInt size)
+{
+   StField *f;
+
+   vg_assert(st->kind == TyStruct || st->kind == TyUnion);
+
+   if (st->t_struct.nfieldalloc == st->t_struct.nfield) {
+      StField *n = VG_(arena_malloc)(VG_AR_SYMTAB, 
+                                    sizeof(StField) * (st->t_struct.nfieldalloc + 2));
+      VG_(memcpy)(n, st->t_struct.fields, sizeof(*n) * st->t_struct.nfield);
+      if (st->t_struct.fields != NULL)
+        VG_(arena_free)(VG_AR_SYMTAB, st->t_struct.fields);
+      st->t_struct.nfieldalloc++;
+      st->t_struct.fields = n;
+   }
+
+   f = &st->t_struct.fields[st->t_struct.nfield++];
+   f->name = name;
+   f->type = type;
+   f->offset = off;
+   f->size = size;
+}
+
+
+SymType *VG_(st_mkenum)(SymType *st, UInt ntags)
+{
+   st = alloc(st);
+
+   vg_assert(st->kind == TyUnresolved || st->kind == TyUnknown || st->kind == TyEnum);
+
+   st->kind = TyEnum;
+   st->t_enum.ntag = 0;
+   st->t_enum.tags = NULL;
+   
+   return st;
+}
+
+SymType *VG_(st_mkarray)(SymType *st, SymType *idxtype, SymType *type)
+{
+   st = alloc(st);
+
+   vg_assert(st->kind == TyUnresolved || st->kind == TyUnknown);
+
+   st->kind = TyArray;
+   st->t_array.type = type;
+   st->t_array.idxtype = idxtype;
+   
+   return st;
+}
+
+SymType *VG_(st_mktypedef)(SymType *st, Char *name, SymType *type)
+{
+   st = alloc(st);
+
+   vg_assert(st->kind == TyUnresolved || st->kind == TyUnknown ||
+            st->kind == TyStruct || st->kind == TyUnion ||
+            st->kind == TyTypedef);
+   
+   st->kind = TyTypedef;
+   st->name = name;
+   st->t_typedef.type = type;
+
+   return st;
+}
+
+
+SymType *VG_(st_basetype)(SymType *type, Bool do_resolve)
+{
+   while (type->kind == TyTypedef || (do_resolve && type->kind == TyUnresolved)) {
+      if (do_resolve)
+        resolve(type);
+
+      if (type->kind == TyTypedef)
+        type = type->t_typedef.type;
+   }
+
+   return type;
+}
+
+UInt VG_(st_sizeof)(SymType *ty)
+{
+   return ty->size;
+}
+
+#ifndef TEST
+/*
+  Hash of visited addresses, so we don't get stuck in loops.  It isn't
+  simply enough to keep track of addresses, since we need to interpret
+  the memory according to the type.  If a given location has multiple
+  pointers with different types (for example, void * and struct foo *),
+  then we need to look at it under each type.
+*/
+struct visited {
+   Addr                a;
+   SymType     *ty;
+   struct visited *next;
+};
+
+#define VISIT_HASHSZ   1021
+
+static struct visited *visit_hash[VISIT_HASHSZ];
+
+static inline Bool test_visited(Addr a, SymType *type)
+{
+   struct visited *v;
+   UInt b = (UInt)a % VISIT_HASHSZ;
+   Bool ret = False;
+
+   for(v = visit_hash[b]; v != NULL; v = v->next) {
+      if (v->a == a && v->ty == type) {
+        ret = True;
+        break;
+      }
+   }
+   
+   return ret;
+}
+
+static Bool has_visited(Addr a, SymType *type)
+{
+   static const Bool debug = False;
+   Bool ret;
+
+   ret = test_visited(a, type);
+
+   if (!ret) {
+      UInt b = (UInt)a % VISIT_HASHSZ;
+      struct visited * v = VG_(arena_malloc)(VG_AR_SYMTAB, sizeof(*v));
+
+      v->a = a;
+      v->ty = type;
+      v->next = visit_hash[b];
+      visit_hash[b] = v;
+   }
+   
+   if (debug)
+      VG_(printf)("has_visited(a=%p, ty=%p) -> %d\n", a, type, ret);
+
+   return ret;
+}
+
+static void clear_visited(void)
+{
+   UInt i;
+   
+   for(i = 0; i < VISIT_HASHSZ; i++) {
+      struct visited *v, *n;
+      for(v = visit_hash[i]; v != NULL; v = n) {
+        n = v->next;
+        VG_(arena_free)(VG_AR_SYMTAB, v);
+      }
+      visit_hash[i] = NULL;
+   }
+}
+
+static void bprintf(void (*send)(Char), const Char *fmt, ...)
+{
+   va_list vargs;
+
+   va_start(vargs, fmt);
+   VG_(vprintf)(send, fmt, vargs);
+   va_end(vargs);
+}
+
+#define SHADOWCHUNK    0       /* no longer have a core allocator */
+
+#if SHADOWCHUNK
+static ShadowChunk *findchunk(Addr a)
+{
+   Bool find(ShadowChunk *sc) {
+      return a >= sc->data && a < (sc->data+sc->size);
+   }
+   return VG_(any_matching_mallocd_ShadowChunks)(find);
+}
+#endif
+
+static vki_ksigaction sigbus_saved;
+static vki_ksigaction sigsegv_saved;
+static vki_ksigset_t  blockmask_saved;
+static jmp_buf valid_addr_jmpbuf;
+
+static void valid_addr_handler(int sig)
+{
+   //VG_(printf)("OUCH! %d\n", sig);
+   __builtin_longjmp(valid_addr_jmpbuf, 1);
+}
+
+/* catch badness signals because we're going to be
+   playing around in untrusted memory */
+static void setup_signals(void)
+{
+   Int res;
+   vki_ksigaction sigbus_new;
+   vki_ksigaction sigsegv_new;
+   vki_ksigset_t  unblockmask_new;
+
+   /* Temporarily install a new sigsegv and sigbus handler, and make
+      sure SIGBUS, SIGSEGV and SIGTERM are unblocked.  (Perhaps the
+      first two can never be blocked anyway?)  */
+
+   sigbus_new.ksa_handler = valid_addr_handler;
+   sigbus_new.ksa_flags = VKI_SA_ONSTACK | VKI_SA_RESTART;
+   sigbus_new.ksa_restorer = NULL;
+   res = VG_(ksigemptyset)( &sigbus_new.ksa_mask );
+   vg_assert(res == 0);
+
+   sigsegv_new.ksa_handler = valid_addr_handler;
+   sigsegv_new.ksa_flags = VKI_SA_ONSTACK | VKI_SA_RESTART;
+   sigsegv_new.ksa_restorer = NULL;
+   res = VG_(ksigemptyset)( &sigsegv_new.ksa_mask );
+   vg_assert(res == 0+0);
+
+   res =  VG_(ksigemptyset)( &unblockmask_new );
+   res |= VG_(ksigaddset)( &unblockmask_new, VKI_SIGBUS );
+   res |= VG_(ksigaddset)( &unblockmask_new, VKI_SIGSEGV );
+   res |= VG_(ksigaddset)( &unblockmask_new, VKI_SIGTERM );
+   vg_assert(res == 0+0+0);
+
+   res = VG_(ksigaction)( VKI_SIGBUS, &sigbus_new, &sigbus_saved );
+   vg_assert(res == 0+0+0+0);
+
+   res = VG_(ksigaction)( VKI_SIGSEGV, &sigsegv_new, &sigsegv_saved );
+   vg_assert(res == 0+0+0+0+0);
+
+   res = VG_(ksigprocmask)( VKI_SIG_UNBLOCK, &unblockmask_new, &blockmask_saved );
+   vg_assert(res == 0+0+0+0+0+0);
+}
+
+static void restore_signals(void)
+{
+   Int res;
+
+   /* Restore signal state to whatever it was before. */
+   res = VG_(ksigaction)( VKI_SIGBUS, &sigbus_saved, NULL );
+   vg_assert(res == 0 +0);
+
+   res = VG_(ksigaction)( VKI_SIGSEGV, &sigsegv_saved, NULL );
+   vg_assert(res == 0 +0 +0);
+
+   res = VG_(ksigprocmask)( VKI_SIG_SETMASK, &blockmask_saved, NULL );
+   vg_assert(res == 0 +0 +0 +0);
+}
+
+/* if false, setup and restore signals for every access */
+#define LAZYSIG                1
+
+static Bool is_valid_addr(Addr a)
+{
+   static SymType faulted = { TyError };
+   static const Bool debug = False;
+   volatile Bool ret = False;
+
+   if ((a > VKI_BYTES_PER_PAGE) &&
+       !test_visited(a, &faulted)) {
+      if (!LAZYSIG)
+        setup_signals();
+   
+      if (__builtin_setjmp(valid_addr_jmpbuf) == 0) {
+        volatile UInt *volatile ptr = (volatile UInt *)a;
+
+        *ptr;
+
+        ret = True;
+      } else {
+        /* cache bad addresses in visited table */
+        has_visited(a, &faulted);
+        ret = False;
+      }
+
+      if (!LAZYSIG)
+        restore_signals();
+   }
+
+   if (debug)
+      VG_(printf)("is_valid_addr(%p) -> %d\n", a, ret);
+
+   return ret;
+}
+
+static Int free_varlist(Variable *list)
+{
+   Variable *next;
+   Int count = 0;
+
+   for(; list != NULL; list = next) {
+      next = list->next;
+      count++;
+      if (list->name)
+        VG_(arena_free)(VG_AR_SYMTAB, list->name);
+      VG_(arena_free)(VG_AR_SYMTAB, list);
+   }
+   return count;
+}
+
+/* Composite: struct, union, array
+   Non-composite: everything else
+ */
+static inline Bool is_composite(SymType *ty)
+{
+   switch(ty->kind) {
+   case TyUnion:
+   case TyStruct:
+   case TyArray:
+      return True;
+
+   default:
+      return False;
+   }
+}
+
+/* There's something at the end of the rainbow */
+static inline Bool is_followable(SymType *ty)
+{
+   return ty->kind == TyPointer || is_composite(ty);
+}
+
+#define MAX_PLY                7       /* max depth we go */
+#define MAX_ELEMENTS   5000    /* max number of array elements we scan */
+#define MAX_VARS       10000   /* max number of variables total traversed */
+
+Char *VG_(describe_addr)(ThreadId tid, Addr addr)
+{
+   static const Bool debug = False;
+   static const Bool memaccount = False; /* match creates to frees */
+   Addr eip;                   /* thread's EIP */
+   Variable *list;             /* worklist */
+   Variable *keeplist;         /* container variables */
+   Variable *found;            /* the chain we found */
+   Char *buf = NULL;           /* the result */
+   UInt bufsz = 0;
+   UInt bufidx = 0;
+   Int created=0, freed=0;
+   Int numvars = MAX_VARS;
+
+   /* add a character to the result buffer */
+   void addbuf(Char c) {
+      if ((bufidx+1) >= bufsz) {
+        Char *n;
+
+        if (bufsz == 0)
+           bufsz = 8;
+        else
+           bufsz *= 2;
+
+        /* use skin malloc so that the skin client can free it */
+        n = VG_(malloc)(bufsz);
+        if (buf != NULL && bufidx != 0)
+           VG_(memcpy)(n, buf, bufidx);
+        if (buf != NULL)
+           VG_(free)(buf);
+        buf = n;
+      }
+      buf[bufidx++] = c;
+      buf[bufidx] = '\0';
+   }
+
+
+   clear_visited();
+
+   found = NULL;
+   keeplist = NULL;
+
+   eip = VG_(get_EIP)(tid);
+   list = VG_(get_scope_variables)(tid);
+
+   if (memaccount) {
+      Variable *v;
+
+      for(v = list; v != NULL; v = v->next)
+        created++;
+   }
+
+   if (debug) {
+      Char file[100];
+      Int line;
+      if (!VG_(get_filename_linenum)(eip, file, sizeof(file), &line))
+        file[0] = 0;
+      VG_(printf)("describing address %p for tid=%d @ %s:%d\n", addr, tid, file, line);
+   }
+
+   if (LAZYSIG)
+      setup_signals();
+
+   /* breadth-first traversal of all memory visible to the program at
+      the current point */
+   while(list != NULL && found == NULL) {
+      Variable **prev = &list;
+      Variable *var, *next;
+      Variable *newlist = NULL, *newlistend = NULL;
+
+      if (debug)
+        VG_(printf)("----------------------------------------\n");
+
+      for(var = list; var != NULL; var = next) {
+        SymType *type = var->type;
+        Bool keep = False;
+
+        /* Add a new variable to the list */
+        void newvar(Char *name, SymType *ty, Addr valuep, UInt size) {
+           Variable *v;
+
+           /* have we been here before? */
+           if (has_visited(valuep, ty))
+              return;
+           
+           /* are we too deep? */
+           if (var->distance > MAX_PLY)
+              return;
+
+           /* have we done too much? */
+           if (numvars-- == 0)
+              return;
+
+           if (memaccount)
+              created++;
+           
+           v = VG_(arena_malloc)(VG_AR_SYMTAB, sizeof(*v));
+
+           if (name)
+              v->name = VG_(arena_strdup)(VG_AR_SYMTAB, name);
+           else
+              v->name = NULL;
+           v->type = VG_(st_basetype)(ty, False);
+           v->valuep = valuep;
+           v->size = size == -1 ? ty->size : size;
+           v->container = var;
+           v->distance = var->distance + 1;
+           v->next = NULL;
+
+           if (newlist == NULL)
+              newlist = newlistend = v;
+           else {
+              newlistend->next = v;
+              newlistend = v;
+           }
+           
+           if (debug)
+              VG_(printf)("    --> %d: name=%s type=%p(%s %s) container=%p &val=%p\n", 
+                          v->distance, v->name, 
+                          v->type, ppkind(v->type->kind), 
+                          v->type->name ? (char *)v->type->name : "",
+                          v->container, v->valuep);
+           keep = True;
+           return;
+        }
+
+        next = var->next;
+
+        if (debug)
+           VG_(printf)("  %d: name=%s type=%p(%s %s) container=%p &val=%p\n", 
+                       var->distance, var->name, 
+                       var->type, ppkind(var->type->kind), 
+                       var->type->name ? (char *)var->type->name : "",
+                       var->container, var->valuep);
+   
+        if (0 && has_visited(var->valuep, var->type)) {
+           /* advance prev; we're keeping this one on the doomed list */
+           prev = &var->next;
+           continue;
+        }
+
+        if (!is_composite(var->type) && 
+            addr >= var->valuep && addr < (var->valuep + var->size)) {
+           /* at hit - remove it from the list, add it to the
+              keeplist and set found */
+           found = var;
+           *prev = var->next;
+           var->next = keeplist;
+           keeplist = var;
+           break;
+        }
+
+        type = VG_(st_basetype)(type, True);
+        
+        switch(type->kind) {
+        case TyUnion:
+        case TyStruct: {
+           Int i;
+
+           if (debug)
+              VG_(printf)("    %d fields\n", type->t_struct.nfield);
+           for(i = 0; i < type->t_struct.nfield; i++) {
+              StField *f = &type->t_struct.fields[i];
+              newvar(f->name, f->type, var->valuep + (f->offset / 8), (f->size + 7) / 8);
+           }
+           break;
+        }
+
+        case TyArray: {
+           Int i; 
+           Int offset;         /* offset of index for non-0-based arrays */
+           Int min, max;       /* range of indicies we care about (0 based) */
+           SymType *ty = type->t_array.type;
+           vg_assert(type->t_array.idxtype->kind == TyRange);
+
+           offset = type->t_array.idxtype->t_range.min;
+           min = 0;
+           max = type->t_array.idxtype->t_range.max - offset;
+
+           if ((max-min+1) == 0) {
+#if SHADOWCHUNK
+              /* zero-sized array - look at allocated memory */
+              ShadowChunk *sc = findchunk(var->valuep);
+
+              if (sc != NULL) {
+                 max = ((sc->data + sc->size - var->valuep) / ty->size) + min;
+                 if (debug)
+                    VG_(printf)("    zero sized array: using min=%d max=%d\n",
+                                min, max);
+              }
+#endif
+           }
+
+           /* If this array's elements can't take us anywhere useful,
+              just look to see if an element itself is being pointed
+              to; otherwise just skip the whole thing */
+           if (!is_followable(ty)) {
+              UInt sz = ty->size * (max+1);
+
+              if (debug)
+                 VG_(printf)("    non-followable array (sz=%d): checking addr %p in range %p-%p\n",
+                             sz, addr, var->valuep, (var->valuep + sz));
+              if (addr >= var->valuep && addr <= (var->valuep + sz))
+                 min = max = (addr - var->valuep) / ty->size;
+              else
+                 break;
+           }
+
+           /* truncate array if it's too big */
+           if (max-min+1 > MAX_ELEMENTS)
+              max = min+MAX_ELEMENTS;
+           
+           if (debug)
+              VG_(printf)("    array index %d - %d\n", min, max);
+           for(i = min; i <= max; i++) {
+              Char b[10];
+              VG_(sprintf)(b, "[%d]", i+offset);
+              newvar(b, ty, var->valuep + (i * ty->size), -1);
+           }
+
+           break;
+        }
+
+        case TyPointer:
+           /* follow */
+           /* XXX work out a way of telling whether a pointer is
+              actually a decayed array, and treat it accordingly */
+           if (is_valid_addr(var->valuep))
+              newvar(NULL, type->t_pointer.type, *(Addr *)var->valuep, -1);
+           break;
+
+        case TyUnresolved:
+           VG_(printf)("var %s is unresolved (type=%p)\n", var->name, type);
+           break;
+
+        default:
+           /* Simple non-composite, non-pointer type */
+           break;
+        }
+        
+        if (keep) {
+           /* ironically, keep means remove it from the list */
+           *prev = next;
+           
+           /* being kept - add it if not already there */
+           if (keeplist != var) {
+              var->next = keeplist;
+              keeplist = var;
+           }
+        } else {
+           /* advance prev; we're keeping it on the doomed list */
+           prev = &var->next;
+        }
+      }
+
+      /* kill old list */
+      freed += free_varlist(list);
+      list = NULL;
+
+      if (found) {
+        /* kill new list too */
+        freed += free_varlist(newlist);
+        newlist = newlistend = NULL;
+      } else {
+        /* new list becomes old list */
+        list = newlist;
+      }
+   }
+
+   if (LAZYSIG)
+      restore_signals();
+
+   if (found != NULL) {
+      Int len = 0;
+      Char file[100];
+      Int line;
+
+      /* Try to generate an idiomatic C-like expression from what
+        we've found. */
+
+      {
+        Variable *v;
+        for(v = found; v != NULL; v = v->container) {
+           if (debug)
+              VG_(printf)("v=%p (%s) %s\n",
+                          v, v->name ? v->name : (Char *)"",
+                          ppkind(v->type->kind));
+           
+           len += (v->name ? VG_(strlen)(v->name) : 0) + 5;
+        }
+      }
+
+      /* now that we know how long the expression will be
+        (approximately) build it up */
+      {
+        Char expr[len*2];
+        Char *sp = &expr[len]; /* pointer at start of string */
+        Char *ep = sp;         /* pointer at end of string */
+        void genstring(Variable *v, Variable *inner) {
+           Variable *c = v->container;
+
+           if (c != NULL)
+              genstring(c, v);
+
+           if (v->name != NULL) {
+              len = VG_(strlen)(v->name);
+              VG_(memcpy)(ep, v->name, len);
+              ep += len;
+           }
+
+           switch(v->type->kind) {
+           case TyPointer:
+              /* pointer-to-structure/union handled specially */
+              if (inner == NULL ||
+                  !(inner->type->kind == TyStruct || inner->type->kind == TyUnion)) {
+                 *--sp = '*';
+                 *--sp = '(';
+                 *ep++ = ')';
+              }
+              break;
+
+           case TyStruct:
+           case TyUnion:
+              if (c && c->type->kind == TyPointer) {
+                 *ep++ = '-';
+                 *ep++ = '>';
+              } else
+                 *ep++ = '.';
+              break;
+
+           default:
+              break;
+           }
+        }
+
+        {
+           Bool ptr = True;
+
+           /* If the result is already a pointer, just use that as
+              the value, otherwise generate &(...) around the
+              expression. */
+           if (found->container && found->container->type->kind == TyPointer) {
+              vg_assert(found->name == NULL);
+
+              found->name = found->container->name;
+              found->container->name = NULL;
+              found->container = found->container->container;
+           } else {
+              bprintf(addbuf, "&(");
+              ptr = False;
+           }
+
+           genstring(found, NULL);
+
+           if (!ptr)
+              *ep++ = ')';
+        }
+
+        *ep++ = '\0';
+
+        bprintf(addbuf, sp);
+
+        if (addr != found->valuep)
+           bprintf(addbuf, "+%d", addr - found->valuep);
+
+        if (VG_(get_filename_linenum)(eip, file, sizeof(file), &line))
+           bprintf(addbuf, " at %s:%d", file, line, addr);
+      }
+   }
+
+   freed += free_varlist(keeplist);
+
+   if (memaccount)
+      VG_(printf)("created %d, freed %d\n", created, freed);
+
+   clear_visited();
+
+   if (debug)
+      VG_(printf)("returning buf=%s\n", buf);
+
+   return buf;
+}
+#endif /* TEST */
diff --git a/coregrind/vg_symtypes.h b/coregrind/vg_symtypes.h
new file mode 100644 (file)
index 0000000..5d7988c
--- /dev/null
@@ -0,0 +1,83 @@
+#ifndef __VG_SYMTYPES_H
+#define __VG_SYMTYPES_H
+
+/* ============================================================
+   Intra-Valgrind interfaces for vg_symtypes.c
+   ============================================================ */
+
+/* Lets try to make these opaque */
+typedef struct _SymType SymType;
+
+/* ------------------------------------------------------------
+   Constructors for various SymType nodes
+   ------------------------------------------------------------ */
+
+/* Find the basetype for a given type: that is, if type is a typedef,
+   return the typedef'd type.  If resolve is true, it will resolve
+   unresolved symbols.  If type is not a typedef, then this is just
+   returns type.
+*/
+SymType *VG_(st_basetype)(SymType *type, Bool resolve);
+
+void VG_(st_setname)(SymType *ty, Char *name);
+
+typedef void (SymResolver)(SymType *, void *);
+
+/* Create an unresolved type */
+SymType *VG_(st_mkunresolved)(SymType *, SymResolver *resolve, void *data);
+
+/* update an unresolved type's data */
+void VG_(st_unresolved_setdata)(SymType *, SymResolver *resolve, void *data);
+
+Bool VG_(st_isresolved)(SymType *);
+UInt VG_(st_sizeof)(SymType *);
+
+/* Unknown type (unparsable) */
+SymType *VG_(st_mkunknown)(SymType *);
+
+SymType *VG_(st_mkvoid)(SymType *);
+
+SymType *VG_(st_mkint)(SymType *, UInt size, Bool isSigned);
+SymType *VG_(st_mkbool)(SymType *, UInt size);
+SymType *VG_(st_mkchar)(SymType *, Bool isSigned);
+SymType *VG_(st_mkfloat)(SymType *, UInt size);
+SymType *VG_(st_mkdouble)(SymType *, UInt size);
+
+SymType *VG_(st_mkpointer)(SymType *, SymType *);
+SymType *VG_(st_mkrange)(SymType *, SymType *, Int min, Int max);
+
+SymType *VG_(st_mkstruct)(SymType *, UInt size, UInt nfields);
+SymType *VG_(st_mkunion)(SymType *, UInt size, UInt nfields);
+void VG_(st_addfield)(SymType *, Char *name, SymType *, UInt off, UInt size);
+
+SymType *VG_(st_mkenum)(SymType *, UInt ntags);
+SymType *VG_(st_addtag)(SymType *, Char *name, Int val);
+
+SymType *VG_(st_mkarray)(SymType *, SymType *idxtype, SymType *artype);
+
+SymType *VG_(st_mktypedef)(SymType *, Char *name, SymType *type);
+
+Bool VG_(st_isstruct)(SymType *);
+Bool VG_(st_isunion)(SymType *);
+Bool VG_(st_isenum)(SymType *);
+
+/* ------------------------------------------------------------
+   Interface with vg_symtab2.c
+   ------------------------------------------------------------ */
+
+/* Typed value */
+typedef struct _Variable Variable;
+
+struct _Variable {
+   Char                *name;          /* name */
+   SymType     *type;          /* type of value */
+   Addr                valuep;         /* pointer to value */
+   UInt                size;           /* size of value */
+   UInt                distance;       /* "distance" from site of interest */
+   Variable    *next;
+   Variable    *container;
+};
+
+Variable *VG_(get_scope_variables)(ThreadId tid);
+
+#endif /* VG_SYMTYPES_H */
index 08905b04ff17918327dec6b108744f8356a81d16..8df6483ab98af28ae0b351dc7a46ead7483d86d4 100644 (file)
@@ -2129,7 +2129,9 @@ UCodeBlock* SK_(instrument) ( UCodeBlock* cb_in, Addr not_used )
               default:
                  VG_(skin_panic)("bad size");
               }
-           
+
+              /* XXX all registers should be flushed to baseblock
+                 here */
               uInstr1(cb, CCALL, 0, TempReg, u_in->val1);
               uCCall(cb, (Addr)help, 1, 1, False);
            } else
@@ -2148,6 +2150,8 @@ UCodeBlock* SK_(instrument) ( UCodeBlock* cb_in, Addr not_used )
               uInstr2(cb, MOV,   4, Literal, 0, TempReg, t_size);
               uLiteral(cb, (UInt)u_in->size);
 
+              /* XXX all registers should be flushed to baseblock
+                 here */
               uInstr2(cb, CCALL, 0, TempReg, u_in->val2, TempReg, t_size);
               uCCall(cb, (Addr) & eraser_mem_help_read_N, 2, 2, False);
 
@@ -2172,6 +2176,8 @@ UCodeBlock* SK_(instrument) ( UCodeBlock* cb_in, Addr not_used )
                  VG_(skin_panic)("bad size");
               }
 
+              /* XXX all registers should be flushed to baseblock
+                 here */
               uInstr2(cb, CCALL, 0, TempReg, u_in->val2, TempReg, u_in->val1);
               uCCall(cb, (Addr)help, 2, 2, False);
            } else
@@ -2189,6 +2195,8 @@ UCodeBlock* SK_(instrument) ( UCodeBlock* cb_in, Addr not_used )
            t_size = newTemp(cb);
            uInstr2(cb, MOV,   4, Literal, 0, TempReg, t_size);
            uLiteral(cb, (UInt)u_in->size);
+              /* XXX all registers should be flushed to baseblock
+                 here */
            uInstr2(cb, CCALL, 0, TempReg, u_in->val2, TempReg, t_size);
            uCCall(cb, (Addr) & eraser_mem_help_write_N, 2, 2, False);
 
@@ -2272,6 +2280,8 @@ typedef
       const Char* section;
       /* True if is just-below %esp -- could be a gcc bug. */
       Bool maybe_gcc;
+      /* symbolic address description */
+      Char *expr;
    }
    AddrInfo;
 
@@ -2310,6 +2320,7 @@ void clear_AddrInfo ( AddrInfo* ai )
    ai->section    = "???";
    ai->stack_tid  = VG_INVALID_THREADID;
    ai->maybe_gcc  = False;
+   ai->expr       = NULL;
 }
 
 static __inline__
@@ -2443,6 +2454,8 @@ static void record_eraser_error ( ThreadId tid, Addr a, Bool is_write,
    err_extra.prevstate = prevstate;
    if (clo_execontext)
       err_extra.lasttouched = getExeContext(a);
+   err_extra.addrinfo.expr = VG_(describe_addr)(tid, a);
+
    VG_(maybe_record_error)( tid, EraserErr, a, 
                             (is_write ? "writing" : "reading"),
                             &err_extra);
@@ -2513,6 +2526,10 @@ Bool SK_(eq_SkinError) ( VgRes not_used, Error* e1, Error* e2 )
 
 static void pp_AddrInfo ( Addr a, AddrInfo* ai )
 {
+   if (ai->expr != NULL)
+      VG_(message)(Vg_UserMsg, 
+                  "  Address %p == %s", a, ai->expr);
+   
    switch (ai->akind) {
       case Stack: 
          VG_(message)(Vg_UserMsg, 
@@ -2520,6 +2537,9 @@ static void pp_AddrInfo ( Addr a, AddrInfo* ai )
                       a, ai->stack_tid);
          break;
       case Unknown:
+        if (ai->expr != NULL)
+           break;
+
          if (ai->maybe_gcc) {
             VG_(message)(Vg_UserMsg, 
                "  Address %p is just below %%esp.  Possibly a bug in GCC/G++",
index d8d168a3733cf4b5858c48f46b4e9c82121c1db0..faf5d72de3226c82373f902aa4523117a372de15 100644 (file)
@@ -1452,6 +1452,10 @@ extern Bool VG_(get_objname)  ( Addr a, Char* objname,  Int n_objname  );
 */
 extern Char* VG_(describe_eip)(Addr eip, Char* buf, Int n_buf);
 
+/* Returns a string containing an expression for the given
+   address. String is malloced with VG_(malloc)() */
+Char *VG_(describe_addr)(ThreadId, Addr);
+
 /* A way to get information about what segments are mapped */
 typedef struct _SegInfo SegInfo;