Until
recently, FPO information was undocumented. However, documentation
eventually showed up in an obscure corner of the MSDN CD-ROM. More
recently, WINNT.H from the Windows NT 4.0 Win32 SDK included a
definition for the FPO_DATA structure, which is essentially all you need
to know.
Yet another form of debug information is relatively new and
undocumented, except for a few obscure references in WINNT.H and the
Win32 SDK help. This type of information is known as OMAP. Apparently,
as part of Microsoft's internal build procedure, small fragments of code
in EXEs and DLLs are moved around to put the most commonly used code at
the beginning of the code section. This presumably keeps the process
memory working set as small as possible. However, when shifting around
the blocks of code, the corresponding debug information isn't updated.
Instead, OMAP information is created. It lets symbol table code
translate between the original address in a symbol table and the
modified address where the variable or line of code really exists in
memory.
In WINNT.H, you'll see two #defines, IMAGE_DEBUG_TYPE_OMAP_TO_SRC and
IMAGE_
DEBUG_TYPE_OMAP_FROM_SRC, that provide evidence for the existence of
OMAP information. Likewise, in the description of the IMAGEHLP_SYMBOL
structure in the Win32 SDK, you'll see the #define SYMF_OMAP_
GENERATED. The question is, where can you find examples of OMAP
information? If you use Windows NT, you can find it nearly everywhere.
Prowling through the DBG files, which Microsoft provides in the Win32
SDK for most system components, you'll find that many of them have OMAP
information.
DBG Files
The DBG files provide the debug information for your use without
including it in the executable. Microsoft provides symbol tables for all
the components of Windows NT. However, by putting them in separate DBG
files, you don't have to pay the overhead of increased disk usage if you
don't need the symbol tables. You can simply copy the DBG files for the
system components you use.
DBG files are nothing more than a collection of the various types of
debug information. Following a standard header at the beginning of a DBG
file is a directory of the various types of debug information in the
file. If you've written code that works with symbol tables in
executables, it's really not hard to modify them to work with DBG files
as well.
The standard method for creating a DBG file is to build your executable
file with whatever types of debug information you want. Remember, doing
a debug build doesn't necessarily mean that you have to disable
optimizations. Once the executable is created, use the REBASE program
from the Win32 SDK to strip the symbols out of the executable and put
them into a DBG file. Alternatively, if you write your own tools,
IMAGHELP.DLL has a SplitSymbols API that can create a DBG file.
If
you're going to use my MSJEXHND framework (or something like it), you
should seriously consider making DBG files for your final release. In
your release build, leave all your optimizations on, but enable
debugging optimization (and optionally, line-number information). After
building your executable, use REBASE to strip the symbol information out
into a DBG file. This way, you'll have symbols for debugging your
release build, but your users won't.
Using IMAGEHLP
Now
that I've rambled on about symbol tables and DBG files, let me tie this
back together with the original topic: symbolic stack traces in an
exception report. IMAGEHLP.DLL supports and uses nearly everything that
I've described above. For starters, it can read CodeView information,
PDB files, and COFF debug information to translate symbolic names to
addresses. If FPO data is present, IMAGEHLP uses it to walk the stack
even when EBP-style stack frames are missing. For Microsoft executable
files that have undergone working set optimization, IMAGEHLP uses the
OMAP information to provide correct symbolic addresses. And IMAGEHLP can
do all of this either from executable files or from separate .DBG
files.
With
all these capabilities, IMAGEHLP.DLL may be suitable for all of the
symbol table needs of some applications. However, there are limitations
and restrictions that bear mentioning. First, IMAGEHLP doesn't have APIs
for accessing source-line information. Debuggers need source-line
information to step through code at the source level. Next, some of the
functions in the Windows NT 4.0 version of IMAGEHLP.DLL don't work on
Windows 95, including some of the functions I'll use this month.
Finally, IMAGEHLP only knows about Microsoft-supported symbol formats.
Developers using Borland and other non-CodeView or COFF compilers will
have to find some other way to access their symbol tables.
So
how can IMAGEHLP improve the MSJExceptionHandler code? Last month, I
mentioned that my Intel-specific stack-walking code wouldn't work if no
stack frames were generated (for example, if I had compiled the program
with optimizations enabled for my release build). IMAGEHLP.DLL, with the
aid of FPO data, can still walk the stack in this situation. Even if I
could walk the stack, logical addresses like
|