Process memory layout for 32-bit Linux programs
There are actually two memory layouts for 32-bit x86 Linux programs; the old and the new one. First, the simple version of the common bits:
- first off, the 4Gb address space is split into the lower 3G, for the process, and the upper 1G, for the kernel.
- the program's code, data, and bss start at 128 Mb (0x08048000) and go upwards.
- the stack grows down from just below the 3 Gb boundary, ending at 0xbfffffff or 0xc0000000.
On modern Linuxes the top of the stack is randomized a bit, so you won't see it ending exactly at the 3 Gb boundary.
The difference between the old and the new memory layouts is the default
mmap()'d objects, which includes the dynamic linker and
shared libraries. In the old memory layout,
mmap() starts at 1 Gb
(0x40000000), aka TASK_UNMAPPED_BASE, and grows upwards. In the new
mmap() starts below the 'bottom' of the stack and grows
downwards (although, like the stack, it is generally randomized a bit).
(More technically, the kernel picks a 'top of
mmap() area' address
that is at least 128 Mb below the top of the process's stack, but may be
more if the process has a larger soft stack size limit.)
The old memory layout had a number of limitations and unfortunate
consequences; for example, a dynamically linked process could not
sbrk() very much before it ran into the dynamic linker at 1Gb.
(In fact, on at least some kernel versions you simply couldn't run a dynamically linked program that had more than 896 Mb of code, data, and bss, because it would collide explosively with the dynamic linker. People periodically ran into this limitation.)
If you look at
/proc/<pid>/maps you will also see a 'vdso'
object at the top of memory, inside the kernel's upper
1Gb. This is a virtual shared library that the kernel maps into
people's address space for them to use; you can see it in
output as 'linux-gate.so.1'. (A description of it is here.)