Skip to content

Case 29: GNU IFUNC Transition

Field Value
Verdict ๐ŸŸข COMPATIBLE
Category Quality (Compatible)
Platforms Linux
Flags โ€”
Detected ChangeKinds ifunc_introduced
Source files examples/case29_ifunc_transition/

Category: ELF / Symbol Quality | Verdict: ๐ŸŸข COMPATIBLE

Verdict and consumer impact

dispatch is a regular function (STT_FUNC) in v1 and a GNU indirect function (STT_GNU_IFUNC) in v2 โ€” the symbol now points at a resolver that the dynamic linker calls once at load time to pick the actual implementation address, which is then patched into the GOT. This is the mechanism glibc uses to dispatch memcpy/strlen to CPU-specific implementations. The transition is transparent to callers: the call site, signature, and calling convention are unchanged, so any binary compiled against v1 continues to work against v2 without recompilation.

Old/new diff

old/lib.c new/lib.c
int dispatch(int x) { return x * 2; } int dispatch(int x) __attribute__((ifunc("resolve_dispatch"))); with a resolve_dispatch() resolver returning dispatch_generic

abicheck command

gcc -shared -fPIC -g old/lib.c -Iold -o libdispatch_v1.so
gcc -shared -fPIC -g new/lib.c -Inew -o libdispatch_v2.so
abicheck compare libdispatch_v1.so libdispatch_v2.so

Expected abicheck finding

Verdict: COMPATIBLE (exit 0)

Quality Issues:
- ifunc_introduced: Symbol became GNU_IFUNC: dispatch

Minimum evidence

min_evidence: L0 โ€” ELF's symbol table records each symbol's type directly (STT_FUNC vs. STT_GNU_IFUNC); the exported-symbol table alone is enough, no debug info or headers needed.

Why abicheck catches it

abicheck reads each exported symbol's ELF st_info type field from both .so files; a symbol that is STT_FUNC in the old snapshot and STT_GNU_IFUNC in the new one (or vice versa) is reported as ifunc_introduced, a quality finding rather than a break, since the PLT/GOT indirection is resolved transparently by the dynamic linker.

Runtime failure demonstration

No observable effect on existing binaries โ€” dispatch(5) returns the identical value both before and after the swap. The PLT/GOT mechanism handles the indirection: the dynamic linker calls the resolver once at load time and patches the GOT entry, so the caller's call site never changes.

# Build old library + 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 library (no recompile)
gcc -shared -fPIC -g new/lib.c -Inew -o libdispatch.so
./app
# โ†’ dispatch(5) = 10 (expected 10)   (identical)

Safe redesign

No redesign needed โ€” this is already a safe transition. The only edge cases are tooling-level: debugger breakpoints set on an IFUNC symbol hit the resolver on first call rather than the resolved implementation, and very old ld.so versions predating IFUNC support cannot load the library at all (a deployment-target concern, not an ABI contract violation).

Real-world example: glibc's memcpy, strlen, and other hot-path string/memory functions are GNU IFUNCs, dispatching to SSE/AVX/NEON implementations chosen at load time based on detected CPU features.

Cross-tool comparison

abidw --out-file v1.xml libdispatch_v1.so
abidw --out-file v2.xml libdispatch_v2.so
abidiff v1.xml v2.xml

References


Source files

  • CMakeLists.txt
  • app.c

See also: Examples overview ยท All COMPATIBLE cases ยท Category: Quality (Compatible).