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