#include <stdint.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <mach/mach_time.h> // mach_absolute_time()
#include <pthread/pthread.h>
#include <libkern/OSAtomic.h>
#include <vector>
#include <algorithm>
#include "AllImages.h"
#include "MachOParser.h"
#include "libdyldEntryVector.h"
#include "Logging.h"
#include "Loading.h"
#include "Tracing.h"
#include "LaunchCache.h"
#include "DyldSharedCache.h"
#include "PathOverrides.h"
#include "DyldCacheParser.h"
extern const char** appleParams;
struct __cxa_range_t {
const void* addr;
size_t length;
};
extern "C" void __cxa_finalize_ranges(const __cxa_range_t ranges[], unsigned int count);
VIS_HIDDEN bool gUseDyld3 = false;
namespace dyld3 {
class VIS_HIDDEN LoadedImage {
public:
enum class State { uninited=3, beingInited=2, inited=0 };
typedef launch_cache::binary_format::Image BinaryImage;
LoadedImage(const mach_header* mh, const BinaryImage* bi);
bool operator==(const LoadedImage& rhs) const;
void init(const mach_header* mh, const BinaryImage* bi);
const mach_header* loadedAddress() const { return (mach_header*)((uintptr_t)_loadAddress & ~0x7ULL); }
State state() const { return (State)((uintptr_t)_loadAddress & 0x3ULL); }
const BinaryImage* image() const { return _image; }
bool neverUnload() const { return ((uintptr_t)_loadAddress & 0x4ULL); }
void setState(State s) { _loadAddress = (mach_header*)((((uintptr_t)_loadAddress) & ~0x3ULL) | (uintptr_t)s); }
void setNeverUnload() { _loadAddress = (mach_header*)(((uintptr_t)_loadAddress) | 0x4ULL); }
private:
const mach_header* _loadAddress; const BinaryImage* _image;
};
bool LoadedImage::operator==(const LoadedImage& rhs) const
{
return (_image == rhs._image) && (loadedAddress() == rhs.loadedAddress());
}
struct VIS_HIDDEN DlopenCount {
bool operator==(const DlopenCount& rhs) const;
const mach_header* loadAddress;
uintptr_t refCount;
};
bool DlopenCount::operator==(const DlopenCount& rhs) const
{
return (loadAddress == rhs.loadAddress) && (refCount == rhs.refCount);
}
LoadedImage::LoadedImage(const mach_header* mh, const BinaryImage* bi)
: _loadAddress(mh), _image(bi)
{
assert(loadedAddress() == mh);
setState(State::uninited);
}
void LoadedImage::init(const mach_header* mh, const BinaryImage* bi)
{
_loadAddress = mh;
_image = bi;
assert(loadedAddress() == mh);
setState(State::uninited);
}
template <typename T, int C> class ReaderWriterChunkedVector;
template <typename T, int C>
class VIS_HIDDEN ChunkedVector {
public:
static ChunkedVector<T,C>* make(uint32_t count);
void forEach(uint32_t& startIndex, bool& outerStop, void (^callback)(uint32_t index, const T& value, bool& stop)) const;
void forEach(uint32_t& startIndex, bool& outerStop, void (^callback)(uint32_t index, T& value, bool& stop));
T* add(const T& value);
T* add(uint32_t count, const T values[]);
void remove(uint32_t index);
uint32_t count() const { return _inUseCount; }
uint32_t freeCount() const { return _allocCount - _inUseCount; }
private:
T& element(uint32_t index) { return ((T*)_elements)[index]; }
const T& element(uint32_t index) const { return ((T*)_elements)[index]; }
friend class ReaderWriterChunkedVector<T,C>;
ChunkedVector<T,C>* _next = nullptr;
uint32_t _allocCount = C;
uint32_t _inUseCount = 0;
uint8_t _elements[C*sizeof(T)] = { 0 };
};
template <typename T, int C>
class VIS_HIDDEN ReaderWriterChunkedVector {
public:
T* add(uint32_t count, const T values[]);
T* add(const T& value) { return add(1, &value); }
T* addNoLock(uint32_t count, const T values[]);
T* addNoLock(const T& value) { return addNoLock(1, &value); }
void remove(const T& value);
uint32_t count() const;
void forEachWithReadLock(void (^callback)(uint32_t index, const T& value, bool& stop)) const;
void forEachWithWriteLock(void (^callback)(uint32_t index, T& value, bool& stop));
void forEachNoLock(void (^callback)(uint32_t index, const T& value, bool& stop)) const;
T& operator[](size_t index);
uint32_t countNoLock() const;
void withReadLock(void (^withLock)()) const;
void withWriteLock(void (^withLock)()) const;
void acquireWriteLock();
void releaseWriteLock();
void dump(void (^callback)(const T& value)) const;
private:
mutable pthread_rwlock_t _lock = PTHREAD_RWLOCK_INITIALIZER;
ChunkedVector<T,C> _firstChunk;
};
typedef void (*NotifyFunc)(const mach_header* mh, intptr_t slide);
static ReaderWriterChunkedVector<NotifyFunc, 4> sLoadNotifiers;
static ReaderWriterChunkedVector<NotifyFunc, 4> sUnloadNotifiers;
static ReaderWriterChunkedVector<LoadedImage, 4> sLoadedImages;
static ReaderWriterChunkedVector<DlopenCount, 4> sDlopenRefCounts;
static ReaderWriterChunkedVector<const launch_cache::BinaryImageGroupData*, 4> sKnownGroups;
#if __MAC_OS_X_VERSION_MIN_REQUIRED
static ReaderWriterChunkedVector<__NSObjectFileImage, 2> sNSObjectFileImages;
#endif
template <typename T, int C>
ChunkedVector<T,C>* ChunkedVector<T,C>::make(uint32_t count)
{
size_t size = sizeof(ChunkedVector) + sizeof(T) * (count-C);
ChunkedVector<T,C>* result = (ChunkedVector<T,C>*)malloc(size);
result->_next = nullptr;
result->_allocCount = count;
result->_inUseCount = 0;
return result;
}
template <typename T, int C>
void ChunkedVector<T,C>::forEach(uint32_t& outerIndex, bool& outerStop, void (^callback)(uint32_t index, const T& value, bool& stop)) const
{
for (uint32_t i=0; i < _inUseCount; ++i) {
callback(outerIndex, element(i), outerStop);
++outerIndex;
if ( outerStop )
break;
}
}
template <typename T, int C>
void ChunkedVector<T,C>::forEach(uint32_t& outerIndex, bool& outerStop, void (^callback)(uint32_t index, T& value, bool& stop))
{
for (uint32_t i=0; i < _inUseCount; ++i) {
callback(outerIndex, element(i), outerStop);
++outerIndex;
if ( outerStop )
break;
}
}
template <typename T, int C>
T* ChunkedVector<T,C>::add(const T& value)
{
return add(1, &value);
}
template <typename T, int C>
T* ChunkedVector<T,C>::add(uint32_t count, const T values[])
{
assert(count <= (_allocCount - _inUseCount));
T* result = &element(_inUseCount);
memmove(result, values, sizeof(T)*count);
_inUseCount += count;
return result;
}
template <typename T, int C>
void ChunkedVector<T,C>::remove(uint32_t index)
{
assert(index < _inUseCount);
int moveCount = _inUseCount - index - 1;
if ( moveCount >= 1 ) {
memmove(&element(index), &element(index+1), sizeof(T)*moveCount);
}
_inUseCount--;
}
template <typename T, int C>
void ReaderWriterChunkedVector<T,C>::withReadLock(void (^work)()) const
{
assert(pthread_rwlock_rdlock(&_lock) == 0);
work();
assert(pthread_rwlock_unlock(&_lock) == 0);
}
template <typename T, int C>
void ReaderWriterChunkedVector<T,C>::withWriteLock(void (^work)()) const
{
assert(pthread_rwlock_wrlock(&_lock) == 0);
work();
assert(pthread_rwlock_unlock(&_lock) == 0);
}
template <typename T, int C>
void ReaderWriterChunkedVector<T,C>::acquireWriteLock()
{
assert(pthread_rwlock_wrlock(&_lock) == 0);
}
template <typename T, int C>
void ReaderWriterChunkedVector<T,C>::releaseWriteLock()
{
assert(pthread_rwlock_unlock(&_lock) == 0);
}
template <typename T, int C>
uint32_t ReaderWriterChunkedVector<T,C>::count() const
{
__block uint32_t result = 0;
withReadLock(^() {
for (const ChunkedVector<T,C>* chunk = &_firstChunk; chunk != nullptr; chunk = chunk->_next) {
result += chunk->count();
}
});
return result;
}
template <typename T, int C>
uint32_t ReaderWriterChunkedVector<T,C>::countNoLock() const
{
uint32_t result = 0;
for (const ChunkedVector<T,C>* chunk = &_firstChunk; chunk != nullptr; chunk = chunk->_next) {
result += chunk->count();
}
return result;
}
template <typename T, int C>
T* ReaderWriterChunkedVector<T,C>::addNoLock(uint32_t count, const T values[])
{
T* result = nullptr;
ChunkedVector<T,C>* lastChunk = &_firstChunk;
while ( lastChunk->_next != nullptr )
lastChunk = lastChunk->_next;
if ( lastChunk->freeCount() >= count ) {
result = lastChunk->add(count, values);
}
else {
uint32_t allocCount = count;
uint32_t remainder = count % C;
if ( remainder != 0 )
allocCount = count + C - remainder;
ChunkedVector<T,C>* newChunk = ChunkedVector<T,C>::make(allocCount);
result = newChunk->add(count, values);
lastChunk->_next = newChunk;
}
return result;
}
template <typename T, int C>
T* ReaderWriterChunkedVector<T,C>::add(uint32_t count, const T values[])
{
__block T* result = nullptr;
withWriteLock(^() {
result = addNoLock(count, values);
});
return result;
}
template <typename T, int C>
void ReaderWriterChunkedVector<T,C>::remove(const T& valueToRemove)
{
__block bool stopStorage = false;
withWriteLock(^() {
ChunkedVector<T,C>* chunkNowEmpty = nullptr;
__block uint32_t indexStorage = 0;
__block bool found = false;
for (ChunkedVector<T,C>* chunk = &_firstChunk; chunk != nullptr; chunk = chunk->_next) {
uint32_t chunkStartIndex = indexStorage;
__block uint32_t foundIndex = 0;
chunk->forEach(indexStorage, stopStorage, ^(uint32_t index, const T& value, bool& stop) {
if ( value == valueToRemove ) {
foundIndex = index - chunkStartIndex;
found = true;
stop = true;
}
});
if ( found ) {
chunk->remove(foundIndex);
found = false;
if ( chunk->count() == 0 )
chunkNowEmpty = chunk;
}
}
if ( chunkNowEmpty ) {
for (ChunkedVector<T,C>* chunk = &_firstChunk; chunk != nullptr; chunk = chunk->_next) {
if ( chunk->_next == chunkNowEmpty ) {
chunk->_next = chunkNowEmpty->_next;
if ( chunkNowEmpty != &_firstChunk )
free(chunkNowEmpty);
break;
}
}
}
});
}
template <typename T, int C>
void ReaderWriterChunkedVector<T,C>::forEachWithReadLock(void (^callback)(uint32_t index, const T& value, bool& stop)) const
{
__block uint32_t index = 0;
__block bool stop = false;
withReadLock(^() {
for (const ChunkedVector<T,C>* chunk = &_firstChunk; chunk != nullptr; chunk = chunk->_next) {
chunk->forEach(index, stop, callback);
if ( stop )
break;
}
});
}
template <typename T, int C>
void ReaderWriterChunkedVector<T,C>::forEachWithWriteLock(void (^callback)(uint32_t index, T& value, bool& stop))
{
__block uint32_t index = 0;
__block bool stop = false;
withReadLock(^() {
for (ChunkedVector<T,C>* chunk = &_firstChunk; chunk != nullptr; chunk = chunk->_next) {
chunk->forEach(index, stop, callback);
if ( stop )
break;
}
});
}
template <typename T, int C>
void ReaderWriterChunkedVector<T,C>::forEachNoLock(void (^callback)(uint32_t index, const T& value, bool& stop)) const
{
uint32_t index = 0;
bool stop = false;
for (const ChunkedVector<T,C>* chunk = &_firstChunk; chunk != nullptr; chunk = chunk->_next) {
chunk->forEach(index, stop, callback);
if ( stop )
break;
}
}
template <typename T, int C>
T& ReaderWriterChunkedVector<T,C>::operator[](size_t targetIndex)
{
__block T* result = nullptr;
forEachNoLock(^(uint32_t index, T const& value, bool& stop) {
if ( index == targetIndex ) {
result = (T*)&value;
stop = true;
}
});
return *result;
}
template <typename T, int C>
void ReaderWriterChunkedVector<T,C>::dump(void (^callback)(const T& value)) const
{
log("dump ReaderWriterChunkedVector at %p\n", this);
__block uint32_t index = 0;
__block bool stop = false;
withReadLock(^() {
for (const ChunkedVector<T,C>* chunk = &_firstChunk; chunk != nullptr; chunk = chunk->_next) {
log(" chunk at %p\n", chunk);
chunk->forEach(index, stop, ^(uint32_t i, const T& value, bool& s) {
callback(value);
});
}
});
}
AllImages gAllImages;
void AllImages::init(const BinaryClosure* closure, const void* dyldCacheLoadAddress, const char* dyldCachePath,
const dyld3::launch_cache::DynArray<loader::ImageInfo>& initialImages)
{
_mainClosure = closure;
_initialImages = &initialImages;
_dyldCacheAddress = dyldCacheLoadAddress;
_dyldCachePath = dyldCachePath;
uint32_t count = (uint32_t)initialImages.count();
dyld_image_info oldDyldInfo[count];
for (int i=0; i < count; ++i) {
launch_cache::Image img(initialImages[i].imageData);
oldDyldInfo[i].imageLoadAddress = initialImages[i].loadAddress;
oldDyldInfo[i].imageFilePath = img.path();
oldDyldInfo[i].imageFileModDate = 0;
}
_oldAllImageInfos->infoArray = oldDyldInfo;
_oldAllImageInfos->infoArrayCount = count;
_oldAllImageInfos->notification(dyld_image_adding, count, oldDyldInfo);
_oldAllImageInfos->infoArray = nullptr;
_oldAllImageInfos->infoArrayCount = 0;
}
void AllImages::setProgramVars(ProgramVars* vars)
{
_programVars = vars;
}
void AllImages::applyInitialImages()
{
addImages(*_initialImages);
_initialImages = nullptr; }
void AllImages::mirrorToOldAllImageInfos()
{
_oldAllImageInfos->infoArray = nullptr;
uint32_t imageCount = sLoadedImages.countNoLock();
if ( _oldArrayAllocCount < imageCount ) {
uint32_t newAllocCount = imageCount + 16;
dyld_image_info* newArray = (dyld_image_info*)malloc(sizeof(dyld_image_info)*newAllocCount);
if ( _oldAllImageArray != nullptr ) {
memcpy(newArray, _oldAllImageArray, sizeof(dyld_image_info)*_oldAllImageInfos->infoArrayCount);
free(_oldAllImageArray);
}
_oldAllImageArray = newArray;
_oldArrayAllocCount = newAllocCount;
}
sLoadedImages.forEachNoLock(^(uint32_t index, const LoadedImage& loadedImage, bool& stop) {
launch_cache::Image img(loadedImage.image());
_oldAllImageArray[index].imageLoadAddress = loadedImage.loadedAddress();
_oldAllImageArray[index].imageFilePath = imagePath(loadedImage.image());
_oldAllImageArray[index].imageFileModDate = 0;
});
_oldAllImageInfos->infoArrayCount = imageCount;
_oldAllImageInfos->infoArrayChangeTimestamp = mach_absolute_time();
_oldAllImageInfos->infoArray = _oldAllImageArray;
}
void AllImages::addImages(const launch_cache::DynArray<loader::ImageInfo>& newImages)
{
uint32_t count = (uint32_t)newImages.count();
assert(count != 0);
STACK_ALLOC_DYNARRAY(LoadedImage, count, loadedImagesArray);
for (uint32_t i=0; i < count; ++i) {
loadedImagesArray[i].init(newImages[i].loadAddress, newImages[i].imageData);
if (newImages[i].neverUnload)
loadedImagesArray[i].setNeverUnload();
}
sLoadedImages.add(count, &loadedImagesArray[0]);
if ( _oldAllImageInfos != nullptr ) {
if ( _initialImages != nullptr ) {
mirrorToOldAllImageInfos();
}
else {
sLoadedImages.withReadLock(^{
mirrorToOldAllImageInfos();
});
}
dyld_image_info oldDyldInfo[count];
for (int i=0; i < count; ++i) {
launch_cache::Image img(newImages[i].imageData);
oldDyldInfo[i].imageLoadAddress = newImages[i].loadAddress;
oldDyldInfo[i].imageFilePath = imagePath(newImages[i].imageData);
oldDyldInfo[i].imageFileModDate = 0;
}
_oldAllImageInfos->notification(dyld_image_adding, count, oldDyldInfo);
}
for (int i=0; i < count; ++i) {
launch_cache::Image img(newImages[i].imageData);
log_loads("dyld: %s\n", imagePath(newImages[i].imageData));
}
#if !TARGET_IPHONE_SIMULATOR
if (kdebug_is_enabled(KDBG_CODE(DBG_DYLD, DBG_DYLD_UUID, DBG_DYLD_UUID_MAP_A))) {
for (uint32_t i=0; i < count; ++i) {
launch_cache::Image img(newImages[i].imageData);
struct stat stat_buf;
fsid_t fsid = {{ 0, 0 }};
fsobj_id_t fsobjid = { 0, 0 };
if (img.isDiskImage() && stat(imagePath(newImages[i].imageData), &stat_buf) == 0 ) {
fsobjid = *(fsobj_id_t*)&stat_buf.st_ino;
fsid = {{ stat_buf.st_dev, 0 }};
}
kdebug_trace_dyld_image(DBG_DYLD_UUID_MAP_A, img.uuid(), fsobjid, fsid, newImages[i].loadAddress);
}
}
#endif
const uint32_t existingNotifierCount = sLoadNotifiers.count();
NotifyFunc existingNotifiers[existingNotifierCount];
NotifyFunc* existingNotifierArray = existingNotifiers;
sLoadNotifiers.forEachWithReadLock(^(uint32_t index, const NotifyFunc& func, bool& stop) {
if ( index < existingNotifierCount )
existingNotifierArray[index] = func;
});
for (uint32_t j=0; j < existingNotifierCount; ++j) {
NotifyFunc func = existingNotifierArray[j];
for (uint32_t i=0; i < count; ++i) {
MachOParser parser(newImages[i].loadAddress);
log_notifications("dyld: add notifier %p called with mh=%p\n", func, newImages[i].loadAddress);
func(newImages[i].loadAddress, parser.getSlide());
}
}
if ( _objcNotifyMapped != nullptr ) {
const char* pathsBuffer[count];
const mach_header* mhBuffer[count];
uint32_t imagesWithObjC = 0;
for (uint32_t i=0; i < count; ++i) {
launch_cache::Image img(newImages[i].imageData);
if ( img.hasObjC() ) {
pathsBuffer[imagesWithObjC] = imagePath(newImages[i].imageData);
mhBuffer[imagesWithObjC] = newImages[i].loadAddress;
++imagesWithObjC;
}
}
if ( imagesWithObjC != 0 ) {
(*_objcNotifyMapped)(imagesWithObjC, pathsBuffer, mhBuffer);
if ( log_notifications("dyld: objc-mapped-notifier called with %d images:\n", imagesWithObjC) ) {
for (uint32_t i=0; i < imagesWithObjC; ++i) {
log_notifications("dyld: objc-mapped: %p %s\n", mhBuffer[i], pathsBuffer[i]);
}
}
}
}
notifyMonitorLoads(newImages);
}
void AllImages::removeImages(const launch_cache::DynArray<loader::ImageInfo>& unloadImages)
{
uint32_t count = (uint32_t)unloadImages.count();
assert(count != 0);
const uint32_t existingNotifierCount = sUnloadNotifiers.count();
NotifyFunc existingNotifiers[existingNotifierCount];
NotifyFunc* existingNotifierArray = existingNotifiers;
sUnloadNotifiers.forEachWithReadLock(^(uint32_t index, const NotifyFunc& func, bool& stop) {
if ( index < existingNotifierCount )
existingNotifierArray[index] = func;
});
for (uint32_t j=0; j < existingNotifierCount; ++j) {
NotifyFunc func = existingNotifierArray[j];
for (uint32_t i=0; i < count; ++i) {
MachOParser parser(unloadImages[i].loadAddress);
log_notifications("dyld: remove notifier %p called with mh=%p\n", func, unloadImages[i].loadAddress);
func(unloadImages[i].loadAddress, parser.getSlide());
}
}
if ( _objcNotifyUnmapped != nullptr ) {
for (uint32_t i=0; i < count; ++i) {
launch_cache::Image img(unloadImages[i].imageData);
if ( img.hasObjC() ) {
(*_objcNotifyUnmapped)(imagePath(unloadImages[i].imageData), unloadImages[i].loadAddress);
log_notifications("dyld: objc-unmapped-notifier called with image %p %s\n", unloadImages[i].loadAddress, imagePath(unloadImages[i].imageData));
}
}
}
#if !TARGET_IPHONE_SIMULATOR
if (kdebug_is_enabled(KDBG_CODE(DBG_DYLD, DBG_DYLD_UUID, DBG_DYLD_UUID_MAP_A))) {
for (uint32_t i=0; i < count; ++i) {
launch_cache::Image img(unloadImages[i].imageData);
struct stat stat_buf;
fsid_t fsid = {{ 0, 0 }};
fsobj_id_t fsobjid = { 0, 0 };
if (stat(imagePath(unloadImages[i].imageData), &stat_buf) == 0 ) {
fsobjid = *(fsobj_id_t*)&stat_buf.st_ino;
fsid = {{ stat_buf.st_dev, 0 }};
}
kdebug_trace_dyld_image(DBG_DYLD_UUID_UNMAP_A, img.uuid(), fsobjid, fsid, unloadImages[i].loadAddress);
}
}
#endif
for (uint32_t i=0; i < count; ++i) {
LoadedImage info(unloadImages[i].loadAddress, unloadImages[i].imageData);
sLoadedImages.remove(info);
}
sLoadedImages.withReadLock(^{
mirrorToOldAllImageInfos();
});
dyld_image_info oldDyldInfo[count];
for (int i=0; i < count; ++i) {
launch_cache::Image img(unloadImages[i].imageData);
oldDyldInfo[i].imageLoadAddress = unloadImages[i].loadAddress;
oldDyldInfo[i].imageFilePath = imagePath(unloadImages[i].imageData);
oldDyldInfo[i].imageFileModDate = 0;
}
_oldAllImageInfos->notification(dyld_image_removing, count, oldDyldInfo);
for (int i=0; i < count; ++i) {
launch_cache::Image img(unloadImages[i].imageData);
loader::unmapImage(unloadImages[i].imageData, unloadImages[i].loadAddress);
log_loads("dyld: unloaded %s\n", imagePath(unloadImages[i].imageData));
}
notifyMonitorUnloads(unloadImages);
}
void AllImages::setNeverUnload(const loader::ImageInfo& existingImage)
{
sLoadedImages.forEachWithWriteLock(^(uint32_t index, dyld3::LoadedImage &value, bool &stop) {
if (value.image() == existingImage.imageData) {
value.setNeverUnload();
stop = true;
}
});
}
uint32_t AllImages::count() const
{
return sLoadedImages.count();
}
launch_cache::Image AllImages::findByLoadOrder(uint32_t index, const mach_header** loadAddress) const
{
__block const BinaryImage* foundImage = nullptr;
sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) {
if ( anIndex == index ) {
foundImage = loadedImage.image();
*loadAddress = loadedImage.loadedAddress();
stop = true;
}
});
return launch_cache::Image(foundImage);
}
launch_cache::Image AllImages::findByLoadAddress(const mach_header* loadAddress) const
{
__block const BinaryImage* foundImage = nullptr;
sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) {
if ( loadedImage.loadedAddress() == loadAddress ) {
foundImage = loadedImage.image();
stop = true;
}
});
return launch_cache::Image(foundImage);
}
bool AllImages::findIndexForLoadAddress(const mach_header* loadAddress, uint32_t& index)
{
__block bool result = false;
sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) {
if ( loadedImage.loadedAddress() == loadAddress ) {
index = anIndex;
result = true;
stop = true;
}
});
return result;
}
void AllImages::forEachImage(void (^handler)(uint32_t imageIndex, const mach_header* loadAddress, const launch_cache::Image image, bool& stop)) const
{
sLoadedImages.forEachWithReadLock(^(uint32_t imageIndex, const LoadedImage& loadedImage, bool& stop) {
handler(imageIndex, loadedImage.loadedAddress(), launch_cache::Image(loadedImage.image()), stop);
});
}
launch_cache::Image AllImages::findByOwnedAddress(const void* addr, const mach_header** loadAddress, uint8_t* permissions) const
{
if ( _initialImages != nullptr ) {
for (int i=0; i < _initialImages->count(); ++i) {
const loader::ImageInfo& entry = (*_initialImages)[i];
launch_cache::Image anImage(entry.imageData);
if ( anImage.containsAddress(addr, entry.loadAddress, permissions) ) {
*loadAddress = entry.loadAddress;
return entry.imageData;
}
}
return launch_cache::Image(nullptr);
}
if ( (_dyldCacheAddress != nullptr) && (addr > _dyldCacheAddress) ) {
const DyldSharedCache* dyldCache = (DyldSharedCache*)_dyldCacheAddress;
if ( addr < (void*)((uint8_t*)_dyldCacheAddress+dyldCache->mappedSize()) ) {
size_t cacheVmOffset = ((uint8_t*)addr - (uint8_t*)_dyldCacheAddress);
DyldCacheParser cacheParser(dyldCache, false);
launch_cache::ImageGroup cachedDylibsGroup(cacheParser.cachedDylibsGroup());
uint32_t mhCacheOffset;
uint8_t foundPermissions;
launch_cache::Image image(cachedDylibsGroup.findImageByCacheOffset(cacheVmOffset, mhCacheOffset, foundPermissions));
if ( image.valid() ) {
*loadAddress = (mach_header*)((uint8_t*)_dyldCacheAddress + mhCacheOffset);
if ( permissions != nullptr )
*permissions = foundPermissions;
return image;
}
}
}
__block const BinaryImage* foundImage = nullptr;
sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) {
launch_cache::Image anImage(loadedImage.image());
if ( anImage.containsAddress(addr, loadedImage.loadedAddress(), permissions) ) {
*loadAddress = loadedImage.loadedAddress();
foundImage = loadedImage.image();
stop = true;
}
});
return launch_cache::Image(foundImage);
}
const mach_header* AllImages::findLoadAddressByImage(const BinaryImage* targetImage) const
{
__block const mach_header* foundAddress = nullptr;
sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) {
if ( targetImage == loadedImage.image() ) {
foundAddress = loadedImage.loadedAddress();
stop = true;
}
});
return foundAddress;
}
const mach_header* AllImages::mainExecutable() const
{
assert(_programVars != nullptr);
return (const mach_header*)_programVars->mh;
}
launch_cache::Image AllImages::mainExecutableImage() const
{
assert(_mainClosure != nullptr);
const launch_cache::Closure mainClosure(_mainClosure);
const dyld3::launch_cache::ImageGroup mainGroup = mainClosure.group();
const uint32_t mainExecutableIndex = mainClosure.mainExecutableImageIndex();
const dyld3::launch_cache::Image mainImage = mainGroup.image(mainExecutableIndex);
return mainImage;
}
void AllImages::setMainPath(const char* path )
{
_mainExeOverridePath = path;
}
const char* AllImages::imagePath(const BinaryImage* binImage) const
{
#if __IPHONE_OS_VERSION_MIN_REQUIRED
if ( _mainExeOverridePath != nullptr ) {
if ( binImage == mainExecutableImage().binaryData() )
return _mainExeOverridePath;
}
#endif
launch_cache::Image image(binImage);
return image.path();
}
void AllImages::setInitialGroups()
{
DyldCacheParser cacheParser((DyldSharedCache*)_dyldCacheAddress, false);
sKnownGroups.addNoLock(cacheParser.cachedDylibsGroup());
sKnownGroups.addNoLock(cacheParser.otherDylibsGroup());
launch_cache::Closure closure(_mainClosure);
sKnownGroups.addNoLock(closure.group().binaryData());
}
const launch_cache::binary_format::ImageGroup* AllImages::cachedDylibsGroup()
{
return sKnownGroups[0];
}
const launch_cache::binary_format::ImageGroup* AllImages::otherDylibsGroup()
{
return sKnownGroups[1];
}
const AllImages::BinaryImageGroup* AllImages::mainClosureGroup()
{
return sKnownGroups[2];
}
uint32_t AllImages::currentGroupsCount() const
{
return sKnownGroups.count();
}
void AllImages::copyCurrentGroups(ImageGroupList& groups) const
{
sKnownGroups.forEachWithReadLock(^(uint32_t index, const dyld3::launch_cache::binary_format::ImageGroup* const &grpData, bool &stop) {
if ( index < groups.count() )
groups[index] = grpData;
});
}
void AllImages::copyCurrentGroupsNoLock(ImageGroupList& groups) const
{
sKnownGroups.forEachNoLock(^(uint32_t index, const dyld3::launch_cache::binary_format::ImageGroup* const &grpData, bool &stop) {
if ( index < groups.count() )
groups[index] = grpData;
});
}
const mach_header* AllImages::alreadyLoaded(uint64_t inode, uint64_t mtime, bool bumpRefCount)
{
__block const mach_header* result = nullptr;
sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) {
launch_cache::Image img(loadedImage.image());
if ( img.validateUsingModTimeAndInode() ) {
if ( (img.fileINode() == inode) && (img.fileModTime() == mtime) ) {
result = loadedImage.loadedAddress();
if ( bumpRefCount && !loadedImage.neverUnload() )
incRefCount(loadedImage.loadedAddress());
stop = true;
}
}
});
return result;
}
const mach_header* AllImages::alreadyLoaded(const char* path, bool bumpRefCount)
{
__block const mach_header* result = nullptr;
uint32_t targetHash = launch_cache::ImageGroup::hashFunction(path);
sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) {
launch_cache::Image img(loadedImage.image());
if ( (img.pathHash() == targetHash) && (strcmp(path, imagePath(loadedImage.image())) == 0) ) {
result = loadedImage.loadedAddress();
if ( bumpRefCount && !loadedImage.neverUnload() )
incRefCount(loadedImage.loadedAddress());
stop = true;
}
});
if ( result == nullptr ) {
launch_cache::ImageGroup mainGroup(mainClosureGroup());
STACK_ALLOC_DYNARRAY(const launch_cache::BinaryImageGroupData*, currentGroupsCount(), currentGroupsList);
copyCurrentGroups(currentGroupsList);
mainGroup.forEachImageRefOverride(currentGroupsList, ^(launch_cache::Image standardDylib, launch_cache::Image overrideDyilb, bool& stop) {
if ( strcmp(standardDylib.path(), path) == 0 ) {
result = alreadyLoaded(overrideDyilb.path(), bumpRefCount);
stop = true;
}
});
}
return result;
}
const mach_header* AllImages::alreadyLoaded(const BinaryImage* binImage, bool bumpRefCount)
{
const mach_header* result = findLoadAddressByImage(binImage);
if ( result != nullptr ) {
launch_cache::Image loadedImage(binImage);
if ( bumpRefCount && !loadedImage.neverUnload() )
incRefCount(result);
}
return result;
}
void AllImages::incRefCount(const mach_header* loadAddress)
{
__block bool found = false;
sDlopenRefCounts.forEachWithWriteLock(^(uint32_t index, DlopenCount& entry, bool& stop) {
if ( entry.loadAddress == loadAddress ) {
found = true;
entry.refCount += 1;
stop = true;
}
});
if ( !found ) {
DlopenCount newEnty = { loadAddress, 1 };
sDlopenRefCounts.add(newEnty);
}
}
void AllImages::decRefCount(const mach_header* loadAddress)
{
__block bool refCountNowZero = false;
sDlopenRefCounts.forEachWithWriteLock(^(uint32_t index, DlopenCount& entry, bool& stop) {
if ( entry.loadAddress == loadAddress ) {
entry.refCount -= 1;
stop = true;
if ( entry.refCount == 0 )
refCountNowZero = true;
}
});
if ( refCountNowZero ) {
DlopenCount delEnty = { loadAddress, 0 };
sDlopenRefCounts.remove(delEnty);
garbageCollectImages();
}
}
#if __MAC_OS_X_VERSION_MIN_REQUIRED
__NSObjectFileImage* AllImages::addNSObjectFileImage()
{
__block __NSObjectFileImage* result = nullptr;
sNSObjectFileImages.forEachWithWriteLock(^(uint32_t index, __NSObjectFileImage& value, bool& stop) {
if ( (value.path == nullptr) && (value.memSource == nullptr) ) {
result = &value;
stop = true;
}
});
if ( result != nullptr )
return result;
__NSObjectFileImage empty;
return sNSObjectFileImages.add(empty);
}
bool AllImages::hasNSObjectFileImage(__NSObjectFileImage* ofi)
{
__block bool result = false;
sNSObjectFileImages.forEachNoLock(^(uint32_t index, const __NSObjectFileImage& value, bool& stop) {
if ( &value == ofi ) {
result = ((value.memSource != nullptr) || (value.path != nullptr));
stop = true;
}
});
return result;
}
void AllImages::removeNSObjectFileImage(__NSObjectFileImage* ofi)
{
sNSObjectFileImages.forEachWithWriteLock(^(uint32_t index, __NSObjectFileImage& value, bool& stop) {
if ( &value == ofi ) {
ofi->path = nullptr;
ofi->memSource = nullptr;
ofi->memLength = 0;
ofi->loadAddress = nullptr;
ofi->binImage = nullptr;
stop = true;
}
});
}
#endif
class VIS_HIDDEN Reaper
{
public:
Reaper(uint32_t count, const LoadedImage** unloadables, bool* inUseArray);
void garbageCollect();
void finalizeDeadImages();
private:
typedef launch_cache::binary_format::Image BinaryImage;
void markDirectlyDlopenedImagesAsUsed();
void markDependentOfInUseImages();
void markDependentsOf(const LoadedImage*);
bool loadAddressIsUnloadable(const mach_header* loadAddr, uint32_t& index);
bool imageIsUnloadable(const BinaryImage* binImage, uint32_t& foundIndex);
uint32_t inUseCount();
void dump(const char* msg);
const LoadedImage** _unloadablesArray;
bool* _inUseArray;
uint32_t _arrayCount;
uint32_t _deadCount;
};
Reaper::Reaper(uint32_t count, const LoadedImage** unloadables, bool* inUseArray)
: _unloadablesArray(unloadables), _inUseArray(inUseArray),_arrayCount(count)
{
}
bool Reaper::loadAddressIsUnloadable(const mach_header* loadAddr, uint32_t& foundIndex)
{
for (uint32_t i=0; i < _arrayCount; ++i) {
if ( _unloadablesArray[i]->loadedAddress() == loadAddr ) {
foundIndex = i;
return true;
}
}
return false;
}
bool Reaper::imageIsUnloadable(const BinaryImage* binImage, uint32_t& foundIndex)
{
for (uint32_t i=0; i < _arrayCount; ++i) {
if ( _unloadablesArray[i]->image() == binImage ) {
foundIndex = i;
return true;
}
}
return false;
}
void Reaper::markDirectlyDlopenedImagesAsUsed()
{
sDlopenRefCounts.forEachWithReadLock(^(uint32_t refCountIndex, const dyld3::DlopenCount& dlEntry, bool& stop) {
if ( dlEntry.refCount != 0 ) {
uint32_t foundIndex;
if ( loadAddressIsUnloadable(dlEntry.loadAddress, foundIndex) ) {
_inUseArray[foundIndex] = true;
}
}
});
}
uint32_t Reaper::inUseCount()
{
uint32_t count = 0;
for (uint32_t i=0; i < _arrayCount; ++i) {
if ( _inUseArray[i] )
++count;
}
return count;
}
void Reaper::markDependentsOf(const LoadedImage* entry)
{
const launch_cache::Image image(entry->image());
STACK_ALLOC_DYNARRAY(const launch_cache::BinaryImageGroupData*, gAllImages.currentGroupsCount(), currentGroupsList);
gAllImages.copyCurrentGroups(currentGroupsList);
image.forEachDependentImage(currentGroupsList, ^(uint32_t depIndex, dyld3::launch_cache::Image depImage, dyld3::launch_cache::Image::LinkKind kind, bool& stop) {
uint32_t foundIndex;
if ( !depImage.neverUnload() && imageIsUnloadable(depImage.binaryData(), foundIndex) ) {
_inUseArray[foundIndex] = true;
}
});
}
void Reaper::markDependentOfInUseImages()
{
for (uint32_t i=0; i < _arrayCount; ++i) {
if ( _inUseArray[i] )
markDependentsOf(_unloadablesArray[i]);
}
}
void Reaper::dump(const char* msg)
{
for (uint32_t i=0; i < _arrayCount; ++i) {
dyld3::launch_cache::Image image(_unloadablesArray[i]->image());
}
}
void Reaper::garbageCollect()
{
markDirectlyDlopenedImagesAsUsed();
uint32_t lastCount = inUseCount();
bool countChanged = false;
do {
markDependentOfInUseImages();
uint32_t newCount = inUseCount();
countChanged = (newCount != lastCount);
lastCount = newCount;
} while (countChanged);
_deadCount = _arrayCount - inUseCount();
}
void Reaper::finalizeDeadImages()
{
if ( _deadCount == 0 )
return;
__cxa_range_t ranges[_deadCount];
__cxa_range_t* rangesArray = ranges;
__block unsigned int rangesCount = 0;
for (uint32_t i=0; i < _arrayCount; ++i) {
if ( _inUseArray[i] )
continue;
dyld3::launch_cache::Image image(_unloadablesArray[i]->image());
image.forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool &stop) {
if ( permissions & VM_PROT_EXECUTE ) {
rangesArray[rangesCount].addr = (char*)(_unloadablesArray[i]->loadedAddress()) + vmOffset;
rangesArray[rangesCount].length = (size_t)vmSize;
++rangesCount;
}
});
}
__cxa_finalize_ranges(ranges, rangesCount);
}
void AllImages::garbageCollectImages()
{
int32_t newCount = OSAtomicIncrement32(&_gcCount);
if ( newCount != 1 )
return;
do {
const uint32_t loadedImageCount = sLoadedImages.count();
const LoadedImage* unloadables[loadedImageCount];
bool unloadableInUse[loadedImageCount];
const LoadedImage** unloadablesArray = unloadables;
bool* unloadableInUseArray = unloadableInUse;
__block uint32_t unloadableCount = 0;
sLoadedImages.withReadLock(^() {
sLoadedImages.forEachNoLock(^(uint32_t index, const LoadedImage& entry, bool& stop) {
const launch_cache::Image image(entry.image());
if ( !image.neverUnload() && !entry.neverUnload() ) {
unloadablesArray[unloadableCount] = &entry;
unloadableInUseArray[unloadableCount] = false;
++unloadableCount;
}
});
Reaper reaper(unloadableCount, unloadablesArray, unloadableInUseArray);
reaper.garbageCollect();
reaper.finalizeDeadImages();
});
uint32_t removalCount = 0;
for (uint32_t i=0; i < unloadableCount; ++i) {
if ( !unloadableInUse[i] )
++removalCount;
}
if ( removalCount > 0 ) {
STACK_ALLOC_DYNARRAY(loader::ImageInfo, removalCount, unloadImages);
uint32_t removalIndex = 0;
for (uint32_t i=0; i < unloadableCount; ++i) {
if ( !unloadableInUse[i] ) {
unloadImages[removalIndex].loadAddress = unloadables[i]->loadedAddress();
unloadImages[removalIndex].imageData = unloadables[i]->image();
++removalIndex;
}
}
removeImages(unloadImages);
}
newCount = OSAtomicDecrement32(&_gcCount);
}
while (newCount > 0);
}
VIS_HIDDEN
const launch_cache::binary_format::Image* AllImages::messageClosured(const char* path, const char* apiName, const char* closuredErrorMessages[3], int& closuredErrorMessagesCount)
{
__block const launch_cache::binary_format::Image* result = nullptr;
sKnownGroups.withWriteLock(^() {
ClosureBuffer::CacheIdent cacheIdent;
bzero(&cacheIdent, sizeof(cacheIdent));
if ( _dyldCacheAddress != nullptr ) {
const DyldSharedCache* dyldCache = (DyldSharedCache*)_dyldCacheAddress;
dyldCache->getUUID(cacheIdent.cacheUUID);
cacheIdent.cacheAddress = (unsigned long)_dyldCacheAddress;
cacheIdent.cacheMappedSize = dyldCache->mappedSize();
}
gPathOverrides.forEachPathVariant(path, ^(const char* possiblePath, bool& stopVariants) {
struct stat statBuf;
if ( stat(possiblePath, &statBuf) == 0 ) {
if ( S_ISDIR(statBuf.st_mode) ) {
log_apis(" %s: path is directory: %s\n", apiName, possiblePath);
if ( closuredErrorMessagesCount < 3 )
closuredErrorMessages[closuredErrorMessagesCount++] = strdup("not a file");
}
else {
STACK_ALLOC_DYNARRAY(const launch_cache::BinaryImageGroupData*, sKnownGroups.countNoLock(), currentGroupsList);
gAllImages.copyCurrentGroupsNoLock(currentGroupsList);
dyld3::launch_cache::DynArray<const dyld3::launch_cache::binary_format::ImageGroup*> nonCacheGroupList(currentGroupsList.count()-2, ¤tGroupsList[2]);
const dyld3::launch_cache::binary_format::ImageGroup* closuredCreatedGroupData = nullptr;
ClosureBuffer closureBuilderInput(cacheIdent, path, nonCacheGroupList, gPathOverrides);
ClosureBuffer closureBuilderOutput = dyld3::closured_CreateImageGroup(closureBuilderInput);
if ( !closureBuilderOutput.isError() ) {
vm_protect(mach_task_self(), closureBuilderOutput.vmBuffer(), closureBuilderOutput.vmBufferSize(), false, VM_PROT_READ);
closuredCreatedGroupData = closureBuilderOutput.imageGroup();
log_apis(" %s: closured built ImageGroup for path: %s\n", apiName, possiblePath);
sKnownGroups.addNoLock(closuredCreatedGroupData);
launch_cache::ImageGroup group(closuredCreatedGroupData);
result = group.imageBinary(0);
stopVariants = true;
}
else {
log_apis(" %s: closured failed for path: %s, error: %s\n", apiName, possiblePath, closureBuilderOutput.errorMessage());
if ( closuredErrorMessagesCount < 3 ) {
closuredErrorMessages[closuredErrorMessagesCount++] = strdup(closureBuilderOutput.errorMessage());
}
closureBuilderOutput.free();
}
}
}
else {
log_apis(" %s: file does not exist for path: %s\n", apiName, possiblePath);
}
});
});
return result;
}
const AllImages::BinaryImage* AllImages::findImageInKnownGroups(const char* path)
{
__block const AllImages::BinaryImage* result = nullptr;
sKnownGroups.forEachWithReadLock(^(uint32_t index, const dyld3::launch_cache::binary_format::ImageGroup* const& grpData, bool& stop) {
launch_cache::ImageGroup group(grpData);
uint32_t ignore;
if ( const AllImages::BinaryImage* binImage = group.findImageByPath(path, ignore) ) {
result = binImage;
stop = true;
}
});
return result;
}
bool AllImages::imageUnloadable(const launch_cache::Image& image, const mach_header* loadAddress) const
{
if ( image.neverUnload() )
return false;
__block bool foundAsNeverUnload = false;
sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) {
if ( loadedImage.loadedAddress() == loadAddress ) {
stop = true;
if ( loadedImage.neverUnload() )
foundAsNeverUnload = true;
}
});
if ( foundAsNeverUnload )
return false;
return true;
}
void AllImages::addLoadNotifier(NotifyFunc func)
{
const uint32_t existingCount = sLoadedImages.count();
const mach_header* existingMHs[existingCount];
const mach_header** existingArray = existingMHs;
sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) {
if ( anIndex < existingCount )
existingArray[anIndex] = loadedImage.loadedAddress();
});
for (uint32_t i=0; i < existingCount; i++) {
MachOParser parser(existingArray[i]);
log_notifications("dyld: add notifier %p called with mh=%p\n", func, existingArray[i]);
func(existingArray[i], parser.getSlide());
}
sLoadNotifiers.add(func);
}
void AllImages::addUnloadNotifier(NotifyFunc func)
{
sUnloadNotifiers.add(func);
}
void AllImages::setObjCNotifiers(_dyld_objc_notify_mapped map, _dyld_objc_notify_init init, _dyld_objc_notify_unmapped unmap)
{
_objcNotifyMapped = map;
_objcNotifyInit = init;
_objcNotifyUnmapped = unmap;
uint32_t maxCount = count();
const char* pathsBuffer[maxCount];
const mach_header* mhBuffer[maxCount];
__block const char** paths = pathsBuffer;
__block const mach_header** mhs = mhBuffer;
__block uint32_t imagesWithObjC = 0;
sLoadedImages.forEachWithReadLock(^(uint32_t anIndex, const LoadedImage& loadedImage, bool& stop) {
launch_cache::Image img(loadedImage.image());
if ( img.hasObjC() ) {
mhs[imagesWithObjC] = loadedImage.loadedAddress();
paths[imagesWithObjC] = imagePath(loadedImage.image());
++imagesWithObjC;
}
});
if ( imagesWithObjC != 0 ) {
(*map)(imagesWithObjC, pathsBuffer, mhBuffer);
if ( log_notifications("dyld: objc-mapped-notifier called with %d images:\n", imagesWithObjC) ) {
for (uint32_t i=0; i < imagesWithObjC; ++i) {
log_notifications("dyld: objc-mapped: %p %s\n", mhBuffer[i], pathsBuffer[i]);
}
}
}
}
void AllImages::vmAccountingSetSuspended(bool suspend)
{
#if __arm__ || __arm64__
log_fixups("vm.footprint_suspend=%d\n", suspend);
int newValue = suspend ? 1 : 0;
int oldValue = 0;
size_t newlen = sizeof(newValue);
size_t oldlen = sizeof(oldValue);
sysctlbyname("vm.footprint_suspend", &oldValue, &oldlen, &newValue, newlen);
#endif
}
void AllImages::applyInterposingToDyldCache(const launch_cache::binary_format::Closure* closure, const dyld3::launch_cache::DynArray<loader::ImageInfo>& initialImages)
{
launch_cache::Closure mainClosure(closure);
launch_cache::ImageGroup mainGroup = mainClosure.group();
DyldCacheParser cacheParser((DyldSharedCache*)_dyldCacheAddress, false);
const launch_cache::binary_format::ImageGroup* dylibsGroupData = cacheParser.cachedDylibsGroup();
launch_cache::ImageGroup dyldCacheDylibGroup(dylibsGroupData);
__block bool suspendedAccounting = false;
mainGroup.forEachDyldCacheSymbolOverride(^(uint32_t patchTableIndex, const launch_cache::binary_format::Image* imageData, uint32_t imageOffset, bool& stop) {
bool foundInImages = false;
for (int i=0; i < initialImages.count(); ++i) {
if ( initialImages[i].imageData == imageData ) {
foundInImages = true;
uintptr_t replacement = (uintptr_t)(initialImages[i].loadAddress) + imageOffset;
dyldCacheDylibGroup.forEachDyldCachePatchLocation(_dyldCacheAddress, patchTableIndex, ^(uintptr_t* locationToPatch, uintptr_t addend, bool& innerStop) {
if ( !suspendedAccounting ) {
vmAccountingSetSuspended(true);
suspendedAccounting = true;
}
log_fixups("dyld: cache fixup: *%p = %p\n", locationToPatch, (void*)replacement);
*locationToPatch = replacement + addend;
});
break;
}
}
if ( !foundInImages ) {
launch_cache::Image img(imageData);
log_fixups("did not find loaded image to patch into cache: %s\n", img.path());
}
});
if ( suspendedAccounting )
vmAccountingSetSuspended(false);
}
void AllImages::runLibSystemInitializer(const mach_header* libSystemAddress, const launch_cache::binary_format::Image* libSystemBinImage)
{
launch_cache::Image libSystemImage(libSystemBinImage);
libSystemImage.forEachInitializer(libSystemAddress, ^(const void* func) {
Initializer initFunc = (Initializer)func;
dyld3::kdebug_trace_dyld_duration(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)func, 0, ^{
initFunc(NXArgc, NXArgv, environ, appleParams, _programVars);
});
log_initializers("called initialzer %p in %s\n", initFunc, libSystemImage.path());
});
sLoadedImages.forEachWithWriteLock(^(uint32_t anIndex, LoadedImage& loadedImage, bool& stop) {
if ( loadedImage.loadedAddress() == libSystemAddress ) {
loadedImage.setState(LoadedImage::State::inited);
stop = true;
}
});
}
void AllImages::runInitialzersBottomUp(const mach_header* imageLoadAddress)
{
launch_cache::Image topImage = findByLoadAddress(imageLoadAddress);
if ( topImage.isInvalid() )
return;
STACK_ALLOC_DYNARRAY(const launch_cache::BinaryImageGroupData*, currentGroupsCount(), currentGroupsList);
copyCurrentGroups(currentGroupsList);
topImage.forEachInitBefore(currentGroupsList, ^(launch_cache::Image imageToInit) {
__block LoadedImage* foundEntry = nullptr;
sLoadedImages.forEachWithReadLock(^(uint32_t index, const LoadedImage& entry, bool& stop) {
if ( entry.image() == imageToInit.binaryData() ) {
foundEntry = (LoadedImage*)&entry;
stop = true;
}
});
assert(foundEntry != nullptr);
pthread_mutex_lock(&_initializerLock);
if ( foundEntry->state() == LoadedImage::State::beingInited ) {
log_initializers("dyld: already initializing '%s'\n", imagePath(imageToInit.binaryData()));
}
if ( foundEntry->state() == LoadedImage::State::uninited ) {
foundEntry->setState(LoadedImage::State::beingInited);
pthread_mutex_unlock(&_initializerLock);
if ( (_objcNotifyInit != nullptr) && imageToInit.mayHavePlusLoads() ) {
log_notifications("dyld: objc-init-notifier called with mh=%p, path=%s\n", foundEntry->loadedAddress(), imagePath(imageToInit.binaryData()));
(*_objcNotifyInit)(imagePath(imageToInit.binaryData()), foundEntry->loadedAddress());
}
imageToInit.forEachInitializer(foundEntry->loadedAddress(), ^(const void* func) {
Initializer initFunc = (Initializer)func;
dyld3::kdebug_trace_dyld_duration(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)func, 0, ^{
initFunc(NXArgc, NXArgv, environ, appleParams, _programVars);
});
log_initializers("dyld: called initialzer %p in %s\n", initFunc, imageToInit.path());
});
pthread_mutex_lock(&_initializerLock);
foundEntry->setState(LoadedImage::State::inited);
}
pthread_mutex_unlock(&_initializerLock);
});
}
}