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 = 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 ; |
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_mach_kernel_addr(LLDB_INVALID_ADDRESS) {} |
118 | |
119 | // Destructor |
120 | ProcessMachCore::~ProcessMachCore() { |
121 | Clear(); |
122 | // We need to call finalize on the process before destroying ourselves to |
123 | // make sure all of the broadcaster cleanup goes as planned. If we destruct |
124 | // this class, then Process::~Process() might have problems trying to fully |
125 | // destroy the broadcaster. |
126 | Finalize(destructing: true /* destructing */); |
127 | } |
128 | |
129 | bool ProcessMachCore::CheckAddressForDyldOrKernel(lldb::addr_t addr, |
130 | addr_t &dyld, |
131 | addr_t &kernel) { |
132 | Log *log(GetLog(mask: LLDBLog::DynamicLoader | LLDBLog::Process)); |
133 | llvm::MachO::mach_header ; |
134 | Status error; |
135 | dyld = kernel = LLDB_INVALID_ADDRESS; |
136 | if (DoReadMemory(addr, buf: &header, size: sizeof(header), error) != sizeof(header)) |
137 | return false; |
138 | if (header.magic == llvm::MachO::MH_CIGAM || |
139 | header.magic == llvm::MachO::MH_CIGAM_64) { |
140 | header.magic = llvm::byteswap<uint32_t>(V: header.magic); |
141 | header.cputype = llvm::byteswap<uint32_t>(V: header.cputype); |
142 | header.cpusubtype = llvm::byteswap<uint32_t>(V: header.cpusubtype); |
143 | header.filetype = llvm::byteswap<uint32_t>(V: header.filetype); |
144 | header.ncmds = llvm::byteswap<uint32_t>(V: header.ncmds); |
145 | header.sizeofcmds = llvm::byteswap<uint32_t>(V: header.sizeofcmds); |
146 | header.flags = llvm::byteswap<uint32_t>(V: header.flags); |
147 | } |
148 | |
149 | if (header.magic == llvm::MachO::MH_MAGIC || |
150 | header.magic == llvm::MachO::MH_MAGIC_64) { |
151 | // Check MH_EXECUTABLE to see if we can find the mach image that contains |
152 | // the shared library list. The dynamic loader (dyld) is what contains the |
153 | // list for user applications, and the mach kernel contains a global that |
154 | // has the list of kexts to load |
155 | switch (header.filetype) { |
156 | case llvm::MachO::MH_DYLINKER: |
157 | LLDB_LOGF(log, |
158 | "ProcessMachCore::%s found a user " |
159 | "process dyld binary image at 0x%" PRIx64, |
160 | __FUNCTION__, addr); |
161 | dyld = addr; |
162 | return true; |
163 | |
164 | case llvm::MachO::MH_EXECUTE: |
165 | // Check MH_EXECUTABLE file types to see if the dynamic link object flag |
166 | // is NOT set. If it isn't, then we have a mach_kernel. |
167 | if ((header.flags & llvm::MachO::MH_DYLDLINK) == 0) { |
168 | LLDB_LOGF(log, |
169 | "ProcessMachCore::%s found a mach " |
170 | "kernel binary image at 0x%" PRIx64, |
171 | __FUNCTION__, addr); |
172 | // Address of the mach kernel "struct mach_header" in the core file. |
173 | kernel = addr; |
174 | return true; |
175 | } |
176 | break; |
177 | } |
178 | } |
179 | return false; |
180 | } |
181 | |
182 | void ProcessMachCore::CreateMemoryRegions() { |
183 | ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); |
184 | SectionList *section_list = core_objfile->GetSectionList(); |
185 | const uint32_t num_sections = section_list->GetNumSections(depth: 0); |
186 | |
187 | bool ranges_are_sorted = true; |
188 | addr_t vm_addr = 0; |
189 | for (uint32_t i = 0; i < num_sections; ++i) { |
190 | Section *section = section_list->GetSectionAtIndex(idx: i).get(); |
191 | if (section && section->GetFileSize() > 0) { |
192 | lldb::addr_t section_vm_addr = section->GetFileAddress(); |
193 | FileRange file_range(section->GetFileOffset(), section->GetFileSize()); |
194 | VMRangeToFileOffset::Entry range_entry( |
195 | section_vm_addr, section->GetByteSize(), file_range); |
196 | |
197 | if (vm_addr > section_vm_addr) |
198 | ranges_are_sorted = false; |
199 | vm_addr = section->GetFileAddress(); |
200 | VMRangeToFileOffset::Entry *last_entry = m_core_aranges.Back(); |
201 | |
202 | if (last_entry && |
203 | last_entry->GetRangeEnd() == range_entry.GetRangeBase() && |
204 | last_entry->data.GetRangeEnd() == range_entry.data.GetRangeBase()) { |
205 | last_entry->SetRangeEnd(range_entry.GetRangeEnd()); |
206 | last_entry->data.SetRangeEnd(range_entry.data.GetRangeEnd()); |
207 | } else { |
208 | m_core_aranges.Append(entry: range_entry); |
209 | } |
210 | // Some core files don't fill in the permissions correctly. If that is |
211 | // the case assume read + execute so clients don't think the memory is |
212 | // not readable, or executable. The memory isn't writable since this |
213 | // plug-in doesn't implement DoWriteMemory. |
214 | uint32_t permissions = section->GetPermissions(); |
215 | if (permissions == 0) |
216 | permissions = lldb::ePermissionsReadable | lldb::ePermissionsExecutable; |
217 | m_core_range_infos.Append(entry: VMRangeToPermissions::Entry( |
218 | section_vm_addr, section->GetByteSize(), permissions)); |
219 | } |
220 | } |
221 | if (!ranges_are_sorted) { |
222 | m_core_aranges.Sort(); |
223 | m_core_range_infos.Sort(); |
224 | } |
225 | } |
226 | |
227 | // Some corefiles have a UUID stored in a low memory |
228 | // address. We inspect a set list of addresses for |
229 | // the characters 'uuid' and 16 bytes later there will |
230 | // be a uuid_t UUID. If we can find a binary that |
231 | // matches the UUID, it is loaded with no slide in the target. |
232 | bool ProcessMachCore::LoadBinaryViaLowmemUUID() { |
233 | Log *log(GetLog(mask: LLDBLog::DynamicLoader | LLDBLog::Process)); |
234 | ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); |
235 | |
236 | uint64_t lowmem_uuid_addresses[] = {0x2000204, 0x1000204, 0x1000020, 0x4204, |
237 | 0x1204, 0x1020, 0x4020, 0xc00, |
238 | 0xC0, 0}; |
239 | |
240 | for (uint64_t addr : lowmem_uuid_addresses) { |
241 | const VMRangeToFileOffset::Entry *core_memory_entry = |
242 | m_core_aranges.FindEntryThatContains(addr); |
243 | if (core_memory_entry) { |
244 | const addr_t offset = addr - core_memory_entry->GetRangeBase(); |
245 | const addr_t bytes_left = core_memory_entry->GetRangeEnd() - addr; |
246 | // (4-bytes 'uuid' + 12 bytes pad for align + 16 bytes uuid_t) == 32 bytes |
247 | if (bytes_left >= 32) { |
248 | char strbuf[4]; |
249 | if (core_objfile->CopyData( |
250 | offset: core_memory_entry->data.GetRangeBase() + offset, length: 4, dst: &strbuf) && |
251 | strncmp(s1: "uuid" , s2: (char *)&strbuf, n: 4) == 0) { |
252 | uuid_t uuid_bytes; |
253 | if (core_objfile->CopyData(offset: core_memory_entry->data.GetRangeBase() + |
254 | offset + 16, |
255 | length: sizeof(uuid_t), dst: uuid_bytes)) { |
256 | UUID uuid(uuid_bytes, sizeof(uuid_t)); |
257 | if (uuid.IsValid()) { |
258 | LLDB_LOGF(log, |
259 | "ProcessMachCore::LoadBinaryViaLowmemUUID: found " |
260 | "binary uuid %s at low memory address 0x%" PRIx64, |
261 | uuid.GetAsString().c_str(), addr); |
262 | // We have no address specified, only a UUID. Load it at the file |
263 | // address. |
264 | const bool value_is_offset = true; |
265 | const bool force_symbol_search = true; |
266 | const bool notify = true; |
267 | const bool set_address_in_target = true; |
268 | const bool allow_memory_image_last_resort = false; |
269 | if (DynamicLoader::LoadBinaryWithUUIDAndAddress( |
270 | process: this, name: llvm::StringRef(), uuid, value: 0, value_is_offset, |
271 | force_symbol_search, notify, set_address_in_target, |
272 | allow_memory_image_last_resort)) { |
273 | m_dyld_plugin_name = DynamicLoaderStatic::GetPluginNameStatic(); |
274 | } |
275 | // We found metadata saying which binary should be loaded; don't |
276 | // try an exhaustive search. |
277 | return true; |
278 | } |
279 | } |
280 | } |
281 | } |
282 | } |
283 | } |
284 | return false; |
285 | } |
286 | |
287 | bool ProcessMachCore::LoadBinariesViaMetadata() { |
288 | Log *log(GetLog(mask: LLDBLog::DynamicLoader | LLDBLog::Process)); |
289 | ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); |
290 | |
291 | addr_t objfile_binary_value; |
292 | bool objfile_binary_value_is_offset; |
293 | UUID objfile_binary_uuid; |
294 | ObjectFile::BinaryType type; |
295 | |
296 | // This will be set to true if we had a metadata hint |
297 | // specifying a UUID or address -- and we should not fall back |
298 | // to doing an exhaustive search. |
299 | bool found_binary_spec_in_metadata = false; |
300 | |
301 | if (core_objfile->GetCorefileMainBinaryInfo(value&: objfile_binary_value, |
302 | value_is_offset&: objfile_binary_value_is_offset, |
303 | uuid&: objfile_binary_uuid, type)) { |
304 | if (log) { |
305 | log->Printf(format: "ProcessMachCore::LoadBinariesViaMetadata: using binary hint " |
306 | "from 'main bin spec' " |
307 | "LC_NOTE with UUID %s value 0x%" PRIx64 |
308 | " value is offset %d and type %d" , |
309 | objfile_binary_uuid.GetAsString().c_str(), |
310 | objfile_binary_value, objfile_binary_value_is_offset, type); |
311 | } |
312 | found_binary_spec_in_metadata = true; |
313 | |
314 | // If this is the xnu kernel, don't load it now. Note the correct |
315 | // DynamicLoader plugin to use, and the address of the kernel, and |
316 | // let the DynamicLoader handle the finding & loading of the binary. |
317 | if (type == ObjectFile::eBinaryTypeKernel) { |
318 | m_mach_kernel_addr = objfile_binary_value; |
319 | m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic(); |
320 | } else if (type == ObjectFile::eBinaryTypeUser) { |
321 | m_dyld_addr = objfile_binary_value; |
322 | m_dyld_plugin_name = DynamicLoaderMacOSXDYLD::GetPluginNameStatic(); |
323 | } else { |
324 | const bool force_symbol_search = true; |
325 | const bool notify = true; |
326 | const bool set_address_in_target = true; |
327 | const bool allow_memory_image_last_resort = false; |
328 | if (DynamicLoader::LoadBinaryWithUUIDAndAddress( |
329 | process: this, name: llvm::StringRef(), uuid: objfile_binary_uuid, |
330 | value: objfile_binary_value, value_is_offset: objfile_binary_value_is_offset, |
331 | force_symbol_search, notify, set_address_in_target, |
332 | allow_memory_image_last_resort)) { |
333 | m_dyld_plugin_name = DynamicLoaderStatic::GetPluginNameStatic(); |
334 | } |
335 | } |
336 | } |
337 | |
338 | // This checks for the presence of an LC_IDENT string in a core file; |
339 | // LC_IDENT is very obsolete and should not be used in new code, but if the |
340 | // load command is present, let's use the contents. |
341 | UUID ident_uuid; |
342 | addr_t ident_binary_addr = LLDB_INVALID_ADDRESS; |
343 | std::string corefile_identifier = core_objfile->GetIdentifierString(); |
344 | |
345 | // Search for UUID= and stext= strings in the identifier str. |
346 | if (corefile_identifier.find(s: "UUID=" ) != std::string::npos) { |
347 | size_t p = corefile_identifier.find(s: "UUID=" ) + strlen(s: "UUID=" ); |
348 | std::string uuid_str = corefile_identifier.substr(pos: p, n: 36); |
349 | ident_uuid.SetFromStringRef(uuid_str); |
350 | if (log) |
351 | log->Printf(format: "Got a UUID from LC_IDENT/kern ver str LC_NOTE: %s" , |
352 | ident_uuid.GetAsString().c_str()); |
353 | found_binary_spec_in_metadata = true; |
354 | } |
355 | if (corefile_identifier.find(s: "stext=" ) != std::string::npos) { |
356 | size_t p = corefile_identifier.find(s: "stext=" ) + strlen(s: "stext=" ); |
357 | if (corefile_identifier[p] == '0' && corefile_identifier[p + 1] == 'x') { |
358 | ident_binary_addr = |
359 | ::strtoul(nptr: corefile_identifier.c_str() + p, endptr: nullptr, base: 16); |
360 | if (log) |
361 | log->Printf(format: "Got a load address from LC_IDENT/kern ver str " |
362 | "LC_NOTE: 0x%" PRIx64, |
363 | ident_binary_addr); |
364 | found_binary_spec_in_metadata = true; |
365 | } |
366 | } |
367 | |
368 | // Search for a "Darwin Kernel" str indicating kernel; else treat as |
369 | // standalone |
370 | if (corefile_identifier.find(s: "Darwin Kernel" ) != std::string::npos && |
371 | ident_uuid.IsValid() && ident_binary_addr != LLDB_INVALID_ADDRESS) { |
372 | if (log) |
373 | log->Printf( |
374 | format: "ProcessMachCore::LoadBinariesViaMetadata: Found kernel binary via " |
375 | "LC_IDENT/kern ver str LC_NOTE" ); |
376 | m_mach_kernel_addr = ident_binary_addr; |
377 | found_binary_spec_in_metadata = true; |
378 | } else if (ident_uuid.IsValid()) { |
379 | // We have no address specified, only a UUID. Load it at the file |
380 | // address. |
381 | const bool value_is_offset = false; |
382 | const bool force_symbol_search = true; |
383 | const bool notify = true; |
384 | const bool set_address_in_target = true; |
385 | const bool allow_memory_image_last_resort = false; |
386 | if (DynamicLoader::LoadBinaryWithUUIDAndAddress( |
387 | process: this, name: llvm::StringRef(), uuid: ident_uuid, value: ident_binary_addr, |
388 | value_is_offset, force_symbol_search, notify, |
389 | set_address_in_target, allow_memory_image_last_resort)) { |
390 | found_binary_spec_in_metadata = true; |
391 | m_dyld_plugin_name = DynamicLoaderStatic::GetPluginNameStatic(); |
392 | } |
393 | } |
394 | |
395 | // Finally, load any binaries noted by "load binary" LC_NOTEs in the |
396 | // corefile |
397 | if (core_objfile->LoadCoreFileImages(process&: *this)) { |
398 | found_binary_spec_in_metadata = true; |
399 | m_dyld_plugin_name = DynamicLoaderStatic::GetPluginNameStatic(); |
400 | } |
401 | |
402 | if (!found_binary_spec_in_metadata && LoadBinaryViaLowmemUUID()) |
403 | found_binary_spec_in_metadata = true; |
404 | |
405 | // LoadCoreFileImges may have set the dynamic loader, e.g. in |
406 | // PlatformDarwinKernel::LoadPlatformBinaryAndSetup(). |
407 | // If we now have a dynamic loader, save its name so we don't |
408 | // un-set it later. |
409 | if (m_dyld_up) |
410 | m_dyld_plugin_name = GetDynamicLoader()->GetPluginName(); |
411 | |
412 | return found_binary_spec_in_metadata; |
413 | } |
414 | |
415 | void ProcessMachCore::LoadBinariesViaExhaustiveSearch() { |
416 | Log *log(GetLog(mask: LLDBLog::DynamicLoader | LLDBLog::Process)); |
417 | |
418 | // Search the pages of the corefile for dyld or mach kernel |
419 | // binaries. There may be multiple things that look like a kernel |
420 | // in the corefile; disambiguating to the correct one can be difficult. |
421 | |
422 | std::vector<addr_t> dylds_found; |
423 | std::vector<addr_t> kernels_found; |
424 | |
425 | const size_t num_core_aranges = m_core_aranges.GetSize(); |
426 | for (size_t i = 0; i < num_core_aranges; ++i) { |
427 | const VMRangeToFileOffset::Entry *entry = m_core_aranges.GetEntryAtIndex(i); |
428 | lldb::addr_t section_vm_addr_start = entry->GetRangeBase(); |
429 | lldb::addr_t section_vm_addr_end = entry->GetRangeEnd(); |
430 | for (lldb::addr_t section_vm_addr = section_vm_addr_start; |
431 | section_vm_addr < section_vm_addr_end; section_vm_addr += 0x1000) { |
432 | addr_t dyld, kernel; |
433 | if (CheckAddressForDyldOrKernel(addr: section_vm_addr, dyld, kernel)) { |
434 | if (dyld != LLDB_INVALID_ADDRESS) |
435 | dylds_found.push_back(x: dyld); |
436 | if (kernel != LLDB_INVALID_ADDRESS) |
437 | kernels_found.push_back(x: kernel); |
438 | } |
439 | } |
440 | } |
441 | |
442 | // If we found more than one dyld mach-o header in the corefile, |
443 | // pick the first one. |
444 | if (dylds_found.size() > 0) |
445 | m_dyld_addr = dylds_found[0]; |
446 | if (kernels_found.size() > 0) |
447 | m_mach_kernel_addr = kernels_found[0]; |
448 | |
449 | // Zero or one kernels found, we're done. |
450 | if (kernels_found.size() < 2) |
451 | return; |
452 | |
453 | // In the case of multiple kernel images found in the core file via |
454 | // exhaustive search, we may not pick the correct one. See if the |
455 | // DynamicLoaderDarwinKernel's search heuristics might identify the correct |
456 | // one. |
457 | |
458 | // SearchForDarwinKernel will call this class' GetImageInfoAddress method |
459 | // which will give it the addresses we already have. |
460 | // Save those aside and set |
461 | // m_mach_kernel_addr/m_dyld_addr to an invalid address temporarily so |
462 | // DynamicLoaderDarwinKernel does a real search for the kernel using its |
463 | // own heuristics. |
464 | |
465 | addr_t saved_mach_kernel_addr = m_mach_kernel_addr; |
466 | addr_t saved_user_dyld_addr = m_dyld_addr; |
467 | m_mach_kernel_addr = LLDB_INVALID_ADDRESS; |
468 | m_dyld_addr = LLDB_INVALID_ADDRESS; |
469 | |
470 | addr_t better_kernel_address = |
471 | DynamicLoaderDarwinKernel::SearchForDarwinKernel(process: this); |
472 | |
473 | m_mach_kernel_addr = saved_mach_kernel_addr; |
474 | m_dyld_addr = saved_user_dyld_addr; |
475 | |
476 | if (better_kernel_address != LLDB_INVALID_ADDRESS) { |
477 | LLDB_LOGF(log, |
478 | "ProcessMachCore::%s: Using " |
479 | "the kernel address " |
480 | "from DynamicLoaderDarwinKernel" , |
481 | __FUNCTION__); |
482 | m_mach_kernel_addr = better_kernel_address; |
483 | } |
484 | } |
485 | |
486 | void ProcessMachCore::LoadBinariesAndSetDYLD() { |
487 | Log *log(GetLog(mask: LLDBLog::DynamicLoader | LLDBLog::Process)); |
488 | |
489 | bool found_binary_spec_in_metadata = LoadBinariesViaMetadata(); |
490 | if (!found_binary_spec_in_metadata) |
491 | LoadBinariesViaExhaustiveSearch(); |
492 | |
493 | if (m_dyld_plugin_name.empty()) { |
494 | // If we found both a user-process dyld and a kernel binary, we need to |
495 | // decide which to prefer. |
496 | if (GetCorefilePreference() == eKernelCorefile) { |
497 | if (m_mach_kernel_addr != LLDB_INVALID_ADDRESS) { |
498 | LLDB_LOGF(log, |
499 | "ProcessMachCore::%s: Using kernel " |
500 | "corefile image " |
501 | "at 0x%" PRIx64, |
502 | __FUNCTION__, m_mach_kernel_addr); |
503 | m_dyld_plugin_name = DynamicLoaderDarwinKernel::GetPluginNameStatic(); |
504 | } else if (m_dyld_addr != LLDB_INVALID_ADDRESS) { |
505 | LLDB_LOGF(log, |
506 | "ProcessMachCore::%s: Using user process dyld " |
507 | "image at 0x%" PRIx64, |
508 | __FUNCTION__, m_dyld_addr); |
509 | m_dyld_plugin_name = DynamicLoaderMacOSXDYLD::GetPluginNameStatic(); |
510 | } |
511 | } else { |
512 | if (m_dyld_addr != LLDB_INVALID_ADDRESS) { |
513 | LLDB_LOGF(log, |
514 | "ProcessMachCore::%s: Using user process dyld " |
515 | "image at 0x%" PRIx64, |
516 | __FUNCTION__, m_dyld_addr); |
517 | m_dyld_plugin_name = DynamicLoaderMacOSXDYLD::GetPluginNameStatic(); |
518 | } else 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 | } |
526 | } |
527 | } |
528 | } |
529 | |
530 | void ProcessMachCore::CleanupMemoryRegionPermissions() { |
531 | if (m_dyld_plugin_name != DynamicLoaderMacOSXDYLD::GetPluginNameStatic()) { |
532 | // For non-user process core files, the permissions on the core file |
533 | // segments are usually meaningless, they may be just "read", because we're |
534 | // dealing with kernel coredumps or early startup coredumps and the dumper |
535 | // is grabbing pages of memory without knowing what they are. If they |
536 | // aren't marked as "executable", that can break the unwinder which will |
537 | // check a pc value to see if it is in an executable segment and stop the |
538 | // backtrace early if it is not ("executable" and "unknown" would both be |
539 | // fine, but "not executable" will break the unwinder). |
540 | size_t core_range_infos_size = m_core_range_infos.GetSize(); |
541 | for (size_t i = 0; i < core_range_infos_size; i++) { |
542 | VMRangeToPermissions::Entry *ent = |
543 | m_core_range_infos.GetMutableEntryAtIndex(i); |
544 | ent->data = lldb::ePermissionsReadable | lldb::ePermissionsExecutable; |
545 | } |
546 | } |
547 | } |
548 | |
549 | // Process Control |
550 | Status ProcessMachCore::DoLoadCore() { |
551 | Status error; |
552 | if (!m_core_module_sp) { |
553 | error.SetErrorString("invalid core module" ); |
554 | return error; |
555 | } |
556 | |
557 | ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); |
558 | if (core_objfile == nullptr) { |
559 | error.SetErrorString("invalid core object file" ); |
560 | return error; |
561 | } |
562 | |
563 | SetCanJIT(false); |
564 | |
565 | // The corefile's architecture is our best starting point. |
566 | ArchSpec arch(m_core_module_sp->GetArchitecture()); |
567 | if (arch.IsValid()) |
568 | GetTarget().SetArchitecture(arch_spec: arch); |
569 | |
570 | CreateMemoryRegions(); |
571 | |
572 | LoadBinariesAndSetDYLD(); |
573 | |
574 | CleanupMemoryRegionPermissions(); |
575 | |
576 | AddressableBits addressable_bits = core_objfile->GetAddressableBits(); |
577 | SetAddressableBitMasks(addressable_bits); |
578 | |
579 | return error; |
580 | } |
581 | |
582 | lldb_private::DynamicLoader *ProcessMachCore::GetDynamicLoader() { |
583 | if (m_dyld_up.get() == nullptr) |
584 | m_dyld_up.reset(p: DynamicLoader::FindPlugin(process: this, plugin_name: m_dyld_plugin_name)); |
585 | return m_dyld_up.get(); |
586 | } |
587 | |
588 | bool ProcessMachCore::DoUpdateThreadList(ThreadList &old_thread_list, |
589 | ThreadList &new_thread_list) { |
590 | if (old_thread_list.GetSize(can_update: false) == 0) { |
591 | // Make up the thread the first time this is called so we can setup our one |
592 | // and only core thread state. |
593 | ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); |
594 | |
595 | if (core_objfile) { |
596 | std::set<tid_t> used_tids; |
597 | const uint32_t num_threads = core_objfile->GetNumThreadContexts(); |
598 | std::vector<tid_t> tids; |
599 | if (core_objfile->GetCorefileThreadExtraInfos(tids)) { |
600 | assert(tids.size() == num_threads); |
601 | |
602 | // Find highest tid value. |
603 | tid_t highest_tid = 0; |
604 | for (uint32_t i = 0; i < num_threads; i++) { |
605 | if (tids[i] != LLDB_INVALID_THREAD_ID && tids[i] > highest_tid) |
606 | highest_tid = tids[i]; |
607 | } |
608 | tid_t current_unused_tid = highest_tid + 1; |
609 | for (uint32_t i = 0; i < num_threads; i++) { |
610 | if (tids[i] == LLDB_INVALID_THREAD_ID) { |
611 | tids[i] = current_unused_tid++; |
612 | } |
613 | } |
614 | } else { |
615 | // No metadata, insert numbers sequentially from 0. |
616 | for (uint32_t i = 0; i < num_threads; i++) { |
617 | tids.push_back(x: i); |
618 | } |
619 | } |
620 | |
621 | for (uint32_t i = 0; i < num_threads; i++) { |
622 | ThreadSP thread_sp = |
623 | std::make_shared<ThreadMachCore>(args&: *this, args&: tids[i], args&: i); |
624 | new_thread_list.AddThread(thread_sp); |
625 | } |
626 | } |
627 | } else { |
628 | const uint32_t num_threads = old_thread_list.GetSize(can_update: false); |
629 | for (uint32_t i = 0; i < num_threads; ++i) |
630 | new_thread_list.AddThread(thread_sp: old_thread_list.GetThreadAtIndex(idx: i, can_update: false)); |
631 | } |
632 | return new_thread_list.GetSize(can_update: false) > 0; |
633 | } |
634 | |
635 | void ProcessMachCore::RefreshStateAfterStop() { |
636 | // Let all threads recover from stopping and do any clean up based on the |
637 | // previous thread state (if any). |
638 | m_thread_list.RefreshStateAfterStop(); |
639 | // SetThreadStopInfo (m_last_stop_packet); |
640 | } |
641 | |
642 | Status ProcessMachCore::DoDestroy() { return Status(); } |
643 | |
644 | // Process Queries |
645 | |
646 | bool ProcessMachCore::IsAlive() { return true; } |
647 | |
648 | bool ProcessMachCore::WarnBeforeDetach() const { return false; } |
649 | |
650 | // Process Memory |
651 | size_t ProcessMachCore::ReadMemory(addr_t addr, void *buf, size_t size, |
652 | Status &error) { |
653 | // Don't allow the caching that lldb_private::Process::ReadMemory does since |
654 | // in core files we have it all cached our our core file anyway. |
655 | return DoReadMemory(addr: FixAnyAddress(pc: addr), buf, size, error); |
656 | } |
657 | |
658 | size_t ProcessMachCore::DoReadMemory(addr_t addr, void *buf, size_t size, |
659 | Status &error) { |
660 | ObjectFile *core_objfile = m_core_module_sp->GetObjectFile(); |
661 | size_t bytes_read = 0; |
662 | |
663 | if (core_objfile) { |
664 | // Segments are not always contiguous in mach-o core files. We have core |
665 | // files that have segments like: |
666 | // Address Size File off File size |
667 | // ---------- ---------- ---------- ---------- |
668 | // LC_SEGMENT 0x000f6000 0x00001000 0x1d509ee8 0x00001000 --- --- 0 |
669 | // 0x00000000 __TEXT LC_SEGMENT 0x0f600000 0x00100000 0x1d50aee8 0x00100000 |
670 | // --- --- 0 0x00000000 __TEXT LC_SEGMENT 0x000f7000 0x00001000 |
671 | // 0x1d60aee8 0x00001000 --- --- 0 0x00000000 __TEXT |
672 | // |
673 | // Any if the user executes the following command: |
674 | // |
675 | // (lldb) mem read 0xf6ff0 |
676 | // |
677 | // We would attempt to read 32 bytes from 0xf6ff0 but would only get 16 |
678 | // unless we loop through consecutive memory ranges that are contiguous in |
679 | // the address space, but not in the file data. |
680 | while (bytes_read < size) { |
681 | const addr_t curr_addr = addr + bytes_read; |
682 | const VMRangeToFileOffset::Entry *core_memory_entry = |
683 | m_core_aranges.FindEntryThatContains(addr: curr_addr); |
684 | |
685 | if (core_memory_entry) { |
686 | const addr_t offset = curr_addr - core_memory_entry->GetRangeBase(); |
687 | const addr_t bytes_left = core_memory_entry->GetRangeEnd() - curr_addr; |
688 | const size_t bytes_to_read = |
689 | std::min(a: size - bytes_read, b: (size_t)bytes_left); |
690 | const size_t curr_bytes_read = core_objfile->CopyData( |
691 | offset: core_memory_entry->data.GetRangeBase() + offset, length: bytes_to_read, |
692 | dst: (char *)buf + bytes_read); |
693 | if (curr_bytes_read == 0) |
694 | break; |
695 | bytes_read += curr_bytes_read; |
696 | } else { |
697 | // Only set the error if we didn't read any bytes |
698 | if (bytes_read == 0) |
699 | error.SetErrorStringWithFormat( |
700 | "core file does not contain 0x%" PRIx64, curr_addr); |
701 | break; |
702 | } |
703 | } |
704 | } |
705 | |
706 | return bytes_read; |
707 | } |
708 | |
709 | Status ProcessMachCore::DoGetMemoryRegionInfo(addr_t load_addr, |
710 | MemoryRegionInfo ®ion_info) { |
711 | region_info.Clear(); |
712 | const VMRangeToPermissions::Entry *permission_entry = |
713 | m_core_range_infos.FindEntryThatContainsOrFollows(addr: load_addr); |
714 | if (permission_entry) { |
715 | if (permission_entry->Contains(r: load_addr)) { |
716 | region_info.GetRange().SetRangeBase(permission_entry->GetRangeBase()); |
717 | region_info.GetRange().SetRangeEnd(permission_entry->GetRangeEnd()); |
718 | const Flags permissions(permission_entry->data); |
719 | region_info.SetReadable(permissions.Test(bit: ePermissionsReadable) |
720 | ? MemoryRegionInfo::eYes |
721 | : MemoryRegionInfo::eNo); |
722 | region_info.SetWritable(permissions.Test(bit: ePermissionsWritable) |
723 | ? MemoryRegionInfo::eYes |
724 | : MemoryRegionInfo::eNo); |
725 | region_info.SetExecutable(permissions.Test(bit: ePermissionsExecutable) |
726 | ? MemoryRegionInfo::eYes |
727 | : MemoryRegionInfo::eNo); |
728 | region_info.SetMapped(MemoryRegionInfo::eYes); |
729 | } else if (load_addr < permission_entry->GetRangeBase()) { |
730 | region_info.GetRange().SetRangeBase(load_addr); |
731 | region_info.GetRange().SetRangeEnd(permission_entry->GetRangeBase()); |
732 | region_info.SetReadable(MemoryRegionInfo::eNo); |
733 | region_info.SetWritable(MemoryRegionInfo::eNo); |
734 | region_info.SetExecutable(MemoryRegionInfo::eNo); |
735 | region_info.SetMapped(MemoryRegionInfo::eNo); |
736 | } |
737 | return Status(); |
738 | } |
739 | |
740 | region_info.GetRange().SetRangeBase(load_addr); |
741 | region_info.GetRange().SetRangeEnd(LLDB_INVALID_ADDRESS); |
742 | region_info.SetReadable(MemoryRegionInfo::eNo); |
743 | region_info.SetWritable(MemoryRegionInfo::eNo); |
744 | region_info.SetExecutable(MemoryRegionInfo::eNo); |
745 | region_info.SetMapped(MemoryRegionInfo::eNo); |
746 | return Status(); |
747 | } |
748 | |
749 | void ProcessMachCore::Clear() { m_thread_list.Clear(); } |
750 | |
751 | void ProcessMachCore::Initialize() { |
752 | static llvm::once_flag g_once_flag; |
753 | |
754 | llvm::call_once(flag&: g_once_flag, F: []() { |
755 | PluginManager::RegisterPlugin(name: GetPluginNameStatic(), |
756 | description: GetPluginDescriptionStatic(), create_callback: CreateInstance); |
757 | }); |
758 | } |
759 | |
760 | addr_t ProcessMachCore::GetImageInfoAddress() { |
761 | // If we found both a user-process dyld and a kernel binary, we need to |
762 | // decide which to prefer. |
763 | if (GetCorefilePreference() == eKernelCorefile) { |
764 | if (m_mach_kernel_addr != LLDB_INVALID_ADDRESS) { |
765 | return m_mach_kernel_addr; |
766 | } |
767 | return m_dyld_addr; |
768 | } else { |
769 | if (m_dyld_addr != LLDB_INVALID_ADDRESS) { |
770 | return m_dyld_addr; |
771 | } |
772 | return m_mach_kernel_addr; |
773 | } |
774 | } |
775 | |
776 | lldb_private::ObjectFile *ProcessMachCore::GetCoreObjectFile() { |
777 | return m_core_module_sp->GetObjectFile(); |
778 | } |
779 | |