In my opinion, the best way to get started is
- Write small snippets of C code (later Objective-C)
- Look at the appropriate build code
- Learn enough to understand assembly code
- Repeat!
For this you can use Xcode:
- Create a new iOS project (Single View app is great)
- Add file C scratchpad.c
- In the project build settings, set "Generate Debug Symbols" to "No"
- Make sure the target is an iOS device, not a simulator
- Open scratchpad.c and open the assistant editor.
- Install the helper in the assembly and select "Release"
Example 1
Add the following function to scratchpad.c:
void do_nothing(void) { return; }
If you update the assembly in the helper editor, you will see many lines starting with periods (directives), and then
_do_nothing: @ BB
Now let them ignore the directives and look at these three lines. With a little search on the Internet you will find out that these lines:
- Label (function name with underscore prefix).
- Only comment released by compiler.
- Return statement
b means a branch, now ignores x (it has something to do with switching between sets of commands), and lr is a link register where callers store the return address.
Example 2
Put a little up and change the code to:
extern void do_nothing(void); void do_nothing_twice(void) { do_nothing(); do_nothing(); }
After saving and updating the assembly, you will receive the following code:
_do_nothing_twice: @ BB
Again, a little searching on the Internet, you will find out the meaning of each line. One more job needs to be done, because make two calls: the first call should return to us, so we need to change lr . This is done with the blx command, which not only goes to _do_nothing , but also stores the address of the next command (return address) in lr .
Since we are changing the return address, we need to store it somewhere, so it is pushed onto the stack. The second jump has the suffix .w , but this time ignore it. Why doesn't the function look like this?
_do_nothing_twice: @ BB
This will work too, but on iOS, the convention is to keep the frame pointer in r7 . The frame pointer points to the place on the stack where we store the previous frame pointer and the previous return address.
So what the code does: first, it pushes r7 and lr r7 stack, then sets r7 to point to the new stack stack (which is at the top of the stack, and sp points to the top of the stack), then it branches into the first times, then restores r7 and lr ; finally, it branches for the second time. A bx lr at the end is not required because the called function will return to lr , which indicates our caller.
Example 3
Let's look at the last example:
void swap(int *x, int *y) { int temp = *x; *x = *y; *y = temp; }
Build Code:
_swap: @ BB
With a little searching, you will find out that the arguments and return values ββare stored in the registers r0 - r3 , and we can use them freely for our calculations. Which makes the code simple: it loads the value that r0 and r1 points to r2 and r3 , then saves them back in the exchange order, then drops them back.
etc
What it is: write small fragments, get enough information to roughly understand what is happening on each line, repeat. Hope this helps!