Coverage Report

Created: 2024-09-08 06:22

/src/capstonenext/arch/LoongArch/LoongArchMapping.c
Line
Count
Source (jump to first uncovered line)
1
/* Capstone Disassembly Engine */
2
/* By Jiajie Chen <c@jia.je>, 2024 */
3
/*    Yanglin Xun <1109673069@qq.com>, 2024 */
4
5
#ifdef CAPSTONE_HAS_LOONGARCH
6
7
#include <stdio.h>
8
#include <string.h>
9
10
#include <capstone/capstone.h>
11
#include <capstone/loongarch.h>
12
13
#include "../../Mapping.h"
14
#include "../../MCDisassembler.h"
15
#include "../../cs_priv.h"
16
#include "../../cs_simple_types.h"
17
18
#include "LoongArchMapping.h"
19
#include "LoongArchLinkage.h"
20
21
#define GET_REGINFO_ENUM
22
#define GET_REGINFO_MC_DESC
23
#include "LoongArchGenRegisterInfo.inc"
24
25
#define GET_INSTRINFO_ENUM
26
#include "LoongArchGenInstrInfo.inc"
27
28
void LoongArch_init_mri(MCRegisterInfo *MRI)
29
0
{
30
0
  MCRegisterInfo_InitMCRegisterInfo(MRI, LoongArchRegDesc,
31
0
            sizeof(LoongArchRegDesc), 0, 0,
32
0
            LoongArchMCRegisterClasses,
33
0
            ARR_SIZE(LoongArchMCRegisterClasses),
34
0
            0, 0, LoongArchRegDiffLists, 0,
35
0
            LoongArchSubRegIdxLists,
36
0
            ARR_SIZE(LoongArchSubRegIdxLists), 0);
37
0
}
38
39
const char *LoongArch_reg_name(csh handle, unsigned int reg)
40
0
{
41
0
  int syntax_opt = ((cs_struct *)(uintptr_t)handle)->syntax;
42
43
0
  if (syntax_opt & CS_OPT_SYNTAX_NOREGNAME) {
44
0
    return LoongArch_LLVM_getRegisterName(reg,
45
0
                  LoongArch_NoRegAltName);
46
0
  }
47
0
  return LoongArch_LLVM_getRegisterName(reg, LoongArch_RegAliasName);
48
0
}
49
50
void LoongArch_get_insn_id(cs_struct *h, cs_insn *insn, unsigned int id)
51
0
{
52
  // Not used by LoongArch. Information is set after disassembly.
53
0
}
54
55
static const char *const insn_name_maps[] = {
56
#include "LoongArchGenCSMappingInsnName.inc"
57
};
58
59
const char *LoongArch_insn_name(csh handle, unsigned int id)
60
0
{
61
0
#ifndef CAPSTONE_DIET
62
0
  if (id < ARR_SIZE(insn_name_maps))
63
0
    return insn_name_maps[id];
64
  // not found
65
0
  return NULL;
66
#else
67
  return NULL;
68
#endif
69
0
}
70
71
#ifndef CAPSTONE_DIET
72
static const name_map group_name_maps[] = {
73
  { LOONGARCH_GRP_INVALID, NULL },
74
75
  { LOONGARCH_GRP_JUMP, "jump" },
76
  { LOONGARCH_GRP_CALL, "call" },
77
  { LOONGARCH_GRP_RET, "return" },
78
  { LOONGARCH_GRP_INT, "int" },
79
  { LOONGARCH_GRP_IRET, "iret" },
80
  { LOONGARCH_GRP_PRIVILEGE, "privilege" },
81
  { LOONGARCH_GRP_BRANCH_RELATIVE, "branch_relative" },
82
83
// architecture-specific groups
84
#include "LoongArchGenCSFeatureName.inc"
85
};
86
#endif
87
88
const char *LoongArch_group_name(csh handle, unsigned int id)
89
0
{
90
0
#ifndef CAPSTONE_DIET
91
0
  return id2name(group_name_maps, ARR_SIZE(group_name_maps), id);
92
#else
93
  return NULL;
94
#endif
95
0
}
96
97
void LoongArch_reg_access(const cs_insn *insn, cs_regs regs_read,
98
        uint8_t *regs_read_count, cs_regs regs_write,
99
        uint8_t *regs_write_count)
