简体   繁体   中英

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

I'm working on a C++ project in Visual Studio 2008 IDE, where I need to use Intel's new RDRAND instruction. I did a quick search, and MSDN recommends using _rdrand64_step intrinsic defined in immintrin.h , which I do not have in VS 2008.

In a 32-bit compiled code I can get away with using asm keyword as such:

    __asm
    {
        xor eax, eax

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

But on x64 asm is not supported.

Can you suggest how can I compile my project for 64-bit with the RDRAND instruction?

You either need to upgrade your compiler to one that does support the _rdrand64_step intrinsic (supported since Visual Studio 2012), or use normal (external) assembly to create your own functions (since Visual C++ does not support inline assembly for x86-64 targets).

For example:

_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

If you're using the version of MASM from Visual Studio 2008, you'll probably have to comment out the RDRAND instructions and uncomment the DB directives that follow them.

Wow, it took me a while to figure it out. Here's the steps for Visual Studio 2008 for x64 compilation only:

(A) Create a blank project: File -> New -> Project. Then click on "Visual C++" and select "Empty Project." Name it something, and click OK to create.

(B) Go to your VS installation folder, in my case it was C:\\Program Files (x86)\\Microsoft Visual Studio 9.0\\VC\\VCProjectDefaults and copy masm.rules file and name it masm64.rules

(C) Open masm64.rules in Notepad and search for Microsoft Macro Assembler and change it to x64 Microsoft Macro Assembler . There will be two places to do it. Then search for ml.exe and change it to ml64.exe . Then save that file and close Notepad.

(D) Right-click your project in "Solution Explorer" and select "Custom build rules" and check x64 Microsoft Macro Assembler and click OK.

(E) Right-click your project in "Solution Explorer" and select Add -> New Item, select Text File (.txt) and name it something with .asm extension. I'll call it funcs_asm_x64.asm . Then click OK.

(F) Open funcs_asm_x64.asm and type your x64 asm. For me I was interested in calling RDRAND with a 64-bit operand. I did the following. This function will take one parameter as a pointer to a 64-bit integer that it will fill out with random bits. It will return 1 in rax if success, otherwise it will return 0.

One thing to remember here is that x64 code uses only __fastcall calling convention , meaning that first 4 parameters for a function are passed in registers: RCX , RDX , R8 , and R9 :

.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) Then right-click your project in "Solution Explorer" and select Add -> New Item, select C++ File (.cpp) and name it main.cpp and click OK to create. Then add the following to the main.cpp file:

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

 void main()
{
}

The main part is the extern "C" definition. main() method is needed to satisfy MASM requirements.

(H) Then go to Build -> Configuration Manager and open the drop-down list where it says "Active solution platform" and select New. Then pick "x64" in "Type or select the new platform" and click OK. Then select "x64" as "Active solution platform" and also select "Release" in "Active solution configuration."

(I) Close configuration manager window and build solution. If it succeeded look for funcs_asm_x64.obj file in \\x64\\Release folder for your solution. Copy that file into your main solution folder (where you needed to use RDRAND instruction.)

(J) Then in your main solution where you need to use RDRAND instruction, right-click on your project in "Solution Explorer" and go to Properties. Then go to Linker -> Command Line and add your obj file name. Obviously do so only for x64 platform for Debug and Release . In my case it was funcs_asm_x64.obj . Click OK to save.

(K) Then to use this function that I just created, first add the extern "C" definition just like you had in the first project:

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

and then you can call it as such (obviously it cannot be inlined):

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

(1) Obviously all of the above is not necessary for the Win32 or x86 build. For that simply use the inline assembly like I showed in my original post.

(2) Also obviously you will need to call __cpuid command to ensure that RDRAND instruction is supported. On many CPUs it is still not. So if it is not, then don't call my RdRand64 method, as it will crash! You can use this code to check and store result somewhere in a global variable:

#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) There is a way to include the asm file in the same project in VS 2008 . Unfortunately if you do that you won't be able to switch the project back to Win32 and compile if you need to. So if you're compiling it only for x64 then save a step and do all of it in the same solution.

It's fairly easy, although indirect: Create a tiny C wrapper for _rdrand64_step , compile it into an .OBJ file using VS2012 with no fancy options (No /LTCG, no /Gs etc), and link this object file as-is into your VS2008 project. The VS2008 compiler may not know the instruction, but the VS2008 linker doesn't care.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM