]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Try to find the PDB file for a given PE file by the totally kludgey
authorJulian Seward <jseward@acm.org>
Fri, 12 Feb 2010 12:12:39 +0000 (12:12 +0000)
committerJulian Seward <jseward@acm.org>
Fri, 12 Feb 2010 12:12:39 +0000 (12:12 +0000)
method of doing "strings file.dll | egrep '\.pdb|\.PDB'".

Distantly derived from a patch by leiz@ucla.edu.  Fixes #222902,
although I still would prefer to do this the proper way, by parsing
the PE file properly.

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

coregrind/m_debuginfo/debuginfo.c
coregrind/m_debuginfo/priv_readpdb.h
coregrind/m_debuginfo/readpdb.c

index 9f27d501e11e1c26173ce1443a3bbffd2566b822..9727cbce5b5028aad5159e9ece3b814652c0c6dc 100644 (file)
@@ -38,6 +38,7 @@
 #include "pub_core_libcassert.h"
 #include "pub_core_libcprint.h"
 #include "pub_core_libcfile.h"
+#include "pub_core_libcproc.h"   // VG_(getenv)
 #include "pub_core_seqmatch.h"
 #include "pub_core_options.h"
 #include "pub_core_redir.h"      // VG_(redir_notify_{new,delete}_SegInfo)
