This tutorial will walk you through debugging amd64AT&T ASM using the GNU as assembler and gdb. Why would you ever want to do this? More than likely you were sitting around bored drinking cocktails and decided to give assembly a try.
If you are running an amd64 distribution of Linux, you just need gdb, ld, and as installed. Otherwise, you need to download VirtualBox and install amd64 Linux to follow along. The distribution shouldn’t matter.
We are going to use an example application that is fairly common. It comes from the book Programming from the Ground Up by Jonathan Bartlett. In this example, we have attempted to take their code and port it to amd64 using the extra 32-bits of each register. This isn’t out of necessity, but for a learning exercise – in fact, only using the first 16-bits will dramatically increase portability. However, we have made a mistake somewhere, and our application is having unexpected output.
Essentially, what this application does is takes the list of numbers defined at the top and outputs the largest of them as the return value of the application. Simple stuff. Lets take a look at the code:
The expected output would be 222. In fact, before our alterations it did output 222. But when we tried to use the second half of the registers, the application started returning 0. Let’s step through this in GDB
Assembling and Linking
We need to include debugger symbols in the resulting object file from as
Using man as, we can see the command --gstabs+ which will include the stabs debugging information with GNU extensions for gdb. So lets use that.
Next, we need to pass our object file through the linker, which should leave our symbols intact.
Loading into GDB
Now lets drop into a debugging session with gdb
We want to drop a breakpoint at the entrypoint of the application, run the application, and then step through until we place 3 (the first value) into the register.
Here, we use b to set a breakpoint at _start. We then use s to step through the application line by line. Since the symbols stored here are from our ASM source file, we don’t need to use si. We get to the point where our value has been placed in the register %rax and we print the values of all the registers.
That looks okay, we have 3 loaded into the register like we expected. Let’s move onto the next value to see what happens.
Woah! Now that is weird. What is going on here? What is this 287762808832 number? Where did it come from? Let’s step again to see if this madness continues.
We have returned to sanity. The next number is 67. So then why was there a nonsensical number between 3 and 67? Let’s exit out of the debugging session and take a look at the source file again.
In our source file, specifically this line, we notice something is off:
We are loading in our number 4 bytes at a time, but they are defined as an array of .quad which are 8 bytes. So our mysterious number showing up between values is in fact the remaining 4 bytes of each number we are storing!
We update both occurences of this line to:
Re-assemble and re-link, and our problem is solved!
You just debugged your first assembly programming using GDB!