100
0
{
101
0
  uint8_t i;
102
0
  uint8_t read_count, write_count;
103
0
  cs_loongarch *loongarch = &(insn->detail->loongarch);
104
105
0
  read_count = insn->detail->regs_read_count;
106
0
  write_count = insn->detail->regs_write_count;
107
108
  // implicit registers
109
0
  memcpy(regs_read, insn->detail->regs_read,
110
0
         read_count * sizeof(insn->detail->regs_read[0]));
111
0
  memcpy(regs_write, insn->detail->regs_write,
112
0
         write_count * sizeof(insn->detail->regs_write[0]));
113
114
  // explicit registers
115
0
  for (i = 0; i < loongarch->op_count; i++) {
116
0
    cs_loongarch_op *op = &(loongarch->operands[i]);
117
0
    switch ((int)op->type) {
118
0
    case LOONGARCH_OP_REG:
119
0
      if ((op->access & CS_AC_READ) &&
120
0
          !arr_exist(regs_read, read_count, op->reg)) {
121
0
        regs_read[read_count] = (uint16_t)op->reg;
122
0
        read_count++;
123
0
      }
124
0
      if ((op->access & CS_AC_WRITE) &&
125
0
          !arr_exist(regs_write, write_count, op->reg)) {
126
0
        regs_write[write_count] = (uint16_t)op->reg;
127
0
        write_count++;
128
0
      }
129
0
      break;
130
0
    case LOONGARCH_OP_MEM:
131
      // registers appeared in memory references always being read
132
0
      if ((op->mem.base != LOONGARCH_REG_INVALID) &&
133
0
          !arr_exist(regs_read, read_count, op->mem.base)) {
134
0
        regs_read[read_count] = (uint16_t)op->mem.base;
135
0
        read_count++;
136
0
      }
137
0
      if ((insn->detail->writeback) &&
138
0
          (op->mem.base != LOONGARCH_REG_INVALID) &&
139
0
          !arr_exist(regs_write, write_count, op->mem.base)) {
140
0
        regs_write[write_count] =
141
0
          (uint16_t)op->mem.base;
142
0
        write_count++;
143
0
      }
144
0
    default:
145
0
      break;
146
0
    }
147
0
  }
148
149
0
  *regs_read_count = read_count;
150
0
  *regs_write_count = write_count;
151
0
}
152
153
const insn_map loongarch_insns[] = {
154
#include "LoongArchGenCSMappingInsn.inc"
155
};
156
157
void LoongArch_rewrite_memory_operand(MCInst *MI)
158
0
{
159
  // rewrite base + disp operands to memory operands in memory instructions
160
  // convert e.g.
161
  // ld.d   $t3, $t2, 0x410
162
  // op_count: 3
163
  //         operands[0].type: REG = t3
164
  //         operands[0].access: WRITE
165
  //         operands[1].type: REG = t2
166
  //         operands[1].access: READ
167
  //         operands[2].type: IMM = 0x410
168
  //         operands[2].access: READ
169
  // to:
170
  // op_count: 3
171
  //         operands[0].type: REG = t3
172
  //         operands[0].access: WRITE
173
  //         operands[1].type: MEM
174
  //                 operands[1].mem.base: REG = t2
175
  //                 operands[1].mem.disp: 0x410
176
  //        operands[1].access: READ
177
178
0
  if (!detail_is_set(MI))
179
0
    return;
180
181
0
  const loongarch_suppl_info *suppl_info =
182
0
    map_get_suppl_info(MI, loongarch_insns);
183
0
  if (suppl_info->memory_access == CS_AC_INVALID) {
184
    // not memory instruction
185
0
    return;
186
0
  }
187
188
  // handle special cases
189
0
  unsigned int base;
190
0
  switch (MI->flat_insn->id) {
191
0
  case LOONGARCH_INS_SC_Q:
192
0
  case LOONGARCH_INS_LLACQ_W:
193
0
  case LOONGARCH_INS_LLACQ_D:
194
0
  case LOONGARCH_INS_SCREL_W:
195
0
  case LOONGARCH_INS_SCREL_D:
196
    // last register rj is memory operand
197
0
    LoongArch_get_detail_op(MI, -1)->type = LOONGARCH_OP_MEM;
198
0
    base = LoongArch_get_detail_op(MI, -1)->reg;
199
0
    LoongArch_get_detail_op(MI, -1)->mem.base = base;
200
0
    LoongArch_get_detail_op(MI, -1)->access =
201
0
      suppl_info->memory_access;
202
0
    return;
203
204
0
  case LOONGARCH_INS_LDGT_B:
205
0
  case LOONGARCH_INS_LDGT_H:
206
0
  case LOONGARCH_INS_LDGT_W:
207
0
  case LOONGARCH_INS_LDGT_D:
208
0
  case LOONGARCH_INS_LDLE_B:
209
0
  case LOONGARCH_INS_LDLE_H:
210
0
  case LOONGARCH_INS_LDLE_W:
211
0
  case LOONGARCH_INS_LDLE_D:
212
0
  case LOONGARCH_INS_STGT_B:
213
0
  case LOONGARCH_INS_STGT_H:
214
0
  case LOONGARCH_INS_STGT_W:
215
0
  case LOONGARCH_INS_STGT_D:
216
0
  case LOONGARCH_INS_STLE_B:
217
0
  case LOONGARCH_INS_STLE_H:
218
0
  case LOONGARCH_INS_STLE_W:
219
0
  case LOONGARCH_INS_STLE_D:
220
0
  case LOONGARCH_INS_FLDLE_S:
221
0
  case LOONGARCH_INS_FLDLE_D:
222
0
  case LOONGARCH_INS_FLDGT_S:
223
0
  case LOONGARCH_INS_FLDGT_D:
224
0
  case LOONGARCH_INS_FSTLE_S:
225
0
  case LOONGARCH_INS_FSTLE_D:
226
0
  case LOONGARCH_INS_FSTGT_S:
227
0
  case LOONGARCH_INS_FSTGT_D:
228
    // second register rj is memory operand
229
0
    LoongArch_get_detail_op(MI, -2)->type = LOONGARCH_OP_MEM;
230
0
    base = LoongArch_get_detail_op(MI, -2)->reg;
231
0
    LoongArch_get_detail_op(MI, -2)->mem.base = base;
232
0
    LoongArch_get_detail_op(MI, -2)->access =
233
0
      suppl_info->memory_access;
234
0
    return;
235
0
  default:
236
0
    break;
237
0
  }
238
239
0
  switch (suppl_info->form) {
240
0
  case LOONGARCH_INSN_FORM_FMT2RI12:   // ld, ldl, ldr, st, stl, str
241
0
  case LOONGARCH_INSN_FORM_FMT2RI14:   // ll, sc, ldptr, stptr
242
0
  case LOONGARCH_INSN_FORM_FMT2RI9_VRI:  // vldrepl.d
243
0
  case LOONGARCH_INSN_FORM_FMT2RI10_VRI:   // vldrepl.w
244
0
  case LOONGARCH_INSN_FORM_FMT2RI11_VRI:   // vldrepl.h
245
0
  case LOONGARCH_INSN_FORM_FMT2RI12_VRI:   // vld, vldrepl, vst
246
0
  case LOONGARCH_INSN_FORM_FMT2RI8I1_VRII: // vstelm.d
247
0
  case LOONGARCH_INSN_FORM_FMT2RI8I2_VRII: // vstelm.w
248
0
  case LOONGARCH_INSN_FORM_FMT2RI8I3_VRII: // vstelm.h
249
0
  case LOONGARCH_INSN_FORM_FMT2RI8I4_VRII: // vstelm.b
250
0
  case LOONGARCH_INSN_FORM_FMT2RI9_XRI:  // xvldrepl.d
251
0
  case LOONGARCH_INSN_FORM_FMT2RI10_XRI:   // xvldrepl.w
252
0
  case LOONGARCH_INSN_FORM_FMT2RI11_XRI:   // xvldrepl.h
253
0
  case LOONGARCH_INSN_FORM_FMT2RI12_XRI:   // xvld, xvldrepl, xvst
254
0
  case LOONGARCH_INSN_FORM_FMT2RI8I2_XRII: // xvstelm.d
255
0
  case LOONGARCH_INSN_FORM_FMT2RI8I3_XRII: // xvstelm.w
256
0
  case LOONGARCH_INSN_FORM_FMT2RI8I4_XRII: // xvstelm.h
257
0
  case LOONGARCH_INSN_FORM_FMT2RI8I5_XRII: // xvstelm.b
258
0
  case LOONGARCH_INSN_FORM_FMTPRELD:   // preld
259
0
  case LOONGARCH_INSN_FORM_FPFMT2RI12:   // fld, fst
260
    // immediate offset
261
0
    LoongArch_get_detail_op(MI, -2)->type = LOONGARCH_OP_MEM;
262
0
    base = LoongArch_get_detail_op(MI, -2)->reg;
263
0
    LoongArch_get_detail_op(MI, -2)->mem.base = base;
264
0
    LoongArch_get_detail_op(MI, -2)->mem.disp =
265
0
      LoongArch_get_detail_op(MI, -1)->imm;
266
0
    LoongArch_get_detail_op(MI, -2)->access =
267
0
      suppl_info->memory_access;
268
0
    LoongArch_dec_op_count(MI);
269
0
    break;
270
271
0
  case LOONGARCH_INSN_FORM_FMT3R: // ldx, stx, amo
272
0
    if (suppl_info->memory_access == CS_AC_READ_WRITE) {
273
      // amo: read + write
274
      // last register rj is memory operand
275
0
      LoongArch_get_detail_op(MI, -1)->type =
276
0
        LOONGARCH_OP_MEM;
277
0
      base = LoongArch_get_detail_op(MI, -1)->reg;
278
0
      LoongArch_get_detail_op(MI, -1)->mem.base = base;
279
0
      LoongArch_get_detail_op(MI, -1)->access =
280
0
        suppl_info->memory_access;
281
0
      break;
282
0
    }
283
    // fallthrough
284
285
0
  case LOONGARCH_INSN_FORM_FPFMTMEM:  // fldx, fstx
286
0
  case LOONGARCH_INSN_FORM_FMT3R_VRR: // vldx, vstx
287
0
  case LOONGARCH_INSN_FORM_FMT3R_XRR: // xvldx, xvstx
288
0
  case LOONGARCH_INSN_FORM_FMTPRELDX: // preldx
289
    // register offset
290
0
    LoongArch_get_detail_op(MI, -2)->type = LOONGARCH_OP_MEM;
291
0
    base = LoongArch_get_detail_op(MI, -2)->reg;
292
0
    LoongArch_get_detail_op(MI, -2)->mem.base = base;
293
0
    LoongArch_get_detail_op(MI, -2)->mem.index =
294
0
      LoongArch_get_detail_op(MI, -1)->reg;
295
0
    LoongArch_get_detail_op(MI, -2)->access =
296
0
      suppl_info->memory_access;
297
0
    LoongArch_dec_op_count(MI);
298
0
    break;
299
300
0
  default:
301
0
    assert(0 && "Unknown LoongArch memory instruction");
302
0
    break;
303
0
  }
304
0
}
305
306
void LoongArch_set_instr_map_data(MCInst *MI)
307
0
{
308
0
  map_cs_id(MI, loongarch_insns, ARR_SIZE(loongarch_insns));
309
0
  map_implicit_reads(MI, loongarch_insns);
310
0
  map_implicit_writes(MI, loongarch_insns);
311
0
  map_groups(MI, loongarch_insns);
312
0
  const loongarch_suppl_info *suppl_info =
313
0
    map_get_suppl_info(MI, loongarch_insns);
314
0
  if (suppl_info) {
315
0
    LoongArch_get_detail(MI)->format = suppl_info->form;
316
0
  }
317
0
}
318
319
bool LoongArch_getInstruction(csh handle, const uint8_t *code, size_t code_len,
320
            MCInst *instr, uint16_t *size, uint64_t address,
321
            void *info)
