Skip to content

Commit dcada40

Browse files
committed
Revert "Merge remote-tracking branch 'origin/DynamicHooksUpdate'"
This reverts commit fa0de84, reversing changes made to 795b5c4.
1 parent fa0de84 commit dcada40

29 files changed

Lines changed: 811 additions & 1952 deletions

addons/source-python/packages/source-python/memory/__init__.py

Lines changed: 17 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -14,51 +14,40 @@
1414
# >> FORWARD IMPORTS
1515
# =============================================================================
1616
# Source.Python
17-
# core
18-
from core import AutoUnload
1917
# memory
2018
from _memory import BinaryFile
21-
from _memory import CallingConvention
19+
from _memory import Callback
2220
from _memory import Convention
2321
from _memory import DataType
2422
from _memory import EXPOSED_CLASSES
2523
from _memory import Function
2624
from _memory import NULL
2725
from _memory import Pointer
28-
from _memory import ProcessorRegister
29-
from _memory import Register
30-
from _memory import Registers
3126
from _memory import StackData
3227
from _memory import TYPE_SIZES
3328
from _memory import alloc
3429
from _memory import find_binary
35-
from _memory import get_data_type_size
3630
from _memory import get_object_pointer
3731
from _memory import get_size
3832
from _memory import make_object
3933

4034

