Skip to content

Commit b7fa201

Browse files
committed
Issue #20160: broken ctypes calling convention on MSVC / 64-bit Windows (large structs) Patch by mattip
1 parent fbaf931 commit b7fa201

File tree

6 files changed

+104
-15
lines changed

6 files changed

+104
-15
lines changed

Lib/ctypes/test/test_win32.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,29 @@ class RECT(Structure):
111111

112112
dll = CDLL(_ctypes_test.__file__)
113113

114-
pt = POINT(10, 10)
115-
rect = RECT(0, 0, 20, 20)
116-
self.assertEqual(1, dll.PointInRect(byref(rect), pt))
114+
pt = POINT(15, 25)
115+
left = c_long.in_dll(dll, 'left')
116+
top = c_long.in_dll(dll, 'top')
117+
right = c_long.in_dll(dll, 'right')
118+
bottom = c_long.in_dll(dll, 'bottom')
119+
rect = RECT(left, top, right, bottom)
120+
PointInRect = dll.PointInRect
121+
PointInRect.argtypes = [POINTER(RECT), POINT]
122+
self.assertEqual(1, PointInRect(byref(rect), pt))
123+
124+
ReturnRect = dll.ReturnRect
125+
ReturnRect.argtypes = [c_int, RECT, POINTER(RECT), POINT, RECT,
126+
POINTER(RECT), POINT, RECT]
127+
ReturnRect.restype = RECT
128+
for i in range(4):
129+
ret = ReturnRect(i, rect, pointer(rect), pt, rect,
130+
byref(rect), pt, rect)
131+
# the c function will check and modify ret if something is
132+
# passed in improperly
133+
self.assertEqual(ret.left, left.value)
134+
self.assertEqual(ret.right, right.value)
135+
self.assertEqual(ret.top, top.value)
136+
self.assertEqual(ret.bottom, bottom.value)
117137

118138
if __name__ == '__main__':
119139
unittest.main()

Modules/_ctypes/_ctypes_test.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,49 @@ EXPORT(int) PointInRect(RECT *prc, POINT pt)
527527
return 1;
528528
}
529529

