| 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(flag&: g_once_flag, F: []() { |
| 105 | PluginManager::RegisterPlugin(name: GetPluginNameStatic(), |
| 106 | description: GetPluginDescriptionStatic(), create_callback: CreateInstance); |
| 107 | }); |
| 108 | } |
| 109 | |
| 110 | void ProcessFreeBSDKernel::Terminate() { |
| 111 | PluginManager::UnregisterPlugin(create_callback: 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(can_update: 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 | load_addr: FindSymbol(name: "proc_off_p_list" ), byte_size: 4, fail_value: -1, error); |
| 147 | int32_t offset_p_pid = |
| 148 | ReadSignedIntegerFromMemory(load_addr: FindSymbol(name: "proc_off_p_pid" ), byte_size: 4, fail_value: -1, error); |
| 149 | int32_t offset_p_threads = ReadSignedIntegerFromMemory( |
| 150 | load_addr: FindSymbol(name: "proc_off_p_threads" ), byte_size: 4, fail_value: -1, error); |
| 151 | int32_t offset_p_comm = ReadSignedIntegerFromMemory( |
| 152 | load_addr: FindSymbol(name: "proc_off_p_comm" ), byte_size: 4, fail_value: -1, error); |
| 153 | |
| 154 | int32_t offset_td_tid = ReadSignedIntegerFromMemory( |
| 155 | load_addr: FindSymbol(name: "thread_off_td_tid" ), byte_size: 4, fail_value: -1, error); |
| 156 | int32_t offset_td_plist = ReadSignedIntegerFromMemory( |
| 157 | load_addr: FindSymbol(name: "thread_off_td_plist" ), byte_size: 4, fail_value: -1, error); |
| 158 | int32_t offset_td_pcb = ReadSignedIntegerFromMemory( |
| 159 | load_addr: FindSymbol(name: "thread_off_td_pcb" ), byte_size: 4, fail_value: -1, error); |
| 160 | int32_t offset_td_oncpu = ReadSignedIntegerFromMemory( |
| 161 | load_addr: FindSymbol(name: "thread_off_td_oncpu" ), byte_size: 4, fail_value: -1, error); |
| 162 | int32_t offset_td_name = ReadSignedIntegerFromMemory( |
| 163 | load_addr: FindSymbol(name: "thread_off_td_name" ), byte_size: 4, fail_value: -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(load_addr: FindSymbol(name: "dumptid" ), byte_size: 4, fail_value: -1, error); |
| 175 | lldb::addr_t dumppcb = FindSymbol(name: "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(load_addr: FindSymbol(name: "pcb_size" ), byte_size: 4, fail_value: -1, error); |
| 181 | lldb::addr_t stoppcbs = FindSymbol(name: "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(vm_addr: FindSymbol(name: "allproc" ), error); |
| 191 | proc != 0 && proc != LLDB_INVALID_ADDRESS; |
| 192 | proc = ReadPointerFromMemory(vm_addr: proc + offset_p_list, error)) { |
| 193 | int32_t pid = |
| 194 | ReadSignedIntegerFromMemory(load_addr: proc + offset_p_pid, byte_size: 4, fail_value: -1, error); |
| 195 | // process' command-line string |
| 196 | char comm[fbsd_maxcomlen + 1]; |
| 197 | ReadCStringFromMemory(vm_addr: proc + offset_p_comm, cstr: comm, cstr_max_len: 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(vm_addr: proc + offset_p_threads, error); |
| 204 | td != 0; td = ReadPointerFromMemory(vm_addr: td + offset_td_plist, error)) { |
| 205 | int32_t tid = |
| 206 | ReadSignedIntegerFromMemory(load_addr: td + offset_td_tid, byte_size: 4, fail_value: -1, error); |
| 207 | lldb::addr_t pcb_addr = |
| 208 | ReadPointerFromMemory(vm_addr: td + offset_td_pcb, error); |
| 209 | // whether process was on CPU (-1 if not, otherwise CPU number) |
| 210 | int32_t oncpu = |
| 211 | ReadSignedIntegerFromMemory(load_addr: td + offset_td_oncpu, byte_size: 4, fail_value: -2, error); |
| 212 | // thread name |
| 213 | char thread_name[fbsd_maxcomlen + 1]; |
| 214 | ReadCStringFromMemory(vm_addr: td + offset_td_name, cstr: thread_name, |
| 215 | cstr_max_len: 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(Fmt: "(pid {0}) {1}" , Vals&: pid, Vals&: comm); |
| 222 | if (*thread_name && strcmp(s1: thread_name, s2: 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(Fmt: " (on CPU {0})" , Vals&: 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(can_update: false); |
| 252 | for (uint32_t i = 0; i < num_threads; ++i) |
| 253 | new_thread_list.AddThread(thread_sp: old_thread_list.GetThreadAtIndex(idx: i, can_update: false)); |
| 254 | } |
| 255 | return new_thread_list.GetSize(can_update: 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(p: DynamicLoader::FindPlugin( |
| 266 | process: this, plugin_name: 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(name: ConstString(name)); |
| 273 | return sym ? sym->GetLoadAddress(target: &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 | |