Skip to content

Case 59: Function Became Inline (outlined โ†’ inline)

Field Value
Verdict ๐Ÿ”ด BREAKING
Category Breaking
Platforms Linux, macOS
Flags ABI break
Detected ChangeKinds func_removed
Source files browse on GitHub

Category: Symbol API | Verdict: BREAKING (API_BREAK)

What this case is about

v1 exports fast_abs and fast_max as regular outlined functions in the shared library. v2 moves them to the header as static inline โ€” the symbols disappear from .dynsym.

This is the inverse of case47 (inline_to_outlined), which is compatible. Moving a function from outlined to inline is breaking because existing binaries depend on the symbol being in the library.

What breaks at binary level

  • Symbols removed: fast_abs and fast_max are no longer in .dynsym.
  • Dynamic linker fails: Existing binaries that reference these symbols get undefined symbol errors at load time.
  • Source compatibility preserved: New compilations with the v2 header work fine (the inline definition is available), but old binaries break.

What abicheck detects

  • FUNC_REMOVED: Both function symbols are absent from v2's export table. From the binary perspective, this is indistinguishable from deletion.

Overall verdict: BREAKING

How to reproduce

Linux:

gcc -shared -fPIC -g bad.c  -o libbad.so
gcc -shared -fPIC -g good.c -o libgood.so

nm -D libbad.so  | grep fast_  # โ†’ T fast_abs, T fast_max
nm -D libgood.so | grep fast_  # โ†’ (nothing โ€” inlined away)

# Link app against v1
gcc -g app.c -L. libbad.so -Wl,-rpath,. -o app
./app  # works

# Swap to v2
cp libgood.so libbad.so
./app
# โ†’ error: undefined symbol: fast_abs

macOS:

cc -shared -fPIC -g bad.c  -o libbad.dylib
cc -shared -fPIC -g good.c -o libgood.dylib

nm libbad.dylib  | grep fast_  # โ†’ T _fast_abs, T _fast_max
nm libgood.dylib | grep fast_  # โ†’ (nothing โ€” inlined away)

# Link app against v1
cc -g app.c -L. -lbad -Wl,-rpath,@loader_path -o app
./app  # works

# Swap to v2
cp libgood.dylib libbad.dylib
./app
# โ†’ dyld: Symbol not found: _fast_abs

How to fix

Keep an outlined fallback alongside the inline version:

/* header */
static inline int fast_abs(int x) { return x < 0 ? -x : x; }

/* .c file โ€” provide an exported symbol for backward compat */
int fast_abs(int x) { return x < 0 ? -x : x; }

Or use a __attribute__((weak)) symbol.

References


Source files

See also: Examples overview ยท All BREAKING cases ยท Category: Breaking.