0

我正在尝试学习如何理解汇编程序中的 c 函数。我使用“gcc code.c -m32 -o code -S -fno-stack-protector”编译了以下 c 程序

#include <stdio.h>

void function( int a, int b, int c )
{
   char buffer1[5];
   char buffer2[10];
}

void main()
{
   function( 1, 2, 3 );
}

汇编输出如下:

        .section    __TEXT,__text,regular,pure_instructions
    .globl  _function
    .align  4, 0x90
_function:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $28, %esp
    movl    16(%ebp), %eax
    movl    12(%ebp), %ecx
    movl    8(%ebp), %edx
    movl    %edx, -4(%ebp)
    movl    %ecx, -8(%ebp)
    movl    %eax, -12(%ebp)
    addl    $28, %esp
    popl    %ebp
    ret

    .globl  _main
    .align  4, 0x90
_main:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $24, %esp
    movl    $1, (%esp)
    movl    $2, 4(%esp)
    movl    $3, 8(%esp)
    call    _function
    addl    $24, %esp
    popl    %ebp
    ret


.subsections_via_symbols

esp 和 ebp 寄存器不能相等,因为定义 int a,b,c 的偏移量在 _main 和 _function 中不同。哪一行从 ebp 中减去 4?

谢谢!

4

2 回答 2

2

调用函数通常是这样的:

1) 在函数外部,参数被压入堆栈

subl    $24, %esp
movl    $1, (%esp)
movl    $2, 4(%esp)
movl    $3, 8(%esp)

2) 在函数内部,在堆栈上分配空间来保存参数和局部变量。

低于 $28, %esp

在这种情况下,您有 3 个整数和 15 个字符,总共 27 个字节;这与 4 字节边界对齐。

3) 然后将参数复制到本地堆栈空间中。

movl    16(%ebp), %eax
movl    12(%ebp), %ecx
movl    8(%ebp), %edx
movl    %edx, -4(%ebp)
movl    %ecx, -8(%ebp)
movl    %eax, -12(%ebp)

这里,16(%ebp) 和朋友是推到外面的参数的地址,-4(%ebp) 是新分配的堆栈空间中的地址,这些值被复制到其中。

于 2013-03-04T20:04:00.767 回答
1

无需在任何地方减去 4,因为它是返回地址(给定 32 位架构)将消耗堆栈上的前 4 个字节。

因此,当引用堆栈上的元素时,差价为 4 美元。在指令之前没有推送返回地址call因此偏移量 $0 $4 和 $8 在 main 中很好:

movl    $1, (%esp)
movl    $2, 4(%esp)
movl    $3, 8(%esp)

执行:

call    _function

in main 会将 eip 的内容(返回地址)推入堆栈,从而将 esp 增加 4。

在函数中,作为返回地址(消耗 4 个字节)将位于堆栈顶部,正确的偏移量是 -$4、-$8 和 -$12,这正是您的函数中使用的值:

movl    %edx, -4(%ebp)
movl    %ecx, -8(%ebp)
movl    %eax, -12(%ebp)
于 2013-03-04T19:55:22.023 回答