Case 29 โ GNU IFUNC Transition¶
| Field | Value |
|---|---|
| Verdict | ๐ข COMPATIBLE |
| Category | Quality (Compatible) |
| Platforms | Linux |
| Flags | โ |
Detected ChangeKinds |
โ |
| Source files | browse on GitHub |
Verdict: ๐ข COMPATIBLE abicheck verdict: COMPATIBLE (informational/warning)
What changes¶
| Version | ELF symbol type |
|---|---|
| v1 | dispatch: STT_FUNC (regular function) |
| v2 | dispatch: STT_GNU_IFUNC (indirect function) |
What is GNU IFUNC?¶
GNU IFUNC (indirect function) is a mechanism for runtime CPU dispatch. Instead of pointing directly to the function body, the symbol points to a resolver function that returns the address of the best implementation for the current CPU. The dynamic linker calls the resolver at load time and patches the GOT entry.
Common uses:
- glibc's memcpy, strlen, etc. use IFUNC to select SSE/AVX/NEON implementations.
- Math libraries dispatch to architecture-specific code paths.
Why this is NOT a binary ABI break¶
The transition from regular function to IFUNC (or back) is transparent to callers. The PLT/GOT mechanism handles the indirection automatically:
- Caller calls
dispatch()through the PLT (same as before). - Dynamic linker resolves the symbol โ if IFUNC, calls the resolver first.
- GOT entry is patched with the final function address.
- All subsequent calls go directly to the resolved address.
abicheck classifies this as COMPATIBLE because: - The calling convention is unchanged. - The function signature is unchanged. - Symbol resolution succeeds transparently. - This is purely an implementation optimization.
What it does affect¶
- Debugger behavior: breakpoints on IFUNC symbols may behave differently (breakpoint hits the resolver on first call, then the resolved implementation).
- Static analysis tools: may not recognize the indirection.
- Older dynamic linkers: very old ld.so versions may not support IFUNC (but this is a deployment concern, not an ABI contract issue).
Code diff¶
-int dispatch(int x) { return x * 2; }
+static int dispatch_generic(int x) { return x * 2; }
+
+/* IFUNC resolver โ called by dynamic linker at load time */
+static int (*resolve_dispatch(void))(int) { return dispatch_generic; }
+int dispatch(int x) __attribute__((ifunc("resolve_dispatch")));
In production, the resolver would typically select between multiple implementations (e.g., generic vs AVX) based on CPU feature detection. This example uses a single implementation for simplicity.
Real Failure Demo¶
Severity: INFORMATIONAL
Scenario: app calls dispatch(5). Both regular function and IFUNC return 10 transparently.
# Build old lib (regular function) + app
gcc -shared -fPIC -g old/lib.c -Iold -o libdispatch.so
gcc -g app.c -Iold -L. -ldispatch -Wl,-rpath,. -o app
./app
# โ dispatch(5) = 10 (expected 10)
# Swap in new lib (GNU IFUNC โ resolver picks implementation at load time)
gcc -shared -fPIC -g new/lib.c -Inew -o libdispatch.so
./app
# โ dispatch(5) = 10 (expected 10) โ identical result
Why INFORMATIONAL: The PLT/GOT mechanism handles IFUNC resolution transparently.
The caller uses the same call site; the dynamic linker calls the resolver once at
load time and patches the GOT entry. Only edge cases differ: debugger breakpoints
may hit the resolver first, and very old ld.so versions may not support IFUNC.
Why runtime result may differ from verdict¶
IFUNC: PLT/GOT transparent to caller, runtime compat
References¶
Source files¶
See also: Examples overview ยท All COMPATIBLE cases ยท Category: Quality (Compatible).