1 //===-- ProcessOpenBSDKernel.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/Static/DynamicLoaderStatic.h"
14 #include "ProcessOpenBSDKernel.h"
15 #include "ThreadOpenBSDKernel.h"
16 
17 #if defined(__OpenBSD__)
18 #include <kvm.h>
19 #define _KERNEL
20 #include <machine/cpu.h>
21 #include <sys/proc.h>
22 #include <sys/exec_elf.h>
23 #undef _KERNEL
24 #endif
25 
26 using namespace lldb;
27 using namespace lldb_private;
28 
29 LLDB_PLUGIN_DEFINE(ProcessOpenBSDKernel)
30 
31 namespace {
32 
33 #if defined(__OpenBSD__)
34 class ProcessOpenBSDKernelKVM : public ProcessOpenBSDKernel {
35 public:
36   ProcessOpenBSDKernelKVM(lldb::TargetSP target_sp, lldb::ListenerSP listener,
37 			  kvm_t *fvc);
38 
39   ~ProcessOpenBSDKernelKVM();
40 
41   size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
42 		      lldb_private::Status &error) override;
43 
44 private:
45   kvm_t *m_kvm;
46 
47   const char *GetError();
48 };
49 #endif // defined(__OpenBSD__)
50 
51 } // namespace
52 
ProcessOpenBSDKernel(lldb::TargetSP target_sp,ListenerSP listener_sp)53 ProcessOpenBSDKernel::ProcessOpenBSDKernel(lldb::TargetSP target_sp,
54 					   ListenerSP listener_sp)
55     : PostMortemProcess(target_sp, listener_sp) {}
56 
CreateInstance(lldb::TargetSP target_sp,ListenerSP listener_sp,const FileSpec * crash_file,bool can_connect)57 lldb::ProcessSP ProcessOpenBSDKernel::CreateInstance(lldb::TargetSP target_sp,
58 						     ListenerSP listener_sp,
59 						     const FileSpec *crash_file,
60 						     bool can_connect) {
61   ModuleSP executable = target_sp->GetExecutableModule();
62   if (crash_file && !can_connect && executable) {
63 #if defined(__OpenBSD__)
64     char buf[4];
65     FILE *fp = fopen(crash_file->GetPath().c_str(), "r");
66     if (fp == NULL)
67       return nullptr;
68     size_t r = fread(buf, 1, sizeof(buf), fp);
69     fclose(fp);
70     if (r != sizeof(buf) || memcmp(buf, ELFMAG, sizeof(buf)) == 0)
71       return nullptr;
72     kvm_t *kvm =
73 	kvm_open(executable->GetFileSpec().GetPath().c_str(),
74 		 crash_file->GetPath().c_str(), nullptr, O_RDONLY, nullptr);
75     if (kvm)
76       return std::make_shared<ProcessOpenBSDKernelKVM>(target_sp, listener_sp,
77 						       kvm);
78 #endif
79   }
80   return nullptr;
81 }
82 
Initialize()83 void ProcessOpenBSDKernel::Initialize() {
84   static llvm::once_flag g_once_flag;
85 
86   llvm::call_once(g_once_flag, []() {
87     PluginManager::RegisterPlugin(GetPluginNameStatic(),
88 				  GetPluginDescriptionStatic(), CreateInstance);
89   });
90 }
91 
Terminate()92 void ProcessOpenBSDKernel::Terminate() {
93   PluginManager::UnregisterPlugin(ProcessOpenBSDKernel::CreateInstance);
94 }
95 
DoDestroy()96 Status ProcessOpenBSDKernel::DoDestroy() { return Status(); }
97 
CanDebug(lldb::TargetSP target_sp,bool plugin_specified_by_name)98 bool ProcessOpenBSDKernel::CanDebug(lldb::TargetSP target_sp,
99 				    bool plugin_specified_by_name) {
100   return true;
101 }
102 
RefreshStateAfterStop()103 void ProcessOpenBSDKernel::RefreshStateAfterStop() {}
104 
DoUpdateThreadList(ThreadList & old_thread_list,ThreadList & new_thread_list)105 bool ProcessOpenBSDKernel::DoUpdateThreadList(ThreadList &old_thread_list,
106 					      ThreadList &new_thread_list) {
107   if (old_thread_list.GetSize(false) == 0) {
108     // Make up the thread the first time this is called so we can set our one
109     // and only core thread state up.
110 
111     // We cannot construct a thread without a register context as that crashes
112     // LLDB but we can construct a process without threads to provide minimal
113     // memory reading support.
114     switch (GetTarget().GetArchitecture().GetMachine()) {
115     case llvm::Triple::aarch64:
116     case llvm::Triple::x86:
117     case llvm::Triple::x86_64:
118       break;
119     default:
120       return false;
121     }
122 
123     Status error;
124     int32_t i;
125     lldb::addr_t dumppcb = FindSymbol("dumppcb");
126     uint32_t offset_p_list = offsetof(proc, p_list);
127     uint32_t offset_p_addr = offsetof(proc, p_addr);
128     uint32_t offset_p_tid = offsetof(proc, p_tid);
129     uint32_t offset_p_p = offsetof(proc, p_p);
130     uint32_t offset_ps_comm = offsetof(process, ps_comm);
131     uint32_t offset_ps_pid = offsetof(process, ps_pid);
132     uint32_t offset_ci_curproc = offsetof(cpu_info, ci_curproc);
133     char    comm[_MAXCOMLEN];
134 
135     int32_t ncpu = ReadSignedIntegerFromMemory(FindSymbol("ncpus"),
136 					       4, -1, error);
137     if (ncpu < 0)
138       return false;
139 
140     lldb::addr_t cpu_procs[ncpu];
141 
142     if (dumppcb != LLDB_INVALID_ADDRESS) {
143       std::string thread_desc = llvm::formatv("Crashed Thread");
144       ThreadSP thread_sp {
145 		new ThreadOpenBSDKernel(*this, 0, dumppcb, thread_desc)};
146 	new_thread_list.AddThread(thread_sp);
147     }
148 
149     lldb::addr_t cpu_info = FindSymbol("cpu_info");
150     lldb::addr_t cpu_info_array = (cpu_info == LLDB_INVALID_ADDRESS) ?
151       ReadPointerFromMemory(FindSymbol("cpu_info_list"), error) : cpu_info;
152     for (i = 0; i < ncpu ; i++) {
153       lldb::addr_t ci =
154 	ReadPointerFromMemory(cpu_info_array + sizeof(void*) * i, error);
155       cpu_procs[i] = ReadPointerFromMemory(ci + offset_ci_curproc, error);
156     }
157 
158     for (lldb::addr_t proc = ReadPointerFromMemory(FindSymbol("allproc"), error);
159 	 proc != 0 && proc != LLDB_INVALID_ADDRESS;
160 	 proc = ReadPointerFromMemory(proc + offset_p_list, error)) {
161 
162       lldb::tid_t tid = ReadSignedIntegerFromMemory(proc + offset_p_tid, 4, -1,
163 						    error);
164       lldb::addr_t process = ReadPointerFromMemory(proc + offset_p_p, error);
165       ReadMemory(process + offset_ps_comm, &comm, sizeof(comm), error);
166       u_int32_t pid = ReadSignedIntegerFromMemory(process + offset_ps_pid, 4,
167 						  -1, error);
168       lldb::addr_t p_addr = ReadPointerFromMemory(proc + offset_p_addr, error);
169       for (i = 0; i < ncpu; i++)
170 	if (cpu_procs[i] == proc)
171 	  break;
172       std::string thread_desc;
173       if (i == ncpu)
174 	thread_desc = llvm::formatv("(pid:{0}) {1}", pid, comm);
175       else
176 	thread_desc = llvm::formatv("(pid:{0}) {1} (cpu {2})", pid, comm, i);
177       ThreadSP thread_sp {
178 		new ThreadOpenBSDKernel(*this, tid, p_addr, thread_desc)};
179 	new_thread_list.AddThread(thread_sp);
180     }
181   } else {
182     const uint32_t num_threads = old_thread_list.GetSize(false);
183     for (uint32_t i = 0; i < num_threads; ++i)
184       new_thread_list.AddThread(old_thread_list.GetThreadAtIndex(i, false));
185   }
186   return new_thread_list.GetSize(false) > 0;
187 }
188 
DoLoadCore()189 Status ProcessOpenBSDKernel::DoLoadCore() {
190   // The core is already loaded by CreateInstance().
191   return Status();
192 }
193 
GetDynamicLoader()194 DynamicLoader *ProcessOpenBSDKernel::GetDynamicLoader() {
195   if (m_dyld_up.get() == nullptr)
196     m_dyld_up.reset(DynamicLoader::FindPlugin(
197 	this, DynamicLoaderStatic::GetPluginNameStatic()));
198   return m_dyld_up.get();
199 }
200 
FindSymbol(const char * name)201 lldb::addr_t ProcessOpenBSDKernel::FindSymbol(const char *name) {
202   ModuleSP mod_sp = GetTarget().GetExecutableModule();
203   const Symbol *sym = mod_sp->FindFirstSymbolWithNameAndType(ConstString(name));
204   return sym ? sym->GetLoadAddress(&GetTarget()) : LLDB_INVALID_ADDRESS;
205 }
206 
207 #if defined(__OpenBSD__)
208 
ProcessOpenBSDKernelKVM(lldb::TargetSP target_sp,ListenerSP listener_sp,kvm_t * fvc)209 ProcessOpenBSDKernelKVM::ProcessOpenBSDKernelKVM(lldb::TargetSP target_sp,
210 						 ListenerSP listener_sp,
211 						 kvm_t *fvc)
212     : ProcessOpenBSDKernel(target_sp, listener_sp), m_kvm(fvc) {}
213 
~ProcessOpenBSDKernelKVM()214 ProcessOpenBSDKernelKVM::~ProcessOpenBSDKernelKVM() {
215   if (m_kvm)
216     kvm_close(m_kvm);
217 }
218 
DoReadMemory(lldb::addr_t addr,void * buf,size_t size,Status & error)219 size_t ProcessOpenBSDKernelKVM::DoReadMemory(lldb::addr_t addr, void *buf,
220 					     size_t size, Status &error) {
221   ssize_t rd = 0;
222   rd = kvm_read(m_kvm, addr, buf, size);
223   if (rd < 0 || static_cast<size_t>(rd) != size) {
224     error.SetErrorStringWithFormat("Reading memory failed: %s", GetError());
225     return rd > 0 ? rd : 0;
226   }
227   return rd;
228 }
229 
GetError()230 const char *ProcessOpenBSDKernelKVM::GetError() { return kvm_geterr(m_kvm); }
231 
232 #endif // defined(__OpenBSD__)
233