mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-22 08:33:12 -05:00
Merge pull request #4693 from EagleBearerYS/fix/libc-windows-undeliverable-exception
fix(libc): report host C++ exceptions that Windows cannot deliver
This commit is contained in:
3 files changed
+86
No files matched your search
@@ -240,6 +240,14 @@ if(BUILD_TESTING)
|
||||
set_tests_properties(windows_write_watch PROPERTIES TIMEOUT 20)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
add_test_executable(undeliverable_host_exception_tests tests/UndeliverableHostException.cpp)
|
||||
add_dependencies(undeliverable_host_exception_tests libc)
|
||||
add_test(NAME undeliverable_host_exception COMMAND undeliverable_host_exception_tests $<TARGET_FILE:libc>)
|
||||
set_tests_properties(undeliverable_host_exception PROPERTIES
|
||||
PASS_REGULAR_EXPRESSION "C\\+\\+ exception St9bad_alloc raised on thread [0-9]+ cannot be delivered: 0x[0-9a-f]+ undeliverable_host_exception_tests\\.exe\\+0x[0-9a-f]+ has no unwind data")
|
||||
endif()
|
||||
|
||||
add_test_executable(guest_offline_imports_tests tests/GuestOfflineImports.cpp)
|
||||
target_include_directories(guest_offline_imports_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_link_libraries(guest_offline_imports_tests PRIVATE libSceHttp libSceNpEntitlementAccess libSceRudp libc)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <tlhelp32.h>
|
||||
#include "prx/libc/include/CallTrace.hpp"
|
||||
#include "prx/libc/include/GuestArena.hpp"
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <csignal>
|
||||
@@ -226,6 +227,50 @@ void TraceSse4a(std::uint64_t rip, const sse4a::Instruction& instruction, const
|
||||
Report("[sse4a] #%llu %s %s (length %u, index %u, %u bytes) at %s on thread %lu\n", count, mnemonic, operands, field.length, field.index, static_cast<unsigned>(instruction.size), line, GetCurrentThreadId());
|
||||
}
|
||||
|
||||
constexpr DWORD GccExceptionMask = 0x20ffffffu;
|
||||
constexpr DWORD GccException = 0x20474343u;
|
||||
constexpr std::uint64_t GccExceptionClass = 0x474e5543432b2b00ull;
|
||||
constexpr std::uint64_t CxaExceptionTypeOffset = 80;
|
||||
|
||||
std::uint64_t FrameWithoutUnwindData(const CONTEXT& start) {
|
||||
const HMODULE runtime[] = {GetModuleHandleA("KERNELBASE.dll"), GetModuleHandleA("libgcc_s_seh-1.dll"), GetModuleHandleA("libstdc++-6.dll")};
|
||||
CONTEXT context = start;
|
||||
for (int depth = 0; depth < 64 && context.Rip != 0; ++depth) {
|
||||
HMODULE module = nullptr;
|
||||
GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, reinterpret_cast<LPCSTR>(context.Rip), &module);
|
||||
DWORD64 imageBase = 0;
|
||||
const auto entry = RtlLookupFunctionEntry(context.Rip, &imageBase, nullptr);
|
||||
if (module == nullptr || std::find(std::begin(runtime), std::end(runtime), module) == std::end(runtime)) return entry == nullptr ? context.Rip : 0;
|
||||
if (entry == nullptr) return 0;
|
||||
PVOID handlerData = nullptr;
|
||||
DWORD64 establisher = 0;
|
||||
RtlVirtualUnwind(UNW_FLAG_NHANDLER, imageBase, context.Rip, entry, &context, &handlerData, &establisher, nullptr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ReadWord(std::uint64_t address, std::uint64_t* value) {
|
||||
return ReadCode(address, reinterpret_cast<std::uint8_t*>(value), sizeof(*value)) == sizeof(*value);
|
||||
}
|
||||
|
||||
void ReportUndeliverableException(const EXCEPTION_POINTERS* info, std::uint64_t frame) {
|
||||
const auto* record = info->ExceptionRecord;
|
||||
char type[256] = "of unknown type";
|
||||
const auto header = record->NumberParameters >= 1 ? static_cast<std::uint64_t>(record->ExceptionInformation[0]) : 0;
|
||||
std::uint64_t exceptionClass = 0;
|
||||
std::uint64_t typeInfo = 0;
|
||||
std::uint64_t namePointer = 0;
|
||||
if (ReadWord(header, &exceptionClass) && exceptionClass == GccExceptionClass && ReadWord(header - CxaExceptionTypeOffset, &typeInfo) && ReadWord(typeInfo + 8, &namePointer)) {
|
||||
std::uint8_t name[sizeof(type)];
|
||||
const auto copied = ReadCode(namePointer, name, sizeof(name));
|
||||
if (const auto* end = static_cast<const std::uint8_t*>(std::memchr(name, 0, copied))) std::memcpy(type, name, static_cast<std::size_t>(end - name) + 1);
|
||||
}
|
||||
char line[MAX_PATH + 64];
|
||||
DescribeAddress(frame, line, sizeof(line));
|
||||
Report("\nFATAL: C++ exception %s raised on thread %lu cannot be delivered: %s has no unwind data\n", type, GetCurrentThreadId(), line);
|
||||
ReportStackCandidates(info->ContextRecord->Rsp, 32, 0x4000);
|
||||
}
|
||||
|
||||
bool HandleSse4a(EXCEPTION_POINTERS* info) {
|
||||
if (!g_sse4aEmulation) return false;
|
||||
const auto* record = info->ExceptionRecord;
|
||||
@@ -250,6 +295,10 @@ LONG WINAPI ReportCrash(EXCEPTION_POINTERS* info) {
|
||||
if (HandleWatch(info)) return EXCEPTION_CONTINUE_EXECUTION;
|
||||
if (HandleSse4a(info)) return EXCEPTION_CONTINUE_EXECUTION;
|
||||
const auto* record = info->ExceptionRecord;
|
||||
if ((record->ExceptionCode & GccExceptionMask) == GccException) {
|
||||
if (const auto frame = FrameWithoutUnwindData(*info->ContextRecord)) ReportUndeliverableException(info, frame);
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
if (!IsFatal(record->ExceptionCode)) return EXCEPTION_CONTINUE_SEARCH;
|
||||
if (reported.exchange(true)) {
|
||||
// Another thread is already reporting; let it finish before this fault ends the process.
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <new>
|
||||
#include <windows.h>
|
||||
|
||||
namespace {
|
||||
|
||||
[[gnu::noinline]] void* AllocateWithoutUnwindData(std::size_t size) {
|
||||
void* const block = ::operator new(size);
|
||||
std::printf("allocated %p\n", block);
|
||||
return block;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc != 2) {
|
||||
std::fprintf(stderr, "usage: %s <libc.prx>\n", argv[0]);
|
||||
return 2;
|
||||
}
|
||||
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
|
||||
if (LoadLibraryExA(argv[1], nullptr, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS) == nullptr) {
|
||||
std::fprintf(stderr, "%s: LoadLibraryEx failed with error %lu\n", argv[1], GetLastError());
|
||||
return 1;
|
||||
}
|
||||
AllocateWithoutUnwindData(std::size_t{1} << 46);
|
||||
std::fputs("operator new returned instead of throwing\n", stderr);
|
||||
return 1;
|
||||
}
|
||||
Reference in new issue
Block a user