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
13using namespace lldb_private;
14
15static 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
27DWARFDebugRanges::DWARFDebugRanges() : m_range_map() {}
28
29void 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
40bool 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
75void 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
111bool 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