3

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?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
RNPF
  • 231
  • 3
  • 23
  • Why not use something like ARMSIM instead if all you want to do is try out short assembly snippets? – Michael Sep 15 '16 at 06:29
  • 2
    That indeed won't assemble because you're missing the `AREA ...` directive which the `END` is supposed to close, and (possibly, modulo markdown formatting) the appropriate indentation. [Never assume...](http://www.keil.com/support/man/docs/armasm/armasm_dom1359731140888.htm) And yes, as Michael says, this kind of experimentation is pretty much exactly what instruction set simulators are for. – Notlikethat Sep 15 '16 at 07:59

1 Answers1

9

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.)

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • 1
    I never looked but does `gdb-multiarch` support it? – Michael Petch Sep 15 '16 at 07:32
  • _"@END This isn't an instruction."_ - indeed, it's a (necessary) directive in armasm syntax, although the rest of the code is not well-formed directive- and indentation-wise. – Notlikethat Sep 15 '16 at 07:55
  • @Notlikethat: This answer is just what I came up with for single-stepping a couple ARM instructions. It's at least partly for my own benefit, since last time I figured this out I didn't write down the qemu and gdb cmds! I'm not an ARM developer, just casually curious. The OP mentioned uVision4, but I don't know what that is, and the OP didn't say anything about requiring an answer that used it, as opposed to any other way to build and run ARM code. I don't know anything about ARM assemblers other than gas, although I'm not surprised to hear that there are some that use different directives. – Peter Cordes Sep 15 '16 at 08:18
  • 1
    Oh, sure, it's a great and useful answer (I've always assumed this was possible but never actually tried, now I know exactly how!) - just thought I'd try to clarify _why_ you had to munge the OP's code ;) FWIW, µVision is Keil's microcontroller development suite, which uses ARM's armcc/armasm toolchain - those both have very different flavours from GCC/GAS. – Notlikethat Sep 15 '16 at 09:12