What You Will Learn
Chapter 42 of TLPI builds on the shared library fundamentals from Chapter 41 and covers advanced, production-level features. You will learn how Linux programs can load shared libraries at runtime without recompiling, how to precisely control which symbols a library exposes, how to maintain backward compatibility using symbol versioning, how to run setup/teardown code automatically when a library loads, how to override library functions for testing, and how to debug the dynamic linker itself.
These topics are essential for writing plug-in architectures, building robust shared libraries, and debugging complex Linux applications. Interview panels at embedded and systems companies frequently test this material.
Tutorial Files
Understand what dynamically loaded libraries are, why they matter, the four core API functions, and how to link against libdl.
Deep dive into dlopen() — all flags explained (RTLD_LAZY, RTLD_NOW, RTLD_GLOBAL, RTLD_LOCAL, RTLD_NODELETE, RTLD_NOLOAD, RTLD_DEEPBIND), reference counting, and dependency loading.
Error handling with dlerror(), obtaining function/variable addresses with dlsym(), correct function pointer casting, RTLD_DEFAULT and RTLD_NEXT pseudohandles.
Closing libraries with dlclose(), inspecting symbol addresses with dladdr(), and making main program symbols available to dynamically loaded libraries using –export-dynamic. Full working dynload example.
Why symbol visibility matters, static keyword, GCC visibility attribute (hidden/default), and the three problems that uncontrolled visibility causes.
Writing .map version scripts to precisely control which symbols are exported, global/local keywords, wildcard patterns, anonymous version tags, and readelf verification.
Export multiple versions of the same function from one shared library. .symver assembler directives, @ vs @@, version tag dependencies, backward compatibility without major version bumps.
Automatically execute code when a library loads/unloads using GCC constructor and destructor attributes, multiple init/fini functions, and the older _init()/_fini() approach.
Override any library function at runtime using LD_PRELOAD. Per-process vs system-wide preloading, security restrictions for setuid programs, practical testing use cases.
Use LD_DEBUG to trace library searches, symbol resolution, relocations, and version dependencies. All keyword options explained with real output examples.
Ready to Master Shared Libraries?
Start from file 1 and work through each topic in order. Each file builds on the previous one.
