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