[A64] Fix ARM64 DWARF/SEH unwind info and add registration test
This commit is contained in:
@@ -847,3 +847,107 @@ TEST_CASE("FPCR_PRESERVED_ACROSS_HOST_CALLBACK", "[backend]") {
|
||||
processor->backend()->SetGuestRoundingMode(ctx, 0);
|
||||
memory->SystemHeapFree(stack_address);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Unwind info registration for JIT code
|
||||
// =============================================================================
|
||||
// Verify that the backend registers unwind data for JIT'd functions so that
|
||||
// debuggers, profilers, and exception handlers can walk the stack through
|
||||
// JIT code.
|
||||
//
|
||||
// Windows: RtlLookupFunctionEntry directly queries the registered SEH tables.
|
||||
// POSIX: we call backtrace() from inside a JIT callback and verify we get
|
||||
// enough frames to have unwound through the JIT thunks. This exercises the
|
||||
// DWARF .eh_frame data registered via __register_frame.
|
||||
|
||||
#if !XE_PLATFORM_WIN32
|
||||
#include <execinfo.h>
|
||||
static int jit_backtrace_depth = 0;
|
||||
static void CaptureJITBacktrace(ppc::PPCContext* ctx, void* arg0, void* arg1) {
|
||||
void* frames[64];
|
||||
jit_backtrace_depth = backtrace(frames, 64);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE("JIT_UNWIND_INFO_REGISTERED", "[backend]") {
|
||||
auto memory = std::make_unique<Memory>();
|
||||
memory->Initialize();
|
||||
|
||||
std::unique_ptr<xe::cpu::backend::Backend> backend;
|
||||
#if XE_ARCH_AMD64
|
||||
backend.reset(new xe::cpu::backend::x64::X64Backend());
|
||||
#elif XE_ARCH_ARM64
|
||||
backend.reset(new xe::cpu::backend::a64::A64Backend());
|
||||
#endif
|
||||
REQUIRE(backend);
|
||||
|
||||
auto processor = std::make_unique<Processor>(memory.get(), nullptr);
|
||||
processor->Setup(std::move(backend));
|
||||
|
||||
#if XE_PLATFORM_WIN32
|
||||
// Compile a minimal guest function and check that Windows can find its
|
||||
// RUNTIME_FUNCTION entry via RtlLookupFunctionEntry.
|
||||
auto module = std::make_unique<TestModule>(
|
||||
processor.get(), "Test",
|
||||
[](uint32_t address) { return address == 0x80000000; },
|
||||
[](HIRBuilder& b) {
|
||||
b.Return();
|
||||
return true;
|
||||
},
|
||||
/*skip_cf_simplification=*/true);
|
||||
processor->AddModule(std::move(module));
|
||||
processor->backend()->CommitExecutableRange(0x80000000, 0x80010000);
|
||||
|
||||
auto fn = processor->ResolveFunction(0x80000000);
|
||||
REQUIRE(fn != nullptr);
|
||||
|
||||
auto* guest_fn = static_cast<GuestFunction*>(fn);
|
||||
void* code = guest_fn->machine_code();
|
||||
REQUIRE(code != nullptr);
|
||||
|
||||
DWORD64 image_base = 0;
|
||||
auto* entry = RtlLookupFunctionEntry(reinterpret_cast<DWORD64>(code),
|
||||
&image_base, nullptr);
|
||||
REQUIRE(entry != nullptr);
|
||||
REQUIRE(image_base != 0);
|
||||
#else
|
||||
// On POSIX, call backtrace() from inside a JIT callback. If the .eh_frame
|
||||
// unwind info is correctly registered, backtrace will unwind through:
|
||||
// callback -> GuestToHostThunk -> guest func -> HostToGuestThunk -> Call
|
||||
// giving at least 4 frames. Without unwind info it stops at 1-2.
|
||||
jit_backtrace_depth = 0;
|
||||
|
||||
auto* builtin_fn = processor->DefineBuiltin(
|
||||
"CaptureJITBacktrace", CaptureJITBacktrace, nullptr, nullptr);
|
||||
|
||||
auto module = std::make_unique<TestModule>(
|
||||
processor.get(), "Test",
|
||||
[](uint32_t address) { return address == 0x80000000; },
|
||||
[builtin_fn](HIRBuilder& b) {
|
||||
b.CallExtern(builtin_fn);
|
||||
b.Return();
|
||||
return true;
|
||||
},
|
||||
/*skip_cf_simplification=*/true);
|
||||
processor->AddModule(std::move(module));
|
||||
processor->backend()->CommitExecutableRange(0x80000000, 0x80010000);
|
||||
|
||||
auto fn = processor->ResolveFunction(0x80000000);
|
||||
REQUIRE(fn != nullptr);
|
||||
|
||||
uint32_t stack_size = 64 * 1024;
|
||||
uint32_t stack_address = memory->SystemHeapAlloc(stack_size);
|
||||
auto thread_state = std::make_unique<ThreadState>(processor.get(), 0x100,
|
||||
stack_address + stack_size);
|
||||
auto ctx = thread_state->context();
|
||||
ctx->lr = 0xBCBCBCBC;
|
||||
|
||||
fn->Call(thread_state.get(), uint32_t(ctx->lr));
|
||||
|
||||
REQUIRE(jit_backtrace_depth >= 4);
|
||||
|
||||
memory->SystemHeapFree(stack_address);
|
||||
#endif
|
||||
|
||||
memory.reset();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user