1 //===- DWARFListTable.h -----------------------------------------*- C++ -*-===// 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 #ifndef LLVM_DEBUGINFO_DWARF_DWARFLISTTABLE_H 10 #define LLVM_DEBUGINFO_DWARF_DWARFLISTTABLE_H 11 12 #include "llvm/BinaryFormat/Dwarf.h" 13 #include "llvm/DebugInfo/DIContext.h" 14 #include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h" 15 #include "llvm/Support/Errc.h" 16 #include "llvm/Support/Error.h" 17 #include "llvm/Support/Format.h" 18 #include "llvm/Support/raw_ostream.h" 19 #include <cstdint> 20 #include <map> 21 #include <vector> 22 23 namespace llvm { 24 25 /// A base class for DWARF list entries, such as range or location list 26 /// entries. 27 struct DWARFListEntryBase { 28 /// The offset at which the entry is located in the section. 29 uint64_t Offset; 30 /// The DWARF encoding (DW_RLE_* or DW_LLE_*). 31 uint8_t EntryKind; 32 /// The index of the section this entry belongs to. 33 uint64_t SectionIndex; 34 }; 35 36 /// A base class for lists of entries that are extracted from a particular 37 /// section, such as range lists or location lists. 38 template <typename ListEntryType> class DWARFListType { 39 using EntryType = ListEntryType; 40 using ListEntries = std::vector<EntryType>; 41 42 protected: 43 ListEntries Entries; 44 45 public: 46 const ListEntries &getEntries() const { return Entries; } 47 bool empty() const { return Entries.empty(); } 48 void clear() { Entries.clear(); } 49 Error extract(DWARFDataExtractor Data, uint64_t HeaderOffset, 50 uint64_t *OffsetPtr, StringRef SectionName, 51 StringRef ListStringName); 52 }; 53 54 /// A class representing the header of a list table such as the range list 55 /// table in the .debug_rnglists section. 56 class DWARFListTableHeader { 57 struct Header { 58 /// The total length of the entries for this table, not including the length 59 /// field itself. 60 uint64_t Length = 0; 61 /// The DWARF version number. 62 uint16_t Version; 63 /// The size in bytes of an address on the target architecture. For 64 /// segmented addressing, this is the size of the offset portion of the 65 /// address. 66 uint8_t AddrSize; 67 /// The size in bytes of a segment selector on the target architecture. 68 /// If the target system uses a flat address space, this value is 0. 69 uint8_t SegSize; 70 /// The number of offsets that follow the header before the range lists. 71 uint32_t OffsetEntryCount; 72 }; 73 74 Header HeaderData; 75 /// The table's format, either DWARF32 or DWARF64. 76 dwarf::DwarfFormat Format; 77 /// The offset at which the header (and hence the table) is located within 78 /// its section. 79 uint64_t HeaderOffset; 80 /// The name of the section the list is located in. 81 StringRef SectionName; 82 /// A characterization of the list for dumping purposes, e.g. "range" or 83 /// "location". 84 StringRef ListTypeString; 85 86 public: 87 DWARFListTableHeader(StringRef SectionName, StringRef ListTypeString) 88 : SectionName(SectionName), ListTypeString(ListTypeString) {} 89 90 void clear() { 91 HeaderData = {}; 92 } 93 uint64_t getHeaderOffset() const { return HeaderOffset; } 94 uint8_t getAddrSize() const { return HeaderData.AddrSize; } 95 uint64_t getLength() const { return HeaderData.Length; } 96 uint16_t getVersion() const { return HeaderData.Version; } 97 uint32_t getOffsetEntryCount() const { return HeaderData.OffsetEntryCount; } 98 StringRef getSectionName() const { return SectionName; } 99 StringRef getListTypeString() const { return ListTypeString; } 100 dwarf::DwarfFormat getFormat() const { return Format; } 101 102 /// Return the size of the table header including the length but not including 103 /// the offsets. 104 static uint8_t getHeaderSize(dwarf::DwarfFormat Format) { 105 switch (Format) { 106 case dwarf::DwarfFormat::DWARF32: 107 return 12; 108 case dwarf::DwarfFormat::DWARF64: 109 return 20; 110 } 111 llvm_unreachable("Invalid DWARF format (expected DWARF32 or DWARF64"); 112 } 113 114 void dump(DataExtractor Data, raw_ostream &OS, 115 DIDumpOptions DumpOpts = {}) const; 116 Optional<uint64_t> getOffsetEntry(DataExtractor Data, uint32_t Index) const { 117 if (Index >= HeaderData.OffsetEntryCount) 118 return None; 119 120 return getOffsetEntry(Data, getHeaderOffset() + getHeaderSize(Format), Format, Index); 121 } 122 123 static Optional<uint64_t> getOffsetEntry(DataExtractor Data, 124 uint64_t OffsetTableOffset, 125 dwarf::DwarfFormat Format, 126 uint32_t Index) { 127 uint8_t OffsetByteSize = Format == dwarf::DWARF64 ? 8 : 4; 128 uint64_t Offset = OffsetTableOffset + OffsetByteSize * Index; 129 auto R = Data.getUnsigned(&Offset, OffsetByteSize); 130 return R; 131 } 132 133 /// Extract the table header and the array of offsets. 134 Error extract(DWARFDataExtractor Data, uint64_t *OffsetPtr); 135 136 /// Returns the length of the table, including the length field, or 0 if the 137 /// length has not been determined (e.g. because the table has not yet been 138 /// parsed, or there was a problem in parsing). 139 uint64_t length() const; 140 }; 141 142 /// A class representing a table of lists as specified in the DWARF v5 143 /// standard for location lists and range lists. The table consists of a header 144 /// followed by an array of offsets into a DWARF section, followed by zero or 145 /// more list entries. The list entries are kept in a map where the keys are 146 /// the lists' section offsets. 147 template <typename DWARFListType> class DWARFListTableBase { 148 DWARFListTableHeader Header; 149 /// A mapping between file offsets and lists. It is used to find a particular 150 /// list based on an offset (obtained from DW_AT_ranges, for example). 151 std::map<uint64_t, DWARFListType> ListMap; 152 /// This string is displayed as a heading before the list is dumped 153 /// (e.g. "ranges:"). 154 StringRef HeaderString; 155 156 protected: 157 DWARFListTableBase(StringRef SectionName, StringRef HeaderString, 158 StringRef ListTypeString) 159 : Header(SectionName, ListTypeString), HeaderString(HeaderString) {} 160 161 public: 162 void clear() { 163 Header.clear(); 164 ListMap.clear(); 165 } 166 /// Extract the table header and the array of offsets. 167 Error extractHeaderAndOffsets(DWARFDataExtractor Data, uint64_t *OffsetPtr) { 168 return Header.extract(Data, OffsetPtr); 169 } 170 /// Extract an entire table, including all list entries. 171 Error extract(DWARFDataExtractor Data, uint64_t *OffsetPtr); 172 /// Look up a list based on a given offset. Extract it and enter it into the 173 /// list map if necessary. 174 Expected<DWARFListType> findList(DWARFDataExtractor Data, 175 uint64_t Offset) const; 176 177 uint64_t getHeaderOffset() const { return Header.getHeaderOffset(); } 178 uint8_t getAddrSize() const { return Header.getAddrSize(); } 179 uint32_t getOffsetEntryCount() const { return Header.getOffsetEntryCount(); } 180 dwarf::DwarfFormat getFormat() const { return Header.getFormat(); } 181 182 void dump(DWARFDataExtractor Data, raw_ostream &OS, 183 llvm::function_ref<Optional<object::SectionedAddress>(uint32_t)> 184 LookupPooledAddress, 185 DIDumpOptions DumpOpts = {}) const; 186 187 /// Return the contents of the offset entry designated by a given index. 188 Optional<uint64_t> getOffsetEntry(DataExtractor Data, uint32_t Index) const { 189 return Header.getOffsetEntry(Data, Index); 190 } 191 /// Return the size of the table header including the length but not including 192 /// the offsets. This is dependent on the table format, which is unambiguously 193 /// derived from parsing the table. 194 uint8_t getHeaderSize() const { 195 return DWARFListTableHeader::getHeaderSize(getFormat()); 196 } 197 198 uint64_t length() { return Header.length(); } 199 }; 200 201 template <typename DWARFListType> 202 Error DWARFListTableBase<DWARFListType>::extract(DWARFDataExtractor Data, 203 uint64_t *OffsetPtr) { 204 clear(); 205 if (Error E = extractHeaderAndOffsets(Data, OffsetPtr)) 206 return E; 207 208 Data.setAddressSize(Header.getAddrSize()); 209 Data = DWARFDataExtractor(Data, getHeaderOffset() + Header.length()); 210 while (Data.isValidOffset(*OffsetPtr)) { 211 DWARFListType CurrentList; 212 uint64_t Off = *OffsetPtr; 213 if (Error E = CurrentList.extract(Data, getHeaderOffset(), OffsetPtr, 214 Header.getSectionName(), 215 Header.getListTypeString())) 216 return E; 217 ListMap[Off] = CurrentList; 218 } 219 220 assert(*OffsetPtr == Data.size() && 221 "mismatch between expected length of table and length " 222 "of extracted data"); 223 return Error::success(); 224 } 225 226 template <typename ListEntryType> 227 Error DWARFListType<ListEntryType>::extract(DWARFDataExtractor Data, 228 uint64_t HeaderOffset, 229 uint64_t *OffsetPtr, 230 StringRef SectionName, 231 StringRef ListTypeString) { 232 if (*OffsetPtr < HeaderOffset || *OffsetPtr >= Data.size()) 233 return createStringError(errc::invalid_argument, 234 "invalid %s list offset 0x%" PRIx64, 235 ListTypeString.data(), *OffsetPtr); 236 Entries.clear(); 237 while (Data.isValidOffset(*OffsetPtr)) { 238 ListEntryType Entry; 239 if (Error E = Entry.extract(Data, OffsetPtr)) 240 return E; 241 Entries.push_back(Entry); 242 if (Entry.isSentinel()) 243 return Error::success(); 244 } 245 return createStringError(errc::illegal_byte_sequence, 246 "no end of list marker detected at end of %s table " 247 "starting at offset 0x%" PRIx64, 248 SectionName.data(), HeaderOffset); 249 } 250 251 template <typename DWARFListType> 252 void DWARFListTableBase<DWARFListType>::dump( 253 DWARFDataExtractor Data, raw_ostream &OS, 254 llvm::function_ref<Optional<object::SectionedAddress>(uint32_t)> 255 LookupPooledAddress, 256 DIDumpOptions DumpOpts) const { 257 Header.dump(Data, OS, DumpOpts); 258 OS << HeaderString << "\n"; 259 260 // Determine the length of the longest encoding string we have in the table, 261 // so we can align the output properly. We only need this in verbose mode. 262 size_t MaxEncodingStringLength = 0; 263 if (DumpOpts.Verbose) { 264 for (const auto &List : ListMap) 265 for (const auto &Entry : List.second.getEntries()) 266 MaxEncodingStringLength = 267 std::max(MaxEncodingStringLength, 268 dwarf::RangeListEncodingString(Entry.EntryKind).size()); 269 } 270 271 uint64_t CurrentBase = 0; 272 for (const auto &List : ListMap) 273 for (const auto &Entry : List.second.getEntries()) 274 Entry.dump(OS, getAddrSize(), MaxEncodingStringLength, CurrentBase, 275 DumpOpts, LookupPooledAddress); 276 } 277 278 template <typename DWARFListType> 279 Expected<DWARFListType> 280 DWARFListTableBase<DWARFListType>::findList(DWARFDataExtractor Data, 281 uint64_t Offset) const { 282 // Extract the list from the section and enter it into the list map. 283 DWARFListType List; 284 if (Header.length()) 285 Data = DWARFDataExtractor(Data, getHeaderOffset() + Header.length()); 286 if (Error E = 287 List.extract(Data, Header.length() ? getHeaderOffset() : 0, &Offset, 288 Header.getSectionName(), Header.getListTypeString())) 289 return std::move(E); 290 return List; 291 } 292 293 } // end namespace llvm 294 295 #endif // LLVM_DEBUGINFO_DWARF_DWARFLISTTABLE_H 296