/src/capstonenext/arch/PowerPC/PPCMapping.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Capstone Disassembly Engine */ |
2 | | /* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2015 */ |
3 | | |
4 | | #include "capstone/ppc.h" |
5 | | #ifdef CAPSTONE_HAS_POWERPC |
6 | | |
7 | | #include <stdio.h> // debug |
8 | | #include <string.h> |
9 | | |
10 | | #include "../../cs_simple_types.h" |
11 | | #include "../../Mapping.h" |
12 | | #include "../../MCDisassembler.h" |
13 | | #include "../../utils.h" |
14 | | |
15 | | #include "PPCLinkage.h" |
16 | | #include "PPCMapping.h" |
17 | | #include "PPCMCTargetDesc.h" |
18 | | |
19 | | #define GET_REGINFO_MC_DESC |
20 | | #include "PPCGenRegisterInfo.inc" |
21 | | |
22 | | void PPC_init_mri(MCRegisterInfo *MRI) |
23 | 2.16k | { |
24 | 2.16k | MCRegisterInfo_InitMCRegisterInfo(MRI, PPCRegDesc, PPC_REG_ENDING, 0, 0, |
25 | 2.16k | PPCMCRegisterClasses, |
26 | 2.16k | ARR_SIZE(PPCMCRegisterClasses), 0, 0, |
27 | 2.16k | PPCRegDiffLists, 0, PPCSubRegIdxLists, |
28 | 2.16k | ARR_SIZE(PPCSubRegIdxLists), |
29 | 2.16k | PPCRegEncodingTable); |
30 | 2.16k | } |
31 | | |
32 | | const char *PPC_reg_name(csh handle, unsigned int reg) |
33 | 48.6k | { |
34 | 48.6k | if (reg > PPC_REG_INVALID && reg < PPC_REG_ENDING) |
35 | 48.6k | return PPC_LLVM_getRegisterName(reg); |
36 | 0 | return NULL; |
37 | 48.6k | } |
38 | | |
39 | | // given internal insn id, return public instruction info |
40 | | void PPC_get_insn_id(cs_struct *h, cs_insn *insn, unsigned int id) |
41 | 69.3k | { |
42 | | // We do this after Instruction disassembly. |
43 | 69.3k | } |
44 | | |
45 | | #ifndef CAPSTONE_DIET |
46 | | |
47 | | static const char *const insn_name_maps[] = { |
48 | | #include "PPCGenCSMappingInsnName.inc" |
49 | | }; |
50 | | |
51 | | static const name_map insn_alias_mnem_map[] = { |
52 | | #include "PPCGenCSAliasMnemMap.inc" |
53 | | { PPC_INS_ALIAS_SLWI, "slwi" }, |
54 | | { PPC_INS_ALIAS_SRWI, "srwi" }, |
55 | | { PPC_INS_ALIAS_SLDI, "sldi" }, |
56 | | { PPC_INS_ALIAS_END, NULL }, |
57 | | }; |
58 | | |
59 | | #endif |
60 | | |
61 | | const char *PPC_insn_name(csh handle, unsigned int id) |
62 | 69.3k | { |
63 | 69.3k | #ifndef CAPSTONE_DIET |
64 | 69.3k | if (id < PPC_INS_ALIAS_END && id > PPC_INS_ALIAS_BEGIN) { |
65 | 0 | if (id - PPC_INS_ALIAS_BEGIN >= ARR_SIZE(insn_alias_mnem_map)) |
66 | 0 | return NULL; |
67 | | |
68 | 0 | return insn_alias_mnem_map[id - PPC_INS_ALIAS_BEGIN - 1].name; |
69 | 0 | } |
70 | 69.3k | if (id >= PPC_INS_ENDING) |
71 | 0 | return NULL; |
72 | | |
73 | 69.3k | return insn_name_maps[id]; |
74 | | #else |
75 | | return NULL; |
76 | | #endif |
77 | 69.3k | } |
78 | | |
79 | | #ifndef CAPSTONE_DIET |
80 | | static const name_map group_name_maps[] = { |
81 | | // generic groups |
82 | | { PPC_GRP_INVALID, NULL }, |
83 | | { PPC_GRP_JUMP, "jump" }, |
84 | | { PPC_GRP_CALL, "call" }, |
85 | | { PPC_GRP_INT, "int" }, |
86 | | { PPC_GRP_PRIVILEGE, "privilege" }, |
87 | | { PPC_GRP_BRANCH_RELATIVE, "branch_relative" }, |
88 | | |
89 | | // architecture-specific groups |
90 | | #include "PPCGenCSFeatureName.inc" |
91 | | }; |
92 | | #endif |
93 | | |
94 | | const char *PPC_group_name(csh handle, unsigned int id) |
95 | 54.5k | { |
96 | 54.5k | #ifndef CAPSTONE_DIET |
97 | 54.5k | return id2name(group_name_maps, ARR_SIZE(group_name_maps), id); |
98 | | #else |
99 | | return NULL; |
100 | | #endif |
101 | 54.5k | } |
102 | | |
103 | | const insn_map ppc_insns[] = { |
104 | | #include "PPCGenCSMappingInsn.inc" |
105 | | }; |
106 | | |
107 | | void PPC_check_updates_cr0(MCInst *MI) |
108 | 70.7k | { |
109 | 70.7k | #ifndef CAPSTONE_DIET |
110 | 70.7k | if (!detail_is_set(MI)) |
111 | 0 | return; |
112 | 70.7k | cs_detail *detail = get_detail(MI); |
113 | 89.5k | for (int i = 0; i < detail->regs_write_count; ++i) { |
114 | 24.7k | if (detail->regs_write[i] == 0) |
115 | 0 | return; |
116 | 24.7k | if (detail->regs_write[i] == PPC_REG_CR0) { |
117 | 5.96k | PPC_get_detail(MI)->update_cr0 = true; |
118 | 5.96k | return; |
119 | 5.96k | } |
120 | 24.7k | } |
121 | 70.7k | #endif // CAPSTONE_DIET |
122 | 70.7k | } |
123 | | |
124 | | /// Parses and adds the branch predicate information and the BH field. |
125 | | static void PPC_add_branch_predicates(MCInst *MI, const uint8_t *Bytes, |
126 | | size_t BytesLen) |
127 | 70.7k | { |
128 | 70.7k | if (!detail_is_set(MI)) |
129 | 0 | return; |
130 | 70.7k | #ifndef CAPSTONE_DIET |
131 | 70.7k | assert(MI && Bytes); |
132 | 70.7k | if (BytesLen < 4) |
133 | 778 | return; |
134 | | |
135 | 69.9k | ppc_insn_form form = ppc_insns[MI->Opcode].suppl_info.ppc.form; |
136 | 69.9k | bool b_form = ppc_is_b_form(form); |
137 | 69.9k | if (!(b_form || form == PPC_INSN_FORM_XLFORM_2)) |
138 | 61.3k | return; |
139 | | |
140 | 8.55k | uint32_t Inst = readBytes32(MI, Bytes); |
141 | | |
142 | 8.55k | uint8_t bi = 0; |
143 | 8.55k | if (b_form) |
144 | 8.31k | bi = (Inst & PPC_INSN_FORM_B_BI_MASK) >> 16; |
145 | 236 | else |
146 | 236 | bi = (Inst & PPC_INSN_FORM_XL_BI_MASK) >> 16; |
147 | | |
148 | 8.55k | uint8_t bo = 0; |
149 | 8.55k | if (b_form) |
150 | 8.31k | bo = (Inst & PPC_INSN_FORM_B_BO_MASK) >> 21; |
151 | 236 | else |
152 | 236 | bo = (Inst & PPC_INSN_FORM_XL_BO_MASK) >> 21; |
153 | | |
154 | 8.55k | PPC_get_detail(MI)->bc.bo = bo; |
155 | 8.55k | PPC_get_detail(MI)->bc.bi = bi; |
156 | 8.55k | PPC_get_detail(MI)->bc.crX_bit = bi % 4; |
157 | 8.55k | PPC_get_detail(MI)->bc.crX = PPC_REG_CR0 + (bi / 4); |
158 | 8.55k | PPC_get_detail(MI)->bc.hint = PPC_get_hint(bo); |
159 | 8.55k | PPC_get_detail(MI)->bc.pred_cr = PPC_get_branch_pred(bi, bo, true); |
160 | 8.55k | PPC_get_detail(MI)->bc.pred_ctr = PPC_get_branch_pred(bi, bo, false); |
161 | | |
162 | 8.55k | if (ppc_is_b_form(form)) |
163 | 8.31k | return; |
164 | | |
165 | 236 | uint8_t bh = (Inst & PPC_INSN_FORM_XL_BH_MASK) >> 11; |
166 | 236 | uint16_t xo = (Inst & PPC_INSN_FORM_XL_XO_MASK) >> 1; |
167 | | // Pre-defined values for XO fields (PowerISA v3.1B) |
168 | 236 | uint16_t bcctr_xo_field = 528; |
169 | 236 | uint16_t bctar_xo_field = 560; |
170 | 236 | bool cond = (xo == bcctr_xo_field || xo == bctar_xo_field); |
171 | 236 | switch (bh) { |
172 | 0 | default: |
173 | 0 | assert(0 && "Invalid BH value."); |
174 | 118 | case 0: |
175 | 118 | PPC_get_detail(MI)->bc.bh = cond ? PPC_BH_NO_SUBROUTINE_RET : |
176 | 118 | PPC_BH_SUBROUTINE_RET; |
177 | 118 | break; |
178 | 31 | case 1: |
179 | 31 | PPC_get_detail(MI)->bc.bh = cond ? PPC_BH_RESERVED : |
180 | 31 | PPC_BH_NO_SUBROUTINE_RET; |
181 | 31 | break; |
182 | 68 | case 2: |
183 | 68 | PPC_get_detail(MI)->bc.bh = PPC_BH_RESERVED; |
184 | 68 | break; |
185 | 19 | case 3: |
186 | 19 | PPC_get_detail(MI)->bc.bh = PPC_BH_NOT_PREDICTABLE; |
187 | 19 | break; |
188 | 236 | } |
189 | 236 | #endif // CAPSTONE_DIET |
190 | 236 | } |
191 | | |
192 | | void PPC_set_instr_map_data(MCInst *MI, const uint8_t *Bytes, size_t BytesLen) |
193 | 70.7k | { |
194 | 70.7k | map_cs_id(MI, ppc_insns, ARR_SIZE(ppc_insns)); |
195 | 70.7k | map_implicit_reads(MI, ppc_insns); |
196 | 70.7k | map_implicit_writes(MI, ppc_insns); |
197 | 70.7k | map_groups(MI, ppc_insns); |
198 | 70.7k | PPC_add_branch_predicates(MI, Bytes, BytesLen); |
199 | 70.7k | PPC_check_updates_cr0(MI); |
200 | 70.7k | const ppc_suppl_info *suppl_info = map_get_suppl_info(MI, ppc_insns); |
201 | 70.7k | if (suppl_info) { |
202 | 70.7k | PPC_get_detail(MI)->format = suppl_info->form; |
203 | 70.7k | } |
204 | 70.7k | } |
205 | | |
206 | | /// Initialize PPCs detail. |
207 | | void PPC_init_cs_detail(MCInst *MI) |
208 | 70.7k | { |
209 | 70.7k | if (!detail_is_set(MI)) |
210 | 0 | return; |
211 | 70.7k | memset(get_detail(MI), 0, offsetof(cs_detail, ppc) + sizeof(cs_ppc)); |
212 | 70.7k | PPC_get_detail(MI)->bc.bi = UINT8_MAX; |
213 | 70.7k | PPC_get_detail(MI)->bc.bo = UINT8_MAX; |
214 | 70.7k | PPC_get_detail(MI)->bc.crX = PPC_REG_INVALID; |
215 | 70.7k | PPC_get_detail(MI)->bc.crX_bit = PPC_BI_INVALID; |
216 | 70.7k | PPC_get_detail(MI)->bc.pred_cr = PPC_PRED_INVALID; |
217 | 70.7k | PPC_get_detail(MI)->bc.pred_ctr = PPC_PRED_INVALID; |
218 | 70.7k | PPC_get_detail(MI)->bc.hint = PPC_BR_NOT_GIVEN; |
219 | 70.7k | PPC_get_detail(MI)->bc.bh = PPC_BH_INVALID; |
220 | 70.7k | } |
221 | | |
222 | | void PPC_printer(MCInst *MI, SStream *O, void * /* MCRegisterInfo* */ info) |
223 | 69.3k | { |
224 | 69.3k | MI->MRI = (MCRegisterInfo *)info; |
225 | 69.3k | MI->fillDetailOps = detail_is_set(MI); |
226 | 69.3k | MI->flat_insn->usesAliasDetails = map_use_alias_details(MI); |
227 | 69.3k | PPC_LLVM_printInst(MI, MI->address, "", O); |
228 | 69.3k | #ifndef CAPSTONE_DIET |
229 | 69.3k | map_set_alias_id(MI, O, insn_alias_mnem_map, |
230 | 69.3k | ARR_SIZE(insn_alias_mnem_map)); |
231 | 69.3k | #endif |
232 | 69.3k | } |
233 | | |
234 | | bool PPC_getInstruction(csh handle, const uint8_t *bytes, size_t bytes_len, |
235 | | MCInst *instr, uint16_t *size, uint64_t address, |
236 | | void *info) |
237 | 70.7k | { |
238 | 70.7k | PPC_init_cs_detail(instr); |
239 | 70.7k | DecodeStatus result = PPC_LLVM_getInstruction( |
240 | 70.7k | handle, bytes, bytes_len, instr, size, address, info); |
241 | 70.7k | PPC_set_instr_map_data(instr, bytes, bytes_len); |
242 | 70.7k | return result != MCDisassembler_Fail; |
243 | 70.7k | } |
244 | | |
245 | | bool PPC_getFeatureBits(unsigned int mode, unsigned int feature) |
246 | 309k | { |
247 | 309k | if ((feature == PPC_FeatureQPX) && (mode & CS_MODE_QPX) == 0) { |
248 | 36.5k | return false; |
249 | 273k | } else if ((feature == PPC_FeatureSPE) && (mode & CS_MODE_SPE) == 0) { |
250 | 36.5k | return false; |
251 | 236k | } else if ((feature == PPC_FeatureBookE) && |
252 | 236k | (mode & CS_MODE_BOOKE) == 0) { |
253 | 138 | return false; |
254 | 236k | } else if ((feature == PPC_FeaturePS) && (mode & CS_MODE_PS) == 0) { |
255 | 18.7k | return false; |
256 | 18.7k | } |
257 | | |
258 | | // No AIX support for now. |
259 | 217k | if (feature == PPC_FeatureModernAIXAs || feature == PPC_AIXOS) |
260 | 72.2k | return false; |
261 | | // TODO Make it optional |
262 | 145k | if (feature == PPC_FeatureMSYNC) |
263 | 49 | return false; |
264 | | |
265 | | // By default support everything |
266 | 145k | return true; |
267 | 145k | } |
268 | | |
269 | | #ifndef CAPSTONE_DIET |
270 | | static const map_insn_ops insn_operands[] = { |
271 | | #include "PPCGenCSMappingInsnOp.inc" |
272 | | }; |
273 | | #endif |
274 | | |
275 | | /// @brief Handles memory operands. |
276 | | /// @param MI The MCInst. |
277 | | /// @param OpNum The operand index. |
278 | | static void handle_memory_operand(MCInst *MI, unsigned OpNum) |
279 | 23.4k | { |
280 | 23.4k | cs_op_type op_type = map_get_op_type(MI, OpNum) & ~CS_OP_MEM; |
281 | | |
282 | | // If this is called from printOperand() we do not know if a |
283 | | // register is a base or an offset reg (imm is always disponent). |
284 | | // So we assume the base register is always added before the offset register |
285 | | // and set the flag appropriately. |
286 | 23.4k | bool is_off_reg = |
287 | 23.4k | ((op_type == CS_OP_REG) && |
288 | 23.4k | PPC_get_detail_op(MI, 0)->mem.base != PPC_REG_INVALID); |
289 | 23.4k | PPC_set_detail_op_mem(MI, OpNum, MCInst_getOpVal(MI, OpNum), |
290 | 23.4k | is_off_reg); |
291 | 23.4k | } |
292 | | |
293 | | static void add_cs_detail_general(MCInst *MI, ppc_op_group op_group, |
294 | | unsigned OpNum) |
295 | 225k | { |
296 | 225k | if (!detail_is_set(MI)) |
297 | 0 | return; |
298 | | |
299 | 225k | switch (op_group) { |
300 | 0 | default: |
301 | 0 | printf("General operand group %d not handled!\n", op_group); |
302 | 0 | return; |
303 | 142k | case PPC_OP_GROUP_Operand: { |
304 | 142k | cs_op_type op_type = map_get_op_type(MI, OpNum); |
305 | | |
306 | | // Check for memory operands emitted via printOperand() |
307 | 142k | if (doing_mem(MI) && !(op_type & CS_OP_MEM)) { |
308 | | // Close previous memory operand |
309 | 412 | set_mem_access(MI, false); |
310 | 141k | } else if (doing_mem(MI) || (op_type & CS_OP_MEM)) { |
311 | | // The memory operands use printOperand() to |
312 | | // emit their register and immediates. |
313 | 23.4k | if (!doing_mem(MI)) |
314 | 902 | set_mem_access(MI, true); |
315 | 23.4k | handle_memory_operand(MI, OpNum); |
316 | 23.4k | return; |
317 | 23.4k | } |
318 | | |
319 | 118k | assert((op_type & CS_OP_MEM) == |
320 | 118k | 0); // doing_mem should have been true. |
321 | | |
322 | 118k | if (op_type == CS_OP_REG) |
323 | 117k | PPC_set_detail_op_reg(MI, OpNum, |
324 | 117k | MCInst_getOpVal(MI, OpNum)); |
325 | 823 | else if (op_type == CS_OP_IMM) |
326 | 823 | PPC_set_detail_op_imm(MI, OpNum, |
327 | 823 | MCInst_getOpVal(MI, OpNum)); |
328 | 0 | else |
329 | 0 | assert(0 && "Operand type not handled."); |
330 | 118k | break; |
331 | 118k | } |
332 | 118k | case PPC_OP_GROUP_ImmZeroOperand: |
333 | 3.34k | case PPC_OP_GROUP_U1ImmOperand: |
334 | 3.99k | case PPC_OP_GROUP_U2ImmOperand: |
335 | 5.15k | case PPC_OP_GROUP_U3ImmOperand: |
336 | 7.04k | case PPC_OP_GROUP_U4ImmOperand: |
337 | 20.3k | case PPC_OP_GROUP_U5ImmOperand: |
338 | 22.0k | case PPC_OP_GROUP_U6ImmOperand: |
339 | 22.1k | case PPC_OP_GROUP_U7ImmOperand: |
340 | 22.1k | case PPC_OP_GROUP_U8ImmOperand: |
341 | 22.2k | case PPC_OP_GROUP_U10ImmOperand: |
342 | 22.4k | case PPC_OP_GROUP_U12ImmOperand: |
343 | 22.4k | PPC_set_detail_op_imm(MI, OpNum, |
344 | 22.4k | (uint32_t)MCInst_getOpVal(MI, OpNum)); |
345 | 22.4k | break; |
346 | 5.17k | case PPC_OP_GROUP_U16ImmOperand: |
347 | 5.17k | if (!MCOperand_isImm(MCInst_getOperand(MI, OpNum))) |
348 | | // Handled in printOperand() |
349 | 0 | return; |
350 | 5.17k | PPC_set_detail_op_imm(MI, OpNum, |
351 | 5.17k | (uint32_t)MCInst_getOpVal(MI, OpNum)); |
352 | 5.17k | break; |
353 | 28 | case PPC_OP_GROUP_S5ImmOperand: { |
354 | 28 | int Imm = MCOperand_getImm(MCInst_getOperand(MI, (OpNum))); |
355 | 28 | Imm = SignExtend32((Imm), 5); |
356 | 28 | PPC_set_detail_op_imm(MI, OpNum, Imm); |
357 | 28 | break; |
358 | 5.17k | } |
359 | 698 | case PPC_OP_GROUP_S12ImmOperand: { |
360 | 698 | int64_t Imm = SignExtend64( |
361 | 698 | MCOperand_getImm(MCInst_getOperand(MI, (OpNum))), 12); |
362 | 698 | if (doing_mem(MI)) { |
363 | 698 | PPC_set_detail_op_mem(MI, OpNum, Imm, true); |
364 | 698 | break; |
365 | 698 | } |
366 | 0 | PPC_set_detail_op_imm(MI, OpNum, Imm); |
367 | 0 | break; |
368 | 698 | } |
369 | 25.6k | case PPC_OP_GROUP_S16ImmOperand: { |
370 | 25.6k | if (!MCOperand_isImm(MCInst_getOperand(MI, OpNum))) |
371 | | // Handled in printOperand() |
372 | 0 | return; |
373 | 25.6k | int16_t Imm = MCOperand_getImm(MCInst_getOperand(MI, (OpNum))); |
374 | 25.6k | if (doing_mem(MI)) { |
375 | 16.3k | PPC_set_detail_op_mem(MI, OpNum, Imm, true); |
376 | 16.3k | break; |
377 | 16.3k | } |
378 | 9.27k | PPC_set_detail_op_imm(MI, OpNum, Imm); |
379 | 9.27k | break; |
380 | 25.6k | } |
381 | 648 | case PPC_OP_GROUP_S34ImmOperand: { |
382 | 648 | if (!MCOperand_isImm(MCInst_getOperand(MI, OpNum))) |
383 | | // Handled in printOperand() |
384 | 0 | return; |
385 | 648 | int64_t Imm = MCOperand_getImm(MCInst_getOperand(MI, (OpNum))); |
386 | 648 | if (doing_mem(MI)) { |
387 | 437 | PPC_set_detail_op_mem(MI, OpNum, Imm, true); |
388 | 437 | break; |
389 | 437 | } |
390 | 211 | PPC_set_detail_op_imm(MI, OpNum, Imm); |
391 | 211 | break; |
392 | 648 | } |
393 | 0 | case PPC_OP_GROUP_ATBitsAsHint: { |
394 | 0 | PPC_get_detail(MI)->bc.hint = |
395 | 0 | (ppc_br_hint)MCInst_getOpVal(MI, OpNum); |
396 | 0 | break; |
397 | 648 | } |
398 | 3.16k | case PPC_OP_GROUP_AbsBranchOperand: { |
399 | 3.16k | if (!MCOperand_isImm(MCInst_getOperand(MI, OpNum))) |
400 | | // Handled in printOperand() |
401 | 0 | return; |
402 | 3.16k | unsigned Val = MCInst_getOpVal(MI, OpNum) << 2; |
403 | 3.16k | int32_t Imm = SignExtend32(Val, 32); |
404 | 3.16k | PPC_get_detail_op(MI, 0)->type = PPC_OP_IMM; |
405 | 3.16k | PPC_get_detail_op(MI, 0)->imm = Imm; |
406 | 3.16k | PPC_get_detail_op(MI, 0)->access = map_get_op_access(MI, OpNum); |
407 | 3.16k | PPC_inc_op_count(MI); |
408 | 3.16k | break; |
409 | 3.16k | } |
410 | 0 | case PPC_OP_GROUP_TLSCall: |
411 | | // Handled in PPCInstPrinter and printOperand. |
412 | 0 | return; |
413 | 65 | case PPC_OP_GROUP_crbitm: { |
414 | 65 | unsigned CCReg = MCInst_getOpVal(MI, OpNum); |
415 | 65 | PPC_set_detail_op_reg(MI, OpNum, CCReg); |
416 | 65 | break; |
417 | 3.16k | } |
418 | 6.38k | case PPC_OP_GROUP_BranchOperand: { |
419 | 6.38k | if (!MCOperand_isImm(MCInst_getOperand(MI, (OpNum)))) |
420 | | // Handled in printOperand() |
421 | 0 | return; |
422 | 6.38k | int32_t Imm = SignExtend32( |
423 | 6.38k | ((unsigned)MCInst_getOpVal(MI, (OpNum)) << 2), 32); |
424 | 6.38k | uint64_t Address = MI->address + Imm; |
425 | 6.38k | if (IS_32BIT(MI->csh->mode)) |
426 | 1.88k | Address &= 0xffffffff; |
427 | 6.38k | PPC_get_detail_op(MI, 0)->type = PPC_OP_IMM; |
428 | 6.38k | PPC_get_detail_op(MI, 0)->imm = Address; |
429 | 6.38k | PPC_get_detail_op(MI, 0)->access = map_get_op_access(MI, OpNum); |
430 | 6.38k | PPC_inc_op_count(MI); |
431 | 6.38k | break; |
432 | 6.38k | } |
433 | | // Memory operands have their `set_mem_access()` calls |
434 | | // in PPCInstPrinter. |
435 | 16.3k | case PPC_OP_GROUP_MemRegImm: |
436 | 18.6k | case PPC_OP_GROUP_MemRegReg: { |
437 | | // These cases print 0 if the base register is R0. |
438 | | // So no printOperand() function is called. |
439 | | // We must handle the zero case here. |
440 | 18.6k | unsigned OpNumReg = 0; |
441 | 18.6k | if (op_group == PPC_OP_GROUP_MemRegImm) |
442 | 16.3k | OpNumReg = OpNum + 1; |
443 | 2.31k | else |
444 | 2.31k | OpNumReg = OpNum; |
445 | | |
446 | 18.6k | MCOperand *Op = MCInst_getOperand(MI, OpNumReg); |
447 | 18.6k | if (MCOperand_isReg(Op) && MCOperand_getReg(Op) == PPC_R0) { |
448 | 0 | PPC_get_detail_op(MI, 0)->mem.base = PPC_REG_ZERO; |
449 | 0 | PPC_get_detail_op(MI, 0)->type = PPC_OP_MEM; |
450 | 0 | PPC_get_detail_op(MI, 0)->access = |
451 | 0 | map_get_op_access(MI, OpNum); |
452 | 0 | } |
453 | 18.6k | break; |
454 | 16.3k | } |
455 | 21 | case PPC_OP_GROUP_MemRegImmHash: |
456 | 212 | case PPC_OP_GROUP_MemRegImm34: |
457 | 458 | case PPC_OP_GROUP_MemRegImm34PCRel: |
458 | | // Handled in other printOperand functions. |
459 | 458 | break; |
460 | 225k | } |
461 | 225k | } |
462 | | |
463 | | /// Fills cs_detail with the data of the operand. |
464 | | /// Calls to this function should not be added by hand! Please checkout the |
465 | | /// patch `AddCSDetail` of the CppTranslator. |
466 | | void PPC_add_cs_detail(MCInst *MI, ppc_op_group op_group, va_list args) |
467 | 225k | { |
468 | 225k | if (!detail_is_set(MI) || !map_fill_detail_ops(MI)) |
469 | 0 | return; |
470 | | |
471 | 225k | switch (op_group) { |
472 | 0 | default: |
473 | 0 | printf("Operand group %d not handled!\n", op_group); |
474 | 0 | return; |
475 | 0 | case PPC_OP_GROUP_PredicateOperand: { |
476 | 0 | unsigned OpNum = va_arg(args, unsigned); |
477 | 0 | const char *Modifier = va_arg(args, const char *); |
478 | 0 | if ((strcmp(Modifier, "cc") == 0) || |
479 | 0 | (strcmp(Modifier, "pm") == 0)) { |
480 | 0 | unsigned Val = MCInst_getOpVal(MI, OpNum); |
481 | 0 | unsigned bo = Val & 0x1f; |
482 | 0 | unsigned bi = (Val & 0x1e0) >> 5; |
483 | 0 | PPC_get_detail(MI)->bc.bo = bo; |
484 | 0 | PPC_get_detail(MI)->bc.bi = bi; |
485 | 0 | PPC_get_detail(MI)->bc.crX_bit = bi % 4; |
486 | 0 | PPC_get_detail(MI)->bc.crX = PPC_REG_CR0 + (bi / 4); |
487 | 0 | PPC_get_detail(MI)->bc.pred_cr = |
488 | 0 | PPC_get_branch_pred(bi, bo, true); |
489 | 0 | PPC_get_detail(MI)->bc.pred_ctr = |
490 | 0 | PPC_get_branch_pred(bi, bo, false); |
491 | 0 | PPC_get_detail(MI)->bc.hint = PPC_get_hint(bo); |
492 | 0 | } |
493 | 0 | return; |
494 | 0 | } |
495 | 698 | case PPC_OP_GROUP_S12ImmOperand: |
496 | 142k | case PPC_OP_GROUP_Operand: |
497 | 145k | case PPC_OP_GROUP_MemRegReg: |
498 | 146k | case PPC_OP_GROUP_U6ImmOperand: |
499 | 160k | case PPC_OP_GROUP_U5ImmOperand: |
500 | 176k | case PPC_OP_GROUP_MemRegImm: |
501 | 202k | case PPC_OP_GROUP_S16ImmOperand: |
502 | 202k | case PPC_OP_GROUP_U2ImmOperand: |
503 | 207k | case PPC_OP_GROUP_U16ImmOperand: |
504 | 214k | case PPC_OP_GROUP_BranchOperand: |
505 | 217k | case PPC_OP_GROUP_AbsBranchOperand: |
506 | 220k | case PPC_OP_GROUP_U1ImmOperand: |
507 | 220k | case PPC_OP_GROUP_TLSCall: |
508 | 221k | case PPC_OP_GROUP_U3ImmOperand: |
509 | 221k | case PPC_OP_GROUP_S5ImmOperand: |
510 | 221k | case PPC_OP_GROUP_MemRegImmHash: |
511 | 223k | case PPC_OP_GROUP_U4ImmOperand: |
512 | 223k | case PPC_OP_GROUP_U10ImmOperand: |
513 | 223k | case PPC_OP_GROUP_crbitm: |
514 | 224k | case PPC_OP_GROUP_S34ImmOperand: |
515 | 224k | case PPC_OP_GROUP_ImmZeroOperand: |
516 | 224k | case PPC_OP_GROUP_MemRegImm34: |
517 | 225k | case PPC_OP_GROUP_MemRegImm34PCRel: |
518 | 225k | case PPC_OP_GROUP_U8ImmOperand: |
519 | 225k | case PPC_OP_GROUP_U12ImmOperand: |
520 | 225k | case PPC_OP_GROUP_U7ImmOperand: |
521 | 225k | case PPC_OP_GROUP_ATBitsAsHint: { |
522 | 225k | unsigned OpNum = va_arg(args, unsigned); |
523 | 225k | add_cs_detail_general(MI, op_group, OpNum); |
524 | 225k | return; |
525 | 225k | } |
526 | 225k | } |
527 | 225k | } |
528 | | |
529 | | void PPC_set_detail_op_mem(MCInst *MI, unsigned OpNum, uint64_t Val, |
530 | | bool is_off_reg) |
531 | 40.9k | { |
532 | 40.9k | if (!detail_is_set(MI)) |
533 | 0 | return; |
534 | | |
535 | 40.9k | assert(map_get_op_type(MI, OpNum) & CS_OP_MEM); |
536 | 40.9k | cs_op_type secondary_type = map_get_op_type(MI, OpNum) & ~CS_OP_MEM; |
537 | | |
538 | 40.9k | switch (secondary_type) { |
539 | 0 | default: |
540 | 0 | assert(0 && "Secondary type not supported yet."); |
541 | 23.4k | case CS_OP_REG: |
542 | 23.4k | if (is_off_reg) { |
543 | 2.88k | PPC_get_detail_op(MI, 0)->mem.offset = Val; |
544 | 2.88k | if (PPC_get_detail_op(MI, 0)->mem.base != PPC_REG_INVALID) |
545 | 2.88k | set_mem_access(MI, false); |
546 | 20.6k | } else { |
547 | 20.6k | PPC_get_detail_op(MI, 0)->mem.base = Val; |
548 | 20.6k | if (MCInst_opIsTying(MI, OpNum)) |
549 | 0 | map_add_implicit_write(MI, MCInst_getOpVal(MI, OpNum)); |
550 | 20.6k | } |
551 | 23.4k | break; |
552 | 17.4k | case CS_OP_IMM: |
553 | 17.4k | PPC_get_detail_op(MI, 0)->mem.disp = Val; |
554 | 17.4k | if (PPC_get_detail_op(MI, 0)->mem.base != PPC_REG_INVALID) |
555 | 0 | set_mem_access(MI, false); |
556 | 17.4k | break; |
557 | 40.9k | } |
558 | | |
559 | 40.9k | PPC_get_detail_op(MI, 0)->type = PPC_OP_MEM; |
560 | 40.9k | PPC_get_detail_op(MI, 0)->access = map_get_op_access(MI, OpNum); |
561 | 40.9k | } |
562 | | |
563 | | /// Adds a register PPC operand at position OpNum and increases the op_count by |
564 | | /// one. |
565 | | void PPC_set_detail_op_reg(MCInst *MI, unsigned OpNum, ppc_reg Reg) |
566 | 117k | { |
567 | 117k | if (!detail_is_set(MI)) |
568 | 0 | return; |
569 | 117k | assert(!(map_get_op_type(MI, OpNum) & CS_OP_MEM)); |
570 | 117k | assert(map_get_op_type(MI, OpNum) == CS_OP_REG); |
571 | | |
572 | 117k | PPC_get_detail_op(MI, 0)->type = PPC_OP_REG; |
573 | 117k | PPC_get_detail_op(MI, 0)->reg = Reg; |
574 | 117k | PPC_get_detail_op(MI, 0)->access = map_get_op_access(MI, OpNum); |
575 | 117k | PPC_inc_op_count(MI); |
576 | 117k | } |
577 | | |
578 | | /// Adds an immediate PPC operand at position OpNum and increases the op_count |
579 | | /// by one. |
580 | | void PPC_set_detail_op_imm(MCInst *MI, unsigned OpNum, int64_t Imm) |
581 | 38.0k | { |
582 | 38.0k | if (!detail_is_set(MI)) |
583 | 0 | return; |
584 | 38.0k | assert(!(map_get_op_type(MI, OpNum) & CS_OP_MEM)); |
585 | 38.0k | assert(map_get_op_type(MI, OpNum) == CS_OP_IMM); |
586 | | |
587 | 38.0k | PPC_get_detail_op(MI, 0)->type = PPC_OP_IMM; |
588 | 38.0k | PPC_get_detail_op(MI, 0)->imm = Imm; |
589 | 38.0k | PPC_get_detail_op(MI, 0)->access = map_get_op_access(MI, OpNum); |
590 | 38.0k | PPC_inc_op_count(MI); |
591 | 38.0k | } |
592 | | |
593 | | void PPC_set_mem_access(MCInst *MI, bool status) |
594 | 44.0k | { |
595 | 44.0k | if (!detail_is_set(MI)) |
596 | 0 | return; |
597 | 44.0k | if ((!status && !doing_mem(MI)) || (status && doing_mem(MI))) |
598 | 2.46k | return; // Nothing to do |
599 | | |
600 | 41.6k | set_doing_mem(MI, status); |
601 | 41.6k | if (status) { |
602 | 20.8k | PPC_get_detail_op(MI, 0)->type = PPC_OP_MEM; |
603 | 20.8k | PPC_get_detail_op(MI, 0)->mem.base = PPC_REG_INVALID; |
604 | 20.8k | PPC_get_detail_op(MI, 0)->mem.offset = PPC_REG_INVALID; |
605 | 20.8k | PPC_get_detail_op(MI, 0)->mem.disp = 0; |
606 | | |
607 | 20.8k | #ifndef CAPSTONE_DIET |
608 | 20.8k | uint8_t access = |
609 | 20.8k | map_get_op_access(MI, PPC_get_detail(MI)->op_count); |
610 | 20.8k | PPC_get_detail_op(MI, 0)->access = access; |
611 | 20.8k | #endif |
612 | 20.8k | } else { |
613 | | // done, select the next operand slot |
614 | 20.8k | PPC_inc_op_count(MI); |
615 | 20.8k | } |
616 | 41.6k | } |
617 | | |
618 | | void PPC_setup_op(cs_ppc_op *op) |
619 | 682 | { |
620 | 682 | memset(op, 0, sizeof(cs_ppc_op)); |
621 | 682 | op->type = PPC_OP_INVALID; |
622 | 682 | } |
623 | | |
624 | | /// Inserts a immediate to the detail operands at @index. |
625 | | /// Already present operands are moved. |
626 | | void PPC_insert_detail_op_imm_at(MCInst *MI, unsigned index, int64_t Val, |
627 | | cs_ac_type access) |
628 | 682 | { |
629 | 682 | if (!detail_is_set(MI) || !map_fill_detail_ops(MI)) |
630 | 0 | return; |
631 | | |
632 | 682 | assert(PPC_get_detail(MI)->op_count < PPC_NUM_OPS); |
633 | | |
634 | 682 | cs_ppc_op op; |
635 | 682 | PPC_setup_op(&op); |
636 | 682 | op.type = PPC_OP_IMM; |
637 | 682 | op.imm = Val; |
638 | 682 | op.access = access; |
639 | | |
640 | 682 | cs_ppc_op *ops = PPC_get_detail(MI)->operands; |
641 | 682 | int i = PPC_get_detail(MI)->op_count - 1; |
642 | 682 | for (; i >= index; --i) { |
643 | 176 | ops[i + 1] = ops[i]; |
644 | 176 | if (i == index) |
645 | 176 | break; |
646 | 176 | } |
647 | 682 | ops[index] = op; |
648 | 682 | PPC_inc_op_count(MI); |
649 | 682 | } |
650 | | |
651 | | #endif |