DtkCore
DTK Core module
dvtablehook.h
1// SPDX-FileCopyrightText: 2019 - 2022 UnionTech Software Technology Co., Ltd.
2//
3// SPDX-License-Identifier: LGPL-3.0-or-later
4
5#ifndef DVTABLEHOOK_H
6#define DVTABLEHOOK_H
7
8#include <dtkcore_global.h>
9
10#include <QObject>
11#include <QSet>
12#include <QDebug>
13
14#include <functional>
15#include <type_traits>
16
17DCORE_BEGIN_NAMESPACE
18
19class LIBDTKCORESHARED_EXPORT DVtableHook
20{
21public:
22 static inline quintptr toQuintptr(const void *ptr)
23 {
24 return *(quintptr*)ptr;
25 }
26
27 static inline int getVtableSize(quintptr **obj)
28 {
29 quintptr *begin = *obj;
30 while(*begin) ++begin;
31 return begin - *obj;
32 }
33
34 static inline quintptr *getVtableOfObject(const void *obj)
35 {
36 return *(quintptr**)obj;
37 }
38
39 template <typename T>
40 static quintptr *getVtableOfClass()
41 {
42 QByteArray vtable_symbol(typeid(T).name());
43 vtable_symbol.prepend("_ZTV");
44
45 quintptr *vfptr_t1 = reinterpret_cast<quintptr*>(resolve(vtable_symbol.constData()));
46
47 if (!vfptr_t1)
48 return nullptr;
49
50 // symbol address + 2 * sizeof(quintptr) = virtal table start address
51 return vfptr_t1 + 2;
52 }
53
54 static int getDestructFunIndex(quintptr **obj, std::function<void(void)> destoryObjFun);
55 static constexpr const QObject *getQObject(...) { return nullptr;}
56 static constexpr const QObject *getQObject(const QObject *obj) { return obj;}
57 static void autoCleanVtable(const void *obj);
58 static bool ensureVtable(const void *obj, std::function<void(void)> destoryObjFun);
59 static bool hasVtable(const void *obj);
60 static void resetVtable(const void *obj);
61 static quintptr resetVfptrFun(const void *obj, quintptr functionOffset);
62 static quintptr originalFun(const void *obj, quintptr functionOffset);
63 static bool forceWriteMemory(void *adr, const void *data, size_t length);
64 static QFunctionPointer resolve(const char *symbol);
65
66 template <typename T> class OverrideDestruct : public T { ~OverrideDestruct() override;};
67 template <typename List1, typename List2> struct CheckCompatibleArguments { enum { value = false }; };
68 template <typename List> struct CheckCompatibleArguments<List, List> { enum { value = true }; };
69
70 template<typename Fun1, typename Fun2>
71 static bool overrideVfptrFun(quintptr *vfptr_t1, Fun1 fun1, quintptr *vfptr_t2, Fun2 fun2, bool forceWrite)
72 {
73 typedef QtPrivate::FunctionPointer<Fun1> FunInfo1;
74 typedef QtPrivate::FunctionPointer<Fun2> FunInfo2;
75
76 //compilation error if the arguments does not match.
78 "Function1 and Function2 arguments are not compatible.");
79 Q_STATIC_ASSERT_X((CheckCompatibleArguments<QtPrivate::List<typename FunInfo1::ReturnType>, QtPrivate::List<typename FunInfo2::ReturnType>>::value),
80 "Function1 and Function2 return type are not compatible..");
81
83 quintptr fun1_offset = toQuintptr(&fun1);
84 quintptr fun2_offset = toQuintptr(&fun2);
85
86 if (fun1_offset < 0 || fun1_offset > UINT_LEAST16_MAX)
87 return false;
88
89 quintptr *vfun = vfptr_t1 + fun1_offset / sizeof(quintptr);
90
91 // if the fun2 is not virtual function
92 if (fun2_offset <= UINT_LEAST16_MAX) {
93 fun2_offset = *(vfptr_t2 + fun2_offset / sizeof(quintptr));
94 }
95
96 if (forceWrite)
97 return forceWriteMemory(vfun, &fun2_offset, sizeof(fun2_offset));
98
99 *vfun = fun2_offset;
100
101 return true;
102 }
103
104 template<typename Fun1, typename Fun2>
105 static bool overrideVfptrFun(const typename QtPrivate::FunctionPointer<Fun1>::Object *t1, Fun1 fun1,
106 const typename QtPrivate::FunctionPointer<Fun2>::Object *t2, Fun2 fun2)
107 {
108 typedef QtPrivate::FunctionPointer<Fun1> FunInfo1;
109 // 检查析构函数是否为虚
110 class OverrideDestruct : public FunInfo1::Object { ~OverrideDestruct() override;};
111
112 if (!ensureVtable((void*)t1, std::bind(&_destory_helper<typename FunInfo1::Object>, t1))) {
113 return false;
114 }
115
116 quintptr *vfptr_t1 = getVtableOfObject(t1);
117 quintptr *vfptr_t2 = getVtableOfObject(t2);
118
119 bool ok = overrideVfptrFun(vfptr_t1, fun1, vfptr_t2, fun2, false);
120
121 if (!ok) {
122 // 恢复旧环境
123 resetVtable(t1);
124 }
125
126 return ok;
127 }
128
129 template<typename Class, typename Fun1, typename Fun2>
130 static bool overrideVfptrFun(Fun1 fun1, const typename QtPrivate::FunctionPointer<Fun2>::Object *t2, Fun2 fun2)
131 {
132 quintptr *vfptr_t1 = getVtableOfClass<Class>();
133
134 if (!vfptr_t1) {
135 abort();
136 }
137
138 quintptr *vfptr_t2 = getVtableOfObject(t2);
139
140 return overrideVfptrFun(vfptr_t1, fun1, vfptr_t2, fun2, true);
141 }
142
143 template<typename Fun1, typename Fun2>
144 static bool overrideVfptrFun(Fun1 fun1, const typename QtPrivate::FunctionPointer<Fun2>::Object *t2, Fun2 fun2)
145 {
146 typedef QtPrivate::FunctionPointer<Fun1> FunInfo1;
147 return overrideVfptrFun<typename FunInfo1::Object>(fun1, t2, fun2);
148 }
149
150 template<typename Func> struct FunctionPointer { };
151 template<class Obj, typename Ret, typename... Args> struct FunctionPointer<Ret (Obj::*) (Args...)>
152 {
153 typedef QtPrivate::List<Obj*, Args...> Arguments;
154 };
155 template<class Obj, typename Ret, typename... Args> struct FunctionPointer<Ret (Obj::*) (Args...) const>
156 {
157 typedef QtPrivate::List<Obj*, Args...> Arguments;
158 };
159 template<typename Fun1, typename Fun2>
160 static typename std::enable_if<QtPrivate::FunctionPointer<Fun2>::ArgumentCount >= 0, bool>::type
161 overrideVfptrFun(quintptr *vfptr_t1, Fun1 fun1, Fun2 fun2, bool forceWrite)
162 {
163 typedef QtPrivate::FunctionPointer<Fun1> FunInfo1;
164 typedef QtPrivate::FunctionPointer<Fun2> FunInfo2;
165
166 Q_STATIC_ASSERT(!FunInfo2::IsPointerToMemberFunction);
167 //compilation error if the arguments does not match.
168 Q_STATIC_ASSERT_X((CheckCompatibleArguments<typename FunctionPointer<Fun1>::Arguments, typename FunInfo2::Arguments>::value),
169 "Function1 and Function2 arguments are not compatible.");
170 Q_STATIC_ASSERT_X((CheckCompatibleArguments<QtPrivate::List<typename FunInfo1::ReturnType>, QtPrivate::List<typename FunInfo2::ReturnType>>::value),
171 "Function1 and Function2 return type are not compatible..");
172
174 quintptr fun1_offset = toQuintptr(&fun1);
175 quintptr fun2_offset = toQuintptr(&fun2);
176
177 if (fun1_offset < 0 || fun1_offset > UINT_LEAST16_MAX)
178 return false;
179
180 quintptr *vfun = vfptr_t1 + fun1_offset / sizeof(quintptr);
181
182 if (forceWrite)
183 return forceWriteMemory(vfun, &fun2_offset, sizeof(fun2_offset));
184
185 *vfun = fun2_offset;
186
187 return true;
188 }
189
190 template<typename StdFun, typename Func> struct StdFunWrap {};
191 template<typename StdFun, class Obj, typename Ret, typename... Args>
192 struct StdFunWrap<StdFun, Ret (Obj::*) (Args...)> {
193 typedef std::function<Ret(Obj*, Args...)> StdFunType;
194 static inline StdFunType fun(StdFunType f, bool check = true) {
195 static StdFunType fun = f;
196 static bool initialized = false;
197 if (initialized && check) {
198 qWarning("The StdFunWrap is dirty! Don't use std::bind(use lambda functions).");
199 }
200 initialized = true;
201 return fun;
202 }
203 static Ret call(Obj *o, Args... args) {
204 return fun(call, false)(o, std::forward<Args>(args)...);
205 }
206 };
207 template<typename StdFun, class Obj, typename Ret, typename... Args>
208 struct StdFunWrap<StdFun, Ret (Obj::*) (Args...) const> : StdFunWrap<StdFun, Ret (Obj::*) (Args...)>{};
209
210 template<typename Fun1, typename Fun2>
211 static inline typename std::enable_if<QtPrivate::FunctionPointer<Fun2>::ArgumentCount == -1, bool>::type
212 overrideVfptrFun(quintptr *vfptr_t1, Fun1 fun1, Fun2 fun2, bool forceWrite)
213 {
214 typedef QtPrivate::FunctionPointer<Fun1> FunInfo1;
215 const int FunctorArgumentCount = QtPrivate::ComputeFunctorArgumentCount<Fun2, typename FunctionPointer<Fun1>::Arguments>::Value;
216
217 Q_STATIC_ASSERT_X((FunctorArgumentCount >= 0),
218 "Function1 and Function2 arguments are not compatible.");
219 const int Fun2ArgumentCount = (FunctorArgumentCount >= 0) ? FunctorArgumentCount : 0;
220 typedef typename QtPrivate::FunctorReturnType<Fun2, typename QtPrivate::List_Left<typename FunctionPointer<Fun1>::Arguments, Fun2ArgumentCount>::Value>::Value Fun2ReturnType;
221
222 Q_STATIC_ASSERT_X((QtPrivate::AreArgumentsCompatible<Fun2ReturnType, typename FunInfo1::ReturnType>::value),
223 "Function1 and Function2 return type are not compatible.");
224
226 return overrideVfptrFun(vfptr_t1, fun1, StdFunWrap<Fun2, Fun1>::call, forceWrite);
227 }
228
236 template<typename Fun1, typename Fun2>
237 static bool overrideVfptrFun(const typename QtPrivate::FunctionPointer<Fun1>::Object *t1, Fun1 fun1, Fun2 fun2)
238 {
239 typedef QtPrivate::FunctionPointer<Fun1> FunInfo1;
240 // 检查析构函数是否为虚
241 class OverrideDestruct : public FunInfo1::Object { ~OverrideDestruct() override;};
242
243 if (!ensureVtable((void*)t1, std::bind(&_destory_helper<typename FunInfo1::Object>, t1))) {
244 return false;
245 }
246
247 bool ok = overrideVfptrFun(getVtableOfObject(t1), fun1, fun2, false);
248
249 if (!ok) {
250 // 恢复旧环境
251 resetVtable(t1);
252 }
253
254 return true;
255 }
256
257 template<typename Class, typename Fun1, typename Fun2>
258 static bool overrideVfptrFun(Fun1 fun1, Fun2 fun2)
259 {
260 quintptr *vfptr_t1 = getVtableOfClass<Class>();
261
262 if (!vfptr_t1) {
263 abort();
264 }
265
266 return overrideVfptrFun(vfptr_t1, fun1, fun2, true);
267 }
268
269 template<typename Fun1, typename Fun2>
270 static bool overrideVfptrFun(Fun1 fun1, Fun2 fun2)
271 {
272 typedef QtPrivate::FunctionPointer<Fun1> FunInfo1;
273 return overrideVfptrFun<typename FunInfo1::Object>(fun1, fun2);
274 }
275
276 template<typename Fun1>
277 static bool resetVfptrFun(const typename QtPrivate::FunctionPointer<Fun1>::Object *obj, Fun1 fun)
278 {
279 return resetVfptrFun((void*)obj, toQuintptr(&fun)) > 0;
280 }
281
282 template<typename Fun>
283 static Fun originalFun(const typename QtPrivate::FunctionPointer<Fun>::Object *obj, Fun fun)
284 {
285 quintptr o_fun = originalFun((void*)obj, toQuintptr(&fun));
286
287 return *reinterpret_cast<Fun*>(o_fun);
288 }
289
290 template<typename Fun, typename... Args>
291 static typename QtPrivate::FunctionPointer<Fun>::ReturnType
292 callOriginalFun(typename QtPrivate::FunctionPointer<Fun>::Object *obj, Fun fun, Args&&... args)
293 {
294 quintptr fun_offset = toQuintptr(&fun);
295
296 class _ResetVFun
297 {
298 public:
299 ~_ResetVFun() {
300 *(vfptr + offset / sizeof(quintptr)) = oldFun;
301 }
302 quintptr *vfptr = nullptr;
303 quint16 offset = 0;
304 quintptr oldFun = 0;
305 };
306
307 _ResetVFun rvf;
308
309 rvf.vfptr = *(quintptr**)(obj);
310 rvf.offset = fun_offset;
311 rvf.oldFun = DVtableHook::resetVfptrFun((void*)obj, fun_offset);
312
313 if (!rvf.oldFun) {
314 qWarning() << "Reset the function failed, object:" << obj;
315 abort();
316 }
317
318 // call
319 return (obj->*fun)(std::forward<Args>(args)...);
320 }
321
322private:
323 static bool copyVtable(quintptr **obj);
324 static bool clearGhostVtable(const void *obj);
325
326 template<typename T>
327 static void _destory_helper(const T *obj) {
328 delete obj;
329 }
330
331 static QMap<quintptr**, quintptr*> objToOriginalVfptr;
332 static QMap<const void*, quintptr*> objToGhostVfptr;
333 static QMap<const void*, quintptr> objDestructFun;
334};
335
336DCORE_END_NAMESPACE
337
338#endif // DVTABLEHOOK_H
Definition: dvtablehook.h:66
Definition: dvtablehook.h:20
static bool ::type overrideVfptrFun(quintptr *vfptr_t1, Fun1 fun1, Fun2 fun2, bool forceWrite)
Definition: dvtablehook.h:161
static bool overrideVfptrFun(const typename QtPrivate::FunctionPointer< Fun1 >::Object *t1, Fun1 fun1, Fun2 fun2)
Definition: dvtablehook.h:237
static bool overrideVfptrFun(quintptr *vfptr_t1, Fun1 fun1, quintptr *vfptr_t2, Fun2 fun2, bool forceWrite)
Definition: dvtablehook.h:71
Definition: dvtablehook.h:150
Definition: dvtablehook.h:190