1 | //===-- CPPLanguageRuntime.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 <cstring> |
10 | #include <iostream> |
11 | |
12 | #include <memory> |
13 | |
14 | #include "CPPLanguageRuntime.h" |
15 | |
16 | #include "llvm/ADT/StringRef.h" |
17 | |
18 | #include "lldb/Symbol/Block.h" |
19 | #include "lldb/Symbol/Variable.h" |
20 | #include "lldb/Symbol/VariableList.h" |
21 | |
22 | #include "lldb/Core/PluginManager.h" |
23 | #include "lldb/Core/UniqueCStringMap.h" |
24 | #include "lldb/Symbol/CompileUnit.h" |
25 | #include "lldb/Target/ABI.h" |
26 | #include "lldb/Target/ExecutionContext.h" |
27 | #include "lldb/Target/RegisterContext.h" |
28 | #include "lldb/Target/SectionLoadList.h" |
29 | #include "lldb/Target/StackFrame.h" |
30 | #include "lldb/Target/StackFrameRecognizer.h" |
31 | #include "lldb/Target/ThreadPlanRunToAddress.h" |
32 | #include "lldb/Target/ThreadPlanStepInRange.h" |
33 | #include "lldb/Utility/Timer.h" |
34 | |
35 | using namespace lldb; |
36 | using namespace lldb_private; |
37 | |
38 | static ConstString g_this = ConstString("this" ); |
39 | // Artificial coroutine-related variables emitted by clang. |
40 | static ConstString g_promise = ConstString("__promise" ); |
41 | static ConstString g_coro_frame = ConstString("__coro_frame" ); |
42 | |
43 | char CPPLanguageRuntime::ID = 0; |
44 | |
45 | /// A frame recognizer that is installed to hide libc++ implementation |
46 | /// details from the backtrace. |
47 | class LibCXXFrameRecognizer : public StackFrameRecognizer { |
48 | std::array<RegularExpression, 2> m_hidden_regex; |
49 | RecognizedStackFrameSP m_hidden_frame; |
50 | |
51 | struct LibCXXHiddenFrame : public RecognizedStackFrame { |
52 | bool ShouldHide() override { return true; } |
53 | }; |
54 | |
55 | public: |
56 | LibCXXFrameRecognizer() |
57 | : m_hidden_regex{ |
58 | // internal implementation details in the `std::` namespace |
59 | // std::__1::__function::__alloc_func<void (*)(), std::__1::allocator<void (*)()>, void ()>::operator()[abi:ne200000] |
60 | // std::__1::__function::__func<void (*)(), std::__1::allocator<void (*)()>, void ()>::operator() |
61 | // std::__1::__function::__value_func<void ()>::operator()[abi:ne200000]() const |
62 | // std::__2::__function::__policy_invoker<void (int, int)>::__call_impl[abi:ne200000]<std::__2::__function::__default_alloc_func<int (*)(int, int), int (int, int)>> |
63 | // std::__1::__invoke[abi:ne200000]<void (*&)()> |
64 | // std::__1::__invoke_void_return_wrapper<void, true>::__call[abi:ne200000]<void (*&)()> |
65 | RegularExpression{R"(^std::__[^:]*::__)" }, |
66 | // internal implementation details in the `std::ranges` namespace |
67 | // std::__1::ranges::__sort::__sort_fn_impl[abi:ne200000]<std::__1::__wrap_iter<int*>, std::__1::__wrap_iter<int*>, bool (*)(int, int), std::__1::identity> |
68 | RegularExpression{R"(^std::__[^:]*::ranges::__)" }, |
69 | }, |
70 | m_hidden_frame(new LibCXXHiddenFrame()) {} |
71 | |
72 | std::string GetName() override { return "libc++ frame recognizer" ; } |
73 | |
74 | lldb::RecognizedStackFrameSP |
75 | RecognizeFrame(lldb::StackFrameSP frame_sp) override { |
76 | if (!frame_sp) |
77 | return {}; |
78 | const auto &sc = frame_sp->GetSymbolContext(resolve_scope: lldb::eSymbolContextFunction); |
79 | if (!sc.function) |
80 | return {}; |
81 | |
82 | // Check if we have a regex match |
83 | for (RegularExpression &r : m_hidden_regex) { |
84 | if (!r.Execute(string: sc.function->GetNameNoArguments())) |
85 | continue; |
86 | |
87 | // Only hide this frame if the immediate caller is also within libc++. |
88 | lldb::ThreadSP thread_sp = frame_sp->GetThread(); |
89 | if (!thread_sp) |
90 | return {}; |
91 | lldb::StackFrameSP parent_frame_sp = |
92 | thread_sp->GetStackFrameAtIndex(idx: frame_sp->GetFrameIndex() + 1); |
93 | if (!parent_frame_sp) |
94 | return {}; |
95 | const auto &parent_sc = |
96 | parent_frame_sp->GetSymbolContext(resolve_scope: lldb::eSymbolContextFunction); |
97 | if (!parent_sc.function) |
98 | return {}; |
99 | if (parent_sc.function->GetNameNoArguments().GetStringRef().starts_with( |
100 | Prefix: "std::" )) |
101 | return m_hidden_frame; |
102 | } |
103 | |
104 | return {}; |
105 | } |
106 | }; |
107 | |
108 | CPPLanguageRuntime::CPPLanguageRuntime(Process *process) |
109 | : LanguageRuntime(process) { |
110 | if (process) |
111 | process->GetTarget().GetFrameRecognizerManager().AddRecognizer( |
112 | recognizer: StackFrameRecognizerSP(new LibCXXFrameRecognizer()), module: {}, |
113 | symbol: std::make_shared<RegularExpression>(args: "^std::__[^:]*::" ), |
114 | /*mangling_preference=*/symbol_mangling: Mangled::ePreferDemangledWithoutArguments, |
115 | /*first_instruction_only=*/false); |
116 | } |
117 | |
118 | bool CPPLanguageRuntime::IsAllowedRuntimeValue(ConstString name) { |
119 | return name == g_this || name == g_promise || name == g_coro_frame; |
120 | } |
121 | |
122 | llvm::Error CPPLanguageRuntime::GetObjectDescription(Stream &str, |
123 | ValueObject &object) { |
124 | // C++ has no generic way to do this. |
125 | return llvm::createStringError(Fmt: "C++ does not support object descriptions" ); |
126 | } |
127 | |
128 | llvm::Error |
129 | CPPLanguageRuntime::GetObjectDescription(Stream &str, Value &value, |
130 | ExecutionContextScope *exe_scope) { |
131 | // C++ has no generic way to do this. |
132 | return llvm::createStringError(Fmt: "C++ does not support object descriptions" ); |
133 | } |
134 | |
135 | bool contains_lambda_identifier(llvm::StringRef &str_ref) { |
136 | return str_ref.contains(Other: "$_" ) || str_ref.contains(Other: "'lambda'" ); |
137 | } |
138 | |
139 | CPPLanguageRuntime::LibCppStdFunctionCallableInfo |
140 | line_entry_helper(Target &target, const SymbolContext &sc, Symbol *symbol, |
141 | llvm::StringRef first_template_param_sref, bool has_invoke) { |
142 | |
143 | CPPLanguageRuntime::LibCppStdFunctionCallableInfo optional_info; |
144 | |
145 | Address address = sc.GetFunctionOrSymbolAddress(); |
146 | |
147 | Address addr; |
148 | if (target.ResolveLoadAddress(load_addr: address.GetCallableLoadAddress(target: &target), |
149 | so_addr&: addr)) { |
150 | LineEntry line_entry; |
151 | addr.CalculateSymbolContextLineEntry(line_entry); |
152 | |
153 | if (contains_lambda_identifier(str_ref&: first_template_param_sref) || has_invoke) { |
154 | // Case 1 and 2 |
155 | optional_info.callable_case = lldb_private::CPPLanguageRuntime:: |
156 | LibCppStdFunctionCallableCase::Lambda; |
157 | } else { |
158 | // Case 3 |
159 | optional_info.callable_case = lldb_private::CPPLanguageRuntime:: |
160 | LibCppStdFunctionCallableCase::CallableObject; |
161 | } |
162 | |
163 | optional_info.callable_symbol = *symbol; |
164 | optional_info.callable_line_entry = line_entry; |
165 | optional_info.callable_address = addr; |
166 | } |
167 | |
168 | return optional_info; |
169 | } |
170 | |
171 | CPPLanguageRuntime::LibCppStdFunctionCallableInfo |
172 | CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo( |
173 | lldb::ValueObjectSP &valobj_sp) { |
174 | LLDB_SCOPED_TIMER(); |
175 | |
176 | LibCppStdFunctionCallableInfo optional_info; |
177 | |
178 | if (!valobj_sp) |
179 | return optional_info; |
180 | |
181 | // Member __f_ has type __base*, the contents of which will hold: |
182 | // 1) a vtable entry which may hold type information needed to discover the |
183 | // lambda being called |
184 | // 2) possibly hold a pointer to the callable object |
185 | // e.g. |
186 | // |
187 | // (lldb) frame var -R f_display |
188 | // (std::__1::function<void (int)>) f_display = { |
189 | // __buf_ = { |
190 | // … |
191 | // } |
192 | // __f_ = 0x00007ffeefbffa00 |
193 | // } |
194 | // (lldb) memory read -fA 0x00007ffeefbffa00 |
195 | // 0x7ffeefbffa00: ... `vtable for std::__1::__function::__func<void (*) ... |
196 | // 0x7ffeefbffa08: ... `print_num(int) at std_function_cppreference_exam ... |
197 | // |
198 | // We will be handling five cases below, std::function is wrapping: |
199 | // |
200 | // 1) a lambda we know at compile time. We will obtain the name of the lambda |
201 | // from the first template pameter from __func's vtable. We will look up |
202 | // the lambda's operator()() and obtain the line table entry. |
203 | // 2) a lambda we know at runtime. A pointer to the lambdas __invoke method |
204 | // will be stored after the vtable. We will obtain the lambdas name from |
205 | // this entry and lookup operator()() and obtain the line table entry. |
206 | // 3) a callable object via operator()(). We will obtain the name of the |
207 | // object from the first template parameter from __func's vtable. We will |
208 | // look up the objects operator()() and obtain the line table entry. |
209 | // 4) a member function. A pointer to the function will stored after the |
210 | // we will obtain the name from this pointer. |
211 | // 5) a free function. A pointer to the function will stored after the vtable |
212 | // we will obtain the name from this pointer. |
213 | ValueObjectSP member_f_(valobj_sp->GetChildMemberWithName(name: "__f_" )); |
214 | |
215 | if (member_f_) { |
216 | ValueObjectSP sub_member_f_(member_f_->GetChildMemberWithName(name: "__f_" )); |
217 | |
218 | if (sub_member_f_) |
219 | member_f_ = sub_member_f_; |
220 | } |
221 | |
222 | if (!member_f_) |
223 | return optional_info; |
224 | |
225 | lldb::addr_t member_f_pointer_value = member_f_->GetValueAsUnsigned(fail_value: 0); |
226 | |
227 | optional_info.member_f_pointer_value = member_f_pointer_value; |
228 | |
229 | if (!member_f_pointer_value) |
230 | return optional_info; |
231 | |
232 | ExecutionContext exe_ctx(valobj_sp->GetExecutionContextRef()); |
233 | Process *process = exe_ctx.GetProcessPtr(); |
234 | |
235 | if (process == nullptr) |
236 | return optional_info; |
237 | |
238 | uint32_t address_size = process->GetAddressByteSize(); |
239 | Status status; |
240 | |
241 | // First item pointed to by __f_ should be the pointer to the vtable for |
242 | // a __base object. |
243 | lldb::addr_t vtable_address = |
244 | process->ReadPointerFromMemory(vm_addr: member_f_pointer_value, error&: status); |
245 | |
246 | if (status.Fail()) |
247 | return optional_info; |
248 | |
249 | lldb::addr_t vtable_address_first_entry = |
250 | process->ReadPointerFromMemory(vm_addr: vtable_address + address_size, error&: status); |
251 | |
252 | if (status.Fail()) |
253 | return optional_info; |
254 | |
255 | lldb::addr_t address_after_vtable = member_f_pointer_value + address_size; |
256 | // As commented above we may not have a function pointer but if we do we will |
257 | // need it. |
258 | lldb::addr_t possible_function_address = |
259 | process->ReadPointerFromMemory(vm_addr: address_after_vtable, error&: status); |
260 | |
261 | if (status.Fail()) |
262 | return optional_info; |
263 | |
264 | Target &target = process->GetTarget(); |
265 | |
266 | if (!target.HasLoadedSections()) |
267 | return optional_info; |
268 | |
269 | Address vtable_first_entry_resolved; |
270 | |
271 | if (!target.ResolveLoadAddress(load_addr: vtable_address_first_entry, |
272 | so_addr&: vtable_first_entry_resolved)) |
273 | return optional_info; |
274 | |
275 | Address vtable_addr_resolved; |
276 | SymbolContext sc; |
277 | Symbol *symbol = nullptr; |
278 | |
279 | if (!target.ResolveLoadAddress(load_addr: vtable_address, so_addr&: vtable_addr_resolved)) |
280 | return optional_info; |
281 | |
282 | target.GetImages().ResolveSymbolContextForAddress( |
283 | so_addr: vtable_addr_resolved, resolve_scope: eSymbolContextEverything, sc); |
284 | symbol = sc.symbol; |
285 | |
286 | if (symbol == nullptr) |
287 | return optional_info; |
288 | |
289 | llvm::StringRef vtable_name(symbol->GetName().GetStringRef()); |
290 | bool found_expected_start_string = |
291 | vtable_name.starts_with(Prefix: "vtable for std::__1::__function::__func<" ); |
292 | |
293 | if (!found_expected_start_string) |
294 | return optional_info; |
295 | |
296 | // Given case 1 or 3 we have a vtable name, we are want to extract the first |
297 | // template parameter |
298 | // |
299 | // ... __func<main::$_0, std::__1::allocator<main::$_0> ... |
300 | // ^^^^^^^^^ |
301 | // |
302 | // We could see names such as: |
303 | // main::$_0 |
304 | // Bar::add_num2(int)::'lambda'(int) |
305 | // Bar |
306 | // |
307 | // We do this by find the first < and , and extracting in between. |
308 | // |
309 | // This covers the case of the lambda known at compile time. |
310 | size_t first_open_angle_bracket = vtable_name.find(C: '<') + 1; |
311 | size_t first_comma = vtable_name.find(C: ','); |
312 | |
313 | llvm::StringRef first_template_parameter = |
314 | vtable_name.slice(Start: first_open_angle_bracket, End: first_comma); |
315 | |
316 | Address function_address_resolved; |
317 | |
318 | // Setup for cases 2, 4 and 5 we have a pointer to a function after the |
319 | // vtable. We will use a process of elimination to drop through each case |
320 | // and obtain the data we need. |
321 | if (target.ResolveLoadAddress(load_addr: possible_function_address, |
322 | so_addr&: function_address_resolved)) { |
323 | target.GetImages().ResolveSymbolContextForAddress( |
324 | so_addr: function_address_resolved, resolve_scope: eSymbolContextEverything, sc); |
325 | symbol = sc.symbol; |
326 | } |
327 | |
328 | // These conditions are used several times to simplify statements later on. |
329 | bool has_invoke = |
330 | (symbol ? symbol->GetName().GetStringRef().contains(Other: "__invoke" ) : false); |
331 | auto calculate_symbol_context_helper = [](auto &t, |
332 | SymbolContextList &sc_list) { |
333 | SymbolContext sc; |
334 | t->CalculateSymbolContext(&sc); |
335 | sc_list.Append(sc); |
336 | }; |
337 | |
338 | // Case 2 |
339 | if (has_invoke) { |
340 | SymbolContextList scl; |
341 | calculate_symbol_context_helper(symbol, scl); |
342 | |
343 | return line_entry_helper(target, sc: scl[0], symbol, first_template_param_sref: first_template_parameter, |
344 | has_invoke); |
345 | } |
346 | |
347 | // Case 4 or 5 |
348 | if (symbol && !symbol->GetName().GetStringRef().starts_with(Prefix: "vtable for" ) && |
349 | !contains_lambda_identifier(str_ref&: first_template_parameter) && !has_invoke) { |
350 | optional_info.callable_case = |
351 | LibCppStdFunctionCallableCase::FreeOrMemberFunction; |
352 | optional_info.callable_address = function_address_resolved; |
353 | optional_info.callable_symbol = *symbol; |
354 | |
355 | return optional_info; |
356 | } |
357 | |
358 | std::string func_to_match = first_template_parameter.str(); |
359 | |
360 | auto it = CallableLookupCache.find(Key: func_to_match); |
361 | if (it != CallableLookupCache.end()) |
362 | return it->second; |
363 | |
364 | SymbolContextList scl; |
365 | |
366 | CompileUnit *vtable_cu = |
367 | vtable_first_entry_resolved.CalculateSymbolContextCompileUnit(); |
368 | llvm::StringRef name_to_use = func_to_match; |
369 | |
370 | // Case 3, we have a callable object instead of a lambda |
371 | // |
372 | // TODO |
373 | // We currently don't support this case a callable object may have multiple |
374 | // operator()() varying on const/non-const and number of arguments and we |
375 | // don't have a way to currently distinguish them so we will bail out now. |
376 | if (!contains_lambda_identifier(str_ref&: name_to_use)) |
377 | return optional_info; |
378 | |
379 | if (vtable_cu && !has_invoke) { |
380 | lldb::FunctionSP func_sp = |
381 | vtable_cu->FindFunction(matching_lambda: [name_to_use](const FunctionSP &f) { |
382 | auto name = f->GetName().GetStringRef(); |
383 | if (name.starts_with(Prefix: name_to_use) && name.contains(Other: "operator" )) |
384 | return true; |
385 | |
386 | return false; |
387 | }); |
388 | |
389 | if (func_sp) { |
390 | calculate_symbol_context_helper(func_sp, scl); |
391 | } |
392 | } |
393 | |
394 | if (symbol == nullptr) |
395 | return optional_info; |
396 | |
397 | // Case 1 or 3 |
398 | if (scl.GetSize() >= 1) { |
399 | optional_info = line_entry_helper(target, sc: scl[0], symbol, |
400 | first_template_param_sref: first_template_parameter, has_invoke); |
401 | } |
402 | |
403 | CallableLookupCache[func_to_match] = optional_info; |
404 | |
405 | return optional_info; |
406 | } |
407 | |
408 | lldb::ThreadPlanSP |
409 | CPPLanguageRuntime::GetStepThroughTrampolinePlan(Thread &thread, |
410 | bool stop_others) { |
411 | ThreadPlanSP ret_plan_sp; |
412 | |
413 | lldb::addr_t curr_pc = thread.GetRegisterContext()->GetPC(); |
414 | |
415 | TargetSP target_sp(thread.CalculateTarget()); |
416 | |
417 | if (!target_sp->HasLoadedSections()) |
418 | return ret_plan_sp; |
419 | |
420 | Address pc_addr_resolved; |
421 | SymbolContext sc; |
422 | Symbol *symbol; |
423 | |
424 | if (!target_sp->ResolveLoadAddress(load_addr: curr_pc, so_addr&: pc_addr_resolved)) |
425 | return ret_plan_sp; |
426 | |
427 | target_sp->GetImages().ResolveSymbolContextForAddress( |
428 | so_addr: pc_addr_resolved, resolve_scope: eSymbolContextEverything, sc); |
429 | symbol = sc.symbol; |
430 | |
431 | if (symbol == nullptr) |
432 | return ret_plan_sp; |
433 | |
434 | llvm::StringRef function_name(symbol->GetName().GetCString()); |
435 | |
436 | // Handling the case where we are attempting to step into std::function. |
437 | // The behavior will be that we will attempt to obtain the wrapped |
438 | // callable via FindLibCppStdFunctionCallableInfo() and if we find it we |
439 | // will return a ThreadPlanRunToAddress to the callable. Therefore we will |
440 | // step into the wrapped callable. |
441 | // |
442 | bool found_expected_start_string = |
443 | function_name.starts_with(Prefix: "std::__1::function<" ); |
444 | |
445 | if (!found_expected_start_string) |
446 | return ret_plan_sp; |
447 | |
448 | AddressRange range_of_curr_func; |
449 | sc.GetAddressRange(scope: eSymbolContextEverything, range_idx: 0, use_inline_block_range: false, range&: range_of_curr_func); |
450 | |
451 | StackFrameSP frame = thread.GetStackFrameAtIndex(idx: 0); |
452 | |
453 | if (frame) { |
454 | ValueObjectSP value_sp = frame->FindVariable(name: g_this); |
455 | |
456 | CPPLanguageRuntime::LibCppStdFunctionCallableInfo callable_info = |
457 | FindLibCppStdFunctionCallableInfo(valobj_sp&: value_sp); |
458 | |
459 | if (callable_info.callable_case != LibCppStdFunctionCallableCase::Invalid && |
460 | value_sp->GetValueIsValid()) { |
461 | // We found the std::function wrapped callable and we have its address. |
462 | // We now create a ThreadPlan to run to the callable. |
463 | ret_plan_sp = std::make_shared<ThreadPlanRunToAddress>( |
464 | args&: thread, args&: callable_info.callable_address, args&: stop_others); |
465 | return ret_plan_sp; |
466 | } else { |
467 | // We are in std::function but we could not obtain the callable. |
468 | // We create a ThreadPlan to keep stepping through using the address range |
469 | // of the current function. |
470 | ret_plan_sp = std::make_shared<ThreadPlanStepInRange>( |
471 | args&: thread, args&: range_of_curr_func, args&: sc, args: nullptr, args: eOnlyThisThread, |
472 | args: eLazyBoolYes, args: eLazyBoolYes); |
473 | return ret_plan_sp; |
474 | } |
475 | } |
476 | |
477 | return ret_plan_sp; |
478 | } |
479 | |
480 | bool CPPLanguageRuntime::IsSymbolARuntimeThunk(const Symbol &symbol) { |
481 | llvm::StringRef mangled_name = |
482 | symbol.GetMangled().GetMangledName().GetStringRef(); |
483 | // Virtual function overriding from a non-virtual base use a "Th" prefix. |
484 | // Virtual function overriding from a virtual base must use a "Tv" prefix. |
485 | // Virtual function overriding thunks with covariant returns use a "Tc" |
486 | // prefix. |
487 | return mangled_name.starts_with(Prefix: "_ZTh" ) || mangled_name.starts_with(Prefix: "_ZTv" ) || |
488 | mangled_name.starts_with(Prefix: "_ZTc" ); |
489 | } |
490 | |