This is the result of looking at virtual memory of a process in gdb; I have some questions regarding this:
-
Why are some parts of the virtual memory are repeated? For example, our program (stack6) and libc library is repeated 4 times; if they have partitioned them into different parts, then why? Why not just put them all together?
-
Is the top path (/opt/pro…) the instruction section (text section) of our virtual memory and only contains the instructions?
-
Why are the sizes of the 4 libc's different? What's the deal with the offset, if we already have the size and starting addr, then what is offset for?
-
Where are the data, bss, kernel and heap sections and why do some parts of the above picture have no info about them? Is there any better option in gdb that actually shows all the parts?
-
Is there any better program than gdb that shows the virtual memory part of our process much better? I just want to have a good visual of an actual virtual memory, which debugging program provides the best result.
The sections that I mentioned :
Best Answer
There’s one important piece of information missing from
gdb
’s output: the pages’ permissions. (They’re shown on Solaris and FreeBSD, but not on Linux.) You can see those by looking at/proc/<pid>/maps
; the maps for your Protostar example show(The Protostar example runs in a VM which is easy to hack, presumably to make the exercises tractable: there’s no NX protection, and no ASLR.)
You’ll see above that what appears to be repeated mappings in
gdb
actually corresponds to different mappings with different permissions. The text segment is mapped read-only and executable; the data segment is mapped read-only; BSS and the heap are mapped read-write. Ideally, the data segment, BSS and heap are not executable, but this example lacks NX support so they are executable. Each shared library gets its own mapping for its text segment, data segment and BSS. The fourth mapping is a non-readable, non-writable, non-executable segment typically used to guard against buffer overflows (although given the age of the kernel and C library used here this might be something different).The offset, when given, indicates the offset of the data within the file, which doesn’t necessarily have much to do with its position in the address space. When loaded, this is subject to alignment constraints; for example,
libc-2.11.2.so
’s program headers specify two “LOAD” headers:(Use
readelf -l
to see this.)These can result in multiple mappings at the same offset, with different virtual addresses, if the sections mapped to the segments have different protection flags. In
stack6
’s case:(This also explains the small size shown by
proc info mappings
forstack6
: each header requests less than 4KiB, with a 4KiB alignment, so it gets two 4KiB mappings with the same offset at different addresses.)Blank mappings correspond to anonymous mappings; see
man 5 proc
for details. You’d need to break onmmap
ingdb
to determine what they correspond to.You can’t see the kernel mappings (apart from the legacy
vsyscall
on some architectures) because they don’t matter from the process’s perspective (they’re inaccessible).I don’t know of a better
gdb
option, I always use/proc/$$/maps
.See How programs get run: ELF binaries for details of the ELF format as read by the kernel, and how it maps to memory allocations; it has pointers to lots more reference material.