【问题标题】:What does VC/C++ naked attribute do?VC/C++ 裸属性有什么作用?
【发布时间】:2011-02-17 15:30:34
【问题描述】:

From msdn

对于用裸函数声明的函数 属性,编译器生成代码 没有序言和结语代码。你 可以使用此功能编写自己的 序言/结语代码序列使用 内联汇编代码。裸函数 在写作中特别有用 虚拟设备驱动程序。

 __declspec(naked) declarator

什么是“序言和结语代码”。我看到一个用 C 代码编写的库,只使用 libc 在设备或固件上运行。它调用函数没有问题,裸关键字有什么作用,为什么需要它?

注意:我不确定这些库中的函数使用什么调用约定。

【问题讨论】:

  • 请注意,它不是 C 或 C++ 关键字:它是 Visual C++ 提供的语言扩展(它也不是 Visual C++ 中的关键字,它是一个属性)。 @Daniel 提供的链接对序言和尾声提供了不错的解释。可以在here 找到该页面的最新版本(至少乍一看是相同的)。

标签: c++ c visual-c++


【解决方案1】:

Prolog:在函数体之前运行的代码,通常是处理函数入口和参数处理的代码。 Epilog:在函数体之后运行的代码,通常是处理函数返回和返回值的代码。

有了“裸体”,你必须/有机会自己写这些东西。

【讨论】:

    【解决方案2】:

    Prolog 和 Epilog 代码是设置调用堆栈的第一条/最后几条指令。当您实现类似中断例程的东西时,您会使用裸机,您需要严格控制该函数中出现的确切指令。

    【讨论】:

      【解决方案3】:

      __declspec(naked) 指令删除自动生成的序言/结语。

      函数的序言/结语是保存和恢复寄存器并适当移动堆栈指针的样板代码。

      __fastcall 调用约定为例。它指定前两个参数在寄存器中(ECX 和 EDX),其余参数在堆栈中的右->左。所以对于一个函数:

      void __fastcall DoFoo(int first, int second);

      我的汇编器有点生锈,但序言可能看起来像:

      mov %ecx, first
      mov %edx, second
      pushl %ebp
      mov %esp, %ebp
      sub bytes, %esp
      

      然而,不同的调用约定会产生不同的序言/结语代码。

      Wiki

      【讨论】:

        【解决方案4】:

        prolog 和 epilog 代码通常会处理堆栈,通常在堆栈上传递和返回参数。要求编译器不生成这意味着您必须自己实现访问参数的正确方法。

        不确定它是否还涉及为函数自己的参数分配堆栈空间,这通常在函数的一开始就完成(然后在函数退出之前取消完成),所以看起来很可能。

        【讨论】:

          【解决方案5】:

          如果没有 __declspec(naked),您的编译器将负责正确的调用约定处理(将输入参数推入堆栈,为局部变量“保留”空间等)。在某些情况下,您可以自己完成。

          例如 - 如果没有 __declspec(naked) 以下前 3(prolog) 和后 3(epilog) 指令,您的编译器将提供以下指令(假设使用了 cdecl 调用约定)。

          __declspec(naked) void func(int a, int b, int c, int d)
          {
              _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
          

          顺便说一句 - args 以相反的顺序推送(与调用 func 时发现的顺序相比)以允许可变长度函数工作

          【讨论】:

            猜你喜欢
            • 2011-10-21
            • 2019-10-16
            • 1970-01-01
            • 1970-01-01
            • 2018-05-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多