]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Add support for reading Windows PDB debug info (symbols and line
authorJulian Seward <jseward@acm.org>
Wed, 22 Apr 2009 22:42:10 +0000 (22:42 +0000)
committerJulian Seward <jseward@acm.org>
Wed, 22 Apr 2009 22:42:10 +0000 (22:42 +0000)
numbers) when Valgrind is running Wine.  Modified version of a
patch by John Reiser (vgsvn+wine-load-pdb-debuginfo.patch) with
extensions to read a second format of line number tables.

Wine uses a new client request, VG_USERREQ__LOAD_PDB_DEBUGINFO,
to tell Valgrind when to read PDB info.  Wine's implementation
of module loading is vastly different from that used by
ld-linux.so, and it is too difficult to recognize what is going
on just by observing the calls to mmap and mprotect.

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

coregrind/Makefile.am
coregrind/m_debuginfo/debuginfo.c
coregrind/m_debuginfo/priv_readpdb.h [new file with mode: 0644]
coregrind/m_debuginfo/priv_storage.h
coregrind/m_debuginfo/readelf.c
coregrind/m_debuginfo/readpdb.c [new file with mode: 0644]
coregrind/m_debuginfo/storage.c
coregrind/m_scheduler/scheduler.c
coregrind/m_stacktrace.c
coregrind/pub_core_debuginfo.h
include/valgrind.h

index 59695c2e942c5fad720b88ed233cb8b076c50f29..fa2a4f1efe27443775822b0873855fed919bc7e6 100644 (file)
@@ -158,6 +158,7 @@ noinst_HEADERS = \
        m_debuginfo/priv_storage.h      \
        m_debuginfo/priv_tytypes.h      \
        m_debuginfo/priv_readstabs.h    \
+       m_debuginfo/priv_readpdb.h      \
        m_debuginfo/priv_d3basics.h     \
        m_debuginfo/priv_readdwarf.h    \
        m_debuginfo/priv_readdwarf3.h   \
@@ -246,6 +247,7 @@ COREGRIND_LINUX_SOURCE = \
        m_debuginfo/readdwarf.c \
        m_debuginfo/readdwarf3.c \
        m_debuginfo/readstabs.c \
+       m_debuginfo/readpdb.c \
        m_syswrap/syswrap-generic.c \
        m_ume/elf.c \
        m_ume/main.c \
index 842161e4752cbc1e1e3b4453729cd85010db37c0..b4e2e63756d19a553ebfc6d482faef4fb4ed6e12 100644 (file)
@@ -61,6 +61,7 @@
 #if defined(VGO_linux)
 # include "priv_readelf.h"
 # include "priv_readdwarf3.h"
+# include "priv_readpdb.h"
 #elif defined(VGO_aix5)
 # include "pub_core_debuglog.h"
 # include "pub_core_libcproc.h"
@@ -217,6 +218,7 @@ static void free_DebugInfo ( DebugInfo* di )
    if (di->loctab)     ML_(dinfo_free)(di->loctab);
    if (di->cfsi)       ML_(dinfo_free)(di->cfsi);
    if (di->cfsi_exprs) VG_(deleteXA)(di->cfsi_exprs);
+   if (di->fpo)        ML_(dinfo_free)(di->fpo);
 
    for (chunk = di->strchunks; chunk != NULL; chunk = next) {
       next = chunk->next;
@@ -872,6 +874,143 @@ void VG_(di_notify_mprotect)( Addr a, SizeT len, UInt prot )
    }
 }
 
+/*--------- PDB (windows debug info) reading --------- */
+
+/* this should really return ULong, as per VG_(di_notify_mmap). */
+void VG_(di_notify_pdb_debuginfo)( Int fd_obj, Addr avma_obj,
+                                   SizeT total_size,
+                                   PtrdiffT unknown_purpose__reloc )
+{
+   Int    r, sz_exename;
+   ULong  obj_mtime, pdb_mtime;
+   Char   exename[VKI_PATH_MAX];
+   Char*  pdbname = NULL;
+   Char*  dot;
+   SysRes sres;
+   Int    fd_pdbimage;
+   SizeT  n_pdbimage;
+   struct vg_stat stat_buf;
+
+   if (VG_(clo_verbosity) > 0) {
+      VG_(message)(Vg_UserMsg, "");
+      VG_(message)(Vg_UserMsg,
+         "LOAD_PDB_DEBUGINFO(fd=%d, avma=%#lx, total_size=%lu, "
+         "uu_reloc=%#lx)", 
+         fd_obj, avma_obj, total_size, unknown_purpose__reloc
+      );
+   }
+
+   /* 'fd' refers to the .exe/.dll we're dealing with.  Get its modification
+      time into obj_mtime. */
+   r = VG_(fstat)(fd_obj, &stat_buf);
+   if (r == -1)
+      goto out; /* stat failed ?! */
+   vg_assert(r == 0);
+   obj_mtime = stat_buf.st_mtime;
+
+   /* and get its name into exename[]. */
+   vg_assert(VKI_PATH_MAX > 100); /* to ensure /proc/self/fd/%d is safe */
+   VG_(memset)(exename, 0, sizeof(exename));
+   VG_(sprintf)(exename, "/proc/self/fd/%d", fd_obj);
+   /* convert exename from a symlink to real name .. overwrites the
+      old contents of the buffer.  Ick. */
+   sz_exename = VG_(readlink)(exename, exename, sizeof(exename)-2 );
+   if (sz_exename == -1)
+      goto out; /* readlink failed ?! */
+   vg_assert(sz_exename >= 0 && sz_exename < sizeof(exename));
+   vg_assert(exename[sizeof(exename)-1] == 0);
+
+   if (VG_(clo_verbosity) > 0) {
+      VG_(message)(Vg_UserMsg, "LOAD_PDB_DEBUGINFO: objname: %s", exename);
+   }
+
+   /* Try to find a matching PDB file from which to read debuginfo.
+      Windows PE files have symbol tables and line number information,
+      but MSVC doesn't seem to use them. */
+   /* Why +5 ?  Because in the worst case, we could find a dot as the
+      last character of pdbname, and we'd then put "pdb" right after
+      it, hence extending it a bit. */
+   pdbname = ML_(dinfo_zalloc)("di.debuginfo.lpd1", sz_exename+5);
+   VG_(strcpy)(pdbname, exename);
+   vg_assert(pdbname[sz_exename+5-1] == 0);
+   dot = VG_(strrchr)(pdbname, '.');
+   if (!dot)
+      goto out; /* there's no dot in the exe's name ?! */
+   if (dot[1] == 0)
+      goto out; /* hmm, path ends in "." */
+
+   if ('A' <= dot[1] && dot[1] <= 'Z')
+      VG_(strcpy)(dot, ".PDB");
+   else
+      VG_(strcpy)(dot, ".pdb");
+
+   vg_assert(pdbname[sz_exename+5-1] == 0);
+
+   /* See if we can find it, and check it's in-dateness. */
+   sres = VG_(stat)(pdbname, &stat_buf);
+   if (sres.isError) {
+      VG_(message)(Vg_UserMsg, "Warning: Missing or un-stat-able %s",
+                               pdbname);
+   if (VG_(clo_verbosity) > 0)
+      VG_(message)(Vg_UserMsg, "LOAD_PDB_DEBUGINFO: missing: %s", pdbname);
+      goto out;
+   }
+   pdb_mtime = stat_buf.st_mtime;
+   if (pdb_mtime < obj_mtime ) {
+      /* PDB file is older than PE file - ignore it or we will either
+         (a) print wrong stack traces or more likely (b) crash. */
+      VG_(message)(Vg_UserMsg, "Warning: Ignoring %s since it is older than %s",
+                               pdbname, exename);
+      goto out;
+   }
+
+   sres = VG_(open)(pdbname, VKI_O_RDONLY, 0);
+   if (sres.isError) {
+      VG_(message)(Vg_UserMsg, "Warning: Can't open %s", pdbname);
+      goto out;
+   }
+
+   /* Looks promising; go on to try and read stuff from it. */
+   fd_pdbimage = sres.res;
+   n_pdbimage  = stat_buf.st_size;
+   sres = VG_(am_mmap_file_float_valgrind)( n_pdbimage, VKI_PROT_READ,
+                                            fd_pdbimage, 0 );
+   if (sres.isError) {
+      VG_(close)(fd_pdbimage);
+      goto out;
+   }
+
+   if (VG_(clo_verbosity) > 0)
+      VG_(message)(Vg_UserMsg, "LOAD_PDB_DEBUGINFO: pdbname: %s", pdbname);
+
+   /* play safe; always invalidate the CFI cache.  I don't know if
+      this is necessary, but anyway .. */
+   cfsi_cache__invalidate();
+   /* dump old info for this range, if any */
+   discard_syms_in_range( avma_obj, total_size );
+
+   { void* pdbimage = (void*)sres.res;
+     DebugInfo* di = find_or_create_DebugInfo_for(exename, NULL/*membername*/ );
+
+     /* this di must be new, since we just nuked any old stuff in the range */
+     vg_assert(di && !di->have_rx_map && !di->have_rw_map);
+     vg_assert(!di->have_dinfo);
+
+     /* don't set up any of the di-> fields; let
+        ML_(read_pdb_debug_info) do it. */
+     ML_(read_pdb_debug_info)( di, avma_obj, unknown_purpose__reloc,
+                               pdbimage, n_pdbimage, pdbname, pdb_mtime );
+     // JRS fixme: take notice of return value from read_pdb_debug_info,
+     // and handle failure
+     vg_assert(di->have_dinfo); // fails if PDB read failed
+     VG_(am_munmap_valgrind)( (Addr)pdbimage, n_pdbimage );
+     VG_(close)(fd_pdbimage);
+   }
+
+  out:
+   if (pdbname) ML_(dinfo_free)(pdbname);
+}
+
 #endif /* defined(VGO_linux) */
 
 
@@ -1981,6 +2120,127 @@ Bool VG_(use_CF_info) ( /*MOD*/Addr* ipP,
 }
 
 
+/*--------------------------------------------------------------*/
+/*---                                                        ---*/
+/*--- TOP LEVEL: FOR UNWINDING THE STACK USING               ---*/
+/*---            MSVC FPO INFO                               ---*/
+/*---                                                        ---*/
+/*--------------------------------------------------------------*/
+
+Bool VG_(use_FPO_info) ( /*MOD*/Addr* ipP,
+                         /*MOD*/Addr* spP,
+                         /*MOD*/Addr* fpP,
+                         Addr min_accessible,
+                         Addr max_accessible )
+{
+   Word       i;
+   DebugInfo* di;
+   FPO_DATA*  fpo = NULL;
+   Addr       spHere;
+
+   static UWord n_search = 0;
+   static UWord n_steps = 0;
+   n_search++;
+
+   if (0) VG_(printf)("search FPO for %#lx\n", *ipP);
+
+   for (di = debugInfo_list; di != NULL; di = di->next) {
+      n_steps++;
+
+      /* Use the per-DebugInfo summary address ranges to skip
+         inapplicable DebugInfos quickly. */
+      if (di->fpo == NULL)
+         continue;
+      if (*ipP < di->fpo_minavma || *ipP > di->fpo_maxavma)
+         continue;
+
+      i = ML_(search_one_fpotab)( di, *ipP );
+      if (i != -1) {
+         Word j;
+         if (0) {
+            /* debug printing only */
+            VG_(printf)("look for %#lx  size %ld i %ld\n",
+                        *ipP, di->fpo_size, i);
+            for (j = 0; j < di->fpo_size; j++)
+               VG_(printf)("[%02ld] %#x %d\n", 
+                            j, di->fpo[j].ulOffStart, di->fpo[j].cbProcSize);
+         }
+         vg_assert(i >= 0 && i < di->fpo_size);
+         fpo = &di->fpo[i];
+         break;
+      }
+   }
+
+   if (fpo == NULL)
+      return False;
+
+   if (0 && ((n_search & 0x7FFFF) == 0))
+      VG_(printf)("VG_(use_FPO_info): %lu searches, "
+                  "%lu DebugInfos looked at\n",
+                  n_search, n_steps);
+
+
+   /* Start of performance-enhancing hack: once every 64 (chosen
+      hackily after profiling) successful searches, move the found
+      DebugInfo one step closer to the start of the list.  This makes
+      future searches cheaper.  For starting konqueror on amd64, this
+      in fact reduces the total amount of searching done by the above
+      find-the-right-DebugInfo loop by more than a factor of 20. */
+   if ((n_search & 0x3F) == 0) {
+      /* Move si one step closer to the start of the list. */
+      //move_DebugInfo_one_step_forward( di );
+   }
+   /* End of performance-enhancing hack. */
+
+   if (0) {
+      VG_(printf)("found fpo: ");
+      //ML_(ppFPO)(fpo);
+   }
+
+   /*
+   Stack layout is:
+   %esp->
+      4*.cbRegs  {%edi, %esi, %ebp, %ebx}
+      4*.cdwLocals
+      return_pc
+      4*.cdwParams
+   prior_%esp->
+
+   Typical code looks like:
+      sub $4*.cdwLocals,%esp
+         Alternative to above for >=4KB (and sometimes for smaller):
+            mov $size,%eax
+            call __chkstk  # WinNT performs page-by-page probe!
+               __chkstk is much like alloc(), except that on return
+               %eax= 5+ &CALL.  Thus it could be used as part of
+               Position Independent Code to locate the Global Offset Table.
+      push %ebx
+      push %ebp
+      push %esi
+         Other once-only instructions often scheduled >here<.
+      push %edi
+
+   If the pc is within the first .cbProlog bytes of the function,
+   then you must disassemble to see how many registers have been pushed,
+   because instructions in the prolog may be scheduled for performance.
+   The order of PUSH is always %ebx, %ebp, %esi, %edi, with trailing
+   registers not pushed when .cbRegs < 4.  This seems somewhat strange
+   because %ebp is the register whose usage you want to minimize,
+   yet it is in the first half of the PUSH list.
+
+   I don't know what happens when the compiler constructs an outgoing CALL.
+   %esp could move if outgoing parameters are PUSHed, and this affects
+   traceback for errors during the PUSHes. */
+   spHere = *spP;
+
+   *ipP = *(Addr *)(spHere + 4*(fpo->cbRegs + fpo->cdwLocals));
+   *spP =           spHere + 4*(fpo->cbRegs + fpo->cdwLocals + 1 + fpo->cdwParams);
+   *fpP = *(Addr *)(spHere + 4*2);
+   return True;
+}
+
+
 /*--------------------------------------------------------------*/
 /*---                                                        ---*/
 /*--- TOP LEVEL: GENERATE DESCRIPTION OF DATA ADDRESSES      ---*/
