44#ifdef REXGLUE_ENABLE_PROFILING
45#include <tracy/Tracy.hpp>
46#define REX_HOOK(subroutine, function) \
47 extern "C" REX_DETAIL_ENTRY_EXPORT REX_FUNC(subroutine) { \
48 ZoneNamedN(___tracy_hook_zone, #subroutine, TracyIsStarted); \
49 rex::ppc::GuestToHostThunk<function>(ctx, base); \
52#define REX_HOOK(subroutine, function) \
53 extern "C" REX_DETAIL_ENTRY_EXPORT REX_FUNC(subroutine) { \
54 rex::ppc::GuestToHostThunk<function>(ctx, base); \
69#define REX_HOOK_RAW(name) extern "C" REX_DETAIL_ENTRY_EXPORT REX_FUNC(name)
80#define REX_STUB(subroutine) \
81 extern "C" REX_DETAIL_ENTRY_EXPORT REX_FUNC(subroutine) { \
82 static constinit ::std::atomic_flag rex_stub_seen_; \
83 ::rex::log::detail::WriteStub(::rex::log::cpu, rex_stub_seen_, #subroutine, {}); \
96#define REX_STUB_LOG(subroutine, msg) \
97 extern "C" REX_DETAIL_ENTRY_EXPORT REX_FUNC(subroutine) { \
98 static constinit ::std::atomic_flag rex_stub_seen_; \
99 ::rex::log::detail::WriteStub(::rex::log::cpu, rex_stub_seen_, #subroutine, msg); \
112#define REX_STUB_RETURN(subroutine, value) \
113 extern "C" REX_DETAIL_ENTRY_EXPORT REX_FUNC(subroutine) { \
114 static constinit ::std::atomic_flag rex_stub_seen_; \
115 const auto rex_stub_value_ = (value); \
116 ::rex::log::detail::WriteStub( \
117 ::rex::log::cpu, rex_stub_seen_, #subroutine, \
118 ::std::format("returning {:#x}", static_cast<::rex::u32>(rex_stub_value_))); \
119 ctx.r3.u64 = rex_stub_value_; \
132#define REX_EXPORT(name, function) REX_HOOK(name, function)
143#define REX_EXPORT_STUB(name) REX_STUB(name)
155#define REX_EXPORT_STUB_RETURN(name, value) REX_STUB_RETURN(name, value)
170#define REX_IMPORT(symbol, callable, sig) \
171 REX_EXTERN(symbol); \
172 [[maybe_unused]] static rex::ppc::ImportFunction<sig> callable { \
188 DetourFrame* previous;
195REX_PPC_API DetourFrame*& GetInnermostDetourFrame();
207REX_PPC_API std::string CheckDetourLocalWrites(
const char* name,
const PPCCodegenFlags& flags,
209 const PPCContext& at_exit, std::atomic_flag& warned);
220 const PPCContext& at_exit, std::atomic_flag& warned);
230REX_PPC_API bool CheckDetourArguments(
const char* name,
const PPCCallUsage& usage, u8 declared_gpr,
241REX_PPC_API void CheckBoundDetourArguments(
const char* name, PPCFunc* host, u8 declared_gpr,
245struct DeclaredArguments {
254template <
typename Guest>
255constexpr DeclaredArguments GetDeclaredArguments() {
256 DeclaredArguments declared{};
258 for (u32 slot = place.slot;
260 declared.gpr_mask =
static_cast<u8
>(declared.gpr_mask | (1u << slot));
262 if (place.fpr >= 0) {
263 declared.fpr_mask =
static_cast<u16
>(declared.fpr_mask | (1u << place.fpr));
274template <
typename R,
typename... A>
275struct DetourBody<R (*)(A...)> {
276 static constexpr bool kContext =
false;
278 using Guest = R(A...);
279 using Args = std::tuple<A...>;
283template <
typename R,
typename... A>
285 static constexpr bool kContext =
true;
287 using Guest = R(A...);
288 using Args = std::tuple<A...>;
296template <
typename R,
typename... Args>
297R CallOriginal(DetourFrame& frame, PPCFunc& fn, Args... args) {
298 static_assert(!HiddenReturnType<R>,
"A detour cannot return a structure by value");
300 u8* base = frame.base;
301 frame.ran_original =
true;
302 auto arguments = std::make_tuple(args...);
303 const u32 caller_r1 = ctx.
r1.u32;
304 const u64 caller_lr = ctx.
lr;
305 const bool opened = caller_r1 != frame.entry_r1;
312 ctx.
r1.u32 = caller_r1;
319template <
typename Guest,
typename Args,
size_t... I>
320void ReadDetourArgs(
PPCContext& ctx, u8* base, Args& args, std::index_sequence<I...>)
noexcept {
322 ((std::get<I>(args) =
336 DetourScope(
PPCContext& ctx, u8* base,
bool chained)
337 : frame_{&ctx, base, ctx.
r1.u32,
338 ctx.
lr,
false, chained ? GetInnermostDetourFrame() :
nullptr},
342 GetInnermostDetourFrame() = &frame_;
349 frame_.ctx->r1.u32 = frame_.entry_r1;
350 frame_.ctx->lr = frame_.entry_lr;
351 frame_.ctx->detour_frame = context_previous_;
353 GetInnermostDetourFrame() = frame_.previous;
357 const DetourFrame& GetFrame()
const {
return frame_; }
361 DetourFrame* context_previous_;
368template <
typename Sig>
372template <
typename R,
typename... Args>
373struct DetourOriginal<R(Args...)> {
376 constexpr explicit DetourOriginal(PPCFunc& original) noexcept : fn(original) {}
383 detail::DetourFrame* frame = detail::GetInnermostDetourFrame();
384 REX_ASSERT(frame !=
nullptr,
"A detour original was called outside a detour body");
385 return detail::CallOriginal<R>(*frame, fn, args...);
390template <
typename R,
typename... Args>
394 constexpr explicit DetourOriginal(PPCFunc& original) noexcept : fn(original) {}
399 REX_ASSERT(frame !=
nullptr,
"A detour original was called outside a detour body");
400 return detail::CallOriginal<R>(*frame, this->fn, args...);
412 [[maybe_unused]] PPCFunc* host) {
413 using Traits = detail::DetourBody<
decltype(Body)>;
414 using R = Traits::Result;
417 detail::CheckHookContext(ctx, name);
421#if !REX_PLATFORM_WIN32
424 typename Traits::Args args;
425 detail::ReadDetourArgs<typename Traits::Guest>(
426 ctx, base, args, std::make_index_sequence<std::tuple_size_v<typename Traits::Args>>());
429 static constinit std::atomic_flag warned;
430 static constinit std::atomic_flag arguments_checked;
432 if (!arguments_checked.test_and_set()) {
433 constexpr detail::DeclaredArguments kDeclared =
434 detail::GetDeclaredArguments<typename Traits::Guest>();
435 detail::CheckBoundDetourArguments(name, host, kDeclared.gpr_mask, kDeclared.fpr_mask);
438 detail::DetourScope scope(ctx, base, !Traits::kContext);
440 auto invoke = [&]() -> R {
441 if constexpr (Traits::kContext) {
442 return std::apply([&](
auto&... values) -> R {
return Body(ctx, values...); }, args);
444 return std::apply(Body, args);
447 auto check_exit = [&] {
449 const detail::DetourFrame& frame = scope.GetFrame();
450 if (!frame.ran_original) {
451 REXCPU_ERROR(
"REX_DETOUR {} returned without running its original", name);
453 REX_ASSERT(frame.ran_original,
"REX_DETOUR returned without running its original");
454 if (ctx.
r1.u32 != frame.entry_r1) {
455 REXCPU_ERROR(
"REX_DETOUR {} left a StackFrame open, r1 is {:#x} and was {:#x}", name,
456 ctx.
r1.u32, frame.entry_r1);
458 REX_ASSERT(ctx.
r1.u32 == frame.entry_r1,
"REX_DETOUR left a StackFrame open");
459 if (ctx.
r2.u64 != at_entry.
r2.u64) {
460 REXCPU_ERROR(
"REX_DETOUR {} changed r2", name);
462 REX_ASSERT(ctx.
r2.u64 == at_entry.
r2.u64,
"REX_DETOUR changed r2");
463 if (ctx.
r13.u64 != at_entry.
r13.u64) {
464 REXCPU_ERROR(
"REX_DETOUR {} changed r13", name);
467 detail::CheckBoundDetourLocalWrites(name, at_entry, ctx, warned);
472 if constexpr (std::is_void_v<R>) {
474 asm volatile(
"" :::
"memory");
477 auto result = invoke();
478 asm volatile(
"" :::
"memory");
498#ifdef REXGLUE_ENABLE_PROFILING
499#define REX_DETAIL_DETOUR_ZONE(name) ZoneNamedN(___tracy_hook_zone, #name, TracyIsStarted);
501#define REX_DETAIL_DETOUR_ZONE(name)
520#define REX_DETOUR(name, Ret, params) \
521 REX_EXTERN(__imp__##name); \
522 struct name##_rex_detour { \
523 static inline const rex::ppc::DetourOriginal<Ret params> original{__imp__##name}; \
524 static Ret Body params; \
526 REX_HOOK_RAW(name) { \
527 REX_DETAIL_DETOUR_ZONE(name) \
528 rex::ppc::RunDetour<&name##_rex_detour::Body>(ctx, base, #name, name); \
530 Ret name##_rex_detour::Body params
#define REX_ASSERT(cond,...)
Definition assert.h:84
PPC image layout, embedded metadata assets and pdata decoding.
Definition title_runtime.h:33
u32 OpenCallFrame(PPCContext &ctx, u8 *base, u32 stack_bytes)
Definition call.h:782
void StoreHostResult(PPCContext &ctx, u8 *base, u32 hidden_pointer, const RetT &value)
Definition call.h:672
R LoadGuestResult(u8 *base, const PPCRegister &r3, const PPCRegister &f1, const PPCVRegister &v1, u32 result_storage)
Definition call.h:822
constexpr u32 GetStackArgumentBytes()
Definition call.h:795
void RunDetour(PPCContext &ctx, u8 *base, const char *name, PPCFunc *host)
Definition hook.h:411
void TranslateArgsToGuest(PPCContext &ctx, u8 *base, std::tuple< TArgs... > &) noexcept
Definition call.h:595
constexpr auto PlaceSignatureArgs(R(*)(Args...))
Definition call.h:552
std::expected< T, Error > Result
Definition result.h:98
const std::atomic< bool > * stop_requested
Definition context.h:634
PPCRegister f1
Definition context.h:461
u64 lr
Definition context.h:428
PPCRegister r2
Definition context.h:397
PPCRegister r3
Definition context.h:389
rex::ppc::detail::DetourFrame * detour_frame
Definition context.h:453
PPCVRegister v1
Definition context.h:499
PPCRegister r13
Definition context.h:407
PPCRegister r1
Definition context.h:396
static T GetValue(PPCContext &ctx, u8 *base, const ArgPlace &arg) noexcept
Definition call.h:339
static constexpr size_t kIntegerRegisters
Definition call.h:130
R operator()(Args... args) const
Definition hook.h:382
R operator()(PPCContext &ctx, Args... args) const
Definition hook.h:397
Integer types, byte order wrappers and size literals.
Move only function, const propagating pointer and scope guard.