我正在尝试理解简单C函数的底层程序集.
program1.c
void function() {
char buffer[1];
}
Run Code Online (Sandbox Code Playgroud)
=>
push %ebp
mov %esp, %ebp
sub $0x10, %esp
leave
ret
Run Code Online (Sandbox Code Playgroud)
不确定它是如何到达0x10的?不是字符1字节,即8位,所以它应该是0x08?
program2.c
void function() {
char buffer[4];
}
Run Code Online (Sandbox Code Playgroud)
=>
push %ebp
mov %esp, %ebp
sub $0x18, %esp
mov ...
mov ...
[a bunch of random instructions]
Run Code Online (Sandbox Code Playgroud)
不确定它是如何到达0x18的?另外,为什么在指令之后有那么多附加SUB指令?我所做的只是将数组的长度从1更改为4.
GCC 使用-mpreferred-stack-boundary=4默认的x86 32位和64位的ABI,所以它保持%esp16B对齐.
我能够在Godbolt Compiler Explorer上用gcc 4.8.2重现你的输出-O0 -m32
void f1() { char buffer[1]; }
pushl %ebp
movl %esp, %ebp # make a stack frame (`enter` is super slow, so gcc doesn't use it)
subl $16, %esp
leave # `leave` is not terrible compared to mov/pop
ret
Run Code Online (Sandbox Code Playgroud)
您必须使用一个版本的gcc 使用-fstack-protector默认启用.较新的gcc通常不配置为执行此操作,因此您不会获得相同的sentinel值并检查写入堆栈.(在godbolt链接中尝试更新的gcc)
void f4() { char buffer[4]; }
pushl %ebp #
movl %esp, %ebp # make a stack frame
subl $24, %esp # IDK why it reserves 24, rather than 16 or 32B, but prob. has something to do with aligning the stack for the possible call to __stack_chk_fail
movl %gs:20, %eax # load a value from thread-local storage
movl %eax, -12(%ebp) # store it on the stack
xorl %eax, %eax # tmp59
movl -12(%ebp), %eax # D.1377, tmp60
xorl %gs:20, %eax # check that the sentinel value matches what we stored
je .L3 #,
call __stack_chk_fail #
.L3:
leave
ret
Run Code Online (Sandbox Code Playgroud)
显然gcc认为char buffer[4]是"易受攻击的对象",但不是char buffer[1].没有-fstack-protector,甚至在asm中也几乎没有差别-O0.