1 | //===-- ProcessMachCore.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 <cerrno> |
10 | #include <cstdlib> |
11 | |
12 | #include "llvm/Support/MathExtras.h" |
13 | #include "llvm/Support/Threading.h" |
14 | |
15 | #include "lldb/Core/Debugger.h" |
16 | #include "lldb/Core/Module.h" |
17 | #include "lldb/Core/ModuleSpec.h" |
18 | #include "lldb/Core/PluginManager.h" |
19 | #include "lldb/Core/Section.h" |
20 | #include "lldb/Host/Host.h" |
21 | #include "lldb/Symbol/ObjectFile.h" |
22 | #include "lldb/Target/MemoryRegionInfo.h" |
23 | #include "lldb/Target/SectionLoadList.h" |
24 | #include "lldb/Target/Target.h" |
25 | #include "lldb/Target/Thread.h" |
26 | #include "lldb/Utility/AppleUuidCompatibility.h" |
27 | #include "lldb/Utility/DataBuffer.h" |
28 | #include "lldb/Utility/LLDBLog.h" |
29 | #include "lldb/Utility/Log.h" |
30 | #include "lldb/Utility/State.h" |
31 | #include "lldb/Utility/UUID.h" |
32 | |
33 | #include "ProcessMachCore.h" |
34 | #include "Plugins/Process/Utility/StopInfoMachException.h" |
35 | #include "ThreadMachCore.h" |
36 | |
37 | // Needed for the plug-in names for the dynamic loaders. |
38 | #include "lldb/Host/SafeMachO.h" |
39 | |
40 | #include "Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.h" |
41 | #include "Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h" |
42 | #include "Plugins/DynamicLoader/Static/DynamicLoaderStatic.h" |
43 | #include "Plugins/ObjectFile/Mach-O/ObjectFileMachO.h" |
44 | #include "Plugins/Platform/MacOSX/PlatformDarwinKernel.h" |
45 | |
46 | #include <memory> |
47 | #include <mutex> |
48 | |
49 | using namespace lldb; |
50 | using namespace lldb_private; |
51 | |
52 | LLDB_PLUGIN_DEFINE(ProcessMachCore) |
53 | |
54 | llvm::StringRef ProcessMachCore::GetPluginDescriptionStatic() { |
55 | return "Mach-O core file debugging plug-in."; |
56 | } |
57 | |
58 | void ProcessMachCore::Terminate() { |
59 | PluginManager::UnregisterPlugin(create_callback: ProcessMachCore::CreateInstance); |
60 | } |
61 | |
62 | lldb::ProcessSP ProcessMachCore::CreateInstance(lldb::TargetSP target_sp, |
63 | ListenerSP listener_sp, |
64 | const FileSpec *crash_file, |
65 | bool can_connect) { |
66 | lldb::ProcessSP process_sp; |
67 | if (crash_file && !can_connect) { |
68 | const size_t header_size = sizeof(llvm::MachO::mach_header); |
69 | auto data_sp = FileSystem::Instance().CreateDataBuffer( |
70 | path: crash_file->GetPath(), size: header_size, offset: 0); |
71 | if (data_sp && data_sp->GetByteSize() == header_size) { |
72 | DataExtractor data(data_sp, lldb::eByteOrderLittle, 4); |
73 | |
74 | lldb::offset_t data_offset = 0; |
75 | llvm::MachO::mach_header mach_header; |
76 | if (ObjectFileMachO::ParseHeader(data, data_offset_ptr: &data_offset, header&: mach_header)) { |
77 | if (mach_header.filetype == llvm::MachO::MH_CORE) |
78 | process_sp = std::make_shared<ProcessMachCore>(args&: target_sp, args&: listener_sp, |
79 | args: *crash_file); |
80 | } |
81 | } |
82 | } |
83 | return process_sp; |
84 | } |
85 | |
86 | bool ProcessMachCore::CanDebug(lldb::TargetSP target_sp, |
87 | bool plugin_specified_by_name) { |
88 | if (plugin_specified_by_name) |
89 | return true; |
90 | |
91 | // For now we are just making sure the file exists for a given module |
92 | if (!m_core_module_sp && FileSystem::Instance().Exists(file_spec: m_core_file)) { |
93 | // Don't add the Target's architecture to the ModuleSpec - we may be |
94 | // working with a core file that doesn't have the correct cpusubtype in the |
95 | // header but we should still try to use it - |
96 | // ModuleSpecList::FindMatchingModuleSpec enforces a strict arch mach. |
97 | ModuleSpec core_module_spec(m_core_file); |
98 | Status error(ModuleList::GetSharedModule(module_spec: core_module_spec, module_sp&: m_core_module_sp, |
99 | module_search_paths_ptr: nullptr, old_modules: nullptr, did_create_ptr: nullptr)); |
100 | |
101 | if (m_core_module_sp) { |
102 | ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); |
103 | if (core_objfile && core_objfile->GetType() == ObjectFile::eTypeCoreFile) |
104 | return true; |
105 | } |
106 | } |
107 | return false; |
108 | } |
109 | |
110 | // ProcessMachCore constructor |
111 | ProcessMachCore::ProcessMachCore(lldb::TargetSP target_sp, |
112 | ListenerSP listener_sp, |
113 | const FileSpec &core_file) |
114 | : PostMortemProcess(target_sp, listener_sp, core_file), m_core_aranges(), |
115 | m_core_range_infos(), m_core_module_sp(), |
116 | m_dyld_addr(LLDB_INVALID_ADDRESS), |
117 | m_dyld_all_image_infos_addr(LLDB_INVALID_ADDRESS), |
118 | m_mach_kernel_addr(LLDB_INVALID_ADDRESS) {} |
119 | |
120 | // Destructor |
121 | ProcessMachCore::~ProcessMachCore() { |
122 | Clear(); |
123 | // We need to call finalize on the process before destroying ourselves to |
124 | // make sure all of the broadcaster cleanup goes as planned. If we destruct |
125 | // this class, then Process::~Process() might have problems trying to fully |
126 | // destroy the broadcaster. |
127 | Finalize(destructing: true /* destructing */); |
128 | } |
129 | |
130 | bool ProcessMachCore::CheckAddressForDyldOrKernel(lldb::addr_t addr, |
131 | addr_t &dyld, |
132 | addr_t &kernel) { |
133 | Log *log(GetLog(mask: LLDBLog::DynamicLoader | LLDBLog::Process)); |
134 | llvm::MachO::mach_header header; |
135 | Status error; |
136 | dyld = kernel = LLDB_INVALID_ADDRESS; |
137 | if (DoReadMemory(addr, buf: &header, size: sizeof(header), error) != sizeof(header)) |
138 | return false; |
139 | if (header.magic == llvm::MachO::MH_CIGAM || |
140 | header.magic == llvm::MachO::MH_CIGAM_64) { |
141 | header.magic = llvm::byteswap<uint32_t>(V: header.magic); |
142 | header.cputype = llvm::byteswap<uint32_t>(V: header.cputype); |
143 | header.cpusubtype = llvm::byteswap<uint32_t>(V: header.cpusubtype); |
144 | header.filetype = llvm::byteswap<uint32_t>(V: header.filetype); |
145 | header.ncmds = llvm::byteswap<uint32_t>(V: header.ncmds); |
146 | header.sizeofcmds = llvm::byteswap<uint32_t>(V: header.sizeofcmds); |
147 | header.flags = llvm::byteswap<uint32_t>(V: header.flags); |
148 | } |
149 | |
150 | if (header.magic == llvm::MachO::MH_MAGIC || |
151 | header.magic == llvm::MachO::MH_MAGIC_64) { |
152 | // Check MH_EXECUTABLE to see if we can find the mach image that contains |
153 | // the shared library list. The dynamic loader (dyld) is what contains the |
154 | // list for user applications, and the mach kernel contains a global that |
155 | // has the list of kexts to load |
156 | switch (header.filetype) { |
157 | case llvm::MachO::MH_DYLINKER: |
158 | LLDB_LOGF(log, |
159 | "ProcessMachCore::%s found a user " |
160 | "process dyld binary image at 0x%"PRIx64, |
161 | __FUNCTION__, addr); |
162 | dyld = addr; |
163 | return true; |
164 | |
165 | case llvm::MachO::MH_EXECUTE: |
166 | // Check MH_EXECUTABLE file types to see if the dynamic link object flag |
167 | // is NOT set. If it isn't, then we have a mach_kernel. |
168 | if ((header.flags & llvm::MachO::MH_DYLDLINK) == 0) { |
169 | LLDB_LOGF(log, |
170 | "ProcessMachCore::%s found a mach " |
171 | "kernel binary image at 0x%"PRIx64, |
172 | __FUNCTION__, addr); |
173 | // Address of the mach kernel "struct mach_header" in the core file. |
174 | kernel = addr; |
175 | return true; |
176 | } |
177 | break; |
178 | } |
179 | } |
180 | return false; |
181 | } |
182 | |
183 | void ProcessMachCore::CreateMemoryRegions() { |
184 | ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); |
185 | SectionList *section_list = core_objfile->GetSectionList(); |
186 | const uint32_t num_sections = section_list->GetNumSections(depth: 0); |
187 | |
188 | bool ranges_are_sorted = true; |
189 | addr_t vm_addr = 0; |
190 | for (uint32_t i = 0; i < num_sections; ++i) { |
191 | Section *section = section_list->GetSectionAtIndex(idx: i).get(); |
192 | if (section && section->GetFileSize() > 0) { |
193 | lldb::addr_t section_vm_addr = section->GetFileAddress(); |
194 | FileRange file_range(section->GetFileOffset(), section->GetFileSize()); |
195 | VMRangeToFileOffset::Entry range_entry( |
196 | section_vm_addr, section->GetByteSize(), file_range); |
197 | |
198 | if (vm_addr > section_vm_addr) |
199 | ranges_are_sorted = false; |
200 | vm_addr = section->GetFileAddress(); |
201 | VMRangeToFileOffset::Entry *last_entry = m_core_aranges.Back(); |
202 | |
203 | if (last_entry && |
204 | last_entry->GetRangeEnd() == range_entry.GetRangeBase() && |
205 | last_entry->data.GetRangeEnd() == range_entry.data.GetRangeBase()) { |
206 | last_entry->SetRangeEnd(range_entry.GetRangeEnd()); |
207 | last_entry->data.SetRangeEnd(range_entry.data.GetRangeEnd()); |
208 | } else { |
209 | m_core_aranges.Append(entry: range_entry); |
210 | } |
211 | // Some core files don't fill in the permissions correctly. If that is |
212 | // the case assume read + execute so clients don't think the memory is |
213 | // not readable, or executable. The memory isn't writable since this |
214 | // plug-in doesn't implement DoWriteMemory. |
215 | uint32_t permissions = section->GetPermissions(); |
216 | if (permissions == 0) |
217 | permissions = lldb::ePermissionsReadable | lldb::ePermissionsExecutable; |
218 | m_core_range_infos.Append(entry: VMRangeToPermissions::Entry( |
219 | section_vm_addr, section->GetByteSize(), permissions)); |
220 | } |
221 | } |
222 | if (!ranges_are_sorted) { |
223 | m_core_aranges.Sort(); |
224 | m_core_range_infos.Sort(); |
225 | } |
226 | } |
227 | |
228 | // Some corefiles have a UUID stored in a low memory |
229 | // address. We inspect a set list of addresses for |
230 | // the characters 'uuid' and 16 bytes later there will |
231 | // be a uuid_t UUID. If we can find a binary that |
232 | // matches the UUID, it is loaded with no slide in the target. |
233 | bool ProcessMachCore::LoadBinaryViaLowmemUUID() { |
234 | Log *log(GetLog(mask: LLDBLog::DynamicLoader | LLDBLog::Process)); |
235 | ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); |
236 | |
237 | uint64_t lowmem_uuid_addresses[] = {0x2000204, 0x1000204, 0x1000020, 0x4204, |
238 | 0x1204, 0x1020, 0x4020, 0xc00, |
239 | 0xC0, 0}; |
240 | |
241 | for (uint64_t addr : lowmem_uuid_addresses) { |
242 | const VMRangeToFileOffset::Entry *core_memory_entry = |
243 | m_core_aranges.FindEntryThatContains(addr); |
244 | if (core_memory_entry) { |
245 | const addr_t offset = addr - core_memory_entry->GetRangeBase(); |
246 | const addr_t bytes_left = core_memory_entry->GetRangeEnd() - addr; |
247 | // (4-bytes 'uuid' + 12 bytes pad for align + 16 bytes uuid_t) == 32 bytes |
248 | if (bytes_left >= 32) { |
249 | char strbuf[4]; |
250 | if (core_objfile->CopyData( |
251 | offset: core_memory_entry->data.GetRangeBase() + offset, length: 4, dst: &strbuf) && |
252 | strncmp(s1: "uuid", s2: (char *)&strbuf, n: 4) == 0) { |
253 | uuid_t uuid_bytes; |
254 | if (core_objfile->CopyData(offset: core_memory_entry->data.GetRangeBase() + |
255 | offset + 16, |
256 | length: sizeof(uuid_t), dst: uuid_bytes)) { |
257 | UUID uuid(uuid_bytes, sizeof(uuid_t)); |
258 | if (uuid.IsValid()) { |
259 | LLDB_LOGF(log, |
260 | "ProcessMachCore::LoadBinaryViaLowmemUUID: found " |
261 | "binary uuid %s at low memory address 0x%"PRIx64, |
262 | uuid.GetAsString().c_str(), addr); |
263 | // We have no address specified, only a UUID. Load it at the file |
264 | // address. |
265 | const bool value_is_offset = true; |
266 | const bool force_symbol_search = true; |
267 | const bool notify = true; |
268 | const bool set_address_in_target = true; |
269 | const bool allow_memory_image_last_resort = false; |
270 | if (DynamicLoader::LoadBinaryWithUUIDAndAddress( |
271 | process: this, name: llvm::StringRef(), uuid, value: 0, value_is_offset, |
272 | force_symbol_search, notify, set_address_in_target, |
273 | allow_memory_image_last_resort)) { |
274 | m_dyld_plugin_name = DynamicLoaderStatic::GetPluginNameStatic(); |
275 | } |
276 | // We found metadata saying which binary should be loaded; don't |
277 | // try an exhaustive search. |
278 | return true; |
279 | } |
280 | } |
281 | } |
282 | } |
283 | } |
284 | } |
285 | return false; |
286 | } |
287 | |
288 | bool ProcessMachCore::LoadBinariesViaMetadata() { |
289 | Log *log(GetLog(mask: LLDBLog::DynamicLoader | LLDBLog::Process)); |
290 | ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); |
291 | |
292 | addr_t objfile_binary_value; |
293 | bool objfile_binary_value_is_offset; |
294 | UUID objfile_binary_uuid; |
295 | ObjectFile::BinaryType type; |
296 | |
297 | // This will be set to true if we had a metadata hint |
298 | // specifying a UUID or address -- and we should not fall back |
299 | // to doing an exhaustive search. |
300 | bool found_binary_spec_in_metadata = false; |
301 | |
302 | if (core_objfile->GetCorefileMainBinaryInfo(value&: objfile_binary_value, |
303 | value_is_offset&: objfile_binary_value_is_offset, |
304 | uuid&: objfile_binary_uuid, type)) { |
305 | if (log) { |
306 | log->Printf(format: "ProcessMachCore::LoadBinariesViaMetadata: using binary hint " |
307 | "from 'main bin spec' " |
308 | "LC_NOTE with UUID %s value 0x%"PRIx64 |
309 | " value is offset %d and type %d", |
310 | objfile_binary_uuid.GetAsString().c_str(), |
311 | objfile_binary_value, objfile_binary_value_is_offset, type); |
312 | } |
313 | found_binary_spec_in_metadata = true; |
314 | |
315 | // If this is the xnu kernel, don't load it now. Note the correct |
316 | // DynamicLoader plugin to use, and the address of the kernel, and |
317 | // let the DynamicLoader handle the finding & loading of the binary. |
318 | if (type == ObjectFile::eBinaryTypeKernel) { |
319 | m_mach_kernel_addr = objfile_binary_value; |
320 | m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic(); |
321 | } else if (type == ObjectFile::eBinaryTypeUser) { |
322 | m_dyld_addr = objfile_binary_value; |
323 | m_dyld_plugin_name = DynamicLoaderMacOSXDYLD::GetPluginNameStatic(); |
324 | } else if (type == ObjectFile::eBinaryTypeUserAllImageInfos) { |
325 | m_dyld_all_image_infos_addr = objfile_binary_value; |
326 | m_dyld_plugin_name = DynamicLoaderMacOSXDYLD::GetPluginNameStatic(); |
327 | } else { |
328 | const bool force_symbol_search = true; |
329 | const bool notify = true; |
330 | const bool set_address_in_target = true; |
331 | const bool allow_memory_image_last_resort = false; |
332 | if (DynamicLoader::LoadBinaryWithUUIDAndAddress( |
333 | process: this, name: llvm::StringRef(), uuid: objfile_binary_uuid, |
334 | value: objfile_binary_value, value_is_offset: objfile_binary_value_is_offset, |
335 | force_symbol_search, notify, set_address_in_target, |
336 | allow_memory_image_last_resort)) { |
337 | m_dyld_plugin_name = DynamicLoaderStatic::GetPluginNameStatic(); |
338 | } |
339 | } |
340 | } |
341 | |
342 | // This checks for the presence of an LC_IDENT string in a core file; |
343 | // LC_IDENT is very obsolete and should not be used in new code, but if the |
344 | // load command is present, let's use the contents. |
345 | UUID ident_uuid; |
346 | addr_t ident_binary_addr = LLDB_INVALID_ADDRESS; |
347 | std::string corefile_identifier = core_objfile->GetIdentifierString(); |
348 | |
349 | // Search for UUID= and stext= strings in the identifier str. |
350 | if (corefile_identifier.find(s: "UUID=") != std::string::npos) { |
351 | size_t p = corefile_identifier.find(s: "UUID=") + strlen(s: "UUID="); |
352 | std::string uuid_str = corefile_identifier.substr(pos: p, n: 36); |
353 | ident_uuid.SetFromStringRef(uuid_str); |
354 | if (log) |
355 | log->Printf(format: "Got a UUID from LC_IDENT/kern ver str LC_NOTE: %s", |
356 | ident_uuid.GetAsString().c_str()); |
357 | found_binary_spec_in_metadata = true; |
358 | } |
359 | if (corefile_identifier.find(s: "stext=") != std::string::npos) { |
360 | size_t p = corefile_identifier.find(s: "stext=") + strlen(s: "stext="); |
361 | if (corefile_identifier[p] == '0' && corefile_identifier[p + 1] == 'x') { |
362 | ident_binary_addr = |
363 | ::strtoul(nptr: corefile_identifier.c_str() + p, endptr: nullptr, base: 16); |
364 | if (log) |
365 | log->Printf(format: "Got a load address from LC_IDENT/kern ver str " |
366 | "LC_NOTE: 0x%"PRIx64, |
367 | ident_binary_addr); |
368 | found_binary_spec_in_metadata = true; |
369 | } |
370 | } |
371 | |
372 | // Search for a "Darwin Kernel" str indicating kernel; else treat as |
373 | // standalone |
374 | if (corefile_identifier.find(s: "Darwin Kernel") != std::string::npos && |
375 | ident_uuid.IsValid() && ident_binary_addr != LLDB_INVALID_ADDRESS) { |
376 | if (log) |
377 | log->Printf( |
378 | format: "ProcessMachCore::LoadBinariesViaMetadata: Found kernel binary via " |
379 | "LC_IDENT/kern ver str LC_NOTE"); |
380 | m_mach_kernel_addr = ident_binary_addr; |
381 | found_binary_spec_in_metadata = true; |
382 | } else if (ident_uuid.IsValid()) { |
383 | // We have no address specified, only a UUID. Load it at the file |
384 | // address. |
385 | const bool value_is_offset = false; |
386 | const bool force_symbol_search = true; |
387 | const bool notify = true; |
388 | const bool set_address_in_target = true; |
389 | const bool allow_memory_image_last_resort = false; |
390 | if (DynamicLoader::LoadBinaryWithUUIDAndAddress( |
391 | process: this, name: llvm::StringRef(), uuid: ident_uuid, value: ident_binary_addr, |
392 | value_is_offset, force_symbol_search, notify, |
393 | set_address_in_target, allow_memory_image_last_resort)) { |
394 | found_binary_spec_in_metadata = true; |
395 | m_dyld_plugin_name = DynamicLoaderStatic::GetPluginNameStatic(); |
396 | } |
397 | } |
398 | |
399 | // Finally, load any binaries noted by "load binary" LC_NOTEs in the |
400 | // corefile |
401 | if (core_objfile->LoadCoreFileImages(process&: *this)) { |
402 | found_binary_spec_in_metadata = true; |
403 | m_dyld_plugin_name = DynamicLoaderStatic::GetPluginNameStatic(); |
404 | } |
405 | |
406 | if (!found_binary_spec_in_metadata && LoadBinaryViaLowmemUUID()) |
407 | found_binary_spec_in_metadata = true; |
408 | |
409 | // LoadCoreFileImges may have set the dynamic loader, e.g. in |
410 | // PlatformDarwinKernel::LoadPlatformBinaryAndSetup(). |
411 | // If we now have a dynamic loader, save its name so we don't |
412 | // un-set it later. |
413 | if (m_dyld_up) |
414 | m_dyld_plugin_name = GetDynamicLoader()->GetPluginName(); |
415 | |
416 | return found_binary_spec_in_metadata; |
417 | } |
418 | |
419 | void ProcessMachCore::LoadBinariesViaExhaustiveSearch() { |
420 | Log *log(GetLog(mask: LLDBLog::DynamicLoader | LLDBLog::Process)); |
421 | |
422 | // Search the pages of the corefile for dyld or mach kernel |
423 | // binaries. There may be multiple things that look like a kernel |
424 | // in the corefile; disambiguating to the correct one can be difficult. |
425 | |
426 | std::vector<addr_t> dylds_found; |
427 | std::vector<addr_t> kernels_found; |
428 | |
429 | // To do an exhaustive search, we'll need to create data extractors |
430 | // to get correctly sized/endianness fields. If we had a main binary |
431 | // already, we would have set the Target to that - so here we'll use |
432 | // the corefile's cputype/cpusubtype as the best guess. |
433 | if (!GetTarget().GetArchitecture().IsValid()) { |
434 | // The corefile's architecture is our best starting point. |
435 | ArchSpec arch(m_core_module_sp->GetArchitecture()); |
436 | if (arch.IsValid()) { |
437 | LLDB_LOGF(log, |
438 | "ProcessMachCore::%s: Setting target ArchSpec based on " |
439 | "corefile mach-o cputype/cpusubtype", |
440 | __FUNCTION__); |
441 | GetTarget().SetArchitecture(arch_spec: arch); |
442 | } |
443 | } |
444 | |
445 | const size_t num_core_aranges = m_core_aranges.GetSize(); |
446 | for (size_t i = 0; i < num_core_aranges; ++i) { |
447 | const VMRangeToFileOffset::Entry *entry = m_core_aranges.GetEntryAtIndex(i); |
448 | lldb::addr_t section_vm_addr_start = entry->GetRangeBase(); |
449 | lldb::addr_t section_vm_addr_end = entry->GetRangeEnd(); |
450 | for (lldb::addr_t section_vm_addr = section_vm_addr_start; |
451 | section_vm_addr < section_vm_addr_end; section_vm_addr += 0x1000) { |
452 | addr_t dyld, kernel; |
453 | if (CheckAddressForDyldOrKernel(addr: section_vm_addr, dyld, kernel)) { |
454 | if (dyld != LLDB_INVALID_ADDRESS) |
455 | dylds_found.push_back(x: dyld); |
456 | if (kernel != LLDB_INVALID_ADDRESS) |
457 | kernels_found.push_back(x: kernel); |
458 | } |
459 | } |
460 | } |
461 | |
462 | // If we found more than one dyld mach-o header in the corefile, |
463 | // pick the first one. |
464 | if (dylds_found.size() > 0) |
465 | m_dyld_addr = dylds_found[0]; |
466 | if (kernels_found.size() > 0) |
467 | m_mach_kernel_addr = kernels_found[0]; |
468 | |
469 | // Zero or one kernels found, we're done. |
470 | if (kernels_found.size() < 2) |
471 | return; |
472 | |
473 | // In the case of multiple kernel images found in the core file via |
474 | // exhaustive search, we may not pick the correct one. See if the |
475 | // DynamicLoaderDarwinKernel's search heuristics might identify the correct |
476 | // one. |
477 | |
478 | // SearchForDarwinKernel will call this class' GetImageInfoAddress method |
479 | // which will give it the addresses we already have. |
480 | // Save those aside and set |
481 | // m_mach_kernel_addr/m_dyld_addr to an invalid address temporarily so |
482 | // DynamicLoaderDarwinKernel does a real search for the kernel using its |
483 | // own heuristics. |
484 | |
485 | addr_t saved_mach_kernel_addr = m_mach_kernel_addr; |
486 | addr_t saved_user_dyld_addr = m_dyld_addr; |
487 | m_mach_kernel_addr = LLDB_INVALID_ADDRESS; |
488 | m_dyld_addr = LLDB_INVALID_ADDRESS; |
489 | m_dyld_all_image_infos_addr = LLDB_INVALID_ADDRESS; |
490 | |
491 | addr_t better_kernel_address = |
492 | DynamicLoaderDarwinKernel::SearchForDarwinKernel(process: this); |
493 | |
494 | m_mach_kernel_addr = saved_mach_kernel_addr; |
495 | m_dyld_addr = saved_user_dyld_addr; |
496 | |
497 | if (better_kernel_address != LLDB_INVALID_ADDRESS) { |
498 | LLDB_LOGF(log, |
499 | "ProcessMachCore::%s: Using " |
500 | "the kernel address " |
501 | "from DynamicLoaderDarwinKernel", |
502 | __FUNCTION__); |
503 | m_mach_kernel_addr = better_kernel_address; |
504 | } |
505 | } |
506 | |
507 | void ProcessMachCore::LoadBinariesAndSetDYLD() { |
508 | Log *log(GetLog(mask: LLDBLog::DynamicLoader | LLDBLog::Process)); |
509 | |
510 | bool found_binary_spec_in_metadata = LoadBinariesViaMetadata(); |
511 | if (!found_binary_spec_in_metadata) |
512 | LoadBinariesViaExhaustiveSearch(); |
513 | |
514 | if (m_dyld_plugin_name.empty()) { |
515 | // If we found both a user-process dyld and a kernel binary, we need to |
516 | // decide which to prefer. |
517 | if (GetCorefilePreference() == eKernelCorefile) { |
518 | if (m_mach_kernel_addr != LLDB_INVALID_ADDRESS) { |
519 | LLDB_LOGF(log, |
520 | "ProcessMachCore::%s: Using kernel " |
521 | "corefile image " |
522 | "at 0x%"PRIx64, |
523 | __FUNCTION__, m_mach_kernel_addr); |
524 | m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic(); |
525 | } else if (m_dyld_addr != LLDB_INVALID_ADDRESS) { |
526 | LLDB_LOGF(log, |
527 | "ProcessMachCore::%s: Using user process dyld " |
528 | "image at 0x%"PRIx64, |
529 | __FUNCTION__, m_dyld_addr); |
530 | m_dyld_plugin_name = DynamicLoaderMacOSXDYLD::GetPluginNameStatic(); |
531 | } else if (m_dyld_all_image_infos_addr != LLDB_INVALID_ADDRESS) { |
532 | LLDB_LOGF(log, |
533 | "ProcessMachCore::%s: Using user process dyld " |
534 | "dyld_all_image_infos at 0x%"PRIx64, |
535 | __FUNCTION__, m_dyld_all_image_infos_addr); |
536 | m_dyld_plugin_name = DynamicLoaderMacOSXDYLD::GetPluginNameStatic(); |
537 | } |
538 | } else { |
539 | if (m_dyld_addr != LLDB_INVALID_ADDRESS) { |
540 | LLDB_LOGF(log, |
541 | "ProcessMachCore::%s: Using user process dyld " |
542 | "image at 0x%"PRIx64, |
543 | __FUNCTION__, m_dyld_addr); |
544 | m_dyld_plugin_name = DynamicLoaderMacOSXDYLD::GetPluginNameStatic(); |
545 | } else if (m_dyld_all_image_infos_addr != LLDB_INVALID_ADDRESS) { |
546 | LLDB_LOGF(log, |
547 | "ProcessMachCore::%s: Using user process dyld " |
548 | "dyld_all_image_infos at 0x%"PRIx64, |
549 | __FUNCTION__, m_dyld_all_image_infos_addr); |
550 | } else if (m_mach_kernel_addr != LLDB_INVALID_ADDRESS) { |
551 | LLDB_LOGF(log, |
552 | "ProcessMachCore::%s: Using kernel " |
553 | "corefile image " |
554 | "at 0x%"PRIx64, |
555 | __FUNCTION__, m_mach_kernel_addr); |
556 | m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic(); |
557 | } |
558 | } |
559 | } |
560 | } |
561 | |
562 | void ProcessMachCore::CleanupMemoryRegionPermissions() { |
563 | if (m_dyld_plugin_name != DynamicLoaderMacOSXDYLD::GetPluginNameStatic()) { |
564 | // For non-user process core files, the permissions on the core file |
565 | // segments are usually meaningless, they may be just "read", because we're |
566 | // dealing with kernel coredumps or early startup coredumps and the dumper |
567 | // is grabbing pages of memory without knowing what they are. If they |
568 | // aren't marked as "executable", that can break the unwinder which will |
569 | // check a pc value to see if it is in an executable segment and stop the |
570 | // backtrace early if it is not ("executable" and "unknown" would both be |
571 | // fine, but "not executable" will break the unwinder). |
572 | size_t core_range_infos_size = m_core_range_infos.GetSize(); |
573 | for (size_t i = 0; i < core_range_infos_size; i++) { |
574 | VMRangeToPermissions::Entry *ent = |
575 | m_core_range_infos.GetMutableEntryAtIndex(i); |
576 | ent->data = lldb::ePermissionsReadable | lldb::ePermissionsExecutable; |
577 | } |
578 | } |
579 | } |
580 | |
581 | // Process Control |
582 | Status ProcessMachCore::DoLoadCore() { |
583 | Status error; |
584 | if (!m_core_module_sp) { |
585 | error = Status::FromErrorString(str: "invalid core module"); |
586 | return error; |
587 | } |
588 | Log *log(GetLog(mask: LLDBLog::DynamicLoader | LLDBLog::Target)); |
589 | |
590 | ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); |
591 | if (core_objfile == nullptr) { |
592 | error = Status::FromErrorString(str: "invalid core object file"); |
593 | return error; |
594 | } |
595 | |
596 | SetCanJIT(false); |
597 | |
598 | // If we have an executable binary in the Target already, |
599 | // use that to set the Target's ArchSpec. |
600 | // |
601 | // Don't initialize the ArchSpec based on the corefile's cputype/cpusubtype |
602 | // here, the corefile creator may not know the correct subtype of the code |
603 | // that is executing, initialize the Target to that, and if the |
604 | // main binary has Python code which initializes based on the Target arch, |
605 | // get the wrong subtype value. |
606 | ModuleSP exe_module_sp = GetTarget().GetExecutableModule(); |
607 | if (exe_module_sp && exe_module_sp->GetArchitecture().IsValid()) { |
608 | LLDB_LOGF(log, |
609 | "ProcessMachCore::%s: Was given binary + corefile, setting " |
610 | "target ArchSpec to binary to start", |
611 | __FUNCTION__); |
612 | GetTarget().SetArchitecture(arch_spec: exe_module_sp->GetArchitecture()); |
613 | } |
614 | |
615 | CreateMemoryRegions(); |
616 | |
617 | LoadBinariesAndSetDYLD(); |
618 | |
619 | CleanupMemoryRegionPermissions(); |
620 | |
621 | exe_module_sp = GetTarget().GetExecutableModule(); |
622 | if (exe_module_sp && exe_module_sp->GetArchitecture().IsValid()) { |
623 | LLDB_LOGF(log, |
624 | "ProcessMachCore::%s: have executable binary in the Target " |
625 | "after metadata/scan. Setting Target's ArchSpec based on " |
626 | "that.", |
627 | __FUNCTION__); |
628 | GetTarget().SetArchitecture(arch_spec: exe_module_sp->GetArchitecture()); |
629 | } else { |
630 | // The corefile's architecture is our best starting point. |
631 | ArchSpec arch(m_core_module_sp->GetArchitecture()); |
632 | if (arch.IsValid()) { |
633 | LLDB_LOGF(log, |
634 | "ProcessMachCore::%s: Setting target ArchSpec based on " |
635 | "corefile mach-o cputype/cpusubtype", |
636 | __FUNCTION__); |
637 | GetTarget().SetArchitecture(arch_spec: arch); |
638 | } |
639 | } |
640 | |
641 | AddressableBits addressable_bits = core_objfile->GetAddressableBits(); |
642 | SetAddressableBitMasks(addressable_bits); |
643 | |
644 | return error; |
645 | } |
646 | |
647 | lldb_private::DynamicLoader *ProcessMachCore::GetDynamicLoader() { |
648 | if (m_dyld_up.get() == nullptr) |
649 | m_dyld_up.reset(p: DynamicLoader::FindPlugin(process: this, plugin_name: m_dyld_plugin_name)); |
650 | return m_dyld_up.get(); |
651 | } |
652 | |
653 | bool ProcessMachCore::DoUpdateThreadList(ThreadList &old_thread_list, |
654 | ThreadList &new_thread_list) { |
655 | if (old_thread_list.GetSize(can_update: false) == 0) { |
656 | // Make up the thread the first time this is called so we can setup our one |
657 | // and only core thread state. |
658 | ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); |
659 | |
660 | if (core_objfile) { |
661 | const uint32_t num_threads = core_objfile->GetNumThreadContexts(); |
662 | std::vector<lldb::tid_t> tids; |
663 | if (core_objfile->GetCorefileThreadExtraInfos(tids)) { |
664 | assert(tids.size() == num_threads); |
665 | |
666 | // Find highest tid value. |
667 | lldb::tid_t highest_tid = 0; |
668 | for (uint32_t i = 0; i < num_threads; i++) { |
669 | if (tids[i] != LLDB_INVALID_THREAD_ID && tids[i] > highest_tid) |
670 | highest_tid = tids[i]; |
671 | } |
672 | lldb::tid_t current_unused_tid = highest_tid + 1; |
673 | for (uint32_t i = 0; i < num_threads; i++) { |
674 | if (tids[i] == LLDB_INVALID_THREAD_ID) { |
675 | tids[i] = current_unused_tid++; |
676 | } |
677 | } |
678 | } else { |
679 | // No metadata, insert numbers sequentially from 0. |
680 | for (uint32_t i = 0; i < num_threads; i++) { |
681 | tids.push_back(x: i); |
682 | } |
683 | } |
684 | |
685 | for (uint32_t i = 0; i < num_threads; i++) { |
686 | ThreadSP thread_sp = |
687 | std::make_shared<ThreadMachCore>(args&: *this, args&: tids[i], args&: i); |
688 | new_thread_list.AddThread(thread_sp); |
689 | } |
690 | } |
691 | } else { |
692 | const uint32_t num_threads = old_thread_list.GetSize(can_update: false); |
693 | for (uint32_t i = 0; i < num_threads; ++i) |
694 | new_thread_list.AddThread(thread_sp: old_thread_list.GetThreadAtIndex(idx: i, can_update: false)); |
695 | } |
696 | return new_thread_list.GetSize(can_update: false) > 0; |
697 | } |
698 | |
699 | void ProcessMachCore::RefreshStateAfterStop() { |
700 | // Let all threads recover from stopping and do any clean up based on the |
701 | // previous thread state (if any). |
702 | m_thread_list.RefreshStateAfterStop(); |
703 | // SetThreadStopInfo (m_last_stop_packet); |
704 | } |
705 | |
706 | Status ProcessMachCore::DoDestroy() { return Status(); } |
707 | |
708 | // Process Queries |
709 | |
710 | bool ProcessMachCore::IsAlive() { return true; } |
711 | |
712 | bool ProcessMachCore::WarnBeforeDetach() const { return false; } |
713 | |
714 | // Process Memory |
715 | size_t ProcessMachCore::ReadMemory(addr_t addr, void *buf, size_t size, |
716 | Status &error) { |
717 | // Don't allow the caching that lldb_private::Process::ReadMemory does since |
718 | // in core files we have it all cached our our core file anyway. |
719 | return DoReadMemory(addr: FixAnyAddress(pc: addr), buf, size, error); |
720 | } |
721 | |
722 | size_t ProcessMachCore::DoReadMemory(addr_t addr, void *buf, size_t size, |
723 | Status &error) { |
724 | ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); |
725 | size_t bytes_read = 0; |
726 | |
727 | if (core_objfile) { |
728 | // Segments are not always contiguous in mach-o core files. We have core |
729 | // files that have segments like: |
730 | // Address Size File off File size |
731 | // ---------- ---------- ---------- ---------- |
732 | // LC_SEGMENT 0x000f6000 0x00001000 0x1d509ee8 0x00001000 --- --- 0 |
733 | // 0x00000000 __TEXT LC_SEGMENT 0x0f600000 0x00100000 0x1d50aee8 0x00100000 |
734 | // --- --- 0 0x00000000 __TEXT LC_SEGMENT 0x000f7000 0x00001000 |
735 | // 0x1d60aee8 0x00001000 --- --- 0 0x00000000 __TEXT |
736 | // |
737 | // Any if the user executes the following command: |
738 | // |
739 | // (lldb) mem read 0xf6ff0 |
740 | // |
741 | // We would attempt to read 32 bytes from 0xf6ff0 but would only get 16 |
742 | // unless we loop through consecutive memory ranges that are contiguous in |
743 | // the address space, but not in the file data. |
744 | while (bytes_read < size) { |
745 | const addr_t curr_addr = addr + bytes_read; |
746 | const VMRangeToFileOffset::Entry *core_memory_entry = |
747 | m_core_aranges.FindEntryThatContains(addr: curr_addr); |
748 | |
749 | if (core_memory_entry) { |
750 | const addr_t offset = curr_addr - core_memory_entry->GetRangeBase(); |
751 | const addr_t bytes_left = core_memory_entry->GetRangeEnd() - curr_addr; |
752 | const size_t bytes_to_read = |
753 | std::min(a: size - bytes_read, b: (size_t)bytes_left); |
754 | const size_t curr_bytes_read = core_objfile->CopyData( |
755 | offset: core_memory_entry->data.GetRangeBase() + offset, length: bytes_to_read, |
756 | dst: (char *)buf + bytes_read); |
757 | if (curr_bytes_read == 0) |
758 | break; |
759 | bytes_read += curr_bytes_read; |
760 | } else { |
761 | // Only set the error if we didn't read any bytes |
762 | if (bytes_read == 0) |
763 | error = Status::FromErrorStringWithFormat( |
764 | format: "core file does not contain 0x%"PRIx64, curr_addr); |
765 | break; |
766 | } |
767 | } |
768 | } |
769 | |
770 | return bytes_read; |
771 | } |
772 | |
773 | Status ProcessMachCore::DoGetMemoryRegionInfo(addr_t load_addr, |
774 | MemoryRegionInfo ®ion_info) { |
775 | region_info.Clear(); |
776 | const VMRangeToPermissions::Entry *permission_entry = |
777 | m_core_range_infos.FindEntryThatContainsOrFollows(addr: load_addr); |
778 | if (permission_entry) { |
779 | if (permission_entry->Contains(r: load_addr)) { |
780 | region_info.GetRange().SetRangeBase(permission_entry->GetRangeBase()); |
781 | region_info.GetRange().SetRangeEnd(permission_entry->GetRangeEnd()); |
782 | const Flags permissions(permission_entry->data); |
783 | region_info.SetReadable(permissions.Test(bit: ePermissionsReadable) |
784 | ? MemoryRegionInfo::eYes |
785 | : MemoryRegionInfo::eNo); |
786 | region_info.SetWritable(permissions.Test(bit: ePermissionsWritable) |
787 | ? MemoryRegionInfo::eYes |
788 | : MemoryRegionInfo::eNo); |
789 | region_info.SetExecutable(permissions.Test(bit: ePermissionsExecutable) |
790 | ? MemoryRegionInfo::eYes |
791 | : MemoryRegionInfo::eNo); |
792 | region_info.SetMapped(MemoryRegionInfo::eYes); |
793 | } else if (load_addr < permission_entry->GetRangeBase()) { |
794 | region_info.GetRange().SetRangeBase(load_addr); |
795 | region_info.GetRange().SetRangeEnd(permission_entry->GetRangeBase()); |
796 | region_info.SetReadable(MemoryRegionInfo::eNo); |
797 | region_info.SetWritable(MemoryRegionInfo::eNo); |
798 | region_info.SetExecutable(MemoryRegionInfo::eNo); |
799 | region_info.SetMapped(MemoryRegionInfo::eNo); |
800 | } |
801 | return Status(); |
802 | } |
803 | |
804 | region_info.GetRange().SetRangeBase(load_addr); |
805 | region_info.GetRange().SetRangeEnd(LLDB_INVALID_ADDRESS); |
806 | region_info.SetReadable(MemoryRegionInfo::eNo); |
807 | region_info.SetWritable(MemoryRegionInfo::eNo); |
808 | region_info.SetExecutable(MemoryRegionInfo::eNo); |
809 | region_info.SetMapped(MemoryRegionInfo::eNo); |
810 | return Status(); |
811 | } |
812 | |
813 | void ProcessMachCore::Clear() { m_thread_list.Clear(); } |
814 | |
815 | void ProcessMachCore::Initialize() { |
816 | static llvm::once_flag g_once_flag; |
817 | |
818 | llvm::call_once(flag&: g_once_flag, F: []() { |
819 | PluginManager::RegisterPlugin(name: GetPluginNameStatic(), |
820 | description: GetPluginDescriptionStatic(), create_callback: CreateInstance); |
821 | }); |
822 | } |
823 | |
824 | addr_t ProcessMachCore::GetImageInfoAddress() { |
825 | // The DynamicLoader plugin will call back in to this Process |
826 | // method to find the virtual address of one of these: |
827 | // 1. The xnu mach kernel binary Mach-O header |
828 | // 2. The dyld binary Mach-O header |
829 | // 3. dyld's dyld_all_image_infos object |
830 | // |
831 | // DynamicLoaderMacOSX will accept either the dyld Mach-O header |
832 | // address or the dyld_all_image_infos interchangably, no need |
833 | // to distinguish between them. It disambiguates by the Mach-O |
834 | // file magic number at the start. |
835 | if (GetCorefilePreference() == eKernelCorefile) { |
836 | if (m_mach_kernel_addr != LLDB_INVALID_ADDRESS) |
837 | return m_mach_kernel_addr; |
838 | if (m_dyld_addr != LLDB_INVALID_ADDRESS) |
839 | return m_dyld_addr; |
840 | } else { |
841 | if (m_dyld_addr != LLDB_INVALID_ADDRESS) |
842 | return m_dyld_addr; |
843 | if (m_mach_kernel_addr != LLDB_INVALID_ADDRESS) |
844 | return m_mach_kernel_addr; |
845 | } |
846 | |
847 | // m_dyld_addr and m_mach_kernel_addr both |
848 | // invalid, return m_dyld_all_image_infos_addr |
849 | // in case it has a useful value. |
850 | return m_dyld_all_image_infos_addr; |
851 | } |
852 | |
853 | lldb_private::ObjectFile *ProcessMachCore::GetCoreObjectFile() { |
854 | return m_core_module_sp->GetObjectFile(); |
855 | } |
856 |
Definitions
- GetPluginDescriptionStatic
- Terminate
- CreateInstance
- CanDebug
- ProcessMachCore
- ~ProcessMachCore
- CheckAddressForDyldOrKernel
- CreateMemoryRegions
- LoadBinaryViaLowmemUUID
- LoadBinariesViaMetadata
- LoadBinariesViaExhaustiveSearch
- LoadBinariesAndSetDYLD
- CleanupMemoryRegionPermissions
- DoLoadCore
- GetDynamicLoader
- DoUpdateThreadList
- RefreshStateAfterStop
- DoDestroy
- IsAlive
- WarnBeforeDetach
- ReadMemory
- DoReadMemory
- DoGetMemoryRegionInfo
- Clear
- Initialize
- GetImageInfoAddress
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more