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.