汇编:功能模板冥想

时间:2014-11-22 15:20:05

标签: function assembly x86 stack gas

我现在正在学习装配,而且我不会对(可能是)标准功能模板有所了解。

因此,根据this really nice book“要记住的功能形式如下:”

function_label:
    pushl   %ebp
    movl    %esp, %ebp
    < normal function code goes here>
    movl    %ebp, %esp
    popl    %ebp
    ret
好吧,我对它很满意,但有一件小事我不明白。在“正常功能代码”之后,我们恢复esp的初始(预调用)值,该值先前存储在ebp中。

现在,我清楚地理解为什么我们希望将esp值提供给未触及的调用上下文。我不明白的是在函数执行期间可以在哪些条件下更改esp值。

这个模板中包含的对我们自己的某种保护(如果我们以某种方式破坏我们代码中某处的堆栈)?或者可能更改函数内的堆栈值是正常的做法? 或者,即使我们没有对它执行任何操作,初始esp值也可能在执行期间最终更改? (事实上​​,我无法弄清楚这是怎么回事。)

在考虑这个问题时我觉得很傻,并在这个简单的代码中用esp检查gdb值:

.section .data
message:
    .asciz "> Hello from function #%d\n"

.section .text
.globl main

main:
    nop
    call    overhere
    pushl   $0
    call    exit
overhere:
    pushl   %ebp
    movl    %esp, %ebp
    pushl   $1
    pushl   $message
    call    printf
    add     $8, %esp
    movl    %ebp, %esp
    popl    %ebp
    ret 

并且esp(正如我实际预期的那样)未受影响,因此将ebp移动到esp实际上并未改变任何内容。

现在,我希望很清楚我想知道的是什么:

  • esp值最终会自行更改吗? (我敢打赌它不能。)
  • 如果不能,那么上面的这个模板显然假定程序员可能会在函数内部以某种方式更改它。但我无法弄清楚为什么在地球上可能需要这样做,所以 - 将esp值改为错误?

提前谢谢你,原谅我的无知。

1 个答案:

答案 0 :(得分:0)

我很困惑你错过了明确更改espadd $8, %esp的说明。所以答案显然是肯定的,它可能会在功能期间发生变化而不是错误。请注意,pushcall也会更改它,实际上add是为了补偿两个push指令(ret在{{}的末尾1}}将平衡printf)。更改call的其他典型原因是局部变量的分配。这显示在您显示的功能模板中,在esp之后通常看起来像sub $size_of_locals, %esp

那就是说,你不需要使用movl %esp, %ebp来记住堆栈指针,只要你确保它在函数出口处具有与进入时相同的值。在启用优化时,最新版本的gcc不会使用ebp,否则您可以使用ebp来执行此操作。