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
219 lines
7.0 KiB
C++
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__)
|