blob: 668527a4d81bb7a145aabccb129daf9a14f8f7b9 [file] [log] [blame]
Christopher Ferris61d40972017-06-12 19:14:20 -07001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Christopher Ferris61d40972017-06-12 19:14:20 -070017#include <stdint.h>
18
Christopher Ferris2fcf4cf2018-01-23 17:52:23 -080019#include <unwindstack/DwarfError.h>
Christopher Ferrisd226a512017-07-14 10:37:19 -070020#include <unwindstack/DwarfStructs.h>
21#include <unwindstack/Memory.h>
22
Christopher Ferris94167032017-06-28 18:56:52 -070023#include "Check.h"
Christopher Ferrisc9dee842017-11-03 14:50:27 -070024#include "DwarfEhFrameWithHdr.h"
Christopher Ferris4cc36d22018-06-06 14:47:31 -070025#include "DwarfEncoding.h"
Christopher Ferrisd226a512017-07-14 10:37:19 -070026
27namespace unwindstack {
Christopher Ferris61d40972017-06-12 19:14:20 -070028
Christopher Ferris4cc36d22018-06-06 14:47:31 -070029static inline bool IsEncodingRelative(uint8_t encoding) {
30 encoding >>= 4;
31 return encoding > 0 && encoding <= DW_EH_PE_funcrel;
32}
33
Christopher Ferris61d40972017-06-12 19:14:20 -070034template <typename AddressType>
Christopher Ferris4cc36d22018-06-06 14:47:31 -070035bool DwarfEhFrameWithHdr<AddressType>::Init(uint64_t offset, uint64_t size, uint64_t load_bias) {
36 load_bias_ = load_bias;
Christopher Ferris61d40972017-06-12 19:14:20 -070037
38 memory_.clear_func_offset();
39 memory_.clear_text_offset();
40 memory_.set_data_offset(offset);
41 memory_.set_cur_offset(offset);
Christopher Ferris92acaac2018-06-21 10:44:02 -070042 pc_offset_ = offset;
Christopher Ferris61d40972017-06-12 19:14:20 -070043
44 // Read the first four bytes all at once.
Christopher Ferris4cc36d22018-06-06 14:47:31 -070045 uint8_t data[4];
Christopher Ferris61d40972017-06-12 19:14:20 -070046 if (!memory_.ReadBytes(data, 4)) {
Christopher Ferris2fcf4cf2018-01-23 17:52:23 -080047 last_error_.code = DWARF_ERROR_MEMORY_INVALID;
48 last_error_.address = memory_.cur_offset();
Christopher Ferris61d40972017-06-12 19:14:20 -070049 return false;
50 }
51
52 version_ = data[0];
53 if (version_ != 1) {
54 // Unknown version.
Christopher Ferris2fcf4cf2018-01-23 17:52:23 -080055 last_error_.code = DWARF_ERROR_UNSUPPORTED_VERSION;
Christopher Ferris61d40972017-06-12 19:14:20 -070056 return false;
57 }
58
59 ptr_encoding_ = data[1];
60 uint8_t fde_count_encoding = data[2];
61 table_encoding_ = data[3];
62 table_entry_size_ = memory_.template GetEncodedSize<AddressType>(table_encoding_);
63
64 memory_.set_pc_offset(memory_.cur_offset());
65 if (!memory_.template ReadEncodedValue<AddressType>(ptr_encoding_, &ptr_offset_)) {
Christopher Ferris2fcf4cf2018-01-23 17:52:23 -080066 last_error_.code = DWARF_ERROR_MEMORY_INVALID;
67 last_error_.address = memory_.cur_offset();
Christopher Ferris61d40972017-06-12 19:14:20 -070068 return false;
69 }
70
71 memory_.set_pc_offset(memory_.cur_offset());
72 if (!memory_.template ReadEncodedValue<AddressType>(fde_count_encoding, &fde_count_)) {
Christopher Ferris2fcf4cf2018-01-23 17:52:23 -080073 last_error_.code = DWARF_ERROR_MEMORY_INVALID;
74 last_error_.address = memory_.cur_offset();
Christopher Ferris61d40972017-06-12 19:14:20 -070075 return false;
76 }
77
Christopher Ferris1a141a02018-01-24 08:52:47 -080078 if (fde_count_ == 0) {
Christopher Ferris2fcf4cf2018-01-23 17:52:23 -080079 last_error_.code = DWARF_ERROR_NO_FDES;
Christopher Ferris1a141a02018-01-24 08:52:47 -080080 return false;
81 }
82
Christopher Ferris61d40972017-06-12 19:14:20 -070083 entries_offset_ = memory_.cur_offset();
84 entries_end_ = offset + size;
85 entries_data_offset_ = offset;
86 cur_entries_offset_ = entries_offset_;
87
88 return true;
89}
90
91template <typename AddressType>
Christopher Ferris92acaac2018-06-21 10:44:02 -070092const DwarfFde* DwarfEhFrameWithHdr<AddressType>::GetFdeFromPc(uint64_t pc) {
93 uint64_t fde_offset;
94 if (!GetFdeOffsetFromPc(pc, &fde_offset)) {
Christopher Ferris61d40972017-06-12 19:14:20 -070095 return nullptr;
96 }
Christopher Ferris92acaac2018-06-21 10:44:02 -070097 const DwarfFde* fde = this->GetFdeFromOffset(fde_offset);
98 if (fde == nullptr) {
99 return nullptr;
100 }
101
102 // Guaranteed pc >= pc_start, need to check pc in the fde range.
103 if (pc < fde->pc_end) {
104 return fde;
105 }
106 last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
107 return nullptr;
Christopher Ferris61d40972017-06-12 19:14:20 -0700108}
109
110template <typename AddressType>
Christopher Ferrisc9dee842017-11-03 14:50:27 -0700111const typename DwarfEhFrameWithHdr<AddressType>::FdeInfo*
112DwarfEhFrameWithHdr<AddressType>::GetFdeInfoFromIndex(size_t index) {
Christopher Ferris61d40972017-06-12 19:14:20 -0700113 auto entry = fde_info_.find(index);
114 if (entry != fde_info_.end()) {
115 return &fde_info_[index];
116 }
117 FdeInfo* info = &fde_info_[index];
118
119 memory_.set_data_offset(entries_data_offset_);
120 memory_.set_cur_offset(entries_offset_ + 2 * index * table_entry_size_);
Christopher Ferris4cc36d22018-06-06 14:47:31 -0700121 memory_.set_pc_offset(0);
Christopher Ferris61d40972017-06-12 19:14:20 -0700122 uint64_t value;
123 if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value) ||
124 !memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
Christopher Ferris2fcf4cf2018-01-23 17:52:23 -0800125 last_error_.code = DWARF_ERROR_MEMORY_INVALID;
126 last_error_.address = memory_.cur_offset();
Christopher Ferris61d40972017-06-12 19:14:20 -0700127 fde_info_.erase(index);
128 return nullptr;
129 }
Christopher Ferris4cc36d22018-06-06 14:47:31 -0700130
131 // Relative encodings require adding in the load bias.
132 if (IsEncodingRelative(table_encoding_)) {
133 value += load_bias_;
134 }
Christopher Ferrise37e2d02018-02-09 15:57:39 -0800135 info->pc = value;
Christopher Ferris61d40972017-06-12 19:14:20 -0700136 return info;
137}
138
139template <typename AddressType>
Christopher Ferrisc9dee842017-11-03 14:50:27 -0700140bool DwarfEhFrameWithHdr<AddressType>::GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset,
141 uint64_t total_entries) {
Christopher Ferris94167032017-06-28 18:56:52 -0700142 CHECK(fde_count_ > 0);
143 CHECK(total_entries <= fde_count_);
Christopher Ferris61d40972017-06-12 19:14:20 -0700144
145 size_t first = 0;
146 size_t last = total_entries;
147 while (first < last) {
148 size_t current = (first + last) / 2;
149 const FdeInfo* info = GetFdeInfoFromIndex(current);
Christopher Ferrisd96cbae2017-11-08 11:01:18 -0800150 if (info == nullptr) {
151 return false;
152 }
Christopher Ferris61d40972017-06-12 19:14:20 -0700153 if (pc == info->pc) {
154 *fde_offset = info->offset;
155 return true;
156 }
157 if (pc < info->pc) {
158 last = current;
159 } else {
160 first = current + 1;
161 }
162 }
163 if (last != 0) {
164 const FdeInfo* info = GetFdeInfoFromIndex(last - 1);
Christopher Ferrisd96cbae2017-11-08 11:01:18 -0800165 if (info == nullptr) {
166 return false;
167 }
Christopher Ferris61d40972017-06-12 19:14:20 -0700168 *fde_offset = info->offset;
169 return true;
170 }
171 return false;
172}
173
174template <typename AddressType>
Christopher Ferrisc9dee842017-11-03 14:50:27 -0700175bool DwarfEhFrameWithHdr<AddressType>::GetFdeOffsetSequential(uint64_t pc, uint64_t* fde_offset) {
Christopher Ferris94167032017-06-28 18:56:52 -0700176 CHECK(fde_count_ != 0);
Christopher Ferris2fcf4cf2018-01-23 17:52:23 -0800177 last_error_.code = DWARF_ERROR_NONE;
178 last_error_.address = 0;
179
Christopher Ferris61d40972017-06-12 19:14:20 -0700180 // We can do a binary search if the pc is in the range of the elements
181 // that have already been cached.
182 if (!fde_info_.empty()) {
183 const FdeInfo* info = &fde_info_[fde_info_.size() - 1];
184 if (pc >= info->pc) {
185 *fde_offset = info->offset;
186 return true;
187 }
188 if (pc < info->pc) {
189 return GetFdeOffsetBinary(pc, fde_offset, fde_info_.size());
190 }
191 }
192
193 if (cur_entries_offset_ == 0) {
194 // All entries read, or error encountered.
195 return false;
196 }
197
198 memory_.set_data_offset(entries_data_offset_);
199 memory_.set_cur_offset(cur_entries_offset_);
Christopher Ferris4cc36d22018-06-06 14:47:31 -0700200 memory_.set_pc_offset(0);
Christopher Ferris61d40972017-06-12 19:14:20 -0700201 cur_entries_offset_ = 0;
202
203 FdeInfo* prev_info = nullptr;
204 for (size_t current = fde_info_.size();
205 current < fde_count_ && memory_.cur_offset() < entries_end_; current++) {
Christopher Ferris61d40972017-06-12 19:14:20 -0700206 FdeInfo* info = &fde_info_[current];
Christopher Ferris4cc36d22018-06-06 14:47:31 -0700207 uint64_t value;
208 if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value) ||
209 !memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
Christopher Ferris61d40972017-06-12 19:14:20 -0700210 fde_info_.erase(current);
Christopher Ferris2fcf4cf2018-01-23 17:52:23 -0800211 last_error_.code = DWARF_ERROR_MEMORY_INVALID;
212 last_error_.address = memory_.cur_offset();
Christopher Ferris61d40972017-06-12 19:14:20 -0700213 return false;
214 }
Christopher Ferris4cc36d22018-06-06 14:47:31 -0700215
216 // Relative encodings require adding in the load bias.
217 if (IsEncodingRelative(table_encoding_)) {
218 value += load_bias_;
219 }
220 info->pc = value;
Christopher Ferris61d40972017-06-12 19:14:20 -0700221
222 if (pc < info->pc) {
223 if (prev_info == nullptr) {
224 return false;
225 }
226 cur_entries_offset_ = memory_.cur_offset();
227 *fde_offset = prev_info->offset;
228 return true;
229 }
230 prev_info = info;
231 }
232
233 if (fde_count_ == fde_info_.size() && pc >= prev_info->pc) {
234 *fde_offset = prev_info->offset;
235 return true;
236 }
237 return false;
238}
239
240template <typename AddressType>
Christopher Ferrisc9dee842017-11-03 14:50:27 -0700241bool DwarfEhFrameWithHdr<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
Christopher Ferris61d40972017-06-12 19:14:20 -0700242 if (fde_count_ == 0) {
243 return false;
244 }
245
246 if (table_entry_size_ > 0) {
247 // Do a binary search since the size of each table entry is fixed.
248 return GetFdeOffsetBinary(pc, fde_offset, fde_count_);
249 } else {
250 // Do a sequential search since each table entry size is variable.
251 return GetFdeOffsetSequential(pc, fde_offset);
252 }
253}
254
Christopher Ferris92acaac2018-06-21 10:44:02 -0700255template <typename AddressType>
256void DwarfEhFrameWithHdr<AddressType>::GetFdes(std::vector<const DwarfFde*>* fdes) {
257 for (size_t i = 0; i < fde_count_; i++) {
258 const FdeInfo* info = GetFdeInfoFromIndex(i);
259 if (info == nullptr) {
260 break;
261 }
262 const DwarfFde* fde = this->GetFdeFromOffset(info->offset);
263 if (fde == nullptr) {
264 break;
265 }
266 fdes->push_back(fde);
267 }
268}
269
Christopher Ferrisc9dee842017-11-03 14:50:27 -0700270// Explicitly instantiate DwarfEhFrameWithHdr
271template class DwarfEhFrameWithHdr<uint32_t>;
272template class DwarfEhFrameWithHdr<uint64_t>;
Christopher Ferrisd226a512017-07-14 10:37:19 -0700273
274} // namespace unwindstack