Coverage Report

Created: 2025-07-01 07:03

/src/capstonenext/Mapping.c
Line
Count
Source (jump to first uncovered line)
1
/* Capstone Disassembly Engine */
2
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2013-2019 */
3
/*    Rot127 <unisono@quyllur.org>, 2022-2023 */
4
5
#include "Mapping.h"
6
#include "capstone/capstone.h"
7
#include "cs_priv.h"
8
#include "utils.h"
9
10
// create a cache for fast id lookup
11
static unsigned short *make_id2insn(const insn_map *insns, unsigned int size)
12
3.56k
{
13
  // NOTE: assume that the max id is always put at the end of insns array
14
3.56k
  unsigned short max_id = insns[size - 1].id;
15
3.56k
  unsigned int i;
16
17
3.56k
  unsigned short *cache =
18
3.56k
    (unsigned short *)cs_mem_calloc(max_id + 1, sizeof(*cache));
19
20
923k
  for (i = 1; i < size; i++)
21
920k
    cache[insns[i].id] = i;
22
23
3.56k
  return cache;
24
3.56k
}
25
26
// look for @id in @insns, given its size in @max. first time call will update
27
// @cache. return 0 if not found
28
unsigned short insn_find(const insn_map *insns, unsigned int max,
29
       unsigned int id, unsigned short **cache)
