Skip to content

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:

  1. Caller calls dispatch() through the PLT (same as before).
  2. Dynamic linker resolves the symbol โ€” if IFUNC, calls the resolver first.
  3. GOT entry is patched with the final function address.
  4. 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).