Я работаю над эмулятором Xbox1 в Delphi, и, поскольку я запускаю игры на локальном процессоре, мне нужно создать отказоустойчивую систему для инструкций ring0, которые могут возникать внутри игрового кода.
Чтобы перехватить эти инструкции, я узнал, что SetUnhandledExceptionFilter может зарегистрировать функцию, которая будет вызываться для исключений, отличных от Delphi (при условии, что я установил JITEnable значение выше 0). Сигнатура зарегистрированной функции обратного вызова гласит:
function ExceptionFilter(E: LPEXCEPTION_POINTERS): Integer; stdcall;
Внутри этой функции, Я могу проверить наличие незаконных инструкций следующим образом:
// STATUS_PRIVILEGED_INSTRUCTION = $C0000096
if E.ExceptionRecord.ExceptionCode = STATUS_PRIVILEGED_INSTRUCTION then
Одна из инструкций, вызывающих нарушение, - это WVINDB ($ 0F, $ 09), которую я могу обнаружить следующим образом:
// See if the instruction pointer is a WBINVD opcode :
if (PAnsiChar(E.ExceptionRecord.ExceptionAddress)[0] = #$0F)
and (PAnsiChar(E.ExceptionRecord.ExceptionAddress)[1] = #$09) then
Все это работает (при условии, что я запустил это вне отладчик), но я не могу заставить код выполняться за пределами ошибочной инструкции - я пробовал это так:
begin
// Skip the WBINVD instruction, and continue execution :
Inc(DWORD(E.ExceptionRecord.ExceptionAddress), 2);
Result := EXCEPTION_CONTINUE_EXECUTION;
Exit;
end;
Увы, это не сработает. На самом деле, я бы использовал реальный указатель инструкций (E.ContextRecord.Eip), но почему-то весь ContextRecord не кажется заполненным.
Что я могу сделать, чтобы этот работал так, как задумано?
PS: При работе с отладчиком я ожидал, что этот код попадет в мою подпрограмму ExceptionFilter, но это не так - он работает только без отладчика; Почему?
DebugHook := 0; // Act as if there's no debugger
// Trigger a privileged instruction exception via this ring0 instruction :
asm
WBINVD
end;
// Prove that my exception-filter worked :
ShowMessage('WBINVD succesfully ignored!');