From 103a1684c856aa345875787329a43c7aa65184e7 Mon Sep 17 00:00:00 2001 From: Misha Chornyi <99709299+mc-nv@users.noreply.github.com> Date: Wed, 12 Aug 2026 11:40:47 -0700 Subject: [PATCH] build(before-code-freeze): Export the stub's dynamic symbols when libpython is static (#451) * build: Export the stub's dynamic symbols when libpython is static The manylinux base container builds CPython with --disable-shared, so on that platform triton_python_backend_stub links libpython3.x.a and the Py_* symbols live in the executable rather than a shared library. Python C extension modules are dlopen'ed and deliberately leave Py_* undefined -- every one of the 77 lib-dynload modules in the image does, math.so alone with 63 undefined symbols and no libpython in DT_NEEDED -- so they can only bind against the stub's dynamic symbol table. Without the export the stub links and starts, then fails on the first `import math`. Set ENABLE_EXPORTS, which makes CMake add the platform's flag (-Wl,--export-dynamic on Linux, per Platform/Linux.cmake), matching how CPython links its own interpreter. Gate it on the resolved library being an archive rather than on the platform. A shared libpython needs no export, so the Debian build is unaffected, and the condition stays correct if a target moves between the two linkage modes. The condition reads PYTHON_LIBRARY, not PYTHON_LIBRARIES: pybind11 arrives through FetchContent_MakeAvailable, which uses add_subdirectory, so only the cache entry find_library creates crosses back into this scope. PYTHON_LIBRARIES is a plain variable in pybind11's directory and is empty here, which would have made this a silent no-op. * docs: Correct the stale RHEL_BUILD rationale The comment said RHEL_BUILD existed because pybind would otherwise pick up Python 3.6 in the RHEL base container, so PYBIND11_PYTHON_VERSION was set to force 3.12. That pin has been dropped from build.py -- pybind11 prefers the newest entry of its own Python_ADDITIONAL_VERSIONS list, and the manylinux base container puts a single interpreter first on PATH. RHEL_BUILD itself stays: it still selects the stub's link settings further down. Only the justification was wrong. (cherry picked from commit 96d6021c501fec1aa9621c503d5dfdcac1664aa7) --- CMakeLists.txt | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f5c5b293..d0bcdfed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2020-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# Copyright 2020-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -93,9 +93,15 @@ FetchContent_Declare( GIT_SHALLOW ON ) -# RHEL base container has multiple version of Python installed. By default -# it seems like pybind will pickup v3.6, so we specifically assign it to -# search for 3.12 here. +# Detect a RHEL-family build. Used further down to adjust how the stub +# executable is linked. +# +# This used to also justify passing PYBIND11_PYTHON_VERSION, because pybind +# would otherwise pick up an older interpreter present in the RHEL base +# container. That is no longer needed: pybind11 prefers the newest entry of +# its own Python_ADDITIONAL_VERSIONS list, and the manylinux base container +# puts a single interpreter first on PATH. Set PYBIND11_PYTHON_VERSION +# explicitly if a build ever has to target a specific version again. set(RHEL_BUILD OFF) if(LINUX) file(STRINGS "/etc/os-release" DISTRO_ID_LIKE REGEX "ID_LIKE") @@ -285,6 +291,30 @@ target_compile_options( ) target_compile_definitions(triton-python-backend-stub PRIVATE TRITON_PB_STUB) +# When libpython is a static archive -- the manylinux base container builds +# CPython with --disable-shared -- the Py_* symbols land inside the stub +# executable instead of a shared library. Python C extension modules are +# dlopen'ed and deliberately leave Py_* undefined, so they can only resolve +# against the stub's dynamic symbol table. ENABLE_EXPORTS makes CMake add the +# platform's flag for that (-Wl,--export-dynamic on Linux), the same way +# CPython links its own interpreter binary. +# +# A shared libpython needs none of this, since modules bind to it directly. +# Key off the linkage rather than the platform so this stays correct if a +# target ever moves between the two. +# +# PYTHON_LIBRARY rather than PYTHON_LIBRARIES: pybind11 is pulled in with +# FetchContent_MakeAvailable, which uses add_subdirectory, so only the cache +# entry that find_library creates survives into this scope. PYTHON_LIBRARIES +# is a plain variable set inside pybind11's directory and reads empty here. +if(PYTHON_LIBRARY MATCHES "\\.a$") + set_target_properties( + triton-python-backend-stub + PROPERTIES + ENABLE_EXPORTS TRUE + ) +endif() + # RHEL assets are not released in a container environment nor do the current # Python lib versions in the manylinux base container match those currently # available for RHEL8 package managers. Therefore, we package the correct