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