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/FreeBSD-Kernel/DynamicLoaderFreeBSDKernel.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 // In later FreeBSD versions stoppcbs is a pointer to the array. 182 int32_t osreldate = 183 ReadSignedIntegerFromMemory(FindSymbol("osreldate"), 4, -1, error); 184 if (stoppcbs != LLDB_INVALID_ADDRESS && osreldate >= 1400089) 185 stoppcbs = ReadPointerFromMemory(stoppcbs, error); 186 187 // from FreeBSD sys/param.h 188 constexpr size_t fbsd_maxcomlen = 19; 189 190 // iterate through a linked list of all processes 191 // allproc is a pointer to the first list element, p_list field 192 // (found at offset_p_list) specifies the next element 193 for (lldb::addr_t proc = 194 ReadPointerFromMemory(FindSymbol("allproc"), error); 195 proc != 0 && proc != LLDB_INVALID_ADDRESS; 196 proc = ReadPointerFromMemory(proc + offset_p_list, error)) { 197 int32_t pid = 198 ReadSignedIntegerFromMemory(proc + offset_p_pid, 4, -1, error); 199 // process' command-line string 200 char comm[fbsd_maxcomlen + 1]; 201 ReadCStringFromMemory(proc + offset_p_comm, comm, sizeof(comm), error); 202 203 // iterate through a linked list of all process' threads 204 // the initial thread is found in process' p_threads, subsequent 205 // elements are linked via td_plist field 206 for (lldb::addr_t td = 207 ReadPointerFromMemory(proc + offset_p_threads, error); 208 td != 0; td = ReadPointerFromMemory(td + offset_td_plist, error)) { 209 int32_t tid = 210 ReadSignedIntegerFromMemory(td + offset_td_tid, 4, -1, error); 211 lldb::addr_t pcb_addr = 212 ReadPointerFromMemory(td + offset_td_pcb, error); 213 // whether process was on CPU (-1 if not, otherwise CPU number) 214 int32_t oncpu = 215 ReadSignedIntegerFromMemory(td + offset_td_oncpu, 4, -2, error); 216 // thread name 217 char thread_name[fbsd_maxcomlen + 1]; 218 ReadCStringFromMemory(td + offset_td_name, thread_name, 219 sizeof(thread_name), error); 220 221 // if we failed to read TID, ignore this thread 222 if (tid == -1) 223 continue; 224 225 std::string thread_desc = llvm::formatv("(pid {0}) {1}", pid, comm); 226 if (*thread_name && strcmp(thread_name, comm)) { 227 thread_desc += '/'; 228 thread_desc += thread_name; 229 } 230 231 // roughly: 232 // 1. if the thread crashed, its PCB is going to be at "dumppcb" 233 // 2. if the thread was on CPU, its PCB is going to be on the CPU 234 // 3. otherwise, its PCB is in the thread struct 235 if (tid == dumptid) { 236 // NB: dumppcb can be LLDB_INVALID_ADDRESS if reading it failed 237 pcb_addr = dumppcb; 238 thread_desc += " (crashed)"; 239 } else if (oncpu != -1) { 240 // if we managed to read stoppcbs and pcb_size, use them to find 241 // the correct PCB 242 if (stoppcbs != LLDB_INVALID_ADDRESS && pcbsize > 0) 243 pcb_addr = stoppcbs + oncpu * pcbsize; 244 else 245 pcb_addr = LLDB_INVALID_ADDRESS; 246 thread_desc += llvm::formatv(" (on CPU {0})", oncpu); 247 } 248 249 ThreadSP thread_sp{ 250 new ThreadFreeBSDKernel(*this, tid, pcb_addr, thread_desc)}; 251 new_thread_list.AddThread(thread_sp); 252 } 253 } 254 } else { 255 const uint32_t num_threads = old_thread_list.GetSize(false); 256 for (uint32_t i = 0; i < num_threads; ++i) 257 new_thread_list.AddThread(old_thread_list.GetThreadAtIndex(i, false)); 258 } 259 return new_thread_list.GetSize(false) > 0; 260 } 261 262 Status ProcessFreeBSDKernel::DoLoadCore() { 263 // The core is already loaded by CreateInstance(). 264 return Status(); 265 } 266 267 DynamicLoader *ProcessFreeBSDKernel::GetDynamicLoader() { 268 if (m_dyld_up.get() == nullptr) 269 m_dyld_up.reset(DynamicLoader::FindPlugin( 270 this, DynamicLoaderFreeBSDKernel::GetPluginNameStatic())); 271 return m_dyld_up.get(); 272 } 273 274 lldb::addr_t ProcessFreeBSDKernel::FindSymbol(const char *name) { 275 ModuleSP mod_sp = GetTarget().GetExecutableModule(); 276 const Symbol *sym = mod_sp->FindFirstSymbolWithNameAndType(ConstString(name)); 277 return sym ? sym->GetLoadAddress(&GetTarget()) : LLDB_INVALID_ADDRESS; 278 } 279 280 #if LLDB_ENABLE_FBSDVMCORE 281 282 ProcessFreeBSDKernelFVC::ProcessFreeBSDKernelFVC(lldb::TargetSP target_sp, 283 ListenerSP listener_sp, 284 fvc_t *fvc) 285 : ProcessFreeBSDKernel(target_sp, listener_sp), m_fvc(fvc) {} 286 287 ProcessFreeBSDKernelFVC::~ProcessFreeBSDKernelFVC() { 288 if (m_fvc) 289 fvc_close(m_fvc); 290 } 291 292 size_t ProcessFreeBSDKernelFVC::DoReadMemory(lldb::addr_t addr, void *buf, 293 size_t size, Status &error) { 294 ssize_t rd = 0; 295 rd = fvc_read(m_fvc, addr, buf, size); 296 if (rd < 0 || static_cast<size_t>(rd) != size) { 297 error.SetErrorStringWithFormat("Reading memory failed: %s", GetError()); 298 return rd > 0 ? rd : 0; 299 } 300 return rd; 301 } 302 303 const char *ProcessFreeBSDKernelFVC::GetError() { return fvc_geterr(m_fvc); } 304 305 #endif // LLDB_ENABLE_FBSDVMCORE 306 307 #if defined(__FreeBSD__) 308 309 ProcessFreeBSDKernelKVM::ProcessFreeBSDKernelKVM(lldb::TargetSP target_sp, 310 ListenerSP listener_sp, 311 kvm_t *fvc) 312 : ProcessFreeBSDKernel(target_sp, listener_sp), m_kvm(fvc) {} 313 314 ProcessFreeBSDKernelKVM::~ProcessFreeBSDKernelKVM() { 315 if (m_kvm) 316 kvm_close(m_kvm); 317 } 318 319 size_t ProcessFreeBSDKernelKVM::DoReadMemory(lldb::addr_t addr, void *buf, 320 size_t size, Status &error) { 321 ssize_t rd = 0; 322 rd = kvm_read2(m_kvm, addr, buf, size); 323 if (rd < 0 || static_cast<size_t>(rd) != size) { 324 error.SetErrorStringWithFormat("Reading memory failed: %s", GetError()); 325 return rd > 0 ? rd : 0; 326 } 327 return rd; 328 } 329 330 const char *ProcessFreeBSDKernelKVM::GetError() { return kvm_geterr(m_kvm); } 331 332 #endif // defined(__FreeBSD__) 333