A library file is a bundled collection of object files, packaged with an index for fast symbol lookup. Libraries hold reusable subroutines that many programs need: math functions, I/O routines, string manipulation, networking, GUI primitives. Used during linking to incorporate those subroutines into a program without recompiling them every time.

The canonical example: the C standard library (libc), which provides printf, malloc, strlen, fopen, and the rest of the standard C functions every C program uses.

Two main flavors

Static libraries

Suffix: .a (Unix), .lib (Windows).

The Linker copies the needed members into the final executable. Once linked, the library code becomes part of the executable. The library file isn’t needed to run the program.

Pros:

  • Self-contained executable — no runtime dependencies.
  • No risk of “missing library” errors at run time.
  • Function calls are slightly faster (no indirection through dynamic resolution).

Cons:

  • Larger executables (each program includes its own copy of common library code).
  • Library updates require relinking every program that uses them.
  • Wastes memory when many programs run simultaneously and each holds its own copy.

Shared / dynamic libraries

Suffix: .so (Linux), .dll (Windows), .dylib (macOS).

The linker only records that the library is required. The actual library code is loaded at runtime by the Loader (or the dynamic linker that the loader invokes).

Pros:

  • Smaller executables.
  • Multiple programs can share one in-memory copy of the library.
  • Library updates take effect for all programs without recompilation (good for security patches).

Cons:

  • “Missing library” errors at run time if the library isn’t installed correctly.
  • Slight indirection overhead per call.
  • “DLL hell” — version conflicts when programs need different versions of the same library.

How linking with libraries works

When you compile a program that uses printf:

  1. The compiler emits a call to printf in the object file, marked as an external reference.
  2. The linker is told to use libc (-lc on Unix command lines).
  3. The linker searches libc for printf, finds it, and either:
    • Static: copies printf’s machine code into the executable.
    • Dynamic: writes a stub that defers to the dynamic linker at runtime.

For dynamic linking, the executable contains a list of required libraries. At launch, the Loader reads this list, locates each library on disk (using the system’s library search path), maps it into the program’s address space, and resolves the stubs.

Library structure

A .a file (static library on Unix) is essentially an archive (think .zip) of object files, with a global symbol index for fast lookup. You can extract individual .o files from it:

ar -t libfoo.a            # list members
ar -x libfoo.a member.o   # extract

A .so file (shared library) is a single linkable object with extra metadata about its exported symbols. Tools like nm and objdump work on .so files just like on .o files.

Examples

Common Linux libraries:

  • libc.so.6 — C standard library (printf, malloc, file I/O).
  • libm.so.6 — math library (sin, cos, sqrt).
  • libpthread.so.0 — POSIX threads.
  • libstdc++.so.6 — C++ standard library.
  • libssl.so — SSL/TLS encryption.

A typical small Linux executable depends on around 5 shared libraries (libc, libm, libpthread, the dynamic-linker stub, and one or two more); large desktop applications can pull in 50+. Run ldd <executable> to see which.

Why libraries matter

Without libraries, every program would need its own copy of every utility function. The original Unix philosophy was small composable programs sharing libraries — and modern systems still rely heavily on this for system stability and disk/memory efficiency.

For embedded systems with no dynamic loader, static libraries are the only option — everything is linked into a single firmware image.

For the format the library bundles, see Object file. For the link step that uses libraries, see Linker. For runtime library loading, see Loader.