1//===- DWARFDebugRnglists.cpp ---------------------------------------------===//
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#include "llvm/DebugInfo/DWARF/DWARFDebugRnglists.h"
10#include "llvm/BinaryFormat/Dwarf.h"
11#include "llvm/DebugInfo/DWARF/DWARFFormValue.h"
12#include "llvm/DebugInfo/DWARF/DWARFUnit.h"
13#include "llvm/Support/Errc.h"
14#include "llvm/Support/Error.h"
15#include "llvm/Support/Format.h"
16#include "llvm/Support/raw_ostream.h"
17
18using namespace llvm;
19
20Error RangeListEntry::extract(DWARFDataExtractor Data, uint64_t *OffsetPtr) {
21 Offset = *OffsetPtr;
22 SectionIndex = -1ULL;
23 // The caller should guarantee that we have at least 1 byte available, so
24 // we just assert instead of revalidate.
25 assert(*OffsetPtr < Data.size() &&
26 "not enough space to extract a rangelist encoding");
27 uint8_t Encoding = Data.getU8(offset_ptr: OffsetPtr);
28
29 DataExtractor::Cursor C(*OffsetPtr);
30 switch (Encoding) {
31 case dwarf::DW_RLE_end_of_list:
32 Value0 = Value1 = 0;
33 break;
34 // TODO: Support other encodings.
35 case dwarf::DW_RLE_base_addressx: {
36 Value0 = Data.getULEB128(C);
37 break;
38 }
39 case dwarf::DW_RLE_startx_endx:
40 Value0 = Data.getULEB128(C);
41 Value1 = Data.getULEB128(C);
42 break;
43 case dwarf::DW_RLE_startx_length: {
44 Value0 = Data.getULEB128(C);
45 Value1 = Data.getULEB128(C);
46 break;
47 }
48 case dwarf::DW_RLE_offset_pair: {
49 Value0 = Data.getULEB128(C);
50 Value1 = Data.getULEB128(C);
51 break;
52 }
53 case dwarf::DW_RLE_base_address: {
54 Value0 = Data.getRelocatedAddress(C, SecIx: &SectionIndex);
55 break;
56 }
57 case dwarf::DW_RLE_start_end: {
58 Value0 = Data.getRelocatedAddress(C, SecIx: &SectionIndex);
59 Value1 = Data.getRelocatedAddress(C);
60 break;
61 }
62 case dwarf::DW_RLE_start_length: {
63 Value0 = Data.getRelocatedAddress(C, SecIx: &SectionIndex);
64 Value1 = Data.getULEB128(C);
65 break;
66 }
67 default:
68 consumeError(Err: C.takeError());
69 return createStringError(EC: errc::not_supported,
70 Fmt: "unknown rnglists encoding 0x%" PRIx32
71 " at offset 0x%" PRIx64,
72 Vals: uint32_t(Encoding), Vals: Offset);
73 }
74
75 if (!C) {
76 consumeError(Err: C.takeError());
77 return createStringError(
78 EC: errc::invalid_argument,
79 Fmt: "read past end of table when reading %s encoding at offset 0x%" PRIx64,
80 Vals: dwarf::RLEString(RLE: Encoding).data(), Vals: Offset);
81 }
82
83 *OffsetPtr = C.tell();
84 EntryKind = Encoding;
85 return Error::success();
86}
87
88DWARFAddressRangesVector DWARFDebugRnglist::getAbsoluteRanges(
89 std::optional<object::SectionedAddress> BaseAddr, DWARFUnit &U) const {
90 return getAbsoluteRanges(
91 BaseAddr, AddressByteSize: U.getAddressByteSize(),
92 LookupPooledAddress: [&](uint32_t Index) { return U.getAddrOffsetSectionItem(Index); });
93}
94
95DWARFAddressRangesVector DWARFDebugRnglist::getAbsoluteRanges(
96 std::optional<object::SectionedAddress> BaseAddr, uint8_t AddressByteSize,
97 function_ref<std::optional<object::SectionedAddress>(uint32_t)>
98 LookupPooledAddress) const {
99 DWARFAddressRangesVector Res;
100 uint64_t Tombstone = dwarf::computeTombstoneAddress(AddressByteSize);
101 for (const RangeListEntry &RLE : Entries) {
102 if (RLE.EntryKind == dwarf::DW_RLE_end_of_list)
103 break;
104 if (RLE.EntryKind == dwarf::DW_RLE_base_addressx) {
105 BaseAddr = LookupPooledAddress(RLE.Value0);
106 if (!BaseAddr)
107 BaseAddr = {.Address: RLE.Value0, .SectionIndex: -1ULL};
108 continue;
109 }
110 if (RLE.EntryKind == dwarf::DW_RLE_base_address) {
111 BaseAddr = {.Address: RLE.Value0, .SectionIndex: RLE.SectionIndex};
112 continue;
113 }
114
115 DWARFAddressRange E;
116 E.SectionIndex = RLE.SectionIndex;
117 if (BaseAddr && E.SectionIndex == -1ULL)
118 E.SectionIndex = BaseAddr->SectionIndex;
119
120 switch (RLE.EntryKind) {
121 case dwarf::DW_RLE_offset_pair:
122 E.LowPC = RLE.Value0;
123 if (E.LowPC == Tombstone)
124 continue;
125 E.HighPC = RLE.Value1;
126 if (BaseAddr) {
127 if (BaseAddr->Address == Tombstone)
128 continue;
129 E.LowPC += BaseAddr->Address;
130 E.HighPC += BaseAddr->Address;
131 }
132 break;
133 case dwarf::DW_RLE_start_end:
134 E.LowPC = RLE.Value0;
135 E.HighPC = RLE.Value1;
136 break;
137 case dwarf::DW_RLE_start_length:
138 E.LowPC = RLE.Value0;
139 E.HighPC = E.LowPC + RLE.Value1;
140 break;
141 case dwarf::DW_RLE_startx_length: {
142 auto Start = LookupPooledAddress(RLE.Value0);
143 if (!Start)
144 Start = {.Address: 0, .SectionIndex: -1ULL};
145 E.SectionIndex = Start->SectionIndex;
146 E.LowPC = Start->Address;
147 E.HighPC = E.LowPC + RLE.Value1;
148 break;
149 }
150 case dwarf::DW_RLE_startx_endx: {
151 auto Start = LookupPooledAddress(RLE.Value0);
152 if (!Start)
153 Start = {.Address: 0, .SectionIndex: -1ULL};
154 auto End = LookupPooledAddress(RLE.Value1);
155 if (!End)
156 End = {.Address: 0, .SectionIndex: -1ULL};
157 // FIXME: Some error handling if Start.SectionIndex != End.SectionIndex
158 E.SectionIndex = Start->SectionIndex;
159 E.LowPC = Start->Address;
160 E.HighPC = End->Address;
161 break;
162 }
163 default:
164 // Unsupported encodings should have been reported during extraction,
165 // so we should not run into any here.
166 llvm_unreachable("Unsupported range list encoding");
167 }
168 if (E.LowPC == Tombstone)
169 continue;
170 Res.push_back(x: E);
171 }
172 return Res;
173}
174
175void RangeListEntry::dump(
176 raw_ostream &OS, uint8_t AddrSize, uint8_t MaxEncodingStringLength,
177 uint64_t &CurrentBase, DIDumpOptions DumpOpts,
178 llvm::function_ref<std::optional<object::SectionedAddress>(uint32_t)>
179 LookupPooledAddress) const {
180 auto PrintRawEntry = [](raw_ostream &OS, const RangeListEntry &Entry,
181 uint8_t AddrSize, DIDumpOptions DumpOpts) {
182 if (DumpOpts.Verbose) {
183 DumpOpts.DisplayRawContents = true;
184 DWARFAddressRange(Entry.Value0, Entry.Value1)
185 .dump(OS, AddressSize: AddrSize, DumpOpts);
186 OS << " => ";
187 }
188 };
189
190 if (DumpOpts.Verbose) {
191 // Print the section offset in verbose mode.
192 OS << format(Fmt: "0x%8.8" PRIx64 ":", Vals: Offset);
193 auto EncodingString = dwarf::RangeListEncodingString(Encoding: EntryKind);
194 // Unsupported encodings should have been reported during parsing.
195 assert(!EncodingString.empty() && "Unknown range entry encoding");
196 OS << format(Fmt: " [%s%*c", Vals: EncodingString.data(),
197 Vals: MaxEncodingStringLength - EncodingString.size() + 1, Vals: ']');
198 if (EntryKind != dwarf::DW_RLE_end_of_list)
199 OS << ": ";
200 }
201
202 uint64_t Tombstone = dwarf::computeTombstoneAddress(AddressByteSize: AddrSize);
203
204 switch (EntryKind) {
205 case dwarf::DW_RLE_end_of_list:
206 OS << (DumpOpts.Verbose ? "" : "<End of list>");
207 break;
208 case dwarf::DW_RLE_base_addressx: {
209 if (auto SA = LookupPooledAddress(Value0))
210 CurrentBase = SA->Address;
211 else
212 CurrentBase = Value0;
213 if (!DumpOpts.Verbose)
214 return;
215 DWARFFormValue::dumpAddress(OS&: OS << ' ', AddressSize: AddrSize, Address: Value0);
216 break;
217 }
218 case dwarf::DW_RLE_base_address:
219 // In non-verbose mode we do not print anything for this entry.
220 CurrentBase = Value0;
221 if (!DumpOpts.Verbose)
222 return;
223 DWARFFormValue::dumpAddress(OS&: OS << ' ', AddressSize: AddrSize, Address: Value0);
224 break;
225 case dwarf::DW_RLE_start_length:
226 PrintRawEntry(OS, *this, AddrSize, DumpOpts);
227 DWARFAddressRange(Value0, Value0 + Value1).dump(OS, AddressSize: AddrSize, DumpOpts);
228 break;
229 case dwarf::DW_RLE_offset_pair:
230 PrintRawEntry(OS, *this, AddrSize, DumpOpts);
231 if (CurrentBase != Tombstone)
232 DWARFAddressRange(Value0 + CurrentBase, Value1 + CurrentBase)
233 .dump(OS, AddressSize: AddrSize, DumpOpts);
234 else
235 OS << "dead code";
236 break;
237 case dwarf::DW_RLE_start_end:
238 DWARFAddressRange(Value0, Value1).dump(OS, AddressSize: AddrSize, DumpOpts);
239 break;
240 case dwarf::DW_RLE_startx_length: {
241 PrintRawEntry(OS, *this, AddrSize, DumpOpts);
242 uint64_t Start = 0;
243 if (auto SA = LookupPooledAddress(Value0))
244 Start = SA->Address;
245 DWARFAddressRange(Start, Start + Value1).dump(OS, AddressSize: AddrSize, DumpOpts);
246 break;
247 }
248 case dwarf::DW_RLE_startx_endx: {
249 PrintRawEntry(OS, *this, AddrSize, DumpOpts);
250 uint64_t Start = 0;
251 if (auto SA = LookupPooledAddress(Value0))
252 Start = SA->Address;
253 uint64_t End = 0;
254 if (auto SA = LookupPooledAddress(Value1))
255 End = SA->Address;
256 DWARFAddressRange(Start, End).dump(OS, AddressSize: AddrSize, DumpOpts);
257 break;
258 }
259 default:
260 llvm_unreachable("Unsupported range list encoding");
261 }
262 OS << "\n";
263}
264