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 , |
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 { |
56 | struct { |
57 | /// The total length of the entries for this table, not including the length |
58 | /// field itself. |
59 | uint64_t = 0; |
60 | /// The DWARF version number. |
61 | uint16_t ; |
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 ; |
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 ; |
69 | /// The number of offsets that follow the header before the range lists. |
70 | uint32_t ; |
71 | }; |
72 | |
73 | Header ; |
74 | /// The table's format, either DWARF32 or DWARF64. |
75 | dwarf::DwarfFormat ; |
76 | /// The offset at which the header (and hence the table) is located within |
77 | /// its section. |
78 | uint64_t ; |
79 | /// The name of the section the list is located in. |
80 | StringRef ; |
81 | /// A characterization of the list for dumping purposes, e.g. "range" or |
82 | /// "location". |
83 | StringRef ; |
84 | |
85 | public: |
86 | (StringRef SectionName, StringRef ListTypeString) |
87 | : SectionName(SectionName), ListTypeString(ListTypeString) {} |
88 | |
89 | void () { |
90 | HeaderData = {}; |
91 | } |
92 | uint64_t () const { return HeaderOffset; } |
93 | uint8_t () const { return HeaderData.AddrSize; } |
94 | uint64_t () const { return HeaderData.Length; } |
95 | uint16_t () const { return HeaderData.Version; } |
96 | uint32_t () const { return HeaderData.OffsetEntryCount; } |
97 | StringRef () const { return SectionName; } |
98 | StringRef () const { return ListTypeString; } |
99 | dwarf::DwarfFormat () 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 (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 (DataExtractor Data, raw_ostream &OS, |
114 | DIDumpOptions DumpOpts = {}) const; |
115 | std::optional<uint64_t> (DataExtractor Data, |
116 | uint32_t Index) const { |
117 | if (Index >= HeaderData.OffsetEntryCount) |
118 | return std::nullopt; |
119 | |
120 | return getOffsetEntry(Data, OffsetTableOffset: getHeaderOffset() + getHeaderSize(Format), Format, Index); |
121 | } |
122 | |
123 | static std::optional<uint64_t> (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_ptr: &Offset, byte_size: OffsetByteSize); |
130 | return R; |
131 | } |
132 | |
133 | /// Extract the table header and the array of offsets. |
134 | Error (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 () 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 ; |
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 ; |
155 | |
156 | protected: |
157 | DWARFListTableBase(StringRef SectionName, StringRef , |
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 () 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> (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 () const { |
197 | return DWARFListTableHeader::getHeaderSize(Format: getFormat()); |
198 | } |
199 | |
200 | uint64_t length() { return Header.length(); } |
201 | }; |
202 | |
203 | template <typename DWARFListType> |
204 | Error DWARFListTableBase<DWARFListType>::(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(offset: *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>::(DWARFDataExtractor Data, |
230 | uint64_t , |
231 | uint64_t *OffsetPtr, |
232 | StringRef SectionName, |
233 | StringRef ListTypeString) { |
234 | if (*OffsetPtr < HeaderOffset || *OffsetPtr >= Data.size()) |
235 | return createStringError(EC: errc::invalid_argument, |
236 | Fmt: "invalid %s list offset 0x%" PRIx64, |
237 | Vals: ListTypeString.data(), Vals: *OffsetPtr); |
238 | Entries.clear(); |
239 | while (Data.isValidOffset(offset: *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(EC: errc::illegal_byte_sequence, |
248 | Fmt: "no end of list marker detected at end of %s table " |
249 | "starting at offset 0x%" PRIx64, |
250 | Vals: SectionName.data(), Vals: HeaderOffset); |
251 | } |
252 | |
253 | template <typename DWARFListType> |
254 | void DWARFListTableBase<DWARFListType>::( |
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(Encoding: 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>::(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 | |