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