I've been writing x86 Assembly for years and I've never run across this. I'm hoping someone can point me in the right direction so I can have a "Doh!" moment.
In pseudocode, when I write 42 DIV 4
, the division works as expected. When I write RDRAND DIV 4
, the CPU throws an exception. I'm doing unsigned, 16-bit arithmetic, but I've reproduced this in 32 and 64-bit as well.
I've reproduced this on Intel Xeon & Core CPUs as well as an AMD EPYC... so I'm thinking this is deliberate and not an isolated thing.
Here's the source code. I'm compiling it like this: clang -masm=intel -o div_bug div_bug.c
#include <stdio.h>
#include <stdint.h>
int main() {
printf( "div_bug\n" );
uint8_t rval;
// rdrand sets CF on success... I've tried this with and without this
// check and the program still throws an exception
// This works as expected
asm volatile (
// "try_again_1:"
"rdrand ax;" // If CF==0, then the rdrand failed... try again
// "jnc try_again_1;"
"mov ax, 42;" // This works... as expected
"mov cl, 4;"
"div cl;"
"mov %0, ah;"
:"=r" ( rval ) // Output
: // Input
:"ax", "cl", "cc" ); // Clobbers
printf( "The first return value is %u\n", rval );
// This throws a `Floating point exception (core dumped)`
asm volatile (
// "try_again_2:"
"rdrand ax;" // If CF==0, then the rdrand failed... try again
// "jnc try_again_2;"
// "mov ax, 42;" // <---- Remove this and it breaks
"mov cl, 4;"
"div cl;"
"mov %0, ah;"
:"=r" ( rval ) // Output
: // Input
:"ax", "cl", "cc" ); // Clobbers
printf( "The second return value is %u\n", rval );
}
I've also got a repo of the code here: https://github.com/marknelsonengineer/div_bug.git
I've researched the DIV
and RDRAND
instructions in Intel's SDM and I don't see anything obvious.
I'd expect RDRAND
to return a number... that I can divide by 4.
My question is: Why does DIV
throw an exception when the numerator comes from RDRAND
?
Thank you, Mark
Most of the time your random number will be >= 1024, which means that the result of dividing by 4 will not fit in 8 bits, and you'll get a #DE execption. From the Intel manual describing DIV:
IF OperandSize = 8 (* Word/Byte Operation *)
THEN
temp ← AX / SRC;
IF temp > FFH
THEN #DE; (* Divide error *)
ELSE
AL ← temp;
AH ← AX MOD SRC;
FI;
FI;