Recognize a platform binary in ProcessGDBRemote which determines plugins

Complete support of the binary-addresses key in the qProcessInfo packet
in ProcessGDBRemote, for detecting if one of the binaries needs to be
handled by a Platform plugin, and can be used to set the Process'
DynamicLoader plugin and the Target's Platform plugin.

Implement this method in PlatformDarwinKernel to recognize a kernel
fileset at that address, find the actual kernel address in the
fileset, set DynamicLoaderDarwinKernel and PlatformDarwinKernel
in the Process/Target; register the kernel address with the dynamic
loader so it will be loaded later during attach.

This patch only addresses the live debug scenario with a gdb remote
serial protocol connection. I'll handle corefiles in a subsequent
patch that builds on this.

Differential Revision: https://reviews.llvm.org/D133534
rdar://98754861
This commit is contained in:
Jason Molenda 2022-09-09 14:52:59 -07:00
parent bd16ffb389
commit 1a608cfb5c
11 changed files with 245 additions and 98 deletions

View File

@ -846,6 +846,34 @@ public:
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 @@ public:
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<lldb::PlatformSP> collection;
mutable std::recursive_mutex m_mutex;

View File

@ -641,6 +641,8 @@ public:
/// 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.

View File

@ -230,6 +230,10 @@ ModuleSP DynamicLoader::LoadBinaryWithUUIDAndAddress(Process *process,
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;

View File

@ -130,6 +130,20 @@ static DynamicLoaderDarwinKernelProperties &GetGlobalProperties() {
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 @@ DynamicLoader *DynamicLoaderDarwinKernel::CreateInstance(Process *process,
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 @@ DynamicLoader *DynamicLoaderDarwinKernel::CreateInstance(Process *process,
// 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 @@ DynamicLoader *DynamicLoaderDarwinKernel::CreateInstance(Process *process,
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 @@ DynamicLoaderDarwinKernel::SearchForDarwinKernel(Process *process) {
lldb::addr_t
DynamicLoaderDarwinKernel::SearchForKernelAtSameLoadAddr(Process *process) {
Module *exe_module = process->GetTarget().GetExecutableModulePointer();
if (exe_module == nullptr)
if (!is_kernel(process->GetTarget().GetExecutableModulePointer()))
return LLDB_INVALID_ADDRESS;
ObjectFile *exe_objfile = exe_module->GetObjectFile();
if (exe_objfile == nullptr)
return LLDB_INVALID_ADDRESS;
if (exe_objfile->GetType() != ObjectFile::eTypeExecutable ||
exe_objfile->GetStrata() != ObjectFile::eStrataKernel)
return LLDB_INVALID_ADDRESS;
if (!exe_objfile->GetBaseAddress().IsValid())
return LLDB_INVALID_ADDRESS;
@ -475,8 +473,7 @@ DynamicLoaderDarwinKernel::CheckForKernelImageAtAddress(lldb::addr_t addr,
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 @@ void DynamicLoaderDarwinKernel::UpdateIfNeeded() {
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 @@ bool DynamicLoaderDarwinKernel::KextImageInfo::LoadImageAtFileAddress(
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 @@ bool DynamicLoaderDarwinKernel::KextImageInfo::ReadMemoryModule(
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 @@ bool DynamicLoaderDarwinKernel::KextImageInfo::ReadMemoryModule(
}
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 @@ bool DynamicLoaderDarwinKernel::KextImageInfo::ReadMemoryModule(
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 @@ bool DynamicLoaderDarwinKernel::KextImageInfo::LoadImageUsingMemoryModule(
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 @@ bool DynamicLoaderDarwinKernel::KextImageInfo::LoadImageUsingMemoryModule(
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 @@ DynamicLoaderDarwinKernel::KextImageInfo::GetArchitecture() const {
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() &&

View File

@ -44,6 +44,8 @@ add_lldb_library(lldbPluginPlatformMacOSX PLUGIN
lldbSymbol
lldbTarget
lldbUtility
lldbPluginDynamicLoaderDarwinKernel
lldbPluginObjectContainerMachOFileset
lldbPluginPlatformPOSIX
${OBJC_LIBS}
CLANG_LIBS

View File

@ -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 <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 +728,28 @@ Status PlatformDarwinKernel::GetSharedModule(
// "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 @@ Status PlatformDarwinKernel::GetSharedModuleKernel(
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 @@ Status PlatformDarwinKernel::GetSharedModuleKernel(
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 @@ Status PlatformDarwinKernel::ExamineKextForMatchingUUID(
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<ObjectContainerMachOFileset *>(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<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;

View File

@ -154,6 +154,9 @@ protected:
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.

View File

@ -2206,12 +2206,13 @@ bool GDBRemoteCommunicationClient::GetCurrentProcessInfo(bool allow_lazy) {
++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);
}
}
}

View File

@ -593,8 +593,19 @@ Status ProcessGDBRemote::DoConnectRemote(llvm::StringRef remote_url) {
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);
}

View File

@ -2079,3 +2079,21 @@ PlatformSP PlatformList::Create(llvm::StringRef name) {
m_platforms.push_back(platform_sp);
return platform_sp;
}
bool PlatformList::LoadPlatformBinaryAndSetup(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->LoadPlatformBinaryAndSetup(process, addr, notify))
return true;
}
}
return false;
}

View File

@ -2653,6 +2653,10 @@ DynamicLoader *Process::GetDynamicLoader() {
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) {