Chapter 7. Debugging Machine Language Code
Prev |  | Next

## Chapter 7. Debugging Machine Language Code

This chapter explains how to debug machine language code by:

- “Examining and Changing Register Values”

- “Examining Memory and Disassembling Code”

- “Setting Machine-Level Breakpoints”

- “Continuing Execution After a Machine-Level Breakpoint”

- “Tracing Execution at the Machine Level”

- “Writing Conditional Commands at the Machine Level”

- “Stepping Through Machine Code”

## Examining and Changing Register Values

Using `dbx`, you can examine and change the hardware registers during execution of your program. Table 7-1 lists the machine form of the register names and the alternate software names as defined in the include file `regdef.h`.

Table 7-1. Hardware Registers and Aliases

Register | Software Name | Description
--- | --- | ---
$r0 | $zero | Always 0
$r1 | $at | Reserved for assembler
$r2... $r3 | $v0... $v1 | Expression evaluations, function return values, static links
$r4... $r7 | $a0... $a3 | Arguments
$r8... $r11 | $t0... $t7 $a4... $a7, $ta0... $ta3 | Temporaries (32 bit) Arguments (64 bit)
$r12... $r15 | $t4... $t7, $t0... $t3 $ta0... $ta3 | Temporaries (32 bit) Temporaries (64 bit)
$r16... $r23 | $s0... $s7 | Saved across procedure calls
$r24... $r25 | $t8... $t9 | Temporaries
$r26... $r27 | $k0... $k1 | Reserved for kernel
$r28 | $gp | Global pointer
$r29 | $sp | Stack pointer
$r30 | $s8 | Saved across procedure calls
$r31 | $ra | Return address
$mmhi |  | Most significant multiply/divide result register
$mmlo |  | Least significant multiply/divide result register
$fcsr |  | Floating point control and status register
$feir |  | Floating point exception instruction register
$cause |  | Exception cause register
$d0, $d2, ... $d30 $d0, $d2, ... $d31 |  | Double precision floating point registers (32 bit) (64 bit)
$f0, $f2, ... $f30 $f0, $f1, ... $f31 |  | Single precision floating point registers (32 bit) (64 bit)

For registers with alternate names, the `dbx` variable $regstyle controls which name is displayed when you disassemble code (as described in “Examining Memory and Disassembling Code”). If $regstyle is set to 0, then `dbx` uses the alternate form of the register name (for example, “zero” instead of “r0,” and “t1” instead of “r9”); if $regstyle is anything other than 0, the machine names are used (“r0” through “r31”).

### Printing Register Values

Use the ```printregs` command to print the values stored in all registers.

The base in which the register values are displayed depends on the values of the `dbx` variables $octints and $hexints. By default, `dbx` prints the register values in decimal. You can set the output base to octal by setting the `dbx` variable $octints to a nonzero value. You can set the output base to hexadecimal by setting the `dbx` variable $hexints to a nonzero value. If you set both $octints and $hexints to nonzero values, $hexints takes precedence.

