如何将 RDRAND 指令添加到用 VS 2008 编译的 64 位代码中?

How to add RDRAND instruction into 64-bit code compiled with VS 2008?

我在 Visual Studio 2008 IDE 从事 C++ 项目,我需要使用 immintrin.h 中定义的英特尔新 RDRAND instruction. I did a quick search, and MSDN recommends using _rdrand64_step 内在函数VS 2008 中没有。

在 32 位编译代码中,我可以这样使用 asm 关键字:

    __asm
    {
        xor eax, eax

        ;RDRAND instruction = Set random value into EAX.
        ;Will set overflow [C] flag if success
        _emit 0x0F
        _emit 0xC7
        _emit 0xF0
    }

但在 x64 上 asm 不受支持。

你能建议我如何使用 RDRAND 指令为 64 位编译我的项目吗?

您需要将编译器升级到支持 _rdrand64_step 内在函数(自 Visual Studio 2012 年起支持),或者使用普通(外部)程序集来创建您自己的函数(自 Visual C++ 不支持 x86-64 目标的内联汇编。

例如:

_TEXT   SEGMENT

    PUBLIC rdrand32_step
    PUBLIC rdrand32_retry
    PUBLIC rdrand64_step
    PUBLIC rdrand64_retry

    ; int rdrand32_step(unsigned *p)
rdrand32_step PROC
    xor     eax, eax
    rdrand  edx
    ; DB    0fh, 0c7h, 0f2h
    setc    al
    mov     [rcx], edx
    ret
rdrand32_step ENDP

    ; unsigned rdrand32_retry()
rdrand32_retry PROC
retry:
    rdrand  eax
    ; DB    0fh, 0c7h, 0f0h
    jnc     retry
    ret
rdrand32_retry ENDP

    ; int rdrand64_step(unsigned long long *p)
rdrand64_step PROC
    xor     eax, eax
    rdrand  rdx
    ; DB    048h, 0fh, 0c7h, 0f2h
    setc    al
    mov     [rcx], edx
    ret
rdrand64_step ENDP

    ; unsigned long long rdrand64_retry()
rdrand64_retry PROC
retry:
    rdrand  rax
    ; DB    048h, 0fh, 0c7h, 0f0h
    jnc     retry
    ret
rdrand64_retry ENDP

_TEXT   ENDS

    END

如果您使用的是 Visual Studio 2008 年的 MASM 版本,您可能需要注释掉 RDRAND 指令并取消注释后面的 DB 指令。

哇,我花了一段时间才弄明白。以下是 Visual Studio 2008 仅用于 x64 编译的步骤:

(A) 创建一个空白项目:文件 -> 新建 -> 项目。然后点击 "Visual C++" 和 select "Empty Project." 给它起个名字,然后点击确定创建。

(B) 转到您的 VS 安装文件夹,在我的例子中是 C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\VCProjectDefaults 并复制 masm.rules 文件并将其命名为 masm64.rules

(C) 在记事本中打开masm64.rules并搜索Microsoft Macro Assembler并将其更改为x64 Microsoft Macro Assembler。将有两个地方可以做到这一点。然后搜索 ml.exe 并将其更改为 ml64.exe。然后保存该文件并关闭记事本。

(D) 右键单击​​ "Solution Explorer" 和 select "Custom build rules" 中的项目并选中 x64 Microsoft Macro Assembler 并单击确定.

(E) 在 "Solution Explorer" 中右键单击您的项目,然后 select 添加 -> 新项目,select Text File (.txt) 并将其命名为 .asm 扩展名。我称之为 funcs_asm_x64.asm。然后点击确定。

(F) 打开 funcs_asm_x64.asm 并输入你的 x64 asm。对我来说,我对使用 64 位操作数调用 RDRAND 很感兴趣。我做了以下事情。该函数将一个参数作为指向一个 64 位整数的指针,它将用随机位填充。如果成功它将 return 1 in rax ,否则它将 return 0.

这里要记住的一件事是 x64 代码仅使用 __fastcall calling convention,这意味着函数的前 4 个参数在寄存器中传递:RCXRDXR8R9

.code

RdRand64  PROC
    ; RCX = pointer to receive random 64-bit value
    ; RETURN: [RAX] = 1 if success, 0 if failed

    xor         rax, rax

    test        rcx, rcx
    jz          lbl_out

    ;push       rdx
    xor         rdx, rdx
    DB          048h, 0fh, 0c7h, 0f2h       ;RDRAND RDX

    setc        al
    mov         [rcx], rdx
    ;pop        rdx

 lbl_out:
    ret

RdRand64  ENDP

END

(G)然后在"Solution Explorer"中右击你的项目,select添加->新建项,selectC++ File (.cpp)并命名为main.cpp,点击确定创建。然后将以下内容添加到 main.cpp 文件中:

extern "C" __int64 __fastcall RdRand64(unsigned __int64* pRndVal);

 void main()
{
}

主要部分是extern "C"定义。 main() 需要方法来满足 MASM 要求。

(H) 然后转到构建 -> 配置管理器并打开显示 "Active solution platform" 和 select 新建的下拉列表。然后在 "Type or select the new platform" 中选择 "x64" 并单击确定。然后 select "x64" as "Active solution platform" 还有 select "Release" in "Active solution configuration."

(I) 关闭配置管理器 window 并构建解决方案。如果成功,请在 \x64\Release 文件夹中寻找 funcs_asm_x64.obj 文件作为您的解决方案。将该文件复制到您的主解决方案文件夹中(您需要在其中使用 RDRAND 指令。)

(J) 然后在您需要使用 RDRAND 指令的主要解决方案中,右键单击 "Solution Explorer" 中的项目并转到特性。然后转到链接器 -> 命令行并添加您的 obj 文件名。显然只对 DebugReleasex64 平台这样做。在我的例子中是 funcs_asm_x64.obj。点击确定保存。

(K) 然后要使用我刚刚创建的函数,首先添加 extern "C" 定义,就像您在第一个项目中所做的那样:

extern "C" __int64 __fastcall RdRand64(unsigned __int64* pRndVal);

然后你可以这样调用它(显然它不能内联):

unsigned __int64 randomNumber = 0;
__int64 bResult = RdRand64(&randomNumber);

(1) 显然以上所有内容对于 Win32x86 构建都不是必需的。为此,只需使用我在原始 post.

中展示的内联程序集

(2) 同样显然您需要调用 __cpuid 命令以确保支持 RDRAND 指令。在许多 CPU 上,它仍然不是。所以如果不是,那么不要调用我的 RdRand64 方法,因为它会崩溃!您可以使用此代码检查结果并将其存储在全局变量中的某处:

#include <intrin.h>

bool is_RDRAND_supported()
{
    int name[4] = {0};
    __cpuid(name, 0);

    if(name[1] == 0x756e6547 &&         //uneG
        name[2] == 0x6c65746e &&        //letn
        name[3] == 0x49656e69)          //Ieni
    {
        int data[4] = {0};
        __cpuid(data, 1);

        //Check bit 30 on the 2nd index (ECX register)
        if(data[2] & (0x1 << 30))
        {
            //Supported!
            return true;
        }
    }

    return false;
}

(3) 有一种方法可以将asm 文件包含在VS 2008 的同一个项目中。不幸的是,如果您这样做,您将无法将项目切换回 Win32 并在需要时进行编译。因此,如果您仅为 x64 编译它,则保存一个步骤并在同一解决方案中完成所有操作。

这相当简单,尽管是间接的:为 _rdrand64_step 创建一个小型 C 包装器,使用 VS2012 将其编译成一个 .OBJ 文件,没有花哨的选项(没有 /LTCG,没有 /Gs 等),并且 link 这个目标文件按原样放入你的 VS2008 项目中。 VS2008编译器可能不知道指令,但是VS2008linker不关心