diff options
Diffstat (limited to 'contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.cpp')
| -rw-r--r-- | contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.cpp | 583 |
1 files changed, 583 insertions, 0 deletions
diff --git a/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.cpp b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.cpp new file mode 100644 index 000000000000..62b0ad37a9fc --- /dev/null +++ b/contrib/llvm-project/lldb/source/Plugins/SymbolFile/DWARF/HashedNameToDIE.cpp @@ -0,0 +1,583 @@ +//===-- HashedNameToDIE.cpp -------------------------------------*- C++ -*-===// +// +// 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 "HashedNameToDIE.h" +#include "llvm/ADT/StringRef.h" + +void DWARFMappedHash::ExtractDIEArray(const DIEInfoArray &die_info_array, + DIEArray &die_offsets) { + const size_t count = die_info_array.size(); + for (size_t i = 0; i < count; ++i) + die_offsets.emplace_back(die_info_array[i]); +} + +void DWARFMappedHash::ExtractDIEArray(const DIEInfoArray &die_info_array, + const dw_tag_t tag, + DIEArray &die_offsets) { + if (tag == 0) { + ExtractDIEArray(die_info_array, die_offsets); + } else { + const size_t count = die_info_array.size(); + for (size_t i = 0; i < count; ++i) { + const dw_tag_t die_tag = die_info_array[i].tag; + bool tag_matches = die_tag == 0 || tag == die_tag; + if (!tag_matches) { + if (die_tag == DW_TAG_class_type || die_tag == DW_TAG_structure_type) + tag_matches = + tag == DW_TAG_structure_type || tag == DW_TAG_class_type; + } + if (tag_matches) + die_offsets.emplace_back(die_info_array[i]); + } + } +} + +void DWARFMappedHash::ExtractDIEArray(const DIEInfoArray &die_info_array, + const dw_tag_t tag, + const uint32_t qualified_name_hash, + DIEArray &die_offsets) { + if (tag == 0) { + ExtractDIEArray(die_info_array, die_offsets); + } else { + const size_t count = die_info_array.size(); + for (size_t i = 0; i < count; ++i) { + if (qualified_name_hash != die_info_array[i].qualified_name_hash) + continue; + const dw_tag_t die_tag = die_info_array[i].tag; + bool tag_matches = die_tag == 0 || tag == die_tag; + if (!tag_matches) { + if (die_tag == DW_TAG_class_type || die_tag == DW_TAG_structure_type) + tag_matches = + tag == DW_TAG_structure_type || tag == DW_TAG_class_type; + } + if (tag_matches) + die_offsets.emplace_back(die_info_array[i]); + } + } +} + +void DWARFMappedHash::ExtractClassOrStructDIEArray( + const DIEInfoArray &die_info_array, + bool return_implementation_only_if_available, DIEArray &die_offsets) { + const size_t count = die_info_array.size(); + for (size_t i = 0; i < count; ++i) { + const dw_tag_t die_tag = die_info_array[i].tag; + if (die_tag == 0 || die_tag == DW_TAG_class_type || + die_tag == DW_TAG_structure_type) { + if (die_info_array[i].type_flags & eTypeFlagClassIsImplementation) { + if (return_implementation_only_if_available) { + // We found the one true definition for this class, so only return + // that + die_offsets.clear(); + die_offsets.emplace_back(die_info_array[i]); + return; + } else { + // Put the one true definition as the first entry so it matches first + die_offsets.emplace(die_offsets.begin(), die_info_array[i]); + } + } else { + die_offsets.emplace_back(die_info_array[i]); + } + } + } +} + +void DWARFMappedHash::ExtractTypesFromDIEArray( + const DIEInfoArray &die_info_array, uint32_t type_flag_mask, + uint32_t type_flag_value, DIEArray &die_offsets) { + const size_t count = die_info_array.size(); + for (size_t i = 0; i < count; ++i) { + if ((die_info_array[i].type_flags & type_flag_mask) == type_flag_value) + die_offsets.emplace_back(die_info_array[i]); + } +} + +const char *DWARFMappedHash::GetAtomTypeName(uint16_t atom) { + switch (atom) { + case eAtomTypeNULL: + return "NULL"; + case eAtomTypeDIEOffset: + return "die-offset"; + case eAtomTypeCUOffset: + return "cu-offset"; + case eAtomTypeTag: + return "die-tag"; + case eAtomTypeNameFlags: + return "name-flags"; + case eAtomTypeTypeFlags: + return "type-flags"; + case eAtomTypeQualNameHash: + return "qualified-name-hash"; + } + return "<invalid>"; +} + +DWARFMappedHash::DIEInfo::DIEInfo(dw_offset_t o, dw_tag_t t, uint32_t f, + uint32_t h) + : die_offset(o), tag(t), type_flags(f), qualified_name_hash(h) {} + +DWARFMappedHash::Prologue::Prologue(dw_offset_t _die_base_offset) + : die_base_offset(_die_base_offset), atoms(), atom_mask(0), + min_hash_data_byte_size(0), hash_data_has_fixed_byte_size(true) { + // Define an array of DIE offsets by first defining an array, and then define + // the atom type for the array, in this case we have an array of DIE offsets + AppendAtom(eAtomTypeDIEOffset, DW_FORM_data4); +} + +void DWARFMappedHash::Prologue::ClearAtoms() { + hash_data_has_fixed_byte_size = true; + min_hash_data_byte_size = 0; + atom_mask = 0; + atoms.clear(); +} + +bool DWARFMappedHash::Prologue::ContainsAtom(AtomType atom_type) const { + return (atom_mask & (1u << atom_type)) != 0; +} + +void DWARFMappedHash::Prologue::Clear() { + die_base_offset = 0; + ClearAtoms(); +} + +void DWARFMappedHash::Prologue::AppendAtom(AtomType type, dw_form_t form) { + atoms.push_back({type, form}); + atom_mask |= 1u << type; + switch (form) { + case DW_FORM_indirect: + case DW_FORM_exprloc: + case DW_FORM_flag_present: + case DW_FORM_ref_sig8: + llvm_unreachable("Unhandled atom form"); + + case DW_FORM_addrx: + case DW_FORM_string: + case DW_FORM_block: + case DW_FORM_block1: + case DW_FORM_sdata: + case DW_FORM_udata: + case DW_FORM_ref_udata: + case DW_FORM_GNU_addr_index: + case DW_FORM_GNU_str_index: + hash_data_has_fixed_byte_size = false; + LLVM_FALLTHROUGH; + case DW_FORM_flag: + case DW_FORM_data1: + case DW_FORM_ref1: + case DW_FORM_sec_offset: + min_hash_data_byte_size += 1; + break; + + case DW_FORM_block2: + hash_data_has_fixed_byte_size = false; + LLVM_FALLTHROUGH; + case DW_FORM_data2: + case DW_FORM_ref2: + min_hash_data_byte_size += 2; + break; + + case DW_FORM_block4: + hash_data_has_fixed_byte_size = false; + LLVM_FALLTHROUGH; + case DW_FORM_data4: + case DW_FORM_ref4: + case DW_FORM_addr: + case DW_FORM_ref_addr: + case DW_FORM_strp: + min_hash_data_byte_size += 4; + break; + + case DW_FORM_data8: + case DW_FORM_ref8: + min_hash_data_byte_size += 8; + break; + } +} + +lldb::offset_t +DWARFMappedHash::Prologue::Read(const lldb_private::DataExtractor &data, + lldb::offset_t offset) { + ClearAtoms(); + + die_base_offset = data.GetU32(&offset); + + const uint32_t atom_count = data.GetU32(&offset); + if (atom_count == 0x00060003u) { + // Old format, deal with contents of old pre-release format + while (data.GetU32(&offset)) + /* do nothing */; + + // Hardcode to the only known value for now. + AppendAtom(eAtomTypeDIEOffset, DW_FORM_data4); + } else { + for (uint32_t i = 0; i < atom_count; ++i) { + AtomType type = (AtomType)data.GetU16(&offset); + dw_form_t form = (dw_form_t)data.GetU16(&offset); + AppendAtom(type, form); + } + } + return offset; +} + +size_t DWARFMappedHash::Prologue::GetByteSize() const { + // Add an extra count to the atoms size for the zero termination Atom that + // gets written to disk + return sizeof(die_base_offset) + sizeof(uint32_t) + + atoms.size() * sizeof(Atom); +} + +size_t DWARFMappedHash::Prologue::GetMinimumHashDataByteSize() const { + return min_hash_data_byte_size; +} + +bool DWARFMappedHash::Prologue::HashDataHasFixedByteSize() const { + return hash_data_has_fixed_byte_size; +} + +size_t DWARFMappedHash::Header::GetByteSize(const HeaderData &header_data) { + return header_data.GetByteSize(); +} + +lldb::offset_t DWARFMappedHash::Header::Read(lldb_private::DataExtractor &data, + lldb::offset_t offset) { + offset = MappedHash::Header<Prologue>::Read(data, offset); + if (offset != UINT32_MAX) { + offset = header_data.Read(data, offset); + } + return offset; +} + +bool DWARFMappedHash::Header::Read(const lldb_private::DWARFDataExtractor &data, + lldb::offset_t *offset_ptr, + DIEInfo &hash_data) const { + const size_t num_atoms = header_data.atoms.size(); + if (num_atoms == 0) + return false; + + for (size_t i = 0; i < num_atoms; ++i) { + DWARFFormValue form_value(nullptr, header_data.atoms[i].form); + + if (!form_value.ExtractValue(data, offset_ptr)) + return false; + + switch (header_data.atoms[i].type) { + case eAtomTypeDIEOffset: // DIE offset, check form for encoding + hash_data.die_offset = + DWARFFormValue::IsDataForm(form_value.Form()) + ? form_value.Unsigned() + : form_value.Reference(header_data.die_base_offset); + break; + + case eAtomTypeTag: // DW_TAG value for the DIE + hash_data.tag = (dw_tag_t)form_value.Unsigned(); + break; + + case eAtomTypeTypeFlags: // Flags from enum TypeFlags + hash_data.type_flags = (uint32_t)form_value.Unsigned(); + break; + + case eAtomTypeQualNameHash: // Flags from enum TypeFlags + hash_data.qualified_name_hash = form_value.Unsigned(); + break; + + default: + // We can always skip atoms we don't know about + break; + } + } + return hash_data.die_offset != DW_INVALID_OFFSET; +} + +DWARFMappedHash::MemoryTable::MemoryTable( + lldb_private::DWARFDataExtractor &table_data, + const lldb_private::DWARFDataExtractor &string_table, const char *name) + : MappedHash::MemoryTable<uint32_t, Header, DIEInfoArray>(table_data), + m_data(table_data), m_string_table(string_table), m_name(name) {} + +const char * +DWARFMappedHash::MemoryTable::GetStringForKeyType(KeyType key) const { + // The key in the DWARF table is the .debug_str offset for the string + return m_string_table.PeekCStr(key); +} + +bool DWARFMappedHash::MemoryTable::ReadHashData(uint32_t hash_data_offset, + HashData &hash_data) const { + lldb::offset_t offset = hash_data_offset; + offset += 4; // Skip string table offset that contains offset of hash name in + // .debug_str + const uint32_t count = m_data.GetU32(&offset); + if (count > 0) { + hash_data.resize(count); + for (uint32_t i = 0; i < count; ++i) { + if (!m_header.Read(m_data, &offset, hash_data[i])) + return false; + } + } else + hash_data.clear(); + return true; +} + +DWARFMappedHash::MemoryTable::Result +DWARFMappedHash::MemoryTable::GetHashDataForName( + llvm::StringRef name, lldb::offset_t *hash_data_offset_ptr, + Pair &pair) const { + pair.key = m_data.GetU32(hash_data_offset_ptr); + pair.value.clear(); + + // If the key is zero, this terminates our chain of HashData objects for this + // hash value. + if (pair.key == 0) + return eResultEndOfHashData; + + // There definitely should be a string for this string offset, if there + // isn't, there is something wrong, return and error + const char *strp_cstr = m_string_table.PeekCStr(pair.key); + if (strp_cstr == nullptr) { + *hash_data_offset_ptr = UINT32_MAX; + return eResultError; + } + + const uint32_t count = m_data.GetU32(hash_data_offset_ptr); + const size_t min_total_hash_data_size = + count * m_header.header_data.GetMinimumHashDataByteSize(); + if (count > 0 && + m_data.ValidOffsetForDataOfSize(*hash_data_offset_ptr, + min_total_hash_data_size)) { + // We have at least one HashData entry, and we have enough data to parse at + // least "count" HashData entries. + + // First make sure the entire C string matches... + const bool match = name == strp_cstr; + + if (!match && m_header.header_data.HashDataHasFixedByteSize()) { + // If the string doesn't match and we have fixed size data, we can just + // add the total byte size of all HashData objects to the hash data + // offset and be done... + *hash_data_offset_ptr += min_total_hash_data_size; + } else { + // If the string does match, or we don't have fixed size data then we + // need to read the hash data as a stream. If the string matches we also + // append all HashData objects to the value array. + for (uint32_t i = 0; i < count; ++i) { + DIEInfo die_info; + if (m_header.Read(m_data, hash_data_offset_ptr, die_info)) { + // Only happened if the HashData of the string matched... + if (match) + pair.value.push_back(die_info); + } else { + // Something went wrong while reading the data + *hash_data_offset_ptr = UINT32_MAX; + return eResultError; + } + } + } + // Return the correct response depending on if the string matched or not... + if (match) + return eResultKeyMatch; // The key (cstring) matches and we have lookup + // results! + else + return eResultKeyMismatch; // The key doesn't match, this function will + // get called + // again for the next key/value or the key terminator which in our case is + // a zero .debug_str offset. + } else { + *hash_data_offset_ptr = UINT32_MAX; + return eResultError; + } +} + +DWARFMappedHash::MemoryTable::Result +DWARFMappedHash::MemoryTable::AppendHashDataForRegularExpression( + const lldb_private::RegularExpression ®ex, + lldb::offset_t *hash_data_offset_ptr, Pair &pair) const { + pair.key = m_data.GetU32(hash_data_offset_ptr); + // If the key is zero, this terminates our chain of HashData objects for this + // hash value. + if (pair.key == 0) + return eResultEndOfHashData; + + // There definitely should be a string for this string offset, if there + // isn't, there is something wrong, return and error + const char *strp_cstr = m_string_table.PeekCStr(pair.key); + if (strp_cstr == nullptr) + return eResultError; + + const uint32_t count = m_data.GetU32(hash_data_offset_ptr); + const size_t min_total_hash_data_size = + count * m_header.header_data.GetMinimumHashDataByteSize(); + if (count > 0 && + m_data.ValidOffsetForDataOfSize(*hash_data_offset_ptr, + min_total_hash_data_size)) { + const bool match = regex.Execute(llvm::StringRef(strp_cstr)); + + if (!match && m_header.header_data.HashDataHasFixedByteSize()) { + // If the regex doesn't match and we have fixed size data, we can just + // add the total byte size of all HashData objects to the hash data + // offset and be done... + *hash_data_offset_ptr += min_total_hash_data_size; + } else { + // If the string does match, or we don't have fixed size data then we + // need to read the hash data as a stream. If the string matches we also + // append all HashData objects to the value array. + for (uint32_t i = 0; i < count; ++i) { + DIEInfo die_info; + if (m_header.Read(m_data, hash_data_offset_ptr, die_info)) { + // Only happened if the HashData of the string matched... + if (match) + pair.value.push_back(die_info); + } else { + // Something went wrong while reading the data + *hash_data_offset_ptr = UINT32_MAX; + return eResultError; + } + } + } + // Return the correct response depending on if the string matched or not... + if (match) + return eResultKeyMatch; // The key (cstring) matches and we have lookup + // results! + else + return eResultKeyMismatch; // The key doesn't match, this function will + // get called + // again for the next key/value or the key terminator which in our case is + // a zero .debug_str offset. + } else { + *hash_data_offset_ptr = UINT32_MAX; + return eResultError; + } +} + +size_t DWARFMappedHash::MemoryTable::AppendAllDIEsThatMatchingRegex( + const lldb_private::RegularExpression ®ex, + DIEInfoArray &die_info_array) const { + const uint32_t hash_count = m_header.hashes_count; + Pair pair; + for (uint32_t offset_idx = 0; offset_idx < hash_count; ++offset_idx) { + lldb::offset_t hash_data_offset = GetHashDataOffset(offset_idx); + while (hash_data_offset != UINT32_MAX) { + const lldb::offset_t prev_hash_data_offset = hash_data_offset; + Result hash_result = + AppendHashDataForRegularExpression(regex, &hash_data_offset, pair); + if (prev_hash_data_offset == hash_data_offset) + break; + + // Check the result of getting our hash data + switch (hash_result) { + case eResultKeyMatch: + case eResultKeyMismatch: + // Whether we matches or not, it doesn't matter, we keep looking. + break; + + case eResultEndOfHashData: + case eResultError: + hash_data_offset = UINT32_MAX; + break; + } + } + } + die_info_array.swap(pair.value); + return die_info_array.size(); +} + +size_t DWARFMappedHash::MemoryTable::AppendAllDIEsInRange( + const uint32_t die_offset_start, const uint32_t die_offset_end, + DIEInfoArray &die_info_array) const { + const uint32_t hash_count = m_header.hashes_count; + for (uint32_t offset_idx = 0; offset_idx < hash_count; ++offset_idx) { + bool done = false; + lldb::offset_t hash_data_offset = GetHashDataOffset(offset_idx); + while (!done && hash_data_offset != UINT32_MAX) { + KeyType key = m_data.GetU32(&hash_data_offset); + // If the key is zero, this terminates our chain of HashData objects for + // this hash value. + if (key == 0) + break; + + const uint32_t count = m_data.GetU32(&hash_data_offset); + for (uint32_t i = 0; i < count; ++i) { + DIEInfo die_info; + if (m_header.Read(m_data, &hash_data_offset, die_info)) { + if (die_info.die_offset == 0) + done = true; + if (die_offset_start <= die_info.die_offset && + die_info.die_offset < die_offset_end) + die_info_array.push_back(die_info); + } + } + } + } + return die_info_array.size(); +} + +size_t DWARFMappedHash::MemoryTable::FindByName(llvm::StringRef name, + DIEArray &die_offsets) { + if (name.empty()) + return 0; + + DIEInfoArray die_info_array; + if (FindByName(name, die_info_array)) + DWARFMappedHash::ExtractDIEArray(die_info_array, die_offsets); + return die_info_array.size(); +} + +size_t DWARFMappedHash::MemoryTable::FindByNameAndTag(llvm::StringRef name, + const dw_tag_t tag, + DIEArray &die_offsets) { + DIEInfoArray die_info_array; + if (FindByName(name, die_info_array)) + DWARFMappedHash::ExtractDIEArray(die_info_array, tag, die_offsets); + return die_info_array.size(); +} + +size_t DWARFMappedHash::MemoryTable::FindByNameAndTagAndQualifiedNameHash( + llvm::StringRef name, const dw_tag_t tag, + const uint32_t qualified_name_hash, DIEArray &die_offsets) { + DIEInfoArray die_info_array; + if (FindByName(name, die_info_array)) + DWARFMappedHash::ExtractDIEArray(die_info_array, tag, qualified_name_hash, + die_offsets); + return die_info_array.size(); +} + +size_t DWARFMappedHash::MemoryTable::FindCompleteObjCClassByName( + llvm::StringRef name, DIEArray &die_offsets, bool must_be_implementation) { + DIEInfoArray die_info_array; + if (FindByName(name, die_info_array)) { + if (must_be_implementation && + GetHeader().header_data.ContainsAtom(eAtomTypeTypeFlags)) { + // If we have two atoms, then we have the DIE offset and the type flags + // so we can find the objective C class efficiently. + DWARFMappedHash::ExtractTypesFromDIEArray(die_info_array, UINT32_MAX, + eTypeFlagClassIsImplementation, + die_offsets); + } else { + // We don't only want the one true definition, so try and see what we can + // find, and only return class or struct DIEs. If we do have the full + // implementation, then return it alone, else return all possible + // matches. + const bool return_implementation_only_if_available = true; + DWARFMappedHash::ExtractClassOrStructDIEArray( + die_info_array, return_implementation_only_if_available, die_offsets); + } + } + return die_offsets.size(); +} + +size_t DWARFMappedHash::MemoryTable::FindByName(llvm::StringRef name, + DIEInfoArray &die_info_array) { + if (name.empty()) + return 0; + + Pair kv_pair; + size_t old_size = die_info_array.size(); + if (Find(name, kv_pair)) { + die_info_array.swap(kv_pair.value); + return die_info_array.size() - old_size; + } + return 0; +} |
