Directly check PEB for IsDebuggerAttached

Add constexpr getters to magicdiv class so it can be used from jitted x64/dxbc
Track the guest return address as well for guest/host sync, if multiple entries have the same guest stack find the first one with a matching guest retaddr. this fixes epic mickey 2 (which the previous guest-stack change had allowed to go ingame for a bit) and potentially also a crash in fable3.
Break if under debugger when stackpoints are overflowed

Add much more useful output for host exceptions, print out xenia_canary.exe relative offsets if exception is in module, formatmessage for ntstatus/win32err, strerror

Minor d3d12 microoptimization, instead of doing SetEventOnCompletion + WaitForSingleObject do SetEventOnCompletion w/ nullptr so that the wait happens in kernel mode, avoiding two extra context switches

add unimplemented kernel functions:
ExAllocatePoolWithTag
ObReferenceObject

ObDereferenceObject has no return value.
Log a message when ObDereferenceObject/Reference receive unregistered guest kernel objects
gave ObLookupThreadByThreadId its correct error status
hoist object_types initialization out of ObReferenceObjectByHandle
Fix out parameter values on error for a few kernel funcs
add note about msr to KeSetCurrentStackPointers
add X_STATUS_OBJECT_TYPE_MISMATCH check for xeNtSetEvent
add msr_mask field to X_KPCR
This commit is contained in:
chss95cs@gmail.com
2022-12-04 12:38:19 -08:00
parent 1eb61aa9ab
commit a63f424c0a
18 changed files with 412 additions and 105 deletions

View File

