blob: 045fb363fc21bce6118bb5d48aa824cb7799d6f6 [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
17#include <assert.h>
18#include <stdint.h>
19
20#include "DwarfEhFrame.h"
21#include "DwarfMemory.h"
22#include "DwarfSection.h"
23#include "DwarfStructs.h"
24#include "Memory.h"
25
26template <typename AddressType>
27bool DwarfEhFrame<AddressType>::Init(uint64_t offset, uint64_t size) {
28 uint8_t data[4];
29
30 memory_.clear_func_offset();
31 memory_.clear_text_offset();
32 memory_.set_data_offset(offset);
33 memory_.set_cur_offset(offset);
34
35 // Read the first four bytes all at once.
36 if (!memory_.ReadBytes(data, 4)) {
37 last_error_ = DWARF_ERROR_MEMORY_INVALID;
38 return false;
39 }
40
41 version_ = data[0];
42 if (version_ != 1) {
43 // Unknown version.
44 last_error_ = DWARF_ERROR_UNSUPPORTED_VERSION;
45 return false;
46 }
47
48 ptr_encoding_ = data[1];
49 uint8_t fde_count_encoding = data[2];
50 table_encoding_ = data[3];
51 table_entry_size_ = memory_.template GetEncodedSize<AddressType>(table_encoding_);
52
53 memory_.set_pc_offset(memory_.cur_offset());
54 if (!memory_.template ReadEncodedValue<AddressType>(ptr_encoding_, &ptr_offset_)) {
55 last_error_ = DWARF_ERROR_MEMORY_INVALID;
56 return false;
57 }
58
59 memory_.set_pc_offset(memory_.cur_offset());
60 if (!memory_.template ReadEncodedValue<AddressType>(fde_count_encoding, &fde_count_)) {
61 last_error_ = DWARF_ERROR_MEMORY_INVALID;
62 return false;
63 }
64
65 entries_offset_ = memory_.cur_offset();
66 entries_end_ = offset + size;
67 entries_data_offset_ = offset;
68 cur_entries_offset_ = entries_offset_;
69
70 return true;
71}
72
73template <typename AddressType>
74const DwarfFde* DwarfEhFrame<AddressType>::GetFdeFromIndex(size_t index) {
75 const FdeInfo* info = GetFdeInfoFromIndex(index);
76 if (info == nullptr) {
77 return nullptr;
78 }
79 return this->GetFdeFromOffset(info->offset);
80}
81
82template <typename AddressType>
83const typename DwarfEhFrame<AddressType>::FdeInfo* DwarfEhFrame<AddressType>::GetFdeInfoFromIndex(
84 size_t index) {
85 auto entry = fde_info_.find(index);
86 if (entry != fde_info_.end()) {
87 return &fde_info_[index];
88 }
89 FdeInfo* info = &fde_info_[index];
90
91 memory_.set_data_offset(entries_data_offset_);
92 memory_.set_cur_offset(entries_offset_ + 2 * index * table_entry_size_);
93 memory_.set_pc_offset(memory_.cur_offset());
94 uint64_t value;
95 if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value) ||
96 !memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
97 last_error_ = DWARF_ERROR_MEMORY_INVALID;
98 fde_info_.erase(index);
99 return nullptr;
100 }
101 info->pc = value;
102 return info;
103}
104
105template <typename AddressType>
106bool DwarfEhFrame<AddressType>::GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset,
107 uint64_t total_entries) {
108 assert(fde_count_ > 0);
109 assert(total_entries <= fde_count_);
110
111 size_t first = 0;
112 size_t last = total_entries;
113 while (first < last) {
114 size_t current = (first + last) / 2;
115 const FdeInfo* info = GetFdeInfoFromIndex(current);
116 if (pc == info->pc) {
117 *fde_offset = info->offset;
118 return true;
119 }
120 if (pc < info->pc) {
121 last = current;
122 } else {
123 first = current + 1;
124 }
125 }
126 if (last != 0) {
127 const FdeInfo* info = GetFdeInfoFromIndex(last - 1);
128 *fde_offset = info->offset;
129 return true;
130 }
131 return false;
132}
133
134template <typename AddressType>
135bool DwarfEhFrame<AddressType>::GetFdeOffsetSequential(uint64_t pc, uint64_t* fde_offset) {
136 assert(fde_count_ != 0);
137 last_error_ = DWARF_ERROR_NONE;
138 // We can do a binary search if the pc is in the range of the elements
139 // that have already been cached.
140 if (!fde_info_.empty()) {
141 const FdeInfo* info = &fde_info_[fde_info_.size() - 1];
142 if (pc >= info->pc) {
143 *fde_offset = info->offset;
144 return true;
145 }
146 if (pc < info->pc) {
147 return GetFdeOffsetBinary(pc, fde_offset, fde_info_.size());
148 }
149 }
150
151 if (cur_entries_offset_ == 0) {
152 // All entries read, or error encountered.
153 return false;
154 }
155
156 memory_.set_data_offset(entries_data_offset_);
157 memory_.set_cur_offset(cur_entries_offset_);
158 cur_entries_offset_ = 0;
159
160 FdeInfo* prev_info = nullptr;
161 for (size_t current = fde_info_.size();
162 current < fde_count_ && memory_.cur_offset() < entries_end_; current++) {
163 memory_.set_pc_offset(memory_.cur_offset());
164 uint64_t value;
165 if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &value)) {
166 last_error_ = DWARF_ERROR_MEMORY_INVALID;
167 return false;
168 }
169
170 FdeInfo* info = &fde_info_[current];
171 if (!memory_.template ReadEncodedValue<AddressType>(table_encoding_, &info->offset)) {
172 fde_info_.erase(current);
173 last_error_ = DWARF_ERROR_MEMORY_INVALID;
174 return false;
175 }
176 info->pc = value;
177
178 if (pc < info->pc) {
179 if (prev_info == nullptr) {
180 return false;
181 }
182 cur_entries_offset_ = memory_.cur_offset();
183 *fde_offset = prev_info->offset;
184 return true;
185 }
186 prev_info = info;
187 }
188
189 if (fde_count_ == fde_info_.size() && pc >= prev_info->pc) {
190 *fde_offset = prev_info->offset;
191 return true;
192 }
193 return false;
194}
195
196template <typename AddressType>
197bool DwarfEhFrame<AddressType>::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) {
198 if (fde_count_ == 0) {
199 return false;
200 }
201
202 if (table_entry_size_ > 0) {
203 // Do a binary search since the size of each table entry is fixed.
204 return GetFdeOffsetBinary(pc, fde_offset, fde_count_);
205 } else {
206 // Do a sequential search since each table entry size is variable.
207 return GetFdeOffsetSequential(pc, fde_offset);
208 }
209}
210
211// Explicitly instantiate DwarfEhFrame.
212template class DwarfEhFrame<uint32_t>;
213template class DwarfEhFrame<uint64_t>;