Coverage Report

Created: 2026-02-26 07:11

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
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