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