<?xml version="1.0" encoding="UTF-8" ?>

<bugzilla version="5.2"
          urlbase="https://bugzilla.altlinux.org/"
          
          maintainer="jenya@basealt.ru"
>

    <bug>
          <bug_id>61138</bug_id>
          
          <creation_ts>2026-10-20 11:10:16 +0300</creation_ts>
          <short_desc>256-check_python3_abi.brp переименовывает в .abi3.so расширения, собранные без limited API</short_desc>
          <delta_ts>2026-10-20 11:10:16 +0300</delta_ts>
          <reporter_accessible>1</reporter_accessible>
          <cclist_accessible>1</cclist_accessible>
          <classification_id>4</classification_id>
          <classification>Development</classification>
          <product>Sisyphus</product>
          <component>rpm-build-python3</component>
          <version>unstable</version>
          <rep_platform>x86_64</rep_platform>
          <op_sys>Linux</op_sys>
          <bug_status>NEW</bug_status>
          <resolution></resolution>
          
          
          <bug_file_loc></bug_file_loc>
          <status_whiteboard></status_whiteboard>
          <keywords></keywords>
          <priority>P5</priority>
          <bug_severity>normal</bug_severity>
          <target_milestone>---</target_milestone>
          
          
          <everconfirmed>1</everconfirmed>
          <reporter name="Stanislav Levin">slev</reporter>
          <assigned_to name="Ivan Zakharyaschev">imz</assigned_to>
          <cc>darktemplar</cc>
    
    <cc>george</cc>
    
    <cc>glebfm</cc>
    
    <cc>imz</cc>
    
    <cc>kotopesutility</cc>
    
    <cc>ldv</cc>
    
    <cc>slev</cc>
    
    <cc>vitty</cc>
          
          <qa_contact>qa-sisyphus</qa_contact>

      

      

      

          <comment_sort_order>oldest_to_newest</comment_sort_order>  
          <long_desc isprivate="0" >
    <commentid>302876</commentid>
    <comment_count>0</comment_count>
    <who name="Stanislav Levin">slev</who>
    <bug_when>2026-10-20 11:10:16 +0300</bug_when>
    <thetext>256-check_python3_abi.brp переименовывает модуль расширения из                    
.cpython-3XX.so в .abi3.so, если все функции Python C API, которые он             
использует, есть в списке stable ABI. Но по символам этого не узнать.             
                                                                                  
Модуль работает с объектами Python двумя способами:                               
- вызывает функции Python C API - их имена записаны в .so как                     
  неразрешённые символы, при загрузке модуля они берутся из                       
  интерпретатора python3; brp их видит;                                           
- сам читает поля структур объектов - это делают макросы и inline-                
  функции из заголовков Python. Компилятор заменяет имя поля на его               
  адрес внутри структуры (смещение) из заголовков той версии Python, с            
  которой идёт сборка, и записывает это число прямо в машинный код.               
  Имени не остаётся, поэтому brp этого не видит.

Без Py_LIMITED_API многие макросы читают поля напрямую. С                         
Py_LIMITED_API те же макросы становятся вызовами функций или недоступны           
совсем. Stable ABI обещает сохранять только часть структур и полей (см.           
python3/Misc/stable_abi.toml). Например, что читают макросы без limited API:                
                                                                                  
- PyUnicode_Check читает поле tp_flags структуры PyTypeObject.                    
  PyTypeObject в stable ABI - &apos;opaque&apos;: &quot;No members are part of the ABI,          
  nor is the size&quot;. Нарушение stable ABI.                                         
- PyTuple_GET_ITEM читает поле ob_item структуры PyTupleObject. Этой              
  структуры в stable ABI нет. Нарушение stable ABI.                               
- PyUnicode_GET_LENGTH читает поле length структуры PyASCIIObject. Этой           
  структуры в stable ABI нет. Нарушение stable ABI.                              
                                                                                 
Подробные ссылки на исходники - в конце.

Шаги для воспроизведения:                                                        
                                                                                 
$ cat &gt; foo.c &lt;&lt;&apos;EOF&apos;                                                            
#include &lt;Python.h&gt;                                                              
static PyObject *is_str(PyObject *m, PyObject *obj)                              
{                                                                                
    return PyBool_FromLong(PyUnicode_Check(obj));                                
}                                                                                
static PyMethodDef methods[] = {{&quot;is_str&quot;, is_str, METH_O, NULL}, {NULL}};       
static struct PyModuleDef module = {                                             
    .m_base = PyModuleDef_HEAD_INIT,                                             
    .m_name = &quot;foo&quot;,                                                             
    .m_methods = methods,                                                        
};                                                                               
PyMODINIT_FUNC PyInit_foo(void) { return PyModuleDef_Init(&amp;module); }            
EOF                                                                              
$ F=&quot;$(python3-config --cflags)&quot;                                                 
$ gcc -shared -fPIC $F foo.c -o foo.cpython-314.so                               
$ gcc -shared -fPIC $F -DPy_LIMITED_API=0x030E0000 foo.c -o foo.abi3.so

