1 /*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "dwarf_section.h"
17 #include <securec.h>
18 #include "dfx_log.h"
19 #include "dwarf_cfa_instructions.h"
20
21 namespace OHOS {
22 namespace HiviewDFX {
23 namespace {
24 #undef LOG_DOMAIN
25 #undef LOG_TAG
26 #define LOG_DOMAIN 0xD002D11
27 #define LOG_TAG "DfxDwarfSection"
28 }
29
DwarfSection(std::shared_ptr<DfxMemory> memory)30 DwarfSection::DwarfSection(std::shared_ptr<DfxMemory> memory) : memory_(memory)
31 {
32 (void)memset_s(&lastErrorData_, sizeof(UnwindErrorData), 0, sizeof(UnwindErrorData));
33 }
34
LinearSearchEntry(uintptr_t pc,struct UnwindTableInfo uti,struct UnwindEntryInfo & uei)35 bool DwarfSection::LinearSearchEntry(uintptr_t pc, struct UnwindTableInfo uti, struct UnwindEntryInfo& uei)
36 {
37 uintptr_t fdeCount = uti.tableLen;
38 uintptr_t tableData = uti.tableData;
39 LOGU("LinearSearchEntry tableData:%p, tableLen: %u", (void*)tableData, (uint32_t)fdeCount);
40 uintptr_t i = 0, ptr = tableData;
41 FrameDescEntry fdeInfo;
42 while (i++ < fdeCount && ptr < uti.endPc) {
43 uintptr_t fdeAddr = ptr;
44 if (GetCieOrFde(ptr, fdeInfo)) {
45 if (pc >= fdeInfo.pcStart && pc < fdeInfo.pcEnd) {
46 LOGU("Fde entry addr: %" PRIx64 "", (uint64_t)fdeAddr);
47 uei.unwindInfo = (void *)(fdeAddr);
48 uei.format = UNW_INFO_FORMAT_REMOTE_TABLE;
49 return true;
50 }
51 } else {
52 break;
53 }
54 }
55 return false;
56 }
57
SearchEntry(uintptr_t pc,struct UnwindTableInfo uti,struct UnwindEntryInfo & uei)58 bool DwarfSection::SearchEntry(uintptr_t pc, struct UnwindTableInfo uti, struct UnwindEntryInfo& uei)
59 {
60 MAYBE_UNUSED auto segbase = uti.segbase;
61 uintptr_t fdeCount = uti.tableLen;
62 uintptr_t tableData = uti.tableData;
63 LOGU("SearchEntry pc: %p segbase:%p, tableData:%p, tableLen: %u",
64 (void*)pc, (void*)segbase, (void*)tableData, (uint32_t)fdeCount);
65
66 // do binary search, encode is stored in symbol file, we have no means to find?
67 // hard code for 1b DwarfEncoding
68 uintptr_t ptr = 0;
69 uintptr_t entry = 0;
70 uintptr_t low = 0;
71 uintptr_t high = fdeCount;
72 DwarfTableEntry dwarfTableEntry;
73 while (low < high) {
74 uintptr_t cur = (low + high) / 2; // 2 : binary search divided parameter
75 ptr = (uintptr_t) tableData + cur * sizeof(DwarfTableEntry);
76 if (!memory_->ReadS32(ptr, &dwarfTableEntry.startPc, true)) {
77 lastErrorData_.SetAddrAndCode(ptr, UNW_ERROR_INVALID_MEMORY);
78 return false;
79 }
80 uintptr_t startPc = static_cast<uintptr_t>(dwarfTableEntry.startPc) + segbase;
81 if (startPc == pc) {
82 if (!memory_->ReadS32(ptr, &dwarfTableEntry.fdeOffset, true)) {
83 lastErrorData_.SetAddrAndCode(ptr, UNW_ERROR_INVALID_MEMORY);
84 return false;
85 }
86 entry = static_cast<uintptr_t>(dwarfTableEntry.fdeOffset) + segbase;
87 break;
88 } else if (pc < startPc) {
89 high = cur;
90 } else {
91 low = cur + 1;
92 }
93 }
94
95 if (entry == 0) {
96 if (high != 0) {
97 ptr = (uintptr_t) tableData + (high - 1) * sizeof(DwarfTableEntry);
98 ptr += 4; // 4 : four bytes
99 if (!memory_->ReadS32(ptr, &dwarfTableEntry.fdeOffset, true)) {
100 lastErrorData_.SetAddrAndCode(ptr, UNW_ERROR_INVALID_MEMORY);
101 return false;
102 }
103 entry = static_cast<uintptr_t>(dwarfTableEntry.fdeOffset) + segbase;
104 } else {
105 return false;
106 }
107 }
108
109 LOGU("Fde entry addr: %" PRIx64 "", (uint64_t)entry);
110 uei.unwindInfo = (void *)(entry);
111 uei.format = UNW_INFO_FORMAT_REMOTE_TABLE;
112 return true;
113 }
114
Step(uintptr_t fdeAddr,std::shared_ptr<DfxRegs> regs,std::shared_ptr<RegLocState> rs)115 bool DwarfSection::Step(uintptr_t fdeAddr, std::shared_ptr<DfxRegs> regs, std::shared_ptr<RegLocState> rs)
116 {
117 FrameDescEntry fdeInfo;
118 if (!ParseFde(fdeAddr, fdeAddr, fdeInfo)) {
119 LOGE("Failed to parse fde?");
120 lastErrorData_.SetAddrAndCode(fdeAddr, UNW_ERROR_DWARF_INVALID_FDE);
121 return false;
122 }
123 if (regs->GetPc() < fdeInfo.pcStart || regs->GetPc() > fdeInfo.pcEnd) {
124 return false;
125 }
126 LOGU("pc: %p, FDE start: %p", (void*)regs->GetPc(), (void*)fdeInfo.pcStart);
127 DwarfCfaInstructions dwarfInstructions(memory_);
128 if (!dwarfInstructions.Parse(regs->GetPc(), fdeInfo, *(rs.get()))) {
129 LOGE("Failed to parse dwarf instructions?");
130 lastErrorData_.SetAddrAndCode(regs->GetPc(), UNW_ERROR_DWARF_INVALID_INSTR);
131 return false;
132 }
133 return true;
134 }
135
GetCieOrFde(uintptr_t & addr,FrameDescEntry & fdeInfo)136 bool DwarfSection::GetCieOrFde(uintptr_t &addr, FrameDescEntry &fdeInfo)
137 {
138 uintptr_t ptr = addr;
139 bool isCieEntry = false;
140 ParseCieOrFdeHeader(ptr, fdeInfo, isCieEntry);
141
142 if (isCieEntry) {
143 if (!ParseCie(addr, ptr, fdeInfo.cie)) {
144 LOGE("Failed to Parse CIE?");
145 return false;
146 }
147 addr = fdeInfo.cie.instructionsEnd;
148 } else {
149 if (!ParseFde(addr, ptr, fdeInfo)) {
150 LOGE("Failed to Parse FDE?");
151 return false;
152 }
153 addr = fdeInfo.instructionsEnd;
154 }
155 return true;
156 }
157
ParseCieOrFdeHeader(uintptr_t & ptr,FrameDescEntry & fdeInfo,bool & isCieEntry)158 void DwarfSection::ParseCieOrFdeHeader(uintptr_t& ptr, FrameDescEntry &fdeInfo, bool& isCieEntry)
159 {
160 uint32_t value32 = 0;
161 memory_->ReadU32(ptr, &value32, true);
162 uintptr_t ciePtr = 0;
163 uintptr_t instructionsEnd = 0;
164 if (value32 == static_cast<uint32_t>(-1)) {
165 uint64_t value64;
166 memory_->ReadU64(ptr, &value64, true);
167 instructionsEnd = ptr + value64;
168
169 memory_->ReadU64(ptr, &value64, true);
170 ciePtr = static_cast<uintptr_t>(value64);
171 if (ciePtr == cie64Value_) {
172 isCieEntry = true;
173 fdeInfo.cie.instructionsEnd = instructionsEnd;
174 fdeInfo.cie.pointerEncoding = DW_EH_PE_sdata8;
175 } else {
176 fdeInfo.instructionsEnd = instructionsEnd;
177 fdeInfo.cieAddr = static_cast<uintptr_t>(ptr - ciePtr);
178 }
179 ptr += sizeof(uint64_t);
180 } else {
181 instructionsEnd = ptr + value32;
182 memory_->ReadU32(ptr, &value32, false);
183 ciePtr = static_cast<uintptr_t>(value32);
184 if (ciePtr == cie32Value_) {
185 isCieEntry = true;
186 fdeInfo.cie.instructionsEnd = instructionsEnd;
187 fdeInfo.cie.pointerEncoding = DW_EH_PE_sdata4;
188 } else {
189 fdeInfo.instructionsEnd = instructionsEnd;
190 fdeInfo.cieAddr = static_cast<uintptr_t>(ptr - ciePtr);
191 }
192 ptr += sizeof(uint32_t);
193 }
194 }
195
ParseFde(uintptr_t fdeAddr,uintptr_t fdePtr,FrameDescEntry & fdeInfo)196 bool DwarfSection::ParseFde(uintptr_t fdeAddr, uintptr_t fdePtr, FrameDescEntry &fdeInfo)
197 {
198 LOGU("fdeAddr: %" PRIx64 "", (uint64_t)fdeAddr);
199 if (!fdeEntries_.empty()) {
200 auto iter = fdeEntries_.find(fdeAddr);
201 if (iter != fdeEntries_.end()) {
202 fdeInfo = iter->second;
203 return true;
204 }
205 }
206
207 if (fdeAddr == fdePtr) {
208 bool isCieEntry = false;
209 ParseCieOrFdeHeader(fdePtr, fdeInfo, isCieEntry);
210 if (isCieEntry) {
211 LOGE("ParseFde error, is Cie Entry?");
212 return false;
213 }
214 }
215 if (!FillInFde(fdePtr, fdeInfo)) {
216 LOGE("ParseFde error, failed to fill FDE?");
217 fdeEntries_.erase(fdeAddr);
218 return false;
219 }
220 fdeEntries_[fdeAddr] = fdeInfo;
221 return true;
222 }
223
FillInFde(uintptr_t ptr,FrameDescEntry & fdeInfo)224 bool DwarfSection::FillInFde(uintptr_t ptr, FrameDescEntry &fdeInfo)
225 {
226 if (!ParseCie(fdeInfo.cieAddr, fdeInfo.cieAddr, fdeInfo.cie)) {
227 LOGE("Failed to parse CIE?");
228 return false;
229 }
230
231 if (fdeInfo.cie.segmentSize != 0) {
232 // Skip over the segment selector for now.
233 ptr += fdeInfo.cie.segmentSize;
234 }
235 // Parse pc begin and range.
236 LOGU("pointerEncoding: %02x", fdeInfo.cie.pointerEncoding);
237 uintptr_t pcStart = memory_->ReadEncodedValue(ptr, fdeInfo.cie.pointerEncoding);
238 uintptr_t pcRange = memory_->ReadEncodedValue(ptr, (fdeInfo.cie.pointerEncoding & 0x0F));
239
240 fdeInfo.lsda = 0;
241 // Check for augmentation length.
242 if (fdeInfo.cie.hasAugmentationData) {
243 uintptr_t augLen = memory_->ReadUleb128(ptr);
244 uintptr_t instructionsPtr = ptr + augLen;
245 if (fdeInfo.cie.lsdaEncoding != DW_EH_PE_omit) {
246 uintptr_t lsdaPtr = ptr;
247 if (memory_->ReadEncodedValue(ptr, (fdeInfo.cie.lsdaEncoding & 0x0F)) != 0) {
248 fdeInfo.lsda = memory_->ReadEncodedValue(lsdaPtr, fdeInfo.cie.lsdaEncoding);
249 }
250 }
251 ptr = instructionsPtr;
252 }
253
254 fdeInfo.instructionsOff = ptr;
255 fdeInfo.pcStart = pcStart;
256 fdeInfo.pcEnd = pcStart + pcRange;
257 LOGU("FDE pcStart: %p, pcEnd: %p", (void*)(fdeInfo.pcStart), (void*)(fdeInfo.pcEnd));
258 return true;
259 }
260
ParseCie(uintptr_t cieAddr,uintptr_t ciePtr,CommonInfoEntry & cieInfo)261 bool DwarfSection::ParseCie(uintptr_t cieAddr, uintptr_t ciePtr, CommonInfoEntry &cieInfo)
262 {
263 LOGU("cieAddr: %" PRIx64 "", (uint64_t)cieAddr);
264 if (!cieEntries_.empty()) {
265 auto iter = cieEntries_.find(cieAddr);
266 if (iter != cieEntries_.end()) {
267 cieInfo = iter->second;
268 return true;
269 }
270 }
271 if (cieAddr == ciePtr) {
272 cieInfo.lsdaEncoding = DW_EH_PE_omit;
273 bool isCieEntry = false;
274 FrameDescEntry fdeInfo;
275 ParseCieOrFdeHeader(ciePtr, fdeInfo, isCieEntry);
276 if (!isCieEntry) {
277 LOGE("ParseCie error, is not Cie Entry?");
278 return false;
279 }
280 cieInfo = fdeInfo.cie;
281 }
282 if (!FillInCie(ciePtr, cieInfo)) {
283 LOGE("ParseCie error, failed to fill Cie?");
284 cieEntries_.erase(cieAddr);
285 return false;
286 }
287 cieEntries_[cieAddr] = cieInfo;
288 return true;
289 }
290
FillInCie(uintptr_t ptr,CommonInfoEntry & cieInfo)291 bool DwarfSection::FillInCie(uintptr_t ptr, CommonInfoEntry &cieInfo)
292 {
293 uint8_t version;
294 memory_->ReadU8(ptr, &version, true);
295 LOGU("Cie version: %d", version);
296 if (version != DW_EH_VERSION && version != 3 && version != 4 && version != 5) { // 3 4 5 : cie version
297 LOGE("Invalid cie version: %d", version);
298 lastErrorData_.SetAddrAndCode(ptr, UNW_ERROR_UNSUPPORTED_VERSION);
299 return false;
300 }
301
302 // save augmentation string
303 uint8_t ch;
304 std::vector<char> augStr;
305 augStr.clear();
306 while (true) {
307 memory_->ReadU8(ptr, &ch, true);
308 if (ch == '\0') {
309 break;
310 }
311 augStr.push_back(ch);
312 }
313
314 // Segment Size
315 if (version == 4 || version == 5) { // 4 5 : cie version
316 // Skip the Address Size field since we only use it for validation.
317 ptr += 1;
318 memory_->ReadU8(ptr, &cieInfo.segmentSize, true);
319 } else {
320 cieInfo.segmentSize = 0;
321 }
322
323 // parse code alignment factor
324 cieInfo.codeAlignFactor = (uint32_t)memory_->ReadUleb128(ptr);
325 LOGU("codeAlignFactor: %d", cieInfo.codeAlignFactor);
326
327 // parse data alignment factor
328 cieInfo.dataAlignFactor = (int32_t)memory_->ReadSleb128(ptr);
329 LOGU("dataAlignFactor: %d", cieInfo.dataAlignFactor);
330
331 // parse return address register
332 if (version == DW_EH_VERSION) {
333 uint8_t val;
334 memory_->ReadU8(ptr, &val, true);
335 cieInfo.returnAddressRegister = static_cast<uintptr_t>(val);
336 } else {
337 cieInfo.returnAddressRegister = (uintptr_t)memory_->ReadUleb128(ptr);
338 }
339 LOGU("returnAddressRegister: %d", (int)cieInfo.returnAddressRegister);
340
341 // parse augmentation data based on augmentation string
342 if (augStr.empty() || augStr[0] != 'z') {
343 cieInfo.instructionsOff = ptr;
344 return true;
345 }
346 cieInfo.hasAugmentationData = true;
347 // parse augmentation data length
348 MAYBE_UNUSED uintptr_t augSize = memory_->ReadUleb128(ptr);
349 LOGU("augSize: %" PRIxPTR "", augSize);
350 cieInfo.instructionsOff = ptr + augSize;
351
352 for (size_t i = 1; i < augStr.size(); ++i) {
353 switch (augStr[i]) {
354 case 'P':
355 uint8_t personalityEncoding;
356 memory_->ReadU8(ptr, &personalityEncoding, true);
357 cieInfo.personality = memory_->ReadEncodedValue(ptr, personalityEncoding);
358 break;
359 case 'L':
360 memory_->ReadU8(ptr, &cieInfo.lsdaEncoding, true);
361 LOGU("cieInfo.lsdaEncoding: %x", cieInfo.lsdaEncoding);
362 break;
363 case 'R':
364 memory_->ReadU8(ptr, &cieInfo.pointerEncoding, true);
365 LOGU("cieInfo.pointerEncoding: %x", cieInfo.pointerEncoding);
366 break;
367 case 'S':
368 cieInfo.isSignalFrame = true;
369 break;
370 default:
371 break;
372 }
373 }
374 return true;
375 }
376 } // namespace HiviewDFX
377 } // namespace OHOS