Borland debug information

Working on Maupiti Island I learned how DOS programs stored debug information.

MZ executables would usually store debug information at the end of the binary file, with each compiler producing the information in it's own format. Borland tools would produce the following:

Format of Borland debugging information header (following load image):
Offset	Size	Description	)
 00h	WORD	signature 52FBh
 02h	WORD	version ID
 04h	DWORD	size of name pool in bytes
 08h	WORD	number of names in name pool
 0Ah	WORD	number of type entries
 0Ch	WORD	number of structure members
 0Eh	WORD	number of symbols
 10h	WORD	number of global symbols
 12h	WORD	number of modules
 14h	WORD	number of locals (optional)
 16h	WORD	number of scopes in table
 18h	WORD	number of line-number entries
 1Ah	WORD	number of include files
 1Ch	WORD	number of segment records
 1Eh	WORD	number of segment/file correlations
 20h	DWORD	size of load image after removing uninitialized data and debug
 24h	DWORD	debugger hook; pointer into debugged program whose meaning
		  depends on program flags
 28h	BYTE	program flags
		bit 0: case-sensitive link
		bit 1: pascal overlay program
 29h	WORD	no longer used
 2Bh	WORD	size of data pool in bytes
 2Dh	BYTE	padding
 2Eh	WORD	size of following header extension (currently 00h, 10h, or 20h)
 30h	WORD	number of classes
 32h	WORD	number of parents
 34h	WORD	number of global classes (currently unused)
 36h	WORD	number of overloads (currently unused)
 38h	WORD	number of scope classes
 3Ah	WORD	number of module classes
 3Ch	WORD	number of coverage offsets
 3Eh	DWORD	offset relative to symbol base of name pool
 42h	WORD	number of browser information records
 44h	WORD	number of optimized symbol records
 46h	WORD	debugging flags
 48h  8 BYTEs	padding
Note:	additional information on the Borland debugging info may be found in
	  Borland's Open Architecture Handbook
SeeAlso: #01600

with the help of Borland's Open Architecture Handbook I've created a template for 010Editor to parse the debug information.

typedef struct
    WORD   MZSignature              ;
    WORD   UsedBytesInTheLastPage   ;
    WORD   FileSizeInPages          ;
    WORD   NumberOfRelocationItems  ;
    WORD   HeaderSizeInParagraphs   ;
    WORD   MinimumExtraParagraphs   ;
    WORD   MaximumExtraParagraphs   ;
    WORD   InitialRelativeSS        ;
    WORD   InitialSP                ;
    WORD   Checksum                 ;
    WORD   InitialIP                ;
    WORD   InitialRelativeCS        ;
    WORD   AddressOfRelocationTable ;
    WORD   OverlayNumber            ;
    WORD   Reserved[4]              ;
    WORD   OEMid                    ;
    WORD   OEMinfo                  ;
    WORD   Reserved2[10]            ;
    LONG   AddressOfNewExeHeader    ;

typedef struct
 WORD	magic_number ;
 WORD	version_id ;
 DWORD	names ;
 WORD	names_count ;
 WORD	types_count ;
 WORD	members_count ;
 WORD	symbols_count ;
 WORD	globals_count ;
 WORD	modules_count ;
 WORD	locals_count ;
 WORD	scopes_count ;
 WORD	lines_count ;
 WORD	source_count ;
 WORD	segment_counts ;
 WORD	correlation_count ;
 DWORD	image_size ;
 DWORD	debugger_hook ;
 BYTE	program_flags ;
// bit 0: case-sensitive link
// bit 1: pascal overlay program
 WORD	stringsegoffset ;
 WORD	data_count ;
 BYTE	filler1 ;
 WORD	extension_size ;

 if (extension_size > 0) 
    WORD	class_entries ;
    WORD	parent_entries ;
    WORD	global_classes ;
    WORD	NumberOfOverloads ;
    WORD	scope_class_entries ;
    WORD	module_class_entries ;
    WORD	CoverageOffsetCount ;
    DWORD	NamePoolOffset;
    WORD	BrowserEntries ;
    WORD	OptSymEntries ;
    WORD	DebugFlags ;
    DWORD  refInfoSize ;
    BYTE   filler2[14];

// 8 BYTEs	padding

typedef struct
    unsigned short symbol_name ;
    unsigned short symbol_type;
    unsigned short symbol_offset ;
    unsigned short symbol_segment ;
    unsigned byte  symbol_class : 3;
    unsigned byte  has_valid_BP : 1;
    usigned byte  return_address_word_offset : 3;

typedef struct
    unsigned short module_name ;
    unsigned char language;
    unsigned byte memory_model : 3;
    unsigned byte underbars_on : 1;
    unsigned short symbols_index;
    unsigned short symbols_count;
    unsigned short source_files_index;
    unsigned short source_files_count;
    unsigned short correlation_index;
    unsigned short correlation_count;

Printf("EXE.bt Begin\n");

if (DosHeader.MZSignature != 0x5A4D)
    Printf("Invalid DOS Magic.\n");
    return 1;

local DWORD extra_data_start = DosHeader.FileSizeInPages * 512;
if (DosHeader.UsedBytesInTheLastPage) {
  extra_data_start -= (512 - DosHeader.UsedBytesInTheLastPage);  

if (extra_data_start >= FileSize()) {
    Printf("No Debug Info.\n");
    return 1;


if (DebugHeader.magic_number != 0x52FB) {
    Printf("Invalid Debug Info Signature.\n");    
    return 1;

SYMBOL_RECORD SymbolRecord[DebugHeader.symbols_count] ;
MODULE_HEADER Module[DebugHeader.modules_count] ;

string readName(unsigned short nameIndex) {
    int originalLocation = FTell();
    FSeek(FileSize() - DebugHeader.names);
    local int i = 0;
    string name;    
    int currLocation = FTell();
    while(i < nameIndex) {            
        name = ReadString(currLocation);        
        FSeek(currLocation + ReadStringLength(currLocation));
        currLocation = FTell();
    return name;

string readSymbol(SYMBOL_RECORD &sym) {
    return readName(sym.symbol_name);

string readModule(MODULE_HEADER &module) {
    return readName(module.module_name);