Skip to content

Instantly share code, notes, and snippets.

@rgommers
Created September 16, 2026 22:35
Show Gist options
  • Select an option

  • Save rgommers/c6945e013a2519d98ecccf68de52404d to your computer and use it in GitHub Desktop.

Select an option

Save rgommers/c6945e013a2519d98ecccf68de52404d to your computer and use it in GitHub Desktop.
Idea for a way to encode the PyTorch Stable ABI Target into extension modules automatically

PyTorch Stable ABI Target Marker

Goals and constraints

The goal is to make the stable ABI target of a compiled PyTorch extension discoverable from the resulting binary.

The design should have the following properties:

  • No additional work for extension authors. Using the PyTorch stable headers and optionally setting TORCH_TARGET_VERSION should be sufficient. There should be no extra source file, build-system integration, linker flag, or post-processing step.
  • Cross-platform. The mechanism should work with ELF, Mach-O, and PE/COFF rather than relying on a format-specific metadata facility.
  • Robust against normal compiler and linker optimization. The marker should survive normal dead-code elimination, section garbage collection, and stripping.
  • Directly represent the declared ABI target. The binary should preserve the value of TORCH_FEATURE_VERSION, i.e. the TORCH_TARGET_VERSION selected by the extension when present, or the PyTorch headers' TORCH_ABI_VERSION otherwise.
  • Easy to inspect offline. Package managers, wheel auditing tools, and tools such as torch-abi-audit should be able to determine the target version without importing or executing the extension.
  • Extensible. The encoding should have an explicit schema/version so that more ABI metadata can be added later without ambiguity.

PyTorch already computes the relevant value in torch/csrc/stable/version.h: TORCH_FEATURE_VERSION is set to TORCH_TARGET_VERSION when supplied, otherwise to TORCH_ABI_VERSION.

The PyTorch ABI version itself is already represented as a 64-bit value in torch/headeronly/version.h.in, with major, minor, patch, and ABI-tag fields.

Proposed design

Have the PyTorch stable headers automatically emit one exported ABI marker symbol into every compiled extension that uses the stable ABI.

For example, reserve a symbol name such as:

__torch_stable_abi_marker_v1

The symbol contains a small self-identifying record whose payload includes the compile-time value of TORCH_FEATURE_VERSION.

A binary representation is preferable to stringifying the macro value, because TORCH_TARGET_VERSION may be supplied as an arbitrary valid constant expression. For example:

extern const unsigned char __torch_stable_abi_marker_v1[] = {
    'T', 'O', 'R', 'C', 'H', 'A', 'B', 'I',

    1,  /* marker schema version */

    (TORCH_FEATURE_VERSION >> 56) & 0xff,
    (TORCH_FEATURE_VERSION >> 48) & 0xff,
    (TORCH_FEATURE_VERSION >> 40) & 0xff,
    (TORCH_FEATURE_VERSION >> 32) & 0xff,
    (TORCH_FEATURE_VERSION >> 24) & 0xff,
    (TORCH_FEATURE_VERSION >> 16) & 0xff,
    (TORCH_FEATURE_VERSION >>  8) & 0xff,
     TORCH_FEATURE_VERSION        & 0xff,
};

Using a canonical byte order makes the record easy to inspect without having to interpret the target binary's native endianness.

The record could later grow to include other fields, for example an ABI epoch/tag, while the leading magic and schema byte provide a stable way to identify and decode it.

Automatic inclusion

This should be implemented in the stable headers rather than requiring extension authors to instantiate the marker themselves.

The stable API headers already include torch/csrc/stable/version.h. Examples include:

That means the marker can be emitted centrally from the stable-header machinery.

An extension author would continue doing only what they already do today, for example:

#define TORCH_TARGET_VERSION ...
#include <torch/csrc/stable/library.h>

or equivalently provide TORCH_TARGET_VERSION as a compiler definition.

No change should be required in setup.py, CMake, Meson, pyproject.toml, or the final linker invocation.

Export and retention

The marker should be an exported symbol, not merely an unreferenced static string.

