Skip to content

Native aarch64 rebuild breaks cross-aarch64 C++ extension linking downstream (LDCXXSHARED regression) #880

Description

@leofang

Downstream cross-aarch64 recipes with a C++ Python extension (e.g. numba-cuda, libnvshmem) started failing at link time after python-feedstock's aarch64 build was flipped from cross to native. The compiler stack and the recipe-side sed are both unchanged; what changed is what CPython's own configure step emits into the sysconfigdata.

When it landed

The "go native for arm builds" change removed linux_aarch64: linux_64 from conda-forge.yml, landing on 2026-06-11 to 2026-06-12 across every currently-live branch except 3.12:

Each PR bundled the flip with an unrelated main change (version bump or openssl backport); the flip itself is a one-liner in conda-forge.yml.

Symptom (downstream cross-aarch64 link step)

$PREFIX/compiler_compat/ld: $BUILD_PREFIX/bin/../libexec/gcc/aarch64-conda-linux-gnu/14.3.0/liblto_plugin.so: error loading plugin: ... cannot open shared object file: No such file or directory

Misleading error — the actual issue is the aarch64 ld shim in $PREFIX/compiler_compat/ being handed an x86_64 LTO plugin path via --plugin.

Bisect (numba-cuda-feedstock main, last-green vs first-fail)

  • gcc_impl_linux-aarch64 / binutils_impl_linux-aarch64 / ld_impl_linux-aarch64: identical (14.3.0=h77f067d_19 / 2.45.1=default_hc5fa074_102 / 2.45.1=default_h27e1c4c_102)
  • python (linux-aarch64): 3.10.20=h28be5d3_0_cpython3.10.20=h4f76b5d_1_cpython

Extracting _sysconfigdata_aarch64_conda_linux_gnu.py (the "raw configure" variant, saved before the recipe-side sed) from each package:

  • h28be5d3_0 (cross-built): 'CXX': 'aarch64-conda-linux-gnu-c++'
  • h4f76b5d_1 (native-built): 'CXX': 'aarch64-conda-linux-gnu-c++ -pthread'

LDCXXSHARED = "$(CXX) -shared", so it picks up the same -pthread on native. Then recipe/build_base.sh#L453-L456:

sed -i.bak "s@-pthread@-pthread -B $PREFIX/compiler_compat@g" sysconfigfile

turns the default sysconfigdata's LDCXXSHARED into 'g++ -pthread -B $PREFIX/compiler_compat -shared'. Setuptools uses LDCXXSHARED verbatim for C++ extension links, so -B $PREFIX/compiler_compat reaches the cross-toolchain link line and routes ld through the aarch64 shim. On a linux-64 build host driving an x86_64 GCC, the aarch64 ld can't handle the x86_64 LTO plugin GCC hands it via --plugin, and the link fails.

Why C-only cross-builds haven't been failing the same way

LDSHARED has always had -B $PREFIX/compiler_compat in it too, but patch 0019-Fix-LDSHARED-when-CC-is-overriden-on-Linux-too.patch makes distutils' customize_compiler strip the sysconfig CC prefix off LDSHARED and replace it with os.environ['CC']. Since LDSHARED starts with exactly the sysconfig CC string ('gcc -pthread -B $PREFIX/compiler_compat'), the entire -pthread -B compiler_compat chunk is consumed along with gcc, leaving only -shared -Wl,... in the resulting command. No equivalent exists for LDCXXSHARED, so it's used as-is.

Why 3.12 isn't affected

The 3.12 branch of this feedstock still has linux_aarch64: linux_64 in conda-forge.yml — it wasn't included in the "go native" flip. Correspondingly, only linux_aarch64_python3.12 passes in numba-cuda-feedstock run 28638905979; all other Python versions fail on aarch64.

Confirmed affected feedstocks

Suggested fixes

  1. Extend patch 0019 to also strip the sysconfig CXX prefix off LDCXXSHARED — symmetric with the C side. Preserves the sed's intent for native/system-gcc users.
  2. Or scope the sed itself away from CXX/LDCXXSHARED so -pthread -B compiler_compat never enters those vars.

Happy to test either against numba-cuda-feedstock's CI once a rebuild is available.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions