• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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