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