Michał Górny fb785877a9 [lldb] [Process/FreeBSDKernel] Introduce libkvm support
Introduce initial support for using libkvm on FreeBSD.  The library
can be used as an alternate implementation for processing kernel
coredumps but it can also be used to access live kernel memory through
specifying "/dev/mem" as the core file, i.e.:

    lldb --core /dev/mem /boot/kernel/kernel

Differential Revision: https://reviews.llvm.org/D116005
2021-12-22 16:14:03 +01:00

219 lines
7.0 KiB
C++

//===-- ProcessFreeBSDKernel.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 "lldb/Core/Module.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Target/DynamicLoader.h"
#include "Plugins/DynamicLoader/Static/DynamicLoaderStatic.h"
#include "ProcessFreeBSDKernel.h"
#include "ThreadFreeBSDKernel.h"
#if LLDB_ENABLE_FBSDVMCORE
#include <fvc.h>
#endif
#if defined(__FreeBSD__)
#include <kvm.h>
#endif
using namespace lldb;
using namespace lldb_private;
LLDB_PLUGIN_DEFINE(ProcessFreeBSDKernel)
namespace {
#if LLDB_ENABLE_FBSDVMCORE
class ProcessFreeBSDKernelFVC : public ProcessFreeBSDKernel {
public:
ProcessFreeBSDKernelFVC(lldb::TargetSP target_sp, lldb::ListenerSP listener,
fvc_t *fvc);
~ProcessFreeBSDKernelFVC();
size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
lldb_private::Status &error) override;
private:
fvc_t *m_fvc;
const char *GetError();
};
#endif // LLDB_ENABLE_FBSDVMCORE
#if defined(__FreeBSD__)
class ProcessFreeBSDKernelKVM : public ProcessFreeBSDKernel {
public:
ProcessFreeBSDKernelKVM(lldb::TargetSP target_sp, lldb::ListenerSP listener,
kvm_t *fvc);
~ProcessFreeBSDKernelKVM();
size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
lldb_private::Status &error) override;
private:
kvm_t *m_kvm;
const char *GetError();
};
#endif // defined(__FreeBSD__)
} // namespace
ProcessFreeBSDKernel::ProcessFreeBSDKernel(lldb::TargetSP target_sp,
ListenerSP listener_sp)
: PostMortemProcess(target_sp, listener_sp) {}
lldb::ProcessSP ProcessFreeBSDKernel::CreateInstance(lldb::TargetSP target_sp,
ListenerSP listener_sp,
const FileSpec *crash_file,
bool can_connect) {
ModuleSP executable = target_sp->GetExecutableModule();
if (crash_file && !can_connect && executable) {
#if LLDB_ENABLE_FBSDVMCORE
fvc_t *fvc =
fvc_open(executable->GetFileSpec().GetPath().c_str(),
crash_file->GetPath().c_str(), nullptr, nullptr, nullptr);
if (fvc)
return std::make_shared<ProcessFreeBSDKernelFVC>(target_sp, listener_sp,
fvc);
#endif
#if defined(__FreeBSD__)
kvm_t *kvm =
kvm_open2(executable->GetFileSpec().GetPath().c_str(),
crash_file->GetPath().c_str(), O_RDONLY, nullptr, nullptr);
if (kvm)
return std::make_shared<ProcessFreeBSDKernelKVM>(target_sp, listener_sp,
kvm);
#endif
}
return nullptr;
}
void ProcessFreeBSDKernel::Initialize() {
static llvm::once_flag g_once_flag;
llvm::call_once(g_once_flag, []() {
PluginManager::RegisterPlugin(GetPluginNameStatic(),
GetPluginDescriptionStatic(), CreateInstance);
});
}
void ProcessFreeBSDKernel::Terminate() {
PluginManager::UnregisterPlugin(ProcessFreeBSDKernel::CreateInstance);
}
Status ProcessFreeBSDKernel::DoDestroy() { return Status(); }
bool ProcessFreeBSDKernel::CanDebug(lldb::TargetSP target_sp,
bool plugin_specified_by_name) {
return true;
}
void ProcessFreeBSDKernel::RefreshStateAfterStop() {}
bool ProcessFreeBSDKernel::DoUpdateThreadList(ThreadList &old_thread_list,
ThreadList &new_thread_list) {
if (old_thread_list.GetSize(false) == 0) {
// Make up the thread the first time this is called so we can set our one
// and only core thread state up.
// We cannot construct a thread without a register context as that crashes
// LLDB but we can construct a process without threads to provide minimal
// memory reading support.
switch (GetTarget().GetArchitecture().GetMachine()) {
case llvm::Triple::aarch64:
case llvm::Triple::x86:
case llvm::Triple::x86_64:
break;
default:
return false;
}
const Symbol *pcb_sym =
GetTarget().GetExecutableModule()->FindFirstSymbolWithNameAndType(
ConstString("dumppcb"));
ThreadSP thread_sp(new ThreadFreeBSDKernel(
*this, 1, pcb_sym ? pcb_sym->GetFileAddress() : LLDB_INVALID_ADDRESS));
new_thread_list.AddThread(thread_sp);
} else {
const uint32_t num_threads = old_thread_list.GetSize(false);
for (uint32_t i = 0; i < num_threads; ++i)
new_thread_list.AddThread(old_thread_list.GetThreadAtIndex(i, false));
}
return new_thread_list.GetSize(false) > 0;
}
Status ProcessFreeBSDKernel::DoLoadCore() {
// The core is already loaded by CreateInstance().
return Status();
}
DynamicLoader *ProcessFreeBSDKernel::GetDynamicLoader() {
if (m_dyld_up.get() == nullptr)
m_dyld_up.reset(DynamicLoader::FindPlugin(
this, DynamicLoaderStatic::GetPluginNameStatic()));
return m_dyld_up.get();
}
#if LLDB_ENABLE_FBSDVMCORE
ProcessFreeBSDKernelFVC::ProcessFreeBSDKernelFVC(lldb::TargetSP target_sp,
ListenerSP listener_sp,
fvc_t *fvc)
: ProcessFreeBSDKernel(target_sp, listener_sp), m_fvc(fvc) {}
ProcessFreeBSDKernelFVC::~ProcessFreeBSDKernelFVC() {
if (m_fvc)
fvc_close(m_fvc);
}
size_t ProcessFreeBSDKernelFVC::DoReadMemory(lldb::addr_t addr, void *buf,
size_t size, Status &error) {
ssize_t rd = 0;
rd = fvc_read(m_fvc, addr, buf, size);
if (rd < 0 || static_cast<size_t>(rd) != size) {
error.SetErrorStringWithFormat("Reading memory failed: %s", GetError());
return rd > 0 ? rd : 0;
}
return rd;
}
const char *ProcessFreeBSDKernelFVC::GetError() { return fvc_geterr(m_fvc); }
#endif // LLDB_ENABLE_FBSDVMCORE
#if defined(__FreeBSD__)
ProcessFreeBSDKernelKVM::ProcessFreeBSDKernelKVM(lldb::TargetSP target_sp,
ListenerSP listener_sp,
kvm_t *fvc)
: ProcessFreeBSDKernel(target_sp, listener_sp), m_kvm(fvc) {}
ProcessFreeBSDKernelKVM::~ProcessFreeBSDKernelKVM() {
if (m_kvm)
kvm_close(m_kvm);
}
size_t ProcessFreeBSDKernelKVM::DoReadMemory(lldb::addr_t addr, void *buf,
size_t size, Status &error) {
ssize_t rd = 0;
rd = kvm_read2(m_kvm, addr, buf, size);
if (rd < 0 || static_cast<size_t>(rd) != size) {
error.SetErrorStringWithFormat("Reading memory failed: %s", GetError());
return rd > 0 ? rd : 0;
}
return rd;
}
const char *ProcessFreeBSDKernelKVM::GetError() { return kvm_geterr(m_kvm); }
#endif // defined(__FreeBSD__)