Memory Layout¶
for region in lief.runtime.memory_layout():
print(f"{region.addr:#014x}-{region.end_addr:#014x} {region.name}")
for (const LIEF::runtime::MemoryLayout::Region& region :
LIEF::runtime::memory_layout())
{
std::cout << region.addr() << '-' << region.end_addr() << ' ' << region.name()
<< '\n';
}
for region in lief::runtime::memory_layout() {
println!(
"{:#014x}-{:#014x} {}",
region.addr(),
region.end_addr(),
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]
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. 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 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: Inspecting the layout¶
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()
);
}
Linux / Android¶
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