ARM编程新手咨询:函数开头需入栈哪些寄存器及参数位置
Hey there! Great questions—these are core fundamentals for writing reliable ARM assembly code, so let's break them down clearly using the ARM Architecture Procedure Call Standard (AAPCS) (the standard rulebook for how functions interact on ARM systems).
1. Which registers to push onto the stack at the start of a function?
It all comes down to two categories of registers defined by AAPCS: callee-saved and caller-saved registers.
Callee-saved registers: These are registers that your function must preserve for the caller. If your function uses any of these, you must push them onto the stack at the start and pop them back off before returning. The callee-saved registers are:
- r4 to r11 (including fp, which is r11, if you use it to set up a frame pointer)
- lr (r14, the link register) if your function calls other functions (since lr gets overwritten with the return address of the new call)
- Note: sp (r13, stack pointer) is always managed carefully, so you don't push it unless you're doing advanced stack manipulation for exceptions.
Example: If your function uses r4 and sets up a frame pointer, your prologue (start of the function) might look like this:
push {r4, fp, lr} ; Save callee-saved registers we'll use mov fp, sp ; Set up frame pointerCaller-saved registers: These are r0-r3, r12, and flags. You don't need to save these at the start of your function—whoever called your function is responsible for saving them if they need their values later. You can use these freely without worrying about preserving their original values.
2. Where are function parameters stored?
Again, AAPCS defines this clearly:
The first four parameters are passed in registers r0, r1, r2, r3, in order. So the first argument goes to r0, second to r1, etc.
Any parameters beyond the fourth are passed on the stack, pushed from right to left (so the 5th parameter is the first one pushed onto the stack before calling the function).
For larger types (like structs bigger than 4 bytes, or 64-bit values on 32-bit ARM), there are special rules (e.g., using pairs of registers or allocating stack space), but for most basic cases (integers, pointers), the r0-r3 + stack rule applies.
Example: If you're calling a function
void my_func(int a, int b, int c, int d, int e), the caller would do:push {e} ; Push the 5th parameter first (right-to-left) mov r0, a mov r1, b mov r2, c mov r3, d bl my_func ; Call the function add sp, sp, #4 ; Clean up the stack parameterInside
my_func, you'd access r0-r3 for the first four args, and load e from the stack (using the frame pointer or stack pointer).
Also, quick bonus: Function return values are passed back in r0 (and r1 if it's a 64-bit value on 32-bit ARM).
Hope that clears things up—feel free to ask if you want to dive deeper into edge cases!
内容的提问来源于stack exchange,提问作者B3nanana

