1 //===-- ProcessFreeBSDKernel.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 "ProcessFreeBSDKernel.h" 15 #include "ThreadFreeBSDKernel.h" 16 17 #if LLDB_ENABLE_FBSDVMCORE 18 #include <fvc.h> 19 #endif 20 #if defined(__FreeBSD__) 21 #include <kvm.h> 22 #endif 23 24 using namespace lldb; 25 using namespace lldb_private; 26 27 LLDB_PLUGIN_DEFINE(ProcessFreeBSDKernel) 28 29 namespace { 30 31 #if LLDB_ENABLE_FBSDVMCORE 32 class ProcessFreeBSDKernelFVC : public ProcessFreeBSDKernel { 33 public: 34 ProcessFreeBSDKernelFVC(lldb::TargetSP target_sp, lldb::ListenerSP listener, 35 fvc_t *fvc); 36 37 ~ProcessFreeBSDKernelFVC(); 38 39 size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size, 40 lldb_private::Status &error) override; 41 42 private: 43 fvc_t *m_fvc; 44 45 const char *GetError(); 46 }; 47 #endif // LLDB_ENABLE_FBSDVMCORE 48 49 #if defined(__FreeBSD__) 50 class ProcessFreeBSDKernelKVM : public ProcessFreeBSDKernel { 51 public: 52 ProcessFreeBSDKernelKVM(lldb::TargetSP target_sp, lldb::ListenerSP listener, 53 kvm_t *fvc); 54 55 ~ProcessFreeBSDKernelKVM(); 56 57 size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size, 58 lldb_private::Status &error) override; 59 60 private: 61 kvm_t *m_kvm; 62 63 const char *GetError(); 64 }; 65 #endif // defined(__FreeBSD__) 66 67 } // namespace 68 69 ProcessFreeBSDKernel::ProcessFreeBSDKernel(lldb::TargetSP target_sp, 70 ListenerSP listener_sp) 71 : PostMortemProcess(target_sp, listener_sp) {} 72 73 lldb::ProcessSP ProcessFreeBSDKernel::CreateInstance(lldb::TargetSP target_sp, 74 ListenerSP listener_sp, 75 const FileSpec *crash_file, 76 bool can_connect) { 77 ModuleSP executable = target_sp->GetExecutableModule(); 78 if (crash_file && !can_connect && executable) { 79 #if LLDB_ENABLE_FBSDVMCORE 80 fvc_t *fvc = 81 fvc_open(executable->GetFileSpec().GetPath().c_str(), 82 crash_file->GetPath().c_str(), nullptr, nullptr, nullptr); 83 if (fvc) 84 return std::make_shared<ProcessFreeBSDKernelFVC>(target_sp, listener_sp, 85 fvc); 86 #endif 87 88 #if defined(__FreeBSD__) 89 kvm_t *kvm = 90 kvm_open2(executable->GetFileSpec().GetPath().c_str(), 91 crash_file->GetPath().c_str(), O_RDONLY, nullptr, nullptr); 92 if (kvm) 93 return std::make_shared<ProcessFreeBSDKernelKVM>(target_sp, listener_sp, 94 kvm); 95 #endif 96 } 97 return nullptr; 98 } 99 100 void ProcessFreeBSDKernel::Initialize() { 101 static llvm::once_flag g_once_flag; 102 103 llvm::call_once(g_once_flag, []() { 104 PluginManager::RegisterPlugin(GetPluginNameStatic(), 105 GetPluginDescriptionStatic(), CreateInstance); 106 }); 107 } 108 109 void ProcessFreeBSDKernel::Terminate() { 110 PluginManager::UnregisterPlugin(ProcessFreeBSDKernel::CreateInstance); 111 } 112 113 Status ProcessFreeBSDKernel::DoDestroy() { return Status(); } 114 115 bool ProcessFreeBSDKernel::CanDebug(lldb::TargetSP target_sp, 116 bool plugin_specified_by_name) { 117 return true; 118 } 119 120 void ProcessFreeBSDKernel::RefreshStateAfterStop() {} 121 122 bool ProcessFreeBSDKernel::DoUpdateThreadList(ThreadList &old_thread_list, 123 ThreadList &new_thread_list) { 124 if (old_thread_list.GetSize(false) == 0) { 125 // Make up the thread the first time this is called so we can set our one 126 // and only core thread state up. 127 128 // We cannot construct a thread without a register context as that crashes 129 // LLDB but we can construct a process without threads to provide minimal 130 // memory reading support. 131 switch (GetTarget().GetArchitecture().GetMachine()) { 132 case llvm::Triple::aarch64: 133 case llvm::Triple::x86: 134 case llvm::Triple::x86_64: 135 break; 136 default: 137 return false; 138 } 139 140 Status error; 141 142 // struct field offsets are written as symbols so that we don't have 143 // to figure them out ourselves 144 int32_t offset_p_list = ReadSignedIntegerFromMemory( 145 FindSymbol("proc_off_p_list"), 4, -1, error); 146 int32_t offset_p_pid = 147 ReadSignedIntegerFromMemory(FindSymbol("proc_off_p_pid"), 4, -1, error); 148 int32_t offset_p_threads = ReadSignedIntegerFromMemory( 149 FindSymbol("proc_off_p_threads"), 4, -1, error); 150 int32_t offset_p_comm = ReadSignedIntegerFromMemory( 151 FindSymbol("proc_off_p_comm"), 4, -1, error); 152 153 int32_t offset_td_tid = ReadSignedIntegerFromMemory( 154 FindSymbol("thread_off_td_tid"), 4, -1, error); 155 int32_t offset_td_plist = ReadSignedIntegerFromMemory( 156 FindSymbol("thread_off_td_plist"), 4, -1, error); 157 int32_t offset_td_pcb = ReadSignedIntegerFromMemory( 158 FindSymbol("thread_off_td_pcb"), 4, -1, error); 159 int32_t offset_td_oncpu = ReadSignedIntegerFromMemory( 160 FindSymbol("thread_off_td_oncpu"), 4, -1, error); 161 int32_t offset_td_name = ReadSignedIntegerFromMemory( 162 FindSymbol("thread_off_td_name"), 4, -1, error); 163 164 // fail if we were not able to read any of the offsets 165 if (offset_p_list == -1 || offset_p_pid == -1 || offset_p_threads == -1 || 166 offset_p_comm == -1 || offset_td_tid == -1 || offset_td_plist == -1 || 167 offset_td_pcb == -1 || offset_td_oncpu == -1 || offset_td_name == -1) 168 return false; 169 170 // dumptid contains the thread-id of the crashing thread 171 // dumppcb contains its PCB 172 int32_t dumptid = 173 ReadSignedIntegerFromMemory(FindSymbol("dumptid"), 4, -1, error); 174 lldb::addr_t dumppcb = FindSymbol("dumppcb"); 175 176 // stoppcbs is an array of PCBs on all CPUs 177 // each element is of size pcb_size 178 int32_t pcbsize = 179 ReadSignedIntegerFromMemory(FindSymbol("pcb_size"), 4, -1, error); 180 lldb::addr_t stoppcbs = FindSymbol("stoppcbs"); 181 182 // from FreeBSD sys/param.h 183 constexpr size_t fbsd_maxcomlen = 19; 184 185 // iterate through a linked list of all processes 186 // allproc is a pointer to the first list element, p_list field 187 // (found at offset_p_list) specifies the next element 188 for (lldb::addr_t proc = 189 ReadPointerFromMemory(FindSymbol("allproc"), error); 190 proc != 0 && proc != LLDB_INVALID_ADDRESS; 191 proc = ReadPointerFromMemory(proc + offset_p_list, error)) { 192 int32_t pid = 193 ReadSignedIntegerFromMemory(proc + offset_p_pid, 4, -1, error); 194 // process' command-line string 195 char comm[fbsd_maxcomlen + 1]; 196 ReadCStringFromMemory(proc + offset_p_comm, comm, sizeof(comm), error); 197 198 // iterate through a linked list of all process' threads 199 // the initial thread is found in process' p_threads, subsequent 200 // elements are linked via td_plist field 201 for (lldb::addr_t td = 202 ReadPointerFromMemory(proc + offset_p_threads, error); 203 td != 0; td = ReadPointerFromMemory(td + offset_td_plist, error)) { 204 int32_t tid = 205 ReadSignedIntegerFromMemory(td + offset_td_tid, 4, -1, error); 206 lldb::addr_t pcb_addr = 207 ReadPointerFromMemory(td + offset_td_pcb, error); 208 // whether process was on CPU (-1 if not, otherwise CPU number) 209 int32_t oncpu = 210 ReadSignedIntegerFromMemory(td + offset_td_oncpu, 4, -2, error); 211 // thread name 212 char thread_name[fbsd_maxcomlen + 1]; 213 ReadCStringFromMemory(td + offset_td_name, thread_name, 214 sizeof(thread_name), error); 215 216 // if we failed to read TID, ignore this thread 217 if (tid == -1) 218 continue; 219 220 std::string thread_desc = llvm::formatv("(pid {0}) {1}", pid, comm); 221 if (*thread_name && strcmp(thread_name, comm)) { 222 thread_desc += '/'; 223 thread_desc += thread_name; 224 } 225 226 // roughly: 227 // 1. if the thread crashed, its PCB is going to be at "dumppcb" 228 // 2. if the thread was on CPU, its PCB is going to be on the CPU 229 // 3. otherwise, its PCB is in the thread struct 230 if (tid == dumptid) { 231 // NB: dumppcb can be LLDB_INVALID_ADDRESS if reading it failed 232 pcb_addr = dumppcb; 233 thread_desc += " (crashed)"; 234 } else if (oncpu != -1) { 235 // if we managed to read stoppcbs and pcb_size, use them to find 236 // the correct PCB 237 if (stoppcbs != LLDB_INVALID_ADDRESS && pcbsize > 0) 238 pcb_addr = stoppcbs + oncpu * pcbsize; 239 else 240 pcb_addr = LLDB_INVALID_ADDRESS; 241 thread_desc += llvm::formatv(" (on CPU {0})", oncpu); 242 } 243 244 ThreadSP thread_sp{ 245 new ThreadFreeBSDKernel(*this, tid, pcb_addr, thread_desc)}; 246 new_thread_list.AddThread(thread_sp); 247 } 248 } 249 } else { 250 const uint32_t num_threads = old_thread_list.GetSize(false); 251 for (uint32_t i = 0; i < num_threads; ++i) 252 new_thread_list.AddThread(old_thread_list.GetThreadAtIndex(i, false)); 253 } 254 return new_thread_list.GetSize(false) > 0; 255 } 256 257 Status ProcessFreeBSDKernel::DoLoadCore() { 258 // The core is already loaded by CreateInstance(). 259 return Status(); 260 } 261 262 DynamicLoader *ProcessFreeBSDKernel::GetDynamicLoader() { 263 if (m_dyld_up.get() == nullptr) 264 m_dyld_up.reset(DynamicLoader::FindPlugin( 265 this, DynamicLoaderStatic::GetPluginNameStatic())); 266 return m_dyld_up.get(); 267 } 268 269 lldb::addr_t ProcessFreeBSDKernel::FindSymbol(const char *name) { 270 ModuleSP mod_sp = GetTarget().GetExecutableModule(); 271 const Symbol *sym = mod_sp->FindFirstSymbolWithNameAndType(ConstString(name)); 272 return sym ? sym->GetLoadAddress(&GetTarget()) : LLDB_INVALID_ADDRESS; 273 } 274 275 #if LLDB_ENABLE_FBSDVMCORE 276 277 ProcessFreeBSDKernelFVC::ProcessFreeBSDKernelFVC(lldb::TargetSP target_sp, 278 ListenerSP listener_sp, 279 fvc_t *fvc) 280 : ProcessFreeBSDKernel(target_sp, listener_sp), m_fvc(fvc) {} 281 282 ProcessFreeBSDKernelFVC::~ProcessFreeBSDKernelFVC() { 283 if (m_fvc) 284 fvc_close(m_fvc); 285 } 286 287 size_t ProcessFreeBSDKernelFVC::DoReadMemory(lldb::addr_t addr, void *buf, 288 size_t size, Status &error) { 289 ssize_t rd = 0; 290 rd = fvc_read(m_fvc, addr, buf, size); 291 if (rd < 0 || static_cast<size_t>(rd) != size) { 292 error.SetErrorStringWithFormat("Reading memory failed: %s", GetError()); 293 return rd > 0 ? rd : 0; 294 } 295 return rd; 296 } 297 298 const char *ProcessFreeBSDKernelFVC::GetError() { return fvc_geterr(m_fvc); } 299 300 #endif // LLDB_ENABLE_FBSDVMCORE 301 302 #if defined(__FreeBSD__) 303 304 ProcessFreeBSDKernelKVM::ProcessFreeBSDKernelKVM(lldb::TargetSP target_sp, 305 ListenerSP listener_sp, 306 kvm_t *fvc) 307 : ProcessFreeBSDKernel(target_sp, listener_sp), m_kvm(fvc) {} 308 309 ProcessFreeBSDKernelKVM::~ProcessFreeBSDKernelKVM() { 310 if (m_kvm) 311 kvm_close(m_kvm); 312 } 313 314 size_t ProcessFreeBSDKernelKVM::DoReadMemory(lldb::addr_t addr, void *buf, 315 size_t size, Status &error) { 316 ssize_t rd = 0; 317 rd = kvm_read2(m_kvm, addr, buf, size); 318 if (rd < 0 || static_cast<size_t>(rd) != size) { 319 error.SetErrorStringWithFormat("Reading memory failed: %s", GetError()); 320 return rd > 0 ? rd : 0; 321 } 322 return rd; 323 } 324 325 const char *ProcessFreeBSDKernelKVM::GetError() { return kvm_geterr(m_kvm); } 326 327 #endif // defined(__FreeBSD__) 328