src/hook_mid_context.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /** | ||
| 2 | * @file hook_mid_context.cpp | ||
| 3 | * @brief This TU implements the hook::MidContext accessor bridge over the backend register frame. | ||
| 4 | */ | ||
| 5 | |||
| 6 | #include "DetourModKit/hook.hpp" | ||
| 7 | |||
| 8 | #include "internal/hook_backend.hpp" | ||
| 9 | |||
| 10 | #include <cstddef> | ||
| 11 | #include <cstdint> | ||
| 12 | #include <cstring> | ||
| 13 | |||
| 14 | namespace DetourModKit | ||
| 15 | { | ||
| 16 | namespace hook | ||
| 17 | { | ||
| 18 | // The hook::MidContext accessor bridge keeps MidContext incomplete. These accessors alone recover the real | ||
| 19 | // safetyhook::Context64 through reinterpret_cast. The reference always denotes the exact Context64 from the | ||
| 20 | // backend, so the cast is well-defined. | ||
| 21 | 19 | std::uintptr_t &gpr(MidContext &ctx, Gpr reg) noexcept | |
| 22 | { | ||
| 23 | 19 | auto &context = reinterpret_cast<safetyhook::Context64 &>(ctx); | |
| 24 |
3/16✗ Branch 2 → 3 not taken.
✗ Branch 2 → 4 not taken.
✓ Branch 2 → 5 taken 13 times.
✓ Branch 2 → 6 taken 4 times.
✗ Branch 2 → 7 not taken.
✗ Branch 2 → 8 not taken.
✗ Branch 2 → 9 not taken.
✓ Branch 2 → 10 taken 2 times.
✗ Branch 2 → 11 not taken.
✗ Branch 2 → 12 not taken.
✗ Branch 2 → 13 not taken.
✗ Branch 2 → 14 not taken.
✗ Branch 2 → 15 not taken.
✗ Branch 2 → 16 not taken.
✗ Branch 2 → 17 not taken.
✗ Branch 2 → 18 not taken.
|
19 | switch (reg) |
| 25 | { | ||
| 26 | ✗ | case Gpr::Rax: | |
| 27 | ✗ | return context.rax; | |
| 28 | ✗ | case Gpr::Rbx: | |
| 29 | ✗ | return context.rbx; | |
| 30 | 13 | case Gpr::Rcx: | |
| 31 | 13 | return context.rcx; | |
| 32 | 4 | case Gpr::Rdx: | |
| 33 | 4 | return context.rdx; | |
| 34 | ✗ | case Gpr::Rsi: | |
| 35 | ✗ | return context.rsi; | |
| 36 | ✗ | case Gpr::Rdi: | |
| 37 | ✗ | return context.rdi; | |
| 38 | ✗ | case Gpr::Rbp: | |
| 39 | ✗ | return context.rbp; | |
| 40 | 2 | case Gpr::R8: | |
| 41 | 2 | return context.r8; | |
| 42 | ✗ | case Gpr::R9: | |
| 43 | ✗ | return context.r9; | |
| 44 | ✗ | case Gpr::R10: | |
| 45 | ✗ | return context.r10; | |
| 46 | ✗ | case Gpr::R11: | |
| 47 | ✗ | return context.r11; | |
| 48 | ✗ | case Gpr::R12: | |
| 49 | ✗ | return context.r12; | |
| 50 | ✗ | case Gpr::R13: | |
| 51 | ✗ | return context.r13; | |
| 52 | ✗ | case Gpr::R14: | |
| 53 | ✗ | return context.r14; | |
| 54 | ✗ | case Gpr::R15: | |
| 55 | ✗ | return context.r15; | |
| 56 | } | ||
| 57 | // Every enumerator returns above. The rax return keeps the function well-formed. | ||
| 58 | ✗ | return context.rax; | |
| 59 | } | ||
| 60 | |||
| 61 | 1 | std::uintptr_t stack_pointer(const MidContext &ctx) noexcept | |
| 62 | { | ||
| 63 | 1 | return reinterpret_cast<const safetyhook::Context64 &>(ctx).rsp; | |
| 64 | } | ||
| 65 | |||
| 66 | 1 | std::uintptr_t &resume_stack_pointer(MidContext &ctx) noexcept | |
| 67 | { | ||
| 68 | 1 | return reinterpret_cast<safetyhook::Context64 &>(ctx).trampoline_rsp; | |
| 69 | } | ||
| 70 | |||
| 71 | 1 | std::uintptr_t &instruction_pointer(MidContext &ctx) noexcept | |
| 72 | { | ||
| 73 | 1 | return reinterpret_cast<safetyhook::Context64 &>(ctx).rip; | |
| 74 | } | ||
| 75 | |||
| 76 | 3 | std::uintptr_t &flags(MidContext &ctx) noexcept | |
| 77 | { | ||
| 78 | 3 | return reinterpret_cast<safetyhook::Context64 &>(ctx).rflags; | |
| 79 | } | ||
| 80 | |||
| 81 | // The mid-hook assembly stub stores each captured register at a fixed offset. These assertions pin the C++ | ||
| 82 | // layout to the complete frame ABI, with the XMM prefix and integer/resume tail (T-XMM). Pointer arithmetic | ||
| 83 | // from &xmm0 across distinct members is undefined C++. Layout assertions do not legalize it, so the switch | ||
| 84 | // below uses explicit member selection. | ||
| 85 | static_assert(sizeof(safetyhook::Xmm) == 16); | ||
| 86 | static_assert(offsetof(safetyhook::Context64, xmm0) == 0); | ||
| 87 | static_assert(offsetof(safetyhook::Context64, xmm1) == 16); | ||
| 88 | static_assert(offsetof(safetyhook::Context64, xmm2) == 32); | ||
| 89 | static_assert(offsetof(safetyhook::Context64, xmm3) == 48); | ||
| 90 | static_assert(offsetof(safetyhook::Context64, xmm4) == 64); | ||
| 91 | static_assert(offsetof(safetyhook::Context64, xmm5) == 80); | ||
| 92 | static_assert(offsetof(safetyhook::Context64, xmm6) == 96); | ||
| 93 | static_assert(offsetof(safetyhook::Context64, xmm7) == 112); | ||
| 94 | static_assert(offsetof(safetyhook::Context64, xmm8) == 128); | ||
| 95 | static_assert(offsetof(safetyhook::Context64, xmm9) == 144); | ||
| 96 | static_assert(offsetof(safetyhook::Context64, xmm10) == 160); | ||
| 97 | static_assert(offsetof(safetyhook::Context64, xmm11) == 176); | ||
| 98 | static_assert(offsetof(safetyhook::Context64, xmm12) == 192); | ||
| 99 | static_assert(offsetof(safetyhook::Context64, xmm13) == 208); | ||
| 100 | static_assert(offsetof(safetyhook::Context64, xmm14) == 224); | ||
| 101 | static_assert(offsetof(safetyhook::Context64, xmm15) == 240); | ||
| 102 | static_assert(offsetof(safetyhook::Context64, rflags) == 256); | ||
| 103 | static_assert(offsetof(safetyhook::Context64, r15) == 264); | ||
| 104 | static_assert(offsetof(safetyhook::Context64, r14) == 272); | ||
| 105 | static_assert(offsetof(safetyhook::Context64, r13) == 280); | ||
| 106 | static_assert(offsetof(safetyhook::Context64, r12) == 288); | ||
| 107 | static_assert(offsetof(safetyhook::Context64, r11) == 296); | ||
| 108 | static_assert(offsetof(safetyhook::Context64, r10) == 304); | ||
| 109 | static_assert(offsetof(safetyhook::Context64, r9) == 312); | ||
| 110 | static_assert(offsetof(safetyhook::Context64, r8) == 320); | ||
| 111 | static_assert(offsetof(safetyhook::Context64, rdi) == 328); | ||
| 112 | static_assert(offsetof(safetyhook::Context64, rsi) == 336); | ||
| 113 | static_assert(offsetof(safetyhook::Context64, rdx) == 344); | ||
| 114 | static_assert(offsetof(safetyhook::Context64, rcx) == 352); | ||
| 115 | static_assert(offsetof(safetyhook::Context64, rbx) == 360); | ||
| 116 | static_assert(offsetof(safetyhook::Context64, rax) == 368); | ||
| 117 | static_assert(offsetof(safetyhook::Context64, rbp) == 376); | ||
| 118 | static_assert(offsetof(safetyhook::Context64, rsp) == 384); | ||
| 119 | static_assert(offsetof(safetyhook::Context64, trampoline_rsp) == 392); | ||
| 120 | static_assert(offsetof(safetyhook::Context64, rip) == 400); | ||
| 121 | static_assert(sizeof(safetyhook::Context64) == 408); | ||
| 122 | |||
| 123 | 20 | XmmView xmm(const MidContext &ctx, std::size_t index) noexcept | |
| 124 | { | ||
| 125 | 20 | XmmView view{}; | |
| 126 | 20 | const auto &context = reinterpret_cast<const safetyhook::Context64 &>(ctx); | |
| 127 | 20 | const safetyhook::Xmm *reg = nullptr; | |
| 128 |
17/17✓ Branch 2 → 3 taken 2 times.
✓ Branch 2 → 4 taken 1 time.
✓ Branch 2 → 5 taken 1 time.
✓ Branch 2 → 6 taken 1 time.
✓ Branch 2 → 7 taken 1 time.
✓ Branch 2 → 8 taken 1 time.
✓ Branch 2 → 9 taken 1 time.
✓ Branch 2 → 10 taken 1 time.
✓ Branch 2 → 11 taken 1 time.
✓ Branch 2 → 12 taken 1 time.
✓ Branch 2 → 13 taken 1 time.
✓ Branch 2 → 14 taken 1 time.
✓ Branch 2 → 15 taken 1 time.
✓ Branch 2 → 16 taken 1 time.
✓ Branch 2 → 17 taken 1 time.
✓ Branch 2 → 18 taken 1 time.
✓ Branch 2 → 19 taken 3 times.
|
20 | switch (index) |
| 129 | { | ||
| 130 | 2 | case 0: | |
| 131 | 2 | reg = &context.xmm0; | |
| 132 | 2 | break; | |
| 133 | 1 | case 1: | |
| 134 | 1 | reg = &context.xmm1; | |
| 135 | 1 | break; | |
| 136 | 1 | case 2: | |
| 137 | 1 | reg = &context.xmm2; | |
| 138 | 1 | break; | |
| 139 | 1 | case 3: | |
| 140 | 1 | reg = &context.xmm3; | |
| 141 | 1 | break; | |
| 142 | 1 | case 4: | |
| 143 | 1 | reg = &context.xmm4; | |
| 144 | 1 | break; | |
| 145 | 1 | case 5: | |
| 146 | 1 | reg = &context.xmm5; | |
| 147 | 1 | break; | |
| 148 | 1 | case 6: | |
| 149 | 1 | reg = &context.xmm6; | |
| 150 | 1 | break; | |
| 151 | 1 | case 7: | |
| 152 | 1 | reg = &context.xmm7; | |
| 153 | 1 | break; | |
| 154 | 1 | case 8: | |
| 155 | 1 | reg = &context.xmm8; | |
| 156 | 1 | break; | |
| 157 | 1 | case 9: | |
| 158 | 1 | reg = &context.xmm9; | |
| 159 | 1 | break; | |
| 160 | 1 | case 10: | |
| 161 | 1 | reg = &context.xmm10; | |
| 162 | 1 | break; | |
| 163 | 1 | case 11: | |
| 164 | 1 | reg = &context.xmm11; | |
| 165 | 1 | break; | |
| 166 | 1 | case 12: | |
| 167 | 1 | reg = &context.xmm12; | |
| 168 | 1 | break; | |
| 169 | 1 | case 13: | |
| 170 | 1 | reg = &context.xmm13; | |
| 171 | 1 | break; | |
| 172 | 1 | case 14: | |
| 173 | 1 | reg = &context.xmm14; | |
| 174 | 1 | break; | |
| 175 | 1 | case 15: | |
| 176 | 1 | reg = &context.xmm15; | |
| 177 | 1 | break; | |
| 178 | 3 | default: | |
| 179 | // Fail closed: an out-of-range index returns the zeroed view. | ||
| 180 | 3 | return view; | |
| 181 | } | ||
| 182 | // The 16 bytes are copied out by value: XMM is surfaced read-only. | ||
| 183 | 17 | std::memcpy(view.bytes.data(), reg->u8, view.bytes.size()); | |
| 184 | 17 | return view; | |
| 185 | } | ||
| 186 | } // namespace hook | ||
| 187 | } // namespace DetourModKit | ||
| 188 |