Skip to content

Commit f0f4564

Browse files
committed
Specify minimum PyGC_Head and PyObject alignment to fix build failure
As documented in InternalDocs/garbage_collector.md, the garbage collector stores flags in the least significant two bits of the _gc_prev pointer in struct PyGC_Head. Consequently, this pointer is only capable of storing a location that's aligned to a 4-byte boundary. This alignment requirement is documented but it's not actually encoded. The code only works when python happens to run on a platform that has a sufficiently large minimum alignment for the structs in question. The same problem arises with PyObject pointers because the least significant bits get used for PyStackRef tags. Since we know that 2 bits are needed, we also know the minimum alignment that's needed. Let's make that explicit, so the compiler can then make those bits available. This patch fixes a segfault in _bootstrap_python. In 3.14.0 beta 2 this fixes the "Assertion `!PyStackRef_IsTaggedInt(ref)' failed" when built with --config-pydebug. Also, making the requirements explicit improves clarity. This bug was previously investigated by Adrian Glaubitz here: https://lists.debian.org/debian-68k/2024/11/msg00020.html https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1087600 Although Adrian's patch isn't really correct (because natural alignment is not needed), he deserves full credit for finding the root cause.
1 parent 12d3f88 commit f0f4564

File tree

4 files changed

+11
-4
lines changed

4 files changed

+11
-4
lines changed

Include/internal/pycore_gc.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ static inline void _PyObject_GC_SET_SHARED(PyObject *op) {
133133
*/
134134
#define _PyGC_NEXT_MASK_OLD_SPACE_1 1
135135

136-
#define _PyGC_PREV_SHIFT 2
136+
#define _PyGC_PREV_SHIFT _PyObject_ALIGNMENT_SHIFT
137137
#define _PyGC_PREV_MASK (((uintptr_t) -1) << _PyGC_PREV_SHIFT)
138138

139139
/* set for debugging information */

Include/internal/pycore_interp_structs.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,9 @@ typedef struct {
163163

164164
// Tagged pointer to previous object in the list.
165165
// Lowest two bits are used for flags documented later.
166+
// Those bits are made available by the struct's minimum alignment.
166167
uintptr_t _gc_prev;
167-
} PyGC_Head;
168+
} PyGC_Head Py_ALIGNED(1 << _PyObject_ALIGNMENT_SHIFT);
168169

169170
#define _PyGC_Head_UNUSED PyGC_Head
170171

Include/object.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@ whose size is determined when the object is allocated.
101101
#define PyObject_VAR_HEAD PyVarObject ob_base;
102102
#define Py_INVALID_SIZE (Py_ssize_t)-1
103103

104+
/* PyObjects are given a minimum alignment so that the least significant bits
105+
* of an object pointer become available for other purposes.
106+
*/
107+
#define _PyObject_ALIGNMENT_SHIFT 2
108+
104109
/* Nothing is actually declared to be a PyObject, but every pointer to
105110
* a Python object can be cast to a PyObject*. This is inheritance built
106111
* by hand. Similarly every pointer to a variable-size Python object can,
@@ -142,7 +147,7 @@ struct _object {
142147
#endif
143148

144149
PyTypeObject *ob_type;
145-
};
150+
} Py_ALIGNED(1 << _PyObject_ALIGNMENT_SHIFT);
146151
#else
147152
// Objects that are not owned by any thread use a thread id (tid) of zero.
148153
// This includes both immortal objects and objects whose reference count
@@ -160,7 +165,7 @@ struct _object {
160165
uint32_t ob_ref_local; // local reference count
161166
Py_ssize_t ob_ref_shared; // shared (atomic) reference count
162167
PyTypeObject *ob_type;
163-
};
168+
} Py_ALIGNED(1 << _PyObject_ALIGNMENT_SHIFT);
164169
#endif
165170

166171
/* Cast argument to PyObject* type. */
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix crash when building on Linux/m68k.

0 commit comments

Comments
 (0)