【问题标题】:converting ASM instruction RDRand to Win64将 ASM 指令 RDRand 转换为 Win64
【发布时间】:2021-03-18 21:23:49
【问题描述】:

我有这个功能(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
  ...

它到底是做什么的?

【问题讨论】:

  • xor eax,eax在EAX寄存器中存储0,对应False返回值。
  • @Olivier 谢谢,所以现在我理解了这部分:) 我猜 mov eax,1 在结果中存储 True :)
  • 该手册 db 的东西显然对 rdrand ecx 进行了编码(在 32 位和 64 位模式下)。如果你想要rdrand rcx,你可以在它前面添加一个REX.W=1 前缀(db $48)。您还必须找出 Delphi 使用什么寄存器来传递指针输入,例如mov [rax], ecxrcx.
  • 是的,创建布尔返回值的更简单方法是setc al。 (如果 Delphi 想要将零扩展为完整的 reg,那么您需要在 rdrand 设置标志之前对 EAX 进行异或零处理,因此如果指针输入位于同一个 retval 寄存器中,则不太方便)。如果您不进行分支,则始终存储整数结果,即使 RDRAND 说它无效。或者,您可以通过循环 RDRAND 成功、循环 retry: rdrand rax / jnc retry / ret 在此函数中隐藏 RDRAND 重试
  • @peter 编译器在最后添加了一个 ret。调用约定是标准的 Windows x64。 db 是因为 Delphi 编译器不知道 rdrand。

标签: delphi assembly x86-64 rdrand


【解决方案1】:

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

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;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-12-29
    • 1970-01-01
    • 1970-01-01
    • 2021-09-17
    • 2011-03-23
    • 2011-11-29
    • 2014-09-11
    相关资源
    最近更新 更多