Loading...
--- dyld/dyld-1340/framework/CacheScavenger.cpp
+++ /dev/null
@@ -1,277 +0,0 @@
-/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- vim: ft=cpp et ts=4 sw=4:
- *
- * Copyright (c) 2025 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
-
-#include <fcntl.h>
-#include <stdio.h>
-#include <libgen.h>
-#include <unistd.h>
-#include <sys/stat.h>
-#include <uuid/uuid.h>
-
-#include "AAREncoder.h"
-#include "Allocator.h"
-#include "PropertyList.h"
-#include "SnapshotShared.h"
-#include "Header.h"
-#include "DyldSharedCache.h"
-#include "Vector.h"
-
-#include "ProcessScavenger.h"
-
-#if TARGET_OS_OSX
-namespace {
-struct CacheMapping {
- void* address;
- size_t fileSize;
- size_t vmSize;
- uint64_t preferredLoadAddress;
-};
-void addSubCacheFileInfo(uint64_t cacheVMAddress, PropertyList::Array &files, dyld_cache_header* subcacheHeader, CacheMapping& cacheMapping, std::string fileName) {
- using Array = PropertyList::Array;
- using Integer = PropertyList::Integer;
- using String = PropertyList::String;
- using Dictionary = PropertyList::Dictionary;
-
- auto& subCacheFile = files.addObject<Dictionary>();
-
- subCacheFile.addObjectForKey<String>("name", fileName);
- subCacheFile.addObjectForKey<PropertyList::UUID>(kDyldAtlasSharedCacheUUIDKey,subcacheHeader->uuid);
- subCacheFile.addObjectForKey<Integer>("voff",subcacheHeader->sharedRegionStart-cacheVMAddress);
- subCacheFile.addObjectForKey<Integer>("fsze", cacheMapping.fileSize);
- subCacheFile.addObjectForKey<Integer>("padr", subcacheHeader->sharedRegionStart);
- auto& mappingsArray = subCacheFile.addObjectForKey<Array>(kDyldAtlasSharedCacheMappingArrayKey);
- auto* mappings = (dyld_cache_mapping_info*)((uint8_t*)subcacheHeader + subcacheHeader->mappingOffset);
-
- uint64_t lastAddress = 0;
- for ( auto i = 0; i < subcacheHeader->mappingCount; ++i) {
- auto& mapping = mappingsArray.addObject<Dictionary>();
- mapping.addObjectForKey<Integer>(kDyldAtlasSharedCacheMappingsSizeKey, mappings[i].size);
- mapping.addObjectForKey<Integer>(kDyldAtlasSharedCacheMappingsPreferredLoadAddressKey, mappings[i].address);
- mapping.addObjectForKey<Integer>(kDyldAtlasSharedCacheMappingsFileOffsetKey, mappings[i].fileOffset);
- mapping.addObjectForKey<Integer>(kDyldAtlasSharedCacheMappingsMaxProtKey, mappings[i].maxProt);
- if (mappings[i].address + mappings[i].size > lastAddress) {
- lastAddress = mappings[i].address + mappings[i].size;
- }
- }
- cacheMapping.vmSize = lastAddress-subcacheHeader->sharedRegionStart;
- subCacheFile.addObjectForKey<Integer>("size", cacheMapping.vmSize);
-}
-
-static
-CacheMapping mapFile(std::string dir, std::string name) {
- std::string fullPath = dir + "/" + name;
- int fd = open(fullPath.c_str(), O_RDONLY);
- if (fd < 0) {
- return { nullptr, 0, 0 };
- }
- struct stat stat_buf;
- if (fstat(fd, &stat_buf) != 0) {
- close(fd);
- return { nullptr, 0, 0 };
- }
- void* mapping = mmap(nullptr, stat_buf.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
- close(fd);
- if (mapping == MAP_FAILED) {
- return { nullptr, 0 };
- }
- dyld_cache_header* cacheHeader = (dyld_cache_header*)mapping;
-
- // Validate that this is a cache file
- if ( strncmp(cacheHeader->magic, "dyld_v1", 7) != 0 ) {
- munmap(mapping, stat_buf.st_size);
- return { nullptr, 0 };
- }
- return { mapping, static_cast<size_t>(stat_buf.st_size), 0, cacheHeader->sharedRegionStart };
-}
-
-static
-void unmapFile(CacheMapping& mapping) {
- if (mapping.address == nullptr) { return; }
- if (mapping.fileSize == 0) { return; }
- munmap(mapping.address, mapping.fileSize);
-}
-
-static
-std::span<const dyld_cache_image_info> cacheImageInfos(dyld_cache_header* header) {
- if ( header->mappingOffset >= offsetof(dyld_cache_header, imagesCount) ) {
- dyld_cache_image_info* start = (dyld_cache_image_info*)((char*)header + header->imagesOffset);
- dyld_cache_image_info* end = &start[header->imagesCount];
- return { start, end };
- }
- dyld_cache_image_info* start = (dyld_cache_image_info*)((char*)header + header->imagesOffsetOld);
- dyld_cache_image_info* end = &start[header->imagesCount];
- return { start, end };
-}
-
-static
-std::span<const dyld_cache_image_text_info> cacheTextImageSegments(dyld_cache_header* header)
-{
- // check for old cache without imagesText array
- if ( (header->mappingOffset <= offsetof(dyld_cache_header, imagesTextOffset)) || (header->imagesTextCount == 0) )
- return { };
-
- const dyld_cache_image_text_info* imagesText = (dyld_cache_image_text_info*)((char*)header + header->imagesTextOffset);
- const dyld_cache_image_text_info* imagesTextEnd = &imagesText[header->imagesTextCount];
- return { imagesText, imagesTextEnd };
-}
-
-static
-void scavengeCache(const char* path, ByteStream& byteStream) {
- char buffer[MAXPATHLEN];
- const char* dir = strdup(dirname_r(path, buffer));
- const char* basename = strdup(basename_r(path, buffer));
- std::vector<CacheMapping> cacheMappings;
-
- STACK_ALLOCATOR(allocator, 0);
- using Array = PropertyList::Array;
- using Dictionary = PropertyList::Dictionary;
- using Integer = PropertyList::Integer;
- using String = PropertyList::String;
- auto propertyListEncoder = PropertyList(allocator);
- auto& rootDictionary = propertyListEncoder.rootDictionary();
- // The same plist contains both the customer and shared cache data, since they share layouts
- // We include dictionaries at the root so they can be lookup by leaf name or UUID
- auto& byUuidDictionary = rootDictionary.addObjectForKey<Dictionary>("uuids");
- auto& byNameDictionary = rootDictionary.addObjectForKey<Dictionary>("names");
- std::string cacheName = basename;
- auto mainCacheMapping = mapFile(dir, basename);
- if (mainCacheMapping.address == nullptr) { return; }
- dyld_cache_header* cacheHeader = (dyld_cache_header*)mainCacheMapping.address;
- uuid_string_t cacheUUID = {0};
- uuid_unparse_upper(cacheHeader->uuid, cacheUUID);
- Dictionary* cacheAtlas = &byUuidDictionary.addObjectForKey<Dictionary>(cacheUUID);
- byNameDictionary.insertObjectForKey(cacheName, *cacheAtlas);
- cacheAtlas->addObjectForKey<PropertyList::UUID>(kDyldAtlasSharedCacheUUIDKey, cacheHeader->uuid);
- cacheAtlas->addObjectForKey<Integer>(kDyldAtlasSharedCachePreferredLoadAddressKey, cacheHeader->sharedRegionStart);
- cacheAtlas->addObjectForKey<Integer>(kDyldAtlasSharedCacheVMSizeKey, cacheHeader->sharedRegionSize);
- if (!uuid_is_null(cacheHeader->symbolFileUUID)) {
- cacheAtlas->addObjectForKey<String>(kDyldAtlasSharedCacheSymbolFileName, cacheName + ".symbols");
- cacheAtlas->addObjectForKey<PropertyList::UUID>(kDyldAtlasSharedCacheSymbolFileName, cacheHeader->symbolFileUUID);
- }
- // We only support scavenging on `macOS`, and all caches on macOS have 8 byte pointers
- cacheAtlas->addObjectForKey<Integer>("psze", 8);
-
- auto& files = cacheAtlas->addObjectForKey<Array>("dscs");
- addSubCacheFileInfo(cacheHeader->sharedRegionStart, files, cacheHeader, mainCacheMapping, cacheName);
- cacheMappings.push_back(mainCacheMapping);
-
- if (cacheHeader->mappingOffset <= offsetof(dyld_cache_header, cacheSubType) ) {
- for (auto i = 0; i < cacheHeader->subCacheArrayCount; ++i) {
- char* fileSuffix = nullptr;
- asprintf(&fileSuffix, "%u", i+1);
- auto subCacheMapping = mapFile(dir, basename + cacheName + fileSuffix);
- dyld_cache_header* subCacheHeader = (dyld_cache_header*)subCacheMapping.address;
- addSubCacheFileInfo(cacheHeader->sharedRegionStart, files, subCacheHeader, subCacheMapping, cacheName + fileSuffix);
- cacheMappings.push_back(subCacheMapping);
- free((void*)fileSuffix);
- }
- } else {
- const dyld_subcache_entry* subCacheEntries = (dyld_subcache_entry*)((uintptr_t)cacheHeader + cacheHeader->subCacheArrayOffset);
- for (auto i = 0; i < cacheHeader->subCacheArrayCount; ++i) {
- auto subCacheMapping = mapFile(dir, cacheName + subCacheEntries[i].fileSuffix);
- dyld_cache_header* subCacheHeader = (dyld_cache_header*)subCacheMapping.address;
- addSubCacheFileInfo(cacheHeader->sharedRegionStart, files, subCacheHeader, subCacheMapping, subCacheEntries[i].fileSuffix);
- cacheMappings.push_back(subCacheMapping);
- }
- }
- Array* images = &cacheAtlas->addObjectForKey<Array>(kDyldAtlasSharedCacheImageArrayKey);
- std::span<const dyld_cache_image_info> cacheImages = cacheImageInfos(cacheHeader);
- std::span<const dyld_cache_image_text_info> cacheTextSegments = cacheTextImageSegments(cacheHeader);
-
- for (auto i = 0; i < cacheImages.size(); ++i ) {
- auto& image = images->addObject<Dictionary>();
- auto& segments = image.addObjectForKey<Array>(kDyldAtlasImageSegmentArrayKey);
- uint64_t imageAddress = cacheImages[i].address;
- image.addObjectForKey<String>(kDyldAtlasImageInstallnameKey, (const char*)cacheHeader + cacheTextSegments[i].pathOffset);
- image.addObjectForKey<Integer>(kDyldAtlasImagePreferredLoadAddressKey, imageAddress);
- uuid_t uuid;
- const char* uuidBegin = (const char*)&cacheTextSegments[i].uuid[0];
- std::copy(uuidBegin, uuidBegin+16, &uuid[0]);
- image.addObjectForKey<PropertyList::UUID>(kDyldAtlasImageUUIDKey, uuid);
- auto mapping = std::find_if(cacheMappings.begin(), cacheMappings.end(), [&](CacheMapping& cacheMapping) {
- uint64_t startAddress = (uint64_t)cacheMapping.preferredLoadAddress;
- uint64_t endAddress = startAddress + cacheMapping.vmSize;
- if (imageAddress < startAddress) { return false; }
- if (imageAddress >= endAddress) { return false; }
- return true;
- });
- assert(mapping != cacheMappings.end());
- uint64_t subcacheImageOffset = imageAddress - mapping->preferredLoadAddress;
- uint8_t* machHeaderAddress = (uint8_t*)mapping->address + subcacheImageOffset;
- std::span<uint8_t> machHeaderSpan = std::span(machHeaderAddress, (uint8_t*)mapping->address + mapping->fileSize);
- const mach_o::Header* mh = mach_o::Header::isMachO(machHeaderSpan);
- mh->forEachSegment(^(const mach_o::Header::SegmentInfo &info, bool &stop) {
- auto& segment = segments.addObject<Dictionary>();
- segment.addObjectForKey<String>(kDyldAtlasSegmentNameKey, info.segmentName);
- segment.addObjectForKey<Integer>(kDyldAtlasSegmentPreferredLoadAddressKey, info.vmaddr);
- segment.addObjectForKey<Integer>(kDyldAtlasSegmentSizeKey, info.vmsize);
- segment.addObjectForKey<Integer>(kDyldAtlasSegmentFileOffsetKey, info.fileOffset);
- segment.addObjectForKey<Integer>(kDyldAtlasSegmentFileSizeKey, info.fileSize);
- if ( info.segmentName == "__TEXT" ) {
- segment.addObjectForKey<Integer>(kDyldAtlasSegmentPermissionsKey, VM_PROT_READ | VM_PROT_EXECUTE);
- } else if ( info.segmentName == "__LINKEDIT" ) {
- segment.addObjectForKey<Integer>(kDyldAtlasSegmentPermissionsKey, VM_PROT_READ);
- } else {
- segment.addObjectForKey<Integer>(kDyldAtlasSegmentPermissionsKey, VM_PROT_READ | VM_PROT_WRITE);
- }
- });
- }
-
- for (auto cacheMapping: cacheMappings) {
- unmapFile(cacheMapping);
- }
-
- ByteStream fileStream(allocator);
- propertyListEncoder.encode(fileStream);
- AAREncoder aarEncoder(allocator);
-
- std::string plistPath = std::string("caches/uuids/") + cacheUUID + ".plist";
- std::string symlinkTarget = std::string("../uuids/") + cacheUUID + ".plist";
- std::string symlinkSource = std::string("caches/names/") + cacheName + ".plist";
-
- aarEncoder.addFile(plistPath, fileStream.span());
- aarEncoder.addSymLink(symlinkSource, symlinkTarget);
-
- ByteStream outputStream(allocator);
- aarEncoder.encode(outputStream);
- std::copy(outputStream.begin(), outputStream.end(), std::back_insert_iterator(byteStream));
-}
-};
-#endif
-
-void* scavengeCache(const char* path, uint64_t* bufferSize) {
-#if TARGET_OS_OSX
- STACK_ALLOCATOR(allocator, 0);
- ByteStream outputStream(allocator);
- scavengeCache(path, outputStream);
- *bufferSize = outputStream.size();
- if (*bufferSize == 0) { return nullptr; }
- std::byte* buffer = (std::byte*)malloc((size_t)(*bufferSize));
- std::copy(outputStream.begin(), outputStream.end(), (std::byte*)buffer);
- return (void*)buffer;
-#else
- *bufferSize = 0;
- return nullptr;
-#endif
-}