In previous posts Running NASM inside C inside GDB. Part 1. Integers and Running NASM inside C inside GDB. Part 2. More arguments we executed simple programs.
The problem with simple programs, is that they are simple. You don’t even always need to debug it. But what happens in the real life, is that applications are much bigger and complex. And if your program doesn’t work as expected, you need to understand what went wrong.
Debugger to the rescue!
My experience with debuggers
I first learned to debug programs in mid 90s, when I played around with Borland TurboPascal 5.5 and later 7.0. It has already an IDE with a decent debugger. It allowed me to execute programs step by step, watch variables and understand where the problems came from.
The most awesome was TurboDebugger, which showed memory, stack, registers, CPU flags, program listing. It was really easy to understand how ASM commands change registers and flags.
Unfortunatelly, I haven’t find anything similar for the x86 64bit assembly, but there’s something even more powerful.
GDB / LLDB
In fact, GDB exists for 30 years already! You can still debug everything from the command line.
I will show the examples of working with LLDB
Let’s try to run
num_calc inside the debugger:
Time to execute it for the first time:
Ok cool, it runs, but we don’t really do anything.
Time to pause the execution of the program inside our ASM function. For this we’ll need to add a breakpoint. There are multiple ways of setting it (check manual), but we’ll add a breakpoint by function name
What will happen next, if we’d
run the programm again, it would stop right in the beginning of the function:
We can check the state of the registers with the help of
re r or
re r/d (for decimal), or just some of the registers
re r rax rbx rdx
What can we learn from this screen? It shows that our arguments are now in
rdx registers (
We can also learn that other registers are pretty random (better not to touch
rip registers, as they are pretty important).
Ok, let’s get back to the code:
di will show us where we paused. Time to proceed with the
And we could see that first command
rdx-5 executed successfully, the value of the register changed.
Next command will multiply
rdi by 8 by shifting 3 bits left:
Quite easy to see that by three
111 bits shifted by 3 positions left, and we have
Next command will divide
4 or is the same as shifting bits right by 2 positions:
Again, bits shifted two positions right, and the answer is
8/4=2 easy ;)
Next we would add
rsi to the
Here we have
56+2=58. Time to multiply the numbers:
Final piece of calculation done:
rax = rax * rdi or
rax = 58 * 4 = 232
That’s it. Setting breakpoints in the code, stepping over the lines of code and examining the state of the registers can help understanding where calculation goes wrong.
Source code on github: nasm-c-gdb