I got an interesting situation with the following code:
static void DivideByZero() {
// volatile to prevent compiler optimizations.
volatile float zero = 0.0f;
volatile float result __attribute__((unused)) = 123.0f / zero;
}
DivideByZero();
int raised = fetestexcept(FE_DIVBYZERO | FE_OVERFLOW);
ASSERT_TRUE((raised & FE_DIVBYZERO) != 0);
When I run my qemu device with KVM
support I got the following results:
FE_DIVBYZERO !=0; //and it's ok
But when I run the same source without KVM
support:
FE_DIVBYZERO ==0; //and it's not ok
As I understand this situation, it happens, because in mxcsr
the register bit (div by zero) is not set. But I don't understand why this bit is not set.
Any ideas?
UPDATE :
Same situation has happend for android
emulator based on qemu.
emulator -avd test -qemu
return: FE_DIVBYZERO !=0;
emulator -avd test -qemu -disable-kvm
return: FE_DIVBYZERO ==0;
The MXCSR
register is described in the
Intel® 64 and IA-32 Architectures Software Developer’s Manual
On modern x86 processors the compiler maps floating point operations as scalar
SIMD, using the same resources that vector (SSE)
instructions use.
The MXCSR
register controls the operation of both scalar
and vector (SSE)
floating point instructions. I've included the relevant section describing the MXCSR below. MXCSR[9]
is the Divide-by-Zero Mask, if cleared (0) then the CPU will raise an exception when Divide by Zero is detected. When you are running in a virtual machine exceptions are "virtualized", they are handled by a "hypervisor", KVM in your case. The hypervisor then decides whether to reflect the exception back to the guest virtual machine. My theory is that the Divide-by-Zero Mask is cleared, the exception is raised, KVM and/or QEMU is clearing the flag that indicates a divide by zero exception happened MXCSR[2]
and resumes your virtual machine. This is therefore likely a bug in KVM/QEMU.
You could issue fegetexcept() before the DivideByZero()
to find out if the Divide-By-Zero exception is masked (1) or not (0). If it is not masked then you could use fedisableexcept() to mask it.
When QEMU works as a software emulation.
2.8 Exception support
longjmp() is used when an exception such as division by zero is encountered.
The host SIGSEGV and SIGBUS signal handlers are used to get invalid memory accesses. The simulated program counter is found by retranslating the corresponding basic block and by looking where the host program counter was at the exception point.
Looks like the FPU state is lost too and the bits indicating what is the FPU exception - division by zero/invalid arguments in your case are lost. And QEMU could not properly emulate fetestexcept. Hence the result.
When QEMU works using hardware virtualization - KVM, the floating point exceptions are handled properly - so the test is correct.
-- edit: Other posibility is that the exceptions mode is initialized differently: You may try to add that to enable div by zero exception. feenableexcept (FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With