322
0
{
323
0
  uint64_t temp_size;
324
0
  LoongArch_init_cs_detail(instr);
325
0
  bool Result = LoongArch_LLVM_getInstruction(instr, &temp_size, code,
326
0
                code_len, address, info) !=
327
0
          MCDisassembler_Fail;
328
0
  LoongArch_set_instr_map_data(instr);
329
0
  *size = temp_size;
330
0
  return Result;
331
0
}
332
333
/// Adds group to the instruction which are not defined in LLVM.
334
static void LoongArch_add_cs_groups(MCInst *MI)
335
0
{
336
0
  if (!MI->flat_insn->detail)
337
0
    return;
338
0
  unsigned Opcode = MI->flat_insn->id;
339
0
  cs_loongarch *loongarch = &(MI->flat_insn->detail->loongarch);
340
0
  switch (Opcode) {
341
0
  default:
342
0
    return;
343
0
  case LOONGARCH_INS_BL:
344
0
    add_group(MI, LOONGARCH_GRP_CALL);
345
0
    break;
346
0
  case LOONGARCH_INS_JIRL:
347
0
    if (loongarch->op_count == 3 &&
348
0
        loongarch->operands[0].reg == LOONGARCH_REG_RA) {
349
      // call: jirl ra, rj, offs16
350
0
      add_group(MI, LOONGARCH_GRP_CALL);
351
0
    } else if (loongarch->op_count == 0) {
352
      // ret
353
0
      add_group(MI, LOONGARCH_GRP_RET);
354
0
    } else if (loongarch->op_count == 1) {
355
      // jr rj
356
0
      add_group(MI, LOONGARCH_GRP_JUMP);
357
0
    }
358
0
    break;
359
0
  case LOONGARCH_INS_B:
360
0
  case LOONGARCH_INS_BCEQZ:
361
0
  case LOONGARCH_INS_BEQ:
362
0
  case LOONGARCH_INS_BEQZ:
363
0
  case LOONGARCH_INS_BGE:
364
0
  case LOONGARCH_INS_BGEU:
365
0
  case LOONGARCH_INS_BLT:
366
0
  case LOONGARCH_INS_BLTU:
367
0
  case LOONGARCH_INS_BNE:
368
0
  case LOONGARCH_INS_BNEZ:
369
0
    add_group(MI, LOONGARCH_GRP_JUMP);
370
0
    add_group(MI, LOONGARCH_GRP_BRANCH_RELATIVE);
371
0
    break;
372
0
  case LOONGARCH_INS_SYSCALL:
373
0
    add_group(MI, LOONGARCH_GRP_INT);
374
0
    break;
375
0
  case LOONGARCH_INS_ERTN:
376
0
    add_group(MI, LOONGARCH_GRP_IRET);
377
0
    add_group(MI, LOONGARCH_GRP_PRIVILEGE);
378
0
    break;
379
0
  case LOONGARCH_INS_CSRXCHG:
380
0
  case LOONGARCH_INS_CACOP:
381
0
  case LOONGARCH_INS_LDDIR:
382
0
  case LOONGARCH_INS_LDPTE:
383
0
  case LOONGARCH_INS_IOCSRRD_B:
384
0
  case LOONGARCH_INS_IOCSRRD_H:
385
0
  case LOONGARCH_INS_IOCSRRD_W:
386
0
  case LOONGARCH_INS_IOCSRRD_D:
387
0
  case LOONGARCH_INS_IOCSRWR_B:
388
0
  case LOONGARCH_INS_IOCSRWR_H:
389
0
  case LOONGARCH_INS_IOCSRWR_W:
390
0
  case LOONGARCH_INS_IOCSRWR_D:
391
0
  case LOONGARCH_INS_TLBCLR:
392
0
  case LOONGARCH_INS_TLBFLUSH:
393
0
  case LOONGARCH_INS_TLBSRCH:
394
0
  case LOONGARCH_INS_TLBRD:
395
0
  case LOONGARCH_INS_TLBWR:
396
0
  case LOONGARCH_INS_INVTLB:
397
0
    add_group(MI, LOONGARCH_GRP_PRIVILEGE);
398
0
    break;
399
0
  }
400
0
}
401
402
void LoongArch_printer(MCInst *MI, SStream *O,
403
           void * /* MCRegisterInfo* */ info)