Один и тот же исходный код даёт два разных модуля:                               
                                                                                 
$ nm -D --undefined-only foo.cpython-314.so | grep Py                            
                 U PyBool_FromLong                                               
                 U PyModuleDef_Init                                              
$ nm -D --undefined-only foo.abi3.so | grep Py                                   
                 U PyBool_FromLong                                               
                 U PyModuleDef_Init                                              
                 U Py_TYPE                                                       
                 U PyType_GetFlags                                               
                                                                                 
В foo.abi3.so PyUnicode_Check - это вызовы Py_TYPE и PyType_GetFlags.            
В foo.cpython-314.so вместо них встроен код, который сам читает поле             
tp_flags структуры PyTypeObject.                                                 
                                                                                 
Лог brp для foo.cpython-314.so:                            
                                                                                 
process_so.py: .../site-packages/foo.cpython-314.so requires only stable         
API from python3: {&apos;PyBool_FromLong&apos;, &apos;PyModuleDef_Init&apos;}                        
256-check_python3_abi.brp: Rename .../site-packages/foo.cpython-314.so           
to .../site-packages/foo.abi3.so                                                 
                                                                                 
Фактический результат:                                                           
foo.cpython-314.so переименован в foo.abi3.so. Теперь он выглядит так            
же, как настоящая сборка с limited API, но в нём по-прежнему зашиты              
смещения полей структур Python 3.14.  

Предлагаемое исправление:                                                         
Согласно документации (Py_LIMITED_API, Py_TARGET_ABI3T (3.15+)):                  
https://docs.python.org/3/c-api/stable.html#compiling-for-stable-abi              
                                                                                  
&gt; To compile for a Stable ABI, define one or both of the following macros to the lowest Python version your extension should support, in Py_PACK_VERSION format  
                                                                                  
Нет обещания, что наличие *только* abi3 символов делает модуль abi3 совместимым.  
Проверка символов может доказать, что модуль НЕ совместим с abi3, но не может     
доказать, что он совместим. Это решает только сборка.                             
                                                                                  
Ссылки на исходники                                                               
https://git.altlinux.org/gitoskop/#/gears/p/python3.git/-/blob/sisyphus/python3/Include/

PyUnicode_Check(op):                                                              
1. unicodeobject.h:106-107 - макрос:                                              
   PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_UNICODE_SUBCLASS).                 
2. Py_TYPE(op): без limited API (или limited &lt; 3.14) - inline                     
   _Py_TYPE, return ob-&gt;ob_type (object.h:268-275); с limited API &gt;=              
   3.14 - функция Py_TYPE (object.h:262).                                         
3. PyType_FastSubclass - макрос, вызывает PyType_HasFeature                       
   (object.h:798).                                                                
4. PyType_HasFeature (object.h:782-796): с limited API -                          
   flags = PyType_GetFlags(type) (функция, объявлена в object.h:431;              
   комментарий &quot;PyTypeObject is opaque in the limited C API&quot;); без                
   limited API - flags = type-&gt;tp_flags (object.h:792).                           
                                                                                  
PyTuple_GET_ITEM(op, index):                                                      
1. cpython/tupleobject.h подключается только при #ifndef Py_LIMITED_API           
   (tupleobject.h:37-41); с limited API макроса нет, есть только функция          
   PyTuple_GetItem (tupleobject.h:32).                                           
2. cpython/tupleobject.h:29 - макрос:                                            
   (_PyTuple_CAST(op)-&gt;ob_item[(index)]).                                        
3. _PyTuple_CAST (cpython/tupleobject.h:18-19) - приведение к                    
   PyTupleObject*.                                                               
4. PyTupleObject (cpython/tupleobject.h:5-13): PyObject_VAR_HEAD, затем          
   массив ob_item (строка 12).

PyUnicode_GET_LENGTH(op):                                                        
1. cpython/unicodeobject.h подключается только при                               
   #ifndef Py_LIMITED_API (unicodeobject.h:1020-1024); с limited API             
   макроса нет. Есть только функция PyUnicode_GetLength                          
   (unicodeobject.h:158-163), и то лишь при limited API &gt;= 3.3:                  
   #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 &gt;= 0x03030000.               
2. cpython/unicodeobject.h:301 - макрос, вызывает static inline функцию          
   PyUnicode_GET_LENGTH (cpython/unicodeobject.h:298-300).                       
3. Функция: return _PyASCIIObject_CAST(op)-&gt;length.                              
4. _PyASCIIObject_CAST (cpython/unicodeobject.h:187-189) - приведение к          
   PyASCIIObject*.                                                               
5. PyASCIIObject (cpython/unicodeobject.h:54-163), поле length                   
   (строка 100).</thetext>
  </long_desc>
      
      

    </bug>

</bugzilla>