To examine the register values in hexadecimal, enter the following:

    (dbx) set $hexints = 1
    (dbx) printregs
    r0/zero=0x0     r1/at=0x19050
    r2/v0=0x8       r3/v1=0x100120e0
    r4/a0=0x4       r5/a1=0xffffffad78
    r6/a2=0xffffffad88      r7/a3=0x0
    r8/a4=0x10      r9/a5=0x20
    r10/a6=0x0      r11/a7=0xfbd5990
    r12/t0=0x0      r13/t1=0x0
    r14/t2=0x65     r15/t3=0x0
    r16/s0=0x1      r17/s1=0xffffffad78
    r18/s2=0xffffffad88     r19/s3=0xffffffaf70
    r20/s4=0x0      r21/s5=0x0
    r22/s6=0x0      r23/s7=0x0
    r24/t8=0x0      r25/t9=0x10001034
    r26/k0=0x0      r27/k1=0x20
    r28/gp=0x1001a084       r29/sp=0xffffffaca0
    r30/s8=0x0      r31/ra=0x1000110c

    mdhi=0x0        mdlo=0xe0
    cause=0x24      pc=0x10001050
    fpcsr=0x0
    f0=0.0000000e+00        f1=0.0000000e+00        f2=0.0000000e+00
    f3=0.0000000e+00        f4=0.0000000e+00        f5=0.0000000e+00
    f6=0.0000000e+00        f7=0.0000000e+00        f8=0.0000000e+00
    f9=0.0000000e+00        f10=0.0000000e+00       f11=0.0000000e+00
    f12=0.0000000e+00       f13=0.0000000e+00       f14=0.0000000e+00
    f15=0.0000000e+00       f16=0.0000000e+00       f17=0.0000000e+00
    f18=0.0000000e+00       f19=0.0000000e+00       f20=0.0000000e+00
    f21=0.0000000e+00       f22=0.0000000e+00       f23=0.0000000e+00
    f24=0.0000000e+00       f25=0.0000000e+00       f26=0.0000000e+00
    f27=0.0000000e+00       f28=0.0000000e+00       f29=0.0000000e+00
    f30=0.0000000e+00       f31=0.0000000e+00
    d0=0.000000000000000e+00        d1=0.000000000000000e+00
    d2=0.000000000000000e+00        d3=0.000000000000000e+00
    d4=0.000000000000000e+00        d5=0.000000000000000e+00
    d6=0.000000000000000e+00        d7=0.000000000000000e+00
    d8=0.000000000000000e+00        d9=0.000000000000000e+00
    d10=0.000000000000000e+00       d11=0.000000000000000e+00
    d12=0.000000000000000e+00       d13=0.000000000000000e+00
    d14=0.000000000000000e+00       d15=0.000000000000000e+00
    d16=0.000000000000000e+00       d17=0.000000000000000e+00
    d18=0.000000000000000e+00       d19=0.000000000000000e+00
    d20=0.000000000000000e+00       d21=0.000000000000000e+00
    d22=0.000000000000000e+00       d23=0.000000000000000e+00
    d24=0.000000000000000e+00       d25=0.000000000000000e+00
    d26=0.000000000000000e+00       d27=0.000000000000000e+00
    d28=0.000000000000000e+00       d29=0.000000000000000e+00
    d30=0.000000000000000e+00       d31=0.000000000000000e+00

(Note that there are twice as many floating point registers with 64-bit programs.) You can also use the value of a single register in an expression by typing the name of the register preceded by a dollar sign ($).

For example, to print the current value of the program counter (the pc register), enter:

    (dbx) printx $pc
    0x10001050

### Changing Register Values

In the same way you change the values of program variables, you can use the ```assign` command to change the value of registers:

`assign` register `=` expression |  | Assigns the value of expression to register. You must precede the name of the register with a dollar sign ($).

For example:

    (dbx) assign $f0 = 3.14159
    3.1415899999999999
    (dbx) assign $t3 = 0x5a
    0x5a

By default, the `assign` register command changes the register value in the current activation level, which is a typical operation. To force the hardware register to be updated regardless of the current activation level, use the `$ set $framereg` command.

## Examining Memory and Disassembling Code

The forward slash (`/`) and question mark (`?`) commands allow you to examine the contents of memory. Depending on the format you specify, you can display the values as numbers, characters, or disassembled machine code. Note that all common forms of address are supported. Some unusual expressions may not be accepted unless enclosed in parentheses, as in (address)/count format.

The commands for examining memory have the following syntax:

address `````/` count format |  | Prints the contents of the specified address, or disassembles the code for the instruction at the specified address. Repeat for a total of count addresses in increasing address—in other words, an “examine forward” command. The format codes are listed in Table 7-2.
address ```?` count format |  | Prints the contents of the specified address or, disassembles the code for the instruction at the specified address. Repeat for a total of count addresses in decreasing address—in other words, an “examine backward” command. The format codes are listed in Table 7-2.
address `/` count `L` value mask |  | Examines count 32-bit words in increasing addresses; prints those 32-bit words which, when ORed with mask, equals value. This command searches memory for specific patterns.
`./` |  | Repeats the previous examine command with increasing address.
`.?` |  | Repeats the previous examine command with decreasing address. Table 7-2. Memory Display Format Codes
Format Code | Displays Memory in the Format
--- | ---
i | print machine instructions (disassemble)
d | print a 16-bit word in signed decimal
D | print a 32-bit word in signed decimal
dd | print a 64-bit word in signed decimal
o | print a 16-bit word in octal
O | print a 32-bit word in octal
oo | print a 64-bit word in octal
x | print a 16-bit word in hexadecimal
X | print a 32-bit word in hexadecimal
xx | print a 64-bit word in hexadecimal
v | print a 16-bit word in unsigned decimal
V | print a 32-bit word in unsigned decimal
vv | print a 64-bit word in unsigned decimal
L | like X but use with val mask
b | print a byte in octal
c | print a byte as character
s | print a string of characters that ends in a null byte
f | print a single-precision real number
g | print a double-precision real number

