Skip to content

Commit 4e766d2

Browse files
committed
Added the ability to create C++ callbacks
- Added template raw_method, which is able to create variadic methods
1 parent 814db3b commit 4e766d2

File tree

10 files changed

+365
-60
lines changed

10 files changed

+365
-60
lines changed

src/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,13 +242,15 @@ Set(SOURCEPYTHON_MEMORY_MODULE_HEADERS
242242
core/modules/memory/memory_tools.h
243243
core/modules/memory/memory_scanner.h
244244
core/modules/memory/memory_hooks.h
245+
core/modules/memory/memory_callback.h
245246
)
246247

247248
Set(SOURCEPYTHON_MEMORY_MODULE_SOURCES
248249
core/modules/memory/memory_scanner.cpp
249250
core/modules/memory/memory_tools.cpp
250251
core/modules/memory/memory_hooks.cpp
251252
core/modules/memory/memory_wrap_python.cpp
253+
core/modules/memory/memory_callback.cpp
252254
thirdparty/DynamicHooks/asm.cpp
253255
thirdparty/DynamicHooks/utilities.cpp
254256
thirdparty/DynamicHooks/DynamicHooks.cpp
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
/**
2+
* =============================================================================
3+
* Source Python
4+
* Copyright (C) 2012 Source Python Development Team. All rights reserved.
5+
* =============================================================================
6+
*
7+
* This program is free software; you can redistribute it and/or modify it under
8+
* the terms of the GNU General Public License, version 3.0, as published by the
9+
* Free Software Foundation.
10+
*
11+
* This program is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13+
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14+
* details.
15+
*
16+
* You should have received a copy of the GNU General Public License along with
17+
* this program. If not, see <http://www.gnu.org/licenses/>.
18+
*
19+
* As a special exception, the Source Python Team gives you permission
20+
* to link the code of this program (as well as its derivative works) to
21+
* "Half-Life 2," the "Source Engine," and any Game MODs that run on software
22+
* by the Valve Corporation. You must obey the GNU General Public License in
23+
* all respects for all other code used. Additionally, the Source.Python
24+
* Development Team grants this exception to all derivative works.
25+
*/
26+
27+
// ============================================================================
28+
// >> INCLUDES
29+
// ============================================================================
30+
#include "memory_callback.h"
31+
#include "utility/wrap_macros.h"
32+
33+
#include "AsmJit.h"
34+
using namespace AsmJit;
35+
36+
#include "utilities.h"
37+
#include "DynamicHooks.h"
38+
using namespace DynamicHooks;
39+
40+
41+
// ============================================================================
42+
// >> CLASSES
43+
// ============================================================================
44+
#define GET_CALLBACK_CALLER(type) \
45+
GET_FUNCTION(type, CallbackCaller<type>, CCallback*, unsigned long, unsigned long)
46+
47+
CCallback::CCallback(object oCallback, Convention_t eConv, char* szParams)
48+
: CFunction(NULL, eConv, szParams)
49+
{
50+
m_eConv = eConv;
51+
m_oCallback = oCallback;
52+
53+
// Parse the parameter string
54+
m_pParams = new Param_t;
55+
m_pRetParam = new Param_t;
56+
ParseParams(eConv, szParams, m_pParams, m_pRetParam);
57+
58+
// Find the proper callback caller function
59+
void* pCallCallbackFunc = NULL;
60+
switch(m_pRetParam->m_cParam)
61+
{
62+
case SIGCHAR_VOID: pCallCallbackFunc = GET_CALLBACK_CALLER(void); break;
63+
case SIGCHAR_BOOL: pCallCallbackFunc = GET_CALLBACK_CALLER(bool); break;
64+
case SIGCHAR_CHAR: pCallCallbackFunc = GET_CALLBACK_CALLER(char); break;
65+
case SIGCHAR_UCHAR: pCallCallbackFunc = GET_CALLBACK_CALLER(unsigned char); break;
66+
case SIGCHAR_SHORT: pCallCallbackFunc = GET_CALLBACK_CALLER(short); break;
67+
case SIGCHAR_USHORT: pCallCallbackFunc = GET_CALLBACK_CALLER(unsigned short); break;
68+
case SIGCHAR_INT: pCallCallbackFunc = GET_CALLBACK_CALLER(int); break;
69+
case SIGCHAR_UINT: pCallCallbackFunc = GET_CALLBACK_CALLER(unsigned int); break;
70+
case SIGCHAR_LONG: pCallCallbackFunc = GET_CALLBACK_CALLER(long); break;
71+
case SIGCHAR_ULONG: pCallCallbackFunc = GET_CALLBACK_CALLER(unsigned long); break;
72+
case SIGCHAR_LONGLONG: pCallCallbackFunc = GET_CALLBACK_CALLER(long long); break;
73+
case SIGCHAR_ULONGLONG: pCallCallbackFunc = GET_CALLBACK_CALLER(unsigned long long); break;
74+
case SIGCHAR_FLOAT: pCallCallbackFunc = GET_CALLBACK_CALLER(float); break;
75+
case SIGCHAR_DOUBLE: pCallCallbackFunc = GET_CALLBACK_CALLER(double); break;
76+
case SIGCHAR_POINTER: pCallCallbackFunc = GET_CALLBACK_CALLER(void *); break;
77+
case SIGCHAR_STRING: pCallCallbackFunc = GET_CALLBACK_CALLER(char *); break;
78+
default: BOOST_RAISE_EXCEPTION(PyExc_ValueError, "Unknown return type.");
79+
}
80+
81+
// Generate the function
82+
Assembler a;
83+
84+
// Epilog
85+
a.push(ebp);
86+
a.mov(ebp, esp);
87+
88+
// Call callback caller
89+
a.push(ecx);
90+
a.push(ebp);
91+
a.push(imm((sysint_t) this));
92+
a.call(pCallCallbackFunc);
93+
a.add(esp, imm(12));
94+
95+
// Prolog
96+
a.mov(esp, ebp);
97+
a.pop(ebp);
98+
99+
// Return
100+
a.ret(imm(GetPopSize()));
101+
102+
m_ulAddr = (unsigned long) a.make();
103+
}
104+
105+
CCallback::~CCallback()
106+
{
107+
delete m_pParams;
108+
delete m_pRetParam;
109+
}
110+
111+
int CCallback::GetPopSize()
112+
{
113+
#ifdef _WIN32
114+
if (m_eConv == CONV_THISCALL || m_eConv == CONV_STDCALL)
115+
{
116+
Param_t* pParam = GetArgument(GetArgumentCount() - 1);
117+
return pParam->m_iOffset + pParam->m_iSize;
118+
}
119+
#endif
120+
return 0;
121+
}
122+
123+
int CCallback::GetArgumentCount()
124+
{
125+
int count = 0;
126+
Param_t* temp = m_pParams;
127+
while(temp)
128+
{
129+
count++;
130+
temp = temp->m_pNext;
131+
}
132+
return count - 1;
133+
}
134+
135+
Param_t* CCallback::GetArgument(int iIndex)
136+
{
137+
Param_t* temp = m_pParams;
138+
while(temp && iIndex > 0)
139+
{
140+
iIndex--;
141+
temp = temp->m_pNext;
142+
}
143+
return temp;
144+
}
145+
146+
void CCallback::Free()
147+
{
148+
// TODO: Figure out how to use std::free() on the generated code, so we can
149+
// use Dealloc().
150+
MemoryManager::getGlobal()->free((void *) m_ulAddr);
151+
m_ulAddr = 0;
152+
}
153+
154+
155+
// ============================================================================
156+
// >> FUNCTIONS
157+
// ============================================================================
158+
template<class T>
159+
T GetArgument(CCallback* pCallback, unsigned long ulEBP, unsigned long ulECX, int iIndex)
160+
{
161+
#ifdef _WIN32
162+
if (pCallback->m_eConv == CONV_THISCALL && iIndex == 0)
163+
return *(T *) &ulECX;
164+
#endif
165+
166+
return *(T *) (ulEBP + pCallback->GetArgument(iIndex)->m_iOffset + 8);
167+
}
168+
169+
object CallCallback(CCallback* pCallback, unsigned long ulEBP, unsigned long ulECX)
170+
{
171+
BEGIN_BOOST_PY()
172+
173+
list arg_list;
174+
for(int i=0; i < pCallback->GetArgumentCount(); i++)
175+
{
176+
object val;
177+
switch(pCallback->GetArgument(i)->m_cParam)
178+
{
179+
case SIGCHAR_BOOL: val = object(GetArgument<bool>(pCallback, ulEBP, ulECX, i)); break;
180+
case SIGCHAR_CHAR: val = object(GetArgument<char>(pCallback, ulEBP, ulECX, i)); break;
181+
case SIGCHAR_UCHAR: val = object(GetArgument<unsigned char>(pCallback, ulEBP, ulECX, i)); break;
182+
case SIGCHAR_SHORT: val = object(GetArgument<short>(pCallback, ulEBP, ulECX, i)); break;
183+
case SIGCHAR_USHORT: val = object(GetArgument<unsigned short>(pCallback, ulEBP, ulECX, i)); break;
184+
case SIGCHAR_INT: val = object(GetArgument<int>(pCallback, ulEBP, ulECX, i)); break;
185+
case SIGCHAR_UINT: val = object(GetArgument<unsigned int>(pCallback, ulEBP, ulECX, i)); break;
186+
case SIGCHAR_LONG: val = object(GetArgument<long>(pCallback, ulEBP, ulECX, i)); break;
187+
case SIGCHAR_ULONG: val = object(GetArgument<unsigned long>(pCallback, ulEBP, ulECX, i)); break;
188+
case SIGCHAR_LONGLONG: val = object(GetArgument<long long>(pCallback, ulEBP, ulECX, i)); break;
189+
case SIGCHAR_ULONGLONG: val = object(GetArgument<unsigned long long>(pCallback, ulEBP, ulECX, i)); break;
190+
case SIGCHAR_FLOAT: val = object(GetArgument<float>(pCallback, ulEBP, ulECX, i)); break;
191+
case SIGCHAR_DOUBLE: val = object(GetArgument<double>(pCallback, ulEBP, ulECX, i)); break;
192+
case SIGCHAR_POINTER: val = object(CPointer(GetArgument<unsigned long>(pCallback, ulEBP, ulECX, i))); break;
193+
case SIGCHAR_STRING: val = object(GetArgument<const char*>(pCallback, ulEBP, ulECX, i)); break;
194+
default: BOOST_RAISE_EXCEPTION(PyExc_TypeError, "Unknown type."); break;
195+
}
196+
arg_list.append(val);
197+
}
198+
arg_list.append(CPointer((unsigned long) ulEBP));
199+
return eval("lambda func, args: func(*args)")(pCallback->m_oCallback, arg_list);
200+
201+
END_BOOST_PY_NORET()
202+
203+
// Throw an exception. We will crash now :(
204+
throw;
205+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/**
2+
* =============================================================================
3+
* Source Python
4+
* Copyright (C) 2012 Source Python Development Team. All rights reserved.
5+
* =============================================================================
6+
*
7+
* This program is free software; you can redistribute it and/or modify it under
8+
* the terms of the GNU General Public License, version 3.0, as published by the
9+
* Free Software Foundation.
10+
*
11+
* This program is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13+
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14+
* details.
15+
*
16+
* You should have received a copy of the GNU General Public License along with
17+
* this program. If not, see <http://www.gnu.org/licenses/>.
18+
*
19+
* As a special exception, the Source Python Team gives you permission
20+
* to link the code of this program (as well as its derivative works) to
21+
* "Half-Life 2," the "Source Engine," and any Game MODs that run on software
22+
* by the Valve Corporation. You must obey the GNU General Public License in
23+
* all respects for all other code used. Additionally, the Source.Python
24+
* Development Team grants this exception to all derivative works.
25+
*/
26+
27+
#ifndef _MEMORY_CALLBACK_H
28+
#define _MEMORY_CALLBACK_H
29+
30+
// ============================================================================
31+
// >> INCLUDES
32+
// ============================================================================
33+
#include "memory_tools.h"
34+
#include "utility/sp_util.h"
35+
36+
37+
// ============================================================================
38+
// >> FUNCTIONS
39+
// ============================================================================
40+
// Forward declarations
41+
class CCallback;
42+
43+
object CallCallback(CCallback* pCallback, unsigned long ulEBP, unsigned long ulECX);
44+
45+
template<class T>
46+
T CallbackCaller(CCallback* pCallback, unsigned long ulEBP, unsigned long ulECX)
47+
{
48+
return extract<T>(CallCallback(pCallback, ulEBP, ulECX));
49+
}
50+
51+
template<>
52+
inline void CallbackCaller(CCallback* pCallback, unsigned long ulEBP, unsigned long ulECX)
53+
{
54+
CallCallback(pCallback, ulEBP, ulECX);
55+
}
56+
57+
template<>
58+
inline void* CallbackCaller(CCallback* pCallback, unsigned long ulEBP, unsigned long ulECX)
59+
{
60+
return (void *) ExtractPyPtr(CallCallback(pCallback, ulEBP, ulECX));
61+
}
62+
63+
64+
// ============================================================================
65+
// >> CLASSES
66+
// ============================================================================
67+
class CCallback: public CFunction
68+
{
69+
public:
70+
CCallback(object oCallback, Convention_t eConv, char* szParams);
71+
~CCallback();
72+
73+
int GetPopSize();
74+
int GetArgumentCount();
75+
Param_t* GetArgument(int iIndex);
76+
void Free();
77+
78+
public:
79+
// For variadic functions
80+
object m_oCallback;
81+
Param_t* m_pParams;
82+
Param_t* m_pRetParam;
83+
};
84+
85+
#endif // _MEMORY_CALLBACK_H

src/core/modules/memory/memory_hooks.cpp

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ CStackData::CStackData(CHook* pHook)
137137
m_pHook = pHook;
138138
}
139139

140-
object CStackData::get_item(unsigned int iIndex)
140+
object CStackData::GetItem(unsigned int iIndex)
141141
{
142142
if (iIndex >= (unsigned int) m_pHook->GetArgumentCount())
143143
BOOST_RAISE_EXCEPTION(PyExc_IndexError, "Index out of range.")
@@ -170,7 +170,7 @@ object CStackData::get_item(unsigned int iIndex)
170170
return retval;
171171
}
172172

173-
void CStackData::set_item(unsigned int iIndex, object value)
173+
void CStackData::SetItem(unsigned int iIndex, object value)
174174
{
175175
if (iIndex >= (unsigned int) m_pHook->GetArgumentCount())
176176
BOOST_RAISE_EXCEPTION(PyExc_IndexError, "Index out of range.")
@@ -196,9 +196,4 @@ void CStackData::set_item(unsigned int iIndex, object value)
196196
case SIGCHAR_STRING: SetArgument<const char *>(m_pHook, iIndex, value); break;
197197
default: BOOST_RAISE_EXCEPTION(PyExc_TypeError, "Unknown type.")
198198
}
199-
}
200-
201-
CPointer* CStackData::get_esp()
202-
{
203-
return new CPointer((unsigned long) m_pHook->m_pESP);
204199
}
Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
#ifndef MEMORY_HOOKS_H
22
#define MEMORY_HOOKS_H
33

