Professional Documents
Culture Documents
EXE-file
Module Segment PE-Header
Linker
ModuleIndex LinkerSegment, System DCU
Code
Name Offset, Size
Segments Flags
Compiler
Initialized Data
Unit1.DCU
ScrModule ScrModRange
ModuleIndex Debug-info
Segment
Ranges Start EndOffset
SourceFiles Debug-info
Module Unit2.DCU
SourceFile Debug-info
SourceFileRange Module
Name
Segment Module
Ranges
Start-EndOffset
AlignSym {$D+,L+}
LineNumberOffsets
SrcModule
AlignSym
AlignSym SymbolInfo SrcModule
ModuleIndex Unit1.Pas
Name, Kind AlignSym
SymbolInfos Info, TypeInfo
(StartSymbols Names
NextSymbol) TypeInfo Types Unit2.Pas
Name Unit2.Inc
Browse-info Core.ASM
TypeKind, Info
➤ Listing 4
A source module (BORDEBUG_ using the BorDebugStartSymbols
SSTSRCMODULE) has much more and BorDebugNextSymbol routines.
information available. For our pur- This gives us the kind of symbol, of what it produces when it’s run
poses, we are just interested in the and the offset and size of the on itself.
names of the source files that make symbol inside the debug informa- We can see that all the module
up the source module. We first get tion. Notice that the end of the sub-sections come first. Each sub-
the number of source files and symbol section is detected by section one has a sequential
ranges using the BorDebugSrcModule receiving the special value 0 for all module index, starting with 1. A
call. Ignoring the RangeCount for three parameters. To keep the size module index of 0 means that the
now, we then dynamically allocate of the output down, we don’t write sub-section is not associated with
three arrays to hold the offset, anything for each symbol, but just a specific module, this applies to
name index and range count for update a global counter and write the Global Types and the Names
each source file. Then we get the total count for each symbol sub-sections at the bottom of the
BorDebug to fill in information into section. listing.
our arrays by calling the BorDebug- Phew! As you can see we have to Furthermore, the offsets of each
SrcModuleSources API. At this point write a fair amount of code just to sub-section refer to the offset from
we don’t really care about the off- do some simple dumping of basic the beginning of the .EXE file,
sets and ranges, so we just free information. This API just screams where the debug information for
them again. Now that we have an to be wrapped in a set of classes. that sub-section can be found.
array of the name indices for the And we will do that. However, I Likewise, the sub-section size is
source files, we can just loop and think it is better to first use the raw the size of the debug information.
write each name out (again calling API in order to learn properly how Note that all Module subsections
BorDebugNameIndexToName to con- it works and how we can best have a segment count of 3. We’ll
vert each name index into a string). design our classes later. come back to this.
We must remember to free the Let’s see if we can gain some The program was compiled with
array of name indices. more understanding by studying the debug version of the RTL units.
Finally, for the symbol sections, the output from this program. So the first source module is the
we just iterate over all the symbols Listing 5 is an abbreviated version one for System.Pas. Note that the
➤ Listing 7
Also, all other sub-sections will be find a match, we keep the supplied
ignored, speeding up the scanning line number and unit name infor-
defining a new public method process. This could probably be mation (ie, the name of the current
called FindUnitnameLinenumber, see tuned further by ignoring source source file entry).
Listing 8. modules that have ranges which After the Scan call returns, we
The FindUnitnameLinenumber fall outside the address. If you need check if we found a match, and
function first saves the Address to look up a large number of return those values. On the disk is
parameter in a field so that the addresses quickly, you probably a small demonstration application,
ScanLineNumberOffset method can want to load the information into AddrLookup.dpr, which shows
see the value. Then it calls the Scan your own custom data structures. how to use this class: you can see it
method, indicating that it is inter- Inside the ScanLineNumberOffset running in Figure 4.
ested in the source modules and method we check if the current Note that the addresses used by
the files they contain. This ensures address is lower or equal to the this sample application are offsets
that the ScanLineNumberOffset will address we’re looking for. If we relative to the start of the code seg-
be called for all the source files don’t find a perfect match, we ment. Actual runtime addresses
found in the debug information. select the closest match. When we will be different, so you will have to
➤ Listing 8
convert those into relative offsets re-usable TDumpBorDebugScanner
by subtracting the base address of class. This inherits from TCustom-
the executable (typically $0040000 BorDebugScanner and overrides just I’ve created a little console pro-
for an .EXE file). In addition you about every virtual method to gram that uses this class to dump
normally have to subtract $1000 dump all the available debug infor- all the debug information of the file
(added by the linker). mation. The actual dumping is del- given on the command line to the
egated to a public OnDump event. standard output: see the code for
The Gigantic Dumper This way we can reuse all that BDDmpAll.Dpr in Listing 9.
No article about the TD32 debug boring dumping code (650 lines), Be warned that this program
format would be complete without and just plug in any media we like produces large outputs. When I ran
a generic dumper program. To (write it to standard output, dump it on itself, it produced a text file of
write this, I first developed a it to a TMemo control, or stream it). about 3.8Mb! No doubt much of
this information is redundant, but
it does give you a hint of just how
much information is in there. Just
for the heck of it, I also wrote a GUI
version: see Listing 10 and Figure
5.
Again, be aware that loading a
➤ Above: Figure 4 ➤ Below: Figure 5
file can take quite a few seconds.
Notice the special trick I use to
grow the Dump string. Without this,
the program eats memory like
crazy and takes forever to run. I’m
using a vanilla TMemo in this sample,
because it ran much slower with a
TRichEdit control.
Conclusion
I could continue this article almost
ad infinitum, but I have to put the
➤ Listing 9
➤ Listing 10
limit somewhere. Now I have given
you a Pascal wrapper around the
BorDebug.DLL, a set of wrapper References
classes, a simplified scanner tem-
plate class, a couple of 1. Borland Turbo Debugger, a free download:
www.borland.com/bcppbuilder/turbodebugger/
descendants that do some real
work, plus examples of how to use 2. Numega BoundsChecker for Delphi:
it all too. www.numega.com/products/aed/del.shtml
Possible usability extensions 3. Turbo Power Sleuth QA Suite:
would be to write a non-visual com- www.turbopower.com/products/sleuthqa/
ponent that has events for all the 4. Atanas Stoyanov’s MemProof home page:
virtual scanning methods. More www.totalqa.com/downloads/ memproof.asp
interesting would be to find appli-
cations for the debug information 5. AutomatedQA QTime:
www.totalqa.com/index.asp
itself.
One possible candidate is the 6. Intel VTune: http://developer.intel.com/vtune/
‘old’ Exceptional Stack Tracer 7. Wotsit’s Format – The programmer’s reference: www.wotsit.org/
code, presented back in Issue 50.
8. Stefan Hoffmeister: www.econos.de/index.html
Currently, Vitaly’s RTLI code uses
information from the .MAP file. It 9. Hallvard Vassbotn, TDM Issue 50, October 1999, Exceptional Stack Tracing
could be extended to take the infor- 10. John Thomas, Borland Debug Hook Library and Header File:
mation from the TD32 info, either http://ww6.borland.com/codecentral/ccweb.exe/listing?id=14513
at design-time to re-pack it, or 11. Unfortunately, TDStrp32.EXE is not included with Delphi, nor the free
directly at runtime. Another inter- BC++ compiler: www.borland.com/bcppbuilder/freecompiler/
esting project would be to write a
12. Borland’s Turbo Assembler 5.0:
custom Win32 symbolic debugger
http://shop.borland.com/Product/ 0,1057,3-15-CQ100146,00.html
or runtime inspector. Or maybe
take up the competition with 13. Hallvard Vassbotn, TDM Issue 38, November 1998, Slimming The Fat
QTime and Sleuth QA and write Off Your Apps
your own profiler? The world is 14. Hallvard Vassbotn, TDM Issue 43, March 1999, DelayLoading of DLLs
now very much your oyster!