386SWAT Symbolic Debugging

This document covers the basics of symbolic debugging support for 386SWAT. The 386SWAT API specifics are documented in VCPIDBG.DOC; here we will describe actual usage of 386SWAT for symbolic debugging, and the tools needed to load symbols.

386SWAT Profile Options
Using symbols
New Commands Within 386SWAT
Utility Programs

386SWAT Profile Options

Several profile options are new to 386SWAT:


Specify number of address bits to use for address hash table. Values less than 8 and over 12 are ignored.

Address hash table storage in bytes is 2 ^ (ADDRHBITS + 2). The minimum is therefore 1K and the maximum, 16K.

ADDRHBITS defaults to 12. When symbols are present, all possible addresses in the disassembly screen are searched against the address hash table. Possible hits require traversal of the address hash buckets chain, similar to name hash bucket traversal. A "next address bucket" pointer is part of the symbol record. Thus the set of symbol records forms two interleaved linked lists: the address bucket chain and the name bucket chain.

The number of bits specified by ADDRHBITS are taken from a resolved linear address, shifted left 2 bits, and used as an index into the table of dword pointers based at the address contained in SYMNHASH. The address pointed to is an offset from the address in SYMBASE, and points to the beginning symbol record in that address bucket chain. If the value is -1 (FFFFFFFF), there is no such address.

Smaller address hash tables may significantly impact unassembly.


Specify number of 1K blocks to be used for symbol name hashing. Values less than 1 and over 255 are ignored.

To speed symbol name searches, all names are hashed using the following algorithm:

extern unsigned int HASHPRIME;

int hashpjw (char *s)
  char *p;
  unsigned long h=0, g;

  for (p=s; *p; p++)
     h = (h << 4) + (unsigned) tolower (*p);
     if (g = (h & 0xf0000000))
       h = h ^ (g >> 24);
       h = h ^ g;

  return ((int) (h % HASHPRIME));

HASHPRIME is derived from a table of prime numbers closest to multiples of 256. Here are some examples:

    1         257
    2         509
    3         769
  254       65027
  255       65287

The storage occupied by the name hashing table is HASHPRIMES[BUCKETS-1] * 4, or approximately BUCKETS * 1K.

When a hash value is calculated from a name, that value is shifted left 2 bits and used as an index into the table of dword bucket pointers based at the address contained in SYMNHASH. A bucket pointer may contain -1 (FFFFFFFF) or an offset from the address contained in SYMBASE. In this case, a "bucket" actually points to a symbol record. Next name pointers in each symbol record form a linked list which may be traversed to find an exact name match.

The smaller the BUCKETS value, the longer these bucket chains will be, thus increasing name search time. This is only likely to be a performance hit when loading symbols, since 386SWAT checks for existing symbols by name. Searches for symbols entered at the command line would not be visibly impacted by longer searches.


Enable file I/O functions within 386SWAT. This is needed for using the file browser, and for source level debugging.


Enables file I/O (same as LOADSYM). Also loads symbols from the specified .SSF file (created by MAPSSF) at initialization time.


Specify search path for source browser. If not specified, only the current directory is searched. If a path is specified, the current directory (.) must be included explicitly. For example, PATH=c:\qui\quilib,c:\qui\inst will not search the current directory for source files.


Specify how far 386SWAT continues proximity searches and with what granularity. The default is 1 word. The high order byte of PROXSRCH contains the granularity in bytes, and the low order byte contains the number of units to search. The default is 0201h. 0108h would do byte granular searches from x+1 to x+8.

Proximity searches should not visibly impact disassembly performance. A separate check for an empty symbol table ensures that no time is wasted in unnecessary calls to the symbol search routine (which has its own check for empty tables). No proximity searches are done for label display and data display; this only affects values within the disassembly.

The command line option PS allows these settings to be changed on the fly.


This option sets the number of bytes to be used for the 386SWAT symbol table. The internal format of 386SWAT's symbol table is 12 bytes larger than the symbol table used for the 386SWAT API calls. Each public or line number occupies 23 bytes plus the length of the symbol name.

SYMSIZE is used to calculate a default value for BUCKETS (described below). If BUCKETS is not explicitly defined in the profile, it will be set to min (255, max (1, SYMSIZE / 4096)).

If SYMSIZE is not specified, it will default to 4096 bytes. The default BUCKETS value (if also not specified) will then be 1. See below for BUCKETS storage requirements.

