#include "ClosurePrinter.h"
#include "JSONWriter.h"
+#include "objc-shared-cache.h"
+
using namespace dyld3::json;
namespace dyld3 {
return std::string("bind to ") + targetImage->leafName() + " - " + hex8(-signExtend);
}
else
- return std::string("bind to ") + targetImage->leafName() + " + " + hex8(target.image.offset);
+ return std::string("bind to ") + hex4(target.image.imageNum) + "-" + targetImage->leafName() + " + " + hex8(target.image.offset);
break;
case Image::ResolvedSymbolTarget::kindSharedCache:
return std::string("bind to dyld cache + ") + hex8(target.sharedCache.offset);
return "???";
}
+static const char* nameForType(TypedBytes::Type type) {
+ switch (type) {
+ // containers
+ case TypedBytes::Type::launchClosure:
+ return "launchClosure";
+ case TypedBytes::Type::imageArray:
+ return "imageArray";
+ case TypedBytes::Type::image:
+ return "image";
+ case TypedBytes::Type::dlopenClosure:
+ return "dlopenClosure";
+ // attributes for Images
+ case TypedBytes::Type::imageFlags:
+ return "imageFlags";
+ case TypedBytes::Type::pathWithHash:
+ return "pathWithHash";
+ case TypedBytes::Type::fileInodeAndTime:
+ return "fileInodeAndTime";
+ case TypedBytes::Type::cdHash:
+ return "cdHash";
+ case TypedBytes::Type::uuid:
+ return "uuid";
+ case TypedBytes::Type::mappingInfo:
+ return "mappingInfo";
+ case TypedBytes::Type::diskSegment:
+ return "diskSegment";
+ case TypedBytes::Type::cacheSegment:
+ return "cacheSegment";
+ case TypedBytes::Type::dependents:
+ return "dependents";
+ case TypedBytes::Type::initOffsets:
+ return "initOffsets";
+ case TypedBytes::Type::dofOffsets:
+ return "dofOffsets";
+ case TypedBytes::Type::codeSignLoc:
+ return "codeSignLoc";
+ case TypedBytes::Type::fairPlayLoc:
+ return "fairPlayLoc";
+ case TypedBytes::Type::rebaseFixups:
+ return "rebaseFixups";
+ case TypedBytes::Type::bindFixups:
+ return "bindFixups";
+ case TypedBytes::Type::cachePatchInfo:
+ return "cachePatchInfo";
+ case TypedBytes::Type::textFixups:
+ return "textFixups";
+ case TypedBytes::Type::imageOverride:
+ return "imageOverride";
+ case TypedBytes::Type::initBefores:
+ return "initBefores";
+ case TypedBytes::Type::initsSection:
+ return "initSection";
+ case TypedBytes::Type::chainedFixupsTargets:
+ return "chainedFixupsTargets";
+ case TypedBytes::Type::termOffsets:
+ return "termOffsets";
+ case TypedBytes::Type::chainedStartsOffset:
+ return "chainedStartsOffset";
+ case TypedBytes::Type::objcFixups:
+ return "objcFixups";
+ // attributes for Closures (launch or dlopen)
+ case TypedBytes::Type::closureFlags:
+ return "closureFlags";
+ case TypedBytes::Type::dyldCacheUUID:
+ return "dyldCacheUUID";
+ case TypedBytes::Type::missingFiles:
+ return "missingFiles";
+ case TypedBytes::Type::envVar:
+ return "envVar";
+ case TypedBytes::Type::topImage:
+ return "topImage";
+ case TypedBytes::Type::libDyldEntry:
+ return "libDyldEntry";
+ case TypedBytes::Type::libSystemNum:
+ return "libSystemNum";
+ case TypedBytes::Type::mainEntry:
+ return "mainEntry";
+ case TypedBytes::Type::startEntry:
+ return "startEntry";
+ case TypedBytes::Type::cacheOverrides:
+ return "cacheOverrides";
+ case TypedBytes::Type::interposeTuples:
+ return "interposeTuples";
+ case TypedBytes::Type::existingFiles:
+ return "existingFiles";
+ case TypedBytes::Type::selectorTable:
+ return "selectorTable";
+ case TypedBytes::Type::classTable:
+ return "classTable";
+ case TypedBytes::Type::warning:
+ return "warning";
+ case TypedBytes::Type::duplicateClassesTable:
+ return "duplicateClassesTable";
+ case TypedBytes::Type::progVars:
+ return "programVars";
+ }
+}
-static Node buildImageNode(const Image* image, const Array<const ImageArray*>& imagesArrays, bool printFixups, bool printDependentsDetails, const uint8_t* cacheStart=nullptr)
+static Node buildImageNode(const Image* image, const Array<const ImageArray*>& imagesArrays, bool printFixups,
+ bool printDependentsDetails, bool printRaw,
+ const DyldSharedCache* dyldCache, const closure::ObjCSelectorOpt* selOpt,
+ const Array<closure::Image::ObjCSelectorImage>& selImages)
{
__block Node imageNode;
imageNode.map["image-num"].value = hex4(image->imageNum());
imageNode.map["path"].value = image->path();
+
+ if (printRaw) {
+ __block Node attributes;
+ image->forEachAttribute(^(const TypedBytes *typedBytes, bool &stop) {
+ Node anAttribute;
+ anAttribute.map["type"].value = decimal((uint32_t)typedBytes->type);
+ anAttribute.map["type-name"].value = nameForType((TypedBytes::Type)typedBytes->type);
+ anAttribute.map["length"].value = decimal(typedBytes->payloadLength);
+ attributes.array.push_back(anAttribute);
+ });
+ imageNode.map["attributes"] = attributes;
+ return imageNode;
+ }
+
__block Node imageAliases;
image->forEachAlias(^(const char* aliasPath, bool& stop) {
Node anAlias;
imageNode.map["has-weak-defs"].value = (image->hasWeakDefs() ? "true" : "false");
imageNode.map["has-plus-loads"].value = (image->mayHavePlusLoads() ? "true" : "false");
imageNode.map["never-unload"].value = (image->neverUnload() ? "true" : "false");
-// imageNode.map["platform-binary"].value = (image->isPlatformBinary() ? "true" : "false");
+ imageNode.map["has-precomputed-objc"].value = (image->hasPrecomputedObjC() ? "true" : "false");
// if ( image->cwdMustBeThisDir() )
// imageNode.map["cwd-must-be-this-dir"].value = "true";
if ( !image->inDyldCache() ) {
imageNode.map["file-mod-time"].value = hex(inode);
imageNode.map["file-inode"].value = hex(mTime);
}
- uint8_t cdHash[20];
- if ( image->hasCdHash(cdHash) ) {
+ image->forEachCDHash(^(const uint8_t *cdHash, bool& stop) {
std::string cdHashStr;
cdHashStr.reserve(24);
for (int i=0; i < 20; ++i) {
else
cdHashStr += 'a' + (nibbleL-10);
}
- if ( cdHashStr != "0000000000000000000000000000000000000000" )
- imageNode.map["cd-hash"].value = cdHashStr;
- }
- else {
- #if 0
- const uint8_t* cdHash = image->cdHash16();
- std::string cdHashStr;
- cdHashStr.reserve(32);
- for (int j=0; j < 16; ++j) {
- uint8_t byte = cdHash[j];
- uint8_t nibbleL = byte & 0x0F;
- uint8_t nibbleH = byte >> 4;
- if ( nibbleH < 10 )
- cdHashStr += '0' + nibbleH;
- else
- cdHashStr += 'a' + (nibbleH-10);
- if ( nibbleL < 10 )
- cdHashStr += '0' + nibbleL;
- else
- cdHashStr += 'a' + (nibbleL-10);
+ if ( cdHashStr != "0000000000000000000000000000000000000000" ) {
+ Node hashNode;
+ hashNode.value = cdHashStr;
+ imageNode.map["cd-hashes"].array.push_back(hashNode);
}
- imageNode.map["file-cd-hash-16"].value = cdHashStr;
- #endif
- }
+ });
imageNode.map["total-vm-size"].value = hex(image->vmSizeToMap());
uint64_t sliceOffset = image->sliceOffsetInFile();
if ( sliceOffset != 0 )
imageNode.map["file-offset-of-slice"].value = hex(sliceOffset);
//if ( image->hasTextRelocs() )
// imageNode.map["has-text-relocs"].value = "true";
- image->forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool& stop) {
+ image->forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool laterReadOnly, bool& stop) {
Node segInfoNode;
segInfoNode.map["file-offset"].value = hex(fileOffset);
segInfoNode.map["file-size"].value = hex(fileSize);
segInfoNode.map["vm-size"].value = hex(vmSize);
- segInfoNode.map["permissions"].value = hex(permissions);
imageNode.map["mappings"].array.push_back(segInfoNode);
+ switch ( permissions ) {
+ case 0:
+ segInfoNode.map["permissions"].value = "--";
+ break;
+ case 1:
+ segInfoNode.map["permissions"].value = "r";
+ break;
+ case 2:
+ segInfoNode.map["permissions"].value = "ro"; // r/w then r/o
+ break;
+ case 3:
+ segInfoNode.map["permissions"].value = "rw";
+ break;
+ case 4:
+ segInfoNode.map["permissions"].value = "rx";
+ break;
+ default:
+ segInfoNode.map["permissions"].value = "??";
+ }
});
-
+ uint64_t expectedInode;
+ uint64_t expectedMtime;
+ if ( image->hasFileModTimeAndInode(expectedInode, expectedMtime) ) {
+ imageNode.map["file-inode"].value = hex(expectedInode);
+ imageNode.map["file-mod-time"].value = hex(expectedMtime);
+ }
if ( printFixups ) {
image->forEachFixup(^(uint64_t imageOffsetToRebase, bool &stop) {
}, ^(uint64_t imageOffsetToBind, Image::ResolvedSymbolTarget target, bool &stop) {
// bind
imageNode.map["fixups"].map[hex8(imageOffsetToBind)].value = printTarget(imagesArrays, target);
- }, ^(uint64_t imageOffsetStart, const Array<Image::ResolvedSymbolTarget>& targets, bool& stop) {
+ }, ^(uint64_t startsStructImageOffset, const Array<Image::ResolvedSymbolTarget>& targets, bool& stop) {
// chain
- imageNode.map["fixups"].map[hex8(imageOffsetStart)].value = "chain-start";
+ imageNode.map["fixups-chain-starts-offset"].value = hex8(startsStructImageOffset);
for (const Image::ResolvedSymbolTarget& target: targets) {
Node targetNode;
targetNode.value = printTarget(imagesArrays, target);
imageNode.map["fixups-targets"].array.push_back(targetNode);
}
+
+ }, ^(uint64_t imageOffsetToFixup) {
+ // fixupObjCImageInfo
+ imageNode.map["fixups"].map[hex8(imageOffsetToFixup)].value = "objc image info pre-optimized by dyld flag";
+ }, ^(uint64_t imageOffsetToBind, Image::ResolvedSymbolTarget target, bool &stop) {
+ // fixupObjCProtocol
+ imageNode.map["fixups"].map[hex8(imageOffsetToBind)].value = printTarget(imagesArrays, target);
+ }, ^(uint64_t imageOffsetToFixup, uint32_t selectorIndex, bool inSharedCache, bool &stop) {
+ // fixupObjCSelRefs
+ Image::ResolvedSymbolTarget target;
+ if ( inSharedCache ) {
+ const char* selectorString = dyldCache->objcOpt()->selopt()->getEntryForIndex(selectorIndex);
+ target.sharedCache.kind = Image::ResolvedSymbolTarget::kindSharedCache;
+ target.sharedCache.offset = (uint64_t)selectorString - (uint64_t)dyldCache;
+ } else {
+ ImageNum imageNum;
+ uint64_t vmOffset;
+ bool gotLocation = selOpt->getStringLocation(selectorIndex, selImages, imageNum, vmOffset);
+ assert(gotLocation);
+ target.image.kind = Image::ResolvedSymbolTarget::kindImage;
+ target.image.imageNum = imageNum;
+ target.image.offset = vmOffset;
+ }
+ imageNode.map["fixups"].map[hex8(imageOffsetToFixup)].value = printTarget(imagesArrays, target);
+ }, ^(uint64_t imageOffsetToFixup, bool &stop) {
+ // fixupObjCStableSwift
+ imageNode.map["fixups"].map[hex8(imageOffsetToFixup)].value = "objc set stable Swift";
+ }, ^(uint64_t imageOffsetToFixup, bool &stop) {
+ // fixupObjCMethodList
+ imageNode.map["fixups"].map[hex8(imageOffsetToFixup)].value = "objc set fixed up method list";
});
image->forEachTextReloc(^(uint32_t imageOffsetToRebase, bool &stop) {
// rebase
}
else {
if ( printFixups ) {
- image->forEachPatchableExport(^(uint32_t cacheOffsetOfImpl, const char* name) {
- __block Node implNode;
- implNode.map["name"].value = name;
- implNode.map["impl-cache-offset"].value = hex8(cacheOffsetOfImpl);
- image->forEachPatchableUseOfExport(cacheOffsetOfImpl, ^(Image::PatchableExport::PatchLocation patchLocation) {
- Node siteNode;
- siteNode.map["cache-offset"].value = hex8(patchLocation.cacheOffset);
- if ( patchLocation.addend != 0 )
- siteNode.map["addend"].value = hex(patchLocation.addend);
- if ( patchLocation.authenticated != 0 ) {
- siteNode.map["key"].value = patchLocation.keyName();
- siteNode.map["address-diversity"].value = patchLocation.usesAddressDiversity ? "true" : "false";
- siteNode.map["discriminator"].value = hex4(patchLocation.discriminator);
- }
- implNode.map["usage-sites"].array.push_back(siteNode);
+ if ( dyldCache != nullptr ) {
+ uint32_t imageIndex = image->imageNum() - (uint32_t)dyldCache->cachedDylibsImageArray()->startImageNum();
+ dyldCache->forEachPatchableExport(imageIndex, ^(uint32_t cacheOffsetOfImpl, const char* name) {
+ __block Node implNode;
+ implNode.map["name"].value = name;
+ implNode.map["impl-cache-offset"].value = hex8(cacheOffsetOfImpl);
+ dyldCache->forEachPatchableUseOfExport(imageIndex, cacheOffsetOfImpl, ^(dyld_cache_patchable_location patchLocation) {
+ Node siteNode;
+ siteNode.map["cache-offset"].value = hex8(patchLocation.cacheOffset);
+ if ( patchLocation.addend != 0 )
+ siteNode.map["addend"].value = hex(patchLocation.addend);
+ if ( patchLocation.authenticated != 0 ) {
+ siteNode.map["key"].value = DyldSharedCache::keyName(patchLocation);
+ siteNode.map["address-diversity"].value = patchLocation.usesAddressDiversity ? "true" : "false";
+ siteNode.map["discriminator"].value = hex4(patchLocation.discriminator);
+ }
+ implNode.map["usage-sites"].array.push_back(siteNode);
+ });
+ imageNode.map["patches"].array.push_back(implNode);
});
- imageNode.map["patches"].array.push_back(implNode);
- });
+ }
}
}
});
// add initializers
- image->forEachInitializer(nullptr, ^(const void* initializer) {
- Node initNode;
- initNode.value = hex((long)initializer);
- imageNode.map["initializer-offsets"].array.push_back(initNode);
+ bool usesInitsSection = image->forEachInitializerSection(^(uint32_t sectionOffset, uint32_t sectionSize) {
+ Node initSectNode;
+ initSectNode.map["offset"].value = hex(sectionOffset);
+ initSectNode.map["size"].value = hex(sectionSize);
+ imageNode.map["initializers-section"].array.push_back(initSectNode);
});
-
+ if ( !usesInitsSection ) {
+ image->forEachInitializer(nullptr, ^(const void* initializer) {
+ Node initNode;
+ initNode.value = hex((long)initializer);
+ imageNode.map["initializer-offsets"].array.push_back(initNode);
+ });
+ }
__block Node initBeforeNode;
image->forEachImageToInitBefore(^(ImageNum imageToInit, bool& stop) {
Node beforeNode;
imageNode.map["initializer-order"].array.push_back(beforeNode);
});
+ // add static terminators
+ image->forEachTerminator(nullptr, ^(const void* terminator) {
+ Node termNode;
+ termNode.value = hex8((long)terminator);
+ imageNode.map["terminator-offsets"].array.push_back(termNode);
+ });
+
ImageNum cacheImageNum;
if ( image->isOverrideOfDyldCacheImage(cacheImageNum) ) {
imageNode.map["override-of-dyld-cache-image"].value = ImageArray::findImage(imagesArrays, cacheImageNum)->path();
}
+ if ( image->inDyldCache() && image->overridableDylib() ) {
+ imageNode.map["overridable-dylib"].value = "true";
+ }
+
#if 0
// add things to init before this image
}
-static Node buildImageArrayNode(const ImageArray* imageArray, const Array<const ImageArray*>& imagesArrays, bool printFixups, bool printDependentsDetails, const uint8_t* cacheStart=nullptr)
+static Node buildImageArrayNode(const ImageArray* imageArray, const Array<const ImageArray*>& imagesArrays,
+ bool printFixups, bool printDependentsDetails, bool printRaw,
+ const DyldSharedCache* dyldCache, const closure::ObjCSelectorOpt* selOpt,
+ const Array<closure::Image::ObjCSelectorImage>& selImages)
{
__block Node images;
imageArray->forEachImage(^(const Image* image, bool& stop) {
- images.array.push_back(buildImageNode(image, imagesArrays, printFixups, printDependentsDetails, cacheStart));
+ images.array.push_back(buildImageNode(image, imagesArrays, printFixups, printDependentsDetails, printRaw, dyldCache, selOpt, selImages));
});
return images;
}
-static Node buildClosureNode(const DlopenClosure* closure, const Array<const ImageArray*>& imagesArrays, bool printFixups, bool printDependentsDetails)
+static Node buildClosureNode(const DlopenClosure* closure, const Array<const ImageArray*>& imagesArrays,
+ bool printFixups, bool printRaw, bool printDependentsDetails)
{
__block Node root;
- root.map["images"] = buildImageArrayNode(closure->images(), imagesArrays, printFixups, printDependentsDetails);
+ root.map["images"] = buildImageArrayNode(closure->images(), imagesArrays,
+ printFixups, printDependentsDetails, printRaw,
+ nullptr, nullptr, {});
closure->forEachPatchEntry(^(const Closure::PatchEntry& patchEntry) {
Node patchNode;
patchNode.map["func-dyld-cache-offset"].value = hex8(patchEntry.exportCacheOffset);
- patchNode.map["func-image-num"].value = hex8(patchEntry.overriddenDylibInCache);
+ patchNode.map["func-image-num"].value = hex4(patchEntry.overriddenDylibInCache);
patchNode.map["replacement"].value = printTarget(imagesArrays, patchEntry.replacement);
root.map["dyld-cache-fixups"].array.push_back(patchNode);
});
return root;
}
-static Node buildClosureNode(const LaunchClosure* closure, const Array<const ImageArray*>& imagesArrays, bool printFixups, bool printDependentsDetails)
+static Node buildClosureNode(const LaunchClosure* closure, const Array<const ImageArray*>& imagesArrays,
+ bool printFixups, bool printDependentsDetails, bool printRaw,
+ const DyldSharedCache* dyldCache)
{
__block Node root;
- root.map["images"] = buildImageArrayNode(closure->images(), imagesArrays, printFixups, printDependentsDetails);
+
+ Array<Image::ObjCSelectorImage> selectorImages;
+ const closure::ObjCSelectorOpt* selectorHashTable = nullptr;
+ bool hasPreoptimizedObjCSelectors = closure->selectorHashTable(selectorImages, selectorHashTable);
+
+ root.map["images"] = buildImageArrayNode(closure->images(), imagesArrays, printFixups,
+ printDependentsDetails, printRaw,
+ dyldCache, selectorHashTable, selectorImages);
+
+ if ( printRaw ) {
+ __block Node attributes;
+ closure->forEachAttribute(^(const TypedBytes *typedBytes, bool &stop) {
+ Node anAttribute;
+ anAttribute.map["type"].value = decimal((uint32_t)typedBytes->type);
+ anAttribute.map["type-name"].value = nameForType((TypedBytes::Type)typedBytes->type);
+ anAttribute.map["length"].value = decimal(typedBytes->payloadLength);
+ attributes.array.push_back(anAttribute);
+ });
+ root.map["attributes"] = attributes;
+ return root;
+ }
closure->forEachPatchEntry(^(const Closure::PatchEntry& patchEntry) {
Node patchNode;
patchNode.map["func-dyld-cache-offset"].value = hex8(patchEntry.exportCacheOffset);
- patchNode.map["func-image-num"].value = hex8(patchEntry.overriddenDylibInCache);
+ patchNode.map["func-image-num"].value = hex4(patchEntry.overriddenDylibInCache);
patchNode.map["replacement"].value = printTarget(imagesArrays, patchEntry.replacement);
root.map["dyld-cache-fixups"].array.push_back(patchNode);
});
root.map["must-be-missing-files"].array.push_back(fileNode);
});
+ // add skipped files array if they exist
+ closure->forEachSkipIfExistsFile(^(const LaunchClosure::SkippedFile &file, bool &stop) {
+ Node fileNode;
+ fileNode.map["path"].value = file.path;
+ fileNode.map["file-mod-time"].value = hex(file.mtime);
+ fileNode.map["file-inode"].value = hex(file.inode);
+ root.map["skipped-existing-files"].array.push_back(fileNode);
+ });
+
// add interposing info, if any
closure->forEachInterposingTuple(^(const InterposingTuple& tuple, bool& stop) {
Node tupleNode;
root.map["interposing-tuples"].array.push_back(tupleNode);
});
- closure->forEachPatchEntry(^(const Closure::PatchEntry& patchEntry) {
- Node patchNode;
- patchNode.map["func-dyld-cache-offset"].value = hex8(patchEntry.exportCacheOffset);
- patchNode.map["func-image-num"].value = hex8(patchEntry.overriddenDylibInCache);
- patchNode.map["replacement"].value = printTarget(imagesArrays, patchEntry.replacement);
- root.map["dyld-cache-fixups"].array.push_back(patchNode);
- });
-
root.map["initial-image-count"].value = decimal(closure->initialLoadCount());
-#if 0
-
-
// add env-vars if they exist
closure->forEachEnvVar(^(const char* keyEqualValue, bool& stop) {
const char* equ = strchr(keyEqualValue, '=');
}
});
+ if (hasPreoptimizedObjCSelectors) {
+ __block Node selectorsNode;
+ selectorHashTable->forEachString(selectorImages,
+ ^(uint64_t selVMOffset, ImageNum imageNum) {
+ // Convert to a target we can get a real name for
+ dyld3::closure::Image::ResolvedSymbolTarget target;
+ target.image.kind = dyld3::closure::Image::ResolvedSymbolTarget::kindImage;
+ target.image.imageNum = imageNum;
+ target.image.offset = selVMOffset;
+
+ Node targetNode;
+ targetNode.value = printTarget(imagesArrays, target);
+ selectorsNode.array.push_back(targetNode);
+ });
+
+ root.map["objc-selectors"] = selectorsNode;
+ }
+
+ Array<Image::ObjCClassImage> classImages;
+ const ObjCClassOpt* classHashTable = nullptr;
+ const ObjCClassOpt* protocolHashTable = nullptr;
+ if (closure->classAndProtocolHashTables(classImages, classHashTable, protocolHashTable)) {
+ if ( classHashTable != nullptr ) {
+ __block Node classesNode;
+
+ classHashTable->forEachClass(classImages,
+ ^(uint64_t classNameVMOffset, ImageNum imageNum) {
+ // Convert to a target we can get a real name for
+ dyld3::closure::Image::ResolvedSymbolTarget target;
+ target.image.kind = dyld3::closure::Image::ResolvedSymbolTarget::kindImage;
+ target.image.imageNum = imageNum;
+ target.image.offset = classNameVMOffset;
+
+ Node targetNode;
+ targetNode.value = printTarget(imagesArrays, target);
+
+ Node classNode;
+ classNode.map["name"] = targetNode;
+ classesNode.array.push_back(classNode);
+ },
+ ^(uint64_t classVMOffset, ImageNum imageNum) {
+ dyld3::closure::Image::ResolvedSymbolTarget implTarget;
+ implTarget.image.kind = dyld3::closure::Image::ResolvedSymbolTarget::kindImage;
+ implTarget.image.imageNum = imageNum;
+ implTarget.image.offset = classVMOffset;
+
+ Node implNode;
+ implNode.value = printTarget(imagesArrays, implTarget);
+ classesNode.array.back().map["implementations"].array.push_back(implNode);
+ });
+
+ root.map["objc-classes"] = classesNode;
+ }
+
+ if ( protocolHashTable != nullptr ) {
+ __block Node protocolsNode;
+
+ protocolHashTable->forEachClass(classImages,
+ ^(uint64_t protocolNameVMOffset, ImageNum imageNum) {
+ // Convert to a target we can get a real name for
+ dyld3::closure::Image::ResolvedSymbolTarget target;
+ target.image.kind = dyld3::closure::Image::ResolvedSymbolTarget::kindImage;
+ target.image.imageNum = imageNum;
+ target.image.offset = protocolNameVMOffset;
+
+ Node targetNode;
+ targetNode.value = printTarget(imagesArrays, target);
+
+ Node protocolNode;
+ protocolNode.map["name"] = targetNode;
+ protocolsNode.array.push_back(protocolNode);
+ },
+ ^(uint64_t protocolVMOffset, ImageNum imageNum) {
+ dyld3::closure::Image::ResolvedSymbolTarget implTarget;
+ implTarget.image.kind = dyld3::closure::Image::ResolvedSymbolTarget::kindImage;
+ implTarget.image.imageNum = imageNum;
+ implTarget.image.offset = protocolVMOffset;
+
+ Node implNode;
+ implNode.value = printTarget(imagesArrays, implTarget);
+ protocolsNode.array.back().map["implementations"].array.push_back(implNode);
+ });
+
+ root.map["objc-protocols"] = protocolsNode;
+ }
+ }
+
+ const ObjCClassDuplicatesOpt* duplicateClassesHashTable = nullptr;
+ closure->duplicateClassesHashTable(duplicateClassesHashTable);
+ if ( duplicateClassesHashTable != nullptr ) {
+ __block Node duplicateClassesNode;
+ duplicateClassesHashTable->forEachClass(^(Image::ObjCDuplicateClass duplicateClass) {
+ objc_opt::objc_clsopt_t* clsOpt = dyldCache->objcOpt()->clsopt();
+ const char* className = clsOpt->getClassNameForIndex(duplicateClass.sharedCacheClassOptIndex);
+ const void* classImpl = clsOpt->getClassForIndex(duplicateClass.sharedCacheClassOptIndex, duplicateClass.sharedCacheClassDuplicateIndex);
+
+ // Convert to a target we can get a real name for
+ dyld3::closure::Image::ResolvedSymbolTarget target;
+ target.sharedCache.kind = dyld3::closure::Image::ResolvedSymbolTarget::kindSharedCache;
+ target.sharedCache.offset = (uint64_t)classImpl - (uint64_t)dyldCache;
+
+ Node targetNode;
+ targetNode.value = printTarget(imagesArrays, target);
+
+ Node duplicateClassNode;
+ duplicateClassNode.map["name"].value = className;
+ duplicateClassNode.map["implementation"] = targetNode;
+ duplicateClassNode.array.push_back(targetNode);
+
+ duplicateClassesNode.array.push_back(duplicateClassNode);
+ });
+
+ root.map["objc-duplicate-classes"] = duplicateClassesNode;
+ }
+
+ // add warnings for objc if they exist
+ closure->forEachWarning(Closure::Warning::duplicateObjCClass, ^(const char *warning, bool &stop) {
+ Node warningNode;
+ warningNode.value = warning;
+ root.map["objc-duplicate-class-warnings"].array.push_back(warningNode);
+ });
+
+ // add program vars info for old macOS binaries
+ uint32_t progVarsOffset;
+ if ( closure->hasProgramVars(progVarsOffset) )
+ root.map["program-vars-offset"].value = hex8(progVarsOffset);
+
+#if 0
+
// add uuid of dyld cache this closure requires
closure.dyldCacheUUID();
return root;
}
-void printImageAsJSON(const Image* image, const Array<const ImageArray*>& imagesArrays, bool printFixups, FILE* out)
+void printImageAsJSON(const Image* image, const Array<const ImageArray*>& imagesArrays,
+ bool printFixups, bool printRaw, const DyldSharedCache* dyldCache, std::ostream& out)
{
- Node root = buildImageNode(image, imagesArrays, printFixups, false);
+ Node root = buildImageNode(image, imagesArrays, printFixups, false, printRaw, dyldCache, nullptr, {});
printJSON(root, 0, out);
}
-void printDyldCacheImagesAsJSON(const DyldSharedCache* dyldCache, bool printFixups, FILE* out)
+void printDyldCacheImagesAsJSON(const DyldSharedCache* dyldCache, bool printFixups, bool printRaw, std::ostream& out)
{
const dyld3::closure::ImageArray* dylibs = dyldCache->cachedDylibsImageArray();
STACK_ALLOC_ARRAY(const ImageArray*, imagesArrays, 2);
imagesArrays.push_back(dylibs);
- Node root = buildImageArrayNode(dylibs, imagesArrays, printFixups, false, (uint8_t*)dyldCache);
+ Node root = buildImageArrayNode(dylibs, imagesArrays, printFixups, false, printRaw, dyldCache, nullptr, {});
printJSON(root, 0, out);
}
-void printClosureAsJSON(const LaunchClosure* cls, const Array<const ImageArray*>& imagesArrays, bool printFixups, FILE* out)
+void printClosureAsJSON(const LaunchClosure* cls, const Array<const ImageArray*>& imagesArrays,
+ bool printFixups, bool printRaw, const DyldSharedCache* dyldCache, std::ostream& out)
{
- Node root = buildClosureNode(cls, imagesArrays, printFixups, false);
+ Node root = buildClosureNode(cls, imagesArrays, printFixups, false, printRaw, dyldCache);
printJSON(root, 0, out);
}
-void printClosureAsJSON(const DlopenClosure* cls, const Array<const ImageArray*>& imagesArrays, bool printFixups, FILE* out)
+void printClosureAsJSON(const DlopenClosure* cls, const Array<const ImageArray*>& imagesArrays,
+ bool printFixups, bool printRaw, const DyldSharedCache* dyldCache, std::ostream& out)
{
- Node root = buildClosureNode(cls, imagesArrays, printFixups, false);
+ Node root = buildClosureNode(cls, imagesArrays, printFixups, printRaw, false);
printJSON(root, 0, out);
}