@@ -2914,7 +3174,6 @@ void* /* really, XArray* of GlobalBlock */
 }
 
 
-
 /*------------------------------------------------------------*/
 /*--- DebugInfo accessor functions                         ---*/
 /*------------------------------------------------------------*/
diff --git a/coregrind/m_debuginfo/priv_readpdb.h b/coregrind/m_debuginfo/priv_readpdb.h
new file mode 100644 (file)
index 0000000..eb88017
--- /dev/null
@@ -0,0 +1,53 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Reading of syms & debug info from PDB-format files.          ---*/
+/*---                                               priv_readpdb.h ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+   Spring 2008:
+      derived from readelf.c and valgrind-20031012-wine/vg_symtab2.c
+      derived from wine-1.0/tools/winedump/pdb.c and msc.c
+
+   Copyright (C) 2000-2008 Julian Seward
+      jseward@acm.org
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307, USA.
+
+   The GNU General Public License is contained in the file COPYING.
+*/
+
+#ifndef __PRIV_READPDB_H
+#define __PRIV_READPDB_H
+
+/* Returns True if OK, False for any kind of failure. */
+extern Bool ML_(read_pdb_debug_info)(
+               DebugInfo* di,
+               Addr       obj_avma,
+               PtrdiffT   unknown_purpose__reloc,
+               void*      pdbimage,
+               SizeT      n_pdbimage,
+               Char*      pdbname,
+               ULong      pdbmtime
+            );
+
+#endif /* ndef __PRIV_READPDB_H */
+
+/*--------------------------------------------------------------------*/
+/*--- end                                           priv_readpdb.h ---*/
+/*--------------------------------------------------------------------*/
index 233fdab1f2a63923eb5a0cd959d3d907b89dba0a..65324deb1c0fc8a2ba3d0649fe94e958ead1aba8 100644 (file)
@@ -208,6 +208,29 @@ extern Int ML_(CfiExpr_DwReg) ( XArray* dst, Int reg );
 
 extern void ML_(ppCfiExpr)( XArray* src, Int ix );
 
+/* ---------------- FPO INFO (Windows PE) -------------- */
+
+/* for apps using Wine: MSVC++ PDB FramePointerOmitted: somewhat like
+   a primitive CFI */
+typedef
+   struct _FPO_DATA {  /* 16 bytes */
+      UInt   ulOffStart; /* offset of 1st byte of function code */
+      UInt   cbProcSize; /* # bytes in function */
+      UInt   cdwLocals;  /* # bytes/4 in locals */
+      UShort cdwParams;  /* # bytes/4 in params */
+      UChar  cbProlog;   /* # bytes in prolog */
+      UChar  cbRegs :3;  /* # regs saved */
+      UChar  fHasSEH:1;  /* Structured Exception Handling */
+      UChar  fUseBP :1;  /* EBP has been used */
+      UChar  reserved:1;
+      UChar  cbFrame:2;  /* frame type */
+   }
+   FPO_DATA;
+
+#define PDB_FRAME_FPO  0
+#define PDB_FRAME_TRAP 1
+#define PDB_FRAME_TSS  2
+
 /* --------------------- VARIABLES --------------------- */
 
 typedef
