1*f6aab3d8Srobert //===-- ProcessFreeBSDKernel.cpp ------------------------------------------===//
2*f6aab3d8Srobert //
3*f6aab3d8Srobert // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*f6aab3d8Srobert // See https://llvm.org/LICENSE.txt for license information.
5*f6aab3d8Srobert // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*f6aab3d8Srobert //
7*f6aab3d8Srobert //===----------------------------------------------------------------------===//
8*f6aab3d8Srobert
9*f6aab3d8Srobert #include "lldb/Core/Module.h"
10*f6aab3d8Srobert #include "lldb/Core/PluginManager.h"
11*f6aab3d8Srobert #include "lldb/Target/DynamicLoader.h"
12*f6aab3d8Srobert
13*f6aab3d8Srobert #include "Plugins/DynamicLoader/Static/DynamicLoaderStatic.h"
14*f6aab3d8Srobert #include "ProcessFreeBSDKernel.h"
15*f6aab3d8Srobert #include "ThreadFreeBSDKernel.h"
16*f6aab3d8Srobert
17*f6aab3d8Srobert #if LLDB_ENABLE_FBSDVMCORE
18*f6aab3d8Srobert #include <fvc.h>
19*f6aab3d8Srobert #endif
20*f6aab3d8Srobert #if defined(__FreeBSD__)
21*f6aab3d8Srobert #include <kvm.h>
22*f6aab3d8Srobert #endif
23*f6aab3d8Srobert
24*f6aab3d8Srobert using namespace lldb;
25*f6aab3d8Srobert using namespace lldb_private;
26*f6aab3d8Srobert
27*f6aab3d8Srobert LLDB_PLUGIN_DEFINE(ProcessFreeBSDKernel)
28*f6aab3d8Srobert
29*f6aab3d8Srobert namespace {
30*f6aab3d8Srobert
31*f6aab3d8Srobert #if LLDB_ENABLE_FBSDVMCORE
32*f6aab3d8Srobert class ProcessFreeBSDKernelFVC : public ProcessFreeBSDKernel {
33*f6aab3d8Srobert public:
34*f6aab3d8Srobert ProcessFreeBSDKernelFVC(lldb::TargetSP target_sp, lldb::ListenerSP listener,
35*f6aab3d8Srobert fvc_t *fvc);
36*f6aab3d8Srobert
37*f6aab3d8Srobert ~ProcessFreeBSDKernelFVC();
38*f6aab3d8Srobert
39*f6aab3d8Srobert size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
40*f6aab3d8Srobert lldb_private::Status &error) override;
41*f6aab3d8Srobert
42*f6aab3d8Srobert private:
43*f6aab3d8Srobert fvc_t *m_fvc;
44*f6aab3d8Srobert
45*f6aab3d8Srobert const char *GetError();
46*f6aab3d8Srobert };
47*f6aab3d8Srobert #endif // LLDB_ENABLE_FBSDVMCORE
48*f6aab3d8Srobert
49*f6aab3d8Srobert #if defined(__FreeBSD__)
50*f6aab3d8Srobert class ProcessFreeBSDKernelKVM : public ProcessFreeBSDKernel {
51*f6aab3d8Srobert public:
52*f6aab3d8Srobert ProcessFreeBSDKernelKVM(lldb::TargetSP target_sp, lldb::ListenerSP listener,
53*f6aab3d8Srobert kvm_t *fvc);
54*f6aab3d8Srobert
55*f6aab3d8Srobert ~ProcessFreeBSDKernelKVM();
56*f6aab3d8Srobert
57*f6aab3d8Srobert size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
58*f6aab3d8Srobert lldb_private::Status &error) override;
59*f6aab3d8Srobert
60*f6aab3d8Srobert private:
61*f6aab3d8Srobert kvm_t *m_kvm;
62*f6aab3d8Srobert
63*f6aab3d8Srobert const char *GetError();
64*f6aab3d8Srobert };
65*f6aab3d8Srobert #endif // defined(__FreeBSD__)
66*f6aab3d8Srobert
67*f6aab3d8Srobert } // namespace
68*f6aab3d8Srobert
ProcessFreeBSDKernel(lldb::TargetSP target_sp,ListenerSP listener_sp)69*f6aab3d8Srobert ProcessFreeBSDKernel::ProcessFreeBSDKernel(lldb::TargetSP target_sp,
70*f6aab3d8Srobert ListenerSP listener_sp)
71*f6aab3d8Srobert : PostMortemProcess(target_sp, listener_sp) {}
72*f6aab3d8Srobert
CreateInstance(lldb::TargetSP target_sp,ListenerSP listener_sp,const FileSpec * crash_file,bool can_connect)73*f6aab3d8Srobert lldb::ProcessSP ProcessFreeBSDKernel::CreateInstance(lldb::TargetSP target_sp,
74*f6aab3d8Srobert ListenerSP listener_sp,
75*f6aab3d8Srobert const FileSpec *crash_file,
76*f6aab3d8Srobert bool can_connect) {
77*f6aab3d8Srobert ModuleSP executable = target_sp->GetExecutableModule();
78*f6aab3d8Srobert if (crash_file && !can_connect && executable) {
79*f6aab3d8Srobert #if LLDB_ENABLE_FBSDVMCORE
80*f6aab3d8Srobert fvc_t *fvc =
81*f6aab3d8Srobert fvc_open(executable->GetFileSpec().GetPath().c_str(),
82*f6aab3d8Srobert crash_file->GetPath().c_str(), nullptr, nullptr, nullptr);
83*f6aab3d8Srobert if (fvc)
84*f6aab3d8Srobert return std::make_shared<ProcessFreeBSDKernelFVC>(target_sp, listener_sp,
85*f6aab3d8Srobert fvc);
86*f6aab3d8Srobert #endif
87*f6aab3d8Srobert
88*f6aab3d8Srobert #if defined(__FreeBSD__)
89*f6aab3d8Srobert kvm_t *kvm =
90*f6aab3d8Srobert kvm_open2(executable->GetFileSpec().GetPath().c_str(),
91*f6aab3d8Srobert crash_file->GetPath().c_str(), O_RDONLY, nullptr, nullptr);
92*f6aab3d8Srobert if (kvm)
93*f6aab3d8Srobert return std::make_shared<ProcessFreeBSDKernelKVM>(target_sp, listener_sp,
94*f6aab3d8Srobert kvm);
95*f6aab3d8Srobert #endif
96*f6aab3d8Srobert }
97*f6aab3d8Srobert return nullptr;
98*f6aab3d8Srobert }
99*f6aab3d8Srobert
Initialize()100*f6aab3d8Srobert void ProcessFreeBSDKernel::Initialize() {
101*f6aab3d8Srobert static llvm::once_flag g_once_flag;
102*f6aab3d8Srobert
103*f6aab3d8Srobert llvm::call_once(g_once_flag, []() {
104*f6aab3d8Srobert PluginManager::RegisterPlugin(GetPluginNameStatic(),
105*f6aab3d8Srobert GetPluginDescriptionStatic(), CreateInstance);
106*f6aab3d8Srobert });
107*f6aab3d8Srobert }
108*f6aab3d8Srobert
Terminate()109*f6aab3d8Srobert void ProcessFreeBSDKernel::Terminate() {
110*f6aab3d8Srobert PluginManager::UnregisterPlugin(ProcessFreeBSDKernel::CreateInstance);
111*f6aab3d8Srobert }
112*f6aab3d8Srobert
DoDestroy()113*f6aab3d8Srobert Status ProcessFreeBSDKernel::DoDestroy() { return Status(); }
114*f6aab3d8Srobert
CanDebug(lldb::TargetSP target_sp,bool plugin_specified_by_name)115*f6aab3d8Srobert bool ProcessFreeBSDKernel::CanDebug(lldb::TargetSP target_sp,
116*f6aab3d8Srobert bool plugin_specified_by_name) {
117*f6aab3d8Srobert return true;
118*f6aab3d8Srobert }
119*f6aab3d8Srobert
RefreshStateAfterStop()120*f6aab3d8Srobert void ProcessFreeBSDKernel::RefreshStateAfterStop() {}
121*f6aab3d8Srobert
DoUpdateThreadList(ThreadList & old_thread_list,ThreadList & new_thread_list)122*f6aab3d8Srobert bool ProcessFreeBSDKernel::DoUpdateThreadList(ThreadList &old_thread_list,
123*f6aab3d8Srobert ThreadList &new_thread_list) {
124*f6aab3d8Srobert if (old_thread_list.GetSize(false) == 0) {
125*f6aab3d8Srobert // Make up the thread the first time this is called so we can set our one
126*f6aab3d8Srobert // and only core thread state up.
127*f6aab3d8Srobert
128*f6aab3d8Srobert // We cannot construct a thread without a register context as that crashes
129*f6aab3d8Srobert // LLDB but we can construct a process without threads to provide minimal
130*f6aab3d8Srobert // memory reading support.
131*f6aab3d8Srobert switch (GetTarget().GetArchitecture().GetMachine()) {
132*f6aab3d8Srobert case llvm::Triple::aarch64:
133*f6aab3d8Srobert case llvm::Triple::x86:
134*f6aab3d8Srobert case llvm::Triple::x86_64:
135*f6aab3d8Srobert break;
136*f6aab3d8Srobert default:
137*f6aab3d8Srobert return false;
138*f6aab3d8Srobert }
139*f6aab3d8Srobert
140*f6aab3d8Srobert Status error;
141*f6aab3d8Srobert
142*f6aab3d8Srobert // struct field offsets are written as symbols so that we don't have
143*f6aab3d8Srobert // to figure them out ourselves
144*f6aab3d8Srobert int32_t offset_p_list = ReadSignedIntegerFromMemory(
145*f6aab3d8Srobert FindSymbol("proc_off_p_list"), 4, -1, error);
146*f6aab3d8Srobert int32_t offset_p_pid =
147*f6aab3d8Srobert ReadSignedIntegerFromMemory(FindSymbol("proc_off_p_pid"), 4, -1, error);
148*f6aab3d8Srobert int32_t offset_p_threads = ReadSignedIntegerFromMemory(
149*f6aab3d8Srobert FindSymbol("proc_off_p_threads"), 4, -1, error);
150*f6aab3d8Srobert int32_t offset_p_comm = ReadSignedIntegerFromMemory(
151*f6aab3d8Srobert FindSymbol("proc_off_p_comm"), 4, -1, error);
152*f6aab3d8Srobert
153*f6aab3d8Srobert int32_t offset_td_tid = ReadSignedIntegerFromMemory(
154*f6aab3d8Srobert FindSymbol("thread_off_td_tid"), 4, -1, error);
155*f6aab3d8Srobert int32_t offset_td_plist = ReadSignedIntegerFromMemory(
156*f6aab3d8Srobert FindSymbol("thread_off_td_plist"), 4, -1, error);
157*f6aab3d8Srobert int32_t offset_td_pcb = ReadSignedIntegerFromMemory(
158*f6aab3d8Srobert FindSymbol("thread_off_td_pcb"), 4, -1, error);
159*f6aab3d8Srobert int32_t offset_td_oncpu = ReadSignedIntegerFromMemory(
160*f6aab3d8Srobert FindSymbol("thread_off_td_oncpu"), 4, -1, error);
161*f6aab3d8Srobert int32_t offset_td_name = ReadSignedIntegerFromMemory(
162*f6aab3d8Srobert FindSymbol("thread_off_td_name"), 4, -1, error);
163*f6aab3d8Srobert
164*f6aab3d8Srobert // fail if we were not able to read any of the offsets
165*f6aab3d8Srobert if (offset_p_list == -1 || offset_p_pid == -1 || offset_p_threads == -1 ||
166*f6aab3d8Srobert offset_p_comm == -1 || offset_td_tid == -1 || offset_td_plist == -1 ||
167*f6aab3d8Srobert offset_td_pcb == -1 || offset_td_oncpu == -1 || offset_td_name == -1)
168*f6aab3d8Srobert return false;
169*f6aab3d8Srobert
170*f6aab3d8Srobert // dumptid contains the thread-id of the crashing thread
171*f6aab3d8Srobert // dumppcb contains its PCB
172*f6aab3d8Srobert int32_t dumptid =
173*f6aab3d8Srobert ReadSignedIntegerFromMemory(FindSymbol("dumptid"), 4, -1, error);
174*f6aab3d8Srobert lldb::addr_t dumppcb = FindSymbol("dumppcb");
175*f6aab3d8Srobert
176*f6aab3d8Srobert // stoppcbs is an array of PCBs on all CPUs
177*f6aab3d8Srobert // each element is of size pcb_size
178*f6aab3d8Srobert int32_t pcbsize =
179*f6aab3d8Srobert ReadSignedIntegerFromMemory(FindSymbol("pcb_size"), 4, -1, error);
180*f6aab3d8Srobert lldb::addr_t stoppcbs = FindSymbol("stoppcbs");
181*f6aab3d8Srobert
182*f6aab3d8Srobert // from FreeBSD sys/param.h
183*f6aab3d8Srobert constexpr size_t fbsd_maxcomlen = 19;
184*f6aab3d8Srobert
185*f6aab3d8Srobert // iterate through a linked list of all processes
186*f6aab3d8Srobert // allproc is a pointer to the first list element, p_list field
187*f6aab3d8Srobert // (found at offset_p_list) specifies the next element
188*f6aab3d8Srobert for (lldb::addr_t proc =
189*f6aab3d8Srobert ReadPointerFromMemory(FindSymbol("allproc"), error);
190*f6aab3d8Srobert proc != 0 && proc != LLDB_INVALID_ADDRESS;
191*f6aab3d8Srobert proc = ReadPointerFromMemory(proc + offset_p_list, error)) {
192*f6aab3d8Srobert int32_t pid =
193*f6aab3d8Srobert ReadSignedIntegerFromMemory(proc + offset_p_pid, 4, -1, error);
194*f6aab3d8Srobert // process' command-line string
195*f6aab3d8Srobert char comm[fbsd_maxcomlen + 1];
196*f6aab3d8Srobert ReadCStringFromMemory(proc + offset_p_comm, comm, sizeof(comm), error);
197*f6aab3d8Srobert
198*f6aab3d8Srobert // iterate through a linked list of all process' threads
199*f6aab3d8Srobert // the initial thread is found in process' p_threads, subsequent
200*f6aab3d8Srobert // elements are linked via td_plist field
201*f6aab3d8Srobert for (lldb::addr_t td =
202*f6aab3d8Srobert ReadPointerFromMemory(proc + offset_p_threads, error);
203*f6aab3d8Srobert td != 0; td = ReadPointerFromMemory(td + offset_td_plist, error)) {
204*f6aab3d8Srobert int32_t tid =
205*f6aab3d8Srobert ReadSignedIntegerFromMemory(td + offset_td_tid, 4, -1, error);
206*f6aab3d8Srobert lldb::addr_t pcb_addr =
207*f6aab3d8Srobert ReadPointerFromMemory(td + offset_td_pcb, error);
208*f6aab3d8Srobert // whether process was on CPU (-1 if not, otherwise CPU number)
209*f6aab3d8Srobert int32_t oncpu =
210*f6aab3d8Srobert ReadSignedIntegerFromMemory(td + offset_td_oncpu, 4, -2, error);
211*f6aab3d8Srobert // thread name
212*f6aab3d8Srobert char thread_name[fbsd_maxcomlen + 1];
213*f6aab3d8Srobert ReadCStringFromMemory(td + offset_td_name, thread_name,
214*f6aab3d8Srobert sizeof(thread_name), error);
215*f6aab3d8Srobert
216*f6aab3d8Srobert // if we failed to read TID, ignore this thread
217*f6aab3d8Srobert if (tid == -1)
218*f6aab3d8Srobert continue;
219*f6aab3d8Srobert
220*f6aab3d8Srobert std::string thread_desc = llvm::formatv("(pid {0}) {1}", pid, comm);
221*f6aab3d8Srobert if (*thread_name && strcmp(thread_name, comm)) {
222*f6aab3d8Srobert thread_desc += '/';
223*f6aab3d8Srobert thread_desc += thread_name;
224*f6aab3d8Srobert }
225*f6aab3d8Srobert
226*f6aab3d8Srobert // roughly:
227*f6aab3d8Srobert // 1. if the thread crashed, its PCB is going to be at "dumppcb"
228*f6aab3d8Srobert // 2. if the thread was on CPU, its PCB is going to be on the CPU
229*f6aab3d8Srobert // 3. otherwise, its PCB is in the thread struct
230*f6aab3d8Srobert if (tid == dumptid) {
231*f6aab3d8Srobert // NB: dumppcb can be LLDB_INVALID_ADDRESS if reading it failed
232*f6aab3d8Srobert pcb_addr = dumppcb;
233*f6aab3d8Srobert thread_desc += " (crashed)";
234*f6aab3d8Srobert } else if (oncpu != -1) {
235*f6aab3d8Srobert // if we managed to read stoppcbs and pcb_size, use them to find
236*f6aab3d8Srobert // the correct PCB
237*f6aab3d8Srobert if (stoppcbs != LLDB_INVALID_ADDRESS && pcbsize > 0)
238*f6aab3d8Srobert pcb_addr = stoppcbs + oncpu * pcbsize;
239*f6aab3d8Srobert else
240*f6aab3d8Srobert pcb_addr = LLDB_INVALID_ADDRESS;
241*f6aab3d8Srobert thread_desc += llvm::formatv(" (on CPU {0})", oncpu);
242*f6aab3d8Srobert }
243*f6aab3d8Srobert
244*f6aab3d8Srobert ThreadSP thread_sp{
245*f6aab3d8Srobert new ThreadFreeBSDKernel(*this, tid, pcb_addr, thread_desc)};
246*f6aab3d8Srobert new_thread_list.AddThread(thread_sp);
247*f6aab3d8Srobert }
248*f6aab3d8Srobert }
249*f6aab3d8Srobert } else {
250*f6aab3d8Srobert const uint32_t num_threads = old_thread_list.GetSize(false);
251*f6aab3d8Srobert for (uint32_t i = 0; i < num_threads; ++i)
252*f6aab3d8Srobert new_thread_list.AddThread(old_thread_list.GetThreadAtIndex(i, false));
253*f6aab3d8Srobert }
254*f6aab3d8Srobert return new_thread_list.GetSize(false) > 0;
255*f6aab3d8Srobert }
256*f6aab3d8Srobert
DoLoadCore()257*f6aab3d8Srobert Status ProcessFreeBSDKernel::DoLoadCore() {
258*f6aab3d8Srobert // The core is already loaded by CreateInstance().
259*f6aab3d8Srobert return Status();
260*f6aab3d8Srobert }
261*f6aab3d8Srobert
GetDynamicLoader()262*f6aab3d8Srobert DynamicLoader *ProcessFreeBSDKernel::GetDynamicLoader() {
263*f6aab3d8Srobert if (m_dyld_up.get() == nullptr)
264*f6aab3d8Srobert m_dyld_up.reset(DynamicLoader::FindPlugin(
265*f6aab3d8Srobert this, DynamicLoaderStatic::GetPluginNameStatic()));
266*f6aab3d8Srobert return m_dyld_up.get();
267*f6aab3d8Srobert }
268*f6aab3d8Srobert
FindSymbol(const char * name)269*f6aab3d8Srobert lldb::addr_t ProcessFreeBSDKernel::FindSymbol(const char *name) {
270*f6aab3d8Srobert ModuleSP mod_sp = GetTarget().GetExecutableModule();
271*f6aab3d8Srobert const Symbol *sym = mod_sp->FindFirstSymbolWithNameAndType(ConstString(name));
272*f6aab3d8Srobert return sym ? sym->GetLoadAddress(&GetTarget()) : LLDB_INVALID_ADDRESS;
273*f6aab3d8Srobert }
274*f6aab3d8Srobert
275*f6aab3d8Srobert #if LLDB_ENABLE_FBSDVMCORE
276*f6aab3d8Srobert
ProcessFreeBSDKernelFVC(lldb::TargetSP target_sp,ListenerSP listener_sp,fvc_t * fvc)277*f6aab3d8Srobert ProcessFreeBSDKernelFVC::ProcessFreeBSDKernelFVC(lldb::TargetSP target_sp,
278*f6aab3d8Srobert ListenerSP listener_sp,
279*f6aab3d8Srobert fvc_t *fvc)
280*f6aab3d8Srobert : ProcessFreeBSDKernel(target_sp, listener_sp), m_fvc(fvc) {}
281*f6aab3d8Srobert
~ProcessFreeBSDKernelFVC()282*f6aab3d8Srobert ProcessFreeBSDKernelFVC::~ProcessFreeBSDKernelFVC() {
283*f6aab3d8Srobert if (m_fvc)
284*f6aab3d8Srobert fvc_close(m_fvc);
285*f6aab3d8Srobert }
286*f6aab3d8Srobert
DoReadMemory(lldb::addr_t addr,void * buf,size_t size,Status & error)287*f6aab3d8Srobert size_t ProcessFreeBSDKernelFVC::DoReadMemory(lldb::addr_t addr, void *buf,
288*f6aab3d8Srobert size_t size, Status &error) {
289*f6aab3d8Srobert ssize_t rd = 0;
290*f6aab3d8Srobert rd = fvc_read(m_fvc, addr, buf, size);
291*f6aab3d8Srobert if (rd < 0 || static_cast<size_t>(rd) != size) {
292*f6aab3d8Srobert error.SetErrorStringWithFormat("Reading memory failed: %s", GetError());
293*f6aab3d8Srobert return rd > 0 ? rd : 0;
294*f6aab3d8Srobert }
295*f6aab3d8Srobert return rd;
296*f6aab3d8Srobert }
297*f6aab3d8Srobert
GetError()298*f6aab3d8Srobert const char *ProcessFreeBSDKernelFVC::GetError() { return fvc_geterr(m_fvc); }
299*f6aab3d8Srobert
300*f6aab3d8Srobert #endif // LLDB_ENABLE_FBSDVMCORE
301*f6aab3d8Srobert
302*f6aab3d8Srobert #if defined(__FreeBSD__)
303*f6aab3d8Srobert
ProcessFreeBSDKernelKVM(lldb::TargetSP target_sp,ListenerSP listener_sp,kvm_t * fvc)304*f6aab3d8Srobert ProcessFreeBSDKernelKVM::ProcessFreeBSDKernelKVM(lldb::TargetSP target_sp,
305*f6aab3d8Srobert ListenerSP listener_sp,
306*f6aab3d8Srobert kvm_t *fvc)
307*f6aab3d8Srobert : ProcessFreeBSDKernel(target_sp, listener_sp), m_kvm(fvc) {}
308*f6aab3d8Srobert
~ProcessFreeBSDKernelKVM()309*f6aab3d8Srobert ProcessFreeBSDKernelKVM::~ProcessFreeBSDKernelKVM() {
310*f6aab3d8Srobert if (m_kvm)
311*f6aab3d8Srobert kvm_close(m_kvm);
312*f6aab3d8Srobert }
313*f6aab3d8Srobert
DoReadMemory(lldb::addr_t addr,void * buf,size_t size,Status & error)314*f6aab3d8Srobert size_t ProcessFreeBSDKernelKVM::DoReadMemory(lldb::addr_t addr, void *buf,
315*f6aab3d8Srobert size_t size, Status &error) {
316*f6aab3d8Srobert ssize_t rd = 0;
317*f6aab3d8Srobert rd = kvm_read2(m_kvm, addr, buf, size);
318*f6aab3d8Srobert if (rd < 0 || static_cast<size_t>(rd) != size) {
319*f6aab3d8Srobert error.SetErrorStringWithFormat("Reading memory failed: %s", GetError());
320*f6aab3d8Srobert return rd > 0 ? rd : 0;
321*f6aab3d8Srobert }
322*f6aab3d8Srobert return rd;
323*f6aab3d8Srobert }
324*f6aab3d8Srobert
GetError()325*f6aab3d8Srobert const char *ProcessFreeBSDKernelKVM::GetError() { return kvm_geterr(m_kvm); }
326*f6aab3d8Srobert
327*f6aab3d8Srobert #endif // defined(__FreeBSD__)
328