root/tools/code_gen.c

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

DEFINITIONS

This source file includes following definitions.
  1. skip_space
  2. skip_token
  3. skip_string
  4. next_token
  5. parse_line
  6. next_line
  7. print_args
  8. set_op_name
  9. set_op_comment
  10. new_op
  11. chk_args
  12. parse_FILE
  13. parse_ENDFILE
  14. parse_COPY
  15. parse_FUNC
  16. parse_ENDFUNC
  17. parse_ASM
  18. parse_ENDASM
  19. parse_FW
  20. parse_PATCHSUB
  21. parse_PATCHVAL
  22. parse_REM
  23. parse_SKIP
  24. parse_CONTFW
  25. parse_LI
  26. op_prelabel
  27. op_COPY
  28. op_FUNC
  29. op_FW
  30. op_PATCHSUB
  31. op_PATCHVAL
  32. op_REM
  33. do_ops
  34. run_op
  35. parse_ops
  36. usage
  37. error
  38. main

   1 /*
   2  * CHDK code_gen
   3  *      automate generation of boot.c, capt_seq.c and movie_rec.c source files for CHDK
   4  *      based on chdk-dasm.c from CHDK-PT, created by waterwingz, with extensions from finsig_dryos.c
   5 *
   6 *   usage :- code_gen ROMBASE instructions_file firmware_dump_file
   7 *
   8 *   where:
   9 *       ROMBASE = start address of the firmware in the camera
  10 *       instructions_file   = filename of the instructions file used to generate the code files
  11 *       firmware_dump_file  = filename of the camera firmware dump
  12 *
  13 *   If present the progam will also read stubs_entry.S and stubs_entry_2.S to get function names and addresses
  14 */
  15 
  16 /*
  17     Notes.
  18     ======
  19 
  20     The purpose of this program is to generate the source code files used to boot CHDK and override / patch
  21     the default firmware behaviour. This is done by mimicing the normal camera startup code but with key
  22     elements replaced or changed to insert the CHDK code.
  23 
  24     A key component of this is to disassemble the firmware code, copy it to the CHDK source file, and then
  25     patch the necessary instructions for CHDK. This can be tedious and it is easy to make mistakes.
  26 
  27     CHDK-PT provides a GUI interface to examine and dump the firmware instructions in the correct format
  28     thus achieving the first two goals.
  29 
  30     This program aims to automate the third step of applying the patches and generating final source files.
  31 
  32     Note: this program is not magic - it will still require some digging into the camera firmware disassembly
  33     and finding functions and function lengths. It may help with the overall porting process, especially for
  34     a new firmware version for an existing camera port.
  35 */
  36 
  37 /*
  38     Format of the instructions file used to generate the output code files.
  39     =======================================================================
  40 
  41     The input file is a series of instructions, one per line, that control the processing of this program.
  42     The instructions perform the following operations:
  43         - Open a new file for output
  44         - Close the output file
  45         - Copy lines from the instruction file directly to the output file unchanged
  46                 e.g. to add header sections to .c files
  47         - Copy the contents of another file to the output file
  48                 e.g. to store common code blocks in other source files
  49         - Begin disassembly of a new function
  50         - End function disassembly
  51         - Add the 'asm...' directive to the file to begin disassebled code
  52         - End the 'asm...' code block
  53         - Disassemble a block of code from the firmware into the output file
  54         - Skip over a block of firmware (e.g. DCD code)
  55         - Disassemble an instruction; but comment it out in the output file (including a comment for explanation)
  56         - Disassemble and patch an instruction that references another function
  57                 e.g. convert BL sub_XXXXXXX to BL sub_XXXXXXXX_my
  58         - Disassemble and patch the immediate value in an instruction
  59                 e.g. convert LDR R2, #0x800 to LDR R2, #0x2000
  60         - Generate a B instruction that jumps back to the firmware at the current address (continue execution in the firmware)
  61 
  62     Each line of the input file looks like:
  63         operation arguments
  64     Arguments are of the form:
  65         name
  66         value
  67         name value
  68         name=value
  69     The first form is used when no value is required, the second when a single argument is needed, and the third and fourth are equivalent.
  70 
  71     Note there is not much error checking so lines not formatted correctly may cause invalid results.
  72     
  73     Lines with unrecognised operations or blank lines are ignores - so you can document the instruction file.
  74     It is recommended to use C style single line comments (//) to document the file, multi line comments are not implemented.
  75 
  76     Notes:
  77         Operation and argument names are case sensitive.
  78         Optional arguments below are shown in square brackets.
  79         Alternate versions of an argument are shown with '|' seperating them.
  80         Arguments with spaces (e.g. comments) must be enclosed in double quotes.
  81         Numeric arguments can be entered as decimal or hex, hex arguments must begin with 0x.
  82         If no FILE instruction is active output is written to stdout
  83 
  84     The available operations and arguments are:
  85         FILE filename                               - opens a new file for output
  86         ENDFILE                                     - closes the current file
  87         LI 0|1                                      - enables (LI 1) or disables (LI 0) output of line numbers against disassembled firmware instructions 
  88                                                       useful when determining function length
  89         >>>                                         - copies all lines from the input file to the current output file until
  90                                                       a line beginning with '<<<' is found.
  91         >>> prelabel                                - as above, except the lines are copied before the 'loc_XXXXXXXX' label on the current instruction
  92                                                       if the current instruction address has a 'loc_XXXXXXXX' label (for branches, etc), the label is
  93                                                       normally written to the output file first.
  94                                                       the 'prelabel' argument causes the lines to be copied before the label.
  95         <<<                                         - ends the direct copy started above
  96         >>> file=filename                           - copies the contents of the named file to the output file (note no '<<<' is needed to end the copy)
  97         FUNC name=FuncName start=X [end=Y length=N] - starts a new function called 'FuncName' writing the prologue to the output file
  98                                                       
  99                                                       Note: one of 'end' or 'length' are required; but may be calculated (see below)
 100                                                       start = function start address in the firmware
 101                                                       end = address of the last instruciton in the function
 102                                                       length = number of instructions
 103                                                       If end is not supplied it is calculated from length & start
 104                                                       If length is not supplied it is calculated from end & start
 105                                                       Validation -> length == (end - start / 4) + 1;
 106 
 107                                                       If 'end' or 'length' are not initially supplied then the code will try and calculate them
 108                                                       - If the FUNC instructions are all fixed length (FW n, PATCHSUB, PATCHVAL, REM & SKIP) then the
 109                                                         length of each is totalled to calculate 'length'
 110                                                       - Otherwise (instructions includes 'FW $') the program searches for the next 'STMFD SP!,{...,LR}'
 111                                                         instruction. If found within 500 instructions it then searches back for the last instruction
 112                                                         that is either 'LDMFD SP!,{...,PC}', 'B', 'BX LR', or 'LDR PC,[SP,...]' (within the prev 50 instruction).
 113                                                         If found this instruction is considered the 'end' address.
 114 
 115         FUNC ref=N [end=Y length=N]                 - starts a new function; with the name and start address from a PATCHSUB (see below)
 116                                                       end and length rules as above
 117         FUNC sig=SigName [name=FuncName] [end=Y length=N]
 118                                                     - starts a new function. The address and name are looked up in the values
 119                                                       loaded from the stubs files. If supplied 'name' will override the function name from stubs.
 120                                                       end and length rules as above
 121         ENDFUNC                                     - ends the current function and writes the epiligue code
 122         ASM                                         - writes the 'asm...' stuff to begin assembly code in the .c source
 123         ENDASM                                      - ends the 'asm...' code block
 124         ENDASM prelabel                             - ends the 'asm...' code block
 125                                                       if there is a label for the next instruction output the label first
 126         FW                                          - disassemble a single instruction from the firmware to the output file at the current address
 127                                                       the initial start address is set by FUNC
 128                                                       the address is updated after each FW instruction by the number of instructions output
 129         FW n                                        - disassemble the next 'n' instructions to the output file. 
 130         FW $                                        - disassemble from the current address to the end of the function
 131         FW $-n                                      - disassemble from the current address to the end of the function - 'n' instructions
 132         FW Ln                                       - disassemble from the current address to line # 'n' in the current function (use LI 1 to see line numbers)
 133         FW [n|$|$-n|Ln] ^X                          - as above but reset the current disassembly address to X before disassembling anything
 134                                                       can be used to jump forward in the function.
 135                                                       X must be inside the function start - end range
 136         FW [n|$|$-n|Ln] -X                          - as above but reset the current function end address to X before disassembling anything
 137                                                       no idea if this is of any actual use.
 138                                                       X must be inside the function start - end range
 139         REM ["comment"]                             - Disassemble a single instruction; but comment it out in the output file
 140                                                       the optional comment is added to the output file (to explain if necessary)
 141         SKIP                                        - Updates the disassembly address by one instruction (skips over the instruction)
 142         SKIP n                                      - Updates the disassembly address by 'n' instructions (skips over the instructions)
 143         ->FW                                        - generates a 'B sub_XXXXXXXX' instruction to the current firmware address
 144                                                       used to jump back to the firmware to continue execution
 145         PATCHVAL value=N [comment="S"]              - disassembles a single instruction, replacing any immediate value in the instruction with N
 146                                                       comments are written that show the original value
 147                                                       e.g. can be used to convert
 148                                                             LDR R2, #0x800
 149                                                       to    LDR R2, #0x2000
 150                                                       if supplied the 'S' comment value is also written to the output
 151         PATCHSUB [comment="S"]                      - disassembles the next instruction, patching a function reference.
 152                                                       this changes sub_XXXXXXXX to sub_XXXXXXXX_my
 153                                                       e.g. convert
 154                                                             BL sub_FF811234
 155                                                       to    BL sub_FF811234_my
 156                                                       or convert
 157                                                             LDR R1, =sub_FF811234
 158                                                       to    LDR R1, =sub_FF811234_my
 159                                                       if supplied the 'S' comment value is also written to the output
 160         PATCHSUB name=FuncName [comment="S"]        - disassembles the next instruction, patching a function reference.
 161                                                       this changes sub_XXXXXXXX to FuncName
 162                                                       e.g. convert
 163                                                             BL sub_FF811234
 164                                                       to    BL FuncName
 165                                                       or convert
 166                                                             LDR R1, =sub_FF811234
 167                                                       to    LDR R1, =FuncName
 168                                                       if supplied the 'S' comment value is also written to the output
 169         PATCHSUB ref=N [name=FN] [comment="s"]      - as above, this disassembles and patches a function reference instruction
 170                                                       in addition this saves the function address and patched name to reference array at index 'N'
 171                                                       this can then be used in the 'FUNC ref=N' operation to retrieve the saved name and address
 172                                                       to begin a new function. This saves the effort of entering the name and address on the FUNC
 173                                                       line and makes the instructions more portable across firmware versions.
 174                                                       Up to 20 patch references can be saved.
 175 
 176     Some examples from the G12 port:
 177 
 178         FILE boot.c                                 - opens the boot.c file for output
 179 
 180         >>> file=../boot_hdr.c                      - copies the content of the common boot_hdr.c file to boot.c
 181 
 182         FUNC start=0xFF81000C length=86 name=boot   - begins disassebly of the boot loader at address FF81000C with a length of 86 instructions
 183         ASM                                         - adds the 'asm..' code
 184         FW $-1                                      - disassemble the firmware boot code up to, but not including, the last instruction
 185         PATCHSUB ref=0                              - disassemble the last instruction, with the function name patched
 186         ENDASM                                      - ends the 'asm...' code
 187         ENDFUNC                                     - ends the function (writes the epilogue)
 188 
 189         FUNC ref=0 length=27                        - starts the next function using the name and address saved in the PATCHSUB ref=0 above.
 190                                                     - this allows the instructions to be used on another port without entering new values
 191         >>>                                         - copy the lines below to the function (before the assembler code)
 192 
 193                 //http://chdk.setepontos.com/index.php/topic,4194.0.html
 194                 *(int*)0x1938=(int)taskHook;
 195                 *(int*)0x193C=(int)taskHook;
 196     
 197                 // replacement of sub_FF864BE0 for correct power-on.
 198                 *(int*)(0x25E0) = (*(int*)0xC0220108)&1 ? 0x100000 : 0x200000; 
 199 
 200         <<<                                         - stop copying
 201         ASM                                         - adds the 'asm..' code
 202         FW $-1                                      - disassemble the firmware boot code up to, but not including, the last instruction
 203         PATCHSUB ref=0                              - disassemble the last instruction, with the function name patched
 204                                                     - we reuse the same 'ref=0' since we are done with the previous values
 205         ENDASM                                      - ends the 'asm...' code
 206         ENDFUNC                                     - ends the function (writes the epilogue)
 207 
 208         ....
 209 
 210         FUNC ref=0 length=9
 211         ASM
 212         FW 7
 213         PATCHSUB name=mykbd_task                    - Patch a call so it now uses the CHDK kbd task
 214         PATCHVAL value=0x2000 comment="stack size for new task_PhySw"
 215                                                     - Patch the instruction that sets the kbd task stack size (with a comment)
 216         ->FW                                        - contine execution in the firmware
 217         ENDASM
 218         ENDFUNC
 219 
 220         ENDFILE                                     - close boot.c
 221 
 222         FILE movie_rec.c                            - open the movie_rec.c file
 223 
 224         ....
 225 
 226         FUNC sig=task_MovieRecord length=85 name=movie_record_task
 227                                                     - start the movie_record_task hook function, get the start address from stubs found
 228                                                       by finsig_dryos
 229         ASM
 230         FW 58
 231         >>>
 232 
 233         "    BL      unlock_optical_zoom\n"     // added to unlock the zoom
 234 
 235         <<<
 236         FW 3
 237         >>>
 238 
 239         "    LDR     R0, =0x7318-4\n" // <----   -4 //+ set in sub_FF9867F8 above
 240         "    BL      set_quality\n"                 //+ reset here to user value
 241 
 242         <<<
 243         FW $
 244         ENDASM
 245         ENDFUNC
 246 
 247         ....
 248 
 249         ENDFILE                                     - close movie_rec.c
 250 
 251     Another example of using multiple patch references (from the IXUS 310)
 252 
 253         ....
 254 
 255         FUNC ref=0 length=21                        - patched function using previously saved ref
 256         ASM
 257         FW 5
 258         PATCHSUB ref=0                              - we need to patch two functions inside this one, save the reference to the first
 259         FW
 260         PATCHSUB ref=1                              - and patch and save the reference to the second function
 261         FW $
 262         ENDASM
 263         ENDFUNC
 264 
 265         FUNC ref=0 length=67                        - now generate the code for the first patched function above
 266         ASM
 267         FW 37
 268         PATCHSUB ref=2                              - which calls a third one
 269         FW 12
 270         PATCHSUB                                    - this is the same function as the previous PATCHSUB so need to save the reference
 271         FW $
 272         ENDASM
 273         ENDFUNC
 274 
 275         FUNC ref=1 length=53                        - now generate the code for the second patch function above
 276         ASM
 277         FW $-3
 278         PATCHSUB
 279         FW $
 280         ENDASM
 281         ENDFUNC
 282 
 283         FUNC ref=2 length=1                         - and finally the code for the third function
 284                                                       note: we are not disassembling any of the firmware code for this function
 285                                                       instead we add some CHDK code, then jump directly to the original
 286                                                       firmware address.
 287                                                       In this case length=1 is required as length=0 is considered an error.
 288         ASM
 289         >>>
 290         "    STMFD   SP!, {R4,R5,LR} \n"
 291         "    BL      chdk_process_touch \n"
 292         "    LDMFD   SP!, {R4,R5,LR} \n"
 293         "    CMP     R0, #0 \n"
 294         "    BXNE    LR \n"
 295         <<<
 296         ->FW
 297         ENDASM
 298         ENDFUNC
 299 
 300         ....
 301  */
 302 
 303 #include <stdio.h>
 304 #include <stdlib.h>
 305 #include <stdint.h>
 306 #include <string.h>
 307 #include <ctype.h>
 308 #include <sys/stat.h>
 309 
 310 #include "stubs_load.h"
 311 #include "firmware_load.h"
 312 #include "chdk_dasm.h"
 313 
 314 //------------------------------------------------------------------------------------------------------------
 315 
 316 firmware    *fw;
 317 
 318 //------------------------------------------------------------------------------------------------------------
 319 // Parsing for instruction file
 320 int lineno;
 321 int largc;
 322 char largs[20][256];
 323 char *last_line;
 324 char token[1024];
 325 
 326 char* skip_space(char* c)
 327 {
 328     while (*c && ((*c == ' ') || (*c == '\t') || (*c == '='))) c++;
 329     return c;
 330 }
 331 
 332 char* skip_token(char* c)
 333 {
 334     while (*c && (*c != ' ') && (*c != '\t') && (*c != '=')) c++;
 335     return c;
 336 }
 337 
 338 char* skip_string(char* c)
 339 {
 340     while (*c && (*c != '"')) c++;
 341     return c;
 342 }
 343 
 344 char* next_token(char *line)
 345 {
 346     token[0] = 0;
 347     char *c = skip_space(line);
 348     char *n;
 349     if (*c)
 350     {
 351         if (*c == '"')
 352         {
 353             c++;
 354             n = skip_string(c);
 355             strncpy(token, c, n-c);
 356             token[n-c] = 0;
 357             return skip_space((*n)?n+1:n);
 358         }
 359         else
 360         {
 361             n = skip_token(c);
 362             strncpy(token, c, n-c);
 363             token[n-c] = 0;
 364             return skip_space(n);
 365         }
 366     }
 367     return c;
 368 }
 369 
 370 void parse_line(char *line)
 371 {
 372     last_line = line;
 373     largc = 0;
 374     largs[largc][0] = 0;
 375     while (line && *line)
 376     {
 377         line = next_token(line);
 378         strcpy(largs[largc++], token);
 379     }
 380 }
 381 
 382 char* next_line(char *line)
 383 {
 384     char *nxt = strchr(line,'\r');
 385     if (nxt != 0)
 386     {
 387         *nxt++ = 0;
 388         if (*nxt == '\n')
 389             nxt++;
 390     }
 391     else
 392     {
 393         nxt = strchr(line,'\n');
 394         if (nxt != 0)
 395             *nxt++ = 0;
 396     }
 397     lineno++;
 398     return nxt;
 399 }
 400 
 401 void print_args()
 402 {
 403     int n;
 404     for (n=0; n<largc; n++) fprintf(stderr,"\t%d %s\n",n,largs[n]);
 405 }
 406 
 407 //------------------------------------------------------------------------------------------------------------
 408 
 409 // Globals used for instruction processing
 410 FILE *outfile;
 411 int direct_copy;
 412 int in_func;
 413 
 414 //------------------------------------------------------------------------------------------------------------
 415 
 416 #define NULL_OP     0
 417 #define FILE_OP     1
 418 #define ENDFILE_OP  2
 419 #define COPY_OP     3
 420 #define COPY_LINE   4
 421 #define FUNC_OP     5
 422 #define ENDFUNC_OP  6
 423 #define ASM_OP      7
 424 #define ENDASM_OP   8
 425 #define FW_OP       9
 426 #define PATCHSUB_OP 10
 427 #define PATCHVAL_OP 11
 428 #define REM_OP      12
 429 #define SKIP_OP     13
 430 #define CONTFW_OP   14
 431 #define LI_OP       15
 432 
 433 typedef struct _op
 434 {
 435     struct _op  *next;
 436 
 437     int         operation;
 438     char        *source;
 439     int         lineno;
 440 
 441     char        *name;
 442     char        *comment;
 443     int         prelabel;
 444     t_address   func_start;
 445     t_address   func_end;
 446     int         func_len;
 447     int         patch_ref;
 448     t_address   fw_start;
 449     t_address   fw_end;
 450     int         fw_func_end_offset;
 451     int         fw_is_func_end_offset;
 452     int         fw_func_start_offset;
 453     int         fw_is_func_start_offset;
 454     int         fw_len;
 455     t_address   patch_new_val;
 456     int         skip_len;
 457     int         li_state;
 458 } op;
 459 
 460 op *op_head, *op_tail, *cur_func;
 461 
 462 void set_op_name(op *p, char *nm)
 463 {
 464     if (nm)
 465     {
 466         p->name = malloc(strlen(nm)+1);
 467         strcpy(p->name,nm);
 468     }
 469     else
 470     {
 471         p->name = 0;
 472     }
 473 }
 474 
 475 void set_op_comment(op *p, char *s)
 476 {
 477     if (s)
 478     {
 479         p->comment = malloc(strlen(s)+1);
 480         strcpy(p->comment,s);
 481     }
 482     else
 483     {
 484         p->comment = 0;
 485     }
 486 }
 487 
 488 op *new_op(int type)
 489 {
 490     op *p = malloc(sizeof(op));
 491 
 492     p->operation = type;
 493 
 494     p->source = malloc(strlen(last_line)+1);
 495     strcpy(p->source,last_line);
 496     p->lineno = lineno;
 497 
 498     p->name = 0;
 499     p->comment = 0;
 500     p->prelabel = 0;
 501     p->func_start = 0;
 502     p->func_end = 0;
 503     p->func_len = 0;
 504     p->patch_ref = -1;
 505     p->fw_start = 0;
 506     p->fw_end = 0;
 507     p->fw_func_end_offset = 0;
 508     p->fw_is_func_end_offset = 0;
 509     p->fw_func_start_offset = 0;
 510     p->fw_is_func_start_offset = 0;
 511     p->fw_len = -1;
 512     p->patch_new_val = 0;
 513     p->skip_len = 0;
 514     p->li_state = 0;
 515 
 516     if (op_tail)
 517     {
 518         op_tail->next = p;
 519     }
 520     else
 521     {
 522         op_head = p;
 523     }
 524 
 525     op_tail = p;
 526     p->next = 0;
 527 
 528     return p;
 529 }
 530 
 531 void chk_args(int count, char *msg, op *p)
 532 {
 533     if (largc != count+1)
 534     {
 535         fprintf(stderr,"ERROR - %s\n",msg);
 536         fprintf(stderr,"LINE - %d, SOURCE - %s\n",p->lineno,p->source);
 537         exit(1);
 538     }
 539 }
 540 
 541 //------------------------------------------------------------------------------------------------------------
 542 
 543 // Open an output file
 544 void parse_FILE()
 545 {
 546     op *p = new_op(FILE_OP);
 547     chk_args(1,"Missing FILE name",p);
 548     set_op_name(p,largs[1]);
 549 }
 550 
 551 // Close output file
 552 void parse_ENDFILE()
 553 {
 554     new_op(ENDFILE_OP);
 555 }
 556 
 557 // Direct copy input to output file
 558 void parse_COPY()
 559 {
 560     op *p = new_op(COPY_OP);
 561     int n;
 562 
 563     // Check if copy goes before or after the current instruction address label
 564     for (n=1; n<largc; n++)
 565     {
 566         if (strcmp(largs[n],"prelabel") == 0)
 567             p->prelabel = 1;
 568     }
 569 
 570     direct_copy = 1;
 571 
 572     // Check if the copy is from another source file - if so just copy that file
 573     for (n=1; n<largc; n++)
 574     {
 575         if (strcmp(largs[n],"file") == 0)
 576         {
 577             set_op_name(p,largs[++n]);
 578             direct_copy = 0;
 579         }
 580     }
 581 }
 582 
 583 // Start a new Function disassembly:
 584 //  - sets up addresses & length
 585 //  - does pass 1 & 2 of the disassembly to get the labels
 586 //  - writes the function start to the source
 587 void parse_FUNC()
 588 {
 589     op *p = new_op(FUNC_OP);
 590     int n;
 591 
 592     for (n=1; n<largc; n++)
 593     {
 594         if (strcmp(largs[n], "name") == 0)
 595         {
 596             set_op_name(p,largs[++n]);
 597         }
 598         else if (strcmp(largs[n], "start") == 0)
 599         {
 600             p->func_start = strtoul(largs[++n],0,0);
 601         }
 602         else if (strcmp(largs[n], "end") == 0)
 603         {
 604             p->func_end = strtoul(largs[++n],0,0);
 605         }
 606         else if (strcmp(largs[n], "length") == 0)
 607         {
 608             p->func_len = strtoul(largs[++n],0,0);
 609         }
 610         else if (strcmp(largs[n], "ref") == 0)
 611         {
 612             p->patch_ref = strtol(largs[++n],0,0);
 613         }
 614         else if (strcmp(largs[n], "sig") == 0)
 615         {
 616             osig *sig = find_sig(fw->sv->stubs, largs[++n]);
 617             if (sig == 0) chk_args(-1,"Can't find firmware function for 'sig='",p);
 618             p->func_start = sig->val;
 619             p->name = sig->nm;
 620         }
 621         else
 622         {
 623             chk_args(-1,"Invalid FUNC argument",p);
 624         }
 625     }
 626 }
 627 
 628 // End function, writes the function end to the file
 629 void parse_ENDFUNC()
 630 {
 631     new_op(ENDFUNC_OP);
 632 }
 633 
 634 // Writes the 'asm...' stuff to the file
 635 void parse_ASM()
 636 {
 637     new_op(ASM_OP);
 638 }
 639 
 640 // Ends the 'asm...' block
 641 void parse_ENDASM()
 642 {
 643     op *p = new_op(ENDASM_OP);
 644     int n;
 645 
 646     // Check if copy goes before or after the current instruction address label
 647     for (n=1; n<largc; n++)
 648     {
 649         if (strcmp(largs[n],"prelabel") == 0)
 650             p->prelabel = 1;
 651     }
 652 }
 653 
 654 // Disassemble a block of firmware code to the file
 655 void parse_FW()
 656 {
 657     op *p = new_op(FW_OP);
 658     int n;
 659 
 660     for (n=1; n<largc; n++)
 661     {
 662         switch (largs[n][0])
 663         {
 664         case '^':
 665             p->fw_start = strtoul(largs[n]+1,0,0);
 666             break;
 667         case '-':
 668             p->fw_end = strtoul(largs[n]+1,0,0);
 669             break;
 670         case '$':
 671             p->fw_func_end_offset = strtol(largs[n]+1,0,0) * 4;
 672             p->fw_is_func_end_offset = 1;
 673             break;
 674         case 'L':
 675             p->fw_func_start_offset = strtol(largs[n]+1,0,0) * 4 - 4;
 676             p->fw_is_func_start_offset = 1;
 677             break;
 678         default:
 679             if (strcmp(largs[n], "comment") == 0)
 680             {
 681                 set_op_comment(p,largs[++n]);
 682             }
 683             else
 684             {
 685                 p->fw_len = strtoul(largs[n],0,0);
 686                 if (p->fw_len == 0) p->fw_len++;
 687             }
 688             break;
 689         }
 690     }
 691 
 692     if ((p->fw_start != 0) && (p->fw_end != 0))
 693         p->fw_len = (p->fw_end - p->fw_start) / 4 + 1;
 694 }
 695 
 696 // Disassemble and patch a single instruction that references another sub_XXXXXXXX
 697 // The instruction can be B/BL/LDR etc.
 698 void parse_PATCHSUB()
 699 {
 700     op *p = new_op(PATCHSUB_OP);
 701     int n;
 702 
 703     for (n=1; n<largc; n++)
 704     {
 705         if (strcmp(largs[n], "name") == 0)
 706         {
 707             set_op_name(p,largs[++n]);
 708         }
 709         else if (strcmp(largs[n], "ref") == 0)
 710         {
 711             p->patch_ref = strtoul(largs[++n],0,0);
 712         }
 713         else if (strcmp(largs[n], "comment") == 0)
 714         {
 715             set_op_comment(p,largs[++n]);
 716         }
 717         else
 718         {
 719             chk_args(-1,"Invalid PATCHSUB argument",p);
 720         }
 721     }
 722 }
 723 
 724 // Disassemble and patch a single instruction, replacing an immediate value
 725 void parse_PATCHVAL()
 726 {
 727     op *p = new_op(PATCHVAL_OP);
 728     int n;
 729 
 730     for (n=1; n<largc; n++)
 731     {
 732         if (strcmp(largs[n], "value") == 0)
 733         {
 734             p->patch_new_val = strtoul(largs[++n],0,0);
 735         }
 736         else if (strcmp(largs[n], "comment") == 0)
 737         {
 738             set_op_comment(p,largs[++n]);
 739         }
 740         else
 741         {
 742             chk_args(-1,"Invalid PATCHVAL argument",p);
 743         }
 744     }
 745 }
 746 
 747 // Disassemble and comment out a single instruction
 748 // An optional comment can be added to the line with the reason for removal
 749 void parse_REM()
 750 {
 751     op *p = new_op(REM_OP);
 752 
 753     if (largc > 1)
 754     {
 755         set_op_comment(p,largs[1]);
 756     }
 757 }
 758 
 759 // Skip N words in the firmware - used to skip over DCD in the middle of a function
 760 void parse_SKIP()
 761 {
 762     op *p = new_op(SKIP_OP);
 763 
 764     if (largc > 1)
 765         p->skip_len = strtol(largs[1],0,0);
 766     else
 767         p->skip_len = 1;
 768 }
 769 
 770 // Generate a B instruction to jump back to the firmware code at the current address
 771 void parse_CONTFW()
 772 {
 773     new_op(CONTFW_OP);
 774 }
 775 
 776 // Enable or disable line number output
 777 void parse_LI()
 778 {
 779     op *p = new_op(LI_OP);
 780     if (largc > 1)
 781         p->li_state = strtol(largs[1],0,0);
 782 }
 783 
 784 //------------------------------------------------------------------------------------------------------------
 785 
 786 void op_prelabel(op *p)
 787 {
 788     // If after then write label to output and remove from list (so it doesn't get written twice)
 789     if (in_func && (p->prelabel == 0) && (addr <= options.end_address))
 790     {
 791         if (l_search(branch_list, addr))
 792         {
 793             fprintf(outfile,"\n\"loc_%08X:\\n\"\n", addr) ;     
 794             l_remove(branch_list, addr);
 795         }
 796     }
 797 }
 798 
 799 // Direct copy input to output file
 800 void op_COPY(op *p)
 801 {
 802     int x;
 803 
 804     op_prelabel(p);
 805 
 806     // Check if the copy is from another source file - if so just copy that file
 807     if (p->name)
 808     {
 809         FILE *fp = fopen(p->name,"r");
 810         if (!fp)
 811         {
 812             fprintf(stderr,"Can't open file '%s'\n",p->name);
 813             exit(1);
 814         }
 815 
 816         char buf[1024];
 817         do
 818         {
 819             x = fread(buf,1,1024,fp);
 820             if (x > 0) fwrite(buf,1,x,outfile);
 821         } while (x > 0);
 822 
 823         fclose(fp);
 824         direct_copy = 0;
 825     }
 826 }
 827 
 828 // Start a new Function disassembly:
 829 //  - sets up addresses & length
 830 //  - does pass 1 & 2 of the disassembly to get the labels
 831 //  - writes the function start to the source
 832 void op_FUNC(op *p)
 833 {
 834     char func_name[256];
 835 
 836     *func_name = 0;
 837 
 838     cur_func = p;
 839 
 840     if (p->name)
 841         strcpy(func_name, p->name);
 842 
 843     if (p->patch_ref >= 0)
 844     {
 845         p->func_start = patch_ref_address[p->patch_ref];
 846         strcpy(func_name, patch_ref_name[p->patch_ref]);
 847     }
 848 
 849     in_func = 1;
 850 
 851     if (p->func_start == 0) chk_args(-1,"Missing FUNC start address",p);
 852 
 853     if ((p->func_end == 0) && (p->func_len == 0))
 854     {
 855         op *n = p->next;
 856         int cont = 1;
 857         while (n && cont)
 858         {
 859             switch (n->operation)
 860             {
 861             case FW_OP:
 862                 if (n->fw_is_func_end_offset)
 863                 {
 864                     cont = 0;
 865                     p->func_len = 0;
 866                 }
 867                 else
 868                 {
 869                     p->func_len += n->fw_len;
 870                 }
 871                 break;
 872             case PATCHSUB_OP:
 873             case PATCHVAL_OP:
 874             case REM_OP:
 875                 p->func_len++;
 876                 break;
 877             case SKIP_OP:
 878                 p->func_len += n->skip_len;
 879                 break;
 880             case ENDFUNC_OP:
 881                 cont = 0;
 882                 break;
 883             }
 884             n = n->next;
 885         }
 886 
 887         if (p->func_len == 0)
 888         {
 889             p->func_end = find_end(fw, p->func_start);
 890             if (p->func_end == 0)
 891                 chk_args(-1,"Missing FUNC end address or length",p);
 892         }
 893     }
 894 
 895     if ((p->func_end == 0) && (p->func_len > 0))
 896         p->func_end = p->func_start + p->func_len * 4 - 4;
 897     if ((p->func_len == 0) && (p->func_end > 0))
 898         p->func_len = (p->func_end - p->func_start) / 4 + 1;
 899     if (p->func_end < p->func_start) chk_args(-1,"FUNC start > end",p);
 900     if (p->func_len != ((p->func_end - p->func_start) / 4 + 1)) chk_args(-1,"FUNC start/end/length mismatch",p);
 901 
 902     if (*func_name == 0)
 903         sprintf(func_name, "sub_%08X_my", p->func_start);
 904 
 905     options.start_address = p->func_start;
 906     options.end_address   = p->func_end;
 907 
 908     set_ignore_errors(1);
 909     options.flags |= disopt_remember_branches;
 910     disassemble1(fw, p->func_start, p->func_len);
 911     options.flags &= ~disopt_remember_branches;
 912     set_ignore_errors(0);
 913 
 914     fprintf(outfile,"\n/*************************************************************/");
 915     fprintf(outfile,"\n//** %s @ 0x%08X - 0x%08X, length=%d\n", func_name, p->func_start, last_used_addr, (last_used_addr - p->func_start) / 4 + 1 ) ;
 916     fprintf(outfile,"void __attribute__((naked,noinline)) %s() {\n", func_name);
 917 
 918     addr = p->func_start;
 919 }
 920 
 921 // Disassemble a block of firmware code to the file
 922 void op_FW(op *p)
 923 {
 924     t_address start_address = addr;
 925     t_address end_address = addr;
 926 
 927     patch_comment = p->comment;
 928     if (patch_comment)
 929     {
 930         options.flags |= disopt_patch_comment;
 931     }
 932 
 933     if (p->fw_start > 0)
 934         start_address = p->fw_start;
 935     if (p->fw_end > 0)
 936         end_address = p->fw_end;
 937     if (p->fw_is_func_end_offset)
 938         end_address = cur_func->func_end + p->fw_func_end_offset;
 939     if (p->fw_is_func_start_offset)
 940         end_address = cur_func->func_start + p->fw_func_start_offset;
 941     if (p->fw_len > 0)
 942         end_address = start_address + p->fw_len * 4 - 4;
 943 
 944     disassemble(fw, outfile, start_address, (end_address + 4 - start_address) / 4);
 945 }
 946 
 947 // Disassemble and patch a single instruction that references another sub_XXXXXXXX
 948 // The instruction can be B/BL/LDR etc.
 949 void op_PATCHSUB(op *p)
 950 {
 951     patch_func_name = p->name;
 952     save_patch_ref = p->patch_ref;
 953     patch_comment = p->comment;
 954     if (patch_comment)
 955     {
 956         options.flags |= disopt_patch_comment;
 957     }
 958 
 959     options.flags |= disopt_patch_branch;
 960     disassemble(fw, outfile, addr, 1);
 961     options.flags &= ~(disopt_patch_branch|disopt_patch_comment);
 962 }
 963 
 964 // Disassemble and patch a single instruction, replacing an immediate value
 965 void op_PATCHVAL(op *p)
 966 {
 967     patch_new_val = p->patch_new_val;
 968     patch_comment = p->comment;
 969     if (patch_comment)
 970     {
 971         options.flags |= disopt_patch_comment;
 972     }
 973 
 974     options.flags |= disopt_patch_value;
 975     disassemble(fw, outfile, addr, 1);
 976     options.flags &= ~(disopt_patch_value|disopt_patch_comment);
 977 }
 978 
 979 // Disassemble and comment out a single instruction
 980 // An optional comment can be added to the line with the reason for removal
 981 void op_REM(op *p)
 982 {
 983     patch_comment = p->comment;
 984     if (patch_comment)
 985     {
 986         options.flags |= disopt_patch_comment;
 987     }
 988 
 989     options.flags |= disopt_comment_lines;
 990     disassemble(fw, outfile, addr, 1);
 991     options.flags &= ~(disopt_comment_lines|disopt_patch_comment);
 992 }
 993 
 994 //------------------------------------------------------------------------------------------------------------
 995 
 996 // Process operation on current line
 997 void do_ops(op *p)
 998 {
 999     switch (p->operation)
1000     {
1001     case FILE_OP:
1002         // Open an output file
1003         outfile = fopen(p->name, "w");
1004         fprintf(outfile, "/*\n * %s - auto-generated by CHDK code_gen.\n */\n", p->name);
1005         break;
1006     case ENDFILE_OP:
1007         // Close output file
1008         if (outfile != stdout)
1009         {
1010             fclose(outfile);
1011             outfile = stdout;
1012         }
1013         break;
1014     case COPY_OP:
1015         op_COPY(p);
1016         break;
1017     case COPY_LINE:
1018         fprintf(outfile, "%s\n", p->comment);
1019         break;
1020     case FUNC_OP:
1021         op_FUNC(p);
1022         break;
1023     case ENDFUNC_OP:
1024         // End function, writes the function end to the file
1025         fprintf(outfile,"}\n");
1026         in_func = 0;
1027         break;
1028     case ASM_OP:
1029         // Writes the 'asm...' stuff to the file
1030         fprintf(outfile,"asm volatile (\n");
1031         break;
1032     case ENDASM_OP:
1033         // Ends the 'asm...' block
1034         op_prelabel(p);
1035         fprintf(outfile,");\n");
1036         break;
1037     case FW_OP:
1038         op_FW(p);
1039         break;
1040     case PATCHSUB_OP:
1041         op_PATCHSUB(p);
1042         break;
1043     case PATCHVAL_OP:
1044         op_PATCHVAL(p);
1045         break;
1046     case REM_OP:
1047         op_REM(p);
1048         break;
1049     case SKIP_OP:
1050         // Skip N words in the firmware - used to skip over DCD in the middle of a function
1051         addr += (p->skip_len * 4);
1052         break;
1053     case CONTFW_OP:
1054         // Generate a B instruction to jump back to the firmware code at the current address
1055         if (options.flags & disopt_line_numbers) fprintf(outfile,"       ");
1056         fprintf(outfile,"\"    LDR     PC, =0x%08X \\n\"  // Continue in firmware\n",addr);
1057         break;
1058     case LI_OP:
1059         // set line number output state
1060         if (p->li_state != 0)
1061             options.flags |= disopt_line_numbers;
1062         else
1063             options.flags &= ~disopt_line_numbers;
1064         break;
1065     }
1066 }
1067 
1068 // Process an operation
1069 int run_op(char *name, void (*func)())
1070 {
1071     if (strcmp(largs[0], name) == 0)
1072     {
1073         func();
1074         return 1;
1075     }
1076     return 0;
1077 }
1078 
1079 // Pre-Process operation on current line
1080 void parse_ops()
1081 {
1082     if (largc > 0)
1083     {
1084         if (run_op("FILE",      parse_FILE))       return;
1085         if (run_op("ENDFILE",   parse_ENDFILE))    return;
1086         if (run_op(">>>",       parse_COPY))       return;
1087         if (run_op("FUNC",      parse_FUNC))       return;
1088         if (run_op("ENDFUNC",   parse_ENDFUNC))    return;
1089         if (run_op("ASM",       parse_ASM))        return;
1090         if (run_op("ENDASM",    parse_ENDASM))     return;
1091         if (run_op("FW",        parse_FW))         return;
1092         if (run_op("PATCHSUB",  parse_PATCHSUB))   return;
1093         if (run_op("PATCHVAL",  parse_PATCHVAL))   return;
1094         if (run_op("REM",       parse_REM))        return;
1095         if (run_op("SKIP",      parse_SKIP))       return;
1096         if (run_op("->FW",      parse_CONTFW))     return;
1097         if (run_op("LI",        parse_LI))         return;
1098     }
1099 }
1100 
1101 //------------------------------------------------------------------------------------------------------------
1102 
1103 void usage(char *err)
1104 {
1105     fprintf(stderr,"code_gen <base> <primary> [alt base] - Error = %s\n",err);
1106     exit(1);
1107 }
1108 
1109 op* last_op = 0;
1110 
1111 void error(char *fmt, int n)
1112 {
1113     if (last_op)
1114         fprintf(stderr,"Line - %d, Source --> %s\n",last_op->lineno,last_op->source);
1115     exit(1);
1116 }
1117 
1118 //------------------------------------------------------------------------------------------------------------
1119 
1120 int main(int ac, const char * av[]) 
1121 {
1122     // Check number of args
1123     if (ac < 4)
1124     {
1125         fprintf(stderr,"Usage: code_gen ROMBASE code_gen.txt PRIMARY.BIN [ALT_ROMBASE]\n");
1126         exit(1);
1127     }
1128 
1129     // Get ROMBASE value
1130     options.ROM_start = strtoul(av[1],0,0);
1131     if (options.ROM_start == 0)
1132     {
1133         fprintf(stderr,"Invalid ROMBASE.\n");
1134         exit(1);
1135     }
1136 
1137     // Get instruction file length
1138     struct stat st;
1139     if (stat(av[2],&st) != 0)
1140     {
1141         fprintf(stderr, "Failed to stat \"%s\".\n", av[2]);
1142         exit(1);
1143     }
1144     size_t len = st.st_size;
1145 
1146     // Open & read instruction file
1147     FILE *fp = fopen(av[2], "rb");
1148     if (!fp)
1149     {
1150         fprintf(stderr, "Failed to open \"%s\".\n", av[2]);
1151         exit(1);
1152     }
1153 
1154     // Allocate block for instruction file
1155     char *def = malloc(len+1);
1156     if (def == 0)
1157     {
1158         fprintf(stderr, "Not enough memory.\n");
1159         exit(1);
1160     }
1161 
1162     // Read instruction file
1163     size_t n = fread(def, 1, len, fp);
1164     def[n] = 0;
1165     fclose(fp);
1166 
1167     // Open Firmware dump file
1168     fw = malloc(sizeof(firmware));
1169     load_firmware(fw, av[3], av[1], (ac==5)?av[4]:0, OS_DRYOS);
1170 
1171     // Load function name/address values from stubs files
1172     fw->sv = new_stub_values();
1173     load_funcs(fw->sv, "funcs_by_name.csv");
1174     load_stubs(fw->sv, "stubs_entry.S", 0);
1175     load_stubs(fw->sv, "stubs_entry_2.S", 0);   // Load second so values override stubs_entry.S
1176 
1177     outfile = stdout;
1178     direct_copy = 0;
1179     in_func = 0;
1180 
1181     op_head = op_tail = 0;
1182 
1183     // Pre-Process
1184     lineno = 0;
1185     char *line = def;
1186     while ((line != 0) && (*line != 0))
1187     {
1188         char *nxt = next_line(line);
1189 
1190         // Check if just copying from input file to output file
1191         if (direct_copy)
1192         {
1193             // Check if end of copy
1194             if (strncmp(line,"<<<",3) == 0)
1195             {
1196                 direct_copy = 0;
1197             }
1198             else
1199             {
1200                 op *p = new_op(COPY_LINE);
1201                 set_op_comment(p,line);
1202             }
1203         }
1204         else
1205         {
1206             parse_line(line);
1207             parse_ops();
1208         }
1209 
1210         line = nxt;
1211     }
1212 
1213     // Process
1214     op *p = op_head;
1215     while (p)
1216     {
1217         last_op = p;
1218         do_ops(p);
1219         p = p->next;
1220     }
1221 
1222     if (outfile != stdout)
1223         fclose(outfile);
1224 
1225     return 0;
1226 }

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