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/raw_ostream.h" 18 #include <cstdint> 19 #include <map> 20 #include <vector> 21 22 namespace llvm { 23 24 /// A base class for DWARF list entries, such as range or location list 25 /// entries. 26 struct DWARFListEntryBase { 27 /// The offset at which the entry is located in the section. 28 uint64_t Offset; 29 /// The DWARF encoding (DW_RLE_* or DW_LLE_*). 30 uint8_t EntryKind; 31 /// The index of the section this entry belongs to. 32 uint64_t SectionIndex; 33 }; 34 35 /// A base class for lists of entries that are extracted from a particular 36 /// section, such as range lists or location lists. 37 template <typename ListEntryType> class DWARFListType { 38 using EntryType = ListEntryType; 39 using ListEntries = std::vector<EntryType>; 40 41 protected: 42 ListEntries Entries; 43 44 public: 45 const ListEntries &getEntries() const { return Entries; } 46 bool empty() const { return Entries.empty(); } 47 void clear() { Entries.clear(); } 48 Error extract(DWARFDataExtractor Data, uint64_t HeaderOffset, 49 uint64_t *OffsetPtr, StringRef SectionName, 50 StringRef ListStringName); 51 }; 52 53 /// A class representing the header of a list table such as the range list 54 /// table in the .debug_rnglists section. 55 class DWARFListTableHeader { 56 struct Header { 57 /// The total length of the entries for this table, not including the length 58 /// field itself. 59 uint64_t Length = 0; 60 /// The DWARF version number. 61 uint16_t Version; 62 /// The size in bytes of an address on the target architecture. For 63 /// segmented addressing, this is the size of the offset portion of the 64 /// address. 65 uint8_t AddrSize; 66 /// The size in bytes of a segment selector on the target architecture. 67 /// If the target system uses a flat address space, this value is 0. 68 uint8_t SegSize; 69 /// The number of offsets that follow the header before the range lists. 70 uint32_t OffsetEntryCount; 71 }; 72 73 Header HeaderData; 74 /// The table's format, either DWARF32 or DWARF64. 75 dwarf::DwarfFormat Format; 76 /// The offset at which the header (and hence the table) is located within 77 /// its section. 78 uint64_t HeaderOffset; 79 /// The name of the section the list is located in. 80 StringRef SectionName; 81 /// A characterization of the list for dumping purposes, e.g. "range" or 82 /// "location". 83 StringRef ListTypeString; 84 85 public: 86 DWARFListTableHeader(StringRef SectionName, StringRef ListTypeString) 87 : SectionName(SectionName), ListTypeString(ListTypeString) {} 88 89 void clear() { 90 HeaderData = {}; 91 } 92 uint64_t getHeaderOffset() const { return HeaderOffset; } 93 uint8_t getAddrSize() const { return HeaderData.AddrSize; } 94 uint64_t getLength() const { return HeaderData.Length; } 95 uint16_t getVersion() const { return HeaderData.Version; } 96 uint32_t getOffsetEntryCount() const { return HeaderData.OffsetEntryCount; } 97 StringRef getSectionName() const { return SectionName; } 98 StringRef getListTypeString() const { return ListTypeString; } 99 dwarf::DwarfFormat getFormat() const { return Format; } 100 101 /// Return the size of the table header including the length but not including 102 /// the offsets. 103 static uint8_t getHeaderSize(dwarf::DwarfFormat Format) { 104 switch (Format) { 105 case dwarf::DwarfFormat::DWARF32: 106 return 12; 107 case dwarf::DwarfFormat::DWARF64: 108 return 20; 109 } 110 llvm_unreachable("Invalid DWARF format (expected DWARF32 or DWARF64"); 111 } 112 113 void dump(DataExtractor Data, raw_ostream &OS, 114 DIDumpOptions DumpOpts = {}) const; 115 std::optional<uint64_t> getOffsetEntry(DataExtractor Data, 116 uint32_t Index) const { 117 if (Index >= HeaderData.OffsetEntryCount) 118 return std::nullopt; 119 120 return getOffsetEntry(Data, getHeaderOffset() + getHeaderSize(Format), Format, Index); 121 } 122 123 static std::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 183 dump(DWARFDataExtractor Data, raw_ostream &OS, 184 llvm::function_ref<std::optional<object::SectionedAddress>(uint32_t)> 185 LookupPooledAddress, 186 DIDumpOptions DumpOpts = {}) const; 187 188 /// Return the contents of the offset entry designated by a given index. 189 std::optional<uint64_t> getOffsetEntry(DataExtractor Data, 190 uint32_t Index) const { 191 return Header.getOffsetEntry(Data, Index); 192 } 193 /// Return the size of the table header including the length but not including 194 /// the offsets. This is dependent on the table format, which is unambiguously 195 /// derived from parsing the table. 196 uint8_t getHeaderSize() const { 197 return DWARFListTableHeader::getHeaderSize(getFormat()); 198 } 199 200 uint64_t length() { return Header.length(); } 201 }; 202 203 template <typename DWARFListType> 204 Error DWARFListTableBase<DWARFListType>::extract(DWARFDataExtractor Data, 205 uint64_t *OffsetPtr) { 206 clear(); 207 if (Error E = extractHeaderAndOffsets(Data, OffsetPtr)) 208 return E; 209 210 Data.setAddressSize(Header.getAddrSize()); 211 Data = DWARFDataExtractor(Data, getHeaderOffset() + Header.length()); 212 while (Data.isValidOffset(*OffsetPtr)) { 213 DWARFListType CurrentList; 214 uint64_t Off = *OffsetPtr; 215 if (Error E = CurrentList.extract(Data, getHeaderOffset(), OffsetPtr, 216 Header.getSectionName(), 217 Header.getListTypeString())) 218 return E; 219 ListMap[Off] = CurrentList; 220 } 221 222 assert(*OffsetPtr == Data.size() && 223 "mismatch between expected length of table and length " 224 "of extracted data"); 225 return Error::success(); 226 } 227 228 template <typename ListEntryType> 229 Error DWARFListType<ListEntryType>::extract(DWARFDataExtractor Data, 230 uint64_t HeaderOffset, 231 uint64_t *OffsetPtr, 232 StringRef SectionName, 233 StringRef ListTypeString) { 234 if (*OffsetPtr < HeaderOffset || *OffsetPtr >= Data.size()) 235 return createStringError(errc::invalid_argument, 236 "invalid %s list offset 0x%" PRIx64, 237 ListTypeString.data(), *OffsetPtr); 238 Entries.clear(); 239 while (Data.isValidOffset(*OffsetPtr)) { 240 ListEntryType Entry; 241 if (Error E = Entry.extract(Data, OffsetPtr)) 242 return E; 243 Entries.push_back(Entry); 244 if (Entry.isSentinel()) 245 return Error::success(); 246 } 247 return createStringError(errc::illegal_byte_sequence, 248 "no end of list marker detected at end of %s table " 249 "starting at offset 0x%" PRIx64, 250 SectionName.data(), HeaderOffset); 251 } 252 253 template <typename DWARFListType> 254 void DWARFListTableBase<DWARFListType>::dump( 255 DWARFDataExtractor Data, raw_ostream &OS, 256 llvm::function_ref<std::optional<object::SectionedAddress>(uint32_t)> 257 LookupPooledAddress, 258 DIDumpOptions DumpOpts) const { 259 Header.dump(Data, OS, DumpOpts); 260 OS << HeaderString << "\n"; 261 262 // Determine the length of the longest encoding string we have in the table, 263 // so we can align the output properly. We only need this in verbose mode. 264 size_t MaxEncodingStringLength = 0; 265 if (DumpOpts.Verbose) { 266 for (const auto &List : ListMap) 267 for (const auto &Entry : List.second.getEntries()) 268 MaxEncodingStringLength = 269 std::max(MaxEncodingStringLength, 270 dwarf::RangeListEncodingString(Entry.EntryKind).size()); 271 } 272 273 uint64_t CurrentBase = 0; 274 for (const auto &List : ListMap) 275 for (const auto &Entry : List.second.getEntries()) 276 Entry.dump(OS, getAddrSize(), MaxEncodingStringLength, CurrentBase, 277 DumpOpts, LookupPooledAddress); 278 } 279 280 template <typename DWARFListType> 281 Expected<DWARFListType> 282 DWARFListTableBase<DWARFListType>::findList(DWARFDataExtractor Data, 283 uint64_t Offset) const { 284 // Extract the list from the section and enter it into the list map. 285 DWARFListType List; 286 if (Header.length()) 287 Data = DWARFDataExtractor(Data, getHeaderOffset() + Header.length()); 288 if (Error E = 289 List.extract(Data, Header.length() ? getHeaderOffset() : 0, &Offset, 290 Header.getSectionName(), Header.getListTypeString())) 291 return std::move(E); 292 return List; 293 } 294 295 } // end namespace llvm 296 297 #endif // LLVM_DEBUGINFO_DWARF_DWARFLISTTABLE_H 298