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