总线错误:在Mac OS X上使用GCC进行内联x86组装

时间:2011-04-26 00:50:59

标签: macos gcc assembly

当我尝试在Snow Leopard上运行使用gcc 4.2.1编译的代码时,我收到“总线错误”

#include <stdio.h>

/*__declspec(naked)*/ void
doStuff(unsigned long int val, unsigned long int flags, unsigned char *result)
{
    __asm{
        push eax
        push ebx
       push ecx
        push edx

        mov eax, dword ptr[esp + 24]//val
        mov ebx, dword ptr[esp + 28]//flags
        //mov ecx, dword ptr[esp + 32]//result

        and eax, ebx
        mov result, eax

        pop edx
        pop ecx
        pop ebx
        pop eax

        ret
    }
}

int main(int argc, char *argv[])
{
    unsigned long val = 0xAA00A1F2; 
    unsigned long flags = 0x00100001;   
    unsigned char result = 0x0;

    doStuff(val, flags, &result);   
    printf("Result is: %2Xh\n", result);

    return 0;
}

我使用以下命令编译gcc -fasm-blocks -m32 -o so so.c,没有任何错误或警告。我试图在doStuff()函数中运行一些汇编指令并将结果分配给答案。我做错了什么?

注意:这在Windows上的Visual Studio中运行良好,但我不得不注释掉declspec(裸体)以获取gcc在Mac上编译它。

2 个答案:

答案 0 :(得分:6)

您收到总线错误的原因是您在汇编代码中调用retret会导致程序控件转移到堆栈顶部的返回地址,您可以使用pushpop进行操作。我强烈建议查看英特尔指令集参考中的ret

下面是我在运行Mac OS X 10.6.7的iMac上编译并成功运行的代码。

#include <stdio.h>

/*__declspec(naked)*/ void
doStuff(unsigned long int val, unsigned long int flags, unsigned char *result)
{
  __asm
    {
        push eax
        push ebx
        push ecx

        mov eax, dword ptr[ebp + 8]  //val
        mov ebx, dword ptr[ebp + 12] //flags
        mov ecx, dword ptr[ebp + 16] //result

        and eax, ebx
        mov [ecx], eax

        pop ecx
        pop ebx
        pop eax
      }
}

int main(int argc, char *argv[])
{
  unsigned long val =   0xAA00A1F2;
  unsigned long flags = 0x00100002;
  unsigned char result = 0x0;

  doStuff(val, flags, &result);
  printf("Result is: %2Xh\n", result);

  return 0;
}

值得注意的变化是:

  1. 删除内联程序集中的ret
  2. 使用注册ebp代替esp将参数引用至doStuff
  3. flags更改为0x00100002
  4. 更改(1)修复了总线错误,(2)使参数更加一致,并且(3)只是确保函数按预期工作的快速方法。

    最后,我强烈建议您熟悉GNU调试器GDB,如果您还没有。您可以在项目页面http://www.gnu.org/software/gdb/找到有关它的更多信息,以及有关http://developer.apple.com/library/mac/#documentation/DeveloperTools/gdb/gdb/gdb_toc.html的Mac实施和教程的信息。

    编辑:添加了基本信息/链接到GDB,

答案 1 :(得分:1)

编译器将prologues和epilogues添加到函数调用中,这些prologues和epilogues负责设置堆栈帧,为局部变量保留堆栈空间,销毁堆栈帧并返回调用者。

使用帧指针时没有局部变量的函数的典型序言可能如下所示:

push ebp
mov ebp, esp

这将调用者的帧指针保存在堆栈上,并使当前帧指针在函数输入时等于堆栈指针。

相应的结语将是:

pop ebp
ret

恢复前一帧指针并返回给调用者。

如果你告诉gcc不使用帧指针(-fomit-frame-pointer),相应的序言将为空,结尾只包含ret

__declspec(naked)可能类似于gcc的__attribute__((naked))gcc's function attributes),它仅适用于某些体系结构,而不适用于x86。所以,在gcc上,你最好让Dean Pucsek建议你回到调用者的调用者。