@@ -725,10 +725,11 @@ ResolveFunctionThunk X64HelperEmitter::EmitResolveFunctionThunk() {
return (ResolveFunctionThunk)fn;
}
// r11 = size of callers stack, r8 = return address w/ adjustment
//i'm not proud of this code, but it shouldn't be executed frequently at all
// i'm not proud of this code, but it shouldn't be executed frequently at all
void* X64HelperEmitter::EmitGuestAndHostSynchronizeStackHelper() {
_code_offsets code_offsets = {};
code_offsets.prolog = getSize();
push(rbx);
mov(rbx, GetBackendCtxPtr(offsetof(X64BackendContext, stackpoints)));
mov(eax,
GetBackendCtxPtr(offsetof(X64BackendContext, current_stackpoint_depth)));
@@ -741,8 +742,9 @@ void* X64HelperEmitter::EmitGuestAndHostSynchronizeStackHelper() {
Xbyak::Label signed_underflow{};
xor_(r12d, r12d);
//todo: should use Loop instruction here if hasFastLoop,
//currently xbyak does not support it but its super easy to modify xbyak to have it
// todo: should use Loop instruction here if hasFastLoop,
// currently xbyak does not support it but its super easy to modify xbyak to
// have it
L(looper);
imul(edx, ecx, sizeof(X64BackendStackpoint));
mov(r10d, ptr[rbx + rdx + offsetof(X64BackendStackpoint, guest_stack_)]);
@@ -760,12 +762,47 @@ void* X64HelperEmitter::EmitGuestAndHostSynchronizeStackHelper() {
}
js(signed_underflow, T_NEAR); // should be impossible!!
jmp(looper, T_NEAR);
L(loopout);
Xbyak::Label skip_adjust{};
cmp(r12d, 1);//should never happen?
cmp(r12d, 1); // should never happen?
jle(skip_adjust, T_NEAR);
Xbyak::Label we_good{};
// now we need to make sure that the return address matches
// mov(r9d, ptr[GetContextReg() + offsetof(ppc::PPCContext, lr)]);
pop(r9); // guest retaddr
// r10d = the guest_stack
// while guest_stack is equal and return address is not equal, decrement
Xbyak::Label search_for_retaddr{};
Xbyak::Label we_good_but_increment{};
L(search_for_retaddr);
imul(edx, ecx, sizeof(X64BackendStackpoint));
cmp(r10d, ptr[rbx + rdx + offsetof(X64BackendStackpoint, guest_stack_)]);
jnz(we_good_but_increment, T_NEAR);
cmp(r9d,
ptr[rbx + rdx + offsetof(X64BackendStackpoint, guest_return_address_)]);
jz(we_good, T_NEAR); // stack is equal, return address is equal, we've got
// our destination stack
dec(ecx);
jmp(search_for_retaddr, T_NEAR);
Xbyak::Label checkbp{};
L(we_good_but_increment);
add(edx, sizeof(X64BackendStackpoint));
inc(ecx);
jmp(checkbp, T_NEAR);
L(we_good);
//we're popping this return address, so go down by one
sub(edx, sizeof(X64BackendStackpoint));
dec(ecx);
L(checkbp);
mov(rsp, ptr[rbx + rdx + offsetof(X64BackendStackpoint, host_stack_)]);
if (IsFeatureEnabled(kX64FlagsIndependentVars)) {
inc(ecx);
@@ -773,13 +810,13 @@ void* X64HelperEmitter::EmitGuestAndHostSynchronizeStackHelper() {
add(ecx, 1);
}
// this->DebugBreak();
sub(rsp, r11); // adjust stack
mov(GetBackendCtxPtr(offsetof(X64BackendContext, current_stackpoint_depth)),
ecx); // set next stackpoint index to be after the one we restored to
jmp(r8);
L(skip_adjust);
pop(rbx);
jmp(r8); // return to caller
code_offsets.prolog_stack_alloc = getSize();
code_offsets.body = getSize();
@@ -787,24 +824,11 @@ void* X64HelperEmitter::EmitGuestAndHostSynchronizeStackHelper() {
code_offsets.tail = getSize();
L(signed_underflow);
//find a good, compact way to signal error here
// maybe an invalid opcode that we execute, then detect in an exception handler?
// find a good, compact way to signal error here
// maybe an invalid opcode that we execute, then detect in an exception
// handler?
this->DebugBreak();
// stack unwinding, take first entry
//actually, no reason to have this
/*mov(rsp, ptr[rbx + offsetof(X64BackendStackpoint, host_stack_)]);
mov(ptr[rbx + offsetof(X64BackendStackpoint, guest_stack_)], r9d);
sub(rsp, r11);
xor_(eax, eax);
inc(eax);
mov(GetBackendCtxPtr(offsetof(X64BackendContext, current_stackpoint_depth)),
eax);
jmp(r8);*/
// this->DebugBreak(); // err, add an xe::FatalError to call for this
return EmitCurrentForOffsets(code_offsets);
}

View File

@@ -48,7 +48,7 @@ struct X64BackendStackpoint {
// pad to 16 bytes so we never end up having a 64 bit load/store for
// host_stack_ straddling two lines. Consider this field reserved for future
// use
unsigned unused_;
unsigned guest_return_address_;
};
// located prior to the ctx register
// some things it would be nice to have be per-emulator instance instead of per

View File

@@ -486,6 +486,7 @@ uint64_t ResolveFunction(void* raw_context, uint64_t target_address) {
if (cvars::enable_host_guest_stack_synchronization) {
auto processor = thread_state->processor();
auto module_for_address =
processor->LookupModule(static_cast<uint32_t>(target_address));
@@ -498,6 +499,7 @@ uint64_t ResolveFunction(void* raw_context, uint64_t target_address) {
if (flags->is_return_site) {
auto ones_with_address = processor->FindFunctionsWithAddress(
static_cast<uint32_t>(target_address));
if (ones_with_address.size() != 0) {
// this loop to find a host address for the guest address is
// necessary because FindFunctionsWithAddress works via a range
@@ -618,21 +620,42 @@ uint64_t ResolveFunction(void* raw_context, uint64_t target_address) {
and 5 bytes for the jmp with no cycles taken for the jump
which will be predicted not taken.
Our handling for the check is implemented in X64HelperEmitter::EmitGuestAndHostSynchronizeStackHelper. we don't call it directly though,
instead we go through backend()->synchronize_guest_and_host_stack_helper_for_size(num_bytes_needed_to_represent_stack_size). we place the stack size after the
call instruction so we can load it in the helper and readjust the return address to point after the literal value.
Our handling for the check is implemented in
X64HelperEmitter::EmitGuestAndHostSynchronizeStackHelper. we
don't call it directly though, instead we go through
backend()->synchronize_guest_and_host_stack_helper_for_size(num_bytes_needed_to_represent_stack_size).
we place the stack size after the call instruction so we can
load it in the helper and readjust the return address to point
after the literal value.
The helper is going to search the array of stackpoints to find the first one that is greater than or equal to the current stack pointer, when it finds
the entry it will set the currently host rsp to the host stack pointer value in the entry, and then subtract the stack size of the caller from that.
the current stackpoint index is adjusted to point to the one after the stackpoint we restored to.
The helper is going to search the array of
stackpoints to find the first one that is greater than or
equal to the current stack pointer, when it finds the entry it
will set the currently host rsp to the host stack pointer
value in the entry, and then subtract the stack size of the
caller from that. the current stackpoint index is adjusted to
point to the one after the stackpoint we restored to.
The helper then jumps back to the function that was longjmp'ed to, with the host stack in its proper state. it just works!
The helper then jumps back to the function
that was longjmp'ed to, with the host stack in its proper
state. it just works!
*/
if (num_frames_bigger > 1) {
/*
* can't do anything about this right now :(
* epic mickey is quite slow due to having to call resolve on
* every longjmp, and it longjmps a lot but if we add an
* indirection we lose our stack misalignment check
*/
/* reinterpret_cast<X64CodeCache*>(backend->code_cache())
->AddIndirection(static_cast<uint32_t>(target_address),
static_cast<uint32_t>(host_address));
*/
return host_address;
}
}
@@ -1649,6 +1672,9 @@ Xbyak::Address X64Emitter::GetBackendFlagsPtr() const {
}
void X64Emitter::HandleStackpointOverflowError(ppc::PPCContext* context) {
if (debugging::IsDebuggerAttached()) {
debugging::Break();
}
// context->lr
// todo: show lr in message?
xe::FatalError(
@@ -1674,6 +1700,9 @@ void X64Emitter::PushStackpoint() {
mov(qword[rbx + offsetof(X64BackendStackpoint, host_stack_)], rsp);
mov(dword[rbx + offsetof(X64BackendStackpoint, guest_stack_)], r8d);
mov(r8d, qword[GetContextReg() + offsetof(ppc::PPCContext, lr)]);
mov(dword[rbx + offsetof(X64BackendStackpoint, guest_return_address_)], r8d);
if (IsFeatureEnabled(kX64FlagsIndependentVars)) {
inc(eax);
} else {
@@ -1716,24 +1745,6 @@ void X64Emitter::EnsureSynchronizedGuestAndHostStack() {
// need to be made
// that result in the stack not being 8 byte misaligned on context reentry
#if 0
Xbyak::Label skip{};
mov(r8, qword[GetContextReg() + offsetof(ppc::PPCContext, r[1])]);
mov(rbx, GetBackendCtxPtr(offsetof(X64BackendContext, stackpoints)));
imul(eax,
GetBackendCtxPtr(offsetof(X64BackendContext, current_stackpoint_depth)),
sizeof(X64BackendStackpoint));
sub(eax, sizeof(X64BackendStackpoint));
add(rbx, rax);
cmp(r8d, dword[rbx + offsetof(X64BackendStackpoint, guest_stack_)]);
jle(skip, T_NEAR);
Xbyak::Label skip{};
mov(r11d, stack_size());
call(backend_->synchronize_guest_and_host_stack_helper());
L(skip);
#endif
Xbyak::Label& return_from_sync = this->NewCachedLabel();
// if we got here somehow from setjmp or the like we ought to have a
@@ -1747,7 +1758,6 @@ void X64Emitter::EnsureSynchronizedGuestAndHostStack() {
uint32_t stack32 = static_cast<uint32_t>(e.stack_size());
auto backend = e.backend();
if (stack32 < 256) {
e.call(backend->synchronize_guest_and_host_stack_helper_for_size(1));
e.db(stack32);

View File

@@ -11,6 +11,7 @@
#include "xenia/base/assert.h"
#include "xenia/base/byte_order.h"
#include "xenia/base/cvar.h"
#include "xenia/base/memory.h"
#include "xenia/base/profiling.h"
#include "xenia/base/reset_scope.h"
@@ -22,6 +23,10 @@
#include "xenia/cpu/ppc/ppc_opcode_info.h"
#include "xenia/cpu/ppc/ppc_scanner.h"
#include "xenia/cpu/processor.h"
#include "xenia/cpu/xex_module.h"
DEFINE_bool(dump_translated_hir_functions, false, "dumps translated hir",
"CPU");
namespace xe {
namespace cpu {
@@ -107,10 +112,44 @@ class HirBuilderScope {
~HirBuilderScope() {
if (builder_) {
builder_->RemoveCurrent();
}
}
}
};
void PPCTranslator::DumpHIR(GuestFunction* function, PPCHIRBuilder* builder) {
if (cvars::dump_translated_hir_functions) {
StringBuffer buffer{};
builder_->Dump(&buffer);
XexModule* mod = dynamic_cast<XexModule*>(function->module());
std::wstring folder_name = L"hirdump";
if (mod) {
xex2_opt_execution_info* opt_exec_info = nullptr;
if (mod->GetOptHeader(XEX_HEADER_EXECUTION_INFO, &opt_exec_info)) {
folder_name =
L"hirdump_title_" + std::to_wstring(opt_exec_info->title_id);
}
}
std::filesystem::path folder_path{folder_name};
if (!std::filesystem::exists(folder_path)) {
std::filesystem::create_directory(folder_path);
}
{
wchar_t tmpbuf[64];
_snwprintf(tmpbuf, 64, L"%X", function->address());
folder_path.append(&tmpbuf[0]);
}
FILE* f = fopen(folder_path.generic_u8string().c_str(), "w");
if (f) {
fputs(buffer.buffer(), f);
fclose(f);
}
}
}
bool PPCTranslator::Translate(GuestFunction* function,
uint32_t debug_info_flags) {
SCOPE_profile_cpu_f("cpu");
@@ -203,6 +242,8 @@ bool PPCTranslator::Translate(GuestFunction* function,
string_buffer_.Reset();
}
DumpHIR(function, builder_.get());
// Assemble to backend machine code.
if (!assembler_->Assemble(function, builder_.get(), debug_info_flags,
std::move(debug_info))) {

View File

@@ -31,7 +31,9 @@ class PPCTranslator {
~PPCTranslator();
bool Translate(GuestFunction* function, uint32_t debug_info_flags);
void DumpHIR(GuestFunction* function, PPCHIRBuilder* builder);
void Reset();
private:
void DumpSource(GuestFunction* function, StringBuffer* string_buffer);