If you have to do any serious debugging or code analysis on a platform, you would want to know about the calling convention. Since ARM is dominant in embedded and mobile space, and increasingly challenging Intel in server space, I thought it would be useful to take a closer look at how things work in ARM, more specifically on a popular device like the iPhone1. For a refresher on x64 calling convention check out one of my earlier posts.
First things first, to see disassembly in Xcode2, you would turn on Debug->Debug Workflow->Show Disassembly When Debugging. To see registers when you are debugging, in the Xcode Debug area, left click on Auto at the bottom left and switch to All Variables, Registers, Globals and Statics. If you prefer the debugger console, you can issue register read or register read -a to lldb and get the register state that way3.
In terms of instructions involved when calling functions, ARM does not have a call instruction like Intel x86/x64. Instead branching instructions are also used for calling. The simplest branching instruction B <address>, just jumps to the address therefore making it equivalent of x86 jmp instruction. While that is useful and can be used for functions that never return, most functions want to return to caller.
The BL <address> instruction uses a link register (also known as lr or r14) for heading back to caller. The instruction copies the return address to lr before branching to the address. BLX instruction allows change of instruction set (Thumb instructions that save on instruction size, and allow mixing of 16 and 32 bit instructions, are considered a different instruction set) but otherwise behaves just like BL. This is slightly different from Intel call, which pushes return address on stack. So how is lr maintained if the called function were to call another function ? lr gets pushed into the stack in that case and gets popped before returning to caller. Obviously lr is preserved in the calling convention.