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