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