CommonLibSSE (powerof3)
Loading...
Searching...
No Matches
Trampoline.h
Go to the documentation of this file.
1#pragma once
2
3#if defined(SKSE_SUPPORT_XBYAK)
4namespace Xbyak
5{
6 class CodeGenerator;
7}
8#endif
9
10namespace SKSE
11{
13 {
14 public:
15 using deleter_type = std::function<void(void* a_mem, std::size_t a_size)>;
16
17 Trampoline() = default;
18 Trampoline(const Trampoline&) = delete;
19
20 Trampoline(Trampoline&& a_rhs) noexcept { move_from(std::move(a_rhs)); }
21
22 explicit Trampoline(std::string_view a_name) :
23 _name(a_name)
24 {}
25
26 ~Trampoline() { release(); }
27
28 Trampoline& operator=(const Trampoline&) = delete;
29
30 Trampoline& operator=(Trampoline&& a_rhs) noexcept
31 {
32 if (this != std::addressof(a_rhs)) {
33 move_from(std::move(a_rhs));
34 }
35 return *this;
36 }
37
38 void create(std::size_t a_size) { return create(a_size, nullptr); }
39 void create(std::size_t a_size, void* a_module);
40
41 void set_trampoline(void* a_trampoline, std::size_t a_size, deleter_type a_deleter = {})
42 {
43 auto trampoline = static_cast<std::byte*>(a_trampoline);
44 if (trampoline) {
45 constexpr auto INT3 = static_cast<int>(0xCC);
46 std::memset(trampoline, INT3, a_size);
47 }
48
49 release();
50
51 _deleter = std::move(a_deleter);
52 _data = trampoline;
53 _capacity = a_size;
54 _size = 0;
55
56 log_stats();
57 }
58
59 [[nodiscard]] void* allocate(std::size_t a_size)
60 {
61 auto result = do_allocate(a_size);
62 log_stats();
63 return result;
64 }
65
66#ifdef SKSE_SUPPORT_XBYAK
67 [[nodiscard]] void* allocate(Xbyak::CodeGenerator& a_code);
68#endif
69
70 template <class T>
71 [[nodiscard]] T* allocate()
72 {
73 return static_cast<T*>(allocate(sizeof(T)));
74 }
75
76 [[nodiscard]] constexpr std::size_t empty() const noexcept { return _capacity == 0; }
77 [[nodiscard]] constexpr std::size_t capacity() const noexcept { return _capacity; }
78 [[nodiscard]] constexpr std::size_t allocated_size() const noexcept { return _size; }
79 [[nodiscard]] constexpr std::size_t free_size() const noexcept { return _capacity - _size; }
80
81 template <std::size_t N>
82 std::uintptr_t write_branch(std::uintptr_t a_src, std::uintptr_t a_dst)
83 {
84 std::uint8_t data = 0;
85 if constexpr (N == 5) {
86 // E9 cd
87 // JMP rel32
88 data = 0xE9;
89 } else if constexpr (N == 6) {
90 // FF /4
91 // JMP r/m64
92 data = 0x25;
93 } else {
94 static_assert(false && N, "invalid branch size");
95 }
96
97 return write_branch<N>(a_src, a_dst, data);
98 }
99
100 template <std::size_t N, class F>
101 std::uintptr_t write_branch(std::uintptr_t a_src, F a_dst)
102 {
103 return write_branch<N>(a_src, stl::unrestricted_cast<std::uintptr_t>(a_dst));
104 }
105
106 template <std::size_t N>
107 std::uintptr_t write_call(std::uintptr_t a_src, std::uintptr_t a_dst)
108 {
109 std::uint8_t data = 0;
110 if constexpr (N == 5) {
111 // E8 cd
112 // CALL rel32
113 data = 0xE8;
114 } else if constexpr (N == 6) {
115 // FF /2
116 // CALL r/m64
117 data = 0x15;
118 } else {
119 static_assert(false && N, "invalid call size");
120 }
121
122 return write_branch<N>(a_src, a_dst, data);
123 }
124
125 template <std::size_t N, class F>
126 std::uintptr_t write_call(std::uintptr_t a_src, F a_dst)
127 {
128 return write_call<N>(a_src, stl::unrestricted_cast<std::uintptr_t>(a_dst));
129 }
130
131 private:
132 [[nodiscard]] void* do_create(std::size_t a_size, std::uintptr_t a_address);
133 [[nodiscard]] void* do_allocate(std::size_t a_size);
134
135 void write_5branch(std::uintptr_t a_src, std::uintptr_t a_dst, std::uint8_t a_opcode);
136 void write_6branch(std::uintptr_t a_src, std::uintptr_t a_dst, std::uint8_t a_modrm);
137
138 template <std::size_t N>
139 [[nodiscard]] std::uintptr_t write_branch(std::uintptr_t a_src, std::uintptr_t a_dst, std::uint8_t a_data)
140 {
141 const auto isNop = *reinterpret_cast<std::int8_t*>(a_src) == 0x90;
142 const auto disp = reinterpret_cast<std::int32_t*>(a_src + N - 4);
143 const auto nextOp = a_src + N;
144 const auto func = isNop ? 0 : nextOp + *disp;
145
146 if constexpr (N == 5) {
147 write_5branch(a_src, a_dst, a_data);
148 } else if constexpr (N == 6) {
149 write_6branch(a_src, a_dst, a_data);
150 } else {
151 static_assert(false && N, "invalid branch size");
152 }
153
154 return func;
155 }
156
157 void move_from(Trampoline&& a_rhs)
158 {
159 _5branches = std::move(a_rhs._5branches);
160 _6branches = std::move(a_rhs._6branches);
161 _name = std::move(a_rhs._name);
162
163 _deleter = std::move(a_rhs._deleter);
164
165 _data = a_rhs._data;
166 a_rhs._data = nullptr;
167
168 _capacity = a_rhs._capacity;
169 a_rhs._capacity = 0;
170
171 _size = a_rhs._size;
172 a_rhs._size = 0;
173 }
174
175 void log_stats() const;
176
177 [[nodiscard]] bool in_range(std::ptrdiff_t a_disp) const
178 {
179 constexpr auto min = std::numeric_limits<std::int32_t>::min();
180 constexpr auto max = std::numeric_limits<std::int32_t>::max();
181
182 return min <= a_disp && a_disp <= max;
183 }
184
185 void release()
186 {
187 if (_data && _deleter) {
188 _deleter(_data, _capacity);
189 }
190
191 _5branches.clear();
192 _6branches.clear();
193 _data = nullptr;
194 _capacity = 0;
195 _size = 0;
196 }
197
198 std::map<std::uintptr_t, std::byte*> _5branches;
199 std::map<std::uintptr_t, std::byte*> _6branches;
200 std::string _name{ "Default Trampoline"sv };
201 deleter_type _deleter;
202 std::byte* _data{ nullptr };
203 std::size_t _capacity{ 0 };
204 std::size_t _size{ 0 };
205 };
206
208}
Definition Trampoline.h:13
std::uintptr_t write_call(std::uintptr_t a_src, std::uintptr_t a_dst)
Definition Trampoline.h:107
Trampoline()=default
constexpr std::size_t allocated_size() const noexcept
Definition Trampoline.h:78
T * allocate()
Definition Trampoline.h:71
std::function< void(void *a_mem, std::size_t a_size)> deleter_type
Definition Trampoline.h:15
void * allocate(std::size_t a_size)
Definition Trampoline.h:59
~Trampoline()
Definition Trampoline.h:26
std::uintptr_t write_branch(std::uintptr_t a_src, std::uintptr_t a_dst)
Definition Trampoline.h:82
Trampoline(std::string_view a_name)
Definition Trampoline.h:22
Trampoline & operator=(const Trampoline &)=delete
Trampoline & operator=(Trampoline &&a_rhs) noexcept
Definition Trampoline.h:30
Trampoline(const Trampoline &)=delete
void set_trampoline(void *a_trampoline, std::size_t a_size, deleter_type a_deleter={})
Definition Trampoline.h:41
Trampoline(Trampoline &&a_rhs) noexcept
Definition Trampoline.h:20
std::uintptr_t write_branch(std::uintptr_t a_src, F a_dst)
Definition Trampoline.h:101
void create(std::size_t a_size)
Definition Trampoline.h:38
constexpr std::size_t capacity() const noexcept
Definition Trampoline.h:77
std::uintptr_t write_call(std::uintptr_t a_src, F a_dst)
Definition Trampoline.h:126
constexpr std::size_t empty() const noexcept
Definition Trampoline.h:76
constexpr std::size_t free_size() const noexcept
Definition Trampoline.h:79
void create(std::size_t a_size, void *a_module)
NiColor min(const NiColor &a_lhs, const NiColor &a_rhs)
Definition ColorUtil.h:63
NiColor max(const NiColor &a_lhs, const NiColor &a_rhs)
Definition ColorUtil.h:71
Definition API.h:14
Trampoline & GetTrampoline()