Skip to content

Commit 2f3ba27

Browse files
miss-islingtonembray
authored andcommitted
[3.6] bpo-30353: Fix pass by value for structs on 64-bit Cygwin/MinGW (GH-1559) (GH-5954)
(cherry picked from commit 9ba3aa4) Co-authored-by: Erik Bray <erik.m.bray@gmail.com>
1 parent 15425cf commit 2f3ba27

File tree

5 files changed

+66
-2
lines changed

5 files changed

+66
-2
lines changed

Lib/ctypes/test/test_as_parameter.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,10 @@ class S2H(Structure):
169169
s2h = dll.ret_2h_func(self.wrap(inp))
170170
self.assertEqual((s2h.x, s2h.y), (99*2, 88*3))
171171

172+
# Test also that the original struct was unmodified (i.e. was passed by
173+
# value)
174+
self.assertEqual((inp.x, inp.y), (99, 88))
175+
172176
def test_struct_return_8H(self):
173177
class S8I(Structure):
174178
_fields_ = [("a", c_int),

Lib/ctypes/test/test_structures.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,28 @@ class X(Structure):
417417
self.assertEqual(s.second, 0xcafebabe)
418418
self.assertEqual(s.third, 0x0bad1dea)
419419

420+
def test_pass_by_value_in_register(self):
421+
class X(Structure):
422+
_fields_ = [
423+
('first', c_uint),
424+
('second', c_uint)
425+
]
426+
427+
s = X()
428+
s.first = 0xdeadbeef
429+
s.second = 0xcafebabe
430+
dll = CDLL(_ctypes_test.__file__)
431+
func = dll._testfunc_reg_struct_update_value
432+
func.argtypes = (X,)
433+
func.restype = None
434+
func(s)
435+
self.assertEqual(s.first, 0xdeadbeef)
436+
self.assertEqual(s.second, 0xcafebabe)
437+
got = X.in_dll(dll, "last_tfrsuv_arg")
438+
self.assertEqual(s.first, got.first)
439+
self.assertEqual(s.second, got.second)
440+
441+
420442
class PointerMemberTestCase(unittest.TestCase):
421443

422444
def test(self):
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix ctypes pass-by-value for structs on 64-bit Cygwin/MinGW.

Modules/_ctypes/_ctypes_test.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,24 @@ _testfunc_large_struct_update_value(Test in)
5757
((volatile Test *)&in)->third = 0x0badf00d;
5858
}
5959

60+
typedef struct {
61+
unsigned int first;
62+
unsigned int second;
63+
} TestReg;
64+
65+
66+
EXPORT(TestReg) last_tfrsuv_arg;
67+
68+
69+
EXPORT(void)
70+
_testfunc_reg_struct_update_value(TestReg in)
71+
{
72+
last_tfrsuv_arg = in;
73+
((volatile TestReg *)&in)->first = 0x0badf00d;
74+
((volatile TestReg *)&in)->second = 0x0badf00d;
75+
}
76+
77+
6078
EXPORT(void)testfunc_array(int values[4])
6179
{
6280
printf("testfunc_array %d %d %d %d\n",

Modules/_ctypes/callproc.c

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,6 +1040,13 @@ GetComError(HRESULT errcode, GUID *riid, IUnknown *pIunk)
10401040
}
10411041
#endif
10421042

1043+
#if (defined(__x86_64__) && (defined(__MINGW64__) || defined(__CYGWIN__))) || \
1044+
defined(__aarch64__)
1045+
#define CTYPES_PASS_BY_REF_HACK
1046+
#define POW2(x) (((x & ~(x - 1)) == x) ? x : 0)
1047+
#define IS_PASS_BY_REF(x) (x > 8 || !POW2(x))
1048+
#endif
1049+
10431050
/*
10441051
* Requirements, must be ensured by the caller:
10451052
* - argtuple is tuple of arguments
@@ -1137,8 +1144,20 @@ PyObject *_ctypes_callproc(PPROC pProc,
11371144
}
11381145
for (i = 0; i < argcount; ++i) {
11391146
atypes[i] = args[i].ffi_type;
1140-
if (atypes[i]->type == FFI_TYPE_STRUCT
1141-
)
1147+
#ifdef CTYPES_PASS_BY_REF_HACK
1148+
size_t size = atypes[i]->size;
1149+
if (IS_PASS_BY_REF(size)) {
1150+
void *tmp = alloca(size);
1151+
if (atypes[i]->type == FFI_TYPE_STRUCT)
1152+
memcpy(tmp, args[i].value.p, size);
1153+
else
1154+
memcpy(tmp, (void*)&args[i].value, size);
1155+
1156+
avalues[i] = tmp;
1157+
}
1158+
else
1159+
#endif
1160+
if (atypes[i]->type == FFI_TYPE_STRUCT)
11421161
avalues[i] = (void *)args[i].value.p;
11431162
else
11441163
avalues[i] = (void *)&args[i].value;

0 commit comments

Comments
 (0)