LIEF’s dyld shared cache support allows the inspection and extraction of libraries from Apple dyld shared cache.
import lief
dyld_cache: lief.dsc.DylibSharedCache = lief.dsc.load("macos-15.0.1/")
#include <LIEF/DyldSharedCache.hpp>
std::unique_ptr<LIEF::dsc::DyldSharedCache> dyld_cache = LIEF::dsc::load("macos-15.0.1/")
let dyld_cache = lief::dsc::load_from_path("macos-15.0.1/", "");
Warning
dyld_cache: lief.dsc.DylibSharedCache = ...
for dylib in dyld_cache.libraries:
print("0x{:016x}: {}".format(dylib.address, dylib.path))
std::unique_ptr<LIEF::dsc::DyldSharedCache> dyld_cache;
for (std::unique_ptr<LIEF::dsc::Dylib> dylib : dyld_cache->libraries()) {
std::cout << dylib->address() << ' ' << dylib->path() << '\n';
}
let dyld_cache: lief::dsc::DyldSharedCache;
for dylib in dyld_cache.libraries() {
println!("0x{:016x}: {}", dylib.address(), dylib.path());
}
dyld_cache: lief.dsc.DylibSharedCache = ...
liblockdown = dyld_cache.find_lib_from_name("liblockdown.dylib")
macho: lief.MachO.Binary = liblockdown.get()
for segment in macho.segments:
print(segment.name)
std::unique_ptr<LIEF::dsc::DyldSharedCache> dyld_cache;
std::unique_ptr<Dylib> liblockdown = dyld_cache->find_lib_from_name("liblockdown.dylib");
std::unique_ptr<LIEF::MachO::Binary> macho = liblockdown.get();
for (const LIEF::MachO::SegmentCommand& segment : macho->segments()) {
std::cout << segment.name() << '\n';
}
let dyld_cache: lief::dsc::DyldSharedCache;
let liblockdown = dyld_cache.find_lib_from_name("liblockdown.dylib").unwrap();
let macho = liblockdown.get().unwrap();
for segment in macho.segments() {
println!("{}", segment.name());
}
liblockdown = dyld_cache.find_lib_from_name("liblockdown.dylib")
macho: lief.MachO.Binary = liblockdown.get()
macho.write("on-disk-liblockdown.dylib")
std::unique_ptr<LIEF::dsc::DyldSharedCache> dyld_cache;
std::unique_ptr<Dylib> liblockdown = dyld_cache->find_lib_from_name("liblockdown.dylib");
std::unique_ptr<LIEF::MachO::Binary> macho = liblockdown.get();
macho->write("on-disk-liblockdown.dylib");
let liblockdown = dyld_cache.find_lib_from_name("liblockdown.dylib").unwrap();
let macho = liblockdown.get().unwrap();
macho.write("on-disk-liblockdown.dylib");
Warning
Note
These functions parse all the format structures (with decent performances) because:
Most of the binary’s sizes are less than gigabytes.
A complete representation is required for modifying binaries.
From a technical perspective, LIEF is using a LIEF::FileStream
to access (on-demand) dyld shared cache structures. Thus, the in-memory consumption is limited to the size of the structures being accessed. The drawback of this FileStream
is that since this is a file-based access, it takes more time compared to a LIEF::VectorStream
.
Additionally, LIEF’s dyld shared cache implementation heavily relies on the iterator pattern to follow the principle: don’t pay overhead for what you don’t access.
When it is possible, LIEF implements the trait of a random access iterator [1] so that we can programmatically do:
dyld_cache: lief.dsc.DyldSharedCache = ...
# No cost
libraries = cache.libraries
# O(1) cost
first_lib = libraries[0]
# O(len(libraries)) cost
for lib in libraries:
print(lib.path)
std::unique_ptr<LIEF::dsc::DyldSharedCache> dyld_cache;
// No cost
auto libraries = dyld_cache.libraries();
// O(1) cost
std::unique_ptr<Dylib> first_lib = libraries[0];
// O(libraries.size()) cost
for (const auto& dylib : libraries) {
std::cout << dylib.path() << '\n';
}
When you should turn caching on?
You can skip LIEF’s caching if:
You don’t plan to extract libraries from the shared cache.
You plan to extract only one library from the shared cache and only once
You don’t want to have LIEF cache artifacts on your system.
By default, the cache mechanism is not enabled.
Rust API: lief::dsc