fix(crusher): shim __libc_single_threaded for glibc < 2.32 + extend audit

PR #396's X2 dry-run caught a wheel-import failure on the manylinux_2_28
floor matrix entry (both x86_64 and aarch64). Same class as #355:

  ImportError: ... undefined symbol: __libc_single_threaded

`__libc_single_threaded` is a single-byte char added in glibc 2.32.
Newer libstdc++ (gcc 11+) reads it inside `__cxa_thread_atexit_impl`
to elide locking on the single-threaded fast path. ORT prebuilt static
archives compiled with gcc-14.2.1 against glibc-2.38+ headers bake in
the reference. Users with glibc < 2.32 hit ImportError on
`import headroom._core`.

Latent since the ORT artifact bump that started using gcc 14. X1 is
the gate that catches it at release time; X2 caught it at PR time —
exactly as designed.

Fix:
1. glibc_compat.c adds Section B: `char __libc_single_threaded = 0;`
   Setting to 0 (multi-threaded) is safe; libstdc++ takes the locked
   slow path. Setting to 1 would race in any multithreaded Rust wheel.
2. build.rs adds `-Wl,-u,__libc_single_threaded` so the shim's archive
   members are pulled regardless of scan order.
3. audit_wheel_glibc_symbols.py POST_FLOOR_SYMBOLS adds the new
   symbol — verified locally: the audit now rejects the failing
   PR #396 wheel with the right message.
This commit is contained in:
chopratejas 2026-05-05 13:59:21 -07:00
parent 2fc73d0f73
commit 17d6207bf5
7 changed files with 108 additions and 23 deletions

View file

@ -47,16 +47,31 @@ import tempfile
import zipfile
from pathlib import Path
# Symbol families introduced after specific glibc versions, beyond what
# `auditwheel` already checks. Add here as new bug classes surface.
# Symbols introduced after specific glibc versions, beyond what
# `auditwheel` already checks (auditwheel relies on the GLIBC_x.y
# version tag baked into versioned symbols; the entries below are
# either tagless or family-tagged, neither of which auditwheel
# catches). Add here as new bug classes surface.
#
# Each entry is `(symbol_name_prefix, min_glibc_version_introduced, justification_url)`.
# `startswith(prefix)` is used to match — for a single symbol use the
# full name as the prefix (no other symbol starts with it).
POST_FLOOR_SYMBOLS = [
# C23 strtol family. Issue #355.
(
"__isoc23_",
(2, 38),
"https://sourceware.org/glibc/wiki/Release/2.38",
),
# Single-threaded fast-path flag read by libstdc++ (gcc 11+).
# Caught by the X1 smoke gate on PR #396 (X2 dry-run) on the
# manylinux_2_28 floor entry — the audit had let the wheel
# through because it didn't know about this symbol.
(
"__libc_single_threaded",
(2, 32),
"https://sourceware.org/glibc/wiki/Release/2.32",
),
]