Static Library and Shared Library

Usually, you have files (ex: main.cpp) calling functions from other files, which we call libraries.

We divide into 2 types of libraries

  1. static libraries (“.a” extension)
  2. shared libraries (”.so” extension)

What is the difference between the 2 kinds of libraries?

The difference is that a static library will generate a copy each time it is called, and the shared library has only one copy, which saves space.

// Static Library
add_library( hello libHelloSLAM.cpp )
 
// Shared library
add_library( hello_shared SHARED libHelloSLAM.cpp )
 
...
// Link the files at the end
target_link_libraries(helloExecutable hello)

When do I need add_library?

Whenever you are including other .h files, and these interface files have implementation files (.cpp format), you need to add those so they are linked properly. For Eigen, add_library is not needed because it doesn’t have .cpp files, only .h files.

it searches for the makefile and executes it. A makefile can look like the following:

default:
	g++ main.cpp -o out

Isn't everything just a library?

im a little confused, isnt everything a library, like it just doesnt have a main?

  • Object file (.o): compiled output of one translation unit. It can contain functions/classes, and it can have undefined references.
  • Library (.a / .so): a bundle of compiled code meant to be reused/linked by other targets.
  • Executable: a linked program that has an entry point (main in C/C++ is your entry point; the real entry is _start provided by CRT/startup code).

Static libraries (.a)

When linking, the linker does NOT copy the whole .a into the executable, but rather extracts & copies only the needed member .os.

Result: Bigger binary, but the executable has no runtime dependency on the .a.

Shared libraries (.so/.dylib/.dll)

The linker does NOT copy the library code into your executable.

Result: smaller binary, but you must have the shared library available at runtime.

When we apt install?

Usually yes: when you apt install a library (the runtime package), you’re typically installing a shared library that lives on disk as versioned .so files.

Executable contains:

  • A dependency list (on Linux/ELF these show up as NEEDED entries): e.g. “needs libfoo.so.1
  • A dynamic symbol table / relocation info: “I need symbol foo_init
  • Call indirections so calls can be patched once the real address is known
    (Linux/ELF: PLT/GOT; Windows: import tables; macOS: dyld stubs)

So the executable contains instructions for how to connect, not the code itself.

How does a program call a shared library??

1) OS loads your executable

Kernel maps your executable into memory and transfers control to the runtime loader (dynamic linker).

  • Linux: ld-linux-x86-64.so.2 (varies by arch)
  • macOS: dyld
  • Windows: loader handles DLLs
2) Loader finds the shared libraries

It reads the executable’s dependency list (“NEEDED: libfoo.so.1”) and searches in paths like:

  • embedded paths in the binary (e.g., rpath/runpath)
  • environment variables (Linux: LD_LIBRARY_PATH)
  • system library dirs (/lib, /usr/lib, etc.)
  • cache (ldconfig on Linux)
3) Loader maps the .so into memory

It memory-maps the library file into the process address space (like loading more code pages). Now the library’s code is in memory alongside your executable’s code.

4) Loader resolves symbols + relocations

It finds where foo_init actually lives inside libfoo.so and patches the call indirections so your program’s call to foo_init() jumps to the correct address.

After this, calling a library function is just a normal CPU jump/call to an address.

What happens when 2 programs use the same library at runtime?

Are there 2 copies of the library, or just 1 copy? It’s just 1 copy.

Then, can’t there be data race? but then can there not be data races?

no, because the library’s writable state (globals/heap, etc.) is per-process, so the two programs aren’t actually reading/writing the same bytes.

The library’s writable global state (its .data/.bss) is generally per-process, not shared across unrelated processes.

For static libs, put objects before libraries:

  • g++ main.o -lfoo
  • g++ -lfoo main.o (can fail: library scanned before symbols are known)