C++ Program Loading

β€’5 min

Understand how C++ programs are loaded and executed by the operating system.

Best viewed on desktop for optimal interactive experience

Program Loading & Execution

When you run a C++ program, the OS performs complex steps to transform the executable file into a running process.

Program Loading Process

1

Executable File

Processing

Binary file on disk (ELF format)

On Disk
0 KB
2

Kernel Loads File

OS reads ELF headers and validates

Kernel Space
4 KB
3

Create Process

Allocate process control block (PCB)

Initializing
8 KB
4

Map Segments

Map code and data into virtual memory

Memory Mapped
64 KB
5

Load Dynamic Linker

Load ld-linux.so for dynamic linking

Linking
128 KB
6

Resolve Libraries

Load and link shared libraries

Libraries Loaded
2048 KB
7

Initialize Runtime

Set up stack, heap, and globals

Runtime Ready
4096 KB
8

Execute main()

Transfer control to user code

Running
4096+ KB

Memory Usage Progression

1
2
3
4
5
6
7
8

Memory Layout

Once loaded, your program has a well-defined memory structure:

Process Memory Layout

Virtual Address Space (x86-64)

Heap grows up
Stack grows down

Stack

Return addresses, parameters, local vars

Common Contents:
int local_var = 42;
char buffer[256];
function parameters
return addresses
Start Address:0x7FFFFFFFFFFF
Size:8MB (typical)
Memory Permissions
Read
Write
Execute

Useful Commands

cat /proc/self/mapsView memory map
pmap <pid>Process memory
size ./programSegment sizes
ulimit -sStack limit

Loading Steps

  1. Read ELF headers

    • Validate executable format
    • Check architecture compatibility
    • Find entry point
  2. Create process

    • Allocate Process Control Block (PCB)
    • Assign Process ID (PID)
    • Create virtual address space
  3. Map segments

    • Map code (.text) as read-execute
    • Map data (.data, .bss) as read-write
    • Set up memory protections
  4. Load dynamic linker

    • If dynamically linked, load ld.so
    • Resolve library dependencies
    • Perform relocations
  5. Initialize runtime

    • Set up stack and heap
    • Run global constructors
    • Initialize Thread Local Storage
  6. Transfer control

    • Jump to _start (not main!)
    • C runtime setup
    • Finally call main()

Memory Regions

Stack & Heap

  • Stack: Local variables, function calls
  • Heap: Dynamic allocations

Code & Data

  • .text: Executable instructions
  • .data: Initialized globals
  • .bss: Uninitialized globals
  • .rodata: Constants

Memory Mapping

  • Shared libraries
  • Memory-mapped files
  • Thread stacks

Before main()

Your code doesn't run first:

// Global constructors run before main class Global { Global() { std::cout << "Before main!\n"; } } g; int main() { std::cout << "In main\n"; return 0; } // Output: // Before main! // In main

Runtime Environment

Process Information

# View process memory cat /proc/self/maps # Check memory usage pmap <pid> # Monitor runtime strace ./program

Environment Variables

# Dynamic linker paths LD_LIBRARY_PATH=/custom/lib # Preload libraries LD_PRELOAD=./hook.so # Debug loading LD_DEBUG=all ./program

Loading Topics

Explore specific aspects:

The complete journey:

  1. Compilation - Source to object files
  2. Linking - Object files to executable
  3. Loading - Executable to process (you are here)

Advanced Topics

If you found this explanation helpful, consider sharing it with others.

Mastodon