Imported Upstream version 1.1.0
[gammaray-debian.git] / hooking / abstractfunctionoverwriter.cpp
blob8a4685bb7b08b4234f076372d509a5e9c0e27e8f
1 /*
2 This file is part of GammaRay, the Qt application inspection and
3 manipulation tool.
5 Copyright (C) 2010-2011 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
6 Author: Andreas Holzammer <andreas.holzammer@kdab.com>
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 2 of the License, or
11 (at your option) any later version.
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.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "abstractfunctionoverwriter.h"
23 #include <iostream>
25 #ifdef ARCH_X86
26 const long worstSizeForLongJump = 10;
27 #elif defined(ARCH_64)
28 const long worstSizeForLongJump = 14;
29 #else
30 # error "Unsupported hardware architecture!"
31 #endif
33 using namespace GammaRay;
35 bool AbstractFunctionOverwriter::writeShortJump(void *target, void *const func)
37 quint8 *cur = (quint8 *) target;
39 //E9 relative short jump is 5 bytes long
40 bool ret = unprotectMemory(page_align(target), roundToNextPage(5));
42 if (!ret) {
43 std::cerr << "Failed to unprotect memory: " << page_align(target);
44 return false;
47 *cur = 0xE9;
48 cur++;
49 *((quint32 *)cur) = (unsigned long)func - (unsigned long)(cur + 4);
51 ret = reprotectMemory(page_align(target), roundToNextPage(5));
53 if (!ret) {
54 std::cerr << "Failed to reprotect memory: " << page_align(target);
55 return false;
58 return true;
61 bool AbstractFunctionOverwriter::writeLongJump(void *target, void *const func)
63 quint8 *cur = (quint8 *) target;
65 bool ret = unprotectMemory(page_align(target), roundToNextPage(worstSizeForLongJump));
67 if (!ret) {
68 std::cerr << "Failed to unprotect memory: " << page_align(target);
69 return false;
72 *cur = 0xff;
73 *(++cur) = 0x25;
75 #ifdef ARCH_X86
76 *((quint32 *) ++cur) = (quint32)(((quint32) cur) + sizeof (quint32));
77 cur += sizeof (quint32);
78 *((quint32 *)cur) = (quint32)func;
79 #elif defined(ARCH_64)
80 *((quint32 *) ++cur) = 0;
81 cur += sizeof (quint32);
82 *((quint64*)cur) = (quint64)func;
83 #else
84 # error "Unsupported hardware architecture!"
85 #endif
87 ret = reprotectMemory(page_align(target), roundToNextPage(worstSizeForLongJump));
89 if (!ret) {
90 std::cerr << "Failed to reprotect memory: " << page_align(target);
91 return false;
94 return true;
97 void *AbstractFunctionOverwriter::getMemoryNearAddress(void *const addr, size_t size)
99 Q_ASSERT(blocksize() > size);
101 #if defined(ARCH_64)
102 intptr_t minAddr;
103 intptr_t maxAddr;
105 getAddressRange(minAddr, maxAddr);
107 minAddr = std::max<intptr_t>(minAddr, reinterpret_cast<intptr_t>(addr) - 0x20000000);
108 maxAddr = std::min<intptr_t>(maxAddr, reinterpret_cast<intptr_t>(addr) + 0x20000000);
109 #endif
111 for (QList<MemorySegment>::Iterator it = memoryPool.begin(); it != memoryPool.end(); ++it) {
112 if (it->free >= size) {
113 #if defined(ARCH_64)
114 if (!((intptr_t)it->mem > minAddr && (intptr_t)it->mem < maxAddr)) {
115 continue;
117 #endif
118 quint8 *mem = (quint8 *)it->mem + (it->size - it->free);
119 it->free -= size;
120 return mem;
124 void *mem = 0;
125 #ifdef ARCH_X86
126 Q_UNUSED(addr);
127 mem = reserveMemory(0, blocksize());
128 #elif defined(ARCH_64)
129 intptr_t min = minAddr / blocksize();
130 intptr_t max = maxAddr / blocksize();
131 int rel = 0;
132 for (int i = 0; i < (max - min + 1); ++i) {
133 rel = -rel + (i & 1);
134 void* query = reinterpret_cast<void*>(((min + max) / 2 + rel) * blocksize());
136 Q_ASSERT(!((size_t)query & (pagesize() - 1)));
138 if (isMemoryFree(query, blocksize())) {
139 mem = reserveMemory(query, blocksize());
140 if (mem != 0 &&
141 reinterpret_cast<intptr_t>(mem) > minAddr &&
142 reinterpret_cast<intptr_t>(mem) < maxAddr) {
143 break;
147 #else
148 #error "Unsupported hardware architecture!"
149 #endif
150 if (!mem) {
151 std::cerr << "Error could not find memory close to: " << addr;
152 return 0;
154 if (!commitMemory(mem, blocksize())) {
155 return 0;
157 MemorySegment memSegment;
158 memSegment.mem = mem;
159 memSegment.size = blocksize();
160 memSegment.free = blocksize() - size;
161 memoryPool.append(memSegment);
162 return mem;
165 void *AbstractFunctionOverwriter::createTrampoline(void *const func, void *const replacement)
167 void *mem = getMemoryNearAddress(func, worstSizeForLongJump);
168 if (!mem) {
169 return 0;
171 bool ret = writeLongJump(mem, replacement);
172 if (!ret) {
173 return 0;
175 return mem;
178 AbstractFunctionOverwriter::~AbstractFunctionOverwriter()
182 bool AbstractFunctionOverwriter::overwriteFunction(const QString &orignalFunc,
183 void * const replacementFunc)
185 void *func = qtCoreFunctionLookup(orignalFunc);
186 if (!func) {
187 std::cerr << "Failed to lookup: " << orignalFunc.toLatin1().data();
188 return false;
190 void *mem = createTrampoline(func, replacementFunc);
191 if (!mem) {
192 return false;
195 bool ret = writeShortJump(func, mem);
197 return ret;
200 void *AbstractFunctionOverwriter::page_align(void *addr) const
202 Q_ASSERT(addr != 0);
203 return (void *)((size_t)addr & ~(pagesize() - 1));
206 size_t AbstractFunctionOverwriter::roundToNextPage(size_t addr) const
208 Q_ASSERT(addr != 0);
209 return (size_t)page_align((void*)(addr + (pagesize() - 1)));
212 size_t GammaRay::AbstractFunctionOverwriter::blocksize()
214 return roundToNextPage(std::max((worstSizeForLongJump * 4), pagesize()));