1

Below picture is quoted from the book Programming from the Ground Up.

enter image description here

  • Why the program memory region is limited between 0xbfffffff and 0x8048000? What's the rationale behind this choice? What's outside this region?

  • What this picture illustrates should be a 32bit program. What's the memory layout for a 64bit program?

  • The picture mentions "at startup", so will the layout change during running?

  • Last but not the least, does the Linux kernel follow this layout, too?

smwikipedia
  • 61,609
  • 92
  • 309
  • 482

1 Answers1

5
  • Linux on 32-bit x86 has traditionally had a 3G/1G user/kernel split.

    • Userspace memory is always mapped in the 0x00000000-0xBFFFFFFF range (that is, the lower 3GB, out of of the 4GB that can be directly addressed in 32-bit mode). This entire space is remapped when switching between processes.

    • Kernel memory is always mapped in the 0xC0000000-0xFFFFFFFF range (the upper 1GB). It is inaccessible at userspace's privilege level, but when the context switch to the kernel happens, it can access all its memory without re-mapping anything.

    I have an older answer that goes into this in some more detail.

  • On x86 the stack grows downward (starting high and moving toward lower addresses). This is pretty much an arbitrary decision in the CPU design (that's what the push/pop/call/ret instructions do to %esp). Linux starts it up at the top of the range.

    In contrast, the program data is mapped into the low end by default. Historically, the kernel was mapped below 0x08000000, so the lowest address available for userspace to use was above that. This is no longer true, but it explains the original 0x08040000 load address.

  • The space in between is used for the heap. The "Break" marker gets moved up/down by calls to brk() or sbrk(); memory below it is accessible for program use. Historically, the C runtime moves the program break up to satisfy the space requirements of malloc.

  • On current 64-bit x86 CPUs, only addresses 0x0000000000000000-0x00007FFFFFFFFFFF and 0xFFFF800000000000-0xFFFFFFFFFFFFFFFF are canonical. 64-bit pointers, but "only" 48-bits worth of usable address space. (Which is 256TB, so it'll be a while before we reach that limit.) The hardware will trigger a fault if you try to access any non-canonical address (0x0000800000000000-0xFFFF7FFFFFFFFFFF). Linux maps userspace in the lower half and kernel in the higher half.

    The stack still grows down, so Linux still starts it toward the top of the range, and the fixed mappings start toward the bottom, with the heap growing up above them.

  • Yes. In addition to the program break moving up and down as the heap grows and shrinks, the program can also use mmap/munmap to map/unmap various things into its address space, such as files, shared memory, anonymous memory, etc. In fact, C runtimes these days map anonymous memory in chunks in addition to or instead of manipulating the program break.

  • The kernel itself lives in the upper 1G on 32-bit x86 and the upper 128TB on 64-bit x86. Its layout is mostly unimportant (and invisible) to userspace, but includes things such as stacks for every kernel thread and the kernel side of every user thread, page tables, caches, DMA buffers, etc.

Other notes:

All of this is about virtual addresses, not physical addresses.

The lowest address accessible to userspace is not necessarily 0. C programs expect NULL to be a bad address (which can lead to exploits if it is actually valid), and the kernel enforces /proc/sys/vm/mmap_min_addr which defaults to 64k now.

Linux these days (well over a decade now) maps the VDSO at an extreme high address, so the stack starts below that. Once upon a time the vsyscall page was also up there, but now it lives in a page of kernel space.

All the addresses may be shuffled around due to ASLR. This isn't very effective on a 32-bit address space (you can't randomize the lower 12 bits because of page alignment, possibly more due to other constraints), but there's plenty of bits in 64-bit mode.

Community
  • 1
  • 1
ephemient
  • 198,619
  • 38
  • 280
  • 391
  • 1
    I have another [old answer](http://stackoverflow.com/a/12016560/20713) that touches on the initial stack layout. Linux starts all programs with the environment variables and command-line arguments on the stack, which the C runtime arranges before `main()`. – ephemient Feb 03 '17 at 05:18