blob: fd6a457ef2df51e08029dda48fb4e1d734cdfa6a [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);
42
43 // Read the first four bytes all at once.
Christopher Ferris4cc36d22018-06-06 14:47:31 -070044 uint8_t data[4];
Christopher Ferris61d40972017-06-12 19:14:20 -070045 if (!memory_.ReadBytes(data, 4)) {
Christopher Ferris2fcf4cf2018-01-23 17:52:23 -080046 last_error_.code = DWARF_ERROR_MEMORY_INVALID;
47 last_error_.address = memory_.cur_offset();
Christopher Ferris61d40972017-06-12 19:14:20 -070048 return false;
49 }
50
51 version_ = data[0];
52 if (version_ != 1) {
53 // Unknown version.
Christopher Ferris2fcf4cf2018-01-23 17:52:23 -080054 last_error_.code = DWARF_ERROR_UNSUPPORTED_VERSION;
Christopher Ferris61d40972017-06-12 19:14:20 -070055 return false;
56 }
57
58 ptr_encoding_ = data[1];
59 uint8_t fde_count_encoding = data[2];
60 table_encoding_ = data[3];
61 table_entry_size_ = memory_.template GetEncodedSize<AddressType>(table_encoding_);
62
63 memory_.set_pc_offset(memory_.cur_offset());
64 if (!memory_.template ReadEncodedValue<AddressType>(ptr_encoding_, &ptr_offset_)) {
Christopher Ferris2fcf4cf2018-01-23 17:52:23 -080065 last_error_.code = DWARF_ERROR_MEMORY_INVALID;
66 last_error_.address = memory_.cur_offset();
Christopher Ferris61d40972017-06-12 19:14:20 -070067 return false;
68 }
69
70 memory_.set_pc_offset(memory_.cur_offset());
71 if (!memory_.template ReadEncodedValue<AddressType>(fde_count_encoding, &fde_count_)) {
Christopher Ferris2fcf4cf2018-01-23 17:52:23 -080072 last_error_.code = DWARF_ERROR_MEMORY_INVALID;
73 last_error_.address = memory_.cur_offset();
Christopher Ferris61d40972017-06-12 19:14:20 -070074 return false;
75 }
76
Christopher Ferris1a141a02018-01-24 08:52:47 -080077 if (fde_count_ == 0) {
Christopher Ferris2fcf4cf2018-01-23 17:52:23 -080078 last_error_.code = DWARF_ERROR_NO_FDES;
Christopher Ferris1a141a02018-01-24 08:52:47 -080079 return false;
80 }
81
Christopher Ferris61d40972017-06-12 19:14:20 -070082 entries_offset_ = memory_.cur_offset();
83 entries_end_ = offset + size;
84 entries_data_offset_ = offset;
85 cur_entries_offset_ = entries_offset_;
86
87 return true;
88}
89
90template <typename AddressType>
Christopher Ferrisc9dee842017-11-03 14:50:27 -070091const DwarfFde* DwarfEhFrameWithHdr<AddressType>::GetFdeFromIndex(size_t index) {
Christopher Ferris61d40972017-06-12 19:14:20 -070092 const FdeInfo* info = GetFdeInfoFromIndex(index);
93 if (info == nullptr) {
94 return nullptr;
95 }
96 return this->GetFdeFromOffset(info->offset);
97}
98
99template <typename AddressType>
Christopher Ferrisc9dee842017-11-03 14:50:27 -0700100const typename DwarfEhFrameWithHdr<AddressType>::FdeInfo*
101DwarfEhFrameWithHdr<AddressType>::GetFdeInfoFromIndex(size_t index) {
Christopher Ferris61d40972017-06-12 19:14:20 -0700102 auto entry = fde_info_.find(index);
103 if (entry != fde_info_.end()) {
104 return &fde_info_[index];
105 }
106 FdeInfo* info = &fde_info_[index];
107
108 memory_.set_data_offset(entries_data_offset_);
109 memory_.set_cur_offset(entries_offset_ + 2 * index * table_entry_size_);
Christopher Ferris4cc36d22018-06-06 14:47:31 -0700110 memory_.set_pc_offset(0);
Christopher Ferris61d40972017-06-12 19:14:20 -0700111 uint64_t value;
112 if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value) ||
113 !memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
Christopher Ferris2fcf4cf2018-01-23 17:52:23 -0800114 last_error_.code = DWARF_ERROR_MEMORY_INVALID;
115 last_error_.address = memory_.cur_offset();
Christopher Ferris61d40972017-06-12 19:14:20 -0700116 fde_info_.erase(index);
117 return nullptr;
118 }
Christopher Ferris4cc36d22018-06-06 14:47:31 -0700119
120 // Relative encodings require adding in the load bias.
121 if (IsEncodingRelative(table_encoding_)) {
122 value += load_bias_;
123 }
Christopher Ferrise37e2d02018-02-09 15:57:39 -0800124 info->pc = value;
Christopher Ferris61d40972017-06-12 19:14:20 -0700125 return info;
126}
127
128template <typename AddressType>
Christopher Ferrisc9dee842017-11-03 14:50:27 -0700129bool DwarfEhFrameWithHdr<AddressType>::GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset,
130 uint64_t total_entries) {
Christopher Ferris94167032017-06-28 18:56:52 -0700131 CHECK(fde_count_ > 0);
132 CHECK(total_entries <= fde_count_);
Christopher Ferris61d40972017-06-12 19:14:20 -0700133
134 size_t first = 0;
135 size_t last = total_entries;
136 while (first < last) {
137 size_t current = (first + last) / 2;
138 const FdeInfo* info = GetFdeInfoFromIndex(current);
Christopher Ferrisd96cbae2017-11-08 11:01:18 -0800139 if (info == nullptr) {
140 return false;
141 }
Christopher Ferris61d40972017-06-12 19:14:20 -0700142 if (pc == info->pc) {
143 *fde_offset = info->offset;
144 return true;
145 }
146 if (pc < info->pc) {
147 last = current;
148 } else {
149 first = current + 1;
150 }
151 }
152 if (last != 0) {
153 const FdeInfo* info = GetFdeInfoFromIndex(last - 1);
Christopher Ferrisd96cbae2017-11-08 11:01:18 -0800154 if (info == nullptr) {
155 return false;
156 }
Christopher Ferris61d40972017-06-12 19:14:20 -0700157 *fde_offset = info->offset;
158 return true;
159 }
160 return false;
161}
162
163template <typename AddressType>
Christopher Ferrisc9dee842017-11-03 14:50:27 -0700164bool DwarfEhFrameWithHdr<AddressType>::GetFdeOffsetSequential(uint64_t pc, uint64_t* fde_offset) {
Christopher Ferris94167032017-06-28 18:56:52 -0700165 CHECK(fde_count_ != 0);
Christopher Ferris2fcf4cf2018-01-23 17:52:23 -0800166 last_error_.code = DWARF_ERROR_NONE;
167 last_error_.address = 0;
168
Christopher Ferris61d40972017-06-12 19:14:20 -0700169 // We can do a binary search if the pc is in the range of the elements
170 // that have already been cached.
171 if (!fde_info_.empty()) {
172 const FdeInfo* info = &fde_info_[fde_info_.size() - 1];
173 if (pc >= info->pc) {
174 *fde_offset = info->offset;
175 return true;
176 }
177 if (pc < info->pc) {
178 return GetFdeOffsetBinary(pc, fde_offset, fde_info_.size());
179 }
180 }
181
182 if (cur_entries_offset_ == 0) {
183 // All entries read, or error encountered.
184 return false;
185 }
186
187 memory_.set_data_offset(entries_data_offset_);
188 memory_.set_cur_offset(cur_entries_offset_);
Christopher Ferris4cc36d22018-06-06 14:47:31 -0700189 memory_.set_pc_offset(0);
Christopher Ferris61d40972017-06-12 19:14:20 -0700190 cur_entries_offset_ = 0;
191
192 FdeInfo* prev_info = nullptr;
193 for (size_t current = fde_info_.size();
194 current < fde_count_ && memory_.cur_offset() < entries_end_; current++) {
Christopher Ferris61d40972017-06-12 19:14:20 -0700195 FdeInfo* info = &fde_info_[current];
Christopher Ferris4cc36d22018-06-06 14:47:31 -0700196 uint64_t value;
197 if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value) ||
198 !memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
Christopher Ferris61d40972017-06-12 19:14:20 -0700199 fde_info_.erase(current);
Christopher Ferris2fcf4cf2018-01-23 17:52:23 -0800200 last_error_.code = DWARF_ERROR_MEMORY_INVALID;
201 last_error_.address = memory_.cur_offset();
Christopher Ferris61d40972017-06-12 19:14:20 -0700202 return false;
203 }
Christopher Ferris4cc36d22018-06-06 14:47:31 -0700204
205 // Relative encodings require adding in the load bias.
206 if (IsEncodingRelative(table_encoding_)) {
207 value += load_bias_;
208 }
209 info->pc = value;
Christopher Ferris61d40972017-06-12 19:14:20 -0700210
211 if (pc < info->pc) {
212 if (prev_info == nullptr) {
213 return false;
214 }
215 cur_entries_offset_ = memory_.cur_offset();
216 *fde_offset = prev_info->offset;
217 return true;
218 }
219 prev_info = info;
220 }
221
222 if (fde_count_ == fde_info_.size() && pc >= prev_info->pc) {
223 *fde_offset = prev_info->offset;
224 return true;
225 }
226 return false;
227}
228
229template <typename AddressType>
Christopher Ferrisc9dee842017-11-03 14:50:27 -0700230bool DwarfEhFrameWithHdr<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
Christopher Ferris61d40972017-06-12 19:14:20 -0700231 if (fde_count_ == 0) {
232 return false;
233 }
234
235 if (table_entry_size_ > 0) {
236 // Do a binary search since the size of each table entry is fixed.
237 return GetFdeOffsetBinary(pc, fde_offset, fde_count_);
238 } else {
239 // Do a sequential search since each table entry size is variable.
240 return GetFdeOffsetSequential(pc, fde_offset);
241 }
242}
243
Christopher Ferrisc9dee842017-11-03 14:50:27 -0700244// Explicitly instantiate DwarfEhFrameWithHdr
245template class DwarfEhFrameWithHdr<uint32_t>;
246template class DwarfEhFrameWithHdr<uint64_t>;
Christopher Ferrisd226a512017-07-14 10:37:19 -0700247
248} // namespace unwindstack