2010-05-16 77 views
1

在汇编:非法指令的gcc汇编

.globl _test 

_test: 

pushl %ebp 
movl %esp, %ebp 
movl 8(%ebp), %eax 

pushl %eax 
call printf 

popl %ebp 
ret 

从调用C

main() 
{ 
    _test("Hello"); 
} 

编译:

GCC -m32 -o测试test.c的test.s

这代码有时会给我非法的指令,并且在其他时候会导致段错误 在gdc我总是得到非法的指令,这只是一个简单的测试,我有一个更大的程序工作,突然没有上诉原因后停止工作,现在我总是得到这个错误,即使我从头开始像上面。

我已经缩小到pushl%eax & call printf,如果我注释掉那些代码运行良好的行。

任何想法? (我正在我的大学linux群集上运行该程序,所以我没有更改任何设置..)

+0

在通话结束后,您忘了从堆栈中删除参数到'printf()'。你的'popl%ebp'会把错误的东西关掉,你的'ret'试图跳到错误的位置 - 因为堆栈里有一个字比你预期的多。 – 2012-05-29 17:05:28

回答

3

最后两条指令破坏了堆栈基址指针。任何依靠ebp(基指针)指向实际堆栈空间的代码都将失败。通常期望ebp指向堆栈空间是一个安全的假设,并且在与C代码接口时不应使该假定失效。

您正在做pushl %eax(或任何其他注册),然后做popl %ebp。这两个在一起的效果与做movl %eax, %ebp的效果相同。

我假设你试图返回存储在eax中的值。在C调用约定中,eax用于返回值,因此不需要对其进行推送或对其执行任何操作,只需将值留在其中,其他代码就会将其提取出来。如果这不是你想要做的,那么我很难理解你为什么要在这个函数的末尾推动%eax。

+0

感谢您指出这一点,我更新了一个更加理智的示例,但我仍然得到了错误。 printf弹出堆栈的最后一项,所以popl ebp现在应该是正确的。 – Bernt 2010-05-16 19:23:25

+1

@Bernt:你错了''printf'弹出任何东西离开你的堆栈。在Linux/x86中,当您调用的函数返回时,堆栈保持不变 - 您之前“推送”的所有参数仍然存在。这就是你的代码中的问题,一个字你不期望。 – 2012-05-29 17:07:22

2

leave替换弹出式指令。这会恢复堆栈和基址指针。