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.