-
Notifications
You must be signed in to change notification settings - Fork 283
Expand file tree
/
Copy pathSharedCache.h
More file actions
266 lines (204 loc) · 9.68 KB
/
SharedCache.h
File metadata and controls
266 lines (204 loc) · 9.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
#pragma once
#include <shared_mutex>
#include <vector>
#include <Dyld.h>
#include "binaryninjaapi.h"
#include "MachO.h"
#include "VirtualMemory.h"
struct CacheSymbol
{
BNSymbolType type;
BNSymbolBinding binding = NoBinding;
uint64_t address;
std::string name;
CacheSymbol() = default;
CacheSymbol(BNSymbolType type, BNSymbolBinding binding, uint64_t address, std::string name) :
type(type), binding(binding), address(address), name(std::move(name))
{}
~CacheSymbol() = default;
CacheSymbol(const CacheSymbol& other) = default;
CacheSymbol& operator=(const CacheSymbol& other) = default;
CacheSymbol(CacheSymbol&& other) noexcept = default;
CacheSymbol& operator=(CacheSymbol&& other) noexcept = default;
std::pair<std::string, BinaryNinja::Ref<BinaryNinja::Type>> DemangledName(BinaryNinja::BinaryView& view) const;
// NOTE: you should really only call this when adding the symbol to the view.
std::pair<BinaryNinja::Ref<BinaryNinja::Symbol>, BinaryNinja::Ref<BinaryNinja::Type>> GetBNSymbolAndType(BinaryNinja::BinaryView& view) const;
};
enum class CacheRegionType
{
Image,
StubIsland,
DyldData,
NonImage,
};
struct CacheRegion
{
CacheRegionType type;
std::string name;
uint64_t start;
uint64_t size;
// Associate this region with this image, this makes it easier to identify what image owns this region.
std::optional<uint64_t> imageStart;
BNSegmentFlag flags;
CacheRegion() = default;
~CacheRegion() = default;
CacheRegion(const CacheRegion& other) = default;
CacheRegion& operator=(const CacheRegion& other) = default;
CacheRegion(CacheRegion&& other) noexcept = default;
CacheRegion& operator=(CacheRegion&& other) noexcept = default;
AddressRange AsAddressRange() const { return {start, start + size}; }
BNSectionSemantics SectionSemanticsForRegion() const
{
if ((flags & SegmentExecutable) && (flags & SegmentDenyWrite))
return ReadOnlyCodeSectionSemantics;
if (flags & SegmentExecutable)
return DefaultSectionSemantics;
if (flags & SegmentDenyWrite)
return ReadOnlyDataSectionSemantics;
return ReadWriteDataSectionSemantics;
}
};
// Represents a single image and its associated memory regions.
struct CacheImage
{
uint64_t headerAddress;
std::string path;
// A list to the start of memory regions associated with the image.
// This lets us load all regions for a given image easily.
std::vector<uint64_t> regionStarts;
std::shared_ptr<SharedCacheMachOHeader> header;
CacheImage() = default;
~CacheImage() = default;
CacheImage(const CacheImage& other) = default;
CacheImage& operator=(const CacheImage& other) = default;
CacheImage(CacheImage&& other) noexcept = default;
CacheImage& operator=(CacheImage&& other) noexcept = default;
// Get the file name from the path.
std::string GetName() const { return BaseFileName(path); }
// Get the names of the dependencies.
std::vector<std::string> GetDependencies() const;
};
enum class CacheEntryType
{
Primary,
Secondary,
// A special entry that holds symbols for other cache entries.
Symbols,
// If the type is marked as this then all mappings will be marked as such.
DyldData,
// A single stub mapping file.
Stub,
};
// Describes a single files cache information
class CacheEntry
{
CacheEntryType m_type;
std::string m_filePath;
std::string m_fileName;
dyld_cache_header m_header {};
// Mappings tell us _where_ to map the regions within the flat address space.
// Without this we wouldn't know where the entry is supposed to exist in the address space.
std::vector<dyld_cache_mapping_info> m_mappings {};
// Mapping of image path to image info, used within ProcessImagesAndRegions to add them to the cache.
// Also used to retrieve the image dependencies.
std::vector<std::pair<std::string, dyld_cache_image_info>> m_images {};
std::shared_ptr<MappedFileRegion> m_file;
public:
CacheEntry(std::string filePath, std::string fileName, CacheEntryType type, dyld_cache_header header,
std::vector<dyld_cache_mapping_info> mappings, std::vector<std::pair<std::string, dyld_cache_image_info>> images,
std::shared_ptr<MappedFileRegion> file);
CacheEntry() = default;
CacheEntry(const CacheEntry&) = default;
CacheEntry(CacheEntry&&) = default;
CacheEntry& operator=(CacheEntry&&) = default;
// Construct a cache entry from the file on disk.
static CacheEntry FromFile(const std::string& filePath, const std::string& fileName, CacheEntryType type);
const std::shared_ptr<MappedFileRegion>& GetFile() const { return m_file; }
// Get the headers virtual address.
// This is useful if you need to read relative to the start of the entry file.
std::optional<uint64_t> GetHeaderAddress() const;
// Get the mapped address for a given file offset.
// Ex. passing 0x0 will retrieve the mapped address for the start of the file (i.e. the header)
std::optional<uint64_t> GetMappedAddress(uint64_t fileOffset) const;
CacheEntryType GetType() const { return m_type; }
// Ex. "/myuser/mypath/dyld_shared_cache_arm64e"
const std::string& GetFilePath() const { return m_filePath; }
// Ex. "dyld_shared_cache_arm64e"
const std::string GetFileName() const { return m_fileName; }
const dyld_cache_header& GetHeader() const { return m_header; }
const std::vector<dyld_cache_mapping_info>& GetMappings() const { return m_mappings; }
const std::vector<std::pair<std::string, dyld_cache_image_info>>& GetImages() const { return m_images; }
};
// The ID for a given CacheEntry, use this instead of passing a pointer around to avoid complexity :V
typedef uint32_t CacheEntryId;
// The C in DSC.
// This represents the entire cache, all regions and images are visible from here.
// This is the dump for all the information, and what the workflow activities and the UI want.
// Creating this is expensive, both in actual processing and just copying, so we only generate this
// once every time the database is open.
class SharedCache
{
// Calculated within `AddEntry`, this indicates where the shared cache image is based at.
uint64_t m_baseAddress = 0;
// The shared cache can own the virtual memory, this is fine...
std::shared_ptr<VirtualMemory> m_vm;
std::vector<CacheEntry> m_entries {};
// This information is used in tandem with the cache images to load memory regions into the binary view.
AddressRangeMap<CacheRegion> m_regions {};
// Describes the images of the cache.
std::unordered_map<uint64_t, CacheImage> m_images {};
// All the external symbols for this cache. Both mapped and unmapped (not in the view).
std::unordered_map<uint64_t, CacheSymbol> m_symbols {};
// Quickly lookup a symbol by name, populated by `FinalizeSymbols`.
// `m_namedSymbols` is modified in a worker thread spawned by view init so we must not get a symbol until its populated.
std::unordered_map<std::string, uint64_t> m_namedSymbols {};
// Used to guard `m_namedSymbols` as it's accessed on multiple threads.
// NOTE: Wrapped in unique_ptr to keep SharedCache movable.
std::unique_ptr<std::shared_mutex> m_namedSymMutex;
// Local symbols entry and its mapping, used to read symbol tables from the .symbols file.
// These are handled separately as they are not mapped into the main virtual memory of the cache.
std::optional<CacheEntry> m_localSymbolsEntry;
std::shared_ptr<VirtualMemory> m_localSymbolsVM;
bool ProcessEntryImage(const std::string& path, const dyld_cache_image_info& info);
// Add a region known not to overlap with another, otherwise use AddRegion.
// returns whether the region was inserted.
bool AddNonOverlappingRegion(CacheRegion region);
public:
SharedCache() = default;
explicit SharedCache(uint64_t addressSize);
SharedCache(const SharedCache &) = delete;
SharedCache &operator=(const SharedCache &) = delete;
SharedCache(SharedCache &&) noexcept = default;
SharedCache &operator=(SharedCache &&) noexcept = default;
uint64_t GetBaseAddress() const { return m_baseAddress; }
std::shared_ptr<VirtualMemory> GetVirtualMemory() const { return m_vm; }
const std::vector<CacheEntry>& GetEntries() const { return m_entries; }
const AddressRangeMap<CacheRegion>& GetRegions() const { return m_regions; }
const std::unordered_map<uint64_t, CacheImage>& GetImages() const { return m_images; }
const std::unordered_map<uint64_t, CacheSymbol>& GetSymbols() const { return m_symbols; }
const std::optional<CacheEntry>& GetLocalSymbolsEntry() const { return m_localSymbolsEntry; }
std::shared_ptr<VirtualMemory> GetLocalSymbolsVM() const { return m_localSymbolsVM; }
void AddImage(CacheImage&& image);
// Add a region that may overlap with another.
void AddRegion(CacheRegion&& region);
void AddSymbol(CacheSymbol symbol);
void AddSymbols(std::vector<CacheSymbol>&& symbols);
// Adds the cache entry and populates the virtual memory using the mapping information.
// After being added the entry is read only, there is nothing that can modify it.
void AddEntry(CacheEntry entry);
void ProcessEntryImages(const CacheEntry& entry);
void ProcessEntryRegions(const CacheEntry& entry);
void ProcessEntrySlideInfo(const CacheEntry& entry) const;
// Construct the named symbols lookup map for use with `GetSymbolWithName`.
void ProcessSymbols();
std::optional<CacheEntry> GetEntryContaining(uint64_t address) const;
std::optional<CacheEntry> GetEntryWithImage(const CacheImage& image) const;
std::optional<CacheRegion> GetRegionAt(uint64_t address) const;
std::optional<CacheRegion> GetRegionContaining(uint64_t address) const;
std::optional<CacheImage> GetImageAt(uint64_t address) const;
std::optional<CacheImage> GetImageContaining(uint64_t address) const;
// TODO: Rename to GetImageWithPath and then make another one for the image name.
std::optional<CacheImage> GetImageWithName(const std::string& name) const;
std::optional<CacheSymbol> GetSymbolAt(uint64_t address) const;
std::optional<CacheSymbol> GetSymbolWithName(const std::string& name);
};