Case 53: Namespace Pollution (Generic Symbol Names)¶
| Field | Value |
|---|---|
| Verdict | ๐ด BREAKING |
| Category | Breaking |
| Platforms | Linux |
| Flags | ABI break, API break, Bad practice |
Detected ChangeKinds |
func_removed, func_added |
| Source files | browse on GitHub |
Category: API Design / Policy | Verdict: BREAKING (bad practice)
What this case is about¶
v1 exports functions with extremely generic names: init, process,
cleanup, status. v2 renames them to mylib_init, mylib_process,
mylib_cleanup, mylib_status with a proper library prefix.
This is a design-level bad practice: unprefixed names in C shared libraries are a collision time-bomb in any non-trivial application that links multiple libraries.
Why namespace pollution is bad practice¶
C has no namespaces. All exported symbols from all shared libraries loaded in a process share a single flat namespace. Generic names cause:
- Silent symbol interposition: If two libraries export
init(), the dynamic linker picks one (typically the first loaded). The other library silently calls the wrong function with no warning. - Hard-to-debug crashes: Symbol collisions produce mysterious behavior โ wrong return values, corrupted state, segfaults in unrelated code.
- Prevents library composition: Applications cannot safely link two
libraries with overlapping symbol names without
RTLD_LOCALhacks. - LD_PRELOAD conflicts: Tools like sanitizers, profilers, and debug
libraries commonly use names like
initandcleanup.
What abicheck detects¶
FUNC_REMOVED:init,process,cleanup,statusremovedFUNC_ADDED:mylib_init,mylib_process,mylib_cleanup,mylib_statusaddedNAMESPACE_POLLUTION: v1 exports symbols without a consistent prefix
Verdict: BREAKING (bad practice) โ symbols renamed; v1 was the bad practice.
How to reproduce¶
# Build both versions
gcc -shared -fPIC -g bad.c -o libbad.so
gcc -shared -fPIC -g good.c -o libgood.so
# Check exports
nm -D libbad.so | grep ' T '
# โ T cleanup, T init, T process, T status โ generic names!
nm -D libgood.so | grep ' T '
# โ T mylib_cleanup, T mylib_init, T mylib_process, T mylib_status โ prefixed
# Demonstrate the collision
cat > other_lib.c <<'EOF'
int init(void) { return 99; } /* another library's init */
EOF
gcc -shared -fPIC other_lib.c -o libother.so
# Link app against both โ silent collision
gcc -g app.c -L. -lbad -lother -Wl,-rpath,. -o app
LD_DEBUG=symbols ./app 2>&1 | grep 'init'
# โ init() resolves to libother.so's version โ wrong library!
# Run abicheck
python3 -m abicheck.cli dump libbad.so -o /tmp/v1.json
python3 -m abicheck.cli dump libgood.so -o /tmp/v2.json
python3 -m abicheck.cli compare /tmp/v1.json /tmp/v2.json
# โ BREAKING (renamed symbols) + NAMESPACE_POLLUTION warning
How to fix¶
Use a consistent prefix for all exported symbols:
/* Good: all symbols prefixed with library name */
int mylib_init(void);
int mylib_process(int data);
void mylib_cleanup(void);
Or use a version script to hide unprefixed names:
Real-world examples¶
- zlib uses
z_prefix:z_inflate,z_deflate - OpenSSL uses
SSL_,EVP_,BN_prefixes - SQLite prefixes everything with
sqlite3_ - libpng uses
png_prefix
Libraries that historically did NOT prefix (like early POSIX open, read,
write) cause endless compatibility headaches.
References¶
Source files¶
See also: Examples overview ยท All BREAKING cases ยท Category: Breaking.