root/tools/firmware_load_ng.c

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

DEFINITIONS

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

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