brintos

brintos / llvm-project-archived public Read only

0
0
Text · 12.1 KiB · d209e5b Raw
333 lines · cpp
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-exception6//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_FBSDVMCORE18#include <fvc.h>19#endif20#if defined(__FreeBSD__)21#include <kvm.h>22#endif23 24using namespace lldb;25using namespace lldb_private;26 27LLDB_PLUGIN_DEFINE(ProcessFreeBSDKernel)28 29namespace {30 31#if LLDB_ENABLE_FBSDVMCORE32class ProcessFreeBSDKernelFVC : public ProcessFreeBSDKernel {33public: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 42private:43  fvc_t *m_fvc;44 45  const char *GetError();46};47#endif // LLDB_ENABLE_FBSDVMCORE48 49#if defined(__FreeBSD__)50class ProcessFreeBSDKernelKVM : public ProcessFreeBSDKernel {51public: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 60private:61  kvm_t *m_kvm;62 63  const char *GetError();64};65#endif // defined(__FreeBSD__)66 67} // namespace68 69ProcessFreeBSDKernel::ProcessFreeBSDKernel(lldb::TargetSP target_sp,70                                           ListenerSP listener_sp,71                                           const FileSpec &core_file)72    : PostMortemProcess(target_sp, listener_sp, core_file) {}73 74lldb::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_FBSDVMCORE81    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#endif88 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#endif97  }98  return nullptr;99}100 101void 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 110void ProcessFreeBSDKernel::Terminate() {111  PluginManager::UnregisterPlugin(ProcessFreeBSDKernel::CreateInstance);112}113 114Status ProcessFreeBSDKernel::DoDestroy() { return Status(); }115 116bool ProcessFreeBSDKernel::CanDebug(lldb::TargetSP target_sp,117                                    bool plugin_specified_by_name) {118  return true;119}120 121void ProcessFreeBSDKernel::RefreshStateAfterStop() {}122 123bool 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 one127    // and only core thread state up.128 129    // We cannot construct a thread without a register context as that crashes130    // LLDB but we can construct a process without threads to provide minimal131    // 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 have144    // to figure them out ourselves145    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 offsets166    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 thread172    // dumppcb contains its PCB173    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 CPUs178    // each element is of size pcb_size179    int32_t pcbsize =180        ReadSignedIntegerFromMemory(FindSymbol("pcb_size"), 4, -1, error);181    lldb::addr_t stoppcbs = FindSymbol("stoppcbs");182 183    // from FreeBSD sys/param.h184    constexpr size_t fbsd_maxcomlen = 19;185 186    // iterate through a linked list of all processes187    // allproc is a pointer to the first list element, p_list field188    // (found at offset_p_list) specifies the next element189    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 string196      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' threads200      // the initial thread is found in process' p_threads, subsequent201      // elements are linked via td_plist field202      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 name213        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 thread218        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 CPU230        // 3. otherwise, its PCB is in the thread struct231        if (tid == dumptid) {232          // NB: dumppcb can be LLDB_INVALID_ADDRESS if reading it failed233          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 find237          // the correct PCB238          if (stoppcbs != LLDB_INVALID_ADDRESS && pcbsize > 0)239            pcb_addr = stoppcbs + oncpu * pcbsize;240          else241            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 258Status ProcessFreeBSDKernel::DoLoadCore() {259  // The core is already loaded by CreateInstance().260  return Status();261}262 263DynamicLoader *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 270lldb::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_FBSDVMCORE277 278ProcessFreeBSDKernelFVC::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 284ProcessFreeBSDKernelFVC::~ProcessFreeBSDKernelFVC() {285  if (m_fvc)286    fvc_close(m_fvc);287}288 289size_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 301const char *ProcessFreeBSDKernelFVC::GetError() { return fvc_geterr(m_fvc); }302 303#endif // LLDB_ENABLE_FBSDVMCORE304 305#if defined(__FreeBSD__)306 307ProcessFreeBSDKernelKVM::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 313ProcessFreeBSDKernelKVM::~ProcessFreeBSDKernelKVM() {314  if (m_kvm)315    kvm_close(m_kvm);316}317 318size_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 330const char *ProcessFreeBSDKernelKVM::GetError() { return kvm_geterr(m_kvm); }331 332#endif // defined(__FreeBSD__)333