30
95.2k
{
31
95.2k
  if (id > insns[max - 1].id)
32
0
    return 0;
33
34
95.2k
  if (*cache == NULL)
35
3.56k
    *cache = make_id2insn(insns, max);
36
37
95.2k
  return (*cache)[id];
38
95.2k
}
39
40
// Gives the id for the given @name if it is saved in @map.
41
// Returns the id or -1 if not found.
42
int name2id(const name_map *map, int max, const char *name)
43
55.4k
{
44
55.4k
  CS_ASSERT_RET_VAL(map && name, -1);
45
55.4k
  int i;
46
47
8.03M
  for (i = 0; i < max; i++) {
48
8.03M
    if (!map[i].name) {
49
2.11k
      return -1;
50
2.11k
    }
51
8.03M
    if (!strcmp(map[i].name, name)) {
52
51.4k
      return map[i].id;
53
51.4k
    }
54
8.03M
  }
55
56
  // nothing match
57
1.85k
  return -1;
58
55.4k
}
59
60
// Gives the name for the given @id if it is saved in @map.
61
// Returns the name or NULL if not found.
62
const char *id2name(const name_map *map, int max, const unsigned int id)
63
2.03M
{
64
2.03M
  int i;
65
66
78.4M
  for (i = 0; i < max; i++) {
67
78.4M
    if (map[i].id == id) {
68
2.02M
      return map[i].name;
69
2.02M
    }
70
78.4M
  }
71
72
  // nothing match
73
6.98k
  return NULL;
74
2.03M
}
75
76
/// Adds a register to the implicit write register list.
77
/// It will not add the same register twice.
78
void map_add_implicit_write(MCInst *MI, uint32_t Reg)
79
266k
{
80
266k
  if (!MI->flat_insn->detail)
81
0
    return;
82
83
266k
  uint16_t *regs_write = MI->flat_insn->detail->regs_write;
84
269k
  for (int i = 0; i < MAX_IMPL_W_REGS; ++i) {
85
269k
    if (i == MI->flat_insn->detail->regs_write_count) {
86
255k
      regs_write[i] = Reg;
87
255k
      MI->flat_insn->detail->regs_write_count++;
88
255k
      return;
89
255k
    }
90
13.5k
    if (regs_write[i] == Reg)
91
11.1k
      return;
92
13.5k
  }
93
266k
}
94
95
/// Adds a register to the implicit read register list.
96
/// It will not add the same register twice.
97
void map_add_implicit_read(MCInst *MI, uint32_t Reg)
98
135k
{
99
135k
  if (!MI->flat_insn->detail)
100
0
    return;
101
102
135k
  uint16_t *regs_read = MI->flat_insn->detail->regs_read;
103
141k
  for (int i = 0; i < MAX_IMPL_R_REGS; ++i) {
104
141k
    if (i == MI->flat_insn->detail->regs_read_count) {
105
124k
      regs_read[i] = Reg;
106
124k
      MI->flat_insn->detail->regs_read_count++;
107
124k
      return;
108
124k
    }
109
16.9k
    if (regs_read[i] == Reg)
110
10.4k
      return;
111
16.9k
  }
112
135k
}
113
114
/// Removes a register from the implicit write register list.
115
void map_remove_implicit_write(MCInst *MI, uint32_t Reg)
116
26.4k
{
117
26.4k
  if (!MI->flat_insn->detail)
118
0
    return;
119
120
26.4k
  uint16_t *regs_write = MI->flat_insn->detail->regs_write;
121
26.4k
  bool shorten_list = false;
122
29.6k
  for (int i = 0; i < MAX_IMPL_W_REGS; ++i) {
123
29.6k
    if (shorten_list) {
124
3.17k
      regs_write[i - 1] = regs_write[i];
125
3.17k
    }
126
29.6k
    if (i >= MI->flat_insn->detail->regs_write_count)
127
26.4k
      return;
128
129
3.17k
    if (regs_write[i] == Reg) {
130
3.17k
      MI->flat_insn->detail->regs_write_count--;
131
      // The register should exist only once in the list.
132
3.17k
      CS_ASSERT_RET(!shorten_list);
133
3.17k
      shorten_list = true;
134
3.17k
    }
135
3.17k
  }
136
26.4k
}
137
138
/// Copies the implicit read registers of @imap to @MI->flat_insn.
139
/// Already present registers will be preserved.
140
void map_implicit_reads(MCInst *MI, const insn_map *imap)
141
1.26M
{
142
1.26M
#ifndef CAPSTONE_DIET
143
1.26M
  if (!MI->flat_insn->detail)
144
0
    return;
145
146
1.26M
  cs_detail *detail = MI->flat_insn->detail;
147
1.26M
  unsigned Opcode = MCInst_getOpcode(MI);
148
1.26M
  unsigned i = 0;
149
1.26M
  uint16_t reg = imap[Opcode].regs_use[i];
150
1.35M
  while (reg != 0) {
151
85.3k
    if (i >= MAX_IMPL_R_REGS ||
152
85.3k
        detail->regs_read_count >= MAX_IMPL_R_REGS) {
153
0
      printf("ERROR: Too many implicit read register defined in "
154
0
             "instruction mapping.\n");
155
0
      return;
156
0
    }
157
85.3k
    detail->regs_read[detail->regs_read_count++] = reg;
158
85.3k
    if (i + 1 < MAX_IMPL_R_REGS) {
159
      // Select next one
160
85.3k
      reg = imap[Opcode].regs_use[++i];
161
85.3k
    }
162
85.3k
  }
163
1.26M
#endif // CAPSTONE_DIET
164
1.26M
}
165
166
/// Copies the implicit write registers of @imap to @MI->flat_insn.
167
/// Already present registers will be preserved.
168
void map_implicit_writes(MCInst *MI, const insn_map *imap)
169
1.26M
{
170
1.26M
#ifndef CAPSTONE_DIET
171
1.26M
  if (!MI->flat_insn->detail)
172
0
    return;
173
174
1.26M
  cs_detail *detail = MI->flat_insn->detail;
175
1.26M
  unsigned Opcode = MCInst_getOpcode(MI);
176
1.26M
  unsigned i = 0;
177
1.26M
  uint16_t reg = imap[Opcode].regs_mod[i];
178
1.50M
  while (reg != 0) {
179
232k
    if (i >= MAX_IMPL_W_REGS ||
180
232k
        detail->regs_write_count >= MAX_IMPL_W_REGS) {
181
0
      printf("ERROR: Too many implicit write register defined in "
182
0
             "instruction mapping.\n");
183
0
      return;
184
0
    }
185
232k
    detail->regs_write[detail->regs_write_count++] = reg;
186
232k
    if (i + 1 < MAX_IMPL_W_REGS) {
187
      // Select next one
188
232k
      reg = imap[Opcode].regs_mod[++i];
189
232k
    }
190
232k
  }
191
1.26M
#endif // CAPSTONE_DIET
192
1.26M
}
193
194
/// Adds a given group to @MI->flat_insn.
195
/// A group is never added twice.
196
void add_group(MCInst *MI, unsigned /* arch_group */ group)
197
66.3k
{
198
66.3k
#ifndef CAPSTONE_DIET
199
66.3k
  if (!MI->flat_insn->detail)
200
0
    return;
201
202
66.3k
  cs_detail *detail = MI->flat_insn->detail;
203
66.3k
  if (detail->groups_count >= MAX_NUM_GROUPS) {
204
0
    printf("ERROR: Too many groups defined.\n");
205
0
    return;
206
0
  }
207
147k
  for (int i = 0; i < detail->groups_count; ++i) {
208
81.4k
    if (detail->groups[i] == group) {
209
212
      return;
210
212
    }
211
81.4k
  }
212
66.1k
  detail->groups[detail->groups_count++] = group;
213
66.1k
#endif // CAPSTONE_DIET
214
66.1k
}
215
216
/// Copies the groups from @imap to @MI->flat_insn.
217
/// Already present groups will be preserved.
218
void map_groups(MCInst *MI, const insn_map *imap)
219
1.26M
{
220
1.26M
#ifndef CAPSTONE_DIET
221
1.26M
  if (!MI->flat_insn->detail)
222
0
    return;
223
224
1.26M
  cs_detail *detail = MI->flat_insn->detail;
225
1.26M
  unsigned Opcode = MCInst_getOpcode(MI);
226
1.26M
  unsigned i = 0;
227
1.26M
  uint16_t group = imap[Opcode].groups[i];
228
2.72M
  while (group != 0) {
229
1.45M
    if (detail->groups_count >= MAX_NUM_GROUPS) {
230
0
      printf("ERROR: Too many groups defined in instruction mapping.\n");
231
0
      return;
232
0
    }
233
1.45M
    detail->groups[detail->groups_count++] = group;
234
1.45M
    group = imap[Opcode].groups[++i];
235
1.45M
  }
236
1.26M
#endif // CAPSTONE_DIET
237
1.26M
}
238
239
/// Returns the pointer to the supllementary information in
240
/// the instruction mapping table @imap or NULL in case of failure.
241
const void *map_get_suppl_info(MCInst *MI, const insn_map *imap)
242
999k
{
243
999k
#ifndef CAPSTONE_DIET
244
999k
  if (!MI->flat_insn->detail)
245
0
    return NULL;
246
247
999k
  unsigned Opcode = MCInst_getOpcode(MI);
248
999k
  return &imap[Opcode].suppl_info;
249
#else
250
  return NULL;
251
#endif // CAPSTONE_DIET
252
999k
}
253
254
// Search for the CS instruction id for the given @MC_Opcode in @imap.
255
// return -1 if none is found.
256
unsigned int find_cs_id(unsigned MC_Opcode, const insn_map *imap,
257
      unsigned imap_size)
