PDB files out of the debugger


I suppose that I don’t need to stress how important it is to have valid PDB files while debugging. Normally the PDB files are silently loaded by the debugger and you are happy to see all the symbols resolved in your modules window. Unfortunately you may also run into situation when the debugger will not be able to find matching symbols. The reason may be as trivial as a broken Internet connection or much more complicated like mismatched signatures. In this post I’m going to show you how to check your symbol files before debugging as well as how to extract the source file information from them. As there are different ways (and tools) to manipulate the symbol files I will present the ones that I am aware of, but feel free to leave comments about tools that I might have missed and I will try to update the post.

Downloading a PDB file for a given PE file

Some time ago Mike Stall blogged about different Symbol APIs available for developers. As the PDB file format is a Microsoft’s secret all the tools that I’m presenting are just wrappers over those APIs. To work with PDB files we first need to get them. Let’s list the tools that will help us in it.

symchk.exe

Symbol Checker (Symchk.exe) is an application that compares executable files to symbol files to verify that the matching symbols are available. Symchk may also be used to populate your symbol cache. It can read symbol information from PE files (exe, dll), dump files and processes. It also supports recursive directory search and batch files.

We will start from loading symbols for the kernel32.dll library:

c:\Windows\System32>echo %_NT_SYMBOL_PATH%
SRV*C:\Symbols\MSS*http://referencesource.microsoft.com/symbols;SRV*C:\Symbols\MSS*http://msdl.microsoft.com/download/symbols

c:\Windows\System32>symchk /v /os kernel32.dll
[SYMCHK] Searching for symbols to c:\Windows\System32\kernel32.dll in path SRV*C:\Symbols\MSS*http://referencesource.microsoft.com/symbols;SRV*C:\Symbols\MSS*http://msdl.microsoft.com/download/symbols
DBGHELP: Symbol Search Path: SRV*C:\Symbols\MSS*http://referencesource.microsoft.com/symbols;SRV*C:\Symbols\MSS*http://msdl.microsoft.com/download/symbols
[SYMCHK] Using search path "SRV*C:\Symbols\MSS*http://referencesource.microsoft.com/symbols;SRV*C:\Symbols\MSS*http://msdl.microsoft.com/download/symbols"
DBGHELP: No header for c:\Windows\System32\kernel32.dll.  Searching for image on disk
DBGHELP: c:\Windows\System32\kernel32.dll - OK
SYMSRV:  C:\Symbols\MSS\kernel32.pdb\9B30FD7CD6B44975BF34B43B6EF668212\kernel32.pdb not found
SYMSRV:  http://referencesource.microsoft.com/symbols/kernel32.pdb/9B30FD7CD6B44975BF34B43B6EF668212/kernel32.pdb not found
SYMSRV:  kernel32.pdb from http://msdl.microsoft.com/download/symbols: 704453 bytes - copied
DBGHELP: kernel32 - public symbols
         C:\Symbols\MSS\kernel32.pdb\9B30FD7CD6B44975BF34B43B6EF668212\kernel32.pdb
