Memory Layout

The interface exposes the memory layout of the current process: the regions that are mapped in its address space.
for region in lief.runtime.memory_layout():
    print(f"{region.addr:#014x}-{region.end_addr:#014x} {region.name}")

For a process running /usr/bin/cat, this prints:

0x563668f9d000-0x563668f9f000 /usr/bin/cat
0x563668f9f000-0x563668fa6000 /usr/bin/cat
0x563668fa6000-0x563668fa9000 /usr/bin/cat
0x563668fa9000-0x563668faa000 /usr/bin/cat
0x563668faa000-0x563668fab000 /usr/bin/cat
0x56367cb52000-0x56367cb73000 [heap]
0x7f2d28e00000-0x7f2d29196000 /usr/lib/locale/locale-archive
0x7f2d291be000-0x7f2d29200000
0x7f2d29200000-0x7f2d29224000 /usr/lib/libc.so.6
[...]
0x7f2d29478000-0x7f2d2947a000 [vdso]
0x7f2d2947a000-0x7f2d2947b000 /lib64/ld-linux-x86-64.so.2
[...]
0x7ffd162f1000-0x7ffd16312000 [stack]
0xffffffffff600000-0xffffffffff601000 [vsyscall]
A is a contiguous range of memory described by its address range and the name can be either:
  • the name or the path of the module mapped at this address (e.g. /usr/lib/libc.so.6);

  • the identifier of a region that is not backed by a file (e.g. [stack], [heap], [vdso]);

  • empty, for anonymous regions.

As shown in the output above, a module is not mapped as a single region: it usually gets one region per set of permissions.

Inspecting the layout

The following snippet iterates over the memory layout to

  • compute how much memory is mapped

  • the footprint of each module

  • the region that backs a given address

address: int

count = 0
mapped = 0
footprint: defaultdict[str, int] = defaultdict(int)
enclosing: lief.runtime.MemoryLayout.Region | None = None

for region in lief.runtime.memory_layout():
    count += 1
    mapped += region.size

    name = region.name if region.name else "<anonymous>"
    footprint[name] += region.size

    if region.contains(address):
        enclosing = region

print(f"{count} regions, {mapped // 1024} KB mapped")

for name, size in footprint.items():
    print(f"{size:#010x} {name}")

if enclosing is not None:
    print(f"{address:#x}: {enclosing.name}+{address - enclosing.addr:#x}")

Linux / Android

On Linux and Android, the kernel names the regions that back the stack and the heap of the process, so both can be located by name:

for region in lief.runtime.memory_layout():
    # On Linux and Android, the kernel names the regions that back the
    # stack and the heap of the process.
    if region.name in ("[stack]", "[heap]"):
        print(f"{region.name}: {region.addr:#014x}-{region.end_addr:#014x}")
[heap]: 0x56367cb52000-0x56367cb73000
[stack]: 0x7ffd162f1000-0x7ffd16312000