bpo-22273: Update ctypes to correctly handle arrays in small structur… · python/cpython@ce62dcc (original) (raw)
`@@ -350,6 +350,9 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
`
350
350
`int pack;
`
351
351
`Py_ssize_t ffi_ofs;
`
352
352
`int big_endian;
`
``
353
`+
#if defined(X86_64)
`
``
354
`+
int arrays_seen = 0;
`
``
355
`+
#endif
`
353
356
``
354
357
`/* HACK Alert: I cannot be bothered to fix ctypes.com, so there has to
`
355
358
` be a way to use the old, broken semantics: fields are not extended
`
`@@ -501,6 +504,10 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
`
501
504
`Py_XDECREF(pair);
`
502
505
`return -1;
`
503
506
` }
`
``
507
`+
#if defined(X86_64)
`
``
508
`+
if (PyCArrayTypeObject_Check(desc))
`
``
509
`+
arrays_seen = 1;
`
``
510
`+
#endif
`
504
511
`dict = PyType_stgdict(desc);
`
505
512
`if (dict == NULL) {
`
506
513
`Py_DECREF(pair);
`
`@@ -641,6 +648,106 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
`
641
648
`stgdict->align = total_align;
`
642
649
`stgdict->length = len; /* ADD ffi_ofs? */
`
643
650
``
``
651
`+
#if defined(X86_64)
`
``
652
+
``
653
`+
#define MAX_ELEMENTS 16
`
``
654
+
``
655
`+
if (arrays_seen && (size <= 16)) {
`
``
656
`+
/*
`
``
657
`+
- See bpo-22273. Arrays are normally treated as pointers, which is
`
``
658
`+
- fine when an array name is being passed as parameter, but not when
`
``
659
`+
- passing structures by value that contain arrays. On 64-bit Linux,
`
``
660
`+
- small structures passed by value are passed in registers, and in
`
``
661
`+
- order to do this, libffi needs to know the true type of the array
`
``
662
`+
- members of structs. Treating them as pointers breaks things.
`
``
663
`+
`
``
664
`+
- By small structures, we mean ones that are 16 bytes or less. In that
`
``
665
`+
- case, there can't be more than 16 elements after unrolling arrays,
`
``
666
`+
- as we (will) disallow bitfields. So we can collect the true ffi_type
`
``
667
`+
- values in a fixed-size local array on the stack and, if any arrays
`
``
668
`+
- were seen, replace the ffi_type_pointer.elements with a more
`
``
669
`+
- accurate set, to allow libffi to marshal them into registers
`
``
670
`+
- correctly. It means one more loop over the fields, but if we got
`
``
671
`+
- here, the structure is small, so there aren't too many of those.
`
``
672
`+
*/
`
``
673
`+
ffi_type *actual_types[MAX_ELEMENTS + 1];
`
``
674
`+
int actual_type_index = 0;
`
``
675
+
``
676
`+
memset(actual_types, 0, sizeof(actual_types));
`
``
677
`+
for (i = 0; i < len; ++i) {
`
``
678
`+
PyObject *name, *desc;
`
``
679
`+
PyObject *pair = PySequence_GetItem(fields, i);
`
``
680
`+
StgDictObject *dict;
`
``
681
`+
int bitsize = 0;
`
``
682
+
``
683
`+
if (pair == NULL) {
`
``
684
`+
return -1;
`
``
685
`+
}
`
``
686
`+
if (!PyArg_ParseTuple(pair, "UO|i", &name, &desc, &bitsize)) {
`
``
687
`+
PyErr_SetString(PyExc_TypeError,
`
``
688
`+
"'fields' must be a sequence of (name, C type) pairs");
`
``
689
`+
Py_XDECREF(pair);
`
``
690
`+
return -1;
`
``
691
`+
}
`
``
692
`+
dict = PyType_stgdict(desc);
`
``
693
`+
if (dict == NULL) {
`
``
694
`+
Py_DECREF(pair);
`
``
695
`+
PyErr_Format(PyExc_TypeError,
`
``
696
`+
"second item in fields tuple (index %zd) must be a C type",
`
``
697
`+
i);
`
``
698
`+
return -1;
`
``
699
`+
}
`
``
700
`+
if (!PyCArrayTypeObject_Check(desc)) {
`
``
701
`+
/* Not an array. Just copy over the element ffi_type. */
`
``
702
`+
actual_types[actual_type_index++] = &dict->ffi_type_pointer;
`
``
703
`+
assert(actual_type_index <= MAX_ELEMENTS);
`
``
704
`+
}
`
``
705
`+
else {
`
``
706
`+
int length = dict->length;
`
``
707
`+
StgDictObject *edict;
`
``
708
+
``
709
`+
edict = PyType_stgdict(dict->proto);
`
``
710
`+
if (edict == NULL) {
`
``
711
`+
Py_DECREF(pair);
`
``
712
`+
PyErr_Format(PyExc_TypeError,
`
``
713
`+
"second item in fields tuple (index %zd) must be a C type",
`
``
714
`+
i);
`
``
715
`+
return -1;
`
``
716
`+
}
`
``
717
`+
/* Copy over the element's type, length times. */
`
``
718
`+
while (length > 0) {
`
``
719
`+
actual_types[actual_type_index++] = &edict->ffi_type_pointer;
`
``
720
`+
assert(actual_type_index <= MAX_ELEMENTS);
`
``
721
`+
length--;
`
``
722
`+
}
`
``
723
`+
}
`
``
724
`+
Py_DECREF(pair);
`
``
725
`+
}
`
``
726
+
``
727
`+
actual_types[actual_type_index++] = NULL;
`
``
728
`+
/*
`
``
729
`+
- Replace the old elements with the new, taking into account
`
``
730
`+
- base class elements where necessary.
`
``
731
`+
*/
`
``
732
`+
assert(stgdict->ffi_type_pointer.elements);
`
``
733
`+
PyMem_Free(stgdict->ffi_type_pointer.elements);
`
``
734
`+
stgdict->ffi_type_pointer.elements = PyMem_New(ffi_type *,
`
``
735
`+
ffi_ofs + actual_type_index);
`
``
736
`+
if (stgdict->ffi_type_pointer.elements == NULL) {
`
``
737
`+
PyErr_NoMemory();
`
``
738
`+
return -1;
`
``
739
`+
}
`
``
740
`+
if (ffi_ofs) {
`
``
741
`+
memcpy(stgdict->ffi_type_pointer.elements,
`
``
742
`+
basedict->ffi_type_pointer.elements,
`
``
743
`+
ffi_ofs * sizeof(ffi_type *));
`
``
744
+
``
745
`+
}
`
``
746
`+
memcpy(&stgdict->ffi_type_pointer.elements[ffi_ofs], actual_types,
`
``
747
`+
actual_type_index * sizeof(ffi_type *));
`
``
748
`+
}
`
``
749
`+
#endif
`
``
750
+
644
751
`/* We did check that this flag was NOT set above, it must not
`
645
752
` have been set until now. */
`
646
753
`if (stgdict->flags & DICTFLAG_FINAL) {
`