I want to generate a random 32-bit number. I am using rdrand
for this. However, I am having some problems. Since the number can be no more than 32 bits large, I am doing rdrand eax
. Here is where the problem arises:
I need to be able to refer to this 32-bit number in a 64-bit register since the rest of my codebase uses 64-bit registers. I figured that I could clear rax
with a xor
to itself, and then only load half of it with rdrand eax
. Then I could look at rax
, have one half be at most a 32-bit number, and the other half be cleared.
When I compare rax
with the maximum 32-bit number size, 2147483647, I get very inconsistent results. Half of the time, the exit code is 1, meaning that the number in rax
is, in fact, smaller than 32-bits. But the other half of the time I get 0. It's almost like the result in eax
is not always less than or equal to 2147483647, which is unexpected given what this documentation says.
Does anyone know what is going wrong in my thought process? I am very curious to know. (Note: I'm assembling with Clang on macOS.)
.global _main
.text
# how to generate a 32-bit random number that is in rax?
_main:
xor rax, rax # clear the top half of the eax-rax register pair
rdrand eax # a 32-bit number in rax
cmp rax, 2147483647
jle smaller_than_32 # rax <= MAX_32_BIT
xor rdi, rdi
jmp end
smaller_than_32:
mov rdi, 1
end:
mov rax, 0x2000001
syscall
2147483647
, or equivalently, 0x7FFFFFFF
, is the maximum signed 32-bit number. rdrand eax
can put any value from 0x00000000
to 0xFFFFFFFF
in eax
. You have a few choices for how to handle this:
4294967295
, or equivalently 0xFFFFFFFF
, the maximum unsigned 32-bit number, instead.cmp eax, 2147483647
instead of cmp rax, 2147483647
. Since jle
operates on the result of comparing signed integers, this will cause what you're now seeing as 2147483648
through 4294967295
to instead be interpreted as -2147483648
through -1
.rax
always be zero, have them match the sign bit of eax
instead (this is known as sign-extending). You can do this by doing movsx rax, eax
right after rdrand eax
. This will cause rax
to hold a value between -2147483648
and 2147483647
instead of between 0
and 4294967295
.Any of those changes will result in your conditional jump always being taken, as you expect. If you want rax
to end up being between 0
and 4294967295
, then choose option 1 or 2. If you want rax
to end up being between -2147483648
and 2147483647
, then choose option 3.