too*_*zzy 1 c memory arrays assembly stack-memory
我在 C 中有 2 个函数:
void func1(unsigned char x)
{
unsigned char a[10][5];
a[0][0] = 1;
a[9][4] = 2;
}
void func2(unsigned char x)
{
unsigned char a[10][5];
a[0][0] = 1;
a[9][4] = 2;
unsigned char b[10];
b[0] = 4;
b[9] = 5;
}
Run Code Online (Sandbox Code Playgroud)
编译:
gcc 7.3 x86-64
-O0 -g
操作系统:
16.04.1-Ubuntu x86-64
生产的英特尔功能组装:
func1(unsigned char):
pushq %rbp
movq %rsp, %rbp
movl %edi, %eax
movb %al, -68(%rbp)
movb $1, -64(%rbp)
movb $2, -15(%rbp)
nop
popq %rbp
ret
func2(unsigned char):
pushq %rbp
movq %rsp, %rbp
movl %edi, %eax
movb %al, -84(%rbp)
movb $1, -64(%rbp)
movb $2, -15(%rbp)
movb $4, -74(%rbp)
movb $5, -65(%rbp)
nop
popq %rbp
ret
Run Code Online (Sandbox Code Playgroud)
我可以看到为 50 字节数组分配了 64 字节。似乎它在 16 字节边界上分配堆栈,因为分配了 10 字节 - 16 字节。
我的问题:
1) 数组的 16 字节边界上是否有一些堆栈对齐标准?因为 int、long int 等变量显然没有在 16 字节边界上分配。
2)为什么数组以如此奇怪的方式分配?我们可以看到,对于数组a
,在50 个字节的有效负载之后添加了14 个字节作为对齐填充,但对于数组,在我们的10 个字节的有效负载之前分配了6 个对齐字节。我错过了什么吗?b
3) 为什么函数参数传入EDI
( unsigned char x
),放置在堆栈 4 字节远离我们的数组内存开始(包括填充)。所以字节变量(AL register
)也被填充或什么?为什么是4 个字节?
x86_64 abi 需要 16 字节堆栈对齐(进入函数时堆栈指针必须是 16 字节对齐)。但是你看到的过度对齐是由-O0
; 用-O1
或更高,阵列更有效地对准。例如
void func2(unsigned char x)
{
unsigned char a[10][5];
a[0][0] = 1;
a[9][4] = 2;
unsigned char b[10];
b[0] = 4;
b[9] = 5;
__asm__ __volatile__("" :: "r"(a), "r"(b) : "memory");
}
Run Code Online (Sandbox Code Playgroud)
原因:
gcc -O1 -g x.c -c
objdump -d x.o
0000000000000010 <func2>:
10: c6 44 24 c0 01 movb $0x1,-0x40(%rsp)
15: c6 44 24 f1 02 movb $0x2,-0xf(%rsp)
1a: c6 44 24 b6 04 movb $0x4,-0x4a(%rsp)
1f: c6 44 24 bf 05 movb $0x5,-0x41(%rsp)
24: 48 8d 44 24 c0 lea -0x40(%rsp),%rax
29: 48 8d 54 24 b6 lea -0x4a(%rsp),%rdx
2e: c3 retq
Run Code Online (Sandbox Code Playgroud)
-Os
或-O3
创建其他布局。