Skip to content

Commit e536320

Browse files
bpo-34603, ctypes/libffi_msvc: Fix returning structs from functions (GH-9258)
(cherry picked from commit 7843cae) Co-authored-by: Vladimir Matveev <[email protected]>
1 parent 03d8e7c commit e536320

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
@@ -54,6 +54,24 @@ def test_noargs(self):
5454
windll.user32.GetDesktopWindow()
5555

5656

57+
@unittest.skipUnless(sys.platform == "win32", 'Windows-specific test')
58+
class ReturnStructSizesTestCase(unittest.TestCase):
59+
def test_sizes(self):
60+
dll = CDLL(_ctypes_test.__file__)
61+
for i in range(1, 11):
62+
fields = [ (f"f{f}", c_char) for f in range(1, i + 1)]
63+
class S(Structure):
64+
_fields_ = fields
65+
f = getattr(dll, f"TestSize{i}")
66+
f.restype = S
67+
res = f()
68+
for i, f in enumerate(fields):
69+
value = getattr(res, f[0])
70+
expected = bytes([ord('a') + i])
71+
self.assertEquals(value, expected)
72+
73+
74+
5775
@unittest.skipUnless(sys.platform == "win32", 'Windows-specific test')
5876
class TestWintypes(unittest.TestCase):
5977
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
@@ -662,6 +662,200 @@ EXPORT(void) TwoOutArgs(int a, int *pi, int b, int *pj)
662662
*pj += b;
663663
}
664664

665+
#ifdef MS_WIN32
666+
667+
typedef struct {
668+
char f1;
669+
} Size1;
670+
671+
typedef struct {
672+
char f1;
673+
char f2;
674+
} Size2;
675+
676+
typedef struct {
677+
char f1;
678+
char f2;
679+
char f3;
680+
} Size3;
681+
682+
typedef struct {
683+
char f1;
684+
char f2;
685+
char f3;
686+
char f4;
687+
} Size4;
688+
689+
typedef struct {
690+
char f1;
691+
char f2;
692+
char f3;
693+
char f4;
694+
char f5;
695+
} Size5;
696+
697+
typedef struct {
698+
char f1;
699+
char f2;
700+
char f3;
701+
char f4;
702+
char f5;
703+
char f6;
704+
} Size6;
705+
706+
typedef struct {
707+
char f1;
708+
char f2;
709+
char f3;
710+
char f4;
711+
char f5;
712+
char f6;
713+
char f7;
714+
} Size7;
715+
716+
typedef struct {
717+
char f1;
718+
char f2;
719+
char f3;
720+
char f4;
721+
char f5;
722+
char f6;
723+
char f7;
724+
char f8;
725+
} Size8;
726+
727+
typedef struct {
728+
char f1;
729+
char f2;
730+
char f3;
731+
char f4;
732+
char f5;
733+
char f6;
734+
char f7;
735+
char f8;
736+
char f9;
737+
} Size9;
738+
739+
typedef struct {
740+
char f1;
741+
char f2;
742+
char f3;
743+
char f4;
744+
char f5;
745+
char f6;
746+
char f7;
747+
char f8;
748+
char f9;
749+
char f10;
750+
} Size10;
751+
752+
EXPORT(Size1) TestSize1() {
753+
Size1 f;
754+
f.f1 = 'a';
755+
return f;
756+
}
757+
758+
EXPORT(Size2) TestSize2() {
759+
Size2 f;
760+
f.f1 = 'a';
761+
f.f2 = 'b';
762+
return f;
763+
}
764+
765+
EXPORT(Size3) TestSize3() {
766+
Size3 f;
767+
f.f1 = 'a';
768+
f.f2 = 'b';
769+
f.f3 = 'c';
770+
return f;
771+
}
772+
773+
EXPORT(Size4) TestSize4() {
774+
Size4 f;
775+
f.f1 = 'a';
776+
f.f2 = 'b';
777+
f.f3 = 'c';
778+
f.f4 = 'd';
779+
return f;
780+
}
781+
782+
EXPORT(Size5) TestSize5() {
783+
Size5 f;
784+
f.f1 = 'a';
785+
f.f2 = 'b';
786+
f.f3 = 'c';
787+
f.f4 = 'd';
788+
f.f5 = 'e';
789+
return f;
790+
}
791+
792+
EXPORT(Size6) TestSize6() {
793+
Size6 f;
794+
f.f1 = 'a';
795+
f.f2 = 'b';
796+
f.f3 = 'c';
797+
f.f4 = 'd';
798+
f.f5 = 'e';
799+
f.f6 = 'f';
800+
return f;
801+
}
802+
803+
EXPORT(Size7) TestSize7() {
804+
Size7 f;
805+
f.f1 = 'a';
806+
f.f2 = 'b';
807+
f.f3 = 'c';
808+
f.f4 = 'd';
809+
f.f5 = 'e';
810+
f.f6 = 'f';
811+
f.f7 = 'g';
812+
return f;
813+
}
814+
815+
EXPORT(Size8) TestSize8() {
816+
Size8 f;
817+
f.f1 = 'a';
818+
f.f2 = 'b';
819+
f.f3 = 'c';
820+
f.f4 = 'd';
821+
f.f5 = 'e';
822+
f.f6 = 'f';
823+
f.f7 = 'g';
824+
f.f8 = 'h';
825+
return f;
826+
}
827+
828+
EXPORT(Size9) TestSize9() {
829+
Size9 f;
830+
f.f1 = 'a';
831+
f.f2 = 'b';
832+
f.f3 = 'c';
833+
f.f4 = 'd';
834+
f.f5 = 'e';
835+
f.f6 = 'f';
836+
f.f7 = 'g';
837+
f.f8 = 'h';
838+
f.f9 = 'i';
839+
return f;
840+
}
841+
842+
EXPORT(Size10) TestSize10() {
843+
Size10 f;
844+
f.f1 = 'a';
845+
f.f2 = 'b';
846+
f.f3 = 'c';
847+
f.f4 = 'd';
848+
f.f5 = 'e';
849+
f.f6 = 'f';
850+
f.f7 = 'g';
851+
f.f8 = 'h';
852+
f.f9 = 'i';
853+
f.f10 = 'j';
854+
return f;
855+
}
856+
857+
#endif
858+
665859
#ifdef MS_WIN32
666860
EXPORT(S2H) __stdcall s_ret_2h_func(S2H inp) { return ret_2h_func(inp); }
667861
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
@@ -715,9 +715,9 @@ ffi_type *_ctypes_get_ffi_type(PyObject *obj)
715715
It returns small structures in registers
716716
*/
717717
if (dict->ffi_type_pointer.type == FFI_TYPE_STRUCT) {
718-
if (dict->ffi_type_pointer.size <= 4)
718+
if (can_return_struct_as_int(dict->ffi_type_pointer.size))
719719
return &ffi_type_sint32;
720-
else if (dict->ffi_type_pointer.size <= 8)
720+
else if (can_return_struct_as_sint64 (dict->ffi_type_pointer.size))
721721
return &ffi_type_sint64;
722722
}
723723
#endif

