【问题标题】:Intel DRNG giving only giving 4 bytes of data instead of 8英特尔 DRNG 只给出 4 个字节的数据而不是 8 个
【发布时间】:2017-09-25 15:16:52
【问题描述】:

我正在尝试在 C++ 中实现 Intel DRNG

根据其生成 64 位 unsigned long long 的指南,代码应为:

int rdrand64_step (unsigned long long *rand)
{
    unsigned char ok;
    asm volatile ("rdrand %0; setc %1"
                  : "=r" (*rand), "=qm" (ok));
    return (int) ok;
}

然而,这个函数rand 的输出只给了我一个只有 32 位的输出,如图所示。

        bd4a749d
        d461c2a8
        8f666eee
        d1d5bcc4
        c6f4a412

为什么会发生这种情况?

更多信息:我使用的 IDE 是代码块

【问题讨论】:

  • 在哪个操作系统、哪个版本的 C++ 和哪个编译器上? C++11 有<random>,最好使用它(因为它是可移植的)。 Code::Blocks不是编译器(它运行一个编译器,也许是GCC)而是一个IDE
  • 我使用的编译器是 codeblocks Code::Blocks 是一个 IDE。我假设编译器是 mingw 的某个版本。
  • Linux 有/dev/random 参见random(4),而mingw 是-或包含-GCC 的端口。在终端中运行 g++ -vg++ --version。一个足够新的 mingw 应该提供一个 C++11 编译器
  • 我相信这不适用于 32 位代码。虽然我可能读错了文档。
  • 但是为什么你需要实施一些DRNG?为什么你不能使用 <random> ??请编辑您的问题进行解释!

标签: c++ random intel


【解决方案1】:

使用immintrin.h 中的int _rdrand64_step (unsigned __int64* val) 而不是编写内联汇编。你不需要它,避免它的原因有很多(包括这个):https://gcc.gnu.org/wiki/DontUseInlineAsm


在这种情况下,问题在于您可能正在编译 32 位代码,所以当然 64 位 rdrand 是不可编码的。但是您使用 inline-asm 的方式最终为您提供了 32 位 rdrand,并为高半部分存储来自另一个寄存器的垃圾。

gcc -Wall -O3 -m32 -march=ivybridge(clang 类似)产生 (on Godbolt):

In function 'rdrand64_step':
7 : <source>:7:1: warning: unsupported size for integer register

rdrand64_step:
    push    ebx
    rdrand ecx; setc al
    mov     edx, DWORD PTR [esp+8]    # load the pointer arg
    movzx   eax, al
    mov     DWORD PTR [edx], ecx
    mov     DWORD PTR [edx+4], ebx    # store garbage in the high half of *rand
    pop     ebx
    ret

我猜你用一个碰巧有ebx=0的调用者调用了这个函数。或者你使用了不同的编译器,做了不同的事情。内联后可能会发生其他事情。如果你查看你实际编译的反汇编,你可以准确地解释发生了什么。


如果你使用了内在函数,你会得到 error: '_rdrand64_step' was not declared in this scope,因为 immintrin.h 只在 64 位模式下声明它(并且带有暗示 rdrand 支持的 -march 设置. 或[-mrdrnd]3。最佳选择:使用-march=native,如果您在目标机器上构建)。

对于重试循环,您还将获得更高效的代码,至少使用 clang:

unsigned long long use_intrinsic(void) {
    unsigned long long rand;
    while(!_rdrand64_step(&rand));  // TODO: retry limit in case RNG is broken.
    return rand;
}

use_intrinsic:                          # @use_intrinsic
.LBB2_1:                                # =>This Inner Loop Header: Depth=1
    rdrand  rax
    jae     .LBB2_1
    ret

这避免了 setcc 然后对其进行测试,这当然是多余的。 gcc6 具有从内联 asm 返回标志结果的语法。您也可以使用asm goto 并在asm 中放置一个jcc,跳转到label: return 1; 目标或通过return 0。 (inline-asm 文档有一个这样做的例子。https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html。另见the inline-assembly tag wiki。)

使用您的 inline-asm,clang(在 64 位模式下)将其编译为:

use_asm:
.LBB1_1:
    rdrand  rax
    setb    byte ptr [rsp - 1]
    cmp     byte ptr [rsp - 1], 0
    je      .LBB1_1
    ret

(clang 对包含内存在内的多个选项的约束做出了错误的决定。)

gcc7.2 和 ICC17 实际上最终从 asm 得到的代码比从内在得到的更好。他们使用 cmovc 得到 0 或 1,然后使用 test 得到。这很愚蠢。但这是一个 gcc/ICC 错过的优化,希望如此。

【讨论】:

  • 是的,看来我正在使用 32 位编译器,我尝试使用 minGW64 更改它,但是当我使用 cout &lt;&lt; “sizeof(void*) = ” &lt;&lt; sizeof(void*) &lt;&lt; ";" &lt;&lt; endl; 测试运行它时,我得到 4 而不是预期的 8 并使用我的代码我仍然只能得到 32 位返回。是因为我的 IDE 是 32 位的?
  • @albusSimba:如果默认为-m32,您的IDE 必须通过-m64。 IDK 关于您的 IDE 的任何内容,但没有理由 32 位进程在运行 64 位可执行文件时会遇到问题。它不会将编译器作为库加载(是吗?)
  • 是的,我已经通过了-m64,但它仍然没有通过cout &lt;&lt; “sizeof(void*) = ” &lt;&lt; sizeof(void*) &lt;&lt; ";" &lt;&lt; endl; 的简单测试,这意味着它仍然在 32 位上运行
  • @albusSimba:同时传递-v(详细)以查看它是否在用户提供的选项后附加了-m32。您还可以检查 __x86_64__ 是否由预处理器定义,以防它在 64 位模式下做一些奇怪的事情,例如 32 位指针(如 Linux x32 ABI)。或者只是谷歌更多关于使用你的IDE。
  • inlining failed in call to always_inline 'int _rdrand64_step(long long unsigned int*) 我切换到 clion 并且我的代码似乎有效,但我想知道为什么使用 immintrin.h 会抛出这个错误,这是我第一次看到这个错误。
猜你喜欢
  • 1970-01-01
  • 2023-03-11
  • 2021-05-25
  • 1970-01-01
  • 1970-01-01
  • 2023-01-13
  • 2023-03-28
  • 1970-01-01
  • 2023-03-11
相关资源
最近更新 更多