Assembly Tutorial – Debugging Assembly Code

Coding in assembly can be quite tricky. The syntax is unintuitive. For example when we want to open a file, we don’t call a simple one line function with a name like “open”, instead we have to set multiple registers and use a system call. It is also extremely unforgiving, we can easily set the wrong register or set a register to the wrong value without noticing, and then our code will just fall over and there will be no helpful error message.

How do we diagnose these errors? Well thankfully we can debug assembly with the standard GNU debugger gdb. The process of debugging assembly in gdb is very similar to debugging C or C++.

Suppose we have an assembly program that is supposed to write to stdout but unfortunately does not:

 .section .data
msg:
.string "Hello world\n"

 .section .text

 .globl _start
_start:

 movq $1, %rax
 movq $10, %rdi
 movq $msg, %rsi
 movq $12, %rdx
 syscall


 movq $60, %rax
 movq $0, %rdi
 syscall

We have carefully combed through this code, but have not found the error yet, so we decide to debug it. To debug, first we must assemble the code with debug symbols. To do this we assemble with the extra command line option –gstabs+:

as --gstabs+ write.s -o write.o

We then link the file as we normally do:

ld write.o -o write

Now instead of running the binary file that has been created we pass it as an argument to gdb:

gdb write

This will load the write binary into gdb, after gdb spits out some general information you should have a command line that looks like

Reading symbols from write...
(gdb)

To set breakpoints in gdb we use the command:

b <filename>:<linenumber>

In our case we will set a breakpoint at the start label:

b write.s:10

We then tell gdb to start the execution of our program with the run command, ‘r’. If this runs succesfully our command line will look like:

Breakpoint 1, _start () at write.s:10
10	 movq $1, %rax
(gdb) 

While debugging we can step to the next line of code executed with the ‘s’ command. Let’s say we step all the way to line 14 as so:

(gdb) s
11	 movq $10, %rdi
(gdb) s
12	 movq $msg, %rsi
(gdb) s
13	 movq $12, %rdx
(gdb) s
14	 syscall
(gdb) 

Let’s have a look at what’s in the registers, to do this we use the ‘info registers’ command:

(gdb) info registers
rax            0x4                 1
rbx            0xa                 0
rcx            0x402000            0
rdx            0xc                 12
rsi            0x0                 4202496
rdi            0x0                 10
rbp            0x0                 0x0
rsp            0x7fffffffd870      0x7fffffffd870
r8             0x0                 0
r9             0x0                 0
r10            0x0                 0
r11            0x0                 0
r12            0x0                 0
r13            0x0                 0
r14            0x0                 0
r15            0x0                 0
rip            0x401014            0x401014 <_start+20>
eflags         0x202               [ IF ]
cs             0x33                51
ss             0x2b                43
ds             0x0                 0
es             0x0                 0
fs             0x0                 0
gs             0x0                 0
(gdb) 

Each of the individual registers is listed along with the value it contains, first in hex and then in a more human readable form. If we just want to see the value in a single register we use ‘info registers <register name>’, for example,

info registers rdi

gives,

rdi            0xa                 10
(gdb) 

Now, as this point in the code we are attempting to write to stdout, so the rdi register should be set to 1, however we can see it is set to ten. If we look back at the code we have stepped through we see that on line 11 we set the rdi register to 10, whoops!

There are a few other useful commands we have left out.

  • info – prints a numbered list of breakpoints
  • delete <breakpoint number> – deletes a breakpoint
  • c – continues execution to the next breakoint and
  • r – can be called at any point to restart execution from the beginning.

We’ll learn more about debugging in a later post, but this should be all you need to get started!

Leave a Reply

Your email address will not be published. Required fields are marked *