The loader is the OS component that takes a compiled executable from disk, copies it into main memory, and starts it running. It’s the last step of the build-and-run cycle:

source → [compiler] → object files → [linker] → executable → [loader] → running program

The compiler, assembler, and linker all run before the program ever starts. The loader runs at launch time — when the user double-clicks an icon, types a command, or one program tries to start another.

What the loader does

When you launch a program:

  1. The loader reads the executable file’s header, which says how big the program is, where in memory it should be loaded, and where its entry point (the first instruction) is.
  2. The loader allocates memory for the program’s code, data, stack, and heap.
  3. It copies the program’s code and initialized data from disk into the allocated memory.
  4. It sets up the stack for the new program (allocating a stack region, setting the stack pointer).
  5. It sets the PC to the program’s entry point.
  6. Execution begins. The processor starts fetching instructions from the new PC, and the program runs.

While the program is running, the loader is essentially done. When the program exits, the loader is responsible for releasing the allocated memory and notifying whoever launched it.

Static vs. dynamic loading

  • Static loading: The whole executable is loaded into memory at launch time. Simple, predictable.
  • Dynamic loading: Parts of the executable (or shared libraries) are loaded on-demand the first time they’re needed. Faster startup, less memory footprint, more complex.

Modern OSes use a mix. The main executable is loaded statically; shared libraries are loaded dynamically (sometimes lazily) by the dynamic linker, which is invoked by the loader at startup.

Address fixups at load time

If the executable was linked at a fixed base address but the OS chooses a different address (for security via address space layout randomization or because the chosen address is taken), the loader has to relocate the executable — patching every absolute address in the code to point to the new actual location. The executable carries relocation entries (similar to those used at link time) so the loader knows what to fix up.

For position-independent code (PIC), no relocation is needed — the code uses PC-relative addressing throughout, so it works at any base address.

Loading shared libraries

When the executable uses shared libraries (.so on Linux, .dll on Windows), the dynamic linker (loaded by the OS as part of the loader pathway):

  1. Finds each required library on disk.
  2. Loads it into memory (or reuses it if already loaded by another program).
  3. Resolves any pending function references in the executable to point at the library’s actual addresses.
  4. Hands off to the program’s entry point.

This is why a program might fail to launch with a “missing DLL” or “library not found” error — the loader couldn’t resolve a dependency.

On simple systems

On microcontrollers and embedded systems, there often is no loader in the OS sense — the executable is burned into ROM at the program’s intended memory address, and the processor jumps to it on reset. The “load” is done at manufacturing time, not at runtime.

Even on those systems, when something dynamic is needed (loading a new firmware, running user code in RAM), there’s typically a small loader-like routine that copies code from one place to another and jumps to it.