Our nightly build process was broken for a long time, such that it generated PDB files that were a few hours different in age than the corresponding image files. I have since fixed the problem.
However, I would like to start using a symbol server, but cannot due to having to use these age-mismatched pdb files. I work around this issue by using the .symopt +0x40 method in windbg. That means I have to organize all my pdb files by hand, and after years upon years of releases, that adds up.
I am looking for a way to modify the mechanism that windbg uses to mark a pdb's age, and force it to match my image file. The utility ChkMatch does something similar, but for pdb signatures. The developer states on the page "ChkMatch is capable of making an executable and PDB file match if they have different signatures but the same age (see this article for more information about PDB signature and age). If the age differs, the tool cannot make the files match."
I took a look inside a hexeditor, and even found what looked like the bits corresponding to the age, but it must pull some more tricks internally, cause I couldn't get it to work.
Any ideas?
EDIT: I don't know if this helps, but in my particular case the age difference was caused by unnecessarily relinking dll's, which would recreate the PDB files as well. However, our build process was storing the original dlls (before the relink), and the pdb after the relink. I thought about somehow recreating such a situation by hand. Meaning, forcing a relink on a DLL, but saving off the pdb in both cases. Then I could do a binary compare of the two files to see how they changed. Perhaps run some sort of patching software that does this automatically? By seeing what exactly changed in my control case, perhaps I could do the same to the DLLs and PDBs saved in my companies build process?
EDIT: I FIGURED IT OUT!!!! Thanks to one of the comments on the first answer, I checked out a link to the pdfs of the book "Undocumented Windows 2000 Secrets: A Programmers Cookbook". And the author goes into great detail about the pdb file format. As I said before, I had already loaded the pdb into a hex editor and flipped some bits around appearing that I made the age/signature match, but it didn't work. Well, after using the utility from the W2k secrets book to "explode" the pdb into the included streams, I found out that they hide another reference to the age in stream 3!!!!!!! Once I flipped that one as well, it matched up in windbg. THIS IS HUGE!!!! Thank you so much....symbol server HERE I COME!
the windbg will not modify pdb's age - it only looks it up to match that of executable - the compiler does when it (re)generates executable and debug files.
now, based on the debuginfo.com article, it is not too difficult to arrive at the proper debug directory (of type codeview), match it against PDB7 signature and make modifications to either age or GUID inside an executable. why is that not an option?
i guess, you want to update pdb instead? i'm afraid, pdb is a proprietary format. there're multiple read-only APIs (dbghelp.dll and dia sdk), but as far as modifications go, you need to guess the details to be able to modify.
Or you could just use the suggestion here to have windbg ignore mismatched signatures and age:
http://www.debuginfo.com/articles/debuginfomatch.html
... While by default it [windbg] also does not allow to load unmatched debug information, .symopt debugger command can change the default behaviour. After we have issued “.symopt+0x40” command, the debugger will happily accept and load unmatched PDB and DBG files.
Hope this helps.
Although as SamB said, in PDB(format 7, my test is based on VS2010 generated .exe and .pdb, and windbg 6.9.0003.113 X86) there's one extra reference to age, so totally there will be 3 ages to modify in PDB file. Unfortunately, SamB did not told us how to find the magic 3rd age, the stream 3? no! according to my test, I extract more than 100 pdb streams, I tried 02(if SamB is 0-indexed) and 03, both cannot find the age.
Fixing the other 2 ages is easy, as soon as you have a hex editor and windbg.
using symchk to get the Signature(a GUID) of the your mismatched PDB file: symchk your.exe /v /s .
The typically output will contains:
[SYMCHK] ------------------------------------
SymbolCheckVersion 0x00000002
Result 0x00010001
DbgFilename CPP_Snippet.dbg
DbgTimeDateStamp 0x00000000
DbgSizeOfImage 0x00000000
DbgChecksum 0x00000000
PdbFilename E:\zrf\C_CPP\CPP_Snippet.pdb
PdbSignature {6D8D99B0-E96B-4093-9D97-8BDC5152B6E0}
PdbDbiAge 0x00000188
Search the last part of the of the GUID: 8BDC5152B6E0, because only the last part is byte-order-free from big-endian/little-endian issue, it's just exactly same as in pdb files. Be careful to search as raw hex value, to make it more accurate, you should verify the other values in the GUID(need to rever byte-order in X86)exactly matches. There will be exactly 2 GUIDs found inside the PDB file, the accompanying age is just before the first byte of the GUID. Modify it. That's it!
my brute way to find out the 3rd age.
dump the hex number of your PDB file, one byte(2 hex numbers) per line. od -v -t x1 your.pdb | sed 's/^[0-9a-f]* //;s/ /\n/g' > age_offset.txt
get the line number of every matched age, in my case it's 4 consecutive lines which has value 88 01 00 00, vim age_offset.txt :g/88\n01\n00\n00/s/^/\= (line('.') . ':')/
This is a ex mode command, which should be support by a recent version of vim.
:v/:/d
This will delete all lines which does not contains ':', the remained lines are line numbers which is the offset of every matched age.
:%s/:.*//
This will trim :88 and leaving the offset alone.
:%s/.*/\=(submatch(0) - 1)/
This command substract every number by 1, I do this because the line number in vim is 1-index, and the byte offset of every age should be 0-index to make the co-worker utility happy.
:w
save the file
Now we get a text file with every line contains a decimal number representing an offset, from this offset, the following 4 bytes is candidate for your dreaming age.
Next I'm trying to modify every potential age and then try to check it by symchk until it matches, every time only one offset will be patched.
First of all, I will backup a PDB with the 2 ages(and GUID) be modified. Let's called it ori.pdb
Here's the batch script to do the hard work:
for /F usebackq %%i in (`type age_offset.txt`) DO ( copy /y ori.pdb CPP_Snippet.pdb @rem dd if=ori.pdb bs=1c count=4 skip=%%i | xxd -g1 | grep "88 01 00 00" || echo "Bad data at %%i" && goto exit dd if=pdb_age.dat of=CPP_Snippet.pdb bs=1c count=4 seek=%%i conv=notrunc symchk CPP_Snippet.exe /s . && echo "Found it at offset %%i" && goto exit ) :exit
Good lucky, I found the right place at 38th offset.
It's not the fastest way to try-error out the correct offset to patch, but it works for me, it's my prototype to make sure there's only 1 extra age to fix, otherwise, the possible combination is huge(I've 111 age candidate to try) and thus a try-error way is not pragmatic.
I think it's very easy to write an utility to do the same job in a faster way.
BTW: according to my test. chkmatch may report match while symchk and windbg vs think it mismatch.
windbg command !itoldyouso match while .reload /f your_module.exe still cannot match.
After the 3 ages being fixed, not only windbg but also visual studio can load the pdb files.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With