530+
EXPORT(int left = 10);
531+
EXPORT(int top = 20);
532+
EXPORT(int right = 30);
533+
EXPORT(int bottom = 40);
534+
535+
EXPORT(RECT) ReturnRect(int i, RECT ar, RECT* br, POINT cp, RECT dr,
536+
RECT *er, POINT fp, RECT gr)
537+
{
538+
/*Check input */
539+
if (ar.left + br->left + dr.left + er->left + gr.left != left * 5)
540+
{
541+
ar.left = 100;
542+
return ar;
543+
}
544+
if (ar.right + br->right + dr.right + er->right + gr.right != right * 5)
545+
{
546+
ar.right = 100;
547+
return ar;
548+
}
549+
if (cp.x != fp.x)
550+
{
551+
ar.left = -100;
552+
}
553+
if (cp.y != fp.y)
554+
{
555+
ar.left = -200;
556+
}
557+
switch(i)
558+
{
559+
case 0:
560+
return ar;
561+
break;
562+
case 1:
563+
return dr;
564+
break;
565+
case 2:
566+
return gr;
567+
break;
568+
569+
}
570+
return ar;
571+
}
572+
530573
typedef struct {
531574
short x;
532575
short y;

Modules/_ctypes/callproc.c

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,9 +1140,6 @@ PyObject *_ctypes_callproc(PPROC pProc,
11401140
for (i = 0; i < argcount; ++i) {
11411141
atypes[i] = args[i].ffi_type;
11421142
if (atypes[i]->type == FFI_TYPE_STRUCT
1143-
#ifdef _WIN64
1144-
&& atypes[i]->size <= sizeof(void *)
1145-
#endif
11461143
)
11471144
avalues[i] = (void *)args[i].value.p;
11481145
else

Modules/_ctypes/libffi_msvc/ffi.c

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,15 @@ void ffi_prep_args(char *stack, extended_cif *ecif)
102102
FFI_ASSERT(0);
103103
}
104104
}
105+
#ifdef _WIN64
106+
else if (z > 8)
107+
{
108+
/* On Win64, if a single argument takes more than 8 bytes,
109+
then it is always passed by reference. */
110+
*(void **)argp = *p_argv;
111+
z = 8;
112+
}
113+
#endif
105114
else
106115
{
107116
memcpy(argp, *p_argv, z);
@@ -124,14 +133,25 @@ ffi_status ffi_prep_cif_machdep(ffi_cif *cif)
124133
switch (cif->rtype->type)
125134
{
126135
case FFI_TYPE_VOID:
127-
case FFI_TYPE_STRUCT:
128136
case FFI_TYPE_SINT64:
129137
case FFI_TYPE_FLOAT:
130138
case FFI_TYPE_DOUBLE:
131139
case FFI_TYPE_LONGDOUBLE:
132140
cif->flags = (unsigned) cif->rtype->type;
133141
break;
134142

143+
case FFI_TYPE_STRUCT:
144+
/* MSVC returns small structures in registers. Put in cif->flags
145+
the value FFI_TYPE_STRUCT only if the structure is big enough;
146+
otherwise, put the 4- or 8-bytes integer type. */
147+
if (cif->rtype->size <= 4)
148+
cif->flags = FFI_TYPE_INT;
149+
else if (cif->rtype->size <= 8)
150+
cif->flags = FFI_TYPE_SINT64;
151+
else
152+
cif->flags = FFI_TYPE_STRUCT;
153+
break;
154+
135155
case FFI_TYPE_UINT64:
136156
#ifdef _WIN64
137157
case FFI_TYPE_POINTER:
@@ -201,8 +221,7 @@ ffi_call(/*@dependent@*/ ffi_cif *cif,
201221
#else
202222
case FFI_SYSV:
203223
/*@-usedef@*/
204-
/* Function call needs at least 40 bytes stack size, on win64 AMD64 */
205-
return ffi_call_AMD64(ffi_prep_args, &ecif, cif->bytes ? cif->bytes : 40,
224+
return ffi_call_AMD64(ffi_prep_args, &ecif, cif->bytes,
206225
cif->flags, ecif.rvalue, fn);
207226
/*@=usedef@*/
208227
break;
@@ -227,7 +246,7 @@ void *
227246
#else
228247
static void __fastcall
229248
#endif
230-
ffi_closure_SYSV (ffi_closure *closure, int *argp)
249+
ffi_closure_SYSV (ffi_closure *closure, char *argp)
231250
{
232251
// this is our return value storage
233252
long double res;
@@ -237,7 +256,7 @@ ffi_closure_SYSV (ffi_closure *closure, int *argp)
237256
void **arg_area;
238257
unsigned short rtype;
239258
void *resp = (void*)&res;
240-
void *args = &argp[1];
259+
void *args = argp + sizeof(void*);
241260

242261
cif = closure->cif;
243262
arg_area = (void**) alloca (cif->nargs * sizeof (void*));

Modules/_ctypes/libffi_msvc/prep_cif.c

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,9 @@ ffi_status ffi_prep_cif(/*@out@*/ /*@partial@*/ ffi_cif *cif,
116116
#if !defined M68K && !defined __x86_64__ && !defined S390
117117
/* Make space for the return structure pointer */
118118
if (cif->rtype->type == FFI_TYPE_STRUCT
119-
/* MSVC returns small structures in registers. But we have a different
120-
workaround: pretend int32 or int64 return type, and converting to
121-
structure afterwards. */
119+
#ifdef _WIN32
120+
&& (cif->rtype->size > 8) /* MSVC returns small structs in registers */
121+
#endif
122122
#ifdef SPARC
123123
&& (cif->abi != FFI_V9 || cif->rtype->size > 32)
124124
#endif
@@ -145,6 +145,10 @@ ffi_status ffi_prep_cif(/*@out@*/ /*@partial@*/ ffi_cif *cif,
145145
&& cif->abi != FFI_V9))
146146
bytes += sizeof(void*);
147147
else
148+
#elif defined (_WIN64)
149+
if ((*ptr)->type == FFI_TYPE_STRUCT && ((*ptr)->size > 8))
150+
bytes += sizeof(void*);
151+
else
148152
#endif
149153
{
150154
#if !defined(_MSC_VER) && !defined(__MINGW32__)
@@ -168,6 +172,12 @@ ffi_status ffi_prep_cif(/*@out@*/ /*@partial@*/ ffi_cif *cif,
168172
#endif
169173
}
170174

175+
#ifdef _WIN64
176+
/* Function call needs at least 40 bytes stack size, on win64 AMD64 */
177+
if (bytes < 40)
178+
bytes = 40;
179+
#endif
180+
171181
cif->bytes = bytes;
172182

173183
/* Perform machine dependent cif processing */

Modules/_ctypes/libffi_msvc/types.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ FFI_INTEGRAL_TYPEDEF(sint32, 4, 4, FFI_TYPE_SINT32);
4343
FFI_INTEGRAL_TYPEDEF(float, 4, 4, FFI_TYPE_FLOAT);
4444

4545
#if defined ALPHA || defined SPARC64 || defined X86_64 || defined S390X \
46-
|| defined IA64
46+
|| defined IA64 || defined _WIN64
4747

4848
FFI_INTEGRAL_TYPEDEF(pointer, 8, 8, FFI_TYPE_POINTER);
4949

0 commit comments

Comments
 (0)