Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Qemu, div by zero, mxcsr register

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;

like image 681
Laser Avatar asked Feb 28 '13 11:02

Laser


2 Answers

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.

MXCSR Control/Status Register

like image 161
amdn Avatar answered Sep 19 '22 23:09

amdn


When QEMU works as a software emulation.

From http://qemu.weilnetz.de/qemu-tech.html#intro_005fx86_005femulation.

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.

The virtual CPU cannot retrieve the exact EFLAGS register because in some cases it is not computed because of condition code optimisations. It is not a big concern because the emulated code can still be restarted in any cases.

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);

like image 25
Alexander Atanasov Avatar answered Sep 18 '22 23:09

Alexander Atanasov