【问题标题】:What is the purpose of the sub esp, eax in function prologue?sub esp, eax in function prologue 的目的是什么?
【发布时间】:2014-04-23 20:34:16
【问题描述】:

我有一个简单的函数序言(对于 C 程序的 main(),没有参数),如下所示:

0x8048384     push ebp                     
0x8048385     mov ebp,esp               
0x8048387     sub esp,0x18                
0x804838a     and esp,0xfffffff0          
0x804838d     mov eax,0x0                 
0x8048392     sub esp,eax

我很好奇,将 eax 设置为 0 然后从 esp 中减去它的目的是什么?这似乎是浪费的指令。是否还有其他 gcc 生成的函数序言的示例,其中这将具有更多意义(或者 eax 的值不是 0)?

提前致谢!

编辑:整个 C 程序(来自“探索的艺术”一书)如下。它仅使用 -g 选项编译,使用 gcc 3.3.6(是的,很旧,但它来自虚拟机映像)。这应该是关于堆栈的一课。

void test_function(int a, int b, int c, int d)
{
    int flag;
    char buffer[10];
    flag 31337;
    buffer[0] = 'A';
}

int main()
{
    test_function(1, 2, 3, 4);
}

提供编辑#2 反汇编(gcc -g -S -masm=intel):

    .file   "stack_example.c"
    .intel_syntax
    .file 1 "stack_example.c"
    .section    .debug_abbrev,"",@progbits
.Ldebug_abbrev0:
    .section    .debug_info,"",@progbits
.Ldebug_info0:
    .section    .debug_line,"",@progbits
.Ldebug_line0:
    .text
.Ltext0:
.globl test_function
    .type   test_function, @function
test_function:
.LFB3:
    .loc 1 1 0
    push    %ebp
.LCFI0:
    mov %ebp, %esp
.LCFI1:
    sub %esp, 40
.LCFI2:
    .loc 1 5 0
.LBB2:
    mov DWORD PTR [%ebp-12], 31337
    .loc 1 6 0
    mov BYTE PTR [%ebp-40], 65
    .loc 1 7 0
    leave
    ret
.LBE2:
.LFE3:
    .size   test_function, .-test_function
.globl main
    .type   main, @function
main:
.LFB5:
    .loc 1 9 0
    push    %ebp
.LCFI3:
    mov %ebp, %esp
.LCFI4:
    sub %esp, 24
.LCFI5:
    and %esp, -16
    mov %eax, 0
    sub %esp, %eax
    .loc 1 10 0
    mov DWORD PTR [%esp+12], 4
    mov DWORD PTR [%esp+8], 3
    mov DWORD PTR [%esp+4], 2
    mov DWORD PTR [%esp], 1
    call    test_function
    .loc 1 11 0
    leave
    ret
.LFE5:
    .size   main, .-main
    .section    .debug_frame,"",@progbits
.Lframe0:
    .long   .LECIE0-.LSCIE0
.LSCIE0:
    .long   0xffffffff
    .byte   0x1
    .string ""
    .uleb128 0x1
    .sleb128 -4
    .byte   0x8
    .byte   0xc
    .uleb128 0x4
    .uleb128 0x4
    .byte   0x88
    .uleb128 0x1
    .align 4
.LECIE0:
.LSFDE0:
    .long   .LEFDE0-.LASFDE0
.LASFDE0:
    .long   .Lframe0
    .long   .LFB3
    .long   .LFE3-.LFB3
    .byte   0x4
    .long   .LCFI0-.LFB3
    .byte   0xe
    .uleb128 0x8
    .byte   0x85
    .uleb128 0x2
    .byte   0x4
    .long   .LCFI1-.LCFI0
    .byte   0xd
    .uleb128 0x5
    .align 4
.LEFDE0:
.LSFDE2:
    .long   .LEFDE2-.LASFDE2
.LASFDE2:
    .long   .Lframe0
    .long   .LFB5
    .long   .LFE5-.LFB5
    .byte   0x4
    .long   .LCFI3-.LFB5
    .byte   0xe
    .uleb128 0x8
    .byte   0x85
    .uleb128 0x2
    .byte   0x4
    .long   .LCFI4-.LCFI3
    .byte   0xd
    .uleb128 0x5
    .align 4
.LEFDE2:
    .text
