CommonLibSSE (powerof3)
Loading...
Searching...
No Matches
Relocation.h
Go to the documentation of this file.
1#pragma once
2
3#include "REL/Module.h"
4
5#include "SKSE/Trampoline.h"
6
7#define REL_MAKE_MEMBER_FUNCTION_POD_TYPE_HELPER_IMPL(a_nopropQual, a_propQual, ...) \
8 template < \
9 class R, \
10 class Cls, \
11 class... Args> \
12 struct member_function_pod_type<R (Cls::*)(Args...) __VA_ARGS__ a_nopropQual a_propQual> \
13 { \
14 using type = R(__VA_ARGS__ Cls*, Args...) a_propQual; \
15 }; \
16 \
17 template < \
18 class R, \
19 class Cls, \
20 class... Args> \
21 struct member_function_pod_type<R (Cls::*)(Args..., ...) __VA_ARGS__ a_nopropQual a_propQual> \
22 { \
23 using type = R(__VA_ARGS__ Cls*, Args..., ...) a_propQual; \
24 };
25
26#define REL_MAKE_MEMBER_FUNCTION_POD_TYPE_HELPER(a_qualifer, ...) \
27 REL_MAKE_MEMBER_FUNCTION_POD_TYPE_HELPER_IMPL(a_qualifer, , ##__VA_ARGS__) \
28 REL_MAKE_MEMBER_FUNCTION_POD_TYPE_HELPER_IMPL(a_qualifer, noexcept, ##__VA_ARGS__)
29
30#define REL_MAKE_MEMBER_FUNCTION_POD_TYPE(...) \
31 REL_MAKE_MEMBER_FUNCTION_POD_TYPE_HELPER(, __VA_ARGS__) \
32 REL_MAKE_MEMBER_FUNCTION_POD_TYPE_HELPER(&, ##__VA_ARGS__) \
33 REL_MAKE_MEMBER_FUNCTION_POD_TYPE_HELPER(&&, ##__VA_ARGS__)
34
35#define REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE_HELPER_IMPL(a_nopropQual, a_propQual, ...) \
36 template < \
37 class R, \
38 class Cls, \
39 class... Args> \
40 struct member_function_non_pod_type<R (Cls::*)(Args...) __VA_ARGS__ a_nopropQual a_propQual> \
41 { \
42 using type = R&(__VA_ARGS__ Cls*, void*, Args...)a_propQual; \
43 }; \
44 \
45 template < \
46 class R, \
47 class Cls, \
48 class... Args> \
49 struct member_function_non_pod_type<R (Cls::*)(Args..., ...) __VA_ARGS__ a_nopropQual a_propQual> \
50 { \
51 using type = R&(__VA_ARGS__ Cls*, void*, Args..., ...)a_propQual; \
52 };
53
54#define REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE_HELPER(a_qualifer, ...) \
55 REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE_HELPER_IMPL(a_qualifer, , ##__VA_ARGS__) \
56 REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE_HELPER_IMPL(a_qualifer, noexcept, ##__VA_ARGS__)
57
58#define REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE(...) \
59 REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE_HELPER(, __VA_ARGS__) \
60 REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE_HELPER(&, ##__VA_ARGS__) \
61 REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE_HELPER(&&, ##__VA_ARGS__)
62
63namespace REL
64{
65 namespace detail
66 {
67 template <class>
69
74
75 template <class F>
77
78 template <class>
80
85
86 template <class F>
88
89 // https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention
90
91 template <class T>
93 std::disjunction<
94 std::bool_constant<sizeof(T) == 1>,
95 std::bool_constant<sizeof(T) == 2>,
96 std::bool_constant<sizeof(T) == 4>,
97 std::bool_constant<sizeof(T) == 8>>
98 {};
99
100 template <class T>
102 std::conjunction<
103 std::is_trivially_constructible<T>,
104 std::is_trivially_destructible<T>,
105 std::is_trivially_copy_assignable<T>,
106 std::negation<
107 std::is_polymorphic<T>>>
108 {};
109
110 template <class T>
112 std::is_standard_layout<T>
113 {};
114
115 template <class T, class = void>
116 struct is_x64_pod :
117 std::true_type
118 {};
119
120 template <class T>
122 T,
123 std::enable_if_t<
124 std::is_union_v<T>>> :
125 std::false_type
126 {};
127
128 template <class T>
130 T,
131 std::enable_if_t<
132 std::is_class_v<T>>> :
133 std::conjunction<
134 meets_length_req<T>,
135 meets_function_req<T>,
136 meets_member_req<T>>
137 {};
138
139 template <class T>
140 inline constexpr bool is_x64_pod_v = is_x64_pod<T>::value;
141
142 template <
143 class F,
144 class First,
145 class... Rest>
146 decltype(auto) invoke_member_function_non_pod(F&& a_func, First&& a_first, Rest&&... a_rest) //
147 noexcept(std::is_nothrow_invocable_v<F, First, Rest...>)
148 {
149 using result_t = std::invoke_result_t<F, First, Rest...>;
150 std::aligned_storage_t<sizeof(result_t), alignof(result_t)> result;
151
153 auto func = stl::unrestricted_cast<func_t*>(std::forward<F>(a_func));
154
155 return func(std::forward<First>(a_first), std::addressof(result), std::forward<Rest>(a_rest)...);
156 }
157 }
158
159 inline constexpr std::uint8_t NOP = 0x90;
160 inline constexpr std::uint8_t NOP2[] = { 0x66, 0x90 };
161 inline constexpr std::uint8_t NOP3[] = { 0x0F, 0x1F, 0x00 };
162 inline constexpr std::uint8_t NOP4[] = { 0x0F, 0x1F, 0x40, 0x00 };
163 inline constexpr std::uint8_t NOP5[] = { 0x0F, 0x1F, 0x44, 0x00, 0x00 };
164 inline constexpr std::uint8_t NOP6[] = { 0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00 };
165 inline constexpr std::uint8_t NOP7[] = { 0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00 };
166 inline constexpr std::uint8_t NOP8[] = { 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 };
167 inline constexpr std::uint8_t NOP9[] = { 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 };
168
169 inline constexpr std::uint8_t JMP8 = 0xEB;
170 inline constexpr std::uint8_t JMP32 = 0xE9;
171 inline constexpr std::uint8_t RET = 0xC3;
172 inline constexpr std::uint8_t INT3 = 0xCC;
173
174 template <class F, class... Args>
175 std::invoke_result_t<F, Args...> invoke(F&& a_func, Args&&... a_args) //
176 noexcept(std::is_nothrow_invocable_v<F, Args...>) //
177 requires(std::invocable<F, Args...>)
178 {
179 if constexpr (std::is_member_function_pointer_v<std::decay_t<F>>) {
180 if constexpr (detail::is_x64_pod_v<std::invoke_result_t<F, Args...>>) { // member functions == free functions in x64
182 auto func = stl::unrestricted_cast<func_t*>(std::forward<F>(a_func));
183 return func(std::forward<Args>(a_args)...);
184 } else { // shift args to insert result
185 return detail::invoke_member_function_non_pod(std::forward<F>(a_func), std::forward<Args>(a_args)...);
186 }
187 } else {
188 return std::forward<F>(a_func)(std::forward<Args>(a_args)...);
189 }
190 }
191
192 void safe_write(std::uintptr_t a_dst, const void* a_src, std::size_t a_count);
193
194 template <std::integral T>
195 void safe_write(std::uintptr_t a_dst, const T& a_data)
196 {
197 safe_write(a_dst, std::addressof(a_data), sizeof(T));
198 }
199
200 template <class T>
201 void safe_write(std::uintptr_t a_dst, std::span<T> a_data)
202 {
203 safe_write(a_dst, a_data.data(), a_data.size_bytes());
204 }
205
206 void safe_fill(std::uintptr_t a_dst, std::uint8_t a_value, std::size_t a_count);
207
208 template <class T = std::uintptr_t>
210 {
211 public:
213 std::conditional_t<
214 std::is_member_pointer_v<T> || std::is_function_v<std::remove_pointer_t<T>>,
215 std::decay_t<T>,
216 T>;
217
218 constexpr Relocation() noexcept = default;
219
220 explicit constexpr Relocation(std::uintptr_t a_address) noexcept :
221 _impl{ a_address }
222 {}
223
224 explicit Relocation(Offset a_offset) :
225 _impl{ a_offset.address() }
226 {}
227
228 explicit Relocation(ID a_id) :
229 _impl{ a_id.address() }
230 {}
231
232 explicit Relocation(ID a_id, std::ptrdiff_t a_offset) :
233 _impl{ a_id.address() + a_offset }
234 {}
235
236 constexpr Relocation& operator=(std::uintptr_t a_address) noexcept
237 {
238 _impl = a_address;
239 return *this;
240 }
241
243 {
244 _impl = a_offset.address();
245 return *this;
246 }
247
249 {
250 _impl = a_id.address();
251 return *this;
252 }
253
254 template <class U = value_type>
255 [[nodiscard]] decltype(auto) operator*() const noexcept
256 requires(std::is_pointer_v<U>)
257 {
258 return *get();
259 }
260
261 template <class U = value_type>
262 [[nodiscard]] auto operator->() const noexcept
263 requires(std::is_pointer_v<U>)
264 {
265 return get();
266 }
267
268 template <class... Args>
269 std::invoke_result_t<const value_type&, Args...> operator()(Args&&... a_args) const
270 noexcept(std::is_nothrow_invocable_v<const value_type&, Args...>)
271 requires(std::invocable<const value_type&, Args...>)
272 {
273 return REL::invoke(get(), std::forward<Args>(a_args)...);
274 }
275
276 [[nodiscard]] constexpr std::uintptr_t address() const noexcept { return _impl; }
277 [[nodiscard]] std::size_t offset() const { return _impl - base(); }
278
279 [[nodiscard]] value_type get() const
280 noexcept(std::is_nothrow_copy_constructible_v<value_type>)
281 {
282 assert(_impl != 0);
283 return stl::unrestricted_cast<value_type>(_impl);
284 }
285
286 template <std::ptrdiff_t O = 0>
287 void replace_func(const std::size_t a_count, const std::uintptr_t a_dst)
288 requires(std::same_as<value_type, std::uintptr_t>)
289 {
290#pragma pack(push, 1)
291 struct Assembly
292 {
293 std::uint8_t jmp;
294 std::uint8_t modrm;
295 std::int32_t disp;
296 std::uint64_t addr;
297 };
298 static_assert(sizeof(Assembly) == 0xE);
299#pragma pack(pop)
300
301 Assembly assembly{
302 .jmp = static_cast<std::uint8_t>(0xFF),
303 .modrm = static_cast<std::uint8_t>(0x25),
304 .disp = static_cast<std::int32_t>(0),
305 .addr = static_cast<std::uint64_t>(a_dst),
306 };
307
308 safe_fill(address() + O, INT3, a_count);
309 safe_write(address() + O, &assembly, sizeof(assembly));
310 }
311
312 template <std::ptrdiff_t O = 0, class F>
313 void replace_func(const std::size_t a_count, const F a_dst)
314 requires(std::same_as<value_type, std::uintptr_t>)
315 {
316 replace_func<O>(a_count, stl::unrestricted_cast<std::uintptr_t>(a_dst));
317 }
318
319 template <std::integral U>
320 void write(const U& a_data)
321 requires(std::same_as<value_type, std::uintptr_t>)
322 {
323 safe_write(address(), std::addressof(a_data), sizeof(T));
324 }
325
326 template <class U>
327 void write(const std::span<U> a_data)
328 requires(std::same_as<value_type, std::uintptr_t>)
329 {
330 safe_write(address(), a_data.data(), a_data.size_bytes());
331 }
332
333 template <std::size_t N>
334 std::uintptr_t write_branch(const std::uintptr_t a_dst)
335 requires(std::same_as<value_type, std::uintptr_t>)
336 {
337 return SKSE::GetTrampoline().write_branch<N>(address(), a_dst);
338 }
339
340 template <std::size_t N, class F>
341 std::uintptr_t write_branch(const F a_dst)
342 requires(std::same_as<value_type, std::uintptr_t>)
343 {
344 return SKSE::GetTrampoline().write_branch<N>(address(), stl::unrestricted_cast<std::uintptr_t>(a_dst));
345 }
346
347 template <std::size_t N>
348 std::uintptr_t write_call(const std::uintptr_t a_dst)
349 requires(std::same_as<value_type, std::uintptr_t>)
350 {
351 return SKSE::GetTrampoline().write_call<N>(address(), a_dst);
352 }
353
354 template <std::size_t N, class F>
355 std::uintptr_t write_call(const F a_dst)
356 requires(std::same_as<value_type, std::uintptr_t>)
357 {
358 return SKSE::GetTrampoline().write_call<N>(address(), stl::unrestricted_cast<std::uintptr_t>(a_dst));
359 }
360
361 void write_fill(const std::uint8_t a_value, const std::size_t a_count)
362 requires(std::same_as<value_type, std::uintptr_t>)
363 {
364 safe_fill(address(), a_value, a_count);
365 }
366
367 template <class U = value_type>
368 std::uintptr_t write_vfunc(const std::size_t a_idx, const std::uintptr_t a_newFunc)
369 requires(std::same_as<U, std::uintptr_t>)
370 {
371 const auto addr = address() + (sizeof(void*) * a_idx);
372 const auto result = *reinterpret_cast<std::uintptr_t*>(addr);
373 safe_write(addr, a_newFunc);
374 return result;
375 }
376
377 template <class F>
378 std::uintptr_t write_vfunc(const std::size_t a_idx, const F a_newFunc)
379 requires(std::same_as<value_type, std::uintptr_t>)
380 {
381 return write_vfunc(a_idx, stl::unrestricted_cast<std::uintptr_t>(a_newFunc));
382 }
383
384 private:
385 // clang-format off
386 [[nodiscard]] static std::uintptr_t base() { return Module::get().base(); }
387 // clang-format on
388
389 std::uintptr_t _impl{ 0 };
390 };
391}
392
393#undef REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE
394#undef REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE_HELPER
395#undef REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE_HELPER_IMPL
396
397#undef REL_MAKE_MEMBER_FUNCTION_POD_TYPE
398#undef REL_MAKE_MEMBER_FUNCTION_POD_TYPE_HELPER
399#undef REL_MAKE_MEMBER_FUNCTION_POD_TYPE_HELPER_IMPL
Definition ID.h:351
std::uintptr_t address() const
Definition ID.h:365
std::uintptr_t base() const noexcept
Definition Module.h:60
static Module & get()
Definition Module.h:54
Definition Offset.h:8
std::uintptr_t address() const
Definition Offset.h:22
Definition Relocation.h:210
std::uintptr_t write_branch(const F a_dst)
Definition Relocation.h:341
std::uintptr_t write_branch(const std::uintptr_t a_dst)
Definition Relocation.h:334
constexpr std::uintptr_t address() const noexcept
Definition Relocation.h:276
std::conditional_t< std::is_member_pointer_v< T >||std::is_function_v< std::remove_pointer_t< T > >, std::decay_t< T >, T > value_type
Definition Relocation.h:216
Relocation & operator=(ID a_id)
Definition Relocation.h:248
void replace_func(const std::size_t a_count, const std::uintptr_t a_dst)
Definition Relocation.h:287
value_type get() const noexcept(std::is_nothrow_copy_constructible_v< value_type >)
Definition Relocation.h:279
std::uintptr_t write_vfunc(const std::size_t a_idx, const F a_newFunc)
Definition Relocation.h:378
void replace_func(const std::size_t a_count, const F a_dst)
Definition Relocation.h:313
Relocation(Offset a_offset)
Definition Relocation.h:224
std::uintptr_t write_vfunc(const std::size_t a_idx, const std::uintptr_t a_newFunc)
Definition Relocation.h:368
auto operator->() const noexcept
Definition Relocation.h:262
Relocation(ID a_id, std::ptrdiff_t a_offset)
Definition Relocation.h:232
void write(const U &a_data)
Definition Relocation.h:320
constexpr Relocation & operator=(std::uintptr_t a_address) noexcept
Definition Relocation.h:236
std::uintptr_t write_call(const std::uintptr_t a_dst)
Definition Relocation.h:348
constexpr Relocation() noexcept=default
void write(const std::span< U > a_data)
Definition Relocation.h:327
std::uintptr_t write_call(const F a_dst)
Definition Relocation.h:355
void write_fill(const std::uint8_t a_value, const std::size_t a_count)
Definition Relocation.h:361
Relocation(ID a_id)
Definition Relocation.h:228
std::invoke_result_t< const value_type &, Args... > operator()(Args &&... a_args) const noexcept(std::is_nothrow_invocable_v< const value_type &, Args... >)
Definition Relocation.h:269
std::size_t offset() const
Definition Relocation.h:277
Relocation & operator=(Offset a_offset)
Definition Relocation.h:242
std::uintptr_t write_call(std::uintptr_t a_src, std::uintptr_t a_dst)
Definition Trampoline.h:107
std::uintptr_t write_branch(std::uintptr_t a_src, std::uintptr_t a_dst)
Definition Trampoline.h:82
typename member_function_non_pod_type< F >::type member_function_non_pod_type_t
Definition Relocation.h:87
decltype(auto) invoke_member_function_non_pod(F &&a_func, First &&a_first, Rest &&... a_rest) noexcept(std::is_nothrow_invocable_v< F, First, Rest... >)
Definition Relocation.h:146
typename member_function_pod_type< F >::type member_function_pod_type_t
Definition Relocation.h:76
constexpr bool is_x64_pod_v
Definition Relocation.h:140
REL_MAKE_MEMBER_FUNCTION_POD_TYPE()
REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE()
Definition ID.h:6
std::invoke_result_t< F, Args... > invoke(F &&a_func, Args &&... a_args) noexcept(std::is_nothrow_invocable_v< F, Args... >)
Definition Relocation.h:175
constexpr std::uint8_t NOP6[]
Definition Relocation.h:164
constexpr std::uint8_t JMP8
Definition Relocation.h:169
constexpr std::uint8_t NOP
Definition Relocation.h:159
constexpr std::uint8_t NOP4[]
Definition Relocation.h:162
constexpr std::uint8_t INT3
Definition Relocation.h:172
constexpr std::uint8_t NOP8[]
Definition Relocation.h:166
constexpr std::uint8_t NOP2[]
Definition Relocation.h:160
void safe_fill(std::uintptr_t a_dst, std::uint8_t a_value, std::size_t a_count)
void safe_write(std::uintptr_t a_dst, const void *a_src, std::size_t a_count)
constexpr std::uint8_t NOP5[]
Definition Relocation.h:163
constexpr std::uint8_t NOP3[]
Definition Relocation.h:161
constexpr std::uint8_t NOP9[]
Definition Relocation.h:167
constexpr std::uint8_t RET
Definition Relocation.h:171
constexpr std::uint8_t JMP32
Definition Relocation.h:170
constexpr std::uint8_t NOP7[]
Definition Relocation.h:165
Trampoline & GetTrampoline()
Definition EffectArchetypes.h:65
Definition Relocation.h:118
Definition Relocation.h:108
Definition Relocation.h:98
Definition Relocation.h:113
Definition Relocation.h:68