• 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_cfa_instructions.h"
17 #include <cstdio>
18 #include <cstring>
19 #include "dfx_log.h"
20 #include "dfx_instr_statistic.h"
21 #include "dfx_regs_qut.h"
22 #include "dwarf_define.h"
23 
24 namespace OHOS {
25 namespace HiviewDFX {
26 namespace {
27 #undef LOG_DOMAIN
28 #undef LOG_TAG
29 #define LOG_DOMAIN 0xD002D11
30 #define LOG_TAG "DfxDwarfCfaInstructions"
31 }
32 
Iterate(uintptr_t pc,FrameDescEntry fde,uintptr_t instStart,uintptr_t instEnd,RegLocState & rsState)33 bool DwarfCfaInstructions::Iterate(uintptr_t pc, FrameDescEntry fde,
34     uintptr_t instStart, uintptr_t instEnd, RegLocState &rsState)
35 {
36     uintptr_t instPtr = instStart;
37     uintptr_t pcOffset = fde.pcStart;
38     rsState.pcStart = pcOffset;
39     backupRsState_ = rsState;
40     const auto& cie = fde.cie;
41     while (true) {
42         if (pcOffset > pc) {
43             rsState.pcEnd = pcOffset;
44             break;
45         }
46         if (instPtr >= instEnd) {
47             rsState.pcEnd = fde.pcEnd;
48             break;
49         }
50         rsState.pcStart = pcOffset;
51 
52         // Read the cfa information.
53         uint8_t opCode;
54         if (!memory_->ReadU8(instPtr, &opCode, true)) {
55             break;
56         }
57 
58         if (!DecodeDwCfa(opCode, cie, pcOffset, instPtr, rsState)) {
59             break;
60         }
61     }
62     LOGU("rsState pcStart=%" PRIx64 ", pcEnd=%" PRIx64 "", (uint64_t)rsState.pcStart, (uint64_t)rsState.pcEnd);
63     return true;
64 }
65 
DecodeDwCfa(uint8_t opCode,CommonInfoEntry cie,uintptr_t & pcOffset,uintptr_t & instPtr,RegLocState & rsState)66 bool DwarfCfaInstructions::DecodeDwCfa(uint8_t opCode, CommonInfoEntry cie,
67     uintptr_t& pcOffset, uintptr_t& instPtr, RegLocState &rsState)
68 {
69     uintptr_t value = 0;
70     int64_t offset = 0;
71     uint64_t reg = 0;
72     uint64_t reg2 = 0;
73     size_t qutIdx = 0;
74 
75     switch (opCode) {
76         case DW_CFA_nop:
77             LOGU("DW_CFA_nop");
78             break;
79         case DW_CFA_set_loc:
80             value = memory_->ReadEncodedValue(instPtr, (DwarfEncoding)cie.pointerEncoding);
81             pcOffset = value;
82             LOGU("DW_CFA_set_loc: new offset=%" PRIu64 "", static_cast<uint64_t>(pcOffset));
83             break;
84         case DW_CFA_advance_loc1:
85             value = memory_->ReadEncodedValue(instPtr, (DwarfEncoding)DW_EH_PE_udata1);
86             pcOffset += (value * cie.codeAlignFactor);
87             LOGU("DW_CFA_advance_loc1: new offset=%" PRIu64 "", static_cast<uint64_t>(pcOffset));
88             break;
89         case DW_CFA_advance_loc2:
90             value = memory_->ReadEncodedValue(instPtr, (DwarfEncoding)DW_EH_PE_udata2);
91             pcOffset += (value * cie.codeAlignFactor);
92             LOGU("DW_CFA_advance_loc2: %" PRIu64 " to %" PRIx64 "",
93                   static_cast<uint64_t>(value * cie.codeAlignFactor), static_cast<uint64_t>(pcOffset));
94             break;
95         case DW_CFA_advance_loc4:
96             value = memory_->ReadEncodedValue(instPtr, (DwarfEncoding)DW_EH_PE_udata4);
97             pcOffset += (value * cie.codeAlignFactor);
98             LOGU("DW_CFA_advance_loc4: new offset=%" PRIu64 "", static_cast<uint64_t>(pcOffset));
99             break;
100         case DW_CFA_offset_extended:
101             reg = memory_->ReadUleb128(instPtr);
102             offset = (int64_t)(memory_->ReadUleb128(instPtr) * cie.codeAlignFactor);
103             LOGU("DW_CFA_offset_extended: reg=%d", (int)reg);
104             if (!DfxRegsQut::IsQutReg(static_cast<uint16_t>(reg), qutIdx)) {
105                 INSTR_STATISTIC(UnsupportedDwCfaOffset, reg, UNW_ERROR_UNSUPPORTED_QUT_REG);
106                 break;
107             }
108             rsState.locs[qutIdx].type = REG_LOC_MEM_OFFSET;
109             rsState.locs[qutIdx].val = offset;
110             break;
111         case DW_CFA_restore_extended:
112             reg = memory_->ReadUleb128(instPtr);
113             LOGU("DW_CFA_restore_extended: reg=%d", (int)reg);
114             if (!DfxRegsQut::IsQutReg(static_cast<uint16_t>(reg), qutIdx)) {
115                 INSTR_STATISTIC(UnsupportedDwCfaRestore, reg, UNW_ERROR_UNSUPPORTED_QUT_REG);
116                 break;
117             }
118             rsState.locs[qutIdx] = backupRsState_.locs[qutIdx];
119             break;
120         case DW_CFA_undefined:
121             reg = memory_->ReadUleb128(instPtr);
122             LOGU("DW_CFA_undefined: reg=%d", (int)reg);
123             if (!DfxRegsQut::IsQutReg(static_cast<uint16_t>(reg), qutIdx)) {
124                 INSTR_STATISTIC(UnsupportedDwCfaUndefined, reg, UNW_ERROR_UNSUPPORTED_QUT_REG);
125                 break;
126             }
127             rsState.locs[qutIdx].type = REG_LOC_UNDEFINED;  // cfa offset
128             break;
129         case DW_CFA_same_value:
130             reg = memory_->ReadUleb128(instPtr);
131             LOGU("DW_CFA_same_value: reg=%d", (int)reg);
132             if (!DfxRegsQut::IsQutReg(static_cast<uint16_t>(reg), qutIdx)) {
133                 INSTR_STATISTIC(UnsupportedDwCfaSame, reg, UNW_ERROR_UNSUPPORTED_QUT_REG);
134                 break;
135             }
136             rsState.locs[qutIdx].type = REG_LOC_UNUSED;
137             break;
138         case DW_CFA_register:
139             reg = memory_->ReadUleb128(instPtr);
140             reg2 = memory_->ReadUleb128(instPtr);
141             LOGU("DW_CFA_register: reg=%d, reg2=%d", (int)reg, (int)reg2);
142             if (!DfxRegsQut::IsQutReg(static_cast<uint16_t>(reg2), qutIdx) ||
143                 !DfxRegsQut::IsQutReg(static_cast<uint16_t>(reg), qutIdx)) {
144                 INSTR_STATISTIC(UnsupportedDwCfaRegister, reg, UNW_ERROR_UNSUPPORTED_QUT_REG);
145                 break;
146             }
147             rsState.locs[qutIdx].type = REG_LOC_REGISTER;  // register is saved in current register
148             rsState.locs[qutIdx].val = static_cast<intptr_t>(reg2);
149             break;
150         case DW_CFA_remember_state:
151             saveRsStates_.push(rsState);
152             LOGU("DW_CFA_remember_state");
153             break;
154         case DW_CFA_restore_state:
155             if (saveRsStates_.size() == 0) {
156                 LOGU("DW_CFA_restore_state: Attempt to restore without remember");
157             } else {
158                 rsState = saveRsStates_.top();
159                 saveRsStates_.pop();
160                 LOGU("DW_CFA_restore_state");
161             }
162             break;
163         case DW_CFA_def_cfa:
164             reg = memory_->ReadUleb128(instPtr);
165             offset = (int64_t)memory_->ReadUleb128(instPtr);
166             rsState.cfaReg = (uint32_t)reg;
167             rsState.cfaRegOffset = (int32_t)offset;
168             LOGU("DW_CFA_def_cfa: reg=%d, offset=%" PRIu64 "", (int)reg, offset);
169             break;
170         case DW_CFA_def_cfa_register:
171             reg = memory_->ReadUleb128(instPtr);
172             rsState.cfaReg = (uint32_t)reg;
173             LOGU("DW_CFA_def_cfa_register: reg=%d", (int)reg);
174             break;
175         case DW_CFA_def_cfa_offset:
176             rsState.cfaRegOffset = (int32_t)memory_->ReadUleb128(instPtr);
177             LOGU("DW_CFA_def_cfa_offset: cfaRegOffset=%d", rsState.cfaRegOffset);
178             break;
179         case DW_CFA_offset_extended_sf:
180             reg = memory_->ReadUleb128(instPtr);
181             offset = (int64_t)(memory_->ReadSleb128(instPtr)) * cie.dataAlignFactor;
182             if (!DfxRegsQut::IsQutReg(static_cast<uint16_t>(reg), qutIdx)) {
183                 INSTR_STATISTIC(UnsupportedDwCfaOffset, reg, UNW_ERROR_UNSUPPORTED_QUT_REG);
184                 break;
185             }
186             rsState.locs[qutIdx].type = REG_LOC_MEM_OFFSET;
187             rsState.locs[qutIdx].val = offset;
188             break;
189         case DW_CFA_def_cfa_sf:
190             reg = memory_->ReadUleb128(instPtr);
191             offset = (int64_t)(memory_->ReadSleb128(instPtr)) * cie.dataAlignFactor;
192             LOGU("DW_CFA_def_cfa_sf: reg=%d, offset=%d", rsState.cfaReg, rsState.cfaRegOffset);
193             rsState.cfaReg = (uint32_t)reg;
194             rsState.cfaRegOffset = (int32_t)offset;
195             break;
196         case DW_CFA_def_cfa_offset_sf:
197             offset = (int64_t)(memory_->ReadSleb128(instPtr)) * cie.dataAlignFactor;
198             rsState.cfaRegOffset = (int32_t)offset;
199             LOGU("DW_CFA_def_cfa_offset_sf: offset=%d", rsState.cfaRegOffset);
200             break;
201         case DW_CFA_val_offset:
202             reg = memory_->ReadUleb128(instPtr);
203             offset = (int64_t)(memory_->ReadUleb128(instPtr) * cie.codeAlignFactor);
204             LOGU("DW_CFA_val_offset: reg=%d, offset=%" PRIu64 "", (int)reg, offset);
205             if (!DfxRegsQut::IsQutReg(static_cast<uint16_t>(reg), qutIdx)) {
206                 INSTR_STATISTIC(UnsupportedDwCfaValOffset, reg, UNW_ERROR_UNSUPPORTED_QUT_REG);
207                 break;
208             }
209             rsState.locs[qutIdx].type = REG_LOC_VAL_OFFSET;
210             rsState.locs[qutIdx].val = offset;
211             break;
212         case DW_CFA_val_offset_sf:
213             reg = memory_->ReadUleb128(instPtr);
214             offset = memory_->ReadSleb128(instPtr) * static_cast<int64_t>(cie.codeAlignFactor);
215             LOGU("DW_CFA_val_offset_sf: reg=%d, offset=%" PRIu64 "", (int)reg, offset);
216             if (!DfxRegsQut::IsQutReg(static_cast<uint16_t>(reg), qutIdx)) {
217                 INSTR_STATISTIC(UnsupportedDwCfaValOffset, reg, UNW_ERROR_UNSUPPORTED_QUT_REG);
218                 break;
219             }
220             rsState.locs[qutIdx].type = REG_LOC_VAL_OFFSET;
221             rsState.locs[qutIdx].val = offset;
222             break;
223         case DW_CFA_def_cfa_expression:
224             rsState.cfaReg = 0;
225             rsState.cfaExprPtr = instPtr;
226             instPtr += static_cast<uintptr_t>(memory_->ReadUleb128(instPtr));
227             break;
228         case DW_CFA_expression:
229             reg = memory_->ReadUleb128(instPtr);
230             if (!DfxRegsQut::IsQutReg(static_cast<uint16_t>(reg), qutIdx)) {
231                 INSTR_STATISTIC(UnsupportedDwCfaExpr, reg, UNW_ERROR_UNSUPPORTED_QUT_REG);
232             } else {
233                 rsState.locs[qutIdx].type = REG_LOC_MEM_EXPRESSION;
234                 rsState.locs[qutIdx].val = static_cast<intptr_t>(instPtr);
235             }
236             instPtr += static_cast<uintptr_t>(memory_->ReadUleb128(instPtr));
237             break;
238         case DW_CFA_val_expression:
239             reg = memory_->ReadUleb128(instPtr);
240             if (!DfxRegsQut::IsQutReg(static_cast<uint16_t>(reg), qutIdx)) {
241                 INSTR_STATISTIC(UnsupportedDwCfaValExpr, reg, UNW_ERROR_UNSUPPORTED_QUT_REG);
242             } else {
243                 rsState.locs[qutIdx].type = REG_LOC_VAL_EXPRESSION;
244                 rsState.locs[qutIdx].val = static_cast<intptr_t>(instPtr);
245             }
246             instPtr += static_cast<uintptr_t>(memory_->ReadUleb128(instPtr));
247             break;
248 #if defined(__aarch64__)
249         case DW_CFA_AARCH64_negate_ra_state:
250             rsState.pseudoReg ^= 0x1;
251             LOGU("DW_CFA_AARCH64_negate_ra_state");
252             break;
253 #endif
254         case DW_CFA_GNU_negative_offset_extended:
255             reg = memory_->ReadUleb128(instPtr);
256             offset = -(int64_t)memory_->ReadUleb128(instPtr);
257             LOGU("DW_CFA_GNU_negative_offset_extended: reg=%d, offset=%" PRIu64 "", (int)reg, offset);
258             if (!DfxRegsQut::IsQutReg(static_cast<uint16_t>(reg), qutIdx)) {
259                 INSTR_STATISTIC(UnsupportedDwCfaOffset, reg, UNW_ERROR_UNSUPPORTED_QUT_REG);
260                 break;
261             }
262             rsState.locs[qutIdx].type = REG_LOC_MEM_OFFSET;
263             rsState.locs[qutIdx].val = offset;
264             break;
265 
266         default:
267             uint8_t operand = opCode & 0x3F;
268             // Check the 2 high bits.
269             switch (opCode & 0xC0) {
270                 case DW_CFA_advance_loc:
271                     pcOffset += operand * cie.codeAlignFactor;
272                     LOGU("DW_CFA_advance_loc: pcOffset=%" PRIu64 "", static_cast<uint64_t>(pcOffset));
273                     break;
274                 case DW_CFA_offset:
275                     reg = operand;
276                     offset = (int64_t)memory_->ReadUleb128(instPtr) * cie.dataAlignFactor;
277                     LOGU("DW_CFA_offset: reg=%d, offset=%" PRId64 "", (int)reg, offset);
278                     if (!DfxRegsQut::IsQutReg(static_cast<uint16_t>(reg), qutIdx)) {
279                         INSTR_STATISTIC(UnsupportedDwCfaOffset, reg, UNW_ERROR_UNSUPPORTED_QUT_REG);
280                         break;
281                     }
282                     rsState.locs[qutIdx].type = REG_LOC_MEM_OFFSET;
283                     rsState.locs[qutIdx].val = offset;
284                     break;
285                 case DW_CFA_restore:
286                     reg = operand;
287                     LOGU("DW_CFA_restore: reg=%d", (int)reg);
288                     if (!DfxRegsQut::IsQutReg(static_cast<uint16_t>(reg), qutIdx)) {
289                         INSTR_STATISTIC(UnsupportedDwCfaRestore, reg, UNW_ERROR_UNSUPPORTED_QUT_REG);
290                         break;
291                     }
292                     rsState.locs[qutIdx] = backupRsState_.locs[qutIdx];
293                     break;
294                 default:
295                     LOGU("DW_CFA_unknown: opcode=0x%02x", opCode);
296                     break;
297             }
298     }
299     return true;
300 }
301 
Parse(uintptr_t pc,FrameDescEntry fde,RegLocState & rsState)302 bool DwarfCfaInstructions::Parse(uintptr_t pc, FrameDescEntry fde, RegLocState &rsState)
303 {
304     LOGU("Iterate cie operations");
305     if (!Iterate(pc, fde, fde.cie.instructionsOff, fde.cie.instructionsEnd, rsState)) {
306         LOGE("Failed to run cie inst");
307         return false;
308     }
309 
310     LOGU("Iterate fde operations");
311     if (!Iterate(pc, fde, fde.instructionsOff, fde.instructionsEnd, rsState)) {
312         LOGE("Failed to run fde inst");
313         return false;
314     }
315     return true;
316 }
317 }   // namespace HiviewDFX
318 }   // namespace OHOS
319