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