1 //===- DWARFDebugRnglists.cpp ---------------------------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "llvm/DebugInfo/DWARF/DWARFDebugRnglists.h"
11 
12 #include "llvm/BinaryFormat/Dwarf.h"
13 #include "llvm/Support/Error.h"
14 #include "llvm/Support/Format.h"
15 #include "llvm/Support/raw_ostream.h"
16 
17 using namespace llvm;
18 
19 void DWARFDebugRnglists::clear() {
20   HeaderData = {};
21   Offsets.clear();
22   Ranges.clear();
23 }
24 
25 template <typename... Ts>
26 static Error createError(char const *Fmt, const Ts &... Vals) {
27   std::string Buffer;
28   raw_string_ostream Stream(Buffer);
29   Stream << format(Fmt, Vals...);
30   return make_error<StringError>(Stream.str(), inconvertibleErrorCode());
31 }
32 
33 Error DWARFDebugRnglists::extract(DWARFDataExtractor Data,
34                                   uint32_t *OffsetPtr) {
35   clear();
36   HeaderOffset = *OffsetPtr;
37 
38   // Read and verify the length field.
39   if (!Data.isValidOffsetForDataOfSize(*OffsetPtr, sizeof(uint32_t)))
40     return createError("section is not large enough to contain a "
41                        ".debug_rnglists table length at offset 0x%" PRIx32,
42                        *OffsetPtr);
43   // TODO: Add support for DWARF64.
44   HeaderData.Length = Data.getU32(OffsetPtr);
45   if (HeaderData.Length == 0xffffffffu)
46     return createError(
47         "DWARF64 is not supported in .debug_rnglists at offset 0x%" PRIx32,
48         HeaderOffset);
49   if (HeaderData.Length + sizeof(uint32_t) < sizeof(Header))
50     return createError(".debug_rnglists table at offset 0x%" PRIx32
51                        " has too small length (0x%" PRIx32
52                        ") to contain a complete header",
53                        HeaderOffset, length());
54   uint32_t End = HeaderOffset + length();
55   if (!Data.isValidOffsetForDataOfSize(HeaderOffset, End - HeaderOffset))
56     return createError(
57         "section is not large enough to contain a .debug_rnglists table "
58         "of length 0x%" PRIx32 " at offset 0x%" PRIx32,
59         length(), HeaderOffset);
60 
61   HeaderData.Version = Data.getU16(OffsetPtr);
62   HeaderData.AddrSize = Data.getU8(OffsetPtr);
63   HeaderData.SegSize = Data.getU8(OffsetPtr);
64   HeaderData.OffsetEntryCount = Data.getU32(OffsetPtr);
65 
66   // Perform basic validation of the remaining header fields.
67   if (HeaderData.Version != 5)
68     return createError("unrecognised .debug_rnglists table version %" PRIu16
69                        " in table at offset 0x%" PRIx32,
70                        HeaderData.Version, HeaderOffset);
71   if (HeaderData.AddrSize != 4 && HeaderData.AddrSize != 8)
72     return createError(".debug_rnglists table at offset 0x%" PRIx32
73                        " has unsupported address size %hhu",
74                        HeaderOffset, HeaderData.AddrSize);
75   if (HeaderData.SegSize != 0)
76     return createError(".debug_rnglists table at offset 0x%" PRIx32
77                        " has unsupported segment selector size %" PRIu8,
78                        HeaderOffset, HeaderData.SegSize);
79   if (End < HeaderOffset + sizeof(HeaderData) +
80                 HeaderData.OffsetEntryCount * sizeof(uint32_t))
81     return createError(".debug_rnglists table at offset 0x%" PRIx32
82                        " has more offset entries (%" PRIu32
83                        ") than there is space for",
84                        HeaderOffset, HeaderData.OffsetEntryCount);
85 
86   Data.setAddressSize(HeaderData.AddrSize);
87 
88   for (uint32_t I = 0; I < HeaderData.OffsetEntryCount; ++I)
89     Offsets.push_back(Data.getU32(OffsetPtr));
90 
91   DWARFRangeList CurrentRanges;
92   while (*OffsetPtr < End) {
93     uint32_t EntryOffset = *OffsetPtr;
94     uint8_t Encoding = Data.getU8(OffsetPtr);
95     MaxEncodingStringLength =
96         std::max(MaxEncodingStringLength,
97                  (uint8_t)dwarf::RangeListEncodingString(Encoding).size());
98     switch (Encoding) {
99     case dwarf::DW_RLE_end_of_list:
100       CurrentRanges.push_back(RangeListEntry{ EntryOffset, Encoding, 0, 0 });
101       Ranges.insert(Ranges.end(), std::move(CurrentRanges));
102       CurrentRanges.clear();
103       break;
104     // TODO: Support other encodings.
105     case dwarf::DW_RLE_base_addressx:
106       return createError("unsupported rnglists encoding DW_RLE_base_addressx "
107                          "at offset 0x%" PRIx32,
108                          *OffsetPtr - 1);
109     case dwarf::DW_RLE_startx_endx:
110       return createError("unsupported rnglists encoding DW_RLE_startx_endx at "
111                          "offset 0x%" PRIx32,
112                          *OffsetPtr - 1);
113     case dwarf::DW_RLE_startx_length:
114       return createError("unsupported rnglists encoding DW_RLE_startx_length "
115                          "at offset 0x%" PRIx32,
116                          *OffsetPtr - 1);
117     case dwarf::DW_RLE_offset_pair: {
118       uint32_t PreviousOffset = *OffsetPtr - 1;
119       uint64_t StartingOffset = Data.getULEB128(OffsetPtr);
120       uint64_t EndingOffset = Data.getULEB128(OffsetPtr);
121       if (End < *OffsetPtr)
122         return createError("read past end of table when reading "
123                            "DW_RLE_offset_pair encoding at offset 0x%" PRIx32,
124                            PreviousOffset);
125       CurrentRanges.push_back(
126           RangeListEntry{EntryOffset, Encoding, StartingOffset, EndingOffset});
127       break;
128     }
129     case dwarf::DW_RLE_base_address: {
130       if ((End - *OffsetPtr) < HeaderData.AddrSize)
131         return createError("insufficient space remaining in table for "
132                            "DW_RLE_base_address encoding at offset 0x%" PRIx32,
133                            *OffsetPtr - 1);
134       uint64_t Base = Data.getAddress(OffsetPtr);
135       CurrentRanges.push_back(RangeListEntry{EntryOffset, Encoding, Base, 0});
136       break;
137     }
138     case dwarf::DW_RLE_start_end: {
139       if ((End - *OffsetPtr) < unsigned(HeaderData.AddrSize * 2))
140         return createError("insufficient space remaining in table for "
141                            "DW_RLE_start_end encoding "
142                            "at offset 0x%" PRIx32,
143                            *OffsetPtr - 1);
144       uint64_t Start = Data.getAddress(OffsetPtr);
145       uint64_t End = Data.getAddress(OffsetPtr);
146       CurrentRanges.push_back(
147           RangeListEntry{EntryOffset, Encoding, Start, End});
148       break;
149     }
150     case dwarf::DW_RLE_start_length: {
151       uint32_t PreviousOffset = *OffsetPtr - 1;
152       uint64_t Start = Data.getAddress(OffsetPtr);
153       uint64_t Length = Data.getULEB128(OffsetPtr);
154       if (End < *OffsetPtr)
155         return createError("read past end of table when reading "
156                            "DW_RLE_start_length encoding at offset 0x%" PRIx32,
157                            PreviousOffset);
158       CurrentRanges.push_back(
159           RangeListEntry{EntryOffset, Encoding, Start, Length});
160       break;
161     }
162     default:
163       Ranges.insert(Ranges.end(), std::move(CurrentRanges));
164       return createError("unknown rnglists encoding 0x%" PRIx32
165                          " at offset 0x%" PRIx32,
166                          uint32_t(Encoding), *OffsetPtr - 1);
167     }
168   }
169 
170   // If OffsetPtr does not indicate the End offset, then either the above loop
171   // terminated prematurely, or we encountered a malformed encoding, but did not
172   // report an error when we should have done.
173   assert(*OffsetPtr == End &&
174          "did not detect malformed data or loop ended unexpectedly");
175 
176   // If CurrentRanges is not empty, we have a malformed section, because we did
177   // not find a DW_RLE_end_of_list marker at the end of the last list.
178   if (!CurrentRanges.empty())
179     return createError(
180         "no end of list marker detected at end of .debug_rnglists table "
181         "starting at offset 0x%" PRIx32,
182         HeaderOffset);
183   return Error::success();
184 }
185 
186 static void dumpRangeEntry(raw_ostream &OS,
187                            DWARFDebugRnglists::RangeListEntry Entry,
188                            uint8_t AddrSize, uint8_t MaxEncodingStringLength,
189                            uint64_t &CurrentBase, DIDumpOptions DumpOpts) {
190   auto PrintRawEntry = [](raw_ostream &OS,
191                           DWARFDebugRnglists::RangeListEntry Entry,
192                           uint8_t AddrSize, DIDumpOptions DumpOpts) {
193     if (DumpOpts.Verbose) {
194       DumpOpts.DisplayRawContents = true;
195       DWARFAddressRange(Entry.Value0, Entry.Value1)
196           .dump(OS, AddrSize, DumpOpts);
197       OS << " => ";
198     }
199   };
200 
201   if (DumpOpts.Verbose) {
202     // Print the section offset in verbose mode.
203     OS << format("0x%8.8" PRIx32 ":", Entry.Offset);
204     auto EncodingString = dwarf::RangeListEncodingString(Entry.EntryKind);
205     // Unsupported encodings should have been reported during parsing.
206     assert(!EncodingString.empty() && "Unknown range entry encoding");
207     OS << format(" [%s%*c", EncodingString.data(),
208                  MaxEncodingStringLength - EncodingString.size() + 1, ']');
209     if (Entry.EntryKind != dwarf::DW_RLE_end_of_list)
210       OS << ": ";
211   }
212 
213   switch (Entry.EntryKind) {
214   case dwarf::DW_RLE_end_of_list:
215     OS << (DumpOpts.Verbose ? "" : "<End of list>");
216     break;
217   case dwarf::DW_RLE_base_address:
218     // In non-verbose mode we do not print anything for this entry.
219     CurrentBase = Entry.Value0;
220     if (!DumpOpts.Verbose)
221       return;
222     OS << format(" 0x%*.*" PRIx64, AddrSize * 2, AddrSize * 2, Entry.Value0);
223     break;
224   case dwarf::DW_RLE_start_length:
225     PrintRawEntry(OS, Entry, AddrSize, DumpOpts);
226     DWARFAddressRange(Entry.Value0, Entry.Value0 + Entry.Value1)
227         .dump(OS, AddrSize, DumpOpts);
228     break;
229   case dwarf::DW_RLE_offset_pair:
230     PrintRawEntry(OS, Entry, AddrSize, DumpOpts);
231     DWARFAddressRange(Entry.Value0 + CurrentBase, Entry.Value1 + CurrentBase)
232         .dump(OS, AddrSize, DumpOpts);
233     break;
234   case dwarf::DW_RLE_start_end:
235     DWARFAddressRange(Entry.Value0, Entry.Value1).dump(OS, AddrSize, DumpOpts);
236     break;
237   default:
238     llvm_unreachable("Unsupported range list encoding");
239   }
240   OS << "\n";
241 }
242 
243 void DWARFDebugRnglists::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const {
244   if (DumpOpts.Verbose)
245     OS << format("0x%8.8" PRIx32 ": ", HeaderOffset);
246   OS << format("Range List Header: length = 0x%8.8" PRIx32
247                ", version = 0x%4.4" PRIx16 ", "
248                "addr_size = 0x%2.2" PRIx8 ", seg_size = 0x%2.2" PRIx8
249                ", offset_entry_count = "
250                "0x%8.8" PRIx32 "\n",
251                HeaderData.Length, HeaderData.Version, HeaderData.AddrSize,
252                HeaderData.SegSize, HeaderData.OffsetEntryCount);
253 
254   if (HeaderData.OffsetEntryCount > 0) {
255     OS << "Offsets: [";
256     for (const auto &Off : Offsets) {
257       OS << format("\n0x%8.8" PRIx32, Off);
258       if (DumpOpts.Verbose)
259         OS << format(" => 0x%8.8" PRIx32,
260                      Off + HeaderOffset + sizeof(HeaderData));
261     }
262     OS << "\n]\n";
263   }
264   OS << "Ranges:\n";
265 
266   uint64_t CurrentBase = 0;
267   for (const auto &List : Ranges)
268     for (const auto &Entry : List)
269       dumpRangeEntry(OS, Entry, HeaderData.AddrSize, MaxEncodingStringLength,
270                      CurrentBase, DumpOpts);
271 }
272 
273 uint32_t DWARFDebugRnglists::length() const {
274   if (HeaderData.Length == 0)
275     return 0;
276   // TODO: DWARF64 support.
277   return HeaderData.Length + sizeof(uint32_t);
278 }
279