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