diff --git a/lldb/include/lldb/Target/Platform.h b/lldb/include/lldb/Target/Platform.h --- a/lldb/include/lldb/Target/Platform.h +++ b/lldb/include/lldb/Target/Platform.h @@ -846,6 +846,34 @@ return nullptr; } + /// Detect a binary in memory that will determine which Platform and + /// DynamicLoader should be used in this target/process, and update + /// the Platform/DynamicLoader. + /// The binary will be loaded into the Target, or will be registered with + /// the DynamicLoader so that it will be loaded at a later stage. Returns + /// true to indicate that this is a platform binary and has been + /// loaded/registered, no further action should be taken by the caller. + /// + /// \param[in] process + /// Process read memory from, a Process must be provided. + /// + /// \param[in] addr + /// Address of a binary in memory. + /// + /// \param[in] notify + /// Whether ModulesDidLoad should be called, if a binary is loaded. + /// Caller may prefer to call ModulesDidLoad for multiple binaries + /// that were loaded at the same time. + /// + /// \return + /// Returns true if the binary was loaded in the target (or will be + /// via a DynamicLoader). Returns false if the binary was not + /// loaded/registered, and the caller must load it into the target. + virtual bool LoadPlatformBinaryAndSetup(Process *process, lldb::addr_t addr, + bool notify) { + return false; + } + virtual CompilerType GetSiginfoType(const llvm::Triple &triple); virtual Args GetExtraStartupCommands(); @@ -1026,6 +1054,32 @@ lldb::PlatformSP Create(llvm::StringRef name); + /// Detect a binary in memory that will determine which Platform and + /// DynamicLoader should be used in this target/process, and update + /// the Platform/DynamicLoader. + /// The binary will be loaded into the Target, or will be registered with + /// the DynamicLoader so that it will be loaded at a later stage. Returns + /// true to indicate that this is a platform binary and has been + /// loaded/registered, no further action should be taken by the caller. + /// + /// \param[in] process + /// Process read memory from, a Process must be provided. + /// + /// \param[in] addr + /// Address of a binary in memory. + /// + /// \param[in] notify + /// Whether ModulesDidLoad should be called, if a binary is loaded. + /// Caller may prefer to call ModulesDidLoad for multiple binaries + /// that were loaded at the same time. + /// + /// \return + /// Returns true if the binary was loaded in the target (or will be + /// via a DynamicLoader). Returns false if the binary was not + /// loaded/registered, and the caller must load it into the target. + bool LoadPlatformBinaryAndSetup(Process *process, lldb::addr_t addr, + bool notify); + protected: typedef std::vector collection; mutable std::recursive_mutex m_mutex; diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -641,6 +641,8 @@ /// plug-in. virtual DynamicLoader *GetDynamicLoader(); + void SetDynamicLoader(lldb::DynamicLoaderUP dyld); + // Returns AUXV structure found in many ELF-based environments. // // The default action is to return an empty data buffer. diff --git a/lldb/source/Core/DynamicLoader.cpp b/lldb/source/Core/DynamicLoader.cpp --- a/lldb/source/Core/DynamicLoader.cpp +++ b/lldb/source/Core/DynamicLoader.cpp @@ -230,6 +230,10 @@ Log *log = GetLog(LLDBLog::DynamicLoader); if (module_sp.get()) { + // Ensure the Target has an architecture set in case + // we need it while processing this binary/eh_frame/debug info. + if (!target.GetArchitecture().IsValid()) + target.SetArchitecture(module_sp->GetArchitecture()); target.GetImages().AppendIfNeeded(module_sp, false); bool changed = false; diff --git a/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp b/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp --- a/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp +++ b/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp @@ -130,6 +130,20 @@ return g_settings; } +static bool is_kernel(Module *module) { + if (!module) + return false; + ObjectFile *objfile = module->GetObjectFile(); + if (!objfile) + return false; + if (objfile->GetType() != ObjectFile::eTypeExecutable) + return false; + if (objfile->GetStrata() != ObjectFile::eStrataKernel) + return false; + + return true; +} + // Create an instance of this class. This function is filled into the plugin // info class that gets handed out by the plugin factory and allows the lldb to // instantiate an instance of this class. @@ -138,15 +152,8 @@ if (!force) { // If the user provided an executable binary and it is not a kernel, this // plugin should not create an instance. - Module *exe_module = process->GetTarget().GetExecutableModulePointer(); - if (exe_module) { - ObjectFile *object_file = exe_module->GetObjectFile(); - if (object_file) { - if (object_file->GetStrata() != ObjectFile::eStrataKernel) { - return nullptr; - } - } - } + if (!is_kernel(process->GetTarget().GetExecutableModulePointer())) + return nullptr; // If the target's architecture does not look like an Apple environment, // this plugin should not create an instance. @@ -176,7 +183,6 @@ // At this point if there is an ExecutableModule, it is a kernel and the // Target is some variant of an Apple system. If the Process hasn't provided // the kernel load address, we need to look around in memory to find it. - const addr_t kernel_load_address = SearchForDarwinKernel(process); if (CheckForKernelImageAtAddress(kernel_load_address, process).IsValid()) { process->SetCanRunCode(false); @@ -188,18 +194,15 @@ lldb::addr_t DynamicLoaderDarwinKernel::SearchForDarwinKernel(Process *process) { addr_t kernel_load_address = process->GetImageInfoAddress(); - if (kernel_load_address == LLDB_INVALID_ADDRESS) { + if (kernel_load_address == LLDB_INVALID_ADDRESS) kernel_load_address = SearchForKernelAtSameLoadAddr(process); - if (kernel_load_address == LLDB_INVALID_ADDRESS) { - kernel_load_address = SearchForKernelWithDebugHints(process); - if (kernel_load_address == LLDB_INVALID_ADDRESS) { - kernel_load_address = SearchForKernelNearPC(process); - if (kernel_load_address == LLDB_INVALID_ADDRESS) { - kernel_load_address = SearchForKernelViaExhaustiveSearch(process); - } - } - } - } + if (kernel_load_address == LLDB_INVALID_ADDRESS) + kernel_load_address = SearchForKernelWithDebugHints(process); + if (kernel_load_address == LLDB_INVALID_ADDRESS) + kernel_load_address = SearchForKernelNearPC(process); + if (kernel_load_address == LLDB_INVALID_ADDRESS) + kernel_load_address = SearchForKernelViaExhaustiveSearch(process); + return kernel_load_address; } @@ -209,16 +212,11 @@ lldb::addr_t DynamicLoaderDarwinKernel::SearchForKernelAtSameLoadAddr(Process *process) { Module *exe_module = process->GetTarget().GetExecutableModulePointer(); - if (exe_module == nullptr) - return LLDB_INVALID_ADDRESS; - ObjectFile *exe_objfile = exe_module->GetObjectFile(); - if (exe_objfile == nullptr) + if (!is_kernel(process->GetTarget().GetExecutableModulePointer())) return LLDB_INVALID_ADDRESS; - if (exe_objfile->GetType() != ObjectFile::eTypeExecutable || - exe_objfile->GetStrata() != ObjectFile::eStrataKernel) - return LLDB_INVALID_ADDRESS; + ObjectFile *exe_objfile = exe_module->GetObjectFile(); if (!exe_objfile->GetBaseAddress().IsValid()) return LLDB_INVALID_ADDRESS; @@ -475,8 +473,7 @@ return UUID(); } - if (exe_objfile->GetType() == ObjectFile::eTypeExecutable && - exe_objfile->GetStrata() == ObjectFile::eStrataKernel) { + if (is_kernel(memory_module_sp.get())) { ArchSpec kernel_arch(eArchTypeMachO, header.cputype, header.cpusubtype); if (!process->GetTarget().GetArchitecture().IsCompatibleMatch( kernel_arch)) { @@ -525,10 +522,10 @@ LoadKernelModuleIfNeeded(); SetNotificationBreakpointIfNeeded(); } -/// Called after attaching a process. -/// -/// Allow DynamicLoader plug-ins to execute some code after -/// attaching to a process. + +/// We've attached to a remote connection, or read a corefile. +/// Now load the kernel binary and potentially the kexts, add +/// them to the Target. void DynamicLoaderDarwinKernel::DidAttach() { PrivateInitialize(m_process); UpdateIfNeeded(); @@ -574,14 +571,7 @@ void DynamicLoaderDarwinKernel::KextImageInfo::SetModule(ModuleSP module_sp) { m_module_sp = module_sp; - if (module_sp.get() && module_sp->GetObjectFile()) { - if (module_sp->GetObjectFile()->GetType() == ObjectFile::eTypeExecutable && - module_sp->GetObjectFile()->GetStrata() == ObjectFile::eStrataKernel) { - m_kernel_image = true; - } else { - m_kernel_image = false; - } - } + m_kernel_image = is_kernel(module_sp.get()); } ModuleSP DynamicLoaderDarwinKernel::KextImageInfo::GetModule() { @@ -671,18 +661,7 @@ if (memory_module_sp.get() == nullptr) return false; - bool is_kernel = false; - if (memory_module_sp->GetObjectFile()) { - if (memory_module_sp->GetObjectFile()->GetType() == - ObjectFile::eTypeExecutable && - memory_module_sp->GetObjectFile()->GetStrata() == - ObjectFile::eStrataKernel) { - is_kernel = true; - } else if (memory_module_sp->GetObjectFile()->GetType() == - ObjectFile::eTypeSharedLibrary) { - is_kernel = false; - } - } + bool this_is_kernel = is_kernel(memory_module_sp.get()); // If this is a kext, and the kernel specified what UUID we should find at // this load address, require that the memory module have a matching UUID or @@ -707,8 +686,8 @@ } m_memory_module_sp = memory_module_sp; - m_kernel_image = is_kernel; - if (is_kernel) { + m_kernel_image = this_is_kernel; + if (this_is_kernel) { if (log) { // This is unusual and probably not intended LLDB_LOGF(log, @@ -718,22 +697,6 @@ if (memory_module_sp->GetArchitecture().IsValid()) { process->GetTarget().SetArchitecture(memory_module_sp->GetArchitecture()); } - if (m_uuid.IsValid()) { - ModuleSP exe_module_sp = process->GetTarget().GetExecutableModule(); - if (exe_module_sp.get() && exe_module_sp->GetUUID().IsValid()) { - if (m_uuid != exe_module_sp->GetUUID()) { - // The user specified a kernel binary that has a different UUID than - // the kernel actually running in memory. This never ends well; - // clear the user specified kernel binary from the Target. - - m_module_sp.reset(); - - ModuleList user_specified_kernel_list; - user_specified_kernel_list.Append(exe_module_sp); - process->GetTarget().GetImages().Remove(user_specified_kernel_list); - } - } - } } return true; @@ -771,6 +734,17 @@ Stream &s = target.GetDebugger().GetOutputStream(); s.Printf("Kernel UUID: %s\n", m_uuid.GetAsString().c_str()); s.Printf("Load Address: 0x%" PRIx64 "\n", m_load_address); + + // Start of a kernel debug session, we have the UUID of the kernel. + // Go through the target's list of modules and if there are any kernel + // modules with non-matching UUIDs, remove them. The user may have added + // the wrong kernel binary manually and it will only confuse things. + ModuleList incorrect_kernels; + for (ModuleSP module_sp : target.GetImages().Modules()) { + if (is_kernel(module_sp.get()) && module_sp->GetUUID() != m_uuid) + incorrect_kernels.Append(module_sp); + } + target.GetImages().Remove(incorrect_kernels); } if (!m_module_sp) { @@ -841,10 +815,6 @@ if (m_module_sp) { if (m_uuid.IsValid() && m_module_sp->GetUUID() == m_uuid) { target.GetImages().AppendIfNeeded(m_module_sp, false); - if (IsKernel() && - target.GetExecutableModulePointer() != m_module_sp.get()) { - target.SetExecutableModule(m_module_sp, eLoadDependentsNo); - } } } } @@ -980,8 +950,11 @@ void DynamicLoaderDarwinKernel::LoadKernelModuleIfNeeded() { if (!m_kext_summary_header_ptr_addr.IsValid()) { m_kernel.Clear(); - m_kernel.SetModule(m_process->GetTarget().GetExecutableModule()); - m_kernel.SetIsKernel(true); + ModuleSP module_sp = m_process->GetTarget().GetExecutableModule(); + if (is_kernel(module_sp.get())) { + m_kernel.SetModule(module_sp); + m_kernel.SetIsKernel(true); + } ConstString kernel_name("mach_kernel"); if (m_kernel.GetModule().get() && m_kernel.GetModule()->GetObjectFile() && diff --git a/lldb/source/Plugins/Platform/MacOSX/CMakeLists.txt b/lldb/source/Plugins/Platform/MacOSX/CMakeLists.txt --- a/lldb/source/Plugins/Platform/MacOSX/CMakeLists.txt +++ b/lldb/source/Plugins/Platform/MacOSX/CMakeLists.txt @@ -44,6 +44,8 @@ lldbSymbol lldbTarget lldbUtility + lldbPluginDynamicLoaderDarwinKernel + lldbPluginObjectContainerMachOFileset lldbPluginPlatformPOSIX ${OBJC_LIBS} CLANG_LIBS diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.h b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.h --- a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.h +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.h @@ -154,6 +154,9 @@ const UUID &uuid, const ArchSpec &arch, lldb::ModuleSP &exe_module_sp); + bool LoadPlatformBinaryAndSetup(Process *process, lldb::addr_t addr, + bool notify) override; + // Most of the ivars are assembled under FileSystem::EnumerateDirectory calls // where the function being called for each file/directory must be static. // We'll pass a this pointer as a baton and access the ivars directly. diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.cpp b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.cpp --- a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.cpp +++ b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.cpp @@ -12,6 +12,7 @@ // source/Host/macosx/cfcpp utilities #include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Debugger.h" #include "lldb/Core/Module.h" #include "lldb/Core/ModuleList.h" #include "lldb/Core/ModuleSpec.h" @@ -26,6 +27,7 @@ #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" #include "lldb/Utility/ArchSpec.h" +#include "lldb/Utility/DataBufferHeap.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" @@ -39,6 +41,8 @@ #include #include "Host/macosx/cfcpp/CFCBundle.h" +#include "Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.h" +#include "Plugins/ObjectContainer/Mach-O-Fileset/ObjectContainerMachOFileset.h" using namespace lldb; using namespace lldb_private; @@ -724,23 +728,28 @@ // "com.apple.driver.AppleIRController") and search our kext index. std::string kext_bundle_id = platform_file.GetPath(); - if (!kext_bundle_id.empty() && module_spec.GetUUID().IsValid()) { - if (kext_bundle_id == "mach_kernel") { - return GetSharedModuleKernel(module_spec, process, module_sp, - module_search_paths_ptr, old_modules, - did_create_ptr); + if (module_spec.GetUUID().IsValid()) { + // DynamicLoaderDarwinKernel uses the magic name mach_kernel, + // UUID search can get here with no name - and it may be a kernel. + if (kext_bundle_id == "mach_kernel" || kext_bundle_id.empty()) { + error = GetSharedModuleKernel(module_spec, process, module_sp, + module_search_paths_ptr, old_modules, + did_create_ptr); + if (error.Success() && module_sp) { + return error; + } } else { return GetSharedModuleKext(module_spec, process, module_sp, module_search_paths_ptr, old_modules, did_create_ptr); } - } else { - // Give the generic methods, including possibly calling into DebugSymbols - // framework on macOS systems, a chance. - return PlatformDarwin::GetSharedModule(module_spec, process, module_sp, - module_search_paths_ptr, old_modules, - did_create_ptr); } + + // Give the generic methods, including possibly calling into DebugSymbols + // framework on macOS systems, a chance. + return PlatformDarwin::GetSharedModule(module_spec, process, module_sp, + module_search_paths_ptr, old_modules, + did_create_ptr); } Status PlatformDarwinKernel::GetSharedModuleKext( @@ -798,7 +807,8 @@ module_sp->MatchesModuleSpec(kern_spec)) { // module_sp is an actual kernel binary we want to add. if (process) { - process->GetTarget().GetImages().AppendIfNeeded(module_sp); + const bool notify = false; + process->GetTarget().GetImages().AppendIfNeeded(module_sp, notify); error.Clear(); return error; } else { @@ -830,7 +840,8 @@ module_sp->MatchesModuleSpec(kern_spec)) { // module_sp is an actual kernel binary we want to add. if (process) { - process->GetTarget().GetImages().AppendIfNeeded(module_sp); + const bool notify = false; + process->GetTarget().GetImages().AppendIfNeeded(module_sp, notify); error.Clear(); return error; } else { @@ -908,6 +919,70 @@ return {}; } +static addr_t find_kernel_in_macho_fileset(Process *process, + addr_t input_addr) { + Status error; + WritableDataBufferSP header_data(new DataBufferHeap(512, 0)); + if (!process->ReadMemory(input_addr, header_data->GetBytes(), + header_data->GetByteSize(), error) || + !error.Success()) + return LLDB_INVALID_ADDRESS; + ModuleSP module_sp(new Module(ModuleSpec())); + ObjectContainerSP container_sp( + ObjectContainerMachOFileset::CreateMemoryInstance( + module_sp, header_data, process->shared_from_this(), input_addr)); + if (!container_sp) + return LLDB_INVALID_ADDRESS; + + ObjectContainerMachOFileset *fileset_container = + static_cast(container_sp.get()); + ObjectContainerMachOFileset::Entry *entry = + fileset_container->FindEntry("com.apple.kernel"); + if (entry) + return entry->vmaddr; + return LLDB_INVALID_ADDRESS; +} + +bool PlatformDarwinKernel::LoadPlatformBinaryAndSetup(Process *process, + lldb::addr_t input_addr, + bool notify) { + Log *log = + GetLog(LLDBLog::Platform | LLDBLog::DynamicLoader | LLDBLog::Process); + + if (!process) + return false; + + addr_t actual_address = find_kernel_in_macho_fileset(process, input_addr); + + LLDB_LOGF(log, + "PlatformDarwinKernel::%s check address 0x%" PRIx64 " for " + "a macho fileset, got back kernel address 0x%" PRIx64, + __FUNCTION__, input_addr, actual_address); + + if (actual_address == LLDB_INVALID_ADDRESS) + return false; + + // We have a xnu kernel binary, this is a kernel debug session. + // Set the Target's Platform to be PlatformDarwinKernel, and the + // Process' DynamicLoader to be DynamicLoaderDarwinKernel. + + PlatformSP platform_sp = + process->GetTarget().GetDebugger().GetPlatformList().Create( + PlatformDarwinKernel::GetPluginNameStatic()); + if (platform_sp) + process->GetTarget().SetPlatform(platform_sp); + + DynamicLoaderUP dyld_up = + std::make_unique(process, actual_address); + if (!dyld_up) + return false; + + // Process owns it now + process->SetDynamicLoader(std::move(dyld_up)); + + return true; +} + std::vector PlatformDarwinKernel::GetSupportedArchitectures( const ArchSpec &process_host_arch) { std::vector result; diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -2206,12 +2206,13 @@ ++num_keys_decoded; } } else if (name.equals("binary-addresses")) { - addr_t addr; - while (!value.empty()) { - llvm::StringRef addr_str; - std::tie(addr_str, value) = value.split(','); - if (!addr_str.getAsInteger(16, addr)) - m_binary_addresses.push_back(addr); + m_binary_addresses.clear(); + ++num_keys_decoded; + for (llvm::StringRef x : llvm::split(value, ',')) { + addr_t vmaddr; + x.consume_front("0x"); + if (llvm::to_integer(x, vmaddr, 16)) + m_binary_addresses.push_back(vmaddr); } } } diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -593,8 +593,19 @@ UUID uuid; const bool value_is_slide = false; for (addr_t addr : bin_addrs) { - const bool force_symbol_search = true; const bool notify = true; + // First see if this is a special platform + // binary that may determine the DynamicLoader and + // Platform to be used in this Process/Target in the + // process of loading it. + if (GetTarget() + .GetDebugger() + .GetPlatformList() + .LoadPlatformBinaryAndSetup(this, addr, notify)) + continue; + + const bool force_symbol_search = true; + // Second manually load this binary into the Target. DynamicLoader::LoadBinaryWithUUIDAndAddress( this, uuid, addr, value_is_slide, force_symbol_search, notify); } diff --git a/lldb/source/Target/Platform.cpp b/lldb/source/Target/Platform.cpp --- a/lldb/source/Target/Platform.cpp +++ b/lldb/source/Target/Platform.cpp @@ -2079,3 +2079,21 @@ m_platforms.push_back(platform_sp); return platform_sp; } + +bool PlatformList::LoadPlatformBinaryAndSetup(Process *process, + lldb::addr_t addr, bool notify) { + std::lock_guard guard(m_mutex); + + PlatformCreateInstance create_callback; + for (int idx = 0; + (create_callback = PluginManager::GetPlatformCreateCallbackAtIndex(idx)); + ++idx) { + ArchSpec arch; + PlatformSP platform_sp = create_callback(true, &arch); + if (platform_sp) { + if (platform_sp->LoadPlatformBinaryAndSetup(process, addr, notify)) + return true; + } + } + return false; +} diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -2653,6 +2653,10 @@ return m_dyld_up.get(); } +void Process::SetDynamicLoader(DynamicLoaderUP dyld_up) { + m_dyld_up = std::move(dyld_up); +} + DataExtractor Process::GetAuxvData() { return DataExtractor(); } llvm::Expected Process::SaveCore(llvm::StringRef outfile) {