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

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