CommonLibVR
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...>) requires(std::invocable<const value_type&, Args...>)
271 {
272 return REL::invoke(get(), std::forward<Args>(a_args)...);
273 }
274
275 [[nodiscard]] constexpr std::uintptr_t address() const noexcept { return _impl; }
276 [[nodiscard]] std::size_t offset() const { return _impl - base(); }
277
278 [[nodiscard]] value_type get() const
279 noexcept(std::is_nothrow_copy_constructible_v<value_type>)
280 {
281 assert(_impl != 0);
282 return stl::unrestricted_cast<value_type>(_impl);
283 }
284
285 template <std::ptrdiff_t O = 0>
286 void replace_func(const std::size_t a_count, const std::uintptr_t a_dst) requires(std::same_as<value_type, std::uintptr_t>)
287 {
288#pragma pack(push, 1)
289 struct Assembly
290 {
291 std::uint8_t jmp;
292 std::uint8_t modrm;
293 std::int32_t disp;
294 std::uint64_t addr;
295 };
296 static_assert(sizeof(Assembly) == 0xE);
297#pragma pack(pop)
298
299 Assembly assembly{
300 .jmp = static_cast<std::uint8_t>(0xFF),
301 .modrm = static_cast<std::uint8_t>(0x25),
302 .disp = static_cast<std::int32_t>(0),
303 .addr = static_cast<std::uint64_t>(a_dst),
304 };
305
306 safe_fill(address() + O, INT3, a_count);
307 safe_write(address() + O, &assembly, sizeof(assembly));
308 }
309
310 template <std::ptrdiff_t O = 0, class F>
311 void replace_func(const std::size_t a_count, const F a_dst) requires(std::same_as<value_type, std::uintptr_t>)
312 {
313 replace_func<O>(a_count, stl::unrestricted_cast<std::uintptr_t>(a_dst));
314 }
315
316 template <std::integral U>
317 void write(const U& a_data) requires(std::same_as<value_type, std::uintptr_t>)
318 {
319 safe_write(address(), std::addressof(a_data), sizeof(T));
320 }
321
322 template <class U>
323 void write(const std::span<U> a_data) requires(std::same_as<value_type, std::uintptr_t>)
324 {
325 safe_write(address(), a_data.data(), a_data.size_bytes());
326 }
327
328 template <std::size_t N>
329 std::uintptr_t write_branch(const std::uintptr_t a_dst) requires(std::same_as<value_type, std::uintptr_t>)
330 {
331 return SKSE::GetTrampoline().write_branch<N>(address(), a_dst);
332 }
333
334 template <std::size_t N, class F>
335 std::uintptr_t write_branch(const F a_dst) requires(std::same_as<value_type, std::uintptr_t>)
336 {
337 return SKSE::GetTrampoline().write_branch<N>(address(), stl::unrestricted_cast<std::uintptr_t>(a_dst));
338 }
339
340 template <std::size_t N>
341 std::uintptr_t write_call(const std::uintptr_t a_dst) requires(std::same_as<value_type, std::uintptr_t>)
342 {
343 return SKSE::GetTrampoline().write_call<N>(address(), a_dst);
344 }
345
346 template <std::size_t N, class F>
347 std::uintptr_t write_call(const F a_dst) requires(std::same_as<value_type, std::uintptr_t>)
348 {
349 return SKSE::GetTrampoline().write_call<N>(address(), stl::unrestricted_cast<std::uintptr_t>(a_dst));
350 }
351
352 void write_fill(const std::uint8_t a_value, const std::size_t a_count) requires(std::same_as<value_type, std::uintptr_t>)
353 {
354 safe_fill(address(), a_value, a_count);
355 }
356
357 template <class U = value_type>
358 std::uintptr_t write_vfunc(const std::size_t a_idx, const std::uintptr_t a_newFunc) requires(std::same_as<U, std::uintptr_t>)
359 {
360 const auto addr = address() + (sizeof(void*) * a_idx);
361 const auto result = *reinterpret_cast<std::uintptr_t*>(addr);
362 safe_write(addr, a_newFunc);
363 return result;
364 }
365
366 template <class F>
367 std::uintptr_t write_vfunc(const std::size_t a_idx, const F a_newFunc) requires(std::same_as<value_type, std::uintptr_t>)
368 {
369 return write_vfunc(a_idx, stl::unrestricted_cast<std::uintptr_t>(a_newFunc));
370 }
371
372 private:
373 // clang-format off
374 [[nodiscard]] static std::uintptr_t base() { return Module::get().base(); }
375 // clang-format on
376
377 std::uintptr_t _impl{ 0 };
378 };
379}
380
381#undef REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE
382#undef REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE_HELPER
383#undef REL_MAKE_MEMBER_FUNCTION_NON_POD_TYPE_HELPER_IMPL
384
385#undef REL_MAKE_MEMBER_FUNCTION_POD_TYPE
386#undef REL_MAKE_MEMBER_FUNCTION_POD_TYPE_HELPER
387#undef REL_MAKE_MEMBER_FUNCTION_POD_TYPE_HELPER_IMPL
Definition ID.h:427
std::uintptr_t address() const
Definition ID.h:441
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:335
std::uintptr_t write_branch(const std::uintptr_t a_dst)
Definition Relocation.h:329
constexpr std::uintptr_t address() const noexcept
Definition Relocation.h:275
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:286
value_type get() const noexcept(std::is_nothrow_copy_constructible_v< value_type >)
Definition Relocation.h:278
std::uintptr_t write_vfunc(const std::size_t a_idx, const F a_newFunc)
Definition Relocation.h:367
void replace_func(const std::size_t a_count, const F a_dst)
Definition Relocation.h:311
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:358
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:317
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:341
constexpr Relocation() noexcept=default
void write(const std::span< U > a_data)
Definition Relocation.h:323
std::uintptr_t write_call(const F a_dst)
Definition Relocation.h:347
void write_fill(const std::uint8_t a_value, const std::size_t a_count)
Definition Relocation.h:352
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:276
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:9
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