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