[issue22273] abort when passing certain structs by value using ctypes
Eryk Sun
report at bugs.python.org
Mon Feb 20 15:48:21 EST 2017
Eryk Sun added the comment:
classify_argument is in Modules/_ctypes/libffi/src/x86/ffi64.c. This file is for the 64-bit Unix ABI. libffi doesn't use it for 64-bit Windows.
Some (all?) Linux distros link ctypes to the system libffi. In my experience, building 3.6 on Linux defaults to the system libffi, and I don't personally know how to override this. Configuring "--without-system-ffi" seems to be ignored.
Regarding your Test struct example, it's ok in this particular case to classify an 8-byte integer array the same as an 8-byte pointer. It doesn't abort() because the 2nd word isn't left unclassified (i.e. X86_64_NO_CLASS).
import ctypes
class Test(ctypes.Structure):
_fields_ = (('foo', ctypes.c_int),
('bar', ctypes.c_int),
('data', ctypes.c_uint8 * 8))
@ctypes.CFUNCTYPE(None, Test)
def func(t):
print('foo:', t.foo)
print('bar:', t.bar)
print('data:', t.data[:])
t = Test(5, 10, tuple(range(8)))
>>> hex(id(Test))
'0x9d8ad8'
The ctypes Structure has 3 elements. The first two are ffi_type_sint32 (FFI_TYPE_SINT32 == 10), and the third is ffi_type_pointer (FFI_TYPE_POINTER == 14):
(gdb) set $dict = (StgDictObject *)(((PyTypeObject *)0x9d8ad8)->tp_dict)
(gdb) p *$dict->ffi_type_pointer->elements[0]
$1 = {size = 4, alignment = 4, type = 10, elements = 0x0}
(gdb) p *$dict->ffi_type_pointer->elements[1]
$2 = {size = 4, alignment = 4, type = 10, elements = 0x0}
(gdb) p *$dict->ffi_type_pointer->elements[2]
$3 = {size = 8, alignment = 8, type = 14, elements = 0x0}
(gdb) p $dict->ffi_type_pointer->elements[3]
$4 = (struct _ffi_type *) 0x0
classify_argument() recursively classifies and merges these elements. The first two get merged as X86_64_INTEGER_CLASS, and the 'pointer' (actually an array) is X86_64_INTEGER_CLASS.
>>> func(t)
Breakpoint 1, ffi_call (cif=cif at entry=0x7fffffffd570, fn=fn at entry=0x7ffff7fee010,
rvalue=rvalue at entry=0x7fffffffd630, avalue=avalue at entry=0x7fffffffd610)
at ../src/x86/ffi64.c:424
[...snip...]
458 for (i = 0; i < avn; ++i)
(gdb)
462 n = examine_argument (arg_types[i], classes, 0, &ngpr, &nsse);
(gdb)
463 if (n == 0
(gdb) p n
$6 = 2
(gdb) p ngpr
$7 = 2
(gdb) p classes[0]
$8 = X86_64_INTEGER_CLASS
(gdb) p classes[1]
$9 = X86_64_INTEGER_CLASS
The struct is passed in two general-purpose integer registers, rdi and rsi:
Breakpoint 2, ffi_call_unix64 () at ../src/x86/unix64.S:49
49 movq (%rsp), %r10 /* Load return address. */
[...snip...]
76 call *%r11
(gdb) p/x $rdi
$10 = 0xa00000005
(gdb) p/x $rsi
$11 = 0x706050403020100
(gdb) c
Continuing.
foo: 5
bar: 10
data: [0, 1, 2, 3, 4, 5, 6, 7]
----------
_______________________________________
Python tracker <report at bugs.python.org>
<http://bugs.python.org/issue22273>
_______________________________________
More information about the Python-bugs-list
mailing list