404
0
{
405
0
  MCRegisterInfo *MRI = (MCRegisterInfo *)info;
406
0
  MI->MRI = MRI;
407
408
0
  LoongArch_LLVM_printInst(MI, MI->address, "", O);
409
410
0
  LoongArch_rewrite_memory_operand(MI);
411
0
  LoongArch_add_cs_groups(MI);
412
0
}
413
414
void LoongArch_setup_op(cs_loongarch_op *op)
415
0
{
416
0
  memset(op, 0, sizeof(cs_loongarch_op));
417
0
  op->type = LOONGARCH_OP_INVALID;
418
0
}
419
420
void LoongArch_init_cs_detail(MCInst *MI)
421
0
{
422
0
  if (detail_is_set(MI)) {
423
0
    unsigned int i;
424
425
0
    memset(get_detail(MI), 0,
426
0
           offsetof(cs_detail, loongarch) + sizeof(cs_loongarch));
427
428
0
    for (i = 0; i < ARR_SIZE(LoongArch_get_detail(MI)->operands);
429
0
         i++)
430
0
      LoongArch_setup_op(
431
0
        &LoongArch_get_detail(MI)->operands[i]);
432
0
  }
433
0
}
434
435
static const map_insn_ops insn_operands[] = {
436
#include "LoongArchGenCSMappingInsnOp.inc"
437
};
438
439
void LoongArch_set_detail_op_imm(MCInst *MI, unsigned OpNum,
440
         loongarch_op_type ImmType, int64_t Imm)