For example, to display 10 disassembled machine instructions starting at the current address of the program counter, enter:

    (dbx) $pc/10i
    *[main:26, 0x400290]    sw      zero,28(sp)
     [main:27, 0x400294]    sw      zero,24(sp)
     [main:29, 0x400298]    lw      t1,28(sp)
     [main:29, 0x40029c]    lw      t2,32(sp)
     [main:29, 0x4002a0]    nop
     [main:29, 0x4002a4]    slt     at,t1,t2
     [main:29, 0x4002a8]    beq     at,zero,0x4002ec
     [main:29, 0x4002ac]    nop
     [main:31, 0x4002b0]    lw      t3,28(sp)
     [main:31, 0x4002b4]    nop

To disassemble another 10 lines, enter:

    (dbx) ./
     [main:31, 0x4002b8]    addiu   t4,t3,1
     [main:31, 0x4002bc]    sw      t4,28(sp)
     [main:32, 0x4002c0]    lw      t5,24(sp)
     [main:32, 0x4002c4]    lw      t6,28(sp)
     [main:32, 0x4002c8]    nop
     [main:32, 0x4002cc]    addu    t7,t5,t6
     [main:32, 0x4002d0]    sw      t7,24(sp)
     [main:33, 0x4002d4]    lw      t8,28(sp)
     [main:33, 0x4002d8]    lw      t9,32(sp)
     [main:33, 0x4002dc]    nop

To examine ten 32-bit words starting at address 0x7fffc754, and print those whose least significant byte is hexadecimal 0x19, enter:

    (dbx) 0x7fffc754 / 10 L 0x19 0xff
    7fffc758:  00000019

Consider a single-precision floating point array named array. You can examine the six consecutive elements, beginning with the fifth element, by entering:

    (dbx) &array[4] / 6f
    7fffc748:  0.2500000 0.2000000 0.1666667 0.1428571
    7fffc758:  0.1250000 0.1111111

## Setting Machine-Level Breakpoints

`dbx` allows you to set breakpoints while debugging machine code just as you can while debugging source code. You set breakpoints at the machine code level using the ```stopi` command.

The conditional and unconditional versions of the `stopi` commands work in the same way as the `stop` command described in “Setting Breakpoints” with these exceptions:

- The `stopi` command checks its breakpoint conditions on a machine-instruction level instead of a source-code level.

- The `stopi at` command requires an address rather than a line number.

Each breakpoint is assigned a number when you create it. Use this number to reference the breakpoint in the various commands provided for manipulating breakpoints (for example, ```disable`, ```enable`, and ```delete`, all described in “Managing Breakpoints, Traces, and Conditional Commands”).

### Syntax of the stopi Command

The syntax of the `stopi` command is:

`stopi at` |  | Sets an unconditional breakpoint at the current instruction.
`stopi at` `address` |  | Sets an unconditional breakpoint at the specified address.
`stopi in` `procedure` |  | Sets an unconditional breakpoint to stop execution upon entering the specified procedure.
`stopi` [`expression`| `variable`] |  | Inspects the value before executing each machine instruction and stops if the value has changed. If the expression is of type pointer, look at the data pointed to and watch until it changes. If the expression is not of type pointer, look at the 32 bits at that address (assume the expression evaluates to an address).
`stopi` [`expression`| `variable`] `at` `address` |  | Inspects the value when the program is at the given address and stops if the value has changed (for machine-level debugging). If the expression is of type pointer, look at the data pointed to and watch until it changes. If the expression is not of type pointer, look at the 32 bits at that address (assume the expression evaluates to an address).
`stopi` [`expression`| `variable`] `in` `procedure` |  | Inspects the value at every machine instruction within a given procedure and stops if the value has changed. If the expression is of type pointer, look at the data pointed to and watch until it changes. If the expression is not of type pointer, look at the 32 bits at that address (assume the expression evaluates to an address).
`stopi` `if` `expression` |  | Evaluates the expression before executing each instruction and stops if the expression is true. Note that execution is very slow if you choose this type of conditional breakpoint.
`stopi` `at` `address` `if` `expression` |  | Evaluates the expression at the given address and stops if the expression is true.
`stopi` `in` `procedure` `if` `expression` |  | Evaluates the expression at every instruction within a given procedure and stops if the expression is true.
`stopi` [`expression1`| `variable`] `if` `expression2` |  | Tests both conditions before executing each machine instruction. Stops if both conditions are true. If `expression1` is of type pointer, look at the data pointed to and watch until it changes. If `expression1` is not of type pointer, look at the 32 bits at that address (assume the expression evaluates to an address).
`stopi` [`expression1`| `variable`] `at` `address` `if` `expression2` |  | Tests both conditions at the given address (for machine-level debugging). Stops if both conditions are true. If `expression1` is of type pointer, look at the data pointed to and watch until it changes. If `expression1` is not of type pointer, look at the 32 bits at that address (assume the expression evaluates to an address).
`stopi` [`expression1`| `variable`] `in` `procedure` `if` `expression2` |  | Tests the expression each time that the given variable changes within the given procedure. If `expression1` is of type pointer, look at the data pointed to and watch until it changes. If `expression1` is not of type pointer, look at the 32 bits at that address (assume the expression evaluates to an address).