.Letext0:
    .section    .debug_info
    .long   0x118
    .value  0x2
    .long   .Ldebug_abbrev0
    .byte   0x4
    .uleb128 0x1
    .long   .Ldebug_line0
    .long   .Letext0
    .long   .Ltext0
    .string "stack_example.c"
    .string "/home/booksrc"
    .string "GNU C 3.3.6 (Ubuntu 1:3.3.6-15ubuntu1)"
    .byte   0x1
    .uleb128 0x2
    .long   0xd5
    .byte   0x1
    .string "test_function"
    .byte   0x1
    .byte   0x1
    .byte   0x1
    .long   .LFB3
    .long   .LFE3
    .byte   0x1
    .byte   0x55
    .uleb128 0x3
    .string "a"
    .byte   0x1
    .byte   0x1
    .long   0xd5
    .byte   0x2
    .byte   0x91
    .sleb128 8
    .uleb128 0x3
    .string "b"
    .byte   0x1
    .byte   0x1
    .long   0xd5
    .byte   0x2
    .byte   0x91
    .sleb128 12
    .uleb128 0x3
    .string "c"
    .byte   0x1
    .byte   0x1
    .long   0xd5
    .byte   0x2
    .byte   0x91
    .sleb128 16
    .uleb128 0x3
    .string "d"
    .byte   0x1
    .byte   0x1
    .long   0xd5
    .byte   0x2
    .byte   0x91
    .sleb128 20
    .uleb128 0x4
    .string "flag"
    .byte   0x1
    .byte   0x2
    .long   0xd5
    .byte   0x2
    .byte   0x91
    .sleb128 -12
    .uleb128 0x4
    .string "buffer"
    .byte   0x1
    .byte   0x3
    .long   0xdc
    .byte   0x2
    .byte   0x91
    .sleb128 -40
    .byte   0x0
    .uleb128 0x5
    .string "int"
    .byte   0x4
    .byte   0x5
    .uleb128 0x6
    .long   0xec
    .long   0xfc
    .uleb128 0x7
    .long   0xec
    .byte   0x9
    .byte   0x0
    .uleb128 0x5
    .string "unsigned int"
    .byte   0x4
    .byte   0x7
    .uleb128 0x5
    .string "char"
    .byte   0x1
    .byte   0x6
    .uleb128 0x8
    .byte   0x1
    .string "main"
    .byte   0x1
    .byte   0x9
    .long   0xd5
    .long   .LFB5
    .long   .LFE5
    .byte   0x1
    .byte   0x55
    .byte   0x0
    .section    .debug_abbrev
    .uleb128 0x1
    .uleb128 0x11
    .byte   0x1
    .uleb128 0x10
    .uleb128 0x6
    .uleb128 0x12
    .uleb128 0x1
    .uleb128 0x11
    .uleb128 0x1
    .uleb128 0x3
    .uleb128 0x8
    .uleb128 0x1b
    .uleb128 0x8
    .uleb128 0x25
    .uleb128 0x8
    .uleb128 0x13
    .uleb128 0xb
    .byte   0x0
    .byte   0x0
    .uleb128 0x2
    .uleb128 0x2e
    .byte   0x1
    .uleb128 0x1
    .uleb128 0x13
    .uleb128 0x3f
    .uleb128 0xc
    .uleb128 0x3
    .uleb128 0x8
    .uleb128 0x3a
    .uleb128 0xb
    .uleb128 0x3b
    .uleb128 0xb
    .uleb128 0x27
    .uleb128 0xc
    .uleb128 0x11
    .uleb128 0x1
    .uleb128 0x12
    .uleb128 0x1
    .uleb128 0x40
    .uleb128 0xa
    .byte   0x0
    .byte   0x0
    .uleb128 0x3
    .uleb128 0x5
    .byte   0x0
    .uleb128 0x3
    .uleb128 0x8
    .uleb128 0x3a
    .uleb128 0xb
    .uleb128 0x3b
    .uleb128 0xb
    .uleb128 0x49
    .uleb128 0x13
    .uleb128 0x2
    .uleb128 0xa
    .byte   0x0
    .byte   0x0
    .uleb128 0x4
    .uleb128 0x34
    .byte   0x0
    .uleb128 0x3
    .uleb128 0x8
    .uleb128 0x3a
    .uleb128 0xb
    .uleb128 0x3b
    .uleb128 0xb
    .uleb128 0x49
    .uleb128 0x13
    .uleb128 0x2
    .uleb128 0xa
    .byte   0x0
    .byte   0x0
    .uleb128 0x5
    .uleb128 0x24
    .byte   0x0
    .uleb128 0x3
    .uleb128 0x8
    .uleb128 0xb
    .uleb128 0xb
    .uleb128 0x3e
    .uleb128 0xb
    .byte   0x0
    .byte   0x0
    .uleb128 0x6
    .uleb128 0x1
    .byte   0x1
    .uleb128 0x1
    .uleb128 0x13
    .uleb128 0x49
    .uleb128 0x13
    .byte   0x0
    .byte   0x0
    .uleb128 0x7
    .uleb128 0x21
    .byte   0x0
    .uleb128 0x49
    .uleb128 0x13
    .uleb128 0x2f
    .uleb128 0xb
    .byte   0x0
    .byte   0x0
    .uleb128 0x8
    .uleb128 0x2e
    .byte   0x0
    .uleb128 0x3f
    .uleb128 0xc
    .uleb128 0x3
    .uleb128 0x8
    .uleb128 0x3a
    .uleb128 0xb
    .uleb128 0x3b
    .uleb128 0xb
    .uleb128 0x49
    .uleb128 0x13
    .uleb128 0x11
    .uleb128 0x1
    .uleb128 0x12
    .uleb128 0x1
    .uleb128 0x40
    .uleb128 0xa
    .byte   0x0
    .byte   0x0
    .byte   0x0
    .section    .debug_pubnames,"",@progbits
    .long   0x29
    .value  0x2
    .long   .Ldebug_info0
    .long   0x11c
    .long   0x63
    .string "test_function"
    .long   0x104
    .string "main"
    .long   0x0
    .section    .debug_aranges,"",@progbits
    .long   0x1c
    .value  0x2
    .long   .Ldebug_info0
    .byte   0x4
    .byte   0x0
    .value  0x0
    .value  0x0
    .long   .Ltext0
    .long   .Letext0-.Ltext0
    .long   0x0
    .long   0x0
    .section    .note.GNU-stack,"",@progbits
    .ident  "GCC: (GNU) 3.3.6 (Ubuntu 1:3.3.6-15ubuntu1)"