441
0
{
442
0
  if (!detail_is_set(MI))
443
0
    return;
444
0
  assert((map_get_op_type(MI, OpNum) & ~CS_OP_MEM) == CS_OP_IMM);
445
0
  assert(ImmType == LOONGARCH_OP_IMM);
446
447
0
  LoongArch_get_detail_op(MI, 0)->type = ImmType;
448
0
  LoongArch_get_detail_op(MI, 0)->imm = Imm;
449
0
  LoongArch_get_detail_op(MI, 0)->access = map_get_op_access(MI, OpNum);
450
0
  LoongArch_inc_op_count(MI);
451
0
}
452
453
void LoongArch_set_detail_op_reg(MCInst *MI, unsigned OpNum, loongarch_reg Reg)
454
0
{
455
0
  if (!detail_is_set(MI))
456
0
    return;
457
0
  assert((map_get_op_type(MI, OpNum) & ~CS_OP_MEM) == CS_OP_REG);
458
459
0
  LoongArch_get_detail_op(MI, 0)->type = LOONGARCH_OP_REG;
460
0
  LoongArch_get_detail_op(MI, 0)->reg = Reg;
461
0
  LoongArch_get_detail_op(MI, 0)->access = map_get_op_access(MI, OpNum);
462
0
  LoongArch_inc_op_count(MI);
463
0
}
464
465
void LoongArch_add_cs_detail(MCInst *MI, int /* loongarch_op_group */ op_group,
466
           va_list args)
