3

我有这个功能(RDRand - 由David Heffernan编写)在 32 位中可以正常工作,但在 64 位中失败:

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;

该函数的文档在这里:https ://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide

尤其是这样写:

本质上,开发人员使用单个操作数调用此指令:将存储随机值的目标寄存器。请注意,此寄存器必须是通用寄存器,寄存器的大小(16、32 或 64 位)将决定返回的随机值的大小。

调用 RDRAND 指令后,调用者必须检查进位标志 (CF) 以确定在执行 RDRAND 指令时是否有可用的随机值。如表 3 所示,值 1 表示随机值可用并放置在调用中提供的目标寄存器中。值 0 表示随机值不可用。在当前架构中,目标寄存器也将作为这种情况的副作用归零。

我对 ASM 的了解很少,我错过了什么?

我也不太明白这个指令:

  ...
  xor  eax,eax
  ret
  ...

它到底是做什么的?

4

1 回答 1

6

如果您想要一个执行完全相同的功能,那么我认为它看起来像这样:

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;

Peter Cordes提出的在 asm 中实现重试循环的建议对我来说似乎是明智的。我不会尝试在这里实施,因为我认为这超出了您的问题范围。

此外,Peter 指出,在 x64 中,您可以读取带有 REX.W=1 前缀的 64 位随机值。看起来像这样:

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;
于 2021-03-19T08:43:36.783 回答