| //===-- DynamicLoaderDarwin.cpp -------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "DynamicLoaderDarwin.h" | |
| #include "lldb/Breakpoint/StoppointCallbackContext.h" | |
| #include "lldb/Core/Debugger.h" | |
| #include "lldb/Core/Module.h" | |
| #include "lldb/Core/ModuleSpec.h" | |
| #include "lldb/Core/PluginManager.h" | |
| #include "lldb/Core/Section.h" | |
| #include "lldb/Expression/DiagnosticManager.h" | |
| #include "lldb/Host/FileSystem.h" | |
| #include "lldb/Host/HostInfo.h" | |
| #include "lldb/Symbol/Function.h" | |
| #include "lldb/Symbol/ObjectFile.h" | |
| #include "lldb/Target/ABI.h" | |
| #include "lldb/Target/RegisterContext.h" | |
| #include "lldb/Target/StackFrame.h" | |
| #include "lldb/Target/Target.h" | |
| #include "lldb/Target/Thread.h" | |
| #include "lldb/Target/ThreadPlanCallFunction.h" | |
| #include "lldb/Target/ThreadPlanRunToAddress.h" | |
| #include "lldb/Target/ThreadPlanRunToBreakpoint.h" | |
| #include "lldb/Target/ThreadPlanStepInstruction.h" | |
| #include "lldb/Utility/DataBuffer.h" | |
| #include "lldb/Utility/DataBufferHeap.h" | |
| #include "lldb/Utility/LLDBLog.h" | |
| #include "lldb/Utility/Log.h" | |
| #include "lldb/Utility/State.h" | |
| #include "llvm/Support/ThreadPool.h" | |
| #include "Plugins/LanguageRuntime/ObjC/ObjCLanguageRuntime.h" | |
| #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" | |
| //#define ENABLE_DEBUG_PRINTF // COMMENT THIS LINE OUT PRIOR TO CHECKIN | |
| #ifdef ENABLE_DEBUG_PRINTF | |
| #include <cstdio> | |
| #define DEBUG_PRINTF(fmt, ...) printf(fmt, ##__VA_ARGS__) | |
| #else | |
| #define DEBUG_PRINTF(fmt, ...) | |
| #endif | |
| #include <memory> | |
| using namespace lldb; | |
| using namespace lldb_private; | |
| // Constructor | |
| DynamicLoaderDarwin::DynamicLoaderDarwin(Process *process) | |
| : DynamicLoader(process), m_dyld_module_wp(), m_libpthread_module_wp(), | |
| m_pthread_getspecific_addr(), m_tid_to_tls_map(), m_dyld_image_infos(), | |
| m_dyld_image_infos_stop_id(UINT32_MAX), m_dyld(), m_mutex() {} | |
| // Destructor | |
| DynamicLoaderDarwin::~DynamicLoaderDarwin() = default; | |
| /// Called after attaching a process. | |
| /// | |
| /// Allow DynamicLoader plug-ins to execute some code after | |
| /// attaching to a process. | |
| void DynamicLoaderDarwin::DidAttach() { | |
| PrivateInitialize(m_process); | |
| DoInitialImageFetch(); | |
| SetNotificationBreakpoint(); | |
| } | |
| /// Called after attaching a process. | |
| /// | |
| /// Allow DynamicLoader plug-ins to execute some code after | |
| /// attaching to a process. | |
| void DynamicLoaderDarwin::DidLaunch() { | |
| PrivateInitialize(m_process); | |
| DoInitialImageFetch(); | |
| SetNotificationBreakpoint(); | |
| } | |
| // Clear out the state of this class. | |
| void DynamicLoaderDarwin::Clear(bool clear_process) { | |
| std::lock_guard<std::recursive_mutex> guard(m_mutex); | |
| if (clear_process) | |
| m_process = nullptr; | |
| m_dyld_image_infos.clear(); | |
| m_dyld_image_infos_stop_id = UINT32_MAX; | |
| m_dyld.Clear(false); | |
| } | |
| ModuleSP DynamicLoaderDarwin::FindTargetModuleForImageInfo( | |
| const ImageInfo &image_info, bool can_create, bool *did_create_ptr) { | |
| if (did_create_ptr) | |
| *did_create_ptr = false; | |
| Target &target = m_process->GetTarget(); | |
| const ModuleList &target_images = target.GetImages(); | |
| ModuleSpec module_spec(image_info.file_spec); | |
| module_spec.GetUUID() = image_info.uuid; | |
| // macCatalyst support: Request matching os/environment. | |
| { | |
| auto &target_triple = target.GetArchitecture().GetTriple(); | |
| if (target_triple.getOS() == llvm::Triple::IOS && | |
| target_triple.getEnvironment() == llvm::Triple::MacABI) { | |
| // Request the macCatalyst variant of frameworks that have both | |
| // a PLATFORM_MACOS and a PLATFORM_MACCATALYST load command. | |
| module_spec.GetArchitecture() = ArchSpec(target_triple); | |
| } | |
| } | |
| ModuleSP module_sp(target_images.FindFirstModule(module_spec)); | |
| if (module_sp && !module_spec.GetUUID().IsValid() && | |
| !module_sp->GetUUID().IsValid()) { | |
| // No UUID, we must rely upon the cached module modification time and the | |
| // modification time of the file on disk | |
| if (module_sp->GetModificationTime() != | |
| FileSystem::Instance().GetModificationTime(module_sp->GetFileSpec())) | |
| module_sp.reset(); | |
| } | |
| if (module_sp || !can_create) | |
| return module_sp; | |
| // See if we have this binary in the Target or the global Module | |
| // cache already. | |
| module_sp = target.GetOrCreateModule(module_spec, /*notify=*/false); | |
| if (!module_sp && | |
| HostInfo::GetArchitecture().IsCompatibleMatch(target.GetArchitecture())) { | |
| SharedCacheImageInfo image_info; | |
| // If we have a shared cache filepath and UUID, ask HostInfo | |
| // if it can provide the SourceCacheImageInfo for the binary | |
| // out of that shared cache. Search by the Module's UUID if | |
| // available, else the filepath. | |
| addr_t sc_base_addr; | |
| UUID sc_uuid; | |
| LazyBool using_sc; | |
| LazyBool private_sc; | |
| FileSpec sc_path; | |
| std::optional<uint64_t> size; | |
| SymbolSharedCacheUse sc_mode = ModuleList::GetGlobalModuleListProperties() | |
| .GetSharedCacheBinaryLoading(); | |
| if (GetSharedCacheInformation(sc_base_addr, sc_uuid, using_sc, private_sc, | |
| sc_path, size) && | |
| sc_uuid) { | |
| if (module_spec.GetUUID()) | |
| image_info = HostInfo::GetSharedCacheImageInfo(module_spec.GetUUID(), | |
| sc_uuid, sc_mode); | |
| else { | |
| std::string filepath = module_spec.GetFileSpec().GetPath(); | |
| image_info = | |
| HostInfo::GetSharedCacheImageInfo(filepath, sc_uuid, sc_mode); | |
| } | |
| } else { | |
| // Fall back to looking lldb's own shared cache by filename | |
| std::string filepath = module_spec.GetFileSpec().GetPath(); | |
| image_info = HostInfo::GetSharedCacheImageInfo(filepath, sc_mode); | |
| } | |
| // If we found it and it has the correct UUID, let's proceed with | |
| // creating a module from the memory contents. | |
| if (image_info.GetUUID() && | |
| (!module_spec.GetUUID() || | |
| module_spec.GetUUID() == image_info.GetUUID())) { | |
| ModuleSpec shared_cache_spec(module_spec.GetFileSpec(), | |
| image_info.GetUUID(), | |
| image_info.GetExtractor()); | |
| module_sp = | |
| target.GetOrCreateModule(shared_cache_spec, false /* notify */); | |
| } | |
| } | |
| // We'll call Target::ModulesDidLoad after all the modules have been | |
| // added to the target, don't let it be called for every one. | |
| if (!module_sp || module_sp->GetObjectFile() == nullptr) { | |
| llvm::Expected<ModuleSP> module_sp_or_err = m_process->ReadModuleFromMemory( | |
| image_info.file_spec, image_info.address, | |
| image_info.mh_and_load_cmd_size); | |
| if (auto err = module_sp_or_err.takeError()) { | |
| LLDB_LOG_ERROR(GetLog(LLDBLog::DynamicLoader), std::move(err), | |
| "Failed to load module from memory: {0}"); | |
| return {}; | |
| } | |
| module_sp = *module_sp_or_err; | |
| } | |
| if (did_create_ptr) | |
| *did_create_ptr = (bool)module_sp; | |
| return module_sp; | |
| } | |
| void DynamicLoaderDarwin::UnloadImages( | |
| const std::vector<lldb::addr_t> &solib_addresses) { | |
| std::lock_guard<std::recursive_mutex> guard(m_mutex); | |
| if (m_process->GetStopID() == m_dyld_image_infos_stop_id) | |
| return; | |
| Log *log = GetLog(LLDBLog::DynamicLoader); | |
| Target &target = m_process->GetTarget(); | |
| LLDB_LOGF(log, "Removing %" PRId64 " modules.", | |
| (uint64_t)solib_addresses.size()); | |
| ModuleList unloaded_module_list; | |
| for (addr_t solib_addr : solib_addresses) { | |
| Address header; | |
| if (header.SetLoadAddress(solib_addr, &target)) { | |
| if (header.GetOffset() == 0) { | |
| ModuleSP module_to_remove(header.GetModule()); | |
| if (module_to_remove.get()) { | |
| LLDB_LOGF(log, "Removing module at address 0x%" PRIx64, solib_addr); | |
| // remove the sections from the Target | |
| UnloadSections(module_to_remove); | |
| // add this to the list of modules to remove | |
| unloaded_module_list.AppendIfNeeded(module_to_remove); | |
| // remove the entry from the m_dyld_image_infos | |
| ImageInfo::collection::iterator pos, end = m_dyld_image_infos.end(); | |
| for (pos = m_dyld_image_infos.begin(); pos != end; pos++) { | |
| if (solib_addr == (*pos).address) { | |
| m_dyld_image_infos.erase(pos); | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if (unloaded_module_list.GetSize() > 0) { | |
| if (log) { | |
| log->PutCString("Unloaded:"); | |
| unloaded_module_list.LogUUIDAndPaths( | |
| log, "DynamicLoaderDarwin::UnloadModules"); | |
| } | |
| m_process->GetTarget().GetImages().Remove(unloaded_module_list); | |
| m_dyld_image_infos_stop_id = m_process->GetStopID(); | |
| } | |
| } | |
| void DynamicLoaderDarwin::UnloadAllImages() { | |
| Log *log = GetLog(LLDBLog::DynamicLoader); | |
| ModuleList unloaded_modules_list; | |
| Target &target = m_process->GetTarget(); | |
| const ModuleList &target_modules = target.GetImages(); | |
| std::lock_guard<std::recursive_mutex> guard(target_modules.GetMutex()); | |
| ModuleSP dyld_sp(GetDYLDModule()); | |
| for (ModuleSP module_sp : target_modules.Modules()) { | |
| // Don't remove dyld - else we'll lose our breakpoint notifying us about | |
| // libraries being re-loaded... | |
| if (module_sp && module_sp != dyld_sp) { | |
| UnloadSections(module_sp); | |
| unloaded_modules_list.Append(module_sp); | |
| } | |
| } | |
| if (unloaded_modules_list.GetSize() != 0) { | |
| if (log) { | |
| log->PutCString("Unloaded:"); | |
| unloaded_modules_list.LogUUIDAndPaths( | |
| log, "DynamicLoaderDarwin::UnloadAllImages"); | |
| } | |
| target.GetImages().Remove(unloaded_modules_list); | |
| m_dyld_image_infos.clear(); | |
| m_dyld_image_infos_stop_id = m_process->GetStopID(); | |
| } | |
| } | |
| // Update the load addresses for all segments in MODULE using the updated INFO | |
| // that is passed in. | |
| bool DynamicLoaderDarwin::UpdateImageLoadAddress(Module *module, | |
| ImageInfo &info) { | |
| bool changed = false; | |
| Log *log = GetLog(LLDBLog::DynamicLoader); | |
| if (module) { | |
| ObjectFile *image_object_file = module->GetObjectFile(); | |
| if (image_object_file) { | |
| SectionList *section_list = image_object_file->GetSectionList(); | |
| if (section_list) { | |
| std::vector<uint32_t> inaccessible_segment_indexes; | |
| // We now know the slide amount, so go through all sections and update | |
| // the load addresses with the correct values. | |
| const size_t num_segments = info.segments.size(); | |
| for (size_t i = 0; i < num_segments; ++i) { | |
| // Only load a segment if it has protections. Things like __PAGEZERO | |
| // don't have any protections, and they shouldn't be slid | |
| SectionSP section_sp( | |
| section_list->FindSectionByName(info.segments[i].name)); | |
| if (info.segments[i].maxprot == 0) { | |
| inaccessible_segment_indexes.push_back(i); | |
| } else { | |
| const addr_t new_section_load_addr = | |
| info.segments[i].vmaddr + info.slide; | |
| static ConstString g_section_name_LINKEDIT("__LINKEDIT"); | |
| if (section_sp) { | |
| // __LINKEDIT sections from files in the shared cache can overlap | |
| // so check to see what the segment name is and pass "false" so | |
| // we don't warn of overlapping "Section" objects, and "true" for | |
| // all other sections. | |
| const bool warn_multiple = | |
| section_sp->GetName() != g_section_name_LINKEDIT; | |
| // If a segment was eliminated for the in-memory image, | |
| // don't map it into lldb's target section load list. | |
| if (info.segments[i].vmsize == 0) { | |
| LLDB_LOG(log, "{0}: Omitting zero-size segment {1}", | |
| info.file_spec.GetFilename(), info.segments[i].name); | |
| continue; | |
| } | |
| if (info.segments[i].vmsize != section_sp->GetByteSize()) | |
| LLDB_LOG(log, | |
| "{0}: In-memory segment size for {1} is {2:x}" | |
| " but file segment size is {3:x}", | |
| info.file_spec.GetFilename(), info.segments[i].name, | |
| info.segments[i].vmsize, section_sp->GetByteSize()); | |
| changed = m_process->GetTarget().SetSectionLoadAddress( | |
| section_sp, new_section_load_addr, warn_multiple); | |
| } | |
| } | |
| } | |
| // If the loaded the file (it changed) and we have segments that are | |
| // not readable or writeable, add them to the invalid memory region | |
| // cache for the process. This will typically only be the __PAGEZERO | |
| // segment in the main executable. We might be able to apply this more | |
| // generally to more sections that have no protections in the future, | |
| // but for now we are going to just do __PAGEZERO. | |
| if (changed && !inaccessible_segment_indexes.empty()) { | |
| for (uint32_t i = 0; i < inaccessible_segment_indexes.size(); ++i) { | |
| const uint32_t seg_idx = inaccessible_segment_indexes[i]; | |
| SectionSP section_sp( | |
| section_list->FindSectionByName(info.segments[seg_idx].name)); | |
| if (section_sp) { | |
| static ConstString g_pagezero_section_name("__PAGEZERO"); | |
| if (g_pagezero_section_name == section_sp->GetName()) { | |
| // __PAGEZERO never slides... | |
| const lldb::addr_t vmaddr = info.segments[seg_idx].vmaddr; | |
| const lldb::addr_t vmsize = info.segments[seg_idx].vmsize; | |
| Process::LoadRange pagezero_range(vmaddr, vmsize); | |
| m_process->AddInvalidMemoryRegion(pagezero_range); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| // We might have an in memory image that was loaded as soon as it was created | |
| if (info.load_stop_id == m_process->GetStopID()) | |
| changed = true; | |
| else if (changed) { | |
| // Update the stop ID when this library was updated | |
| info.load_stop_id = m_process->GetStopID(); | |
| } | |
| return changed; | |
| } | |
| // Unload the segments in MODULE using the INFO that is passed in. | |
| bool DynamicLoaderDarwin::UnloadModuleSections(Module *module, | |
| ImageInfo &info) { | |
| bool changed = false; | |
| if (module) { | |
| ObjectFile *image_object_file = module->GetObjectFile(); | |
| if (image_object_file) { | |
| SectionList *section_list = image_object_file->GetSectionList(); | |
| if (section_list) { | |
| const size_t num_segments = info.segments.size(); | |
| for (size_t i = 0; i < num_segments; ++i) { | |
| SectionSP section_sp( | |
| section_list->FindSectionByName(info.segments[i].name)); | |
| if (section_sp) { | |
| const addr_t old_section_load_addr = | |
| info.segments[i].vmaddr + info.slide; | |
| if (m_process->GetTarget().SetSectionUnloaded( | |
| section_sp, old_section_load_addr)) | |
| changed = true; | |
| } else { | |
| Debugger::ReportWarning(llvm::formatv( | |
| "unable to find and unload segment named " | |
| "'{0}' in '{1}' in macosx dynamic loader plug-in", | |
| info.segments[i].name[0] ? info.segments[i].name : "<invalid>", | |
| image_object_file->GetFileSpec().GetPath())); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return changed; | |
| } | |
| // Given a JSON dictionary (from debugserver, most likely) of binary images | |
| // loaded in the inferior process, add the images to the ImageInfo collection. | |
| bool DynamicLoaderDarwin::JSONImageInformationIntoImageInfo( | |
| StructuredData::ObjectSP image_details, | |
| ImageInfo::collection &image_infos) { | |
| StructuredData::ObjectSP images_sp = | |
| image_details->GetAsDictionary()->GetValueForKey("images"); | |
| if (images_sp.get() == nullptr) | |
| return false; | |
| image_infos.resize(images_sp->GetAsArray()->GetSize()); | |
| for (size_t i = 0; i < image_infos.size(); i++) { | |
| StructuredData::ObjectSP image_sp = | |
| images_sp->GetAsArray()->GetItemAtIndex(i); | |
| if (image_sp.get() == nullptr || image_sp->GetAsDictionary() == nullptr) | |
| return false; | |
| StructuredData::Dictionary *image = image_sp->GetAsDictionary(); | |
| // clang-format off | |
| if (!image->HasKey("load_address") || | |
| !image->HasKey("pathname") || | |
| !image->HasKey("mach_header") || | |
| image->GetValueForKey("mach_header")->GetAsDictionary() == nullptr || | |
| !image->HasKey("segments") || | |
| image->GetValueForKey("segments")->GetAsArray() == nullptr || | |
| !image->HasKey("uuid")) { | |
| return false; | |
| } | |
| // clang-format on | |
| image_infos[i].address = | |
| image->GetValueForKey("load_address")->GetUnsignedIntegerValue(); | |
| image_infos[i].file_spec.SetFile( | |
| image->GetValueForKey("pathname")->GetAsString()->GetValue(), | |
| FileSpec::Style::native); | |
| StructuredData::Dictionary *mh = | |
| image->GetValueForKey("mach_header")->GetAsDictionary(); | |
| image_infos[i].header.magic = | |
| mh->GetValueForKey("magic")->GetUnsignedIntegerValue(); | |
| image_infos[i].header.cputype = | |
| mh->GetValueForKey("cputype")->GetUnsignedIntegerValue(); | |
| image_infos[i].header.cpusubtype = | |
| mh->GetValueForKey("cpusubtype")->GetUnsignedIntegerValue(); | |
| image_infos[i].header.filetype = | |
| mh->GetValueForKey("filetype")->GetUnsignedIntegerValue(); | |
| if (mh->HasKey("sizeof_mh_and_loadcmds")) | |
| image_infos[i].mh_and_load_cmd_size = | |
| mh->GetValueForKey("sizeof_mh_and_loadcmds") | |
| ->GetUnsignedIntegerValue(); | |
| if (image->HasKey("min_version_os_name")) { | |
| std::string os_name = | |
| std::string(image->GetValueForKey("min_version_os_name") | |
| ->GetAsString() | |
| ->GetValue()); | |
| if (os_name == "macosx") | |
| image_infos[i].os_type = llvm::Triple::MacOSX; | |
| else if (os_name == "ios" || os_name == "iphoneos") | |
| image_infos[i].os_type = llvm::Triple::IOS; | |
| else if (os_name == "tvos") | |
| image_infos[i].os_type = llvm::Triple::TvOS; | |
| else if (os_name == "watchos") | |
| image_infos[i].os_type = llvm::Triple::WatchOS; | |
| else if (os_name == "bridgeos") | |
| image_infos[i].os_type = llvm::Triple::BridgeOS; | |
| else if (os_name == "driverkit") | |
| image_infos[i].os_type = llvm::Triple::DriverKit; | |
| else if (os_name == "xros") | |
| image_infos[i].os_type = llvm::Triple::XROS; | |
| else if (os_name == "maccatalyst") { | |
| image_infos[i].os_type = llvm::Triple::IOS; | |
| image_infos[i].os_env = llvm::Triple::MacABI; | |
| } else if (os_name == "iossimulator") { | |
| image_infos[i].os_type = llvm::Triple::IOS; | |
| image_infos[i].os_env = llvm::Triple::Simulator; | |
| } else if (os_name == "tvossimulator") { | |
| image_infos[i].os_type = llvm::Triple::TvOS; | |
| image_infos[i].os_env = llvm::Triple::Simulator; | |
| } else if (os_name == "watchossimulator") { | |
| image_infos[i].os_type = llvm::Triple::WatchOS; | |
| image_infos[i].os_env = llvm::Triple::Simulator; | |
| } else if (os_name == "xrsimulator") { | |
| image_infos[i].os_type = llvm::Triple::XROS; | |
| image_infos[i].os_env = llvm::Triple::Simulator; | |
| } | |
| } | |
| if (image->HasKey("min_version_os_sdk")) { | |
| image_infos[i].min_version_os_sdk = | |
| std::string(image->GetValueForKey("min_version_os_sdk") | |
| ->GetAsString() | |
| ->GetValue()); | |
| } | |
| // Fields that aren't used by DynamicLoaderDarwin so debugserver doesn't | |
| // currently send them in the reply. | |
| if (mh->HasKey("flags")) | |
| image_infos[i].header.flags = | |
| mh->GetValueForKey("flags")->GetUnsignedIntegerValue(); | |
| else | |
| image_infos[i].header.flags = 0; | |
| if (mh->HasKey("ncmds")) | |
| image_infos[i].header.ncmds = | |
| mh->GetValueForKey("ncmds")->GetUnsignedIntegerValue(); | |
| else | |
| image_infos[i].header.ncmds = 0; | |
| if (mh->HasKey("sizeofcmds")) | |
| image_infos[i].header.sizeofcmds = | |
| mh->GetValueForKey("sizeofcmds")->GetUnsignedIntegerValue(); | |
| else | |
| image_infos[i].header.sizeofcmds = 0; | |
| StructuredData::Array *segments = | |
| image->GetValueForKey("segments")->GetAsArray(); | |
| uint32_t segcount = segments->GetSize(); | |
| for (size_t j = 0; j < segcount; j++) { | |
| Segment segment; | |
| StructuredData::Dictionary *seg = | |
| segments->GetItemAtIndex(j)->GetAsDictionary(); | |
| llvm::StringRef seg_name = | |
| seg->GetValueForKey("name")->GetAsString()->GetValue(); | |
| strncpy(segment.name, seg_name.data(), | |
| std::min(seg_name.size(), size_t(16))); | |
| segment.name[16] = '\0'; | |
| segment.vmaddr = seg->GetValueForKey("vmaddr")->GetUnsignedIntegerValue(); | |
| segment.vmsize = seg->GetValueForKey("vmsize")->GetUnsignedIntegerValue(); | |
| segment.fileoff = | |
| seg->GetValueForKey("fileoff")->GetUnsignedIntegerValue(); | |
| segment.filesize = | |
| seg->GetValueForKey("filesize")->GetUnsignedIntegerValue(); | |
| segment.maxprot = | |
| seg->GetValueForKey("maxprot")->GetUnsignedIntegerValue(); | |
| // Fields that aren't used by DynamicLoaderDarwin so debugserver doesn't | |
| // currently send them in the reply. | |
| if (seg->HasKey("initprot")) | |
| segment.initprot = | |
| seg->GetValueForKey("initprot")->GetUnsignedIntegerValue(); | |
| else | |
| segment.initprot = 0; | |
| if (seg->HasKey("flags")) | |
| segment.flags = seg->GetValueForKey("flags")->GetUnsignedIntegerValue(); | |
| else | |
| segment.flags = 0; | |
| if (seg->HasKey("nsects")) | |
| segment.nsects = | |
| seg->GetValueForKey("nsects")->GetUnsignedIntegerValue(); | |
| else | |
| segment.nsects = 0; | |
| image_infos[i].segments.push_back(segment); | |
| } | |
| image_infos[i].uuid.SetFromStringRef( | |
| image->GetValueForKey("uuid")->GetAsString()->GetValue()); | |
| // All sections listed in the dyld image info structure will all either be | |
| // fixed up already, or they will all be off by a single slide amount that | |
| // is determined by finding the first segment that is at file offset zero | |
| // which also has bytes (a file size that is greater than zero) in the | |
| // object file. | |
| // Determine the slide amount (if any) | |
| const size_t num_sections = image_infos[i].segments.size(); | |
| for (size_t k = 0; k < num_sections; ++k) { | |
| // Iterate through the object file sections to find the first section | |
| // that starts of file offset zero and that has bytes in the file... | |
| if ((image_infos[i].segments[k].fileoff == 0 && | |
| image_infos[i].segments[k].filesize > 0) || | |
| (llvm::StringRef(image_infos[i].segments[k].name) == "__TEXT")) { | |
| image_infos[i].slide = | |
| image_infos[i].address - image_infos[i].segments[k].vmaddr; | |
| // We have found the slide amount, so we can exit this for loop. | |
| break; | |
| } | |
| } | |
| } | |
| return true; | |
| } | |
| void DynamicLoaderDarwin::UpdateSpecialBinariesFromPreloadedModules( | |
| std::vector<std::pair<ImageInfo, ModuleSP>> &images) { | |
| uint32_t exe_idx = UINT32_MAX; | |
| uint32_t dyld_idx = UINT32_MAX; | |
| Target &target = m_process->GetTarget(); | |
| Log *log = GetLog(LLDBLog::DynamicLoader); | |
| ConstString g_dyld_sim_filename("dyld_sim"); | |
| ArchSpec target_arch = target.GetArchitecture(); | |
| const size_t images_size = images.size(); | |
| for (size_t i = 0; i < images_size; i++) { | |
| const auto &image_info = images[i].first; | |
| if (image_info.header.filetype == llvm::MachO::MH_DYLINKER) { | |
| // In a "simulator" process we will have two dyld modules -- | |
| // a "dyld" that we want to keep track of, and a "dyld_sim" which | |
| // we don't need to keep track of here. dyld_sim will have a non-macosx | |
| // OS. | |
| if (target_arch.GetTriple().getEnvironment() == llvm::Triple::Simulator && | |
| image_info.os_type != llvm::Triple::OSType::MacOSX) { | |
| continue; | |
| } | |
| dyld_idx = i; | |
| } | |
| if (image_info.header.filetype == llvm::MachO::MH_EXECUTE) { | |
| exe_idx = i; | |
| } | |
| } | |
| // Set the target executable if we haven't found one so far. | |
| if (exe_idx != UINT32_MAX && !target.GetExecutableModule()) { | |
| ModuleSP exe_module_sp = images[exe_idx].second; | |
| if (exe_module_sp) { | |
| LLDB_LOGF(log, "Found executable module: %s", | |
| exe_module_sp->GetFileSpec().GetPath().c_str()); | |
| target.GetImages().AppendIfNeeded(exe_module_sp); | |
| UpdateImageLoadAddress(exe_module_sp.get(), images[exe_idx].first); | |
| if (exe_module_sp.get() != target.GetExecutableModulePointer()) | |
| target.RebuildModuleListWithExecutable(exe_module_sp, | |
| eLoadDependentsNo); | |
| // Update the target executable's arch if necessary. | |
| auto exe_triple = exe_module_sp->GetArchitecture().GetTriple(); | |
| if (target_arch.GetTriple().isArm64e() && | |
| exe_triple.getArch() == llvm::Triple::aarch64 && | |
| !exe_triple.isArm64e()) { | |
| // On arm64e-capable Apple platforms, the system libraries are | |
| // always arm64e, but applications often are arm64. When a | |
| // target is created from a file, LLDB recognizes it as an | |
| // arm64 target, but debugserver will still (technically | |
| // correct) report the process as being arm64e. For | |
| // consistency, set the target to arm64 here, so attaching to | |
| // a live process behaves the same as creating a process from | |
| // file. | |
| auto triple = target_arch.GetTriple(); | |
| triple.setArchName(exe_triple.getArchName()); | |
| target_arch.SetTriple(triple); | |
| target.SetArchitecture(target_arch, /*set_platform=*/false, | |
| /*merge=*/false); | |
| } | |
| } | |
| } | |
| if (dyld_idx != UINT32_MAX) { | |
| ModuleSP dyld_sp = images[dyld_idx].second; | |
| if (dyld_sp.get()) { | |
| LLDB_LOGF(log, "Found dyld module: %s", | |
| dyld_sp->GetFileSpec().GetPath().c_str()); | |
| target.GetImages().AppendIfNeeded(dyld_sp); | |
| UpdateImageLoadAddress(dyld_sp.get(), images[dyld_idx].first); | |
| SetDYLDModule(dyld_sp); | |
| } | |
| } | |
| } | |
| bool DynamicLoaderDarwin::UpdateDYLDImageInfoFromNewImageInfo( | |
| ImageInfo &image_info) { | |
| if (image_info.header.filetype == llvm::MachO::MH_DYLINKER) { | |
| const bool can_create = true; | |
| ModuleSP dyld_sp = | |
| FindTargetModuleForImageInfo(image_info, can_create, nullptr); | |
| if (dyld_sp.get()) { | |
| Target &target = m_process->GetTarget(); | |
| target.GetImages().AppendIfNeeded(dyld_sp); | |
| UpdateImageLoadAddress(dyld_sp.get(), image_info); | |
| SetDYLDModule(dyld_sp); | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| std::optional<lldb_private::Address> DynamicLoaderDarwin::GetStartAddress() { | |
| Log *log = GetLog(LLDBLog::DynamicLoader); | |
| auto log_err = [log](llvm::StringLiteral err_msg) -> std::nullopt_t { | |
| LLDB_LOG_VERBOSE(log, "{}", err_msg); | |
| return std::nullopt; | |
| }; | |
| ModuleSP dyld_sp = GetDYLDModule(); | |
| if (!dyld_sp) | |
| return log_err("Couldn't retrieve DYLD module. Cannot get `start` symbol."); | |
| const Symbol *symbol = | |
| dyld_sp->FindFirstSymbolWithNameAndType(ConstString("_dyld_start")); | |
| if (!symbol) | |
| return log_err("Cannot find `start` symbol in DYLD module."); | |
| return symbol->GetAddress(); | |
| } | |
| void DynamicLoaderDarwin::SetDYLDModule(lldb::ModuleSP &dyld_module_sp) { | |
| m_dyld_module_wp = dyld_module_sp; | |
| } | |
| ModuleSP DynamicLoaderDarwin::GetDYLDModule() { | |
| ModuleSP dyld_sp(m_dyld_module_wp.lock()); | |
| return dyld_sp; | |
| } | |
| void DynamicLoaderDarwin::ClearDYLDModule() { m_dyld_module_wp.reset(); } | |
| std::vector<std::pair<DynamicLoaderDarwin::ImageInfo, ModuleSP>> | |
| DynamicLoaderDarwin::PreloadModulesFromImageInfos( | |
| const ImageInfo::collection &image_infos) { | |
| const auto size = image_infos.size(); | |
| std::vector<std::pair<DynamicLoaderDarwin::ImageInfo, ModuleSP>> images(size); | |
| auto LoadImage = [&](size_t i, ImageInfo::collection::const_iterator it) { | |
| const auto &image_info = *it; | |
| images[i] = std::make_pair( | |
| image_info, FindTargetModuleForImageInfo(image_info, true, nullptr)); | |
| }; | |
| auto it = image_infos.begin(); | |
| bool is_parallel_load = m_process->GetTarget().GetParallelModuleLoad(); | |
| if (is_parallel_load) { | |
| llvm::ThreadPoolTaskGroup taskGroup(Debugger::GetThreadPool()); | |
| for (size_t i = 0; i < size; ++i, ++it) { | |
| taskGroup.async(LoadImage, i, it); | |
| } | |
| taskGroup.wait(); | |
| } else { | |
| for (size_t i = 0; i < size; ++i, ++it) { | |
| LoadImage(i, it); | |
| } | |
| } | |
| return images; | |
| } | |
| bool DynamicLoaderDarwin::AddModulesUsingImageInfos( | |
| ImageInfo::collection &image_infos) { | |
| std::lock_guard<std::recursive_mutex> guard(m_mutex); | |
| auto images = PreloadModulesFromImageInfos(image_infos); | |
| return AddModulesUsingPreloadedModules(images); | |
| } | |
| bool DynamicLoaderDarwin::AddModulesUsingPreloadedModules( | |
| std::vector<std::pair<ImageInfo, ModuleSP>> &images) { | |
| std::lock_guard<std::recursive_mutex> guard(m_mutex); | |
| // Now add these images to the main list. | |
| ModuleList loaded_module_list; | |
| Log *log = GetLog(LLDBLog::DynamicLoader); | |
| Target &target = m_process->GetTarget(); | |
| ModuleList &target_images = target.GetImages(); | |
| for (uint32_t idx = 0; idx < images.size(); ++idx) { | |
| auto &image_info = images[idx].first; | |
| const auto &image_module_sp = images[idx].second; | |
| if (log) { | |
| LLDB_LOGF(log, "Adding new image at address=0x%16.16" PRIx64 ".", | |
| image_info.address); | |
| image_info.PutToLog(log); | |
| } | |
| m_dyld_image_infos.push_back(image_info); | |
| if (image_module_sp) { | |
| ObjectFile *objfile = image_module_sp->GetObjectFile(); | |
| if (objfile) { | |
| SectionList *sections = objfile->GetSectionList(); | |
| if (sections) { | |
| llvm::StringRef commpage_sect_name("__commpage"); | |
| Section *commpage_section = | |
| sections->FindSectionByName(commpage_sect_name).get(); | |
| if (commpage_section) { | |
| ModuleSpec module_spec(objfile->GetFileSpec(), | |
| image_info.GetArchitecture()); | |
| module_spec.SetObjectName(commpage_sect_name.str()); | |
| ModuleSP commpage_image_module_sp( | |
| target_images.FindFirstModule(module_spec)); | |
| if (!commpage_image_module_sp) { | |
| module_spec.SetObjectOffset(objfile->GetFileOffset() + | |
| commpage_section->GetFileOffset()); | |
| module_spec.SetObjectSize(objfile->GetByteSize()); | |
| commpage_image_module_sp = target.GetOrCreateModule(module_spec, | |
| true /* notify */); | |
| if (!commpage_image_module_sp || | |
| commpage_image_module_sp->GetObjectFile() == nullptr) { | |
| llvm::Expected<ModuleSP> module_sp_or_err = | |
| m_process->ReadModuleFromMemory(image_info.file_spec, | |
| image_info.address); | |
| if (auto err = module_sp_or_err.takeError()) { | |
| LLDB_LOG_ERROR(log, std::move(err), | |
| "Failed to read module from memory: {0}"); | |
| } else { | |
| // Always load a memory image right away in the target in case | |
| // we end up trying to read the symbol table from memory... | |
| // The __LINKEDIT will need to be mapped so we can figure out | |
| // where the symbol table bits are... | |
| commpage_image_module_sp = *module_sp_or_err; | |
| bool changed = false; | |
| UpdateImageLoadAddress(commpage_image_module_sp.get(), | |
| image_info); | |
| target.GetImages().Append(commpage_image_module_sp); | |
| if (changed) { | |
| image_info.load_stop_id = m_process->GetStopID(); | |
| loaded_module_list.AppendIfNeeded(commpage_image_module_sp); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| // UpdateImageLoadAddress will return true if any segments change load | |
| // address. We need to check this so we don't mention that all loaded | |
| // shared libraries are newly loaded each time we hit out dyld breakpoint | |
| // since dyld will list all shared libraries each time. | |
| if (UpdateImageLoadAddress(image_module_sp.get(), image_info)) { | |
| target_images.AppendIfNeeded(image_module_sp); | |
| loaded_module_list.AppendIfNeeded(image_module_sp); | |
| } | |
| // To support macCatalyst and legacy iOS simulator, | |
| // update the module's platform with the DYLD info. | |
| ArchSpec dyld_spec = image_info.GetArchitecture(); | |
| auto &dyld_triple = dyld_spec.GetTriple(); | |
| if ((dyld_triple.getEnvironment() == llvm::Triple::MacABI && | |
| dyld_triple.getOS() == llvm::Triple::IOS) || | |
| (dyld_triple.getEnvironment() == llvm::Triple::Simulator && | |
| (dyld_triple.getOS() == llvm::Triple::IOS || | |
| dyld_triple.getOS() == llvm::Triple::TvOS || | |
| dyld_triple.getOS() == llvm::Triple::WatchOS || | |
| dyld_triple.getOS() == llvm::Triple::XROS))) | |
| image_module_sp->MergeArchitecture(dyld_spec); | |
| } | |
| } | |
| if (loaded_module_list.GetSize() > 0) { | |
| if (log) | |
| loaded_module_list.LogUUIDAndPaths(log, | |
| "DynamicLoaderDarwin::ModulesDidLoad"); | |
| m_process->GetTarget().ModulesDidLoad(loaded_module_list); | |
| } | |
| return true; | |
| } | |
| // On Mac OS X libobjc (the Objective-C runtime) has several critical dispatch | |
| // functions written in hand-written assembly, and also have hand-written | |
| // unwind information in the eh_frame section. Normally we prefer analyzing | |
| // the assembly instructions of a currently executing frame to unwind from that | |
| // frame -- but on hand-written functions this profiling can fail. We should | |
| // use the eh_frame instructions for these functions all the time. | |
| // | |
| // As an aside, it would be better if the eh_frame entries had a flag (or were | |
| // extensible so they could have an Apple-specific flag) which indicates that | |
| // the instructions are asynchronous -- accurate at every instruction, instead | |
| // of our normal default assumption that they are not. | |
| bool DynamicLoaderDarwin::AlwaysRelyOnEHUnwindInfo(SymbolContext &sym_ctx) { | |
| ModuleSP module_sp; | |
| if (sym_ctx.symbol) { | |
| module_sp = sym_ctx.symbol->GetAddressRef().GetModule(); | |
| } | |
| if (module_sp.get() == nullptr && sym_ctx.function) | |
| module_sp = sym_ctx.function->GetAddress().GetModule(); | |
| if (module_sp.get() == nullptr) | |
| return false; | |
| ObjCLanguageRuntime *objc_runtime = ObjCLanguageRuntime::Get(*m_process); | |
| return objc_runtime != nullptr && | |
| objc_runtime->IsModuleObjCLibrary(module_sp); | |
| } | |
| // Dump a Segment to the file handle provided. | |
| void DynamicLoaderDarwin::Segment::PutToLog(Log *log, | |
| lldb::addr_t slide) const { | |
| if (slide == 0) | |
| LLDB_LOGF(log, "\t\t%16s [0x%16.16" PRIx64 " - 0x%16.16" PRIx64 ")", name, | |
| vmaddr + slide, vmaddr + slide + vmsize); | |
| else | |
| LLDB_LOGF(log, | |
| "\t\t%16s [0x%16.16" PRIx64 " - 0x%16.16" PRIx64 | |
| ") slide = 0x%" PRIx64, | |
| name, vmaddr + slide, vmaddr + slide + vmsize, slide); | |
| } | |
| lldb_private::ArchSpec DynamicLoaderDarwin::ImageInfo::GetArchitecture() const { | |
| // Update the module's platform with the DYLD info. | |
| lldb_private::ArchSpec arch_spec(lldb_private::eArchTypeMachO, header.cputype, | |
| header.cpusubtype); | |
| if (os_env == llvm::Triple::MacABI && os_type == llvm::Triple::IOS) { | |
| llvm::Triple triple(llvm::Twine(arch_spec.GetArchitectureName()) + | |
| "-apple-ios" + min_version_os_sdk + "-macabi"); | |
| ArchSpec maccatalyst_spec(triple); | |
| if (arch_spec.IsCompatibleMatch(maccatalyst_spec)) | |
| arch_spec.MergeFrom(maccatalyst_spec); | |
| } | |
| if (os_env == llvm::Triple::Simulator && | |
| (os_type == llvm::Triple::IOS || os_type == llvm::Triple::TvOS || | |
| os_type == llvm::Triple::WatchOS || os_type == llvm::Triple::XROS)) { | |
| llvm::Triple triple(llvm::Twine(arch_spec.GetArchitectureName()) + | |
| "-apple-" + llvm::Triple::getOSTypeName(os_type) + | |
| min_version_os_sdk + "-simulator"); | |
| ArchSpec sim_spec(triple); | |
| if (arch_spec.IsCompatibleMatch(sim_spec)) | |
| arch_spec.MergeFrom(sim_spec); | |
| } | |
| return arch_spec; | |
| } | |
| // Dump an image info structure to the file handle provided. | |
| void DynamicLoaderDarwin::ImageInfo::PutToLog(Log *log) const { | |
| if (!log) | |
| return; | |
| if (address == LLDB_INVALID_ADDRESS) { | |
| LLDB_LOG(log, "uuid={} path='{}' (UNLOADED)", uuid.GetAsString(), | |
| file_spec.GetPath()); | |
| } else { | |
| LLDB_LOG(log, "address={0:x+16} uuid={1} path='{2}'", address, | |
| uuid.GetAsString(), file_spec.GetPath()); | |
| for (uint32_t i = 0; i < segments.size(); ++i) | |
| segments[i].PutToLog(log, slide); | |
| } | |
| } | |
| void DynamicLoaderDarwin::PrivateInitialize(Process *process) { | |
| DEBUG_PRINTF("DynamicLoaderDarwin::%s() process state = %s\n", __FUNCTION__, | |
| StateAsCString(m_process->GetState())); | |
| Clear(true); | |
| m_process = process; | |
| } | |
| // Member function that gets called when the process state changes. | |
| void DynamicLoaderDarwin::PrivateProcessStateChanged(Process *process, | |
| StateType state) { | |
| DEBUG_PRINTF("DynamicLoaderDarwin::%s(%s)\n", __FUNCTION__, | |
| StateAsCString(state)); | |
| switch (state) { | |
| case eStateConnected: | |
| case eStateAttaching: | |
| case eStateLaunching: | |
| case eStateInvalid: | |
| case eStateUnloaded: | |
| case eStateExited: | |
| case eStateDetached: | |
| Clear(false); | |
| break; | |
| case eStateStopped: | |
| // Keep trying find dyld and set our notification breakpoint each time we | |
| // stop until we succeed | |
| if (!DidSetNotificationBreakpoint() && m_process->IsAlive()) { | |
| if (NeedToDoInitialImageFetch()) | |
| DoInitialImageFetch(); | |
| SetNotificationBreakpoint(); | |
| } | |
| break; | |
| case eStateRunning: | |
| case eStateStepping: | |
| case eStateCrashed: | |
| case eStateSuspended: | |
| break; | |
| } | |
| } | |
| ThreadPlanSP | |
| DynamicLoaderDarwin::GetStepThroughTrampolinePlan(Thread &thread, | |
| bool stop_others) { | |
| ThreadPlanSP thread_plan_sp; | |
| StackFrame *current_frame = thread.GetStackFrameAtIndex(0).get(); | |
| const SymbolContext ¤t_context = | |
| current_frame->GetSymbolContext(eSymbolContextSymbol); | |
| const Symbol *current_symbol = current_context.symbol; | |
| Log *log = GetLog(LLDBLog::Step); | |
| TargetSP target_sp(thread.CalculateTarget()); | |
| if (current_symbol != nullptr) { | |
| std::vector<Address> addresses; | |
| ConstString current_name = | |
| current_symbol->GetMangled().GetName(Mangled::ePreferMangled); | |
| llvm::StringRef target_symbol_name = current_name; | |
| static const llvm::StringRef g_lazy_stub_name = "$lazyLoadStub"; | |
| // If this is a "lazy library" stub, we don't have a guarantee that the | |
| // target library is loaded at the point where we hit the stub. So we | |
| // have to use a symbol name breakpoint rather than an address one. | |
| if (current_name && target_symbol_name.consume_back(g_lazy_stub_name)) { | |
| auto bkpt_sp = target_sp->CreateBreakpoint( | |
| nullptr, nullptr, target_symbol_name.str().c_str(), | |
| eFunctionNameTypeFull, eLanguageTypeUnknown, /*offset=*/0, | |
| /*is_insn_count=*/false, | |
| /*skip_prologue=*/eLazyBoolNo, /*internal=*/true, | |
| /* hardware=*/false); | |
| return std::make_shared<ThreadPlanRunToBreakpoint>(thread, bkpt_sp, | |
| stop_others); | |
| } | |
| if (current_symbol->IsTrampoline()) { | |
| if (current_name) { | |
| const ModuleList &images = target_sp->GetImages(); | |
| SymbolContextList code_symbols; | |
| images.FindSymbolsWithNameAndType(current_name, eSymbolTypeCode, | |
| code_symbols); | |
| for (const SymbolContext &context : code_symbols) { | |
| Address addr = context.GetFunctionOrSymbolAddress(); | |
| addresses.push_back(addr); | |
| if (log) { | |
| addr_t load_addr = addr.GetLoadAddress(target_sp.get()); | |
| LLDB_LOGF(log, "Found a trampoline target symbol at 0x%" PRIx64 ".", | |
| load_addr); | |
| } | |
| } | |
| SymbolContextList reexported_symbols; | |
| images.FindSymbolsWithNameAndType(current_name, eSymbolTypeReExported, | |
| reexported_symbols); | |
| for (const SymbolContext &context : reexported_symbols) { | |
| if (context.symbol) { | |
| const Symbol *actual_symbol = | |
| context.symbol->ResolveReExportedSymbol(*target_sp.get()); | |
| if (actual_symbol) { | |
| const Address actual_symbol_addr = actual_symbol->GetAddress(); | |
| if (actual_symbol_addr.IsValid()) { | |
| addresses.push_back(actual_symbol_addr); | |
| if (log) { | |
| lldb::addr_t load_addr = | |
| actual_symbol_addr.GetLoadAddress(target_sp.get()); | |
| LLDB_LOGF(log, | |
| "Found a re-exported symbol: %s at 0x%" PRIx64 ".", | |
| actual_symbol->GetName().GetCString(), load_addr); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| SymbolContextList indirect_symbols; | |
| images.FindSymbolsWithNameAndType(current_name, eSymbolTypeResolver, | |
| indirect_symbols); | |
| for (const SymbolContext &context : indirect_symbols) { | |
| Address addr = context.GetFunctionOrSymbolAddress(); | |
| addresses.push_back(addr); | |
| if (log) { | |
| addr_t load_addr = addr.GetLoadAddress(target_sp.get()); | |
| LLDB_LOGF(log, "Found an indirect target symbol at 0x%" PRIx64 ".", | |
| load_addr); | |
| } | |
| } | |
| } | |
| } else if (current_symbol->GetType() == eSymbolTypeReExported) { | |
| // I am not sure we could ever end up stopped AT a re-exported symbol. | |
| // But just in case: | |
| const Symbol *actual_symbol = | |
| current_symbol->ResolveReExportedSymbol(*(target_sp.get())); | |
| if (actual_symbol) { | |
| Address target_addr(actual_symbol->GetAddress()); | |
| if (target_addr.IsValid()) { | |
| LLDB_LOGF( | |
| log, | |
| "Found a re-exported symbol: %s pointing to: %s at 0x%" PRIx64 | |
| ".", | |
| current_symbol->GetName().GetCString(), | |
| actual_symbol->GetName().GetCString(), | |
| target_addr.GetLoadAddress(target_sp.get())); | |
| addresses.push_back( | |
| Address(target_addr.GetLoadAddress(target_sp.get()))); | |
| } | |
| } | |
| } | |
| if (addresses.size() > 0) { | |
| // First check whether any of the addresses point to Indirect symbols, | |
| // and if they do, resolve them: | |
| std::vector<lldb::addr_t> load_addrs; | |
| for (Address address : addresses) { | |
| const Symbol *symbol = address.CalculateSymbolContextSymbol(); | |
| if (symbol && symbol->IsIndirect()) { | |
| Status error; | |
| Address symbol_address = symbol->GetAddress(); | |
| addr_t resolved_addr = thread.GetProcess()->ResolveIndirectFunction( | |
| &symbol_address, error); | |
| if (error.Success()) { | |
| load_addrs.push_back(resolved_addr); | |
| LLDB_LOGF(log, | |
| "ResolveIndirectFunction found resolved target for " | |
| "%s at 0x%" PRIx64 ".", | |
| symbol->GetName().GetCString(), resolved_addr); | |
| } | |
| } else { | |
| load_addrs.push_back(address.GetLoadAddress(target_sp.get())); | |
| } | |
| } | |
| thread_plan_sp = std::make_shared<ThreadPlanRunToAddress>( | |
| thread, load_addrs, stop_others); | |
| } | |
| // One more case we have to consider is "branch islands". These are regular | |
| // TEXT symbols but their names end in .island plus maybe a .digit suffix. | |
| // They are to allow arm64 code to branch further than the size of the | |
| // address slot allows. We just need to single-instruction step in that | |
| // case. | |
| static const char *g_branch_island_pattern = "\\.island\\.?[0-9]*$"; | |
| static RegularExpression g_branch_island_regex(g_branch_island_pattern); | |
| bool is_branch_island = g_branch_island_regex.Execute(current_name); | |
| if (!thread_plan_sp && is_branch_island) { | |
| thread_plan_sp = std::make_shared<ThreadPlanStepInstruction>( | |
| thread, | |
| /* step_over= */ false, /* stop_others */ false, eVoteNoOpinion, | |
| eVoteNoOpinion); | |
| LLDB_LOG(log, "Stepping one instruction over branch island: '{0}'.", | |
| current_name); | |
| } | |
| } else { | |
| LLDB_LOGF(log, "Could not find symbol for step through."); | |
| } | |
| return thread_plan_sp; | |
| } | |
| void DynamicLoaderDarwin::FindEquivalentSymbols( | |
| const lldb_private::Symbol *original_symbol, | |
| lldb_private::ModuleList &images, | |
| lldb_private::SymbolContextList &equivalent_symbols) { | |
| ConstString trampoline_name = | |
| original_symbol->GetMangled().GetName(Mangled::ePreferMangled); | |
| if (!trampoline_name) | |
| return; | |
| static const char *resolver_name_regex = "(_gc|_non_gc|\\$[A-Za-z0-9\\$]+)$"; | |
| std::string equivalent_regex_buf("^"); | |
| equivalent_regex_buf.append(trampoline_name.GetCString()); | |
| equivalent_regex_buf.append(resolver_name_regex); | |
| RegularExpression equivalent_name_regex(equivalent_regex_buf); | |
| images.FindSymbolsMatchingRegExAndType(equivalent_name_regex, eSymbolTypeCode, | |
| equivalent_symbols); | |
| } | |
| lldb::ModuleSP DynamicLoaderDarwin::GetPThreadLibraryModule() { | |
| ModuleSP module_sp = m_libpthread_module_wp.lock(); | |
| if (!module_sp) { | |
| SymbolContextList sc_list; | |
| ModuleSpec module_spec; | |
| module_spec.GetFileSpec().SetFilename("libsystem_pthread.dylib"); | |
| ModuleList module_list; | |
| m_process->GetTarget().GetImages().FindModules(module_spec, module_list); | |
| if (!module_list.IsEmpty()) { | |
| if (module_list.GetSize() == 1) { | |
| module_sp = module_list.GetModuleAtIndex(0); | |
| if (module_sp) | |
| m_libpthread_module_wp = module_sp; | |
| } | |
| } | |
| } | |
| return module_sp; | |
| } | |
| Address DynamicLoaderDarwin::GetPthreadSetSpecificAddress() { | |
| if (!m_pthread_getspecific_addr.IsValid()) { | |
| ModuleSP module_sp = GetPThreadLibraryModule(); | |
| if (module_sp) { | |
| lldb_private::SymbolContextList sc_list; | |
| module_sp->FindSymbolsWithNameAndType(ConstString("pthread_getspecific"), | |
| eSymbolTypeCode, sc_list); | |
| SymbolContext sc; | |
| if (sc_list.GetContextAtIndex(0, sc)) { | |
| if (sc.symbol) | |
| m_pthread_getspecific_addr = sc.symbol->GetAddress(); | |
| } | |
| } | |
| } | |
| return m_pthread_getspecific_addr; | |
| } | |
| lldb::addr_t | |
| DynamicLoaderDarwin::GetThreadLocalData(const lldb::ModuleSP module_sp, | |
| const lldb::ThreadSP thread_sp, | |
| lldb::addr_t tls_file_addr) { | |
| if (!thread_sp || !module_sp) | |
| return LLDB_INVALID_ADDRESS; | |
| std::lock_guard<std::recursive_mutex> guard(m_mutex); | |
| lldb_private::Address tls_addr; | |
| if (!module_sp->ResolveFileAddress(tls_file_addr, tls_addr)) | |
| return LLDB_INVALID_ADDRESS; | |
| Target &target = m_process->GetTarget(); | |
| TypeSystemClangSP scratch_ts_sp = | |
| ScratchTypeSystemClang::GetForTarget(target); | |
| if (!scratch_ts_sp) | |
| return LLDB_INVALID_ADDRESS; | |
| CompilerType clang_void_ptr_type = | |
| scratch_ts_sp->GetBasicType(eBasicTypeVoid).GetPointerType(); | |
| auto evaluate_tls_address = [this, &thread_sp, &clang_void_ptr_type]( | |
| Address func_ptr, | |
| llvm::ArrayRef<addr_t> args) -> addr_t { | |
| EvaluateExpressionOptions options; | |
| lldb::ThreadPlanSP thread_plan_sp(new ThreadPlanCallFunction( | |
| *thread_sp, func_ptr, clang_void_ptr_type, args, options)); | |
| DiagnosticManager execution_errors; | |
| ExecutionContext exe_ctx(thread_sp); | |
| lldb::ExpressionResults results = m_process->RunThreadPlan( | |
| exe_ctx, thread_plan_sp, options, execution_errors); | |
| if (results == lldb::eExpressionCompleted) { | |
| if (lldb::ValueObjectSP result_valobj_sp = | |
| thread_plan_sp->GetReturnValueObject()) { | |
| return result_valobj_sp->GetValueAsUnsigned(LLDB_INVALID_ADDRESS); | |
| } | |
| } | |
| return LLDB_INVALID_ADDRESS; | |
| }; | |
| // On modern apple platforms, there is a small data structure that looks | |
| // approximately like this: | |
| // struct TLS_Thunk { | |
| // void *(*get_addr)(struct TLS_Thunk *); | |
| // size_t key; | |
| // size_t offset; | |
| // } | |
| // | |
| // The strategy is to take get_addr and call it with the address of the | |
| // containing TLS_Thunk structure. | |
| // | |
| // On older apple platforms, the key is treated as a pthread_key_t and passed | |
| // to pthread_getspecific. The pointer returned from that call is added to | |
| // offset to get the relevant data block. | |
| const uint32_t addr_size = m_process->GetAddressByteSize(); | |
| uint8_t buf[sizeof(addr_t) * 3]; | |
| Status error; | |
| const size_t tls_data_size = addr_size * 3; | |
| const size_t bytes_read = target.ReadMemory( | |
| tls_addr, buf, tls_data_size, error, /*force_live_memory = */ true); | |
| if (bytes_read != tls_data_size || error.Fail()) | |
| return LLDB_INVALID_ADDRESS; | |
| DataExtractor data(buf, sizeof(buf), m_process->GetByteOrder(), addr_size); | |
| lldb::offset_t offset = 0; | |
| const addr_t tls_thunk = data.GetAddress(&offset); | |
| const addr_t key = data.GetAddress(&offset); | |
| const addr_t tls_offset = data.GetAddress(&offset); | |
| if (tls_thunk != 0) { | |
| const addr_t fixed_tls_thunk = m_process->FixCodeAddress(tls_thunk); | |
| Address thunk_load_addr; | |
| if (target.ResolveLoadAddress(fixed_tls_thunk, thunk_load_addr)) { | |
| const addr_t tls_load_addr = tls_addr.GetLoadAddress(&target); | |
| const addr_t tls_data = evaluate_tls_address( | |
| thunk_load_addr, llvm::ArrayRef<addr_t>(tls_load_addr)); | |
| if (tls_data != LLDB_INVALID_ADDRESS) | |
| return tls_data; | |
| } | |
| } | |
| if (key != 0) { | |
| // First check to see if we have already figured out the location of | |
| // TLS data for the pthread_key on a specific thread yet. If we have we | |
| // can re-use it since its location will not change unless the process | |
| // execs. | |
| const lldb::tid_t tid = thread_sp->GetID(); | |
| auto tid_pos = m_tid_to_tls_map.find(tid); | |
| if (tid_pos != m_tid_to_tls_map.end()) { | |
| auto tls_pos = tid_pos->second.find(key); | |
| if (tls_pos != tid_pos->second.end()) { | |
| return tls_pos->second + tls_offset; | |
| } | |
| } | |
| Address pthread_getspecific_addr = GetPthreadSetSpecificAddress(); | |
| if (pthread_getspecific_addr.IsValid()) { | |
| const addr_t tls_data = evaluate_tls_address(pthread_getspecific_addr, | |
| llvm::ArrayRef<addr_t>(key)); | |
| if (tls_data != LLDB_INVALID_ADDRESS) | |
| return tls_data + tls_offset; | |
| } | |
| } | |
| return LLDB_INVALID_ADDRESS; | |
| } | |
| bool DynamicLoaderDarwin::UseDYLDSPI(Process *process) { | |
| Log *log = GetLog(LLDBLog::DynamicLoader); | |
| bool use_new_spi_interface = true; | |
| llvm::VersionTuple version = process->GetHostOSVersion(); | |
| if (!version.empty()) { | |
| using namespace llvm; | |
| const Triple::OSType os_type = | |
| process->GetTarget().GetArchitecture().GetTriple().getOS(); | |
| auto OlderThan = [os_type, version](llvm::Triple::OSType o, | |
| llvm::VersionTuple v) -> bool { | |
| return os_type == o && version < v; | |
| }; | |
| if (OlderThan(Triple::MacOSX, VersionTuple(10, 12))) | |
| use_new_spi_interface = false; | |
| if (OlderThan(Triple::IOS, VersionTuple(10))) | |
| use_new_spi_interface = false; | |
| if (OlderThan(Triple::TvOS, VersionTuple(10))) | |
| use_new_spi_interface = false; | |
| if (OlderThan(Triple::WatchOS, VersionTuple(3))) | |
| use_new_spi_interface = false; | |
| // llvm::Triple::BridgeOS and llvm::Triple::XROS always use the new | |
| // libdyld SPI interface. | |
| } else { | |
| // We could not get an OS version string, we are likely not | |
| // connected to debugserver and the packets to call the libdyld SPI | |
| // will not exist. | |
| use_new_spi_interface = false; | |
| } | |
| // Corefiles cannot use the libdyld SPI to get the inferior's | |
| // binaries, we must find it through metadata or a scan | |
| // of the corefile memory. | |
| if (!process->IsLiveDebugSession()) | |
| use_new_spi_interface = false; | |
| if (log) { | |
| if (use_new_spi_interface) | |
| LLDB_LOGF( | |
| log, "DynamicLoaderDarwin::UseDYLDSPI: Use new DynamicLoader plugin"); | |
| else | |
| LLDB_LOGF( | |
| log, "DynamicLoaderDarwin::UseDYLDSPI: Use old DynamicLoader plugin"); | |
| } | |
| return use_new_spi_interface; | |
| } |