James Henderson | c2dfd50 | 2018-02-02 12:45:57 +0000 | [diff] [blame^] | 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 | uint32_t TableOffset = *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 + sizeof(uint32_t) < sizeof(Header)) |
| 46 | return createError(".debug_rnglists table at offset 0x%" PRIx32 |
| 47 | " has too small length (0x%" PRIx32 |
| 48 | ") to contain a complete header", |
| 49 | TableOffset, length()); |
| 50 | uint64_t End = TableOffset + length(); |
| 51 | if (!Data.isValidOffsetForDataOfSize(TableOffset, End - TableOffset)) |
| 52 | return createError( |
| 53 | "section is not large enough to contain a .debug_rnglists table " |
| 54 | "of length 0x%" PRIx32 " at offset 0x%" PRIx32, |
| 55 | length(), TableOffset); |
| 56 | |
| 57 | HeaderData.Version = Data.getU16(OffsetPtr); |
| 58 | HeaderData.AddrSize = Data.getU8(OffsetPtr); |
| 59 | HeaderData.SegSize = Data.getU8(OffsetPtr); |
| 60 | HeaderData.OffsetEntryCount = Data.getU32(OffsetPtr); |
| 61 | |
| 62 | // Perform basic validation of the remaining header fields. |
| 63 | if (HeaderData.Version != 5) |
| 64 | return createError("unrecognised .debug_rnglists table version %" PRIu16 |
| 65 | " in table at offset 0x%" PRIx32, |
| 66 | HeaderData.Version, TableOffset); |
| 67 | if (HeaderData.AddrSize != 4 && HeaderData.AddrSize != 8) |
| 68 | return createError(".debug_rnglists table at offset 0x%" PRIx32 |
| 69 | " has unsupported address size %hhu", |
| 70 | TableOffset, HeaderData.AddrSize); |
| 71 | if (HeaderData.SegSize != 0) |
| 72 | return createError(".debug_rnglists table at offset 0x%" PRIx32 |
| 73 | " has unsupported segment selector size %" PRIu8, |
| 74 | TableOffset, HeaderData.SegSize); |
| 75 | if (End < TableOffset + sizeof(HeaderData) + |
| 76 | HeaderData.OffsetEntryCount * sizeof(uint32_t)) |
| 77 | return createError(".debug_rnglists table at offset 0x%" PRIx32 |
| 78 | " has more offset entries (%" PRIu32 |
| 79 | ") than there is space for", |
| 80 | TableOffset, HeaderData.OffsetEntryCount); |
| 81 | |
| 82 | Data.setAddressSize(HeaderData.AddrSize); |
| 83 | |
| 84 | for (uint32_t I = 0; I < HeaderData.OffsetEntryCount; ++I) |
| 85 | Offsets.push_back(Data.getU32(OffsetPtr)); |
| 86 | |
| 87 | DWARFAddressRangesVector CurrentRanges; |
| 88 | while (*OffsetPtr < End) { |
| 89 | uint8_t Encoding = Data.getU8(OffsetPtr); |
| 90 | switch (Encoding) { |
| 91 | case dwarf::DW_RLE_end_of_list: |
| 92 | Ranges.insert(Ranges.end(), CurrentRanges); |
| 93 | CurrentRanges.clear(); |
| 94 | break; |
| 95 | // TODO: Support other encodings. |
| 96 | case dwarf::DW_RLE_base_addressx: |
| 97 | return createError("unsupported rnglists encoding DW_RLE_base_addressx " |
| 98 | "at offset 0x%" PRIx32, |
| 99 | *OffsetPtr - 1); |
| 100 | case dwarf::DW_RLE_startx_endx: |
| 101 | return createError("unsupported rnglists encoding DW_RLE_startx_endx at " |
| 102 | "offset 0x%" PRIx32, |
| 103 | *OffsetPtr - 1); |
| 104 | case dwarf::DW_RLE_startx_length: |
| 105 | return createError("unsupported rnglists encoding DW_RLE_startx_length " |
| 106 | "at offset 0x%" PRIx32, |
| 107 | *OffsetPtr - 1); |
| 108 | case dwarf::DW_RLE_offset_pair: |
| 109 | return createError("unsupported rnglists encoding DW_RLE_offset_pair at " |
| 110 | "offset 0x%" PRIx32, |
| 111 | *OffsetPtr - 1); |
| 112 | case dwarf::DW_RLE_base_address: |
| 113 | return createError("unsupported rnglists encoding DW_RLE_base_address at " |
| 114 | "offset 0x%" PRIx32, |
| 115 | *OffsetPtr - 1); |
| 116 | case dwarf::DW_RLE_start_end: { |
| 117 | if (End - *OffsetPtr < HeaderData.AddrSize * 2) |
| 118 | return createError("insufficient space remaining in table for " |
| 119 | "DW_RLE_start_end encoding " |
| 120 | "at offset 0x%" PRIx32, |
| 121 | *OffsetPtr - 1); |
| 122 | uint64_t Start = Data.getAddress(OffsetPtr); |
| 123 | uint64_t End = Data.getAddress(OffsetPtr); |
| 124 | CurrentRanges.emplace_back(Start, End); |
| 125 | break; |
| 126 | } |
| 127 | case dwarf::DW_RLE_start_length: { |
| 128 | uint32_t PreviousOffset = *OffsetPtr - 1; |
| 129 | uint64_t Start = Data.getAddress(OffsetPtr); |
| 130 | uint64_t Length = Data.getULEB128(OffsetPtr); |
| 131 | if (End < *OffsetPtr) |
| 132 | return createError("read past end of table when reading " |
| 133 | "DW_RLE_start_length encoding at offset 0x%" PRIx32, |
| 134 | PreviousOffset); |
| 135 | CurrentRanges.emplace_back(Start, Start + Length); |
| 136 | break; |
| 137 | } |
| 138 | default: |
| 139 | Ranges.insert(Ranges.end(), CurrentRanges); |
| 140 | return createError("unknown rnglists encoding 0x%" PRIx32 |
| 141 | " at offset 0x%" PRIx32, |
| 142 | uint32_t(Encoding), *OffsetPtr - 1); |
| 143 | } |
| 144 | } |
| 145 | |
| 146 | // If OffsetPtr does not indicate the End offset, then either the above loop |
| 147 | // terminated prematurely, or we encountered a malformed encoding, but did not |
| 148 | // report an error when we should have done. |
| 149 | assert(*OffsetPtr == End && |
| 150 | "did not detect malformed data or loop ended unexpectedly"); |
| 151 | |
| 152 | // If CurrentRanges is not empty, we have a malformed section, because we did |
| 153 | // not find a DW_RLE_end_of_list marker at the end of the last list. |
| 154 | if (!CurrentRanges.empty()) |
| 155 | return createError( |
| 156 | "no end of list marker detected at end of .debug_rnglists table " |
| 157 | "starting at offset 0x%" PRIx32, |
| 158 | TableOffset); |
| 159 | return Error::success(); |
| 160 | } |
| 161 | |
| 162 | void DWARFDebugRnglists::dump(raw_ostream &OS) const { |
| 163 | // TODO: Add verbose printing of the raw encodings. |
| 164 | OS << format("Range List Header: length = 0x%8.8x, version = 0x%4.4x, " |
| 165 | "addr_size = 0x%2.2x, seg_size = 0x%2.2x, offset_entry_count = " |
| 166 | "0x%8.8x\n", |
| 167 | HeaderData.Length, HeaderData.Version, HeaderData.AddrSize, |
| 168 | HeaderData.SegSize, HeaderData.OffsetEntryCount); |
| 169 | |
| 170 | if (HeaderData.OffsetEntryCount > 0) { |
| 171 | OS << "Offsets: ["; |
| 172 | for (const auto &Off : Offsets) |
| 173 | OS << format("\n0x%8.8x", Off); |
| 174 | OS << "\n]\n"; |
| 175 | } |
| 176 | OS << "Ranges:\n"; |
| 177 | |
| 178 | const uint32_t HexWidth = HeaderData.AddrSize * 2; |
| 179 | for (const auto &List : Ranges) { |
| 180 | for (const auto &Entry : List) |
| 181 | OS << format("[0x%*.*" PRIx64 ", 0x%*.*" PRIx64 ")\n", HexWidth, HexWidth, |
| 182 | Entry.LowPC, HexWidth, HexWidth, Entry.HighPC); |
| 183 | OS << "<End of list>\n"; |
| 184 | } |
| 185 | } |
| 186 | |
| 187 | uint64_t DWARFDebugRnglists::length() const { |
| 188 | if (HeaderData.Length == 0) |
| 189 | return 0; |
| 190 | // TODO: DWARF64 support. |
| 191 | return HeaderData.Length + sizeof(uint32_t); |
| 192 | } |