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