X-Git-Url: https://git.saurik.com/apple/dyld.git/blobdiff_plain/9d871a63e06bad796231b04bbb597a7e6847f827..refs/heads/master:/dyld3/ClosurePrinter.cpp diff --git a/dyld3/ClosurePrinter.cpp b/dyld3/ClosurePrinter.cpp index a00fd7e..e73c9ad 100644 --- a/dyld3/ClosurePrinter.cpp +++ b/dyld3/ClosurePrinter.cpp @@ -30,6 +30,8 @@ #include "ClosurePrinter.h" #include "JSONWriter.h" +#include "objc-shared-cache.h" + using namespace dyld3::json; namespace dyld3 { @@ -47,7 +49,7 @@ static std::string printTarget(const Array& imagesArrays, Ima 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); @@ -62,8 +64,108 @@ static std::string printTarget(const Array& imagesArrays, Ima 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& imagesArrays, bool printFixups, bool printDependentsDetails, const uint8_t* cacheStart=nullptr) +static Node buildImageNode(const Image* image, const Array& imagesArrays, bool printFixups, + bool printDependentsDetails, bool printRaw, + const DyldSharedCache* dyldCache, const closure::ObjCSelectorOpt* selOpt, + const Array& selImages) { __block Node imageNode; @@ -72,6 +174,20 @@ static Node buildImageNode(const Image* image, const Array& i 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; @@ -90,7 +206,7 @@ static Node buildImageNode(const Image* image, const Array& i 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() ) { @@ -112,8 +228,7 @@ static Node buildImageNode(const Image* image, const Array& i 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) { @@ -129,46 +244,51 @@ static Node buildImageNode(const Image* image, const Array& 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) { @@ -177,14 +297,44 @@ static Node buildImageNode(const Image* image, const Array& i }, ^(uint64_t imageOffsetToBind, Image::ResolvedSymbolTarget target, bool &stop) { // bind imageNode.map["fixups"].map[hex8(imageOffsetToBind)].value = printTarget(imagesArrays, target); - }, ^(uint64_t imageOffsetStart, const Array& targets, bool& stop) { + }, ^(uint64_t startsStructImageOffset, const Array& 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 @@ -196,24 +346,27 @@ static Node buildImageNode(const Image* image, const Array& i } 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); - }); + } } } @@ -242,12 +395,19 @@ static Node buildImageNode(const Image* image, const Array& i }); // 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; @@ -257,11 +417,22 @@ static Node buildImageNode(const Image* image, const Array& i 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 @@ -290,25 +461,31 @@ static Node buildImageNode(const Image* image, const Array& i } -static Node buildImageArrayNode(const ImageArray* imageArray, const Array& imagesArrays, bool printFixups, bool printDependentsDetails, const uint8_t* cacheStart=nullptr) +static Node buildImageArrayNode(const ImageArray* imageArray, const Array& imagesArrays, + bool printFixups, bool printDependentsDetails, bool printRaw, + const DyldSharedCache* dyldCache, const closure::ObjCSelectorOpt* selOpt, + const Array& 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& imagesArrays, bool printFixups, bool printDependentsDetails) +static Node buildClosureNode(const DlopenClosure* closure, const Array& 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); }); @@ -316,15 +493,37 @@ static Node buildClosureNode(const DlopenClosure* closure, const Array& imagesArrays, bool printFixups, bool printDependentsDetails) +static Node buildClosureNode(const LaunchClosure* closure, const Array& imagesArrays, + bool printFixups, bool printDependentsDetails, bool printRaw, + const DyldSharedCache* dyldCache) { __block Node root; - root.map["images"] = buildImageArrayNode(closure->images(), imagesArrays, printFixups, printDependentsDetails); + + Array 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); }); @@ -349,6 +548,15 @@ static Node buildClosureNode(const LaunchClosure* closure, const ArrayforEachSkipIfExistsFile(^(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; @@ -357,19 +565,8 @@ static Node buildClosureNode(const LaunchClosure* closure, const ArrayforEachPatchEntry(^(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, '='); @@ -381,6 +578,135 @@ static Node buildClosureNode(const LaunchClosure* closure, const ArrayforEachString(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 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(); @@ -417,31 +743,34 @@ static Node buildClosureNode(const LaunchClosure* closure, const Array& imagesArrays, bool printFixups, FILE* out) +void printImageAsJSON(const Image* image, const Array& 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& imagesArrays, bool printFixups, FILE* out) +void printClosureAsJSON(const LaunchClosure* cls, const Array& 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& imagesArrays, bool printFixups, FILE* out) +void printClosureAsJSON(const DlopenClosure* cls, const Array& 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); }