Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3.7] bpo-34603, ctypes/libffi_msvc: Fix returning structs from functions (GH-9258) #9340

Merged
merged 1 commit into from Sep 16, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 18 additions & 0 deletions Lib/ctypes/test/test_win32.py
Expand Up @@ -54,6 +54,24 @@ def test_noargs(self):
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')
class TestWintypes(unittest.TestCase):
def test_HWND(self):
Expand Down
@@ -0,0 +1 @@
Fix returning structs from functions produced by MSVC
194 changes: 194 additions & 0 deletions Modules/_ctypes/_ctypes_test.c
Expand Up @@ -660,6 +660,200 @@ EXPORT(void) TwoOutArgs(int a, int *pi, int b, int *pj)
*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
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); }
Expand Down
4 changes: 2 additions & 2 deletions Modules/_ctypes/callproc.c
Expand Up @@ -715,9 +715,9 @@ ffi_type *_ctypes_get_ffi_type(PyObject *obj)
It returns small structures in registers
*/
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;
else if (dict->ffi_type_pointer.size <= 8)
else if (can_return_struct_as_sint64 (dict->ffi_type_pointer.size))
return &ffi_type_sint64;
}
#endif
Expand Down
19 changes: 17 additions & 2 deletions Modules/_ctypes/libffi_msvc/ffi.c
Expand Up @@ -145,6 +145,21 @@ void ffi_prep_args(char *stack, extended_cif *ecif)
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 */
ffi_status ffi_prep_cif_machdep(ffi_cif *cif)
{
Expand All @@ -163,9 +178,9 @@ ffi_status ffi_prep_cif_machdep(ffi_cif *cif)
/* MSVC returns small structures in registers. Put in cif->flags
the value FFI_TYPE_STRUCT only if the structure is big enough;
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;
else if (cif->rtype->size <= 8)
else if (can_return_struct_as_sint64(cif->rtype->size))
cif->flags = FFI_TYPE_SINT64;
else
cif->flags = FFI_TYPE_STRUCT;
Expand Down
3 changes: 3 additions & 0 deletions Modules/_ctypes/libffi_msvc/ffi.h
Expand Up @@ -136,6 +136,9 @@ typedef struct _ffi_type
/*@null@*/ struct _ffi_type **elements;
} ffi_type;

int can_return_struct_as_int(size_t);
int can_return_struct_as_sint64(size_t);

/* These are defined in types.c */
extern ffi_type ffi_type_void;
extern ffi_type ffi_type_uint8;
Expand Down
7 changes: 5 additions & 2 deletions Modules/_ctypes/libffi_msvc/prep_cif.c
Expand Up @@ -117,7 +117,8 @@ ffi_status ffi_prep_cif(/*@out@*/ /*@partial@*/ ffi_cif *cif,
/* Make space for the return structure pointer */
if (cif->rtype->type == FFI_TYPE_STRUCT
#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
#ifdef SPARC
&& (cif->abi != FFI_V9 || cif->rtype->size > 32)
Expand Down Expand Up @@ -146,7 +147,9 @@ ffi_status ffi_prep_cif(/*@out@*/ /*@partial@*/ ffi_cif *cif,
bytes += sizeof(void*);
else
#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*);
else
#endif
Expand Down