1 //===-- DWARFDebugRanges.cpp ------------------------------------*- 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 #include "DWARFDebugRanges.h"
10 #include "DWARFUnit.h"
11 #include "lldb/Utility/Stream.h"
12 
13 using namespace lldb_private;
14 
15 static dw_addr_t GetBaseAddressMarker(uint32_t addr_size) {
16   switch(addr_size) {
17     case 2:
18       return 0xffff;
19     case 4:
20       return 0xffffffff;
21     case 8:
22       return 0xffffffffffffffff;
23   }
24   llvm_unreachable("GetBaseAddressMarker unsupported address size.");
25 }
26 
27 DWARFDebugRanges::DWARFDebugRanges() : m_range_map() {}
28 
29 void DWARFDebugRanges::Extract(DWARFContext &context) {
30   DWARFRangeList range_list;
31   lldb::offset_t offset = 0;
32   dw_offset_t debug_ranges_offset = offset;
33   while (Extract(context, &offset, range_list)) {
34     range_list.Sort();
35     m_range_map[debug_ranges_offset] = range_list;
36     debug_ranges_offset = offset;
37   }
38 }
39 
40 bool DWARFDebugRanges::Extract(DWARFContext &context,
41                                lldb::offset_t *offset_ptr,
42                                DWARFRangeList &range_list) {
43   range_list.Clear();
44 
45   lldb::offset_t range_offset = *offset_ptr;
46   const DWARFDataExtractor &debug_ranges_data = context.getOrLoadRangesData();
47   uint32_t addr_size = debug_ranges_data.GetAddressByteSize();
48   dw_addr_t base_addr = 0;
49   dw_addr_t base_addr_marker = GetBaseAddressMarker(addr_size);
50 
51   while (
52       debug_ranges_data.ValidOffsetForDataOfSize(*offset_ptr, 2 * addr_size)) {
53     dw_addr_t begin = debug_ranges_data.GetMaxU64(offset_ptr, addr_size);
54     dw_addr_t end = debug_ranges_data.GetMaxU64(offset_ptr, addr_size);
55 
56     if (!begin && !end) {
57       // End of range list
58       break;
59     }
60 
61     if (begin == base_addr_marker) {
62       base_addr = end;
63       continue;
64     }
65 
66     // Filter out empty ranges
67     if (begin < end)
68       range_list.Append(DWARFRangeList::Entry(begin + base_addr, end - begin));
69   }
70 
71   // Make sure we consumed at least something
72   return range_offset != *offset_ptr;
73 }
74 
75 void DWARFDebugRanges::Dump(Stream &s,
76                             const DWARFDataExtractor &debug_ranges_data,
77                             lldb::offset_t *offset_ptr,
78                             dw_addr_t cu_base_addr) {
79   uint32_t addr_size = s.GetAddressByteSize();
80 
81   dw_addr_t base_addr = cu_base_addr;
82   while (
83       debug_ranges_data.ValidOffsetForDataOfSize(*offset_ptr, 2 * addr_size)) {
84     dw_addr_t begin = debug_ranges_data.GetMaxU64(offset_ptr, addr_size);
85     dw_addr_t end = debug_ranges_data.GetMaxU64(offset_ptr, addr_size);
86     // Extend 4 byte addresses that consists of 32 bits of 1's to be 64 bits of
87     // ones
88     if (begin == 0xFFFFFFFFull && addr_size == 4)
89       begin = LLDB_INVALID_ADDRESS;
90 
91     s.Indent();
92     if (begin == 0 && end == 0) {
93       s.PutCString(" End");
94       break;
95     } else if (begin == LLDB_INVALID_ADDRESS) {
96       // A base address selection entry
97       base_addr = end;
98       s.Address(base_addr, sizeof(dw_addr_t), " Base address = ");
99     } else {
100       // Convert from offset to an address
101       dw_addr_t begin_addr = begin + base_addr;
102       dw_addr_t end_addr = end + base_addr;
103 
104       s.AddressRange(begin_addr, end_addr, sizeof(dw_addr_t), nullptr);
105     }
106   }
107 }
108 
109 bool DWARFDebugRanges::FindRanges(const DWARFUnit *cu,
110                                   dw_offset_t debug_ranges_offset,
111                                   DWARFRangeList &range_list) const {
112   dw_addr_t debug_ranges_address = cu->GetRangesBase() + debug_ranges_offset;
113   range_map_const_iterator pos = m_range_map.find(debug_ranges_address);
114   if (pos != m_range_map.end()) {
115     range_list = pos->second;
116 
117     // All DW_AT_ranges are relative to the base address of the compile
118     // unit. We add the compile unit base address to make sure all the
119     // addresses are properly fixed up.
120     range_list.Slide(cu->GetBaseAddress());
121     return true;
122   }
123   return false;
124 }
125 
126 bool DWARFDebugRngLists::ExtractRangeList(
127     const DWARFDataExtractor &data, uint8_t addrSize,
128     lldb::offset_t *offset_ptr, std::vector<RngListEntry> &rangeList) {
129   rangeList.clear();
130 
131   bool error = false;
132   while (!error) {
133     switch (data.GetU8(offset_ptr)) {
134     case DW_RLE_end_of_list:
135       return true;
136 
137     case DW_RLE_start_length: {
138       dw_addr_t begin = data.GetMaxU64(offset_ptr, addrSize);
139       dw_addr_t len = data.GetULEB128(offset_ptr);
140       rangeList.push_back({DW_RLE_start_length, begin, len});
141       break;
142     }
143 
144     case DW_RLE_start_end: {
145       dw_addr_t begin = data.GetMaxU64(offset_ptr, addrSize);
146       dw_addr_t end = data.GetMaxU64(offset_ptr, addrSize);
147       rangeList.push_back({DW_RLE_start_end, begin, end});
148       break;
149     }
150 
151     case DW_RLE_base_address: {
152       dw_addr_t base = data.GetMaxU64(offset_ptr, addrSize);
153       rangeList.push_back({DW_RLE_base_address, base, 0});
154       break;
155     }
156 
157     case DW_RLE_offset_pair: {
158       dw_addr_t begin = data.GetULEB128(offset_ptr);
159       dw_addr_t end = data.GetULEB128(offset_ptr);
160       rangeList.push_back({DW_RLE_offset_pair, begin, end});
161       break;
162     }
163 
164     case DW_RLE_base_addressx: {
165       dw_addr_t base = data.GetULEB128(offset_ptr);
166       rangeList.push_back({DW_RLE_base_addressx, base, 0});
167       break;
168     }
169 
170     case DW_RLE_startx_endx: {
171       dw_addr_t start = data.GetULEB128(offset_ptr);
172       dw_addr_t end = data.GetULEB128(offset_ptr);
173       rangeList.push_back({DW_RLE_startx_endx, start, end});
174       break;
175     }
176 
177     case DW_RLE_startx_length: {
178       dw_addr_t start = data.GetULEB128(offset_ptr);
179       dw_addr_t length = data.GetULEB128(offset_ptr);
180       rangeList.push_back({DW_RLE_startx_length, start, length});
181       break;
182     }
183 
184     default:
185       lldbassert(0 && "unknown range list entry encoding");
186       error = true;
187     }
188   }
189 
190   return false;
191 }
192 
193 static uint64_t ReadAddressFromDebugAddrSection(const DWARFUnit *cu,
194                                                 uint32_t index) {
195   uint32_t index_size = cu->GetAddressByteSize();
196   dw_offset_t addr_base = cu->GetAddrBase();
197   lldb::offset_t offset = addr_base + index * index_size;
198   return cu->GetSymbolFileDWARF()
199       .GetDWARFContext()
200       .getOrLoadAddrData()
201       .GetMaxU64(&offset, index_size);
202 }
203 
204 bool DWARFDebugRngLists::FindRanges(const DWARFUnit *cu,
205                                     dw_offset_t debug_ranges_offset,
206                                     DWARFRangeList &range_list) const {
207   range_list.Clear();
208   dw_addr_t debug_ranges_address = cu->GetRangesBase() + debug_ranges_offset;
209   auto pos = m_range_map.find(debug_ranges_address);
210   if (pos != m_range_map.end()) {
211     dw_addr_t BaseAddr = cu->GetBaseAddress();
212     for (const RngListEntry &E : pos->second) {
213       switch (E.encoding) {
214       case DW_RLE_start_length:
215         range_list.Append(DWARFRangeList::Entry(E.value0, E.value1));
216         break;
217       case DW_RLE_base_address:
218         BaseAddr = E.value0;
219         break;
220       case DW_RLE_start_end:
221         range_list.Append(DWARFRangeList::Entry(E.value0, E.value1 - E.value0));
222         break;
223       case DW_RLE_offset_pair:
224         range_list.Append(
225             DWARFRangeList::Entry(BaseAddr + E.value0, E.value1 - E.value0));
226         break;
227       case DW_RLE_base_addressx: {
228         BaseAddr = ReadAddressFromDebugAddrSection(cu, E.value0);
229         break;
230       }
231       case DW_RLE_startx_endx: {
232         dw_addr_t start = ReadAddressFromDebugAddrSection(cu, E.value0);
233         dw_addr_t end = ReadAddressFromDebugAddrSection(cu, E.value1);
234         range_list.Append(DWARFRangeList::Entry(start, end - start));
235         break;
236       }
237       case DW_RLE_startx_length: {
238         dw_addr_t start = ReadAddressFromDebugAddrSection(cu, E.value0);
239         range_list.Append(DWARFRangeList::Entry(start, E.value1));
240         break;
241       }
242       default:
243         llvm_unreachable("unexpected encoding");
244       }
245     }
246     return true;
247   }
248   return false;
249 }
250 
251 void DWARFDebugRngLists::Extract(DWARFContext &context) {
252   const DWARFDataExtractor &data = context.getOrLoadRngListsData();
253   lldb::offset_t offset = 0;
254 
255   uint64_t length = data.GetU32(&offset);
256   // FIXME: Handle DWARF64.
257   lldb::offset_t end = offset + length;
258 
259   // Check version.
260   if (data.GetU16(&offset) < 5)
261     return;
262 
263   uint8_t addrSize = data.GetU8(&offset);
264 
265   // We do not support non-zero segment selector size.
266   if (data.GetU8(&offset) != 0) {
267     lldbassert(0 && "not implemented");
268     return;
269   }
270 
271   uint32_t offsetsAmount = data.GetU32(&offset);
272   for (uint32_t i = 0; i < offsetsAmount; ++i)
273     Offsets.push_back(data.GetMaxU64(&offset, 4));
274 
275   lldb::offset_t listOffset = offset;
276   std::vector<RngListEntry> rangeList;
277   while (offset < end && ExtractRangeList(data, addrSize, &offset, rangeList)) {
278     m_range_map[listOffset] = rangeList;
279     listOffset = offset;
280   }
281 }
282 
283 uint64_t DWARFDebugRngLists::GetOffset(size_t Index) const {
284   return Offsets[Index];
285 }
286