ReXGlue SDK
Loading...
Searching...
No Matches
stack.h
Go to the documentation of this file.
1
11
12#pragma once
13
14#include <algorithm>
15#include <array>
16#include <cstring>
17#include <span>
18#include <string_view>
19#include <type_traits>
20
21#include <rex/assert.h>
22#include <rex/memory/endian.h>
24#include <rex/ppc/context.h>
25#include <rex/ppc/call.h>
26#include <rex/types.h>
27#include <rex/platform.h>
28#include <rex/utility.h>
29
30namespace rex::ppc {
31
32class StackFrame;
33
34namespace detail {
35
37constexpr u32 stack_align(u32 size) {
38 return (size + 15u) & ~15u;
39}
40
42template <typename T>
43struct HoldsHostPointer
44 : std::bool_constant<std::is_pointer_v<T> || std::is_member_pointer_v<T> ||
45 std::is_null_pointer_v<T> || memory::is_mapped_ptr_v<T>> {};
46
48template <typename T, size_t N>
49struct HoldsHostPointer<T[N]> : HoldsHostPointer<T> {};
50
52template <typename T, size_t N>
53struct HoldsHostPointer<std::array<T, N>> : HoldsHostPointer<T> {};
54
56template <typename T>
57inline constexpr bool kHoldsHostPointer = HoldsHostPointer<std::remove_cv_t<T>>::value;
58
63REX_PPC_API StackFrame*& GetInnermostStackFrame();
64
69inline BoundThread GetBoundThreadChecked() {
70 const BoundThread bound = GetBoundThread();
71 REX_ASSERT(bound.ctx != nullptr, "StackFrame needs a thread state bound to the calling thread");
72 return bound;
73}
74
75} // namespace detail
76
82 public:
84 StackFrame() : StackFrame(detail::GetBoundThreadChecked()) {}
85
92 StackFrame(PPCContext& ctx, u8* base)
93 : ctx_(ctx), base_(base), saved_r1_(ctx.r1.u32), previous_(detail::GetInnermostStackFrame()) {
94 detail::GetInnermostStackFrame() = this;
95 }
96
99 REX_ASSERT(detail::GetInnermostStackFrame() == this,
100 "StackFrame closed out of order, an inner frame is still open");
101 ctx_.r1.u32 = saved_r1_;
102 detail::GetInnermostStackFrame() = previous_;
103 }
104
110 template <typename T>
112 static_assert(std::is_trivially_copyable_v<T>,
113 "StackFrame pushes only trivially copyable types");
114 static_assert(!detail::kHoldsHostPointer<T>,
115 "StackFrame cannot push a pointer or a MappedPtr, it would copy a host address");
116 const u32 address = Allocate(sizeof(T));
117 T* host = rex::memory::GuestPtr<T*>(base_, address);
118 std::memset(static_cast<void*>(host), 0, sizeof(T));
119 return memory::MappedPtr<T>(host, address);
120 }
121
128 template <typename T>
129 memory::MappedPtr<T> Push(const T& value) {
130 static_assert(std::is_trivially_copyable_v<T>,
131 "StackFrame pushes only trivially copyable types");
132 static_assert(!detail::kHoldsHostPointer<T>,
133 "StackFrame cannot push a pointer or a MappedPtr, it would copy a host address");
134 const u32 address = Allocate(sizeof(T));
135 T* host = rex::memory::GuestPtr<T*>(base_, address);
136 if constexpr (std::is_arithmetic_v<T> || std::is_enum_v<T>) {
137 const T swapped = rex::byte_swap(value);
138 std::memcpy(static_cast<void*>(host), &swapped, sizeof(T));
139 } else {
140 std::memcpy(static_cast<void*>(host), &value, sizeof(T));
141 }
142 return memory::MappedPtr<T>(host, address);
143 }
144
150 memory::MappedPtr<char> PushString(std::string_view text) {
151 const u32 address = Allocate(static_cast<u32>(text.size()) + 1);
152 char* host = rex::memory::GuestPtr<char*>(base_, address);
153 std::memcpy(host, text.data(), text.size());
154 host[text.size()] = '\0';
155 return memory::MappedPtr<char>(host, address);
156 }
157
163 memory::MappedPtr<be_u16> PushWideString(std::u16string_view text) {
164 const u32 address = Allocate((static_cast<u32>(text.size()) + 1) * sizeof(u16));
165 be_u16* host = rex::memory::GuestPtr<be_u16*>(base_, address);
166 std::ranges::transform(text, host,
167 [](char16_t unit) { return be_u16(static_cast<u16>(unit)); });
168 host[text.size()] = u16{0};
169 return memory::MappedPtr<be_u16>(host, address);
170 }
171
177 memory::MappedPtr<u8> PushBytes(std::span<const u8> bytes) {
178 REX_ASSERT(!bytes.empty(), "PushBytes needs at least 1 byte");
179 const u32 address = Allocate(static_cast<u32>(bytes.size()));
180 u8* host = rex::memory::GuestPtr<u8*>(base_, address);
181 std::memcpy(host, bytes.data(), bytes.size());
182 return memory::MappedPtr<u8>(host, address);
183 }
184
185 PPCContext& GetContext() const { return ctx_; }
186
190 u8* GetBase() const { return base_; }
191
192 private:
193 explicit StackFrame(BoundThread bound) : StackFrame(*bound.ctx, bound.base) {}
194
195 u32 Allocate(u32 size) {
196 const u32 new_r1 = ctx_.r1.u32 - detail::stack_align(size);
197 stack_bounds_check(ctx_, base_, new_r1);
198 ctx_.r1.u32 = new_r1;
199 return new_r1;
200 }
201
202 PPCContext& ctx_;
203 u8* base_;
204 u32 saved_r1_;
205 StackFrame* previous_;
206};
207
208} // namespace rex::ppc
#define REX_ASSERT(cond,...)
Definition assert.h:84
Definition utility.h:37
Definition mapped_ptr.h:29
Definition stack.h:81
memory::MappedPtr< char > PushString(std::string_view text)
Definition stack.h:150
memory::MappedPtr< T > Push(const T &value)
Definition stack.h:129
StackFrame(PPCContext &ctx, u8 *base)
Definition stack.h:92
StackFrame()
Definition stack.h:84
memory::MappedPtr< be_u16 > PushWideString(std::u16string_view text)
Definition stack.h:163
~StackFrame()
Definition stack.h:98
u8 * GetBase() const
Definition stack.h:190
memory::MappedPtr< u8 > PushBytes(std::span< const u8 > bytes)
Definition stack.h:177
memory::MappedPtr< T > Push()
Definition stack.h:111
T GuestPtr(u8 *base, u32 guest_address) noexcept
Definition mapped_ptr.h:393
Definition title_runtime.h:33
REX_PPC_API BoundThread GetBoundThread()
void stack_bounds_check(PPCContext &ctx, u8 *base, u32 new_r1)
Definition call.h:748
constexpr T byte_swap(T value) noexcept
Definition types.h:52
Platform flags, packing macros and export markers.
#define REX_PPC_API
Definition platform.h:194
Definition context.h:387
PPCRegister r1
Definition context.h:396
Definition context.h:719
Integer types, byte order wrappers and size literals.
Move only function, const propagating pointer and scope guard.