Coverage Report

Created: 2026-11-12 06:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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 "RISCVBaseInfo.h"
15
#include "RISCVMapping.h"
16
17
#define GET_INSTRINFO_ENUM
18
#include "RISCVGenInstrInfo.inc"
19
20
#define GET_REGINFO_ENUM
21
#define GET_REGINFO_MC_DESC
22
#include "RISCVGenRegisterInfo.inc"
23
24
#include "RISCVInstPrinter.h"
25
26
const char *RISCV_reg_name(csh handle, unsigned int reg)
27
27.4k
{
28
27.4k
  int syntax_opt = ((cs_struct *)(uintptr_t)handle)->syntax;
29
30
27.4k
  if (syntax_opt & CS_OPT_SYNTAX_NOREGNAME) {
31
0
    return RISCV_LLVM_getRegisterName(reg, RISCV_NoRegAltName);
32
0
  }
33
27.4k
  return RISCV_LLVM_getRegisterName(reg, RISCV_ABIRegAltName);
34
27.4k
}
35
36
static const insn_map insns[] = {
37
#include "RISCVGenCSMappingInsn.inc"
38
};
39
40
const insn_map *RISCV_insns = insns;
41
const unsigned int RISCV_insn_count = ARR_SIZE(insns);
42
43
#ifndef CAPSTONE_DIET
44
static const map_insn_ops insn_operands[] = {
45
#include "RISCVGenCSMappingInsnOp.inc"
46
};
47
48
static const name_map insn_alias_mnem_map[] = {
49
#include "RISCVGenCSAliasMnemMap.inc"
50
};
51
#endif
52
53
void RISCV_add_cs_detail_0(MCInst *MI, riscv_op_group opgroup, unsigned OpNum)
54
208k
{
55
208k
  if (!detail_is_set(MI))
56
0
    return;
57
  // Rounding mode: store in detail, not as a regular operand.
58
208k
  if (opgroup == RISCV_OP_GROUP_FRMArg ||
59
204k
      opgroup == RISCV_OP_GROUP_FRMArgLegacy) {
60
4.69k
    unsigned frm = (unsigned)MCInst_getOperand(MI, OpNum)->ImmVal;
61
4.69k
    riscv_rounding_mode rm;
62
4.69k
    switch (frm) {
63
1.35k
    case RISCVFPRndMode_RNE:
64
1.35k
      rm = RISCV_RM_RNE;
65
1.35k
      break;
66
859
    case RISCVFPRndMode_RTZ:
67
859
      rm = RISCV_RM_RTZ;
68
859
      break;
69
1.21k
    case RISCVFPRndMode_RDN:
70
1.21k
      rm = RISCV_RM_RDN;
71
1.21k
      break;
72
490
    case RISCVFPRndMode_RUP:
73
490
      rm = RISCV_RM_RUP;
74
490
      break;
75
79
    case RISCVFPRndMode_RMM:
76
79
      rm = RISCV_RM_RMM;
77
79
      break;
78
700
    case RISCVFPRndMode_DYN:
79
700
      rm = RISCV_RM_DYN;
80
700
      break;
81
0
    default:
82
0
      rm = RISCV_RM_INVALID;
83
0
      break;
84
4.69k
    }
85
4.69k
    RISCV_get_detail(MI)->rounding_mode = rm;
86
4.69k
    return;
87
4.69k
  }
88
89
  // unmasked instructions, the mask register is not real
90
203k
  if (opgroup == RISCV_OP_GROUP_VMaskReg) {
91
5.93k
    MCOperand *mask = MCInst_getOperand(MI, OpNum);
92
5.93k
    if (MCOperand_isReg(mask) &&
93
5.93k
        MCOperand_getReg(mask) == RISCV_NoRegister)
94
3.63k
      return;
95
5.93k
  }
96
97
199k
  if (opgroup == RISCV_OP_GROUP_FPImmOperand) {
98
2.43k
    unsigned Imm = (unsigned)MCInst_getOperand(MI, OpNum)->ImmVal;
99
2.43k
    cs_riscv_op *op = RISCV_get_detail_op_at(MI, OpNum);
100
2.43k
    op->type = RISCV_OP_FP;
101
2.43k
    op->access = (cs_ac_type)map_get_op_access(MI, OpNum);
102
2.43k
    switch (Imm) {
103
419
    case 1: // min
104
419
      switch (MI->Opcode) {
105
202
      case RISCV_FLI_S:
106
202
        op->dimm = (double)FLT_MIN;
107
202
        break;
108
0
      case RISCV_FLI_D:
109
0
        op->dimm = (double)DBL_MIN;
110
0
        break;
111
217
      case RISCV_FLI_H:
112
217
        op->dimm = 6.103515625e-05;
113
217
        break;
114
0
      default:
115
0
        op->dimm = 0.0;
116
0
        break;
117
419
      }
118
419
      break;
119
639
    case 30: // inf
120
639
      op->dimm = INFINITY;
121
639
      break;
122
211
    case 31: // nan
123
211
      op->dimm = NAN;
124
211
      break;
125
1.16k
    default:
126
1.16k
      op->dimm = (double)getFPImm(Imm);
127
1.16k
      break;
128
2.43k
    }
129
2.43k
    RISCV_inc_op_count(MI);
130
2.43k
    return;
131
2.43k
  }
132
197k
  cs_riscv_op *op = RISCV_get_detail_op_at(MI, OpNum);
133
197k
  op->type = (riscv_op_type)map_get_op_type(MI, OpNum);
134
197k
  op->access = (cs_ac_type)map_get_op_access(MI, OpNum);
135
197k
  switch (map_get_op_type(MI, OpNum)) {
136
122k
  case CS_OP_REG:
137
122k
    op->reg = MCInst_getOperand(MI, OpNum)->RegVal;
138
122k
    break;
139
0
  case CS_OP_MEM:
140
0
    op->mem.base = 0;
141
0
    op->mem.disp = MCInst_getOperand(MI, OpNum)->ImmVal;
142
0
    break;
143
40.3k
  case CS_OP_IMM: {
144
40.3k
    uint64_t val = MCInst_getOperand(MI, OpNum)->ImmVal;
145
40.3k
    if (opgroup != RISCV_OP_GROUP_CSRSystemRegister) {
146
37.9k
      op->imm = val;
147
37.9k
      if (opgroup == RISCV_OP_GROUP_BranchOperand) {
148
5.02k
        op->imm += MI->address;
149
5.02k
      }
150
37.9k
    } else /* system register read-write */ {
151
2.43k
      op->type = RISCV_OP_CSR;
152
2.43k
      op->csr = val;
153
      // CSR instruction always read-writes the system operand
154
2.43k
      op->access = CS_AC_READ_WRITE;
155
2.43k
    }
156
40.3k
    break;
157
0
  }
158
19.5k
  case CS_OP_MEM_REG:
159
19.5k
    op->type = (riscv_op_type)CS_OP_MEM;
160
19.5k
    op->mem.base = MCInst_getOperand(MI, OpNum)->RegVal;
161
19.5k
    break;
162
14.9k
  case CS_OP_MEM_IMM:
163
    // fill in the disp in the last operand
164
14.9k
    op = RISCV_get_detail_op_at(MI, OpNum - 1);
165
14.9k
    op->type = (riscv_op_type)CS_OP_MEM;
166
14.9k
    op->mem.disp = MCInst_getOperand(MI, OpNum)->ImmVal;
167
14.9k
    RISCV_dec_op_count(
168
14.9k
      MI); // don't increase the count, cancel the coming increment
169
14.9k
    break;
170
356
  case CS_OP_INVALID:
171
356
    break;
172
0
  default: {
173
0
    CS_ASSERT(0 && "unhandled operand type");
174
0
  }
175
197k
  }
176
197k
  RISCV_inc_op_count(MI);
177
197k
}
178
179
static inline void RISCV_add_adhoc_groups(MCInst *MI);
180
181
void RISCV_add_groups(MCInst *MI)
182
84.3k
{
183
84.3k
  if (!detail_is_set(MI))
184
0
    return;
185
186
84.3k
  get_detail(MI)->groups_count = 0;
187
188
84.3k
#ifndef CAPSTONE_DIET
189
84.3k
  int i = 0;
190
185k
  while (insns[MI->Opcode].groups[i] != 0) {
191
101k
    add_group(MI, insns[MI->Opcode].groups[i]);
192
101k
    i++;
193
101k
  }
194
84.3k
#endif
195
196
84.3k
  RISCV_add_adhoc_groups(MI);
197
84.3k
}
198
199
enum {
200
#define GET_ENUM_VALUES_RISCVOpcode
201
#include "RISCVGenCSSystemOperandsEnum.inc"
202
};
203
204
static inline void RISCV_add_privileged_group(MCInst *MI)
205
84.3k
{
206
84.3k
  const uint8_t *bytes = MI->flat_insn->bytes;
207
84.3k
  uint8_t opcode = bytes[0] & 0x80;
208
  // no privileged instruction has a major opcode other than SYSTEM
209
84.3k
  if (opcode != RISCV_RISCVOPCODE_SYSTEM) {
210
84.3k
    return;
211
84.3k
  }
212
0
  uint8_t func3 = (bytes[1] >> 4) & 0x7;
213
  // no privileged instruction has a minor opcode other than PRIV or PRIVM
214
0
  if (func3 != 0 && func3 != 0x4) {
215
0
    return;
216
0
  }
217
0
  uint16_t func12 = readBytes16(MI, &(bytes[2])) >> 4;
218
  // ecall and ebreak has SYSTEM and PRIV but aren't privileged
219
0
  if (func12 == 0 || func12 == 1) {
220
0
    return;
221
0
  }
222
0
  uint8_t func6 = func12 >> 6;
223
  // a subspace under extension-defined custom SYSTEM instructions that is not privileged
224
0
  if (func6 == 0x23 || func6 == 0x33) {
225
0
    return;
226
0
  }
227
0
  add_group(MI, RISCV_GRP_PRIVILEGE);
228
0
}
229
230
static inline void RISCV_add_interrupt_group(MCInst *MI)
231
84.3k
{
232
84.3k
  if (MI->Opcode == RISCV_ECALL || MI->Opcode == RISCV_EBREAK) {
233
595
    add_group(MI, RISCV_GRP_INT);
234
595
  }
235
84.3k
}
236
237
static inline void RISCV_add_interrupt_ret_group(MCInst *MI)
238
84.3k
{
239
84.3k
  if (MI->Opcode == RISCV_MRET || MI->Opcode == RISCV_SRET) {
240
148
    add_group(MI, RISCV_GRP_IRET);
241
148
  }
242
84.3k
}
243
244
// calls are implemented in RISCV as plain jumps that happen to set a link register containing the return address
245
// but this link register could be given as the null register x0, discarding the return address and making them jumps
246
static inline void RISCV_add_call_group(MCInst *MI)
247
84.3k
{
248
84.3k
  if (MI->Opcode == RISCV_JAL || MI->Opcode == RISCV_JALR) {
249
2.13k
    cs_riscv_op *op = RISCV_get_detail_op_at(MI, 0);
250
2.13k
    if ((op->type == (riscv_op_type)CS_OP_REG) &&
251
1.41k
        op->reg != RISCV_REG_X0 && (op->access & CS_AC_WRITE)) {
252
1.41k
      add_group(MI, RISCV_GRP_CALL);
253
1.41k
    }
254
2.13k
    if (MI->Opcode == RISCV_JAL) {
255
886
      add_group(MI, RISCV_GRP_BRANCH_RELATIVE);
256
886
    }
257
2.13k
  }
258
84.3k
}
259
260
// returns are implemented in RISCV as a plain indirect jump that happen to reference the return address register ra == x1
261
static inline void RISCV_add_ret_group(MCInst *MI)
262
84.3k
{
263
84.3k
  if (MI->Opcode == RISCV_C_JR) {
264
    // indirect jumps whose source is ra
265
373
    cs_riscv_op *op = RISCV_get_detail_op_at(MI, 0);
266
373
    if ((op->type == (riscv_op_type)CS_OP_REG) &&
267
0
        op->reg == RISCV_REG_X1) {
268
0
      add_group(MI, RISCV_GRP_RET);
269
373
    } else {
270
373
      add_group(MI, RISCV_GRP_JUMP);
271
373
    }
272
373
  }
273
84.3k
  if (MI->Opcode == RISCV_JALR) {
274
    // indirect jumps whose source is ra
275
1.24k
    cs_riscv_op *dstreg = RISCV_get_detail_op_at(MI, 0);
276
1.24k
    cs_riscv_op *op = RISCV_get_detail_op_at(MI, 1);
277
1.24k
    cs_riscv_op *op2 = RISCV_get_detail_op_at(MI, 2);
278
1.24k
    if ((op->type == (riscv_op_type)CS_OP_REG) &&
279
1.00k
        op->reg == RISCV_REG_X1 &&
280
476
        op2->type == (riscv_op_type)CS_OP_IMM && op2->imm == 0 &&
281
0
        dstreg->type == (riscv_op_type)CS_OP_REG &&
282
0
        dstreg->reg == RISCV_REG_X0) {
283
0
      add_group(MI, RISCV_GRP_RET);
284
1.24k
    } else {
285
1.24k
      if (!((dstreg->type == (riscv_op_type)CS_OP_REG) &&
286
694
            dstreg->reg != RISCV_REG_X0 &&
287
694
            (dstreg->access & CS_AC_WRITE))) {
288
551
        add_group(MI, RISCV_GRP_JUMP);
289
551
      }
290
1.24k
    }
291
1.24k
  }
292
84.3k
}
293
294
static inline void RISCV_add_adhoc_groups(MCInst *MI)
295
84.3k
{
296
84.3k
  RISCV_add_privileged_group(MI);
297
84.3k
  RISCV_add_interrupt_group(MI);
298
84.3k
  RISCV_add_interrupt_ret_group(MI);
299
84.3k
  RISCV_add_call_group(MI);
300
84.3k
  RISCV_add_ret_group(MI);
301
84.3k
}
302
303
// memset all stalled values in the detail struct to 0 before disassembling any next instruction
304
void RISCV_init_cs_detail(MCInst *MI)
305
85.1k
{
306
85.1k
  if (detail_is_set(MI))
307
85.1k
    memset(get_detail(MI), 0,
308
85.1k
           offsetof(cs_detail, riscv) + sizeof(cs_riscv));
309
85.1k
}
310
311
// for weird reasons some instructions end up with valid operands that are
312
// interspersed with invalid operands, i.e. the operands array is an "island"
313
// of valid operands with invalid gaps between them, this function will compactify
314
// all the valid operands and pad the rest of the array to invalid
315
void RISCV_compact_operands(MCInst *MI)
316
84.3k
{
317
84.3k
  if (!detail_is_set(MI))
318
0
    return;
319
84.3k
  cs_riscv_op *ops = RISCV_get_detail(MI)->operands;
320
84.3k
  unsigned int write_pos = 0;
321
322
  // Move valid elements to front
323
759k
  for (unsigned int read_pos = 0; read_pos < NUM_RISCV_OPS; read_pos++) {
324
674k
    if (ops[read_pos].type != (riscv_op_type)CS_OP_INVALID) {
325
199k
      if (write_pos != read_pos) {
326
16.6k
        ops[write_pos] = ops[read_pos];
327
16.6k
      }
328
199k
      write_pos++;
329
199k
    }
330
674k
  }
331
  // fill the rest, if any, with invalid
332
84.3k
  memset((void *)(&ops[write_pos]), CS_OP_INVALID,
333
84.3k
         (NUM_RISCV_OPS - write_pos) * sizeof(cs_riscv_op));
334
84.3k
}
335
336
// given internal insn id, return public instruction info
337
void RISCV_get_insn_id(cs_struct *h, cs_insn *insn, unsigned int id)
338
84.3k
{
339
84.3k
  insn_map const *insn_map = NULL;
340
341
84.3k
  if ((insn_map = lookup_insn_map(h, id))) {
342
84.3k
    insn->id = insn_map->mapid;
343
344
84.3k
    if (h->detail_opt) {
345
84.3k
#ifndef CAPSTONE_DIET
346
84.3k
      memcpy(insn->detail->regs_read, insn_map->regs_use,
347
84.3k
             sizeof(insn_map->regs_use));
348
84.3k
      insn->detail->regs_read_count =
349
84.3k
        (uint8_t)count_positive(insn_map->regs_use);
350
351
84.3k
      memcpy(insn->detail->regs_write, insn_map->regs_mod,
352
84.3k
             sizeof(insn_map->regs_mod));
353
84.3k
      insn->detail->regs_write_count =
354
84.3k
        (uint8_t)count_positive(insn_map->regs_mod);
355
356
84.3k
      memcpy(insn->detail->groups, insn_map->groups,
357
84.3k
             sizeof(insn_map->groups));
358
84.3k
      insn->detail->groups_count =
359
84.3k
        (uint8_t)count_positive8(insn_map->groups);
360
361
84.3k
      if (insn_map->branch || insn_map->indirect_branch) {
362
        // this insn also belongs to JUMP group. add JUMP group
363
3.43k
        insn->detail
364
3.43k
          ->groups[insn->detail->groups_count] =
365
3.43k
          RISCV_GRP_JUMP;
366
3.43k
        insn->detail->groups_count++;
367
3.43k
      }
368
84.3k
#endif
369
84.3k
    }
370
84.3k
  }
371
84.3k
}
372
373
static const char *const insn_name_maps[] = {
374
#include "RISCVGenCSMappingInsnName.inc"
375
};
376
377
// called from RISCV_LLVM_printInstruction() to avoid exporting
378
// insn_alias_mnem_map and its size via extern declarations
379
void RISCV_set_alias_id(MCInst *MI, SStream *O)
380
84.3k
{
381
84.3k
#ifndef CAPSTONE_DIET
382
84.3k
  map_set_alias_id(MI, O, insn_alias_mnem_map,
383
84.3k
       ARR_SIZE(insn_alias_mnem_map));
384
84.3k
#endif
385
84.3k
}
386
387
const char *RISCV_insn_name(csh handle, unsigned int id)
388
84.3k
{
389
84.3k
#ifndef CAPSTONE_DIET
390
84.3k
  if (id < RISCV_INS_ENDING)
391
84.3k
    return insn_name_maps[id];
392
393
0
  if (id > RISCV_INS_ALIAS_BEGIN && id < RISCV_INS_ALIAS_END)
394
0
    return insn_alias_mnem_map[id - RISCV_INS_ALIAS_BEGIN - 1].name;
395
0
#endif
396
0
  return NULL;
397
0
}
398
399
#ifndef CAPSTONE_DIET
400
static const name_map group_name_maps[] = {
401
  // generic groups
402
  { RISCV_GRP_INVALID, NULL },
403
  { RISCV_GRP_JUMP, "jump" },
404
  { RISCV_GRP_CALL, "call" },
405
  { RISCV_GRP_RET, "ret" },
406
  { RISCV_GRP_INT, "int" },
407
  { RISCV_GRP_IRET, "iret" },
408
  { RISCV_GRP_PRIVILEGE, "privileged" },
409
  { RISCV_GRP_BRANCH_RELATIVE, "branch_relative" },
410
411
// architecture specific
412
#include "RISCVGenCSFeatureName.inc"
413
414
  { RISCV_GRP_ENDING, NULL }
415
};
416
#endif
417
418
const char *RISCV_group_name(csh handle, unsigned int id)
419
159k
{
420
159k
#ifndef CAPSTONE_DIET
421
  // verify group id
422
  // if past the end
423
159k
  if (id >= RISCV_GRP_ENDING ||
424
      // or in the encoding gap between generic groups and arch-specific groups
425
159k
      (id > RISCV_GRP_BRANCH_RELATIVE && id < RISCV_FEATURE_HASSTDEXTI))
426
0
    return NULL;
427
159k
  return id2name(group_name_maps, ARR_SIZE(group_name_maps), id);
428
#else
429
  return NULL;
430
#endif
431
159k
}
432
433
// map instruction name to public instruction ID
434
riscv_insn RISCV_map_insn(const char *name)
435
0
{
436
0
  unsigned int i;
437
0
  for (i = 1; i < ARR_SIZE(insn_name_maps); i++) {
438
0
    if (!strcmp(name, insn_name_maps[i]))
439
0
      return i;
440
0
  }
441
0
#ifndef CAPSTONE_DIET
442
0
  for (i = 0; i < ARR_SIZE(insn_alias_mnem_map); i++) {
443
0
    if (!strcmp(name, insn_alias_mnem_map[i].name))
444
0
      return insn_alias_mnem_map[i].id;
445
0
  }
446
0
#endif
447
0
  return RISCV_INS_INVALID;
448
0
}
449
450
void RISCV_reg_access(const cs_insn *insn, cs_regs regs_read,
451
          uint8_t *regs_read_count, cs_regs regs_write,
452
          uint8_t *regs_write_count)
