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 53 ProcessOpenBSDKernel::ProcessOpenBSDKernel(lldb::TargetSP target_sp, 54 ListenerSP listener_sp) 55 : PostMortemProcess(target_sp, listener_sp) {} 56 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 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 92 void ProcessOpenBSDKernel::Terminate() { 93 PluginManager::UnregisterPlugin(ProcessOpenBSDKernel::CreateInstance); 94 } 95 96 Status ProcessOpenBSDKernel::DoDestroy() { return Status(); } 97 98 bool ProcessOpenBSDKernel::CanDebug(lldb::TargetSP target_sp, 99 bool plugin_specified_by_name) { 100 return true; 101 } 102 103 void ProcessOpenBSDKernel::RefreshStateAfterStop() {} 104 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 189 Status ProcessOpenBSDKernel::DoLoadCore() { 190 // The core is already loaded by CreateInstance(). 191 return Status(); 192 } 193 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 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 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 214 ProcessOpenBSDKernelKVM::~ProcessOpenBSDKernelKVM() { 215 if (m_kvm) 216 kvm_close(m_kvm); 217 } 218 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 230 const char *ProcessOpenBSDKernelKVM::GetError() { return kvm_geterr(m_kvm); } 231 232 #endif // defined(__OpenBSD__) 233