对于用裸体声明的函数 属性,编译器生成代码 没有prolog和epilog代码。您 可以使用此功能编写自己的 prolog / epilog代码序列使用 内联汇编代码。裸体功能 在写作中特别有用 虚拟设备驱动程序。
__declspec(naked) declarator
什么是“prolog和epilog代码”。我看到用C语言编写的库只在libc上运行在设备或固件上。它调用函数没有问题,naked关键字做了什么以及为什么需要它?
注意:我不确定函数在这些库中使用的调用约定。
答案 0 :(得分:7)
Prolog:在函数体之前运行的代码,通常是处理函数入口和参数处理的代码。 Epilog:在函数体之后运行的代码,通常是处理函数返回和返回值的代码。
有了“裸体”,你必须/有机会自己写这些东西。
答案 1 :(得分:4)
Prolog和epilog代码是设置调用堆栈的第一个/最后几个指令。当你实现像中断例程这样的东西时,你需要使用裸体,在那里你需要严格控制该函数中出现的指令。
答案 2 :(得分:2)
__declspec(naked)
指令删除自动生成的prolog / epilog。
函数的prolog / epilog是样板代码,用于保存和恢复寄存器并适当地移动堆栈指针。
以__fastcall
调用约定为例。它指定前两个参数位于寄存器(ECX和EDX)中,其余参数位于堆栈的右侧和左侧。所以对于一个函数:
void __fastcall DoFoo(int first, int second);
我的汇编程序有点生疏,但序言可能看起来像:
mov %ecx, first
mov %edx, second
pushl %ebp
mov %esp, %ebp
sub bytes, %esp
然而,不同的调用约定将生成不同的序言/ epilog代码。
答案 3 :(得分:0)
prolog和epilog代码通常会处理堆栈,通常会传递和返回参数。要求编译器不生成这意味着您必须自己实现访问参数的正确方法。
不确定它是否还涉及为函数自己的参数分配堆栈空间,这通常在函数的最开始时完成(然后在函数退出之前完成),所以看起来很可能。
答案 4 :(得分:0)
如果没有 __ declspec(裸),你的编译器负责正确的调用对象处理(将输入args推送到堆栈,'为局部变量保留'空间等)。在某些情况下,你可以自己做。
例如 - 如果没有 __ declspec(裸)前3(prolog)和最后3(epilog)指令将由您的编译器提供(假设 cdecl 使用了呼叫对象)。
__declspec(naked) void func(inta, intb, int c, intd)
{
_asm{
push ebp
mov ebp, esp
sub esp, 8 // for 2 local int(32bit) variables - if you need it of course
mov dword ptr[ebp-4], 3 // set one local var to 3
mov dword ptr[ebp-8], 4 // set one local var to 4
mov eax, dword ptr [ebp+8] // a
mov ebx, dword ptr [ebp+12] // b
mov ecx, dword ptr [ebp+16] // c
mov edx, dword ptr [ebp+20] // d
add esp, 8 // remove space for local vars
mov esp, ebp
pop ebp
ret
}
}
您现在可以从C / C ++代码中调用此例程,如下所示:
func(0xAA, 0xBB, 0xCC, 0xDD);
将成为:
push 0DDh
push 0CCh
push 0BBh
push 0AAh
call func
BTW - 以相反的顺序推送args(与callinf func中找到的顺序相比)以允许可变长度的funcs工作