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