CommonLibSSE (powerof3)
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 
63 namespace REL
64 {
65  namespace detail
66  {
67  template <class>
69 
73  REL_MAKE_MEMBER_FUNCTION_POD_TYPE(const volatile);
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>
121  struct is_x64_pod<
122  T,
123  std::enable_if_t<
124  std::is_union_v<T>>> :
125  std::false_type
126  {};
127 
128  template <class T>
129  struct is_x64_pod<
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 
152  using func_t = member_function_non_pod_type_t<F>;
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:
212  using value_type =
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:351
std::uintptr_t address() const
Definition: ID.h:365
static Module & get()
Definition: Module.h:54
std::uintptr_t base() const noexcept
Definition: Module.h:60
Definition: Offset.h:8
std::uintptr_t address() const
Definition: Offset.h:22
Definition: Relocation.h:210
void write(const std::span< U > a_data) requires(std
Definition: Relocation.h:323
constexpr std::uintptr_t address() const noexcept
Definition: Relocation.h:275
std::uintptr_t write_branch(const F a_dst) requires(std
Definition: Relocation.h:335
auto operator->() const noexcept requires(std
Definition: Relocation.h:262
std::uintptr_t write_call(const F a_dst) requires(std
Definition: Relocation.h:347
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
constexpr Relocation & operator=(std::uintptr_t a_address) noexcept
Definition: Relocation.h:236
std::invoke_result_t< const value_type &, Args... > operator()(Args &&... a_args) const noexcept(std::is_nothrow_invocable_v< const value_type &, Args... >) requires(std
Definition: Relocation.h:269
std::uintptr_t write_vfunc(const std::size_t a_idx, const std::uintptr_t a_newFunc) requires(std
Definition: Relocation.h:358
std::uintptr_t write_branch(const std::uintptr_t a_dst) requires(std
Definition: Relocation.h:329
std::uintptr_t write_call(const std::uintptr_t a_dst) requires(std
Definition: Relocation.h:341
value_type get() const noexcept(std::is_nothrow_copy_constructible_v< value_type >)
Definition: Relocation.h:278
Relocation(Offset a_offset)
Definition: Relocation.h:224
Relocation(ID a_id, std::ptrdiff_t a_offset)
Definition: Relocation.h:232
Relocation & operator=(Offset a_offset)
Definition: Relocation.h:242
void write_fill(const std::uint8_t a_value, const std::size_t a_count) requires(std
Definition: Relocation.h:352
constexpr Relocation() noexcept=default
void write(const U &a_data) requires(std
Definition: Relocation.h:317
Relocation(ID a_id)
Definition: Relocation.h:228
void replace_func(const std::size_t a_count, const F a_dst) requires(std
Definition: Relocation.h:311
void replace_func(const std::size_t a_count, const std::uintptr_t a_dst) requires(std
Definition: Relocation.h:286
Relocation & operator=(ID a_id)
Definition: Relocation.h:248
std::uintptr_t write_vfunc(const std::size_t a_idx, const F a_newFunc) requires(std
Definition: Relocation.h:367
std::size_t offset() const
Definition: Relocation.h:276
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
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
std::invoke_result_t< F, Args... > invoke(F &&a_func, Args &&... a_args) noexcept(std::is_nothrow_invocable_v< F, Args... >) requires(std
Definition: Relocation.h:175
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
requires(std::invocable< std::remove_reference_t< EF >>) class scope_exit
Definition: PCH.h:153
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