Step 5 Watch It Run in the Debugger

Assuming that you entered Listing 5-1 correctly (or unpacked it from the listings archive), there are no bugs in eatsyscall.asm. That's an uncommon circumstance for programmers, especially those just starting out. Most of the time you'll need to start bug-hunting almost immediately. The easiest way to do this is to load your executable file into a debugger so that you can single-step it, pausing after the execution of each machine instruction in order to see what effect each instruction has on the registers and any variables defined in memory.

Two programs work together to provide you with an enjoyable (well, tolerable) debugging experience: gdb and KDbg. The gdb utility does the way-down-deep CPU magic that debuggers do, and KDbg arranges it all nicely on your display and allows you to control it. To kick off a debugging session, invoke KDbg from the terminal window command line, followed by the name of your executable file:

kdbg eatsyscall

KDbg is not so nearly mute as NASM and ld. It's a KDE app, and puts up a nice graphical window that should look very much like what's shown in

Figure 5-13. The eatsyscall program source code should be displayed in the center pane. If the top pane doesn't say ''Register'' in its upper-left corner, select View ^ Registers and make sure the Registers item has an X beside it.

Figure 5-13: KDbg's startup window

To make a little more room to see your source code, close KDbg's bottom pane. Click the small X in the upper-right corner of the pane. KDbg is capable of displaying a lot of different things in a lot of different windows; but for this quick run-through, having the source code pane and the registers display pane will be enough. Having other windows open will simply confuse you.

If you recall from the overview earlier in this chapter, debuggers need to be told where to stop initially, before you can tell them to begin single-stepping a program—otherwise, they will scoot through execution of the whole program too quickly for you to follow. You have to set an initial breakpoint. The way to do this is to scroll the source code display down until you can see the code that follows the label _start: at the left edge of the program text. Move down two lines and left-click in the empty space at the left edge of the source code pane, between the window's frame and the plus symbol. A red dot should appear where you clicked. This red dot indicates that you have now set a breakpoint on that line of code, which in this case is the instruction mov eax,4 . (Make sure you insert a breakpoint at this instruction, and not at the nop instruction immediately above it in the program!)

Once you have the initial breakpoint set, click the Run button in the top toolbar. The button looks like a page with a downward-pointing arrow to its left. (Hover the mouse pointer over the button and it should say Run.) Two things will happen, essentially instantaneously (see Figure 5-14):

The red dot indicating the breakpoint will be overlain by a green triangle pointing to the right. The triangle indicates the place in the program where execution has paused, and it points at the next instruction that will execute. Note well that the instruction where execution pauses for a breakpoint has not been executed yet.

■ The top pane, which was blank previously, is now filled with a listing of the CPU registers. It's a longish list because it includes all the CPU flags and floating-point processor registers, but you only need to see the top group for this demo.

At this point, the general-purpose registers will all be shown containing zeroes. Your program has begun running, but the only instruction that has run yet is the nop instruction, which does ... nothing. (It's a placeholder, and why it's here in this program will have to wait until the next chapter.)

This will soon change. To do the first single-step, click the Step Into By Instruction button. Hover the mouse pointer over the buttons until you find it. (The button has the focus in Figure 5-14.) As the name of the button suggests, clicking the button triggers the execution of one machine instruction. The green triangle moves one line downward.

Up in the Registers window, things are not the same. Two lines have turned red. The red color indicates that the values shown have changed during the execution of the single step that we just took. The EIP register is the instruction pointer, and it keeps track of which instruction will be executed next. More interesting right now is the state of the EAX register. What had been 0x0 is now 0x4. If you look at the source code, the instruction we just executed was this:

mov eax,4

File View

Execution Breakpoint

Window Settings Help

m Og itô?> o 6>>

& 1 o K

ES

x:

□ Rpgi«itpr

1 i LIP

ftfDerodpri valup

h GP and others

eax

0x0

0

ecx

0X0

_

edx

0X0

©

ebx

0x0

&

esp

Oxbf815560

0xbt815566

ebp

0x0

Ox©

esi

0x0

0

edi

H .H

»

eip

0xb04bb81

9x8048081 <_slail+l>

fioff

0x0

0

fooff

axu

O

Tup

0x0

0

-

n Flaas

-

-

1

ŒB

i

nop

; This no-op keeps qdb happy...

* *

mov pax,4

; Spprify syswritp call

mov cbx,l

; Specify File Descriptor 1: Standard Output

mov etx.EdLMsg

; Pass offset, of the message

mov edx,EatLen

; Pass the length ot the message

int 0011

; Hake kernel call

mov eax,l

; Code for Exit Syscall

mov ebx.O

; Return a code of zero

int uuh

; Make kernel call

:

active Line J a

Figure 5-14: KDbg, ready to single-step

Figure 5-14: KDbg, ready to single-step

The ''MOV'' mnemonic tells us that data is being moved. The left operand is the destination (where data is going) and the right operand is the source (where data is coming from.) What happened is that the instruction put the value 4 in register EAX.

