5.2 Debuggers for NLMs

Sometime during your code development, you will need to use a debugger. The following debugging tools are available to facilitate your development of high-quality software:

Additionally, the linkers can be used to generate a map file, which is a memory map of your program.

5.2.1 Linking Debug Information with WLINK

You can specify the DEBUG directive in a linker directive file to generate debugging information in the executable file. The following options can be specified with the DEBUG directive to generate the following types of debugging information:

DEBUG ALL

Generates all types of debugging information (global symbol, line numbering, local symbol, and typing).

DEBUG NOVELL

Generates global symbol debugging information that can be processed only by the NetWare Internal Debugger

DEBUG NOVELL ONLYEXPORTS

Generates NetWare global symbol information for exported symbols only.

DEBUG ONLYEXPORTS

Generates WVIDEO global symbol information for exported symbols only.

5.2.2 NetWare Internal Debugger

The NetWare internal debugger is an assembly language debugger that is built into the NetWare operating system. This debugger is a command-line debugger that does not display source code. To use the internal debugger, you should have some knowledge of 80386 assembly language and stack-based parameter passing.

The internal debugger was designed specifically to debug NLMs. It includes a set of supplementary commands that are customized for NLMs, such as .A (display abend or break reason) and .P (display all process names and addresses). These are not part of a typical debugger. The internal debugger allows resident debugging, in which the debugger and the test application run on the same server. In addition, the internal debugger provides a way to debug multiple NLMs concurrently.

NOTE:The NDK includes debug versions of the libraries. Debug records are linked in with each of these NLMs, allowing better visibility to developers using the internal debugger.

You can access the NetWare Internal Debugger in any of the following ways:

  • At the server console, simultaneously press the following keys: Left-shift+Right-shift+Alt+Esc.

    NOTE:If the SECURE CONSOLE command is in effect, you cannot access the NetWare Internal Debugger from the keyboard.

  • From a C language program, call Breakpoint.

  • From an assembly language program, issue an INT 3 instruction.

You can then set execution breakpoints, single-step through program execution, examine the contents of memory, and so on.

Some points to be aware of when using the internal debugger are:

  • NetWare runs in the 386 protected mode, using a flat memory model. In a flat memory model, the values in the segment registers do not change once they are initialized by the NetWare operating system. Since they do not change, the internal debugger does not display them.

  • The internal debugger supports program global symbolic information; it does not support local symbolic information. Any symbols that you want to reference from the internal debugger must be system-wide globals. To access symbolic information, the program must be linked with the DEBUG option.

  • The internal debugger is case-sensitive to symbols.

  • All numbers are entered and displayed in hexadecimal format.

  • Bytes, words, double-words, and pointers are pushed onto the stack as 4-byte parameters.

For more specific information on the debugger, see the following:

Debugger Commands

You can recall commands from the NetWare Internal Debugger’s command line buffer by pressing the Up-arrow key. After recalling a command from the command-line buffer, you can edit it. The Right- and Left-arrow keys move the cursor. Insert toggles overwrite. Some of the commands can be repeated by pressing the Enter ; these cases are noted in the command descriptions.

NOTE:If you decide to cancel a command, the Esc key acts like the Enter key. You must use the Delete or Backspace key to erase the command line.

There are four types of help commands in the NetWare Internal Debugger:

  • HE-Help on expressions

  • HB-Help on breakpoints

  • H-General Help

  • .H-Help with the supplementary commands

In the command summaries, a pair of square brackets in the Command indicates an optional parameter. The following categories of commands are available:

Supplementary Commands

The following table lists supplementary commands.

Command

Description

.a

Displays the abend or break reason.

.c

Does a diagnostic core dump to diskette (this can take a great number of diskettes).

.d [address]

If no address is specified, displays a page directory map for the current debugger domain.

When address is specified, displays page entry map for the current debugger domain.

.h

Displays help information about the supplementary commands.

.l offset [offset]

Displays linear address given page map offsets.

.lx address

Displays page offsets and values used for translations.

.m

Displays the names and addresses of the loaded modules.

.p [address]

If no address is specified, displays process (thread) names and addresses.

If address is specified, displays address as a process (thread) control block.

You can use this command to determine what a particular thread is doing. For example, you can examine the values on the stack, which contain return addresses for called functions, to determine what an inactive task is doing (waiting on a semaphore, waiting on keyboard input, and so on). That is, you can construct a "trail" of functions that have been called.

This command now displays the semaphore address when listing processes waiting on a semaphore.

.r

Displays running process (thread) control block. This command displays information about the running thread in the same format as the .p address command.

.s [address]

If address is not specified, displays all screen names and their addresses.

If address is specified, displays the specified address as a screen structure.

A pointer value obtained by the .s command is used as the address parameter. The command .s address is another way to get information about the current activity of a sleeping thread.

.sem [semaphore address]

If an address is not specified, lists all semaphores that have processes waiting on them.

