当我尝试在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上编译它。
答案 0 :(得分:6)
您收到总线错误的原因是您在汇编代码中调用ret
。 ret
会导致程序控件转移到堆栈顶部的返回地址,您可以使用push
和pop
进行操作。我强烈建议查看英特尔指令集参考中的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;
}
值得注意的变化是:
ret
ebp
代替esp
将参数引用至doStuff
flags
更改为0x00100002
更改(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建议你回到调用者的调用者。