453
0
{
454
0
  const cs_riscv *riscv = &(insn->detail->riscv);
455
0
  uint8_t read_count = 0;
456
0
  uint8_t write_count = 0;
457
458
0
  for (int j = 0; j < riscv->op_count; j++) {
459
0
    const cs_riscv_op *op = &riscv->operands[j];
460
461
0
    if (op->type == RISCV_OP_REG) {
462
0
      if ((op->access & CS_AC_WRITE) &&
463
0
          !arr_exist(regs_write, write_count, op->reg)) {
464
0
        regs_write[write_count++] = (uint16_t)op->reg;
465
0
      }
466
0
      if ((op->access & CS_AC_READ) &&
467
0
          !arr_exist(regs_read, read_count, op->reg)) {
468
0
        regs_read[read_count++] = (uint16_t)op->reg;
469
0
      }
470
0
    } else if (op->type == RISCV_OP_MEM) {
471
0
      if (op->mem.base != RISCV_REG_INVALID &&
472
0
          !arr_exist(regs_read, read_count, op->mem.base)) {
473
0
        regs_read[read_count++] =
474
0
          (uint16_t)op->mem.base;
475
0
      }
476
0
    }
477
0
  }
478
479
0
  *regs_read_count = read_count;
480
0
  *regs_write_count = write_count;
481
0
}
482
483
void RISCV_init(MCRegisterInfo *MRI)
484
5.27k
{
485
5.27k
  MCRegisterInfo_InitMCRegisterInfo(MRI, RISCVRegDesc, RISCV_REG_ENDING,
486
5.27k
            0, 0, RISCVMCRegisterClasses,
487
5.27k
            ARR_SIZE(RISCVMCRegisterClasses), 0,
488
5.27k
            0, RISCVRegDiffLists, 0,
489
5.27k
            RISCVSubRegIdxLists,
490
5.27k
            ARR_SIZE(RISCVSubRegIdxLists), 0);
491
5.27k
}
492
493
#endif