If a semaphore address is specified, displays detailed information about the semaphore.

.t

Toggles the "developer option" on or off.

.v

Displays server version.

Breakpoint Commands

The following lists breakpoint commands.

b

Displays all current breakpoints.

bc number

Clears the specified breakpoint.

bca

Clears all breakpoints.

b = address [( condition )]

Sets an execution breakpoint at address.

Example : Breakpoint if MyFunction is called and the first parameter on the stack is equal to 0:

  b = MyFunction [desp+4] == 0
  
br = address [( condition )]

Sets a read or write breakpoint at address.

Example : To check if the code (in the range 14500 to 15500) ever reads or writes to memory location 160FE:

  br = 160FE EIP >= 14500 && eip <= 15500
  
bw = address [( condition )]

Sets a write breakpoint at address.

Example : To check if the code (in the range 14500 to 15500) ever writes to memory location 160FE:

  bw = 160FE EIP >= 14500 && eip <= 15500
  
General Debugger Commands

The following lists the general debugger commands.

c address

Interactively changes memory.

c address=numbers

Changes memory, starting at address, to numbers.

Example : Change byte values starting at 10DFAB to FF, FE, 22.

  c 10DFAB = FF,FE,22
  
c address = "text"

This command is currently not supported.

d address [length]

Dumps length bytes of memory starting at the address. If length is not specified, 256 (decimal) bytes are dumped.

Example : Dump 16 (decimal) bytes at address 00088F20.

  d 88F20 10
  

This command can be repeated by pressing Enter. You can visually scan for a string in the ASCII portion of the dump display by dumping a memory location and then repeatedly pressing Enter to display contiguous blocks of memory.

dl[+ linkOffset ] addr [length]

Traverses a linked list. If length is not specified, 256 (decimal) bytes are dumped.

Example : Suppose the first node in a linked list starts at 50 and the offset of the address of the next node is at offset 4.

To traverse the linked list, displaying 16 (decimal) bytes each time, enter the following command.

  dl+4 50 10
  

To display each successive node in the list, press Enter.

The default link offset is 0, which indicates the end of the list. Thus, dl 50 10 uses a link offset of 0.

This command can be repeated by pressing Enter. You can dump the first node in a linked list and then dump each successive node by pressing Enter. A NULL link marks the end of the list.

f flag=value

Changes the specified flag. value can be 0 or 1.

Example : To change the specified flag to the new value (0 or 1), where flag is CF, AF, ZF, SF, IF, TF, PF, DF, or OF:

  f CF = 0
  
g

Specifies a "Go" instruction, starting from the current EIP.

g [break_addresses]

Specifies a "Go" instruction, starting at the current EIP and ending at the break address or addresses.

Example : Suppose a code breakpoint has just occurred at the start of a C function. To resume execution until the function returns to its caller, use the following command:

  g [desp]
  
h

Displays general help.

hb

Displays breakpoint help.

he

Displays expressions help.

i[b,w,d] port

Inputs a byte, word, or double-word from the specified port. The default is a byte.

Example : To input the value at port 2F0:

  i 2F0
  
m start [L length] pattern

Memory search is currently not supported.

n

Lists all symbol names, also displaying the NLMs defined them.

n symbolname value

Defines a new symbol name at an address.

Example : To give the value 2D46A5 the name x:

  n x 2D46A5
  

Now x can be referenced with other commands, such as:

  b=x, b=x+5, u x.
  

By default, the value is 10. Symbols can be defined with the n command. The y option when the server is started is used to override the default.

n-symbolname

Removes a user-defined symbol name.

n-

Removes all user-defined symbol names.

o[b,w,d] port = value

Outputs byte, word, or double-word to the specified port.

Example : To output 10h to port 320h:

  o 320=10
  
p

Single-steps through the program code; proceeds past calls. (See the s command for stepping into calls.)

This command can be repeated by pressing Enter. A common usage for this command is to run until you hit a breakpoint, and then single-step by entering the p command and then pressing Enter repeatedly to continue single-stepping. By holding down Enter, you can quickly single-step through the program code.

q

Quits to DOS.

r

Displays the registers and flags.

REG = value

Changes the register to the specified value. The registers are EAX, EBX, ECX, EDX, ESI, EDI, EBP, EIP, and EFL.

s

Single-steps through the program code; steps into a call. (See the p command for stepping past calls.)

This command can be repeated by pressing Enter. You can hit a breakpoint and single-step by entering the s command and then pressing Enter repeatedly to continue single-stepping. By holding down Enter, you can quickly single-step through the program code.

t

Same as s.

u address [count]

Disassembles count instructions. If you type u by itself and press Enter, the starting address is assumed to be the contents of EIP, and 16 (decimal) bytes will be disassembled.

Example : Disassemble 16 (decimal) bytes prior to the current instruction.

  u eip-10
  

NOTE:A command such as this might not cause the disassembly to fall on an instruction boundary.

