GLIBCXX and CXXABI Requirements: Check a Binary Against the Runtime Actually Shipped

Before shipping a C++ executable to an older root filesystem, compare the versioned symbols it needs with the symbols exported by the runtime that the image will actually load. Finding a file named libstdc++.so.6 is only the start. The useful release artifact is a required-versus-provided inventory bound to exact executable, dependency and provider identities.

This procedure uses GCC 14.2’s ABI policy and GNU Binutils 2.44 inspection tools. A safe host fixture built one small C++17 program and compared its GLIBCXX and CXXABI symbol requirements with its installed provider. No older runtime or target image was launched.

Keep version namespaces separate

GLIBCXX labels describe versioned libstdc++ interfaces. CXXABI labels identify interfaces in the C++ ABI support area. GLIBC labels belong to a different runtime interface family. Record each namespace independently instead of treating their suffixes as a single operating-system version.

The GCC libstdc++ ABI policy explains the library’s symbol-version evolution and compatibility expectations. Its version history is useful background, but the actual ELF objects remain the evidence for a particular image. A distribution can backport changes or package libraries differently from a simple upstream release mapping.

Do not use a lexical sort of dotted labels as a numeric comparison. More importantly, the highest label found anywhere in a file is not a complete specification of the symbols required by the executable. Debug strings, available provider definitions and actual consumer needs are different inventories.

Consumer symbol-version pairs are compared with the exact selected runtime provider; missing pairs hold the release, while a clean comparison is only one qualification gate.
Original version-requirement workflow with the bounded sixteen-pair host result.

Capture the consumer’s exact requests

Use readelf –version-info to inspect version requirements and readelf -d to record DT_NEEDED names. Use a dynamic-symbol view, such as nm -D –undefined-only, to associate undefined symbols with their version labels. The Binutils 2.44 tools manual documents these distinct views.

Preserve raw symbol names for comparisons and add demangled names only as a reading aid. A demangled rendering can hide distinctions that matter to the linker. Keep binding information too: a general qualification tool must distinguish strong required imports from weak optional references rather than treating every undefined entry identically.

Represent each relevant required interface as a pair of raw symbol name and version label. Also retain its requesting object, because a library or plugin can add requirements absent from the main executable. A program that passes the main-file comparison may still fail when an optional component is loaded later.

Identify the provider selected by the image

Inspect the candidate runtime’s dynamic definitions and version definitions. Record its full path within the image, file hash, ELF machine and class, SONAME and packaging provenance. Resolve symlinks for identity while preserving the requested runtime name in the deployment record.

The glibc loader reference makes search-path selection part of the diagnosis. RPATH, RUNPATH, the environment and configured library locations can select a different file from the one an engineer inspected. Establish the expected selection under the real launcher rather than assuming a convenient sysroot file is authoritative.

For a cross-built image, perform static inspection with suitable host tools. Do not run an untrusted or foreign-architecture executable just to discover dependencies. A controlled target launch comes later, after the image and loader environment are known.

Join requirements to definitions

Normalize the annotation syntax carefully. Inspection tools may show one @ for a reference or nondefault version and @@ for a default definition. The comparison key is the underlying symbol and version, while default-version status remains additional information. A parser that leaves an extra @ in the symbol name can create false missing-interface results.

Calculate required pairs minus provided pairs for each selected provider. Report the requesting object and complete missing pair, not only “runtime too old.” Separately flag unexpected machine, class or dependency identity before accepting a clean pair comparison.

A clean join is a necessary check for the scoped imports; it is not a complete ABI proof. Calling conventions, data layouts, initialization behavior, runtime dependencies and application assumptions still matter. The analysis must also include the provider’s own dependency closure, rather than stopping when libstdc++ itself looks sufficient.

What the host fixture established

The fixture compiled a small program that constructed a std::string and wrote it to standard output. On the x86-64 host, GCC 14.2.0 and Binutils 2.44 exposed sixteen required GLIBCXX/CXXABI symbol-version pairs. All sixteen appeared in the inspected libstdc++.so.6.0.33 provider, whose hash was saved with the result.

The program printed the expected text and exited successfully in that host environment. The required set included several GLIBCXX_3.4.21 string interfaces and a GLIBCXX_3.4.32 initialization interface. These are observations about this binary and toolchain, not minimum requirements for every C++17 program.

A negative inventory model removed one needed pair from a copied provider set. The comparison detected exactly that missing pair. This exercised the set-comparison logic without installing an older runtime or altering the host library. It did not reproduce an actual loader rejection on an older system.

Turn missing pairs into a bounded decision

If the intended baseline lacks required interfaces, hold the release and choose a supported build/runtime combination. Rebuilding against the intended baseline can reduce accidental newer-runtime requirements, but the resulting artifacts still need inspection. Source compatibility alone does not establish the emitted binary’s requirements.

Alternatively, a product may qualify a newer runtime as part of an image update. Review its complete dependencies, architecture, license obligations and update/rollback behavior. Copying one newer shared object into a deployed system or globally changing LD_LIBRARY_PATH is not an adequate compatibility plan.

For final acceptance, test the exact image in an approved disposable environment, including startup, error handling and plugin activation. Preserve the static comparison, selected-provider evidence and runtime result separately. The deliverable should say precisely which artifact closure was checked, which pairs were satisfied and which target behaviors remain untested.

Similar Posts