Shared Libraries
Statically Linked
Loading statically-linked executable object files:

- If two programs are compiled with
--static, they have a global variable, and executed at the same time, that global variable will have the same address
Dynamically Linked
Dynamically linked shared libraries
- Once the program is executed, the loader calls dynamic linker
- Dynamic linker "loads" shared libary
- Nothing is actually loaded
- Memory mapping: pretend it’s in memory (operating system deals with mapping of RAM address). Refer to vitual memory.

Shared libraries is the idea that multiple processes use the same shared library. We do this by putting the shared library into a dedicated place in memory. However, some questions stand:
- There may be many libraries, so we may run out of address space (or at least waste it).
So instead, we compile into position-independent code
Global Offset Table
Position-Independent Code No matter where the library is loaded into memory, the address between addresses are the same. We use a global offset table (GOT) where
- table in data segment (relative position known)
- contains absolute addresses of variables and functions
- get filled with correct values by the dynamic linker
It uses the instruction point register (%rip)
The linker knows the offset between %rip and the GOT.
For example,
Assume b is a global variable defined in a different exectuable object (like a shared library), and our source code performs b++.
The global offset table (in data segment) will look something like this:
| | |
|--|--|--|
|0|address of symbol a|
|1|address of symbol b|
|2|...|
The generate assembly code looks like
mov 0x2008b9(%rip), %rax
addl $1, (%rax)
- The distance between code line and GOT entry 1 is
0x2008b9bytes - The first line of code loads actual address of variable
- The second line increases
bby 1.
Filling GOT entries
References to data (to global variable)
- When an executable object is loaded, the dynamic linker eagerly determines run-time addresses for all referenced global variables, and stores them in the appropriate GOT entries
- Each loaded executable object (including shared libraries) has a symbol table indicating relative offsets of code and data definitions
- The dynamic linker uses the symbol table information to determine the run-time address of any code or data definition
- It knows the base address of each executable object, and the names and offsets of every symbol definition within it
- So it’s capable of producing an address for any defined symbol
References to code (functions)
-
Finding the address of called functions (like calling
printf, which is in thelibcshared library) is much more complicated. -
Function addresses are resolved lazily
- Each (externally-defined) function has an entry in the GOT
- Initial address in the GOT calls into the PLT (procedure linkage table), which invokes the dynamic loader to resolve the address of the called function and store it in the GOT
- Subsequent calls load the function's address from the GOT.
This way, it avoids start up overhead that, since many programs won't use all the functions in one library.
Tools for Manipulating Object Files
| Tool | Description |
|---|---|
| AR | Creates static libraries, and inserts, deletes, and extracts members STRINGS Lists all printable strings |
| STRIP | Deletes symbol table information |
| NM | Lists symbols defined in symbol table |
| READELF | Displays complete structure |
| OBJDUMP | Displays all information, useful to disassemble code |
- These are useful to figure out where something will be loaded.
Dynamic Loading
(of shared libraries)
Shared Libraries are loaded into memory at runtime.
- Loading and symbol resolution happens automatically when an executable is linked against shared libraries.
- E.g., every C program is linked against the C library (
libc), so the libc shared library is loaded automatically when the C program runs - We don't have to do anything explicit.
- E.g., every C program is linked against the C library (
- However, programs can also load shared libraries (and resolve symbols in them) dynamically. You can do this explicitly in your program.
The mechanism on Linux to explicitly dynamically load shared library is with dlfcn.h. Use it by #include <dlfcn.h>.
- Call
dlopento dynamically load a shared library - Once a shared library has been loaded, call
dlsymto get the addresses of data and functions within it - Call dlclose to unload the shared library
- Link the executable or shared library with
-ldl
Function pointers is a pointer to a function. Use them exactly as though they were functions.
Example:
// Declare a pointer to a function returning int and taking
// a single const char * parameter.
int (*ptr)(const char *);
// Make the function pointer point to a compatible function.
ptr = puts;
// Use the pointer to call the function it points to.
ptr("Hello world");
Dynamic loading is useful when
- Interpositioning to “redefine” functions
- Extending program capabilities using “plugins”
Interpositioning
Interpositioning is a technique useful for "redefining" library functions. It means linking (statically or dynamically) functions with the same name into the executable
- This is done to debug or trace program behavior, or to change and extend the behavior of the function.
This is also called "instrumenting" the executable in which functions are redefined by interpositioning.
- Dynamic loading allows the interposed function(s) to call the “real” function(s)
- The
LD_PRELOADenvironment variable can be used to “inject” interposed definitions into an arbitrary program
Linked from Computer System Fundamentals