258
1.26M
{
259
  // binary searching since the IDs are sorted in order
260
1.26M
  unsigned int left, right, m;
261
1.26M
  unsigned int max = imap_size;
262
263
1.26M
  right = max - 1;
264
265
1.26M
  if (MC_Opcode < imap[0].id || MC_Opcode > imap[right].id)
266
    // not found
267
0
    return -1;
268
269
1.26M
  left = 0;
270
271
14.2M
  while (left <= right) {
272
14.2M
    m = (left + right) / 2;
273
14.2M
    if (MC_Opcode == imap[m].id) {
274
1.26M
      return m;
275
1.26M
    }
276
277
13.0M
    if (MC_Opcode < imap[m].id)
278
4.46M
      right = m - 1;
279
8.55M
    else
280
8.55M
      left = m + 1;
281
13.0M
  }
282
283
0
  return -1;
284
1.26M
}
285
286
/// Sets the Capstone instruction id which maps to the @MI opcode.
287
/// If no mapping is found the function returns and prints an error.
288
void map_cs_id(MCInst *MI, const insn_map *imap, unsigned int imap_size)
289
1.26M
{
290
1.26M
  unsigned int i = find_cs_id(MCInst_getOpcode(MI), imap, imap_size);
291
1.26M
  if (i != -1) {
292
1.26M
    MI->flat_insn->id = imap[i].mapid;
293
1.26M
    return;
294
1.26M
  }
295
0
  printf("ERROR: Could not find CS id for MCInst opcode: %d\n",
296
0
         MCInst_getOpcode(MI));
297
0
  return;
298
1.26M
}
299
300
/// Returns the operand type information from the
301
/// mapping table for instruction operands.
302
/// Only usable by `auto-sync` archs!
303
const cs_op_type mapping_get_op_type(MCInst *MI, unsigned OpNum,
304
             const map_insn_ops *insn_ops_map,
305
             size_t map_size)
