以下函数,
#include <string.h>
void func1(char *s)
{
char buffer[4];
strcpy(buffer, s);
}
已编译,
$ arm-linux-gnueabi-gcc -g -fno-stack-protector func1.c -c -o func1.o
$ arm-linux-gnueabi-gcc --version
arm-linux-gnueabi-gcc (Ubuntu 13.2.0-4ubuntu3) 13.2.0
并拆卸:
$ arm-linux-gnueabi-objdump --disassemble=func1 -S func1.o
void func1(char *s)
{
0: e92d4800 push {fp, lr}
4: e28db004 add fp, sp, #4
8: e24dd010 sub sp, sp, #16
c: e50b0010 str r0, [fp, #-16]
char buffer[4];
strcpy(buffer, s);
10: e24b3008 sub r3, fp, #8
14: e51b1010 ldr r1, [fp, #-16]
18: e1a00003 mov r0, r3
1c: ebfffffe bl 0 <strcpy>
}
20: e1a00000 nop @ (mov r0, r0)
24: e24bd004 sub sp, fp, #4
28: e8bd8800 pop {fp, pc}
我的分析strcpy()
:调用(指令)之后0x20
堆栈有以下内容:
|---+---+---+---|
fp - 20 | ????????? | <- current $sp
|---+---+---+---|
fp - 16 | char * s | <- $r1
|---+---+---+---|
fp - 12 | ????????? |
|---+---+---+---|
fp - 8 | char buffer[] | <- $r0, $r3
|---+---+---+---|
fp - 4 | prev $fp | <- $sp after push
|---+---+---+---|
| $lr | <- $fp
|---+---+---+---|
| | | | | <- $sp that had the calling function
|---+---+---+---|
.
.
.
^
| descending addresses
问题:
$sp
指向的内容是什么?由于 ARM 使用完整的降序堆栈,因此该元素被标记为已占用,并且被调用的函数不会使用该空间。- 为什么这两个局部变量之间有未使用的空间?
第一个问题的答案(为什么堆栈末尾有一个额外的字)是 ARM ABI 要求堆栈在每个函数开始时都按 8 字节对齐。
由于编译器假定它在进入时已对齐
func1
,因此它必须添加偶数个字才能调用strcpy
。它知道将多余的字作为填充是浪费。我只能猜测变量之间有空格的原因。我最初以为它是将数组的大小四舍五入为 8 的倍数,但这并不正确,因为填充位于数组之前。也许它希望它是与框架指针的偶数偏移?这也不是一个令人满意的答案。