【问题标题】:How to set REX prefix when using RDRAND under GCC?GCC下使用RDRAND时如何设置REX前缀?
【发布时间】:2016-01-09 14:40:47
【问题描述】:

我正在尝试使用英特尔的 RDRAND 指令。根据Intel® 64 and IA-32 Architectures Software Developer’s Manual, Volume 2(第 4-298 页),RDRAND 默认生成 32 位随机值,即使在 64 位机器上也是如此:

在 64 位模式下,指令的默认操作大小为 32 位。使用 REX.B 形式的 REX 前缀允许访问其他寄存器 (R8-R15)。

我正在尝试使用rdrandq 强制生成 64 位,但它会产生错误(/tmp/ccLxwW6S.s 是由于使用了内联汇编):

$ g++ -Wall rdrand.cxx -o rdrand.exe
/tmp/ccLxwW6S.s: Assembler messages:
/tmp/ccLxwW6S.s:5141: Error: invalid instruction suffix for `rdrand'

如何在 GCC 下强制使用 64 位版本的 RDRAND 指令? GCC下使用RDRAND时如何设置REX前缀?

提前致谢。


在下面的代码中,output 是一个长度为sizebyte[]safety 是一个故障保险。两种不同的字长处理X86, X32, and X64 platforms

#if BOOL_X86
    word32 val;
#else // X32 and X64
    word64 val;
#endif    

    while (size && safety)
    {
        char rc;    
        __asm__ volatile(
#if BOOL_X86
          "rdrandl %0 ; setc %1"
#else
          "rdrandq %0 ; setc %1"
#endif                  
          : "=rm" (val), "=qm" (rc)
          :
          : "cc"
        );

        if (rc)
        {
            size_t count = (size < sizeof(val) ? size : sizeof(val));
            memcpy(output, &val, count);
            size =- count;
        }
        else
        {
            safety--;
        }
    }

如果我从 RDRAND 中删除显式操作数大小(即,使用 rdrand 而不是 rdrandlrdrandq),则在尝试使用 word64 时会出错:

/tmp/ccbeXOvM.s: Assembler messages:
/tmp/ccbeXOvM.s:5167: Error: operand size mismatch for `rdrand'

【问题讨论】:

  • 您是否有任何理由反对使用 _rdrand64_step 内在函数?
  • @Michael - 我更喜欢内联汇编。 RDRAND 很容易测试。否则,我必须添加额外的测试来包含头文件,并担心不支持 RDRAND 内在函数的 GCC 版本,即使底层处理器和汇编器支持它。
  • 显然操作数大小对于 rdrand 是隐含的,即使在 AT&T 语法中也是如此。 rdrand %e[rr] 在使用低位寄存器时没有 REX 前缀,rdrand %r[rr] 有。
  • 你总是可以写出适当的字节序列:48 0f c7 f1..
  • 正如elsewhere 所指出的,RDRAND 没有被记录为接受内存输出。此外,gcc 有一个新的feature,它可以为您节省setc。如果您的编译器版本足够新 (#ifdef __GCC_ASM_FLAG_OUTPUTS__),您可以执行类似 do { asm("rdrand %1" : "=@ccc"(x), "=r"(l)); } while (!x); 的操作,它会为您提供 .L2: rdrand %eax jnc .L2

标签: c gcc x86 inline-assembly rdrand


【解决方案1】:

您不需要操作数大小的后缀,只需将其与适当大小的寄存器一起使用(gcc 将根据您使用的 C 变量的类型来选择)。


gcc main.c -o main

(或)

gcc -m32 main.c -o main

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv) {
  unsigned int rnd32;
#ifdef __x86_64
  long long unsigned int rnd64;
  /*
  The next instruction generates this asm:
  48 0f c7 f0             rdrand %rax
  */
  asm volatile("rdrand %0\n":"=r"(rnd64):);
   printf("\nRND64=0x%llx\n",rnd64);
#endif
  /*
  The next instruction generates this asm:
  0f c7 f1                rdrand %ecx
  */
  asm volatile("rdrand %0\n":"=r"(rnd32):);
  printf("RND32=0x%x\n",rnd32);
  printf("\nAssembler code:\n\n");
  system("objdump -d main|grep rdrand");
  return 0;
}

https://repl.it/@zibri/rdrand

输出(64 位):

RND64=0x2f9f0e7d7f209575
RND32=0xbec8ff00

Assembler code:

   40054f:   48 0f c7 f0             rdrand %rax   
   400557:   0f c7 f1                rdrand %ecx

输出(32 位):

RND32=0xa3d33766

Assembler code:

  59a:  0f c7 f0                rdrand %eax

【讨论】:

  • 您的回滚消除了 "=rm" (val) 是错误的事实,因为 RDRAND 仅适用于寄存器输出。这是操作数能够暗示操作数大小的关键。我认为我的其余编辑也有所改进(否则我不会进行),因为对于大多数未来的读者来说,最有用的建议是简单地使用_rdrand64_step。而且至少在理论上,您应该检查rdrand 的成功/失败状态。如果您确实选择放回任何文本,请随意改写或移动我的任何文本,这不仅仅是回滚与否的选择。
  • 错误?检查代码,编译它。它按原样完美运行。
  • 您的代码没问题,question 中的代码包含一个约束,如果它选择它会导致汇编程序错误。指出这一点很重要:如果内存目标是可能的,它不能暗示 rdrand 的操作数大小,因此您的答案将无效。
  • 我知道 rdrand 有一个寄存器(32 位或 64 位作为操作数),我们在这里讨论的不是汇编程序而是 C。编译器负责将 rdrand 放入寄存器然后返回该寄存器。我认为答案是正确的。
  • 我什至包含了生成的汇编代码来澄清这一点。
猜你喜欢
  • 2020-03-28
  • 1970-01-01
  • 1970-01-01
  • 2011-08-25
  • 1970-01-01
  • 2014-08-28
  • 2015-01-10
  • 2020-11-25
  • 1970-01-01
相关资源
最近更新 更多