306
4.71M
{
307
4.71M
  assert(MI);
308
4.71M
  assert(MI->Opcode < map_size);
309
4.71M
  assert(OpNum < sizeof(insn_ops_map[MI->Opcode].ops) /
310
4.71M
             sizeof(insn_ops_map[MI->Opcode].ops[0]));
311
312
4.71M
  return insn_ops_map[MI->Opcode].ops[OpNum].type;
313
4.71M
}
314
315
/// Returns the operand access flags from the
316
/// mapping table for instruction operands.
317
/// Only usable by `auto-sync` archs!
318
const cs_ac_type mapping_get_op_access(MCInst *MI, unsigned OpNum,
319
               const map_insn_ops *insn_ops_map,
320
               size_t map_size)
321
3.75M
{
322
3.75M
  assert(MI);
323
3.75M
  assert(MI->Opcode < map_size);
324
3.75M
  assert(OpNum < sizeof(insn_ops_map[MI->Opcode].ops) /
325
3.75M
             sizeof(insn_ops_map[MI->Opcode].ops[0]));
326
327
3.75M
  cs_ac_type access = insn_ops_map[MI->Opcode].ops[OpNum].access;
328
3.75M
  if (MCInst_opIsTied(MI, OpNum) || MCInst_opIsTying(MI, OpNum))
329
253k
    access |= (access == CS_AC_READ) ? CS_AC_WRITE : CS_AC_READ;
330
3.75M
  return access;
331
3.75M
}
332
333
/// Returns the operand at detail->arch.operands[op_count + offset]
334
/// Or NULL if detail is not set.
335
#define DEFINE_get_detail_op(arch, ARCH) \
336
  cs_##arch##_op *ARCH##_get_detail_op(MCInst *MI, int offset) \
337
14.3M
  { \
338
14.3M
    if (!MI->flat_insn->detail) \
339
14.3M
      return NULL; \
340
14.3M
    int OpIdx = MI->flat_insn->detail->arch.op_count + offset; \
341
14.3M
    assert(OpIdx >= 0 && OpIdx < MAX_MC_OPS); \
342
14.3M
    return &MI->flat_insn->detail->arch.operands[OpIdx]; \
343
14.3M
  }
ARM_get_detail_op
Line
Count
Source
337
8.51M
  { \
338
8.51M
    if (!MI->flat_insn->detail) \
339
8.51M
      return NULL; \
340
8.51M
    int OpIdx = MI->flat_insn->detail->arch.op_count + offset; \
341
8.51M
    assert(OpIdx >= 0 && OpIdx < MAX_MC_OPS); \
342
8.51M
    return &MI->flat_insn->detail->arch.operands[OpIdx]; \
343
8.51M
  }
PPC_get_detail_op
Line
Count
Source
337
416k
  { \
338
416k
    if (!MI->flat_insn->detail) \
339
416k
      return NULL; \
340
416k
    int OpIdx = MI->flat_insn->detail->arch.op_count + offset; \
341
416k
    assert(OpIdx >= 0 && OpIdx < MAX_MC_OPS); \
342
416k
    return &MI->flat_insn->detail->arch.operands[OpIdx]; \
343
416k
  }
Unexecuted instantiation: TriCore_get_detail_op
AArch64_get_detail_op
Line
Count
Source
337
3.24M
  { \
338
3.24M
    if (!MI->flat_insn->detail) \
339
3.24M
      return NULL; \
340
3.24M
    int OpIdx = MI->flat_insn->detail->arch.op_count + offset; \
341
3.24M
    assert(OpIdx >= 0 && OpIdx < MAX_MC_OPS); \
342
3.24M
    return &MI->flat_insn->detail->arch.operands[OpIdx]; \
343
3.24M
  }
