以下功能,
#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 的倍数,但这是不正确的,因为填充位于数组之前。 也许它希望它与帧指针的偏移量相等? 这也不是一个非常令人满意的答案。