Home / Alt manpages / proc_pid_maps(5)

  • proc_pid_maps(5)
  • File format
  • linux

Read a Linux Process's Memory Map Without Guesswork

You will finish with a repeatable way to inspect a running process's mapped memory, read each line's permissions, and tell file-backed mappings from the heap, stack and other special regions. The commands below are read-only. They do not attach a debugger, pause a process or change its memory. This guide follows the installed proc_pid_maps(5) manpage from Linux man-pages 6.7, packaged here as manpages 6.7-2.

Allow about ten minutes. You need a shell and a running Linux process. Root is not a normal prerequisite, although the kernel's ptrace access check can deny access to another user's process. Start with your own shell so the first result is predictable.

1. Read the map for your current shell

Save the shell's process ID, then print its map:

pid=$$
printf 'Inspecting PID %s\n' "$pid"
sed -n '1,12p' "/proc/$pid/maps"

Expect several lines resembling this, although addresses, libraries and the number of regions will vary:

Inspecting PID 18420
55c8c4b1c000-55c8c4b1d000 r--p 00000000 08:02 131234 /usr/bin/bash
55c8c4b1d000-55c8c4c04000 r-xp 00001000 08:02 131234 /usr/bin/bash
55c8c4c04000-55c8c4c3c000 r--p 000e8000 08:02 131234 /usr/bin/bash
55c8c4c3c000-55c8c4c40000 r--p 0011f000 08:02 131234 /usr/bin/bash
55c8c4c40000-55c8c4c49000 rw-p 00123000 08:02 131234 /usr/bin/bash
55c8e2d9d000-55c8e2dbe000 rw-p 00000000 00:00 0 [heap]

Checkpoint: the file should open without sudo for your own shell. The final field is optional, so do not split a line on whitespace and assume it always has six fields. A blank pathname is a real result for an anonymous mapping.

2. Decode one mapping line

Each record has an address range, four permission characters, a file offset, device number, inode and an optional pathname:

address range             perms offset   dev    inode  pathname
55c8c4c40000-55c8c4c49000 rw-p        00123000 08:02  131234 /usr/bin/bash

The address range is virtual address space, not a physical RAM location. The four permission characters mean read, write, execute, and either shared (s) or private copy-on-write (p). Thus rw-p is writable private memory, while r-xp is executable private memory. The offset is where the mapping begins in its backing file or object.

The device and inode identify a file-backed object. An inode of 0 means there is no associated inode, which is common for the heap and other anonymous regions. The pathname usually names the backing file, but it is not a promise that every byte currently comes from that file. A private mapping can acquire anonymous copy-on-write pages after a write.

Do not treat r-xp as proof that code is currently executing, or rw-p as proof that a region is safe to inspect as text. These are virtual memory permissions. Reading a live process repeatedly can also produce different answers as mappings are created and removed.

3. Find the process regions people usually mean

Filter the complete map for the named pseudo-paths and file names:

pid=$$
grep -E '\[(heap|stack|vdso)\]|/usr/bin/bash' "/proc/$pid/maps"

Typical special entries include [heap], the main process's heap, [stack], the initial thread's stack, and [vdso], the virtual dynamically linked shared object supplied by the kernel. A blank pathname is also normal for anonymous mappings, so a filter that only prints lines ending in a file name is incomplete.

Modern kernels can show named anonymous mappings as [anon: name] from Linux 5.17, and named shared anonymous mappings as [anon_shmem: name] from Linux 6.2. The older [stack:tid] labels were removed in Linux 4.5. If you are writing tooling, accept these version-dependent labels rather than assuming that every bracketed name is one of the three familiar regions.

4. Inspect a different PID carefully

Use an explicit numeric PID and quote the path:

target_pid=12345
if test -r "/proc/$target_pid/maps"; then
    sed -n '1,20p' "/proc/$target_pid/maps"
else
    status=$?
    printf 'Cannot read /proc/%s/maps; check that the process exists and that access is permitted.\n' "$target_pid" >&2
    exit "$status"
fi

Replace 12345 with a real process ID. The read permission test is only a first check: the process may exit between the test and the read, and the kernel applies the PTRACE_MODE_READ_FSCREDS access check described by the manpage. You may need elevated privilege for a process owned by another user:

$ sudo sed -n '1,20p' /proc/12345/maps

Use sudo only when the access policy requires it. It does not make a vanished PID reappear, and it does not freeze the target while you inspect it. For repeatable diagnostics, record the PID, command name and time of collection alongside the map.

5. Relate a file mapping to ELF program headers

For an executable or shared library named in the pathname column, compare the map's offset with the file's loadable segments:

$ readelf -l /usr/bin/bash | sed -n '/Program Headers:/,/Section to Segment mapping:/p'

Look for LOAD entries and their Offset values. A mapping whose offset starts at 00000000 commonly corresponds to the first loadable portion of an ELF file, while later read-only, executable or writable mappings begin at other file offsets. The addresses are relocated at run time, so compare the offset and permissions, not a hard-coded virtual address from another machine.

This is a correlation aid, not a complete memory dump. A pathname ending in (deleted) means the file was unlinked while still mapped. The suffix is useful evidence, but the manpage warns that pathname text can be ambiguous: newlines are escaped as an octal sequence, and a literal backslash sequence cannot always be distinguished from the original pathname.

6. Handle common investigation traps

  • Do not parse the pathname by assuming it has no spaces or that it is always present. Read the first five whitespace-separated fields, then treat the remainder as pathname text.
  • Do not confuse maps with memory usage. It describes virtual regions and access permissions. Use /proc/$pid/smaps when you need per-region accounting such as resident and proportional set size.
  • Do not infer a thread's stack from a [stack:tid] entry on a current kernel. Inspect /proc/$pid/task/$tid/ when thread-specific information is needed.
  • Do not build security decisions from a single pathname or permission character. Mappings can change while a process runs, and the map does not tell you whether data is sensitive or code is trustworthy.

If a process exits during collection, rerun the command with a fresh PID. If access is denied even with the expected privilege, check the process ownership, the system's ptrace restrictions and whether the target is inside another container or PID namespace.

Done means

  • You can read your shell's /proc/$pid/maps without changing system state.
  • You can explain the address, permissions, offset, device, inode and optional pathname fields.
  • You recognise [heap], [stack], [vdso], anonymous mappings and version-dependent names.
  • Your script handles a missing or inaccessible PID instead of silently producing an empty report.
  • You use ELF offsets for correlation and do not mistake the map for a stable memory dump.