在函数序言中,即使使用 -fomit-frame-pointer,堆栈的当前位置也会保存在被调用者保存的寄存器中。
在下面的示例中,sp+4 存储在 r7 中,然后在结尾 (LBB0_3) 中恢复 (r7+4 -> r4; r4 -> sp)。正因为如此,您可以在函数中的任何位置跳转,在函数中的任何点增加堆栈,而不是搞砸堆栈。如果您跳出函数(通过跳转 *addr),您将跳过此结语并彻底搞砸堆栈。
也使用 alloca 的简短示例,它在堆栈上动态分配内存:
clang -arch armv7 -fomit-frame-pointer -c -S -O0 -o -stack.c
#include <alloca.h>
int foo(int sz, int jmp) {
char *buf = alloca(sz);
int rval = 0;
if( jmp ) {
rval = 1;
goto done;
}
volatile int s = 2;
rval = s * 5;
done:
return rval;
}
和拆卸:
_foo:
@ BB#0:
push {r4, r7, lr}
add r7, sp, #4
sub sp, #20
movs r2, #0
movt r2, #0
str r0, [r7, #-8]
str r1, [r7, #-12]
ldr r0, [r7, #-8]
adds r0, #3
bic r0, r0, #3
mov r1, sp
subs r0, r1, r0
mov sp, r0
str r0, [r7, #-16]
str r2, [r7, #-20]
ldr r0, [r7, #-12]
cmp r0, #0
beq LBB0_2
@ BB#1:
movs r0, #1
movt r0, #0
str r0, [r7, #-20]
b LBB0_3
LBB0_2:
movs r0, #2
movt r0, #0
str r0, [r7, #-24]
ldr r0, [r7, #-24]
movs r1, #5
movt r1, #0
muls r0, r1, r0
str r0, [r7, #-20]
LBB0_3:
ldr r0, [r7, #-20]
subs r4, r7, #4
mov sp, r4
pop {r4, r7, pc}