bpo-34603, ctypes/libffi_msvc: Fix returning structs from functions (GH-9258)

This commit is contained in:
Vladimir Matveev 2018-09-15 22:36:29 -07:00 committed by Victor Stinner
parent 10a428b64b
commit 7843caeb90
7 changed files with 240 additions and 6 deletions

View File

@ -54,6 +54,24 @@ class FunctionCallTestCase(unittest.TestCase):
windll.user32.GetDesktopWindow() windll.user32.GetDesktopWindow()
@unittest.skipUnless(sys.platform == "win32", 'Windows-specific test')
class ReturnStructSizesTestCase(unittest.TestCase):
def test_sizes(self):
dll = CDLL(_ctypes_test.__file__)
for i in range(1, 11):
fields = [ (f"f{f}", c_char) for f in range(1, i + 1)]
class S(Structure):
_fields_ = fields
f = getattr(dll, f"TestSize{i}")
f.restype = S
res = f()
for i, f in enumerate(fields):
value = getattr(res, f[0])
expected = bytes([ord('a') + i])
self.assertEquals(value, expected)
@unittest.skipUnless(sys.platform == "win32", 'Windows-specific test') @unittest.skipUnless(sys.platform == "win32", 'Windows-specific test')
class TestWintypes(unittest.TestCase): class TestWintypes(unittest.TestCase):
def test_HWND(self): def test_HWND(self):

View File

@ -0,0 +1 @@
Fix returning structs from functions produced by MSVC

View File

@ -660,6 +660,200 @@ EXPORT(void) TwoOutArgs(int a, int *pi, int b, int *pj)
*pj += b; *pj += b;
} }
#ifdef MS_WIN32
typedef struct {
char f1;
} Size1;
typedef struct {
char f1;
char f2;
} Size2;
typedef struct {
char f1;
char f2;
char f3;
} Size3;
typedef struct {
char f1;
char f2;
char f3;
char f4;
} Size4;
typedef struct {
char f1;
char f2;
char f3;
char f4;
char f5;
} Size5;
typedef struct {
char f1;
char f2;
char f3;
char f4;
char f5;
char f6;
} Size6;
typedef struct {
char f1;
char f2;
char f3;
char f4;
char f5;
char f6;
char f7;
} Size7;
typedef struct {
char f1;
char f2;
char f3;
char f4;
char f5;
char f6;
char f7;
char f8;
} Size8;
typedef struct {
char f1;
char f2;
char f3;
char f4;
char f5;
char f6;
char f7;
char f8;
char f9;
} Size9;
typedef struct {
char f1;
char f2;
char f3;
char f4;
char f5;
char f6;
char f7;
char f8;
char f9;
char f10;
} Size10;
EXPORT(Size1) TestSize1() {
Size1 f;
f.f1 = 'a';
return f;
}
EXPORT(Size2) TestSize2() {
Size2 f;
f.f1 = 'a';
f.f2 = 'b';
return f;
}
EXPORT(Size3) TestSize3() {
Size3 f;
f.f1 = 'a';
f.f2 = 'b';
f.f3 = 'c';
return f;
}
EXPORT(Size4) TestSize4() {
Size4 f;
f.f1 = 'a';
f.f2 = 'b';
f.f3 = 'c';
f.f4 = 'd';
return f;
}
EXPORT(Size5) TestSize5() {
Size5 f;
f.f1 = 'a';
f.f2 = 'b';
f.f3 = 'c';
f.f4 = 'd';
f.f5 = 'e';
return f;
}
EXPORT(Size6) TestSize6() {
Size6 f;
f.f1 = 'a';
f.f2 = 'b';
f.f3 = 'c';
f.f4 = 'd';
f.f5 = 'e';
f.f6 = 'f';
return f;
}
EXPORT(Size7) TestSize7() {
Size7 f;
f.f1 = 'a';
f.f2 = 'b';
f.f3 = 'c';
f.f4 = 'd';
f.f5 = 'e';
f.f6 = 'f';
f.f7 = 'g';
return f;
}
EXPORT(Size8) TestSize8() {
Size8 f;
f.f1 = 'a';
f.f2 = 'b';
f.f3 = 'c';
f.f4 = 'd';
f.f5 = 'e';
f.f6 = 'f';
f.f7 = 'g';
f.f8 = 'h';
return f;
}
EXPORT(Size9) TestSize9() {
Size9 f;
f.f1 = 'a';
f.f2 = 'b';
f.f3 = 'c';
f.f4 = 'd';
f.f5 = 'e';
f.f6 = 'f';
f.f7 = 'g';
f.f8 = 'h';
f.f9 = 'i';
return f;
}
EXPORT(Size10) TestSize10() {
Size10 f;
f.f1 = 'a';
f.f2 = 'b';
f.f3 = 'c';
f.f4 = 'd';
f.f5 = 'e';
f.f6 = 'f';
f.f7 = 'g';
f.f8 = 'h';
f.f9 = 'i';
f.f10 = 'j';
return f;
}
#endif
#ifdef MS_WIN32 #ifdef MS_WIN32
EXPORT(S2H) __stdcall s_ret_2h_func(S2H inp) { return ret_2h_func(inp); } EXPORT(S2H) __stdcall s_ret_2h_func(S2H inp) { return ret_2h_func(inp); }
EXPORT(S8I) __stdcall s_ret_8i_func(S8I inp) { return ret_8i_func(inp); } EXPORT(S8I) __stdcall s_ret_8i_func(S8I inp) { return ret_8i_func(inp); }

