Skip to content

Commit b63a16f

Browse files
vladimavstinner
authored andcommitted
[2.7] bpo-34603, ctypes/libffi_msvc: Fix returning structs from functions (GH-9258) (GH-9425)
Co-authored-by: Vladimir Matveev <v2matveev@outlook.com>
1 parent 29034ba commit b63a16f

File tree

7 files changed

+240
-6
lines changed

7 files changed

+240
-6
lines changed

Lib/ctypes/test/test_win32.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,24 @@ def test_noargs(self):
5252
# This is a special case on win32 x64
5353
windll.user32.GetDesktopWindow()
5454

55+
@unittest.skipUnless(sys.platform == "win32", 'Windows-specific test')
56+
class ReturnStructSizesTestCase(unittest.TestCase):
57+
def test_sizes(self):
58+
dll = CDLL(_ctypes_test.__file__)
59+
for i in range(1, 11):
60+
fields = [ ("f%d" % f, c_char) for f in range(1, i + 1)]
61+
class S(Structure):
62+
_fields_ = fields
63+
f = getattr(dll, "TestSize%d" % i)
64+
f.restype = S
65+
res = f()
66+
for i, f in enumerate(fields):
67+
value = getattr(res, f[0])
68+
expected = chr(ord('a') + i)
69+
self.assertEquals(value, expected)
70+
71+
72+
5573
@unittest.skipUnless(sys.platform == "win32", 'Windows-specific test')
5674
class TestWintypes(unittest.TestCase):
5775
def test_HWND(self):
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix returning structs from functions produced by MSVC

Modules/_ctypes/_ctypes_test.c

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,200 @@ EXPORT(void) TwoOutArgs(int a, int *pi, int b, int *pj)
655655
*pj += b;
656656
}
657657

658+
#ifdef MS_WIN32
659+
660+
typedef struct {
661+
char f1;
662+
} Size1;
663+
664+
typedef struct {
665+
char f1;
666+
char f2;
667+
} Size2;
668+
669+
typedef struct {
670+
char f1;
671+
char f2;
672+
char f3;
673+
} Size3;
674+
675+
typedef struct {
676+
char f1;
677+
char f2;
678+
char f3;
679+
char f4;
680+
} Size4;
681+
682+
typedef struct {
683+
char f1;
684+
char f2;
685+
char f3;
686+
char f4;
687+
char f5;
688+
} Size5;
689+
690+
typedef struct {
691+
char f1;
692+
char f2;
693+
char f3;
694+
char f4;
695+
char f5;
696+
char f6;
697+
} Size6;
698+
699+
typedef struct {
700+
char f1;
701+
char f2;
702+
char f3;
703+
char f4;
704+
char f5;
705+
char f6;
706+
char f7;
707+
} Size7;
708+
709+
typedef struct {
710+
char f1;
711+
char f2;
712+
char f3;
713+
char f4;
714+
char f5;
715+
char f6;
716+
char f7;
717+
char f8;
718+
} Size8;
719+
720+
typedef struct {
721+
char f1;
722+
char f2;
723+
char f3;
724+
char f4;
725+
char f5;
726+
char f6;
727+
char f7;
728+
char f8;
729+
char f9;
730+
} Size9;
731+
732+
typedef struct {
733+
char f1;
734+
char f2;
735+
char f3;
736+
char f4;
737+
char f5;
738+
char f6;
739+
char f7;
740+
char f8;
741+
char f9;
742+
char f10;
743+
} Size10;
744+
745+
EXPORT(Size1) TestSize1() {
746+
Size1 f;
747+
f.f1 = 'a';
748+
return f;
749+
}
750+
751+
EXPORT(Size2) TestSize2() {
752+
Size2 f;
753+
f.f1 = 'a';
754+
f.f2 = 'b';
755+
return f;
756+
}
757+
758+
EXPORT(Size3) TestSize3() {
759+
Size3 f;
760+
f.f1 = 'a';
761+
f.f2 = 'b';
762+
f.f3 = 'c';
763+
return f;
764+
}
765+
766+
EXPORT(Size4) TestSize4() {
767+
Size4 f;
768+
f.f1 = 'a';
769+
f.f2 = 'b';
770+
f.f3 = 'c';
771+
f.f4 = 'd';
772+
return f;
773+
}
774+
775+
EXPORT(Size5) TestSize5() {
776+
Size5 f;
777+
f.f1 = 'a';
778+
f.f2 = 'b';
779+
f.f3 = 'c';
780+
f.f4 = 'd';
781+
f.f5 = 'e';
782+
return f;
783+
}
784+
785+
EXPORT(Size6) TestSize6() {
786+
Size6 f;
787+
f.f1 = 'a';
788+
f.f2 = 'b';
789+
f.f3 = 'c';
790+
f.f4 = 'd';
791+
f.f5 = 'e';
792+
f.f6 = 'f';
793+
return f;
794+
}
795+
796+
EXPORT(Size7) TestSize7() {
797+
Size7 f;
798+
f.f1 = 'a';
799+
f.f2 = 'b';
800+
f.f3 = 'c';
801+
f.f4 = 'd';
802+
f.f5 = 'e';
803+
f.f6 = 'f';
804+
f.f7 = 'g';
805+
return f;
806+
}
807+
808+
EXPORT(Size8) TestSize8() {
809+
Size8 f;
810+
f.f1 = 'a';
811+
f.f2 = 'b';
812+
f.f3 = 'c';
813+
f.f4 = 'd';
814+
f.f5 = 'e';
815+
f.f6 = 'f';
816+
f.f7 = 'g';
817+
f.f8 = 'h';
818+
return f;
819+
}
820+
821+
EXPORT(Size9) TestSize9() {
822+
Size9 f;
823+
f.f1 = 'a';
824+
f.f2 = 'b';
825+
f.f3 = 'c';
826+
f.f4 = 'd';
827+
f.f5 = 'e';
828+
f.f6 = 'f';
829+
f.f7 = 'g';
830+
f.f8 = 'h';
831+
f.f9 = 'i';
832+
return f;
833+
}
834+
835+
EXPORT(Size10) TestSize10() {
836+
Size10 f;
837+
f.f1 = 'a';
838+
f.f2 = 'b';
839+
f.f3 = 'c';
840+
f.f4 = 'd';
841+
f.f5 = 'e';
842+
f.f6 = 'f';
843+
f.f7 = 'g';
844+
f.f8 = 'h';
845+
f.f9 = 'i';
846+
f.f10 = 'j';
847+
return f;
848+
}
849+
850+
#endif
851+
658852
#ifdef MS_WIN32
659853
EXPORT(S2H) __stdcall s_ret_2h_func(S2H inp) { return ret_2h_func(inp); }
660854
EXPORT(S8I) __stdcall s_ret_8i_func(S8I inp) { return ret_8i_func(inp); }

