TRT
Sign in
easyFoundationsconcept

How is a running C process's virtual address space laid out across .text, .rodata, .data, .bss, heap, and stack?

Map each C construct (code, string literals, globals, malloc chunks, locals) to its virtual memory segment and hardware page permissions.

TRT practice prompt — not a verified question from a named employer.

The Runtime Theory Team2 min read
Learn it firstVirtual Memory Layout and Pointer AddressingUnderstand the virtual memory segments of a C process, inspect how the MMU translates addresses, and master pointer referencing and dereferencing.

A strong answer

When the operating system loads an ELF executable into a process's 64-bit virtual address space, it organizes memory into distinct segments with hardware-enforced page table permissions (R, W, X):

  • .text (Read + Execute): Contains the compiled machine code instructions of your functions. Marked non-writable so bugs or attackers cannot overwrite instructions.
  • .rodata (Read-Only): Holds string literals ("hello") and const global tables. Attempting to write to a pointer aimed at .rodata (char *s = "hello"; s[0] = 'H';) triggers an immediate MMU write fault (SIGSEGV), whereas char s[] = "hello"; copies those 6 bytes onto the writable stack.
  • .data (Read + Write): Stores global and static variables that have explicit non-zero compile-time initializers (int max_conns = 1024;).
  • .bss (Read + Write, zero-backed): Holds uninitialized or zero-initialized global and static variables (static char buf[10485760];). On disk, .bss stores only a size number in the ELF header rather than 10 MB of zero bytes; at load time, the kernel maps anonymous zero pages on demand.
  • Heap (Read + Write, grows upward): Managed dynamically at runtime by malloc and free via brk/sbrk and mmap.
  • Stack (Read + Write, Non-Executable, grows downward): Holds function call frames, return addresses, and local automatic variables.

What the interviewer is listening for

  • Accurately places functions, string literals, initialized globals, uninitialized globals, heap allocations, and local variables into their exact segments.
  • Explains hardware page permissions (R-X for .text, R-- for .rodata, RW- for .data/.bss/heap/stack) and why .rodata causes string literal mutation to segfault.
  • Explains why .bss takes zero disk space in the binary file.

Common mistakes

  • Thinking char *s = "hello" allocates a writable string on the stack or heap (the pointer s is on the stack, but the 6 bytes of "hello" sit in read-only .rodata).
  • Assuming two processes that print the same virtual address (e.g., 0x401140) are sharing physical RAM without Copy-on-Write or shared mappings—virtual addresses are translated per process by the CPU's MMU and page tables.

This answer walks

Practice follow-ups

  1. 01Why does modifying a string literal like char *s = 'hello'; s[0] = 'H'; crash while char s[] = 'hello'; s[0] = 'H'; works?
  2. 02Why does an uninitialized 10 MB global array not add 10 MB to the executable file on disk?

Not started

Sign in to save your learning progress.

Sign in to save