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       return createError("unsupported rnglists encoding DW_RLE_offset_pair at "
119                          "offset 0x%" PRIx32,
120                          *OffsetPtr - 1);
121     case dwarf::DW_RLE_base_address:
122       return createError("unsupported rnglists encoding DW_RLE_base_address at "
123                          "offset 0x%" PRIx32,
124                          *OffsetPtr - 1);
125     case dwarf::DW_RLE_start_end: {
126       if ((End - *OffsetPtr) < unsigned(HeaderData.AddrSize * 2))
127         return createError("insufficient space remaining in table for "
128                            "DW_RLE_start_end encoding "
129                            "at offset 0x%" PRIx32,
130                            *OffsetPtr - 1);
131       uint64_t Start = Data.getAddress(OffsetPtr);
132       uint64_t End = Data.getAddress(OffsetPtr);
133       CurrentRanges.push_back(
134           RangeListEntry{EntryOffset, Encoding, Start, End});
135       break;
136     }
137     case dwarf::DW_RLE_start_length: {
138       uint32_t PreviousOffset = *OffsetPtr - 1;
139       uint64_t Start = Data.getAddress(OffsetPtr);
140       uint64_t Length = Data.getULEB128(OffsetPtr);
141       if (End < *OffsetPtr)
142         return createError("read past end of table when reading "
143                            "DW_RLE_start_length encoding at offset 0x%" PRIx32,
144                            PreviousOffset);
145       CurrentRanges.push_back(
146           RangeListEntry{EntryOffset, Encoding, Start, Length});
147       break;
148     }
149     default:
150       Ranges.insert(Ranges.end(), std::move(CurrentRanges));
151       return createError("unknown rnglists encoding 0x%" PRIx32
152                          " at offset 0x%" PRIx32,
153                          uint32_t(Encoding), *OffsetPtr - 1);
154     }
155   }
156 
157   // If OffsetPtr does not indicate the End offset, then either the above loop
158   // terminated prematurely, or we encountered a malformed encoding, but did not
159   // report an error when we should have done.
160   assert(*OffsetPtr == End &&
161          "did not detect malformed data or loop ended unexpectedly");
162 
163   // If CurrentRanges is not empty, we have a malformed section, because we did
164   // not find a DW_RLE_end_of_list marker at the end of the last list.
165   if (!CurrentRanges.empty())
166     return createError(
167         "no end of list marker detected at end of .debug_rnglists table "
168         "starting at offset 0x%" PRIx32,
169         HeaderOffset);
170   return Error::success();
171 }
172 
173 static void dumpRangeEntry(raw_ostream &OS,
174                            DWARFDebugRnglists::RangeListEntry Entry,
175                            uint8_t AddrSize, uint8_t MaxEncodingStringLength,
176                            DIDumpOptions DumpOpts) {
177   if (DumpOpts.Verbose) {
178     // Print the section offset in verbose mode.
179     OS << format("0x%8.8" PRIx32 ":", Entry.Offset);
180     auto EncodingString = dwarf::RangeListEncodingString(Entry.EntryKind);
181     // Unsupported encodings should have been reported during parsing.
182     assert(!EncodingString.empty() && "Unknown range entry encoding");
183     OS << format(" [%s%*c", EncodingString.data(),
184                  MaxEncodingStringLength - EncodingString.size() + 1, ']');
185     if (Entry.EntryKind != dwarf::DW_RLE_end_of_list)
186       OS << ": ";
187   }
188 
189   switch (Entry.EntryKind) {
190   case dwarf::DW_RLE_end_of_list:
191     OS << (DumpOpts.Verbose ? "" : "<End of list>");
192     break;
193   case dwarf::DW_RLE_start_length:
194     if (DumpOpts.Verbose) {
195       // Make the address range display its contents in raw form rather than
196       // as an interval (i.e. without brackets).
197       DumpOpts.DisplayRawContents = true;
198       DWARFAddressRange(Entry.Value0, Entry.Value1)
199           .dump(OS, AddrSize, DumpOpts);
200       OS << " => ";
201     }
202     DumpOpts.DisplayRawContents = false;
203     DWARFAddressRange(Entry.Value0, Entry.Value0 + Entry.Value1)
204         .dump(OS, AddrSize, DumpOpts);
205     break;
206   case dwarf::DW_RLE_start_end:
207     DWARFAddressRange(Entry.Value0, Entry.Value1).dump(OS, AddrSize, DumpOpts);
208     break;
209   default:
210     llvm_unreachable("Unsupported range list encoding");
211   }
212   OS << "\n";
213 }
214 
215 void DWARFDebugRnglists::dump(raw_ostream &OS, DIDumpOptions DumpOpts) const {
216   if (DumpOpts.Verbose)
217     OS << format("0x%8.8" PRIx32 ": ", HeaderOffset);
218   OS << format("Range List Header: length = 0x%8.8" PRIx32
219                ", version = 0x%4.4" PRIx16 ", "
220                "addr_size = 0x%2.2" PRIx8 ", seg_size = 0x%2.2" PRIx8
221                ", offset_entry_count = "
222                "0x%8.8" PRIx32 "\n",
223                HeaderData.Length, HeaderData.Version, HeaderData.AddrSize,
224                HeaderData.SegSize, HeaderData.OffsetEntryCount);
225 
226   if (HeaderData.OffsetEntryCount > 0) {
227     OS << "Offsets: [";
228     for (const auto &Off : Offsets) {
229       OS << format("\n0x%8.8" PRIx32, Off);
230       if (DumpOpts.Verbose)
231         OS << format(" => 0x%8.8" PRIx32,
232                      Off + HeaderOffset + sizeof(HeaderData));
233     }
234     OS << "\n]\n";
235   }
236   OS << "Ranges:\n";
237 
238   for (const auto &List : Ranges)
239     for (const auto &Entry : List)
240       dumpRangeEntry(OS, Entry, HeaderData.AddrSize, MaxEncodingStringLength,
241                      DumpOpts);
242 }
243 
244 uint32_t DWARFDebugRnglists::length() const {
245   if (HeaderData.Length == 0)
246     return 0;
247   // TODO: DWARF64 support.
248   return HeaderData.Length + sizeof(uint32_t);
249 }
250