Modules/_ctypes/callproc.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -747,9 +747,9 @@ ffi_type *_ctypes_get_ffi_type(PyObject *obj)
747747
It returns small structures in registers
748748
*/
749749
if (dict->ffi_type_pointer.type == FFI_TYPE_STRUCT) {
750-
if (dict->ffi_type_pointer.size <= 4)
750+
if (can_return_struct_as_int(dict->ffi_type_pointer.size))
751751
return &ffi_type_sint32;
752-
else if (dict->ffi_type_pointer.size <= 8)
752+
else if (can_return_struct_as_sint64 (dict->ffi_type_pointer.size))
753753
return &ffi_type_sint64;
754754
}
755755
#endif

Modules/_ctypes/libffi_msvc/ffi.c

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,21 @@ void ffi_prep_args(char *stack, extended_cif *ecif)
126126
return;
127127
}
128128

129+
/*
130+
Per: https://msdn.microsoft.com/en-us/library/7572ztz4.aspx
131+
To be returned by value in RAX, user-defined types must have a length
132+
of 1, 2, 4, 8, 16, 32, or 64 bits
133+
*/
134+
int can_return_struct_as_int(size_t s)
135+
{
136+
return s == 1 || s == 2 || s == 4;
137+
}
138+
139+
int can_return_struct_as_sint64(size_t s)
140+
{
141+
return s == 8;
142+
}
143+
129144
/* Perform machine dependent cif processing */
130145
ffi_status ffi_prep_cif_machdep(ffi_cif *cif)
131146
{
@@ -144,9 +159,9 @@ ffi_status ffi_prep_cif_machdep(ffi_cif *cif)
144159
/* MSVC returns small structures in registers. Put in cif->flags
145160
the value FFI_TYPE_STRUCT only if the structure is big enough;
146161
otherwise, put the 4- or 8-bytes integer type. */
147-
if (cif->rtype->size <= 4)
162+
if (can_return_struct_as_int(cif->rtype->size))
148163
cif->flags = FFI_TYPE_INT;
149-
else if (cif->rtype->size <= 8)
164+
else if (can_return_struct_as_sint64(cif->rtype->size))
150165
cif->flags = FFI_TYPE_SINT64;
151166
else
152167
cif->flags = FFI_TYPE_STRUCT;

Modules/_ctypes/libffi_msvc/ffi.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,9 @@ typedef struct _ffi_type
136136
/*@null@*/ struct _ffi_type **elements;
137137
} ffi_type;
138138

139+
int can_return_struct_as_int(size_t);
140+
int can_return_struct_as_sint64(size_t);
141+
139142
/* These are defined in types.c */
140143
extern ffi_type ffi_type_void;
141144
extern ffi_type ffi_type_uint8;

Modules/_ctypes/libffi_msvc/prep_cif.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,8 @@ ffi_status ffi_prep_cif(/*@out@*/ /*@partial@*/ ffi_cif *cif,
117117
/* Make space for the return structure pointer */
118118
if (cif->rtype->type == FFI_TYPE_STRUCT
119119
#ifdef _WIN32
120-
&& (cif->rtype->size > 8) /* MSVC returns small structs in registers */
120+
&& !can_return_struct_as_int(cif->rtype->size) /* MSVC returns small structs in registers */
121+
&& !can_return_struct_as_sint64(cif->rtype->size)
121122
#endif
122123
#ifdef SPARC
123124
&& (cif->abi != FFI_V9 || cif->rtype->size > 32)
@@ -146,7 +147,9 @@ ffi_status ffi_prep_cif(/*@out@*/ /*@partial@*/ ffi_cif *cif,
146147
bytes += sizeof(void*);
147148
else
148149
#elif defined (_WIN64)
149-
if ((*ptr)->type == FFI_TYPE_STRUCT && ((*ptr)->size > 8))
150+
if ((*ptr)->type == FFI_TYPE_STRUCT &&
151+
!can_return_struct_as_int((*ptr)->size) &&
152+
!can_return_struct_as_sint64((*ptr)->size))
150153
bytes += sizeof(void*);
151154
else
152155
#endif

0 commit comments

Comments
 (0)