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