Skip to content

Commit

Permalink
bpo-30353: Fix pass by value for structs on 64-bit Cygwin/MinGW (GH-1559
Browse files Browse the repository at this point in the history
)
  • Loading branch information
embray authored and vsajip committed Jun 7, 2017
1 parent 897bba7 commit 9ba3aa4
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 2 deletions.
4 changes: 4 additions & 0 deletions Lib/ctypes/test/test_as_parameter.py
Expand Up @@ -169,6 +169,10 @@ class S2H(Structure):
s2h = dll.ret_2h_func(self.wrap(inp))
self.assertEqual((s2h.x, s2h.y), (99*2, 88*3))

# Test also that the original struct was unmodified (i.e. was passed by
# value)
self.assertEqual((inp.x, inp.y), (99, 88))

def test_struct_return_8H(self):
class S8I(Structure):
_fields_ = [("a", c_int),
Expand Down
22 changes: 22 additions & 0 deletions Lib/ctypes/test/test_structures.py
Expand Up @@ -417,6 +417,28 @@ class X(Structure):
self.assertEqual(s.second, 0xcafebabe)
self.assertEqual(s.third, 0x0bad1dea)

def test_pass_by_value_in_register(self):
class X(Structure):
_fields_ = [
('first', c_uint),
('second', c_uint)
]

s = X()
s.first = 0xdeadbeef
s.second = 0xcafebabe
dll = CDLL(_ctypes_test.__file__)
func = dll._testfunc_reg_struct_update_value
func.argtypes = (X,)
func.restype = None
func(s)
self.assertEqual(s.first, 0xdeadbeef)
self.assertEqual(s.second, 0xcafebabe)
got = X.in_dll(dll, "last_tfrsuv_arg")
self.assertEqual(s.first, got.first)
self.assertEqual(s.second, got.second)


class PointerMemberTestCase(unittest.TestCase):

def test(self):
Expand Down
18 changes: 18 additions & 0 deletions Modules/_ctypes/_ctypes_test.c
Expand Up @@ -57,6 +57,24 @@ _testfunc_large_struct_update_value(Test in)
((volatile Test *)&in)->third = 0x0badf00d;
}

typedef struct {
unsigned int first;
unsigned int second;
} TestReg;


EXPORT(TestReg) last_tfrsuv_arg;


EXPORT(void)
_testfunc_reg_struct_update_value(TestReg in)
{
last_tfrsuv_arg = in;
((volatile TestReg *)&in)->first = 0x0badf00d;
((volatile TestReg *)&in)->second = 0x0badf00d;
}


EXPORT(void)testfunc_array(int values[4])
{
printf("testfunc_array %d %d %d %d\n",
Expand Down
23 changes: 21 additions & 2 deletions Modules/_ctypes/callproc.c
Expand Up @@ -1039,6 +1039,13 @@ GetComError(HRESULT errcode, GUID *riid, IUnknown *pIunk)
}
#endif

#if (defined(__x86_64__) && (defined(__MINGW64__) || defined(__CYGWIN__))) || \
defined(__aarch64__)
#define CTYPES_PASS_BY_REF_HACK
#define POW2(x) (((x & ~(x - 1)) == x) ? x : 0)
#define IS_PASS_BY_REF(x) (x > 8 || !POW2(x))
#endif

/*
* Requirements, must be ensured by the caller:
* - argtuple is tuple of arguments
Expand Down Expand Up @@ -1136,8 +1143,20 @@ PyObject *_ctypes_callproc(PPROC pProc,
}
for (i = 0; i < argcount; ++i) {
atypes[i] = args[i].ffi_type;
if (atypes[i]->type == FFI_TYPE_STRUCT
)
#ifdef CTYPES_PASS_BY_REF_HACK
size_t size = atypes[i]->size;
if (IS_PASS_BY_REF(size)) {
void *tmp = alloca(size);
if (atypes[i]->type == FFI_TYPE_STRUCT)
memcpy(tmp, args[i].value.p, size);
else
memcpy(tmp, (void*)&args[i].value, size);

avalues[i] = tmp;
}
else
#endif
if (atypes[i]->type == FFI_TYPE_STRUCT)
avalues[i] = (void *)args[i].value.p;
else
avalues[i] = (void *)&args[i].value;
Expand Down

0 comments on commit 9ba3aa4

Please sign in to comment.