jasonmolenda created this revision. jasonmolenda added reviewers: JDevlieghere, jingham. jasonmolenda added a project: LLDB. Herald added a subscriber: mgorny. Herald added a project: All. jasonmolenda requested review of this revision. Herald added a subscriber: lldb-commits.
In an internal development environment, lldb will attach to a gdb stub that can provide the address of a Mach-O fileset including the kernel binary. I added some preliminary support for the `binary-addresses` key in `qProcessInfo` in https://reviews.llvm.org/D130813; this patch fixes that support now that we have a live stub to run against, and passes the addr_t it gets through the PlatformList to see if any Platform knows how to handle it. PlatformDarwinKernel will test the addr_t to see if it is a Mach-O Fileset, using Jonas' mach-o fileset plugin (https://reviews.llvm.org/D132433 et al). If it is a fileset, it will search for a com.apple.kernel entry, and if found, it will set the Process DynamicLoader to be DynamicLoaderDarwinKernel, and register the address of the com.apple.kernel entry in the fileset, to be loaded later by the DynamicLoader plugin. The big trick with this is that I needed to do several Darwin specific things -- parse a Mach-O fileset in memory, detect if this is a Darwin kernel, set the Process DynamicLoader -- when I'm given only an addr_t in ProcessGDBRemote. Going through the Platform plugins and asking them if they can do these things given the addr_t was a way of accomplishing this from generic code. This does mean that I introduced cross-platform dependencies in PlatformDarwinKernel. It needs to directly create a `ObjectContainerMachOFileset` object to use the `FindEntry` methods which are only available to the subclass. I need to instantiate a DynamicLoaderDarwinKernel object and set the Process to use it as the `m_dyld`. I like isolating all of this very-darwin-specific logic down in PlatformDarwinKernel, but I didn't see a good way of working around the dependencies. Testing this is a trick I haven't figured out yet; I'd need a remote stub that will return the `qProcessInfo` `binary-addresses` key, have a Mach-O fileset there with a com.apple.kernel entry pointing to a binary that is structured like a Darwin kernel binary enough to trick lldb into loading it. It's possible to hand-test against a live environment, but faking enough of that to create a test would be a lot. There will be a second patch dealing with corefiles, where I ended up making a lot of changes to ProcessMachCore so it's a rather large diff, and it was possible to separate the two halves, so I did. Any feedback appreciated, I've been working on this one for a little bit now & a fresh set of eyes is welcome. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D133534 Files: lldb/include/lldb/Target/Platform.h lldb/include/lldb/Target/Process.h lldb/source/Core/DynamicLoader.cpp lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp lldb/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.cpp lldb/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.h lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp lldb/source/Target/Platform.cpp lldb/source/Target/Process.cpp lldb/unittests/Core/CMakeLists.txt lldb/unittests/Interpreter/CMakeLists.txt lldb/unittests/Platform/CMakeLists.txt lldb/unittests/Process/CMakeLists.txt lldb/unittests/SymbolFile/DWARF/CMakeLists.txt
Index: lldb/unittests/SymbolFile/DWARF/CMakeLists.txt =================================================================== --- lldb/unittests/SymbolFile/DWARF/CMakeLists.txt +++ lldb/unittests/SymbolFile/DWARF/CMakeLists.txt @@ -10,10 +10,12 @@ lldbCore lldbHost lldbSymbol + lldbPluginDynamicLoaderDarwinKernel lldbPluginObjectFilePECOFF lldbPluginSymbolFileDWARF lldbPluginSymbolFilePDB lldbPluginTypeSystemClang + lldbPluginObjectContainerMachOFileset lldbPluginPlatformMacOSX lldbUtilityHelpers lldbSymbolHelpers Index: lldb/unittests/Process/CMakeLists.txt =================================================================== --- lldb/unittests/Process/CMakeLists.txt +++ lldb/unittests/Process/CMakeLists.txt @@ -17,5 +17,7 @@ lldbUtility lldbUtilityHelpers lldbInterpreter + lldbPluginDynamicLoaderDarwinKernel + lldbPluginObjectContainerMachOFileset lldbPluginPlatformMacOSX ) Index: lldb/unittests/Platform/CMakeLists.txt =================================================================== --- lldb/unittests/Platform/CMakeLists.txt +++ lldb/unittests/Platform/CMakeLists.txt @@ -6,6 +6,8 @@ PlatformTest.cpp LINK_LIBS + lldbPluginDynamicLoaderDarwinKernel + lldbPluginObjectContainerMachOFileset lldbPluginPlatformFreeBSD lldbPluginPlatformLinux lldbPluginPlatformMacOSX Index: lldb/unittests/Interpreter/CMakeLists.txt =================================================================== --- lldb/unittests/Interpreter/CMakeLists.txt +++ lldb/unittests/Interpreter/CMakeLists.txt @@ -14,6 +14,8 @@ lldbUtility lldbUtilityHelpers lldbInterpreter + lldbPluginDynamicLoaderDarwinKernel + lldbPluginObjectContainerMachOFileset lldbPluginPlatformMacOSX LLVMTestingSupport ) Index: lldb/unittests/Core/CMakeLists.txt =================================================================== --- lldb/unittests/Core/CMakeLists.txt +++ lldb/unittests/Core/CMakeLists.txt @@ -14,9 +14,11 @@ LINK_LIBS lldbCore lldbHost + lldbPluginDynamicLoaderDarwinKernel lldbPluginObjectFileELF lldbPluginObjectFileMachO lldbPluginObjectFilePECOFF + lldbPluginObjectContainerMachOFileset lldbPluginPlatformMacOSX lldbPluginSymbolFileSymtab lldbSymbol Index: lldb/source/Target/Process.cpp =================================================================== --- lldb/source/Target/Process.cpp +++ 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<bool> Process::SaveCore(llvm::StringRef outfile) { Index: lldb/source/Target/Platform.cpp =================================================================== --- lldb/source/Target/Platform.cpp +++ lldb/source/Target/Platform.cpp @@ -2079,3 +2079,22 @@ m_platforms.push_back(platform_sp); return platform_sp; } + +bool PlatformList::LoadBinaryAndSetDynamicLoader(Process *process, + lldb::addr_t addr, + bool notify) { + std::lock_guard<std::recursive_mutex> 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->LoadBinaryAndSetDynamicLoader(process, addr, notify)) + return true; + } + } + return false; +} Index: lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp =================================================================== --- lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -593,8 +593,14 @@ UUID uuid; const bool value_is_slide = false; for (addr_t addr : bin_addrs) { - const bool force_symbol_search = true; const bool notify = true; + if (GetTarget() + .GetDebugger() + .GetPlatformList() + .LoadBinaryAndSetDynamicLoader(this, addr, notify)) + continue; + + const bool force_symbol_search = true; DynamicLoader::LoadBinaryWithUUIDAndAddress( this, uuid, addr, value_is_slide, force_symbol_search, notify); } Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp =================================================================== --- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ 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); } } } Index: lldb/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.h =================================================================== --- lldb/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.h +++ lldb/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.h @@ -154,6 +154,9 @@ const UUID &uuid, const ArchSpec &arch, lldb::ModuleSP &exe_module_sp); + bool LoadBinaryAndSetDynamicLoader(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. Index: lldb/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.cpp =================================================================== --- lldb/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.cpp +++ lldb/source/Plugins/Platform/MacOSX/PlatformDarwinKernel.cpp @@ -26,6 +26,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 +40,8 @@ #include <memory> #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 +727,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 +806,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 +839,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 +918,57 @@ 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)) + 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<ObjectContainerMachOFileset *>(container_sp.get()); + ObjectContainerMachOFileset::Entry *entry = + fileset_container->FindEntry("com.apple.kernel"); + if (entry) + return entry->vmaddr; + return LLDB_INVALID_ADDRESS; +} + +bool PlatformDarwinKernel::LoadBinaryAndSetDynamicLoader( + Process *process, lldb::addr_t input_addr, bool notify) { + Log *log = + GetLog(LLDBLog::Platform | LLDBLog::DynamicLoader | LLDBLog::Process); + 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, set the Process' dynamic loader + // appropriately and give it the actual address so it can be loaded later + // in the attach sequence. + DynamicLoaderUP dyld_up( + new DynamicLoaderDarwinKernel(process, actual_address)); + if (!dyld_up) + return false; + + // Process owns it now + process->SetDynamicLoader(std::move(dyld_up)); + + return true; +} + std::vector<ArchSpec> PlatformDarwinKernel::GetSupportedArchitectures( const ArchSpec &process_host_arch) { std::vector<ArchSpec> result; Index: lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp =================================================================== --- lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp +++ lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.cpp @@ -176,7 +176,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 +187,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; } @@ -525,10 +521,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(); @@ -720,14 +716,15 @@ } if (m_uuid.IsValid()) { ModuleSP exe_module_sp = process->GetTarget().GetExecutableModule(); - if (exe_module_sp.get() && exe_module_sp->GetUUID().IsValid()) { + if (exe_module_sp.get() && exe_module_sp->GetUUID().IsValid() && + exe_module_sp->GetUUID() != m_uuid && + exe_module_sp->GetObjectFile() && + exe_module_sp->GetObjectFile()->GetStrata() == + ObjectFile::eStrataKernel) { 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); @@ -841,10 +838,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 +973,12 @@ 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 (module_sp && module_sp->GetObjectFile() && + module_sp->GetObjectFile()->GetStrata() == ObjectFile::eStrataKernel) { + m_kernel.SetModule(module_sp); + m_kernel.SetIsKernel(true); + } ConstString kernel_name("mach_kernel"); if (m_kernel.GetModule().get() && m_kernel.GetModule()->GetObjectFile() && Index: lldb/source/Core/DynamicLoader.cpp =================================================================== --- lldb/source/Core/DynamicLoader.cpp +++ 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; Index: lldb/include/lldb/Target/Process.h =================================================================== --- lldb/include/lldb/Target/Process.h +++ 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. Index: lldb/include/lldb/Target/Platform.h =================================================================== --- lldb/include/lldb/Target/Platform.h +++ lldb/include/lldb/Target/Platform.h @@ -846,6 +846,32 @@ return nullptr; } + /// Given an address of a binary, the platform may be able to + /// set the correct DynamicLoader plugin that should be used for + /// this process, and register this binary with the DynamicLoader + /// so it will be loaded properly. May change the Process + /// DynamicLoader. + /// + /// \param[in] process + /// Process read memory from. + /// + /// \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 LoadBinaryAndSetDynamicLoader(Process *process, + lldb::addr_t addr, bool notify) { + return false; + } + virtual CompilerType GetSiginfoType(const llvm::Triple &triple); virtual Args GetExtraStartupCommands(); @@ -1026,6 +1052,30 @@ lldb::PlatformSP Create(llvm::StringRef name); + /// Given an address of a binary, the platform may be able to + /// set the correct DynamicLoader plugin that should be used for + /// this process, and register this binary with the DynamicLoader + /// so it will be loaded properly. May change the Process + /// DynamicLoader. + /// + /// \param[in] process + /// Process read memory from. + /// + /// \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 LoadBinaryAndSetDynamicLoader(Process *process, lldb::addr_t addr, + bool notify); + protected: typedef std::vector<lldb::PlatformSP> collection; mutable std::recursive_mutex m_mutex;
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits