| //===-- Symbol.cpp --------------------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "lldb/Symbol/Symbol.h" | |
| #include "lldb/Core/Address.h" | |
| #include "lldb/Core/Debugger.h" | |
| #include "lldb/Core/Module.h" | |
| #include "lldb/Core/ModuleSpec.h" | |
| #include "lldb/Core/Section.h" | |
| #include "lldb/Symbol/Function.h" | |
| #include "lldb/Symbol/ObjectFile.h" | |
| #include "lldb/Symbol/SymbolVendor.h" | |
| #include "lldb/Symbol/Symtab.h" | |
| #include "lldb/Target/Process.h" | |
| #include "lldb/Target/Target.h" | |
| #include "lldb/Utility/DataEncoder.h" | |
| #include "lldb/Utility/Stream.h" | |
| #include "llvm/ADT/StringSwitch.h" | |
| using namespace lldb; | |
| using namespace lldb_private; | |
| Symbol::Symbol() | |
| : SymbolContextScope(), m_type_data_resolved(false), m_is_synthetic(false), | |
| m_is_debug(false), m_is_external(false), m_size_is_sibling(false), | |
| m_size_is_synthesized(false), m_size_is_valid(false), | |
| m_demangled_is_synthesized(false), m_contains_linker_annotations(false), | |
| m_is_weak(false), m_type(eSymbolTypeInvalid), m_mangled(), | |
| m_addr_range() {} | |
| Symbol::Symbol(uint32_t symID, llvm::StringRef name, SymbolType type, | |
| bool external, bool is_debug, bool is_trampoline, | |
| bool is_artificial, const lldb::SectionSP §ion_sp, | |
| addr_t offset, addr_t size, bool size_is_valid, | |
| bool contains_linker_annotations, uint32_t flags) | |
| : SymbolContextScope(), m_uid(symID), m_type_data_resolved(false), | |
| m_is_synthetic(is_artificial), m_is_debug(is_debug), | |
| m_is_external(external), m_size_is_sibling(false), | |
| m_size_is_synthesized(false), m_size_is_valid(size_is_valid || size > 0), | |
| m_demangled_is_synthesized(false), | |
| m_contains_linker_annotations(contains_linker_annotations), | |
| m_is_weak(false), m_type(type), m_mangled(name), | |
| m_addr_range(section_sp, offset, size), m_flags(flags) {} | |
| Symbol::Symbol(uint32_t symID, const Mangled &mangled, SymbolType type, | |
| bool external, bool is_debug, bool is_trampoline, | |
| bool is_artificial, const AddressRange &range, | |
| bool size_is_valid, bool contains_linker_annotations, | |
| uint32_t flags) | |
| : SymbolContextScope(), m_uid(symID), m_type_data_resolved(false), | |
| m_is_synthetic(is_artificial), m_is_debug(is_debug), | |
| m_is_external(external), m_size_is_sibling(false), | |
| m_size_is_synthesized(false), | |
| m_size_is_valid(size_is_valid || range.GetByteSize() > 0), | |
| m_demangled_is_synthesized(false), | |
| m_contains_linker_annotations(contains_linker_annotations), | |
| m_is_weak(false), m_type(type), m_mangled(mangled), m_addr_range(range), | |
| m_flags(flags) {} | |
| Symbol::Symbol(const Symbol &rhs) | |
| : SymbolContextScope(rhs), m_uid(rhs.m_uid), m_type_data(rhs.m_type_data), | |
| m_type_data_resolved(rhs.m_type_data_resolved), | |
| m_is_synthetic(rhs.m_is_synthetic), m_is_debug(rhs.m_is_debug), | |
| m_is_external(rhs.m_is_external), | |
| m_size_is_sibling(rhs.m_size_is_sibling), m_size_is_synthesized(false), | |
| m_size_is_valid(rhs.m_size_is_valid), | |
| m_demangled_is_synthesized(rhs.m_demangled_is_synthesized), | |
| m_contains_linker_annotations(rhs.m_contains_linker_annotations), | |
| m_is_weak(rhs.m_is_weak), m_type(rhs.m_type), m_mangled(rhs.m_mangled), | |
| m_addr_range(rhs.m_addr_range), m_flags(rhs.m_flags) {} | |
| const Symbol &Symbol::operator=(const Symbol &rhs) { | |
| if (this != &rhs) { | |
| SymbolContextScope::operator=(rhs); | |
| m_uid = rhs.m_uid; | |
| m_type_data = rhs.m_type_data; | |
| m_type_data_resolved = rhs.m_type_data_resolved; | |
| m_is_synthetic = rhs.m_is_synthetic; | |
| m_is_debug = rhs.m_is_debug; | |
| m_is_external = rhs.m_is_external; | |
| m_size_is_sibling = rhs.m_size_is_sibling; | |
| m_size_is_synthesized = rhs.m_size_is_synthesized; | |
| m_size_is_valid = rhs.m_size_is_valid; | |
| m_demangled_is_synthesized = rhs.m_demangled_is_synthesized; | |
| m_contains_linker_annotations = rhs.m_contains_linker_annotations; | |
| m_is_weak = rhs.m_is_weak; | |
| m_type = rhs.m_type; | |
| m_mangled = rhs.m_mangled; | |
| m_addr_range = rhs.m_addr_range; | |
| m_flags = rhs.m_flags; | |
| } | |
| return *this; | |
| } | |
| llvm::Expected<Symbol> Symbol::FromJSON(const JSONSymbol &symbol, | |
| SectionList *section_list) { | |
| if (!section_list) | |
| return llvm::createStringError("no section list provided"); | |
| if (!symbol.value && !symbol.address) | |
| return llvm::createStringError( | |
| "symbol must contain either a value or an address"); | |
| if (symbol.value && symbol.address) | |
| return llvm::createStringError( | |
| "symbol cannot contain both a value and an address"); | |
| const uint64_t size = symbol.size.value_or(0); | |
| const bool is_artificial = false; | |
| const bool is_trampoline = false; | |
| const bool is_debug = false; | |
| const bool external = false; | |
| const bool size_is_valid = symbol.size.has_value(); | |
| const bool contains_linker_annotations = false; | |
| const uint32_t flags = 0; | |
| if (symbol.address) { | |
| if (SectionSP section_sp = | |
| section_list->FindSectionContainingFileAddress(*symbol.address)) { | |
| const uint64_t offset = *symbol.address - section_sp->GetFileAddress(); | |
| return Symbol(symbol.id.value_or(0), Mangled(symbol.name), | |
| symbol.type.value_or(eSymbolTypeAny), external, is_debug, | |
| is_trampoline, is_artificial, | |
| AddressRange(section_sp, offset, size), size_is_valid, | |
| contains_linker_annotations, flags); | |
| } | |
| return llvm::createStringError( | |
| llvm::formatv("no section found for address: {0:x}", *symbol.address)); | |
| } | |
| // Absolute symbols encode the integer value in the m_offset of the | |
| // AddressRange object and the section is set to nothing. | |
| return Symbol(symbol.id.value_or(0), Mangled(symbol.name), | |
| symbol.type.value_or(eSymbolTypeAny), external, is_debug, | |
| is_trampoline, is_artificial, | |
| AddressRange(SectionSP(), *symbol.value, size), size_is_valid, | |
| contains_linker_annotations, flags); | |
| } | |
| void Symbol::Clear() { | |
| m_uid = UINT32_MAX; | |
| m_mangled.Clear(); | |
| m_type_data = 0; | |
| m_type_data_resolved = false; | |
| m_is_synthetic = false; | |
| m_is_debug = false; | |
| m_is_external = false; | |
| m_size_is_sibling = false; | |
| m_size_is_synthesized = false; | |
| m_size_is_valid = false; | |
| m_demangled_is_synthesized = false; | |
| m_contains_linker_annotations = false; | |
| m_is_weak = false; | |
| m_type = eSymbolTypeInvalid; | |
| m_flags = 0; | |
| m_addr_range.Clear(); | |
| } | |
| bool Symbol::ValueIsAddress() const { | |
| return (bool)m_addr_range.GetBaseAddress().GetSection(); | |
| } | |
| ConstString Symbol::GetDisplayName() const { | |
| return GetMangled().GetDisplayDemangledName(); | |
| } | |
| ConstString Symbol::GetReExportedSymbolName() const { | |
| if (m_type == eSymbolTypeReExported) { | |
| // For eSymbolTypeReExported, the "const char *" from a ConstString is used | |
| // as the offset in the address range base address. We can then make this | |
| // back into a string that is the re-exported name. | |
| intptr_t str_ptr = m_addr_range.GetBaseAddress().GetOffset(); | |
| if (str_ptr != 0) | |
| return ConstString((const char *)str_ptr); | |
| else | |
| return GetName(); | |
| } | |
| return ConstString(); | |
| } | |
| FileSpec Symbol::GetReExportedSymbolSharedLibrary() const { | |
| if (m_type == eSymbolTypeReExported) { | |
| // For eSymbolTypeReExported, the "const char *" from a ConstString is used | |
| // as the offset in the address range base address. We can then make this | |
| // back into a string that is the re-exported name. | |
| intptr_t str_ptr = m_addr_range.GetByteSize(); | |
| if (str_ptr != 0) | |
| return FileSpec((const char *)str_ptr); | |
| } | |
| return FileSpec(); | |
| } | |
| void Symbol::SetReExportedSymbolName(ConstString name) { | |
| SetType(eSymbolTypeReExported); | |
| // For eSymbolTypeReExported, the "const char *" from a ConstString is used | |
| // as the offset in the address range base address. | |
| m_addr_range.GetBaseAddress().SetOffset((uintptr_t)name.GetCString()); | |
| } | |
| bool Symbol::SetReExportedSymbolSharedLibrary(const FileSpec &fspec) { | |
| if (m_type == eSymbolTypeReExported) { | |
| // For eSymbolTypeReExported, the "const char *" from a ConstString is used | |
| // as the offset in the address range base address. | |
| m_addr_range.SetByteSize( | |
| (uintptr_t)ConstString(fspec.GetPath()).GetCString()); | |
| return true; | |
| } | |
| return false; | |
| } | |
| uint32_t Symbol::GetSiblingIndex() const { | |
| return m_size_is_sibling ? m_addr_range.GetByteSize() : UINT32_MAX; | |
| } | |
| bool Symbol::IsTrampoline() const { return m_type == eSymbolTypeTrampoline; } | |
| bool Symbol::IsIndirect() const { return m_type == eSymbolTypeResolver; } | |
| void Symbol::GetDescription( | |
| Stream *s, lldb::DescriptionLevel level, Target *target, | |
| std::optional<Stream::HighlightSettings> settings) const { | |
| s->Printf("id = {0x%8.8x}", m_uid); | |
| if (m_addr_range.GetBaseAddress().GetSection()) { | |
| if (ValueIsAddress()) { | |
| const lldb::addr_t byte_size = GetByteSize(); | |
| if (byte_size > 0) { | |
| s->PutCString(", range = "); | |
| m_addr_range.Dump(s, target, Address::DumpStyleLoadAddress, | |
| Address::DumpStyleFileAddress); | |
| } else { | |
| s->PutCString(", address = "); | |
| m_addr_range.GetBaseAddress().Dump(s, target, | |
| Address::DumpStyleLoadAddress, | |
| Address::DumpStyleFileAddress); | |
| } | |
| } else | |
| s->Printf(", value = 0x%16.16" PRIx64, | |
| m_addr_range.GetBaseAddress().GetOffset()); | |
| } else { | |
| if (m_size_is_sibling) | |
| s->Printf(", sibling = %5" PRIu64, | |
| m_addr_range.GetBaseAddress().GetOffset()); | |
| else | |
| s->Printf(", value = 0x%16.16" PRIx64, | |
| m_addr_range.GetBaseAddress().GetOffset()); | |
| } | |
| if (ConstString demangled = m_mangled.GetDemangledName()) { | |
| s->PutCString(", name=\""); | |
| s->PutCStringColorHighlighted(demangled.GetStringRef(), settings); | |
| s->PutCString("\""); | |
| } | |
| if (ConstString mangled_name = m_mangled.GetMangledName()) { | |
| s->PutCString(", mangled=\""); | |
| s->PutCStringColorHighlighted(mangled_name.GetStringRef(), settings); | |
| s->PutCString("\""); | |
| } | |
| } | |
| void Symbol::Dump(Stream *s, Target *target, uint32_t index, | |
| Mangled::NamePreference name_preference) const { | |
| s->Printf("[%5u] %6u %c%c%c %-15s ", index, GetID(), m_is_debug ? 'D' : ' ', | |
| m_is_synthetic ? 'S' : ' ', m_is_external ? 'X' : ' ', | |
| GetTypeAsString()); | |
| // Make sure the size of the symbol is up to date before dumping | |
| GetByteSize(); | |
| ConstString name = GetMangled().GetName(name_preference); | |
| if (ValueIsAddress()) { | |
| if (!m_addr_range.GetBaseAddress().Dump(s, nullptr, | |
| Address::DumpStyleFileAddress)) | |
| s->Printf("%*s", 18, ""); | |
| s->PutChar(' '); | |
| if (!m_addr_range.GetBaseAddress().Dump(s, target, | |
| Address::DumpStyleLoadAddress)) | |
| s->Printf("%*s", 18, ""); | |
| const char *format = m_size_is_sibling ? " Sibling -> [%5llu] 0x%8.8x %s\n" | |
| : " 0x%16.16" PRIx64 " 0x%8.8x %s\n"; | |
| s->Printf(format, GetByteSize(), m_flags, name.AsCString("")); | |
| } else if (m_type == eSymbolTypeReExported) { | |
| s->Printf( | |
| " 0x%8.8x %s", | |
| m_flags, name.AsCString("")); | |
| ConstString reexport_name = GetReExportedSymbolName(); | |
| intptr_t shlib = m_addr_range.GetByteSize(); | |
| if (shlib) | |
| s->Printf(" -> %s`%s\n", (const char *)shlib, reexport_name.GetCString()); | |
| else | |
| s->Printf(" -> %s\n", reexport_name.GetCString()); | |
| } else { | |
| const char *format = | |
| m_size_is_sibling | |
| ? "0x%16.16" PRIx64 | |
| " Sibling -> [%5llu] 0x%8.8x %s\n" | |
| : "0x%16.16" PRIx64 " 0x%16.16" PRIx64 | |
| " 0x%8.8x %s\n"; | |
| s->Printf(format, m_addr_range.GetBaseAddress().GetOffset(), GetByteSize(), | |
| m_flags, name.AsCString("")); | |
| } | |
| } | |
| uint32_t Symbol::GetPrologueByteSize() { | |
| if (m_type == eSymbolTypeCode || m_type == eSymbolTypeResolver) { | |
| if (!m_type_data_resolved) { | |
| m_type_data_resolved = true; | |
| const Address &base_address = m_addr_range.GetBaseAddress(); | |
| Function *function = base_address.CalculateSymbolContextFunction(); | |
| if (function) { | |
| // Functions have line entries which can also potentially have end of | |
| // prologue information. So if this symbol points to a function, use | |
| // the prologue information from there. | |
| m_type_data = function->GetPrologueByteSize(); | |
| } else { | |
| ModuleSP module_sp(base_address.GetModule()); | |
| SymbolContext sc; | |
| if (module_sp) { | |
| uint32_t resolved_flags = module_sp->ResolveSymbolContextForAddress( | |
| base_address, eSymbolContextLineEntry, sc); | |
| if (resolved_flags & eSymbolContextLineEntry) { | |
| // Default to the end of the first line entry. | |
| m_type_data = sc.line_entry.range.GetByteSize(); | |
| // Set address for next line. | |
| Address addr(base_address); | |
| addr.Slide(m_type_data); | |
| // Check the first few instructions and look for one that has a | |
| // line number that is different than the first entry. This is also | |
| // done in Function::GetPrologueByteSize(). | |
| uint16_t total_offset = m_type_data; | |
| for (int idx = 0; idx < 6; ++idx) { | |
| SymbolContext sc_temp; | |
| resolved_flags = module_sp->ResolveSymbolContextForAddress( | |
| addr, eSymbolContextLineEntry, sc_temp); | |
| // Make sure we got line number information... | |
| if (!(resolved_flags & eSymbolContextLineEntry)) | |
| break; | |
| // If this line number is different than our first one, use it | |
| // and we're done. | |
| if (sc_temp.line_entry.line != sc.line_entry.line) { | |
| m_type_data = total_offset; | |
| break; | |
| } | |
| // Slide addr up to the next line address. | |
| addr.Slide(sc_temp.line_entry.range.GetByteSize()); | |
| total_offset += sc_temp.line_entry.range.GetByteSize(); | |
| // If we've gone too far, bail out. | |
| if (total_offset >= m_addr_range.GetByteSize()) | |
| break; | |
| } | |
| // Sanity check - this may be a function in the middle of code that | |
| // has debug information, but not for this symbol. So the line | |
| // entries surrounding us won't lie inside our function. In that | |
| // case, the line entry will be bigger than we are, so we do that | |
| // quick check and if that is true, we just return 0. | |
| if (m_type_data >= m_addr_range.GetByteSize()) | |
| m_type_data = 0; | |
| } else { | |
| // TODO: expose something in Process to figure out the | |
| // size of a function prologue. | |
| m_type_data = 0; | |
| } | |
| } | |
| } | |
| } | |
| return m_type_data; | |
| } | |
| return 0; | |
| } | |
| bool Symbol::Compare(ConstString name, SymbolType type) const { | |
| if (type == eSymbolTypeAny || m_type == type) { | |
| const Mangled &mangled = GetMangled(); | |
| return mangled.GetMangledName() == name || | |
| mangled.GetDemangledName() == name; | |
| } | |
| return false; | |
| } | |
| const char *Symbol::GetTypeAsString() const { | |
| return GetTypeAsString(static_cast<lldb::SymbolType>(m_type)); | |
| } | |
| void Symbol::CalculateSymbolContext(SymbolContext *sc) { | |
| // Symbols can reconstruct the symbol and the module in the symbol context | |
| sc->symbol = this; | |
| if (ValueIsAddress()) | |
| sc->module_sp = GetAddressRef().GetModule(); | |
| else | |
| sc->module_sp.reset(); | |
| } | |
| ModuleSP Symbol::CalculateSymbolContextModule() { | |
| if (ValueIsAddress()) | |
| return GetAddressRef().GetModule(); | |
| return ModuleSP(); | |
| } | |
| Symbol *Symbol::CalculateSymbolContextSymbol() { return this; } | |
| void Symbol::DumpSymbolContext(Stream *s) { | |
| bool dumped_module = false; | |
| if (ValueIsAddress()) { | |
| ModuleSP module_sp(GetAddressRef().GetModule()); | |
| if (module_sp) { | |
| dumped_module = true; | |
| module_sp->DumpSymbolContext(s); | |
| } | |
| } | |
| if (dumped_module) | |
| s->PutCString(", "); | |
| s->Printf("Symbol{0x%8.8x}", GetID()); | |
| } | |
| lldb::addr_t Symbol::GetByteSize() const { return m_addr_range.GetByteSize(); } | |
| Symbol *Symbol::ResolveReExportedSymbolInModuleSpec( | |
| Target &target, ConstString &reexport_name, ModuleSpec &module_spec, | |
| ModuleList &seen_modules) const { | |
| ModuleSP module_sp; | |
| if (module_spec.GetFileSpec()) { | |
| // Try searching for the module file spec first using the full path | |
| module_sp = target.GetImages().FindFirstModule(module_spec); | |
| if (!module_sp) { | |
| // Next try and find the module by basename in case environment variables | |
| // or other runtime trickery causes shared libraries to be loaded from | |
| // alternate paths | |
| module_spec.GetFileSpec().ClearDirectory(); | |
| module_sp = target.GetImages().FindFirstModule(module_spec); | |
| } | |
| } | |
| if (module_sp) { | |
| // There should not be cycles in the reexport list, but we don't want to | |
| // crash if there are so make sure we haven't seen this before: | |
| if (!seen_modules.AppendIfNeeded(module_sp)) | |
| return nullptr; | |
| lldb_private::SymbolContextList sc_list; | |
| module_sp->FindSymbolsWithNameAndType(reexport_name, eSymbolTypeAny, | |
| sc_list); | |
| for (const SymbolContext &sc : sc_list) { | |
| if (sc.symbol->IsExternal()) | |
| return sc.symbol; | |
| } | |
| // If we didn't find the symbol in this module, it may be because this | |
| // module re-exports some whole other library. We have to search those as | |
| // well: | |
| seen_modules.Append(module_sp); | |
| FileSpecList reexported_libraries = | |
| module_sp->GetObjectFile()->GetReExportedLibraries(); | |
| size_t num_reexported_libraries = reexported_libraries.GetSize(); | |
| for (size_t idx = 0; idx < num_reexported_libraries; idx++) { | |
| ModuleSpec reexported_module_spec; | |
| reexported_module_spec.GetFileSpec() = | |
| reexported_libraries.GetFileSpecAtIndex(idx); | |
| Symbol *result_symbol = ResolveReExportedSymbolInModuleSpec( | |
| target, reexport_name, reexported_module_spec, seen_modules); | |
| if (result_symbol) | |
| return result_symbol; | |
| } | |
| } | |
| return nullptr; | |
| } | |
| Symbol *Symbol::ResolveReExportedSymbol(Target &target) const { | |
| ConstString reexport_name(GetReExportedSymbolName()); | |
| if (reexport_name) { | |
| ModuleSpec module_spec; | |
| ModuleList seen_modules; | |
| module_spec.GetFileSpec() = GetReExportedSymbolSharedLibrary(); | |
| if (module_spec.GetFileSpec()) { | |
| return ResolveReExportedSymbolInModuleSpec(target, reexport_name, | |
| module_spec, seen_modules); | |
| } | |
| } | |
| return nullptr; | |
| } | |
| lldb::addr_t Symbol::GetFileAddress() const { | |
| if (ValueIsAddress()) | |
| return GetAddressRef().GetFileAddress(); | |
| else | |
| return LLDB_INVALID_ADDRESS; | |
| } | |
| lldb::addr_t Symbol::GetLoadAddress(Target *target) const { | |
| if (ValueIsAddress()) | |
| return GetAddressRef().GetLoadAddress(target); | |
| else | |
| return LLDB_INVALID_ADDRESS; | |
| } | |
| ConstString Symbol::GetName() const { return GetMangled().GetName(); } | |
| ConstString Symbol::GetNameNoArguments() const { | |
| return GetMangled().GetName(Mangled::ePreferDemangledWithoutArguments); | |
| } | |
| lldb::addr_t Symbol::ResolveCallableAddress(Target &target) const { | |
| if (GetType() == lldb::eSymbolTypeUndefined) | |
| return LLDB_INVALID_ADDRESS; | |
| Address func_so_addr; | |
| bool is_indirect = IsIndirect(); | |
| if (GetType() == eSymbolTypeReExported) { | |
| Symbol *reexported_symbol = ResolveReExportedSymbol(target); | |
| if (reexported_symbol) { | |
| func_so_addr = reexported_symbol->GetAddress(); | |
| is_indirect = reexported_symbol->IsIndirect(); | |
| } | |
| } else { | |
| func_so_addr = GetAddress(); | |
| is_indirect = IsIndirect(); | |
| } | |
| if (func_so_addr.IsValid()) { | |
| if (!target.GetProcessSP() && is_indirect) { | |
| // can't resolve indirect symbols without calling a function... | |
| return LLDB_INVALID_ADDRESS; | |
| } | |
| lldb::addr_t load_addr = | |
| func_so_addr.GetCallableLoadAddress(&target, is_indirect); | |
| if (load_addr != LLDB_INVALID_ADDRESS) { | |
| return load_addr; | |
| } | |
| } | |
| return LLDB_INVALID_ADDRESS; | |
| } | |
| lldb::DisassemblerSP Symbol::GetInstructions(const ExecutionContext &exe_ctx, | |
| const char *flavor, | |
| bool prefer_file_cache) { | |
| ModuleSP module_sp(m_addr_range.GetBaseAddress().GetModule()); | |
| if (module_sp && exe_ctx.HasTargetScope()) { | |
| return Disassembler::DisassembleRange( | |
| module_sp->GetArchitecture(), nullptr, flavor, nullptr, nullptr, | |
| exe_ctx.GetTargetRef(), m_addr_range, !prefer_file_cache); | |
| } | |
| return lldb::DisassemblerSP(); | |
| } | |
| bool Symbol::GetDisassembly(const ExecutionContext &exe_ctx, const char *flavor, | |
| bool prefer_file_cache, Stream &strm) { | |
| lldb::DisassemblerSP disassembler_sp = | |
| GetInstructions(exe_ctx, flavor, prefer_file_cache); | |
| if (disassembler_sp) { | |
| const bool show_address = true; | |
| const bool show_bytes = false; | |
| const bool show_control_flow_kind = false; | |
| disassembler_sp->GetInstructionList().Dump( | |
| &strm, show_address, show_bytes, show_control_flow_kind, &exe_ctx); | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool Symbol::ContainsFileAddress(lldb::addr_t file_addr) const { | |
| return m_addr_range.ContainsFileAddress(file_addr); | |
| } | |
| bool Symbol::IsSyntheticWithAutoGeneratedName() const { | |
| if (!IsSynthetic()) | |
| return false; | |
| if (!m_mangled) | |
| return true; | |
| ConstString demangled = m_mangled.GetDemangledName(); | |
| return demangled.GetStringRef().starts_with(GetSyntheticSymbolPrefix()); | |
| } | |
| void Symbol::SynthesizeNameIfNeeded() const { | |
| if (m_is_synthetic && !m_mangled) { | |
| // Synthetic symbol names don't mean anything, but they do uniquely | |
| // identify individual symbols so we give them a unique name. The name | |
| // starts with the synthetic symbol prefix, followed by a unique number. | |
| // Typically the UserID of a real symbol is the symbol table index of the | |
| // symbol in the object file's symbol table(s), so it will be the same | |
| // every time you read in the object file. We want the same persistence for | |
| // synthetic symbols so that users can identify them across multiple debug | |
| // sessions, to understand crashes in those symbols and to reliably set | |
| // breakpoints on them. | |
| llvm::SmallString<256> name; | |
| llvm::raw_svector_ostream os(name); | |
| os << GetSyntheticSymbolPrefix() | |
| << llvm::format_hex_no_prefix( | |
| m_addr_range.GetBaseAddress().GetFileAddress(), 0); | |
| m_mangled.SetDemangledName(ConstString(os.str())); | |
| } | |
| } | |
| bool Symbol::Decode(const DataExtractor &data, lldb::offset_t *offset_ptr, | |
| const SectionList *section_list, | |
| const StringTableReader &strtab) { | |
| if (!data.ValidOffsetForDataOfSize(*offset_ptr, 8)) | |
| return false; | |
| m_uid = data.GetU32(offset_ptr); | |
| m_type_data = data.GetU16(offset_ptr); | |
| const uint16_t bitfields = data.GetU16(offset_ptr); | |
| m_type_data_resolved = (1u << 15 & bitfields) != 0; | |
| m_is_synthetic = (1u << 14 & bitfields) != 0; | |
| m_is_debug = (1u << 13 & bitfields) != 0; | |
| m_is_external = (1u << 12 & bitfields) != 0; | |
| m_size_is_sibling = (1u << 11 & bitfields) != 0; | |
| m_size_is_synthesized = (1u << 10 & bitfields) != 0; | |
| m_size_is_valid = (1u << 9 & bitfields) != 0; | |
| m_demangled_is_synthesized = (1u << 8 & bitfields) != 0; | |
| m_contains_linker_annotations = (1u << 7 & bitfields) != 0; | |
| m_is_weak = (1u << 6 & bitfields) != 0; | |
| m_type = bitfields & 0x003f; | |
| if (!m_mangled.Decode(data, offset_ptr, strtab)) | |
| return false; | |
| if (!data.ValidOffsetForDataOfSize(*offset_ptr, 20)) | |
| return false; | |
| const bool is_addr = data.GetU8(offset_ptr) != 0; | |
| const uint64_t value = data.GetU64(offset_ptr); | |
| if (is_addr) { | |
| m_addr_range.GetBaseAddress().ResolveAddressUsingFileSections(value, | |
| section_list); | |
| } else { | |
| m_addr_range.GetBaseAddress().Clear(); | |
| m_addr_range.GetBaseAddress().SetOffset(value); | |
| } | |
| m_addr_range.SetByteSize(data.GetU64(offset_ptr)); | |
| m_flags = data.GetU32(offset_ptr); | |
| return true; | |
| } | |
| // If the size of Symbol has changed, the Encode and | |
| // Decode methods also likely need to be updated and | |
| // the DataFileCache version number in Symtab::Encode | |
| // will need to be incremented as well. | |
| #if __SIZEOF_POINTER__ == 8 | |
| static_assert(sizeof(lldb_private::Symbol) == 80, | |
| "Symbol size has changed, Symbol::Encode and Decode likely need " | |
| "to be updated"); | |
| #endif | |
| /// The encoding format for the symbol is as follows: | |
| /// | |
| /// uint32_t m_uid; | |
| /// uint16_t m_type_data; | |
| /// uint16_t bitfield_data; | |
| /// Mangled mangled; | |
| /// uint8_t is_addr; | |
| /// uint64_t file_addr_or_value; | |
| /// uint64_t size; | |
| /// uint32_t flags; | |
| /// | |
| /// The only tricky thing in this encoding is encoding all of the bits in the | |
| /// bitfields. We use a trick to store all bitfields as a 16 bit value and we | |
| /// do the same thing when decoding the symbol. There are test that ensure this | |
| /// encoding works for each individual bit. Everything else is very easy to | |
| /// store. | |
| void Symbol::Encode(DataEncoder &file, ConstStringTable &strtab) const { | |
| file.AppendU32(m_uid); | |
| file.AppendU16(m_type_data); | |
| uint16_t bitfields = m_type; | |
| if (m_type_data_resolved) | |
| bitfields |= 1u << 15; | |
| if (m_is_synthetic) | |
| bitfields |= 1u << 14; | |
| if (m_is_debug) | |
| bitfields |= 1u << 13; | |
| if (m_is_external) | |
| bitfields |= 1u << 12; | |
| if (m_size_is_sibling) | |
| bitfields |= 1u << 11; | |
| if (m_size_is_synthesized) | |
| bitfields |= 1u << 10; | |
| if (m_size_is_valid) | |
| bitfields |= 1u << 9; | |
| if (m_demangled_is_synthesized) | |
| bitfields |= 1u << 8; | |
| if (m_contains_linker_annotations) | |
| bitfields |= 1u << 7; | |
| if (m_is_weak) | |
| bitfields |= 1u << 6; | |
| file.AppendU16(bitfields); | |
| m_mangled.Encode(file, strtab); | |
| // A symbol's value might be an address, or it might be a constant. If the | |
| // symbol's base address doesn't have a section, then it is a constant value. | |
| // If it does have a section, we will encode the file address and re-resolve | |
| // the address when we decode it. | |
| bool is_addr = m_addr_range.GetBaseAddress().GetSection().get() != nullptr; | |
| file.AppendU8(is_addr); | |
| file.AppendU64(m_addr_range.GetBaseAddress().GetFileAddress()); | |
| file.AppendU64(m_addr_range.GetByteSize()); | |
| file.AppendU32(m_flags); | |
| } | |
| bool Symbol::operator==(const Symbol &rhs) const { | |
| if (m_uid != rhs.m_uid) | |
| return false; | |
| if (m_type_data != rhs.m_type_data) | |
| return false; | |
| if (m_type_data_resolved != rhs.m_type_data_resolved) | |
| return false; | |
| if (m_is_synthetic != rhs.m_is_synthetic) | |
| return false; | |
| if (m_is_debug != rhs.m_is_debug) | |
| return false; | |
| if (m_is_external != rhs.m_is_external) | |
| return false; | |
| if (m_size_is_sibling != rhs.m_size_is_sibling) | |
| return false; | |
| if (m_size_is_synthesized != rhs.m_size_is_synthesized) | |
| return false; | |
| if (m_size_is_valid != rhs.m_size_is_valid) | |
| return false; | |
| if (m_demangled_is_synthesized != rhs.m_demangled_is_synthesized) | |
| return false; | |
| if (m_contains_linker_annotations != rhs.m_contains_linker_annotations) | |
| return false; | |
| if (m_is_weak != rhs.m_is_weak) | |
| return false; | |
| if (m_type != rhs.m_type) | |
| return false; | |
| if (m_mangled != rhs.m_mangled) | |
| return false; | |
| if (m_addr_range.GetBaseAddress() != rhs.m_addr_range.GetBaseAddress()) | |
| return false; | |
| if (m_addr_range.GetByteSize() != rhs.m_addr_range.GetByteSize()) | |
| return false; | |
| if (m_flags != rhs.m_flags) | |
| return false; | |
| return true; | |
| } | |
| #define ENUM_TO_CSTRING(x) \ | |
| case eSymbolType##x: \ | |
| return #x; | |
| const char *Symbol::GetTypeAsString(lldb::SymbolType symbol_type) { | |
| switch (symbol_type) { | |
| ENUM_TO_CSTRING(Invalid); | |
| ENUM_TO_CSTRING(Absolute); | |
| ENUM_TO_CSTRING(Code); | |
| ENUM_TO_CSTRING(Resolver); | |
| ENUM_TO_CSTRING(Data); | |
| ENUM_TO_CSTRING(Trampoline); | |
| ENUM_TO_CSTRING(Runtime); | |
| ENUM_TO_CSTRING(Exception); | |
| ENUM_TO_CSTRING(SourceFile); | |
| ENUM_TO_CSTRING(HeaderFile); | |
| ENUM_TO_CSTRING(ObjectFile); | |
| ENUM_TO_CSTRING(CommonBlock); | |
| ENUM_TO_CSTRING(Block); | |
| ENUM_TO_CSTRING(Local); | |
| ENUM_TO_CSTRING(Param); | |
| ENUM_TO_CSTRING(Variable); | |
| ENUM_TO_CSTRING(VariableType); | |
| ENUM_TO_CSTRING(LineEntry); | |
| ENUM_TO_CSTRING(LineHeader); | |
| ENUM_TO_CSTRING(ScopeBegin); | |
| ENUM_TO_CSTRING(ScopeEnd); | |
| ENUM_TO_CSTRING(Additional); | |
| ENUM_TO_CSTRING(Compiler); | |
| ENUM_TO_CSTRING(Instrumentation); | |
| ENUM_TO_CSTRING(Undefined); | |
| ENUM_TO_CSTRING(ObjCClass); | |
| ENUM_TO_CSTRING(ObjCMetaClass); | |
| ENUM_TO_CSTRING(ObjCIVar); | |
| ENUM_TO_CSTRING(ReExported); | |
| } | |
| return "<unknown SymbolType>"; | |
| } | |
| lldb::SymbolType Symbol::GetTypeFromString(const char *str) { | |
| std::string str_lower = llvm::StringRef(str).lower(); | |
| return llvm::StringSwitch<lldb::SymbolType>(str_lower) | |
| .Case("absolute", eSymbolTypeAbsolute) | |
| .Case("code", eSymbolTypeCode) | |
| .Case("resolver", eSymbolTypeResolver) | |
| .Case("data", eSymbolTypeData) | |
| .Case("trampoline", eSymbolTypeTrampoline) | |
| .Case("runtime", eSymbolTypeRuntime) | |
| .Case("exception", eSymbolTypeException) | |
| .Case("sourcefile", eSymbolTypeSourceFile) | |
| .Case("headerfile", eSymbolTypeHeaderFile) | |
| .Case("objectfile", eSymbolTypeObjectFile) | |
| .Case("commonblock", eSymbolTypeCommonBlock) | |
| .Case("block", eSymbolTypeBlock) | |
| .Case("local", eSymbolTypeLocal) | |
| .Case("param", eSymbolTypeParam) | |
| .Case("variable", eSymbolTypeVariable) | |
| .Case("variableType", eSymbolTypeVariableType) | |
| .Case("lineentry", eSymbolTypeLineEntry) | |
| .Case("lineheader", eSymbolTypeLineHeader) | |
| .Case("scopebegin", eSymbolTypeScopeBegin) | |
| .Case("scopeend", eSymbolTypeScopeEnd) | |
| .Case("additional,", eSymbolTypeAdditional) | |
| .Case("compiler", eSymbolTypeCompiler) | |
| .Case("instrumentation", eSymbolTypeInstrumentation) | |
| .Case("undefined", eSymbolTypeUndefined) | |
| .Case("objcclass", eSymbolTypeObjCClass) | |
| .Case("objcmetaclass", eSymbolTypeObjCMetaClass) | |
| .Case("objcivar", eSymbolTypeObjCIVar) | |
| .Case("reexported", eSymbolTypeReExported) | |
| .Default(eSymbolTypeInvalid); | |
| } | |
| namespace llvm { | |
| namespace json { | |
| bool fromJSON(const llvm::json::Value &value, lldb_private::JSONSymbol &symbol, | |
| llvm::json::Path path) { | |
| llvm::json::ObjectMapper o(value, path); | |
| const bool mapped = o && o.map("value", symbol.value) && | |
| o.map("address", symbol.address) && | |
| o.map("size", symbol.size) && o.map("id", symbol.id) && | |
| o.map("type", symbol.type) && o.map("name", symbol.name); | |
| if (!mapped) | |
| return false; | |
| if (!symbol.value && !symbol.address) { | |
| path.report("symbol must have either a value or an address"); | |
| return false; | |
| } | |
| if (symbol.value && symbol.address) { | |
| path.report("symbol cannot have both a value and an address"); | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool fromJSON(const llvm::json::Value &value, lldb::SymbolType &type, | |
| llvm::json::Path path) { | |
| if (auto str = value.getAsString()) { | |
| llvm::StringRef str_ref = str.value_or(""); | |
| type = Symbol::GetTypeFromString(str_ref.data()); | |
| if (type == eSymbolTypeInvalid) { | |
| path.report("invalid symbol type"); | |
| return false; | |
| } | |
| return true; | |
| } | |
| path.report("expected string"); | |
| return false; | |
| } | |
| } // namespace json | |
| } // namespace llvm |