/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 |  |  | 
| 8 |  | // create a cache for fast id lookup | 
| 9 |  | static unsigned short *make_id2insn(const insn_map *insns, unsigned int size) | 
| 10 | 23.3k | { | 
| 11 |  |   // NOTE: assume that the max id is always put at the end of insns array | 
| 12 | 23.3k |   unsigned short max_id = insns[size - 1].id; | 
| 13 | 23.3k |   unsigned short i; | 
| 14 |  |  | 
| 15 | 23.3k |   unsigned short *cache = | 
| 16 | 23.3k |     (unsigned short *)cs_mem_calloc(max_id + 1, sizeof(*cache)); | 
| 17 |  |  | 
| 18 | 70.4M |   for (i = 1; i < size; i++) | 
| 19 | 70.4M |     cache[insns[i].id] = i; | 
| 20 |  |  | 
| 21 | 23.3k |   return cache; | 
| 22 | 23.3k | } | 
| 23 |  |  | 
| 24 |  | // look for @id in @insns, given its size in @max. first time call will update | 
| 25 |  | // @cache. return 0 if not found | 
| 26 |  | unsigned short insn_find(const insn_map *insns, unsigned int max, | 
| 27 |  |        unsigned int id, unsigned short **cache) | 
| 28 | 1.79M | { | 
| 29 | 1.79M |   if (id > insns[max - 1].id) | 
| 30 | 0 |     return 0; | 
| 31 |  |  | 
| 32 | 1.79M |   if (*cache == NULL) | 
| 33 | 23.3k |     *cache = make_id2insn(insns, max); | 
| 34 |  |  | 
| 35 | 1.79M |   return (*cache)[id]; | 
| 36 | 1.79M | } | 
| 37 |  |  | 
| 38 |  | // Gives the id for the given @name if it is saved in @map. | 
| 39 |  | // Returns the id or -1 if not found. | 
| 40 |  | int name2id(const name_map *map, int max, const char *name) | 
| 41 | 80.6k | { | 
| 42 | 80.6k |   int i; | 
| 43 |  |  | 
| 44 | 15.5M |   for (i = 0; i < max; i++) { | 
| 45 | 15.5M |     if (!strcmp(map[i].name, name)) { | 
| 46 | 47.0k |       return map[i].id; | 
| 47 | 47.0k |     } | 
| 48 | 15.5M |   } | 
| 49 |  |  | 
| 50 |  |   // nothing match | 
| 51 | 33.5k |   return -1; | 
| 52 | 80.6k | } | 
| 53 |  |  | 
| 54 |  | // Gives the name for the given @id if it is saved in @map. | 
| 55 |  | // Returns the name or NULL if not found. | 
| 56 |  | const char *id2name(const name_map *map, int max, const unsigned int id) | 
| 57 | 2.54M | { | 
| 58 | 2.54M |   int i; | 
| 59 |  |  | 
| 60 | 41.9M |   for (i = 0; i < max; i++) { | 
| 61 | 41.9M |     if (map[i].id == id) { | 
| 62 | 2.54M |       return map[i].name; | 
| 63 | 2.54M |     } | 
| 64 | 41.9M |   } | 
| 65 |  |  | 
| 66 |  |   // nothing match | 
| 67 | 0 |   return NULL; | 
| 68 | 2.54M | } | 
| 69 |  |  | 
| 70 |  | /// Adds a register to the implicit write register list. | 
| 71 |  | /// It will not add the same register twice. | 
| 72 |  | void map_add_implicit_write(MCInst *MI, uint32_t Reg) | 
| 73 | 385k | { | 
| 74 | 385k |   if (!MI->flat_insn->detail) | 
| 75 | 0 |     return; | 
| 76 |  |  | 
| 77 | 385k |   uint16_t *regs_write = MI->flat_insn->detail->regs_write; | 
| 78 | 388k |   for (int i = 0; i < MAX_IMPL_W_REGS; ++i) { | 
| 79 | 388k |     if (i == MI->flat_insn->detail->regs_write_count) { | 
| 80 | 365k |       regs_write[i] = Reg; | 
| 81 | 365k |       MI->flat_insn->detail->regs_write_count++; | 
| 82 | 365k |       return; | 
| 83 | 365k |     } | 
| 84 | 22.5k |     if (regs_write[i] == Reg) | 
| 85 | 19.9k |       return; | 
| 86 | 22.5k |   } | 
| 87 | 385k | } | 
| 88 |  |  | 
| 89 |  | /// Adds a register to the implicit read register list. | 
| 90 |  | /// It will not add the same register twice. | 
| 91 |  | void map_add_implicit_read(MCInst *MI, uint32_t Reg) | 
| 92 | 188k | { | 
| 93 | 188k |   if (!MI->flat_insn->detail) | 
| 94 | 0 |     return; | 
| 95 |  |  | 
| 96 | 188k |   uint16_t *regs_read = MI->flat_insn->detail->regs_read; | 
| 97 | 197k |   for (int i = 0; i < MAX_IMPL_W_REGS; ++i) { | 
| 98 | 197k |     if (i == MI->flat_insn->detail->regs_read_count) { | 
| 99 | 170k |       regs_read[i] = Reg; | 
| 100 | 170k |       MI->flat_insn->detail->regs_read_count++; | 
| 101 | 170k |       return; | 
| 102 | 170k |     } | 
| 103 | 26.9k |     if (regs_read[i] == Reg) | 
| 104 | 17.6k |       return; | 
| 105 | 26.9k |   } | 
| 106 | 188k | } | 
| 107 |  |  | 
| 108 |  | /// Removes a register from the implicit write register list. | 
| 109 |  | void map_remove_implicit_write(MCInst *MI, uint32_t Reg) | 
| 110 | 32.2k | { | 
| 111 | 32.2k |   if (!MI->flat_insn->detail) | 
| 112 | 0 |     return; | 
| 113 |  |  | 
| 114 | 32.2k |   uint16_t *regs_write = MI->flat_insn->detail->regs_write; | 
| 115 | 32.2k |   bool shorten_list = false; | 
| 116 | 35.1k |   for (int i = 0; i < MAX_IMPL_W_REGS; ++i) { | 
| 117 | 35.1k |     if (shorten_list) { | 
| 118 | 2.89k |       regs_write[i - 1] = regs_write[i]; | 
| 119 | 2.89k |     } | 
| 120 | 35.1k |     if (i >= MI->flat_insn->detail->regs_write_count) | 
| 121 | 32.2k |       return; | 
| 122 |  |  | 
| 123 | 2.89k |     if (regs_write[i] == Reg) { | 
| 124 | 2.89k |       MI->flat_insn->detail->regs_write_count--; | 
| 125 |  |       // The register should exist only once in the list. | 
| 126 | 2.89k |       assert(!shorten_list); | 
| 127 | 2.89k |       shorten_list = true; | 
| 128 | 2.89k |     } | 
| 129 | 2.89k |   } | 
| 130 | 32.2k | } | 
| 131 |  |  | 
| 132 |  | /// Copies the implicit read registers of @imap to @MI->flat_insn. | 
| 133 |  | /// Already present registers will be preserved. | 
| 134 |  | void map_implicit_reads(MCInst *MI, const insn_map *imap) | 
| 135 | 1.28M | { | 
| 136 | 1.28M | #ifndef CAPSTONE_DIET | 
| 137 | 1.28M |   if (!MI->flat_insn->detail) | 
| 138 | 0 |     return; | 
| 139 |  |  | 
| 140 | 1.28M |   cs_detail *detail = MI->flat_insn->detail; | 
| 141 | 1.28M |   unsigned Opcode = MCInst_getOpcode(MI); | 
| 142 | 1.28M |   unsigned i = 0; | 
| 143 | 1.28M |   uint16_t reg = imap[Opcode].regs_use[i]; | 
| 144 | 1.33M |   while (reg != 0) { | 
| 145 | 52.1k |     if (i >= MAX_IMPL_R_REGS || | 
| 146 | 52.1k |         detail->regs_read_count >= MAX_IMPL_R_REGS) { | 
| 147 | 0 |       printf("ERROR: Too many implicit read register defined in " | 
| 148 | 0 |              "instruction mapping.\n"); | 
| 149 | 0 |       return; | 
| 150 | 0 |     } | 
| 151 | 52.1k |     detail->regs_read[detail->regs_read_count++] = reg; | 
| 152 | 52.1k |     reg = imap[Opcode].regs_use[++i]; | 
| 153 | 52.1k |   } | 
| 154 | 1.28M | #endif // CAPSTONE_DIET | 
| 155 | 1.28M | } | 
| 156 |  |  | 
| 157 |  | /// Copies the implicit write registers of @imap to @MI->flat_insn. | 
| 158 |  | /// Already present registers will be preserved. | 
| 159 |  | void map_implicit_writes(MCInst *MI, const insn_map *imap) | 
| 160 | 1.28M | { | 
| 161 | 1.28M | #ifndef CAPSTONE_DIET | 
| 162 | 1.28M |   if (!MI->flat_insn->detail) | 
| 163 | 0 |     return; | 
| 164 |  |  | 
| 165 | 1.28M |   cs_detail *detail = MI->flat_insn->detail; | 
| 166 | 1.28M |   unsigned Opcode = MCInst_getOpcode(MI); | 
| 167 | 1.28M |   unsigned i = 0; | 
| 168 | 1.28M |   uint16_t reg = imap[Opcode].regs_mod[i]; | 
| 169 | 1.48M |   while (reg != 0) { | 
| 170 | 199k |     if (i >= MAX_IMPL_W_REGS || | 
| 171 | 199k |         detail->regs_write_count >= MAX_IMPL_W_REGS) { | 
| 172 | 0 |       printf("ERROR: Too many implicit write register defined in " | 
| 173 | 0 |              "instruction mapping.\n"); | 
| 174 | 0 |       return; | 
| 175 | 0 |     } | 
| 176 | 199k |     detail->regs_write[detail->regs_write_count++] = reg; | 
| 177 | 199k |     reg = imap[Opcode].regs_mod[++i]; | 
| 178 | 199k |   } | 
| 179 | 1.28M | #endif // CAPSTONE_DIET | 
| 180 | 1.28M | } | 
| 181 |  |  | 
| 182 |  | /// Adds a given group to @MI->flat_insn. | 
| 183 |  | void add_group(MCInst *MI, unsigned /* arch_group */ group) | 
| 184 | 48.9k | { | 
| 185 | 48.9k | #ifndef CAPSTONE_DIET | 
| 186 | 48.9k |   if (!MI->flat_insn->detail) | 
| 187 | 0 |     return; | 
| 188 |  |  | 
| 189 | 48.9k |   cs_detail *detail = MI->flat_insn->detail; | 
| 190 | 48.9k |   if (detail->groups_count >= MAX_NUM_GROUPS) { | 
| 191 | 0 |     printf("ERROR: Too many groups defined.\n"); | 
| 192 | 0 |     return; | 
| 193 | 0 |   } | 
| 194 | 48.9k |   detail->groups[detail->groups_count++] = group; | 
| 195 | 48.9k | #endif // CAPSTONE_DIET | 
| 196 | 48.9k | } | 
| 197 |  |  | 
| 198 |  | /// Copies the groups from @imap to @MI->flat_insn. | 
| 199 |  | /// Already present groups will be preserved. | 
| 200 |  | void map_groups(MCInst *MI, const insn_map *imap) | 
| 201 | 1.28M | { | 
| 202 | 1.28M | #ifndef CAPSTONE_DIET | 
| 203 | 1.28M |   if (!MI->flat_insn->detail) | 
| 204 | 0 |     return; | 
| 205 |  |  | 
| 206 | 1.28M |   cs_detail *detail = MI->flat_insn->detail; | 
| 207 | 1.28M |   unsigned Opcode = MCInst_getOpcode(MI); | 
| 208 | 1.28M |   unsigned i = 0; | 
| 209 | 1.28M |   uint16_t group = imap[Opcode].groups[i]; | 
| 210 | 2.77M |   while (group != 0) { | 
| 211 | 1.48M |     if (detail->groups_count >= MAX_NUM_GROUPS) { | 
| 212 | 0 |       printf("ERROR: Too many groups defined in instruction mapping.\n"); | 
| 213 | 0 |       return; | 
| 214 | 0 |     } | 
| 215 | 1.48M |     detail->groups[detail->groups_count++] = group; | 
| 216 | 1.48M |     group = imap[Opcode].groups[++i]; | 
| 217 | 1.48M |   } | 
| 218 | 1.28M | #endif // CAPSTONE_DIET | 
| 219 | 1.28M | } | 
| 220 |  |  | 
| 221 |  | // Search for the CS instruction id for the given @MC_Opcode in @imap. | 
| 222 |  | // return -1 if none is found. | 
| 223 |  | unsigned int find_cs_id(unsigned MC_Opcode, const insn_map *imap, | 
| 224 |  |       unsigned imap_size) | 
| 225 | 1.28M | { | 
| 226 |  |   // binary searching since the IDs are sorted in order | 
| 227 | 1.28M |   unsigned int left, right, m; | 
| 228 | 1.28M |   unsigned int max = imap_size; | 
| 229 |  |  | 
| 230 | 1.28M |   right = max - 1; | 
| 231 |  |  | 
| 232 | 1.28M |   if (MC_Opcode < imap[0].id || MC_Opcode > imap[right].id) | 
| 233 |  |     // not found | 
| 234 | 0 |     return -1; | 
| 235 |  |  | 
| 236 | 1.28M |   left = 0; | 
| 237 |  |  | 
| 238 | 14.4M |   while (left <= right) { | 
| 239 | 14.4M |     m = (left + right) / 2; | 
| 240 | 14.4M |     if (MC_Opcode == imap[m].id) { | 
| 241 | 1.28M |       return m; | 
| 242 | 1.28M |     } | 
| 243 |  |  | 
| 244 | 13.1M |     if (MC_Opcode < imap[m].id) | 
| 245 | 3.79M |       right = m - 1; | 
| 246 | 9.33M |     else | 
| 247 | 9.33M |       left = m + 1; | 
| 248 | 13.1M |   } | 
| 249 |  |  | 
| 250 | 0 |   return -1; | 
| 251 | 1.28M | } | 
| 252 |  |  | 
| 253 |  | /// Sets the Capstone instruction id which maps to the @MI opcode. | 
| 254 |  | /// If no mapping is found the function returns and prints an error. | 
| 255 |  | void map_cs_id(MCInst *MI, const insn_map *imap, unsigned int imap_size) | 
| 256 | 1.28M | { | 
| 257 | 1.28M |   unsigned int i = find_cs_id(MCInst_getOpcode(MI), imap, imap_size); | 
| 258 | 1.28M |   if (i != -1) { | 
| 259 | 1.28M |     MI->flat_insn->id = imap[i].mapid; | 
| 260 | 1.28M |     return; | 
| 261 | 1.28M |   } | 
| 262 | 0 |   printf("ERROR: Could not find CS id for MCInst opcode: %d\n", | 
| 263 | 0 |          MCInst_getOpcode(MI)); | 
| 264 | 0 |   return; | 
| 265 | 1.28M | } | 
| 266 |  |  | 
| 267 |  | /// Returns the operand type information from the | 
| 268 |  | /// mapping table for instruction operands. | 
| 269 |  | /// Only usable by `auto-sync` archs! | 
| 270 |  | const cs_op_type mapping_get_op_type(MCInst *MI, unsigned OpNum, | 
| 271 |  |              const map_insn_ops *insn_ops_map, | 
| 272 |  |              size_t map_size) | 
| 273 | 11.3M | { | 
| 274 | 11.3M |   assert(MI); | 
| 275 | 11.3M |   assert(MI->Opcode < map_size); | 
| 276 | 11.3M |   assert(OpNum < sizeof(insn_ops_map[MI->Opcode].ops) / | 
| 277 | 11.3M |              sizeof(insn_ops_map[MI->Opcode].ops[0])); | 
| 278 |  |  | 
| 279 | 11.3M |   return insn_ops_map[MI->Opcode].ops[OpNum].type; | 
| 280 | 11.3M | } | 
| 281 |  |  | 
| 282 |  | /// Returns the operand access flags from the | 
| 283 |  | /// mapping table for instruction operands. | 
| 284 |  | /// Only usable by `auto-sync` archs! | 
| 285 |  | const cs_ac_type mapping_get_op_access(MCInst *MI, unsigned OpNum, | 
| 286 |  |                const map_insn_ops *insn_ops_map, | 
| 287 |  |                size_t map_size) | 
| 288 | 3.77M | { | 
| 289 | 3.77M |   assert(MI); | 
| 290 | 3.77M |   assert(MI->Opcode < map_size); | 
| 291 | 3.77M |   assert(OpNum < sizeof(insn_ops_map[MI->Opcode].ops) / | 
| 292 | 3.77M |              sizeof(insn_ops_map[MI->Opcode].ops[0])); | 
| 293 |  |  | 
| 294 | 3.77M |   cs_ac_type access = insn_ops_map[MI->Opcode].ops[OpNum].access; | 
| 295 | 3.77M |   if (MCInst_opIsTied(MI, OpNum) || MCInst_opIsTying(MI, OpNum)) | 
| 296 | 295k |     access |= (access == CS_AC_READ) ? CS_AC_WRITE : CS_AC_READ; | 
| 297 | 3.77M |   return access; | 
| 298 | 3.77M | } | 
| 299 |  |  | 
| 300 |  | /// Returns the operand at detail->arch.operands[op_count + offset] | 
| 301 |  | /// Or NULL if detail is not set. | 
| 302 |  | #define DEFINE_get_detail_op(arch, ARCH) \ | 
| 303 |  |   cs_##arch##_op *ARCH##_get_detail_op(MCInst *MI, int offset) \ | 
| 304 | 14.5M |   { \ | 
| 305 | 14.5M |     if (!MI->flat_insn->detail) \ | 
| 306 | 14.5M |       return NULL; \ | 
| 307 | 14.5M |     int OpIdx = MI->flat_insn->detail->arch.op_count + offset; \ | 
| 308 | 14.5M |     assert(OpIdx >= 0 && OpIdx < MAX_MC_OPS); \ | 
| 309 | 14.5M |     return &MI->flat_insn->detail->arch.operands[OpIdx]; \ | 
| 310 | 14.5M |   } | Line | Count | Source |  | 304 | 13.9M |   { \ |  | 305 | 13.9M |     if (!MI->flat_insn->detail) \ |  | 306 | 13.9M |       return NULL; \ |  | 307 | 13.9M |     int OpIdx = MI->flat_insn->detail->arch.op_count + offset; \ |  | 308 | 13.9M |     assert(OpIdx >= 0 && OpIdx < MAX_MC_OPS); \ |  | 309 | 13.9M |     return &MI->flat_insn->detail->arch.operands[OpIdx]; \ |  | 310 | 13.9M |   } | 
| Line | Count | Source |  | 304 | 657k |   { \ |  | 305 | 657k |     if (!MI->flat_insn->detail) \ |  | 306 | 657k |       return NULL; \ |  | 307 | 657k |     int OpIdx = MI->flat_insn->detail->arch.op_count + offset; \ |  | 308 | 657k |     assert(OpIdx >= 0 && OpIdx < MAX_MC_OPS); \ |  | 309 | 657k |     return &MI->flat_insn->detail->arch.operands[OpIdx]; \ |  | 310 | 657k |   } | 
Unexecuted instantiation: TriCore_get_detail_op | 
| 311 |  |  | 
| 312 |  | DEFINE_get_detail_op(arm, ARM); | 
| 313 |  | DEFINE_get_detail_op(ppc, PPC); | 
| 314 |  | DEFINE_get_detail_op(tricore, TriCore); | 
| 315 |  |  | 
| 316 |  | /// Returns true if for this architecture the | 
| 317 |  | /// alias operands should be filled. | 
| 318 |  | /// TODO: Replace this with a proper option. | 
| 319 |  | ///       So it can be toggled between disas() calls. | 
| 320 | 127k | bool map_use_alias_details(const MCInst *MI) { | 
| 321 | 127k |   assert(MI); | 
| 322 | 127k |   return !(MI->csh->detail_opt & CS_OPT_DETAIL_REAL); | 
| 323 | 127k | } | 
| 324 |  |  | 
| 325 |  | /// Sets the setDetailOps flag to @p Val. | 
| 326 |  | /// If detail == NULLit refuses to set the flag to true. | 
| 327 | 114k | void map_set_fill_detail_ops(MCInst *MI, bool Val) { | 
| 328 | 114k |   assert(MI); | 
| 329 | 114k |   if (!detail_is_set(MI)) { | 
| 330 | 0 |     MI->fillDetailOps = false; | 
| 331 | 0 |     return; | 
| 332 | 0 |   } | 
| 333 |  |  | 
| 334 | 114k |   MI->fillDetailOps = Val; | 
| 335 | 114k | } | 
| 336 |  |  | 
| 337 |  | /// Sets the instruction alias flags and the given alias id. | 
| 338 | 0 | void map_set_is_alias_insn(MCInst *MI, bool Val, uint64_t Alias) { | 
| 339 | 0 |   assert(MI); | 
| 340 | 0 |   MI->isAliasInstr = Val; | 
| 341 | 0 |   MI->flat_insn->is_alias = Val; | 
| 342 | 0 |   MI->flat_insn->alias_id = Alias; | 
| 343 | 0 | } | 
| 344 |  |  | 
| 345 |  | /// Sets an alternative id for some instruction. | 
| 346 |  | /// Or -1 if it fails. | 
| 347 |  | /// You must add (<ARCH>_INS_ALIAS_BEGIN + 1) to the id to get the real id. | 
| 348 | 63.8k | void map_set_alias_id(MCInst *MI, const SStream *O, const name_map *alias_mnem_id_map, int map_size) { | 
| 349 | 63.8k |   if (!MCInst_isAlias(MI)) | 
| 350 | 53.2k |     return; | 
| 351 |  |  | 
| 352 | 10.5k |   char alias_mnem[16] = { 0 }; | 
| 353 | 10.5k |   int i = 0, j = 0; | 
| 354 | 10.5k |   const char *asm_str_buf = O->buffer; | 
| 355 |  |   // Skip spaces and tabs | 
| 356 | 10.5k |   while (asm_str_buf[i] == ' ' || asm_str_buf[i] == '\t') { | 
| 357 | 0 |     if (!asm_str_buf[i]) { | 
| 358 | 0 |       MI->flat_insn->alias_id = -1; | 
| 359 | 0 |       return; | 
| 360 | 0 |     } | 
| 361 | 0 |     ++i; | 
| 362 | 0 |   } | 
| 363 | 73.5k |   for (; j < sizeof(alias_mnem) - 1; ++j, ++i) { | 
| 364 | 73.5k |     if (!asm_str_buf[i] || asm_str_buf[i] == ' ' || asm_str_buf[i] == '\t') | 
| 365 | 10.5k |       break; | 
| 366 | 62.9k |     alias_mnem[j] = O->buffer[i]; | 
| 367 | 62.9k |   } | 
| 368 |  |  | 
| 369 | 10.5k |   MI->flat_insn->alias_id = name2id(alias_mnem_id_map, map_size, alias_mnem); | 
| 370 | 10.5k | } | 
| 371 |  |  |