【问题讨论】:

  • 您使用了什么优化级别?可能是编译器生成了用于创建堆栈帧(用于自动存储)的“样板”,但由于您没有声明局部变量,因此大小为 0。这会随着优化(死代码消除)而消失。跨度>
  • 根据这个答案,stackoverflow.com/questions/11886429/…,它将它与 SSE 指令要求的 16 字节边界对齐。有关更多信息,请参阅链接的答案。
  • 'and esp,0xfffffff0' 正在进行对齐,OP 询问的是 'mov eax,0x0' 和 'sub esp,eax'。
  • @amdn - 我想你可能是对的。我只用 -g 选项编译了这个(没有优化)。不过,我很好奇 gcc 何时会给 eax 一个非“0”的值。这两条指令似乎没用,因为如果您需要为变量分配更多内存,您可以使用 sub esp 指令来完成。
  • gcc 似乎认识到这些局部变量未被使用,因此它没有为它们分配存储空间。尝试使用 -O3 进行编译,看看它的作用。

标签: gcc assembly


【解决方案1】:

更新

看到源代码和完整的反汇编后,我不知道为什么这部分 (mov eax, 0; sub esp,eax) 应该在那里。我没有看到编译器可能一直针对的合理概括。我敢肯定,任何优化级别都会删除这些行,而新版本的 gcc 似乎会忽略它们。

可能是旧版本的 gcc 有这样一个扩展的函数头(尽管我认为没有理由)或者这些命令是 gcc 内部表示的一些工件。或许对gcc历史比较了解的人可以回答这个问题……


旧答案

我不确定,但可能的解释是

push ebp           ; store stack pointer          
mov ebp,esp        ; create local frame
sub esp,0x18       ; make room for some variables + empty space to...        
and esp,0xfffffff0 ; ... align the stack-pointer 
mov eax,0x0        ; eax = sizeof(someClass)
sub esp,eax        ; create room for one object on the aligned stack

这当然假设已经进行了一些优化(比如将对象大小减小到 0,可能是一些内联,例如 alloca),但是 gcc 没有完全优化代码。可能是因为 -Og-g 等标志。

【讨论】:

  • @ooga 我不明白为什么在这种情况下必须对esp 采取行动。很高兴看到相应的来源。我想在一开始的某个地方有一个alloca 调用,它是内联的......
  • 我同意。需要更多信息。
  • @ooga - 添加了更多信息 :) 如果它仍然像泥浆一样清澈,请告诉我。
  • 我对 sub esp, 0x18 创建的 6 个字节感到困惑。 main 函数没有局部变量,是 6 个字节的隐式参数 argcargv
猜你喜欢
  • 1970-01-01
  • 2012-10-10
  • 2013-09-10
  • 1970-01-01
  • 2012-04-05
  • 2014-06-20
  • 2014-10-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多