/src/capstonenext/arch/RISCV/RISCVMapping.c
Line | Count | Source |
1 | | #include "capstone/cs_operand.h" |
2 | | #include "capstone/riscv.h" |
3 | | #include <stdint.h> |
4 | | #include <float.h> |
5 | | #include <math.h> |
6 | | #ifdef CAPSTONE_HAS_RISCV |
7 | | |
8 | | #include <string.h> |
9 | | |
10 | | #include "../../Mapping.h" |
11 | | #include "../../cs_simple_types.h" |
12 | | #include "../../utils.h" |
13 | | |
14 | | #include "RISCVMapping.h" |
15 | | |
16 | | #define GET_INSTRINFO_ENUM |
17 | | #include "RISCVGenInstrInfo.inc" |
18 | | |
19 | | #define GET_REGINFO_ENUM |
20 | | #define GET_REGINFO_MC_DESC |
21 | | #include "RISCVGenRegisterInfo.inc" |
22 | | |
23 | | #include "RISCVInstPrinter.h" |
24 | | |
25 | | const char *RISCV_reg_name(csh handle, unsigned int reg) |
26 | 17.5k | { |
27 | 17.5k | int syntax_opt = ((cs_struct *)(uintptr_t)handle)->syntax; |
28 | | |
29 | 17.5k | if (syntax_opt & CS_OPT_SYNTAX_NOREGNAME) { |
30 | 0 | return RISCV_LLVM_getRegisterName(reg, RISCV_NoRegAltName); |
31 | 0 | } |
32 | 17.5k | return RISCV_LLVM_getRegisterName(reg, RISCV_ABIRegAltName); |
33 | 17.5k | } |
34 | | |
35 | | static const insn_map insns[] = { |
36 | | #include "RISCVGenCSMappingInsn.inc" |
37 | | }; |
38 | | |
39 | | const insn_map *RISCV_insns = insns; |
40 | | const unsigned int RISCV_insn_count = ARR_SIZE(insns); |
41 | | |
42 | | #ifndef CAPSTONE_DIET |
43 | | |
44 | | static const map_insn_ops insn_operands[] = { |
45 | | #include "RISCVGenCSMappingInsnOp.inc" |
46 | | }; |
47 | | |
48 | | #endif |
49 | | |
50 | | void RISCV_add_cs_detail_0(MCInst *MI, riscv_op_group opgroup, unsigned OpNum) |
51 | 175k | { |
52 | 175k | if (!detail_is_set(MI)) |
53 | 0 | return; |
54 | | // are not "true" arguments and has no Capstone equivalent |
55 | 175k | if (opgroup == RISCV_OP_GROUP_FRMArg || |
56 | 172k | opgroup == RISCV_OP_GROUP_FRMArgLegacy) |
57 | 2.34k | return; |
58 | | |
59 | 172k | if (opgroup == RISCV_OP_GROUP_FPImmOperand) { |
60 | 147 | unsigned Imm = (unsigned)MCInst_getOperand(MI, OpNum)->ImmVal; |
61 | 147 | cs_riscv_op *op = RISCV_get_detail_op_at(MI, OpNum); |
62 | 147 | op->type = RISCV_OP_FP; |
63 | 147 | op->access = (cs_ac_type)map_get_op_access(MI, OpNum); |
64 | 147 | switch (Imm) { |
65 | 39 | case 1: // min |
66 | 39 | switch (MI->Opcode) { |
67 | 19 | case RISCV_FLI_S: |
68 | 19 | op->dimm = (double)FLT_MIN; |
69 | 19 | break; |
70 | 0 | case RISCV_FLI_D: |
71 | 0 | op->dimm = (double)DBL_MIN; |
72 | 0 | break; |
73 | 20 | case RISCV_FLI_H: |
74 | 20 | op->dimm = 6.103515625e-05; |
75 | 20 | break; |
76 | 0 | default: |
77 | 0 | op->dimm = 0.0; |
78 | 0 | break; |
79 | 39 | } |
80 | 39 | break; |
81 | 39 | case 30: // inf |
82 | 6 | op->dimm = INFINITY; |
83 | 6 | break; |
84 | 28 | case 31: // nan |
85 | 28 | op->dimm = NAN; |
86 | 28 | break; |
87 | 74 | default: |
88 | 74 | op->dimm = (double)getFPImm(Imm); |
89 | 74 | break; |
90 | 147 | } |
91 | 147 | RISCV_inc_op_count(MI); |
92 | 147 | return; |
93 | 147 | } |
94 | 172k | cs_riscv_op *op = RISCV_get_detail_op_at(MI, OpNum); |
95 | 172k | op->type = (riscv_op_type)map_get_op_type(MI, OpNum); |
96 | 172k | op->access = (cs_ac_type)map_get_op_access(MI, OpNum); |
97 | 172k | switch (map_get_op_type(MI, OpNum)) { |
98 | 103k | case CS_OP_REG: |
99 | 103k | op->reg = MCInst_getOperand(MI, OpNum)->RegVal; |
100 | 103k | break; |
101 | 0 | case CS_OP_MEM: |
102 | 0 | op->mem.base = 0; |
103 | 0 | op->mem.disp = MCInst_getOperand(MI, OpNum)->ImmVal; |
104 | 0 | break; |
105 | 35.4k | case CS_OP_IMM: { |
106 | 35.4k | uint64_t val = MCInst_getOperand(MI, OpNum)->ImmVal; |
107 | 35.4k | if (opgroup != RISCV_OP_GROUP_CSRSystemRegister) { |
108 | 32.8k | op->imm = val; |
109 | 32.8k | if (opgroup == RISCV_OP_GROUP_BranchOperand) { |
110 | 5.56k | op->imm += MI->address; |
111 | 5.56k | } |
112 | 32.8k | } else /* system register read-write */ { |
113 | 2.53k | op->type = RISCV_OP_CSR; |
114 | 2.53k | op->csr = val; |
115 | | // CSR instruction always read-writes the system operand |
116 | 2.53k | op->access = CS_AC_READ_WRITE; |
117 | 2.53k | } |
118 | 35.4k | break; |
119 | 0 | } |
120 | 17.9k | case CS_OP_MEM_REG: |
121 | 17.9k | op->type = (riscv_op_type)CS_OP_MEM; |
122 | 17.9k | op->mem.base = MCInst_getOperand(MI, OpNum)->RegVal; |
123 | 17.9k | break; |
124 | 15.4k | case CS_OP_MEM_IMM: |
125 | | // fill in the disp in the last operand |
126 | 15.4k | op = RISCV_get_detail_op_at(MI, OpNum - 1); |
127 | 15.4k | op->type = (riscv_op_type)CS_OP_MEM; |
128 | 15.4k | op->mem.disp = MCInst_getOperand(MI, OpNum)->ImmVal; |
129 | 15.4k | RISCV_dec_op_count( |
130 | 15.4k | MI); // don't increase the count, cancel the coming increment |
131 | 15.4k | break; |
132 | 259 | case CS_OP_INVALID: |
133 | 259 | break; |
134 | 0 | default: { |
135 | 0 | CS_ASSERT(0 && "unhandled operand type"); |
136 | 0 | } |
137 | 172k | } |
138 | 172k | RISCV_inc_op_count(MI); |
139 | 172k | } |
140 | | |
141 | | static inline void RISCV_add_adhoc_groups(MCInst *MI); |
142 | | |
143 | | void RISCV_add_groups(MCInst *MI) |
144 | 71.9k | { |
145 | 71.9k | if (!detail_is_set(MI)) |
146 | 0 | return; |
147 | | |
148 | 71.9k | get_detail(MI)->groups_count = 0; |
149 | | |
150 | 71.9k | #ifndef CAPSTONE_DIET |
151 | 71.9k | int i = 0; |
152 | 163k | while (insns[MI->Opcode].groups[i] != 0) { |
153 | 91.7k | add_group(MI, insns[MI->Opcode].groups[i]); |
154 | 91.7k | i++; |
155 | 91.7k | } |
156 | 71.9k | #endif |
157 | | |
158 | 71.9k | RISCV_add_adhoc_groups(MI); |
159 | 71.9k | } |
160 | | |
161 | | enum { |
162 | | #define GET_ENUM_VALUES_RISCVOpcode |
163 | | #include "RISCVGenCSSystemOperandsEnum.inc" |
164 | | }; |
165 | | |
166 | | static inline void RISCV_add_privileged_group(MCInst *MI) |
167 | 71.9k | { |
168 | 71.9k | const uint8_t *bytes = MI->flat_insn->bytes; |
169 | 71.9k | uint8_t opcode = bytes[0] & 0x80; |
170 | | // no privileged instruction has a major opcode other than SYSTEM |
171 | 71.9k | if (opcode != RISCV_RISCVOPCODE_SYSTEM) { |
172 | 71.9k | return; |
173 | 71.9k | } |
174 | 0 | uint8_t func3 = (bytes[1] >> 4) & 0x7; |
175 | | // no privileged instruction has a minor opcode other than PRIV or PRIVM |
176 | 0 | if (func3 != 0 && func3 != 0x4) { |
177 | 0 | return; |
178 | 0 | } |
179 | 0 | uint16_t func12 = readBytes16(MI, &(bytes[2])) >> 4; |
180 | | // ecall and ebreak has SYSTEM and PRIV but aren't privileged |
181 | 0 | if (func12 == 0 || func12 == 1) { |
182 | 0 | return; |
183 | 0 | } |
184 | 0 | uint8_t func6 = func12 >> 6; |
185 | | // a subspace under extension-defined custom SYSTEM instructions that is not privileged |
186 | 0 | if (func6 == 0x23 || func6 == 0x33) { |
187 | 0 | return; |
188 | 0 | } |
189 | 0 | add_group(MI, RISCV_GRP_PRIVILEGE); |
190 | 0 | } |
191 | | |
192 | | static inline void RISCV_add_interrupt_group(MCInst *MI) |
193 | 71.9k | { |
194 | 71.9k | if (MI->Opcode == RISCV_ECALL || MI->Opcode == RISCV_EBREAK) { |
195 | 57 | add_group(MI, RISCV_GRP_INT); |
196 | 57 | } |
197 | 71.9k | } |
198 | | |
199 | | static inline void RISCV_add_interrupt_ret_group(MCInst *MI) |
200 | 71.9k | { |
201 | 71.9k | if (MI->Opcode == RISCV_MRET || MI->Opcode == RISCV_SRET) { |
202 | 58 | add_group(MI, RISCV_GRP_IRET); |
203 | 58 | } |
204 | 71.9k | } |
205 | | |
206 | | // calls are implemented in RISCV as plain jumps that happen to set a link register containing the return address |
207 | | // but this link register could be given as the null register x0, discarding the return address and making them jumps |
208 | | static inline void RISCV_add_call_group(MCInst *MI) |
209 | 71.9k | { |
210 | 71.9k | if (MI->Opcode == RISCV_JAL || MI->Opcode == RISCV_JALR) { |
211 | 1.24k | cs_riscv_op *op = RISCV_get_detail_op_at(MI, 0); |
212 | 1.24k | if ((op->type == (riscv_op_type)CS_OP_REG) && |
213 | 456 | op->reg != RISCV_REG_X0 && (op->access & CS_AC_WRITE)) { |
214 | 456 | add_group(MI, RISCV_GRP_CALL); |
215 | 456 | } |
216 | 1.24k | if (MI->Opcode == RISCV_JAL) { |
217 | 530 | add_group(MI, RISCV_GRP_BRANCH_RELATIVE); |
218 | 530 | } |
219 | 1.24k | } |
220 | 71.9k | } |
221 | | |
222 | | // returns are implemented in RISCV as a plain indirect jump that happen to reference the return address register ra == x1 |
223 | | static inline void RISCV_add_ret_group(MCInst *MI) |
224 | 71.9k | { |
225 | 71.9k | if (MI->Opcode == RISCV_C_JR) { |
226 | | // indirect jumps whose source is ra |
227 | 713 | cs_riscv_op *op = RISCV_get_detail_op_at(MI, 0); |
228 | 713 | if ((op->type == (riscv_op_type)CS_OP_REG) && |
229 | 0 | op->reg == RISCV_REG_X1) { |
230 | 0 | add_group(MI, RISCV_GRP_RET); |
231 | 713 | } else { |
232 | 713 | add_group(MI, RISCV_GRP_JUMP); |
233 | 713 | } |
234 | 713 | } |
235 | 71.9k | if (MI->Opcode == RISCV_JALR) { |
236 | | // indirect jumps whose source is ra |
237 | 715 | cs_riscv_op *dstreg = RISCV_get_detail_op_at(MI, 0); |
238 | 715 | cs_riscv_op *op = RISCV_get_detail_op_at(MI, 1); |
239 | 715 | cs_riscv_op *op2 = RISCV_get_detail_op_at(MI, 2); |
240 | 715 | if ((op->type == (riscv_op_type)CS_OP_REG) && |
241 | 362 | op->reg == RISCV_REG_X1 && |
242 | 84 | op2->type == (riscv_op_type)CS_OP_IMM && op2->imm == 0 && |
243 | 0 | dstreg->type == (riscv_op_type)CS_OP_REG && |
244 | 0 | dstreg->reg == RISCV_REG_X0) { |
245 | 0 | add_group(MI, RISCV_GRP_RET); |
246 | 715 | } else { |
247 | 715 | if (!((dstreg->type == (riscv_op_type)CS_OP_REG) && |
248 | 62 | dstreg->reg != RISCV_REG_X0 && |
249 | 653 | (dstreg->access & CS_AC_WRITE))) { |
250 | 653 | add_group(MI, RISCV_GRP_JUMP); |
251 | 653 | } |
252 | 715 | } |
253 | 715 | } |
254 | 71.9k | } |
255 | | |
256 | | static inline void RISCV_add_adhoc_groups(MCInst *MI) |
257 | 71.9k | { |
258 | 71.9k | RISCV_add_privileged_group(MI); |
259 | 71.9k | RISCV_add_interrupt_group(MI); |
260 | 71.9k | RISCV_add_interrupt_ret_group(MI); |
261 | 71.9k | RISCV_add_call_group(MI); |
262 | 71.9k | RISCV_add_ret_group(MI); |
263 | 71.9k | } |
264 | | |
265 | | // for weird reasons some instructions end up with valid operands that are |
266 | | // interspersed with invalid operands, i.e. the operands array is an "island" |
267 | | // of valid operands with invalid gaps between them, this function will compactify |
268 | | // all the valid operands and pad the rest of the array to invalid |
269 | | void RISCV_compact_operands(MCInst *MI) |
270 | 71.9k | { |
271 | 71.9k | if (!detail_is_set(MI)) |
272 | 0 | return; |
273 | 71.9k | cs_riscv_op *ops = RISCV_get_detail(MI)->operands; |
274 | 71.9k | unsigned int write_pos = 0; |
275 | | |
276 | | // Move valid elements to front |
277 | 647k | for (unsigned int read_pos = 0; read_pos < NUM_RISCV_OPS; read_pos++) { |
278 | 575k | if (ops[read_pos].type != (riscv_op_type)CS_OP_INVALID) { |
279 | 172k | if (write_pos != read_pos) { |
280 | 21.4k | ops[write_pos] = ops[read_pos]; |
281 | 21.4k | } |
282 | 172k | write_pos++; |
283 | 172k | } |
284 | 575k | } |
285 | | // fill the rest, if any, with invalid |
286 | 71.9k | memset((void *)(&ops[write_pos]), CS_OP_INVALID, |
287 | 71.9k | (NUM_RISCV_OPS - write_pos) * sizeof(cs_riscv_op)); |
288 | 71.9k | } |
289 | | |
290 | | // some RISC-V instructions have only 2 apparent operands, one of them is read-write |
291 | | // the actual operand information for those instruction should have 3 operands, the first and second are the same operand, |
292 | | // but once with read and once write access |
293 | | // when those instructions are disassembled only the operand entry with the read access is used, |
294 | | // and therefore the read-write operand is wrongly classified as only-read |
295 | | // this logic tries to correct that |
296 | | void RISCV_add_missing_write_access(MCInst *MI) |
297 | 71.9k | { |
298 | 71.9k | if (!detail_is_set(MI)) |
299 | 0 | return; |
300 | 71.9k | if (!isCompressed(MI)) |
301 | 26.5k | return; |
302 | | |
303 | 45.4k | cs_riscv *riscv_details = RISCV_get_detail(MI); |
304 | 45.4k | cs_riscv_op *ops = riscv_details->operands; |
305 | | // make the detection condition as specific as possible |
306 | | // so it doesn't accidentally trigger for other cases |
307 | 45.4k | if (riscv_details->op_count == 2 && ops[0].type == RISCV_OP_INVALID && |
308 | 0 | ops[1].type == RISCV_OP_REG && ops[1].access == CS_AC_READ) { |
309 | 0 | ops[1].access |= CS_AC_WRITE; |
310 | 0 | } |
311 | 45.4k | } |
312 | | |
313 | | // given internal insn id, return public instruction info |
314 | | void RISCV_get_insn_id(cs_struct *h, cs_insn *insn, unsigned int id) |
315 | 71.9k | { |
316 | 71.9k | insn_map const *insn_map = NULL; |
317 | | |
318 | 71.9k | if ((insn_map = lookup_insn_map(h, id))) { |
319 | 71.9k | insn->id = insn_map->mapid; |
320 | | |
321 | 71.9k | if (h->detail_opt) { |
322 | 71.9k | #ifndef CAPSTONE_DIET |
323 | 71.9k | memcpy(insn->detail->regs_read, insn_map->regs_use, |
324 | 71.9k | sizeof(insn_map->regs_use)); |
325 | 71.9k | insn->detail->regs_read_count = |
326 | 71.9k | (uint8_t)count_positive(insn_map->regs_use); |
327 | | |
328 | 71.9k | memcpy(insn->detail->regs_write, insn_map->regs_mod, |
329 | 71.9k | sizeof(insn_map->regs_mod)); |
330 | 71.9k | insn->detail->regs_write_count = |
331 | 71.9k | (uint8_t)count_positive(insn_map->regs_mod); |
332 | | |
333 | 71.9k | memcpy(insn->detail->groups, insn_map->groups, |
334 | 71.9k | sizeof(insn_map->groups)); |
335 | 71.9k | insn->detail->groups_count = |
336 | 71.9k | (uint8_t)count_positive8(insn_map->groups); |
337 | | |
338 | 71.9k | if (insn_map->branch || insn_map->indirect_branch) { |
339 | | // this insn also belongs to JUMP group. add JUMP group |
340 | 4.36k | insn->detail |
341 | 4.36k | ->groups[insn->detail->groups_count] = |
342 | 4.36k | RISCV_GRP_JUMP; |
343 | 4.36k | insn->detail->groups_count++; |
344 | 4.36k | } |
345 | 71.9k | #endif |
346 | 71.9k | } |
347 | 71.9k | } |
348 | 71.9k | } |
349 | | |
350 | | static const char *const insn_name_maps[] = { |
351 | | /*RISCV_INS_INVALID:*/ NULL, |
352 | | |
353 | | #include "RISCVGenCSMappingInsnName.inc" |
354 | | }; |
355 | | |
356 | | const char *RISCV_insn_name(csh handle, unsigned int id) |
357 | 71.9k | { |
358 | 71.9k | #ifndef CAPSTONE_DIET |
359 | 71.9k | if (id >= RISCV_INS_ENDING) |
360 | 0 | return NULL; |
361 | | |
362 | 71.9k | return insn_name_maps[id]; |
363 | | #else |
364 | | return NULL; |
365 | | #endif |
366 | 71.9k | } |
367 | | |
368 | | #ifndef CAPSTONE_DIET |
369 | | static const name_map group_name_maps[] = { |
370 | | // generic groups |
371 | | { RISCV_GRP_INVALID, NULL }, |
372 | | { RISCV_GRP_JUMP, "jump" }, |
373 | | { RISCV_GRP_CALL, "call" }, |
374 | | { RISCV_GRP_RET, "ret" }, |
375 | | { RISCV_GRP_INT, "int" }, |
376 | | { RISCV_GRP_IRET, "iret" }, |
377 | | { RISCV_GRP_PRIVILEGE, "privileged" }, |
378 | | { RISCV_GRP_BRANCH_RELATIVE, "branch_relative" }, |
379 | | |
380 | | // architecture specific |
381 | | #include "RISCVGenCSFeatureName.inc" |
382 | | |
383 | | { RISCV_GRP_ENDING, NULL } |
384 | | }; |
385 | | #endif |
386 | | |
387 | | const char *RISCV_group_name(csh handle, unsigned int id) |
388 | 94.1k | { |
389 | 94.1k | #ifndef CAPSTONE_DIET |
390 | | // verify group id |
391 | | // if past the end |
392 | 94.1k | if (id >= RISCV_GRP_ENDING || |
393 | | // or in the encoding gap between generic groups and arch-specific groups |
394 | 94.1k | (id > RISCV_GRP_BRANCH_RELATIVE && id < RISCV_FEATURE_HASSTDEXTI)) |
395 | 0 | return NULL; |
396 | 94.1k | return id2name(group_name_maps, ARR_SIZE(group_name_maps), id); |
397 | | #else |
398 | | return NULL; |
399 | | #endif |
400 | 94.1k | } |
401 | | |
402 | | // map instruction name to public instruction ID |
403 | | riscv_insn RISCV_map_insn(const char *name) |
404 | 0 | { |
405 | 0 | unsigned int i; |
406 | 0 | for (i = 1; i < ARR_SIZE(insn_name_maps); i++) { |
407 | 0 | if (!strcmp(name, insn_name_maps[i])) |
408 | 0 | return i; |
409 | 0 | } |
410 | 0 | return RISCV_INS_INVALID; |
411 | 0 | } |
412 | | |
413 | | void RISCV_init(MCRegisterInfo *MRI) |
414 | 1.47k | { |
415 | 1.47k | MCRegisterInfo_InitMCRegisterInfo(MRI, RISCVRegDesc, RISCV_REG_ENDING, |
416 | 1.47k | 0, 0, RISCVMCRegisterClasses, |
417 | 1.47k | ARR_SIZE(RISCVMCRegisterClasses), 0, |
418 | 1.47k | 0, RISCVRegDiffLists, 0, |
419 | 1.47k | RISCVSubRegIdxLists, |
420 | 1.47k | ARR_SIZE(RISCVSubRegIdxLists), 0); |
421 | 1.47k | } |
422 | | |
423 | | #endif |