Coverage Report

Created: 2026-06-06 06:15

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