• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "arm_exidx.h"
17 #include <securec.h>
18 #include "dfx_define.h"
19 #include "dfx_regs.h"
20 #include "dfx_regs_qut.h"
21 #include "dfx_log.h"
22 #include "dfx_instr_statistic.h"
23 #include "dfx_util.h"
24 #include "string_printf.h"
25 
26 #if defined(__arm__)
27 
28 namespace OHOS {
29 namespace HiviewDFX {
30 namespace {
31 #undef LOG_DOMAIN
32 #undef LOG_TAG
33 #define LOG_DOMAIN 0xD002D11
34 #define LOG_TAG "DfxArmExidx"
35 
36 #define ARM_EXIDX_CANT_UNWIND   0x00000001
37 #define ARM_EXIDX_COMPACT       0x80000000
38 #define ARM_EXTBL_OP_FINISH     0xb0
39 
40 static const uintptr_t FOUR_BYTE_OFFSET = 4;
41 static const int TWO_BIT_OFFSET = 2;
42 static const int FOUR_BIT_OFFSET = 4;
43 static const int SEVEN_BIT_OFFSET = 7;
44 static const int EIGHT_BIT_OFFSET = 8;
45 static const int SIXTEEN_BIT_OFFSET = 16;
46 static const int TWENTY_FOUR_BIT_OFFSET = 24;
47 static const int TWENTY_EIGHT_BIT_OFFSET = 28;
48 }
49 
Reset(size_t size)50 void ExidxContext::Reset(size_t size)
51 {
52     vsp = 0;
53     transformedBits = 0;
54     if (size != 0) {
55         regs.resize(size);
56     }
57     for (size_t i = 0; i < regs.size(); ++i) {
58         regs[i] = 0;
59     }
60 }
61 
Transform(uint32_t reg)62 void ExidxContext::Transform(uint32_t reg)
63 {
64     LOGU("Transform reg: %d", reg);
65     transformedBits = transformedBits | (1 << reg);
66 }
67 
IsTransformed(uint32_t reg)68 bool ExidxContext::IsTransformed(uint32_t reg)
69 {
70     if (transformedBits & (1 << reg)) {
71         return true;
72     }
73     return false;
74 }
75 
AddUpVsp(int32_t imm)76 void ExidxContext::AddUpVsp(int32_t imm)
77 {
78     LOGU("AddUpVsp imm: %d", imm);
79     vsp += imm;
80 
81     auto qutRegs = DfxRegsQut::GetQutRegs();
82     for (size_t i = 0; i < qutRegs.size(); i++) {
83         uint32_t reg = static_cast<uint32_t>(qutRegs[i]);
84         if (IsTransformed(reg)) {
85             regs[i] += imm;
86         }
87     }
88 }
89 
ArmExidx(std::shared_ptr<DfxMemory> memory)90 ArmExidx::ArmExidx(std::shared_ptr<DfxMemory> memory) : memory_(memory)
91 {
92     (void)memset_s(&lastErrorData_, sizeof(UnwindErrorData), 0, sizeof(UnwindErrorData));
93     context_.Reset(DfxRegsQut::GetQutRegsSize());
94 }
95 
FlushInstr()96 inline void ArmExidx::FlushInstr()
97 {
98     if (rsState_->cfaReg == 0) {
99         rsState_->cfaReg = REG_SP;
100     }
101     rsState_->cfaRegOffset = 0;
102     if (context_.vsp != 0) {
103         LOG_CHECK((context_.vsp & 0x3) == 0);
104         rsState_->cfaRegOffset = context_.vsp;
105     }
106     LOGU("rsState cfaReg: %d, cfaRegOffset: %d", rsState_->cfaReg, rsState_->cfaRegOffset);
107 
108     auto qutRegs = DfxRegsQut::GetQutRegs();
109     for (size_t i = 0; i < qutRegs.size(); i++) {
110         uint32_t reg = static_cast<uint32_t>(qutRegs[i]);
111         if (context_.IsTransformed(reg)) {
112             LOG_CHECK((context_.regs[i] & 0x3) == 0);
113             rsState_->locs[i].type = REG_LOC_MEM_OFFSET;
114             rsState_->locs[i].val = -context_.regs[i];
115             LOGU("rsState reg: %d, locs[%d].val: %d", reg, i, rsState_->locs[i].val);
116         }
117     }
118 
119     context_.Reset();
120 }
121 
LogRawData()122 inline void ArmExidx::LogRawData()
123 {
124     std::string logStr("Raw Data:");
125     for (const uint8_t data : ops_) {
126         logStr += StringPrintf(" 0x%02x", data);
127     }
128     LOGU("%s", logStr.c_str());
129 }
130 
SearchEntry(uintptr_t pc,struct UnwindTableInfo uti,struct UnwindEntryInfo & uei)131 bool ArmExidx::SearchEntry(uintptr_t pc, struct UnwindTableInfo uti, struct UnwindEntryInfo& uei)
132 {
133     uintptr_t tableLen = uti.tableLen / ARM_EXIDX_TABLE_SIZE;
134     uintptr_t tableData = uti.tableData;
135     LOGU("SearchEntry pc: %p tableData: %p, tableLen: %u",
136         (void*)pc, (void*)tableData, (uint32_t)tableLen);
137     if (tableLen == 0) {
138         lastErrorData_.SetCode(UNW_ERROR_NO_UNWIND_INFO);
139         return false;
140     }
141 
142     // do binary search
143     uintptr_t entry = 0;
144     uintptr_t low = 0;
145     uintptr_t high = tableLen;
146     while (low < high) {
147         uintptr_t cur = (low + high) / 2; // 2 : binary search divided parameter
148         uintptr_t ptr = tableData + cur * ARM_EXIDX_TABLE_SIZE;
149         uintptr_t addr = 0;
150         if (!memory_->ReadPrel31(ptr, &addr)) {
151             lastErrorData_.SetAddrAndCode(ptr, UNW_ERROR_ILLEGAL_VALUE);
152             return false;
153         }
154 
155         if (pc == addr) {
156             entry = ptr;
157             break;
158         }
159         if (pc < addr) {
160             high = cur;
161         } else {
162             low = cur + 1;
163         }
164     }
165     if (entry == 0) {
166         if (high != 0) {
167             entry = tableData + (high - 1) * ARM_EXIDX_TABLE_SIZE;
168         } else {
169             lastErrorData_.SetCode(UNW_ERROR_NO_UNWIND_INFO);
170             return false;
171         }
172     }
173 
174     uei.unwindInfoSize = ARM_EXIDX_TABLE_SIZE;
175     uei.unwindInfo = (void *) entry;
176     uei.format = UNW_INFO_FORMAT_ARM_EXIDX;
177     return true;
178 }
179 
ExtractEntryData(uintptr_t entryOffset)180 bool ArmExidx::ExtractEntryData(uintptr_t entryOffset)
181 {
182     LOGU("Exidx entryOffset: %llx", (uint64_t)entryOffset);
183     ops_.clear();
184     uint32_t data = 0;
185     if (entryOffset & 1) {
186         LOGE("entryOffset: %llx error.", (uint64_t)entryOffset);
187         lastErrorData_.SetAddrAndCode(entryOffset, UNW_ERROR_INVALID_ALIGNMENT);
188         return false;
189     }
190 
191     entryOffset += FOUR_BYTE_OFFSET;
192     if (!memory_->ReadU32(entryOffset, &data, false)) {
193         LOGE("entryOffset: %llx error.", (uint64_t)entryOffset);
194         lastErrorData_.SetAddrAndCode(entryOffset, UNW_ERROR_ILLEGAL_VALUE);
195         return false;
196     }
197 
198     if (data == ARM_EXIDX_CANT_UNWIND) {
199         LOGU("This is a CANT UNWIND entry, data: %x.", data);
200         lastErrorData_.SetAddrAndCode(entryOffset, UNW_ERROR_CANT_UNWIND);
201         return false;
202     } else if ((data & ARM_EXIDX_COMPACT) != 0) {
203         if (((data >> TWENTY_FOUR_BIT_OFFSET) & 0x7f) != 0) {
204             LOGE("This is a non-zero index, this code doesn't support other formats.");
205             lastErrorData_.SetCode(UNW_ERROR_INVALID_PERSONALITY);
206             return false;
207         }
208         LOGU("This is a compact table entry, data: %x.", data);
209         ops_.push_back((data >> SIXTEEN_BIT_OFFSET) & 0xff);
210         ops_.push_back((data >> EIGHT_BIT_OFFSET) & 0xff);
211         uint8_t lastOp = data & 0xff;
212         ops_.push_back(lastOp);
213         if (lastOp != ARM_EXTBL_OP_FINISH) {
214             ops_.push_back(ARM_EXTBL_OP_FINISH);
215         }
216         LogRawData();
217         return true;
218     }
219 
220     uintptr_t extabAddr = 0;
221     // prel31 decode point to .ARM.extab
222 #ifndef TEST_ARM_EXIDX
223     if (!memory_->ReadPrel31(entryOffset, &extabAddr)) {
224         lastErrorData_.SetAddrAndCode(entryOffset, UNW_ERROR_INVALID_MEMORY);
225         return false;
226     }
227 #else
228     extabAddr = entryOffset + FOUR_BYTE_OFFSET;
229 #endif
230     return ExtractEntryTab(extabAddr);
231 }
232 
ExtractEntryTab(uintptr_t tabOffset)233 bool ArmExidx::ExtractEntryTab(uintptr_t tabOffset)
234 {
235     uint32_t data = 0;
236     LOGU("Exidx tabOffset: %llx", (uint64_t)tabOffset);
237     if (!memory_->ReadU32(tabOffset, &data, false)) {
238         lastErrorData_.SetAddrAndCode(tabOffset, UNW_ERROR_INVALID_MEMORY);
239         return false;
240     }
241 
242     uint8_t tableCount = 0;
243     if ((data & ARM_EXIDX_COMPACT) == 0) {
244         LOGU("Arm generic personality, data: %x.", data);
245 #ifndef TEST_ARM_EXIDX
246         uintptr_t perRoutine;
247         if (!memory_->ReadPrel31(tabOffset, &perRoutine)) {
248             LOGE("Arm Personality routine error");
249             lastErrorData_.SetAddrAndCode(tabOffset, UNW_ERROR_INVALID_MEMORY);
250             return false;
251         }
252 #endif
253 
254         tabOffset += FOUR_BYTE_OFFSET;
255         // Skip four bytes, because dont have unwind data to read
256         if (!memory_->ReadU32(tabOffset, &data, false)) {
257             lastErrorData_.SetAddrAndCode(tabOffset, UNW_ERROR_INVALID_MEMORY);
258             return false;
259         }
260         tableCount = (data >> TWENTY_FOUR_BIT_OFFSET) & 0xff;
261         ops_.push_back((data >> SIXTEEN_BIT_OFFSET) & 0xff);
262         ops_.push_back((data >> EIGHT_BIT_OFFSET) & 0xff);
263         ops_.push_back(data & 0xff);
264         tabOffset += FOUR_BYTE_OFFSET;
265     } else {
266         LOGU("Arm compact personality, data: %x.", data);
267         if ((data >> TWENTY_EIGHT_BIT_OFFSET) != 0x8) {
268             LOGE("incorrect Arm compact model, [31:28]bit must be 0x8(%x)", data >> TWENTY_EIGHT_BIT_OFFSET);
269             lastErrorData_.SetCode(UNW_ERROR_INVALID_PERSONALITY);
270             return false;
271         }
272         uint8_t personality = (data >> TWENTY_FOUR_BIT_OFFSET) & 0x3;
273         if (personality > 2) { // 2 : personality must be 0 1 2
274             LOGE("incorrect Arm compact personality(%u)", personality);
275             lastErrorData_.SetCode(UNW_ERROR_INVALID_PERSONALITY);
276             return false;
277         }
278         // inline compact model, when personality is 0
279         if (personality == 0) {
280             ops_.push_back((data >> SIXTEEN_BIT_OFFSET) & 0xff);
281         } else if (personality == 1 || personality == 2) { // 2 : personality equal to 2
282             tableCount = (data >> SIXTEEN_BIT_OFFSET) & 0xff;
283             tabOffset += FOUR_BYTE_OFFSET;
284         }
285         ops_.push_back((data >> EIGHT_BIT_OFFSET) & 0xff);
286         ops_.push_back(data & 0xff);
287     }
288     if (tableCount > 5) { // 5 : 5 operators
289         lastErrorData_.SetCode(UNW_ERROR_NOT_SUPPORT);
290         return false;
291     }
292 
293     for (size_t i = 0; i < tableCount; i++) {
294         if (!memory_->ReadU32(tabOffset, &data, false)) {
295             return false;
296         }
297         tabOffset += FOUR_BYTE_OFFSET;
298         ops_.push_back((data >> TWENTY_FOUR_BIT_OFFSET) & 0xff);
299         ops_.push_back((data >> SIXTEEN_BIT_OFFSET) & 0xff);
300         ops_.push_back((data >> EIGHT_BIT_OFFSET) & 0xff);
301         ops_.push_back(data & 0xff);
302     }
303 
304     if (!ops_.empty() && ops_.back() != ARM_EXTBL_OP_FINISH) {
305         ops_.push_back(ARM_EXTBL_OP_FINISH);
306     }
307     LogRawData();
308     return true;
309 }
310 
GetOpCode()311 inline bool ArmExidx::GetOpCode()
312 {
313     if (ops_.empty()) {
314         return false;
315     }
316     curOp_ = ops_.front();
317     ops_.pop_front();
318     LOGU("curOp: %llx", (uint64_t)curOp_);
319     return true;
320 }
321 
Eval(uintptr_t entryOffset)322 bool ArmExidx::Eval(uintptr_t entryOffset)
323 {
324     if (!ExtractEntryData(entryOffset)) {
325         return false;
326     }
327 
328     DecodeTable decodeTable[] = {
329         {0xc0, 0x00, &ArmExidx::Decode00xxxxxx},
330         {0xc0, 0x40, &ArmExidx::Decode01xxxxxx},
331         {0xf0, 0x80, &ArmExidx::Decode1000iiiiiiiiiiii},
332         {0xf0, 0x90, &ArmExidx::Decode1001nnnn},
333         {0xf0, 0xa0, &ArmExidx::Decode1010nnnn},
334         {0xff, 0xb0, &ArmExidx::Decode10110000},
335         {0xff, 0xb1, &ArmExidx::Decode101100010000iiii},
336         {0xff, 0xb2, &ArmExidx::Decode10110010uleb128},
337         {0xff, 0xb3, &ArmExidx::Decode10110011sssscccc},
338         {0xfc, 0xb4, &ArmExidx::Decode101101nn},
339         {0xf8, 0xb8, &ArmExidx::Decode10111nnn},
340         {0xff, 0xc6, &ArmExidx::Decode11000110sssscccc},
341         {0xff, 0xc7, &ArmExidx::Decode110001110000iiii},
342         {0xfe, 0xc8, &ArmExidx::Decode1100100nsssscccc},
343         {0xc8, 0xc8, &ArmExidx::Decode11001yyy},
344         {0xf8, 0xc0, &ArmExidx::Decode11000nnn},
345         {0xf8, 0xd0, &ArmExidx::Decode11010nnn},
346         {0xc0, 0xc0, &ArmExidx::Decode11xxxyyy}
347     };
348     context_.Reset();
349     while (Decode(decodeTable, sizeof(decodeTable) / sizeof(decodeTable[0])));
350     return true;
351 }
352 
Step(uintptr_t entryOffset,std::shared_ptr<RegLocState> rs)353 bool ArmExidx::Step(uintptr_t entryOffset, std::shared_ptr<RegLocState> rs)
354 {
355     if (rs == nullptr) {
356         return false;
357     }
358     rsState_ = rs;
359 
360     if (!Eval(entryOffset)) {
361         return false;
362     }
363 
364     FlushInstr();
365     return true;
366 }
367 
DecodeSpare()368 inline bool ArmExidx::DecodeSpare()
369 {
370     LOGU("Exidx Decode Spare");
371     lastErrorData_.SetCode(UNW_ERROR_ARM_EXIDX_SPARE);
372     return false;
373 }
374 
Decode(DecodeTable decodeTable[],size_t size)375 inline bool ArmExidx::Decode(DecodeTable decodeTable[], size_t size)
376 {
377     if (!GetOpCode()) {
378         return false;
379     }
380 
381     bool ret = false;
382     for (size_t i = 0; i < size; ++i) {
383         if ((curOp_ & decodeTable[i].mask) == decodeTable[i].result) {
384             if (decodeTable[i].decoder != nullptr) {
385                 LOGU("decodeTable[%d].mask: %02x", i, decodeTable[i].mask);
386                 ret = (this->*(decodeTable[i].decoder))();
387                 break;
388             }
389         }
390     }
391     return ret;
392 }
393 
Decode00xxxxxx()394 inline bool ArmExidx::Decode00xxxxxx()
395 {
396     // 00xxxxxx: vsp = vsp + (xxxxxx << 2) + 4. Covers range 0x04-0x100 inclusive
397     context_.AddUpVsp(((curOp_ & 0x3f) << 2) + 4);
398     return true;
399 }
400 
Decode01xxxxxx()401 inline bool ArmExidx::Decode01xxxxxx()
402 {
403     // 01xxxxxx: vsp = vsp - (xxxxxx << 2) + 4. Covers range 0x04-0x100 inclusive
404     context_.AddUpVsp(-(((curOp_ & 0x3f) << 2) + 4));
405     return true;
406 }
407 
Decode1000iiiiiiiiiiii()408 inline bool ArmExidx::Decode1000iiiiiiiiiiii()
409 {
410     uint16_t registers = ((curOp_ & 0x0f) << 8);
411     if (!GetOpCode()) {
412         return false;
413     }
414     registers |= curOp_;
415     if (registers == 0x0) {
416         LOGE("10000000 00000000: Refuse to unwind!");
417         lastErrorData_.SetCode(UNW_ERROR_CANT_UNWIND);
418         return false;
419     }
420 
421     registers <<= FOUR_BIT_OFFSET;
422     LOGU("1000iiii iiiiiiii: registers: %02x)", registers);
423     for (size_t reg = REG_ARM_R4; reg < REG_ARM_LAST; reg++) {
424         if ((registers & (1 << reg))) {
425             if (REG_PC == reg) {
426                 rsState_->isPcSet = true;
427             }
428             context_.Transform(reg);
429             context_.AddUpVsp(FOUR_BYTE_OFFSET);
430         }
431     }
432     return true;
433 }
434 
Decode1001nnnn()435 inline bool ArmExidx::Decode1001nnnn()
436 {
437     uint8_t bits = curOp_ & 0xf;
438     if (bits == REG_ARM_R13 || bits == REG_ARM_R15) {
439         LOGU("10011101 or 10011111: Reserved");
440         lastErrorData_.SetCode(UNW_ERROR_RESERVED_VALUE);
441         return false;
442     }
443     // 1001nnnn: Set vsp = r[nnnn]
444     if ((bits == REG_ARM_R7) || (bits == REG_ARM_R11)) {
445         LOGU("1001nnnn: Set vsp = R%d", bits);
446         if (context_.transformedBits == 0) {
447             // No register transformed, ignore vsp offset.
448             context_.Reset();
449         }
450     } else {
451         INSTR_STATISTIC(UnsupportedArmExidx, bits, curOp_);
452         return false;
453     }
454     rsState_->cfaReg = bits;
455     rsState_->cfaRegOffset = 0;
456     return true;
457 }
458 
Decode1010nnnn()459 inline bool ArmExidx::Decode1010nnnn()
460 {
461     // 10100nnn: Pop r4-r[4+nnn]
462     // 10101nnn: Pop r4-r[4+nnn], r14
463     size_t startReg = REG_ARM_R4;
464     size_t endReg = REG_ARM_R4 + (curOp_ & 0x7);
465     std::string msg = "Pop r" + std::to_string(startReg);
466     if (endReg > startReg) {
467         msg += "-r" + std::to_string(endReg);
468     }
469     if (curOp_ & 0x8) {
470         msg += ", r14";
471     }
472     LOGU("%s", msg.c_str());
473 
474     for (size_t reg = startReg; reg <= endReg; reg++) {
475         context_.Transform(reg);
476         context_.AddUpVsp(FOUR_BYTE_OFFSET);
477     }
478 
479     if (curOp_ & 0x8) {
480         context_.Transform(REG_LR);
481         context_.AddUpVsp(FOUR_BYTE_OFFSET);
482     }
483     return true;
484 }
485 
Decode10110000()486 inline bool ArmExidx::Decode10110000()
487 {
488     LOGU("10110000: Finish");
489     lastErrorData_.SetCode(UNW_ERROR_ARM_EXIDX_FINISH);
490     return true;
491 }
492 
Decode101100010000iiii()493 inline bool ArmExidx::Decode101100010000iiii()
494 {
495     if (!GetOpCode()) {
496         return false;
497     }
498     // 10110001 00000000: spare
499     // 10110001 xxxxyyyy: spare (xxxx != 0000)
500     if (curOp_ == 0x00 || (curOp_ & 0xf0) != 0) {
501         return DecodeSpare();
502     }
503 
504     // 10110001 0000iiii(i not all 0) Pop integer registers under mask{r3, r2, r1, r0}
505     uint8_t registers = curOp_ & 0x0f;
506     LOGU("10110001 0000iiii, registers: %02x", registers);
507     for (size_t reg = 0; reg < 4; reg++) { // 4 : four registers {r3, r2, r1, r0}
508         if ((registers & (1 << reg))) {
509             context_.AddUpVsp(FOUR_BYTE_OFFSET);
510         }
511     }
512     return true;
513 }
514 
Decode10110010uleb128()515 inline bool ArmExidx::Decode10110010uleb128()
516 {
517     // 10110010 uleb128 vsp = vsp + 0x204 + (uleb128 << 2)
518     uint8_t shift = 0;
519     uint32_t uleb128 = 0;
520     do {
521         if (!GetOpCode()) {
522             return false;
523         }
524         uleb128 |= (curOp_ & 0x7f) << shift;
525         shift += SEVEN_BIT_OFFSET;
526     } while ((curOp_ & 0x80) != 0);
527     uint32_t offset = 0x204 + (uleb128 << TWO_BIT_OFFSET);
528     LOGU("vsp = vsp + %d", offset);
529     context_.AddUpVsp(offset);
530     return true;
531 }
532 
Decode10110011sssscccc()533 inline bool ArmExidx::Decode10110011sssscccc()
534 {
535     // Pop VFP double precision registers D[ssss]-D[ssss+cccc] by FSTMFDX
536     if (!GetOpCode()) {
537         return false;
538     }
539     uint8_t popRegCount = (curOp_ & 0x0f) + 1;
540     uint32_t offset = popRegCount * 8 + 4;
541     context_.AddUpVsp(offset);
542     return true;
543 }
544 
Decode101101nn()545 inline bool ArmExidx::Decode101101nn()
546 {
547     return DecodeSpare();
548 }
549 
Decode10111nnn()550 inline bool ArmExidx::Decode10111nnn()
551 {
552     // 10111nnn: Pop VFP double-precision registers D[8]-D[8+nnn] by FSTMFDX
553     uint8_t popRegCount = (curOp_ & 0x07) + 1;
554     uint32_t offset = popRegCount * 8 + 4;
555     context_.AddUpVsp(offset);
556     return true;
557 }
558 
Decode11000110sssscccc()559 inline bool ArmExidx::Decode11000110sssscccc()
560 {
561     // 11000110 sssscccc: Intel Wireless MMX pop wR[ssss]-wR[ssss+cccc] (see remark e)
562     if (!GetOpCode()) {
563         return false;
564     }
565     return Decode11000nnn();
566 }
567 
Decode110001110000iiii()568 inline bool ArmExidx::Decode110001110000iiii()
569 {
570     // Intel Wireless MMX pop wCGR registers under mask {wCGR3,2,1,0}
571     if (!GetOpCode()) {
572         return false;
573     }
574     // 11000111 00000000: Spare
575     // 11000111 xxxxyyyy: Spare (xxxx != 0000)
576     if ((curOp_ & 0xf0) != 0 || curOp_ == 0) {
577         return DecodeSpare();
578     }
579 
580     // 11000111 0000iiii: Intel Wireless MMX pop wCGR registers {wCGR0,1,2,3}
581     for (size_t i = 0; i < 4; i++) { // 4 : four registers
582         if (curOp_ & (1 << i)) {
583             context_.AddUpVsp(FOUR_BYTE_OFFSET);
584         }
585     }
586     return true;
587 }
588 
Decode1100100nsssscccc()589 inline bool ArmExidx::Decode1100100nsssscccc()
590 {
591     // 11001000 sssscccc: Pop VFP double precision registers D[16+ssss]-D[16+ssss+cccc] by VPUSH
592     // 11001001 sssscccc: Pop VFP double precision registers D[ssss]-D[ssss+cccc] by VPUSH
593     if (!GetOpCode()) {
594         return false;
595     }
596     uint8_t popRegCount = (curOp_ & 0x0f) + 1;
597     uint32_t offset = popRegCount * 8;
598     context_.AddUpVsp(offset);
599     return true;
600 }
601 
Decode11001yyy()602 inline bool ArmExidx::Decode11001yyy()
603 {
604     // 11001yyy: Spare (yyy != 000, 001)
605     return DecodeSpare();
606 }
607 
Decode11000nnn()608 inline bool ArmExidx::Decode11000nnn()
609 {
610     // Intel Wireless MMX pop wR[10]-wR[10+nnn]
611     uint8_t popRegCount = (curOp_ & 0x0f) + 1;
612     uint32_t offset = popRegCount * 8;
613     context_.AddUpVsp(offset);
614     return true;
615 }
616 
Decode11010nnn()617 inline bool ArmExidx::Decode11010nnn()
618 {
619     // Pop VFP double-precision registers D[8]-D[8+nnn] saved (as if) by VPUSH (seeremark d)
620     uint8_t popRegCount = (curOp_ & 0x0f) + 1;
621     uint32_t offset = popRegCount * 8;
622     context_.AddUpVsp(offset);
623     return true;
624 }
625 
Decode11xxxyyy()626 inline bool ArmExidx::Decode11xxxyyy()
627 {
628     // 11xxxyyy: Spare (xxx != 000, 001, 010)
629     return DecodeSpare();
630 }
631 } // namespace HiviewDFX
632 } // namespace OHOS
633 #endif