Qemu, div на ноль, регистр mxcsr
Я получил интересную ситуацию со следующим кодом:
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);
Когда я запускаю свое устройство qemu с поддержкой KVM
, я получил следующие результаты:
FE_DIVBYZERO !=0; //and it ok
Но когда я запускаю тот же источник без поддержки KVM
:
FE_DIVBYZERO ==0; //and it not ok
Как я понимаю эту ситуацию, это происходит, потому что в mxcsr
бит регистра (div на ноль) не установлен. Но я не понимаю, почему этот бит не установлен.
Есть идеи?
ОБНОВЛЕНИЕ:
Такая же ситуация для эмулятора android
основана на qemu.
emulator -avd test -qemu
return: FE_DIVBYZERO!= 0;
emulator -avd test -qemu -disable-kvm
return: FE_DIVBYZERO == 0;
Ответы
Ответ 1
Регистр MXCSR
описан в
Руководство разработчика программного обеспечения для разработчиков Intel® 64 и IA-32
В современных процессорах x86 компилятор отображает операции с плавающей запятой как scalar
SIMD, используя те же ресурсы, что и команды vector (SSE)
.
Регистр MXCSR
управляет работой команд scalar
и vector (SSE)
с плавающей запятой. Я включил соответствующий раздел, описывающий MXCSR ниже. MXCSR[9]
- это Divide-by-Zero Mask, если она очищена (0), тогда CPU будет вызывать исключение, когда обнаружен Divide by Zero. Когда вы работаете на виртуальной машине, исключения "виртуализированы", они обрабатываются "гипервизором", KVM в вашем случае. Затем гипервизор решает, следует ли отражать исключение обратно в гостевую виртуальную машину. Моя теория заключается в том, что маска Div-by-Zero очищается, исключение возбуждается, KVM и/или QEMU очищает флаг, который указывает, что деление на нулевое исключение произошло MXCSR[2]
и возобновляет вашу виртуальную машину. Вероятно, это ошибка в KVM/QEMU.
Вы можете выпустить fegetexcept() перед DivideByZero()
, чтобы узнать, есть ли Divide-By- Исключение Zero замаскировано (1) или нет (0). Если он не замаскирован, вы можете использовать fedisableexcept(), чтобы замаскировать его.
![MXCSR Control/Status Register]()
Ответ 2
Когда QEMU работает как эмуляция программного обеспечения.
2.8 Поддержка исключений
longjmp() используется, когда встречается исключение, такое как деление на ноль.
Обработчики сигналов хоста SIGSEGV и SIGBUS используются для получения недопустимых обращений к памяти. Имитационный программный счетчик найден путем повторной передачи соответствующего базового блока и поиска, где счетчик хост-программы находился в точке исключения.
Виртуальный ЦП не может получить точный регистр EFLAGS, потому что в некоторых случаях он не вычисляется из-за оптимизации кода условия. Это не является большой проблемой, потому что эмулированный код все же можно перезапустить в любом случае.
Похоже, что состояние FPU также потеряно, а биты, указывающие, что такое исключение FPU - деление на ноль/недопустимые аргументы в вашем случае, теряются. И QEMU не смог правильно подражать fetestexcept. Следовательно, результат.
Когда QEMU работает с использованием аппаратной виртуализации - KVM, исключения с плавающей запятой обрабатываются должным образом, поэтому тест правильный.
- изменить:
Другая возможность заключается в том, что режим исключений инициализируется по-разному:
Вы можете попытаться добавить это, чтобы включить исключение div на ноль.
feenableexcept (FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW);
Ответ 3
Я не уверен в логике, но QEMU - это программная реализация, а KVM - аппаратная поддержка. Так может быть и в программной реализации бит не установлен из-за неверного вычисления с плавающей запятой или сохранения значения с плавающей запятой.