1 //===-- DWARFDebugRanges.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 "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       DumpAddress(s.AsRawOstream(), base_addr, sizeof(dw_addr_t),
99                   " Base address = ");
100     } else {
101       // Convert from offset to an address
102       dw_addr_t begin_addr = begin + base_addr;
103       dw_addr_t end_addr = end + base_addr;
104 
105       DumpAddressRange(s.AsRawOstream(), begin_addr, end_addr,
106                        sizeof(dw_addr_t), nullptr);
107     }
108   }
109 }
110 
111 bool DWARFDebugRanges::FindRanges(const DWARFUnit *cu,
112                                   dw_offset_t debug_ranges_offset,
113                                   DWARFRangeList &range_list) const {
114   dw_addr_t debug_ranges_address = cu->GetRangesBase() + debug_ranges_offset;
115   range_map_const_iterator pos = m_range_map.find(debug_ranges_address);
116   if (pos != m_range_map.end()) {
117     range_list = pos->second;
118 
119     // All DW_AT_ranges are relative to the base address of the compile
120     // unit. We add the compile unit base address to make sure all the
121     // addresses are properly fixed up.
122     range_list.Slide(cu->GetBaseAddress());
123     return true;
124   }
125   return false;
126 }
127