[SYMCHK] MODULE64 Info ----------------------
[SYMCHK] Struct size: 1680 bytes
[SYMCHK] Base: 0x0000000078D20000
[SYMCHK] Image size: 1175552 bytes
[SYMCHK] Date: 0x4e21213b
[SYMCHK] Checksum: 0x0012386d
[SYMCHK] NumSyms: 0
[SYMCHK] SymType: SymPDB
[SYMCHK] ModName: kernel32
[SYMCHK] ImageName: c:\Windows\System32\kernel32.dll
[SYMCHK] LoadedImage: c:\Windows\System32\kernel32.dll
[SYMCHK] PDB: "C:\Symbols\MSS\kernel32.pdb\9B30FD7CD6B44975BF34B43B6EF668212\kernel32.pdb"
[SYMCHK] CV: RSDS
[SYMCHK] CV DWORD: 0x53445352
[SYMCHK] CV Data:  kernel32.pdb
[SYMCHK] PDB Sig:  0
[SYMCHK] PDB7 Sig: {9B30FD7C-D6B4-4975-BF34-B43B6EF66821}
[SYMCHK] Age: 2
[SYMCHK] PDB Matched:  TRUE
[SYMCHK] DBG Matched:  TRUE
[SYMCHK] Line nubmers: FALSE
[SYMCHK] Global syms:  FALSE
[SYMCHK] Type Info:    FALSE
[SYMCHK] ------------------------------------
SymbolCheckVersion  0x00000002
Result              0x00030001
DbgFilename
DbgTimeDateStamp    0x4e21213b
DbgSizeOfImage      0x0011f000
DbgChecksum         0x0012386d
PdbFilename         C:\Symbols\MSS\kernel32.pdb\9B30FD7CD6B44975BF34B43B6EF668212\kernel32.pdb
PdbSignature        {9B30FD7C-D6B4-4975-BF34-B43B6EF66821}
PdbDbiAge           0x00000002
[SYMCHK] [ 0x00000000 - 0x00030001 ] Checked "c:\Windows\System32\kernel32.dll"

SYMCHK: FAILED files = 0
SYMCHK: PASSED + IGNORED files = 1

As you can see in the verbose mode (/v switch) you receive a lot of information about what symchk is doing. We can even read which symbols API it is using (dbghelp messages). The /os switch informs symchk to print full paths of the symbol files in the output messages. After running this command the kernel32.pdb file should be in our symbol store. If you would like to index the whole System32 directory you would like to use the /r switch which informs symchk to recursively step through the provided directory and download symbols for all the files found, eg. symchk /r /v c:\windows\system32\*.dll

Let’s try to load symbols for the notepad.exe process:

c:\temp\symtest>tasklist /FI "IMAGENAME eq notepad.exe"

Image Name                     PID Session Name        Session#    Mem Usage
========================= ======== ================ =========== ============
notepad.exe                   2264 Console                    1      6 036 K

c:\temp\symtest>symchk /ip 2264 /s SRV*.*http://msdl.microsoft.com/download/symbols

SYMCHK: FAILED files = 0
SYMCHK: PASSED + IGNORED files = 26

With the /ip switch we can provide just process ID and symchk will download symbol files for all the modules loaded in the process. In this example we also used the /s switch which provides symchk with symbol path that it should use (overriding _NT_SYMBOL_PATH if set). In our case we were downloading symbol files from the Microsoft public server to the current directory. The listing of this directory after running this command will look as follows:

c:\temp\symtest>tree .
Folder PATH listing
Volume serial number is 00000002 C622:C13F
C:\TEMP\SYMTEST
├───advapi32.pdb
│   └───6AEFDCFF7F2A429B8532CD2BFDDF85D12
├───CLBCatQ.pdb
│   └───60B9D310C472440BA13F66BFF0FC39E32
├───comctl32.pdb
│   └───943BA638A2CD4D88A1C7E7418EAF796C1
├───comdlg32.pdb
│   └───631B57376F8549FDB2E7A8AB3D2D1FDF2
├───cryptbase.pdb
│   └───F03E074BB9E74C9F9BBFB0E42EF3A0AB2
├───dwmapi.pdb
│   └───8683ED0C3DBE4053883EC22FD9B4F2102
├───gdi32.pdb
│   └───FB9403C3B1304DA192C4D0E3485E25ED2
├───imm32.pdb
│   └───98F27BA5AEE541ECBEE00CD03AD50FEE2
├───kernel32.pdb
│   └───9B30FD7CD6B44975BF34B43B6EF668212
├───kernelbase.pdb
│   └───61044362232B410AA600843CEBFD11612
...

Another interesting switch is (/id) which enables you to debug dump files.

