xref: /freebsd-src/contrib/llvm-project/lldb/source/Plugins/Process/FreeBSDKernel/ProcessFreeBSDKernel.cpp (revision 0eae32dcef82f6f06de6419a0d623d7def0cc8f6)
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/Static/DynamicLoaderStatic.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     const Symbol *pcb_sym =
141         GetTarget().GetExecutableModule()->FindFirstSymbolWithNameAndType(
142             ConstString("dumppcb"));
143     ThreadSP thread_sp(new ThreadFreeBSDKernel(
144         *this, 1, pcb_sym ? pcb_sym->GetFileAddress() : LLDB_INVALID_ADDRESS));
145     new_thread_list.AddThread(thread_sp);
146   } else {
147     const uint32_t num_threads = old_thread_list.GetSize(false);
148     for (uint32_t i = 0; i < num_threads; ++i)
149       new_thread_list.AddThread(old_thread_list.GetThreadAtIndex(i, false));
150   }
151   return new_thread_list.GetSize(false) > 0;
152 }
153 
154 Status ProcessFreeBSDKernel::DoLoadCore() {
155   // The core is already loaded by CreateInstance().
156   return Status();
157 }
158 
159 DynamicLoader *ProcessFreeBSDKernel::GetDynamicLoader() {
160   if (m_dyld_up.get() == nullptr)
161     m_dyld_up.reset(DynamicLoader::FindPlugin(
162         this, DynamicLoaderStatic::GetPluginNameStatic()));
163   return m_dyld_up.get();
164 }
165 
166 #if LLDB_ENABLE_FBSDVMCORE
167 
168 ProcessFreeBSDKernelFVC::ProcessFreeBSDKernelFVC(lldb::TargetSP target_sp,
169                                                  ListenerSP listener_sp,
170                                                  fvc_t *fvc)
171     : ProcessFreeBSDKernel(target_sp, listener_sp), m_fvc(fvc) {}
172 
173 ProcessFreeBSDKernelFVC::~ProcessFreeBSDKernelFVC() {
174   if (m_fvc)
175     fvc_close(m_fvc);
176 }
177 
178 size_t ProcessFreeBSDKernelFVC::DoReadMemory(lldb::addr_t addr, void *buf,
179                                              size_t size, Status &error) {
180   ssize_t rd = 0;
181   rd = fvc_read(m_fvc, addr, buf, size);
182   if (rd < 0 || static_cast<size_t>(rd) != size) {
183     error.SetErrorStringWithFormat("Reading memory failed: %s", GetError());
184     return rd > 0 ? rd : 0;
185   }
186   return rd;
187 }
188 
189 const char *ProcessFreeBSDKernelFVC::GetError() { return fvc_geterr(m_fvc); }
190 
191 #endif // LLDB_ENABLE_FBSDVMCORE
192 
193 #if defined(__FreeBSD__)
194 
195 ProcessFreeBSDKernelKVM::ProcessFreeBSDKernelKVM(lldb::TargetSP target_sp,
196                                                  ListenerSP listener_sp,
197                                                  kvm_t *fvc)
198     : ProcessFreeBSDKernel(target_sp, listener_sp), m_kvm(fvc) {}
199 
200 ProcessFreeBSDKernelKVM::~ProcessFreeBSDKernelKVM() {
201   if (m_kvm)
202     kvm_close(m_kvm);
203 }
204 
205 size_t ProcessFreeBSDKernelKVM::DoReadMemory(lldb::addr_t addr, void *buf,
206                                              size_t size, Status &error) {
207   ssize_t rd = 0;
208   rd = kvm_read2(m_kvm, addr, buf, size);
209   if (rd < 0 || static_cast<size_t>(rd) != size) {
210     error.SetErrorStringWithFormat("Reading memory failed: %s", GetError());
211     return rd > 0 ? rd : 0;
212   }
213   return rd;
214 }
215 
216 const char *ProcessFreeBSDKernelKVM::GetError() { return kvm_geterr(m_kvm); }
217 
218 #endif // defined(__FreeBSD__)
219