How Many Secrets do .NET PDB Files Really Contain?

Now that Devscovery is over, it’s time to get back to answering questions about one of my favorite subjects: PDB files! Yes, I lead an extremely exciting life if PDB files set my heart racing. Maybe they have a pill for that.

In my blog post on PDBs and remote debugging Patrick asked an interesting question about .NET PDB files:

Hi John,

I find it rather strange that the PDB files for .Net applications should be on the remote machine, not the local machine.

After our release builds, we store all PDB and EXE files on a symbol server. That way we can easily debug crash dump (.DMP) files sent to us by customers.

Sometimes we use remote debugging to solve a problem at a customer (using VPN over the Internet), and for native applications, the debugger nicely gets the .PDB file from the symbol server.

However, if we would introduce .Net in our applications, would this mean that we have to send the .PDB file to our customer?

This will not only be a rather slow task (our PDB files can easily be 200 MB), but also this means that we have to give all of our debug information in the hands of our customer.

Aren’t PDB files meant to be ‘sensitive’; something that you want to keep within the walls of your company?

What do you think?

Thanks,

Patrick

While I would love to have .NET PDB files treated the same way as native PDB files we don’t have any choice in the matter as that’s just how it is. I’m sure the debugger teams at Microsoft have heard numerous comments like Patrick’s through the years. Maybe we’ll see everything fixed in a future version of Visual Studio.

Patrick is extremely lucky to be able to remote debug into a customer machines through VPN. I’m sure most of you reading would love to have that scenario! For most of us, just getting a user/admin to turn on logging and send us the output is near as hard as traveling to the moon.

Native binary PDB files are extremely sensitive. As I pointed out in my original article on PDB files, native PDB files contain the following data (if they are not stripped):

  • Public, private, and static function addresses
  • Global variable names and addresses
  • Parameter and local variable names and offsets where to find them on the stack
  • Type data consisting of class, structure, and data definitions
  • Frame Pointer Omission (FPO) data, which is the key to native stack walking on x86
  • Source file names and their lines

If you hand those over to a customer, you’ve given them everything short of source files. In fact, armed with the full native PDB file, it’s not too hard to write a tool with the public DBGHELP symbol API to write a tool that recreates your header files. If you value your job, you never want to let your native PDB files find their way outside your firewall.

.NET PDB files, on the other hand, only contain the following information:

  • Source file names and their lines
  • Local variable names

So how does the debugger know all about your .NET types? From the metadata that’s inside the binary so, there’s no need to duplicate the metadata inside a .NET PDB file. The beauty of .NET is “self describing objects” so you need much more information about your types inside the binary. The metadata is how using reflection you can load a binary you have never seen before and start instantiating it’s types. As you can imagine, that means it’s not too hard to decompile a .NET binary right back to your preferred language of choice because of that same metadata. Choosing the ease of use of .NET means you have to give up something as there is no such thing as a free lunch.

Because .NET PDB files don’t contain anything sensitive, I say give them freedom! In Patrick’s case, I’d give the .NET PDB files to the customer so he can do remote debugging of both native and .NET. All source files are loaded on the local machine where the Visual Studio UI is running so you’re not giving those up.

Preemptive comment strike: Yes, if you do something crazy dumb like embed the domain admin name and login password as part of your build path, that will show up in the .NET PDB file you give to your customers. I’m talking about the normal case here <big smile!>

In fact, I recommend that you create an installer for your .NET PDB files so you can install them into the same directory as the binaries. The debugger, either local or remote, always looks first in the directory where the binary was loaded for the matching .NET PDB file. Asking your customers to manually copy each .NET PDB file to the appropriate directories is a recipe for disaster. Installers are good things, even though the Windows Installer API is a bit painful.

Let me change my “recommend” to “extremely strongly recommend” for creating a .NET PDB file installer. Have you ever noticed an interesting difference between unhandled exceptions on your development machine and test or customer machines? When you look at the unhandled exception on your development machine, you see the call stack along with some very useful information: the source and line for each item in the call stack. On your development machine you have the local build PDB files in the same directory as the binary and the .NET StackTrace field in the Exception class automatically reads them for the source and line information. When you have a co-worker who writes 300 line methods, having the exact line massively helps in the call stack.