@@ -896,7 +897,7 @@ void VG_(di_notify_pdb_debuginfo)( Int fd_obj, Addr avma_obj,
                                    SizeT total_size,
                                    PtrdiffT unknown_purpose__reloc )
 {
-   Int    r, sz_exename;
+   Int    i, r, sz_exename;
    ULong  obj_mtime, pdb_mtime;
    Char   exename[VKI_PATH_MAX];
    Char*  pdbname = NULL;
@@ -939,27 +940,86 @@ void VG_(di_notify_pdb_debuginfo)( Int fd_obj, Addr avma_obj,
       VG_(message)(Vg_UserMsg, "LOAD_PDB_DEBUGINFO: objname: %s\n", 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");
+   /* Try to get the PDB file name from the executable. */
+   pdbname = ML_(find_name_of_pdb_file)(exename);
+   if (pdbname) {
+      vg_assert(VG_(strlen)(pdbname) >= 5); /* 5 = strlen("X.pdb") */
+      /* So we successfully extracted a name from the PE file.  But it's
+         likely to be of the form
+            e:\foo\bar\xyzzy\wibble.pdb
+         and we need to change it into something we can actually open
+         in Wine-world, which basically means turning it into
+            $HOME/.wine/drive_e/foo/bar/xyzzy/wibble.pdb
+         We also take into account $WINEPREFIX, if it is set.
+         For the moment, if the name isn't fully qualified, just forget it
+         (we'd have to root around to find where the pdb actually is)
+      */
+      /* Change all the backslashes to forward slashes */
+      for (i = 0; pdbname[i]; i++) {
+         if (pdbname[i] == '\\')
+            pdbname[i] = '/';
+      }
+      Bool is_quald
+         = ('a' <= VG_(tolower)(pdbname[0]) && VG_(tolower)(pdbname[0]) <= 'z')
+           && pdbname[1] == ':'
+           && pdbname[2] == '/';
+      HChar* home = VG_(getenv)("HOME");
+      HChar* wpfx = VG_(getenv)("WINEPREFIX");
+      if (is_quald && wpfx) {
+         /* Change e:/foo/bar/xyzzy/wibble.pdb
+                to $WINEPREFIX/drive_e/foo/bar/xyzzy/wibble.pdb
+         */
+         Int mashedSzB = VG_(strlen)(pdbname) + VG_(strlen)(wpfx) + 50/*misc*/;
+         HChar* mashed = ML_(dinfo_zalloc)("di.debuginfo.dnpdi.1", mashedSzB);
+         VG_(sprintf)(mashed, "%s/drive_%c%s",
+                      wpfx, pdbname[0], &pdbname[2]);
+         vg_assert(mashed[mashedSzB-1] == 0);
+         ML_(dinfo_free)(pdbname);
+         pdbname = mashed;
+      }
+      else if (is_quald && home && !wpfx) {
+         /* Change e:/foo/bar/xyzzy/wibble.pdb
+                to $HOME/.wine/drive_e/foo/bar/xyzzy/wibble.pdb
+         */
+         Int mashedSzB = VG_(strlen)(pdbname) + VG_(strlen)(home) + 50/*misc*/;
+         HChar* mashed = ML_(dinfo_zalloc)("di.debuginfo.dnpdi.2", mashedSzB);
+         VG_(sprintf)(mashed, "%s/.wine/drive_%c%s",
+                      home, pdbname[0], &pdbname[2]);
+         vg_assert(mashed[mashedSzB-1] == 0);
+         ML_(dinfo_free)(pdbname);
+         pdbname = mashed;
+      } else {
+         /* It's not a fully qualified path, or neither $HOME nor $WINE
+            are set (strange).  Give up. */
+         ML_(dinfo_free)(pdbname);
+         pdbname = NULL;
+      }
+   }
 
-   vg_assert(pdbname[sz_exename+5-1] == 0);
+   /* Try s/exe/pdb/ if we don't have a valid pdbname. */
+   if (!pdbname) {
+      /* 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);
index 3fb84d7be99e8879c05dceb326788283ea2728d2..f5dc310cdfd39884dd7ca39c8a2e2a79c77cfb07 100644 (file)
@@ -48,6 +48,12 @@ extern Bool ML_(read_pdb_debug_info)(
                ULong      pdbmtime
             );
 
+/* Finds the name of the PDB file that's embedded with the specified
+   PE file, or NULL on failure.  Caller deallocates with
+   ML_(dinfo_free). */
+HChar* ML_(find_name_of_pdb_file)( HChar* pename );
+
+
 #endif /* ndef __PRIV_READPDB_H */
 
 #endif // defined(VGO_linux) || defined(VGO_darwin)
index ed08504fae988b40a6854bdf46753c70c891cd53..1f8101c5dd143ed071dc03fc54dc27ebc8107fcc 100644 (file)
@@ -42,7 +42,9 @@
 #include "pub_core_vki.h"          // VKI_PAGE_SIZE
 #include "pub_core_libcbase.h"
 #include "pub_core_libcassert.h"
+#include "pub_core_libcfile.h"     // VG_(open), read, lseek, close
 #include "pub_core_libcprint.h"
+#include "pub_core_libcproc.h"     // VG_(getpid), system
 #include "pub_core_options.h"      // VG_(clo_verbosity)
 #include "pub_core_xarray.h"       // keeps priv_storage.h happy
 #include "pub_core_redir.h"
@@ -2193,6 +2195,8 @@ static void pdb_dump( struct pdb_reader* pdb,
 /*---                                                      ---*/
 /*------------------------------------------------------------*/
 
+/* Read line, symbol and unwind information from a PDB file.
+*/
 Bool ML_(read_pdb_debug_info)(
         DebugInfo* di,
         Addr       obj_avma,
@@ -2422,6 +2426,123 @@ Bool ML_(read_pdb_debug_info)(
    return True;
 }
 
+
+/* Examine a PE file to see if it states the path of an associated PDB
+   file; if so return that.  Caller must deallocate with
+   ML_(dinfo_free).
+*/
+
+HChar* ML_(find_name_of_pdb_file)( HChar* pename )
+{
+   /* This is a giant kludge, of the kind "you did WTF?!?", but it
+      works. */
+   Bool   do_cleanup = False;
+   HChar  tmpname[100], tmpnameroot[50];
+   Int    fd, r;
+   HChar* res = NULL;
+
+   if (!pename)
+      goto out;
+
+   fd = -1;
+   VG_(memset)(tmpnameroot, 0, sizeof(tmpnameroot));
+   VG_(sprintf)(tmpnameroot, "petmp%d", VG_(getpid)());
+   VG_(memset)(tmpname, 0, sizeof(tmpname));
+   fd = VG_(mkstemp)( tmpnameroot, tmpname );
+   if (fd == -1) {
+      VG_(message)(Vg_UserMsg,
+                   "Find PDB file: Can't create /tmp file %s\n", tmpname);
+      goto out;
+   }
+   do_cleanup = True;
+
+   /* Make up the command to run, essentially:
+      sh -c "strings (pename) | egrep '\.pdb|\.PDB' > (tmpname)"
+   */
+   HChar* sh      = "/bin/sh";
+   HChar* strings = "/usr/bin/strings";
+   HChar* egrep   = "/usr/bin/egrep";
+
+   /* (sh) -c "(strings) (pename) | (egrep) 'pdb' > (tmpname) */
+   Int cmdlen = VG_(strlen)(strings) + VG_(strlen)(pename)
+                + VG_(strlen)(egrep) + VG_(strlen)(tmpname)
+                + 100/*misc*/;
+   HChar* cmd = ML_(dinfo_zalloc)("di.readpe.fnopf.cmd", cmdlen);
+   vg_assert(cmd);
+   VG_(sprintf)(cmd, "%s -c \"%s %s | %s '\\.pdb|\\.PDB' >> %s\"",
+                     sh, strings, pename, egrep, tmpname);
+   vg_assert(cmd[cmdlen-1] == 0);
+   if (0) VG_(printf)("QQQQQQQQ: %s\n", cmd);
+
+   r = VG_(system)( cmd );
+   if (r) {
+      VG_(message)(Vg_DebugMsg,
+                   "Find PDB file: Command failed:\n   %s\n", cmd);
+      goto out;
+   }
+
+   /* Find out how big the file is, and get it aboard. */
+   struct vg_stat stat_buf;
+   VG_(memset)(&stat_buf, 0, sizeof(stat_buf));
+
+   SysRes sr = VG_(stat)(tmpname, &stat_buf);
+   if (sr_isError(sr)) {
+      VG_(umsg)("Find PDB file: can't stat %s\n", tmpname);
+      goto out;
+   }
+
+   Int szB = (Int)stat_buf.size;
+   if (szB == 0) {
+      VG_(umsg)("Find PDB file: %s is empty\n", tmpname);
+      goto out;
+   }
+   /* 6 == strlen("X.pdb\n") */
+   if (szB < 6 || szB > 1024/*let's say*/) {
+      VG_(umsg)("Find PDB file: %s has implausible size %d\n",
+                tmpname, szB);
+      goto out;
+   }
+
+   HChar* pdbname = ML_(dinfo_zalloc)("di.readpe.fnopf.pdbname", szB + 1);
+   vg_assert(pdbname);
+   pdbname[szB] = 0;
+
+   Int nread = VG_(read)(fd, pdbname, szB);
+   if (nread != szB) {
+      VG_(umsg)("Find PDB file: read of %s failed\n", tmpname);
+      goto out;
+   }
+   vg_assert(pdbname[szB] == 0);
+
+   /* Check we've got something remotely sane -- must have one dot and
+      one \n in it, and the \n must be at the end */
+   Bool saw_dot = False;
+   Int  saw_n_crs = 0;
+   Int  i;
+   for (i = 0; pdbname[i]; i++) {
+      if (pdbname[i] == '.')  saw_dot = True;
+      if (pdbname[i] == '\n') saw_n_crs++;
+   }
+   if (!saw_dot || saw_n_crs != 1 || pdbname[szB-1] != '\n') {
+      VG_(umsg)("Find PDB file: can't make sense of: %s\n", pdbname);
+      goto out;
+   }
+   /* Change the \n to a terminating zero, so we have a "normal" string */
+   pdbname[szB-1] = 0;
+
+   if (0) VG_(printf)("QQQQQQQQ: got %s\n", pdbname);
+
+   res = pdbname;
+   goto out;
+
+  out:
+   if (do_cleanup) {
+      VG_(close)(fd);
+      VG_(unlink)( tmpname );
+   }
+   return res;
+}
+
 #endif // defined(VGO_linux) || defined(VGO_darwin)
 
 /*--------------------------------------------------------------------*/