#include <stdlib.h>
#include <stdio.h>
#include <Availability.h>
#include "dsc_iterator.h"
#include "dyld_cache_format.h"
#define NO_ULEB
#include "Architectures.hpp"
#include "MachOFileAbstraction.hpp"
#include "CacheFileAbstraction.hpp"
namespace dyld {
template <typename E>
const uint8_t* mappedAddress(const uint8_t* cache, const uint8_t* cacheEnd, uint64_t addr)
{
const dyldCacheHeader<E>* header = (dyldCacheHeader<E>*)cache;
const dyldCacheFileMapping<E>* mappings = (dyldCacheFileMapping<E>*)&cache[header->mappingOffset()];
for (uint32_t i=0; i < header->mappingCount(); ++i) {
if ( (mappings[i].address() <= addr) && (addr < (mappings[i].address() + mappings[i].size())) ) {
uint64_t cacheOffset = mappings[i].file_offset() + addr - mappings[i].address();
const uint8_t* result = &cache[cacheOffset];
if ( result < cacheEnd )
return result;
else
return NULL;
}
}
return NULL;
}
template <typename A>
int walkSegments(const uint8_t* cache, const uint8_t* cacheEnd, const uint8_t* firstSeg, const char* dylibPath, uint64_t inode,uint64_t modTime, const uint8_t* machHeader,
void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo))
{
typedef typename A::P P;
typedef typename A::P::E E;
dyld_shared_cache_dylib_info dylibInfo;
dyld_shared_cache_segment_info segInfo;
dylibInfo.version = 2;
dylibInfo.isAlias = (dylibPath < (char*)firstSeg); dylibInfo.machHeader = machHeader;
dylibInfo.path = dylibPath;
dylibInfo.inode = inode;
dylibInfo.modTime = modTime;
const macho_header<P>* mh = (const macho_header<P>*)machHeader;
const macho_load_command<P>* const cmds = (macho_load_command<P>*)(machHeader + sizeof(macho_header<P>));
if ( (machHeader+ mh->sizeofcmds()) > cacheEnd )
return -1;
const uint32_t cmd_count = mh->ncmds();
const macho_load_command<P>* cmd = cmds;
dylibInfo.uuid = NULL;
for (uint32_t i = 0; i < cmd_count; ++i) {
if ( cmd->cmd() == LC_UUID ) {
const uuid_command* uc = (const uuid_command*)cmd;
dylibInfo.uuid = &uc->uuid;
break;
}
cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
}
cmd = cmds;
for (uint32_t i = 0; i < cmd_count; ++i) {
if ( cmd->cmd() == macho_segment_command<P>::CMD ) {
macho_segment_command<P>* segCmd = (macho_segment_command<P>*)cmd;
uint64_t fileOffset = segCmd->fileoff();
if ( fileOffset == 0 ) {
fileOffset = (machHeader - cache);
}
uint64_t sizem = segCmd->vmsize();
if ( strcmp(segCmd->segname(), "__LINKEDIT") == 0 ) {
if ( (fileOffset+sizem) > (uint64_t)(cacheEnd-cache) )
sizem = (cacheEnd-cache)-fileOffset;
}
segInfo.version = 1;
segInfo.name = segCmd->segname();
segInfo.fileOffset = fileOffset;
segInfo.fileSize = sizem;
if ( segCmd->filesize() > segCmd->vmsize() )
return -1;
segInfo.address = segCmd->vmaddr();
callback(&dylibInfo, &segInfo);
}
cmd = (const macho_load_command<P>*)(((uint8_t*)cmd)+cmd->cmdsize());
}
return 0;
}
template <typename A>
int walkImages(const uint8_t* cache, uint32_t size, void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo))
{
if ( (size > 0) && (size < 0x7000) )
return -1;
typedef typename A::P::E E;
typedef typename A::P P;
const dyldCacheHeader<E>* header = (dyldCacheHeader<E>*)cache;
const dyldCacheImageInfo<E>* dylibs = (dyldCacheImageInfo<E>*)&cache[header->imagesOffset()];
const dyldCacheFileMapping<E>* mappings = (dyldCacheFileMapping<E>*)&cache[header->mappingOffset()];
uint64_t greatestMappingOffset = 0;
for (uint32_t i=0; i < header->mappingCount(); ++i) {
if ( (size != 0) && (mappings[i].file_offset() > size) )
return -1;
uint64_t endOffset = mappings[i].file_offset()+mappings[i].size();
if ( (size != 0) && (endOffset > size) )
return -1;
if ( endOffset > greatestMappingOffset )
greatestMappingOffset = endOffset;
}
const uint8_t* cacheEnd = &cache[size];
if ( size == 0 ) {
cacheEnd = &cache[greatestMappingOffset];
}
else {
if ( size < greatestMappingOffset )
return -1;
}
if ( (const uint8_t*)&dylibs[header->imagesCount()] > cacheEnd )
return -1;
const uint8_t* firstSeg = NULL;
for (uint32_t i=0; i < header->imagesCount(); ++i) {
const char* dylibPath = (char*)cache + dylibs[i].pathFileOffset();
uint64_t inode = dylibs[i].inode();
uint64_t modTime = dylibs[i].modTime();
if ( (const uint8_t*)dylibPath > cacheEnd )
return -1;
const uint8_t* machHeader = mappedAddress<E>(cache, cacheEnd, dylibs[i].address());
if ( machHeader == NULL )
return -1;
if ( machHeader > cacheEnd )
return -1;
if ( firstSeg == NULL )
firstSeg = machHeader;
int result = walkSegments<A>(cache, cacheEnd, firstSeg, dylibPath, inode, modTime, machHeader, callback);
if ( result != 0 )
return result;
}
return 0;
}
}
extern int dyld_shared_cache_iterate(const void* shared_cache_file, uint32_t shared_cache_size,
void (^callback)(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo)) {
const uint8_t* cache = (uint8_t*)shared_cache_file;
if ( strcmp((char*)cache, "dyld_v1 i386") == 0 )
return dyld::walkImages<x86>(cache, shared_cache_size, callback);
else if ( strcmp((char*)cache, "dyld_v1 x86_64") == 0 )
return dyld::walkImages<x86_64>(cache, shared_cache_size, callback);
else if ( strcmp((char*)cache, "dyld_v1 x86_64h") == 0 )
return dyld::walkImages<x86_64>(cache, shared_cache_size, callback);
else if ( strcmp((char*)cache, "dyld_v1 armv5") == 0 )
return dyld::walkImages<arm>(cache, shared_cache_size, callback);
else if ( strcmp((char*)cache, "dyld_v1 armv6") == 0 )
return dyld::walkImages<arm>(cache, shared_cache_size, callback);
else if ( strcmp((char*)cache, "dyld_v1 armv7") == 0 )
return dyld::walkImages<arm>(cache, shared_cache_size, callback);
else if ( strncmp((char*)cache, "dyld_v1 armv7", 14) == 0 )
return dyld::walkImages<arm>(cache, shared_cache_size, callback);
else if ( strcmp((char*)cache, "dyld_v1 arm64") == 0 )
return dyld::walkImages<arm64>(cache, shared_cache_size, callback);
else
return -1;
}
int dyld_shared_cache_iterate_segments_with_slide(const void* shared_cache_file, dyld_shared_cache_iterator_slide_t callback)
{
return dyld_shared_cache_iterate(shared_cache_file, 0, ^(const dyld_shared_cache_dylib_info* dylibInfo, const dyld_shared_cache_segment_info* segInfo) {
callback(dylibInfo->path, segInfo->name, segInfo->fileOffset, segInfo->fileSize, segInfo->address, 0);
});
}
int dyld_shared_cache_iterate_segments_with_slide_nb(const void* shared_cache_file, dyld_shared_cache_iterator_slide_nb_t func, void* userData)
{
return dyld_shared_cache_iterate_segments_with_slide(shared_cache_file, ^(const char* dylibName, const char* segName,
uint64_t offset, uint64_t size, uint64_t mappedddress, uint64_t slide) {
(*func)(dylibName, segName, offset, size, mappedddress, slide, userData);
});
}
int dyld_shared_cache_iterate_segments(const void* shared_cache_file, dyld_shared_cache_iterator_t callback)
{
dyld_shared_cache_iterator_slide_t wrapper_cb = ^(const char* dylibName, const char* segName, uint64_t offset,
uint64_t size, uint64_t mappedddress, uint64_t slide) {
callback(dylibName, segName, offset, size, mappedddress);
};
return dyld_shared_cache_iterate_segments_with_slide(shared_cache_file, wrapper_cb);
}
int dyld_shared_cache_iterate_segments_nb(const void* shared_cache_file, dyld_shared_cache_iterator_nb_t func, void* userData)
{
return dyld_shared_cache_iterate_segments_with_slide(shared_cache_file, ^(const char* dylibName, const char* segName,
uint64_t offset, uint64_t size, uint64_t mappedddress, uint64_t slide) {
(*func)(dylibName, segName, offset, size, mappedddress, userData);
});
}