From 41d291cc9501fd8b6432c26bc44c7aa677b9f214 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Thu, 15 Aug 2024 18:20:17 +0100 Subject: [PATCH 1/6] Add debug offsets that can help in free threaded builds Signed-off-by: Pablo Galindo --- Include/internal/pycore_runtime.h | 38 ++++++++++++++++++++++++++ Include/internal/pycore_runtime_init.h | 28 +++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/Include/internal/pycore_runtime.h b/Include/internal/pycore_runtime.h index d4ffd977940a02..aa171009528d21 100644 --- a/Include/internal/pycore_runtime.h +++ b/Include/internal/pycore_runtime.h @@ -53,6 +53,7 @@ typedef struct _Py_AuditHookEntry { typedef struct _Py_DebugOffsets { char cookie[8]; uint64_t version; + uint64_t free_threaded; // Runtime state offset; struct _runtime_state { uint64_t size; @@ -70,6 +71,7 @@ typedef struct _Py_DebugOffsets { uint64_t imports_modules; uint64_t sysdict; uint64_t builtins; + uint64_t _gil; uint64_t ceval_gil; uint64_t gil_runtime_state_locked; uint64_t gil_runtime_state_holder; @@ -122,14 +124,50 @@ typedef struct _Py_DebugOffsets { struct _type_object { uint64_t size; uint64_t tp_name; + uint64_t tp_repr; + uint64_t tp_flags; } type_object; // PyTuple object offset; struct _tuple_object { uint64_t size; uint64_t ob_item; + uint64_t ob_size; } tuple_object; + // PyList object offset; + struct _list_object { + uint64_t size; + uint64_t ob_item; + } list_object; + + // PyDict object offset; + struct _dict_object { + uint64_t size; + uint64_t ma_keys; + uint64_t ma_values; + } dict_object; + + // PyFloat object offset; + struct _float_object { + uint64_t size; + uint64_t ob_fval; + } float_object; + + // PyLong object offset; + struct _long_object { + uint64_t size; + uint64_t lv_tag; + uint64_t ob_digit; + } long_object; + + // PyBytes object offset; + struct _bytes_object { + uint64_t size; + uint64_t ob_size; + uint64_t ob_sval; + } bytes_object; + // Unicode object offset; struct _unicode_object { uint64_t size; diff --git a/Include/internal/pycore_runtime_init.h b/Include/internal/pycore_runtime_init.h index da2b8d5570de62..957a1e45611ed4 100644 --- a/Include/internal/pycore_runtime_init.h +++ b/Include/internal/pycore_runtime_init.h @@ -34,6 +34,7 @@ extern PyTypeObject _PyExc_MemoryError; .debug_offsets = { \ .cookie = "xdebugpy", \ .version = PY_VERSION_HEX, \ + .free_threaded = Py_GIL_DISABLED, \ .runtime_state = { \ .size = sizeof(_PyRuntimeState), \ .finalizing = offsetof(_PyRuntimeState, _finalizing), \ @@ -48,6 +49,7 @@ extern PyTypeObject _PyExc_MemoryError; .imports_modules = offsetof(PyInterpreterState, imports.modules), \ .sysdict = offsetof(PyInterpreterState, sysdict), \ .builtins = offsetof(PyInterpreterState, builtins), \ + ._gil = offsetof(PyInterpreterState, _gil), \ .ceval_gil = offsetof(PyInterpreterState, ceval.gil), \ .gil_runtime_state_locked = offsetof(PyInterpreterState, _gil.locked), \ .gil_runtime_state_holder = offsetof(PyInterpreterState, _gil.last_holder), \ @@ -90,10 +92,36 @@ extern PyTypeObject _PyExc_MemoryError; .type_object = { \ .size = sizeof(PyTypeObject), \ .tp_name = offsetof(PyTypeObject, tp_name), \ + .tp_repr = offsetof(PyTypeObject, tp_repr), \ + .tp_flags = offsetof(PyTypeObject, tp_flags), \ }, \ .tuple_object = { \ .size = sizeof(PyTupleObject), \ .ob_item = offsetof(PyTupleObject, ob_item), \ + .ob_size = offsetof(PyTupleObject, ob_base.ob_size), \ + }, \ + .list_object = { \ + .size = sizeof(PyListObject), \ + .ob_item = offsetof(PyListObject, ob_item), \ + }, \ + .dict_object = { \ + .size = sizeof(PyDictObject), \ + .ma_keys = offsetof(PyDictObject, ma_keys), \ + .ma_values = offsetof(PyDictObject, ma_values), \ + }, \ + .float_object = { \ + .size = sizeof(PyFloatObject), \ + .ob_fval = offsetof(PyFloatObject, ob_fval), \ + }, \ + .long_object = { \ + .size = sizeof(PyLongObject), \ + .lv_tag = offsetof(_PyLongValue, lv_tag), \ + .ob_digit = offsetof(_PyLongValue, ob_digit), \ + }, \ + .bytes_object = { \ + .size = sizeof(PyBytesObject), \ + .ob_size = offsetof(PyBytesObject, ob_base.ob_size), \ + .ob_sval = offsetof(PyBytesObject, ob_sval), \ }, \ .unicode_object = { \ .size = sizeof(PyUnicodeObject), \ From 324dd3e9f62ea15475669f2c60552e65883c7a73 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Thu, 15 Aug 2024 18:54:24 +0100 Subject: [PATCH 2/6] Ensure the debug cookie only appears once in the binary --- Include/internal/pycore_runtime.h | 2 ++ Include/internal/pycore_runtime_init.h | 4 ++-- Python/pylifecycle.c | 2 +- Python/pystate.c | 4 +++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Include/internal/pycore_runtime.h b/Include/internal/pycore_runtime.h index aa171009528d21..e29d172ebcb5f6 100644 --- a/Include/internal/pycore_runtime.h +++ b/Include/internal/pycore_runtime.h @@ -44,6 +44,8 @@ struct _gilstate_runtime_state { /* Runtime audit hook state */ +#define _Py_Debug_Cookie "xdebugpy" + typedef struct _Py_AuditHookEntry { struct _Py_AuditHookEntry *next; Py_AuditHookFunction hookCFunction; diff --git a/Include/internal/pycore_runtime_init.h b/Include/internal/pycore_runtime_init.h index 957a1e45611ed4..6e805e07a92a6a 100644 --- a/Include/internal/pycore_runtime_init.h +++ b/Include/internal/pycore_runtime_init.h @@ -29,10 +29,10 @@ extern PyTypeObject _PyExc_MemoryError; /* The static initializers defined here should only be used in the runtime init code (in pystate.c and pylifecycle.c). */ -#define _PyRuntimeState_INIT(runtime) \ +#define _PyRuntimeState_INIT(runtime, debug_cookie) \ { \ .debug_offsets = { \ - .cookie = "xdebugpy", \ + .cookie = debug_cookie, \ .version = PY_VERSION_HEX, \ .free_threaded = Py_GIL_DISABLED, \ .runtime_state = { \ diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 3a41c640fd9599..27faf723745c21 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -104,7 +104,7 @@ _PyRuntimeState _PyRuntime #if defined(__linux__) && (defined(__GNUC__) || defined(__clang__)) __attribute__ ((section (".PyRuntime"))) #endif -= _PyRuntimeState_INIT(_PyRuntime); += _PyRuntimeState_INIT(_PyRuntime, _Py_Debug_Cookie); _Py_COMP_DIAG_POP static int runtime_initialized = 0; diff --git a/Python/pystate.c b/Python/pystate.c index bba88b76088e71..4d7bec65ff5c49 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -390,7 +390,7 @@ _Py_COMP_DIAG_IGNORE_DEPR_DECLS Note that we initialize "initial" relative to _PyRuntime, to ensure pre-initialized pointers point to the active runtime state (and not "initial"). */ -static const _PyRuntimeState initial = _PyRuntimeState_INIT(_PyRuntime); +static const _PyRuntimeState initial = _PyRuntimeState_INIT(_PyRuntime, ""); _Py_COMP_DIAG_POP #define LOCKS_INIT(runtime) \ @@ -455,6 +455,8 @@ _PyRuntimeState_Init(_PyRuntimeState *runtime) // Py_Initialize() must be running again. // Reset to _PyRuntimeState_INIT. memcpy(runtime, &initial, sizeof(*runtime)); + // Preserve the cookie from the original runtime. + memcpy(runtime->debug_offsets.cookie, _Py_Debug_Cookie, 8); assert(!runtime->_initialized); } From fea66ba62df907a89667dfe47d3b7a692a36cc1c Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Thu, 15 Aug 2024 18:56:02 +0100 Subject: [PATCH 3/6] fixup! Add debug offsets that can help in free threaded builds --- Include/internal/pycore_runtime.h | 2 +- Include/internal/pycore_runtime_init.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Include/internal/pycore_runtime.h b/Include/internal/pycore_runtime.h index e29d172ebcb5f6..b2373727c9ea55 100644 --- a/Include/internal/pycore_runtime.h +++ b/Include/internal/pycore_runtime.h @@ -73,8 +73,8 @@ typedef struct _Py_DebugOffsets { uint64_t imports_modules; uint64_t sysdict; uint64_t builtins; - uint64_t _gil; uint64_t ceval_gil; + uint64_t gil_runtime_state; uint64_t gil_runtime_state_locked; uint64_t gil_runtime_state_holder; } interpreter_state; diff --git a/Include/internal/pycore_runtime_init.h b/Include/internal/pycore_runtime_init.h index 6e805e07a92a6a..cebf901a556129 100644 --- a/Include/internal/pycore_runtime_init.h +++ b/Include/internal/pycore_runtime_init.h @@ -49,8 +49,8 @@ extern PyTypeObject _PyExc_MemoryError; .imports_modules = offsetof(PyInterpreterState, imports.modules), \ .sysdict = offsetof(PyInterpreterState, sysdict), \ .builtins = offsetof(PyInterpreterState, builtins), \ - ._gil = offsetof(PyInterpreterState, _gil), \ .ceval_gil = offsetof(PyInterpreterState, ceval.gil), \ + .gil_runtime_state= offsetof(PyInterpreterState, _gil), \ .gil_runtime_state_locked = offsetof(PyInterpreterState, _gil.locked), \ .gil_runtime_state_holder = offsetof(PyInterpreterState, _gil.last_holder), \ }, \ From 33c25ba878b4b9a7841c9c8efe7920c6e876b338 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Thu, 15 Aug 2024 19:02:25 +0100 Subject: [PATCH 4/6] fixup! Ensure the debug cookie only appears once in the binary --- Include/internal/pycore_runtime.h | 3 ++- Include/internal/pycore_runtime_init.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Include/internal/pycore_runtime.h b/Include/internal/pycore_runtime.h index b2373727c9ea55..21b14d3745ab5c 100644 --- a/Include/internal/pycore_runtime.h +++ b/Include/internal/pycore_runtime.h @@ -141,6 +141,7 @@ typedef struct _Py_DebugOffsets { struct _list_object { uint64_t size; uint64_t ob_item; + uint64_t ob_size; } list_object; // PyDict object offset; @@ -175,7 +176,7 @@ typedef struct _Py_DebugOffsets { uint64_t size; uint64_t state; uint64_t length; - size_t asciiobject_size; + uint64_t asciiobject_size; } unicode_object; // GC runtime state offset; diff --git a/Include/internal/pycore_runtime_init.h b/Include/internal/pycore_runtime_init.h index cebf901a556129..7fab9710cc863d 100644 --- a/Include/internal/pycore_runtime_init.h +++ b/Include/internal/pycore_runtime_init.h @@ -103,6 +103,7 @@ extern PyTypeObject _PyExc_MemoryError; .list_object = { \ .size = sizeof(PyListObject), \ .ob_item = offsetof(PyListObject, ob_item), \ + .ob_size = offsetof(PyListObject, ob_base.ob_size), \ }, \ .dict_object = { \ .size = sizeof(PyDictObject), \ From c3589132d5cd69f8a7788cba14578796550fcb84 Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Thu, 15 Aug 2024 19:06:38 +0100 Subject: [PATCH 5/6] Moar offsets --- Include/internal/pycore_runtime.h | 6 ++++++ Include/internal/pycore_runtime_init.h | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Include/internal/pycore_runtime.h b/Include/internal/pycore_runtime.h index 21b14d3745ab5c..49375c5f906f30 100644 --- a/Include/internal/pycore_runtime.h +++ b/Include/internal/pycore_runtime.h @@ -46,6 +46,11 @@ struct _gilstate_runtime_state { #define _Py_Debug_Cookie "xdebugpy" +#ifdef Py_GIL_DISABLED +# define _Py_Debug_gilruntimestate_enabled offsetof(struct _gil_runtime_state, enabled) +#else +# define _Py_Debug_gilruntimestate_enabled 0 +#endif typedef struct _Py_AuditHookEntry { struct _Py_AuditHookEntry *next; Py_AuditHookFunction hookCFunction; @@ -75,6 +80,7 @@ typedef struct _Py_DebugOffsets { uint64_t builtins; uint64_t ceval_gil; uint64_t gil_runtime_state; + uint64_t gil_runtime_state_enabled; uint64_t gil_runtime_state_locked; uint64_t gil_runtime_state_holder; } interpreter_state; diff --git a/Include/internal/pycore_runtime_init.h b/Include/internal/pycore_runtime_init.h index 7fab9710cc863d..8ba627b0c0b5d9 100644 --- a/Include/internal/pycore_runtime_init.h +++ b/Include/internal/pycore_runtime_init.h @@ -50,7 +50,8 @@ extern PyTypeObject _PyExc_MemoryError; .sysdict = offsetof(PyInterpreterState, sysdict), \ .builtins = offsetof(PyInterpreterState, builtins), \ .ceval_gil = offsetof(PyInterpreterState, ceval.gil), \ - .gil_runtime_state= offsetof(PyInterpreterState, _gil), \ + .gil_runtime_state = offsetof(PyInterpreterState, _gil), \ + .gil_runtime_state_enabled = _Py_Debug_gilruntimestate_enabled, \ .gil_runtime_state_locked = offsetof(PyInterpreterState, _gil.locked), \ .gil_runtime_state_holder = offsetof(PyInterpreterState, _gil.last_holder), \ }, \ From dba6409c6cf81dee28c8c064861138643128e18b Mon Sep 17 00:00:00 2001 From: Pablo Galindo Date: Thu, 15 Aug 2024 19:17:27 +0100 Subject: [PATCH 6/6] fixup! fixup! Ensure the debug cookie only appears once in the binary --- Include/internal/pycore_runtime.h | 2 ++ Include/internal/pycore_runtime_init.h | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Include/internal/pycore_runtime.h b/Include/internal/pycore_runtime.h index 49375c5f906f30..d4291b87261ae0 100644 --- a/Include/internal/pycore_runtime.h +++ b/Include/internal/pycore_runtime.h @@ -48,8 +48,10 @@ struct _gilstate_runtime_state { #ifdef Py_GIL_DISABLED # define _Py_Debug_gilruntimestate_enabled offsetof(struct _gil_runtime_state, enabled) +# define _Py_Debug_Free_Threaded 1 #else # define _Py_Debug_gilruntimestate_enabled 0 +# define _Py_Debug_Free_Threaded 0 #endif typedef struct _Py_AuditHookEntry { struct _Py_AuditHookEntry *next; diff --git a/Include/internal/pycore_runtime_init.h b/Include/internal/pycore_runtime_init.h index 8ba627b0c0b5d9..1746bbf0eec8b5 100644 --- a/Include/internal/pycore_runtime_init.h +++ b/Include/internal/pycore_runtime_init.h @@ -34,7 +34,7 @@ extern PyTypeObject _PyExc_MemoryError; .debug_offsets = { \ .cookie = debug_cookie, \ .version = PY_VERSION_HEX, \ - .free_threaded = Py_GIL_DISABLED, \ + .free_threaded = _Py_Debug_Free_Threaded, \ .runtime_state = { \ .size = sizeof(_PyRuntimeState), \ .finalizing = offsetof(_PyRuntimeState, _finalizing), \