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