Unexecuted instantiation: Alpha_get_detail_op
Unexecuted instantiation: HPPA_get_detail_op
Unexecuted instantiation: LoongArch_get_detail_op
Mips_get_detail_op
Line
Count
Source
337
891k
  { \
338
891k
    if (!MI->flat_insn->detail) \
339
891k
      return NULL; \
340
891k
    int OpIdx = MI->flat_insn->detail->arch.op_count + offset; \
341
891k
    assert(OpIdx >= 0 && OpIdx < MAX_MC_OPS); \
342
891k
    return &MI->flat_insn->detail->arch.operands[OpIdx]; \
343
891k
  }
RISCV_get_detail_op
Line
Count
Source
337
255k
  { \
338
255k
    if (!MI->flat_insn->detail) \
339
255k
      return NULL; \
340
255k
    int OpIdx = MI->flat_insn->detail->arch.op_count + offset; \
341
255k
    assert(OpIdx >= 0 && OpIdx < MAX_MC_OPS); \
342
255k
    return &MI->flat_insn->detail->arch.operands[OpIdx]; \
343
255k
  }
SystemZ_get_detail_op
Line
Count
Source
337
698k
  { \
338
698k
    if (!MI->flat_insn->detail) \
339
698k
      return NULL; \
340
698k
    int OpIdx = MI->flat_insn->detail->arch.op_count + offset; \
341
698k
    assert(OpIdx >= 0 && OpIdx < MAX_MC_OPS); \
342
698k
    return &MI->flat_insn->detail->arch.operands[OpIdx]; \
343
698k
  }
Xtensa_get_detail_op
Line
Count
Source
337
154k
  { \
338
154k
    if (!MI->flat_insn->detail) \
339
154k
      return NULL; \
340
154k
    int OpIdx = MI->flat_insn->detail->arch.op_count + offset; \
341
154k
    assert(OpIdx >= 0 && OpIdx < MAX_MC_OPS); \
342
154k
    return &MI->flat_insn->detail->arch.operands[OpIdx]; \
343
154k
  }
Unexecuted instantiation: BPF_get_detail_op
Unexecuted instantiation: ARC_get_detail_op
Sparc_get_detail_op
Line
Count
Source
337
171k
  { \
338
171k
    if (!MI->flat_insn->detail) \
339
171k
      return NULL; \
340
171k
    int OpIdx = MI->flat_insn->detail->arch.op_count + offset; \
341
171k
    assert(OpIdx >= 0 && OpIdx < MAX_MC_OPS); \
342
171k
    return &MI->flat_insn->detail->arch.operands[OpIdx]; \
343
171k
  }
