debuggingassemblyarmgdbqemu

How to run a single line of assembly, then see [R1] and condition flags


I'm trying to teach myself assembly. I've got years and year of experience with C, Java and Python- but I cant make ANY headway here and I'm about to give up.

So, I downloaded uVision4, and assumed I could just write a basic assembly program:

MOV R1,  #0x7F0E0C2D
MOV R3,  #0x1048B3C5
ADCS  R1, R3, ROR #0x18
END

So, establish two variables, do an operation, done. Check the Registers for output and debugger for condition flags, surely.

Apparently, this is impossible.

I create the text file, write my code, save as a .asm file, then try to build-

It hates that.

Okay, so I create a new project, add the .asm file,

And it refuses, demanding I apparently write an entire device driver to do a god damn hello world.

How can I run a simple couple lines of code to start learning?


Solution

  • Related: How to single step ARM assembly in GDB on QEMU? is another similar walkthrough, with a program that makes an exit system-call so you can let it run without crashing, not just single-step.


    I do stuff like this all the time on my x86 desktop, using gdb to single-step code. Usually with x86 instructions, but it's doable for ARM cross-development, too. Build with ARM GCC -nostdlib foo.S, and it should set the default entry point to the beginning of your .text section. You do get a warning from the linker, though, if you don't define _start:

    $ arm-linux-gnueabi-gcc -nostdlib arm-simple.S 
    /usr/lib/gcc-cross/arm-linux-gnueabi/5/../../../../arm-linux-gnueabi/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000010098
    

    I had to modify your source for it to assemble. Here's my arm-simple.S:

    .globl _start                                                                                                                                                       
    _start:                      @ having a symbol name makes debugging easier
    
      ldr   R1,  =0x7F0E0C2D       @ ARM immediate constants can't be arbitrary 32-bit values.  Use the ldr reg, =value pseudo-op, which in this case assembles to a PC-relative load from a nearby literal pool.  Often it can use mov reg, #imm or movn reg, #imm
      ldr   R3,  =0x1048B3C5
      ADCS  R1, R3, ROR #0x18
    
    @END  GAS syntax doesn't use ARMASM's end directive.
    

    clang -target arm -mcpu=cortex-15 -c arm-simple.S can assemble this, but even with -static -nostdlib it can't link it into a static executable. Clang tries to use the system gcc as a linker, which is not an ARM linker. So you will need cross-tools installed, at least a linker.

    Then you can use gdb and set a breakpoint at the first instruction, run it, and single step.

    You can even do this in a cross-development environment, with a few wrinkles.


    In one terminal, run QEMU on your binary, waiting for a debugger connection:

    $ arm-linux-gnueabi-gcc -g -nostdlib arm-simple.S
    $ qemu-arm -g 12345 ./a.out                    # user-mode emulation, waiting for gdb to connect
    

    Use -mcpu=something for gcc, and -cpu model for qemu if you want to be specific.


    In another terminal, run ARM gdb (in my case, from Ubuntu's gdb-arm-none-eabi package, since they Ubuntu doesn't distribute a arm-linux-gnueabi-gdb cross-ARM-gdb package for x86).

    TODO: try gdb-multiarch. Regular gdb on an x86 desktop can only debug x86 binaries, so you definitely can't use that.

    $ arm-none-eabi-gdb ./a.out          # give the gdb client the same binary to read symbols / debug info
    (gdb) target remote localhost:12345
    (gdb) layout asm
    (gdb) layout reg
    (gdb) si               # single step by instruction, not source line
    (gdb) si
    

    Then gdb shows:

    +--Register group: general-----------------------------------------------------------------------------------------------------------------------------------------+
    |r0             0x0      0                             r1             0x7f0e0c2d       2131627053            r2             0x0      0                             |
    |r3             0x1048b3c5       273200069             r4             0x0      0                             r5             0x0      0                             |
    |r6             0x0      0                             r7             0x0      0                             r8             0x0      0                             |
    |r9             0x0      0                             r10            0x100ac  65708                         r11            0x0      0                             |
    |r12            0x0      0                             sp             0xf6ffea40       0xf6ffea40            lr             0x0      0                             |
    |pc             0x100a0  0x100a0 <_start+8>            cpsr           0x10     16                                                                                  |
    |                                                                                                                                                                  |
    |                                                                                                                                                                  |
    |                                                                                                                                                                  |
    |                                                                                                                                                                  |
    |                                                                                                                                                                  |
    |                                                                                                                                                                  |
    |                                                                                                                                                                  |
    |                                                                                                                                                                  |
    |                                                                                                                                                                  |
       ----------------------------------------------------------------------------------------------------------------------------------------------------------------+
       |0x10098 <_start>        ldr    r1, [pc, #4]    ; 0x100a4 <_start+12>                                                                                           |
       |0x1009c <_start+4>      ldr    r3, [pc, #4]    ; 0x100a8 <_start+16>                                                                                           |
      >|0x100a0 <_start+8>      adcs   r1, r1, r3, ror #24                                                                                                             |
       |0x100a4 <_start+12>     svcvc  0x000e0c2d                                                                                                                      |
       |0x100a8 <_start+16>     subne  r11, r8, r5, asr #7                                                                                                             |
       |0x100ac                 andeq  r1, r0, r1, asr #18                                                                                                             |
       |0x100b0                 cmnvs  r5, r0, lsl #2                                                                                                                  |
       |0x100b4                 tsteq  r0, r2, ror #18                                                                                                                 |
       |0x100b8                 andeq  r0, r0, pc                                                                                                                      |
       |0x100bc                 subseq r3, r4, r5, lsl #10                                                                                                             |
       |0x100c0                 tsteq  r8, r6, lsl #6                                                                                                                  |
       |0x100c4                 andeq  r0, r0, r9, lsl #2                                                                                                              |
       |0x100c8                 andeq  r0, r0, r12, lsl r0                                                                                                             |
       |0x100cc                 andeq  r0, r0, r2                                                                                                                      |
       |0x100d0                 andeq  r0, r4, r0                                                                                                                      |
       +---------------------------------------------------------------------------------------------------------------------------------------------------------------+
    remote Remote target In: _start                                                                                                              Line: 6    PC: 0x100a0 
    (gdb) si
    

    It highlights the last register(s) modified, which is pretty great.

    It seems to be too old to decode flags (PSR) symbolically, though. Modern x86 gdb does that.

    The disassembly of later instructions after the three from our source is because there are bytes in memory right after them, probably including those 0x7F0E0C2D and 0x1048B3C5. So they disassemble as something. (And if you kept single-stepping, execution would fall into them, since there's no exit system call at the bottom of our code. Which is fine, we only wanted to single-step them in GDB anyway.)


    With lldb

    See Is LLDB compatible with gdbserver (for debugging cross compiled code?) - run lldb ./a.out and use the gdb-remote command:

    (lldb) gdb-remote localhost:12345
    Process 572699 stopped
    * thread #1, stop reason = signal SIGTRAP
        frame #0: 0x00008000 a.out`_start
      ...
    
    (lldb) si    # single-step by instruction
    (lldb) register read r0
          r0 = 0x00000000
    (lldb) register read
       ...
    

    I didn't have success with platform select remote-gdb-server / platform connect connect://localhost:12345 as described in https://lldb.llvm.org/use/remote.html

    TODO: how to pretty-print CPSR (flags).