This command can be repeated by pressing Enter. You can disassemble starting at any memory location by initially entering the u command and then pressing Enter to continue the contiguous disassembly.

v

Displays the server’s screen(s) for viewing. Each time a key is pressed, the next screen is displayed. See the .s command.

x

Exchanges processor stack frames.

z expression

Evaluates the expression.

Example: To display the value at the address computed by adding EBP to EBX shifted right 16 (decimal) times.

  z [ d EBP + (EBX >> 10) ]
  
? [address]

Displays nearest symbols to address. If address is not given, EIP is used.

Example : To determine the NLM and function owning the current instruction, type the following:

  ?
  
SFT III Debugger Commands

The following table lists the debugger commands that are available for the SFT III™ OS only.

Command

Description

.?

Display server state.

DQ

Dump level 3 queue pointers.

DQ address

Dump level 3 queue elements.

Setting Breakpoints

With the NetWare Internal Debugger, you can set execute, write, or read/write breakpoints.

There are four breakpoint registers, allowing a maximum of four simultaneous breakpoints. Breakpoints can be permanent or temporary:

  • To set permanent breakpoints, use the b, br, and bw commands. For permanent breakpoints, you can attach a condition that specifies whether to take the breakpoint. If the condition is true, a breakpoint is taken. If it is false, execution continues without stopping.

  • To set temporary breakpoints, use the g command. For example, a "go" to a specific address is a temporary breakpoint. The p command can also set a temporary breakpoint if the current instruction cannot be single-stepped.

    If you use all four breakpoints and issue a g [desp] command, the following is displayed:

         Go out of breakpoints
      

    If you use all four breakpoints and attempt to proceed past a function call using the p command, the following is displayed:

         Breakpoint not available for proceed
      

    The assembly repeat instructions (such as REPE), the LOOP instruction, and the CALL instruction also require p to set a temporary breakpoint.

Specifying Expressions

The NetWare Internal Debugger determines the order of execution of an expression in accordance with the following:

  • Precedence of grouping operators

  • Precedence of unary, binary, and ternary operators

  • Common algebraic ordering

The following sections explain the various types of operators:

Grouping Operators

The grouping operators ( ), [ ], and { } indicate to the debugger the desired grouping of operations. These operators have the highest precedence (0).

( )

(expression)

The terms inside the parentheses are evaluated first. In the case of parenthetical expressions that are nested, evaluation begins with the innermost parenthetical expression.

[ ]

[size expression]

expression is evaluated first and then used as a memory address. The size specifier can be B,W, or D. The expression evaluates to byte, word, or double-word at the specified address.

For example: Suppose the data at memory location 178D10 is the following byte sequence in Intel storage format 38 F9 99 88. Then, using the z command, which evaluates expressions:

Z [D 178D10]

evaluates to

8899F938

Z [W 178D10]

evaluates to

F938

Z [B 178D10]

evaluates to

38

{ }

{size expression}

expression is evaluated first and then used as a port address. The size specifier can be B,W, or D. The expression evaluates to byte, word, or double-word from the port.

Unary Operators

The unary operators have precedence 1.

Symbol

Description

!

Logical not

-

2’s complement

~

1’s complement

Binary Operators

The binary operators in the following table are ordered from lowest to highest precedence.

Symbol

Description

Precedence

*

Multiply

2

/

Divide

2

%

Mod

2

+

Add

3

-

Subtract

3

>>

Bit shift right

4

<<

Bit shift left

4

>

Greater than

5

<

Less than

5

>=

Greater than or equal to

5

<=

Less than or equal to

5

==

Equal to

6

!=

Not equal to

6

&

Bitwise AND

7

^

Bitwise XOR

8

|

Bitwise OR

9

&&

Logical AND

10

||

Logical OR

11

Ternary Operators

If expression1 is true, the result is the value of expression2; otherwise, the result is the value of expression3.

     expression1 ? expression2 , expression3
  

In the following example, a break is taken on

     myFunction( char *myData )
  

if the carry flag (FLCF) is true and EAX contains 9C, or if the carry flag is false and the first byte of myData is 0:

     b = myFunction ( FLCF ? eax == 9c, [b [desp+4]] == 0 )
  
Registers and Flags

The 80386 registers, which can be used in expressions, are referenced by the names listed in the following table.

Register

Name

EAX

Accumulator register

EBX

Base register

ECX

Count register

EDX

Data register

ESI and EDI

Index registers

ESP and EBP

Base and stack pointer registers

EIP

Instruction pointer register

The flags register is a 32-bit register that contains a number of status bits. This register is sometimes referred to as the status register. The following table lists the flags register bits.

Flag Bit

Name

FLCF

Carry flag

FLAF

Auxiliary carry flag

FLZF

Zero flag

FLSF

Sign flag

FLIF

Interrupt flag

FLTF

Trap flag

FLPF

Parity flag

FLDF

Direction flag

FLOF

Overflow flag