LIEF’s Dyld shared cache support enables the inspection and extraction of libraries from the 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 format structures (with decent performance) because:
Most binary sizes are less than one gigabyte.
A complete representation is required for modifying binaries.
From a technical perspective, LIEF uses a LIEF::FileStream to access Dyld shared cache structures on demand. Thus, in-memory consumption is limited to the size of the structures being accessed. The drawback of using FileStream is that because it uses 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.
Where possible, LIEF implements the random access iterator trait [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 should you 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