root/tools/capdis.c

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

DEFINITIONS

This source file includes following definitions.
  1. new_list
  2. free_list
  3. new_node
  4. l_search
  5. l_insert
  6. l_remove
  7. addr_hash_new
  8. addr_hash_free
  9. addr_hash_get
  10. addr_hash_add
  11. usage
  12. arm_op_type_name
  13. describe_insn_ops
  14. describe_insn_groups
  15. describe_str
  16. describe_const_op
  17. describe_prop_call
  18. do_dis_branch
  19. do_dis_call
  20. do_dis_insn
  21. do_adr_label
  22. do_tbb_data
  23. do_tbh_data
  24. do_tbx_pass1
  25. do_tbx_data
  26. do_dis_range
  27. main

   1 #include <ctype.h>
   2 #include <stdio.h>
   3 #include <string.h>
   4 #include <inttypes.h>
   5 #include <sys/stat.h>
   6 
   7 #include <capstone.h>
   8 
   9 #include "stubs_load.h"
  10 #include "firmware_load_ng.h"
  11 
  12 // borrowed from chdk_dasm.h, chdk_dasm.c
  13 //------------------------------------------------------------------------------------------------------------
  14 typedef unsigned int t_address;
  15 typedef unsigned int t_value;
  16 
  17 /* -----------------------------------------------------------------
  18  * Linked List Routines
  19  * ----------------------------------------------------------------- */
  20  
  21 struct lnode {                    // linked list node - Storage memory address / memory data pairs
  22     struct lnode *next;
  23     t_address address;
  24     t_value data ;
  25 }; 
  26 
  27 struct llist {                    // Head of linked list
  28     struct lnode *head;
  29     int size;
  30 };
  31 
  32 /* -----------------------------------------------------------------
  33  * struct llist * new_list(void)
  34  * ----------------------------------------------------------------- */
  35 struct llist * new_list()
  36 { 
  37     struct llist *lst;
  38 
  39     lst = (struct llist *) malloc(sizeof(struct llist));
  40     if (lst == NULL) {
  41         printf("\n **new_list() : malloc error");
  42         return NULL;
  43     }
  44     lst->head = 0;
  45     lst->size = 0;
  46 
  47     return lst;
  48 }
  49 
  50 void free_list(struct llist *lst)
  51 {
  52     if (lst)
  53     {
  54         struct lnode *p, *n;
  55         p = lst->head;
  56         while (p)
  57         {
  58             n = p->next;
  59             free(p);
  60             p = n;
  61         }
  62         free(lst);
  63     }
  64 }
  65 
  66 /* -----------------------------------------------------------------
  67  * struct new_node * new_node(void * item, t_value data)
  68  * ----------------------------------------------------------------- */
  69 struct lnode * new_node(t_address address, t_value data) {
  70     struct lnode *node;
  71 
  72     node = (struct lnode *) malloc (sizeof (struct lnode));
  73     if (node == NULL) {
  74         printf("\n **new_node() : malloc error");
  75         return NULL;
  76     }
  77     node->address = address;
  78     node->data = data;
  79     node->next = 0;
  80 
  81     return node;
  82 }  
  83 
  84 /* -----------------------------------------------------------------
  85  * int l_search(struct llist *ls, void *address) {
  86  * ----------------------------------------------------------------- */
  87 struct lnode * l_search(struct llist *ls, t_address address) {
  88     struct lnode *node;
  89 
  90     node = ls->head;
  91     while ( node != NULL && node->address != address ) {
  92         node = node->next ;
  93     }
  94     if (node == NULL) {
  95         return 0; /* 'item' not found */
  96     }
  97 
  98     return node;
  99 }
 100 
 101 /* -----------------------------------------------------------------
 102  * int l_insert(struct llist *ls, void *item)
 103  * ----------------------------------------------------------------- */
 104 int l_insert(struct llist *ls, t_address address, t_value data)
 105 {
 106     struct lnode *node;
 107 
 108     if( l_search(ls, address)) return -1 ;
 109     node = new_node(address, data);
 110     if (node == NULL) return 0;
 111     node->next = ls->head;
 112     ls->head = node;  
 113     (ls->size)++;
 114 
 115     return 1;
 116 }
 117 
 118 void l_remove(struct llist *ls, t_address addr)
 119 {
 120     if (ls)
 121     {
 122         struct lnode *p, *l;
 123         l = 0;
 124         p = ls->head;
 125         while (p)
 126         {
 127             if (p->address == addr)
 128             {
 129                 if (l)
 130                     l->next = p->next;
 131                 else
 132                     ls->head = p->next;
 133                 (ls->size)--;
 134                 return;
 135             }
 136             l = p;
 137             p = p->next;
 138         }
 139     }
 140 }
 141  
 142 #define ADDR_HASH_BITS 20
 143 #define ADDR_HASH_BUCKETS   (1<<ADDR_HASH_BITS)
 144 #define ADDR_HASH_MASK   (ADDR_HASH_BUCKETS-1)
 145 #define ADDR_HASH(addr) (((unsigned)(addr) >> 2) & ADDR_HASH_MASK)
 146 struct llist ** addr_hash_new() {
 147     struct llist **addr_hash = malloc(ADDR_HASH_BUCKETS*sizeof(struct llist *));
 148     if(!addr_hash) {
 149         fprintf(stderr,"addr_hash_now: malloc failed\n");
 150         exit(1);
 151     }
 152     memset(addr_hash,0,ADDR_HASH_BUCKETS*sizeof(struct llist *));
 153     return addr_hash;
 154 }
 155 
 156 void addr_hash_free(struct llist **addr_hash) {
 157     int i;
 158     for(i=0;i<ADDR_HASH_BUCKETS; i++) {
 159         if(addr_hash[i]) {
 160             free_list(addr_hash[i]);
 161         }
 162     }
 163     free(addr_hash);
 164 }
 165 struct lnode * addr_hash_get(struct llist **addr_hash,t_address addr) {
 166     unsigned key = ADDR_HASH(addr);
 167     if(!addr_hash[key]) {
 168         return NULL;
 169     }
 170     return l_search(addr_hash[key],addr);
 171 }
 172 
 173 void addr_hash_add(struct llist **addr_hash,t_address addr) {
 174     unsigned key = ADDR_HASH(addr);
 175     if(!addr_hash[key]) {
 176         addr_hash[key] = new_list();
 177     }
 178     l_insert(addr_hash[key],addr,0);
 179 }
 180 
 181 // function address for prop call annotation
 182 // TODO should have a more generalized "recognize function arugments" system
 183 static t_address set_prop; 
 184 static t_address get_prop; 
 185 
 186 void usage(void) {
 187     fprintf(stderr,"usage capdis [options] <file> <load address>\n"
 188                     "options:\n"
 189                     " -c=<count> disassemble at most <count> instructions\n"
 190                     " -s=<address|stub name> start disassembling at <address>. LSB controls ARM/thumb mode\n"
 191                     " -e=<address> stop disassembling at <address>\n"
 192                     " -o=<offset> start disassembling at <offset>. LSB controls ARM/thumb mode\n"
 193                     " -f=<chdk|objdump> format as CHDK inline ASM, or similar to objdump (default clean ASM)\n"
 194                     " -armv5 make firmware_load treat firmware as armv5\n"
 195                     " -stubs[=dir] load / use stubs from dir (default .) for names\n"
 196                     " -props=<n> load propset<n>.h and identify propcases in calls. Requires -stubs\n"
 197                     " -v increase verbosity\n"
 198                     " -d-const add details about pc relative constant LDRs\n"
 199                     " -d-bin print instruction hex dump\n"
 200                     " -d-addr print instruction addresses\n"
 201                     " -d-ops print instruction operand information\n"
 202                     " -d-groups print instruction group information\n"
 203                     " -d all of the -d-* above\n"
 204                     " -noloc don't generate loc_xxx: labels and B loc_xxx\n"
 205                     " -nosub don't generate BL sub_xxx\n"
 206                     " -noconst don't generate LDR Rd,=0xFOO\n"
 207                     " -nostr don't comments for string refs\n"
 208                     " -adrldr convert ADR Rd,#x and similar to LDR RD,=... (default with -f=chdk)\n"
 209                     " -noadrldr don't convert ADR Rd,#x and similar to LDR RD,=...\n"
 210                     " -nofwdata don't attempt to initialize firmware data ranges\n"
 211                     " -jfw generate LDR PC,=0x... to return to firmware at end of disassembled range\n"
 212               );
 213     exit(1);
 214 }
 215 
 216 // map friendly names to arm_op_type_enum
 217 static const char *arm_op_type_name(int op_type) {
 218     switch(op_type) {
 219         case ARM_OP_INVALID:
 220             return "invalid";
 221         case ARM_OP_REG:
 222             return "reg";
 223         case ARM_OP_IMM:
 224             return "imm";
 225         case ARM_OP_MEM:
 226             return "mem";
 227         case ARM_OP_FP:
 228             return "fp";
 229         case ARM_OP_CIMM:
 230             return "cimm";
 231         case ARM_OP_PIMM:
 232             return "pimm";
 233         case ARM_OP_SETEND:
 234             return "setend";
 235         case ARM_OP_SYSREG:
 236             return "sysreg";
 237     }
 238     return "unk";
 239 }
 240 // hack for inline C vs ASM style
 241 const char *comment_start = ";";
 242 
 243 static void describe_insn_ops(csh handle, cs_insn *insn) {
 244     printf("%s OPERANDS %d:\n",comment_start,insn->detail->arm.op_count);
 245     int i;
 246     for(i=0;i<insn->detail->arm.op_count;i++) {
 247         printf("%s  %d: %s",comment_start,i,arm_op_type_name(insn->detail->arm.operands[i].type));
 248         switch(insn->detail->arm.operands[i].type) {
 249             case ARM_OP_IMM:
 250                 printf("=0x%x",insn->detail->arm.operands[i].imm);
 251                 break;
 252             case ARM_OP_MEM: {
 253                 const char *reg=cs_reg_name(handle,insn->detail->arm.operands[i].mem.base);
 254                 if(reg) {
 255                     printf(" base=%s",reg);
 256                 }
 257                 reg=cs_reg_name(handle,insn->detail->arm.operands[i].mem.index);
 258                 if(reg) {
 259                     printf(" index=%s",reg);
 260                 }
 261                 if(insn->detail->arm.operands[i].mem.disp) {
 262                     printf(" scale=%d disp=0x%08x",
 263                         insn->detail->arm.operands[i].mem.scale,
 264                         insn->detail->arm.operands[i].mem.disp);
 265                 }
 266                 break;
 267             }
 268             case ARM_OP_REG:
 269                 printf(" %s",cs_reg_name(handle,insn->detail->arm.operands[i].reg));
 270                 break;
 271             default:
 272                 break;
 273         } 
 274         printf("\n");
 275     }
 276 }
 277 static void describe_insn_groups(csh handle, cs_insn *insn) {
 278     int i;
 279     printf("%s GROUPS %d:",comment_start,insn->detail->groups_count);
 280     for(i=0;i<insn->detail->groups_count;i++) {
 281         if(i>0) {
 282             printf(",");
 283         }
 284         printf("%s",cs_group_name(handle,insn->detail->groups[i]));
 285     }
 286     printf("\n");
 287 }
 288 
 289 #define COMMENT_LEN 255
 290 // if adr is a string, append possibly truncated version to comment
 291 // TODO assumes this is the last thing appended to comment
 292 // TODO should have code to check if adr is known function
 293 // TODO isASCIIstring currently rejects strings longer than 100
 294 static void describe_str(firmware *fw, char *comment, uint32_t adr)
 295 {
 296     int plevel=1;
 297     char *s=(char *)adr2ptr_with_data(fw,adr);
 298     if(!s) {
 299         //printf("s1 bad ptr 0x%08x\n",adr);
 300         return;
 301     }
 302     if(!isASCIIstring(fw,adr)) {
 303         //printf("s1 not ASCII 0x%08x\n",adr);
 304         // not a string, check for valid pointer
 305         uint32_t adr2=*(uint32_t *)s;
 306         // 
 307         s=(char *)adr2ptr_with_data(fw,adr2);
 308         if(!s) {
 309             //printf("s2 bad ptr 0x%08x\n",adr2);
 310             return;
 311         }
 312         if(!isASCIIstring(fw,adr2)) {
 313             //printf("s2 not ASCII 0x%08x\n",adr2);
 314             return;
 315         }
 316         plevel++;
 317     }
 318     int l=strlen(comment);
 319     // remaining space
 320     // TODO might want to limit max string to a modest number of chars
 321     int r=COMMENT_LEN - (l + 4 + plevel); // 4 for space, "" and possibly one inserted backslash
 322     if(r < 1) {
 323         return;
 324     }
 325     char *p=comment+l;
 326     *p++=' ';
 327     int i;
 328     for(i=0;i<plevel;i++) {
 329         *p++='*';
 330     }
 331     *p++='"';
 332     char *e;
 333     int trunc=0;
 334     if(strlen(s) < r) {
 335         e=p+strlen(s);
 336     } else {
 337         // not enough space for ellipsis, give up
 338         if(r <= 3) {
 339             *p++='"';
 340             *p++=0;
 341             return;
 342         }
 343         e=p+r - 3;
 344         trunc=1;
 345     }
 346     while(p < e) {
 347         if(*s == '\r') {
 348             *p++='\\'; 
 349             *p++='r';
 350         } else if(*s == '\n') {
 351             *p++='\\';
 352             *p++='n';
 353         } else {
 354             *p++=*s;
 355         }
 356         s++;
 357     }
 358     if(trunc) {
 359         strcpy(p,"...");
 360         p+=3;
 361     }
 362     *p++='"';
 363     *p++=0;
 364 }
 365 
 366 #define DIS_OPT_LABELS          0x00000001
 367 #define DIS_OPT_SUBS            0x00000002
 368 #define DIS_OPT_CONSTS          0x00000004
 369 #define DIS_OPT_FMT_CHDK        0x00000008
 370 #define DIS_OPT_FMT_OBJDUMP     0x00000010
 371 #define DIS_OPT_ADR_LDR         0x00000020
 372 #define DIS_OPT_STR             0x00000040
 373 #define DIS_OPT_STUBS           0x00000080
 374 #define DIS_OPT_STUBS_LABEL     0x00000100
 375 #define DIS_OPT_PROPS           0x00000200
 376 #define DIS_OPT_JMP_BACK        0x00000400
 377 
 378 #define DIS_OPT_DETAIL_GROUP    0x00010000
 379 #define DIS_OPT_DETAIL_OP       0x00020000
 380 #define DIS_OPT_DETAIL_ADDR     0x00040000
 381 #define DIS_OPT_DETAIL_BIN      0x00080000
 382 #define DIS_OPT_DETAIL_CONST    0x00100000
 383 #define DIS_OPT_DETAIL_ALL      (DIS_OPT_DETAIL_GROUP\
 384                                     |DIS_OPT_DETAIL_OP\
 385                                     |DIS_OPT_DETAIL_ADDR\
 386                                     |DIS_OPT_DETAIL_BIN\
 387                                     |DIS_OPT_DETAIL_CONST)
 388 
 389 // add comments for adr if it is a ref known sub or string
 390 void describe_const_op(firmware *fw, unsigned dis_opts, char *comment, uint32_t adr)
 391 {
 392     osig* ostub=NULL;
 393     if(dis_opts & DIS_OPT_STUBS) {
 394         ostub = find_sig_val(fw->sv->stubs,adr);
 395         if(!ostub) {
 396             uint32_t *p=(uint32_t *)adr2ptr(fw,adr);
 397             if(p) {
 398                 ostub = find_sig_val(fw->sv->stubs,*p);
 399             }
 400         }
 401         // found, and not null
 402         if(ostub && ostub->val) {
 403             // TODO overflow
 404             strcat(comment," ");
 405             strcat(comment,ostub->nm);
 406             // don't bother trying to comment as string
 407             return;
 408         }
 409     }
 410     if(dis_opts & DIS_OPT_STR) {
 411         describe_str(fw,comment,adr);
 412     }
 413 }
 414 
 415 void describe_prop_call(firmware *fw,iter_state_t *is, unsigned dis_opts, char *comment, uint32_t target)
 416 {
 417     if(!(dis_opts & DIS_OPT_PROPS) || (target != get_prop && target != set_prop)) {
 418         return;
 419     }
 420 
 421     uint32_t regs[4];
 422     // backtrack up to 6 instructions, looking for r0 (propcase ID)
 423     if((get_call_const_args(fw,is,6,regs)&1)!=1) {
 424         return;
 425     }
 426     osig* ostub = find_sig_val(fw->sv->propcases,regs[0]);
 427     if(ostub && ostub->val) {
 428         sprintf(comment+strlen(comment)," %s (%d) ",ostub->nm,ostub->val);
 429     } else {
 430         sprintf(comment+strlen(comment)," (%d) ",regs[0]); // print number on propcase call line for easier searching
 431     }
 432 }
 433 
 434 // if branch insn fill in / modify ops, comment as needed, return 1
 435 // TODO code common with do_dis_call should be refactored
 436 int do_dis_branch(firmware *fw, iter_state_t *is, unsigned dis_opts, char *mnem, char *ops, char *comment)
 437 {
 438     uint32_t target = B_target(fw,is->insn);
 439     char op_pfx[8]="";
 440     if(!target) {
 441         target = CBx_target(fw,is->insn);
 442         if(!target) {
 443             return 0;
 444         }
 445         sprintf(op_pfx,"%s, ",cs_reg_name(is->cs_handle,is->insn->detail->arm.operands[0].reg));
 446     }
 447     osig* ostub=NULL;
 448     char *subname=NULL;
 449     uint32_t j_target=0;
 450     if(dis_opts & DIS_OPT_STUBS) {
 451        // search for current thumb state (there is no BX imm, don't currently handle ldr pc,...)
 452        ostub = find_sig_val(fw->sv->stubs,target|is->thumb);
 453        if(ostub) {
 454            if(ostub->is_comment) {
 455                strcat(comment,ostub->nm);
 456            } else {
 457                subname=ostub->nm;
 458            }
 459        } else {
 460            // check for veneer to known function
 461             if(fw_disasm_iter_single(fw,target|is->thumb)) {
 462                 j_target=get_direct_jump_target(fw,fw->is);
 463                 if(j_target) {
 464                     ostub = find_sig_val(fw->sv->stubs,j_target);
 465                     if(ostub) {
 466                         strcat(comment,"-> ");
 467                         strcat(comment,ostub->nm);
 468                     }
 469                 }
 470             }
 471         }
 472     }
 473     if(dis_opts & DIS_OPT_LABELS) {
 474         if(subname) {
 475             if(dis_opts & DIS_OPT_FMT_CHDK) {
 476                 sprintf(ops,"%s_%s",op_pfx,subname);
 477             } else {
 478                 sprintf(ops,"%s%s",op_pfx,subname);
 479             }
 480         } else {
 481             sprintf(ops,"%sloc_%08x",op_pfx,target);
 482         }
 483     } else if (subname) {
 484        strcat(comment,subname);
 485     }
 486     if(j_target) {
 487         describe_prop_call(fw,is,dis_opts,comment,j_target|is->thumb);
 488     } else {
 489         describe_prop_call(fw,is,dis_opts,comment,target|is->thumb);
 490     }
 491     return 1;
 492 }
 493 
 494 // if function call insn fill in / modify ops, comment as needed, return 1
 495 int do_dis_call(firmware *fw, iter_state_t *is, unsigned dis_opts, char *mnem, char *ops, char *comment)
 496 {
 497     if(!((is->insn->id == ARM_INS_BL || is->insn->id == ARM_INS_BLX) 
 498             && is->insn->detail->arm.operands[0].type == ARM_OP_IMM)) {
 499         return 0;
 500     }
 501 
 502     uint32_t target = get_branch_call_insn_target(fw,is); // target with thumb bit set appropriately
 503     osig* ostub=NULL;
 504     char *subname=NULL;
 505     uint32_t j_target=0;
 506     if(dis_opts & DIS_OPT_STUBS) {
 507        ostub = find_sig_val(fw->sv->stubs,target);
 508        if(ostub) {
 509            if(ostub->is_comment) {
 510                strcat(comment,ostub->nm);
 511            } else {
 512                subname=ostub->nm;
 513            }
 514        } else {
 515            // check for veneer to known function
 516             if(fw_disasm_iter_single(fw,target)) {
 517                 j_target=get_direct_jump_target(fw,fw->is);
 518                 if(j_target) {
 519                     ostub = find_sig_val(fw->sv->stubs,j_target);
 520                     if(ostub) {
 521                         // TODO could translate directly into sub call, but need to resolve BL vs BLX
 522                         strcat(comment,"-> ");
 523                         strcat(comment,ostub->nm);
 524                     }
 525                 }
 526             }
 527        }
 528     }
 529     if(dis_opts & DIS_OPT_SUBS) {
 530         if(subname) {
 531             if(dis_opts & DIS_OPT_FMT_CHDK) {
 532                 sprintf(ops,"_%s",subname);
 533             } else {
 534                 strcpy(ops,subname);
 535             }
 536         } else {
 537             sprintf(ops,"sub_%08x",ADR_CLEAR_THUMB(target));
 538         }
 539     } else if (subname) {
 540        strcat(comment,subname);
 541     }
 542     if(j_target) {
 543         describe_prop_call(fw,is,dis_opts,comment,j_target);
 544     } else {
 545         describe_prop_call(fw,is,dis_opts,comment,target);
 546     }
 547     return 1;
 548 }
 549 
 550 static void do_dis_insn(
 551                     firmware *fw,
 552                     iter_state_t *is,
 553                     unsigned dis_opts,
 554                     char *mnem,
 555                     char *ops,
 556                     char *comment,
 557                     tbx_info_t *ti) {
 558 
 559     cs_insn *insn=is->insn;
 560 
 561     // default - use capstone disasm text as-is
 562     strcpy(mnem,insn->mnemonic);
 563     strcpy(ops,insn->op_str);
 564     comment[0]=0;
 565     // handle special cases, naming etc
 566     if(do_dis_branch(fw,is,dis_opts,mnem,ops,comment)) {
 567         return;
 568     }
 569     if(do_dis_call(fw,is,dis_opts,mnem,ops,comment)) {
 570         return;
 571     }
 572     if((dis_opts & (DIS_OPT_CONSTS|DIS_OPT_DETAIL_CONST)) && isLDR_PC(insn))  {
 573         // get address for display, rather than LDR_PC2valptr directly
 574         uint32_t ad=LDR_PC2adr(fw,insn);
 575         uint32_t *pv=(uint32_t *)adr2ptr(fw,ad);
 576         // don't handle neg scale for now, make sure not out of bounds
 577         if(pv) {
 578             if(dis_opts & DIS_OPT_CONSTS) {
 579                 sprintf(ops,"%s, =0x%08x",
 580                         cs_reg_name(is->cs_handle,insn->detail->arm.operands[0].reg),
 581                         *pv);
 582                 if(dis_opts & DIS_OPT_DETAIL_CONST) {
 583                     // show pc + and calculated addr
 584                     sprintf(comment,"[pc, #%d] (0x%08x)",insn->detail->arm.operands[1].mem.disp,ad);
 585                 }
 586             } else if(dis_opts & DIS_OPT_DETAIL_CONST) {
 587                 // thumb2dis.pl style
 588                 sprintf(comment,"0x%08x: (%08x)",ad,*pv);
 589             }
 590             describe_const_op(fw,dis_opts,comment,ad);
 591         } else {
 592             sprintf(comment,"WARNING didn't convert PC rel to constant!");
 593         }
 594     } else if((dis_opts & (DIS_OPT_CONSTS|DIS_OPT_DETAIL_CONST)) && isADRx(insn))  {
 595         unsigned ad=ADRx2adr(fw,insn);
 596         uint32_t *pv=(uint32_t *)adr2ptr(fw,ad);
 597         if(pv) {
 598             if(dis_opts & DIS_OPT_ADR_LDR) {
 599                 strcpy(mnem,"ldr");
 600                 sprintf(ops,"%s, =0x%08x",
 601                         cs_reg_name(is->cs_handle,insn->detail->arm.operands[0].reg),
 602                         ad);
 603                 if(dis_opts & DIS_OPT_DETAIL_CONST) {
 604                     // show original op
 605                     if(insn->id == ARM_INS_ADR) {
 606                         sprintf(comment,"adr %s, #%x (0x%08x)",
 607                                 cs_reg_name(is->cs_handle,insn->detail->arm.operands[0].reg), 
 608                                 insn->detail->arm.operands[1].imm,
 609                                 *pv);
 610                     } else {
 611                         sprintf(comment,"%s %s, pc, #%x (0x%08x)",
 612                                 insn->mnemonic,
 613                                 cs_reg_name(is->cs_handle,insn->detail->arm.operands[0].reg), 
 614                                 insn->detail->arm.operands[2].imm,
 615                                 *pv);
 616                     }
 617                 }
 618             } else {
 619                 if(insn->id == ARM_INS_ADR) {
 620                     if(dis_opts & DIS_OPT_FMT_OBJDUMP) {
 621                         strcpy(mnem,"add");
 622                         sprintf(ops,"%s, pc, #%d",
 623                                 cs_reg_name(is->cs_handle,insn->detail->arm.operands[0].reg), 
 624                                 insn->detail->arm.operands[1].imm);
 625                     } // else keep as adr
 626                 }
 627                 if(dis_opts & DIS_OPT_DETAIL_CONST) {
 628                     // thumb2dis.pl style
 629                     sprintf(comment,"0x%08x: (%08x)",ad,*pv);
 630                 }
 631             }
 632             describe_const_op(fw,dis_opts,comment,ad);
 633         } else {
 634             sprintf(comment,"WARNING didn't convert ADR/ADD/SUB Rd, PC, #x to constant!");
 635         }
 636     } else if(get_TBx_PC_info(fw,is,ti)) {
 637         sprintf(comment+strlen(comment),
 638                     "(jumptable r%d %d elements)",
 639                     insn->detail->arm.operands[0].mem.index - ARM_REG_R0,
 640                     ti->count);
 641     }
 642     // else ... default disassembly
 643 }
 644 
 645 void do_adr_label(firmware *fw, struct llist **branch_list, iter_state_t *is, unsigned dis_opts)
 646 {
 647 
 648     uint32_t adr=is->insn->address;
 649     osig* ostub = NULL;
 650 
 651     if(dis_opts & DIS_OPT_STUBS_LABEL) {
 652        // assume stub was recorded with same thumb state we are disassembling
 653        ostub = find_sig_val(fw->sv->stubs,adr|is->thumb);
 654        // TODO just comment for now
 655        if(ostub) {
 656            printf("%s %s 0x%08x\n",comment_start,ostub->nm,ostub->val);
 657        }
 658     }
 659     if(dis_opts & DIS_OPT_LABELS) {
 660         struct lnode *label = addr_hash_get(branch_list,adr);
 661         if(label) {
 662             if(dis_opts & DIS_OPT_FMT_CHDK) {
 663                 printf("\"");
 664             }
 665             printf("loc_%08x:", label->address);
 666             if(dis_opts & DIS_OPT_FMT_CHDK) {
 667                 printf("\\n\"");
 668             }
 669             printf("\n");
 670         }
 671     }
 672 }
 673 // TODO doesn't pay attention to various dis opts
 674 static void do_tbb_data(firmware *fw, iter_state_t *is, unsigned dis_opts, tbx_info_t *ti)
 675 {
 676     uint32_t adr=ti->start;
 677     if(dis_opts & DIS_OPT_FMT_CHDK) {
 678         printf("\"branchtable_%08x:\\n\"\n",adr);
 679     } else {
 680         printf("branchtable_%08x:\n",adr);
 681     }
 682     int i=0;
 683     while(i < ti->count) {
 684         uint8_t *p=adr2ptr(fw,adr);
 685         if(!p) {
 686             fprintf(stderr,"do_tbb_data: jumptable outside of valid address range at 0x%08x\n",adr);
 687             break;
 688         }
 689         uint32_t target = ti->start+2**p;
 690         // shouldn't happen in normal code? may indicate problem?
 691         if(target <= adr) {
 692             if(dis_opts & DIS_OPT_FMT_CHDK) {
 693                 printf("\"    .byte 0x%02x\\n\" %s invalid juptable data?\n",*p,comment_start);
 694             } else {
 695                 printf("    .byte 0x%02x ; invalid juptable data?\n",*p);
 696             }
 697             i++;
 698             adr++;
 699             continue;
 700         }
 701         if(dis_opts & DIS_OPT_FMT_CHDK) {
 702             printf("\"    .byte((loc_%08x - branchtable_%08x) / 2)\\n\" %s (case %d)\n",target,ti->start,comment_start,i);
 703         } else {
 704             printf("    .byte 0x%02x %s jump to loc_%08x (case %d)\n",*p,comment_start,target,i);
 705         }
 706         adr++;
 707         i++;
 708     }
 709     if(dis_opts & DIS_OPT_FMT_CHDK) {
 710 // note, this is halfword aligned https://sourceware.org/binutils/docs/as/Align.html#Align
 711 // "For other systems, including ppc, i386 using a.out format, arm and strongarm, 
 712 // it is the number of low-order zero bits the location counter must have after advancement."
 713         printf("\".align 1\\n\"\n");
 714     }
 715     // ensure final adr is halfword aligned
 716     if(adr & 1) {
 717         uint8_t *p=adr2ptr(fw,adr);
 718         if(p) {
 719             if(!(dis_opts & DIS_OPT_FMT_CHDK)) {
 720                 printf("    .byte 0x%02x\n",*p);
 721             }
 722         } else { 
 723             fprintf(stderr,"do_tbb_data: jumptable outside of valid address range at 0x%08x\n",adr);
 724         }
 725         adr++;
 726     }
 727     // skip over jumptable
 728     if(!disasm_iter_init(fw,is,adr | is->thumb)) {
 729         fprintf(stderr,"do_tbb_data: disasm_iter_init failed\n");
 730     }
 731 }
 732 
 733 // TODO
 734 // mostly copy/pasted from tbb
 735 // doesn't pay attention to various dis opts
 736 static void do_tbh_data(firmware *fw, iter_state_t *is, unsigned dis_opts, tbx_info_t *ti)
 737 {
 738     uint32_t adr=ti->start;
 739     if(dis_opts & DIS_OPT_FMT_CHDK) {
 740         printf("\"branchtable_%08x:\\n\"\n",adr);
 741     } else {
 742         printf("branchtable_%08x:\n",adr);
 743     }
 744     int i=0;
 745     while(i < ti->count) {
 746         uint16_t *p=(uint16_t *)adr2ptr(fw,adr);
 747         if(!p) {
 748             fprintf(stderr,"do_tbh_data: jumptable outside of valid address range at 0x%08x\n",adr);
 749             break;
 750         }
 751         uint32_t target = ti->start+2**p;
 752         // shouldn't happen in normal code? may indicate problem?
 753         if(target <= adr) {
 754             if(dis_opts & DIS_OPT_FMT_CHDK) {
 755                 printf("\"    .short 0x%04x\\n\" %s invalid juptable data?\n",*p,comment_start);
 756             } else {
 757                 printf("    .short 0x%04x ; invalid juptable data?\n",*p);
 758             }
 759             i++;
 760             adr+=ti->bytes;
 761             continue;
 762         }
 763         if(dis_opts & DIS_OPT_FMT_CHDK) {
 764             printf("\"    .short((loc_%08x - branchtable_%08x) / 2)\\n\" %s (case %d)\n",target,ti->start,comment_start,i);
 765         } else {
 766             printf("    .short 0x%04x %s jump to loc_%08x (case %d)\n",*p,comment_start,target,i);
 767         }
 768         adr+=ti->bytes;
 769         i++;
 770     }
 771     // tbh shouldn't need any alignment fixup
 772     // skip over jumptable
 773     if(!disasm_iter_init(fw,is,adr | is->thumb)) {
 774         fprintf(stderr,"do_tbh_data: disasm_iter_init failed\n");
 775     }
 776 }
 777 
 778 static void do_tbx_pass1(firmware *fw, iter_state_t *is, struct llist **branch_list, unsigned dis_opts, tbx_info_t *ti)
 779 {
 780     uint32_t adr=ti->start;
 781     int i=0;
 782     while(i < ti->count) {
 783         uint8_t *p=adr2ptr(fw,adr);
 784         if(!p) {
 785             fprintf(stderr,"do_tbb_data: jumptable outside of valid address range at 0x%08x\n",adr);
 786             break;
 787         }
 788         uint16_t off;
 789         if(ti->bytes==1) {
 790             off=(uint16_t)*p;
 791         } else {
 792             off=*(uint16_t *)p;
 793         }
 794         uint32_t target = ti->start+2*off;
 795         // shouldn't happen in normal code? may indicate problem? don't add label
 796         if(target <= adr) {
 797             adr++;
 798             continue;
 799         }
 800         if(dis_opts & DIS_OPT_LABELS) {
 801             addr_hash_add(branch_list,target);
 802         }
 803         adr+=ti->bytes;
 804         i++;
 805     }
 806     // ensure final adr is halfword aligned (should only apply to tbb)
 807     if(adr & 1) {
 808         adr++;
 809     }
 810     // skip over jumptable data
 811     if(!disasm_iter_init(fw,is,adr | is->thumb)) {
 812         fprintf(stderr,"do_tbb_data: disasm_iter_init failed\n");
 813     }
 814 }
 815 
 816 // TODO should have a tbb to tbh mode for CHDK, to relax range limits
 817 static void do_tbx_data(firmware *fw, iter_state_t *is, unsigned dis_opts, tbx_info_t *ti)
 818 {
 819     if(ti->bytes==1) {
 820         do_tbb_data(fw,is,dis_opts,ti);
 821     } else {
 822         do_tbh_data(fw,is,dis_opts,ti);
 823     }
 824 }
 825 
 826 static void do_dis_range(firmware *fw,
 827                     unsigned dis_start,
 828                     unsigned dis_count,
 829                     unsigned dis_end,
 830                     unsigned dis_opts)
 831 {
 832     iter_state_t *is=disasm_iter_new(fw,dis_start);
 833     size_t count=0;
 834     struct llist **branch_list = addr_hash_new();
 835     tbx_info_t ti;
 836 
 837     // pre-scan for branches
 838     if(dis_opts & DIS_OPT_LABELS) {
 839         // TODO should use fw_search_insn, but doesn't easily support count
 840         while(count < dis_count &&  is->adr < dis_end) {
 841             if(disasm_iter(fw,is)) {
 842                 uint32_t b_tgt=get_branch_call_insn_target(fw,is);
 843                 if(b_tgt) {
 844                     // currently ignore thumb bit
 845                     addr_hash_add(branch_list,ADR_CLEAR_THUMB(b_tgt));
 846                 } else if(get_TBx_PC_info(fw,is,&ti)) { 
 847                     // handle tbx, so instruction counts will match,
 848                     // less chance of spurious stuff from disassembling jumptable data
 849                     do_tbx_pass1(fw,is,branch_list,dis_opts,&ti);
 850                 }
 851             } else {
 852                 if(!disasm_iter_init(fw,is,(is->adr+is->insn_min_size) | is->thumb)) {
 853                     fprintf(stderr,"do_dis_range: disasm_iter_init failed\n");
 854                     break;
 855                 }
 856             }
 857             count++;
 858         }
 859     }
 860     count=0;
 861     disasm_iter_init(fw,is,dis_start);
 862     while(count < dis_count && is->adr < dis_end) {
 863         if(disasm_iter(fw,is)) {
 864             do_adr_label(fw,branch_list,is,dis_opts);
 865             if(!(dis_opts & DIS_OPT_FMT_OBJDUMP) // objdump format puts these on same line as instruction
 866                 && (dis_opts & (DIS_OPT_DETAIL_ADDR | DIS_OPT_DETAIL_BIN))) {
 867                 printf(comment_start);
 868                 if(dis_opts & DIS_OPT_DETAIL_ADDR) {
 869                     printf(" 0x%"PRIx64"",is->insn->address);
 870                 }
 871                 if(dis_opts & DIS_OPT_DETAIL_BIN) {
 872                     int k;
 873                     for(k=0;k<is->insn->size;k++) {
 874                         printf(" %02x",is->insn->bytes[k]);
 875                     }
 876                 }
 877                 printf("\n");
 878             }
 879             if(dis_opts & DIS_OPT_DETAIL_OP) {
 880                 describe_insn_ops(is->cs_handle,is->insn);
 881             }
 882             if(dis_opts & DIS_OPT_DETAIL_GROUP) {
 883                 describe_insn_groups(is->cs_handle,is->insn);
 884             }
 885             // mnemonic and op size from capstone.h
 886             char insn_mnemonic[32], insn_ops[160], comment[COMMENT_LEN+1];
 887             ti.start=0; // flag so we can do jump table dump below
 888             do_dis_insn(fw,is,dis_opts,insn_mnemonic,insn_ops,comment,&ti);
 889             if(dis_opts & DIS_OPT_FMT_CHDK) {
 890                 printf("\"");
 891             }
 892 /*
 893 objdump / thumb2dis format examples
 894 fc000016: \tf8df d004 \tldr.w\tsp, [pc, #4]\t; 0xfc00001c: (1ffffffc) 
 895 fc000020: \tbf00      \tnop
 896 
 897 TODO most constants are decimal, while capstone defaults to hex
 898 */
 899 
 900             if(dis_opts & DIS_OPT_FMT_OBJDUMP) {// objdump format puts these on same line as instruction
 901                 if(dis_opts & DIS_OPT_DETAIL_ADDR) {
 902                     printf("%08"PRIx64": \t",is->insn->address);
 903                 }
 904                 if(dis_opts & DIS_OPT_DETAIL_BIN) {
 905                     if(is->insn->size == 2) {
 906                         printf("%04x     ",*(unsigned short *)is->insn->bytes);
 907                     } else if(is->insn->size == 4) {
 908                         printf("%04x %04x",*(unsigned short *)is->insn->bytes,*(unsigned short *)(is->insn->bytes+2));
 909                     }
 910                 }
 911                 printf(" \t%s", insn_mnemonic);
 912                 if(strlen(insn_ops)) {
 913                     printf("\t%s",insn_ops);
 914                 }
 915                 if(strlen(comment)) {
 916                     printf("\t%s %s",comment_start,comment);
 917                 }
 918             } else {
 919                 printf("    %-7s", insn_mnemonic);
 920                 if(strlen(insn_ops)) {
 921                     printf(" %s",insn_ops);
 922                 }
 923                 if(dis_opts & DIS_OPT_FMT_CHDK) {
 924                     printf("\\n\"");
 925                 }
 926                 if(strlen(comment)) {
 927                     printf(" %s %s",comment_start,comment);
 928                 }
 929             }
 930             printf("\n");
 931             if(ti.start) {
 932                 do_tbx_data(fw,is,dis_opts,&ti);
 933             }
 934         } else {
 935             uint16_t *pv=(uint16_t *)adr2ptr(fw,is->adr);
 936             // TODO optional data directives
 937             if(pv) {
 938                 if(is->thumb) {
 939                     printf("%s %04x\n",comment_start,*pv);
 940                 } else {
 941                     printf("%s %04x %04x\n",comment_start,*pv,*(pv+1));
 942                 }
 943             } else {
 944                 printf("%s invalid address %"PRIx64"\n",comment_start,is->adr);
 945             }
 946             if(!disasm_iter_init(fw,is,(is->adr+is->insn_min_size)|is->thumb)) {
 947                 fprintf(stderr,"do_dis_range: disasm_iter_init failed\n");
 948                 break;
 949             }
 950         }
 951         count++;
 952     }
 953     if(dis_opts & DIS_OPT_JMP_BACK) {
 954         if(dis_opts & DIS_OPT_FMT_CHDK) {
 955             printf("\"");
 956         }
 957         // note objdump format ignored, doesn't really make sense with jump back anyway
 958         printf("    ldr     pc, =0x%"PRIx64,is->adr|is->thumb);
 959         if(dis_opts & DIS_OPT_FMT_CHDK) {
 960             printf("\\n\"");
 961         }
 962         printf(" %s Continue in firmware\n",comment_start);
 963     }
 964     addr_hash_free(branch_list);
 965 }
 966 
 967 int main(int argc, char** argv)
 968 {
 969     char stubs_dir[256]="";
 970     char *dumpname=NULL;
 971     unsigned load_addr=0xFFFFFFFF;
 972     unsigned offset=0xFFFFFFFF;
 973     unsigned dis_start=0;
 974     const char *dis_start_fn=NULL;
 975     unsigned dis_end=0;
 976     unsigned dis_count=0;
 977     int do_fw_data_init=1;
 978     int verbose=0;
 979     unsigned dis_opts=(DIS_OPT_LABELS|DIS_OPT_SUBS|DIS_OPT_CONSTS|DIS_OPT_STR);
 980     int dis_arch=FW_ARCH_ARMv7;
 981     int do_propset=0;
 982     if(argc < 2) {
 983         usage();
 984     }
 985     int i;
 986     for(i=1; i < argc; i++) {
 987         if ( strncmp(argv[i],"-c=",3) == 0 ) {
 988             dis_count=strtoul(argv[i]+3,NULL,0);
 989         }
 990         else if ( strncmp(argv[i],"-o=",3) == 0 ) {
 991             offset=strtoul(argv[i]+3,NULL,0);
 992         }
 993         else if ( strncmp(argv[i],"-s=",3) == 0 ) {
 994             if(!isdigit(argv[i][3])) {
 995                 dis_start_fn = argv[i]+3;
 996             } else {
 997                 dis_start=strtoul(argv[i]+3,NULL,0);
 998             }
 999         }
1000         else if ( strncmp(argv[i],"-e=",3) == 0 ) {
1001             dis_end=strtoul(argv[i]+3,NULL,0);
1002         }
1003         else if ( strncmp(argv[i],"-f=",3) == 0 ) {
1004             if ( strcmp(argv[i]+3,"chdk") == 0 ) {
1005                 dis_opts |= DIS_OPT_FMT_CHDK;
1006                 dis_opts |= DIS_OPT_ADR_LDR;
1007             } else if ( strcmp(argv[i]+3,"objdump") == 0 ) {
1008                 dis_opts |= DIS_OPT_FMT_OBJDUMP;
1009             } else {
1010                 fprintf(stderr,"unknown output format %s\n",argv[i]+3);
1011                 usage();
1012             }
1013         }
1014         else if ( strcmp(argv[i],"-armv5") == 0 ) {
1015             dis_arch=FW_ARCH_ARMv5;
1016         }
1017         else if ( strcmp(argv[i],"-stubs") == 0 ) {
1018             strcpy(stubs_dir,".");
1019         }
1020         else if ( strncmp(argv[i],"-stubs=",7) == 0 ) {
1021             strcpy(stubs_dir,argv[i]+7);
1022         }
1023         else if ( strncmp(argv[i],"-props=",7) == 0 ) {
1024             do_propset=strtoul(argv[i]+7,NULL,0);
1025         }
1026         else if ( strcmp(argv[i],"-v") == 0 ) {
1027             verbose++;
1028         }
1029         else if ( strcmp(argv[i],"-d") == 0 ) {
1030             dis_opts |= DIS_OPT_DETAIL_ALL;
1031         }
1032         else if ( strcmp(argv[i],"-noloc") == 0 ) {
1033             dis_opts &= ~DIS_OPT_LABELS;
1034         }
1035         else if ( strcmp(argv[i],"-nosub") == 0 ) {
1036             dis_opts &= ~DIS_OPT_SUBS;
1037         }
1038         else if ( strcmp(argv[i],"-noconst") == 0 ) {
1039             dis_opts &= ~DIS_OPT_CONSTS;
1040         }
1041         else if ( strcmp(argv[i],"-nostr") == 0 ) {
1042             dis_opts &= ~DIS_OPT_STR;
1043         }
1044         else if ( strcmp(argv[i],"-noadrldr") == 0 ) {
1045             dis_opts &= ~DIS_OPT_ADR_LDR;
1046         }
1047         else if ( strcmp(argv[i],"-nofwdata") == 0 ) {
1048             do_fw_data_init = 0;
1049         }
1050         else if ( strcmp(argv[i],"-adrldr") == 0 ) {
1051             dis_opts |= DIS_OPT_ADR_LDR;
1052         }
1053         else if ( strcmp(argv[i],"-jfw") == 0 ) {
1054             dis_opts |= DIS_OPT_JMP_BACK;
1055         }
1056         else if ( strcmp(argv[i],"-d-const") == 0 ) {
1057             dis_opts |= DIS_OPT_DETAIL_CONST;
1058         }
1059         else if ( strcmp(argv[i],"-d-group") == 0 ) {
1060             dis_opts |= DIS_OPT_DETAIL_GROUP;
1061         }
1062         else if ( strcmp(argv[i],"-d-op") == 0 ) {
1063             dis_opts |= DIS_OPT_DETAIL_OP;
1064         }
1065         else if ( strcmp(argv[i],"-d-addr") == 0 ) {
1066             dis_opts |= DIS_OPT_DETAIL_ADDR;
1067         }
1068         else if ( strcmp(argv[i],"-d-bin") == 0 ) {
1069             dis_opts |= DIS_OPT_DETAIL_BIN;
1070         }
1071         else if ( argv[i][0]=='-' ) {
1072             fprintf(stderr,"unknown option %s\n",argv[i]);
1073             usage();
1074         } else {
1075             if(!dumpname) {
1076                 dumpname=argv[i];
1077             } else if(load_addr == 0xFFFFFFFF) {
1078                 load_addr=strtoul(argv[i],NULL,0);
1079             } else {
1080                 fprintf(stderr,"unexpected %s\n",argv[i]);
1081                 usage();
1082             }
1083         }
1084     }
1085     if(!dumpname) {
1086         fprintf(stderr,"missing input file\n");
1087         usage();
1088     }
1089     struct stat st;
1090     if(stat(dumpname,&st) != 0) {
1091         fprintf(stderr,"bad input file %s\n",dumpname);
1092         return 1;
1093     }
1094     int dumpsize = st.st_size;
1095 
1096     if(load_addr==0xFFFFFFFF) {
1097         fprintf(stderr,"missing load address\n");
1098         usage();
1099     }
1100 
1101     firmware fw;
1102     if(stubs_dir[0]) {
1103         dis_opts |= (DIS_OPT_STUBS|DIS_OPT_STUBS_LABEL); // TODO may want to split various places stubs names could be used
1104         fw.sv = new_stub_values();
1105         char stubs_path[300];
1106         sprintf(stubs_path,"%s/%s",stubs_dir,"funcs_by_name.csv");
1107         load_funcs(fw.sv, stubs_path);
1108         sprintf(stubs_path,"%s/%s",stubs_dir,"stubs_entry.S");
1109         load_stubs(fw.sv, stubs_path, 1);
1110         sprintf(stubs_path,"%s/%s",stubs_dir,"stubs_entry_2.S");
1111         load_stubs(fw.sv, stubs_path, 1);   // Load second so values override stubs_entry.S
1112     }
1113     if(do_propset) {
1114         fw.sv->propcases = NULL;
1115         if(!(dis_opts & DIS_OPT_STUBS)) {
1116             fprintf(stderr,"-props requires -stubs\n");
1117             usage();
1118         }
1119         char props_path[300];
1120         sprintf(props_path,"%s/../../../../include/propset%d.h",stubs_dir,do_propset);
1121         // printf("load props %s %d\n",props_path,do_propset);
1122         load_propcases(fw.sv, props_path);
1123         if(!fw.sv->propcases) {
1124             fprintf(stderr,"propset %d load failed\n",do_propset);
1125             exit(1);
1126         }
1127         osig *ostub=find_sig(fw.sv->stubs,"SetPropertyCase");
1128         if(!ostub || !ostub->val) {
1129             fprintf(stderr,"SetPropertyCase not found, ignoring -props\n");
1130             set_prop = 0;
1131         } else {
1132             set_prop = ostub->val;
1133         }
1134         ostub=find_sig(fw.sv->stubs,"GetPropertyCase");
1135         if(!ostub || !ostub->val) {
1136             fprintf(stderr,"GetPropertyCase not found, ignoring -props\n");
1137             get_prop = 0;
1138         } else {
1139             get_prop = ostub->val;
1140         }
1141         if(get_prop && set_prop) {
1142             dis_opts |= DIS_OPT_PROPS;
1143         } else {
1144             do_propset=0;
1145         }
1146     }
1147     if(dis_start_fn) {
1148         if(!stubs_dir[0]) {
1149             fprintf(stderr,"-s with function name requires -stubs (did you forget an 0x?)\n");
1150             usage();
1151         }
1152         osig *ostub=find_sig(fw.sv->stubs,dis_start_fn);
1153         if(!ostub || !ostub->val) {
1154             fprintf(stderr,"start function %s not found (did you forget an 0x?)\n",dis_start_fn);
1155             return 1;
1156         }
1157         dis_start=ostub->val;
1158     }
1159 
1160     if(offset != 0xFFFFFFFF) {
1161         if(dis_start) {
1162             fprintf(stderr,"both start and and offset given\n");
1163             usage();
1164         }
1165         dis_start = offset+load_addr;
1166     }
1167     if(dis_end) {
1168         if(dis_count) {
1169             fprintf(stderr,"both end and count given\n");
1170             usage();
1171         }
1172         if(dis_end > load_addr+dumpsize) {
1173             fprintf(stderr,"end > load address + size\n");
1174             usage();
1175         }
1176         if(dis_end < dis_start) {
1177             fprintf(stderr,"end < start\n");
1178             usage();
1179         }
1180         dis_count=(dis_end-dis_start)/2; // need a count for do_dis_range, assume all 16 bit ins
1181     }
1182     if(dis_count==0) {
1183         fprintf(stderr,"missing instruction count\n");
1184         usage();
1185     }
1186     // count, no end
1187     if(dis_end==0) {
1188         dis_end=dis_start + dis_count * 4; // assume all 32 bit ins
1189     }
1190     if((dis_opts & (DIS_OPT_FMT_CHDK | DIS_OPT_FMT_OBJDUMP)) == (DIS_OPT_FMT_CHDK | DIS_OPT_FMT_OBJDUMP)) {
1191         fprintf(stderr,"multiple -f options not allowed\n");
1192         usage();
1193     }
1194     if(dis_opts & (DIS_OPT_FMT_CHDK)) {
1195         comment_start = "//";
1196     }
1197     
1198     firmware_load(&fw,dumpname,load_addr,dis_arch); 
1199     firmware_init_capstone(&fw);
1200     if(do_fw_data_init) {
1201         firmware_init_data_ranges(&fw);
1202     }
1203 
1204     // check for RAM code address
1205     if(dis_start < fw.base) {
1206         adr_range_t *ar=adr_get_range(&fw,dis_start);
1207         if(!ar || ar->type != ADR_RANGE_RAM_CODE) {
1208             fprintf(stderr,"invalid start address 0x%08x\n",dis_start);
1209             return 1;
1210         }
1211     }
1212 
1213     if(verbose) {
1214         printf("%s %s size:0x%x start:0x%x instructions:%d opts:0x%x\n",comment_start,dumpname,dumpsize,dis_start,dis_count,dis_opts);
1215     }
1216 
1217 
1218     do_dis_range(&fw, dis_start, dis_count, dis_end, dis_opts);
1219 
1220     firmware_unload(&fw);
1221 
1222 
1223     return 0;
1224 }

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