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