344
345
DEFINE_get_detail_op(arm, ARM);
346
DEFINE_get_detail_op(ppc, PPC);
347
DEFINE_get_detail_op(tricore, TriCore);
348
DEFINE_get_detail_op(aarch64, AArch64);
349
DEFINE_get_detail_op(alpha, Alpha);
350
DEFINE_get_detail_op(hppa, HPPA);
351
DEFINE_get_detail_op(loongarch, LoongArch);
352
DEFINE_get_detail_op(mips, Mips);
353
DEFINE_get_detail_op(riscv, RISCV);
354
DEFINE_get_detail_op(systemz, SystemZ);
355
DEFINE_get_detail_op(xtensa, Xtensa);
356
DEFINE_get_detail_op(bpf, BPF);
357
DEFINE_get_detail_op(arc, ARC);
358
DEFINE_get_detail_op(sparc, Sparc);
359
360
/// Returns true if for this architecture the
361
/// alias operands should be filled.
362
/// TODO: Replace this with a proper option.
363
///       So it can be toggled between disas() calls.
364
2.16M
bool map_use_alias_details(const MCInst *MI) {
365
2.16M
  assert(MI);
366
2.16M
  return (MI->csh->detail_opt & CS_OPT_ON) && !(MI->csh->detail_opt & CS_OPT_DETAIL_REAL);
367
2.16M
}
368
369
/// Sets the setDetailOps flag to @p Val.
370
/// If detail == NULLit refuses to set the flag to true.
371
2.11M
void map_set_fill_detail_ops(MCInst *MI, bool Val) {
372
2.11M
  CS_ASSERT_RET(MI);
373
2.11M
  if (!detail_is_set(MI)) {
374
0
    MI->fillDetailOps = false;
375
0
    return;
376
0
  }
377
378
2.11M
  MI->fillDetailOps = Val;
379
2.11M
}
380
381
/// Sets the instruction alias flags and the given alias id.
382
0
void map_set_is_alias_insn(MCInst *MI, bool Val, uint64_t Alias) {
383
0
  CS_ASSERT_RET(MI);
384
0
  MI->isAliasInstr = Val;
385
0
  MI->flat_insn->is_alias = Val;
386
0
  MI->flat_insn->alias_id = Alias;
387
0
}
388
389
262k
static inline bool char_ends_mnem(const char c, cs_arch arch) {
390
262k
  switch (arch) {
391
192k
  default:
392
192k
    return (!c || c == ' ' || c == '\t' || c == '.');
393
45.8k
  case CS_ARCH_PPC:
394
45.8k
    return (!c || c == ' ' || c == '\t');
395
24.2k
  case CS_ARCH_SPARC:
396
24.2k
    return (!c || c == ' ' || c == '\t' || c == ',');
397
262k
  }
398
262k
}
399
400
/// Sets an alternative id for some instruction.
401
/// Or -1 if it fails.
402
/// You must add (<ARCH>_INS_ALIAS_BEGIN + 1) to the id to get the real id.
403
1.20M
void map_set_alias_id(MCInst *MI, const SStream *O, const name_map *alias_mnem_id_map, int map_size) {
404
1.20M
  if (!MCInst_isAlias(MI))
405
1.14M
    return;
406
407
55.4k
  char alias_mnem[16] = { 0 };
408
55.4k
  int i = 0, j = 0;
409
55.4k
  const char *asm_str_buf = O->buffer;
410
  // Skip spaces and tabs
411
91.0k
  while (is_blank_char(asm_str_buf[i])) {
412
35.5k
    if (!asm_str_buf[i]) {
413
0
      MI->flat_insn->alias_id = -1;
414
0
      return;
415
0
    }
416
35.5k
    ++i;
417
35.5k
  }
418
262k
  for (; j < sizeof(alias_mnem) - 1; ++j, ++i) {
419
262k
    if (char_ends_mnem(asm_str_buf[i], MI->csh->arch))
420
55.4k
      break;
421
206k
    alias_mnem[j] = asm_str_buf[i];
422
206k
  }
423
424
55.4k
  MI->flat_insn->alias_id = name2id(alias_mnem_id_map, map_size, alias_mnem);
425
55.4k
}
426
427
/// Does a binary search over the given map and searches for @id.
428
/// If @id exists in @map, it sets @found to true and returns
429
/// the value for the @id.
430
/// Otherwise, @found is set to false and it returns UINT64_MAX.
431
///
432
/// Of course it assumes the map is sorted.
433
uint64_t enum_map_bin_search(const cs_enum_id_map *map, size_t map_len,
434
           const char *id, bool *found)
435
0
{
436
0
  size_t l = 0;
437
0
  size_t r = map_len;
438
0
  size_t id_len = strlen(id);
439
440
0
  while (l <= r) {
441
0
    size_t m = (l + r) / 2;
442
0
    size_t j = 0;
443
0
    size_t i = 0;
444
0
    size_t entry_len = strlen(map[m].str);
445
446
0
    while (j < entry_len && i < id_len && id[i] == map[m].str[j]) {
447
0
      ++j, ++i;
448
0
    }
449
0
    if (i == id_len && j == entry_len) {
450
0
      *found = true;
451
0
      return map[m].val;
452
0
    }
453
454
0
    if (id[i] < map[m].str[j]) {
455
0
      r = m - 1;
456
0
    } else if (id[i] > map[m].str[j]) {
457
0
      l = m + 1;
458
0
    }
459
0
    if ((m == 0 && id[i] < map[m].str[j]) || (l + r) / 2 >= map_len) {
460
      // Break before we go out of bounds.
461
0
      break;
462
0
    }
463
0
  }
464
0
  *found = false;
465
0
  return UINT64_MAX;
466
0
}
467