Another great functionality of symchk.exe is something called manifest files. Manifest files contain information about all symbols that must be downloaded. You may then run symchk with /om switch which will produce the manifest file without downloading any symbols. Then you can copy the manifest file to any computer that has the Internet connection and download the symbol files using /im switch. The snippet below shows an usage example:

c:\temp\symtest>symchk /om notepad-symbols.man /ip 2264

SYMCHK: FAILED files = 0
SYMCHK: PASSED + IGNORED files = 26

c:\temp\symtest>type notepad-symbols.man
notepad.pdb,36CFD5F9888C4483B522B9DB242D84782,1
notepad.exe,4a5bc9b335000,1
ntdll.pdb,6192BFDB9F04442995FFCB0BE95172E12,1
ntdll.dll,4ce7c8f91a9000,1
kernel32.pdb,9B30FD7CD6B44975BF34B43B6EF668212,1
kernel32.dll,4e21213b11f000,1
kernelbase.pdb,61044362232B410AA600843CEBFD11612,1
KernelBase.dll,4e21213c6c000,1
...
c:\temp\symtest>symchk /im notepad-symbols.man /s SRV*.*http://msdl.microsoft.com/download/symbols

SYMCHK: FAILED files = 0
SYMCHK: PASSED + IGNORED files = 52

dbh.exe

This tool is a wrapper over the DbgHelp.dll library and uncovers almost all of its functionality. We will just look at the one usage example so if you would like to go deeper have a look at the Debugging Tools for Windows help. When you run dbh.exe with a module name as an argument it will automatically download the symbol files. So by simply calling dbh c:\windows\system32\kernel32.dll info you will download the symbol file (_NT_SYMBOL_PATH environment variable is used) and print information about it and its PE file (kernel32.dll):

c:\temp>dbh c:\windows\system32\kernel32.dll info

    SizeOfStruct : 0x690
     BaseOfImage : 0x1677721664x
       ImageSize : 0x1000000
   TimeDateStamp : 0x4e21213b
        CheckSum : 0x12386d
         NumSyms : 0x0
         SymType : SymPdb
      ModuleName : kernel32
       ImageName : c:\windows\system32\kernel32.dll
 LoadedImageName : c:\windows\system32\kernel32.dll
   LoadedPdbName : C:\Symbols\MSS\kernel32.pdb\9B30FD7CD6B44975BF34B43B6EF668212\kernel32.pdb
           CVSig : 0x53445352
          CVData : kernel32.pdb
          PdbSig : 0x0
        PdbSig70 : 0x9b30fd7c, 0xd6b4, 0x4975, 0xbf, 0x34, 0xb4, 0x3b, 0x6e, 0xf6, 0x68, 0x21
          PdbAge : 0x2
    PdbUnmatched : false
    DbgUnmatched : false
     LineNumbers : false
   GlobalSymbols : false
        TypeInfo : false
   SourceIndexed : false
   PublicSymbols : true
     MachineType : X64

If you would like to see some verbose information about PDB files loaded use -n switch. To change the default symbol path (or override _NT_SYMBOL_PATH settings) use the -s: switch.

dumpbin.exe

You might be surprised that this tool appears here, but have you ever noticed its /PDBPATH[:VERBOSE] switch? Issuing the dumpbin /pdbpath:verbose on our kernel32.dll library will result in downloading the PDB file from the public symbol store:

c:\temp\symtest>dumpbin /pdbpath:verbose c:\windows\system32\kernel32.dll
Microsoft (R) COFF/PE Dumper Version 10.00.40219.01
Copyright (C) Microsoft Corporation.  All rights reserved.

Dump of file c:\windows\system32\kernel32.dll

File Type: DLL
  PDB file 'c:\windows\system32\kernel32.pdb' checked.  (File not found)
  PDB file 'c:\temp\symtest\kernel32.pdb' checked.  (File not found)
  PDB file found at 'C:\Symbols\MSS\kernel32.pdb\9B30FD7CD6B44975BF34B43B6EF668212\kernel32.pdb'

  Summary

        2000 .data
        A000 .pdata
       6E000 .rdata
        8000 .reloc
        1000 .rsrc
       9B000 .text

