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));
181 [[nodiscard]] std::int32_t
make_disp(std::uintptr_t a_rip, std::uintptr_t a_dst)
183 #pragma pack(push, 1)
186 struct TrampolineAssembly
194 static_assert(offsetof(TrampolineAssembly, jmp) == 0x0);
195 static_assert(offsetof(TrampolineAssembly, modrm) == 0x1);
196 static_assert(offsetof(TrampolineAssembly, disp) == 0x2);
197 static_assert(offsetof(TrampolineAssembly, addr) == 0x6);
198 static_assert(
sizeof(TrampolineAssembly) == 0xE);
201 std::ptrdiff_t disp =
202 reinterpret_cast<const std::byte*
>(a_dst) -
203 reinterpret_cast<const std::byte*
>(a_rip);
205 if (!std::in_range<std::int32_t>(disp)) {
206 TrampolineAssembly* mem =
nullptr;
207 if (
const auto it = _5branches.find(a_dst); it != _5branches.end()) {
208 mem =
reinterpret_cast<TrampolineAssembly*
>(it->second);
210 mem = allocate<TrampolineAssembly>();
211 _5branches.emplace(a_dst,
reinterpret_cast<std::byte*
>(mem));
215 reinterpret_cast<const std::byte*
>(mem) -
216 reinterpret_cast<const std::byte*
>(a_rip);
218 if (!std::in_range<std::int32_t>(disp)) {
223 .jmp =
static_cast<std::uint8_t
>(0xFF),
224 .modrm =
static_cast<std::uint8_t
>(0x25),
225 .disp =
static_cast<std::int32_t
>(0),
226 .addr =
static_cast<std::uint64_t
>(a_dst),
230 return static_cast<std::int32_t
>(disp);
234 [[nodiscard]]
void* do_create(std::size_t a_size, std::uintptr_t a_address);
236 [[nodiscard]]
void* do_allocate(std::size_t a_size)
242 auto mem = _data + _size;
248 void write_5branch(std::uintptr_t a_src, std::uintptr_t a_dst, std::uint8_t a_opcode)
250 #pragma pack(push, 1)
257 static_assert(offsetof(SrcAssembly, opcode) == 0x0);
258 static_assert(offsetof(SrcAssembly, disp) == 0x1);
259 static_assert(
sizeof(SrcAssembly) == 0x5);
262 const std::int32_t disp =
make_disp(a_src +
sizeof(SrcAssembly), a_dst);
264 SrcAssembly assembly{
271 void write_6branch(std::uintptr_t a_src, std::uintptr_t a_dst, std::uint8_t a_modrm)
273 #pragma pack(push, 1)
281 static_assert(offsetof(Assembly, opcode) == 0x0);
282 static_assert(offsetof(Assembly, modrm) == 0x1);
283 static_assert(offsetof(Assembly, disp) == 0x2);
284 static_assert(
sizeof(Assembly) == 0x6);
287 std::uintptr_t* mem =
nullptr;
288 if (
const auto it = _6branches.find(a_dst); it != _6branches.end()) {
289 mem =
reinterpret_cast<std::uintptr_t*
>(it->second);
291 mem = allocate<std::uintptr_t>();
292 _6branches.emplace(a_dst,
reinterpret_cast<std::byte*
>(mem));
296 reinterpret_cast<const std::byte*
>(mem) -
297 reinterpret_cast<const std::byte*
>(a_src +
sizeof(Assembly));
298 if (!std::in_range<std::int32_t>(disp)) {
303 .opcode =
static_cast<std::uint8_t
>(0xFF),
305 .disp =
static_cast<std::int32_t
>(disp),
312 template <std::
size_t N>
313 [[nodiscard]] std::uintptr_t
write_branch(std::uintptr_t a_src, std::uintptr_t a_dst, std::uint8_t a_data)
315 const auto disp =
reinterpret_cast<std::int32_t*
>(a_src + N - 4);
316 const auto nextOp = a_src + N;
317 const auto func = nextOp + *disp;
319 if constexpr (N == 5) {
320 write_5branch(a_src, a_dst, a_data);
321 }
else if constexpr (N == 6) {
322 write_6branch(a_src, a_dst, a_data);
324 static_assert(
false && N,
"invalid branch size");
332 _5branches = std::move(a_rhs._5branches);
333 _6branches = std::move(a_rhs._6branches);
334 _name = std::move(a_rhs._name);
336 _deleter = std::move(a_rhs._deleter);
339 a_rhs._data =
nullptr;
341 _capacity = a_rhs._capacity;
348 void log_stats()
const;
352 if (_data && _deleter) {
353 _deleter(_data, _capacity);
363 std::map<std::uintptr_t, std::byte*> _5branches;
364 std::map<std::uintptr_t, std::byte*> _6branches;
367 std::byte* _data{
nullptr };
368 std::size_t _capacity{ 0 };
369 std::size_t _size{ 0 };
constexpr Segment segment(Segment::Name a_segment) const noexcept
Definition: Relocation.h:442
static Module & get()
Definition: Relocation.h:518
void * pointer() const noexcept
Definition: Relocation.h:413
@ textx
Definition: Relocation.h:390
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::int32_t make_disp(std::uintptr_t a_rip, std::uintptr_t a_dst)
Definition: Trampoline.h:181
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:621