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 a 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 numbers: 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, e.g.,
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 a 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, e.g.,
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, e.g.:
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 information 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.
Filed under:
CodeProject,
PDB files,
Tools