By having that .NET PDB file installer, you can have the customer that’s duplicating the unhandled exception install the symbols so when you dump the exception to your log you’ve got the source and line right there. That’s what I call debugging faster!

Keep in mind that every exception thrown uses the StackTrace class so there will be a bit of a performance hit when calculating the call stack because of the symbol look up. As you can guess there’s many variables that affect the performance so I can’t give you an exact number, but the fact you’ll have the exact source and line of your code in the stack means you will debug the problem faster, which, in the end, is the ultimate performance improvement.

Thanks to Patrick for an excellent question and the opportunity to show that giving .NET PDB files to your customers won’t hurt. I expect each of you to have installers for your .NET PDBs done in the next two weeks and I will be checking up on you. Please help my heart keep racing by not hesitating to ask any more questions you might have about PDB files or debugging in general!

John Robbins

View Comments

  • Thanks John, for your answer.
    It's not the one I hoped for, but at least it's probably a correct one.
    Patrick

  • We have been installing the pdb files with our binaries for about the last year, largely due to the benefit of getting line numbers with our Stacktrace entries which is extremely helpful for identifying unhandled errors. Initially the size of the pdb files put me off as they were 100Mb plus and it breached out CD install limits, but now we have moved to DVD installs/downloads its not a problem, and for some reason our pdb files have fallen dramatically in size - must be either compile options of some kind, or maybe the blocksize of our build pc - can anyone explain the sizing of the pdb files?

  • Vince,
    Great to hear you are doing it right! That's kind of bizarre you've seen such a giant drop in PDB size. Did you move from .NET 1.1 to 2.0? I've always thought the PDB size was relatively stable.
    Maybe you just want to consider yourself lucky that they are *decreasing* in size! :)
    - John Robbins

  • Web Google Analytics Data API - JavaScript Interactive Samples Cross-browser JSON Serialization in JavaScript

  • John,
    No we've always been on 2.0 since converting from VB6. I'm not responsible for the builds so its possible they may have initially been debug rather than release builds, or it could be that Service Pack 1 for VS2005 sorted out the problem.
    For info, our Winforms exe of 8Mb gives a pdb size of 9Mb .
    Vince

  • I'm unconvinced by the "super-sensitive" nature of native pdbs.
    Let me explain: the native pdbs contain bits of info that, in the .net world, are always available through reflection (short of what is in the actual managed pdbs, that you say are not sensitive).
    So, if that info is super sensitive, how come it's always available in the managed world ?
    Can you clarify a bit what is available in a native pdb that is not available with a combination of the .net assembly reflection data and it's corresponding pdb ?

  • Should the final release native program also generate the pdb files before being published?
    If switching on the pdb in link setting, what kind of info would be added into the .exe executable file?
    I found our program grows a lot after turning on this flag. Does that have anything which can reveal some sensitive secrete of our program?

Recent Posts

How to Navigate Azure Governance

 Cloud management is difficult to do manually, especially if you work with multiple cloud…

4 days ago

Why Azure’s Scalability is Your Key to Business Growth & Efficiency

Azure’s scalable infrastructure is often cited as one of the primary reasons why it's the…

3 weeks ago

Unlocking the Power of AI in your Software Development Life Cycle (SDLC)

https://www.youtube.com/watch?v=wDzCN0d8SeA Watch our "Unlocking the Power of AI in your Software Development Life Cycle (SDLC)"…

1 month ago

The Role of FinOps in Accelerating Business Innovation

FinOps is a strategic approach to managing cloud costs. It combines financial management best practices…

1 month ago

Azure Kubernetes Security Best Practices

Using Kubernetes with Azure combines the power of Kubernetes container orchestration and the cloud capabilities…

1 month ago

Mastering Compliance: The Definitive Guide to Managed Compliance Services

In the intricate landscape of modern business, compliance is both a cornerstone of operational integrity…

2 months ago