41-
4235
# =============================================================================
4336
# >> ALL DECLARATION
4437
# =============================================================================
4538
__all__ = ('BinaryFile',
4639
'Callback',
47-
'CallingConvention',
4840
'Convention',
4941
'DataType',
5042
'EXPOSED_CLASSES',
5143
'Function',
5244
'NULL',
5345
'Pointer',
54-
'ProcessorRegister',
55-
'Register',
56-
'Registers',
5746
'StackData',
5847
'TYPE_SIZES',
5948
'alloc',
49+
'callback',
6050
'find_binary',
61-
'get_data_type_size',
6251
'get_object_pointer',
6352
'get_size',
6453
'make_object',
@@ -73,54 +62,24 @@
7362

7463

7564
# =============================================================================
76-
# >> CLASSES
65+
# >> FUNCTIONS
7766
# =============================================================================
78-
class Callback(AutoUnload):
79-
80-
"""Create a function in memory that calls a Python callback."""
81-
82-
def __init__(self, convention, arg_types, return_type):
83-
"""Initialize the Callback object.
84-
85-
@param <convention>:
86-
Defines the calling convention of the function.
87-
88-
@param <arg_types>:
89-
Defines the argument types of the function.
90-
91-
@param <return_type>:
92-
Defines the return type of the function.
93-
"""
94-
self.callback = None
95-
96-
# Allocate enough space for a jump, so we can hook it later. Then
97-
# convert it to a function. Of course, this isn't a function, but the
98-
# hook will override it.
99-
self.function = alloc(8, False).make_function(convention, arg_types,
100-
return_type)
101-
102-
# A little hack to access the "self" argument
103-
def hook(args):
104-
return_value = self.callback(args)
105-
if return_value is not None:
106-
return return_value
67+
def callback(convention=Convention.CDECL, args=(), return_type=DataType.VOID):
68+
"""Create a C++ callback that calls back to the decorated function.
10769
108-
if return_type == DataType.VOID:
109-
return 0
70+
EXAMPLE:
11071
111-
# We will crash now :(
112-
raise ValueError('Return value is not allowed to be None.')
72+
@callback(Convention.CDECL, (DataType.INT, DataType.INT), DataType.INT)
73+
def add(x, y, ebp):
74+
return x + y
11375
114-
# Hook the function and make sure the callback doesn't go out of scope
115-
self._hook = self.function.add_pre_hook(hook)
76+
<add> is now a Callback instance, but you can still call it like a normal
77+
Python function:
11678
117-
def __call__(self, callback):
118-
"""Store the given callback."""
119-
assert callable(callback)
120-
self.callback = callback
121-
return self
79+
assert add(4, 6) == 10
80+
"""
81+
def wait_for_func(func):
82+
"""Return a memory callback instance."""
83+
return Callback(func, convention, args, return_type)
12284

123-
def _unload_instance(self):
124-
"""Remove the hook, restore the allocated space and deallocate it."""
125-
# TODO: Remove the hook and restore the allocated space
126-
self.function.dealloc()
85+
return wait_for_func

src/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,12 +267,14 @@ Set(SOURCEPYTHON_MEMORY_MODULE_HEADERS
267267
core/modules/memory/memory_tools.h
268268
core/modules/memory/memory_scanner.h
269269
core/modules/memory/memory_hooks.h
270+
core/modules/memory/memory_callback.h
270271
)
271272

272273
Set(SOURCEPYTHON_MEMORY_MODULE_SOURCES
273274
core/modules/memory/memory_scanner.cpp
274275
core/modules/memory/memory_tools.cpp
275276
core/modules/memory/memory_hooks.cpp
277+
core/modules/memory/memory_callback.cpp
276278
core/modules/memory/memory_wrap_python.cpp
277279
)
278280

src/core/modules/entities/entities_props_wrap_python.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
// Includes.
2929
//-----------------------------------------------------------------------------
3030
#include "export_main.h"
31-
#include "string_t.h"
3231
#include "utilities/wrap_macros.h"
3332
#include "dt_common.h"
3433
#include "dt_send.h"
Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
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 "utilities/wrap_macros.h"
32+
#include "utilities/call_python.h"
33+
34+
#include "AsmJit.h"
35+
using namespace AsmJit;
36+
37+
#include "utilities.h"
38+
#include "DynamicHooks.h"
39+
using namespace DynamicHooks;
40+
41+
42+
// ============================================================================
43+
// >> MACROS
44+
// ============================================================================
45+
#define GET_CALLBACK_CALLER(type) \
46+
(void *) GET_FUNCTION(type, CallbackCaller<type>, CCallback*, unsigned long, unsigned long)
47+
48+
49+
// ============================================================================
50+
// >> CLASSES
51+
// ============================================================================
52+
CCallback::CCallback(object oCallback, Convention_t eConv, tuple args, object return_type, bool bAutoDealloc /* = true */)
53+
: CFunction(NULL, eConv, args, return_type)
54+
{
55+
m_oCallback = oCallback;
56+
m_bAutoDealloc = bAutoDealloc;
57+
58+
// Find the proper callback caller function
59+
void* pCallCallbackFunc = NULL;
60+
61+
DataType_t RetType;
62+
try
63+
{
64+
RetType = extract<DataType_t>(m_oReturnType);
65+
}
66+
catch ( ... )
67+
{
68+
PyErr_Clear();
69+
RetType = DATA_TYPE_POINTER;
70+
}
71+
72+
switch(RetType)
73+
{
74+
case DATA_TYPE_VOID: pCallCallbackFunc = GET_CALLBACK_CALLER(void); break;
75+
case DATA_TYPE_BOOL: pCallCallbackFunc = GET_CALLBACK_CALLER(bool); break;
76+
case DATA_TYPE_CHAR: pCallCallbackFunc = GET_CALLBACK_CALLER(char); break;
77+
case DATA_TYPE_UCHAR: pCallCallbackFunc = GET_CALLBACK_CALLER(unsigned char); break;
78+
case DATA_TYPE_SHORT: pCallCallbackFunc = GET_CALLBACK_CALLER(short); break;
79+
case DATA_TYPE_USHORT: pCallCallbackFunc = GET_CALLBACK_CALLER(unsigned short); break;
80+
case DATA_TYPE_INT: pCallCallbackFunc = GET_CALLBACK_CALLER(int); break;
81+
case DATA_TYPE_UINT: pCallCallbackFunc = GET_CALLBACK_CALLER(unsigned int); break;
82+
case DATA_TYPE_LONG: pCallCallbackFunc = GET_CALLBACK_CALLER(long); break;
83+
case DATA_TYPE_ULONG: pCallCallbackFunc = GET_CALLBACK_CALLER(unsigned long); break;
84+
case DATA_TYPE_LONGLONG: pCallCallbackFunc = GET_CALLBACK_CALLER(long long); break;
85+
case DATA_TYPE_ULONGLONG: pCallCallbackFunc = GET_CALLBACK_CALLER(unsigned long long); break;
86+
case DATA_TYPE_FLOAT: pCallCallbackFunc = GET_CALLBACK_CALLER(float); break;
87+
case DATA_TYPE_DOUBLE: pCallCallbackFunc = GET_CALLBACK_CALLER(double); break;
88+
case DATA_TYPE_POINTER: pCallCallbackFunc = GET_CALLBACK_CALLER(void *); break;
89+
case DATA_TYPE_STRING: pCallCallbackFunc = GET_CALLBACK_CALLER(char *); break;
90+
default: BOOST_RAISE_EXCEPTION(PyExc_ValueError, "Unknown return type.");
91+
}
92+
93+
// Generate the function
94+
Assembler a;
95+
96+
// Epilog
97+
a.push(ebp);
98+
a.mov(ebp, esp);
99+
100+
// Call callback caller
101+
a.push(ecx);
102+
a.push(ebp);
103+
a.push(imm((sysint_t) this));
104+
a.call(pCallCallbackFunc);
105+
a.add(esp, imm(12));
106+
107+
// Prolog
108+
a.mov(esp, ebp);
109+
a.pop(ebp);
110+
111+
// Return
112+
a.ret(imm(GetPopSize()));
113+
114+
m_ulAddr = (unsigned long) a.make();
115+
}
116+
117+
int CCallback::GetPopSize()
118+
{
119+
/*
120+
Who cleans up the stack?
121+
122+
Linux: Always the caller
123+
124+
Windows:
125+
CDECL: Caller
126+
STDCALL + THISCALL: Callee
127+
*/
128+
129+
#ifdef _WIN32
130+
if (m_eConv == CONV_CDECL)
131+
return 0;
132+
133+
int i = 0;
134+
135+
// Skip the this pointer. It's passed in an own register (ECX)
136+
if (m_eConv == CONV_THISCALL)
137+
i++;
138+
139+
int size = 0;
140+
for(; i < len(m_Args); i++)
141+
size += GetTypeSize(extract<DataType_t>(m_Args[i]));
142+
143+
return size;
144+
#endif
145+
return 0;
146+
}
147+
148+
int CCallback::GetArgumentOffset(int iIndex)
149+
{
150+
int offset = 8;
151+
if (iIndex == 0)
152+
return offset;
153+
154+
for(int i=0; i <= iIndex - 1; i++)
155+
{
156+
offset += GetTypeSize(extract<DataType_t>(m_Args[i]));
157+
}
158+
159+
// Subtract the this pointer on Windows
160+
#ifdef _WIN32
161+
if (m_eConv == CONV_THISCALL)
162+
offset -= sizeof(void *);
163+
#endif
164+
165+
return offset;
166+
}
167+
168+
void CCallback::Dealloc()
169+
{
170+
MemoryManager::getGlobal()->free((void *) m_ulAddr);
171+
m_ulAddr = 0;
172+
}
173+
174+
CPointer* CCallback::Realloc(int iSize)
175+
{
176+
BOOST_RAISE_EXCEPTION(PyExc_NotImplementedError, "This function is not implemented for callbacks.")
177+
return NULL;
178+
}
179+
180+
181+
// ============================================================================
182+
// >> FUNCTIONS
183+
// ============================================================================
184+
template<class T>
185+
T GetArgument(CCallback* pCallback, unsigned long ulEBP, unsigned long ulECX, int iIndex)
186+
{
187+
#ifdef _WIN32
188+
if (pCallback->m_eConv == CONV_THISCALL && iIndex == 0)
189+
return *(T *) &ulECX;
190+
#endif
191+
192+
return *(T *) (ulEBP + pCallback->GetArgumentOffset(iIndex));
193+
}
194+
195+
object CallCallback(CCallback* pCallback, unsigned long ulEBP, unsigned long ulECX)
196+
{
197+
// TODO: Make this crash proof
198+
BEGIN_BOOST_PY()
199+
list arg_list;
200+
for(int i=0; i < len(pCallback->m_Args); i++)
201+
{
202+
object val;
203+
switch(extract<DataType_t>(pCallback->m_Args[i]))
204+
{
205+
case DATA_TYPE_BOOL: val = object(GetArgument<bool>(pCallback, ulEBP, ulECX, i)); break;
206+
case DATA_TYPE_CHAR: val = object(GetArgument<char>(pCallback, ulEBP, ulECX, i)); break;
207+
case DATA_TYPE_UCHAR: val = object(GetArgument<unsigned char>(pCallback, ulEBP, ulECX, i)); break;
208+
case DATA_TYPE_SHORT: val = object(GetArgument<short>(pCallback, ulEBP, ulECX, i)); break;
209+
case DATA_TYPE_USHORT: val = object(GetArgument<unsigned short>(pCallback, ulEBP, ulECX, i)); break;
210+
case DATA_TYPE_INT: val = object(GetArgument<int>(pCallback, ulEBP, ulECX, i)); break;
211+
case DATA_TYPE_UINT: val = object(GetArgument<unsigned int>(pCallback, ulEBP, ulECX, i)); break;
212+
case DATA_TYPE_LONG: val = object(GetArgument<long>(pCallback, ulEBP, ulECX, i)); break;
213+
case DATA_TYPE_ULONG: val = object(GetArgument<unsigned long>(pCallback, ulEBP, ulECX, i)); break;
214+
case DATA_TYPE_LONGLONG: val = object(GetArgument<long long>(pCallback, ulEBP, ulECX, i)); break;
215+
case DATA_TYPE_ULONGLONG: val = object(GetArgument<unsigned long long>(pCallback, ulEBP, ulECX, i)); break;
216+
case DATA_TYPE_FLOAT: val = object(GetArgument<float>(pCallback, ulEBP, ulECX, i)); break;
217+
case DATA_TYPE_DOUBLE: val = object(GetArgument<double>(pCallback, ulEBP, ulECX, i)); break;
218+
case DATA_TYPE_POINTER: val = object(ptr(new CPointer(GetArgument<unsigned long>(pCallback, ulEBP, ulECX, i)))); break;
219+
case DATA_TYPE_STRING: val = object(GetArgument<const char*>(pCallback, ulEBP, ulECX, i)); break;
220+
default: BOOST_RAISE_EXCEPTION(PyExc_TypeError, "Unknown argument type."); break;
221+
}
222+
arg_list.append(val);
223+
}
224+
arg_list.append(ptr(new CPointer((unsigned long) ulEBP)));
225+
return eval("lambda func, args: func(*args)")(pCallback->m_oCallback, arg_list);
226+
227+
END_BOOST_PY_NORET()
228+
229+
PythonLog(0, "An exception occured while calling the Python callback. The server will now crash!");
230+
231+
// Throw an exception. We will crash now :(
232+
throw;
233+
}

0 commit comments

Comments
 (0)