Click the Step Into By Instruction button again. The pointer will again move down a line. And again, the red lines in the Registers window indicate what was changed by executing the instruction. The instruction pointer changed again; that shouldn't be a surprise. Every time we execute an instruction, EIP will be red. This time, EAX has turned black again and EBX has turned red. The value in EBX has changed from 0 to 1. (The notation ''0x1'' is just another way of telling us that the value is given in hexadecimal.) Clearly, we've moved the value 1 into register EBX; and that's the instruction we just executed:

mov ebx,1

Click the button again. This time, register ECX will change radically (see Figure 5-15). The precise number you see on your PC for ECX will differ from the precise number I saw when I took the screen shot. The value depends on the individual Linux system, how much memory you have, and what the Linux OS is doing elsewhere in memory. What matters is that a 32-bit hexadecimal value has been moved into ECX. This instruction did the work:

mov ecx,EatMsg

So what did we actually move? If you scroll up into the earlier part of the source code temporarily, you'll see that EatMsg is a quoted string of ordinary characters reading ''Eat at Joe's!'' and not a 32-bit number; but note the comment to the right of the instruction: ''Pass offset of the message.'' What we actually loaded into ECX was not the message itself but the message's address in memory. Technically, in IA-32 protected mode, a data item like EatMsg has both a segment address and an offset address. The segment address, however, is the property of the operating system, and we can safely ignore it when doing this kind of simple user-space programming. Back in the DOS era, when we had to use the real mode segmented memory model, we had to keep track of the segment registers too; doing it the protected mode way means one less headache. (Don't worry; there are plenty more!)

Click Step Into By Instruction again, and register EDX will be given the value 0xe, or (in decimal) 14. This is the length of the character string EatMsg.

At this point all the setup work has been done with respect to moving various values where they need to go. Click the button and execute the next instruction:

int 80H

It looks like nothing has happened—nothing in the Registers window changed—but hold on. Go into KDbg's menus and select View ^ Output. A simple terminal window will appear—and there's EatMsg, telling the world where to go for lunch (see Figure 5-16).

File View

Execution Breakpoint

Window Settings Help

74 OÊ II

1 7* fit A

© o «

S

y.

□ Rpgi<;tpr

t^val up

¿yripfofipri val up

h CP and others

eax

0x4

4

ecx

8x88498ad

1345169B0

edx

0x0

0

ebx

0x1

1

esp

0xbt815560

0xbt815566

ebp

0X0

0X0

esi

0X0

0

edi

8x0

eip

0x8048090

0x8048090 <_stdiL+ie>

fioff

0X0

0

fooff

0x8

8

fop

0x0

0

-

n Flaos

-

M 1 ^

«

nop

; This no-op keeps qdb happy...

%

• +

mov

pax , d

; Spprlfy syswriTP rail

mov

ebx, 1

; Specify File Descriptor 1: Standard Output

muv

etx.Edtflsy

; Pdss offset, of the message

■» +

mov

edx,EatLen

; Pass the length ot the message

int

8011

; Hake kernel call

mov

eax, 1

; Code for Exit Syscall

mov

ebx.0

; Return a code of zero

+ + + +

int

WÖH

; Hake kernel call

:

active Line 3U

Figure 5-15: Moving a memory address into a register

Eat at Joe"5!

Figure 5-16: Program output in a terminal window

The INT 80H instruction is a special one. It generates a Linux system call (affectionately referred to as a syscall) named sys_write, which sends data to the currently active terminal window.

Sending EatMsg to the output window is all that the eatsyscall program was designed to do. Its work is done, and the last three instructions in the program basically tidy up and leave. Click the button and step through them, watching to see that EAX and EBX receive new values before the final int 8 0h, which signals Linux that this program is finished. You'll get a confirmation of that in the bottom line of KDbg's window, which will say ''Program exited normally'' along with the source code line where this exit happened.

One question that may have occurred to you is this: Why is the stepper button called ''Step Into By Instruction''? We just bounced down to the next line; we did not step our way into anything. A full answer will have to wait for a couple of chapters, until we get into procedures, but the gist of it is this: KDbg gives you the option to trace execution step by step into an assembly language procedure, or to let the computer run full speed while executing the procedure. The button Step Into By Instruction specifies to go through a procedure step by step. The button Step Over By Instruction (the next button to the right) allows the procedure to execute at full speed, and pick up single-stepping on the other side of the procedure call.

Why step over a procedure call? Mainly this: procedures are often library procedures, which you or someone else may have written months or years ago. If they are already debugged and working, stepping through them is probably a waste of time. (You can, however, learn a great deal about how the procedure works by watching it run one instruction at a time.)

Conversely, if the procedures are new to the program at hand, you may need to step through them just as carefully as you step through the main part of the program. KDbg gives you the option. This simple program has no procedures, so the Step Into and Step Over buttons do precisely the same thing: execute the next instruction in sequence.

The three single-stepping buttons to the left of Step Into By Instruction are for use when debugging code in higher-level languages such as C. They enable stepping by one high-level statement at a time, not simply one machine instruction at a time. These buttons don't apply to assembly language programming and I won't be discussing them further.

Was this article helpful?

0 0

Post a comment