467
0
{
468
0
  if (!detail_is_set(MI))
469
0
    return;
470
471
0
  unsigned OpNum = va_arg(args, unsigned);
472
  // Handle memory operands later
473
0
  cs_op_type op_type = map_get_op_type(MI, OpNum) & ~CS_OP_MEM;
474
475
  // Fill cs_detail
476
0
  switch (op_group) {
477
0
  default:
478
0
    printf("ERROR: Operand group %d not handled!\n", op_group);
479
0
    assert(0);
480
0
  case LOONGARCH_OP_GROUP_OPERAND:
481
0
    if (op_type == CS_OP_IMM) {
482
0
      LoongArch_set_detail_op_imm(MI, OpNum, LOONGARCH_OP_IMM,
483
0
                MCInst_getOpVal(MI, OpNum));
484
0
    } else if (op_type == CS_OP_REG) {
485
0
      LoongArch_set_detail_op_reg(MI, OpNum,
486
0
                MCInst_getOpVal(MI, OpNum));
487
0
    } else
488
0
      assert(0 && "Op type not handled.");
489
0
    break;
490
0
  case LOONGARCH_OP_GROUP_ATOMICMEMOP:
491
0
    assert(op_type == CS_OP_REG);
492
    // converted to MEM operand later in LoongArch_rewrite_memory_operand
493
0
    LoongArch_set_detail_op_reg(MI, OpNum,
494
0
              MCInst_getOpVal(MI, OpNum));
495
0
    break;
496
0
  }
497
0
}
498
499
#endif