1

使用 Microsoft 编译器的 Visual C++ 允许我们使用以下方法定义内联汇编代码:

__asm {
    nop
}

我需要的是一个宏,它可以将这样的指令乘以n次,例如:

ASM_EMIT_MULT(op, times)

例如:

ASM_EMIT_MULT(0x90, 160)

那可能吗?我怎么能这样做?

4

2 回答 2

6

使用 MASM,这非常简单。安装的一部分是一个名为的文件listing.inc(因为现在每个人都将 MASM 作为 Visual Studio 的一部分,所以这将位于您的 Visual Studio 根目录/VC/include 中)。该文件定义了一系列npad宏,它们采用单个size参数并扩展为适当的非破坏性“填充”操作码序列。如果您只需要一个字节的填充,则使用明显的nop指令。但与其使用一长串的nops 直到达到所需的长度,英特尔实际上推荐了其他适当长度的非破坏性操作码,其他供应商也是如此。这些预定义npad宏使您不必记住该表,更不用说使代码更具可读性。

不幸的是,内联汇编不是一个全功能的汇编器。在像 MASM 这样的真正的汇编程序中缺少很多东西。宏 ( MACRO) 和重复 ( REPEAT/REPT ) 是缺少的东西之一。

但是,ALIGN指令 在内联汇编中使用。这些将生成所需数量的nops 或其他非破坏性操作码,以强制对齐下一条指令。使用它非常简单。这是一个非常愚蠢的示例,我在其中获取了工作代码并在其中添加了随机aligns:

unsigned long CountDigits(unsigned long value)
{
   __asm
   {
      mov    edx, DWORD PTR [value]
      bsr    eax, edx
      align  4
      xor    eax, 1073741792
      mov    eax, DWORD PTR [4 * eax + kMaxDigits+132]
      align  16
      cmp    edx, DWORD PTR [4 * eax + kPowers-4]
      sbb    eax, 0
      align  8
   }
}

这会生成以下输出(MSVC 的汇编列表使用npad x,其中x是字节数,就像您在 MASM 中编写的一样):

PUBLIC CountDigits
_TEXT SEGMENT
_value$ = 8
CountDigits PROC
    00000 8b 54 24 04        mov   edx, DWORD PTR _value$[esp-4]
    00004 0f bd c2           bsr   eax, edx
    00007 90                 npad  1       ;// enforcing the "align 4"
    00008 35 e0 ff ff 3f     xor   eax, 1073741792
    0000d 8b 04 85 84 00     
          00 00              mov   eax, DWORD PTR _kMaxDigits[eax*4+132]
    00014 eb 0a 8d a4 24     
          00 00 00 00 8d     
          49 00              npad  12      ;// enforcing the "align 16"
    00020 3b 14 85 fc ff     
          ff ff              cmp   edx, DWORD PTR _kPowers[eax*4-4]
    00027 83 d8 00           sbb   eax, 0
    0002a 8d 9b 00 00 00     
          00                 npad  6       ;// enforcing the "align 8"
    00030 c2 04 00           ret   4
CountDigits ENDP
_TEXT   ENDS

如果您实际上并不想强制对齐,而只是想插入任意数量的nops (也许作为以后热补丁的填充物?),那么您可以使用 C 宏来模拟效果:

#define NOP1   __asm { nop }
#define NOP2   NOP1  NOP1
#define NOP4   NOP2  NOP2
#define NOP8   NOP4  NOP4
#define NOP16  NOP8  NOP8
// ...
#define NOP64  NOP16 NOP16 NOP16 NOP16
// ...etc.

然后根据需要添加您的代码:

unsigned long CountDigits(unsigned long value)
{
   __asm
   {
      mov   edx, DWORD PTR [value]
      bsr   eax, edx
      NOP8
      xor   eax, 1073741792
      mov   eax, DWORD PTR [4 * eax + kMaxDigits+132]
      NOP4
      cmp   edx, DWORD PTR [4 * eax + kPowers-4]
      sbb   eax, 0
   }
}

产生以下输出:

PUBLIC CountDigits
_TEXT SEGMENT
_value$ = 8
CountDigits PROC
  00000 8b 54 24 04      mov   edx, DWORD PTR _value$[esp-4]
  00004 0f bd c2         bsr   eax, edx
  00007 90               npad  1     ;// these are, of course, just good old NOPs
  00008 90               npad  1
  00009 90               npad  1
  0000a 90               npad  1
  0000b 90               npad  1
  0000c 90               npad  1
  0000d 90               npad  1
  0000e 90               npad  1
  0000f 35 e0 ff ff 3f   xor   eax, 1073741792
  00014 8b 04 85 84 00
        00 00            mov   eax, DWORD PTR _kMaxDigits[eax*4+132]
  0001b 90               npad  1
  0001c 90               npad  1
  0001d 90               npad  1
  0001e 90               npad  1
  0001f 3b 14 85 fc ff
        ff ff            cmp   edx, DWORD PTR _kPowers[eax*4-4]
  00026 83 d8 00         sbb   eax, 0
  00029 c2 04 00         ret   4
CountDigits ENDP
_TEXT ENDS

或者,更酷的是,我们可以使用一些模板元编程魔法来获得相同的style效果。只需定义以下模板函数及其特化(对于防止无限递归很重要):

template <size_t N> __forceinline void npad()
{
    npad<N-1>();
    __asm  { nop }
}
template <> __forceinline void npad<0>()  { }

并像这样使用它:

unsigned long CountDigits(unsigned long value)
{
   __asm
   {
      mov   edx, DWORD PTR [value]
      bsr   eax, edx
   }
   npad<8>();
   __asm
   {
      xor   eax, 1073741792
      mov   eax, DWORD PTR [4 * eax + kMaxDigits+132]
   }
   npad<4>();
   __asm
   {
      cmp   edx, DWORD PTR [4 * eax + kPowers-4]
      sbb   eax, 0
   }
}

这将在所有优化的构建中产生所需的输出(与刚才的输出完全相同) - 无论您优化大小 ( /O1) 还是速度 ( /O2) - …但不是在调试构建中。如果您在调试版本中需要它,则必须求助于 C 宏。:-(

于 2016-07-20T18:29:40.200 回答
1

基于 Cody Gray Answer 和代码示例,使用模板递归和内联或 forceinline 进行元编程,如之前的代码所述

template <size_t N> __forceinline void npad()
{
    npad<N-1>();
    __asm  { nop }
}
template <> __forceinline void npad<0>()  { }

如果不设置一些选项,它不会在 Visual Studio 上运行,也不能保证它会运行

尽管 __forceinline 对编译器的指示比 __inline 更强,但内联仍由编译器自行决定执行,但没有使用启发式方法来确定内联此函数的好处。

您可以在此处阅读有关此内容的更多信息https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-4-c4714?view=vs-2019

在此处输入图像描述

于 2020-03-07T19:51:11.163 回答