Memory Layout¶
Enumerate mapped regions¶
for region in lief.runtime.memory_layout():
perms = lief.runtime.Memory.perm_str(region.permissions)
print(f"{region.addr:#014x}-{region.end_addr:#014x} {perms} {region.name}")
for (const LIEF::runtime::MemoryLayout::Region& region :
LIEF::runtime::memory_layout())
{
std::cout << region.addr() << '-' << region.end_addr() << ' '
<< LIEF::runtime::Memory::perm_str(region.permissions()) << ' '
<< region.name() << '\n';
}
for region in lief::runtime::memory_layout() {
println!(
"{:#014x}-{:#014x} {:?} {}",
region.addr(),
region.end_addr(),
region.permissions(),
region.name()
);
}
For illustration, a Linux process running /usr/bin/cat could have this layout:
0x56460734e000-0x564607350000 r-- /usr/bin/cat
0x564607350000-0x564607357000 r-x /usr/bin/cat
0x564607357000-0x56460735a000 r-- /usr/bin/cat
0x56460735a000-0x56460735b000 r-- /usr/bin/cat
0x56460735b000-0x56460735c000 rw- /usr/bin/cat
0x5646457bc000-0x5646457dd000 rw- [heap]
0x7fc6fcc00000-0x7fc6fcf96000 r-- /usr/lib/locale/locale-archive
0x7fc6fd000000-0x7fc6fd024000 r-- /usr/lib/libc.so.6
0x7fc6fd024000-0x7fc6fd19f000 r-x /usr/lib/libc.so.6
[...]
0x7fc6fd21b000-0x7fc6fd223000 rw-
[...]
0x7fc6fd30d000-0x7fc6fd30f000 r-x [vdso]
0x7fc6fd30f000-0x7fc6fd310000 r-- /lib64/ld-linux-x86-64.so.2
[...]
0x7fffed226000-0x7fffed247000 rw- [stack]
0xffffffffff600000-0xffffffffff601000 --x [vsyscall]
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.
Names and mappings depend on the operating system and can change as the process allocates memory or loads libraries. A region describes a mapping: it does not own the mapped memory.
Inspecting the layout¶
The following snippet iterates over the memory layout to:
Compute the total mapped size
Group that size by region name
Locate the region containing a given address
These totals measure virtual address space. They are not resident-memory (RSS) measurements, and grouping anonymous regions combines unrelated allocations.
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}")
size_t count = 0;
uint64_t mapped = 0;
std::unordered_map<std::string, uint64_t> footprint;
LIEF::runtime::MemoryLayout::Region enclosing;
for (const LIEF::runtime::MemoryLayout::Region& region :
LIEF::runtime::memory_layout())
{
++count;
mapped += region.size();
std::string name =
region.name().empty() ? "<anonymous>" : std::string(region.name());
footprint[name] += region.size();
if (region.contains(address)) {
enclosing = region;
}
}
std::cout << count << " regions, " << (mapped / 1024) << " KB mapped\n";
for (const auto& [name, size] : footprint) {
std::cout << size << ' ' << name << '\n';
}
if (enclosing.size() > 0) {
std::cout << address << ": " << enclosing.name() << '+'
<< (address - enclosing.addr()) << '\n';
}
let mut count = 0usize;
let mut mapped = 0u64;
let mut footprint: BTreeMap<String, u64> = BTreeMap::new();
let mut enclosing: Option<lief::runtime::Region> = None;
for region in lief::runtime::memory_layout() {
count += 1;
mapped += region.size();
let mut name = region.name();
if name.is_empty() {
name = String::from("<anonymous>");
}
*footprint.entry(name).or_insert(0) += region.size();
if region.contains(address) {
enclosing = Some(region);
}
}
println!("{count} regions, {} KB mapped", mapped / 1024);
for (name, size) in &footprint {
println!("{size:#010x} {name}");
}
if let Some(region) = enclosing {
println!(
"{address:#x}: {}+{:#x}",
region.name(),
address - region.addr()
);
}
Permissions¶
The permissions of the regions can be used to look for memory that is both writable and executable:
wx = lief.runtime.Memory.PERM.WRITE | lief.runtime.Memory.PERM.EXEC
for region in lief.runtime.memory_layout():
if (region.permissions & wx) == wx:
print(f"W+X: {region}")
using LIEF::runtime::Memory;
constexpr uint32_t WX = Memory::P_WRITE | Memory::P_EXEC;
for (const LIEF::runtime::MemoryLayout::Region& region :
LIEF::runtime::memory_layout())
{
if ((region.permissions() & WX) == WX) {
std::cout << "W+X: " << region << '\n';
}
}
use lief::runtime::memory::Perm;
for region in lief::runtime::memory_layout() {
if region.permissions().contains(Perm::WRITE | Perm::EXEC) {
println!("W+X: {region}");
}
}
Linux / Android¶
On Linux and Android, named mappings such as [stack] and [heap] can be located when present. They do not account for every thread stack or allocator-managed allocation:
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}")
for (const LIEF::runtime::MemoryLayout::Region& region :
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() == "[stack]" || region.name() == "[heap]") {
std::cout << region.name() << ": " << region.addr() << '-'
<< region.end_addr() << '\n';
}
}
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.
let name = region.name();
if name == "[stack]" || name == "[heap]" {
println!(
"{name}: {:#014x}-{:#014x}",
region.addr(),
region.end_addr()
);
}
}
[heap]: 0x56367cb52000-0x56367cb73000
[stack]: 0x7ffd162f1000-0x7ffd16312000
macOS¶
On macOS, the layout includes the individual regions within nested memory maps, including the dyld shared cache. LIEF tries to provide meaningful names for anonymous regions associated with the dyld shared cache, the stack, …:
[0000000111e9c000, 0000000111f28000]: /private/tmp/LIEF/main/lief/_lief_extended.so:__DATA
[0000000111f28000, 0000000112020000]: /private/tmp/LIEF/main/lief/_lief_extended.so
[000000014f600000, 000000014f604000]: [heap]
[000000016ed08000, 000000016ed0c000]: [stack]
[000000016ed0c000, 000000016fd0c000]: [stack]
[0000000180000000, 000000018ed58000]: <anonymous>
[000000018ed58000, 000000018ede0000]: [dyld shared cache: __TEXT]
[000000018ede0000, 00000001f4000000]: [dyld shared cache: __TEXT]
[00000001f4000000, 00000001f45d8000]: [dyld shared cache: __TEXT]
[00000001f45d8000, 00000001f45dc000]: [dyld shared cache: __TEXT]
[00000001f45dc000, 00000001f6900000]: [dyld shared cache: __DATA_CONST]
[00000001f6900000, 00000001f8000000]: <anonymous>
[00000001f8000000, 00000001f8900000]: <anonymous>
Windows¶
On Windows, the layout includes committed pages and reserved address ranges, including stack, TEB/PEB regions. The reserved ranges and the guard pages (PAGE_GUARD) have no permission while the copy-on-write pages (PAGE_WRITECOPY) are reported as writable:
[0000000000127000, 0000000000182000]: <anonymous>
[0000000000190000, 0000000000193000]: C:\Windows\System32\l_intl.nls
[00000000001a0000, 00000000001b1000]: C:\Windows\System32\C_1252.NLS
[00000000001c0000, 00000000001d1000]: C:\Windows\System32\C_850.NLS
[00000000001e0000, 00000000001e3000]: [pagefile]
[00000000001f0000, 00000000001f4000]: <anonymous>
[0000000000200000, 000000000037f000]: <anonymous>
[000000000037f000, 0000000000388000]: [peb/teb]
[0000000000388000, 0000000000400000]: <anonymous>
[0000000000400000, 0000000000639000]: [stack]
[000000007ffe0000, 000000007ffe1000]: [shared-user-data]
[00007ff6ae6d0000, 00007ff6ae6d1000]: C:\Python314\python.exe
[00007ff6ae6d1000, 00007ff6ae6d2000]: C:\Python314\python.exe
[00007ff6ae6d2000, 00007ff6ae6d3000]: C:\Python314\python.exe
[00007ff6ae6d3000, 00007ff6ae6d4000]: C:\Python314\python.exe