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