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:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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))) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user