| Note: When you stop execution because of a machine-level breakpoint set by one of the `stopi in` commands, a ```where` command at the point of stop may yield an incorrect stack trace. This is because the stack for the function is not completely set up until several machine instructions have been executed. `dbx` attempts to account for this, but is sometimes unsuccessful.

### Linking With DSOs

If you link with a DSO, be careful when you use the `stopi at` command. For example, suppose you enter:

`dbx()` `stopi at functionx`

The breakpoint at functionx is hit only if the gp_prolog instruction is executed. (`gp_prolog` is a short sequence of instructions at the beginning of the routine.)

To avoid this problem, use the `stopi in` command:

`dbx()` `stopi in functionx`

If you really want to use `stopi at`, a safe alternative is to disassemble functionx and put the breakpoint after the `gp_prolog`. For more information on `gp_prolog`, see the MIPSpro Assembly Language Programmer's Guide.

The `tracei at`, `wheni at`, and `conti at` commands described in the following sections also follow this pattern. Use their “`in`” versions to ensure that the function breakpoint is hit.

## Continuing Execution After a Machine-Level Breakpoint

The ``conti command continues executing assembly code after a breakpoint. As with the ```cont` command, if your program stops because `dbx` catches a signal intended for your program, then `dbx` sends that signal to your program when you continue execution. You can also explicitly send a signal to your program when you continue execution. Signal processing and sending signals to your program is discussed in “Using Signal Processing”.

The syntax of the `conti` command is:

`conti` [ `signal`] |  | Continues execution with the current instruction.
`conti` [ `signal`] {`at` | `to`} `address` |  | Sets a temporary breakpoint at the specified address, then resumes execution with the current instruction. When your program reaches the breakpoint at `address`, `dbx` stops your program and deletes the temporary breakpoint.
`conti` [ `signal`] `in` `procedure` |  | Sets a temporary breakpoint to stop execution upon entering the specified procedure, then resumes execution with the current instruction. When your program reaches the breakpoint in `procedure`, `dbx` stops your program and deletes the temporary breakpoint.

See also “Linking With DSOs” for a description on using the `conti in` and `conti at` commands with DSOs.

## Tracing Execution at the Machine Level

The ```tracei` command allows you to observe the progress of your program while debugging machine code, just as you can with the `trace` command while debugging source code. The `tracei` command traces in units of machine instructions instead of in lines of code.

Each trace is assigned a number when you create it. Use this number to reference the breakpoint in the various commands provided for manipulating breakpoints (for example, ```disable`, ```enable`, and ```delete`, all described in “Managing Breakpoints, Traces, and Conditional Commands”).

The syntax of the `tracei` command is:

`tracei` [`expression`| `variable`] |  | Whenever the specified variable changes, `dbx` prints the old and new values of that variable. (For machine-level debugging.) Note that execution is very slow if you choose this type of trace. If the expression is of type pointer, look at the data pointed to and watch until it changes. If the expression is not of type pointer, look at the 32 bits at that address (assume the expression evaluates to an address).
`tracei` `procedure` |  | This command is equivalent to entering `trace` `procedure`. `dbx` prints the values of the parameters passed to the specified procedure whenever your program calls it. Upon return, `dbx` prints the return value.
`tracei` [`expression`| `variable`] `at` `address` |  | Prints the value of the variable whenever your program reaches the specified address. (For machine-level debugging.) If the expression is of type pointer, look at the data pointed to and watch until it changes. If the expression is not of type pointer, look at the 32 bits at that address (assume the expression evaluates to an address).
`tracei` [`expression`| `variable`] `in` `procedure` |  | Whenever the variable changes within the procedure that you specify, `dbx` prints the old and new values of that variable. (For machine-level debugging.) If the expression is of type pointer, look at the data pointed to and watch until it changes. If the expression is not of type pointer, look at the 32 bits at that address (assume the expression evaluates to an address).
`tracei` [`expression1`| `variable`] `at` `address` `if` `expression2` |  | Prints the value of the variable whenever your program reaches the specified address and the given expression is true. (For machine-level debugging.) If `expression1` is of type pointer, look at the data pointed to and watch until it changes. If `expression1` is not of type pointer, look at the 32 bits at that address (assume the expression evaluates to an address).
`tracei` [`expression1`| `variable`] `in` `procedure` `if` `expression2` |  | Whenever the variable changes within the procedure that you specify, `dbx` prints the old and new values of that variable, if the given expression is true. (For machine-level debugging.) If `expression1` is of type pointer, look at the data pointed to and watch until it changes. If `expression1` is not of type pointer, look at the 32 bits at that address (assume the expression evaluates to an address).

See also “Linking With DSOs” for a description on using the `tracei in` and `tracei at` commands with DSOs.

## Writing Conditional Commands at the Machine Level

Use the ```wheni` command to write conditional commands for use in debugging machine code. The `wheni` command works in the same way as the when command described in “Writing Conditional Commands”. The command list is a list of`dbx` commands, separated by semicolons. When the specified conditions are met, the command list is executed. If one of the commands in the list is `stop` (with no operands), then the process stops when the command list is executed.

`wheni if` expression `{`command-list`}` |  | Evaluates the expression before executing each machine instruction. If the expression is true, executes the command list.
`wheni at` address `if` expression `{`command-list`}` |  | Evaluates the expression at the given address. If the expression is true, executes the command list.
`wheni in` procedure `if` expression `{`command-list`}` |  | Evaluates the expression in the given procedure. If the expression is true, executes the command list.
`wheni` variable `at` address `if` expression `{`command-list`}` |  | Tests both conditions at the given address. If the conditions are true, executes the command list. (For machine-level debugging.) If the expression is of type pointer, look at the data pointed to and watch until it changes. If the expression is not of type pointer, look at the 32 bits at that address (assume the expression evaluates to an address).
`wheni` variable `in` procedure `if` expression `{`command-list`}` |  | Tests both conditions at every machine instruction within a given procedure. If they are true, executes the command list.

See also “Linking With DSOs” for a description on using the `wheni in` and `wheni at` commands with DSOs.

## Stepping Through Machine Code

The ``stepi and ````nexti commands allow you to step through machine code in much the same way as you can with the `step` and `next` commands while debugging source code. The `step` and `next` commands step in units of machine instructions instead of in lines of code.

The formats of the `nexti` and `stepi` commands are:

`nexti` [ `integer`] |  | Executes the specified number of machine instructions, stepping over procedures. If you do not provide an argument, `nexti` executes one instruction. If `nexti` encounters any breakpoints, even in procedures that it steps over, it immediately stops execution.
`stepi` |  | Single steps one machine instruction, stepping into procedures (as called by jal and jalr). If `stepi` encounters any breakpoints, it immediately stops execution.
`stepi` [ `n`] |  | Executes the specified number of machine instructions, stepping into procedures (as called by `jal` and `jalr`).

The values of the `dbx` variable $stepintoall affects the `stepi` and `nexti` commands just as they do the `step` and `next` commands. See “Stepping Through Your Program” for a discussion of these variables.

For the `stepj` command, `dbx` determines the next jump by reading through the instructions until it finds a `jump` or `jal` machine instruction. Because it ignores conditional branches, it does not necessarily follow the sequence of the program's execution to find the “next” jump. This means that it may not stop where you expect.

Prev | Table of Contents | Next
Chapter 6. Controlling Program Execution |  | Chapter 8. Multiple Process Debugging