【问题标题】:Allocation of space for static variables on the stack堆栈上静态变量的空间分配
【发布时间】:2014-07-10 11:51:32
【问题描述】:

以下代码包含一个 8 字节的缓冲区。

void vuln() {
    char buffer[8];
    gets(buffer);
    printf("%s",buffer);
}

int main() {
    vuln();
    return 0;
}

因此,预计只有 8 字节的堆栈会被保留给这个缓冲区。但是反汇编显示保留了 16 个字节。

(gdb) 
Dump of assembler code for function vuln:
   0x000000000040057d <+0>:     push   %rbp
   0x000000000040057e <+1>:     mov    %rsp,%rbp
   0x0000000000400581 <+4>:     sub    $0x10,%rsp
   0x0000000000400585 <+8>:     lea    -0x10(%rbp),%rax
   0x0000000000400589 <+12>:    mov    %rax,%rdi
   0x000000000040058c <+15>:    callq  0x400480 <gets@plt>
   0x0000000000400591 <+20>:    lea    -0x10(%rbp),%rax
   0x0000000000400595 <+24>:    mov    %rax,%rsi
   0x0000000000400598 <+27>:    mov    $0x400644,%edi
   0x000000000040059d <+32>:    mov    $0x0,%eax
   0x00000000004005a2 <+37>:    callq  0x400450 <printf@plt>
   0x00000000004005a7 <+42>:    leaveq 
   0x00000000004005a8 <+43>:    retq   
End of assembler dump.

将根据自动脚本中堆栈上缓冲区的预期大小执行一些操作。但这削弱了剧本。我可以知道为什么要为缓冲区分配 16 个字节以便将其合并到脚本中吗?

【问题讨论】:

  • 我已经用 GNU C -O0 编译了它,并且还在内存中分配了 16 个字节;使用 -O6 它增长到 32 个字节。请参阅下面的答案。
  • 您能否添加这些“根据堆栈上缓冲区的预期大小执行”的操作?也许整个问题都有答案。

标签: c stack buffer disassembly


【解决方案1】:

x86-64 ELF psABI 要求堆栈指针对齐:第 3.2.2 节(“堆栈帧”)说

... 输入参数区域的结尾应在 16 字节边界上对齐。 换句话说,值 (%rsp− 8) 在控制时总是 16 的倍数 转移到函数入口点。堆栈指针%rsp 始终指向 最新分配的堆栈帧的结尾。

您的函数在堆栈上分配 8 个字节,然后调用一个单参数函数,gets;该函数的一个参数在寄存器中传递,因此为了维持 ABI 要求,编译器必须在调用函数之前将堆栈指针向下移动 8 个字节。

【讨论】:

  • 那么,在 (%rsp - 8) 中,值 8 取决于什么?当我使用45 bytes buffer 时,编译器在堆栈中分配了 48 个字节。我无法在其中找到数学。如果按照我原来的计算,48 个字节是完美的,因为内存是按字(4 个字节)分配的,而 48 是 12 个字。但是在这种情况下,ABI 要求呢?
  • 在该示例中,在进入函数时,堆栈指针具有某个未知 k 的值 (k*16 - 8)。在push %rbp之后,其值为(k-1)*16;在sub $0x30,%rsp之后,其值为(k-4)*16;然后call gets@plt 隐式执行push %rip,使堆栈指针(k-4)*16 - 8 进入gets。这是否有助于您了解它的工作原理?
【解决方案2】:

正如@Zack 在他的回答中所说,x86-64 ABI 需要 16 字节的堆栈对齐。如果您在 x86 或 x86_64 上使用 gcc,则默认堆栈是 16 字节对齐的。

来自gcc 文档:

-mpreferred-stack-boundary=num 尝试保持堆栈边界与 2 提升到 num 字节边界对齐。如果未指定-mpreferred-stack-boundary,则默认为4(16字节或128位)。

https://gcc.gnu.org/onlinedocs/gcc/i386-and-x86-64-Options.html

进一步查看文档,如果禁用 SSE gcc 可能会将堆栈对齐到 8 个字节(因此违反 ABI 要求)。

【讨论】:

    【解决方案3】:

    您无法控制编译器将在堆栈上执行的操作。您也无法控制堆栈上变量的顺序。还允许编译器引入填充,以便更好地对齐内存中的内容(尽管不在数组内部:这是严格禁止的)。在某些情况下,它甚至不会在堆栈上分配变量,而是在寄存器中。

    如果您在组装层面有特定需求,请在组装中进行。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-08-04
      • 1970-01-01
      • 1970-01-01
      • 2012-12-26
      • 2012-11-05
      • 2010-11-09
      • 2013-12-21
      相关资源
      最近更新 更多