Use SYMSIZE=0 to minimize symbol storage requirements. This will save 4K over the default.

Using symbols

Any symbol may be used as an effective address. For example,

dd 180|{SYMHASH

will display the symbol address hash table. Symbol display occurs automatically if the symbols are loaded properly. See sections below on SWATRUN and MAPSSF.

Symbols are displayed in the Unassemble (F9) screen. Code addresses may be displayed as public names or line numbers (if line numbers were specified with the linker's /LI option). Addresses within the unassembly are handled as intelligently as possible. For example,

mov ax,[bx+7c39]

may be displayed as

mov ax,[bx+FOOBAR]

if FOOBAR's offset is 7C39 and the default segment/selector (DS in this case) matches the segment/selector specified for the symbol FOOBAR.

386SWAT can now resolve addresses CLOSE TO symbols; a construction commonly seen in C code might be:

mov [FOOBAR],bx
mov [7c3b],es

386SWAT will disassemble this as

mov [FOOBAR],bx
mov [FOOBAR+02],es

Another useful feature would be to turn off case sensitivity when searching for symbol matches. This is more of an issue when debugging C code. Again, it could be settable in the profile as well as from the command line.

New Commands Within 386SWAT

The Translate Symbols (TS) command may be used to adjust segment/selector values, as well as to add a constant to symbol offsets, after the symbols are loaded. This is needed for debugging the VxD. The VxD symbols would be loaded with MAPSSF, using a .wsg file to assign selector values and group ID's to the different linker groups. Once SWATVXD has located the offsets of the different VxD groups, they may be translated one at a time.

For example, the VxD's DGROUP is assigned selector 28, group ID 2001, and IGROUP is assigned selector 30, group ID 2002. The .wsg file entries are

2001  P  28    DGROUP    ; Comments
2002  P  30    IGROUP    ; More comments

We load the .MAP file before entering Windows:


We find that the offsets within the Windows flat model segment are

DGROUP  8001BE24
IGROUP  8020397C

The TS command is used to translate both groups:

ts 28 2001 P 28 8001BE24
ts 30 2002 P 30 8020397C

The complete syntax of the TS command is

TS osel ogrp nflag nsel nbase

osel Old selector value
ogrp Target group value (unless specified in .wsg file, 0)
nflag P for PM symbols
V for VM symbols
nsel New selector value (in above example, unchanged)
nbase Base value to be added to all symbol offsets

The Query Symbol (QS) command may be used to display on the command line the name and distance from a given address. For example,

QS .code

might display as

QS .code = VWIN32_Code0001+00005678

Utility Programs


This program is called by SWATRUN to load .MAP files on the fly. It may also be used to produce .SSF files for loading at CONFIG.SYS time (not currently supported by 386SWAT). .SSF files may also be loaded directly by SWATRUN. This is faster than calling MAPSSF every time symbols are loaded.

Besides reading .MAP files, MAPSSF will also process Windows SYM32 files. MAPSSF has been tested with the following .MAP file formats:


MAPSSF [options] fspec1 [fspec2 [...]]

All options are preceded with - or /

-g#,# restrict symbol group types to specified #'s (SYM32 files only). Only symbols with group types matching one of these values will be loaded. The group type in SYM32 files is NOT the same as the group ID defined in a .wsg file.
-l ignore line number information in .MAP file. This option may be used to suppress loading of line number symbols when space is at a premium. Typically, line numbers take about 80% of symbol space.
-o overwrite existing symbol table (default is append). Normally, new symbols are added to 386SWAT. Existing symbols will be updated. This option forces the symbol table to be flushed at the beginning of MAPSSF.
-sxxxxm set selector to xxxx hex, where m is
v for V86/real mode
v+ for V86/real mode, add value to segment,
p for protected mode
The -sxxxxv+ form is used by SWATRUN to add a fixup to all segment values.
-tfname echo 386SWAT tables to file fname. This option may be used to create a file in 386SWAT symbol format (.SSF file).
-wfname[.wsg] read Windows Symbol Group file fname.wsg. This feature may be used for .MAP files or for .SYM files. As an example, the QMAX.WSG file contains:
group   mode  sel  group[!segment]  ; comment
 3000    P     50  PGROUP           ; 386MAX code
 3001    P     18  PGROUP!EDATA     ; 386MAX data
 3001    P     18  PGROUP!VALSEG    ; 386MAX data
    0    D      0  PGROUP!NCODE     ; 386MAX non-resident code
    0    D      0  PGROUP!NDATA     ; 386MAX non-resident data
 3002    P    118  IGROUP!ICODE     ; 386MAX extra code
 3003    P    120  IGROUP!IDATA     ; 386MAX extra data
    0    D      0  XGROUP           ; 386MAX non-resident code/data
    0    D      0  PSPGRP           ; PSP group (non-resident)
 3004    P     70  PDTGRP           ; OFFPDT group (uninitialized)

Note that all groups listed with unique segment values in the Group section of the .MAP file are listed. The real mode groups a XGROUP and PSPGRP will not be loaded.

Association of public symbols with groups is sometimes not sufficient. Both code and data are in PGROUP, but different selectors are used. We therefore specify the individual data segments within PGROUP that will be assigned selector 18h (EDATA and VALSEG). Since segments are more completely specified in the MAP file than groups, MAPSSF will check for segment matches before checking for group matches. We also use this mechanism to avoid loading the NCODE and NDATA segments within PGROUP.

Group matches are made by checking group origin values, which are sorted by value in descending order, against addresses. ALL groups which COULD include an address, from highest to lowest, are checked for inclusion in the .wsg file. The first match determines what changes are made to the matching symbol address.

-v sort symbols by value (default is by name). This option controls which section of the .MAP file is read. If included on the command line, the "By Value" section is read. By default, the "By Name" section is read. By controlling the order in which symbols are passed to 386SWAT, the display in 386SWAT's Ctrl-F6 screen may be sorted by name or by value.
-x# set debugging level to # (default is 0 -- no messages). This is useful for debugging only.
-y default to recognizing files as SYM32 format. Normally, MAPSSF recognizes files as .MAP or .SYM by their extension. If neither extension is used, this option forces recognition as SYM32.


Syntax: Device=d:\path\swatcmd.exe command
  or       swatcmd command

SWATCMD can be loaded via Device= or from the DOS command line. It may be used to pass commands to 386SWAT under program control. Invoking SWATCMD with no options brings up 386SWAT at an INT 03h (assuming TRAPSKIP or TRAPDEBUG is active).


Syntax: SWATRUN [options] progname [arguments]

SWATRUN is a program loader for debugging V86 mode programs. It creates a breakpoint before the first instruction of the target program, and will also attempt to load symbols into 386SWAT via the VCPI debugger API (VCPIDBG.DOC) with the appropriate segment fixup.

If no file extension is specified, SWATRUN looks first for a .COM file then an .EXE file. The filename specified is converted into a fully qualified pathname including drive letter, directory, and extension. SWATRUN will load this file using DOS function 4B01 to determine the segment fixup.

SWATRUN will then look for a matching .SSF file, and if found, will load it directly into 386SWAT. If an .SSF file is not found, SWATRUN will look for a matching .MAP file. If a .MAP file is found, SWATRUN will invoke MAPSSF with the proper parameters to fixup and load the parsed .MAP file. See the section on MAPSSF for further information on .MAP file formats supported and on creation of .SSF files. .SSF file loading is faster, but does not currently report symbol allocation table shortages. If your symbols do not appear to be loaded from an .SSF file, delete the .SSF file. SWATRUN will try to load symbols from the .MAP file with MAPSSF. MAPSSF will report any changes required to the 386SWAT.PRO file.

Valid SWATRUN options are

/b Generate INT 01h at beginning of SWATRUN. This is useful only for debugging the loading process
/mMAPFILE Load MAPFILE instead of progname.map. If no .SSF file is present, SWATRUN constructs a map file name by taking the path and basename of the .EXE or .COM file and adding .MAP. This filename is passed to MAPSSF if it exists. This option may be used to specify a map file on another drive:directory.
/n Do not load symbols. If SWATRUN has already run once, the target program did not go resident, and symbols were loaded, run SWATRUN with this option on subsequent invocations.
/o Overwrite existing 386SWAT symbols. This option is passed to MAPSSF. Any existing symbols will be destroyed.
/sSYMFILE Load SYMFILE instead of progname.ssf. SWATRUN normally constructs a .SSF file name by taking the path and basename of the .EXE or .COM file and adding .SSF. If this file exists and is in the proper format created by MAPSSF, it will be loaded into 386SWAT.
/? Display a help message.