‎Modules/_ctypes/libffi_msvc/ffi.c‎

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,21 @@ void ffi_prep_args(char *stack, extended_cif *ecif)
145145
return;
146146
}
147147

148+
/*
149+
Per: https://msdn.microsoft.com/en-us/library/7572ztz4.aspx
150+
To be returned by value in RAX, user-defined types must have a length
151+
of 1, 2, 4, 8, 16, 32, or 64 bits
152+
*/
153+
int can_return_struct_as_int(size_t s)
154+
{
155+
return s == 1 || s == 2 || s == 4;
156+
}
157+
158+
int can_return_struct_as_sint64(size_t s)
159+
{
160+
return s == 8;
161+
}
162+
148163
/* Perform machine dependent cif processing */
149164
ffi_status ffi_prep_cif_machdep(ffi_cif *cif)
150165
{
@@ -163,9 +178,9 @@ ffi_status ffi_prep_cif_machdep(ffi_cif *cif)
163178
/* MSVC returns small structures in registers. Put in cif->flags
164179
the value FFI_TYPE_STRUCT only if the structure is big enough;
165180
otherwise, put the 4- or 8-bytes integer type. */
166-
if (cif->rtype->size <= 4)
181+
if (can_return_struct_as_int(cif->rtype->size))
167182
cif->flags = FFI_TYPE_INT;
168-
else if (cif->rtype->size <= 8)
183+
else if (can_return_struct_as_sint64(cif->rtype->size))
169184
cif->flags = FFI_TYPE_SINT64;
170185
else
171186
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)