Downloading source code from the source server

srctool.exe

This tool is quite interesting as it allows you to check which source files are indexed in the PDB file and eventually extract them. With -r switch you may check which source code paths were hardcoded in the PDB file, eg.

c:\temp>srctool -r ConsoleApplication1.pdb
D:\lab\symbols-lab\symbols\ConsoleApplication1\Program.cs
D:\lab\symbols-lab\symbols\ConsoleApplication1\AdvertQuickView.cs

Without any switch srctool will examine the source server stream in the PDB file (if it exists) and print commands that will be executed to extract the source files, eg:

c:\temp>srctool ConsoleApplication1.pdb
[D:\lab\symbols-lab\symbols\ConsoleApplication1\Program.cs] cmd: cmd /c svn.exe cat "svn://localhost/test2/Program.cs@1" --non-interactive --username admin --password admin > "
[D:\lab\symbols-lab\symbols\ConsoleApplication1\AdvertQuickView.cs] cmd: cmd /c svn.exe cat "svn://localhost/test2/AdvertQuickView.cs@1" --non-interactive --username admin --password admin > "

ConsoleApplication1.pdb: 2 source files are indexed

To run the commands and thus extract the source files you just need to add the -x switch to the above call. Additionally using the -d switch you may specify the directory to which the source files will be extracted (by default it’s the current directory).

pdbstr

If you would like to have a better control over the source server stream you may check the pdbstr command. With its aid you can read and update the source server information in the PDB file. The source server stream is actually a text block with predefined sections (more info can be found here). You can dump its content by issuing:

c:\temp>pdbstr -r -p:ConsoleApplication1.pdb -s:srcsrv > stream.txt

The stream.txt file should contain something like:

SRCSRV: ini ------------------------------------------------
VERSION=1
INDEXVERSION=2
VERCTRL=Subversion
DATETIME=Thu Nov 17 13:31:46 2011
SRCSRV: variables ------------------------------------------
SVNUSER=admin
SVNPASS=admin
SVN_EXTRACT_TARGET=%targ%\%fnbksl%(%var3%)\%var4%\%fnfile%(%var1%)
SVN_EXTRACT_CMD=cmd /c svn.exe cat "%var2%%var3%@%var4%" --non-interactive --username %svnuser% --password %svnpass% > "
%svn_extract_target%"
SRCSRVTRG=%SVN_extract_target%
SRCSRVCMD=%SVN_extract_cmd%
SRCSRV: source files ---------------------------------------
D:\lab\symbols-lab\symbols\ConsoleApplication1\Program.cs*svn://localhost/*test2/Program.cs*1
D:\lab\symbols-lab\symbols\ConsoleApplication1\AdvertQuickView.cs*svn://localhost/*test2/AdvertQuickView.cs*1
SRCSRV: end ------------------------------------------------

You can then apply the changes you’d like and save the new source stream definition back to the PDB file:

c:\temp>pdbstr -w -p:ConsoleApplication1.pdb -s:srcsrv -i:stream.txt

Pdbstr will not extract the source code files for you but from the source stream content you can easily read where to find them.

That will end our list of PDB tools – I hope that you will find it useful. Please remember to leave a comment if you know any other tool that should be enlisted here.

2 thoughts on “PDB files out of the debugger

  1. Pingback: Pdb | TagHall
  2. Hi Sebastian,

    Thanks for your article. It’s very insteresting.
    I am trying to inspect the symbols of a .NET program but I only can see the public methods. I can’t see any variable (at class or local level).

    I tried with DBH and DIA2DUMP with the same result.
    I am targeting NET 4.0.
    I am building in debug mode.

    Do you have any idea why this is happening ?

    Thanks in advance. Regards.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s