Skip to content

Commit 9c7fbc3

Browse files
committed
[lldb] Introduce a FreeBSDKernel plugin for vmcores
Introduce a FreeBSDKernel plugin that provides the ability to read FreeBSD kernel core dumps. The plugin utilizes libfbsdvmcore to provide support for both "full memory dump" and minidump formats across variety of architectures supported by FreeBSD. It provides the ability to read kernel memory, as well as the crashed thread status with registers on arm64, i386 and x86_64. Differential Revision: https://reviews.llvm.org/D114911
1 parent f4abf28 commit 9c7fbc3

29 files changed

+1244
-1
lines changed

lldb/cmake/modules/LLDBConfig.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ add_optional_dependency(LLDB_ENABLE_LZMA "Enable LZMA compression support in LLD
5858
add_optional_dependency(LLDB_ENABLE_LUA "Enable Lua scripting support in LLDB" LuaAndSwig LUAANDSWIG_FOUND)
5959
add_optional_dependency(LLDB_ENABLE_PYTHON "Enable Python scripting support in LLDB" PythonAndSwig PYTHONANDSWIG_FOUND)
6060
add_optional_dependency(LLDB_ENABLE_LIBXML2 "Enable Libxml 2 support in LLDB" LibXml2 LIBXML2_FOUND VERSION 2.8)
61+
add_optional_dependency(LLDB_ENABLE_FBSDVMCORE "Enable libfbsdvmcore support in LLDB" FBSDVMCore FBSDVMCore_FOUND)
6162

6263
option(LLDB_USE_SYSTEM_SIX "Use six.py shipped with system and do not install a copy of it" OFF)
6364
option(LLDB_USE_ENTITLEMENTS "When codesigning, use entitlements if available" ON)

lldb/include/lldb/Host/Config.h.cmake

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@
4545

4646
#cmakedefine01 LLDB_ENABLE_PYTHON
4747

48+
#cmakedefine01 LLDB_ENABLE_FBSDVMCORE
49+
4850
#cmakedefine01 LLDB_EMBED_PYTHON_HOME
4951

5052
#cmakedefine LLDB_PYTHON_HOME R"(${LLDB_PYTHON_HOME})"

lldb/packages/Python/lldbsuite/test/decorators.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -881,6 +881,9 @@ def skipIfXmlSupportMissing(func):
881881
def skipIfEditlineSupportMissing(func):
882882
return _get_bool_config_skip_if_decorator("editline")(func)
883883

884+
def skipIfFBSDVMCoreSupportMissing(func):
885+
return _get_bool_config_skip_if_decorator("fbsdvmcore")(func)
886+
884887
def skipIfLLVMTargetMissing(target):
885888
config = lldb.SBDebugger.GetBuildConfiguration()
886889
targets = config.GetValueForKey("targets").GetValueForKey("value")

lldb/packages/Python/lldbsuite/test/lldbtest.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1570,7 +1570,7 @@ def findBuiltClang(self):
15701570
return os.environ["CC"]
15711571

15721572

1573-
def yaml2obj(self, yaml_path, obj_path):
1573+
def yaml2obj(self, yaml_path, obj_path, max_size=None):
15741574
"""
15751575
Create an object file at the given path from a yaml file.
15761576
@@ -1580,6 +1580,8 @@ def yaml2obj(self, yaml_path, obj_path):
15801580
if not yaml2obj_bin:
15811581
self.assertTrue(False, "No valid yaml2obj executable specified")
15821582
command = [yaml2obj_bin, "-o=%s" % obj_path, yaml_path]
1583+
if max_size is not None:
1584+
command += ["--max-size=%d" % max_size]
15831585
self.runBuildCommand(command)
15841586

15851587
def getBuildFlags(

lldb/source/API/SBDebugger.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,9 @@ SBStructuredData SBDebugger::GetBuildConfiguration() {
749749
AddBoolConfigEntry(
750750
*config_up, "lua", LLDB_ENABLE_LUA,
751751
"A boolean value that indicates if lua support is enabled in LLDB");
752+
AddBoolConfigEntry(*config_up, "fbsdvmcore", LLDB_ENABLE_FBSDVMCORE,
753+
"A boolean value that indicates if fbsdvmcore support is "
754+
"enabled in LLDB");
752755
AddLLVMTargets(*config_up);
753756

754757
SBStructuredData data;

lldb/source/Plugins/Process/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ add_subdirectory(Utility)
1818
add_subdirectory(elf-core)
1919
add_subdirectory(mach-core)
2020
add_subdirectory(minidump)
21+
add_subdirectory(FreeBSDKernel)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
if (NOT FBSDVMCore_FOUND)
2+
message(STATUS "Skipping FreeBSDKernel plugin due to missing libfbsdvmcore")
3+
return()
4+
endif()
5+
6+
add_lldb_library(lldbPluginProcessFreeBSDKernel PLUGIN
7+
ProcessFreeBSDKernel.cpp
8+
RegisterContextFreeBSDKernel_arm64.cpp
9+
RegisterContextFreeBSDKernel_i386.cpp
10+
RegisterContextFreeBSDKernel_x86_64.cpp
11+
ThreadFreeBSDKernel.cpp
12+
13+
LINK_LIBS
14+
lldbCore
15+
lldbTarget
16+
fbsdvmcore
17+
LINK_COMPONENTS
18+
Support
19+
)
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
//===-- ProcessFreeBSDKernel.cpp ------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "lldb/Core/Module.h"
10+
#include "lldb/Core/PluginManager.h"
11+
#include "lldb/Target/DynamicLoader.h"
12+
13+
#include "ProcessFreeBSDKernel.h"
14+
#include "ThreadFreeBSDKernel.h"
15+
#include "Plugins/DynamicLoader/Static/DynamicLoaderStatic.h"
16+
17+
#include <fvc.h>
18+
19+
using namespace lldb;
20+
using namespace lldb_private;
21+
22+
LLDB_PLUGIN_DEFINE(ProcessFreeBSDKernel)
23+
24+
ProcessFreeBSDKernel::ProcessFreeBSDKernel(lldb::TargetSP target_sp,
25+
ListenerSP listener_sp,
26+
const FileSpec &core_file, void *fvc)
27+
: PostMortemProcess(target_sp, listener_sp), m_fvc(fvc) {}
28+
29+
ProcessFreeBSDKernel::~ProcessFreeBSDKernel() {
30+
if (m_fvc)
31+
fvc_close(static_cast<fvc_t *>(m_fvc));
32+
}
33+
34+
lldb::ProcessSP ProcessFreeBSDKernel::CreateInstance(lldb::TargetSP target_sp,
35+
ListenerSP listener_sp,
36+
const FileSpec *crash_file,
37+
bool can_connect) {
38+
lldb::ProcessSP process_sp;
39+
ModuleSP executable = target_sp->GetExecutableModule();
40+
if (crash_file && !can_connect && executable) {
41+
fvc_t *fvc = fvc_open(
42+
executable->GetFileSpec().GetPath().c_str(),
43+
crash_file->GetPath().c_str(), nullptr, nullptr, nullptr);
44+
if (fvc)
45+
process_sp = std::make_shared<ProcessFreeBSDKernel>(
46+
target_sp, listener_sp, *crash_file, fvc);
47+
}
48+
return process_sp;
49+
}
50+
51+
void ProcessFreeBSDKernel::Initialize() {
52+
static llvm::once_flag g_once_flag;
53+
54+
llvm::call_once(g_once_flag, []() {
55+
PluginManager::RegisterPlugin(GetPluginNameStatic(),
56+
GetPluginDescriptionStatic(), CreateInstance);
57+
});
58+
}
59+
60+
void ProcessFreeBSDKernel::Terminate() {
61+
PluginManager::UnregisterPlugin(ProcessFreeBSDKernel::CreateInstance);
62+
}
63+
64+
Status ProcessFreeBSDKernel::DoDestroy() { return Status(); }
65+
66+
bool ProcessFreeBSDKernel::CanDebug(lldb::TargetSP target_sp,
67+
bool plugin_specified_by_name) {
68+
return true;
69+
}
70+
71+
void ProcessFreeBSDKernel::RefreshStateAfterStop() {}
72+
73+
bool ProcessFreeBSDKernel::DoUpdateThreadList(ThreadList &old_thread_list,
74+
ThreadList &new_thread_list) {
75+
if (old_thread_list.GetSize(false) == 0) {
76+
// Make up the thread the first time this is called so we can set our one
77+
// and only core thread state up.
78+
79+
// We cannot construct a thread without a register context as that crashes
80+
// LLDB but we can construct a process without threads to provide minimal
81+
// memory reading support.
82+
switch (GetTarget().GetArchitecture().GetMachine()) {
83+
case llvm::Triple::aarch64:
84+
case llvm::Triple::x86:
85+
case llvm::Triple::x86_64:
86+
break;
87+
default:
88+
return false;
89+
}
90+
91+
const Symbol *pcb_sym =
92+
GetTarget().GetExecutableModule()->FindFirstSymbolWithNameAndType(
93+
ConstString("dumppcb"));
94+
ThreadSP thread_sp(new ThreadFreeBSDKernel(
95+
*this, 1, pcb_sym ? pcb_sym->GetFileAddress() : LLDB_INVALID_ADDRESS));
96+
new_thread_list.AddThread(thread_sp);
97+
} else {
98+
const uint32_t num_threads = old_thread_list.GetSize(false);
99+
for (uint32_t i = 0; i < num_threads; ++i)
100+
new_thread_list.AddThread(old_thread_list.GetThreadAtIndex(i, false));
101+
}
102+
return new_thread_list.GetSize(false) > 0;
103+
}
104+
105+
Status ProcessFreeBSDKernel::DoLoadCore() {
106+
// The core is already loaded by CreateInstance().
107+
return Status();
108+
}
109+
110+
size_t ProcessFreeBSDKernel::DoReadMemory(lldb::addr_t addr, void *buf,
111+
size_t size, Status &error) {
112+
ssize_t rd = fvc_read(static_cast<fvc_t *>(m_fvc), addr, buf, size);
113+
if (rd < 0 || static_cast<size_t>(rd) != size) {
114+
error.SetErrorStringWithFormat("Reading memory failed: %s",
115+
fvc_geterr(static_cast<fvc_t *>(m_fvc)));
116+
return rd > 0 ? rd : 0;
117+
}
118+
return rd;
119+
}
120+
121+
DynamicLoader *ProcessFreeBSDKernel::GetDynamicLoader() {
122+
if (m_dyld_up.get() == nullptr)
123+
m_dyld_up.reset(DynamicLoader::FindPlugin(
124+
this, DynamicLoaderStatic::GetPluginNameStatic()));
125+
return m_dyld_up.get();
126+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//===-- ProcessFreeBSDKernel.h ----------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLDB_SOURCE_PLUGINS_PROCESS_FREEBSDKERNEL_PROCESSFREEBSDKERNEL_H
10+
#define LLDB_SOURCE_PLUGINS_PROCESS_FREEBSDKERNEL_PROCESSFREEBSDKERNEL_H
11+
12+
#include "lldb/Target/PostMortemProcess.h"
13+
14+
class ProcessFreeBSDKernel : public lldb_private::PostMortemProcess {
15+
public:
16+
ProcessFreeBSDKernel(lldb::TargetSP target_sp, lldb::ListenerSP listener,
17+
const lldb_private::FileSpec &core_file, void *fvc);
18+
19+
~ProcessFreeBSDKernel() override;
20+
21+
static lldb::ProcessSP
22+
CreateInstance(lldb::TargetSP target_sp, lldb::ListenerSP listener,
23+
const lldb_private::FileSpec *crash_file_path,
24+
bool can_connect);
25+
26+
static void Initialize();
27+
28+
static void Terminate();
29+
30+
static llvm::StringRef GetPluginNameStatic() { return "freebsd-kernel"; }
31+
32+
static llvm::StringRef GetPluginDescriptionStatic() {
33+
return "FreeBSD kernel vmcore debugging plug-in.";
34+
}
35+
36+
llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
37+
38+
lldb_private::Status DoDestroy() override;
39+
40+
bool CanDebug(lldb::TargetSP target_sp,
41+
bool plugin_specified_by_name) override;
42+
43+
void RefreshStateAfterStop() override;
44+
45+
lldb_private::Status DoLoadCore() override;
46+
47+
size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
48+
lldb_private::Status &error) override;
49+
50+
lldb_private::DynamicLoader *GetDynamicLoader() override;
51+
52+
protected:
53+
bool DoUpdateThreadList(lldb_private::ThreadList &old_thread_list,
54+
lldb_private::ThreadList &new_thread_list) override;
55+
56+
private:
57+
void *m_fvc;
58+
};
59+
60+
#endif // LLDB_SOURCE_PLUGINS_PROCESS_FREEBSDKERNEL_PROCESSFREEBSDKERNEL_H
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
//===-- RegisterContextFreeBSDKernel_arm64.cpp ----------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "RegisterContextFreeBSDKernel_arm64.h"
10+
#include "Plugins/Process/Utility/lldb-arm64-register-enums.h"
11+
12+
#include "lldb/Target/Process.h"
13+
#include "lldb/Target/Thread.h"
14+
#include "lldb/Utility/RegisterValue.h"
15+
#include "llvm/Support/Endian.h"
16+
17+
using namespace lldb;
18+
using namespace lldb_private;
19+
20+
RegisterContextFreeBSDKernel_arm64::RegisterContextFreeBSDKernel_arm64(
21+
Thread &thread, std::unique_ptr<RegisterInfoPOSIX_arm64> register_info_up,
22+
lldb::addr_t pcb_addr)
23+
: RegisterContextPOSIX_arm64(thread, std::move(register_info_up)),
24+
m_pcb_addr(pcb_addr) {}
25+
26+
bool RegisterContextFreeBSDKernel_arm64::ReadGPR() { return true; }
27+
28+
bool RegisterContextFreeBSDKernel_arm64::ReadFPR() { return true; }
29+
30+
bool RegisterContextFreeBSDKernel_arm64::WriteGPR() {
31+
assert(0);
32+
return false;
33+
}
34+
35+
bool RegisterContextFreeBSDKernel_arm64::WriteFPR() {
36+
assert(0);
37+
return false;
38+
}
39+
40+
bool RegisterContextFreeBSDKernel_arm64::ReadRegister(
41+
const RegisterInfo *reg_info, RegisterValue &value) {
42+
if (m_pcb_addr == LLDB_INVALID_ADDRESS)
43+
return false;
44+
45+
struct {
46+
llvm::support::ulittle64_t x[30];
47+
llvm::support::ulittle64_t lr;
48+
llvm::support::ulittle64_t _reserved;
49+
llvm::support::ulittle64_t sp;
50+
} pcb;
51+
52+
Status error;
53+
size_t rd =
54+
m_thread.GetProcess()->ReadMemory(m_pcb_addr, &pcb, sizeof(pcb), error);
55+
if (rd != sizeof(pcb))
56+
return false;
57+
58+
uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
59+
switch (reg) {
60+
case gpr_x0_arm64:
61+
case gpr_x1_arm64:
62+
case gpr_x2_arm64:
63+
case gpr_x3_arm64:
64+
case gpr_x4_arm64:
65+
case gpr_x5_arm64:
66+
case gpr_x6_arm64:
67+
case gpr_x7_arm64:
68+
case gpr_x8_arm64:
69+
case gpr_x9_arm64:
70+
case gpr_x10_arm64:
71+
case gpr_x11_arm64:
72+
case gpr_x12_arm64:
73+
case gpr_x13_arm64:
74+
case gpr_x14_arm64:
75+
case gpr_x15_arm64:
76+
case gpr_x16_arm64:
77+
case gpr_x17_arm64:
78+
case gpr_x18_arm64:
79+
case gpr_x19_arm64:
80+
case gpr_x20_arm64:
81+
case gpr_x21_arm64:
82+
case gpr_x22_arm64:
83+
case gpr_x23_arm64:
84+
case gpr_x24_arm64:
85+
case gpr_x25_arm64:
86+
case gpr_x26_arm64:
87+
case gpr_x27_arm64:
88+
case gpr_x28_arm64:
89+
case gpr_fp_arm64:
90+
static_assert(gpr_fp_arm64 - gpr_x0_arm64 == 29,
91+
"nonconsecutive arm64 register numbers");
92+
value = pcb.x[reg - gpr_x0_arm64];
93+
break;
94+
case gpr_sp_arm64:
95+
value = pcb.sp;
96+
break;
97+
case gpr_pc_arm64:
98+
// The pc of crashing thread is stored in lr.
99+
value = pcb.lr;
100+
break;
101+
default:
102+
return false;
103+
}
104+
return true;
105+
}
106+
107+
bool RegisterContextFreeBSDKernel_arm64::WriteRegister(
108+
const RegisterInfo *reg_info, const RegisterValue &value) {
109+
return false;
110+
}

0 commit comments

Comments
 (0)