/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Plugins/ObjectContainer/Mach-O-Fileset/ObjectContainerMachOFileset.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===-- ObjectContainerMachOFileset.cpp -----------------------------------===// |
2 | | // |
3 | | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | | // See https://llvm.org/LICENSE.txt for license information. |
5 | | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | | // |
7 | | //===----------------------------------------------------------------------===// |
8 | | |
9 | | #include "ObjectContainerMachOFileset.h" |
10 | | #include "lldb/Core/Module.h" |
11 | | #include "lldb/Core/ModuleSpec.h" |
12 | | #include "lldb/Core/PluginManager.h" |
13 | | #include "lldb/Symbol/ObjectFile.h" |
14 | | #include "lldb/Target/Target.h" |
15 | | #include "lldb/Utility/ArchSpec.h" |
16 | | #include "lldb/Utility/DataBuffer.h" |
17 | | #include "lldb/Utility/Stream.h" |
18 | | #include <optional> |
19 | | |
20 | | using namespace lldb; |
21 | | using namespace lldb_private; |
22 | | using namespace llvm::MachO; |
23 | | |
24 | | LLDB_PLUGIN_DEFINE(ObjectContainerMachOFileset) |
25 | | |
26 | 3.92k | void ObjectContainerMachOFileset::Initialize() { |
27 | 3.92k | PluginManager::RegisterPlugin(GetPluginNameStatic(), |
28 | 3.92k | GetPluginDescriptionStatic(), CreateInstance, |
29 | 3.92k | GetModuleSpecifications, CreateMemoryInstance); |
30 | 3.92k | } |
31 | | |
32 | 3.92k | void ObjectContainerMachOFileset::Terminate() { |
33 | 3.92k | PluginManager::UnregisterPlugin(CreateInstance); |
34 | 3.92k | } |
35 | | |
36 | | ObjectContainerMachOFileset::ObjectContainerMachOFileset( |
37 | | const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp, |
38 | | lldb::offset_t data_offset, const lldb_private::FileSpec *file, |
39 | | lldb::offset_t offset, lldb::offset_t length) |
40 | 0 | : ObjectContainer(module_sp, file, offset, length, data_sp, data_offset), |
41 | 0 | m_memory_addr(LLDB_INVALID_ADDRESS) {} |
42 | | |
43 | | ObjectContainerMachOFileset::ObjectContainerMachOFileset( |
44 | | const lldb::ModuleSP &module_sp, lldb::WritableDataBufferSP data_sp, |
45 | | const lldb::ProcessSP &process_sp, lldb::addr_t header_addr) |
46 | 0 | : ObjectContainer(module_sp, nullptr, 0, data_sp->GetByteSize(), data_sp, |
47 | 0 | 0), |
48 | 0 | m_process_wp(process_sp), m_memory_addr(header_addr) {} |
49 | | |
50 | | ObjectContainer *ObjectContainerMachOFileset::CreateInstance( |
51 | | const lldb::ModuleSP &module_sp, DataBufferSP &data_sp, |
52 | | lldb::offset_t data_offset, const FileSpec *file, |
53 | 60 | lldb::offset_t file_offset, lldb::offset_t length) { |
54 | 60 | if (!data_sp) |
55 | 60 | return {}; |
56 | | |
57 | 0 | DataExtractor data; |
58 | 0 | data.SetData(data_sp, data_offset, length); |
59 | 0 | if (!MagicBytesMatch(data)) |
60 | 0 | return {}; |
61 | | |
62 | 0 | auto container_up = std::make_unique<ObjectContainerMachOFileset>( |
63 | 0 | module_sp, data_sp, data_offset, file, file_offset, length); |
64 | 0 | if (!container_up->ParseHeader()) |
65 | 0 | return {}; |
66 | | |
67 | 0 | return container_up.release(); |
68 | 0 | } |
69 | | |
70 | | ObjectContainer *ObjectContainerMachOFileset::CreateMemoryInstance( |
71 | | const lldb::ModuleSP &module_sp, lldb::WritableDataBufferSP data_sp, |
72 | 4 | const lldb::ProcessSP &process_sp, lldb::addr_t header_addr) { |
73 | 4 | if (!MagicBytesMatch(data_sp, 0, data_sp->GetByteSize())) |
74 | 4 | return {}; |
75 | | |
76 | 0 | auto container_up = std::make_unique<ObjectContainerMachOFileset>( |
77 | 0 | module_sp, data_sp, process_sp, header_addr); |
78 | 0 | if (!container_up->ParseHeader()) |
79 | 0 | return {}; |
80 | | |
81 | 0 | return container_up.release(); |
82 | 0 | } |
83 | | |
84 | 0 | ObjectContainerMachOFileset::~ObjectContainerMachOFileset() = default; |
85 | | |
86 | 0 | static uint32_t MachHeaderSizeFromMagic(uint32_t magic) { |
87 | 0 | switch (magic) { |
88 | 0 | case MH_MAGIC: |
89 | 0 | case MH_CIGAM: |
90 | 0 | return sizeof(struct mach_header); |
91 | 0 | case MH_MAGIC_64: |
92 | 0 | case MH_CIGAM_64: |
93 | 0 | return sizeof(struct mach_header_64); |
94 | 0 | default: |
95 | 0 | return 0; |
96 | 0 | } |
97 | 0 | } |
98 | | |
99 | 0 | static std::optional<mach_header> ParseMachOHeader(DataExtractor &data) { |
100 | 0 | lldb::offset_t offset = 0; |
101 | 0 | mach_header header; |
102 | 0 | header.magic = data.GetU32(&offset); |
103 | 0 | switch (header.magic) { |
104 | 0 | case MH_MAGIC: |
105 | 0 | data.SetByteOrder(endian::InlHostByteOrder()); |
106 | 0 | data.SetAddressByteSize(4); |
107 | 0 | break; |
108 | 0 | case MH_MAGIC_64: |
109 | 0 | data.SetByteOrder(endian::InlHostByteOrder()); |
110 | 0 | data.SetAddressByteSize(8); |
111 | 0 | break; |
112 | 0 | case MH_CIGAM: |
113 | 0 | data.SetByteOrder(endian::InlHostByteOrder() == eByteOrderBig |
114 | 0 | ? eByteOrderLittle |
115 | 0 | : eByteOrderBig); |
116 | 0 | data.SetAddressByteSize(4); |
117 | 0 | break; |
118 | 0 | case MH_CIGAM_64: |
119 | 0 | data.SetByteOrder(endian::InlHostByteOrder() == eByteOrderBig |
120 | 0 | ? eByteOrderLittle |
121 | 0 | : eByteOrderBig); |
122 | 0 | data.SetAddressByteSize(8); |
123 | 0 | break; |
124 | 0 | default: |
125 | 0 | return {}; |
126 | 0 | } |
127 | | |
128 | 0 | header.cputype = data.GetU32(&offset); |
129 | 0 | header.cpusubtype = data.GetU32(&offset); |
130 | 0 | header.filetype = data.GetU32(&offset); |
131 | 0 | header.ncmds = data.GetU32(&offset); |
132 | 0 | header.sizeofcmds = data.GetU32(&offset); |
133 | 0 | return header; |
134 | 0 | } |
135 | | |
136 | | static bool |
137 | | ParseFileset(DataExtractor &data, mach_header header, |
138 | | std::vector<ObjectContainerMachOFileset::Entry> &entries, |
139 | 0 | std::optional<lldb::addr_t> load_addr = std::nullopt) { |
140 | 0 | lldb::offset_t offset = MachHeaderSizeFromMagic(header.magic); |
141 | 0 | lldb::offset_t slide = 0; |
142 | 0 | for (uint32_t i = 0; i < header.ncmds; ++i) { |
143 | 0 | const lldb::offset_t load_cmd_offset = offset; |
144 | 0 | load_command lc = {}; |
145 | 0 | if (data.GetU32(&offset, &lc.cmd, 2) == nullptr) |
146 | 0 | break; |
147 | | |
148 | | // If we know the load address we can compute the slide. |
149 | 0 | if (load_addr) { |
150 | 0 | if (lc.cmd == llvm::MachO::LC_SEGMENT_64) { |
151 | 0 | segment_command_64 segment; |
152 | 0 | data.CopyData(load_cmd_offset, sizeof(segment_command_64), &segment); |
153 | 0 | if (llvm::StringRef(segment.segname) == "__TEXT") |
154 | 0 | slide = *load_addr - segment.vmaddr; |
155 | 0 | } |
156 | 0 | } |
157 | |
|
158 | 0 | if (lc.cmd == LC_FILESET_ENTRY) { |
159 | 0 | fileset_entry_command entry; |
160 | 0 | data.CopyData(load_cmd_offset, sizeof(fileset_entry_command), &entry); |
161 | 0 | lldb::offset_t entry_id_offset = load_cmd_offset + entry.entry_id.offset; |
162 | 0 | const char *id = data.GetCStr(&entry_id_offset); |
163 | 0 | entries.emplace_back(entry.vmaddr + slide, entry.fileoff, |
164 | 0 | std::string(id)); |
165 | 0 | } |
166 | |
|
167 | 0 | offset = load_cmd_offset + lc.cmdsize; |
168 | 0 | } |
169 | |
|
170 | 0 | return true; |
171 | 0 | } |
172 | | |
173 | | bool ObjectContainerMachOFileset::ParseHeader( |
174 | | DataExtractor &data, const lldb_private::FileSpec &file, |
175 | 0 | lldb::offset_t file_offset, std::vector<Entry> &entries) { |
176 | 0 | std::optional<mach_header> header = ParseMachOHeader(data); |
177 | |
|
178 | 0 | if (!header) |
179 | 0 | return false; |
180 | | |
181 | 0 | const size_t header_size = MachHeaderSizeFromMagic(header->magic); |
182 | 0 | const size_t header_and_lc_size = header_size + header->sizeofcmds; |
183 | |
|
184 | 0 | if (data.GetByteSize() < header_and_lc_size) { |
185 | 0 | DataBufferSP data_sp = |
186 | 0 | ObjectFile::MapFileData(file, header_and_lc_size, file_offset); |
187 | 0 | data.SetData(data_sp); |
188 | 0 | } |
189 | |
|
190 | 0 | return ParseFileset(data, *header, entries); |
191 | 0 | } |
192 | | |
193 | 0 | bool ObjectContainerMachOFileset::ParseHeader() { |
194 | 0 | ModuleSP module_sp(GetModule()); |
195 | 0 | if (!module_sp) |
196 | 0 | return false; |
197 | | |
198 | 0 | std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex()); |
199 | |
|
200 | 0 | std::optional<mach_header> header = ParseMachOHeader(m_data); |
201 | 0 | if (!header) |
202 | 0 | return false; |
203 | | |
204 | 0 | const size_t header_size = MachHeaderSizeFromMagic(header->magic); |
205 | 0 | const size_t header_and_lc_size = header_size + header->sizeofcmds; |
206 | |
|
207 | 0 | if (m_data.GetByteSize() < header_and_lc_size) { |
208 | 0 | ProcessSP process_sp(m_process_wp.lock()); |
209 | 0 | DataBufferSP data_sp = |
210 | 0 | process_sp |
211 | 0 | ? ObjectFile::ReadMemory(process_sp, m_memory_addr, |
212 | 0 | header_and_lc_size) |
213 | 0 | : ObjectFile::MapFileData(m_file, header_and_lc_size, m_offset); |
214 | 0 | m_data.SetData(data_sp); |
215 | 0 | } |
216 | |
|
217 | 0 | return ParseFileset(m_data, *header, m_entries, m_memory_addr); |
218 | 0 | } |
219 | | |
220 | | size_t ObjectContainerMachOFileset::GetModuleSpecifications( |
221 | | const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp, |
222 | | lldb::offset_t data_offset, lldb::offset_t file_offset, |
223 | 16 | lldb::offset_t file_size, lldb_private::ModuleSpecList &specs) { |
224 | 16 | const size_t initial_count = specs.GetSize(); |
225 | | |
226 | 16 | DataExtractor data; |
227 | 16 | data.SetData(data_sp, data_offset, data_sp->GetByteSize()); |
228 | | |
229 | 16 | if (MagicBytesMatch(data)) { |
230 | 0 | std::vector<Entry> entries; |
231 | 0 | if (ParseHeader(data, file, file_offset, entries)) { |
232 | 0 | for (const Entry &entry : entries) { |
233 | 0 | const lldb::offset_t entry_offset = entry.fileoff + file_offset; |
234 | 0 | if (ObjectFile::GetModuleSpecifications( |
235 | 0 | file, entry_offset, file_size - entry_offset, specs)) { |
236 | 0 | ModuleSpec &spec = specs.GetModuleSpecRefAtIndex(specs.GetSize() - 1); |
237 | 0 | spec.GetObjectName() = ConstString(entry.id); |
238 | 0 | } |
239 | 0 | } |
240 | 0 | } |
241 | 0 | } |
242 | 16 | return specs.GetSize() - initial_count; |
243 | 16 | } |
244 | | |
245 | | bool ObjectContainerMachOFileset::MagicBytesMatch(DataBufferSP data_sp, |
246 | | lldb::addr_t data_offset, |
247 | 4 | lldb::addr_t data_length) { |
248 | 4 | DataExtractor data; |
249 | 4 | data.SetData(data_sp, data_offset, data_length); |
250 | 4 | return MagicBytesMatch(data); |
251 | 4 | } |
252 | | |
253 | 20 | bool ObjectContainerMachOFileset::MagicBytesMatch(const DataExtractor &data) { |
254 | 20 | lldb::offset_t offset = 0; |
255 | 20 | uint32_t magic = data.GetU32(&offset); |
256 | 20 | switch (magic) { |
257 | 0 | case MH_MAGIC: |
258 | 0 | case MH_CIGAM: |
259 | 4 | case MH_MAGIC_64: |
260 | 4 | case MH_CIGAM_64: |
261 | 4 | break; |
262 | 16 | default: |
263 | 16 | return false; |
264 | 20 | } |
265 | 4 | offset += 4; // cputype |
266 | 4 | offset += 4; // cpusubtype |
267 | 4 | uint32_t filetype = data.GetU32(&offset); |
268 | 4 | return filetype == MH_FILESET; |
269 | 20 | } |
270 | | |
271 | | ObjectFileSP |
272 | 0 | ObjectContainerMachOFileset::GetObjectFile(const lldb_private::FileSpec *file) { |
273 | 0 | ModuleSP module_sp(GetModule()); |
274 | 0 | if (!module_sp) |
275 | 0 | return {}; |
276 | | |
277 | 0 | ConstString object_name = module_sp->GetObjectName(); |
278 | 0 | if (!object_name) |
279 | 0 | return {}; |
280 | | |
281 | 0 | Entry *entry = FindEntry(object_name.GetCString()); |
282 | 0 | if (!entry) |
283 | 0 | return {}; |
284 | | |
285 | 0 | DataBufferSP data_sp; |
286 | 0 | lldb::offset_t data_offset = 0; |
287 | 0 | return ObjectFile::FindPlugin(module_sp, file, m_offset + entry->fileoff, |
288 | 0 | m_data.GetByteSize() - entry->fileoff, data_sp, |
289 | 0 | data_offset); |
290 | 0 | } |
291 | | |
292 | | ObjectContainerMachOFileset::Entry * |
293 | 0 | ObjectContainerMachOFileset::FindEntry(llvm::StringRef id) { |
294 | 0 | for (Entry &entry : m_entries) { |
295 | 0 | if (entry.id == id) |
296 | 0 | return &entry; |
297 | 0 | } |
298 | 0 | return nullptr; |
299 | 0 | } |