4+
//---------------------------------------------------------------------------------
5+
// Includes
6+
//---------------------------------------------------------------------------------
47
#include <list>
58
#include <map>
69

@@ -10,22 +13,30 @@ using namespace boost::python;
1013
#include "memory_tools.h"
1114
#include "DynamicHooks.h"
1215

16+
17+
//---------------------------------------------------------------------------------
18+
// Classes
19+
//---------------------------------------------------------------------------------
1320
class CStackData
1421
{
1522
public:
1623
CStackData(CHook* pHook);
1724

18-
object get_item(unsigned int iIndex);
19-
void set_item(unsigned int iIndex, object value);
25+
object GetItem(unsigned int iIndex);
26+
void SetItem(unsigned int iIndex, object value);
2027

21-
CPointer* get_esp();
28+
CPointer GetESP()
29+
{ return CPointer((unsigned long) m_pHook->m_pESP); }
2230

23-
private:
31+
protected:
2432
CHook* m_pHook;
2533
std::map<int, object> m_mapCache;
2634
};
2735

2836

37+
//---------------------------------------------------------------------------------
38+
// Functions
39+
//---------------------------------------------------------------------------------
2940
bool SP_HookHandler(DynamicHooks::HookType_t eHookType, CHook* pHook);
3041

3142
#endif // MEMORY_HOOKS_H

0 commit comments

Comments
 (0)