forked from linuxdeepin/qt5platform-plugins
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathvtablehook.h
365 lines (294 loc) · 14.1 KB
/
vtablehook.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
// SPDX-FileCopyrightText: 2017 - 2022 Uniontech Software Technology Co.,Ltd.
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#ifndef VTABLEHOOK_H
#define VTABLEHOOK_H
#include <QObject>
#include <QSet>
#include <QDebug>
#include <QLoggingCategory>
#include "global.h"
#include <functional>
DPP_BEGIN_NAMESPACE
#ifndef QT_DEBUG
inline Q_LOGGING_CATEGORY(vtableHook, "deepin.qpa.vtableHook", QtInfoMsg);
#else
inline Q_LOGGING_CATEGORY(vtableHook, "deepin.qpa.vtableHook");
#endif
static inline quintptr toQuintptr(void *ptr)
{
return *(quintptr*)ptr;
}
static inline int getVtableSize(quintptr **obj)
{
quintptr *begin = *obj;
while (true) {
if ((int64_t)*begin == 0 or (int64_t)*begin < QSysInfo::WordSize) // offset will grater than 8 bytes(64 bit)
break;
++begin;
}
return begin - *obj + 2; // for offset and rtti info
}
// http://refspecs.linux-foundation.org/cxxabi-1.83.html#vtable
class VtableHook
{
public:
static inline quintptr *getVtableOfObject(const void *obj)
{
return *(quintptr**)obj;
}
template <typename T>
static quintptr *getVtableOfClass()
{
QByteArray vtable_symbol(typeid(T).name());
vtable_symbol.prepend("_ZTV");
quintptr *vfptr_t1 = reinterpret_cast<quintptr*>(resolve(vtable_symbol.constData()));
return vfptr_t1 ? adjustToEntry(vfptr_t1) : nullptr;
}
static int getDestructFunIndex(quintptr **obj, std::function<void(void)> destoryObjFun);
static constexpr const QObject *getQObject(...) { return nullptr;}
static constexpr const QObject *getQObject(const QObject *obj) { return obj;}
static void autoCleanVtable(const void *obj);
static bool ensureVtable(const void *obj, std::function<void(void)> destoryObjFun);
static bool hasVtable(const void *obj);
static void resetVtable(const void *obj);
static quintptr resetVfptrFun(const void *obj, quintptr functionOffset);
template <typename Fun> // for class
static inline quintptr resetVfptrFun(Fun fun)
{
typedef QtPrivate::FunctionPointer<Fun> FunInfo;
return resetVfptrFun(getVtableOfClass<typename FunInfo::Object>(), toQuintptr(&fun));
}
static quintptr originalFun(const void *obj, quintptr functionOffset);
static bool forceWriteMemory(void *adr, const void *data, size_t length);
static QFunctionPointer resolve(const char *symbol);
template <typename T> class OverrideDestruct : public T { ~OverrideDestruct() override;};
template <typename List1, typename List2> struct CheckCompatibleArguments { enum { value = false }; };
template <typename List> struct CheckCompatibleArguments<List, List> { enum { value = true }; };
template<typename Fun1, typename Fun2>
static bool overrideVfptrFun(quintptr *vfptr_t1, Fun1 fun1, quintptr *vfptr_t2, Fun2 fun2, bool forceWrite)
{
typedef QtPrivate::FunctionPointer<Fun1> FunInfo1;
typedef QtPrivate::FunctionPointer<Fun2> FunInfo2;
//compilation error if the arguments does not match.
Q_STATIC_ASSERT_X((CheckCompatibleArguments<typename FunInfo1::Arguments, typename FunInfo2::Arguments>::value),
"Function1 and Function2 arguments are not compatible.");
Q_STATIC_ASSERT_X((CheckCompatibleArguments<QtPrivate::List<typename FunInfo1::ReturnType>, QtPrivate::List<typename FunInfo2::ReturnType>>::value),
"Function1 and Function2 return type are not compatible..");
//! ({code}) in the form of a code is to eliminate - Wstrict - aliasing build warnings
quintptr fun1_offset = toQuintptr(&fun1);
quintptr fun2_offset = toQuintptr(&fun2);
if (fun1_offset < 0 || fun1_offset > UINT_LEAST16_MAX)
return false;
quintptr *vfun = vfptr_t1 + fun1_offset / sizeof(quintptr);
// if the fun2 is not virtual function
if (fun2_offset <= UINT_LEAST16_MAX) {
fun2_offset = *(vfptr_t2 + fun2_offset / sizeof(quintptr));
}
if (forceWrite)
return forceWriteMemory(vfun, &fun2_offset, sizeof(fun2_offset));
*vfun = fun2_offset;
return true;
}
template<typename Fun1, typename Fun2>
static bool overrideVfptrFun(const typename QtPrivate::FunctionPointer<Fun1>::Object *t1, Fun1 fun1,
const typename QtPrivate::FunctionPointer<Fun2>::Object *t2, Fun2 fun2)
{
typedef QtPrivate::FunctionPointer<Fun1> FunInfo1;
// 检查析构函数是否为虚
class OverrideDestruct : public FunInfo1::Object { ~OverrideDestruct() override;}; //TODO: we can use std::has_virtual_destructor
if (!ensureVtable((void*)t1, std::bind(&_destory_helper<typename FunInfo1::Object>, t1))) {
return false;
}
quintptr *vfptr_t1 = getVtableOfObject(t1);
quintptr *vfptr_t2 = getVtableOfObject(t2);
bool ok = overrideVfptrFun(vfptr_t1, fun1, vfptr_t2, fun2, false);
if (!ok) {
// 恢复旧环境
resetVtable(t1);
}
return ok;
}
template<typename Fun1, typename Fun2>
static bool overrideVfptrFun(Fun1 fun1, const typename QtPrivate::FunctionPointer<Fun2>::Object *t2, Fun2 fun2)
{
typedef QtPrivate::FunctionPointer<Fun1> FunInfo1;
quintptr *vfptr_t1 = getVtableOfClass<typename FunInfo1::Object>();
if (!vfptr_t1) {
abort();
}
quintptr *vfptr_t2 = getVtableOfObject(t2);
return overrideVfptrFun(vfptr_t1, fun1, vfptr_t2, fun2, true);
}
template<typename Func> struct FunctionPointer { };
template<class Obj, typename Ret, typename... Args> struct FunctionPointer<Ret (Obj::*) (Args...)>
{
typedef QtPrivate::List<Obj*, Args...> Arguments;
};
template<class Obj, typename Ret, typename... Args> struct FunctionPointer<Ret (Obj::*) (Args...) const>
{
typedef QtPrivate::List<Obj*, Args...> Arguments;
};
template<typename Fun1, typename Fun2>
static typename std::enable_if<QtPrivate::FunctionPointer<Fun2>::ArgumentCount >= 0, bool>::type
overrideVfptrFun(quintptr *vfptr_t1, Fun1 fun1, Fun2 fun2, bool forceWrite)
{
typedef QtPrivate::FunctionPointer<Fun1> FunInfo1;
typedef QtPrivate::FunctionPointer<Fun2> FunInfo2;
Q_STATIC_ASSERT(!FunInfo2::IsPointerToMemberFunction);
//compilation error if the arguments does not match.
Q_STATIC_ASSERT_X((CheckCompatibleArguments<typename FunctionPointer<Fun1>::Arguments, typename FunInfo2::Arguments>::value),
"Function1 and Function2 arguments are not compatible.");
Q_STATIC_ASSERT_X((CheckCompatibleArguments<QtPrivate::List<typename FunInfo1::ReturnType>, QtPrivate::List<typename FunInfo2::ReturnType>>::value),
"Function1 and Function2 return type are not compatible..");
//! ({code}) in the form of a code is to eliminate - Wstrict - aliasing build warnings
quintptr fun1_offset = toQuintptr(&fun1);
quintptr fun2_offset = toQuintptr(&fun2);
if (fun1_offset < 0 || fun1_offset > UINT_LEAST16_MAX)
return false;
quintptr *vfun = vfptr_t1 + fun1_offset / sizeof(quintptr);
if (forceWrite)
return forceWriteMemory(vfun, &fun2_offset, sizeof(fun2_offset));
*vfun = fun2_offset;
return true;
}
template<typename StdFun, typename Func> struct StdFunWrap {};
template<typename StdFun, class Obj, typename Ret, typename... Args>
struct StdFunWrap<StdFun, Ret (Obj::*) (Args...)> {
typedef std::function<Ret(Obj*, Args...)> StdFunType;
static inline StdFunType fun(StdFunType f, bool check = true) {
static StdFunType fun = f;
static bool initialized = false;
if (initialized && check) {
qCWarning(vtableHook, "The StdFunWrap is dirty! Don't use std::bind(use lambda functions).");
}
initialized = true;
return fun;
}
static Ret call(Obj *o, Args... args) {
return fun(call, false)(o, std::forward<Args>(args)...);
}
};
template<typename StdFun, class Obj, typename Ret, typename... Args>
struct StdFunWrap<StdFun, Ret (Obj::*) (Args...) const> : StdFunWrap<StdFun, Ret (Obj::*) (Args...)>{};
template<typename Fun1, typename Fun2>
static inline typename std::enable_if<QtPrivate::FunctionPointer<Fun2>::ArgumentCount == -1, bool>::type
overrideVfptrFun(quintptr *vfptr_t1, Fun1 fun1, Fun2 fun2, bool forceWrite)
{
typedef QtPrivate::FunctionPointer<Fun1> FunInfo1;
const int FunctorArgumentCount = QtPrivate::ComputeFunctorArgumentCount<Fun2, typename FunctionPointer<Fun1>::Arguments>::Value;
Q_STATIC_ASSERT_X((FunctorArgumentCount >= 0),
"Function1 and Function2 arguments are not compatible.");
const int Fun2ArgumentCount = (FunctorArgumentCount >= 0) ? FunctorArgumentCount : 0;
typedef typename QtPrivate::FunctorReturnType<Fun2, typename QtPrivate::List_Left<typename FunctionPointer<Fun1>::Arguments, Fun2ArgumentCount>::Value>::Value Fun2ReturnType;
Q_STATIC_ASSERT_X((QtPrivate::AreArgumentsCompatible<Fun2ReturnType, typename FunInfo1::ReturnType>::value),
"Function1 and Function2 return type are not compatible.");
StdFunWrap<Fun2, Fun1>::fun(fun2);
return overrideVfptrFun(vfptr_t1, fun1, StdFunWrap<Fun2, Fun1>::call, forceWrite);
}
/*!
* \fn template<typename Fun1, typename Fun2> static bool overrideVfptrFun(const typename QtPrivate::FunctionPointer<Fun1>::Object *t1, Fun1 fun1, Fun2 fun2)
*
* \note 重载多继承类中的多个虚函数时,fun1务必标记成一个类名的函数。否则可能出现内存泄露的情况
* \note 例如 class A 继承于 B,C,D,当需要重载B中的foo1,C中的foo2时,以下函数的fun1需要统一标记为&A::foo1和&A::foo2
* \note 因为如果分开写为&B::foo1和&C::foo2的话,指针转换内部会记录多张虚表,重载多份析构函数,可能导致最开始的部分无法正常析构!
*/
template<typename Fun1, typename Fun2>
static bool overrideVfptrFun(const typename QtPrivate::FunctionPointer<Fun1>::Object *t1, Fun1 fun1, Fun2 fun2) //THIS
{
typedef QtPrivate::FunctionPointer<Fun1> FunInfo1;
// 检查析构函数是否为虚
class OverrideDestruct : public FunInfo1::Object { ~OverrideDestruct() override;};
if (!ensureVtable((void*)t1, std::bind(&_destory_helper<typename FunInfo1::Object>, t1))) {
return false;
}
bool ok = overrideVfptrFun(getVtableOfObject(t1), fun1, fun2, false);
if (!ok) {
// 恢复旧环境
resetVtable(t1);
}
return true;
}
template<typename Class, typename Fun1, typename Fun2>
static bool overrideVfptrFun(Fun1 fun1, Fun2 fun2)
{
quintptr *vfptr_t1 = getVtableOfClass<Class>();
if (!vfptr_t1) {
abort();
}
return overrideVfptrFun(vfptr_t1, fun1, fun2, true);
}
template<typename Fun1, typename Fun2>
static bool overrideVfptrFun(Fun1 fun1, Fun2 fun2)
{
typedef QtPrivate::FunctionPointer<Fun1> FunInfo1;
return overrideVfptrFun<typename FunInfo1::Object>(fun1, fun2);
}
template<typename Fun1>
static bool resetVfptrFun(const typename QtPrivate::FunctionPointer<Fun1>::Object *obj, Fun1 fun)
{
return resetVfptrFun((void*)obj, toQuintptr(&fun)) > 0;
}
template<typename Fun>
static Fun originalFun(const typename QtPrivate::FunctionPointer<Fun>::Object *obj, Fun fun)
{
quintptr o_fun = originalFun((void*)obj, toQuintptr(&fun));
return *reinterpret_cast<Fun*>(o_fun);
}
template<typename Fun, typename... Args>
static typename QtPrivate::FunctionPointer<Fun>::ReturnType
callOriginalFun(typename QtPrivate::FunctionPointer<Fun>::Object *obj, Fun fun, Args&&... args)
{
quintptr fun_offset = toQuintptr(&fun);
class _ResetVFun
{
public:
~_ResetVFun() {
*(vfptr + offset / sizeof(quintptr)) = oldFun;
}
quintptr *vfptr = nullptr;
quint16 offset = 0;
quintptr oldFun = 0;
};
_ResetVFun rvf;
rvf.vfptr = *(quintptr**)(obj);
rvf.offset = fun_offset;
rvf.oldFun = resetVfptrFun((void*)obj, fun_offset);
if (!rvf.oldFun) {
qCWarning(vtableHook) << "Reset the function failed, object address:" << static_cast<void *>(obj);
abort();
}
// call
return (obj->*fun)(std::forward<Args>(args)...);
}
private:
static bool copyVtable(quintptr **obj);
static bool clearGhostVtable(const void *obj);
static void clearAllGhostVtable();
template <typename T>
static T adjustToTop(T obj) // vtableTop: vtable start address, Usually refers to offset_to_top
{
// this function should'n be called when entry is parent entry
using fundamentalType = typename std::remove_cv<typename std::remove_pointer<T>::type>::type;
return obj - static_cast<fundamentalType>(2); // vtable start address = vtable entry - 2
}
template <typename T>
static T adjustToEntry(T obj) // vtableEntry: is located after rtti in the virtual table
{
// this function should'n be called when entry is parent entry
using fundamentalType = typename std::remove_cv<typename std::remove_pointer<T>::type>::type;
return obj + static_cast<fundamentalType>(2); // vtable entry = vtable start address + 2
}
template<typename T>
static void _destory_helper(const T *obj) {
delete obj;
}
static QMap<quintptr**, quintptr*> objToOriginalVfptr;
static QMap<const void*, quintptr*> objToGhostVfptr;
static QMap<const void*, quintptr> objDestructFun;
};
DPP_END_NAMESPACE
#define HookReset VtableHook::resetVfptrFun
#define HookCall VtableHook::callOriginalFun
#define HookOverride VtableHook::overrideVfptrFun
#endif // VTABLEHOOK_H