C++ Program Loading

Understand how C++ programs are loaded and executed by the operating system. Learn ELF format, process creation, memory mapping, and runtime initialization.

5 min|programmingcppruntimelinking
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)

2

Kernel Loads File

OS reads ELF headers and validates

3

Create Process

Allocate process control block (PCB)

4

Map Segments

Map code and data into virtual memory

5

Load Dynamic Linker

Load ld-linux.so for dynamic linking

6

Resolve Libraries

Load and link shared libraries

7

Initialize Runtime

Set up stack, heap, and globals

8

Execute main()

Transfer control to user code

Memory Regions
Stack
Heap
Mmap
BSS
Data
Text
Step 1: Executable File
No memory mapped yet

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()

Dynamic Library Loading

Explore how the dynamic linker loads shared libraries at runtime:

Dynamic Library Loading

Available Shared Libraries

libc.so.6
v2.312.1 MB
printfmallocfree+2 more
libm.so.6
v2.311.5 MB
Depends on: libc.so.6
sincossqrt+2 more
libpthread.so.0
v2.31156 KB
Depends on: libc.so.6
pthread_createpthread_joinpthread_mutex_lock
libstdc++.so.6
v10.21.9 MB
Depends on: libc.so.6, libm.so.6
std::coutstd::vectorstd::string+1 more

Loading Process

Click "Load" to simulate dynamic loading

Dynamic Linker Environment

LD_LIBRARY_PATH=/custom/lib:/usr/local/lib
LD_PRELOAD=./mylib.so
LD_DEBUG=libs,bindings

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