From 3d203ce79ac620e8b7c29e5cf30f51bef5335011 Mon Sep 17 00:00:00 2001 From: Mark Rowe Date: Sat, 8 Feb 2025 10:40:44 -0800 Subject: [PATCH] [SharedCache] Make MetadataSerializable::Load static `MetadataSerializable` is updated to allow a subclass to optionally specify the return type of `Load`. If not specified it defaults to the subclass itself. `SharedCache` specifies `std::optional` to represent the case where the serialized metadata is in a format or version it does not recognize. By making `Load` a static member function and having it return a new object it becomes easier to reason about the state of objects when a deserialization failure occurs. It can return `std::optional` to indicate failure and there is no object left in an unknown state. Prior to this, `Load` worked on an existing instance of an object. It was unclear what state the object would be in if a deserialization failure occurred (its original state prior to `Load`? some partially-loaded state? a null state?). The failure itself also had to be communicated out of band. --- view/sharedcache/api/sharedcache.cpp | 9 +- view/sharedcache/api/sharedcacheapi.h | 70 +++---- .../sharedcache/core/MetadataSerializable.hpp | 40 ++-- view/sharedcache/core/SharedCache.cpp | 177 ++++++++---------- view/sharedcache/core/SharedCache.h | 159 ++++++++-------- 5 files changed, 209 insertions(+), 246 deletions(-) diff --git a/view/sharedcache/api/sharedcache.cpp b/view/sharedcache/api/sharedcache.cpp index e764531cd2..472bb024ae 100644 --- a/view/sharedcache/api/sharedcache.cpp +++ b/view/sharedcache/api/sharedcache.cpp @@ -204,9 +204,8 @@ namespace SharedCacheAPI { BNFreeString(outputStr); if (output.empty()) return {}; - SharedCacheMachOHeader header; - header.LoadFromString(output); - return header; + + return SharedCacheMachOHeader::LoadFromString(output); } std::optional SharedCache::GetMachOHeaderForAddress(uint64_t address) @@ -218,9 +217,7 @@ namespace SharedCacheAPI { BNFreeString(outputStr); if (output.empty()) return {}; - SharedCacheMachOHeader header; - header.LoadFromString(output); - return header; + return SharedCacheMachOHeader::LoadFromString(output); } BNDSCViewState SharedCache::GetState() diff --git a/view/sharedcache/api/sharedcacheapi.h b/view/sharedcache/api/sharedcacheapi.h index cfe7835127..ec64ef10ef 100644 --- a/view/sharedcache/api/sharedcacheapi.h +++ b/view/sharedcache/api/sharedcacheapi.h @@ -211,40 +211,42 @@ namespace SharedCacheAPI { MSS(relocatable); } - void Load(SharedCacheCore::DeserializationContext& context) { - MSL(textBase); - MSL(loadCommandOffset); - MSL_SUBCLASS(ident); - MSL(identifierPrefix); - MSL(installName); - MSL(entryPoints); - MSL(m_entryPoints); - MSL_SUBCLASS(symtab); - MSL_SUBCLASS(dysymtab); - MSL_SUBCLASS(dyldInfo); - // MSL_SUBCLASS(routines64); // FIXME CRASH but also do we even use this? - MSL_SUBCLASS(functionStarts); - MSL_SUBCLASS(moduleInitSections); - MSL_SUBCLASS(exportTrie); - MSL_SUBCLASS(chainedFixups); - MSL(relocationBase); - MSL_SUBCLASS(segments); - MSL_SUBCLASS(linkeditSegment); - MSL_SUBCLASS(sections); - MSL(sectionNames); - MSL_SUBCLASS(symbolStubSections); - MSL_SUBCLASS(symbolPointerSections); - MSL(dylibs); - MSL_SUBCLASS(buildVersion); - MSL_SUBCLASS(buildToolVersions); - MSL(exportTriePath); - MSL(dysymPresent); - MSL(dyldInfoPresent); - MSL(exportTriePresent); - MSL(chainedFixupsPresent); - // MSL(routinesPresent); - MSL(functionStartsPresent); - MSL(relocatable); + static SharedCacheMachOHeader Load(SharedCacheCore::DeserializationContext& context) { + return SharedCacheMachOHeader { + .MSL(textBase), + .MSL(loadCommandOffset), + .MSL(ident), + .MSL(identifierPrefix), + .MSL(installName), + .MSL(entryPoints), + .MSL(m_entryPoints), + .MSL(symtab), + .MSL(dysymtab), + .MSL(dyldInfo), + // .MSL(routines64), // FIXME CRASH but also do we even use this? + .MSL(functionStarts), + .MSL(moduleInitSections), + .MSL(exportTrie), + .MSL(chainedFixups), + .MSL(relocationBase), + .MSL(segments), + .MSL(linkeditSegment), + .MSL(sections), + .MSL(sectionNames), + .MSL(symbolStubSections), + .MSL(symbolPointerSections), + .MSL(dylibs), + .MSL(buildVersion), + .MSL(buildToolVersions), + .MSL(exportTriePath), + .MSL(dysymPresent), + .MSL(dyldInfoPresent), + .MSL(exportTriePresent), + .MSL(chainedFixupsPresent), + // .MSL(routinesPresent), + .MSL(functionStartsPresent), + .MSL(relocatable), + }; } }; diff --git a/view/sharedcache/core/MetadataSerializable.hpp b/view/sharedcache/core/MetadataSerializable.hpp index 7b44ccbdbf..88ca9692e9 100644 --- a/view/sharedcache/core/MetadataSerializable.hpp +++ b/view/sharedcache/core/MetadataSerializable.hpp @@ -34,6 +34,10 @@ avoid that. * */ +#ifndef SHAREDCACHE_CORE_METADATASERIALIZABLE_HPP +#define SHAREDCACHE_CORE_METADATASERIALIZABLE_HPP + +#include #include "binaryninjaapi.h" #include "rapidjson/document.h" #include "rapidjson/stringbuffer.h" @@ -41,9 +45,6 @@ #include "../api/sharedcachecore.h" #include "view/macho/machoview.h" -#ifndef SHAREDCACHE_CORE_METADATASERIALIZABLE_HPP -#define SHAREDCACHE_CORE_METADATASERIALIZABLE_HPP - namespace SharedCacheCore { #define MSS(name) context.store(#name, name) @@ -51,7 +52,6 @@ namespace SharedCacheCore { #define MSS_SUBCLASS(name) Serialize(context, #name, name) #define MSL(name) name = context.load(#name) #define MSL_CAST(name, storedType, type) name = (type)context.load(#name) -#define MSL_SUBCLASS(name) Deserialize(context, #name, name) using namespace BinaryNinja; @@ -83,44 +83,34 @@ struct DeserializationContext { } }; -template -class MetadataSerializable -{ +template +class MetadataSerializable { public: - std::string AsString() const - { + std::string AsString() const { SerializationContext context; Store(context); return context.buffer.GetString(); } - void LoadFromString(const std::string& s) - { + static LoadResult LoadFromString(const std::string& s) { DeserializationContext context; - context.doc.Parse(s.c_str()); - AsDerived().Load(context); + rapidjson::ParseResult result = context.doc.Parse(s.c_str()); + assert(result); + return Derived::Load(context); } - void LoadFromValue(rapidjson::Value& s) - { + static LoadResult LoadFromValue(rapidjson::Value& s) { DeserializationContext context; context.doc.CopyFrom(s, context.doc.GetAllocator()); - AsDerived().Load(context); + return Derived::Load(context); } - Ref AsMetadata() { + Ref AsMetadata() const { return new Metadata(AsString()); } - bool LoadFromMetadata(const Ref& meta) - { - if (!meta->IsString()) - return false; - LoadFromString(meta->GetString()); - return true; - } - + template void Store(SerializationContext& context) const { context.writer.StartObject(); AsDerived().Store(context); diff --git a/view/sharedcache/core/SharedCache.cpp b/view/sharedcache/core/SharedCache.cpp index 062031c660..789f52c284 100644 --- a/view/sharedcache/core/SharedCache.cpp +++ b/view/sharedcache/core/SharedCache.cpp @@ -25,15 +25,16 @@ * */ #include "SharedCache.h" + +#include "DSCView.h" #include "ObjC.h" #include #include +#include #include #include #include #include -#include -#include using namespace BinaryNinja; @@ -55,8 +56,9 @@ int count_trailing_zeros(uint64_t value) { } #endif -struct SharedCache::State -{ +namespace SharedCacheCore { + +struct SharedCacheState { std::unordered_map>>> exportInfos; std::unordered_map>>> @@ -78,10 +80,12 @@ struct SharedCache::State std::optional> objcOptimizationDataRange; std::string baseFilePath; - SharedCacheFormat cacheFormat; + SharedCache::SharedCacheFormat cacheFormat; DSCViewState viewState = DSCViewStateUnloaded; }; +} // namespace SharedCacheCore + struct SharedCache::ViewSpecificState { std::mutex typeLibraryMutex; std::unordered_map> typeLibraries; @@ -91,7 +95,7 @@ struct SharedCache::ViewSpecificState { std::atomic progress; std::mutex stateMutex; - std::shared_ptr cachedState; + std::shared_ptr cachedState; }; @@ -993,33 +997,34 @@ std::shared_ptr SharedCache::GetVMMap(bool mapPages) void SharedCache::DeserializeFromRawView() { - if (m_dscView->QueryMetadata(SharedCacheMetadataTag)) - { + if (m_dscView->QueryMetadata(SharedCacheMetadataTag)) { std::lock_guard lock(m_viewSpecificState->stateMutex); - if (m_viewSpecificState->cachedState) - { + if (m_viewSpecificState->cachedState) { m_state = m_viewSpecificState->cachedState; m_stateIsShared = true; m_metadataValid = true; + return; } - else - { - LoadFromString(m_dscView->GetStringMetadata(SharedCacheMetadataTag)); - } - if (!m_metadataValid) - { - m_logger->LogError("Failed to deserialize Shared Cache metadata"); - WillMutateState(); - MutableState().viewState = DSCViewStateUnloaded; + + if (auto state = LoadFromString(m_dscView->GetStringMetadata(SharedCacheMetadataTag))) { + m_state = std::make_shared(std::move(*state)); + m_stateIsShared = false; + m_metadataValid = true; + return; } - } - else - { - m_metadataValid = true; + + m_state = nullptr; + m_metadataValid = false; + m_logger->LogError("Failed to deserialize Shared Cache metadata"); WillMutateState(); MutableState().viewState = DSCViewStateUnloaded; - MutableState().images.clear(); // fixme ?? + return; } + + m_metadataValid = true; + WillMutateState(); + MutableState().viewState = DSCViewStateUnloaded; + MutableState().images.clear(); // fixme ?? } @@ -3026,7 +3031,7 @@ bool SharedCache::SaveToDSCView() // By moving our state the to cache we can avoid creating a copy in the case // that no further mutations are made to `this`. If we're not done being mutated, // the data will be copied on the first mutation. - auto cachedState = std::make_shared(std::move(*m_state)); + auto cachedState = std::make_shared(std::move(*m_state)); m_state = cachedState; m_stateIsShared = true; @@ -3513,54 +3518,44 @@ void SharedCache::Store(SerializationContext& context) const Serialize(context, "nonImageRegions", State().nonImageRegions); } -void SharedCache::Load(DeserializationContext& context) -{ - if (context.doc.HasMember("metadataVersion")) - { - if (context.doc["metadataVersion"].GetUint() != METADATA_VERSION) - { - m_logger->LogError("Shared Cache metadata version mismatch"); - return; - } +// static +std::optional SharedCache::Load(DeserializationContext& context) { + if (!context.doc.HasMember("metadataVersion")) { + LogError("Shared Cache metadata version missing"); + return std::nullopt; } - else - { - m_logger->LogError("Shared Cache metadata version missing"); - return; + + if (context.doc["metadataVersion"].GetUint() != METADATA_VERSION) { + LogError("Shared Cache metadata version mismatch"); + return std::nullopt; } - m_stateIsShared = false; - m_state = std::make_shared(); + SharedCacheState state; - MutableState().viewState = static_cast(context.load("m_viewState")); - MutableState().cacheFormat = static_cast(context.load("m_cacheFormat")); + state.viewState = static_cast(context.load("m_viewState")); + state.cacheFormat = static_cast(context.load("m_cacheFormat")); - for (auto& startAndHeader : context.doc["headers"].GetArray()) - { - SharedCacheMachOHeader header; - header.LoadFromValue(startAndHeader); - MutableState().headers[header.textBase] = std::move(header); + for (auto& startAndHeader : context.doc["headers"].GetArray()) { + SharedCacheMachOHeader header = SharedCacheMachOHeader::LoadFromValue(startAndHeader); + state.headers[header.textBase] = std::move(header); } - Deserialize(context, "m_imageStarts", MutableState().imageStarts); - Deserialize(context, "m_baseFilePath", MutableState().baseFilePath); + Deserialize(context, "m_imageStarts", state.imageStarts); + Deserialize(context, "m_baseFilePath", state.baseFilePath); - for (const auto& obj1 : context.doc["exportInfos"].GetArray()) - { - std::unordered_map> innerVec; - for (const auto& obj2 : obj1["value"].GetArray()) - { + for (const auto& obj1 : context.doc["exportInfos"].GetArray()) { + std::unordered_map> symbolMap; + for (const auto& obj2 : obj1["value"].GetArray()) { std::string raw = obj2["val2"].GetString(); uint64_t addr = obj2["key"].GetUint64(); - innerVec[addr] = new Symbol(BNCreateSymbol((BNSymbolType)obj2["val1"].GetUint64(), raw.c_str(), + symbolMap[addr] = new Symbol(BNCreateSymbol((BNSymbolType)obj2["val1"].GetUint64(), raw.c_str(), raw.c_str(), raw.c_str(), addr, NoBinding, nullptr, 0)); } - MutableState().exportInfos[obj1["key"].GetUint64()] = std::make_shared>>(innerVec); + state.exportInfos[obj1["key"].GetUint64()] = std::make_shared>>(std::move(symbolMap)); } - for (auto& symbolInfo : context.doc["symbolInfos"].GetArray()) - { + for (auto& symbolInfo : context.doc["symbolInfos"].GetArray()) { std::vector>> symbolInfos; auto symbolInfoArray = symbolInfo["value"].GetArray(); @@ -3570,65 +3565,49 @@ void SharedCache::Load(DeserializationContext& context) symbolInfos.push_back({si["key"].GetUint64(), {static_cast(si["val1"].GetUint64()), si["val2"].GetString()}}); } - MutableState().symbolInfos[symbolInfo["key"].GetUint64()] = std::move(symbolInfos); + state.symbolInfos[symbolInfo["key"].GetUint64()] = std::move(symbolInfos); } - for (auto& bcV : context.doc["backingCaches"].GetArray()) - { - BackingCache bc; - bc.LoadFromValue(bcV); - MutableState().backingCaches.push_back(std::move(bc)); + for (auto& bcV : context.doc["backingCaches"].GetArray()) { + state.backingCaches.push_back(BackingCache::LoadFromValue(bcV)); } - for (auto& imgV : context.doc["images"].GetArray()) - { - CacheImage img; - img.LoadFromValue(imgV); - MutableState().images.push_back(std::move(img)); + for (auto& imgV : context.doc["images"].GetArray()) { + state.images.push_back(CacheImage::LoadFromValue(imgV)); } - for (auto& rV : context.doc["regionsMappedIntoMemory"].GetArray()) - { - MemoryRegion r; - r.LoadFromValue(rV); - MutableState().regionsMappedIntoMemory.push_back(std::move(r)); + for (auto& rV : context.doc["regionsMappedIntoMemory"].GetArray()) { + state.regionsMappedIntoMemory.push_back(MemoryRegion::LoadFromValue(rV)); } - for (auto& siV : context.doc["stubIslands"].GetArray()) - { - MemoryRegion si; - si.LoadFromValue(siV); - MutableState().stubIslandRegions.push_back(std::move(si)); + for (auto& siV : context.doc["stubIslands"].GetArray()) { + state.stubIslandRegions.push_back(MemoryRegion::LoadFromValue(siV)); } - for (auto& siV : context.doc["dyldDataSections"].GetArray()) - { - MemoryRegion si; - si.LoadFromValue(siV); - MutableState().dyldDataRegions.push_back(std::move(si)); + for (auto& siV : context.doc["dyldDataSections"].GetArray()) { + state.dyldDataRegions.push_back(MemoryRegion::LoadFromValue(siV)); } - for (auto& siV : context.doc["nonImageRegions"].GetArray()) - { - MemoryRegion si; - si.LoadFromValue(siV); - MutableState().nonImageRegions.push_back(std::move(si)); + for (auto& siV : context.doc["nonImageRegions"].GetArray()) { + state.nonImageRegions.push_back(MemoryRegion::LoadFromValue(siV)); } - m_metadataValid = true; + return state; } -void BackingCache::Store(SerializationContext& context) const -{ +void BackingCache::Store(SerializationContext& context) const { MSS(path); MSS(isPrimary); MSS(mappings); } -void BackingCache::Load(DeserializationContext& context) -{ - MSL(path); - MSL(isPrimary); - MSL(mappings); + +// static +BackingCache BackingCache::Load(DeserializationContext& context) { + return BackingCache { + .MSL(path), + .MSL(isPrimary), + .MSL(mappings), + }; } #if defined(__GNUC__) || defined(__clang__) @@ -3650,11 +3629,11 @@ void SharedCache::WillMutateState() { if (!m_state) { - m_state = std::make_shared(); + m_state = std::make_shared(); } else if (m_stateIsShared) { - m_state = std::make_shared(*m_state); + m_state = std::make_shared(*m_state); } m_stateIsShared = false; } diff --git a/view/sharedcache/core/SharedCache.h b/view/sharedcache/core/SharedCache.h index 2f6f2ae45e..1b3f8261e4 100644 --- a/view/sharedcache/core/SharedCache.h +++ b/view/sharedcache/core/SharedCache.h @@ -2,15 +2,16 @@ // Created by kat on 5/19/23. // +#ifndef SHAREDCACHE_SHAREDCACHE_H +#define SHAREDCACHE_SHAREDCACHE_H + #include -#include "DSCView.h" #include "VM.h" #include "view/macho/machoview.h" #include "MetadataSerializable.hpp" #include "../api/sharedcachecore.h" -#ifndef SHAREDCACHE_SHAREDCACHE_H -#define SHAREDCACHE_SHAREDCACHE_H +#include DECLARE_SHAREDCACHE_API_OBJECT(BNSharedCache, SharedCache); @@ -25,8 +26,7 @@ namespace SharedCacheCore { const std::string SharedCacheMetadataTag = "SHAREDCACHE-SharedCacheData"; - struct MemoryRegion : public MetadataSerializable - { + struct MemoryRegion : public MetadataSerializable { std::string prettyName; uint64_t start; uint64_t size; @@ -35,8 +35,7 @@ namespace SharedCacheCore { bool headerInitialized = false; BNSegmentFlag flags; - void Store(SerializationContext& context) const - { + void Store(SerializationContext& context) const { MSS(prettyName); MSS(start); MSS(size); @@ -45,48 +44,46 @@ namespace SharedCacheCore { MSS_CAST(flags, uint64_t); } - void Load(DeserializationContext& context) - { - MSL(prettyName); - MSL(start); - MSL(size); - MSL(loaded); - MSL(rawViewOffsetIfLoaded); - MSL_CAST(flags, uint64_t, BNSegmentFlag); + static MemoryRegion Load(DeserializationContext& context) { + return MemoryRegion { + .MSL(prettyName), + .MSL(start), + .MSL(size), + .MSL(loaded), + .MSL(rawViewOffsetIfLoaded), + .MSL_CAST(flags, uint64_t, BNSegmentFlag), + }; } }; - struct CacheImage : public MetadataSerializable - { + struct CacheImage : public MetadataSerializable { std::string installName; uint64_t headerLocation; std::vector regions; - void Store(SerializationContext& context) const - { + void Store(SerializationContext& context) const { MSS(installName); MSS(headerLocation); Serialize(context, "regions"); context.writer.StartArray(); - for (auto& region : regions) - { + for (auto& region : regions) { Serialize(context, region.AsString()); } context.writer.EndArray(); } - void Load(DeserializationContext& context) - { - MSL(installName); - MSL(headerLocation); - auto bArr = context.doc["regions"].GetArray(); - regions.clear(); - for (auto& region : bArr) - { - MemoryRegion r; - r.LoadFromString(region.GetString()); - regions.push_back(r); + static CacheImage Load(DeserializationContext& context) { + auto regionsArray = context.doc["regions"].GetArray(); + std::vector regions; + for (auto& region : regionsArray) { + regions.push_back(MemoryRegion::LoadFromString(region.GetString())); } + + return CacheImage { + .MSL(installName), + .MSL(headerLocation), + .regions = std::move(regions), + }; } }; @@ -111,14 +108,13 @@ namespace SharedCacheCore { uint32_t initProt; }; - struct BackingCache : public MetadataSerializable - { + struct BackingCache : public MetadataSerializable { std::string path; bool isPrimary = false; std::vector mappings; void Store(SerializationContext& context) const; - void Load(DeserializationContext& context); + static BackingCache Load(DeserializationContext& context); }; struct LoadedMapping @@ -436,8 +432,7 @@ namespace SharedCacheCore { bool functionStartsPresent = false; bool relocatable = false; - void Store(SerializationContext& context) const - { + void Store(SerializationContext& context) const { MSS(textBase); MSS(loadCommandOffset); MSS_SUBCLASS(ident); @@ -473,46 +468,47 @@ namespace SharedCacheCore { MSS(functionStartsPresent); MSS(relocatable); } - void Load(DeserializationContext& context) - { - MSL(textBase); - MSL(loadCommandOffset); - MSL_SUBCLASS(ident); - MSL(identifierPrefix); - MSL(installName); - MSL(entryPoints); - MSL(m_entryPoints); - MSL_SUBCLASS(symtab); - MSL_SUBCLASS(dysymtab); - MSL_SUBCLASS(dyldInfo); - // MSL_SUBCLASS(routines64); // FIXME CRASH but also do we even use this? - MSL_SUBCLASS(functionStarts); - MSL_SUBCLASS(moduleInitSections); - MSL_SUBCLASS(exportTrie); - MSL_SUBCLASS(chainedFixups); - MSL(relocationBase); - MSL_SUBCLASS(segments); - MSL_SUBCLASS(linkeditSegment); - MSL_SUBCLASS(sections); - MSL(sectionNames); - MSL_SUBCLASS(symbolStubSections); - MSL_SUBCLASS(symbolPointerSections); - MSL(dylibs); - MSL_SUBCLASS(buildVersion); - MSL_SUBCLASS(buildToolVersions); - MSL(linkeditPresent); - MSL(exportTriePath); - MSL(dysymPresent); - MSL(dyldInfoPresent); - MSL(exportTriePresent); - MSL(chainedFixupsPresent); - // MSL(routinesPresent); - MSL(functionStartsPresent); - MSL(relocatable); + + static SharedCacheMachOHeader Load(DeserializationContext& context) { + return SharedCacheMachOHeader { + .MSL(textBase), + .MSL(loadCommandOffset), + .MSL(ident), + .MSL(identifierPrefix), + .MSL(installName), + .MSL(entryPoints), + .MSL(m_entryPoints), + .MSL(symtab), + .MSL(dysymtab), + .MSL(dyldInfo), + // .MSL(routines64), // FIXME CRASH but also do we even use this? + .MSL(functionStarts), + .MSL(moduleInitSections), + .MSL(exportTrie), + .MSL(chainedFixups), + .MSL(relocationBase), + .MSL(segments), + .MSL(linkeditSegment), + .MSL(sections), + .MSL(sectionNames), + .MSL(symbolStubSections), + .MSL(symbolPointerSections), + .MSL(dylibs), + .MSL(buildVersion), + .MSL(buildToolVersions), + .MSL(linkeditPresent), + .MSL(exportTriePath), + .MSL(dysymPresent), + .MSL(dyldInfoPresent), + .MSL(exportTriePresent), + .MSL(chainedFixupsPresent), + // .MSL(routinesPresent), + .MSL(functionStartsPresent), + .MSL(relocatable), + }; } }; - struct MappingInfo { std::shared_ptr file; @@ -528,8 +524,9 @@ namespace SharedCacheCore { static std::atomic sharedCacheReferences = 0; - class SharedCache : public MetadataSerializable - { + struct SharedCacheState; + + class SharedCache : public MetadataSerializable> { IMPLEMENT_SHAREDCACHE_API_OBJECT(BNSharedCache); std::atomic m_refs = 0; @@ -561,9 +558,7 @@ namespace SharedCacheCore { }; void Store(SerializationContext& context) const; - void Load(DeserializationContext& context); - - struct State; + static std::optional Load(DeserializationContext& context); struct ViewSpecificState; @@ -575,7 +570,7 @@ namespace SharedCacheCore { // Updated as the view is loaded further, more images are added, etc // NOTE: Access via `State()` or `MutableState()` below. // `WillMutateState()` must be called before the first access to `MutableState()`. - std::shared_ptr m_state; + std::shared_ptr m_state; bool m_stateIsShared = false; // Serialized once by PerformInitialLoad and available after m_viewState == Loaded @@ -651,8 +646,8 @@ namespace SharedCacheCore { size_t GetBaseAddress() const; std::optional GetObjCOptimizationHeader(VMReader reader) const; - const State& State() const { return *m_state; } - struct State& MutableState() { AssertMutable(); return *m_state; } + const SharedCacheState& State() const { return *m_state; } + struct SharedCacheState& MutableState() { AssertMutable(); return *m_state; } void AssertMutable() const;