如何在 ../src/x86/ffi64.c:158 处修复 Python 函数 "classify_argument" 中的段错误
How to fix segfault in Python function "classify_argument" at ../src/x86/ffi64.c:158
简介
我正在 python 开发程序。它使用使用 SWIG 链接到 python 的 C 库。
C 库是 TCP 服务器的某种王者,它在单独的 C 线程中处理客户端连接。这些线程从客户端接收数据,然后进行基本的传入数据处理和解析。之后,解析后的数据通过 "Python function callback" 发送到 python。结果 "Python function callback" 然后在 C 中处理并发送回客户端。
我已经解决了一些由我的代码中的错误引起的段错误(双重释放内存,NULL 指针评估)。
但现在我在 Python 解释器代码中遇到了段错误。而且我不明白如何解决它。
调试
我使用 gdb 进行了回溯(为清楚起见,省略了从 5 到 11 的堆栈帧):
#0 classify_argument (type=0x55, classes=0x0, byte_offset=byte_offset@entry=0) at ../src/x86/ffi64.c:158
#1 0x00007ffff4b4212c in examine_argument (type=<optimized out>, classes=classes@entry=0x7fffeb522c60, in_return=in_return@entry=false, pngpr=pngpr@entry=0x7fffeb522c58, pnsse=pnsse@entry=0x7fffeb522c5c) at ../src/x86/ffi64.c:314
#2 0x00007ffff4b4296f in ffi_closure_unix64_inner (closure=0x7ffff7ff20f0, rvalue=0x7fffe4024820, reg_args=0x7fffeb522cc0, argp=0x7fffeb522d90 "") at ../src/x86/ffi64.c:615
#3 0x00007ffff4b42de4 in ffi_closure_unix64 () at ../src/x86/unix64.S:229
#4 0x00007ffff5fd0b21 in MyCCodeConnectionHandler (parameter=0x7ffff7ff20f0, connection=0x7fffe4024820, event=SERVER_CONNECTION_CLOSED) at my_code/server.c:586
...
#12 0x00007ffff7bc4184 in start_thread (arg=0x7fffeb523700) at pthread_create.c:312
#13 0x00007ffff71e403d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:111
当我尝试在 gdb 中打印 type->type 变量时,我看到:
(gdb) print type->type
Cannot access memory at address 0x5f
似乎函数 classify_argument 的 type 参数无效,因为我知道从 0x0000000000000000 到 0x000000000000FFFF 的地址用于空指针检测。这就是 SegFault 的原因。
我已经使用断点调试了对函数 classify_argument 的其他调用,它告诉我 type 参数接收到另一种值:
classify_argument (type=0x7ffff6662ee8, classes=0x1, byte_offset=byte_offset@entry=0) at ../src/x86/ffi64.c:158
classify_argument (type=0x7ffff65e7e10, classes=0x0, byte_offset=byte_offset@entry=0) at ../src/x86/ffi64.c:158
classify_argument (type=0x7ffff65e7e10, classes=0x0, byte_offset=byte_offset@entry=0) at ../src/x86/ffi64.c:158
并且没有段错误。
代码及辅助信息
这是一块../src/x86/ffi64.c:
155 static size_t
156 classify_argument (ffi_type *type, enum x86_64_reg_class classes[],
157 size_t byte_offset)
158 {
159 switch (type->type)
160 {
161 case FFI_TYPE_UINT8:
162 case FFI_TYPE_SINT8:
163 case FFI_TYPE_UINT16:
164 case FFI_TYPE_SINT16:
165 case FFI_TYPE_UINT32:
166 case FFI_TYPE_SINT32:
167 case FFI_TYPE_UINT64:
168 case FFI_TYPE_SINT64:
169 case FFI_TYPE_POINTER:
170 {
171 size_t size = byte_offset + type->size;
172
173 if (size <= 4)
174 {
175 classes[0] = X86_64_INTEGERSI_CLASS;
176 return 1;
177 }
178 else if (size <= 8)
179 {
180 classes[0] = X86_64_INTEGER_CLASS;
我知道在从 C 线程调用 python 代码之前需要获取 GIL 锁。这里有一段my_code/server.c
...
static void
MyCCodeConnectionHandler(void* parameter, ConnectionStruct *connection, ConnectionEvent event)
{
if(parameter != NULL) { //parameter is a pointer to Python callback
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
((void (*)(long, long))parameter)((long)connection, event);
PyGILState_Release(gstate);
}
}
...
paramter 参数在 Python
中具有以下 CTYPES 定义
ConnectionHandlerFuncType = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_long)
并在传递给 C
之前使用此 Python 代码进行转换
def py_callback(connection, event):
# some code here
ctype_function_wrapper = ConnectionHandlerFuncType(py_callback)
cfunction_pointer = ctypes.cast(ctype_function_wrapper, ctypes.c_void_p).value
# cfunction_pointer is passed as parameter arg to MyCCodeConnectionHandler
请帮我解决这个段错误。
Directors 解决了我的问题。似乎这是在 SWIG 中进行回调的唯一正确方法。我将回调传递给 C 级的方式是错误的。
这是我的 C 回调现在的样子:
static void
MyCCodeConnectionHandler(void* parameter, ConnectionStruct *connection, ConnectionEvent event)
{
//parameter contains (ConnectionCallbackDirector *) value casted to (void *)
if(parameter != NULL) {
PyGILState_STATE gstate = PyGILState_Ensure();
((ConnectionCallbackDirector *)parameter)->run(connection, event);
PyGILState_Release(gstate);
}
}
导演代码:
class ConnectionCallbackDirector {
public:
virtual ~ConnectionCallbackDirector() {}
virtual void run(ConnectionStruct *, ConnectionEvent) {}
};
我已将它们放在 SWIG 的 *.i 文件中,enabled director feature for SWIG module and ConnectionCallbackDirector class
。
在python中我写了这个class继承自ConnectionCallbackDirector
class ConnectionCallback(my_c_lib.ConnectionCallbackDirector):
def __init__(self, pycallback):
my_c_lib.ConnectionCallbackDirector.__init__(self)
self._pycallback = pycallback
def run(self, connection, event):
return self._pycallback(connection, event)
并将从 ConnectionCallback class 创建的对象传递给 C 代码。
现在一切都很好!
简介
我正在 python 开发程序。它使用使用 SWIG 链接到 python 的 C 库。 C 库是 TCP 服务器的某种王者,它在单独的 C 线程中处理客户端连接。这些线程从客户端接收数据,然后进行基本的传入数据处理和解析。之后,解析后的数据通过 "Python function callback" 发送到 python。结果 "Python function callback" 然后在 C 中处理并发送回客户端。
我已经解决了一些由我的代码中的错误引起的段错误(双重释放内存,NULL 指针评估)。
但现在我在 Python 解释器代码中遇到了段错误。而且我不明白如何解决它。
调试
我使用 gdb 进行了回溯(为清楚起见,省略了从 5 到 11 的堆栈帧):
#0 classify_argument (type=0x55, classes=0x0, byte_offset=byte_offset@entry=0) at ../src/x86/ffi64.c:158
#1 0x00007ffff4b4212c in examine_argument (type=<optimized out>, classes=classes@entry=0x7fffeb522c60, in_return=in_return@entry=false, pngpr=pngpr@entry=0x7fffeb522c58, pnsse=pnsse@entry=0x7fffeb522c5c) at ../src/x86/ffi64.c:314
#2 0x00007ffff4b4296f in ffi_closure_unix64_inner (closure=0x7ffff7ff20f0, rvalue=0x7fffe4024820, reg_args=0x7fffeb522cc0, argp=0x7fffeb522d90 "") at ../src/x86/ffi64.c:615
#3 0x00007ffff4b42de4 in ffi_closure_unix64 () at ../src/x86/unix64.S:229
#4 0x00007ffff5fd0b21 in MyCCodeConnectionHandler (parameter=0x7ffff7ff20f0, connection=0x7fffe4024820, event=SERVER_CONNECTION_CLOSED) at my_code/server.c:586
...
#12 0x00007ffff7bc4184 in start_thread (arg=0x7fffeb523700) at pthread_create.c:312
#13 0x00007ffff71e403d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:111
当我尝试在 gdb 中打印 type->type 变量时,我看到:
(gdb) print type->type
Cannot access memory at address 0x5f
似乎函数 classify_argument 的 type 参数无效,因为我知道从 0x0000000000000000 到 0x000000000000FFFF 的地址用于空指针检测。这就是 SegFault 的原因。
我已经使用断点调试了对函数 classify_argument 的其他调用,它告诉我 type 参数接收到另一种值:
classify_argument (type=0x7ffff6662ee8, classes=0x1, byte_offset=byte_offset@entry=0) at ../src/x86/ffi64.c:158
classify_argument (type=0x7ffff65e7e10, classes=0x0, byte_offset=byte_offset@entry=0) at ../src/x86/ffi64.c:158
classify_argument (type=0x7ffff65e7e10, classes=0x0, byte_offset=byte_offset@entry=0) at ../src/x86/ffi64.c:158
并且没有段错误。
代码及辅助信息
这是一块../src/x86/ffi64.c:
155 static size_t
156 classify_argument (ffi_type *type, enum x86_64_reg_class classes[],
157 size_t byte_offset)
158 {
159 switch (type->type)
160 {
161 case FFI_TYPE_UINT8:
162 case FFI_TYPE_SINT8:
163 case FFI_TYPE_UINT16:
164 case FFI_TYPE_SINT16:
165 case FFI_TYPE_UINT32:
166 case FFI_TYPE_SINT32:
167 case FFI_TYPE_UINT64:
168 case FFI_TYPE_SINT64:
169 case FFI_TYPE_POINTER:
170 {
171 size_t size = byte_offset + type->size;
172
173 if (size <= 4)
174 {
175 classes[0] = X86_64_INTEGERSI_CLASS;
176 return 1;
177 }
178 else if (size <= 8)
179 {
180 classes[0] = X86_64_INTEGER_CLASS;
我知道在从 C 线程调用 python 代码之前需要获取 GIL 锁。这里有一段my_code/server.c
...
static void
MyCCodeConnectionHandler(void* parameter, ConnectionStruct *connection, ConnectionEvent event)
{
if(parameter != NULL) { //parameter is a pointer to Python callback
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
((void (*)(long, long))parameter)((long)connection, event);
PyGILState_Release(gstate);
}
}
...
paramter 参数在 Python
中具有以下 CTYPES 定义ConnectionHandlerFuncType = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_long)
并在传递给 C
之前使用此 Python 代码进行转换def py_callback(connection, event):
# some code here
ctype_function_wrapper = ConnectionHandlerFuncType(py_callback)
cfunction_pointer = ctypes.cast(ctype_function_wrapper, ctypes.c_void_p).value
# cfunction_pointer is passed as parameter arg to MyCCodeConnectionHandler
请帮我解决这个段错误。
Directors 解决了我的问题。似乎这是在 SWIG 中进行回调的唯一正确方法。我将回调传递给 C 级的方式是错误的。
这是我的 C 回调现在的样子:
static void
MyCCodeConnectionHandler(void* parameter, ConnectionStruct *connection, ConnectionEvent event)
{
//parameter contains (ConnectionCallbackDirector *) value casted to (void *)
if(parameter != NULL) {
PyGILState_STATE gstate = PyGILState_Ensure();
((ConnectionCallbackDirector *)parameter)->run(connection, event);
PyGILState_Release(gstate);
}
}
导演代码:
class ConnectionCallbackDirector {
public:
virtual ~ConnectionCallbackDirector() {}
virtual void run(ConnectionStruct *, ConnectionEvent) {}
};
我已将它们放在 SWIG 的 *.i 文件中,enabled director feature for SWIG module and ConnectionCallbackDirector class
。
在python中我写了这个class继承自ConnectionCallbackDirector
class ConnectionCallback(my_c_lib.ConnectionCallbackDirector):
def __init__(self, pycallback):
my_c_lib.ConnectionCallbackDirector.__init__(self)
self._pycallback = pycallback
def run(self, connection, event):
return self._pycallback(connection, event)
并将从 ConnectionCallback class 创建的对象传递给 C 代码。
现在一切都很好!