How do I get started with ARM on iOS?

Solutions Collect From Internet About “How do I get started with ARM on iOS?”

In my opinion, the best way to get started is to

  1. Write small snippets of C code (later Objective-C)
  2. Look at the corresponding assembly code
  3. Find out enough to understand the assembly code
  4. Repeat!

To do this you can use Xcode:

  1. Create a new iOS project (a Single View Application is fine)
  2. Add a C file scratchpad.c
  3. In the Project Build Settings, set “Generate Debug Symbols” to “No”
  4. Make sure the target is iOS Device, not Simulator
  5. Open up scratchpad.c and open the assistant editor
  6. Set the assistant editor to Assembly and choose “Release”

Example 1

Add the following function to scratchpad.c:

void do_nothing(void)

If you now refresh the Assembly in the assistant editor, you should see lots of lines starting with dots (directives), followed by

@ BB#0:
    bx  lr

Let’s ignore the directives for now and look at these three lines. With a bit of searching on the internet, you’ll find out that these lines are:

  1. A label (the name of the function prefixed with an underscore).
  2. Just a comment emitted by the compiler.
  3. The return statement. The b means branch, ignore the x for now (it has something to do with switching between instruction sets), and lr is the link register, where callers store the return address.

Example 2

Let’s beef it up a bit and change the code to:

extern void do_nothing(void);

void do_nothing_twice(void)

After saving and refreshing the assembly, you get the following code:

@ BB#0:
    push    {r7, lr}
    mov r7, sp
    blx _do_nothing
    pop.w   {r7, lr}
    b.w _do_nothing

Again, with a bit of searching on the internet, you’ll find out the meaning of each line. Some more work needs to be done because make two calls: The first call needs to return to us, so we need to change lr. That is done by the blx instruction, which does not only branch to _do_nothing, but also stores the address of the next instruction (the return address) in lr.

Because we change the return address, we have to store it somewhere, so it is pushed on the stack. The second jump has a .w suffixed to it, but let’s ignore that for now. Why doesn’t the function look like this?

@ BB#0:
    push    {lr}
    blx _do_nothing
    pop.w   {lr}
    b.w _do_nothing

That would work as well, but in iOS, the convention is to store the frame pointer in r7. The frame pointer points to the place in the stack where we store the previous frame pointer and the previous return address.

So what the code does is: First, it pushes r7 and lr to the stack, then it sets r7 to point to the new stack frame (which is on the top of the stack, and sp points to the top of the stack), then it branches for the first time, then it restores r7 and lr, finally it branch for the second time. Abx lr at the end is not needed, because the called function will return to lr, which points to our caller.

Example 3

Let’s have a look at a last example:

void swap(int *x, int *y)
    int temp = *x;
    *x = *y;
    *y = temp;

The assembly code is:

@ BB#0:
    ldr r2, [r0]
    ldr r3, [r1]
    str r3, [r0]
    str r2, [r1]
    bx  lr

With a bit of searching, you will learn that arguments and return values are stored in registers r0r3, and that we may use those freely for our calculations. What the code does is straightforward: It loads the value that r0 and r1 point to in r2 and r3, then it stores them back in exchanged order, then it branches back.

And So On

That’s it: Write small snippets, get enough info to roughly understand what’s going on in each line, repeat. Hope that helps!