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