Exporting it gives the compiler and linker a normal semantic reason to retain the object in the final shared library. This is a concept supported by all three major native binary formats.

A possible implementation is approximately:

/* ELF / Mach-O */
__attribute__((weak, visibility("default"), used))
extern const unsigned char __torch_stable_abi_marker_v1[] = {
    ...
};

/* MSVC / PE-COFF */
__declspec(selectany)
__declspec(dllexport)
extern const unsigned char __torch_stable_abi_marker_v1[] = {
    ...
};

The exact spelling should be validated across supported compiler versions, but the relevant mechanisms are standard:

  • GCC documents the weak and visibility attributes.
  • Clang documents weak, used, and related attributes.
  • MSVC documents selectany for header-defined globals that may appear in multiple translation units, and dllexport for exporting data from a DLL.
  • GNU ld documents section garbage collection and the treatment of externally visible symbols in shared libraries under --gc-sections.

The exported symbol is the primary retention mechanism. Additional compiler attributes such as used or, where available, retain can be added defensively, but should not be the fundamental protocol.

Multiple translation units

Stable headers may be included by many translation units in the same extension. The marker therefore has to be safely coalescable.

The intended behavior is:

  • ELF / Mach-O: use a weak or equivalent coalescable definition.
  • PE/COFF with MSVC: use selectany.

Every translation unit should encode the same TORCH_FEATURE_VERSION. If different translation units are compiled with inconsistent TORCH_TARGET_VERSION settings, that is already a build configuration error; it may be worth detecting explicitly if the linker behavior would otherwise hide it.

The final shared library should expose exactly one logical __torch_stable_abi_marker_v1 record.

Inspection

There are two useful ways to inspect the marker.

Symbol-table inspection

A binary-aware tool can locate the exported symbol by name and read its contents.

This is straightforward with the native symbol-table mechanisms on ELF, Mach-O, and PE/COFF.

Raw binary inspection

Because the record begins with a distinctive magic sequence and uses a platform-independent encoding, a simpler tool can also scan the binary directly for:

TORCHABI

followed by the schema byte and version fields.

The exported symbol provides reliable retention and semantic identity; the self-identifying record provides a simple format-independent inspection path.

Relationship to torch-abi-audit

torch-abi-audit currently inspects undefined symbols to determine whether an extension uses only the PyTorch stable ABI surface.

That answers a different question from the proposed marker:

  • Marker: what stable ABI target did the extension declare at build time?
  • Symbol audit: what PyTorch APIs does the resulting binary actually reference?

These are complementary.

With the marker available, an auditing tool could report both the declared target and the observed API usage, for example:

Declared stable ABI target: 2.10
Stable API usage:            yes
Highest API observed:        2.10
Result:                      consistent

It could also identify cases where the binary does not match its declared contract:

Declared stable ABI target: 2.10
Observed unstable symbol:    c10::...
Result:                      ABI contract violated

This makes the ABI marker authoritative build metadata, while symbol inspection remains a useful independent verifier.

Metadata semantics

The stored value should be described as the stable ABI target, not necessarily the absolute minimum version that could run the extension.

For example, an extension may explicitly target PyTorch 2.12 while only using symbols that were already present in 2.10. In that case:

Declared target:                  2.12
Minimum implied by symbol usage:  2.10

Both values are meaningful, but they answer different questions.

The binary marker should preserve the declared TORCH_FEATURE_VERSION; a separate audit may derive a lower minimum from the actual imported symbols.

Summary

The core proposal is:

Every extension using the PyTorch stable ABI automatically contains one exported, coalescable, self-identifying ABI marker symbol whose payload records the compile-time TORCH_FEATURE_VERSION.

This provides:

  • zero additional work for extension authors;
  • one mechanism across ELF, Mach-O, and PE/COFF;
  • reliable retention through ordinary linking and stripping;
  • offline inspection without importing the extension;
  • an explicit record of the declared ABI target;
  • a clean complement to symbol-based auditing;
  • room for future ABI metadata through a versioned record format.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment