Skip to content

Commit e3f6aa7

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 51dbae8 commit e3f6aa7

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
@@ -660,6 +660,200 @@ EXPORT(void) TwoOutArgs(int a, int *pi, int b, int *pj)
660660
*pj += b;
661661
}
662662

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