delphiassemblyx86-64rdrand

converting ASM instruction RDRand to Win64


I have this function (RDRand - written by David Heffernan) that seam to work ok in 32 bit, but failed in 64 bit :

function TryRdRand(out Value: Cardinal): Boolean;
{$IF defined(CPU64BITS)}
asm .noframe
{$else}
asm
{$ifend}
  db   $0f
  db   $c7
  db   $f1
  jc   @success
  xor  eax,eax
  ret
@success:
  mov  [eax],ecx
  mov  eax,1
end;

doc of the function is here: https://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide

Especially it's written :

Essentially, developers invoke this instruction with a single operand: the destination register where the random value will be stored. Note that this register must be a general purpose register, and the size of the register (16, 32, or 64 bits) will determine the size of the random value returned.

After invoking the RDRAND instruction, the caller must examine the carry flag (CF) to determine whether a random value was available at the time the RDRAND instruction was executed. As Table 3 shows, a value of 1 indicates that a random value was available and placed in the destination register provided in the invocation. A value of 0 indicates that a random value was not available. In current architectures the destination register will also be zeroed as a side effect of this condition.

My knowledge of ASM is quite low, what did I miss ?

Also I do not quite understand this instruction :

  ...
  xor  eax,eax
  ret
  ...

What it's does exactly ?


Solution

  • If you want a function that performs exactly the same then I think that looks like this:

    function TryRdRand(out Value: Cardinal): Boolean;
    asm
    {$if defined(WIN64)}
      .noframe
      // rdrand eax
      db   $0f
      db   $c7
      db   $f0
      jnc  @fail
      mov  [rcx],eax
    {$elseif defined(WIN32)}
      // rdrand ecx
      db   $0f
      db   $c7
      db   $f1
      jnc  @fail
      mov  [eax],ecx
    {$else}
    {$Message Fatal 'TryRdRand not implemented for this platform'}
    {$endif}
      mov  eax,1
      ret
    @fail:
      xor  eax,eax
    end;
    

    The suggestion made by Peter Cordes of implementing a retry loop in the asm looks sensible to me. I will not attempt to implement that here, since I think it is somewhat outside the scope of your question.

    Also, Peter points out that in x64 you can read a 64 bit random value with the REX.W=1 prefix. That would look like this:

    function TryRdRand(out Value: NativeUInt): Boolean;
    asm
    {$if defined(WIN64)}
      .noframe
      // rdrand rax
      db   $48  // REX.W = 1
      db   $0f
      db   $c7
      db   $f0
      jnc  @fail
      mov  [rcx],rax
    {$elseif defined(WIN32)}
      // rdrand ecx
      db   $0f
      db   $c7
      db   $f1
      jnc  @fail
      mov  [eax],ecx
    {$else}
    {$Message Fatal 'TryRdRand not implemented for this platform'}
    {$endif}
      mov  eax,1
      ret
    @fail:
      xor  eax,eax
    end;