@@ -441,6 +464,13 @@ struct _DebugInfo {
    Addr    cfsi_maxavma;
    XArray* cfsi_exprs; /* XArray of CfiExpr */
 
+   /* Optimized code under Wine x86: MSVC++ PDB FramePointerOmitted
+      data.  Non-expandable array, hence .size == .used. */
+   FPO_DATA* fpo;
+   UWord     fpo_size;
+   Addr      fpo_minavma;
+   Addr      fpo_maxavma;
+
    /* Expandable arrays of characters -- the string table.  Pointers
       into this are stable (the arrays are not reallocated). */
    struct strchunk {
@@ -539,6 +569,10 @@ extern Word ML_(search_one_loctab) ( struct _DebugInfo* di, Addr ptr );
    not found.  Binary search.  */
 extern Word ML_(search_one_cfitab) ( struct _DebugInfo* di, Addr ptr );
 
+/* Find a FPO-table index containing the specified pointer, or -1
+   if not found.  Binary search.  */
+extern Word ML_(search_one_fpotab) ( struct _DebugInfo* di, Addr ptr );
+
 /* ------ Misc ------ */
 
 /* Show a non-fatal debug info reading error.  Use vg_panic if
index 84a54128b527bacb9a74b7333805b070145142bd..e0eef8c9cf9282fabf0b3cf55de9b65ffe911ed7 100644 (file)
@@ -1114,6 +1114,8 @@ Bool ML_(read_elf_debug_info) ( struct _DebugInfo* di )
       plt, and toc.
       ---------------------------------------------------------- */
 
+   res = False;
+
    oimage = (Addr)NULL;
    if (VG_(clo_verbosity) > 1 || VG_(clo_trace_redir))
       VG_(message)(Vg_DebugMsg, "Reading syms from %s (%#lx)",
diff --git a/coregrind/m_debuginfo/readpdb.c b/coregrind/m_debuginfo/readpdb.c
new file mode 100644 (file)
index 0000000..fa13d86
--- /dev/null
@@ -0,0 +1,2267 @@
+
+/*--------------------------------------------------------------------*/
+/*--- Reading of syms & debug info from PDB-format files.         ---*/
+/*---                                                   readpdb.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+   Spring 2008:
+      derived from readelf.c and valgrind-20031012-wine/vg_symtab2.c
+      derived from wine-1.0/tools/winedump/pdb.c and msc.c
+
+   Copyright (C) 2000-2008 Julian Seward
+      jseward@acm.org
+   Copyright 2006 Eric Pouech (winedump/pdb.c and msc.c)
+      GNU Lesser General Public License version 2.1 or later applies.
+   Copyright (C) 2008 BitWagon Software LLC
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307, USA.
+
+   The GNU General Public License is contained in the file COPYING.
+*/
+
+#include "pub_core_basics.h"
+#include "pub_core_debuginfo.h"
+#include "pub_core_vki.h"          // VKI_PAGE_SIZE
+#include "pub_core_libcbase.h"
+#include "pub_core_libcassert.h"
+#include "pub_core_libcprint.h"
+#include "pub_core_options.h"      // VG_(clo_verbosity)
+#include "pub_core_xarray.h"       // keeps priv_storage.h happy
+#include "pub_core_redir.h"
+
+#include "priv_misc.h"             /* dinfo_zalloc/free/strdup */
+#include "priv_d3basics.h"
+#include "priv_storage.h"
+#include "priv_readpdb.h"          // self
+
+
+/*------------------------------------------------------------*/
+/*---                                                      ---*/
+/*--- Biasing                                              ---*/
+/*---                                                      ---*/
+/*------------------------------------------------------------*/
+
+/* JRS 2009-Apr-13: Mostly this PDB reader is straightforward.  But
+   the biasing is incomprehensible, and I don't claim to understand it
+   at all.  There are four places where biasing is required:
+
+   - when reading symbol addresses (DEBUG_SnarfCodeView)
+   - when reading old-style line number tables (DEBUG_SnarfLinetab)
+   - when reading new-style line number tables (codeview_dump_linetab2)
+   - when reading FPO (stack-unwind) tables (pdb_dump)
+
+   To complicate matters further, Wine supplies us, via the
+   VG_USERREQ__LOAD_PDB_DEBUGINFO client request that initiates PDB
+   reading, a value 'reloc' which, if you read 'virtual.c' in the Wine
+   sources, looks a lot like a text bias value.  Yet the code below
+   ignores it.
+
+   To make future experimentation with biasing easier, here are four
+   macros which give the bias to use in each of the four cases.  Be
+   warned, they can and do refer to local vars in the relevant
+   functions. */
+
+/* This is the biasing arrangement in John's original patch.  I don't
+   see that is makes any sense for the FPO bias to be hardwired to
+   zero, but perhaps that's OK when the reloc value is also zero.
+   (iow, the FPO bias should actually be 'reloc' ?) */
+#define BIAS_FOR_SYMBOLS   (di->rx_map_avma)
+#define BIAS_FOR_LINETAB   (di->rx_map_avma)
+#define BIAS_FOR_LINETAB2  (di->text_bias)
+#define BIAS_FOR_FPO       0 /* no, really */
+
+/* This module leaks space; enable m_main's calling of
+   VG_(di_discard_ALL_debuginfo)() at shutdown and run with
+   --profile-heap=yes to see.  The main culprit appears to be
+   di.readpe.pdr.1.  I haven't bothered to chase it further. */
+
+
+/*------------------------------------------------------------*/
+/*---                                                      ---*/
+/*--- PE/PDB definitions                                   ---*/
+/*---                                                      ---*/
+/*------------------------------------------------------------*/
+
+typedef  UInt   DWORD;
+typedef  UShort WORD;
+typedef  UChar  BYTE;
+
+
+/* the following DOS and WINDOWS structures, defines and PE/PDB
+ * parsing code are copied or derived from the WINE
+ * project - http://www.winehq.com/
+ */
+
+/*
+ * File formats definitions
+ */
+#define   OFFSET_OF(__c,__f)   ((int)(((char*)&(((__c*)0)->__f))-((char*)0)))
+#define   WIN32_PATH_MAX 256
+
+#pragma pack(2)
+typedef struct _IMAGE_DOS_HEADER {
+    unsigned short  e_magic;      /* 00: MZ Header signature */
+    unsigned short  e_cblp;       /* 02: Bytes on last page of file */
+    unsigned short  e_cp;         /* 04: Pages in file */
+    unsigned short  e_crlc;       /* 06: Relocations */
+    unsigned short  e_cparhdr;    /* 08: Size of header in paragraphs */
+    unsigned short  e_minalloc;   /* 0a: Minimum extra paragraphs needed */
+    unsigned short  e_maxalloc;   /* 0c: Maximum extra paragraphs needed */
+    unsigned short  e_ss;         /* 0e: Initial (relative) SS value */
+    unsigned short  e_sp;         /* 10: Initial SP value */
+    unsigned short  e_csum;       /* 12: Checksum */
+    unsigned short  e_ip;         /* 14: Initial IP value */
+    unsigned short  e_cs;         /* 16: Initial (relative) CS value */
+    unsigned short  e_lfarlc;     /* 18: File address of relocation table */
+    unsigned short  e_ovno;       /* 1a: Overlay number */
+    unsigned short  e_res[4];     /* 1c: Reserved words */
+    unsigned short  e_oemid;      /* 24: OEM identifier (for e_oeminfo) */
+    unsigned short  e_oeminfo;    /* 26: OEM information; e_oemid specific */
+    unsigned short  e_res2[10];   /* 28: Reserved words */
+    unsigned long   e_lfanew;     /* 3c: Offset to extended header */
+} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
+
+#define IMAGE_DOS_SIGNATURE    0x5A4D     /* MZ   */
+#define IMAGE_OS2_SIGNATURE    0x454E     /* NE   */
+#define IMAGE_OS2_SIGNATURE_LE 0x454C     /* LE   */
+#define IMAGE_OS2_SIGNATURE_LX 0x584C     /* LX */
+#define IMAGE_VXD_SIGNATURE    0x454C     /* LE   */
+#define IMAGE_NT_SIGNATURE     0x00004550 /* PE00 */
+
+/* Subsystem Values */
+
+#define IMAGE_SUBSYSTEM_UNKNOWN     0
+#define IMAGE_SUBSYSTEM_NATIVE      1
+#define IMAGE_SUBSYSTEM_WINDOWS_GUI 2  /* Windows GUI subsystem */
+#define IMAGE_SUBSYSTEM_WINDOWS_CUI 3  /* Windows character subsystem*/
+#define IMAGE_SUBSYSTEM_OS2_CUI     5
+#define IMAGE_SUBSYSTEM_POSIX_CUI   7
+
+typedef struct _IMAGE_FILE_HEADER {
+  unsigned short  Machine;
+  unsigned short  NumberOfSections;
+  unsigned long   TimeDateStamp;
+  unsigned long   PointerToSymbolTable;
+  unsigned long   NumberOfSymbols;
+  unsigned short  SizeOfOptionalHeader;
+  unsigned short  Characteristics;
+} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
+
+typedef struct _IMAGE_DATA_DIRECTORY {
+  unsigned long VirtualAddress;
+  unsigned long Size;
+} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
+
+#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
+
+typedef struct _IMAGE_OPTIONAL_HEADER {
+
+  /* Standard fields */
+
+  unsigned short Magic; /* 0x10b or 0x107 */ /* 0x00 */
+  unsigned char  MajorLinkerVersion;
+  unsigned char  MinorLinkerVersion;
+  unsigned long  SizeOfCode;
+  unsigned long  SizeOfInitializedData;
+  unsigned long  SizeOfUninitializedData;
+  unsigned long  AddressOfEntryPoint;        /* 0x10 */
+  unsigned long  BaseOfCode;
+  unsigned long  BaseOfData;
+
+  /* NT additional fields */
+
+  unsigned long ImageBase;
+  unsigned long SectionAlignment;            /* 0x20 */
+  unsigned long FileAlignment;
+  unsigned short MajorOperatingSystemVersion;
+  unsigned short MinorOperatingSystemVersion;
+  unsigned short MajorImageVersion;
+  unsigned short MinorImageVersion;
+  unsigned short MajorSubsystemVersion;      /* 0x30 */
+  unsigned short MinorSubsystemVersion;
+  unsigned long Win32VersionValue;
+  unsigned long SizeOfImage;
+  unsigned long SizeOfHeaders;
+  unsigned long CheckSum;                    /* 0x40 */
+  unsigned short Subsystem;
+  unsigned short DllCharacteristics;
+  unsigned long SizeOfStackReserve;
+  unsigned long SizeOfStackCommit;
+  unsigned long SizeOfHeapReserve;           /* 0x50 */
+  unsigned long SizeOfHeapCommit;
+  unsigned long LoaderFlags;
+  unsigned long NumberOfRvaAndSizes;
+  IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; /* 0x60 */
+  /* 0xE0 */
+} IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER;
+
+typedef struct _IMAGE_NT_HEADERS {
+  unsigned long Signature; /* "PE"\0\0 */       /* 0x00 */
+  IMAGE_FILE_HEADER FileHeader;                 /* 0x04 */
+  IMAGE_OPTIONAL_HEADER OptionalHeader;         /* 0x18 */
+} IMAGE_NT_HEADERS, *PIMAGE_NT_HEADERS;
+
+#define IMAGE_SIZEOF_SHORT_NAME 8
+
+typedef struct _IMAGE_SECTION_HEADER {
+  unsigned char Name[IMAGE_SIZEOF_SHORT_NAME];
+  union {
+    unsigned long PhysicalAddress;
+    unsigned long VirtualSize;
+  } Misc;
+  unsigned long VirtualAddress;
+  unsigned long SizeOfRawData;
+  unsigned long PointerToRawData;
+  unsigned long PointerToRelocations;
+  unsigned long PointerToLinenumbers;
+  unsigned short NumberOfRelocations;
+  unsigned short NumberOfLinenumbers;
+  unsigned long Characteristics;
+} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
+
+#define        IMAGE_SIZEOF_SECTION_HEADER 40
+
+#define IMAGE_FIRST_SECTION(ntheader) \
+  ((PIMAGE_SECTION_HEADER)((LPunsigned char)&((PIMAGE_NT_HEADERS)(ntheader))->OptionalHeader + \
+                           ((PIMAGE_NT_HEADERS)(ntheader))->FileHeader.SizeOfOptionalHeader))
+
+/* These defines are for the Characteristics bitfield. */
+/* #define IMAGE_SCN_TYPE_REG                  0x00000000 - Reserved */
+/* #define IMAGE_SCN_TYPE_DSECT                        0x00000001 - Reserved */
+/* #define IMAGE_SCN_TYPE_NOLOAD               0x00000002 - Reserved */
+/* #define IMAGE_SCN_TYPE_GROUP                        0x00000004 - Reserved */
+/* #define IMAGE_SCN_TYPE_NO_PAD               0x00000008 - Reserved */
+/* #define IMAGE_SCN_TYPE_COPY                 0x00000010 - Reserved */
+
+#define IMAGE_SCN_CNT_CODE                     0x00000020
+#define IMAGE_SCN_CNT_INITIALIZED_DATA         0x00000040
+#define IMAGE_SCN_CNT_UNINITIALIZED_DATA       0x00000080
+
+#define        IMAGE_SCN_LNK_OTHER                     0x00000100
+#define        IMAGE_SCN_LNK_INFO                      0x00000200
+/* #define     IMAGE_SCN_TYPE_OVER             0x00000400 - Reserved */
+#define        IMAGE_SCN_LNK_REMOVE                    0x00000800
+#define        IMAGE_SCN_LNK_COMDAT                    0x00001000
+
+/*                                             0x00002000 - Reserved */
+/* #define IMAGE_SCN_MEM_PROTECTED             0x00004000 - Obsolete */
+#define        IMAGE_SCN_MEM_FARDATA                   0x00008000
+
+/* #define IMAGE_SCN_MEM_SYSHEAP               0x00010000 - Obsolete */
+#define        IMAGE_SCN_MEM_PURGEABLE                 0x00020000
+#define        IMAGE_SCN_MEM_16BIT                     0x00020000
+#define        IMAGE_SCN_MEM_LOCKED                    0x00040000
+#define        IMAGE_SCN_MEM_PRELOAD                   0x00080000
+
+#define        IMAGE_SCN_ALIGN_1BYTES                  0x00100000
+#define        IMAGE_SCN_ALIGN_2BYTES                  0x00200000
+#define        IMAGE_SCN_ALIGN_4BYTES                  0x00300000
+#define        IMAGE_SCN_ALIGN_8BYTES                  0x00400000
+#define        IMAGE_SCN_ALIGN_16BYTES                 0x00500000  /* Default */
+#define IMAGE_SCN_ALIGN_32BYTES                        0x00600000
+#define IMAGE_SCN_ALIGN_64BYTES                        0x00700000
+/*                                             0x00800000 - Unused */
+
+#define IMAGE_SCN_LNK_NRELOC_OVFL              0x01000000
+
+
+#define IMAGE_SCN_MEM_DISCARDABLE              0x02000000
+#define IMAGE_SCN_MEM_NOT_CACHED               0x04000000
+#define IMAGE_SCN_MEM_NOT_PAGED                        0x08000000
+#define IMAGE_SCN_MEM_SHARED                   0x10000000
+#define IMAGE_SCN_MEM_EXECUTE                  0x20000000
+#define IMAGE_SCN_MEM_READ                     0x40000000
+#define IMAGE_SCN_MEM_WRITE                    0x80000000
+
+#pragma pack()
+
+typedef struct _GUID  /* 16 bytes */
+{
+    unsigned int   Data1;
+    unsigned short Data2;
+    unsigned short Data3;
+    unsigned char  Data4[ 8 ];
+} GUID;
+
+/*========================================================================
+ * Process PDB file.
+ */
+
+#pragma pack(1)
+typedef struct _PDB_FILE
+{
+    unsigned long size;
+    unsigned long unknown;
+
+} PDB_FILE, *PPDB_FILE;
+
+// A .pdb file begins with a variable-length one-line text string
+// that ends in "\r\n\032".  This is followed by a 4-byte "signature"
+// ("DS\0\0" for newer files, "JG\0\0" for older files), then
+// aligned up to a 4-byte boundary, then the struct below:
+struct PDB_JG_HEADER
+{
+    //char ident[40];  // "Microsoft C/C++ program database 2.00\r\n\032"
+    //unsigned long  signature;  // "JG\0\0"
+    unsigned int   blocksize;  // 0x400 typical; also 0x800, 0x1000
+    unsigned short freelist;
+    unsigned short total_alloc;
+    PDB_FILE toc;
+    unsigned short toc_block[ 1 ];
+};
+
+struct PDB_DS_HEADER
+{
+    //char   signature[32];  // "Microsoft C/C++ MSF 7.00\r\n\032DS\0\0"
+    unsigned int  block_size;
+    unsigned int unknown1;
+    unsigned int num_pages;
+    unsigned int toc_size;
+    unsigned int unknown2;
+    unsigned int toc_page;
+};
+
+struct PDB_JG_TOC
+{
+    unsigned int  nFiles;
+    PDB_FILE file[ 1 ];
+
+};
+
+struct PDB_DS_TOC
+{
+    unsigned int num_files;
+    unsigned int file_size[1];
+};
+
+struct PDB_JG_ROOT
+{
+    unsigned int  version;
+    unsigned int  TimeDateStamp;
+    unsigned int  age;
+    unsigned int  cbNames;
+    char names[ 1 ];
+};
+
+struct PDB_DS_ROOT
+{
+    unsigned int version;
+    unsigned int TimeDateStamp;
+    unsigned int age;
+    GUID guid;
+    unsigned int cbNames;
+    char names[1];
+};
+
+typedef struct _PDB_TYPES_OLD
+{
+    unsigned long  version;
+    unsigned short first_index;
+    unsigned short last_index;
+    unsigned long  type_size;
+    unsigned short file;
+    unsigned short pad;
+
+} PDB_TYPES_OLD, *PPDB_TYPES_OLD;
+
+typedef struct _PDB_TYPES
+{
+    unsigned long  version;
+    unsigned long  type_offset;
+    unsigned long  first_index;
+    unsigned long  last_index;
+    unsigned long  type_size;
+    unsigned short file;
+    unsigned short pad;
+    unsigned long  hash_size;
+    unsigned long  hash_base;
+    unsigned long  hash_offset;
+    unsigned long  hash_len;
+    unsigned long  search_offset;
+    unsigned long  search_len;
+    unsigned long  unknown_offset;
+    unsigned long  unknown_len;
+
+} PDB_TYPES, *PPDB_TYPES;
+
+typedef struct _PDB_SYMBOL_RANGE
+{
+    unsigned short segment;
+    unsigned short pad1;
+    unsigned long  offset;
+    unsigned long  size;
+    unsigned long  characteristics;
+    unsigned short index;
+    unsigned short pad2;
+
+} PDB_SYMBOL_RANGE, *PPDB_SYMBOL_RANGE;
+
+typedef struct _PDB_SYMBOL_RANGE_EX
+{
+    unsigned short segment;
+    unsigned short pad1;
+    unsigned long  offset;
+    unsigned long  size;
+    unsigned long  characteristics;
+    unsigned short index;
+    unsigned short pad2;
+    unsigned long  timestamp;
+    unsigned long  unknown;
+
+} PDB_SYMBOL_RANGE_EX, *PPDB_SYMBOL_RANGE_EX;
+
+typedef struct _PDB_SYMBOL_FILE
+{
+    unsigned long  unknown1;
+    PDB_SYMBOL_RANGE range;
+    unsigned short flag;
+    unsigned short file;
+    unsigned long  symbol_size;
+    unsigned long  lineno_size;
+    unsigned long  unknown2;
+    unsigned long  nSrcFiles;
+    unsigned long  attribute;
+    char filename[ 1 ];
+
+} PDB_SYMBOL_FILE, *PPDB_SYMBOL_FILE;
+
+typedef struct _PDB_SYMBOL_FILE_EX
+{
+    unsigned long  unknown1;
+    PDB_SYMBOL_RANGE_EX range;
+    unsigned short flag;
+    unsigned short file;
+    unsigned long  symbol_size;
+    unsigned long  lineno_size;
+    unsigned long  unknown2;
+    unsigned long  nSrcFiles;
+    unsigned long  attribute;
+    unsigned long  reserved[ 2 ];
+    char filename[ 1 ];
+
+} PDB_SYMBOL_FILE_EX, *PPDB_SYMBOL_FILE_EX;
+
+typedef struct _PDB_SYMBOL_SOURCE
+{
+    unsigned short nModules;
+    unsigned short nSrcFiles;
+    unsigned short table[ 1 ];
+
+} PDB_SYMBOL_SOURCE, *PPDB_SYMBOL_SOURCE;
+
+typedef struct _PDB_SYMBOL_IMPORT
+{
+    unsigned long unknown1;
+    unsigned long unknown2;
+    unsigned long TimeDateStamp;
+    unsigned long nRequests;
+    char filename[ 1 ];
+
+} PDB_SYMBOL_IMPORT, *PPDB_SYMBOL_IMPORT;
+
+typedef struct _PDB_SYMBOLS_OLD
+{
+    unsigned short hash1_file;
+    unsigned short hash2_file;
+    unsigned short gsym_file;
+    unsigned short pad;
+    unsigned long  module_size;
+    unsigned long  offset_size;
+    unsigned long  hash_size;
+    unsigned long  srcmodule_size;
+
+} PDB_SYMBOLS_OLD, *PPDB_SYMBOLS_OLD;
+
+typedef struct _PDB_SYMBOLS
+{
+    unsigned long  signature;
+    unsigned long  version;
+    unsigned long  unknown;
+    unsigned long  hash1_file;
+    unsigned long  hash2_file;
+    unsigned long  gsym_file;
+    unsigned long  module_size;
+    unsigned long  offset_size;
+    unsigned long  hash_size;
+    unsigned long  srcmodule_size;
+    unsigned long  pdbimport_size;
+    unsigned long  resvd[ 5 ];
+
+} PDB_SYMBOLS, *PPDB_SYMBOLS;
+#pragma pack()
+
+/*========================================================================
+ * Process CodeView symbol information.
+ */
+
+/* from wine-1.0/include/wine/mscvpdb.h */
+
+struct p_string  /* "Pascal string": prefixed by byte containing length */
+{
+    unsigned char               namelen;
+    char                        name[1];
+};
+/* The other kind of "char name[1]" is a "C++ string" terminated by '\0'.
+ * "Name mangling" to encode type information often exceeds 255 bytes.
+ * Instead of using a 2-byte explicit length, they save one byte of space
+ * but incur a strlen().  This is justified by other code that wants
+ * a "C string" [terminated by '\0'] anyway.
+ */
+
+union codeview_symbol
+{
+    struct
+    {
+        short int              len;
+        short int              id;
+    } generic;
+
+    struct
+    {
+       short int               len;
+       short int               id;
+       unsigned int            offset;
+       unsigned short          segment;
+       unsigned short          symtype;
+        struct p_string         p_name;
+    } data_v1;
+
+    struct
+    {
+       short int               len;
+       short int               id;
+       unsigned int            symtype;
+       unsigned int            offset;
+       unsigned short          segment;
+        struct p_string         p_name;
+    } data_v2;
+
+    struct
+    {
+        short int               len;
+        short int               id;
+        unsigned int            symtype;
+        unsigned int            offset;
+        unsigned short          segment;
+        char                    name[1];  /* terminated by '\0' */
+    } data_v3;
+
+    struct
+    {
+       short int               len;
+       short int               id;
+       unsigned int            pparent;
+       unsigned int            pend;
+       unsigned int            next;
+       unsigned int            offset;
+       unsigned short          segment;
+       unsigned short          thunk_len;
+       unsigned char           thtype;
+        struct p_string         p_name;
+    } thunk_v1;
+
+    struct
+    {
+        short int               len;
+        short int               id;
+        unsigned int            pparent;
+        unsigned int            pend;
+        unsigned int            next;
+        unsigned int            offset;
+        unsigned short          segment;
+        unsigned short          thunk_len;
+        unsigned char           thtype;
+        char                    name[1];  /* terminated by '\0' */
+    } thunk_v3;
+
+    struct
+    {
+       short int               len;
+       short int               id;
+       unsigned int            pparent;
+       unsigned int            pend;
+       unsigned int            next;
+       unsigned int            proc_len;
+       unsigned int            debug_start;
+       unsigned int            debug_end;
+       unsigned int            offset;
+       unsigned short          segment;
+       unsigned short          proctype;
+       unsigned char           flags;
+        struct p_string         p_name;
+    } proc_v1;
+
+    struct
+    {
+       short int               len;
+       short int               id;
+       unsigned int            pparent;
+       unsigned int            pend;
+       unsigned int            next;
+       unsigned int            proc_len;
+       unsigned int            debug_start;
+       unsigned int            debug_end;
+       unsigned int            proctype;
+       unsigned int            offset;
+       unsigned short          segment;
+       unsigned char           flags;
+        struct p_string         p_name;
+    } proc_v2;
+
+    struct
+    {
+        short int               len;
+        short int               id;
+        unsigned int            pparent;
+        unsigned int            pend;
+        unsigned int            next;
+        unsigned int            proc_len;
+        unsigned int            debug_start;
+        unsigned int            debug_end;
+        unsigned int            proctype;
+        unsigned int            offset;
+        unsigned short          segment;
+        unsigned char           flags;
+        char                    name[1];  /* terminated by '\0' */
+    } proc_v3;
+
+    struct
+    {
+        short int               len;
+        short int               id;
+        unsigned int            symtype;
+        unsigned int            offset;
+        unsigned short          segment;
+        struct p_string         p_name;
+    } public_v2;
+
+    struct
+    {
+        short int               len;
+        short int               id;
+        unsigned int            symtype;
+        unsigned int            offset;
+        unsigned short          segment;
+        char                    name[1];  /* terminated by '\0' */
+    } public_v3;
+
+    struct
+    {
+       short int               len;            /* Total length of this entry */
+       short int               id;             /* Always S_BPREL_V1 */
+       unsigned int            offset;         /* Stack offset relative to BP */
+       unsigned short          symtype;
+        struct p_string         p_name;
+    } stack_v1;
+
+    struct
+    {
+       short int               len;            /* Total length of this entry */
+       short int               id;             /* Always S_BPREL_V2 */
+       unsigned int            offset;         /* Stack offset relative to EBP */
+       unsigned int            symtype;
+        struct p_string         p_name;
+    } stack_v2;
+
+    struct
+    {
+        short int               len;            /* Total length of this entry */
+        short int               id;             /* Always S_BPREL_V3 */
+        int                     offset;         /* Stack offset relative to BP */
+        unsigned int            symtype;
+        char                    name[1];  /* terminated by '\0' */
+    } stack_v3;
+
+    struct
+    {
+        short int               len;            /* Total length of this entry */
+        short int               id;             /* Always S_BPREL_V3 */
+        int                     offset;         /* Stack offset relative to BP */
+        unsigned int            symtype;
+        unsigned short          unknown;
+        char                    name[1];  /* terminated by '\0' */
+    } stack_xxxx_v3;
+
+    struct
+    {
+       short int               len;            /* Total length of this entry */
+       short int               id;             /* Always S_REGISTER */
+        unsigned short          type;
+        unsigned short          reg;
+        struct p_string         p_name;
+        /* don't handle register tracking */
+    } register_v1;
+
+    struct
+    {
+       short int               len;            /* Total length of this entry */
+       short int               id;             /* Always S_REGISTER_V2 */
+        unsigned int            type;           /* check whether type & reg are correct */
+        unsigned short          reg;
+        struct p_string         p_name;
+        /* don't handle register tracking */
+    } register_v2;
+
+    struct
+    {
+       short int               len;            /* Total length of this entry */
+       short int               id;             /* Always S_REGISTER_V3 */
+        unsigned int            type;           /* check whether type & reg are correct */
+        unsigned short          reg;
+        char                    name[1];  /* terminated by '\0' */
+        /* don't handle register tracking */
+    } register_v3;
+
+    struct
+    {
+        short int               len;
+        short int               id;
+        unsigned int            parent;
+        unsigned int            end;
+        unsigned int            length;
+        unsigned int            offset;
+        unsigned short          segment;
+        struct p_string         p_name;
+    } block_v1;
+
+    struct
+    {
+        short int               len;
+        short int               id;
+        unsigned int            parent;
+        unsigned int            end;
+        unsigned int            length;
+        unsigned int            offset;
+        unsigned short          segment;
+        char                    name[1];  /* terminated by '\0' */
+    } block_v3;
+
+    struct
+    {
+        short int               len;
+        short int               id;
+        unsigned int            offset;
+        unsigned short          segment;
+        unsigned char           flags;
+        struct p_string         p_name;
+    } label_v1;
+
+    struct
+    {
+        short int               len;
+        short int               id;
+        unsigned int            offset;
+        unsigned short          segment;
+        unsigned char           flags;
+        char                    name[1];  /* terminated by '\0' */
+    } label_v3;
+
+    struct
+    {
+        short int               len;
+        short int               id;
+        unsigned short          type;
+        unsigned short          cvalue;         /* numeric leaf */
+#if 0
+        struct p_string         p_name;
+#endif
+    } constant_v1;
+
+    struct
+    {
+        short int               len;
+        short int               id;
+        unsigned                type;
+        unsigned short          cvalue;         /* numeric leaf */
+#if 0
+        struct p_string         p_name;
+#endif
+    } constant_v2;
+
+    struct
+    {
+        short int               len;
+        short int               id;
+        unsigned                type;
+        unsigned short          cvalue;
+#if 0
+        char                    name[1];  /* terminated by '\0' */
+#endif
+    } constant_v3;
+
+    struct
+    {
+        short int               len;
+        short int               id;
+        unsigned short          type;
+        struct p_string         p_name;
+    } udt_v1;
+
+    struct
+    {
+        short int               len;
+        short int               id;
+        unsigned                type;
+        struct p_string         p_name;
+    } udt_v2;
+
+    struct
+    {
+        short int               len;
+        short int               id;
+        unsigned int            type;
+        char                    name[1];  /* terminated by '\0' */
+    } udt_v3;
+
+    struct
+    {
+        short int               len;
+        short int               id;
+        char                    signature[4];
+        struct p_string         p_name;
+    } objname_v1;
+
+    struct
+    {
+        short int               len;
+        short int               id;
+        unsigned int            unknown;
+        struct p_string         p_name;
+    } compiland_v1;
+
+    struct
+    {
+        short int               len;
+        short int               id;
+        unsigned                unknown1[4];
+        unsigned short          unknown2;
+        struct p_string         p_name;
+    } compiland_v2;
+
+    struct
+    {
+        short int               len;
+        short int               id;
+        unsigned int            unknown;
+        char                    name[1];  /* terminated by '\0' */
+    } compiland_v3;
+
+    struct
+    {
+        short int               len;
+        short int               id;
+        unsigned int            offset;
+        unsigned short          segment;
+    } ssearch_v1;
+};
+
+#define S_COMPILAND_V1  0x0001
+#define S_REGISTER_V1   0x0002
+#define S_CONSTANT_V1   0x0003
+#define S_UDT_V1        0x0004
+#define S_SSEARCH_V1    0x0005
+#define S_END_V1        0x0006
+#define S_SKIP_V1       0x0007
+#define S_CVRESERVE_V1  0x0008
+#define S_OBJNAME_V1    0x0009
+#define S_ENDARG_V1     0x000a
+#define S_COBOLUDT_V1   0x000b
+#define S_MANYREG_V1    0x000c
+#define S_RETURN_V1     0x000d
+#define S_ENTRYTHIS_V1  0x000e
+
+#define S_BPREL_V1      0x0200
+#define S_LDATA_V1      0x0201
+#define S_GDATA_V1      0x0202
+#define S_PUB_V1        0x0203
+#define S_LPROC_V1      0x0204
+#define S_GPROC_V1      0x0205
+#define S_THUNK_V1      0x0206
+#define S_BLOCK_V1      0x0207
+#define S_WITH_V1       0x0208
+#define S_LABEL_V1      0x0209
+#define S_CEXMODEL_V1   0x020a
+#define S_VFTPATH_V1    0x020b
+#define S_REGREL_V1     0x020c
+#define S_LTHREAD_V1    0x020d
+#define S_GTHREAD_V1    0x020e
+
+#define S_PROCREF_V1    0x0400
+#define S_DATAREF_V1    0x0401
+#define S_ALIGN_V1      0x0402
+#define S_LPROCREF_V1   0x0403
+
+#define S_REGISTER_V2   0x1001 /* Variants with new 32-bit type indices */
+#define S_CONSTANT_V2   0x1002
+#define S_UDT_V2        0x1003
+#define S_COBOLUDT_V2   0x1004
+#define S_MANYREG_V2    0x1005
+#define S_BPREL_V2      0x1006
+#define S_LDATA_V2      0x1007
+#define S_GDATA_V2      0x1008
+#define S_PUB_V2        0x1009
+#define S_LPROC_V2      0x100a
+#define S_GPROC_V2      0x100b
+#define S_VFTTABLE_V2   0x100c
+#define S_REGREL_V2     0x100d
+#define S_LTHREAD_V2    0x100e
+#define S_GTHREAD_V2    0x100f
+#if 0
+#define S_XXXXXXXXX_32  0x1012  /* seems linked to a function, content unknown */
+#endif
+#define S_COMPILAND_V2  0x1013
+
+#define S_COMPILAND_V3  0x1101
+#define S_THUNK_V3      0x1102
+#define S_BLOCK_V3      0x1103
+#define S_LABEL_V3      0x1105
+#define S_REGISTER_V3   0x1106
+#define S_CONSTANT_V3   0x1107
+#define S_UDT_V3        0x1108
+#define S_BPREL_V3      0x110B
+#define S_LDATA_V3      0x110C
+#define S_GDATA_V3      0x110D
+#define S_PUB_V3        0x110E
+#define S_LPROC_V3      0x110F
+#define S_GPROC_V3      0x1110
+#define S_BPREL_XXXX_V3 0x1111  /* not really understood, but looks like bprel... */
+#define S_MSTOOL_V3     0x1116  /* compiler command line options and build information */
+#define S_PUB_FUNC1_V3  0x1125  /* didn't get the difference between the two */
+#define S_PUB_FUNC2_V3  0x1127
+
+
+/*------------------------------------------------------------*/
+/*---                                                      ---*/
+/*--- pdb-reading: bits and pieces                         ---*/
+/*---                                                      ---*/
+/*------------------------------------------------------------*/
+
+struct pdb_reader
+{
+   void* (*read_file)(struct pdb_reader*, unsigned, unsigned *);
+   // JRS 2009-Apr-8: .uu_n_pdbimage is never used.
+   UChar* pdbimage;      // image address
+   SizeT  uu_n_pdbimage; // size
+   union {
+      struct {
+         struct PDB_JG_HEADER* header;
+         struct PDB_JG_TOC* toc;
+      } jg;
+      struct {
+         struct PDB_DS_HEADER* header;
+         struct PDB_DS_TOC* toc;
+      } ds;
+   } u;
+};
+
+
+static void* pdb_ds_read( struct pdb_reader* pdb,
+                          unsigned* block_list,
+                          unsigned  size )
+{
+   unsigned  blocksize, nBlocks;
+   UChar* buffer;
+   UInt i;
+
+   if (!size) return NULL;
+
+   blocksize = pdb->u.ds.header->block_size;
+   nBlocks   = (size + blocksize - 1) / blocksize;
+   buffer    = ML_(dinfo_zalloc)("di.readpe.pdr.1", nBlocks * blocksize);
+   for (i = 0; i < nBlocks; i++)
+      VG_(memcpy)( buffer + i * blocksize,
+                   pdb->pdbimage + block_list[i] * blocksize,
+                   blocksize );
+   return buffer;
+}
+
+
+static void* pdb_jg_read( struct pdb_reader* pdb,
+                          unsigned short* block_list,
+                          int size )
+{
+   unsigned  blocksize, nBlocks;
+   UChar* buffer;
+   UInt i;
+   //VG_(printf)("pdb_read %p %p %d\n", pdb, block_list, size);
+   if ( !size ) return NULL;
+
+   blocksize = pdb->u.jg.header->blocksize;
+   nBlocks = (size + blocksize-1) / blocksize;
+   buffer = ML_(dinfo_zalloc)("di.readpe.pjr.1", nBlocks * blocksize);
+   for ( i = 0; i < nBlocks; i++ )
+      VG_(memcpy)( buffer + i*blocksize,
+                   pdb->pdbimage + block_list[i]*blocksize, blocksize );
+   return buffer;
+}
+
+
+static void* find_pdb_header( UChar* pdbimage,
+                              unsigned* signature )
+{
+   static char pdbtxt[]= "Microsoft C/C++";
+   UChar* txteof = (UChar*)VG_(strchr)(pdbimage, '\032');
+   if (! txteof)
+      return NULL;
+   if (0!=VG_(strncmp)(pdbimage, pdbtxt, -1+ sizeof(pdbtxt)))
+      return NULL;
+
+   *signature = *(unsigned*)(1+ txteof);
+   return (void*)((~3& (3+ (4+ 1+ (txteof - pdbimage)))) + pdbimage);
+}
+
+
+static void* pdb_ds_read_file( struct pdb_reader* reader,
+                               unsigned  file_number,
+                               unsigned* plength )
+{
+   unsigned i, *block_list;
+   if (!reader->u.ds.toc || file_number >= reader->u.ds.toc->num_files)
+      return NULL;
+   if (reader->u.ds.toc->file_size[file_number] == 0
+       || reader->u.ds.toc->file_size[file_number] == 0xFFFFFFFF)
+      return NULL;
+
+   block_list
+      = reader->u.ds.toc->file_size + reader->u.ds.toc->num_files;
+   for (i = 0; i < file_number; i++)
+      block_list += (reader->u.ds.toc->file_size[i] 
+                     + reader->u.ds.header->block_size - 1)
+                    /
+                    reader->u.ds.header->block_size;
+   if (plength)
+      *plength = reader->u.ds.toc->file_size[file_number];
+   return pdb_ds_read( reader, block_list,
+                       reader->u.ds.toc->file_size[file_number]);
+}
+
+
+static void* pdb_jg_read_file( struct pdb_reader* pdb,
+                               unsigned fileNr,
+                               unsigned *plength )
+{
+   //VG_(printf)("pdb_read_file %p %d\n", pdb, fileNr);
+   unsigned blocksize = pdb->u.jg.header->blocksize;
+   struct PDB_JG_TOC* toc = pdb->u.jg.toc;
+   unsigned i;
+   unsigned short* block_list;
+
+   if ( !toc || fileNr >= toc->nFiles )
+       return NULL;
+
+   block_list
+      = (unsigned short *) &toc->file[ toc->nFiles ];
+   for ( i = 0; i < fileNr; i++ )
+      block_list += (toc->file[i].size + blocksize-1) / blocksize;
+
+   if (plength)
+      *plength = toc->file[fileNr].size;
+   return pdb_jg_read( pdb, block_list, toc->file[fileNr].size );
+}
+
+
+static void pdb_ds_init( struct pdb_reader * reader,
+                         UChar* pdbimage,
+                         SizeT  n_pdbimage )
+{
+   reader->read_file     = pdb_ds_read_file;
+   reader->pdbimage      = pdbimage;
+   reader->uu_n_pdbimage = n_pdbimage;
+   reader->u.ds.toc
+      = pdb_ds_read(
+           reader,
+           (unsigned*)(reader->u.ds.header->block_size 
+                       * reader->u.ds.header->toc_page 
+                       + reader->pdbimage),
+           reader->u.ds.header->toc_size
+        );
+}
+
+
+static void pdb_jg_init( struct pdb_reader* reader,
+                         char* pdbimage,
+                         unsigned n_pdbimage )
+{
+   reader->read_file     = pdb_jg_read_file;
+   reader->pdbimage      = pdbimage;
+   reader->uu_n_pdbimage = n_pdbimage;
+   reader->u.jg.toc = pdb_jg_read(reader,
+                                  reader->u.jg.header->toc_block,
+                                  reader->u.jg.header->toc.size);
+}
+
+
+
+
+static 
+void pdb_check_root_version_and_timestamp( char* pdbname,
+                                           ULong  pdbmtime,
+                                           unsigned  version,
+                                           UInt TimeDateStamp )
+{
+   switch ( version ) {
+      case 19950623:      /* VC 4.0 */
+      case 19950814:
+      case 19960307:      /* VC 5.0 */
+      case 19970604:      /* VC 6.0 */
+      case 20000404:      /* VC 7.0  FIXME?? */
+         break;
+      default:
+         if (VG_(clo_verbosity) > 1)
+            VG_(message)(Vg_UserMsg,
+                         "Unknown .pdb root block version %d\n", version );
+   }
+   if ( TimeDateStamp != pdbmtime ) {
+      if (VG_(clo_verbosity) > 1)
+         VG_(message)(Vg_UserMsg, 
+                     "Wrong time stamp of .PDB file %s (0x%08x, 0x%08llx)\n",
+                      pdbname, TimeDateStamp, pdbmtime );
+   }
+}
+
+
+static DWORD pdb_get_file_size( struct pdb_reader* reader, unsigned idx )
+{
+   if (reader->read_file == pdb_jg_read_file)
+      return reader->u.jg.toc->file[idx].size;
+   else
+      return reader->u.ds.toc->file_size[idx];
+}
+
+
+static void pdb_convert_types_header( PDB_TYPES *types, char* image )
+{
+   VG_(memset)( types, 0, sizeof(PDB_TYPES) );
+   if ( !image )
+      return;
+   if ( *(unsigned long *)image < 19960000 ) {  /* FIXME: correct version? */
+      /* Old version of the types record header */
+      PDB_TYPES_OLD *old = (PDB_TYPES_OLD *)image;
+      types->version     = old->version;
+      types->type_offset = sizeof(PDB_TYPES_OLD);
+      types->type_size   = old->type_size;
+      types->first_index = old->first_index;
+      types->last_index  = old->last_index;
+      types->file        = old->file;
+   } else {
+      /* New version of the types record header */
+      *types = *(PDB_TYPES *)image;
+   }
+}
+
+
+static void pdb_convert_symbols_header( PDB_SYMBOLS *symbols,
+                                        int *header_size, char* image )
+{
+   VG_(memset)( symbols, 0, sizeof(PDB_SYMBOLS) );
+   if ( !image )
+      return;
+   if ( *(unsigned long *)image != 0xffffffff ) {
+      /* Old version of the symbols record header */
+      PDB_SYMBOLS_OLD *old     = (PDB_SYMBOLS_OLD *)image;
+      symbols->version         = 0;
+      symbols->module_size     = old->module_size;
+      symbols->offset_size     = old->offset_size;
+      symbols->hash_size       = old->hash_size;
+      symbols->srcmodule_size  = old->srcmodule_size;
+      symbols->pdbimport_size  = 0;
+      symbols->hash1_file      = old->hash1_file;
+      symbols->hash2_file      = old->hash2_file;
+      symbols->gsym_file       = old->gsym_file;
+      *header_size = sizeof(PDB_SYMBOLS_OLD);
+   } else {
+      /* New version of the symbols record header */
+      *symbols = *(PDB_SYMBOLS *)image;
+      *header_size = sizeof(PDB_SYMBOLS);
+   }
+}
+
+
+/*------------------------------------------------------------*/
+/*---                                                      ---*/
+/*--- Main stuff: reading of symbol addresses              ---*/
+/*---                                                      ---*/
+/*------------------------------------------------------------*/
+
+static Bool debug = False; // JRS: fixme
+
+static ULong DEBUG_SnarfCodeView(
+                DebugInfo* di,
+                IMAGE_SECTION_HEADER* sectp,
+                void* root, /* FIXME: better name */
+                Int offset,
+                Int size
+             )
+{
+   Int    i, length;
+   DiSym  vsym;
+   UChar* nmstr;
+   Char   symname[4096 /*WIN32_PATH_MAX*/];
+
+   Addr  bias = BIAS_FOR_SYMBOLS;
+   ULong n_syms_read = 0;
+
+   if (debug)
+      VG_(message)(Vg_UserMsg, "SnarfCodeView addr=%p offset=%d length=%d", 
+                               root, offset, size );
+
+   VG_(memset)(&vsym, 0, sizeof(vsym));  /* avoid holes */
+   /*
+    * Loop over the different types of records and whenever we
+    * find something we are interested in, record it and move on.
+    */
+   for ( i = offset; i < size; i += length )
+   {
+      union codeview_symbol *sym = (union codeview_symbol *)((char *)root + i);
+
+      length = sym->generic.len + 2;
+
+      //VG_(printf)("id=%x  len=%d\n", sym->generic.id, length);
+      switch ( sym->generic.id ) {
+
+      default:
+         if (0) {
+            VG_(printf)("unknown id 0x%x len=0x%x at %p\n",
+                        sym->generic.id, sym->generic.len, sym);
+            VG_(printf)("  %8x  %8x  %8x  %8x\n", 
+                        ((int *)sym)[1],((int *)sym)[2],
+                        ((int *)sym)[3],((int *)sym)[4]);
+            VG_(printf)("  %8x  %8x  %8x  %8x\n",
+                        ((int *)sym)[5],((int *)sym)[6],
+                        ((int *)sym)[7],((int *)sym)[8]);
+         }
+         break;
+      /*
+       * Global and local data symbols.  We don't associate these
+       * with any given source file.
+       */
+      case S_GDATA_V1:
+      case S_LDATA_V1:
+      case S_PUB_V1:
+         VG_(memcpy)(symname, sym->data_v1.p_name.name,
+                              sym->data_v1.p_name.namelen);
+         symname[sym->data_v1.p_name.namelen] = '\0';
+
+         if (debug)
+            VG_(message)(Vg_UserMsg, "Data %s", symname );
+
+         if (0 /*VG_(needs).data_syms*/) {
+            nmstr = ML_(addStr)(di, symname, sym->data_v1.p_name.namelen);
+
+            vsym.addr = bias + sectp[sym->data_v1.segment-1].VirtualAddress
+                             + sym->data_v1.offset;
+            vsym.name = nmstr;
+            vsym.size = sym->data_v1.p_name.namelen;
+                      // FIXME: .namelen is sizeof(.data) including .name[]
+            vsym.isText = (sym->generic.id == S_PUB_V1);
+            ML_(addSym)( di, &vsym );
+            n_syms_read++;
+         }
+         break;
+      case S_GDATA_V2:
+      case S_LDATA_V2:
+      case S_PUB_V2: {
+         Int const k = sym->data_v2.p_name.namelen;
+         VG_(memcpy)(symname, sym->data_v2.p_name.name, k);
+         symname[k] = '\0';
+
+         if (debug)
+            VG_(message)(Vg_UserMsg,
+                         "S_GDATA_V2/S_LDATA_V2/S_PUB_V2 %s", symname );
+
+         if (sym->generic.id==S_PUB_V2 /*VG_(needs).data_syms*/) {
+            nmstr = ML_(addStr)(di, symname, k);
+
+            vsym.addr = bias + sectp[sym->data_v2.segment-1].VirtualAddress
+                             + sym->data_v2.offset;
+            vsym.name = nmstr;
+            vsym.size = 4000;
+                        // FIXME: data_v2.len is sizeof(.data),
+                        // not size of function!
+            vsym.isText = !!(IMAGE_SCN_CNT_CODE 
+                             & sectp[sym->data_v2.segment-1].Characteristics);
+            ML_(addSym)( di, &vsym );
+            n_syms_read++;
+         }
+         break;
+      }
+      case S_PUB_V3:
+      /* not completely sure of those two anyway */
+      case S_PUB_FUNC1_V3:
+      case S_PUB_FUNC2_V3: {
+         Int k = sym->public_v3.len - (-1+ sizeof(sym->public_v3));
+         if ((-1+ sizeof(symname)) < k)
+            k = -1+ sizeof(symname);
+         VG_(memcpy)(symname, sym->public_v3.name, k);
+         symname[k] = '\0';
+
+         if (debug)
+            VG_(message)(Vg_UserMsg,
+                         "S_PUB_FUNC1_V3/S_PUB_FUNC2_V3/S_PUB_V3 %s", symname );
+
+         if (1  /*sym->generic.id==S_PUB_FUNC1_V3 
+                  || sym->generic.id==S_PUB_FUNC2_V3*/) {
+            nmstr = ML_(addStr)(di, symname, k);
+
+            vsym.addr = bias + sectp[sym->public_v3.segment-1].VirtualAddress
+                             + sym->public_v3.offset;
+            vsym.name = nmstr;
+            vsym.size = 4000;
+                        // FIXME: public_v3.len is not length of the
+                        // .text of the function
+            vsym.isText = !!(IMAGE_SCN_CNT_CODE
+                             & sectp[sym->data_v2.segment-1].Characteristics);
+            ML_(addSym)( di, &vsym );
+            n_syms_read++;
+         }
+         break;
+      }
+
+      /*
+       * Sort of like a global function, but it just points
+       * to a thunk, which is a stupid name for what amounts to
+       * a PLT slot in the normal jargon that everyone else uses.
+       */
+      case S_THUNK_V3:
+      case S_THUNK_V1:
+         /* valgrind ignores PLTs */ /* JRS: it does? */
+         break;
+
+      /*
+       * Global and static functions.
+       */
+      case S_GPROC_V1:
+      case S_LPROC_V1:
+         VG_(memcpy)(symname, sym->proc_v1.p_name.name,
+                              sym->proc_v1.p_name.namelen);
+         symname[sym->proc_v1.p_name.namelen] = '\0';
+         nmstr = ML_(addStr)(di, symname, sym->proc_v1.p_name.namelen);
+
+         vsym.addr = bias + sectp[sym->proc_v1.segment-1].VirtualAddress
+                          + sym->proc_v1.offset;
+         vsym.name = nmstr;
+         vsym.size = sym->proc_v1.proc_len;
+         vsym.isText = True;
+         if (debug)
+             VG_(message)(Vg_UserMsg, "Adding function %s addr=%#lx length=%d",
+                                      symname, vsym.addr, vsym.size );
+         ML_(addSym)( di, &vsym );
+         n_syms_read++;
+         break;
+
+      case S_GPROC_V2:
+      case S_LPROC_V2:
+         VG_(memcpy)(symname, sym->proc_v2.p_name.name,
+                              sym->proc_v2.p_name.namelen);
+         symname[sym->proc_v2.p_name.namelen] = '\0';
+         nmstr = ML_(addStr)(di, symname, sym->proc_v2.p_name.namelen);
+
+         vsym.addr = bias + sectp[sym->proc_v2.segment-1].VirtualAddress
+                          + sym->proc_v2.offset;
+         vsym.name = nmstr;
+         vsym.size = sym->proc_v2.proc_len;
+         vsym.isText = True;
+         if (debug)
+            VG_(message)(Vg_UserMsg, "Adding function %s addr=%#lx length=%d",
+                                     symname, vsym.addr, vsym.size );
+         ML_(addSym)( di, &vsym );
+         n_syms_read++;
+         break;
+      case S_LPROC_V3:
+      case S_GPROC_V3: {
+         if (debug)
+            VG_(message)(Vg_UserMsg,
+                         "S_LPROC_V3/S_GPROC_V3 %s", sym->proc_v3.name );
+
+         if (1) {
+            nmstr = ML_(addStr)(di, sym->proc_v3.name,
+                                    VG_(strlen)(sym->proc_v3.name));
+
+            vsym.addr = bias + sectp[sym->proc_v3.segment-1].VirtualAddress
+                             + sym->proc_v3.offset;
+            vsym.name = nmstr;
+            vsym.size  = sym->proc_v3.proc_len;
+            vsym.isText = 1;
+            ML_(addSym)( di, &vsym );
+            n_syms_read++;
+         }
+         break;
+      }
+      /* JRS: how is flow supposed to arrive at commented out code below? */
+      //if (nest_block)
+      //{
+      //   printf(">>> prev func '%s' still has nest_block %u count\n",
+      //          curr_func, nest_block);
+      //   nest_block = 0;
+      //}
+      //curr_func = strdup(sym->proc_v3.name);
+      /* EPP  unsigned int    pparent; */
+      /* EPP  unsigned int    pend; */
+      /* EPP  unsigned int    next; */
+      /* EPP  unsigned int    debug_start; */
+      /* EPP  unsigned int    debug_end; */
+      /* EPP  unsigned char   flags; */
+      // break;
+
+
+      /*
+       * Function parameters and stack variables.
+       */
+      case S_BPREL_XXXX_V3:
+      case S_BPREL_V3:
+      case S_BPREL_V2:
+      case S_BPREL_V1:
+         /* ignored */
+         break;
+
+      case S_LABEL_V3:  // FIXME
+      case S_LABEL_V1:
+         break;
+
+      case S_SSEARCH_V1:
+      case S_ALIGN_V1:
+      case S_MSTOOL_V3:
+      case S_UDT_V3:
+      case S_UDT_V2:
+      case S_UDT_V1:
+      case S_CONSTANT_V3:
+      case S_CONSTANT_V1:
+      case S_OBJNAME_V1:
+      case S_END_V1:
+      case S_COMPILAND_V3:
+      case S_COMPILAND_V2:
+      case S_COMPILAND_V1:
+      case S_BLOCK_V3:
+      case S_BLOCK_V1:
+      case S_REGISTER_V3:
+      case S_REGISTER_V2:
+      case S_REGISTER_V1:
+         /* ignored */
+         break;
+
+      /*
+       * These are special, in that they are always followed by an
+       * additional length-prefixed string which is *not* included
+       * into the symbol length count.  We need to skip it.
+       */
+      case S_PROCREF_V1:
+      case S_DATAREF_V1:
+      case S_LPROCREF_V1: {
+         unsigned char *name = (unsigned char *)sym + length;
+         length += (*name + 1 + 3) & ~3;
+         break;
+      }
+      } /* switch ( sym->generic.id ) */
+
+   } /* for ( i = offset; i < size; i += length ) */
+
+   return n_syms_read;
+}
+
+
+/*------------------------------------------------------------*/
+/*---                                                      ---*/
+/*--- Main stuff: reading of line number tables            ---*/
+/*---                                                      ---*/
+/*------------------------------------------------------------*/
+
+union any_size
+{
+          char const *c;
+         short const *s;
+           int const *i;
+  unsigned int const *ui;
+};
+
+struct startend
+{
+  unsigned int          start;
+  unsigned int          end;
+};
+
+static ULong DEBUG_SnarfLinetab(
+          DebugInfo* di,
+          IMAGE_SECTION_HEADER* sectp,
+          Char* linetab,
+          Int size
+       )
+{
+   //VG_(printf)("DEBUG_SnarfLinetab %p %p %p %d\n", di, sectp, linetab, size);
+   Int                file_segcount;
+   Char               filename[WIN32_PATH_MAX];
+   UInt               * filetab;
+   UChar              * fn;
+   Int                i;
+   Int                k;
+   UInt               * lt_ptr;
+   Int                nfile;
+   Int                nseg;
+   union any_size     pnt;
+   union any_size     pnt2;
+   struct startend    * start;
+   Int                this_seg;
+
+   Addr  bias = BIAS_FOR_LINETAB;
+   ULong n_lines_read = 0;
+
+   /*
+    * Now get the important bits.
+    */
+   pnt.c = linetab;
+   nfile = *pnt.s++;
+   nseg  = *pnt.s++;
+
+   filetab = (unsigned int *) pnt.c;
+
+   /*
+    * Now count up the number of segments in the file.
+    */
+   nseg = 0;
+   for (i = 0; i < nfile; i++) {
+      pnt2.c = linetab + filetab[i];
+      nseg += *pnt2.s;
+   }
+
+   this_seg = 0;
+   for (i = 0; i < nfile; i++) {
+      UChar *fnmstr;
+      UChar *dirstr;
+
+      /*
+       * Get the pointer into the segment information.
+       */
+      pnt2.c = linetab + filetab[i];
+      file_segcount = *pnt2.s;
+
+      pnt2.ui++;
+      lt_ptr = (unsigned int *) pnt2.c;
+      start = (struct startend *) (lt_ptr + file_segcount);
+
+      /*
+       * Now snarf the filename for all of the segments for this file.
+       */
+      fn = (UChar*) (start + file_segcount);
+      /* fn now points at a Pascal-style string, that is, the first
+         byte is the length, and the remaining up to 255 (presumably)
+         are the contents. */
+      vg_assert(WIN32_PATH_MAX >= 256);
+      VG_(memset)(filename, 0, sizeof(filename));
+      VG_(memcpy)(filename, fn + 1, *fn);
+      vg_assert(filename[ sizeof(filename)-1 ] == 0);
+      filename[(Int)*fn] = 0;
+      fnmstr = VG_(strrchr)(filename, '\\');
+      if (fnmstr == NULL)
+         fnmstr = filename;
+      else 
+         ++fnmstr;
+      k = VG_(strlen)(fnmstr);
+      dirstr = ML_(addStr)(di, filename, *fn - k);
+      fnmstr = ML_(addStr)(di, fnmstr, k);
+
+      for (k = 0; k < file_segcount; k++, this_seg++) {
+         Int linecount;
+         Int segno;
+
+         pnt2.c = linetab + lt_ptr[k];
+
+         segno = *pnt2.s++;
+         linecount = *pnt2.s++;
+
+         if ( linecount > 0 ) {
+            UInt j;
+
+            if (debug)
+               VG_(message)(Vg_UserMsg,
+                  "Adding %d lines for file %s segment %d addr=%#x end=%#x",
+                  linecount, filename, segno, start[k].start, start[k].end );
+
+            for ( j = 0; j < linecount; j++ ) {
+               Addr startaddr = bias + sectp[segno-1].VirtualAddress
+                                     + pnt2.ui[j];
+               Addr endaddr   = bias + sectp[segno-1].VirtualAddress
+                                     + ((j < (linecount - 1))
+                                           ? pnt2.ui[j+1] 
+                                           : start[k].end);
+               if (debug)
+                  VG_(message)(Vg_UserMsg,
+                     "Adding line %d addr=%#lx end=%#lx", 
+                        ((unsigned short *)(pnt2.ui + linecount))[j],
+                        startaddr, endaddr );
+                  ML_(addLineInfo)(
+                     di, fnmstr, dirstr, startaddr, endaddr,
+                     ((unsigned short *)(pnt2.ui + linecount))[j], j );
+                  n_lines_read++;
+               }
+            }
+        }
+    }
+
+    return n_lines_read;
+}
+
+
+
+/* there's a new line tab structure from MS Studio 2005 and after
+ * it's made of:
+ * DWORD        000000f4
+ * DWORD        lineblk_offset (counting bytes after this field)
+ * an array of codeview_linetab2_file structures
+ * an array (starting at <lineblk_offset>) of codeview_linetab2_block structures
+ */
+
+struct codeview_linetab2_file
+{
+    DWORD       offset;         /* offset in string table for filename */
+    WORD        unk;            /* always 0x0110... type of following
+                                   information ??? */
+    BYTE        md5[16];        /* MD5 signature of file (signature on
+                                   file's content or name ???) */
+    WORD        pad0;           /* always 0 */
+};
+
+struct codeview_linetab2_block
+{
+    DWORD       header;         /* 0x000000f2 */
+    DWORD       size_of_block;  /* next block is at # bytes after this field */
+    DWORD       start;          /* start address of function with line numbers */
+    DWORD       seg;            /* segment of function with line numbers */
+    DWORD       size;           /* size of function with line numbers */
+    DWORD       file_offset;    /* offset for accessing corresponding
+                                   codeview_linetab2_file */
+    DWORD       nlines;         /* number of lines in this block */
+    DWORD       size_lines;     /* number of bytes following for line
+                                   number information */
+    struct {
+        DWORD   offset;         /* offset (from <seg>:<start>) for line number */
+        DWORD   lineno;         /* the line number (OR:ed with
+                                   0x80000000 why ???) */
+    } l[1];                     /* actually array of <nlines> */
+};
+
+static ULong codeview_dump_linetab2(
+                DebugInfo* di,
+                Char* linetab,
+                DWORD size,
+                Char* strimage,
+                DWORD strsize,
+                Char* pfx
+             )
+{
+   DWORD       offset;
+   unsigned    i;
+   struct codeview_linetab2_block* lbh;
+   struct codeview_linetab2_file* fd;
+   //const Bool debug = False;
+
+   Addr  bias = BIAS_FOR_LINETAB2;
+   ULong n_line2s_read = 0;
+
+   if (*(const DWORD*)linetab != 0x000000f4)
+      return 0;
+   offset = *((DWORD*)linetab + 1);
+   lbh = (struct codeview_linetab2_block*)(linetab + 8 + offset);
+
+   while ((Char*)lbh < linetab + size) {
+
+      HChar *filename, *dirname;
+      Addr svma_s, svma_e;
+      if (lbh->header != 0x000000f2) {
+         /* FIXME: should also check that whole lbh fits in linetab + size */
+         if (debug)
+            VG_(printf)("%sblock end %x\n", pfx, lbh->header);
+         break;
+      }
+      if (debug)
+         VG_(printf)("%sblock from %04x:%08x-%08x (size %u) (%u lines)\n",
+                     pfx, lbh->seg, lbh->start, lbh->start + lbh->size - 1,
+                     lbh->size, lbh->nlines);
+      fd = (struct codeview_linetab2_file*)(linetab + 8 + lbh->file_offset);
+      if (debug)
+         VG_(printf)(
+            "%s  md5=%02x%02x%02x%02x%02x%02x%02x%02x"
+                    "%02x%02x%02x%02x%02x%02x%02x%02x\n",
+             pfx, fd->md5[ 0], fd->md5[ 1], fd->md5[ 2], fd->md5[ 3],
+                  fd->md5[ 4], fd->md5[ 5], fd->md5[ 6], fd->md5[ 7],
+                  fd->md5[ 8], fd->md5[ 9], fd->md5[10], fd->md5[11],
+                  fd->md5[12], fd->md5[13], fd->md5[14], fd->md5[15] );
+      /* FIXME: should check that string is within strimage + strsize */
+      if (strimage) {
+         dirname  = strimage + fd->offset;
+         filename = VG_(strrchr)(dirname, '\\');
+         if (filename == NULL) {
+            filename = ML_(addStr)(di, dirname, -1);
+            dirname  = NULL;
+         } else {
+            dirname  = ML_(addStr)(di, dirname, VG_(strlen)(dirname) 
+                                                - VG_(strlen)(filename));
+            filename = ML_(addStr)(di, filename+1, -1);
+         }
+      } else {
+         filename = ML_(addStr)(di, "???", -1);
+         dirname  = NULL;
+      }
+
+      if (debug)
+         VG_(printf)("%s  file=%s\n", pfx, filename);
+
+      for (i = 0; i < lbh->nlines; i++) {
+         if (debug)
+            VG_(printf)("%s  offset=%08x line=%d\n",
+                        pfx, lbh->l[i].offset, lbh->l[i].lineno ^ 0x80000000);
+      }
+
+      if (lbh->nlines > 1) {
+         for (i = 0; i < lbh->nlines-1; i++) {
+            svma_s = lbh->start + lbh->l[i].offset;
+            svma_e = lbh->start + lbh->l[i+1].offset-1;
+            if (debug)
+               VG_(printf)("%s  line %d: %08lx to %08lx\n",
+                           pfx, lbh->l[i].lineno ^ 0x80000000, svma_s, svma_e);
+            ML_(addLineInfo)( di, filename, dirname,
+                              bias + svma_s,
+                              bias + svma_e + 1,
+                              lbh->l[i].lineno ^ 0x80000000, 0 );
+            n_line2s_read++;
+         }
+         svma_s = lbh->start + lbh->l[ lbh->nlines-1].offset;
+         svma_e = lbh->start + lbh->size - 1;
+         if (debug)
+            VG_(printf)("%s  line %d: %08lx to %08lx\n",
+                        pfx, lbh->l[ lbh->nlines-1  ].lineno ^ 0x80000000,
+                        svma_s, svma_e);
+          ML_(addLineInfo)( di, filename, dirname,
+                            bias + svma_s,
+                            bias + svma_e + 1,
+                            lbh->l[lbh->nlines-1].lineno ^ 0x80000000, 0 );
+          n_line2s_read++;
+       }
+
+       lbh = (struct codeview_linetab2_block*)
+                ((char*)lbh + 8 + lbh->size_of_block);
+    }
+    return n_line2s_read;
+}
+
+
+/*------------------------------------------------------------*/
+/*---                                                      ---*/
+/*--- Main stuff: pdb_dump                                 ---*/
+/*---                                                      ---*/
+/*------------------------------------------------------------*/
+
+/* JRS fixme: compare with version in current Wine sources */
+static void pdb_dump( struct pdb_reader* pdb,
+                      DebugInfo* di,
+                      Addr pe_avma,
+                      Int  reloc,
+                      IMAGE_SECTION_HEADER* sectp_avma )
+{
+   Int header_size;
+
+   PDB_TYPES types;
+   PDB_SYMBOLS symbols;
+   unsigned len_modimage;
+   char *modimage;
+   char *file; 
+
+   Addr bias_for_fpo = BIAS_FOR_FPO;
+
+   ULong n_fpos_read = 0, n_syms_read = 0,
+         n_lines_read = 0, n_line2s_read = 0;
+
+   // FIXME: symbols for bare indices 1,2,3,5 in .pdb file
+
+   char* types_image   = pdb->read_file( pdb, 2, 0 );
+   char* symbols_image = pdb->read_file( pdb, 3, 0 );
+
+   /* establish filesimage and filessize.  These are only needed for
+      reading linetab2 tables, as far as I can deduce from the Wine
+      sources. */
+   char* filesimage = pdb->read_file( pdb, 12, 0);   /* FIXME: really fixed ??? */
+   UInt  filessize  = 0;
+   if (filesimage) {
+      if (*(const DWORD*)filesimage == 0xeffeeffe) {
+         filessize = *(const DWORD*)(filesimage + 8);
+      } else {
+         if (0)
+            VG_(printf)("wrong header %x expecting 0xeffeeffe\n",
+                        *(const DWORD*)filesimage);
+         ML_(dinfo_free)( (void*)filesimage);
+         filesimage = NULL;
+      }
+   }
+
+   if (VG_(clo_verbosity) > 0) {
+      VG_(message)(Vg_DebugMsg,
+                   "PDB_READER:");
+      VG_(message)(Vg_DebugMsg,
+                   "   BIAS_FOR_SYMBOLS  = %#08lx  %s",
+                   (PtrdiffT)BIAS_FOR_SYMBOLS, VG_STRINGIFY(BIAS_FOR_SYMBOLS));
+      VG_(message)(Vg_DebugMsg,
+                   "   BIAS_FOR_LINETAB  = %#08lx  %s",
+                   (PtrdiffT)BIAS_FOR_LINETAB, VG_STRINGIFY(BIAS_FOR_LINETAB));
+      VG_(message)(Vg_DebugMsg,
+                   "   BIAS_FOR_LINETAB2 = %#08lx  %s",
+                   (PtrdiffT)BIAS_FOR_LINETAB2, VG_STRINGIFY(BIAS_FOR_LINETAB2));
+      VG_(message)(Vg_DebugMsg,
+                   "   BIAS_FOR_FPO      = %#08lx  %s",
+                   (PtrdiffT)BIAS_FOR_FPO, VG_STRINGIFY(BIAS_FOR_FPO));
+      VG_(message)(Vg_DebugMsg,
+                   "   RELOC             = %#08lx",
+                   (PtrdiffT)reloc);
+   }
+
+   /* Since we just use the FPO data without reformatting, at least
+      do a basic sanity check on the struct layout. */
+   vg_assert(sizeof(FPO_DATA) == 16);
+   if (di->text_present) { 
+      /* only load FPO if there's text present (otherwise it's
+         meaningless?) */
+      unsigned sz = 0;
+      di->fpo = pdb->read_file( pdb, 5, &sz );
+      di->fpo_size = sz;
+   } else {
+      vg_assert(di->fpo == NULL);
+      vg_assert(di->fpo_size == 0);
+   }
+
+   if (di->fpo) {
+      Word i;
+      Addr min_svma = ~(Addr)0;
+      Addr max_svma = (Addr)0;
+      vg_assert(sizeof(di->fpo[0]) == 16);
+      di->fpo_size /= sizeof(di->fpo[0]);
+
+      /* Sanity-check the table, and find the min and max avmas. */
+      for (i = 0; i < di->fpo_size; i++) {
+         /* If any of the following assertions fail, we'll need to add
+            an extra pass to tidy up the FPO info -- make them be in
+            order and non-overlapping, since in-orderness and
+            non-overlappingness are required for safe use of
+            ML_(search_one_fpotab). */
+         vg_assert(di->fpo[i].cbProcSize > 0);
+         if (i > 0) {
+            Bool ok;
+            Bool dup
+               = di->fpo[i-1].ulOffStart == di->fpo[i].ulOffStart
+                 && di->fpo[i-1].cbProcSize == di->fpo[i].cbProcSize;
+            /* tolerate exact duplicates -- I think they are harmless
+               w.r.t. termination properties of the binary search in
+               ML_(search_one_fpotab). */
+            if (dup)
+               continue;
+
+            ok = di->fpo[i-1].ulOffStart + di->fpo[i-1].cbProcSize 
+                 <= di->fpo[i].ulOffStart;
+            if (1 && !ok)
+               VG_(printf)("%#x +%d  then  %#x +%d\n",
+                           di->fpo[i-1].ulOffStart, di->fpo[i-1].cbProcSize,
+                           di->fpo[i-0].ulOffStart, di->fpo[i-0].cbProcSize );
+            vg_assert(ok);
+         }
+         /* Update min/max limits as we go along. */
+         if (di->fpo[i].ulOffStart < min_svma)
+            min_svma = di->fpo[i].ulOffStart;
+         if (di->fpo[i].ulOffStart + di->fpo[i].cbProcSize - 1 > max_svma)
+            max_svma = di->fpo[i].ulOffStart + di->fpo[i].cbProcSize - 1;
+      }
+      /* Now bias the table.  This can't be done in the same pass as
+         the sanity check, hence a second loop. */
+      for (i = 0; i < di->fpo_size; i++) {
+         di->fpo[i].ulOffStart += bias_for_fpo;
+      }
+
+      /* And record min/max */
+      vg_assert(min_svma <= max_svma); /* should always hold */
+
+      di->fpo_minavma = min_svma + bias_for_fpo;
+      di->fpo_maxavma = max_svma + bias_for_fpo;
+
+      /* biasing shouldn't cause wraparound (?!) */
+      vg_assert(di->fpo_minavma <= di->fpo_maxavma);
+
+      if (0) {
+         VG_(printf)("XXXXXXXXX min/max svma %#lx %#lx\n",
+                     min_svma, max_svma);
+         VG_(printf)("XXXXXXXXX min/max avma %#lx %#lx\n",
+                     di->fpo_minavma, di->fpo_maxavma);
+      }
+
+      n_fpos_read += (ULong)di->fpo_size;
+   }
+
+   pdb_convert_types_header( &types, types_image );
+   switch ( types.version ) {
+      case 19950410:      /* VC 4.0 */
+      case 19951122:
+      case 19961031:      /* VC 5.0 / 6.0 */
+      case 20040203:      /* VC 7.0  FIXME??  */
+         break;
+      default:
+         if (VG_(clo_verbosity) > 1)
+            VG_(message)(Vg_UserMsg, "Unknown .pdb type info version %ld\n",
+                                     types.version );
+   }
+
+   header_size = 0;
+   pdb_convert_symbols_header( &symbols, &header_size, symbols_image );
+   switch ( symbols.version ) {
+      case 0:            /* VC 4.0 */
+      case 19960307:     /* VC 5.0 */
+      case 19970606:     /* VC 6.0 */
+      case 19990903:     /* VC 7.0  FIXME?? */
+         break;
+      default:
+         if (VG_(clo_verbosity) > 1)
+            VG_(message)(Vg_UserMsg, "Unknown .pdb symbol info version %ld\n",
+                                     symbols.version );
+   }
+
+   /*
+    * Read global symbol table
+    */
+   modimage = pdb->read_file( pdb, symbols.gsym_file, &len_modimage );
+   if (modimage) {
+      if (VG_(clo_verbosity) > 1)
+         VG_(message)(Vg_UserMsg, "Reading global symbols\n" );
+      DEBUG_SnarfCodeView( di, sectp_avma, modimage, 0, len_modimage );
+      ML_(dinfo_free)( (void*)modimage );
+   }
+
+   /*
+    * Read per-module symbol / linenumber tables
+    */
+   file = symbols_image + header_size;
+   while ( file - symbols_image < header_size + symbols.module_size ) {
+      int file_nr, file_index, symbol_size, lineno_size;
+      char *file_name;
+
+      if ( symbols.version < 19970000 ) {
+         PDB_SYMBOL_FILE *sym_file = (PDB_SYMBOL_FILE *) file;
+         file_nr     = sym_file->file;
+         file_name   = sym_file->filename;
+         file_index  = sym_file->range.index;
+         symbol_size = sym_file->symbol_size;
+         lineno_size = sym_file->lineno_size;
+      } else {
+         PDB_SYMBOL_FILE_EX *sym_file = (PDB_SYMBOL_FILE_EX *) file;
+         file_nr     = sym_file->file;
+         file_name   = sym_file->filename;
+         file_index  = sym_file->range.index;
+         symbol_size = sym_file->symbol_size;
+         lineno_size = sym_file->lineno_size;
+      }
+
+      modimage = pdb->read_file( pdb, file_nr, 0 );
+      if (modimage) {
+         Int total_size;
+         if (0) VG_(printf)("lineno_size %d symbol_size %d\n",
+                            lineno_size, symbol_size );
+
+         total_size = pdb_get_file_size(pdb, file_nr);
+
+         if (symbol_size) {
+            if (VG_(clo_verbosity) > 1)
+               VG_(message)(Vg_UserMsg, "Reading symbols for %s", file_name );
+            n_syms_read 
+               += DEBUG_SnarfCodeView( di, sectp_avma, modimage,
+                                           sizeof(unsigned long),
+                                           symbol_size );
+         }
+
+         if (lineno_size) {
+            if (VG_(clo_verbosity) > 1)
+               VG_(message)(Vg_UserMsg, "Reading lines for %s", file_name );
+            n_lines_read
+               += DEBUG_SnarfLinetab( di, sectp_avma,
+                                          modimage + symbol_size, lineno_size );
+         }
+
+         /* anyway, lineno_size doesn't see to really be the size of
+          * the line number information, and it's not clear yet when
+          * to call for linetab2...
+          */
+         n_line2s_read
+            += codeview_dump_linetab2(
+                  di, (char*)modimage + symbol_size + lineno_size,
+                      total_size - (symbol_size + lineno_size),
+                  /* if filesimage is NULL, pass that directly onwards
+                     to codeview_dump_linetab2, so it knows not to
+                     poke around in there. */
+                  filesimage ? filesimage + 12 : NULL,
+                  filessize, "        "
+               );
+
+         ML_(dinfo_free)( (void*)modimage );
+      }
+
+      file_name += VG_(strlen)(file_name) + 1;
+      file = (char *)( 
+                (unsigned long)(file_name
+                                + VG_(strlen)(file_name) + 1 + 3) & ~3 );
+   }
+
+   /*
+    * Cleanup
+    */
+   if ( symbols_image ) ML_(dinfo_free)( symbols_image );
+   if ( types_image ) ML_(dinfo_free)( types_image );
+   if ( pdb->u.jg.toc ) ML_(dinfo_free)( pdb->u.jg.toc );
+
+   if (VG_(clo_verbosity) > 0) {
+      VG_(message)(Vg_DebugMsg,"   # symbols read = %llu", n_syms_read );
+      VG_(message)(Vg_DebugMsg,"   # lines   read = %llu", n_lines_read );
+      VG_(message)(Vg_DebugMsg,"   # line2s  read = %llu", n_line2s_read );
+      VG_(message)(Vg_DebugMsg,"   # fpos    read = %llu", n_fpos_read );
+   }
+}
+
+
+/*------------------------------------------------------------*/
+/*---                                                      ---*/
+/*--- TOP LEVEL for PDB reading                            ---*/
+/*---                                                      ---*/
+/*------------------------------------------------------------*/
+
+Bool ML_(read_pdb_debug_info)(
+        DebugInfo* di,
+        Addr       obj_avma,
+        PtrdiffT   unknown_purpose__reloc,
+        void*      pdbimage,
+        SizeT      n_pdbimage,
+        Char*      pdbname,
+        ULong      pdbmtime
+     )
+{
+   Char*    pe_seg_avma;
+   Int      i;
+   Addr     mapped_avma, mapped_end_avma;
+   unsigned signature;
+   void*    hdr;
+   struct pdb_reader     reader;
+   IMAGE_DOS_HEADER*     dos_avma;
+   IMAGE_NT_HEADERS*     ntheaders_avma;
+   IMAGE_SECTION_HEADER* sectp_avma;
+   IMAGE_SECTION_HEADER* pe_sechdr_avma;
+
+   if (VG_(clo_verbosity) > 1)
+       VG_(message)(Vg_UserMsg, "Processing PDB file %s ", pdbname );
+
+   dos_avma = (IMAGE_DOS_HEADER *)obj_avma;
+   if (dos_avma->e_magic != IMAGE_DOS_SIGNATURE)
+      return False;
+
+   ntheaders_avma
+      = (IMAGE_NT_HEADERS *)((Char*)dos_avma + dos_avma->e_lfanew);
+   if (ntheaders_avma->Signature != IMAGE_NT_SIGNATURE)
+      return False;
+
+   sectp_avma
+      = (IMAGE_SECTION_HEADER *)(
+           (Char*)ntheaders_avma
+           + OFFSET_OF(IMAGE_NT_HEADERS, OptionalHeader)
+           + ntheaders_avma->FileHeader.SizeOfOptionalHeader
+        );
+
+   /* JRS: this seems like something of a hack. */
+   di->soname = ML_(dinfo_strdup)("di.readpdb.rpdi.1", pdbname);
+
+   /* someone (ie WINE) is loading a Windows PE format object.  we
+      need to use its details to determine which area of memory is
+      executable... */
+   pe_seg_avma
+      = (Char*)ntheaders_avma
+        + OFFSET_OF(IMAGE_NT_HEADERS, OptionalHeader)
+        + ntheaders_avma->FileHeader.SizeOfOptionalHeader;
+
+   di->rx_map_avma = (Addr)obj_avma;
+
+   /* Iterate over PE(?) headers.  Try to establish the text_bias,
+      that's all we really care about. */
+   for ( i = 0;
+         i < ntheaders_avma->FileHeader.NumberOfSections;
+         i++, pe_seg_avma += sizeof(IMAGE_SECTION_HEADER) ) {
+      pe_sechdr_avma = (IMAGE_SECTION_HEADER *)pe_seg_avma;
+
+      if (VG_(clo_verbosity) > 1)
+         VG_(message)(Vg_UserMsg,
+                      "  Scanning PE section %s at avma %p svma %#lx",
+                      pe_sechdr_avma->Name, pe_seg_avma,
+                      pe_sechdr_avma->VirtualAddress);
+
+      if (pe_sechdr_avma->Characteristics & IMAGE_SCN_MEM_DISCARDABLE)
+         continue;
+
+      mapped_avma     = (Addr)obj_avma + pe_sechdr_avma->VirtualAddress;
+      mapped_end_avma = mapped_avma + pe_sechdr_avma->Misc.VirtualSize;
+      if (VG_(clo_verbosity) > 1)
+         VG_(message)(Vg_DebugMsg,
+             "   ::: mapped_avma is %#lx", mapped_avma);
+
+      if (pe_sechdr_avma->Characteristics & IMAGE_SCN_CNT_CODE) {
+         di->have_rx_map = True;
+         if (di->rx_map_avma == 0) {
+            di->rx_map_avma = mapped_avma;
+         }
+         if (di->rx_map_size==0) {
+            di->rx_map_foff = pe_sechdr_avma->PointerToRawData;
+         }
+         di->text_present = True;
+         if (di->text_avma==0) {
+            di->text_avma = mapped_avma;
+         }
+         di->text_size   += pe_sechdr_avma->Misc.VirtualSize;
+         di->rx_map_size += pe_sechdr_avma->Misc.VirtualSize;
+      }
+      else if (pe_sechdr_avma->Characteristics 
+               & IMAGE_SCN_CNT_INITIALIZED_DATA) {
+         di->have_rw_map = True;
+         if (di->rw_map_avma == 0) {
+            di->rw_map_avma = mapped_avma;
+         }
+         if (di->rw_map_size==0) {
+            di->rw_map_foff = pe_sechdr_avma->PointerToRawData;
+         }
+         di->data_present = True;
+         if (di->data_avma==0) {
+            di->data_avma = mapped_avma;
+         }
+         di->rw_map_size += pe_sechdr_avma->Misc.VirtualSize;
+         di->data_size   += pe_sechdr_avma->Misc.VirtualSize;
+      }
+      else if (pe_sechdr_avma->Characteristics
+               & IMAGE_SCN_CNT_UNINITIALIZED_DATA) {
+         di->bss_present = True;
+         di->bss_avma = mapped_avma;
+         di->bss_size = pe_sechdr_avma->Misc.VirtualSize;
+      }
+
+      mapped_avma     = VG_PGROUNDDN(mapped_avma);
+      mapped_end_avma = VG_PGROUNDUP(mapped_end_avma);
+
+      /* Urr.  These tests are bogus; ->rx_map_avma is not necessarily
+         the start of the text section. */
+      if ((1 /*VG_(needs).data_syms*/ 
+           || (pe_sechdr_avma->Characteristics & IMAGE_SCN_CNT_CODE))
+          && mapped_avma >= di->rx_map_avma
+          && mapped_avma <= (di->rx_map_avma+di->text_size)
+          && mapped_end_avma > (di->rx_map_avma+di->text_size)) {
+         UInt newsz = mapped_end_avma - di->rx_map_avma;
+         if (newsz > di->text_size) {
+            /* extending the mapping is always needed for PE files
+               under WINE */
+            di->text_size = newsz;
+            di->rx_map_size = newsz;
+         }
+      }
+   }
+
+   if (di->have_rx_map && di->have_rw_map && !di->have_dinfo) {
+      vg_assert(di->filename);
+      TRACE_SYMTAB("\n");
+      TRACE_SYMTAB("------ start PE OBJECT with PDB INFO "
+                   "---------------------\n");
+      TRACE_SYMTAB("------ name = %s\n", di->filename);
+      TRACE_SYMTAB("\n");
+   }
+
+   if (di->text_present) {
+      di->text_bias = di->text_avma - di->text_svma;
+   } else {
+      di->text_bias = 0;
+   }
+
+   if (VG_(clo_verbosity) > 1) {
+      VG_(message)(Vg_DebugMsg,
+                   "rx_map: avma %#lx size %7lu foff %lu\n",
+                   di->rx_map_avma, di->rx_map_size, di->rx_map_foff);
+      VG_(message)(Vg_DebugMsg,
+                   "rw_map: avma %#lx size %7lu foff %lu\n",
+                   di->rw_map_avma, di->rw_map_size, di->rw_map_foff);
+
+      VG_(message)(Vg_DebugMsg,
+                   "  text: avma %#lx svma %#lx size %7lu bias %#lx\n",
+                   di->text_avma, di->text_svma, di->text_size, di->text_bias);
+   }
+
+   /*
+    * Read in TOC and well-known files
+    */
+   signature = 0;
+   hdr = find_pdb_header( pdbimage, &signature );
+   if (0==hdr)
+      return False; /* JRS: significance? no pdb header? */
+
+   VG_(memset)(&reader, 0, sizeof(reader));
+   reader.u.jg.header = hdr;
+
+   if (0==VG_(strncmp)((char const *)&signature, "DS\0\0", 4)) {
+      struct PDB_DS_ROOT* root;
+      pdb_ds_init( &reader, pdbimage, n_pdbimage );
+      root = reader.read_file( &reader, 1, 0 );
+      if (root) {
+         pdb_check_root_version_and_timestamp(
+            pdbname, pdbmtime, root->version, root->TimeDateStamp );
+         ML_(dinfo_free)( root );
+      }
+      pdb_dump( &reader, di, obj_avma, unknown_purpose__reloc, sectp_avma );
+   }
+   else
+   if (0==VG_(strncmp)((char const *)&signature, "JG\0\0", 4)) {
+      struct PDB_JG_ROOT* root;
+      pdb_jg_init( &reader, pdbimage, n_pdbimage );
+      root = reader.read_file( &reader, 1, 0 );
+      if (root) {
+         pdb_check_root_version_and_timestamp(
+            pdbname, pdbmtime, root->version, root->TimeDateStamp);
+         ML_(dinfo_free)( root );
+      }
+      pdb_dump( &reader, di, obj_avma, unknown_purpose__reloc, sectp_avma );
+   }
+
+   if (1) {
+      TRACE_SYMTAB("\n------ Canonicalising the "
+                   "acquired info ------\n");
+      /* prepare read data for use */
+      ML_(canonicaliseTables)( di );
+      /* notify m_redir about it */
+      TRACE_SYMTAB("\n------ Notifying m_redir ------\n");
+      VG_(redir_notify_new_DebugInfo)( di );
+      /* Note that we succeeded */
+      di->have_dinfo = True;
+   } else {
+      TRACE_SYMTAB("\n------ PE with PDB reading failed ------\n");
+      /* Something went wrong (eg. bad ELF file).  Should we delete
+         this DebugInfo?  No - it contains info on the rw/rx
+         mappings, at least. */
+   }
+
+   TRACE_SYMTAB("\n");
+   TRACE_SYMTAB("------ name = %s\n", di->filename);
+   TRACE_SYMTAB("------ end PE OBJECT with PDB INFO "
+                "--------------------\n");
+   TRACE_SYMTAB("\n");
+
+   return True;
+}
+
+
+/*--------------------------------------------------------------------*/
+/*--- end                                                readpdb.c ---*/
+/*--------------------------------------------------------------------*/
index 6a427b6ad273fa162a0a29bce275af2b346c740b..f71099d659a91ada406a88069f143dc1a03cf89f 100644 (file)
@@ -1509,6 +1509,31 @@ Word ML_(search_one_cfitab) ( struct _DebugInfo* di, Addr ptr )
 }
 
 
+/* Find a FPO-table index containing the specified pointer, or -1
+   if not found.  Binary search.  */
+
+Word ML_(search_one_fpotab) ( struct _DebugInfo* di, Addr ptr )
+{
+   Addr const addr = ptr - di->rx_map_avma;
+   Addr a_mid_lo, a_mid_hi;
+   Word mid, size,
+        lo = 0,
+        hi = di->fpo_size-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 = di->fpo[mid].ulOffStart;
+      size     = di->fpo[mid].cbProcSize;
+      a_mid_hi = a_mid_lo + size - 1;
+      vg_assert(a_mid_hi >= a_mid_lo);
+      if (addr < a_mid_lo) { hi = mid-1; continue; }
+      if (addr > a_mid_hi) { lo = mid+1; continue; }
+      vg_assert(addr >= a_mid_lo && addr <= a_mid_hi);
+      return mid;
+   }
+}
+
 /*--------------------------------------------------------------------*/
 /*--- end                                                          ---*/
 /*--------------------------------------------------------------------*/
index 8860a0a3a05d8ea868ff2dff8f81b184a32df56b..e1e0f1ce1ae1bb6dd007559d8a4b9bed0e7b4fbb 100644 (file)
 #include "pub_core_tooliface.h"
 #include "pub_core_translate.h"     // For VG_(translate)()
 #include "pub_core_transtab.h"
+#include "pub_core_debuginfo.h"     // VG_(di_notify_pdb_debuginfo)
 #include "priv_sema.h"
 #include "pub_core_scheduler.h"     // self
 
-/* #include "pub_core_debuginfo.h" */   // DEBUGGING HACK ONLY
-
 
 /* ---------------------------------------------------------------------
    Types and globals for the scheduler.
@@ -1401,6 +1400,11 @@ void do_client_request ( ThreadId tid )
          SET_CLREQ_RETVAL( tid, VG_(get_n_errs_found)() );
          break;
 
+      case VG_USERREQ__LOAD_PDB_DEBUGINFO:
+         VG_(di_notify_pdb_debuginfo)( arg[1], arg[2], arg[3], arg[4] );
+         SET_CLREQ_RETVAL( tid, 0 );     /* return value is meaningless */
+         break;
+
       default:
         if (os_client_request(tid, arg)) {
            // do nothing, os_client_request() handled it
index 86decb5c57f67cfc0617bc5dba4097994106ee9a..aeb98ad2d7669d0c1caae003151522c095bd59f5 100644 (file)
@@ -184,6 +184,17 @@ UInt VG_(get_StackTrace_wrk) ( ThreadId tid_if_known,
          continue;
       }
 
+      /* And, similarly, try for MSVC FPO unwind info. */
+      if ( VG_(use_FPO_info)( &ip, &sp, &fp, fp_min, fp_max ) ) {
+         if (sps) sps[i] = sp;
+         if (fps) fps[i] = fp;
+         ips[i++] = ip;
+         if (debug)
+            VG_(printf)("     ipsC[%d]=0x%08lx\n", i-1, ips[i-1]);
+         ip = ip - 1;
+         continue;
+      }
+
       /* No luck.  We have to give up. */
       break;
    }
index 2695022eed75b70855834f4610d2cf3f843f1080..6f1c944309c514c07e6de77053ec116880679ea9 100644 (file)
@@ -62,6 +62,11 @@ extern ULong VG_(di_notify_mmap)( Addr a, Bool allow_SkFileV );
 extern void VG_(di_notify_munmap)( Addr a, SizeT len );
 
 extern void VG_(di_notify_mprotect)( Addr a, SizeT len, UInt prot );
+
+/* this should really return ULong, as per VG_(di_notify_mmap). */
+extern void VG_(di_notify_pdb_debuginfo)( Int fd, Addr avma,
+                                          SizeT total_size,
+                                          PtrdiffT unknown_purpose__reloc );
 #endif
 
 #if defined(VGO_aix5)
@@ -107,6 +112,13 @@ extern Bool VG_(use_CF_info) ( /*MOD*/Addr* ipP,
                                Addr min_accessible,
                                Addr max_accessible );
 
+/* Use MSVC FPO data to do one step of stack unwinding. */
+extern Bool VG_(use_FPO_info) ( /*MOD*/Addr* ipP,
+                                /*MOD*/Addr* spP,
+                                /*MOD*/Addr* fpP,
+                                Addr min_accessible,
+                                Addr max_accessible );
+
 /* ppc64-linux only: find the TOC pointer (R2 value) that should be in
    force at the entry point address of the function containing
    guest_code_addr.  Returns 0 if not known. */
index c1d704bf237a504448151c5819f741cd20f0ee26..5b4b8af791082211cec364f533d3c9107dfaaffd 100644 (file)
@@ -3625,7 +3625,10 @@ typedef
           /* Stack support. */
           VG_USERREQ__STACK_REGISTER   = 0x1501,
           VG_USERREQ__STACK_DEREGISTER = 0x1502,
-          VG_USERREQ__STACK_CHANGE     = 0x1503
+          VG_USERREQ__STACK_CHANGE     = 0x1503,
+
+          /* Wine support */
+          VG_USERREQ__LOAD_PDB_DEBUGINFO = 0x1601
    } Vg_ClientRequest;
 
 #if !defined(__GNUC__)
@@ -3915,6 +3918,14 @@ VALGRIND_PRINTF_BACKTRACE(const char *format, ...)
                                id, start, end, 0, 0);             \
    }
 
+/* Load PDB debug info for Wine PE image_map. */
+#define VALGRIND_LOAD_PDB_DEBUGINFO(fd, ptr, total_size, delta)   \
+   {unsigned int _qzz_res;                                        \
+    VALGRIND_DO_CLIENT_REQUEST(_qzz_res, 0,                       \
+                               VG_USERREQ__LOAD_PDB_DEBUGINFO,    \
+                               fd, ptr, total_size, delta, 0);    \
+   }
+
 
 #undef PLAT_x86_linux
 #undef PLAT_amd64_linux