3 #if defined(SKSE_SUPPORT_XBYAK)
14 [[nodiscard]] constexpr std::size_t
roundup(std::size_t a_number, std::size_t a_multiple) noexcept
16 if (a_multiple == 0) {
20 const auto remainder = a_number % a_multiple;
21 return remainder == 0 ?
23 a_number + a_multiple - remainder;
26 [[nodiscard]] constexpr std::size_t
rounddown(std::size_t a_number, std::size_t a_multiple) noexcept
28 if (a_multiple == 0) {
32 const auto remainder = a_number % a_multiple;
33 return remainder == 0 ?
42 using deleter_type = std::function<void(
void* a_mem, std::size_t a_size)>;
59 if (
this != std::addressof(a_rhs)) {
60 move_from(std::move(a_rhs));
65 void create(std::size_t a_size) {
return create(a_size,
nullptr); }
67 void create(std::size_t a_size,
void* a_module)
75 a_module = text.
pointer<std::byte>() + text.size();
78 auto mem = do_create(a_size,
reinterpret_cast<std::uintptr_t
>(a_module));
84 [](
void* a_mem, std::size_t) {
93 auto trampoline =
static_cast<std::byte*
>(a_trampoline);
95 constexpr
auto INT3 =
static_cast<int>(0xCC);
96 std::memset(trampoline,
INT3, a_size);
101 _deleter = std::move(a_deleter);
111 auto result = do_allocate(a_size);
116 #ifdef SKSE_SUPPORT_XBYAK
117 [[nodiscard]]
void*
allocate(Xbyak::CodeGenerator& a_code);
123 return static_cast<T*
>(
allocate(
sizeof(T)));
126 [[nodiscard]] constexpr std::size_t
empty() const noexcept {
return _capacity == 0; }
127 [[nodiscard]] constexpr std::size_t
capacity() const noexcept {
return _capacity; }
128 [[nodiscard]] constexpr std::size_t
allocated_size() const noexcept {
return _size; }
129 [[nodiscard]] constexpr std::size_t
free_size() const noexcept {
return _capacity - _size; }
131 template <std::
size_t N>
132 std::uintptr_t
write_branch(std::uintptr_t a_src, std::uintptr_t a_dst)
134 std::uint8_t data = 0;
135 if constexpr (N == 5) {
139 }
else if constexpr (N == 6) {
144 static_assert(
false && N,
"invalid branch size");
147 return write_branch<N>(a_src, a_dst, data);
150 template <std::
size_t N,
class F>
153 return write_branch<N>(a_src, stl::unrestricted_cast<std::uintptr_t>(a_dst));
156 template <std::
size_t N>
157 std::uintptr_t
write_call(std::uintptr_t a_src, std::uintptr_t a_dst)
159 std::uint8_t data = 0;
160 if constexpr (N == 5) {
164 }
else if constexpr (N == 6) {
169 static_assert(
false && N,
"invalid call size");
172 return write_branch<N>(a_src, a_dst, data);
175 template <std::
size_t N,
class F>
178 return write_call<N>(a_src, stl::unrestricted_cast<std::uintptr_t>(a_dst));
182 [[nodiscard]]
void* do_create(std::size_t a_size, std::uintptr_t a_address);
184 [[nodiscard]]
void* do_allocate(std::size_t a_size)
190 auto mem = _data + _size;
196 void write_5branch(std::uintptr_t a_src, std::uintptr_t a_dst, std::uint8_t a_opcode)
198 #pragma pack(push, 1)
205 static_assert(offsetof(SrcAssembly, opcode) == 0x0);
206 static_assert(offsetof(SrcAssembly, disp) == 0x1);
207 static_assert(
sizeof(SrcAssembly) == 0x5);
211 struct TrampolineAssembly
219 static_assert(offsetof(TrampolineAssembly, jmp) == 0x0);
220 static_assert(offsetof(TrampolineAssembly, modrm) == 0x1);
221 static_assert(offsetof(TrampolineAssembly, disp) == 0x2);
222 static_assert(offsetof(TrampolineAssembly, addr) == 0x6);
223 static_assert(
sizeof(TrampolineAssembly) == 0xE);
226 TrampolineAssembly* mem =
nullptr;
227 if (
const auto it = _5branches.find(a_dst); it != _5branches.end()) {
228 mem =
reinterpret_cast<TrampolineAssembly*
>(it->second);
230 mem = allocate<TrampolineAssembly>();
231 _5branches.emplace(a_dst,
reinterpret_cast<std::byte*
>(mem));
235 reinterpret_cast<const std::byte*
>(mem) -
236 reinterpret_cast<const std::byte*
>(a_src +
sizeof(SrcAssembly));
237 if (!in_range(disp)) {
241 SrcAssembly assembly;
242 assembly.opcode = a_opcode;
243 assembly.disp =
static_cast<std::int32_t
>(disp);
246 mem->jmp =
static_cast<std::uint8_t
>(0xFF);
247 mem->modrm =
static_cast<std::uint8_t
>(0x25);
248 mem->disp =
static_cast<std::int32_t
>(0);
249 mem->addr =
static_cast<std::uint64_t
>(a_dst);
252 void write_6branch(std::uintptr_t a_src, std::uintptr_t a_dst, std::uint8_t a_modrm)
254 #pragma pack(push, 1)
262 static_assert(offsetof(Assembly, opcode) == 0x0);
263 static_assert(offsetof(Assembly, modrm) == 0x1);
264 static_assert(offsetof(Assembly, disp) == 0x2);
265 static_assert(
sizeof(Assembly) == 0x6);
268 std::uintptr_t* mem =
nullptr;
269 if (
const auto it = _6branches.find(a_dst); it != _6branches.end()) {
270 mem =
reinterpret_cast<std::uintptr_t*
>(it->second);
272 mem = allocate<std::uintptr_t>();
273 _6branches.emplace(a_dst,
reinterpret_cast<std::byte*
>(mem));
277 reinterpret_cast<const std::byte*
>(mem) -
278 reinterpret_cast<const std::byte*
>(a_src +
sizeof(Assembly));
279 if (!in_range(disp)) {
284 assembly.opcode =
static_cast<std::uint8_t
>(0xFF);
285 assembly.modrm = a_modrm;
286 assembly.disp =
static_cast<std::int32_t
>(disp);
292 template <std::
size_t N>
293 [[nodiscard]] std::uintptr_t
write_branch(std::uintptr_t a_src, std::uintptr_t a_dst, std::uint8_t a_data)
295 const auto disp =
reinterpret_cast<std::int32_t*
>(a_src + N - 4);
296 const auto nextOp = a_src + N;
297 const auto func = nextOp + *disp;
299 if constexpr (N == 5) {
300 write_5branch(a_src, a_dst, a_data);
301 }
else if constexpr (N == 6) {
302 write_6branch(a_src, a_dst, a_data);
304 static_assert(
false && N,
"invalid branch size");
312 _5branches = std::move(a_rhs._5branches);
313 _6branches = std::move(a_rhs._6branches);
314 _name = std::move(a_rhs._name);
316 _deleter = std::move(a_rhs._deleter);
319 a_rhs._data =
nullptr;
321 _capacity = a_rhs._capacity;
328 void log_stats()
const;
330 [[nodiscard]]
bool in_range(std::ptrdiff_t a_disp)
const
332 constexpr
auto min = std::numeric_limits<std::int32_t>::min();
333 constexpr
auto max = std::numeric_limits<std::int32_t>::max();
335 return min <= a_disp && a_disp <= max;
340 if (_data && _deleter) {
341 _deleter(_data, _capacity);
351 std::map<std::uintptr_t, std::byte*> _5branches;
352 std::map<std::uintptr_t, std::byte*> _6branches;
355 std::byte* _data{
nullptr };
356 std::size_t _capacity{ 0 };
357 std::size_t _size{ 0 };
static Module & get()
Definition: Relocation.h:421
Segment segment(Segment::Name a_segment) const noexcept
Definition: Relocation.h:431
void * pointer() const noexcept
Definition: Relocation.h:404
@ textx
Definition: Relocation.h:381
Definition: Trampoline.h:40
Trampoline & operator=(Trampoline &&a_rhs)
Definition: Trampoline.h:57
std::uintptr_t write_call(std::uintptr_t a_src, std::uintptr_t a_dst)
Definition: Trampoline.h:157
constexpr std::size_t allocated_size() const noexcept
Definition: Trampoline.h:128
std::function< void(void *a_mem, std::size_t a_size)> deleter_type
Definition: Trampoline.h:42
T * allocate()
Definition: Trampoline.h:121
void * allocate(std::size_t a_size)
Definition: Trampoline.h:109
~Trampoline()
Definition: Trampoline.h:53
std::uintptr_t write_branch(std::uintptr_t a_src, std::uintptr_t a_dst)
Definition: Trampoline.h:132
Trampoline(std::string_view a_name)
Definition: Trampoline.h:49
Trampoline(const Trampoline &)=delete
std::uintptr_t write_branch(std::uintptr_t a_src, F a_dst)
Definition: Trampoline.h:151
void create(std::size_t a_size)
Definition: Trampoline.h:65
Trampoline(Trampoline &&a_rhs)
Definition: Trampoline.h:47
void set_trampoline(void *a_trampoline, std::size_t a_size)
Definition: Trampoline.h:89
constexpr std::size_t capacity() const noexcept
Definition: Trampoline.h:127
std::uintptr_t write_call(std::uintptr_t a_src, F a_dst)
Definition: Trampoline.h:176
constexpr std::size_t empty() const noexcept
Definition: Trampoline.h:126
constexpr std::size_t free_size() const noexcept
Definition: Trampoline.h:129
void create(std::size_t a_size, void *a_module)
Definition: Trampoline.h:67
Trampoline & operator=(const Trampoline &)=delete
void set_trampoline(void *a_trampoline, std::size_t a_size, deleter_type a_deleter)
Definition: Trampoline.h:91
constexpr std::uint8_t INT3
Definition: Relocation.h:198
void safe_write(std::uintptr_t a_dst, const void *a_src, std::size_t a_count)
Definition: Relocation.h:218
constexpr auto MEM_RELEASE
Definition: WinAPI.h:10
bool VirtualFree(void *a_address, std::size_t a_size, std::uint32_t a_freeType) noexcept
constexpr std::size_t rounddown(std::size_t a_number, std::size_t a_multiple) noexcept
Definition: Trampoline.h:26
constexpr std::size_t roundup(std::size_t a_number, std::size_t a_multiple) noexcept
Definition: Trampoline.h:14
string(const CharT(&)[N]) -> string< CharT, N - 1 >
void report_and_fail(std::string_view a_msg, std::source_location a_loc=std::source_location::current())
Definition: PCH.h:579