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
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits