ARM assembler in Raspberry Pi – Chapter 4
As we advance learning the foundations of ARM assembler, our examples will become longer. Since it is easy to make mistakes, I think it is worth learning how to use GNU Debugger gdb
to debug assembler. If you develop C/C++ in Linux and never used gdb
, shame on you. If you know gdb
this small chapter will explain you how to debug assembler directly.
gdb
We will use the example store01
from chapter 3. Start gdb
specifying the program you are going to debug.
$ gdb --args ./store01 GNU gdb (GDB) 7.4.1-debian Copyright (C) 2012 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "arm-linux-gnueabihf". For bug reporting instructions, please see: ... Reading symbols from /home/roger/asm/chapter03/store01...(no debugging symbols found)...done. (gdb) |
Ok, we are in the interactive mode of gdb
. In this mode you communicate with gdb
using commands. There is a builtin help command called help
. Or you can check the GNU Debugger Documentation. A first command to learn is
(gdb) quit |
Ok, now start gdb
again. The program is not running yet. In fact gdb
will not be able to tell you many things about it since it does not have debugging info. But this is fine, we are debugging assembler, so we do not need much debugging info. So as a first step let’s start the program.
(gdb) start Temporary breakpoint 1 at 0x8390 Starting program: /home/roger/asm/chapter03/store01 Temporary breakpoint 1, 0x00008390 in main () |
Temporary breakpoint 1, 0x00008390 in main ()
Ok, gdb
ran our program up to main
. This is great, we have skipped all the initialization steps of the C library and we are about to run the first instruction of our main
function. Let’s see whats there.
(gdb) disassemble Dump of assembler code for function main: => 0x00008390 : ldr r1, [pc, #40] ; 0x83c0 0x00008394 : mov r3, #3 0x00008398 : str r3, [r1] 0x0000839c : ldr r2, [pc, #32] ; 0x83c4 0x000083a0 : mov r3, #4 0x000083a4 : str r3, [r2] 0x000083a8 : ldr r1, [pc, #16] ; 0x83c0 0x000083ac : ldr r1, [r1] 0x000083b0 : ldr r2, [pc, #12] ; 0x83c4 0x000083b4 : ldr r2, [r2] 0x000083b8 : add r0, r1, r2 0x000083bc : bx lr End of assembler dump. |
Uh-oh! The instructions referring the label addr_of_myvarX
are different. Ok. Ignore that for now, we will learn in a future chapter what has happened. There is an arrow =>
pointing the instruction we are going to run (it has not been run yet). Before running it, let’s inspect some registers.
(gdb) info registers r0 r1 r2 r3 r0 0x1 1 r1 0xbefff744 3204446020 r2 0xbefff74c 3204446028 r3 0x8390 33680 |
We can modify registers using p
which means print
but also evaluates side effects. For instance,
(gdb) p $r0 = 2 $1 = 2 (gdb) info registers r0 r1 r2 r3 r0 0x2 2 r1 0xbefff744 3204446020 r2 0xbefff74c 3204446028 r3 0x8390 33680 |
gdb
has printed $1
, this is the identifier of the result and we can use it when needed, so we can skip some typing. Not very useful now but it will be when we print a complicated expression.
(gdb) p $1 $2 = 2 |
Now we could use $2
, and so on. Ok, time to run the first instruction.
(gdb) stepi 0x00008394 in main () |
Well, not much happened, let’s use disassemble
, again.
(gdb) disassemble Dump of assembler code for function main: 0x00008390 : ldr r1, [pc, #40] ; 0x83c0 => 0x00008394 : mov r3, #3 0x00008398 : str r3, [r1] 0x0000839c : ldr r2, [pc, #32] ; 0x83c4 0x000083a0 : mov r3, #4 0x000083a4 : str r3, [r2] 0x000083a8 : ldr r1, [pc, #16] ; 0x83c0 0x000083ac : ldr r1, [r1] 0x000083b0 : ldr r2, [pc, #12] ; 0x83c4 0x000083b4 : ldr r2, [r2] 0x000083b8 : add r0, r1, r2 0x000083bc : bx lr End of assembler dump. |
Ok, let’s see what happened in r1
.
(gdb) info register r1 r1 0x10564 66916 |
Great, it has changed. In fact this is the address of myvar1
. Let’s check this using its symbolic name and C syntax.
(gdb) p &myvar1 $3 = ( *) 0x10564 |
Great! Can we see what is in this variable?
(gdb) p myvar1 $4 = 0 |
Perfect. This was as expected since in this example we set zero as the initial value of myvar1
and myvar2
. Ok, next step.
(gdb) stepi 0x00008398 in main () (gdb) disas Dump of assembler code for function main: 0x00008390 : ldr r1, [pc, #40] ; 0x83c0 0x00008394 : mov r3, #3 => 0x00008398 : str r3, [r1] 0x0000839c : ldr r2, [pc, #32] ; 0x83c4 0x000083a0 : mov r3, #4 0x000083a4 : str r3, [r2] 0x000083a8 : ldr r1, [pc, #16] ; 0x83c0 0x000083ac : ldr r1, [r1] 0x000083b0 : ldr r2, [pc, #12] ; 0x83c4 0x000083b4 : ldr r2, [r2] 0x000083b8 : add r0, r1, r2 0x000083bc : bx lr End of assembler dump. |
You can use disas
(but not disa
!) as a short for disassemble
. Let’s check what happened to r3
(gdb) info registers r3 r3 0x3 3 |
So far so good. Another more step.
(gdb) stepi 0x0000839c in main () (gdb) disas Dump of assembler code for function main: 0x00008390 : ldr r1, [pc, #40] ; 0x83c0 0x00008394 : mov r3, #3 0x00008398 : str r3, [r1] => 0x0000839c : ldr r2, [pc, #32] ; 0x83c4 0x000083a0 : mov r3, #4 0x000083a4 : str r3, [r2] 0x000083a8 : ldr r1, [pc, #16] ; 0x83c0 0x000083ac : ldr r1, [r1] 0x000083b0 : ldr r2, [pc, #12] ; 0x83c4 0x000083b4 : ldr r2, [r2] 0x000083b8 : add r0, r1, r2 0x000083bc : bx lr End of assembler dump. |
Ok, lets see what happened, we stored r3
, which contained a 3 into myvar1
, right? Let’s check this.
(gdb) p myvar1 $5 = 3 |
Amazing, isn’t it? Ok. Now run until the end.
(gdb) continue Continuing. [Inferior 1 (process 3080) exited with code 07] |
That’s all for today.
ARM assembler in Raspberry Pi – Chapter 3 ARM assembler in Raspberry Pi – Chapter 5
Kind regards,
if you are trying to store data to an address that contains code, yes, it will crash with a segmentation fault because of the memory protections set up by the operating system (and that are enforced by the processor itself). Code segments are in general not writeable.
Kind regards,
Thanks Jason!