View File

@ -715,9 +715,9 @@ ffi_type *_ctypes_get_ffi_type(PyObject *obj)
It returns small structures in registers It returns small structures in registers
*/ */
if (dict->ffi_type_pointer.type == FFI_TYPE_STRUCT) { if (dict->ffi_type_pointer.type == FFI_TYPE_STRUCT) {
if (dict->ffi_type_pointer.size <= 4) if (can_return_struct_as_int(dict->ffi_type_pointer.size))
return &ffi_type_sint32; return &ffi_type_sint32;
else if (dict->ffi_type_pointer.size <= 8) else if (can_return_struct_as_sint64 (dict->ffi_type_pointer.size))
return &ffi_type_sint64; return &ffi_type_sint64;
} }
#endif #endif

View File

@ -145,6 +145,21 @@ void ffi_prep_args(char *stack, extended_cif *ecif)
return; return;
} }
/*
Per: https://msdn.microsoft.com/en-us/library/7572ztz4.aspx
To be returned by value in RAX, user-defined types must have a length
of 1, 2, 4, 8, 16, 32, or 64 bits
*/
int can_return_struct_as_int(size_t s)
{
return s == 1 || s == 2 || s == 4;
}
int can_return_struct_as_sint64(size_t s)
{
return s == 8;
}
/* Perform machine dependent cif processing */ /* Perform machine dependent cif processing */
ffi_status ffi_prep_cif_machdep(ffi_cif *cif) ffi_status ffi_prep_cif_machdep(ffi_cif *cif)
{ {
@ -163,9 +178,9 @@ ffi_status ffi_prep_cif_machdep(ffi_cif *cif)
/* MSVC returns small structures in registers. Put in cif->flags /* MSVC returns small structures in registers. Put in cif->flags
the value FFI_TYPE_STRUCT only if the structure is big enough; the value FFI_TYPE_STRUCT only if the structure is big enough;
otherwise, put the 4- or 8-bytes integer type. */ otherwise, put the 4- or 8-bytes integer type. */
if (cif->rtype->size <= 4) if (can_return_struct_as_int(cif->rtype->size))
cif->flags = FFI_TYPE_INT; cif->flags = FFI_TYPE_INT;
else if (cif->rtype->size <= 8) else if (can_return_struct_as_sint64(cif->rtype->size))
cif->flags = FFI_TYPE_SINT64; cif->flags = FFI_TYPE_SINT64;
else else
cif->flags = FFI_TYPE_STRUCT; cif->flags = FFI_TYPE_STRUCT;

View File

@ -136,6 +136,9 @@ typedef struct _ffi_type
/*@null@*/ struct _ffi_type **elements; /*@null@*/ struct _ffi_type **elements;
} ffi_type; } ffi_type;
int can_return_struct_as_int(size_t);
int can_return_struct_as_sint64(size_t);
/* These are defined in types.c */ /* These are defined in types.c */
extern ffi_type ffi_type_void; extern ffi_type ffi_type_void;
extern ffi_type ffi_type_uint8; extern ffi_type ffi_type_uint8;

View File

@ -117,7 +117,8 @@ ffi_status ffi_prep_cif(/*@out@*/ /*@partial@*/ ffi_cif *cif,
/* Make space for the return structure pointer */ /* Make space for the return structure pointer */
if (cif->rtype->type == FFI_TYPE_STRUCT if (cif->rtype->type == FFI_TYPE_STRUCT
#ifdef _WIN32 #ifdef _WIN32
&& (cif->rtype->size > 8) /* MSVC returns small structs in registers */ && !can_return_struct_as_int(cif->rtype->size) /* MSVC returns small structs in registers */
&& !can_return_struct_as_sint64(cif->rtype->size)
#endif #endif
#ifdef SPARC #ifdef SPARC
&& (cif->abi != FFI_V9 || cif->rtype->size > 32) && (cif->abi != FFI_V9 || cif->rtype->size > 32)
@ -146,7 +147,9 @@ ffi_status ffi_prep_cif(/*@out@*/ /*@partial@*/ ffi_cif *cif,
bytes += sizeof(void*); bytes += sizeof(void*);
else else
#elif defined (_WIN64) #elif defined (_WIN64)
if ((*ptr)->type == FFI_TYPE_STRUCT && ((*ptr)->size > 8)) if ((*ptr)->type == FFI_TYPE_STRUCT &&
!can_return_struct_as_int((*ptr)->size) &&
!can_return_struct_as_sint64((*ptr)->size))
bytes += sizeof(void*); bytes += sizeof(void*);
else else
#endif #endif