1 | //===-- DWARFDebugArangeSet.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 "DWARFDebugArangeSet.h" |
10 | #include "DWARFDataExtractor.h" |
11 | #include "LogChannelDWARF.h" |
12 | #include "llvm/Object/Error.h" |
13 | #include <cassert> |
14 | |
15 | using namespace lldb_private; |
16 | using namespace lldb_private::plugin::dwarf; |
17 | |
18 | DWARFDebugArangeSet::DWARFDebugArangeSet() |
19 | : m_offset(DW_INVALID_OFFSET), m_next_offset(DW_INVALID_OFFSET) {} |
20 | |
21 | void DWARFDebugArangeSet::Clear() { |
22 | m_offset = DW_INVALID_OFFSET; |
23 | m_next_offset = DW_INVALID_OFFSET; |
24 | m_header.length = 0; |
25 | m_header.version = 0; |
26 | m_header.cu_offset = 0; |
27 | m_header.addr_size = 0; |
28 | m_header.seg_size = 0; |
29 | m_arange_descriptors.clear(); |
30 | } |
31 | |
32 | llvm::Error DWARFDebugArangeSet::(const DWARFDataExtractor &data, |
33 | lldb::offset_t *offset_ptr) { |
34 | assert(data.ValidOffset(*offset_ptr)); |
35 | |
36 | m_arange_descriptors.clear(); |
37 | m_offset = *offset_ptr; |
38 | |
39 | // 7.20 Address Range Table |
40 | // |
41 | // Each set of entries in the table of address ranges contained in the |
42 | // .debug_aranges section begins with a header consisting of: a 4-byte |
43 | // length containing the length of the set of entries for this compilation |
44 | // unit, not including the length field itself; a 2-byte version identifier |
45 | // containing the value 2 for DWARF Version 2; a 4-byte offset into |
46 | // the.debug_infosection; a 1-byte unsigned integer containing the size in |
47 | // bytes of an address (or the offset portion of an address for segmented |
48 | // addressing) on the target system; and a 1-byte unsigned integer |
49 | // containing the size in bytes of a segment descriptor on the target |
50 | // system. This header is followed by a series of tuples. Each tuple |
51 | // consists of an address and a length, each in the size appropriate for an |
52 | // address on the target architecture. |
53 | m_header.length = data.GetDWARFInitialLength(offset_ptr); |
54 | // The length could be 4 bytes or 12 bytes, so use the current offset to |
55 | // determine the next offset correctly. |
56 | if (m_header.length > 0) |
57 | m_next_offset = *offset_ptr + m_header.length; |
58 | else |
59 | m_next_offset = DW_INVALID_OFFSET; |
60 | m_header.version = data.GetU16(offset_ptr); |
61 | m_header.cu_offset = data.GetDWARFOffset(offset_ptr); |
62 | m_header.addr_size = data.GetU8(offset_ptr); |
63 | m_header.seg_size = data.GetU8(offset_ptr); |
64 | |
65 | // Try to avoid reading invalid arange sets by making sure: |
66 | // 1 - the version looks good |
67 | // 2 - the address byte size looks plausible |
68 | // 3 - the length seems to make sense |
69 | // 4 - size looks plausible |
70 | // 5 - the arange tuples do not contain a segment field |
71 | if (m_header.version < 2 || m_header.version > 5) |
72 | return llvm::make_error<llvm::object::GenericBinaryError>( |
73 | Args: "Invalid arange header version" ); |
74 | |
75 | if (m_header.addr_size != 4 && m_header.addr_size != 8) |
76 | return llvm::make_error<llvm::object::GenericBinaryError>( |
77 | Args: "Invalid arange header address size" ); |
78 | |
79 | if (m_header.length == 0) |
80 | return llvm::make_error<llvm::object::GenericBinaryError>( |
81 | Args: "Invalid arange header length" ); |
82 | |
83 | if (!data.ValidOffset(offset: m_offset + sizeof(m_header.length) + m_header.length - |
84 | 1)) |
85 | return llvm::make_error<llvm::object::GenericBinaryError>( |
86 | Args: "Invalid arange header length" ); |
87 | |
88 | if (m_header.seg_size) |
89 | return llvm::make_error<llvm::object::GenericBinaryError>( |
90 | Args: "segmented arange entries are not supported" ); |
91 | |
92 | // The first tuple following the header in each set begins at an offset |
93 | // that is a multiple of the size of a single tuple (that is, twice the |
94 | // size of an address). The header is padded, if necessary, to the |
95 | // appropriate boundary. |
96 | const uint32_t = *offset_ptr - m_offset; |
97 | const uint32_t tuple_size = m_header.addr_size << 1; |
98 | uint32_t first_tuple_offset = 0; |
99 | while (first_tuple_offset < header_size) |
100 | first_tuple_offset += tuple_size; |
101 | |
102 | *offset_ptr = m_offset + first_tuple_offset; |
103 | |
104 | Descriptor arangeDescriptor; |
105 | |
106 | static_assert(sizeof(arangeDescriptor.address) == |
107 | sizeof(arangeDescriptor.length), |
108 | "DWARFDebugArangeSet::Descriptor.address and " |
109 | "DWARFDebugArangeSet::Descriptor.length must have same size" ); |
110 | |
111 | const lldb::offset_t next_offset = GetNextOffset(); |
112 | assert(next_offset != DW_INVALID_OFFSET); |
113 | uint32_t num_terminators = 0; |
114 | bool last_was_terminator = false; |
115 | while (*offset_ptr < next_offset) { |
116 | arangeDescriptor.address = data.GetMaxU64(offset_ptr, byte_size: m_header.addr_size); |
117 | arangeDescriptor.length = data.GetMaxU64(offset_ptr, byte_size: m_header.addr_size); |
118 | |
119 | // Each set of tuples is terminated by a 0 for the address and 0 for |
120 | // the length. Some linkers can emit .debug_aranges with multiple |
121 | // terminator pair entries that are still withing the length of the |
122 | // DWARFDebugArangeSet. We want to be sure to parse all entries for |
123 | // this DWARFDebugArangeSet so that we don't stop parsing early and end up |
124 | // treating addresses as a header of the next DWARFDebugArangeSet. We also |
125 | // need to make sure we parse all valid address pairs so we don't omit them |
126 | // from the aranges result, so we can't stop at the first terminator entry |
127 | // we find. |
128 | if (arangeDescriptor.address == 0 && arangeDescriptor.length == 0) { |
129 | ++num_terminators; |
130 | last_was_terminator = true; |
131 | } else { |
132 | last_was_terminator = false; |
133 | // Only add .debug_aranges address entries that have a non zero size. |
134 | // Some linkers will zero out the length field for some .debug_aranges |
135 | // entries if they were stripped. We also could watch out for multiple |
136 | // entries at address zero and remove those as well. |
137 | if (arangeDescriptor.length > 0) |
138 | m_arange_descriptors.push_back(x: arangeDescriptor); |
139 | } |
140 | } |
141 | if (num_terminators > 1) { |
142 | Log *log = GetLog(mask: DWARFLog::DebugInfo); |
143 | LLDB_LOG(log, |
144 | "warning: DWARFDebugArangeSet at %#" PRIx64 " contains %u " |
145 | "terminator entries" , |
146 | m_offset, num_terminators); |
147 | } |
148 | if (last_was_terminator) |
149 | return llvm::ErrorSuccess(); |
150 | |
151 | return llvm::make_error<llvm::object::GenericBinaryError>( |
152 | Args: "arange descriptors not terminated by null entry" ); |
153 | } |
154 | |
155 | class DescriptorContainsAddress { |
156 | public: |
157 | DescriptorContainsAddress(dw_addr_t address) : m_address(address) {} |
158 | bool operator()(const DWARFDebugArangeSet::Descriptor &desc) const { |
159 | return (m_address >= desc.address) && |
160 | (m_address < (desc.address + desc.length)); |
161 | } |
162 | |
163 | private: |
164 | const dw_addr_t m_address; |
165 | }; |
166 | |
167 | dw_offset_t DWARFDebugArangeSet::FindAddress(dw_addr_t address) const { |
168 | DescriptorConstIter end = m_arange_descriptors.end(); |
169 | DescriptorConstIter pos = |
170 | std::find_if(first: m_arange_descriptors.begin(), last: end, // Range |
171 | pred: DescriptorContainsAddress(address)); // Predicate |
172 | if (pos != end) |
173 | return m_header.cu_offset; |
174 | |
175 | return DW_INVALID_OFFSET; |
176 | } |
177 | |