Case 112: LP64 โ ILP64 Integer-Model Switch (oneMKL MKL_INT 32โ64)¶
| Field | Value |
|---|---|
| Verdict | ๐ด BREAKING |
| Category | Breaking |
| Platforms | Linux |
| Flags | ABI break |
Detected ChangeKinds |
integer_model_changed |
| Source files | examples/case112_lp64_ilp64/ |
Category: Numerical-Library ABI Hazard | Verdict: ๐ด BREAKING
Verdict and consumer impact¶
v1 is the LP64 interface: MKL_INT is int (32-bit). v2 is the
ILP64 interface: MKL_INT is long (64-bit). Every public entry point
that takes a dimension, stride, or count โ and every function that returns
one โ flips its integer width at the same time. The function names are
unchanged (extern "C"), so a consumer linked against the LP64 build
resolves the ILP64 symbols at load time but passes/reads integers with the
wrong width: array indices and strides are silently truncated or
misinterpreted. This is the highest-value numerical-library ABI hazard โ
oneMKL ships both interfaces and they are not interchangeable.
Old/new diff¶
| v1.h | v2.h |
|---|---|
typedef int MKL_INT; |
typedef long MKL_INT; |
abicheck command¶
gcc -shared -fPIC -g v1.c -o libv1.so
gcc -shared -fPIC -g v2.c -o libv2.so
abicheck compare libv1.so libv2.so
Expected abicheck finding¶
Verdict: BREAKING (exit 4)
- typedef_base_changed: Typedef base type changed: MKL_INT (int -> long int)
> Underlying type changed; old code using the typedef operates on wrong
representation.
Affected symbols: cblas_isamax, cblas_saxpy, cblas_sscal, mkl_get_max_threads
- integer_model_changed: Integer model changed (LP64 -> ILP64 (32-bit -> 64-bit)):
integer typedef(s) resized: MKL_INT (int -> long int).
> A large fraction of public integer parameters/returns flipped width
together, or a public integer typedef changed its underlying size โ
the signature of an LP64<->ILP64 model switch.
Minimum evidence¶
min_evidence: L1 โ DWARF alone carries the typedef's underlying base
type (MKL_INT pointing at int in v1, long int in v2) and every public
function's parameter/return types, which is enough for typedef_base_changed
and, once enough public integer slots move together, integer_model_changed.
No public headers are needed.
Why abicheck catches it¶
integer_model_changed fires because a large fraction of public integer
parameters/returns flip width together and the MKL_INT typedef itself
changes its underlying size (int โ long int) โ DWARF's
DW_TAG_typedef/DW_AT_type chain records both the typedef and each
function's actual parameter types, so abicheck detects the correlated,
library-wide width flip and reports it as one grouped diagnostic (backed by
the individual typedef_base_changed finding) rather than leaving it as N
unrelated per-function signature changes.
Runtime failure demonstration¶
Severity: CRITICAL (width-dependent โ silent for small positive values, crashes for negative/large ones).
The shipped app.c calls cblas_isamax/mkl_get_max_threads with small
positive values; on x86-64 SysV, writing a 32-bit register (as the LP64
caller does) always zero-extends the upper 32 bits, so a small positive
32-bit int happens to read back correctly as a 64-bit long โ the app's
exit code doesn't change across the swap for this specific fixture:
gcc -shared -fPIC -g v1.c -o libv1.so
gcc -g app.c -L. -lv1 -Wl,-rpath,. -I. -o app
./app
# โ exit 1 (cblas_isamax=0 + mkl_get_max_threads=1)
gcc -shared -fPIC -g v2.c -o libv1.so # swap, no recompile
./app
# โ exit 1 (same โ no visible symptom for this specific small-positive input)
A negative stride makes the same width mismatch fatal, since a negative
32-bit int written to a register does not sign-extend the upper 32 bits
โ the ILP64 callee reads a huge positive long instead:
# a small standalone consumer calling cblas_sscal(n, 2.0f, x, incx=-1)
gcc -shared -fPIC -g v1.c -o libv1.so
gcc -g runtime_demo.c -L. -lv1 -Wl,-rpath,. -I. -o demo
./demo
# โ ok, x[0]=2.000000 (exit 0)
gcc -shared -fPIC -g v2.c -o libv1.so # swap, no recompile
./demo
# โ Segmentation fault (exit 139)
Why CRITICAL: incx=-1 is written as a 32-bit register value by the
LP64 caller; the ILP64 callee reads the same register as a 64-bit long
with its upper bits zero rather than sign-extended, turning -1 into a
huge positive stride and walking x[] far out of bounds.
Safe redesign¶
Never silently swap the integer model of a shipped interface. Ship LP64 and
ILP64 as distinctly named libraries/symbol sets (as oneMKL actually
does โ libmkl_intel_lp64 vs libmkl_intel_ilp64), or gate the choice at
compile time via a distinct header/macro so a consumer's build fails loudly
instead of linking against the wrong width silently.
Real-world example: oneMKL, OpenBLAS, and other BLAS/LAPACK
distributions ship parallel LP64/ILP64 builds for exactly this reason โ
mixing them (e.g. picking up the wrong .so at link or load time) is a
well-known, hard-to-diagnose class of numerical-library bug reports.
Cross-tool comparison¶
Source files¶
CMakeLists.txtapp.cv1.cv1.hv2.cv2.h
See also: Examples overview ยท All BREAKING cases ยท Category: Breaking.