1 | //===-- ItaniumABILanguageRuntime.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 "ItaniumABILanguageRuntime.h" |
10 | |
11 | #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" |
12 | #include "lldb/Breakpoint/BreakpointLocation.h" |
13 | #include "lldb/Core/Mangled.h" |
14 | #include "lldb/Core/Module.h" |
15 | #include "lldb/Core/PluginManager.h" |
16 | #include "lldb/Core/ValueObject.h" |
17 | #include "lldb/Core/ValueObjectMemory.h" |
18 | #include "lldb/DataFormatters/FormattersHelpers.h" |
19 | #include "lldb/Expression/DiagnosticManager.h" |
20 | #include "lldb/Expression/FunctionCaller.h" |
21 | #include "lldb/Interpreter/CommandObject.h" |
22 | #include "lldb/Interpreter/CommandObjectMultiword.h" |
23 | #include "lldb/Interpreter/CommandReturnObject.h" |
24 | #include "lldb/Symbol/Symbol.h" |
25 | #include "lldb/Symbol/SymbolFile.h" |
26 | #include "lldb/Symbol/TypeList.h" |
27 | #include "lldb/Target/Process.h" |
28 | #include "lldb/Target/RegisterContext.h" |
29 | #include "lldb/Target/SectionLoadList.h" |
30 | #include "lldb/Target/StopInfo.h" |
31 | #include "lldb/Target/Target.h" |
32 | #include "lldb/Target/Thread.h" |
33 | #include "lldb/Utility/ConstString.h" |
34 | #include "lldb/Utility/LLDBLog.h" |
35 | #include "lldb/Utility/Log.h" |
36 | #include "lldb/Utility/Scalar.h" |
37 | #include "lldb/Utility/Status.h" |
38 | |
39 | #include <vector> |
40 | |
41 | using namespace lldb; |
42 | using namespace lldb_private; |
43 | |
44 | LLDB_PLUGIN_DEFINE_ADV(ItaniumABILanguageRuntime, CXXItaniumABI) |
45 | |
46 | static const char *vtable_demangled_prefix = "vtable for " ; |
47 | |
48 | char ItaniumABILanguageRuntime::ID = 0; |
49 | |
50 | bool ItaniumABILanguageRuntime::CouldHaveDynamicValue(ValueObject &in_value) { |
51 | const bool check_cxx = true; |
52 | const bool check_objc = false; |
53 | return in_value.GetCompilerType().IsPossibleDynamicType(target_type: nullptr, check_cplusplus: check_cxx, |
54 | check_objc); |
55 | } |
56 | |
57 | TypeAndOrName ItaniumABILanguageRuntime::GetTypeInfo( |
58 | ValueObject &in_value, const VTableInfo &vtable_info) { |
59 | if (vtable_info.addr.IsSectionOffset()) { |
60 | // See if we have cached info for this type already |
61 | TypeAndOrName type_info = GetDynamicTypeInfo(vtable_addr: vtable_info.addr); |
62 | if (type_info) |
63 | return type_info; |
64 | |
65 | if (vtable_info.symbol) { |
66 | Log *log = GetLog(mask: LLDBLog::Object); |
67 | llvm::StringRef symbol_name = |
68 | vtable_info.symbol->GetMangled().GetDemangledName().GetStringRef(); |
69 | LLDB_LOGF(log, |
70 | "0x%16.16" PRIx64 |
71 | ": static-type = '%s' has vtable symbol '%s'\n" , |
72 | in_value.GetPointerValue(), |
73 | in_value.GetTypeName().GetCString(), |
74 | symbol_name.str().c_str()); |
75 | // We are a C++ class, that's good. Get the class name and look it |
76 | // up: |
77 | llvm::StringRef class_name = symbol_name; |
78 | class_name.consume_front(Prefix: vtable_demangled_prefix); |
79 | // We know the class name is absolute, so tell FindTypes that by |
80 | // prefixing it with the root namespace: |
81 | std::string lookup_name("::" ); |
82 | lookup_name.append(s: class_name.data(), n: class_name.size()); |
83 | |
84 | type_info.SetName(class_name); |
85 | ConstString const_lookup_name(lookup_name); |
86 | TypeList class_types; |
87 | ModuleSP module_sp = vtable_info.symbol->CalculateSymbolContextModule(); |
88 | // First look in the module that the vtable symbol came from and |
89 | // look for a single exact match. |
90 | TypeResults results; |
91 | TypeQuery query(const_lookup_name.GetStringRef(), |
92 | TypeQueryOptions::e_exact_match | |
93 | TypeQueryOptions::e_find_one); |
94 | if (module_sp) { |
95 | module_sp->FindTypes(query, results); |
96 | TypeSP type_sp = results.GetFirstType(); |
97 | if (type_sp) |
98 | class_types.Insert(type: type_sp); |
99 | } |
100 | |
101 | // If we didn't find a symbol, then move on to the entire module |
102 | // list in the target and get as many unique matches as possible |
103 | if (class_types.Empty()) { |
104 | query.SetFindOne(false); |
105 | m_process->GetTarget().GetImages().FindTypes(search_first: nullptr, query, results); |
106 | for (const auto &type_sp : results.GetTypeMap().Types()) |
107 | class_types.Insert(type: type_sp); |
108 | } |
109 | |
110 | lldb::TypeSP type_sp; |
111 | if (class_types.Empty()) { |
112 | LLDB_LOGF(log, "0x%16.16" PRIx64 ": is not dynamic\n" , |
113 | in_value.GetPointerValue()); |
114 | return TypeAndOrName(); |
115 | } |
116 | if (class_types.GetSize() == 1) { |
117 | type_sp = class_types.GetTypeAtIndex(idx: 0); |
118 | if (type_sp) { |
119 | if (TypeSystemClang::IsCXXClassType( |
120 | type: type_sp->GetForwardCompilerType())) { |
121 | LLDB_LOGF( |
122 | log, |
123 | "0x%16.16" PRIx64 |
124 | ": static-type = '%s' has dynamic type: uid={0x%" PRIx64 |
125 | "}, type-name='%s'\n" , |
126 | in_value.GetPointerValue(), in_value.GetTypeName().AsCString(), |
127 | type_sp->GetID(), type_sp->GetName().GetCString()); |
128 | type_info.SetTypeSP(type_sp); |
129 | } |
130 | } |
131 | } else { |
132 | size_t i; |
133 | if (log) { |
134 | for (i = 0; i < class_types.GetSize(); i++) { |
135 | type_sp = class_types.GetTypeAtIndex(idx: i); |
136 | if (type_sp) { |
137 | LLDB_LOGF( |
138 | log, |
139 | "0x%16.16" PRIx64 |
140 | ": static-type = '%s' has multiple matching dynamic " |
141 | "types: uid={0x%" PRIx64 "}, type-name='%s'\n" , |
142 | in_value.GetPointerValue(), |
143 | in_value.GetTypeName().AsCString(), |
144 | type_sp->GetID(), type_sp->GetName().GetCString()); |
145 | } |
146 | } |
147 | } |
148 | |
149 | for (i = 0; i < class_types.GetSize(); i++) { |
150 | type_sp = class_types.GetTypeAtIndex(idx: i); |
151 | if (type_sp) { |
152 | if (TypeSystemClang::IsCXXClassType( |
153 | type: type_sp->GetForwardCompilerType())) { |
154 | LLDB_LOGF( |
155 | log, |
156 | "0x%16.16" PRIx64 ": static-type = '%s' has multiple " |
157 | "matching dynamic types, picking " |
158 | "this one: uid={0x%" PRIx64 "}, type-name='%s'\n" , |
159 | in_value.GetPointerValue(), |
160 | in_value.GetTypeName().AsCString(), |
161 | type_sp->GetID(), type_sp->GetName().GetCString()); |
162 | type_info.SetTypeSP(type_sp); |
163 | } |
164 | } |
165 | } |
166 | |
167 | if (log) { |
168 | LLDB_LOGF(log, |
169 | "0x%16.16" PRIx64 |
170 | ": static-type = '%s' has multiple matching dynamic " |
171 | "types, didn't find a C++ match\n" , |
172 | in_value.GetPointerValue(), |
173 | in_value.GetTypeName().AsCString()); |
174 | } |
175 | } |
176 | if (type_info) |
177 | SetDynamicTypeInfo(vtable_addr: vtable_info.addr, type_info); |
178 | return type_info; |
179 | } |
180 | } |
181 | return TypeAndOrName(); |
182 | } |
183 | |
184 | llvm::Error ItaniumABILanguageRuntime::TypeHasVTable(CompilerType type) { |
185 | // Check to make sure the class has a vtable. |
186 | CompilerType original_type = type; |
187 | if (type.IsPointerOrReferenceType()) { |
188 | CompilerType pointee_type = type.GetPointeeType(); |
189 | if (pointee_type) |
190 | type = pointee_type; |
191 | } |
192 | |
193 | // Make sure this is a class or a struct first by checking the type class |
194 | // bitfield that gets returned. |
195 | if ((type.GetTypeClass() & (eTypeClassStruct | eTypeClassClass)) == 0) { |
196 | return llvm::createStringError(EC: std::errc::invalid_argument, |
197 | Fmt: "type \"%s\" is not a class or struct or a pointer to one" , |
198 | Vals: original_type.GetTypeName().AsCString(value_if_empty: "<invalid>" )); |
199 | } |
200 | |
201 | // Check if the type has virtual functions by asking it if it is polymorphic. |
202 | if (!type.IsPolymorphicClass()) { |
203 | return llvm::createStringError(EC: std::errc::invalid_argument, |
204 | Fmt: "type \"%s\" doesn't have a vtable" , |
205 | Vals: type.GetTypeName().AsCString(value_if_empty: "<invalid>" )); |
206 | } |
207 | return llvm::Error::success(); |
208 | } |
209 | |
210 | // This function can accept both pointers or references to classes as well as |
211 | // instances of classes. If you are using this function during dynamic type |
212 | // detection, only valid ValueObjects that return true to |
213 | // CouldHaveDynamicValue(...) should call this function and \a check_type |
214 | // should be set to false. This function is also used by ValueObjectVTable |
215 | // and is can pass in instances of classes which is not suitable for dynamic |
216 | // type detection, these cases should pass true for \a check_type. |
217 | llvm::Expected<LanguageRuntime::VTableInfo> |
218 | ItaniumABILanguageRuntime::GetVTableInfo(ValueObject &in_value, |
219 | bool check_type) { |
220 | |
221 | CompilerType type = in_value.GetCompilerType(); |
222 | if (check_type) { |
223 | if (llvm::Error err = TypeHasVTable(type)) |
224 | return std::move(err); |
225 | } |
226 | ExecutionContext exe_ctx(in_value.GetExecutionContextRef()); |
227 | Process *process = exe_ctx.GetProcessPtr(); |
228 | if (process == nullptr) |
229 | return llvm::createStringError(EC: std::errc::invalid_argument, |
230 | Fmt: "invalid process" ); |
231 | |
232 | AddressType address_type; |
233 | lldb::addr_t original_ptr = LLDB_INVALID_ADDRESS; |
234 | if (type.IsPointerOrReferenceType()) |
235 | original_ptr = in_value.GetPointerValue(address_type: &address_type); |
236 | else |
237 | original_ptr = in_value.GetAddressOf(/*scalar_is_load_address=*/true, |
238 | address_type: &address_type); |
239 | if (original_ptr == LLDB_INVALID_ADDRESS || address_type != eAddressTypeLoad) |
240 | return llvm::createStringError(EC: std::errc::invalid_argument, |
241 | Fmt: "failed to get the address of the value" ); |
242 | |
243 | Status error; |
244 | lldb::addr_t vtable_load_addr = |
245 | process->ReadPointerFromMemory(vm_addr: original_ptr, error); |
246 | |
247 | if (!error.Success() || vtable_load_addr == LLDB_INVALID_ADDRESS) |
248 | return llvm::createStringError(EC: std::errc::invalid_argument, |
249 | Fmt: "failed to read vtable pointer from memory at 0x%" PRIx64, |
250 | Vals: original_ptr); |
251 | |
252 | // The vtable load address can have authentication bits with |
253 | // AArch64 targets on Darwin. |
254 | vtable_load_addr = process->FixDataAddress(pc: vtable_load_addr); |
255 | |
256 | // Find the symbol that contains the "vtable_load_addr" address |
257 | Address vtable_addr; |
258 | if (!process->GetTarget().ResolveLoadAddress(load_addr: vtable_load_addr, so_addr&: vtable_addr)) |
259 | return llvm::createStringError(EC: std::errc::invalid_argument, |
260 | Fmt: "failed to resolve vtable pointer 0x%" |
261 | PRIx64 "to a section" , Vals: vtable_load_addr); |
262 | |
263 | // Check our cache first to see if we already have this info |
264 | { |
265 | std::lock_guard<std::mutex> locker(m_mutex); |
266 | auto pos = m_vtable_info_map.find(x: vtable_addr); |
267 | if (pos != m_vtable_info_map.end()) |
268 | return pos->second; |
269 | } |
270 | |
271 | Symbol *symbol = vtable_addr.CalculateSymbolContextSymbol(); |
272 | if (symbol == nullptr) |
273 | return llvm::createStringError(EC: std::errc::invalid_argument, |
274 | Fmt: "no symbol found for 0x%" PRIx64, |
275 | Vals: vtable_load_addr); |
276 | llvm::StringRef name = symbol->GetMangled().GetDemangledName().GetStringRef(); |
277 | if (name.starts_with(Prefix: vtable_demangled_prefix)) { |
278 | VTableInfo info = {.addr: vtable_addr, .symbol: symbol}; |
279 | std::lock_guard<std::mutex> locker(m_mutex); |
280 | auto pos = m_vtable_info_map[vtable_addr] = info; |
281 | return info; |
282 | } |
283 | return llvm::createStringError(EC: std::errc::invalid_argument, |
284 | Fmt: "symbol found that contains 0x%" PRIx64 " is not a vtable symbol" , |
285 | Vals: vtable_load_addr); |
286 | } |
287 | |
288 | bool ItaniumABILanguageRuntime::GetDynamicTypeAndAddress( |
289 | ValueObject &in_value, lldb::DynamicValueType use_dynamic, |
290 | TypeAndOrName &class_type_or_name, Address &dynamic_address, |
291 | Value::ValueType &value_type) { |
292 | // For Itanium, if the type has a vtable pointer in the object, it will be at |
293 | // offset 0 in the object. That will point to the "address point" within the |
294 | // vtable (not the beginning of the vtable.) We can then look up the symbol |
295 | // containing this "address point" and that symbol's name demangled will |
296 | // contain the full class name. The second pointer above the "address point" |
297 | // is the "offset_to_top". We'll use that to get the start of the value |
298 | // object which holds the dynamic type. |
299 | // |
300 | |
301 | class_type_or_name.Clear(); |
302 | value_type = Value::ValueType::Scalar; |
303 | |
304 | if (!CouldHaveDynamicValue(in_value)) |
305 | return false; |
306 | |
307 | // Check if we have a vtable pointer in this value. If we don't it will |
308 | // return an error, else it will return a valid resolved address. We don't |
309 | // want GetVTableInfo to check the type since we accept void * as a possible |
310 | // dynamic type and that won't pass the type check. We already checked the |
311 | // type above in CouldHaveDynamicValue(...). |
312 | llvm::Expected<VTableInfo> vtable_info_or_err = |
313 | GetVTableInfo(in_value, /*check_type=*/false); |
314 | if (!vtable_info_or_err) { |
315 | llvm::consumeError(Err: vtable_info_or_err.takeError()); |
316 | return false; |
317 | } |
318 | |
319 | const VTableInfo &vtable_info = vtable_info_or_err.get(); |
320 | class_type_or_name = GetTypeInfo(in_value, vtable_info); |
321 | |
322 | if (!class_type_or_name) |
323 | return false; |
324 | |
325 | CompilerType type = class_type_or_name.GetCompilerType(); |
326 | // There can only be one type with a given name, so we've just found |
327 | // duplicate definitions, and this one will do as well as any other. We |
328 | // don't consider something to have a dynamic type if it is the same as |
329 | // the static type. So compare against the value we were handed. |
330 | if (!type) |
331 | return true; |
332 | |
333 | if (TypeSystemClang::AreTypesSame(type1: in_value.GetCompilerType(), type2: type)) { |
334 | // The dynamic type we found was the same type, so we don't have a |
335 | // dynamic type here... |
336 | return false; |
337 | } |
338 | |
339 | // The offset_to_top is two pointers above the vtable pointer. |
340 | Target &target = m_process->GetTarget(); |
341 | const addr_t vtable_load_addr = vtable_info.addr.GetLoadAddress(target: &target); |
342 | if (vtable_load_addr == LLDB_INVALID_ADDRESS) |
343 | return false; |
344 | const uint32_t addr_byte_size = m_process->GetAddressByteSize(); |
345 | const lldb::addr_t offset_to_top_location = |
346 | vtable_load_addr - 2 * addr_byte_size; |
347 | // Watch for underflow, offset_to_top_location should be less than |
348 | // vtable_load_addr |
349 | if (offset_to_top_location >= vtable_load_addr) |
350 | return false; |
351 | Status error; |
352 | const int64_t offset_to_top = m_process->ReadSignedIntegerFromMemory( |
353 | load_addr: offset_to_top_location, byte_size: addr_byte_size, INT64_MIN, error); |
354 | |
355 | if (offset_to_top == INT64_MIN) |
356 | return false; |
357 | // So the dynamic type is a value that starts at offset_to_top above |
358 | // the original address. |
359 | lldb::addr_t dynamic_addr = in_value.GetPointerValue() + offset_to_top; |
360 | if (!m_process->GetTarget().ResolveLoadAddress( |
361 | load_addr: dynamic_addr, so_addr&: dynamic_address)) { |
362 | dynamic_address.SetRawAddress(dynamic_addr); |
363 | } |
364 | return true; |
365 | } |
366 | |
367 | TypeAndOrName ItaniumABILanguageRuntime::FixUpDynamicType( |
368 | const TypeAndOrName &type_and_or_name, ValueObject &static_value) { |
369 | CompilerType static_type(static_value.GetCompilerType()); |
370 | Flags static_type_flags(static_type.GetTypeInfo()); |
371 | |
372 | TypeAndOrName ret(type_and_or_name); |
373 | if (type_and_or_name.HasType()) { |
374 | // The type will always be the type of the dynamic object. If our parent's |
375 | // type was a pointer, then our type should be a pointer to the type of the |
376 | // dynamic object. If a reference, then the original type should be |
377 | // okay... |
378 | CompilerType orig_type = type_and_or_name.GetCompilerType(); |
379 | CompilerType corrected_type = orig_type; |
380 | if (static_type_flags.AllSet(mask: eTypeIsPointer)) |
381 | corrected_type = orig_type.GetPointerType(); |
382 | else if (static_type_flags.AllSet(mask: eTypeIsReference)) |
383 | corrected_type = orig_type.GetLValueReferenceType(); |
384 | ret.SetCompilerType(corrected_type); |
385 | } else { |
386 | // If we are here we need to adjust our dynamic type name to include the |
387 | // correct & or * symbol |
388 | std::string corrected_name(type_and_or_name.GetName().GetCString()); |
389 | if (static_type_flags.AllSet(mask: eTypeIsPointer)) |
390 | corrected_name.append(s: " *" ); |
391 | else if (static_type_flags.AllSet(mask: eTypeIsReference)) |
392 | corrected_name.append(s: " &" ); |
393 | // the parent type should be a correctly pointer'ed or referenc'ed type |
394 | ret.SetCompilerType(static_type); |
395 | ret.SetName(corrected_name.c_str()); |
396 | } |
397 | return ret; |
398 | } |
399 | |
400 | // Static Functions |
401 | LanguageRuntime * |
402 | ItaniumABILanguageRuntime::CreateInstance(Process *process, |
403 | lldb::LanguageType language) { |
404 | // FIXME: We have to check the process and make sure we actually know that |
405 | // this process supports |
406 | // the Itanium ABI. |
407 | if (language == eLanguageTypeC_plus_plus || |
408 | language == eLanguageTypeC_plus_plus_03 || |
409 | language == eLanguageTypeC_plus_plus_11 || |
410 | language == eLanguageTypeC_plus_plus_14) |
411 | return new ItaniumABILanguageRuntime(process); |
412 | else |
413 | return nullptr; |
414 | } |
415 | |
416 | class CommandObjectMultiwordItaniumABI_Demangle : public CommandObjectParsed { |
417 | public: |
418 | CommandObjectMultiwordItaniumABI_Demangle(CommandInterpreter &interpreter) |
419 | : CommandObjectParsed( |
420 | interpreter, "demangle" , "Demangle a C++ mangled name." , |
421 | "language cplusplus demangle [<mangled-name> ...]" ) { |
422 | AddSimpleArgumentList(arg_type: eArgTypeSymbol, repetition_type: eArgRepeatPlus); |
423 | } |
424 | |
425 | ~CommandObjectMultiwordItaniumABI_Demangle() override = default; |
426 | |
427 | protected: |
428 | void DoExecute(Args &command, CommandReturnObject &result) override { |
429 | bool demangled_any = false; |
430 | bool error_any = false; |
431 | for (auto &entry : command.entries()) { |
432 | if (entry.ref().empty()) |
433 | continue; |
434 | |
435 | // the actual Mangled class should be strict about this, but on the |
436 | // command line if you're copying mangled names out of 'nm' on Darwin, |
437 | // they will come out with an extra underscore - be willing to strip this |
438 | // on behalf of the user. This is the moral equivalent of the -_/-n |
439 | // options to c++filt |
440 | auto name = entry.ref(); |
441 | if (name.starts_with(Prefix: "__Z" )) |
442 | name = name.drop_front(); |
443 | |
444 | Mangled mangled(name); |
445 | if (mangled.GuessLanguage() == lldb::eLanguageTypeC_plus_plus) { |
446 | ConstString demangled(mangled.GetDisplayDemangledName()); |
447 | demangled_any = true; |
448 | result.AppendMessageWithFormat(format: "%s ---> %s\n" , entry.c_str(), |
449 | demangled.GetCString()); |
450 | } else { |
451 | error_any = true; |
452 | result.AppendErrorWithFormat(format: "%s is not a valid C++ mangled name\n" , |
453 | entry.ref().str().c_str()); |
454 | } |
455 | } |
456 | |
457 | result.SetStatus( |
458 | error_any ? lldb::eReturnStatusFailed |
459 | : (demangled_any ? lldb::eReturnStatusSuccessFinishResult |
460 | : lldb::eReturnStatusSuccessFinishNoResult)); |
461 | } |
462 | }; |
463 | |
464 | class CommandObjectMultiwordItaniumABI : public CommandObjectMultiword { |
465 | public: |
466 | CommandObjectMultiwordItaniumABI(CommandInterpreter &interpreter) |
467 | : CommandObjectMultiword( |
468 | interpreter, "cplusplus" , |
469 | "Commands for operating on the C++ language runtime." , |
470 | "cplusplus <subcommand> [<subcommand-options>]" ) { |
471 | LoadSubCommand( |
472 | cmd_name: "demangle" , |
473 | command_obj: CommandObjectSP( |
474 | new CommandObjectMultiwordItaniumABI_Demangle(interpreter))); |
475 | } |
476 | |
477 | ~CommandObjectMultiwordItaniumABI() override = default; |
478 | }; |
479 | |
480 | void ItaniumABILanguageRuntime::Initialize() { |
481 | PluginManager::RegisterPlugin( |
482 | name: GetPluginNameStatic(), description: "Itanium ABI for the C++ language" , create_callback: CreateInstance, |
483 | command_callback: [](CommandInterpreter &interpreter) -> lldb::CommandObjectSP { |
484 | return CommandObjectSP( |
485 | new CommandObjectMultiwordItaniumABI(interpreter)); |
486 | }); |
487 | } |
488 | |
489 | void ItaniumABILanguageRuntime::Terminate() { |
490 | PluginManager::UnregisterPlugin(create_callback: CreateInstance); |
491 | } |
492 | |
493 | BreakpointResolverSP ItaniumABILanguageRuntime::CreateExceptionResolver( |
494 | const BreakpointSP &bkpt, bool catch_bp, bool throw_bp) { |
495 | return CreateExceptionResolver(bkpt, catch_bp, throw_bp, for_expressions: false); |
496 | } |
497 | |
498 | BreakpointResolverSP ItaniumABILanguageRuntime::CreateExceptionResolver( |
499 | const BreakpointSP &bkpt, bool catch_bp, bool throw_bp, |
500 | bool for_expressions) { |
501 | // One complication here is that most users DON'T want to stop at |
502 | // __cxa_allocate_expression, but until we can do anything better with |
503 | // predicting unwinding the expression parser does. So we have two forms of |
504 | // the exception breakpoints, one for expressions that leaves out |
505 | // __cxa_allocate_exception, and one that includes it. The |
506 | // SetExceptionBreakpoints does the latter, the CreateExceptionBreakpoint in |
507 | // the runtime the former. |
508 | static const char *g_catch_name = "__cxa_begin_catch" ; |
509 | static const char *g_throw_name1 = "__cxa_throw" ; |
510 | static const char *g_throw_name2 = "__cxa_rethrow" ; |
511 | static const char *g_exception_throw_name = "__cxa_allocate_exception" ; |
512 | std::vector<const char *> exception_names; |
513 | exception_names.reserve(n: 4); |
514 | if (catch_bp) |
515 | exception_names.push_back(x: g_catch_name); |
516 | |
517 | if (throw_bp) { |
518 | exception_names.push_back(x: g_throw_name1); |
519 | exception_names.push_back(x: g_throw_name2); |
520 | } |
521 | |
522 | if (for_expressions) |
523 | exception_names.push_back(x: g_exception_throw_name); |
524 | |
525 | BreakpointResolverSP resolver_sp(new BreakpointResolverName( |
526 | bkpt, exception_names.data(), exception_names.size(), |
527 | eFunctionNameTypeBase, eLanguageTypeUnknown, 0, eLazyBoolNo)); |
528 | |
529 | return resolver_sp; |
530 | } |
531 | |
532 | lldb::SearchFilterSP ItaniumABILanguageRuntime::CreateExceptionSearchFilter() { |
533 | Target &target = m_process->GetTarget(); |
534 | |
535 | FileSpecList filter_modules; |
536 | if (target.GetArchitecture().GetTriple().getVendor() == llvm::Triple::Apple) { |
537 | // Limit the number of modules that are searched for these breakpoints for |
538 | // Apple binaries. |
539 | filter_modules.EmplaceBack(args: "libc++abi.dylib" ); |
540 | filter_modules.EmplaceBack(args: "libSystem.B.dylib" ); |
541 | filter_modules.EmplaceBack(args: "libc++abi.1.0.dylib" ); |
542 | filter_modules.EmplaceBack(args: "libc++abi.1.dylib" ); |
543 | } |
544 | return target.GetSearchFilterForModuleList(containingModuleList: &filter_modules); |
545 | } |
546 | |
547 | lldb::BreakpointSP ItaniumABILanguageRuntime::CreateExceptionBreakpoint( |
548 | bool catch_bp, bool throw_bp, bool for_expressions, bool is_internal) { |
549 | Target &target = m_process->GetTarget(); |
550 | FileSpecList filter_modules; |
551 | BreakpointResolverSP exception_resolver_sp = |
552 | CreateExceptionResolver(bkpt: nullptr, catch_bp, throw_bp, for_expressions); |
553 | SearchFilterSP filter_sp(CreateExceptionSearchFilter()); |
554 | const bool hardware = false; |
555 | const bool resolve_indirect_functions = false; |
556 | return target.CreateBreakpoint(filter_sp, resolver_sp&: exception_resolver_sp, internal: is_internal, |
557 | request_hardware: hardware, resolve_indirect_symbols: resolve_indirect_functions); |
558 | } |
559 | |
560 | void ItaniumABILanguageRuntime::SetExceptionBreakpoints() { |
561 | if (!m_process) |
562 | return; |
563 | |
564 | const bool catch_bp = false; |
565 | const bool throw_bp = true; |
566 | const bool is_internal = true; |
567 | const bool for_expressions = true; |
568 | |
569 | // For the exception breakpoints set by the Expression parser, we'll be a |
570 | // little more aggressive and stop at exception allocation as well. |
571 | |
572 | if (m_cxx_exception_bp_sp) { |
573 | m_cxx_exception_bp_sp->SetEnabled(true); |
574 | } else { |
575 | m_cxx_exception_bp_sp = CreateExceptionBreakpoint( |
576 | catch_bp, throw_bp, for_expressions, is_internal); |
577 | if (m_cxx_exception_bp_sp) |
578 | m_cxx_exception_bp_sp->SetBreakpointKind("c++ exception" ); |
579 | } |
580 | } |
581 | |
582 | void ItaniumABILanguageRuntime::ClearExceptionBreakpoints() { |
583 | if (!m_process) |
584 | return; |
585 | |
586 | if (m_cxx_exception_bp_sp) { |
587 | m_cxx_exception_bp_sp->SetEnabled(false); |
588 | } |
589 | } |
590 | |
591 | bool ItaniumABILanguageRuntime::ExceptionBreakpointsAreSet() { |
592 | return m_cxx_exception_bp_sp && m_cxx_exception_bp_sp->IsEnabled(); |
593 | } |
594 | |
595 | bool ItaniumABILanguageRuntime::ExceptionBreakpointsExplainStop( |
596 | lldb::StopInfoSP stop_reason) { |
597 | if (!m_process) |
598 | return false; |
599 | |
600 | if (!stop_reason || stop_reason->GetStopReason() != eStopReasonBreakpoint) |
601 | return false; |
602 | |
603 | uint64_t break_site_id = stop_reason->GetValue(); |
604 | return m_process->GetBreakpointSiteList().StopPointSiteContainsBreakpoint( |
605 | break_site_id, bp_id: m_cxx_exception_bp_sp->GetID()); |
606 | } |
607 | |
608 | ValueObjectSP ItaniumABILanguageRuntime::GetExceptionObjectForThread( |
609 | ThreadSP thread_sp) { |
610 | if (!thread_sp->SafeToCallFunctions()) |
611 | return {}; |
612 | |
613 | TypeSystemClangSP scratch_ts_sp = |
614 | ScratchTypeSystemClang::GetForTarget(target&: m_process->GetTarget()); |
615 | if (!scratch_ts_sp) |
616 | return {}; |
617 | |
618 | CompilerType voidstar = |
619 | scratch_ts_sp->GetBasicType(type: eBasicTypeVoid).GetPointerType(); |
620 | |
621 | DiagnosticManager diagnostics; |
622 | ExecutionContext exe_ctx; |
623 | EvaluateExpressionOptions options; |
624 | |
625 | options.SetUnwindOnError(true); |
626 | options.SetIgnoreBreakpoints(true); |
627 | options.SetStopOthers(true); |
628 | options.SetTimeout(m_process->GetUtilityExpressionTimeout()); |
629 | options.SetTryAllThreads(false); |
630 | thread_sp->CalculateExecutionContext(exe_ctx); |
631 | |
632 | const ModuleList &modules = m_process->GetTarget().GetImages(); |
633 | SymbolContextList contexts; |
634 | SymbolContext context; |
635 | |
636 | modules.FindSymbolsWithNameAndType( |
637 | name: ConstString("__cxa_current_exception_type" ), symbol_type: eSymbolTypeCode, sc_list&: contexts); |
638 | contexts.GetContextAtIndex(idx: 0, sc&: context); |
639 | if (!context.symbol) { |
640 | return {}; |
641 | } |
642 | Address addr = context.symbol->GetAddress(); |
643 | |
644 | Status error; |
645 | FunctionCaller *function_caller = |
646 | m_process->GetTarget().GetFunctionCallerForLanguage( |
647 | language: eLanguageTypeC, return_type: voidstar, function_address: addr, arg_value_list: ValueList(), name: "caller" , error); |
648 | |
649 | ExpressionResults func_call_ret; |
650 | Value results; |
651 | func_call_ret = function_caller->ExecuteFunction(exe_ctx, args_addr_ptr: nullptr, options, |
652 | diagnostic_manager&: diagnostics, results); |
653 | if (func_call_ret != eExpressionCompleted || !error.Success()) { |
654 | return ValueObjectSP(); |
655 | } |
656 | |
657 | size_t ptr_size = m_process->GetAddressByteSize(); |
658 | addr_t result_ptr = results.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); |
659 | addr_t exception_addr = |
660 | m_process->ReadPointerFromMemory(vm_addr: result_ptr - ptr_size, error); |
661 | |
662 | if (!error.Success()) { |
663 | return ValueObjectSP(); |
664 | } |
665 | |
666 | lldb_private::formatters::InferiorSizedWord exception_isw(exception_addr, |
667 | *m_process); |
668 | ValueObjectSP exception = ValueObject::CreateValueObjectFromData( |
669 | name: "exception" , data: exception_isw.GetAsData(byte_order: m_process->GetByteOrder()), exe_ctx, |
670 | type: voidstar); |
671 | ValueObjectSP dyn_exception |
672 | = exception->GetDynamicValue(valueType: eDynamicDontRunTarget); |
673 | // If we succeed in making a dynamic value, return that: |
674 | if (dyn_exception) |
675 | return dyn_exception; |
676 | |
677 | return exception; |
678 | } |
679 | |
680 | TypeAndOrName ItaniumABILanguageRuntime::GetDynamicTypeInfo( |
681 | const lldb_private::Address &vtable_addr) { |
682 | std::lock_guard<std::mutex> locker(m_mutex); |
683 | DynamicTypeCache::const_iterator pos = m_dynamic_type_map.find(x: vtable_addr); |
684 | if (pos == m_dynamic_type_map.end()) |
685 | return TypeAndOrName(); |
686 | else |
687 | return pos->second; |
688 | } |
689 | |
690 | void ItaniumABILanguageRuntime::SetDynamicTypeInfo( |
691 | const lldb_private::Address &vtable_addr, const TypeAndOrName &type_info) { |
692 | std::lock_guard<std::mutex> locker(m_mutex); |
693 | m_dynamic_type_map[vtable_addr] = type_info; |
694 | } |
695 | |