From: Julian Seward Date: Fri, 12 Feb 2010 12:12:39 +0000 (+0000) Subject: Try to find the PDB file for a given PE file by the totally kludgey X-Git-Tag: svn/VALGRIND_3_6_0~385 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5686e8d89d931391aaaf44cf2fc903d56c86fe60;p=thirdparty%2Fvalgrind.git Try to find the PDB file for a given PE file by the totally kludgey 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 --- diff --git a/coregrind/m_debuginfo/debuginfo.c b/coregrind/m_debuginfo/debuginfo.c index 9f27d501e1..9727cbce5b 100644 --- a/coregrind/m_debuginfo/debuginfo.c +++ b/coregrind/m_debuginfo/debuginfo.c @@ -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); diff --git a/coregrind/m_debuginfo/priv_readpdb.h b/coregrind/m_debuginfo/priv_readpdb.h index 3fb84d7be9..f5dc310cdf 100644 --- a/coregrind/m_debuginfo/priv_readpdb.h +++ b/coregrind/m_debuginfo/priv_readpdb.h @@ -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) diff --git a/coregrind/m_debuginfo/readpdb.c b/coregrind/m_debuginfo/readpdb.c index ed08504fae..1f8101c5dd 100644 --- a/coregrind/m_debuginfo/readpdb.c +++ b/coregrind/m_debuginfo/readpdb.c @@ -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) /*--------------------------------------------------------------------*/