root/tools/firmware_load_ng.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. addBufRange
  2. findRanges
  3. getBufRangeForIndex
  4. find_Nth_str
  5. find_str
  6. find_next_bytes_range
  7. find_bytes_all
  8. find_next_substr_bytes
  9. find_next_str_bytes_range
  10. find_str_bytes_main_fw
  11. find_next_str_bytes
  12. find_str_bytes
  13. isASCIIstring
  14. adr_get_range
  15. adr_get_range_type
  16. ptr2adr
  17. adr2ptr
  18. adr2ptr_with_data
  19. adr_range_type_str
  20. adr_range_desc_str
  21. adr_is_var
  22. adr_is_main_fw_code
  23. find_u32_adr_range
  24. find_u32_adr
  25. fw_u32
  26. fw_memcmp
  27. adr_hist_reset
  28. adr_hist_index
  29. adr_hist_add
  30. adr_hist_get
  31. isARM
  32. isLDR_PC
  33. isLDR_PC_PC
  34. isSUBW_PC
  35. isADDW_PC
  36. isADD_PC
  37. isSUB_PC
  38. isRETx
  39. isPUSH_LR
  40. isPOP_LR
  41. isPOP_PC
  42. isADDx_imm
  43. isSUBx_imm
  44. isADRx
  45. LDR_PC2valptr_thumb
  46. LDR_PC2valptr_arm
  47. LDR_PC2valptr
  48. LDR_PC2adr
  49. ADRx2adr
  50. ADR2adr
  51. ADR2valptr
  52. LDR_PC2val
  53. LDR_PC_PC_target
  54. B_target
  55. CBx_target
  56. BLXimm_target
  57. BL_target
  58. B_BL_target
  59. B_BL_BLXimm_target
  60. get_TBx_PC_info
  61. disasm_iter_new
  62. disasm_iter_free
  63. disasm_iter_set
  64. disasm_iter_init
  65. disasm_iter
  66. disasm_iter_redo
  67. fw_disasm_iter_start
  68. fw_disasm_iter
  69. fw_disasm_iter_single
  70. fw_disasm_adr
  71. fw_search_insn
  72. search_disasm_const_ref
  73. search_disasm_str_ref
  74. search_disasm_calls
  75. search_calls_multi_end
  76. search_disasm_calls_multi
  77. get_call_const_args
  78. get_direct_jump_target
  79. get_branch_call_insn_target
  80. find_and_get_var_ldr
  81. check_simple_func
  82. find_last_call_from_func
  83. insn_match_seq
  84. insn_match
  85. insn_match_any
  86. insn_match_find_next
  87. insn_match_find_nth
  88. insn_match_find_next_seq
  89. fw_search_bytes
  90. fw_add_adr_range
  91. find_dryos_vers
  92. firmware_load
  93. do_blx_check
  94. firmware_init_capstone
  95. find_startup_copy
  96. find_exception_vec
  97. firmware_init_data_ranges
  98. firmware_unload

   1 #include <inttypes.h>
   2 #include <stdio.h>
   3 #include <stdint.h>
   4 #include <string.h>
   5 
   6 #include <capstone.h>
   7 
   8 #include "stubs_load.h" // needed for sv in fw struct
   9 #include "firmware_load_ng.h"
  10 
  11 
  12 // Add a valid range to the list
  13 static void addBufRange(firmware *fw, int o, int l)
  14 {
  15     BufRange *n = malloc(sizeof(BufRange));
  16     n->p = fw->buf32 + o;
  17     n->off = o;
  18     n->len = l;
  19     n->next = 0;
  20     if (fw->br == 0)
  21     {
  22         fw->br = n;
  23     }
  24     else
  25     {
  26         fw->last->next = n;
  27     }
  28     fw->last = n;
  29 }
  30 
  31 // Find valid ranges for the firmware dump
  32 static void findRanges(firmware *fw)
  33 {
  34     int i, j, k;
  35 
  36     // Find all the valid ranges for checking (skips over large blocks of 0xFFFFFFFF)
  37     fw->br = 0; fw->last = 0;
  38     k = -1; j = 0;
  39     for (i = 0; i < fw->size32; i++)
  40     {
  41         if (fw->buf32[i] == 0xFFFFFFFF)   // Possible start of block to skip
  42         {
  43             if (k == -1)            // Mark start of possible skip block
  44             {
  45                 k = i;
  46             }
  47         }
  48         else                        // Found end of block ?
  49         {
  50             if (k != -1)
  51             {
  52                 if (i - k > 32)     // If block more than 32 words then we want to skip it
  53                 {
  54                     if (k - j > 8)
  55                     {
  56                         // Add a range record for the previous valid range (ignore short ranges)
  57                         addBufRange(fw,j,k - j);
  58                     }
  59                     j = i;          // Reset valid range start to current position
  60                 }
  61                 k = -1;             // Reset marker for skip block
  62             }
  63         }
  64     }
  65     // Add range for last valid block
  66     if (k != -1)
  67     {
  68         if (k - j > 8)
  69         {
  70             addBufRange(fw,j,k - j);
  71         }
  72     }
  73     else
  74     {
  75         if (i - j > 8)
  76         {
  77             addBufRange(fw,j,i - j);
  78         }
  79     }
  80 }
  81 
  82 // return the buffrange for a given offset or null if not found
  83 BufRange *getBufRangeForIndex(firmware *fw,int i)
  84 {
  85     BufRange *br = fw->br;
  86     while (br) {
  87         if(i >= br->off && i < br->off + br->len) {
  88             return br;
  89         }
  90         br = br->next;
  91     }
  92     return NULL;
  93 }
  94 
  95 // Find the index of a string in the firmware
  96 // Assumes the string starts on a 32bit boundary.
  97 // String + terminating zero byte should be at least 4 bytes long
  98 // Handles multiple string instances
  99 int find_Nth_str(firmware *fw, char *str, int N)
 100 {
 101     int nlen = strlen(str);
 102     uint32_t nm0 = *((uint32_t*)str);
 103     uint32_t *p;
 104     int j;
 105 
 106     BufRange *br = fw->br;
 107     while (br)
 108     {
 109         for (p = br->p, j = 0; j < br->len - nlen/4; j++, p++)
 110         {
 111             if ((nm0 == *p) && ((nlen<=4) || (memcmp(p+1,str+4,nlen-4) == 0)) )
 112             {
 113                 if (--N == 0)
 114                     return j+br->off;
 115             }
 116         }
 117         br = br->next;
 118     }
 119 
 120     return -1;
 121 }
 122 
 123 int find_str(firmware *fw, char *str)
 124 {
 125     return find_Nth_str(fw, str, 1);
 126 }
 127 
 128 // find sequence of bytes, starting from star_adr, up to max_adr, any alignment
 129 // returns firmware address or 0
 130 // use repeated calls to find multiple
 131 // NOTE only handles ROM addresses
 132 uint32_t find_next_bytes_range(firmware *fw, const void *bytes, size_t len, uint32_t start_adr, uint32_t max_adr)
 133 {
 134     if(!start_adr) {
 135         start_adr = fw->base;
 136     }
 137     if(start_adr < fw->base || start_adr >= fw->base + fw->size8) {
 138         fprintf(stderr,"find_next_bytes_range invalid start_adr 0x%08x\n",start_adr);
 139         return 0;
 140     }
 141     if(!max_adr) {
 142         max_adr = fw->base + fw->size8-1;
 143     }
 144     if(max_adr < fw->base || max_adr >= fw->base + fw->size8) {
 145         fprintf(stderr,"find_next_bytes_range invalid max_adr 0x%08x\n",max_adr);
 146         return 0;
 147     }
 148     int end_k = (max_adr - fw->base);
 149     BufRange *p = getBufRangeForIndex(fw,(start_adr - fw->base)/4);
 150     if(!p) {
 151         return 0;
 152     }
 153     int k = start_adr - fw->base;
 154 
 155     while (k < end_k)
 156     {
 157         for (; k < (p->off + p->len)*4; k++)
 158         {
 159             if (memcmp(fw->buf8+k,bytes,len) == 0) {
 160                 return fw->base+k;
 161             }
 162         }
 163         p = p->next;
 164         if(!p) {
 165             break;
 166         }
 167         k = p->off*4;
 168     }
 169     return 0;
 170 }
 171 
 172 // find up to max matching byte sequences, storing addresses in result
 173 // returns count
 174 int find_bytes_all(firmware *fw, const void *bytes, size_t len, uint32_t adr, uint32_t *result, int max)
 175 {
 176     int i;
 177     for(i=0,adr=find_next_bytes_range(fw,bytes,len,0,0); adr && (i < max); adr=find_next_bytes_range(fw,bytes,len,adr+len,0),i++) {
 178         result[i] = adr;
 179     }
 180     return i;
 181 }
 182 
 183 uint32_t find_next_substr_bytes(firmware *fw, const char *str, uint32_t adr)
 184 {
 185     //fprintf(stderr,"find_next_substr_bytes 0x%08x\n",adr);
 186     // strlen excludes null
 187     return find_next_bytes_range(fw,str,strlen(str),adr,0);
 188 }
 189 
 190 uint32_t find_next_str_bytes_range(firmware *fw, const char *str, uint32_t adr,uint32_t max_adr)
 191 {
 192     // +1 to include the null in memcmp
 193     return find_next_bytes_range(fw,str,strlen(str)+1,adr,max_adr);
 194 }
 195 
 196 // find a string within range of LDR pc or ADR, starting from main fw
 197 uint32_t find_str_bytes_main_fw(firmware *fw, const char *str)
 198 {
 199     // max is end of fw code + 4096, assuming it fits in fw
 200     // while early code could technically load from base - 1k, unlikely
 201     uint32_t max_adr;
 202     if(fw->base + fw->size8 - 4096 > fw->rom_code_search_max_adr) {
 203         max_adr = fw->rom_code_search_max_adr + 4096;
 204     } else {
 205         max_adr = fw->base + fw->size8;
 206     }
 207     // +1 to include the null in memcmp
 208     return find_next_bytes_range(fw,str,strlen(str)+1,fw->rom_code_search_min_adr,max_adr);
 209 }
 210 
 211 uint32_t find_next_str_bytes(firmware *fw, const char *str, uint32_t adr)
 212 {
 213     // +1 to include the null in memcmp
 214     return find_next_bytes_range(fw,str,strlen(str)+1,adr,0);
 215 }
 216 
 217 // Find the index of a string in the firmware, can start at any address
 218 // returns firmware address
 219 uint32_t find_str_bytes(firmware *fw, const char *str)
 220 {
 221     return find_next_str_bytes(fw,str,fw->base);
 222 }
 223 
 224 int isASCIIstring(firmware *fw, uint32_t adr)
 225 {
 226     unsigned char *p = (unsigned char*)adr2ptr_with_data(fw, adr);
 227     if(!p) {
 228         return 0;
 229     }
 230     // TODO should avoid running off end of dump
 231     int i;
 232     for (i = 0; (i < 100) && (p[i] != 0); i++)
 233     {
 234         if (!((p[i] == '\r') || (p[i] == '\n') || (p[i] == '\t') || ((p[i] >= 0x20) && (p[i] <= 0x7f))))
 235         {
 236             return 0;
 237         }
 238     }
 239     if ((i >= 2) && (p[i] == 0))
 240         return 1;
 241     return 0;
 242 }
 243 
 244 // return address range struct for adr, or NULL if not in known range
 245 adr_range_t *adr_get_range(firmware *fw, uint32_t adr)
 246 {
 247     int i;
 248     adr_range_t *r=fw->adr_ranges;
 249     for(i=0;i<fw->adr_range_count;i++) {
 250         if(adr >= r->start && adr < r->start + r->bytes) {
 251             return r;
 252         }
 253         r++;
 254     }
 255     return NULL;
 256 }
 257 
 258 // return what kind of range adr is in
 259 int adr_get_range_type(firmware *fw, uint32_t adr)
 260 {
 261     adr_range_t *r=adr_get_range(fw,adr);
 262     if(!r) {
 263         return ADR_RANGE_INVALID;
 264     }
 265     return r->type;
 266 }
 267 
 268 uint32_t ptr2adr(firmware *fw, uint8_t *ptr)
 269 {
 270     // TODO handle copied, or maybe another func to convert?
 271     return (ptr-fw->buf8)+fw->base;
 272 }
 273 
 274 uint8_t* adr2ptr(firmware *fw, uint32_t adr)
 275 {
 276     adr_range_t *r=adr_get_range(fw,adr);
 277     if(!r) {
 278         return NULL;
 279     }
 280     switch(r->type) {
 281         case ADR_RANGE_RAM_CODE:
 282         case ADR_RANGE_ROM:
 283             return (r->buf)+(adr - r->start);
 284         default:
 285             return NULL;
 286     }
 287 }
 288 
 289 uint8_t* adr2ptr_with_data(firmware *fw, uint32_t adr)
 290 {
 291     adr_range_t *r=adr_get_range(fw,adr);
 292     if(!r) {
 293         return NULL;
 294     }
 295     switch(r->type) {
 296         case ADR_RANGE_RAM_CODE:
 297         case ADR_RANGE_INIT_DATA:
 298         case ADR_RANGE_ROM:
 299             return (r->buf)+(adr - r->start);
 300         default:
 301             return NULL;
 302     }
 303 }
 304 
 305 // return constant string describing type
 306 const char* adr_range_type_str(int type)
 307 {
 308     switch(type) {
 309         case ADR_RANGE_INVALID:
 310             return "(invalid)";
 311         case ADR_RANGE_ROM:
 312             return "ROM";
 313         case ADR_RANGE_RAM_CODE:
 314             return "RAM code";
 315         case ADR_RANGE_INIT_DATA:
 316             return "RAM data";
 317         default:
 318             return "(unknown)";
 319     }
 320 }
 321 
 322 // return constant string describing type and flags
 323 const char* adr_range_desc_str(adr_range_t *r)
 324 {
 325     switch(r->type) {
 326         case ADR_RANGE_INVALID:
 327             return "(invalid)";
 328         case ADR_RANGE_ROM:
 329             return "ROM";
 330         case ADR_RANGE_RAM_CODE:
 331             if(r->flags & ADR_RANGE_FL_EVEC) {
 332                 return "EVEC";
 333             } else if(r->flags & ADR_RANGE_FL_TCM) {
 334                 return "TCM code";
 335             }
 336             return "RAM code";
 337         case ADR_RANGE_INIT_DATA:
 338             return "RAM data";
 339         default:
 340             return "(unknown)";
 341     }
 342 }
 343 
 344 // return true if adr is in firmware DATA or BSS
 345 int adr_is_var(firmware *fw, uint32_t adr)
 346 {
 347     return (adr > fw->data_start && adr < fw->memisostart);
 348 }
 349 
 350 // return true if adr is in the ROM search range, or one of the copied RAM code regions
 351 int adr_is_main_fw_code(firmware *fw, uint32_t adr)
 352 {
 353     int adr_type = adr_get_range_type(fw,adr);
 354     if(adr_type == ADR_RANGE_RAM_CODE) {
 355         return 1;
 356     }
 357     if(adr_type != ADR_RANGE_ROM) {
 358         return 0;
 359     }
 360     if(adr < fw->rom_code_search_min_adr  || adr > fw->rom_code_search_max_adr) {
 361         return 0;
 362     }
 363     return 1;
 364 }
 365 
 366 /*
 367 return firmware address of 32 bit value, starting at address "start", up to max
 368 */
 369 uint32_t find_u32_adr_range(firmware *fw, uint32_t val, uint32_t start,uint32_t maxadr)
 370 {
 371     // TODO 
 372     if(start == 0) {
 373         start=fw->base;
 374     }
 375     if(start & 3) {
 376         fprintf(stderr,"find_u32_adr unaligned start 0x%08x\n",start);
 377         return 0;
 378     }
 379     uint32_t *p=(uint32_t *)adr2ptr(fw,start);
 380     if(!p) {
 381         fprintf(stderr,"find_u32_adr bad start 0x%08x\n",start);
 382         return 0;
 383     }
 384     uint32_t *p_end;
 385     if(maxadr) {
 386         p_end = (uint32_t *)adr2ptr(fw,maxadr);
 387     } else {
 388         p_end = fw->buf32 + fw->size32 - 1;
 389     }
 390     // TODO should use buf ranges
 391     while(p<=p_end) {
 392         if(*p==val) {
 393             return ptr2adr(fw,(uint8_t *)p);
 394         }
 395         p++;
 396     }
 397     return 0;
 398 }
 399 
 400 // as above, full to end of fw
 401 uint32_t find_u32_adr(firmware *fw, uint32_t val, uint32_t start)
 402 {
 403     return find_u32_adr_range(fw,val,start, fw->base + (fw->size8 -4));
 404 }
 405 
 406 // return u32 value at adr
 407 uint32_t fw_u32(firmware *fw, uint32_t adr)
 408 {
 409     uint32_t *p=(uint32_t *)adr2ptr(fw,adr);
 410     if(!p) {
 411         fprintf(stderr,"fw_u32 bad adr 0x%08x\n",adr);
 412         return 0;
 413     }
 414     return *p;
 415 }
 416 
 417 // memcmp, but using a firmware address, returning 1 adr/size out of range
 418 int fw_memcmp(firmware *fw, uint32_t adr,const void *cmp, size_t n)
 419 {
 420     uint32_t *p=(uint32_t *)adr2ptr(fw,adr);
 421     if(!p) {
 422         return 1;
 423     }
 424     if(n >= fw->size8 - (adr - fw->base)) {
 425         return 1;
 426     }
 427     return memcmp(p,cmp,n);
 428 }
 429 
 430 
 431 // ****** address history functions ******
 432 // reset address history to empty
 433 void adr_hist_reset(adr_hist_t *ah)
 434 {
 435     ah->cur=0;
 436     ah->count=0;
 437     // memset shouldn't be needed
 438     // memset(ah->adrs,0,ADR_HIST_SIZE*4);
 439 }
 440 
 441 // return the index of current entry + i. may be negative or positive, wraps. Does not check validity
 442 int adr_hist_index(adr_hist_t *ah, int i)
 443 {
 444     int r=(ah->cur+i)%ADR_HIST_SIZE;
 445     if(r < 0) {
 446         return ADR_HIST_SIZE + r;
 447     }
 448     return r;
 449 }
 450 
 451 // add an entry to address history
 452 void adr_hist_add(adr_hist_t *ah, uint32_t adr)
 453 {
 454     ah->cur=adr_hist_index(ah,1);
 455     ah->adrs[ah->cur]=adr;
 456     if(ah->count < ADR_HIST_SIZE)  {
 457         ah->count++;
 458     }
 459 }
 460 
 461 // return the i'th previous entry in this history, or 0 if not valid (maybe should be -1?)
 462 // i= 0 = most recently disassembled instruction, if any
 463 uint32_t adr_hist_get(adr_hist_t *ah, int i)
 464 {
 465     if(!ah->count || i > ah->count) {
 466         return 0;
 467     }
 468     return ah->adrs[adr_hist_index(ah,-i)];
 469 }
 470 
 471 // ****** instruction analysis utilities ******
 472 // is insn an ARM instruction?
 473 // like cs_insn_group(cs_handle,insn,ARM_GRP_ARM) but doesn't require handle and doesn't check or report errors
 474 int isARM(cs_insn *insn)
 475 {
 476     int i;
 477     for(i=0;i<insn->detail->groups_count;i++) {
 478         if(insn->detail->groups[i] == ARM_GRP_ARM) {
 479             return 1;
 480         }
 481     }
 482     return 0;
 483 }
 484 
 485 /*
 486 is insn a PC relative load?
 487 */
 488 int isLDR_PC(cs_insn *insn)
 489 {
 490     return insn->id == ARM_INS_LDR
 491            && insn->detail->arm.op_count == 2
 492            && insn->detail->arm.operands[0].type == ARM_OP_REG
 493            && insn->detail->arm.operands[1].type == ARM_OP_MEM
 494            && insn->detail->arm.operands[1].mem.base == ARM_REG_PC;
 495 
 496 }
 497 
 498 /*
 499 is insn a PC relative load to PC?
 500 */
 501 int isLDR_PC_PC(cs_insn *insn)
 502 {
 503     if(!isLDR_PC(insn)) {
 504         return 0;
 505     }
 506     return (insn->detail->arm.operands[0].reg == ARM_REG_PC);
 507 }
 508 
 509 //  subw    rd, pc, #x?
 510 int isSUBW_PC(cs_insn *insn)
 511 {
 512     return(insn->id == ARM_INS_SUBW
 513        && insn->detail->arm.op_count == 3
 514        && insn->detail->arm.operands[0].type == ARM_OP_REG
 515        && insn->detail->arm.operands[0].reg != ARM_REG_PC
 516        && insn->detail->arm.operands[1].type == ARM_OP_REG
 517        && insn->detail->arm.operands[1].reg == ARM_REG_PC
 518        && insn->detail->arm.operands[2].type == ARM_OP_IMM);
 519 }
 520 
 521 //  addw    rd, pc, #x?
 522 int isADDW_PC(cs_insn *insn)
 523 {
 524     return(insn->id == ARM_INS_ADDW
 525        && insn->detail->arm.op_count == 3
 526        && insn->detail->arm.operands[0].type == ARM_OP_REG
 527        && insn->detail->arm.operands[0].reg != ARM_REG_PC
 528        && insn->detail->arm.operands[1].type == ARM_OP_REG
 529        && insn->detail->arm.operands[1].reg == ARM_REG_PC
 530        && insn->detail->arm.operands[2].type == ARM_OP_IMM);
 531 }
 532 
 533 // is insn ADD rd, pc, #x  (only generated for ARM in capstone)
 534 int isADD_PC(cs_insn *insn)
 535 {
 536     return (insn->id == ARM_INS_ADD
 537             && insn->detail->arm.op_count == 3
 538             && insn->detail->arm.operands[0].reg != ARM_REG_PC
 539             && insn->detail->arm.operands[1].type == ARM_OP_REG
 540             && insn->detail->arm.operands[1].reg == ARM_REG_PC
 541             && insn->detail->arm.operands[2].type == ARM_OP_IMM);
 542 }
 543 
 544 // is insn SUB rd, pc, #x  (only generated for ARM in capstone)
 545 int isSUB_PC(cs_insn *insn)
 546 {
 547     return (insn->id == ARM_INS_SUB
 548             && insn->detail->arm.op_count == 3
 549             && insn->detail->arm.operands[0].reg != ARM_REG_PC
 550             && insn->detail->arm.operands[1].type == ARM_OP_REG
 551             && insn->detail->arm.operands[1].reg == ARM_REG_PC
 552             && insn->detail->arm.operands[2].type == ARM_OP_IMM);
 553 }
 554 
 555 // does insn look like a function return?
 556 int isRETx(cs_insn *insn)
 557 {
 558     // BX LR
 559     if(insn->id == ARM_INS_BX
 560             && insn->detail->arm.op_count == 1
 561             && insn->detail->arm.operands[0].type == ARM_OP_REG
 562             && insn->detail->arm.operands[0].reg == ARM_REG_LR) {
 563         return 1;
 564     }
 565     
 566     // TODO LDR pc, [sp], imm is somewhat common, but could also be function pointer call
 567     
 568     // POP. capstone translates LDMFD   SP!,... in arm code to pop
 569     if(insn->id == ARM_INS_POP) {
 570         int i;
 571         for(i=0; i < insn->detail->arm.op_count; i++) {
 572             if(insn->detail->arm.operands[i].type == ARM_OP_REG 
 573                 && insn->detail->arm.operands[i].reg == ARM_REG_PC) {
 574                 return 1;
 575             }
 576         }
 577     }
 578     // MOV PC, LR (some tools translate this to RET)
 579     if(insn->id == ARM_INS_MOV
 580             && insn->detail->arm.operands[0].type == ARM_OP_REG
 581             && insn->detail->arm.operands[0].reg == ARM_REG_PC
 582             && insn->detail->arm.operands[1].type == ARM_OP_REG
 583             && insn->detail->arm.operands[1].reg == ARM_REG_LR) {
 584         return 1;
 585     }
 586     return 0;
 587 }
 588 
 589 // does insn push LR (function start -ish)
 590 int isPUSH_LR(cs_insn *insn)
 591 {
 592     if(insn->id != ARM_INS_PUSH) {
 593         return 0;
 594     }
 595     int i;
 596     for(i=0; i < insn->detail->arm.op_count; i++) {
 597         if(insn->detail->arm.operands[i].type == ARM_OP_REG 
 598             && insn->detail->arm.operands[i].reg == ARM_REG_LR) {
 599             return 1;
 600         }
 601     }
 602     return 0;
 603 }
 604 
 605 // does insn pop LR (func end before tail call)
 606 int isPOP_LR(cs_insn *insn)
 607 {
 608     if(insn->id != ARM_INS_POP) {
 609         return 0;
 610     }
 611     int i;
 612     for(i=0; i < insn->detail->arm.op_count; i++) {
 613         if(insn->detail->arm.operands[i].type == ARM_OP_REG 
 614             && insn->detail->arm.operands[i].reg == ARM_REG_LR) {
 615             return 1;
 616         }
 617     }
 618     return 0;
 619 }
 620 
 621 // does insn pop PC
 622 int isPOP_PC(cs_insn *insn)
 623 {
 624     if(insn->id != ARM_INS_POP) {
 625         return 0;
 626     }
 627     int i;
 628     for(i=0; i < insn->detail->arm.op_count; i++) {
 629         if(insn->detail->arm.operands[i].type == ARM_OP_REG 
 630             && insn->detail->arm.operands[i].reg == ARM_REG_PC) {
 631             return 1;
 632         }
 633     }
 634     return 0;
 635 }
 636 
 637 // is the instruction ADD* rx, imm
 638 int isADDx_imm(cs_insn *insn)
 639 {
 640     return ((insn->id == ARM_INS_ADD || insn->id == ARM_INS_ADDW) && insn->detail->arm.operands[1].type == ARM_OP_IMM);
 641 }
 642 // is the instruction SUB* rx, imm
 643 int isSUBx_imm(cs_insn *insn)
 644 {
 645     return (IS_INSN_ID_SUBx(insn->id) && insn->detail->arm.operands[1].type == ARM_OP_IMM);
 646 }
 647 
 648 // is the instruction an ADR or ADR-like instruction?
 649 int isADRx(cs_insn *insn)
 650 {
 651     return ((insn->id == ARM_INS_ADR)
 652         || isSUBW_PC(insn)
 653         || isADDW_PC(insn)
 654         || (isARM(insn) && (isADD_PC(insn) || isSUB_PC(insn))));
 655 }
 656 
 657 // if insn is LDR Rn, [pc,#x] return pointer to value, otherwise null
 658 uint32_t* LDR_PC2valptr_thumb(firmware *fw, cs_insn *insn)
 659 {
 660     if(!isLDR_PC(insn)) {
 661         return NULL;
 662     }
 663     uint32_t adr;
 664     // TODO NOTE doesn't do anything with scale (which can supposedly be neg?),
 665     // appears correct for examples seen so far
 666     adr=(insn->address&~3)+4+insn->detail->arm.operands[1].mem.disp;
 667     return (uint32_t *)adr2ptr(fw,adr);
 668 }
 669 
 670 uint32_t* LDR_PC2valptr_arm(firmware *fw, cs_insn *insn)
 671 {
 672     if(!isLDR_PC(insn)) {
 673         return NULL;
 674     }
 675     uint32_t adr;
 676     // TODO NOTE doesn't do anything with scale (which can supposedly be neg?),
 677     // appears correct for examples seen so far
 678     adr=insn->address+8+insn->detail->arm.operands[1].mem.disp;
 679     return (uint32_t *)adr2ptr(fw,adr);
 680 }
 681 
 682 uint32_t* LDR_PC2valptr(firmware *fw, cs_insn *insn)
 683 {
 684     if(isARM(insn)) {
 685        return LDR_PC2valptr_arm(fw,insn);
 686     } else {
 687        return LDR_PC2valptr_thumb(fw,insn);
 688     }
 689 }
 690 
 691 // return the address of value loaded by LDR rd, [pc, #x] or 0 if not LDR PC
 692 uint32_t LDR_PC2adr(firmware *fw, cs_insn *insn)
 693 {
 694     if(!isLDR_PC(insn)) {
 695         return 0;
 696     }
 697     if(isARM(insn)) {
 698        return insn->address+8+insn->detail->arm.operands[1].mem.disp;
 699     } else {
 700        return (insn->address&~3)+4+insn->detail->arm.operands[1].mem.disp;
 701     }
 702 }
 703 
 704 // return value generated by an ADR or ADR-like instruction, or 0 (which should be rarely generated by ADR)
 705 uint32_t ADRx2adr(firmware *fw, cs_insn *insn)
 706 {
 707     if(insn->id == ARM_INS_ADR) {
 708         return (insn->address&~3)+4+insn->detail->arm.operands[1].imm;
 709     }
 710     if(isSUBW_PC(insn)) {
 711         return (insn->address&~3)+4-insn->detail->arm.operands[2].imm;
 712     }
 713     if(isADDW_PC(insn)) {
 714         return (insn->address&~3)+4+insn->detail->arm.operands[2].imm;
 715     }
 716     if(isARM(insn)) {
 717         if(isADD_PC(insn)) {
 718             return insn->address+8+insn->detail->arm.operands[2].imm;
 719         }
 720         if(isSUB_PC(insn)) {
 721             return insn->address+8-insn->detail->arm.operands[2].imm;
 722         }
 723     }
 724     return 0;
 725 }
 726 
 727 // return the value generated by an ADR (ie, the location of the value as a firmware address)
 728 // NOTE not checked if it is in dump
 729 uint32_t ADR2adr(firmware *fw, cs_insn *insn)
 730 {
 731     if(insn->id != ARM_INS_ADR) {
 732         return 0;
 733     }
 734     // TODO - capstone doesn't appear to generate ADR for ARM
 735     /*
 736     if(cs_insn_group(fw->cs_handle,insn,ARM_GRP_ARM)) {
 737        return 0;
 738     }
 739     */
 740     return (insn->address&~3)+4+insn->detail->arm.operands[1].imm;
 741 }
 742 
 743 // if insn is adr/ AKA ADD Rn, pc,#x return pointer to value, otherwise null
 744 uint32_t* ADR2valptr(firmware *fw, cs_insn *insn)
 745 {
 746     uint32_t adr=ADR2adr(fw,insn);
 747     return (uint32_t *)adr2ptr(fw,adr);
 748 }
 749 
 750 // return value loaded by PC relative LDR instruction, or 0 if out of range
 751 uint32_t LDR_PC2val(firmware *fw, cs_insn *insn)
 752 {
 753     uint32_t *p=LDR_PC2valptr(fw,insn);
 754     if(p) {
 755         return *p;
 756     }
 757     return 0;
 758 }
 759 
 760 // return value loaded by PC relative LDR pc..., or 0 if not matching or out of range
 761 uint32_t LDR_PC_PC_target(firmware *fw, cs_insn *insn)
 762 {
 763     if(!isLDR_PC_PC(insn)) {
 764         return 0;
 765     }
 766     return LDR_PC2val(fw,insn);
 767 }
 768 
 769 // return the target of B instruction, or 0 if current instruction isn't BL
 770 uint32_t B_target(firmware *fw, cs_insn *insn)
 771 {
 772     if(insn->id == ARM_INS_B) {
 773         return insn->detail->arm.operands[0].imm;
 774     }
 775     return 0; // TODO could be valid
 776 }
 777 
 778 
 779 // return the target of CBZ / CBNZ instruction, or 0 if current instruction isn't CBx
 780 uint32_t CBx_target(firmware *fw, cs_insn *insn)
 781 {
 782     if(insn->id == ARM_INS_CBZ || insn->id == ARM_INS_CBNZ) {
 783         return insn->detail->arm.operands[1].imm;
 784     }
 785     return 0; // TODO could be valid
 786 }
 787 
 788 // return the target of BLX instruction, or 0 if current instruction isn't BLX imm
 789 uint32_t BLXimm_target(firmware *fw, cs_insn *insn)
 790 {
 791     if(insn->id == ARM_INS_BLX && insn->detail->arm.operands[0].type == ARM_OP_IMM) {
 792         return insn->detail->arm.operands[0].imm;
 793     }
 794     return 0; // TODO could be valid
 795 }
 796 
 797 
 798 // return the target of BL instruction, or 0 if current instruction isn't BL
 799 uint32_t BL_target(firmware *fw, cs_insn *insn)
 800 {
 801     if(insn->id == ARM_INS_BL) {
 802         return insn->detail->arm.operands[0].imm;
 803     }
 804     return 0; // TODO could be valid
 805 }
 806 
 807 // as above, but also including B for tail calls
 808 uint32_t B_BL_target(firmware *fw, cs_insn *insn)
 809 {
 810     if(insn->id == ARM_INS_B || insn->id == ARM_INS_BL) {
 811         return insn->detail->arm.operands[0].imm;
 812     }
 813     return 0; // TODO could be valid
 814 }
 815 
 816 //
 817 // as above, but also including BLX imm
 818 uint32_t B_BL_BLXimm_target(firmware *fw, cs_insn *insn)
 819 {
 820     if(insn->id == ARM_INS_B
 821         || insn->id == ARM_INS_BL
 822         || (insn->id == ARM_INS_BLX && insn->detail->arm.operands[0].type == ARM_OP_IMM)) {
 823         return insn->detail->arm.operands[0].imm;
 824     }
 825     return 0; // TODO could be valid
 826 }
 827 
 828 // get the (likely) range of jumptable entries from a pc relative TBB or TBH instruction
 829 // returns 0 on error or if instruction is not TBB/TBH
 830 // returns 1 if instruction is TBB/TBH [PC,...] 
 831 int get_TBx_PC_info(firmware *fw,iter_state_t *is, tbx_info_t *ti)
 832 {
 833     if(!(is->insn->id == ARM_INS_TBH || is->insn->id == ARM_INS_TBB) || is->insn->detail->arm.operands[0].mem.base != ARM_REG_PC) {
 834         return 0;
 835     }
 836     ti->start=(uint32_t)is->adr; // after current instruction
 837     ti->first_target=0;
 838     ti->bytes=(is->insn->id == ARM_INS_TBH)?2:1;
 839 
 840     uint32_t max_adr;
 841     // max possible (assuming jumptable is contiguous)
 842     if(ti->bytes==1) {
 843         max_adr=ti->start+(2*255);
 844     } else {
 845         max_adr=ti->start+(2*65535);
 846     }
 847     arm_reg i_reg=is->insn->detail->arm.operands[0].mem.index;
 848     // backtrack looking for
 849     // cmp index reg,#imm
 850     // ...
 851     // bhs ...
 852     int max_backtrack = 8;
 853     if(is->ah.count - 1 < max_backtrack) {
 854         max_backtrack = is->ah.count-1;
 855     }
 856 
 857     int max_count=0;
 858     int found_bhs=0;
 859     int i;
 860     for(i=1;i<=max_backtrack;i++) {
 861         fw_disasm_iter_single(fw,adr_hist_get(&is->ah,i)); // thumb state comes from hist
 862         if(fw->is->insn->id == ARM_INS_B && fw->is->insn->detail->arm.cc == ARM_CC_HS) {
 863             found_bhs=1;
 864             continue;
 865         }
 866         // TODO lots of other ways condition code or reg could be changed in between
 867         if(found_bhs && fw->is->insn->id == ARM_INS_CMP) {
 868             // cmp with correct operands, assume number of jumptable entries
 869             if(fw->is->insn->detail->arm.operands[0].reg == i_reg 
 870                 || fw->is->insn->detail->arm.operands[1].type == ARM_OP_IMM) {
 871                 max_count = fw->is->insn->detail->arm.operands[1].imm;
 872             }
 873             // otherwise, give up
 874             break;
 875         }
 876     }
 877     if(max_count) {
 878         max_adr = ti->start+max_count*ti->bytes;
 879         //printf("get_TBx_PC_info: max_count %d start 0x%08x max_adr=0x%08x\n",max_count,ti->start,max_adr);
 880     }
 881     uint32_t adr=ti->start;
 882     while(adr < max_adr) {
 883         uint8_t *p=adr2ptr(fw,adr);
 884         if(!p) {
 885             fprintf(stderr,"get_TBx_PC_info: jumptable outside of valid address range at 0x%08x\n",adr);
 886             return 0;
 887         }
 888         uint16_t off;
 889         if(ti->bytes==1) {
 890             off=(uint16_t)*p;
 891         } else {
 892             off=*(uint16_t *)p;
 893         }
 894 
 895         // 0, probably padding at the end (could probably break here)
 896         // note shouldn't be padding on tbh, since aligned for thumb
 897         if(!off) {
 898             break;
 899         }
 900         uint32_t target = ti->start+2*off;
 901         // may indicate non-jumptable entry, if count not found, so don't increment adr
 902         if(target <= adr) {
 903             fprintf(stderr,"get_TBx_PC_info: jumptable target 0x%08x inside jumptable %d at 0x%08x\n",target,off,adr);
 904             break;
 905         }
 906         if(!ti->first_target || target < ti->first_target) {
 907             ti->first_target=target;
 908             if(target < max_adr) {
 909                 max_adr=target; // assume jump table ends at/before first target
 910             }
 911         }
 912         adr+=ti->bytes;
 913     }
 914     // if found count, assume it's right
 915     if(max_count) {
 916         ti->count=max_count;
 917     } else {
 918         // otherwise, use final address
 919         ti->count=(adr-ti->start)/ti->bytes;
 920     }
 921     return 1;
 922 }
 923 
 924 // TODO should have variants of above including LDR pc, [pc, #x] for some of the above
 925 
 926 // ****** disassembly iterator utilities ******
 927 // allocate a new iterator state, optionally initializing at adr (0/invalid OK)
 928 iter_state_t *disasm_iter_new(firmware *fw, uint32_t adr)
 929 {
 930     iter_state_t *is=(iter_state_t *)malloc(sizeof(iter_state_t));
 931     // it doesn't currently appear to matter which handle is used to allocate
 932     // only used for overridable malloc functions and error reporting
 933     is->insn=cs_malloc(fw->cs_handle_arm);
 934     disasm_iter_init(fw,is,adr);
 935     return is;
 936 }
 937 
 938 // free iterator state and associated resources
 939 void disasm_iter_free(iter_state_t *is)
 940 {
 941     cs_free(is->insn,1);
 942     free(is);
 943     return;
 944 }
 945 
 946 // set iterator to adr, without clearing history (for branch following)
 947 // thumb bit in adr sets mode
 948 int disasm_iter_set(firmware *fw, iter_state_t *is, uint32_t adr)
 949 {
 950     // set handle based on thumb bit to allow disassembly
 951     if(ADR_IS_THUMB(adr)) {
 952         is->cs_handle=fw->cs_handle_thumb;
 953         is->thumb=1;
 954         is->insn_min_size=2;
 955         adr=ADR_CLEAR_THUMB(adr);// ADR used for iteration must not contain thumb bit
 956     } else {
 957         is->cs_handle=fw->cs_handle_arm;
 958         is->thumb=0;
 959         is->insn_min_size=4;
 960         if(!ADR_IS_ALIGN4(adr)) {
 961             fprintf(stderr,"disasm_iter_set: unaligned ARM address 0x%08x\n",adr);
 962             is->code=NULL;
 963             is->size=0;
 964             is->adr=0;
 965             return 0;
 966         }
 967     }
 968     uint8_t *p=adr2ptr(fw,adr);
 969     if(!p) {
 970 // TODO invalid currently allowed, for new
 971 //        fprintf(stderr,"disasm_iter_set: bad address 0x%08x\n",adr);
 972         is->code=NULL; // make first iter fail
 973         is->size=0;
 974         is->adr=0;
 975         return 0;
 976     }
 977     // TODO should maybe mark is.insn invalid?
 978     is->code=p;
 979     is->size=fw->size8 - (p-fw->buf8);
 980     is->adr=adr;
 981     return 1;
 982 }
 983 
 984 // initialize iterator state at adr, clearing history
 985 int disasm_iter_init(firmware *fw, iter_state_t *is, uint32_t adr)
 986 {
 987     adr_hist_reset(&is->ah);
 988     return disasm_iter_set(fw,is,adr);
 989 }
 990 
 991 // disassemble next instruction, recording address in history
 992 // returns false if state invalid or disassembly fails
 993 // if disassembly fails, is->adr is not incremented
 994 int disasm_iter(firmware *fw, iter_state_t *is)
 995 {
 996     // iter_start not called or invalid
 997     if(!is->code) {
 998         return 0;
 999     }
1000     adr_hist_add(&is->ah,(uint32_t)is->adr | is->thumb); // record thumb state to allow backtracking through state changes
1001     return cs_disasm_iter(is->cs_handle, &is->code, &is->size, &is->adr, is->insn);
1002 }
1003 
1004 // re-disassemble the current instruction
1005 // could be useful if turning detail off/on but doesn't seem to help perf much
1006 // NOTE out of date
1007 #if 0
1008 int disasm_iter_redo(firmware *fw,iter_state_t *is) {
1009     if(!is->code || !is->ah.count) {
1010         return 0;
1011     }
1012     is->code -= is->insn->size;
1013     is->adr -= is->insn->size;
1014     is->size += is->insn->size;
1015     // call iter directly, to avoid touching history
1016     return cs_disasm_iter(is->cs_handle, &is->code, &is->size, &is->adr, is->insn);
1017 }
1018 #endif
1019 
1020 // ***** disassembly utilities operating on the default iterator state *****
1021 /*
1022 initialize iter state to begin iterating at adr
1023 history is cleared
1024 */
1025 int fw_disasm_iter_start(firmware *fw, uint32_t adr)
1026 {
1027     return disasm_iter_init(fw,fw->is,adr);
1028 }
1029 
1030 // disassemble the next instruction, updating cached state
1031 int fw_disasm_iter(firmware *fw)
1032 {
1033     return disasm_iter(fw,fw->is);
1034 }
1035 
1036 // disassemble single instruction at given adr, updating cached values
1037 // history is cleared
1038 int fw_disasm_iter_single(firmware *fw, uint32_t adr)
1039 {
1040     fw_disasm_iter_start(fw,adr);
1041     return fw_disasm_iter(fw);
1042 }
1043 
1044 
1045 // ****** standalone disassembly without an iter_state ******
1046 /*
1047 disassemble up to count instructions starting at firmware address adr
1048 allocates and returns insns in insn, can be freed with cs_free(insn, count)
1049 */
1050 #if 0
1051 size_t fw_disasm_adr(firmware *fw, uint32_t adr, unsigned count, cs_insn **insn)
1052 {
1053     uint8_t *p=adr2ptr(fw,adr);
1054     if(!p) {
1055         *insn=NULL; // ?
1056         return 0;
1057     }
1058     return cs_disasm(fw->cs_handle, p, fw->size8 - (p-fw->buf8), adr, count, insn);
1059 }
1060 #endif
1061 
1062 // ***** utilities for searching disassembly over large ranges ******
1063 /*
1064 iterate over firmware disassembling, calling callback described above after each
1065 successful disassembly iteration.  If disassembly fails, the iter state is advanced
1066 minimum instruction size without calling the callback.
1067 starts at address is taken from the iter_state, which should be initialized with
1068 disasm_iter_new(), disasm_iter_init(), or a previous search or iter call.
1069 end defaults to end of ram code or rom code (before init data, if known), based on start
1070 v1 and udata are provided to the callback
1071 */
1072 uint32_t fw_search_insn(firmware *fw, iter_state_t *is, search_insn_fn f, uint32_t v1, void *udata, uint32_t adr_end)
1073 {
1074     uint32_t adr_start=is->adr;
1075     adr_range_t *r_start=adr_get_range(fw,adr_start);
1076     if(!r_start) {
1077         fprintf(stderr,"fw_search_insn: invalid start address 0x%08x\n",adr_start);
1078         return 0;
1079     }
1080 
1081     // default to end of start range
1082     if(!adr_end) {
1083         if(r_start->type == ADR_RANGE_ROM) {
1084             adr_end = fw->rom_code_search_max_adr;
1085         } else {
1086             adr_end=r_start->start + r_start->bytes - is->insn_min_size;
1087         }
1088     }
1089     adr_range_t *r_end=adr_get_range(fw,adr_end);
1090 
1091     if(!r_end) {
1092         fprintf(stderr,"fw_search_insn: invalid end address 0x%08x\n",adr_end);
1093         return 0;
1094     }
1095     // ignore thumb bit on end adr
1096     adr_end=ADR_CLEAR_THUMB(adr_end);
1097 
1098     if((r_start != r_end) || (adr_end < adr_start)) {
1099         fprintf(stderr,"fw_search_insn: invalid address range 0x%08x 0x%08x\n",adr_start,adr_end);
1100         return 0;
1101     }
1102 
1103     uint32_t adr=adr_start;
1104     // don't bother with buf ranges for RAM code
1105     if(r_start->type != ADR_RANGE_ROM) {
1106         while(adr < adr_end) {
1107             if(disasm_iter(fw,is)) {
1108                 uint32_t r=f(fw,is,v1,udata);
1109                 if(r) {
1110                     return r;
1111                 }
1112                 adr=(uint32_t)is->adr; // adr was updated by iter or called sub
1113             } else {
1114                 // disassembly failed
1115                 // increment by minimum instruction size and re-init
1116                 adr=adr+is->insn_min_size;
1117                 if(!disasm_iter_init(fw,is,adr|is->thumb)) {
1118                     fprintf(stderr,"fw_search_insn: disasm_iter_init failed\n");
1119                     return 0;
1120                 }
1121              }
1122         }
1123         return 0;
1124     }
1125     BufRange *br=fw->br;
1126     // TODO might want to (optionally?) turn off details? For now, caller can set, doesn't seem to help perf much
1127     // TODO when searching ROM, could skip over RAM copied areas (currently just limit default range)
1128     while(br && adr < adr_end) {
1129         uint32_t *p_adr=(uint32_t *)adr2ptr(fw,(uint32_t)adr);
1130         uint32_t *br_end = br->p + br->len;
1131         uint32_t adr_chunk_end = ptr2adr(fw,(uint8_t*)br_end);
1132         if(adr_end < adr_chunk_end) {
1133             adr_chunk_end = adr_end;
1134         }
1135         // address is before start of current range, adjust
1136         if(p_adr < br->p) {
1137             adr=ptr2adr(fw,(uint8_t *)br->p);
1138             if(!disasm_iter_init(fw,is,(uint32_t)adr)) {
1139                 return 0;
1140             }
1141             p_adr=(uint32_t *)adr2ptr(fw,(uint32_t)adr);
1142         } 
1143         //printf("br:0x%08x-0x%08x\n",ptr2adr(fw,(uint8_t *)br->p),ptr2adr(fw,(uint8_t *)(br->p+br->len)));
1144         while(adr < adr_chunk_end) {
1145             if(disasm_iter(fw,is)) {
1146                 uint32_t r=f(fw,is,v1,udata);
1147                 if(r) {
1148                     return r;
1149                 }
1150                 adr=(uint32_t)is->adr; // adr was updated by iter or called sub
1151             } else {
1152                 // disassembly failed. cs_disarm_iter does not update address
1153                 // increment by half word and re-init
1154                 adr=adr+is->insn_min_size;
1155                 if(!disasm_iter_init(fw,is,adr|is->thumb)) {
1156                     fprintf(stderr,"fw_search_insn: disasm_iter_init failed\n");
1157                     return 0;
1158                 }
1159              }
1160         }
1161         // next range
1162         br=br->next;
1163     }
1164     return 0;
1165 }
1166 
1167 // ****** callbacks for use with fw_search_insn ******
1168 
1169 // search for constant references
1170 uint32_t search_disasm_const_ref(firmware *fw, iter_state_t *is, uint32_t val, void *unused)
1171 {
1172 //    printf("%"PRIx64" %s %s\n",is->insn->address,is->insn->mnemonic, is->insn->op_str);
1173     uint32_t av=ADRx2adr(fw,is->insn);
1174     if(av) {
1175 //        printf("adr 0x%08x\n",av);
1176         if(av == val) {
1177             return (uint32_t)is->insn->address;
1178         }
1179         return 0;
1180     }
1181     uint32_t *pv=LDR_PC2valptr(fw,is->insn);
1182     if(pv) {
1183 //        printf("ldr 0x%08x\n",*pv);
1184         if(*pv == val) {
1185             return (uint32_t)is->insn->address;
1186         }
1187     }
1188     return 0;
1189 }
1190 
1191 // search for string ref
1192 uint32_t search_disasm_str_ref(firmware *fw, iter_state_t *is, uint32_t val, void *udata)
1193 {
1194     const char *str=(const char *)udata;
1195 //    printf("%"PRIx64" %s %s\n",is->insn->address,is->insn->mnemonic, is->insn->op_str);
1196     uint32_t av=ADRx2adr(fw,is->insn);
1197     if(av) {
1198 //        printf("adr 0x%08x\n",av);
1199         char *cmp=(char *)adr2ptr_with_data(fw,av);
1200         if(cmp && (strcmp(cmp,str) == 0)) {
1201             return (uint32_t)is->insn->address;
1202         }
1203         return 0;
1204     }
1205     uint32_t *pv=LDR_PC2valptr(fw,is->insn);
1206     if(pv) {
1207 //        printf("ldr 0x%08x\n",*pv);
1208         char *cmp=(char *)adr2ptr_with_data(fw,*pv);
1209         if(cmp && (strcmp(cmp,str) == 0)) {
1210             return (uint32_t)is->insn->address;
1211         }
1212     }
1213     return 0;
1214 }
1215 
1216 // search for calls/jumps to immediate addresses
1217 // thumb bit in address should be set appropriately 
1218 // returns 1 if found, address can be obtained from insn
1219 uint32_t search_disasm_calls(firmware *fw, iter_state_t *is, uint32_t val, void *unused)
1220 {
1221     //printf("%"PRIx64" %s %s\n",is->insn->address,is->insn->mnemonic, is->insn->op_str);
1222     uint32_t sub=get_branch_call_insn_target(fw,is);
1223     if(sub) {
1224         if(sub == val) {
1225             return 1;
1226         }
1227     }
1228     return 0;
1229 }
1230 
1231 // a search_calls_multi_fn that just returns 1
1232 int search_calls_multi_end(firmware *fw, iter_state_t *is, uint32_t adr) {
1233     return 1;
1234 }
1235 
1236 
1237 // Search for calls to multiple functions (more efficient than multiple passes)
1238 // if adr is found in null terminated search_calls_multi_data array, returns fn return value
1239 // otherwise 0
1240 uint32_t search_disasm_calls_multi(firmware *fw, iter_state_t *is, uint32_t unused, void *userdata)
1241 {
1242     search_calls_multi_data_t *data=(search_calls_multi_data_t *)userdata;
1243     uint32_t sub=get_branch_call_insn_target(fw,is);
1244     if(sub) {
1245         while(data->adr) {
1246             if(data->adr == sub) {
1247                 return data->fn(fw,is,sub);
1248             }
1249             data++;
1250         }
1251     }
1252     return 0;
1253 }
1254 
1255 // ****** utilities for extracting register values ******
1256 /*
1257 backtrack through is_init state history picking up constants loaded into r0-r3
1258 return bitmask of regs with values found
1259 affects fw->is, does not affect is_init
1260 
1261 NOTE values may be inaccurate for many reasons, doesn't track all reg affecting ops,
1262 doesn't account for branches landing in the middle of inspected code
1263 doesn't account for many conditional cases
1264 */
1265 int get_call_const_args(firmware *fw, iter_state_t *is_init, int max_backtrack, uint32_t *res)
1266 {
1267     int i;
1268     /*
1269     static int dbg_count=0;
1270     if(is_init->insn->address==...) {
1271         dbg_count=1;
1272     } else {
1273         dbg_count=0;
1274     }
1275     */
1276 
1277     // init regs to zero (to support adds etc)
1278     for (i=0;i<4;i++) {
1279         res[i]=0;
1280     }
1281 
1282     // count includes current instruction (i.e. BL of call)
1283     if(is_init->ah.count <= 1) {
1284         return 0;
1285     }
1286     if(is_init->ah.count - 1 < max_backtrack) {
1287         /*
1288         if(dbg_count > 0) {
1289             printf("max_backtrack %d hist count %d\n",max_backtrack,is_init->ah.count);
1290         }
1291         */
1292         max_backtrack = is_init->ah.count-1;
1293     }
1294     uint32_t found_bits=0; // registers with known const values
1295     uint32_t known_bits=0; // registers with some value
1296 
1297     for(i=1;i<=max_backtrack && known_bits !=0xf;i++) {
1298         // TODO going backwards and calling start each time inefficient
1299         // forward could also find multi-instruction constants in some cases (e.g mov + add, movw + movt)
1300         fw_disasm_iter_single(fw,adr_hist_get(&is_init->ah,i)); // thumb state comes from hist
1301         /*
1302         if(dbg_count > 0) {
1303             printf("backtrack %d:%d  ",dbg_count,i);
1304             printf("%"PRIx64" %s %s\n",fw->is->insn->address,fw->is->insn->mnemonic, fw->is->insn->op_str);
1305         }
1306         */
1307         arm_insn insn_id = fw->is->insn->id;
1308         // BL, BLX etc will trash r0-r3
1309         // only break on unconditional - optimistic, could produce incorrect results
1310         if((insn_id == ARM_INS_BL || insn_id == ARM_INS_BLX 
1311             // B/BX could mean execution goes somewhere totally different, but in practice it often just skipping over a word of data...
1312              /*|| insn_id == ARM_INS_B || insn_id == ARM_INS_BX*/) 
1313              && fw->is->insn->detail->arm.cc == ARM_CC_AL) {
1314             break;
1315         }
1316         
1317         // if the first op isn't REG, continue
1318         // TODO lots of instructions could affect reg even if not first op
1319         if(fw->is->insn->detail->arm.operands[0].type != ARM_OP_REG) {
1320             continue;
1321         }
1322         arm_reg rd = fw->is->insn->detail->arm.operands[0].reg;
1323         // capstone arm.h regs enum R0-R12 are ordered
1324         // enum has entries before R0
1325         if(rd < ARM_REG_R0 || rd > ARM_REG_R3) {
1326             continue;
1327         }
1328 
1329         int rd_i = rd - ARM_REG_R0;
1330         uint32_t rd_bit = 1 << rd_i;
1331         // if we don't already have something for this reg
1332         if(!(known_bits & rd_bit)) {
1333             // know something has been done to this reg
1334             // note doesn't account for conditionals
1335             known_bits |=rd_bit;
1336             // is it an LDR
1337             uint32_t *pv=LDR_PC2valptr(fw,fw->is->insn);
1338             if(pv) {
1339                 res[rd_i] += *pv;
1340 //                if(dbg_count) printf("found ldr r%d,=0x%08x\n",rd_i,res[rd_i]);
1341                 found_bits |=rd_bit;
1342                 continue;
1343             }
1344             uint32_t v=ADRx2adr(fw,fw->is->insn); // assumes ADR doesn't generate 0, probably safe
1345             if(v) {
1346                 res[rd_i] += v;
1347 //                 if(dbg_count) printf("found adrx r%d,0x%08x\n",rd_i,res[rd_i]);
1348                 found_bits |=rd_bit;
1349                 continue;
1350             }
1351             // immediate MOV note MOVT combinations, not accounted for, some handled ADDs below
1352             if( IS_INSN_ID_MOVx(insn_id)
1353                 && fw->is->insn->detail->arm.operands[1].type == ARM_OP_IMM) {
1354                 res[rd_i] += fw->is->insn->detail->arm.operands[1].imm;
1355 //                if(dbg_count) printf("found move r%d,#0x%08x\n",rd_i,res[rd_i]);
1356                 found_bits |=rd_bit;
1357             } else if(isADDx_imm(fw->is->insn)) {
1358                 res[rd_i] += fw->is->insn->detail->arm.operands[1].imm;
1359 //                if(dbg_count) printf("found add r%d,#0x%08x\n",rd_i,res[rd_i]);
1360                 // pretend reg is not known
1361                 known_bits ^=rd_bit;
1362                 // do not set found bit here
1363             } else if(isSUBx_imm(fw->is->insn)) {
1364                 res[rd_i] = (int)(res[rd_i]) - fw->is->insn->detail->arm.operands[1].imm;
1365 //                if(dbg_count) printf("found add r%d,#0x%08x\n",rd_i,res[rd_i]);
1366                 // pretend reg is not known
1367                 known_bits ^=rd_bit;
1368                 // do not set found bit here
1369             }/* else {
1370             }
1371             */
1372         }
1373     }
1374 //    if(dbg_count) printf("get_call_const_args found 0x%08x\n",found_bits);
1375     return found_bits;
1376 }
1377 
1378 /*
1379 starting from is_init, look for a direct jump, such as
1380  B <target>
1381  LDR PC, [pc, #x]
1382  movw ip, #x
1383  movt ip, #x
1384  bx ip
1385 if found, return target address with thumb bit set appropriately
1386 NOTE does not check for conditional
1387 uses fw->is
1388 does not check CBx, since it would generally be part of a function not a veneer
1389 */
1390 uint32_t get_direct_jump_target(firmware *fw, iter_state_t *is_init)
1391 {
1392     uint32_t adr=B_target(fw,is_init->insn);
1393     // B ... return with thumb set to current mode
1394     if(adr) {
1395         return (adr | is_init->thumb);
1396     }
1397     adr=LDR_PC_PC_target(fw,is_init->insn);
1398     // LDR pc #... thumb is set in the loaded address
1399     if(adr) {
1400         return adr;
1401     }
1402     // an immediate move to ip (R12), candidate for multi-instruction veneer
1403     if((is_init->insn->id == ARM_INS_MOV || is_init->insn->id == ARM_INS_MOVW) 
1404         && is_init->insn->detail->arm.operands[0].reg == ARM_REG_IP
1405         && is_init->insn->detail->arm.operands[1].type == ARM_OP_IMM) {
1406         adr = is_init->insn->detail->arm.operands[1].imm;
1407         // iter in default state, starting from is_init
1408         if(!fw_disasm_iter_single(fw,is_init->adr | is_init->thumb)) {
1409             fprintf(stderr,"get_direct_jump_target: disasm single failed at 0x%"PRIx64"\n",fw->is->insn->address);
1410             return 0;
1411         }
1412         // check for MOVT ip, #x
1413         if(!(fw->is->insn->id == ARM_INS_MOVT
1414             && fw->is->insn->detail->arm.operands[0].reg == ARM_REG_IP
1415             && fw->is->insn->detail->arm.operands[1].type == ARM_OP_IMM)) {
1416 // doesn't match second two insn veneer, not really an arror
1417 //            fprintf(stderr,"get_direct_jump_target: not 2 insn ip veneer 0x%"PRIx64"\n",fw->is->insn->address);
1418             return 0;
1419         }
1420         // thumb set in loaded adr
1421         adr = (fw->is->insn->detail->arm.operands[1].imm << 16) | (adr&0xFFFF);
1422         if(!fw_disasm_iter(fw)) {
1423             fprintf(stderr,"get_direct_jump_target: disasm 2 failed at 0x%"PRIx64"\n",fw->is->insn->address);
1424             return 0;
1425         }
1426         // BX ip ?
1427         if(fw->is->insn->id == ARM_INS_BX
1428             && fw->is->insn->detail->arm.operands[0].type == ARM_OP_REG
1429             && fw->is->insn->detail->arm.operands[0].reg == ARM_REG_IP) {
1430             return adr;
1431         }
1432     }
1433     return 0;
1434 }
1435 
1436 /*
1437 return target of any single instruction branch or function call instruction, 
1438 with thumb bit set appropriately
1439 returns 0 if current instruction not branch/call
1440 */
1441 uint32_t get_branch_call_insn_target(firmware *fw, iter_state_t *is)
1442 {
1443     uint32_t adr=B_BL_target(fw,is->insn);
1444     if(adr) {
1445         return (adr | is->thumb);
1446     }
1447     // CBx only exists in thumb
1448     if(is->thumb) {
1449         adr=CBx_target(fw,is->insn);
1450         if(adr) {
1451             return ADR_SET_THUMB(adr);
1452         }
1453     }
1454 
1455     adr=BLXimm_target(fw,is->insn);
1456     if(adr) {
1457         if(is->thumb) {
1458             return adr;
1459         } else {
1460             return adr | is->thumb;
1461         }
1462     }
1463 
1464     adr=LDR_PC_PC_target(fw,is->insn);
1465     if(adr) {
1466         return adr;
1467     }
1468     return 0;
1469 }
1470 
1471 /*
1472 search up to max_search_ins for first LDR, =value
1473 and then match up to max_seq_insns for a sequence like
1474 LDR Rbase,=adr
1475 ... possible intervening ins
1476 SUB Rbase,#adj // optional, may be any add/sub variant
1477 ... possible intervening ins
1478 LDR Rval,[Rbase + #off]
1479 
1480 returns 1 if found, 0 if not
1481 stores registers and constants in *result if successful
1482 
1483 NOTE bad values are possible with intervening ins, short sequences recommended
1484 
1485 TODO similar code for STR would be useful, but in many cases would have to handle load or move into reg_val
1486 */
1487 int find_and_get_var_ldr(firmware *fw,
1488                             iter_state_t *is,
1489                             int max_search_insns,
1490                             int max_seq_insns,
1491                             arm_reg match_val_reg, // ARM_REG_INVALID for any
1492                             var_ldr_desc_t *result)
1493 
1494 {
1495     if(!insn_match_find_next(fw,is,max_search_insns,match_ldr_pc)) {
1496         // printf("find_and_get_var_ldr: LDR PC not found\n");
1497         return 0;
1498     }
1499     var_ldr_desc_t r;
1500     memset(&r,0,sizeof(r));
1501     r.reg_base=is->insn->detail->arm.operands[0].reg;
1502     r.adr_base=LDR_PC2val(fw,is->insn);
1503     int seq_count=1;
1504 
1505     while(seq_count < max_seq_insns) {
1506         // disassembly failed, no match (could ignore..)
1507         if(!disasm_iter(fw,is)) {
1508             return 0;
1509         }
1510         // assume first encountered LDR x,[pc] is the one to use
1511         // give up if we encounter another. Don't know beforehand which reg is base
1512         // NOTE: backward search would allow matching base that eventually ends up in desired reg
1513         if(isLDR_PC(is->insn)) {
1514             // printf("find_and_get_var_ldr: second ldr pc\n");
1515             return  0;
1516         }
1517         seq_count++;
1518         // firmware may use add/sub to get actual firmware base address
1519         if(isADDx_imm(is->insn) || isSUBx_imm(is->insn)) {
1520             if(is->insn->detail->arm.operands[0].reg != r.reg_base) {
1521                 continue;
1522             }
1523             if(isADDx_imm(is->insn)) {
1524                 r.adj=is->insn->detail->arm.operands[1].imm;
1525             } else {
1526                 r.adj=-is->insn->detail->arm.operands[1].imm;
1527             }
1528             if(!disasm_iter(fw,is)) {
1529                 return 0;
1530             }
1531             seq_count++;
1532         } else {
1533             r.adj=0;
1534         }
1535         // try to bail out if base reg trashed 
1536         // BL, BLX etc will trash r0-r3, B, BX go somewhere else
1537         // only break on unconditional - optimistic, could produce incorrect results
1538         // can't account for branches into searched code
1539         if((r.reg_base >= ARM_REG_R0 && r.reg_base <= ARM_REG_R3)
1540                 && (is->insn->id == ARM_INS_BL || is->insn->id == ARM_INS_BLX 
1541                     || is->insn->id == ARM_INS_B || is->insn->id == ARM_INS_BX)
1542                 && is->insn->detail->arm.cc == ARM_CC_AL) {
1543             // printf("find_and_get_var_ldr: bail B*\n");
1544             return 0;
1545         }
1546         if(is->insn->id != ARM_INS_LDR || is->insn->detail->arm.operands[1].reg != r.reg_base) {
1547             // other operation on with base reg as first operand, give up
1548             // simplistic, many other things could affect reg
1549             if(is->insn->detail->arm.operands[0].type == ARM_OP_REG && is->insn->detail->arm.operands[0].reg == r.reg_base) {
1550                 // printf("find_and_get_var_ldr: bail mod base\n");
1551                 return 0;
1552             }
1553             continue;
1554         }
1555         r.reg_val = is->insn->detail->arm.operands[0].reg;
1556         if(match_val_reg != ARM_REG_INVALID && (r.reg_val != match_val_reg)) {
1557             continue;
1558         }
1559         r.off = is->insn->detail->arm.operands[1].mem.disp;
1560         r.adr_adj = r.adr_base + r.adj;
1561         r.adr_final = r.adr_adj + r.off;
1562         memcpy(result,&r,sizeof(r));
1563         return 1;
1564     }
1565     return 0;
1566 }
1567 
1568 /*
1569 check for, and optionally return information about 
1570 functions with return values that can be completely determined
1571 from disassembly
1572 uses fw->is
1573 */
1574 // constants below may  as flags on input, and as return valaue
1575 // no simple function found
1576 #define MATCH_SIMPLE_FUNC_NONE    0x0
1577 // immediately returns, with no value
1578 #define MATCH_SIMPLE_FUNC_NULLSUB 0x1
1579 // immediately returns with a MOV constant
1580 #define MATCH_SIMPLE_FUNC_IMM     0x2
1581 // TODO LDR pc, =const,  ADR
1582 // TODO could also do pointer derefs and return pointer info without val
1583 #define MATCH_SIMPLE_FUNC_ANY     0x3
1584 int check_simple_func(firmware *fw, uint32_t adr, int match_ftype, simple_func_desc_t *info)
1585 {
1586     const insn_match_t match_mov_r0_imm[]={
1587         {MATCH_INS(MOV,   2),  {MATCH_OP_REG(R0),  MATCH_OP_IMM_ANY}},
1588 #if CS_API_MAJOR < 4
1589         {MATCH_INS(MOVS,  2),  {MATCH_OP_REG(R0),  MATCH_OP_IMM_ANY}},
1590 #endif
1591         {ARM_INS_ENDING}
1592     };
1593 
1594     int found = 0;
1595     int found_val = 0;
1596     if(info) {
1597         info->ftype = MATCH_SIMPLE_FUNC_NONE;
1598         info->retval = 0;
1599     }
1600     if(!fw_disasm_iter_single(fw,adr)) {
1601         //fprintf(stderr,"check_simple_func: disasm_iter_single failed 0x%x\n",adr);
1602         return 0;
1603     }
1604     if(match_ftype & MATCH_SIMPLE_FUNC_IMM) {
1605         // check mov r0, #imm
1606         if(insn_match_any(fw->is->insn,match_mov_r0_imm)) {
1607             found_val = fw->is->insn->detail->arm.operands[1].imm; 
1608             found = MATCH_SIMPLE_FUNC_IMM;
1609             // fprintf(stderr,"check_simple_func: found IMM\n");
1610             if(!fw_disasm_iter(fw)) {
1611                 //fprintf(stderr,"check_simple_func: disasm_iter failed 0x%x\n",adr);
1612                 return 0;
1613             }
1614         }
1615     }
1616     if(!isRETx(fw->is->insn)) {
1617         // fprintf(stderr,"check_simple_func: no ret\n");
1618         return 0;
1619     }
1620     // no previous found, check if ret alone
1621     if(!found && (match_ftype & MATCH_SIMPLE_FUNC_NULLSUB)) {
1622         found = MATCH_SIMPLE_FUNC_NULLSUB;
1623         // fprintf(stderr,"check_simple_func: found nullsub\n");
1624     }
1625     if(found) {
1626         if(info) {
1627             info->ftype = found;
1628             info->retval = found_val;
1629         }
1630     }
1631     return found;
1632 }
1633 
1634 /*
1635 advance is trying to find the last function called by a function
1636 function assumed to push lr, pop lr or pc (many small functions don't!)
1637 either the last bl/blximmg before pop {... pc}
1638 or b after pop {... lr}
1639 after min_insns up to max_insns
1640 */
1641 uint32_t find_last_call_from_func(firmware *fw, iter_state_t *is,int min_insns, int max_insns)
1642 {
1643     int push_found=0;
1644     uint32_t last_adr=0;
1645     int count;
1646     for(count=0; count < max_insns; count++) {
1647         if(!disasm_iter(fw,is)) {
1648             fprintf(stderr,"find_last_call_from_func: disasm failed 0x%"PRIx64"\n",is->adr);
1649             return 0;
1650         }
1651         // TODO could match push regs with pop
1652         if(isPUSH_LR(is->insn)) {
1653             // already found a PUSH LR, probably in new function
1654             if(push_found) {
1655                 //printf("find_last_call_from_func: second push pc 0x%"PRIx64"\n",is->adr);
1656                 return 0;
1657             }
1658             push_found=1;
1659             continue;
1660         }
1661         // ignore everything before push (could be some mov/ldr, shoudln't be any calls)
1662         // TODO may want to allow starting in the middle of a function
1663         if(!push_found) {
1664             continue;
1665         }
1666         // found a potential call, store
1667         if(insn_match_any(is->insn,match_bl_blximm) && count >= min_insns) {
1668             //printf("find_last_call_from_func: found call 0x%"PRIx64"\n",is->adr);
1669             last_adr=get_branch_call_insn_target(fw,is);
1670             continue;
1671         }
1672         // found pop PC, can only be stored call if present
1673         if(isPOP_PC(is->insn)) {
1674             // printf("find_last_call_from_func: found pop PC 0x%"PRIx64"\n",is->adr);
1675             if(last_adr) {
1676                 return last_adr;
1677             }
1678             // no call found, or not found within min
1679             return 0;
1680         }
1681         // found pop LR, check if next is unconditional B
1682         if(isPOP_LR(is->insn)) {
1683             // hit func end with less than min, no match
1684             if(count < min_insns) {
1685                 // printf("find_last_call_from_func: pop before min 0x%"PRIx64"\n",is->adr);
1686                 return 0;
1687             }
1688             if(!disasm_iter(fw,is)) {
1689                 fprintf(stderr,"find_last_call_from_func: disasm failed 0x%"PRIx64"\n",is->adr);
1690                 return 0;
1691             }
1692             if(is->insn->id == ARM_INS_B && is->insn->detail->arm.cc == ARM_CC_AL) {
1693                 return get_branch_call_insn_target(fw,is);
1694             }
1695             // doen't go more than one insn after pop (could be more, but uncommon)
1696             // printf("find_last_call_from_func: more than one insn after pop 0x%"PRIx64"\n",is->adr);
1697             return 0;
1698         }
1699         // found another kind of ret, give up
1700         if(isRETx(is->insn)) {
1701             // printf("find_last_call_from_func: other ret 0x%"PRIx64"\n",is->adr);
1702             return 0;
1703         }
1704     }
1705     // printf("find_last_call_from_func: no match in range 0x%"PRIx64"\n",is->adr);
1706     return 0;
1707 }
1708 
1709 // ****** utilities for matching instructions and instruction sequences ******
1710 
1711 // some common matches for insn_match_find_next
1712 const insn_match_t match_b[]={
1713     {MATCH_INS(B,   MATCH_OPCOUNT_IGNORE)},
1714     {ARM_INS_ENDING}
1715 };
1716 const insn_match_t match_bl[]={
1717     {MATCH_INS(BL,  MATCH_OPCOUNT_IGNORE)},
1718     {ARM_INS_ENDING}
1719 };
1720 const insn_match_t match_b_bl[]={
1721     {MATCH_INS(B,   MATCH_OPCOUNT_IGNORE)},
1722     {MATCH_INS(BL,  MATCH_OPCOUNT_IGNORE)},
1723     {ARM_INS_ENDING}
1724 };
1725 
1726 const insn_match_t match_b_bl_blximm[]={
1727     {MATCH_INS(B,   MATCH_OPCOUNT_IGNORE)},
1728     {MATCH_INS(BL,  MATCH_OPCOUNT_IGNORE)},
1729     {MATCH_INS(BLX, 1), {MATCH_OP_IMM_ANY}},
1730     {ARM_INS_ENDING}
1731 };
1732 
1733 const insn_match_t match_bl_blximm[]={
1734     {MATCH_INS(BL,  MATCH_OPCOUNT_IGNORE)},
1735     {MATCH_INS(BLX, 1), {MATCH_OP_IMM_ANY}},
1736     {ARM_INS_ENDING}
1737 };
1738 
1739 const insn_match_t match_bxlr[]={
1740     {MATCH_INS(BX, 1), {MATCH_OP_REG(LR)}},
1741     {ARM_INS_ENDING}
1742 };
1743 
1744 const insn_match_t match_ldr_pc[]={
1745     {MATCH_INS(LDR, 2), {MATCH_OP_REG_ANY,  MATCH_OP_MEM_BASE(PC)}},
1746     {ARM_INS_ENDING}
1747 };
1748 
1749 // iterate as long as sequence of instructions matches sequence defined in match
1750 int insn_match_seq(firmware *fw, iter_state_t *is, const insn_match_t *match)
1751 {
1752     //printf("%"PRIx64" insn_match_seq %s %s\n",is->insn->address,is->insn->mnemonic,is->insn->op_str);
1753     while(match->id != ARM_INS_ENDING && disasm_iter(fw,is) && insn_match(is->insn,match)) {
1754         //printf("%"PRIx64" insn_match_seq next %s %s\n",is->insn->address,is->insn->mnemonic,is->insn->op_str);
1755         match++;
1756     }
1757     return (match->id == ARM_INS_ENDING);
1758 }
1759 
1760 // check if single insn matches values defined by match
1761 int insn_match(cs_insn *insn,const insn_match_t *match)
1762 {
1763     // specific instruction ID requested, check
1764     if(match->id != ARM_INS_INVALID && insn->id != match->id) {
1765         return 0;
1766     }
1767     // condition code requested, check
1768     if(match->cc != ARM_CC_INVALID && insn->detail->arm.cc != match->cc) {
1769         return 0;
1770     }
1771     // no op checks, done
1772     if(match->op_count == MATCH_OPCOUNT_IGNORE) {
1773         return 1;
1774     }
1775     // operand count requested, check
1776     if(match->op_count >= 0 && insn->detail->arm.op_count != match->op_count) {
1777         return 0;
1778     }
1779     int i;
1780     // operands
1781     for(i=0;i<MATCH_MAX_OPS && i < insn->detail->arm.op_count; i++) {
1782         // specific type requested?
1783         if(match->operands[i].type != ARM_OP_INVALID && insn->detail->arm.operands[i].type != match->operands[i].type) {
1784             return 0;
1785         }
1786         // specific registers requested?
1787         if(match->operands[i].reg1 != ARM_REG_INVALID) {
1788             if(insn->detail->arm.operands[i].type == ARM_OP_REG) {
1789                 if(insn->detail->arm.operands[i].reg != match->operands[i].reg1) {
1790                     return 0;
1791                 }
1792             } else if(insn->detail->arm.operands[i].type == ARM_OP_MEM) {
1793                 if(insn->detail->arm.operands[i].mem.base != match->operands[i].reg1) {
1794                     return 0;
1795                 }
1796             } else {
1797                 fprintf(stderr,"insn_match: reg1 match requested on operand not reg or mem %d\n",
1798                         insn->detail->arm.operands[i].type);
1799             }
1800         }
1801         if(match->operands[i].reg2 != ARM_REG_INVALID) {
1802             if(insn->detail->arm.operands[i].type == ARM_OP_MEM) {
1803                 if(insn->detail->arm.operands[i].mem.index != match->operands[i].reg2) {
1804                     return 0;
1805                 }
1806             } else {
1807                 fprintf(stderr,"insn_match: reg2 match requested on operand not reg or mem %d\n",
1808                         insn->detail->arm.operands[i].type);
1809             }
1810         }
1811         if(match->operands[i].flags & MATCH_OP_FL_IMM) {
1812             if(insn->detail->arm.operands[i].type == ARM_OP_IMM
1813                     || insn->detail->arm.operands[i].type == ARM_OP_PIMM
1814                     || insn->detail->arm.operands[i].type == ARM_OP_CIMM) {
1815                 if(insn->detail->arm.operands[i].imm != match->operands[i].imm) {
1816                     return  0;
1817                 }
1818             } else if(insn->detail->arm.operands[i].type == ARM_OP_MEM) {
1819                 if(insn->detail->arm.operands[i].mem.disp != match->operands[i].imm) {
1820                     return  0;
1821                 }
1822             } else {
1823                 fprintf(stderr,"insn_match: imm match requested on operand not imm or mem %d\n",
1824                         insn->detail->arm.operands[i].type);
1825             }
1826         }
1827         if(match->operands[i].flags & MATCH_OP_FL_LAST) {
1828             break;
1829         }
1830     }
1831     return 1;
1832 }
1833 
1834 // check if single insn matches any of the provided matches
1835 int insn_match_any(cs_insn *insn,const insn_match_t *match)
1836 {
1837     const insn_match_t *m;
1838     // check matches
1839     for(m=match;m->id != ARM_INS_ENDING;m++) {
1840         if(insn_match(insn,m)) {
1841             return 1;
1842         }
1843     }
1844     return 0;
1845 }
1846 
1847 // iterate is until current instruction matches any of the provided matches or until limit reached
1848 int insn_match_find_next(firmware *fw, iter_state_t *is, int max_insns, const insn_match_t *match)
1849 {
1850     int i=0;
1851     while(i < max_insns) {
1852         // disassembly failed, no match (could ignore..)
1853         if(!disasm_iter(fw,is)) {
1854             return 0;
1855         }
1856         // printf("%"PRIx64" insn_match_find_next %s %s\n",is->insn->address,is->insn->mnemonic,is->insn->op_str);
1857         if(insn_match_any(is->insn,match)) {
1858             return 1;
1859         }
1860         i++;
1861     }
1862     // limit hit
1863     return 0;
1864 }
1865 
1866 // iterate is until current has matched any of the provided matches N times or until max_insns reached
1867 int insn_match_find_nth(firmware *fw, iter_state_t *is, int max_insns, int num_to_match, const insn_match_t *match)
1868 {
1869     int i=0;
1870     int num_matched=0;
1871     while(i < max_insns) {
1872         // disassembly failed, no match (could ignore..)
1873         if(!disasm_iter(fw,is)) {
1874             return 0;
1875         }
1876         // printf("%"PRIx64" insn_match_find_next %s %s\n",is->insn->address,is->insn->mnemonic,is->insn->op_str);
1877 
1878         const insn_match_t *m;
1879         // check matches
1880         for(m=match;m->id != ARM_INS_ENDING;m++) {
1881             if(insn_match(is->insn,m)) {
1882                 num_matched++;
1883             }
1884         }
1885         if(num_matched == num_to_match) {
1886             return 1;
1887         }
1888         i++;
1889     }
1890     // limit hit
1891     return 0;
1892 }
1893 
1894 // find next matching sequence starting within max_insns
1895 int insn_match_find_next_seq(firmware *fw, iter_state_t *is, int max_insns, const insn_match_t *match)
1896 {
1897     int count=0;
1898     while(count < max_insns) {
1899         const insn_match_t *m=match;
1900         //printf("%"PRIx64" insn_match_find_next_seq %s %s\n",is->insn->address,is->insn->mnemonic,is->insn->op_str);
1901         while(m->id != ARM_INS_ENDING && disasm_iter(fw,is) && insn_match(is->insn,m)) {
1902             m++;
1903             count++;
1904         }
1905         if(m->id == ARM_INS_ENDING) {
1906             return 1;
1907         }
1908         // non-matching
1909         count++;
1910     }
1911     return 0;
1912 }
1913 
1914 
1915 // Search the firmware for something. The desired matching is performed using the supplied 'func' function.
1916 // Continues searching until 'func' returns non-zero - then returns 1
1917 // otherwise returns 0.
1918 // Uses the BufRange structs to speed up searching
1919 // Note: this version searches byte by byte in the firmware dump instead of by words
1920 int fw_search_bytes(firmware *fw, search_bytes_fn func)
1921 {
1922     BufRange *p = fw->br;
1923     while (p)
1924     {
1925         int k;
1926         for (k = p->off*4; k < (p->off + p->len)*4; k++)
1927         {
1928             if (func(fw,k))
1929                 return 1;
1930         }
1931         p = p->next;
1932     }
1933     return 0;
1934 }
1935 
1936 
1937 // ****** firmware loading / initialization / de-allocation ******
1938 // add given address range
1939 void fw_add_adr_range(firmware *fw, uint32_t start, uint32_t end, uint32_t src_start, int type, int flags)
1940 {
1941     if(fw->adr_range_count == FW_MAX_ADR_RANGES) {
1942         fprintf(stderr,"fw_add_adr_range: FW_MAX_ADR_RANGES hit\n");
1943         return;
1944     }
1945     if(src_start < fw->base) {
1946         fprintf(stderr,"fw_add_adr_range: src_start 0x%08x < base 0x%08x\n",src_start,fw->base);
1947         return;
1948     }
1949     if(src_start >= fw->base+fw->size8) {
1950         fprintf(stderr,"fw_add_adr_range: src_start 0x%08x outside dump end 0x%08x\n",src_start,fw->base+fw->size8);
1951         return;
1952     }
1953     if(end <= start) {
1954         fprintf(stderr,"fw_add_adr_range: end 0x%08x <= start 0x%08x\n",end,start);
1955         return;
1956     }
1957     int len=end-start;
1958     if(len > 0xFFFFFFFF - src_start) {
1959         fprintf(stderr,"fw_add_adr_range: range too long %d\n",len);
1960         return;
1961     }
1962     if(len > fw->size8 - (start - fw->base)) {
1963         fprintf(stderr,"fw_add_adr_range: range outside of dump %d\n",len);
1964         return;
1965     }
1966     adr_range_t *r=&fw->adr_ranges[fw->adr_range_count];
1967     // TODO some firmware copies (i.e. g5x code 2) may end on non-word aligned address even though copy is words
1968     r->start=start;
1969     r->src_start=src_start;
1970     r->bytes=len;
1971     r->type=type;
1972     r->flags=flags;
1973     r->buf=fw->buf8 + (r->src_start - fw->base);
1974 
1975     fw->adr_range_count++;
1976 }
1977 
1978 void find_dryos_vers(firmware *fw)
1979 {
1980     const char *sig="DRYOS version 2.3, release #";
1981     fw->dryos_ver_count = find_bytes_all(fw,sig,strlen(sig),fw->base,fw->dryos_ver_list,FW_MAX_DRYOS_VERS);
1982     /*
1983     int i;
1984     for(i=0;i<fw->dryos_ver_count;i++) {
1985         fprintf(stderr,"found %s (%d) @0x%08x\n",
1986             (char *)adr2ptr(fw,fw->dryos_ver_list[i]),
1987             atoi((char *)adr2ptr(fw,fw->dryos_ver_list[i]+strlen(sig))),
1988             fw->dryos_ver_list[i]);
1989     }
1990     */
1991     if(fw->dryos_ver_count) {
1992         if(fw->dryos_ver_count == FW_MAX_DRYOS_VERS) {
1993             fprintf(stderr,"WARNING hit FW_MAX_DRYOS_VERS\n");
1994         }
1995         int i;
1996         int match_i;
1997         uint32_t min_adr = 0xFFFFFFFF;
1998         
1999         // ref should easily be in the first 8M (most near start but g7x2 at >0x500000)
2000         uint32_t maxadr = (fw->rom_code_search_max_adr - 0x800000 > fw->base)?fw->base + 0x800000:fw->rom_code_search_max_adr;
2001         // look for pointer to dryos version nearest to main ROM start, before the string itself
2002         // NOTE it's the *pointer* that must be nearest, the string may not be the first
2003         for(i=0; i<fw->dryos_ver_count; i++) {
2004             // TODO could limit range more, ctypes should be ref'd a lot
2005             // could sanity check not a random value that happens to match
2006             uint32_t adr = find_u32_adr_range(fw,fw->dryos_ver_list[i],fw->rom_code_search_min_adr,maxadr);
2007             if(adr && adr < min_adr) {
2008                 min_adr = adr;
2009                 match_i = i;
2010             }
2011         }
2012         if(min_adr == 0xFFFFFFFF) {
2013             fprintf(stderr,"WARNING dryos version pointer not found, defaulting to first\n");
2014             match_i = 0;
2015             min_adr = 0;
2016         }
2017         fw->dryos_ver_str = (char *)adr2ptr(fw,fw->dryos_ver_list[match_i]);
2018         fw->dryos_ver = atoi((char *)adr2ptr(fw,fw->dryos_ver_list[match_i]+strlen(sig)));
2019         fw->dryos_ver_adr = fw->dryos_ver_list[match_i];
2020         fw->dryos_ver_ref_adr = min_adr;
2021         // fprintf(stderr,"main firmware version %s @ 0x%08x ptr 0x%08x\n",fw->dryos_ver_str,fw->dryos_ver_adr,min_adr);
2022     } else {
2023         fw->dryos_ver=0;
2024         fw->dryos_ver_str=NULL;
2025         fw->dryos_ver_adr=0;
2026     }
2027 }
2028 
2029 // load firmware and initialize stuff that doesn't require disassembly
2030 void firmware_load(firmware *fw, const char *filename, uint32_t base_adr,int fw_arch)
2031 {
2032     FILE *f = fopen(filename, "rb");
2033     if (f == NULL)
2034     {
2035         fprintf(stderr,"Error opening %s\n",filename);
2036         exit(1);        
2037     }
2038     fseek(f,0,SEEK_END);
2039     fw->size8 = ftell(f);
2040     fseek(f,0,SEEK_SET);
2041     // dumps should be an integral number of 32 bit words
2042     // ensures accessing as 32 bit ints safe
2043     if(fw->size8&3) {
2044         fprintf(stderr,"WARNING: dump size %d is not divisible by 4, truncating\n",fw->size8);
2045         fw->size8 &= ~3;
2046     }
2047 
2048     // adjust to ensure base_adr + size doesn't overflow
2049     if(0xFFFFFFFF - base_adr < fw->size8) {
2050         fprintf(stderr,"adjusted dump size 0x%08x->",fw->size8);
2051         fw->size8 = 0xFFFFFFFC - base_adr;
2052         fprintf(stderr,"0x%08x\n",fw->size8);
2053     }
2054 
2055     fw->arch=fw_arch;
2056     fw->size32=fw->size8/4;
2057 
2058     fw->base = base_adr;
2059 
2060     fw->buf8 = malloc(fw->size8);
2061     if(!fw->buf8) {
2062         fprintf(stderr,"malloc %d failed\n",fw->size8);
2063         exit(1);        
2064     }
2065     fread(fw->buf8, 1, fw->size8, f);
2066     fclose(f);
2067     findRanges(fw);
2068 
2069     fw->adr_range_count=0;
2070     // add ROM
2071     fw_add_adr_range(fw,fw->base, fw->base+fw->size8, fw->base, ADR_RANGE_ROM, ADR_RANGE_FL_NONE);
2072 
2073     fw->main_offs = 0;
2074     int k = find_str(fw, "gaonisoy");
2075     // assume firmware start is 32 bit jump over goanisoy
2076     if(k == -1) {
2077         // suppress warning on vxworks, main firmware start is always offset 0
2078         if(find_str(fw,"VxWorks") == -1) {
2079             fprintf(stderr,"WARNING gaonisoy string not found, assuming code start offset 0\n");
2080         }
2081     } else if (k != 1) {
2082         // check at 0x20004 - note doesn't just use offset of first gaonisoy, because could be ref'd in romstarter
2083         if(fw_memcmp(fw,fw->base+0x20004,"gaonisoy",8) == 0) {
2084             fw->main_offs = 0x20000;
2085         } else if (fw_memcmp(fw,fw->base+0x10004,"gaonisoy",8) == 0) { // newer armv5 firmwares base ff81000 start at ff820000
2086             fw->main_offs = 0x10000;
2087         } else {
2088             fprintf(stderr,"WARNING code start offset not found, assuming 0\n");
2089         }
2090     }
2091 
2092     fw->rom_code_search_min_adr = fw->base + fw->main_offs; // 0 if not found
2093     fw->rom_code_search_max_adr=fw->base+fw->size8 - 4; // default == end of fw, may be adjusted by firmware_init_data_ranges
2094 
2095     find_dryos_vers(fw);
2096 
2097     fw->firmware_ver_str = 0;
2098     k = find_str(fw, "Firmware Ver ");
2099     if (k != -1)
2100     {
2101         fw->firmware_ver_str = (char *)fw->buf8 + k*4;
2102     }
2103     // set expected instruction set
2104     if(fw->arch==FW_ARCH_ARMv5) {
2105         fw->thumb_default = 0;
2106     } else if(fw->arch==FW_ARCH_ARMv7) {
2107         fw->thumb_default = 1;
2108     } else {
2109         fprintf(stderr,"firmware_init_capstone: invalid arch\n");
2110     }
2111 }
2112 
2113 // test to verify thumb blx bug is patched in linked capstone
2114 int do_blx_check(firmware *fw)
2115 {
2116 /*
2117 test code blxbork.S
2118 .syntax unified
2119 .globl arm_code
2120 .globl _start
2121 _start:
2122 .code 16
2123 blx arm_code
2124 movs r0, #1
2125 blx arm_code
2126 .align 4
2127 .code 32
2128 arm_code:
2129 bx lr
2130 
2131 arm-none-eabi-gcc -nostdlib blxbork.S -o blxbork.elf
2132 */
2133 
2134 static const uint8_t code[]=
2135     "\x00\xf0\x06\xe8" // blx arm_code (start + 0x10)
2136     "\x01\x20" // movs r0,#1, to cause non-word align
2137     "\x00\xf0\x04\xe8" // blx arm_code
2138 ;
2139     cs_insn *insn;
2140     size_t count;
2141     count = cs_disasm(fw->cs_handle_thumb, code, sizeof(code), 0xFF000000, 3, &insn);
2142 
2143     if(!(count == 3 && insn[0].id == ARM_INS_BLX && insn[2].id == ARM_INS_BLX)) {
2144         fprintf(stderr,"do_blx_check: disassembly failed\n");
2145         return 0;
2146     }
2147 
2148     int r=(insn[0].detail->arm.operands[0].imm == insn[2].detail->arm.operands[0].imm);
2149 
2150 
2151     if(!r) {
2152         fprintf(stderr,"WARNING! Incorrect disassembly is likely\n");
2153     }
2154     cs_free(insn,count);
2155     return r;
2156 }
2157 
2158 // initialize capstone state for loaded fw
2159 int firmware_init_capstone(firmware *fw)
2160 {
2161     if (cs_open(CS_ARCH_ARM, CS_MODE_ARM, &fw->cs_handle_arm) != CS_ERR_OK) {
2162         fprintf(stderr,"cs_open ARM failed\n");
2163         return 0;
2164     }
2165     cs_option(fw->cs_handle_arm, CS_OPT_DETAIL, CS_OPT_ON);
2166     if (cs_open(CS_ARCH_ARM, CS_MODE_THUMB, &fw->cs_handle_thumb) != CS_ERR_OK) {
2167         fprintf(stderr,"cs_open thumb failed\n");
2168         return 0;
2169     }
2170     cs_option(fw->cs_handle_thumb, CS_OPT_DETAIL, CS_OPT_ON);
2171     fw->is=disasm_iter_new(fw,0);
2172     do_blx_check(fw);
2173     return 1;
2174 }
2175 
2176 /*
2177 look for
2178 ldr rx, =ROM ADR
2179 ldr ry, =non-rom adr
2180 ldr rz, =non ROM adr > ry
2181 leave is pointing at last LDR, or last checked instruction
2182 */
2183 
2184 int find_startup_copy(firmware *fw,
2185                          iter_state_t *is,
2186                          int max_search,
2187                          uint32_t *src_start,
2188                          uint32_t *dst_start,
2189                          uint32_t *dst_end)
2190 {
2191     int count=0;
2192     uint32_t *fptr = NULL;
2193     uint32_t *dptr = NULL;
2194     uint32_t *eptr = NULL;
2195     *src_start=0;
2196     *dst_start=0;
2197     *dst_end=0;
2198 
2199     while(disasm_iter(fw,is) && count < max_search) {
2200         uint32_t *pv=LDR_PC2valptr(fw,is->insn);
2201         // not an LDR pc, reset
2202         // TODO some firmwares might use other instructions
2203         if(!pv) {
2204             fptr=dptr=eptr=NULL;
2205         }else if(!fptr) {
2206             // only candidate if in ROM
2207             if(*pv > fw->base) {
2208                 fptr=pv;
2209             }
2210         } else if(!dptr) {
2211             if(*pv < fw->base) {
2212                 dptr=pv;
2213             } else {
2214                 fptr=NULL; // dest address in ROM, reset
2215             }
2216         } else if(!eptr) {
2217             if(*pv < fw->base && *pv > *dptr) {
2218                 eptr=pv;
2219             } else { // dest end address in ROM, or before source, reset
2220                     // TODO maybe should swap instead if < source
2221                 fptr=dptr=NULL;
2222             }
2223         }
2224         if(fptr && dptr && eptr) {
2225             *src_start=*fptr;
2226             *dst_start=*dptr;
2227             *dst_end=*eptr;
2228             return 1;
2229         }
2230         count++;
2231     }
2232     return 0;
2233 }
2234 
2235 void find_exception_vec(firmware *fw, iter_state_t *is)
2236 {
2237     // check for exception vector, d7 id
2238     // only on thumb2 for now
2239     if(fw->arch != FW_ARCH_ARMv7) {
2240         return;
2241     }
2242 
2243     const insn_match_t match_bl_mcr[]={
2244         {MATCH_INS(BL,  1), {MATCH_OP_IMM_ANY}},
2245         // Vector Base Address Register MCR p15, 0, <Rt>, c12, c0, 0 - not present on PMSA
2246         {MATCH_INS(MCR, 6), {MATCH_OP_PIMM(15),MATCH_OP_IMM(0),MATCH_OP_REG_ANY,MATCH_OP_CIMM(12),MATCH_OP_CIMM(0),MATCH_OP_IMM(0)}},
2247         {ARM_INS_ENDING}
2248     };
2249     
2250     // reset to main fw start
2251     disasm_iter_init(fw, is, fw->base + fw->main_offs + 12 + fw->thumb_default);
2252     if(!insn_match_find_next(fw,is,4,match_bl_mcr)) {
2253         printf("no match!\n");
2254         return;
2255     }
2256     // check which instruction we matched
2257     uint32_t faddr = get_branch_call_insn_target(fw,is);
2258     if(faddr) {
2259         // bl = digic6, has function to set up exception vector
2260         disasm_iter_init(fw, is, faddr);
2261         disasm_iter(fw, is);
2262         int ra,rb;
2263         uint32_t va, vb;
2264         if(!IS_INSN_ID_MOVx(is->insn->id) || is->insn->detail->arm.operands[1].type != ARM_OP_IMM) {
2265             return;
2266         }
2267         ra = is->insn->detail->arm.operands[0].reg;
2268         va = is->insn->detail->arm.operands[1].imm;
2269         disasm_iter(fw, is);
2270         if(is->insn->id != ARM_INS_MOVT 
2271             || is->insn->detail->arm.operands[0].reg != ra
2272             || is->insn->detail->arm.operands[1].type != ARM_OP_IMM) {
2273             return;
2274         }
2275         va = (is->insn->detail->arm.operands[1].imm << 16) | (va & 0xFFFF);
2276         // fw has BIC
2277         va = va & ~1;
2278         if(adr_get_range_type(fw,va) != ADR_RANGE_ROM) {
2279             return;
2280         }
2281         disasm_iter(fw, is);
2282         if(!IS_INSN_ID_MOVx(is->insn->id) || is->insn->detail->arm.operands[1].type != ARM_OP_IMM) {
2283             return;
2284         }
2285         rb = is->insn->detail->arm.operands[0].reg;
2286         vb = is->insn->detail->arm.operands[1].imm;
2287         disasm_iter(fw, is);
2288         if(is->insn->id != ARM_INS_MOVT 
2289             || is->insn->detail->arm.operands[0].reg != rb
2290             || is->insn->detail->arm.operands[1].type != ARM_OP_IMM) {
2291             return;
2292         }
2293         vb = (is->insn->detail->arm.operands[1].imm << 16) | (vb & 0xFFFF);
2294         vb = vb & ~1;
2295         if(adr_get_range_type(fw,vb) != ADR_RANGE_ROM) {
2296             return;
2297         }
2298         if(va >= vb) {
2299             return;
2300         }
2301         fw_add_adr_range(fw,0,vb - va, va, ADR_RANGE_RAM_CODE, ADR_RANGE_FL_EVEC | ADR_RANGE_FL_TCM);
2302         // printf("ex vec 0x%08x-0x%08x\n",va,vb);
2303 
2304     } else if(is->insn->id == ARM_INS_MCR) {
2305         // digic 7 = mcr ... 
2306         fw->arch_flags |= FW_ARCH_FL_VMSA;
2307         // rewind 1
2308         disasm_iter_init(fw, is, adr_hist_get(&is->ah,1));
2309         disasm_iter(fw, is);
2310         // uint32_t ex_vec = LDR_PC2val(fw,is->insn);
2311         //printf("found MCR @ 0x%"PRIx64" ex vec at 0x%08x\n",is->insn->address,ex_vec);
2312     }
2313 }
2314 
2315 // init basic copied RAM code / data ranges
2316 void firmware_init_data_ranges(firmware *fw)
2317 {
2318 //TODO maybe should return status
2319     uint32_t src_start, dst_start, dst_end;
2320     uint32_t data_found_copy = 0;
2321 
2322     // start at fw start  + 12 (32 bit jump, gaonisoy)
2323     iter_state_t *is=disasm_iter_new(fw, fw->base + fw->main_offs + 12 + fw->thumb_default);
2324 
2325     fw->data_init_start=0;
2326     fw->data_start=0;
2327     fw->data_len=0;
2328 
2329     fw->memisostart=0;
2330 
2331     int base2_found=0;
2332     int base3_found=0;
2333 
2334     // TODO  pre-d6 ROMs have a lot more stuff before first copy
2335     int max_search=100;
2336     while(find_startup_copy(fw,is,max_search,&src_start,&dst_start,&dst_end)) {
2337         // all known copied code is 3f1000 or higher, guess data
2338         if(dst_start < 0x100000) {
2339             // fprintf(stderr, "data?  @0x%"PRIx64" 0x%08x-0x%08x from 0x%08x\n",is->adr,dst_start,dst_end,src_start);
2340             if(fw->data_init_start) {
2341                 fprintf(stderr,"firmware_init_data_ranges: data already found, unexpected start 0x%08x src 0x%08x end 0x%08x\n",
2342                         dst_start,src_start,dst_end);
2343                 continue;
2344             }
2345 
2346             // not a known value, warn
2347             if(dst_start != 0x1900 && dst_start != 0x8000) {
2348                 fprintf(stderr,"firmware_init_data_ranges: guess unknown ROM data_start 0x%08x src 0x%08x end 0x%08x\n",
2349                         dst_start,src_start,dst_end);
2350             }
2351             fw->data_init_start=src_start;
2352             fw->data_start=dst_start;
2353             fw->data_len=dst_end-dst_start;
2354             fw_add_adr_range(fw,dst_start,dst_end,src_start, ADR_RANGE_INIT_DATA, ADR_RANGE_FL_NONE);
2355             data_found_copy=is->adr;
2356         } else if(dst_start < 0x08000000) { /// highest known first copied ram code 0x01900000
2357             // fprintf(stderr,"code1? @0x%"PRIx64" 0x%08x-0x%08x from 0x%08x\n",is->adr,dst_start,dst_end,src_start);
2358             if(base2_found) {
2359                 fprintf(stderr,"firmware_init_data_ranges: base2 already found, unexpected start 0x%08x src 0x%08x end 0x%08x\n",
2360                         dst_start,src_start,dst_end);
2361                 continue;
2362             }
2363             base2_found=1;
2364             // known values
2365             if( dst_start != 0x003f1000 && 
2366                 dst_start != 0x00431000 && 
2367                 dst_start != 0x00471000 && 
2368                 dst_start != 0x00685000 && 
2369                 dst_start != 0x00671000 && 
2370                 dst_start != 0x006b1000 && 
2371                 dst_start != 0x010c1000 &&
2372                 dst_start != 0x010e1000 &&
2373                 dst_start != 0x01900000) {
2374                 fprintf(stderr,"firmware_init_data_ranges: guess unknown base2 0x%08x src 0x%08x end 0x%08x\n",
2375                         dst_start,src_start,dst_end);
2376             }
2377             fw_add_adr_range(fw,dst_start,dst_end,src_start,ADR_RANGE_RAM_CODE, ADR_RANGE_FL_NONE);
2378         } else { // know < ROM based on match, assume second copied code
2379             // fprintf(stderr, "code2? @0x%"PRIx64" 0x%08x-0x%08x from 0x%08x\n",is->adr,dst_start,dst_end,src_start);
2380             if(base3_found) {
2381                 fprintf(stderr,"firmware_init_data_ranges: base3 already found, unexpected start 0x%08x src 0x%08x end 0x%08x\n",
2382                         dst_start,src_start,dst_end);
2383                 continue;
2384             }
2385             base3_found=1;
2386             if(dst_start != 0xbfe10800 && // known digic 6 value (g5x)
2387                dst_start != 0xdffc4900) { // known digic 7 value (m5)
2388                 fprintf(stderr,"firmware_init_data_ranges: guess unknown base3 0x%08x src 0x%08x end 0x%08x\n",
2389                         dst_start,src_start,dst_end);
2390             }
2391             fw_add_adr_range(fw,dst_start,dst_end,src_start,ADR_RANGE_RAM_CODE, ADR_RANGE_FL_TCM);
2392         }
2393         if(fw->data_start && base2_found && base3_found) {
2394             break;
2395         }
2396         // after first, shorter search range in between copies
2397         max_search=16;
2398     }
2399 
2400     // look for BSS init after last found copy
2401     if(data_found_copy) {
2402         int count=0;
2403         uint32_t *eptr=NULL;
2404         uint32_t *dptr=NULL;
2405         disasm_iter_init(fw,is,(data_found_copy-4) | fw->thumb_default);
2406         while(disasm_iter(fw,is) && count < 20) {
2407             uint32_t *pv=LDR_PC2valptr(fw,is->insn);
2408             // not an LDR pc, reset;
2409             if(!pv) {
2410                 //dptr=eptr=NULL;
2411             } else if(!dptr) {
2412                 // TODO older firmwares use reg with ending value from DATA copy
2413                 // should be equal to end pointer of data
2414                 if(*pv == fw->data_start + fw->data_len) {
2415                     dptr=pv;
2416                 }
2417             } else if(!eptr) {
2418                 if(*pv < fw->base) {
2419                     if(*pv != fw->data_start + fw->data_len) {
2420                         eptr=pv;
2421                     }
2422                 } else { // dest end address in ROM, reset
2423                     eptr=dptr=NULL;
2424                 }
2425             }
2426             if(dptr && eptr) {
2427                 // fprintf(stderr, "bss?   @0x%"PRIx64" 0x%08x-0x%08x\n",is->adr,*dptr,*eptr);
2428                 fw->memisostart=*eptr;
2429                 break;
2430             }
2431             count++;
2432         }
2433     }
2434 
2435     find_exception_vec(fw,is);
2436 
2437     // if data found, adjust default code search range
2438     // TODO could use copied code regions too, but after data on known firmwares
2439     if(fw->data_start) {
2440         fw->rom_code_search_max_adr=fw->data_init_start;
2441     }
2442     // if dryos version string found, use as search limit
2443     if(fw->dryos_ver_adr) {
2444         if(fw->dryos_ver_adr < fw->rom_code_search_max_adr) {
2445             fw->rom_code_search_max_adr = fw->dryos_ver_adr;
2446         }
2447     }
2448     disasm_iter_free(is);
2449 }
2450 
2451 // free resources associated with fw
2452 void firmware_unload(firmware *fw)
2453 {
2454     if(!fw) {
2455         return;
2456     }
2457     if(fw->is) {
2458         disasm_iter_free(fw->is);
2459     }
2460     if(fw->cs_handle_arm) {
2461         cs_close(&fw->cs_handle_arm);
2462     }
2463     if(fw->cs_handle_thumb) {
2464         cs_close(&fw->cs_handle_thumb);
2465     }
2466     free(fw->buf8);
2467     memset(fw,0,sizeof(firmware));
2468 }

/* [<][>][^][v][top][bottom][index][help] */