拆卸简单的C功能

sir*_*day 2 c x86 assembly

我正在尝试理解简单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.

Pet*_*des 6

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.