Files
2026-04-17 20:09:41 +03:00

175 lines
5.0 KiB
C++

/**
* @file ppc/exceptions.h
* @brief Structured Exception Handling (SEH) support for recompiled code
*
* @copyright Copyright (c) 2026 Tom Clay <tomc@tctechstuff.com>
* All rights reserved.
*
* @license BSD 3-Clause License
* See LICENSE file in the project root for full license text.
*
* @remarks Provides cross-platform SEH support for recompiled PPC code.
* On Windows, uses native __try/__except (works with MSVC and Clang-cl).
* On Linux, uses signal handlers for SIGSEGV/SIGBUS.
*
* @usage 1. Call rex::initialize_seh() at startup
* 2. Generated code wraps SEH scopes with try/catch blocks
* 3. Hardware exceptions (null deref, access violation) throw SehException
* 4. Catch blocks call finally handlers and rethrow
*/
#pragma once
#include <cstdint>
#include <exception>
#include <rex/platform.h>
#include <rex/ppc/detail/seh.h>
namespace rex {
//=============================================================================
// SEH Exception Type
//=============================================================================
/**
* @brief Exception thrown when hardware exception occurs in SEH-protected code
*
* This exception is thrown by the SEH translator when a hardware exception
* (e.g., access violation, null pointer dereference) occurs. The recompiled
* code catches this exception, runs finally handlers, and rethrows.
*/
class SehException : public std::exception {
public:
/// Exception codes matching Windows EXCEPTION_* values
enum Code : uint32_t {
ACCESS_VIOLATION = 0xC0000005,
IN_PAGE_ERROR = 0xC0000006,
ILLEGAL_INSTRUCTION = 0xC000001D,
STACK_OVERFLOW = 0xC00000FD,
FLOAT_DIVIDE_BY_ZERO = 0xC000008E,
INTEGER_DIVIDE_BY_ZERO = 0xC0000094,
UNKNOWN = 0xFFFFFFFF
};
explicit SehException(Code code, uintptr_t address = 0) : code_(code), address_(address) {}
const char* what() const noexcept override {
switch (code_) {
case ACCESS_VIOLATION:
return "SEH: Access Violation";
case IN_PAGE_ERROR:
return "SEH: In-Page Error";
case ILLEGAL_INSTRUCTION:
return "SEH: Illegal Instruction";
case STACK_OVERFLOW:
return "SEH: Stack Overflow";
case FLOAT_DIVIDE_BY_ZERO:
return "SEH: Float Divide by Zero";
case INTEGER_DIVIDE_BY_ZERO:
return "SEH: Integer Divide by Zero";
default:
return "SEH: Unknown Exception";
}
}
Code code() const noexcept { return code_; }
uintptr_t address() const noexcept { return address_; }
private:
Code code_;
uintptr_t address_;
};
//=============================================================================
// Initialization
//=============================================================================
/**
* @brief Initialize SEH support for the current thread
*
* On Windows, no per-thread init is needed for native SEH with RaiseException.
* On Linux, sets the SEH active flag to false (will be set true by SEH_TRY).
*/
inline void initialize_seh_thread() {
ppc::detail::seh_active() = false;
}
/**
* @brief Initialize SEH support globally
*
* On Windows, marks SEH as initialized (native SEH needs no setup).
* On Linux, installs signal handlers for SIGSEGV, SIGBUS, SIGFPE, SIGILL.
*/
inline void initialize_seh() {
ppc::detail::seh_initialize();
}
//=============================================================================
// SEH Guard Scope and Macros
//=============================================================================
#if REX_PLATFORM_WIN32
/**
* @brief RAII guard for SEH-protected regions (Windows native SEH version)
* Note: This is a no-op on Windows since we use __try/__except directly
*/
class SehGuard {
public:
SehGuard() = default;
~SehGuard() = default;
SehGuard(const SehGuard&) = delete;
SehGuard& operator=(const SehGuard&) = delete;
};
#define SEH_TRY __try {
#define SEH_CATCH \
} \
__except (::rex::ppc::detail::seh_filter(GetExceptionCode(), GetExceptionInformation())) {
#define SEH_CATCH_ALL \
} \
__except (::rex::ppc::detail::seh_filter(GetExceptionCode(), GetExceptionInformation())) {
#define SEH_RETHROW ::rex::ppc::detail::seh_rethrow()
#define SEH_END }
#else // !REX_PLATFORM_WIN32
/**
* @brief RAII guard for SEH-protected regions (Linux version)
*/
class SehGuard {
public:
SehGuard() {
was_active_ = ppc::detail::seh_active();
ppc::detail::seh_active() = true;
}
~SehGuard() { ppc::detail::seh_active() = was_active_; }
// Non-copyable
SehGuard(const SehGuard&) = delete;
SehGuard& operator=(const SehGuard&) = delete;
private:
bool was_active_ = false;
};
#define SEH_TRY \
{ \
::rex::SehGuard __seh_guard; \
try
#define SEH_CATCH catch (const ::rex::SehException&)
#define SEH_CATCH_ALL catch (...)
#define SEH_RETHROW throw
#define SEH_END }
#endif // REX_PLATFORM_WIN32
} // namespace rex