1513cf72fSasou //===-- ProcessOpenBSDKernel.cpp ------------------------------------------===//
2513cf72fSasou //
3513cf72fSasou // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4513cf72fSasou // See https://llvm.org/LICENSE.txt for license information.
5513cf72fSasou // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6513cf72fSasou //
7513cf72fSasou //===----------------------------------------------------------------------===//
8513cf72fSasou 
9513cf72fSasou #include "lldb/Core/Module.h"
10513cf72fSasou #include "lldb/Core/PluginManager.h"
11513cf72fSasou #include "lldb/Target/DynamicLoader.h"
12513cf72fSasou 
13513cf72fSasou #include "Plugins/DynamicLoader/Static/DynamicLoaderStatic.h"
14513cf72fSasou #include "ProcessOpenBSDKernel.h"
15513cf72fSasou #include "ThreadOpenBSDKernel.h"
16513cf72fSasou 
17513cf72fSasou #if defined(__OpenBSD__)
18513cf72fSasou #include <kvm.h>
19513cf72fSasou #define _KERNEL
20513cf72fSasou #include <machine/cpu.h>
21513cf72fSasou #include <sys/proc.h>
22*ed4f4290Sasou #include <sys/exec_elf.h>
23513cf72fSasou #undef _KERNEL
24513cf72fSasou #endif
25513cf72fSasou 
26513cf72fSasou using namespace lldb;
27513cf72fSasou using namespace lldb_private;
28513cf72fSasou 
29513cf72fSasou LLDB_PLUGIN_DEFINE(ProcessOpenBSDKernel)
30513cf72fSasou 
31513cf72fSasou namespace {
32513cf72fSasou 
33513cf72fSasou #if defined(__OpenBSD__)
34513cf72fSasou class ProcessOpenBSDKernelKVM : public ProcessOpenBSDKernel {
35513cf72fSasou public:
36513cf72fSasou   ProcessOpenBSDKernelKVM(lldb::TargetSP target_sp, lldb::ListenerSP listener,
37513cf72fSasou 			  kvm_t *fvc);
38513cf72fSasou 
39513cf72fSasou   ~ProcessOpenBSDKernelKVM();
40513cf72fSasou 
41513cf72fSasou   size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
42513cf72fSasou 		      lldb_private::Status &error) override;
43513cf72fSasou 
44513cf72fSasou private:
45513cf72fSasou   kvm_t *m_kvm;
46513cf72fSasou 
47513cf72fSasou   const char *GetError();
48513cf72fSasou };
49513cf72fSasou #endif // defined(__OpenBSD__)
50513cf72fSasou 
51513cf72fSasou } // namespace
52513cf72fSasou 
ProcessOpenBSDKernel(lldb::TargetSP target_sp,ListenerSP listener_sp)53513cf72fSasou ProcessOpenBSDKernel::ProcessOpenBSDKernel(lldb::TargetSP target_sp,
54513cf72fSasou 					   ListenerSP listener_sp)
55513cf72fSasou     : PostMortemProcess(target_sp, listener_sp) {}
56513cf72fSasou 
CreateInstance(lldb::TargetSP target_sp,ListenerSP listener_sp,const FileSpec * crash_file,bool can_connect)57513cf72fSasou lldb::ProcessSP ProcessOpenBSDKernel::CreateInstance(lldb::TargetSP target_sp,
58513cf72fSasou 						     ListenerSP listener_sp,
59513cf72fSasou 						     const FileSpec *crash_file,
60513cf72fSasou 						     bool can_connect) {
61513cf72fSasou   ModuleSP executable = target_sp->GetExecutableModule();
62513cf72fSasou   if (crash_file && !can_connect && executable) {
63513cf72fSasou #if defined(__OpenBSD__)
64*ed4f4290Sasou     char buf[4];
65*ed4f4290Sasou     FILE *fp = fopen(crash_file->GetPath().c_str(), "r");
66*ed4f4290Sasou     if (fp == NULL)
67*ed4f4290Sasou       return nullptr;
68*ed4f4290Sasou     size_t r = fread(buf, 1, sizeof(buf), fp);
69*ed4f4290Sasou     fclose(fp);
70*ed4f4290Sasou     if (r != sizeof(buf) || memcmp(buf, ELFMAG, sizeof(buf)) == 0)
71*ed4f4290Sasou       return nullptr;
72513cf72fSasou     kvm_t *kvm =
73513cf72fSasou 	kvm_open(executable->GetFileSpec().GetPath().c_str(),
74513cf72fSasou 		 crash_file->GetPath().c_str(), nullptr, O_RDONLY, nullptr);
75513cf72fSasou     if (kvm)
76513cf72fSasou       return std::make_shared<ProcessOpenBSDKernelKVM>(target_sp, listener_sp,
77513cf72fSasou 						       kvm);
78513cf72fSasou #endif
79513cf72fSasou   }
80513cf72fSasou   return nullptr;
81513cf72fSasou }
82513cf72fSasou 
Initialize()83513cf72fSasou void ProcessOpenBSDKernel::Initialize() {
84513cf72fSasou   static llvm::once_flag g_once_flag;
85513cf72fSasou 
86513cf72fSasou   llvm::call_once(g_once_flag, []() {
87513cf72fSasou     PluginManager::RegisterPlugin(GetPluginNameStatic(),
88513cf72fSasou 				  GetPluginDescriptionStatic(), CreateInstance);
89513cf72fSasou   });
90513cf72fSasou }
91513cf72fSasou 
Terminate()92513cf72fSasou void ProcessOpenBSDKernel::Terminate() {
93513cf72fSasou   PluginManager::UnregisterPlugin(ProcessOpenBSDKernel::CreateInstance);
94513cf72fSasou }
95513cf72fSasou 
DoDestroy()96513cf72fSasou Status ProcessOpenBSDKernel::DoDestroy() { return Status(); }
97513cf72fSasou 
CanDebug(lldb::TargetSP target_sp,bool plugin_specified_by_name)98513cf72fSasou bool ProcessOpenBSDKernel::CanDebug(lldb::TargetSP target_sp,
99513cf72fSasou 				    bool plugin_specified_by_name) {
100513cf72fSasou   return true;
101513cf72fSasou }
102513cf72fSasou 
RefreshStateAfterStop()103513cf72fSasou void ProcessOpenBSDKernel::RefreshStateAfterStop() {}
104513cf72fSasou 
DoUpdateThreadList(ThreadList & old_thread_list,ThreadList & new_thread_list)105513cf72fSasou bool ProcessOpenBSDKernel::DoUpdateThreadList(ThreadList &old_thread_list,
106513cf72fSasou 					      ThreadList &new_thread_list) {
107513cf72fSasou   if (old_thread_list.GetSize(false) == 0) {
108513cf72fSasou     // Make up the thread the first time this is called so we can set our one
109513cf72fSasou     // and only core thread state up.
110513cf72fSasou 
111513cf72fSasou     // We cannot construct a thread without a register context as that crashes
112513cf72fSasou     // LLDB but we can construct a process without threads to provide minimal
113513cf72fSasou     // memory reading support.
114513cf72fSasou     switch (GetTarget().GetArchitecture().GetMachine()) {
115513cf72fSasou     case llvm::Triple::aarch64:
116513cf72fSasou     case llvm::Triple::x86:
117513cf72fSasou     case llvm::Triple::x86_64:
118513cf72fSasou       break;
119513cf72fSasou     default:
120513cf72fSasou       return false;
121513cf72fSasou     }
122513cf72fSasou 
123513cf72fSasou     Status error;
124513cf72fSasou     int32_t i;
125513cf72fSasou     lldb::addr_t dumppcb = FindSymbol("dumppcb");
126513cf72fSasou     uint32_t offset_p_list = offsetof(proc, p_list);
127513cf72fSasou     uint32_t offset_p_addr = offsetof(proc, p_addr);
128513cf72fSasou     uint32_t offset_p_tid = offsetof(proc, p_tid);
129513cf72fSasou     uint32_t offset_p_p = offsetof(proc, p_p);
130513cf72fSasou     uint32_t offset_ps_comm = offsetof(process, ps_comm);
131513cf72fSasou     uint32_t offset_ps_pid = offsetof(process, ps_pid);
132513cf72fSasou     uint32_t offset_ci_curproc = offsetof(cpu_info, ci_curproc);
133513cf72fSasou     char    comm[_MAXCOMLEN];
134513cf72fSasou 
135513cf72fSasou     int32_t ncpu = ReadSignedIntegerFromMemory(FindSymbol("ncpus"),
136513cf72fSasou 					       4, -1, error);
137513cf72fSasou     if (ncpu < 0)
138513cf72fSasou       return false;
139513cf72fSasou 
140513cf72fSasou     lldb::addr_t cpu_procs[ncpu];
141513cf72fSasou 
142513cf72fSasou     if (dumppcb != LLDB_INVALID_ADDRESS) {
143513cf72fSasou       std::string thread_desc = llvm::formatv("Crashed Thread");
144513cf72fSasou       ThreadSP thread_sp {
145513cf72fSasou 		new ThreadOpenBSDKernel(*this, 0, dumppcb, thread_desc)};
146513cf72fSasou 	new_thread_list.AddThread(thread_sp);
147513cf72fSasou     }
148513cf72fSasou 
149513cf72fSasou     lldb::addr_t cpu_info = FindSymbol("cpu_info");
150513cf72fSasou     lldb::addr_t cpu_info_array = (cpu_info == LLDB_INVALID_ADDRESS) ?
151513cf72fSasou       ReadPointerFromMemory(FindSymbol("cpu_info_list"), error) : cpu_info;
152513cf72fSasou     for (i = 0; i < ncpu ; i++) {
153513cf72fSasou       lldb::addr_t ci =
154513cf72fSasou 	ReadPointerFromMemory(cpu_info_array + sizeof(void*) * i, error);
155513cf72fSasou       cpu_procs[i] = ReadPointerFromMemory(ci + offset_ci_curproc, error);
156513cf72fSasou     }
157513cf72fSasou 
158513cf72fSasou     for (lldb::addr_t proc = ReadPointerFromMemory(FindSymbol("allproc"), error);
159513cf72fSasou 	 proc != 0 && proc != LLDB_INVALID_ADDRESS;
160513cf72fSasou 	 proc = ReadPointerFromMemory(proc + offset_p_list, error)) {
161513cf72fSasou 
162513cf72fSasou       lldb::tid_t tid = ReadSignedIntegerFromMemory(proc + offset_p_tid, 4, -1,
163513cf72fSasou 						    error);
164513cf72fSasou       lldb::addr_t process = ReadPointerFromMemory(proc + offset_p_p, error);
165513cf72fSasou       ReadMemory(process + offset_ps_comm, &comm, sizeof(comm), error);
166513cf72fSasou       u_int32_t pid = ReadSignedIntegerFromMemory(process + offset_ps_pid, 4,
167513cf72fSasou 						  -1, error);
168513cf72fSasou       lldb::addr_t p_addr = ReadPointerFromMemory(proc + offset_p_addr, error);
169513cf72fSasou       for (i = 0; i < ncpu; i++)
170513cf72fSasou 	if (cpu_procs[i] == proc)
171513cf72fSasou 	  break;
172513cf72fSasou       std::string thread_desc;
173513cf72fSasou       if (i == ncpu)
174513cf72fSasou 	thread_desc = llvm::formatv("(pid:{0}) {1}", pid, comm);
175513cf72fSasou       else
176513cf72fSasou 	thread_desc = llvm::formatv("(pid:{0}) {1} (cpu {2})", pid, comm, i);
177513cf72fSasou       ThreadSP thread_sp {
178513cf72fSasou 		new ThreadOpenBSDKernel(*this, tid, p_addr, thread_desc)};
179513cf72fSasou 	new_thread_list.AddThread(thread_sp);
180513cf72fSasou     }
181513cf72fSasou   } else {
182513cf72fSasou     const uint32_t num_threads = old_thread_list.GetSize(false);
183513cf72fSasou     for (uint32_t i = 0; i < num_threads; ++i)
184513cf72fSasou       new_thread_list.AddThread(old_thread_list.GetThreadAtIndex(i, false));
185513cf72fSasou   }
186513cf72fSasou   return new_thread_list.GetSize(false) > 0;
187513cf72fSasou }
188513cf72fSasou 
DoLoadCore()189513cf72fSasou Status ProcessOpenBSDKernel::DoLoadCore() {
190513cf72fSasou   // The core is already loaded by CreateInstance().
191513cf72fSasou   return Status();
192513cf72fSasou }
193513cf72fSasou 
GetDynamicLoader()194513cf72fSasou DynamicLoader *ProcessOpenBSDKernel::GetDynamicLoader() {
195513cf72fSasou   if (m_dyld_up.get() == nullptr)
196513cf72fSasou     m_dyld_up.reset(DynamicLoader::FindPlugin(
197513cf72fSasou 	this, DynamicLoaderStatic::GetPluginNameStatic()));
198513cf72fSasou   return m_dyld_up.get();
199513cf72fSasou }
200513cf72fSasou 
FindSymbol(const char * name)201513cf72fSasou lldb::addr_t ProcessOpenBSDKernel::FindSymbol(const char *name) {
202513cf72fSasou   ModuleSP mod_sp = GetTarget().GetExecutableModule();
203513cf72fSasou   const Symbol *sym = mod_sp->FindFirstSymbolWithNameAndType(ConstString(name));
204513cf72fSasou   return sym ? sym->GetLoadAddress(&GetTarget()) : LLDB_INVALID_ADDRESS;
205513cf72fSasou }
206513cf72fSasou 
207513cf72fSasou #if defined(__OpenBSD__)
208513cf72fSasou 
ProcessOpenBSDKernelKVM(lldb::TargetSP target_sp,ListenerSP listener_sp,kvm_t * fvc)209513cf72fSasou ProcessOpenBSDKernelKVM::ProcessOpenBSDKernelKVM(lldb::TargetSP target_sp,
210513cf72fSasou 						 ListenerSP listener_sp,
211513cf72fSasou 						 kvm_t *fvc)
212513cf72fSasou     : ProcessOpenBSDKernel(target_sp, listener_sp), m_kvm(fvc) {}
213513cf72fSasou 
~ProcessOpenBSDKernelKVM()214513cf72fSasou ProcessOpenBSDKernelKVM::~ProcessOpenBSDKernelKVM() {
215513cf72fSasou   if (m_kvm)
216513cf72fSasou     kvm_close(m_kvm);
217513cf72fSasou }
218513cf72fSasou 
DoReadMemory(lldb::addr_t addr,void * buf,size_t size,Status & error)219513cf72fSasou size_t ProcessOpenBSDKernelKVM::DoReadMemory(lldb::addr_t addr, void *buf,
220513cf72fSasou 					     size_t size, Status &error) {
221513cf72fSasou   ssize_t rd = 0;
222513cf72fSasou   rd = kvm_read(m_kvm, addr, buf, size);
223513cf72fSasou   if (rd < 0 || static_cast<size_t>(rd) != size) {
224513cf72fSasou     error.SetErrorStringWithFormat("Reading memory failed: %s", GetError());
225513cf72fSasou     return rd > 0 ? rd : 0;
226513cf72fSasou   }
227513cf72fSasou   return rd;
228513cf72fSasou }
229513cf72fSasou 
GetError()230513cf72fSasou const char *ProcessOpenBSDKernelKVM::GetError() { return kvm_geterr(m_kvm); }
231513cf72fSasou 
232513cf72fSasou #endif // defined(__OpenBSD__)
233