How to build a shared library (.so on Linux, .dylib on macOS), link against it, and handle runtime library loading.
| Aspect | Static (.a) |
Shared (.so/.dylib) |
|---|---|---|
| When code is included | At link time (copied into executable) | At runtime (loaded from disk) |
| Executable size | Larger (contains library code) | Smaller (just a reference) |
| Updating the library | Must re-link the executable | Just replace the .so/.dylib |
| Runtime requirement | None — self-contained | Library file must exist on disk |
Shared libraries can be loaded at any memory address. The code must be compiled so it works regardless of where it's placed:
CXXFLAGS = -std=c++20 -Wall -Wextra -Iinclude -MMD -MP -fPIC$(LIB): $(LIB_OBJS)
$(CXX) -shared -o $@ $^At runtime, the OS needs to find the .so/.dylib. Without -rpath, you'd have to set LD_LIBRARY_PATH (Linux) or DYLD_LIBRARY_PATH (macOS) every time. -rpath embeds the search path in the executable:
# macOS: look next to the executable
RPATH_FLAG = -Wl,-rpath,@executable_path
# Linux: look next to the executable
RPATH_FLAG = -Wl,-rpath,'$$ORIGIN'Library extensions differ by platform. This Makefile detects the OS:
UNAME := $(shell uname)
ifeq ($(UNAME),Darwin)
LIB = libstrutil.dylib
RPATH_FLAG = -Wl,-rpath,@executable_path
else
LIB = libstrutil.so
RPATH_FLAG = -Wl,-rpath,'$$ORIGIN'
endif$(shell uname) runs a shell command and captures the output. := means "evaluate immediately" (not lazily).
cd 10_shared_library
make # builds libstrutil.dylib (or .so) and app
./app # upper, lower, reverse transformations
otool -L app 2>/dev/null || ldd app # see runtime library dependencies
make clean| File | Purpose |
|---|---|
include/strutil.hpp |
Declares to_upper(), to_lower(), reverse() |
src/strutil.cpp |
Implements them |
src/main.cpp |
Uses the library |
Makefile |
-fPIC, -shared, platform detection, -rpath |