| //===-- 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/DataFileCache.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_or_reexport(*this) {} | |
| 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_or_reexport(*this), m_flags(flags) { | |
| if (m_type == eSymbolTypeReExported) | |
| m_addr_or_reexport.GetReExportInfo(*this).Clear(); | |
| else | |
| m_addr_or_reexport.SetAddressRange(*this, | |
| AddressRange(section_sp, offset, size)); | |
| } | |
| 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_or_reexport(*this), m_flags(flags) { | |
| if (m_type == eSymbolTypeReExported) | |
| m_addr_or_reexport.SetReExportInfo(*this, ReExportInfo()); | |
| else | |
| m_addr_or_reexport.SetAddressRange(*this, range); | |
| } | |
| 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_or_reexport(*this), m_flags(rhs.m_flags) { | |
| if (rhs.m_type == eSymbolTypeReExported) | |
| m_addr_or_reexport.SetReExportInfo( | |
| *this, rhs.m_addr_or_reexport.GetReExportInfo(*this)); | |
| else | |
| m_addr_or_reexport.SetAddressRange( | |
| *this, rhs.m_addr_or_reexport.GetAddressRange(*this)); | |
| } | |
| 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_mangled = rhs.m_mangled; | |
| if (m_type != eSymbolTypeReExported && m_type != eSymbolTypeInvalid) | |
| m_addr_or_reexport.GetAddressRange(*this).Clear(); | |
| m_type = rhs.m_type; | |
| if (rhs.m_type == eSymbolTypeReExported) | |
| m_addr_or_reexport.SetReExportInfo( | |
| *this, rhs.m_addr_or_reexport.GetReExportInfo(*this)); | |
| else | |
| m_addr_or_reexport.SetAddressRange( | |
| *this, rhs.m_addr_or_reexport.GetAddressRange(*this)); | |
| 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_or_reexport.GetAddressRange(*this).Clear(); | |
| } | |
| bool Symbol::ValueIsAddress() const { | |
| if (m_type == eSymbolTypeReExported) | |
| return false; | |
| return (bool)m_addr_or_reexport.GetAddressRange(*this) | |
| .GetBaseAddress() | |
| .GetSection(); | |
| } | |
| ConstString Symbol::GetDisplayName() const { | |
| return GetMangled().GetDisplayDemangledName(); | |
| } | |
| ConstString Symbol::GetReExportedSymbolName() const { | |
| if (m_type != eSymbolTypeReExported) | |
| return ConstString(); | |
| return m_addr_or_reexport.GetReExportInfo(*this).name; | |
| } | |
| FileSpec Symbol::GetReExportedSymbolSharedLibrary() const { | |
| if (m_type != eSymbolTypeReExported) | |
| return FileSpec(); | |
| const Symbol::ReExportInfo &reexport = | |
| m_addr_or_reexport.GetReExportInfo(*this); | |
| if (reexport.library_up) | |
| return *reexport.library_up; | |
| else | |
| return FileSpec(); | |
| } | |
| void Symbol::SetReExportedSymbolName(ConstString name) { | |
| if (m_type != eSymbolTypeReExported && m_type != eSymbolTypeInvalid) | |
| m_addr_or_reexport.GetAddressRange(*this).Clear(); | |
| if (m_type != eSymbolTypeReExported) | |
| m_addr_or_reexport.SetReExportInfo(*this, ReExportInfo()); | |
| m_type = eSymbolTypeReExported; | |
| m_addr_or_reexport.GetReExportInfo(*this).name = name; | |
| } | |
| bool Symbol::SetReExportedSymbolSharedLibrary(const FileSpec &fspec) { | |
| if (m_type != eSymbolTypeReExported) | |
| return false; | |
| if (m_type != eSymbolTypeReExported && m_type != eSymbolTypeInvalid) | |
| m_addr_or_reexport.GetAddressRange(*this).Clear(); | |
| m_type = eSymbolTypeReExported; | |
| m_addr_or_reexport.GetReExportInfo(*this).library_up = | |
| std::make_unique<FileSpec>(fspec); | |
| return true; | |
| } | |
| uint32_t Symbol::GetSiblingIndex() const { | |
| return m_size_is_sibling | |
| ? m_addr_or_reexport.GetAddressRange(*this).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_or_reexport.GetAddressRange(*this).GetBaseAddress().GetSection()) { | |
| if (ValueIsAddress()) { | |
| const lldb::addr_t byte_size = GetByteSize(); | |
| if (byte_size > 0) { | |
| s->PutCString(", range = "); | |
| m_addr_or_reexport.GetAddressRange(*this).Dump( | |
| s, target, Address::DumpStyleLoadAddress, | |
| Address::DumpStyleFileAddress); | |
| } else { | |
| s->PutCString(", address = "); | |
| m_addr_or_reexport.GetAddressRange(*this).GetBaseAddress().Dump( | |
| s, target, Address::DumpStyleLoadAddress, | |
| Address::DumpStyleFileAddress); | |
| } | |
| } else | |
| s->Printf(", value = 0x%16.16" PRIx64, | |
| m_addr_or_reexport.GetAddressRange(*this) | |
| .GetBaseAddress() | |
| .GetOffset()); | |
| } else { | |
| if (m_size_is_sibling) | |
| s->Printf(", sibling = %5" PRIu64, | |
| m_addr_or_reexport.GetAddressRange(*this) | |
| .GetBaseAddress() | |
| .GetOffset()); | |
| else | |
| s->Printf(", value = 0x%16.16" PRIx64, | |
| m_addr_or_reexport.GetAddressRange(*this) | |
| .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_or_reexport.GetAddressRange(*this).GetBaseAddress().Dump( | |
| s, nullptr, Address::DumpStyleFileAddress)) | |
| s->Printf("%*s", 18, ""); | |
| s->PutChar(' '); | |
| if (!m_addr_or_reexport.GetAddressRange(*this).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("")); | |
| const FileSpec &shlib = GetReExportedSymbolSharedLibrary(); | |
| if (shlib) | |
| s->Printf(" -> %s`%s\n", shlib.GetPath().c_str(), | |
| GetReExportedSymbolName().GetCString()); | |
| else | |
| s->Printf(" -> %s\n", GetReExportedSymbolName().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_or_reexport.GetAddressRange(*this).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_or_reexport.GetAddressRange(*this).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_or_reexport.GetAddressRange(*this).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_or_reexport.GetAddressRange(*this).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 { | |
| if (GetType() == eSymbolTypeReExported) | |
| return 0; | |
| else | |
| return m_addr_or_reexport.GetAddressRange(*this).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() && !sc.symbol->IsWeak()) | |
| continue; | |
| // Don't return a symbol that itself only re-exports the definition | |
| // (e.g. an ELF filter library's placeholder): the real definition is | |
| // found by following the module-level re-exports below, which also | |
| // guards against cycles. | |
| if (sc.symbol->GetType() == eSymbolTypeReExported) | |
| continue; | |
| 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 lldb::ModuleSP &containing_module_sp) const { | |
| ConstString reexport_name(GetReExportedSymbolName()); | |
| ModuleList seen_modules; | |
| if (reexport_name) { | |
| // Search the library recorded on the symbol itself first. | |
| ModuleSpec module_spec; | |
| module_spec.GetFileSpec() = GetReExportedSymbolSharedLibrary(); | |
| if (module_spec.GetFileSpec()) { | |
| if (Symbol *result = ResolveReExportedSymbolInModuleSpec( | |
| target, reexport_name, module_spec, seen_modules)) | |
| return result; | |
| } | |
| } else { | |
| // This symbol isn't itself marked as a re-export. Some formats (ELF's | |
| // DT_FILTER / DT_AUXILIARY) have no per-symbol tagging: the dynamic | |
| // linker always resolves through the filtee(s) first and only falls | |
| // back to the filter object's own definition if none of them provide | |
| // it, even when the filter also provides a genuine implementation of | |
| // the same symbol. | |
| ObjectFile *object_file = | |
| containing_module_sp ? containing_module_sp->GetObjectFile() : nullptr; | |
| if (!object_file || | |
| !object_file->ReExportedLibrariesShadowLocalDefinitions()) | |
| return nullptr; | |
| // Only exported (global or weak) definitions take part in dynamic | |
| // linking, so local symbols are never shadowed by a filtee. | |
| if (!IsExternal() && !IsWeak()) | |
| return nullptr; | |
| // Use this symbol's own (version-suffix-stripped) name so the filtees | |
| // below are searched for it. | |
| reexport_name = GetName(); | |
| if (!reexport_name) | |
| return nullptr; | |
| if (ContainsLinkerAnnotations()) | |
| reexport_name = ConstString(object_file->StripLinkerSymbolAnnotations( | |
| reexport_name.GetStringRef())); | |
| } | |
| // The recorded library is only the first candidate: the module defining | |
| // this symbol may re-export several libraries which must be searched in | |
| // the order they are declared (e.g. an ELF filter library with multiple | |
| // DT_FILTER / DT_AUXILIARY entries). seen_modules is shared with the | |
| // search above so no library is searched twice. | |
| if (containing_module_sp) { | |
| if (ObjectFile *object_file = containing_module_sp->GetObjectFile()) { | |
| FileSpecList reexported_libraries = object_file->GetReExportedLibraries(); | |
| const size_t count = reexported_libraries.GetSize(); | |
| for (size_t idx = 0; idx < count; ++idx) { | |
| ModuleSpec reexported_module_spec; | |
| reexported_module_spec.GetFileSpec() = | |
| reexported_libraries.GetFileSpecAtIndex(idx); | |
| if (Symbol *result = ResolveReExportedSymbolInModuleSpec( | |
| target, reexport_name, reexported_module_spec, seen_modules)) | |
| return result; | |
| } | |
| } | |
| } | |
| 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 lldb::ModuleSP &containing_module_sp) const { | |
| if (GetType() == lldb::eSymbolTypeUndefined) | |
| return LLDB_INVALID_ADDRESS; | |
| Address func_so_addr; | |
| bool is_indirect = IsIndirect(); | |
| // A symbol from an ELF filter/auxiliary library is resolved through its | |
| // filtee(s) first, falling back to its own definition only if none of them | |
| // provide it, mirroring what the dynamic linker does. | |
| if (Symbol *reexported_symbol = | |
| ResolveReExportedSymbol(target, containing_module_sp)) { | |
| func_so_addr = reexported_symbol->GetAddress(); | |
| is_indirect = reexported_symbol->IsIndirect(); | |
| } else if (GetType() != eSymbolTypeReExported) { | |
| 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_or_reexport.GetAddressRange(*this).GetBaseAddress().GetModule()); | |
| if (module_sp && exe_ctx.HasTargetScope()) { | |
| return Disassembler::DisassembleRange( | |
| module_sp->GetArchitecture(), nullptr, flavor, nullptr, nullptr, | |
| exe_ctx.GetTargetRef(), m_addr_or_reexport.GetAddressRange(*this), | |
| !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_or_reexport.GetAddressRange(*this).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_or_reexport.GetAddressRange(*this) | |
| .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; | |
| if (m_type != eSymbolTypeReExported) { | |
| const bool is_addr = data.GetU8(offset_ptr) != 0; | |
| const uint64_t value = data.GetU64(offset_ptr); | |
| if (is_addr) { | |
| m_addr_or_reexport.GetAddressRange(*this) | |
| .GetBaseAddress() | |
| .ResolveAddressUsingFileSections(value, section_list); | |
| } else { | |
| m_addr_or_reexport.GetAddressRange(*this).GetBaseAddress().Clear(); | |
| m_addr_or_reexport.GetAddressRange(*this).GetBaseAddress().SetOffset( | |
| value); | |
| } | |
| m_addr_or_reexport.GetAddressRange(*this).SetByteSize( | |
| data.GetU64(offset_ptr)); | |
| } else { | |
| m_addr_or_reexport.GetReExportInfo(*this).name = | |
| ConstString(strtab.Get(data.GetU32(offset_ptr))); | |
| llvm::StringRef filename = strtab.Get(data.GetU32(offset_ptr)); | |
| if (!filename.empty()) | |
| m_addr_or_reexport.GetReExportInfo(*this).library_up = | |
| std::make_unique<FileSpec>(filename); | |
| } | |
| 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) == 72, | |
| "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); | |
| if (m_type != eSymbolTypeReExported) { | |
| // 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_or_reexport.GetAddressRange(*this) | |
| .GetBaseAddress() | |
| .GetSection() | |
| .get() != nullptr; | |
| file.AppendU8(is_addr); | |
| file.AppendU64(m_addr_or_reexport.GetAddressRange(*this) | |
| .GetBaseAddress() | |
| .GetFileAddress()); | |
| file.AppendU64(m_addr_or_reexport.GetAddressRange(*this).GetByteSize()); | |
| } else { | |
| file.AppendU32(strtab.Add(m_addr_or_reexport.GetReExportInfo(*this).name)); | |
| if (m_addr_or_reexport.GetReExportInfo(*this).library_up) { | |
| ConstString filepath(m_addr_or_reexport.GetReExportInfo(*this) | |
| .library_up->GetPath() | |
| .c_str()); | |
| file.AppendU32(strtab.Add(filepath)); | |
| } else { | |
| file.AppendU32(strtab.Add(ConstString(""))); | |
| } | |
| } | |
| 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_or_reexport.GetAddressRange(*this).GetBaseAddress() != | |
| rhs.m_addr_or_reexport.GetAddressRange(*this).GetBaseAddress()) | |
| return false; | |
| if (m_addr_or_reexport.GetAddressRange(*this).GetByteSize() != | |
| rhs.m_addr_or_reexport.GetAddressRange(*this).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); | |
| } | |
| AddressRange &Symbol::AddrRangeOrReExport::GetAddressRange(Symbol &sym) { | |
| assert(sym.GetType() != eSymbolTypeReExported); | |
| return m_addr_range; | |
| } | |
| const AddressRange & | |
| Symbol::AddrRangeOrReExport::GetAddressRange(const Symbol &sym) const { | |
| assert(sym.GetType() != eSymbolTypeReExported); | |
| return m_addr_range; | |
| } | |
| Symbol::ReExportInfo & | |
| Symbol::AddrRangeOrReExport::GetReExportInfo(Symbol &sym) { | |
| assert(sym.GetType() == eSymbolTypeReExported); | |
| return m_reexport_info; | |
| } | |
| const Symbol::ReExportInfo & | |
| Symbol::AddrRangeOrReExport::GetReExportInfo(const Symbol &sym) const { | |
| assert(sym.GetType() == eSymbolTypeReExported); | |
| return m_reexport_info; | |
| } | |
| void Symbol::AddrRangeOrReExport::SetAddressRange( | |
| Symbol &sym, const AddressRange addr_range) { | |
| if (sym.GetType() == eSymbolTypeReExported) { | |
| m_reexport_info.Clear(); | |
| sym.SetType(eSymbolTypeInvalid); | |
| } | |
| m_addr_range = addr_range; | |
| } | |
| void Symbol::AddrRangeOrReExport::SetReExportInfo( | |
| Symbol &sym, const Symbol::ReExportInfo reexport_info) { | |
| if (sym.GetType() != eSymbolTypeReExported) { | |
| m_addr_range.Clear(); | |
| sym.SetType(eSymbolTypeReExported); | |
| } | |
| m_reexport_info = reexport_info; | |
| } | |
| 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 |