1

I'm trying to use Intel's RDRAND instruction. According to the Intel® 64 and IA-32 Architectures Software Developer’s Manual, Volume 2 (page 4-298), RDRAND produces 32-bit random values by default, even on 64-bit machines:

In 64-bit mode, the instruction's default operation size is 32 bits. Using a REX prefix in the form of REX.B permits access to additional registers (R8-R15).

I'm trying to force the 64-bit generation using rdrandq, but its producing an error (/tmp/ccLxwW6S.s is due to the use of inline assembly):

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

How do I force the 64-bit version of the RDRAND instruction under GCC? How do I set the REX prefix when using RDRAND under GCC?

Thanks in advance.


In the code below, output is a byte[] with a length of size. safety is a failsafe. The two different word sizes handle the 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--;
        }
    }

If I remove the explicit operand size from RDRAND (i.e., use rdrand rather than rdrandl or rdrandq), then I get an error when attempting to use the word64:

/tmp/ccbeXOvM.s: Assembler messages:
/tmp/ccbeXOvM.s:5167: Error: operand size mismatch for `rdrand'
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
jww
  • 97,681
  • 90
  • 411
  • 885
  • 1
    Are you for any reason against using the `_rdrand64_step` intrinsic? – Michael Foukarakis Oct 12 '15 at 08:27
  • @Michael - I'd prefer inline assembly. RDRAND is easy enough to test for. Otherwise, I have to add additional tests to include headers, and worry about versions of GCC that don't support RDRAND intrinsics even though the underlying processor and the assembler supports it. – jww Oct 12 '15 at 09:26
  • Apparently the operand size is implicit for `rdrand`, even in AT&T-syntax. `rdrand %e[rr]` doesn't have a REX-prefix when using a low register, `rdrand %r[rr]` does. – EOF Oct 12 '15 at 11:52
  • @Michael - I tried to switch to `__builtin_ia32_rdrand`, but trying to use it is more frustrating than trying to use just `RDRAND`. Man this is frustrating... – jww Oct 12 '15 at 18:23
  • 1
    You could always just write out the appropriate series of bytes: `48 0f c7 f1`.. – Michael Foukarakis Oct 12 '15 at 18:29
  • 1
    As has been pointed out [elsewhere](https://gcc.gnu.org/ml/gcc-help/2015-10/msg00076.html), RDRAND is not doc'ed to accept a memory output. Also, there is a new [feature](https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#FlagOutputOperands) for gcc that may be able to save you the `setc`. If your compiler version is recent enough (`#ifdef __GCC_ASM_FLAG_OUTPUTS__`), you can do something like `do { asm("rdrand %1" : "=@ccc"(x), "=r"(l)); } while (!x);` which gives you `.L2: rdrand %eax jnc .L2`. – David Wohlferd Oct 19 '15 at 04:35
  • @jww your assumption is wrong. There is no default behaviour for rdrand. If you use a 64 bit register (like rax) the result will be a 64 bit random number. If you use a 32bit sized register (like eax) the answer will be a 32 bit random number. Please check my answer and mark it as correct answer. – Zibri Mar 23 '19 at 07:20

1 Answers1

0

You don't need an operand-size suffix, just use it with a register of the appropriate size (which gcc will select based on the type of the C variable you use).


gcc main.c -o main

(or)

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

Output (64 bit):

RND64=0x2f9f0e7d7f209575
RND32=0xbec8ff00

Assembler code:

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

Output (32 bit):

RND32=0xa3d33766

Assembler code:

  59a:  0f c7 f0                rdrand %eax
Zibri
  • 9,096
  • 3
  • 52
  • 44
  • Your rollback removed the fact that `"=rm" (val)` is an error because RDRAND only works with register outputs. That's key to the operand being able to imply the operand-size. I thought the rest of my edits were also improvement (otherwise I wouldn't have made them), because for most future readers the most useful advice is to simply use `_rdrand64_step`. And that at least in theory, you're supposed to check the success/failure status of `rdrand`. Feel free to reword or move around any of my text if you do choose to put any of that back in, it's not just a choice of rollback or not. – Peter Cordes Mar 22 '19 at 19:11
  • error? check the code, compile it. it works perfectly as it is. – Zibri Mar 23 '19 at 06:48
  • Your code is fine, the code in the *question* includes a constraint that will cause an assembler error if it ever picks it. Pointing that out is important: if a memory destination were possible, it couldn't imply an operand-size for `rdrand` so your answer wouldn't be valid. – Peter Cordes Mar 23 '19 at 07:13
  • I know that rdrand has a register (32 or 64 bit as operand) we are not discussing assembler here but C. And the compiler takes care of putting rdrand in a register and then returning the register. I think the answer is correct as it is. – Zibri Mar 23 '19 at 07:17
  • I even included the assembler code generated to clarify that. – Zibri Mar 23 '19 at 07:17
  • Yes exactly, we're discussing the C code *in the question*, which uses `"=rm"(val)`, which tells the compiler it has the option of filling in the asm template with `(%rdi, %rcx, 8)` or something, instead of `%rax`, if it wants to. That would fail to assemble. An important part of answering that question is thus to explain that you don't need to handle the memory-operand case because it's impossible. (i.e. that you're not losing out on anything by limiting it with `"=r"`, that's in fact necessary instead of an obstacle to letting the compiler make optimal code.) – Peter Cordes Mar 23 '19 at 07:39
  • I see really no difference in this case using =r or =rm who cares if the compiler chooses rdi,rcx? With "=r" anyway gcc never fails to compile. Also, as I read: <<"=rm” means a register or memory>> but rdrand supports only registers. So "=r" is more correct than "=rm". – Zibri Mar 23 '19 at 22:26
  • The person asking this question thinks that `"=rm"` is the best choice for an output constraint. Your answer doesn't explain why switching to `"=r"` is necessary (because you removed the text I added). They are very much *not* equivalent, as https://godbolt.org/z/YxiVNo shows: you can get gcc or clang to generate invalid asm like `rdrand QWORD PTR [rdi]` if you use `"=rm"` and call it from a function that assigns the result to `*pointer`. Working for one case of surrounding code and compiler version/options does *not* prove that an inline asm statement is correct and safe in general. – Peter Cordes Mar 23 '19 at 22:41
  • If the question didn't use `"=rm"`, there'd be no reason to mention it. But it is important for SO answers to explain what they're fixing in code from the question, so it's clear that it was *necessary* and not just something you decided to do for simplicity. The existence of `"=rm"` in the question makes that *part* of the question that needs to be answered. – Peter Cordes Mar 23 '19 at 23:01