• 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 "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     DFXLOGU("LinearSearchEntry tableData:%{public}p, tableLen: %{public}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                 DFXLOGU("Fde entry addr: %{public}" 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     DFXLOGU("SearchEntry pc: %{public}p segbase:%{public}p, tableData:%{public}p, tableLen: %{public}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_->Read<int32_t>(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_->Read<int32_t>(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_->Read<int32_t>(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     DFXLOGU("Fde entry addr: %{public}" 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         DFXLOGE("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         DFXLOGU("pc: %{public}p, FDE start: %{public}p, end: %{public}p",
129             (void*)pc, (void*)fdeInfo.pcStart, (void*)fdeInfo.pcEnd);
130         return false;
131     }
132     DwarfCfaInstructions dwarfInstructions(memory_);
133     if (!dwarfInstructions.Parse(pc, fdeInfo, *(rs.get()))) {
134         DFXLOGE("Failed to parse dwarf instructions?");
135         lastErrorData_.SetAddrAndCode(pc, UNW_ERROR_DWARF_INVALID_INSTR);
136         return false;
137     }
138     return true;
139 }
140 
GetCieOrFde(uintptr_t & addr,FrameDescEntry & fdeInfo)141 bool DwarfSection::GetCieOrFde(uintptr_t& addr, FrameDescEntry& fdeInfo)
142 {
143     uintptr_t ptr = addr;
144     bool isCieEntry = false;
145     ParseCieOrFdeHeader(ptr, fdeInfo, isCieEntry);
146 
147     if (isCieEntry) {
148         if (!ParseCie(addr, ptr, fdeInfo.cie)) {
149             DFXLOGE("Failed to Parse CIE?");
150             return false;
151         }
152         addr = fdeInfo.cie.instructionsEnd;
153     } else {
154         if (!ParseFde(addr, ptr, fdeInfo)) {
155             DFXLOGE("Failed to Parse FDE?");
156             return false;
157         }
158         addr = fdeInfo.instructionsEnd;
159     }
160     return true;
161 }
162 
ParseCieOrFdeHeader(uintptr_t & ptr,FrameDescEntry & fdeInfo,bool & isCieEntry)163 void DwarfSection::ParseCieOrFdeHeader(uintptr_t& ptr, FrameDescEntry& fdeInfo, bool& isCieEntry)
164 {
165     uint32_t value32 = 0;
166     memory_->Read<uint32_t>(ptr, &value32, true);
167     uintptr_t ciePtr = 0;
168     uintptr_t instructionsEnd = 0;
169     if (value32 == static_cast<uint32_t>(-1)) {
170         uint64_t value64;
171         memory_->Read<uint64_t>(ptr, &value64, true);
172         instructionsEnd = ptr + value64;
173 
174         memory_->Read<uint64_t>(ptr, &value64, true);
175         ciePtr = static_cast<uintptr_t>(value64);
176         if (ciePtr == cie64Value_) {
177             isCieEntry = true;
178             fdeInfo.cie.instructionsEnd = instructionsEnd;
179             fdeInfo.cie.pointerEncoding = DW_EH_PE_sdata8;
180         } else {
181             fdeInfo.instructionsEnd = instructionsEnd;
182             fdeInfo.cieAddr = static_cast<uintptr_t>(ptr - ciePtr);
183         }
184         ptr += sizeof(uint64_t);
185     } else {
186         instructionsEnd = ptr + value32;
187         memory_->Read<uint32_t>(ptr, &value32, false);
188         ciePtr = static_cast<uintptr_t>(value32);
189         if (ciePtr == cie32Value_) {
190             isCieEntry = true;
191             fdeInfo.cie.instructionsEnd = instructionsEnd;
192             fdeInfo.cie.pointerEncoding = DW_EH_PE_sdata4;
193         } else {
194             fdeInfo.instructionsEnd = instructionsEnd;
195             fdeInfo.cieAddr = static_cast<uintptr_t>(ptr - ciePtr);
196         }
197         ptr += sizeof(uint32_t);
198     }
199 }
200 
ParseFde(uintptr_t fdeAddr,uintptr_t fdePtr,FrameDescEntry & fdeInfo)201 bool DwarfSection::ParseFde(uintptr_t fdeAddr, uintptr_t fdePtr, FrameDescEntry& fdeInfo)
202 {
203     DFXLOGU("fdeAddr: %{public}" PRIx64 "", (uint64_t)fdeAddr);
204     if (!fdeEntries_.empty()) {
205         auto iter = fdeEntries_.find(fdeAddr);
206         if (iter != fdeEntries_.end()) {
207             fdeInfo = iter->second;
208             return true;
209         }
210     }
211 
212     if (fdeAddr == fdePtr) {
213         bool isCieEntry = false;
214         ParseCieOrFdeHeader(fdePtr, fdeInfo, isCieEntry);
215         if (isCieEntry) {
216             DFXLOGE("ParseFde error, is Cie Entry?");
217             return false;
218         }
219     }
220     if (!FillInFde(fdePtr, fdeInfo)) {
221         DFXLOGE("ParseFde error, failed to fill FDE?");
222         fdeEntries_.erase(fdeAddr);
223         return false;
224     }
225     fdeEntries_[fdeAddr] = fdeInfo;
226     return true;
227 }
228 
FillInFde(uintptr_t ptr,FrameDescEntry & fdeInfo)229 bool DwarfSection::FillInFde(uintptr_t ptr, FrameDescEntry& fdeInfo)
230 {
231     if (!ParseCie(fdeInfo.cieAddr, fdeInfo.cieAddr, fdeInfo.cie)) {
232         DFXLOGE("Failed to parse CIE?");
233         return false;
234     }
235 
236     if (fdeInfo.cie.segmentSize != 0) {
237         // Skip over the segment selector for now.
238         ptr += fdeInfo.cie.segmentSize;
239     }
240     // Parse pc begin and range.
241     DFXLOGU("pointerEncoding: %{public}02x", fdeInfo.cie.pointerEncoding);
242     uintptr_t pcStart = memory_->ReadEncodedValue(ptr, fdeInfo.cie.pointerEncoding);
243     uintptr_t pcRange = memory_->ReadEncodedValue(ptr, (fdeInfo.cie.pointerEncoding & 0x0F));
244 
245     fdeInfo.lsda = 0;
246     // Check for augmentation length.
247     if (fdeInfo.cie.hasAugmentationData) {
248         uintptr_t augLen = memory_->ReadUleb128(ptr);
249         uintptr_t instructionsPtr = ptr + augLen;
250         if (fdeInfo.cie.lsdaEncoding != DW_EH_PE_omit) {
251             uintptr_t lsdaPtr = ptr;
252             if (memory_->ReadEncodedValue(ptr, (fdeInfo.cie.lsdaEncoding & 0x0F)) != 0) {
253                 fdeInfo.lsda = memory_->ReadEncodedValue(lsdaPtr, fdeInfo.cie.lsdaEncoding);
254             }
255         }
256         ptr = instructionsPtr;
257     }
258 
259     fdeInfo.instructionsOff = ptr;
260     fdeInfo.pcStart = pcStart;
261     fdeInfo.pcEnd = pcStart + pcRange;
262     DFXLOGU("FDE pcStart: %{public}p, pcEnd: %{public}p", (void*)(fdeInfo.pcStart), (void*)(fdeInfo.pcEnd));
263     return true;
264 }
265 
ParseCie(uintptr_t cieAddr,uintptr_t ciePtr,CommonInfoEntry & cieInfo)266 bool DwarfSection::ParseCie(uintptr_t cieAddr, uintptr_t ciePtr, CommonInfoEntry& cieInfo)
267 {
268     DFXLOGU("cieAddr: %{public}" PRIx64 "", (uint64_t)cieAddr);
269     if (!cieEntries_.empty()) {
270         auto iter = cieEntries_.find(cieAddr);
271         if (iter != cieEntries_.end()) {
272             cieInfo = iter->second;
273             return true;
274         }
275     }
276     if (cieAddr == ciePtr) {
277         cieInfo.lsdaEncoding = DW_EH_PE_omit;
278         bool isCieEntry = false;
279         FrameDescEntry fdeInfo;
280         ParseCieOrFdeHeader(ciePtr, fdeInfo, isCieEntry);
281         if (!isCieEntry) {
282             DFXLOGE("ParseCie error, is not Cie Entry?");
283             return false;
284         }
285         cieInfo = fdeInfo.cie;
286     }
287     if (!FillInCie(ciePtr, cieInfo)) {
288         DFXLOGE("ParseCie error, failed to fill Cie?");
289         cieEntries_.erase(cieAddr);
290         return false;
291     }
292     cieEntries_[cieAddr] = cieInfo;
293     return true;
294 }
295 
ParseAugData(uintptr_t & ptr,CommonInfoEntry & cieInfo,const std::vector<char> & augStr)296 void DwarfSection::ParseAugData(uintptr_t& ptr, CommonInfoEntry& cieInfo, const std::vector<char>& augStr)
297 {
298     MAYBE_UNUSED uintptr_t augSize = memory_->ReadUleb128(ptr);
299     DFXLOGU("augSize: %{public}" PRIxPTR "", augSize);
300     cieInfo.instructionsOff = ptr + augSize;
301 
302     for (size_t i = 1; i < augStr.size(); ++i) {
303         switch (augStr[i]) {
304             case 'P':
305                 uint8_t personalityEncoding;
306                 memory_->Read<uint8_t>(ptr, &personalityEncoding, true);
307                 cieInfo.personality = memory_->ReadEncodedValue(ptr, personalityEncoding);
308                 break;
309             case 'L':
310                 memory_->Read<uint8_t>(ptr, &cieInfo.lsdaEncoding, true);
311                 DFXLOGU("cieInfo.lsdaEncoding: %{public}x", cieInfo.lsdaEncoding);
312                 break;
313             case 'R':
314                 memory_->Read<uint8_t>(ptr, &cieInfo.pointerEncoding, true);
315                 DFXLOGU("cieInfo.pointerEncoding: %{public}x", cieInfo.pointerEncoding);
316                 break;
317             case 'S':
318                 cieInfo.isSignalFrame = true;
319                 break;
320             default:
321                 break;
322         }
323     }
324 }
325 
FillInCie(uintptr_t ptr,CommonInfoEntry & cieInfo)326 bool DwarfSection::FillInCie(uintptr_t ptr, CommonInfoEntry& cieInfo)
327 {
328     uint8_t version;
329     memory_->Read<uint8_t>(ptr, &version, true);
330     DFXLOGU("Cie version: %{public}d", version);
331     if (version != DW_EH_VERSION && version != 3 && version != 4 && version != 5) { // 3 4 5 : cie version
332         DFXLOGE("Invalid cie version: %{public}d", version);
333         lastErrorData_.SetAddrAndCode(ptr, UNW_ERROR_UNSUPPORTED_VERSION);
334         return false;
335     }
336 
337     // save augmentation string
338     std::vector<char> augStr;
339     augStr.clear();
340     uint8_t ch;
341     while (memory_->Read<uint8_t>(ptr, &ch, true) && ch != '\0') {
342         augStr.push_back(ch);
343     }
344 
345     // Segment Size
346     if (version == 4 || version == 5) { // 4 5 : cie version
347         // Skip the Address Size field since we only use it for validation.
348         ptr += 1;
349         memory_->Read<uint8_t>(ptr, &cieInfo.segmentSize, true);
350     } else {
351         cieInfo.segmentSize = 0;
352     }
353 
354     // parse code alignment factor
355     cieInfo.codeAlignFactor = static_cast<uint32_t>(memory_->ReadUleb128(ptr));
356     DFXLOGU("codeAlignFactor: %{public}d", cieInfo.codeAlignFactor);
357 
358     // parse data alignment factor
359     cieInfo.dataAlignFactor = static_cast<int32_t>(memory_->ReadSleb128(ptr));
360     DFXLOGU("dataAlignFactor: %{public}d", cieInfo.dataAlignFactor);
361 
362     // parse return address register
363     if (version == DW_EH_VERSION) {
364         uint8_t val;
365         memory_->Read<uint8_t>(ptr, &val, true);
366         cieInfo.returnAddressRegister = static_cast<uintptr_t>(val);
367     } else {
368         cieInfo.returnAddressRegister = static_cast<uintptr_t>(memory_->ReadUleb128(ptr));
369     }
370     DFXLOGU("returnAddressRegister: %{public}d", static_cast<int>(cieInfo.returnAddressRegister));
371 
372     // parse augmentation data based on augmentation string
373     if (augStr.empty() || augStr[0] != 'z') {
374         cieInfo.instructionsOff = ptr;
375         return true;
376     }
377     cieInfo.hasAugmentationData = true;
378     ParseAugData(ptr, cieInfo, augStr);
379 
380     return true;
381 }
382 }   // namespace HiviewDFX
383 }   // namespace OHOS