[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
This commit is contained in:
Michał Górny 2021-12-18 10:09:12 +01:00
parent 5eb271880c
commit fb785877a9
4 changed files with 174 additions and 39 deletions

View File

@ -1,4 +1,12 @@
if (NOT FBSDVMCore_FOUND)
set(FBSDKERNEL_LIBS)
if(FBSDVMCore_FOUND)
list(APPEND FBSDKERNEL_LIBS fbsdvmcore)
endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
list(APPEND FBSDKERNEL_LIBS kvm)
endif()
if (NOT FBSDKERNEL_LIBS)
message(STATUS "Skipping FreeBSDKernel plugin due to missing libfbsdvmcore")
return()
endif()
@ -13,7 +21,7 @@ add_lldb_library(lldbPluginProcessFreeBSDKernel PLUGIN
LINK_LIBS
lldbCore
lldbTarget
fbsdvmcore
${FBSDKERNEL_LIBS}
LINK_COMPONENTS
Support
)

View File

@ -10,42 +10,91 @@
#include "lldb/Core/PluginManager.h"
#include "lldb/Target/DynamicLoader.h"
#include "Plugins/DynamicLoader/Static/DynamicLoaderStatic.h"
#include "ProcessFreeBSDKernel.h"
#include "ThreadFreeBSDKernel.h"
#include "Plugins/DynamicLoader/Static/DynamicLoaderStatic.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)
ProcessFreeBSDKernel::ProcessFreeBSDKernel(lldb::TargetSP target_sp,
ListenerSP listener_sp,
const FileSpec &core_file, void *fvc)
: PostMortemProcess(target_sp, listener_sp), m_fvc(fvc) {}
namespace {
ProcessFreeBSDKernel::~ProcessFreeBSDKernel() {
if (m_fvc)
fvc_close(static_cast<fvc_t *>(m_fvc));
}
#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) {
lldb::ProcessSP process_sp;
ModuleSP executable = target_sp->GetExecutableModule();
if (crash_file && !can_connect && executable) {
fvc_t *fvc = fvc_open(
executable->GetFileSpec().GetPath().c_str(),
crash_file->GetPath().c_str(), nullptr, nullptr, nullptr);
#if LLDB_ENABLE_FBSDVMCORE
fvc_t *fvc =
fvc_open(executable->GetFileSpec().GetPath().c_str(),
crash_file->GetPath().c_str(), nullptr, nullptr, nullptr);
if (fvc)
process_sp = std::make_shared<ProcessFreeBSDKernel>(
target_sp, listener_sp, *crash_file, 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 process_sp;
return nullptr;
}
void ProcessFreeBSDKernel::Initialize() {
@ -107,20 +156,63 @@ Status ProcessFreeBSDKernel::DoLoadCore() {
return Status();
}
size_t ProcessFreeBSDKernel::DoReadMemory(lldb::addr_t addr, void *buf,
size_t size, Status &error) {
ssize_t rd = fvc_read(static_cast<fvc_t *>(m_fvc), addr, buf, size);
if (rd < 0 || static_cast<size_t>(rd) != size) {
error.SetErrorStringWithFormat("Reading memory failed: %s",
fvc_geterr(static_cast<fvc_t *>(m_fvc)));
return rd > 0 ? rd : 0;
}
return rd;
}
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__)

View File

@ -13,10 +13,7 @@
class ProcessFreeBSDKernel : public lldb_private::PostMortemProcess {
public:
ProcessFreeBSDKernel(lldb::TargetSP target_sp, lldb::ListenerSP listener,
const lldb_private::FileSpec &core_file, void *fvc);
~ProcessFreeBSDKernel() override;
ProcessFreeBSDKernel(lldb::TargetSP target_sp, lldb::ListenerSP listener);
static lldb::ProcessSP
CreateInstance(lldb::TargetSP target_sp, lldb::ListenerSP listener,
@ -44,17 +41,11 @@ public:
lldb_private::Status DoLoadCore() override;
size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
lldb_private::Status &error) override;
lldb_private::DynamicLoader *GetDynamicLoader() override;
protected:
bool DoUpdateThreadList(lldb_private::ThreadList &old_thread_list,
lldb_private::ThreadList &new_thread_list) override;
private:
void *m_fvc;
};
#endif // LLDB_SOURCE_PLUGINS_PROCESS_FREEBSDKERNEL_PROCESSFREEBSDKERNEL_H

View File

@ -0,0 +1,44 @@
import os
import struct
import subprocess
import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
class FreeBSDKernelVMCoreTestCase(TestBase):
NO_DEBUG_INFO_TESTCASE = True
mydir = TestBase.compute_mydir(__file__)
def test_mem(self):
kernel_exec = "/boot/kernel/kernel"
mem_device = "/dev/mem"
if not os.access(kernel_exec, os.R_OK):
self.skipTest("Kernel @ %s is not readable" % (kernel_exec,))
if not os.access(mem_device, os.R_OK):
self.skipTest("Memory @ %s is not readable" % (mem_device,))
target = self.dbg.CreateTarget(kernel_exec)
process = target.LoadCore(mem_device)
hz_value = int(subprocess.check_output(["sysctl", "-n", "kern.hz"]))
self.assertTrue(process, PROCESS_IS_VALID)
self.assertEqual(process.GetNumThreads(), 1)
self.assertEqual(process.GetProcessID(), 0)
# test memory reading
self.expect("expr -- *(int *) &hz",
substrs=["(int) $0 = %d" % hz_value])
main_mod = target.GetModuleAtIndex(0)
hz_addr = (main_mod.FindSymbols("hz")[0].symbol.addr
.GetLoadAddress(target))
error = lldb.SBError()
self.assertEqual(process.ReadMemory(hz_addr, 4, error),
struct.pack("<I", hz_value))
self.dbg.DeleteTarget(target)