LaunchCachePrinter.cpp [plain text]
#include <string.h>
#include <string>
#include <map>
#include <vector>
#include "LaunchCache.h"
#include "LaunchCacheFormat.h"
#if !DYLD_IN_PROCESS
namespace dyld3 {
namespace launch_cache {
struct Node
{
std::string value;
std::map<std::string, Node> map;
std::vector<Node> array;
};
static std::string hex(uint64_t value) {
char buff[64];
sprintf(buff, "0x%llX", value);
return buff;
}
static std::string hex5(uint64_t value) {
char buff[64];
sprintf(buff, "0x%05llX", value);
return buff;
}
static std::string decimal(uint64_t value) {
char buff[64];
sprintf(buff, "%llu", value);
return buff;
}
static Node buildImageNode(const Image& image, const ImageGroupList& groupList, bool printFixups, bool printDependentsDetails)
{
__block Node imageNode;
if ( image.isInvalid() )
return imageNode;
const ImageGroup group = image.group();
imageNode.map["path"].value = image.path();
__block Node imageAliases;
group.forEachAliasOf(group.indexInGroup(image.binaryData()), ^(const char* aliasPath, uint32_t aliasPathHash, bool& stop) {
Node anAlias;
anAlias.value = aliasPath;
imageAliases.array.push_back(anAlias);
});
if ( !imageAliases.array.empty() )
imageNode.map["aliases"] = imageAliases;
uuid_string_t uuidStr;
uuid_unparse(*image.uuid(), uuidStr);
imageNode.map["uuid"].value = uuidStr;
imageNode.map["has-objc"].value = (image.hasObjC() ? "true" : "false");
imageNode.map["has-weak-defs"].value = (image.hasWeakDefs() ? "true" : "false");
imageNode.map["never-unload"].value = (image.neverUnload() ? "true" : "false");
imageNode.map["platform-binary"].value = (image.isPlatformBinary() ? "true" : "false");
if ( group.groupNum() == 0 )
imageNode.map["overridable-dylib"].value = (image.overridableDylib() ? "true" : "false");
if ( image.cwdMustBeThisDir() )
imageNode.map["cwd-must-be-this-dir"].value = "true";
if ( image.isDiskImage() ) {
uint32_t csFileOffset;
uint32_t csSize;
if ( image.hasCodeSignature(csFileOffset, csSize) ) {
imageNode.map["code-sign-location"].map["offset"].value = hex(csFileOffset);
imageNode.map["code-sign-location"].map["size"].value = hex(csSize);
}
uint32_t fpTextOffset;
uint32_t fpSize;
if ( image.isFairPlayEncrypted(fpTextOffset, fpSize) ) {
imageNode.map["fairplay-encryption-location"].map["offset"].value = hex(fpTextOffset);
imageNode.map["fairplay-encryption-location"].map["size"].value = hex(fpSize);
}
if ( image.validateUsingModTimeAndInode() ) {
imageNode.map["file-mod-time"].value = hex(image.fileModTime());
imageNode.map["file-inode"].value = hex(image.fileINode());
}
else {
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);
}
imageNode.map["file-cd-hash-16"].value = cdHashStr;
}
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) {
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);
});
if ( printFixups ) {
image.forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool &segStop) {
MemoryRange segContent = { nullptr, vmSize };
std::string segName = "segment-" + decimal(segIndex);
__block Node segmentFixupsNode;
image.forEachFixup(segIndex, segContent, ^(uint64_t segOffset, Image::FixupKind kind, TargetSymbolValue value, bool& stop) {
switch ( kind ) {
case Image::FixupKind::rebase32:
segmentFixupsNode.map[segName].map[hex5(segOffset)].value = "32-bit rebase";
break;
case Image::FixupKind::rebase64:
segmentFixupsNode.map[segName].map[hex5(segOffset)].value = "64-bit rebase";
break;
case Image::FixupKind::rebaseText32 :
segmentFixupsNode.map[segName].map[hex5(segOffset)].value = "32-bit text rebase";
break;
case Image::FixupKind::bind32:
segmentFixupsNode.map[segName].map[hex5(segOffset)].value = std::string("32-bit bind, target=") + value.asString(group);
break;
case Image::FixupKind::bind64:
segmentFixupsNode.map[segName].map[hex5(segOffset)].value = std::string("64-bit bind, target=") + value.asString(group);
break;
case Image::FixupKind::bindText32 :
segmentFixupsNode.map[segName].map[hex5(segOffset)].value = std::string("32-bit text abs bind, target=") + value.asString(group);
break;
case Image::FixupKind::bindTextRel32 :
segmentFixupsNode.map[segName].map[hex5(segOffset)].value = std::string("32-bit text rel bind, target=") + value.asString(group);
break;
case Image::FixupKind::bindImportJmp32 :
segmentFixupsNode.map[segName].map[hex5(segOffset)].value = std::string("32-bit IMPORT JMP rel bind, target=") + value.asString(group);
break;
}
});
if ( segmentFixupsNode.map[segName].map.size() != 0 ) {
imageNode.map["fixups"].array.push_back(segmentFixupsNode);
}
});
}
}
else {
imageNode.map["patch-start-index"].value = decimal(image.patchStartIndex());
imageNode.map["patch-count"].value = decimal(image.patchCount());
}
image.forEachDependentImage(groupList, ^(uint32_t depIndex, Image depImage, Image::LinkKind kind, bool& stop) {
Node depMapNode;
depMapNode.map["path"].value = depImage.path();
if ( printDependentsDetails ) {
ImageGroup depGroup = depImage.group();
uint32_t indexInGroup = depGroup.indexInGroup(depImage.binaryData());
depMapNode.map["group-index"].value = decimal(depGroup.groupNum());
depMapNode.map["index-in-group"].value = decimal(indexInGroup);
}
switch ( kind ) {
case Image::LinkKind::regular:
depMapNode.map["link"].value = "regular";
break;
case Image::LinkKind::reExport:
depMapNode.map["link"].value = "re-export";
break;
case Image::LinkKind::upward:
depMapNode.map["link"].value = "upward";
break;
case Image::LinkKind::weak:
depMapNode.map["link"].value = "weak";
break;
}
imageNode.map["dependents"].array.push_back(depMapNode);
});
__block Node initBeforeNode;
image.forEachInitBefore(groupList, ^(Image beforeImage) {
Node beforeNode;
beforeNode.value = beforeImage.path();
imageNode.map["initializer-order"].array.push_back(beforeNode);
});
image.forEachInitializer(nullptr, ^(const void* initializer) {
Node initNode;
initNode.value = hex((long)initializer);
imageNode.map["initializer-offsets"].array.push_back(initNode);
});
group.forEachImageRefOverride(groupList, ^(Image standardDylib, Image overrideDylib, bool& stop) {
if ( overrideDylib.binaryData() == image.binaryData() ) {
imageNode.map["override-of-cached-dylib"].value = standardDylib.path();
}
});
image.forEachDOF(nullptr, ^(const void* section) {
Node initNode;
initNode.value = hex((long)section);
imageNode.map["dof-offsets"].array.push_back(initNode);
});
return imageNode;
}
static Node buildImageGroupNode(const ImageGroup& group, const ImageGroupList& groupList, bool printFixups, bool printDependentsDetails)
{
Node images;
uint32_t imageCount = group.imageCount();
images.array.reserve(imageCount);
for (uint32_t i=0; i < imageCount; ++i) {
images.array.push_back(buildImageNode(group.image(i), groupList, printFixups, printDependentsDetails));
}
return images;
}
static Node buildClosureNode(const Closure& closure, const ImageGroupList& groupList, bool printFixups, bool printDependentsDetails)
{
__block Node root;
closure.forEachEnvVar(^(const char* keyEqualValue, bool& stop) {
const char* equ = strchr(keyEqualValue, '=');
if ( equ != nullptr ) {
char key[512];
strncpy(key, keyEqualValue, equ-keyEqualValue);
key[equ-keyEqualValue] = '\0';
root.map["env-vars"].map[key].value = equ+1;
}
});
closure.forEachMustBeMissingFile(^(const char* path, bool& stop) {
Node fileNode;
fileNode.value = path;
root.map["must-be-missing-files"].array.push_back(fileNode);
});
const uint8_t* cdHash = closure.cdHash();
std::string cdHashStr;
cdHashStr.reserve(24);
for (int i=0; i < 20; ++i) {
uint8_t byte = cdHash[i];
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" )
root.map["cd-hash"].value = cdHashStr;
closure.dyldCacheUUID();
uuid_string_t cacheUuidStr;
uuid_unparse(*closure.dyldCacheUUID(), cacheUuidStr);
root.map["dyld-cache-uuid"].value = cacheUuidStr;
Node& rootImages = root.map["root-images"];
uint32_t initImageCount = closure.mainExecutableImageIndex();
rootImages.array.resize(initImageCount+1);
for (uint32_t i=0; i <= initImageCount; ++i) {
const Image image = closure.group().image(i);
uuid_string_t uuidStr;
uuid_unparse(*image.uuid(), uuidStr);
rootImages.array[i].value = uuidStr;
}
root.map["initial-image-count"].value = decimal(closure.initialImageCount());
root.map["images"] = buildImageGroupNode(closure.group(), groupList, printFixups, printDependentsDetails);
root.map["group-num"].value = decimal(closure.group().groupNum());
if ( closure.mainExecutableUsesCRT() )
root.map["main-offset"].value = hex(closure.mainExecutableEntryOffset());
else
root.map["start-offset"].value = hex(closure.mainExecutableEntryOffset());
root.map["libdyld-entry-offset"].value = hex(closure.libdyldVectorOffset());
root.map["restricted"].value = (closure.isRestricted() ? "true" : "false");
root.map["library-validation"].value = (closure.usesLibraryValidation() ? "true" : "false");
__block Node cacheOverrides;
closure.group().forEachDyldCacheSymbolOverride(^(uint32_t patchTableIndex, uint32_t imageIndexInClosure, uint32_t imageOffset, bool& stop) {
Node patch;
patch.map["patch-index"].value = decimal(patchTableIndex);
patch.map["replacement"].value = "{closure[" + decimal(imageIndexInClosure) + "]+" + hex(imageOffset) + "}";
cacheOverrides.array.push_back(patch);
});
if ( !cacheOverrides.array.empty() )
root.map["dyld-cache-overrides"].array = cacheOverrides.array;
return root;
}
static void indentBy(uint32_t spaces, FILE* out) {
for (int i=0; i < spaces; ++i) {
fprintf(out, " ");
}
}
static void printJSON(const Node& node, uint32_t indent, FILE* out)
{
if ( !node.map.empty() ) {
fprintf(out, "{");
bool needComma = false;
for (const auto& entry : node.map) {
if ( needComma )
fprintf(out, ",");
fprintf(out, "\n");
indentBy(indent+2, out);
fprintf(out, "\"%s\": ", entry.first.c_str());
printJSON(entry.second, indent+2, out);
needComma = true;
}
fprintf(out, "\n");
indentBy(indent, out);
fprintf(out, "}");
}
else if ( !node.array.empty() ) {
fprintf(out, "[");
bool needComma = false;
for (const auto& entry : node.array) {
if ( needComma )
fprintf(out, ",");
fprintf(out, "\n");
indentBy(indent+2, out);
printJSON(entry, indent+2, out);
needComma = true;
}
fprintf(out, "\n");
indentBy(indent, out);
fprintf(out, "]");
}
else {
fprintf(out, "\"%s\"", node.value.c_str());
}
if ( indent == 0 )
fprintf(out, "\n");
}
void Image::printAsJSON(const ImageGroupList& groupList, bool printFixups, bool printDependentsDetails, FILE* out) const
{
Node image = buildImageNode(*this, groupList, printFixups, printDependentsDetails);
printJSON(image, 0, out);
}
void ImageGroup::printAsJSON(const ImageGroupList& groupList, bool printFixups, bool printDependentsDetails, FILE* out) const
{
Node root;
root.map["images"] = buildImageGroupNode(*this, groupList, printFixups, printDependentsDetails);
root.map["group-num"].value = decimal(groupNum());
root.map["dylibs-expected-on-disk"].value = (dylibsExpectedOnDisk() ? "true" : "false");
printJSON(root, 0, out);
}
void ImageGroup::printStatistics(FILE* out) const
{
__block uint32_t totalRebases = 0;
__block uint32_t totalBinds = 0;
for (uint32_t i=0; i < imageCount(); ++i) {
Image img(image(i));
img.forEachDiskSegment(^(uint32_t segIndex, uint32_t fileOffset, uint32_t fileSize, int64_t vmOffset, uint64_t vmSize, uint8_t permissions, bool &segStop) {
MemoryRange segContent = { nullptr, vmSize };
img.forEachFixup(segIndex, segContent, ^(uint64_t segOffset, Image::FixupKind kind, TargetSymbolValue value, bool& stop) {
if ( kind == Image::FixupKind::rebase64 )
++totalRebases;
else
++totalBinds;
});
});
}
fprintf(out, "ImageGroup:\n");
fprintf(out, " image-count: % 5d\n", _binaryData->imagesPoolCount);
fprintf(out, " alias-count: % 5d\n", _binaryData->imageAliasCount);
fprintf(out, " segments-count: % 5d\n", _binaryData->segmentsPoolCount);
fprintf(out, " dependents-count: % 5d\n", _binaryData->dependentsPoolCount);
fprintf(out, " targets-count: % 5d\n", _binaryData->targetsPoolCount);
fprintf(out, " rebase-count: % 5d\n", totalRebases);
fprintf(out, " bind-count: % 5d\n", totalBinds);
fprintf(out, " fixups-size: % 8d bytes\n", _binaryData->fixupsPoolSize);
fprintf(out, " targets-size: % 8ld bytes\n", _binaryData->targetsPoolCount * sizeof(uint64_t));
fprintf(out, " strings-size: % 8d bytes\n", _binaryData->stringsPoolSize);
fprintf(out, " dofs-size: % 8ld bytes\n", _binaryData->dofOffsetPoolCount * sizeof(uint32_t));
fprintf(out, " indirect-groups-size: % 8ld bytes\n", _binaryData->indirectGroupNumPoolCount * sizeof(uint32_t));
}
void Closure::printAsJSON(const ImageGroupList& groupList, bool printFixups, bool printDependentsDetails, FILE* out) const
{
Node root = buildClosureNode(*this, groupList, printFixups, printDependentsDetails);
printJSON(root, 0, out);
}
void Closure::printStatistics(FILE* out) const
{
fprintf(out, "closure size: %lu\n", size());
group().printStatistics(out);
}
} }
#endif