The following function,
#include <string.h>
void func1(char *s)
{
char buffer[4];
strcpy(buffer, s);
}
is compiled,
$ 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
and disassembled:
$ 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}
My analysis: just after calling strcpy()
(instruction 0x20
) the stack has the following contents:
|---+---+---+---|
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
Questions:
$sp
points to? Since ARM uses a full descending stack, that element is marked as occupied and the called function won't use that space.The answer to your first question (why the extra word at the end of the stack) is that the ARM ABI requires the stack to be 8-byte aligned at the start of every function.
Since the compiler assumes it was aligned at entry to func1
, it must add an even number of words before it is allowed to call strcpy
. It knows it is wasting that extra word as padding.
I can only guess at the reason for the space between the variables. I initially thought it was rounding the size of the array up to a multiple of 8, but this isn't correct because the padding is before the array. Maybe it wants it to be an even offset from the frame pointer? That isn't a very satisfying answer either.