root/tools/finsig_thumb2.c

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

DEFINITIONS

This source file includes following definitions.
  1. bprintf
  2. add_blankline
  3. write_output
  4. add_prop_hit
  5. get_misc_val
  6. get_misc_val_value
  7. save_misc_val
  8. save_misc_val_blobs
  9. find_saved_sig_index
  10. find_saved_sig
  11. get_saved_sig_val
  12. find_saved_sig_index_by_adr
  13. find_saved_sig_by_val
  14. save_sig
  15. add_func_name
  16. save_sig_with_j
  17. find_next_sig_call
  18. is_sig_call
  19. init_disasm_sig_ref
  20. sig_match_str_r0_call
  21. sig_match_reg_evp
  22. sig_match_reg_evp_table
  23. sig_match_reg_evp_alt2
  24. sig_match_unreg_evp_table
  25. sig_match_evp_table_veneer
  26. sig_match_get_nd_value
  27. sig_match_get_current_exp
  28. sig_match_get_current_nd_value
  29. sig_match_imager_active_callback
  30. sig_match_imager_active
  31. sig_match_screenlock_helper
  32. sig_match_screenunlock
  33. sig_match_log_camera_event
  34. sig_match_physw_misc
  35. sig_match_kbd_read_keys
  36. sig_match_get_kbd_state
  37. sig_match_get_dial_hw_position
  38. sig_match_create_jumptable
  39. sig_match_take_semaphore_strict
  40. sig_match_get_semaphore_value
  41. sig_match_stat
  42. sig_match_open
  43. sig_match_open_gt_57
  44. sig_match_close_gt_57
  45. sig_match_umalloc
  46. sig_match_ufree
  47. sig_match_deletefile_fut
  48. sig_match_closedir
  49. save_sig_match_call
  50. sig_match_readfastdir
  51. sig_match_strrchr
  52. sig_match_time
  53. sig_match_strncpy
  54. sig_match_strncmp
  55. sig_match_strtolx
  56. sig_match_exec_evp
  57. sig_match_fgets_fut
  58. sig_match_log
  59. sig_match_pow_dry_52
  60. sig_match_pow_dry_gt_52
  61. sig_match_sqrt
  62. sig_match_get_drive_cluster_size
  63. sig_match_mktime_ext
  64. sig_match_rec2pb
  65. sig_match_get_parameter_data
  66. sig_match_prepdir_x
  67. sig_match_prepdir_1
  68. sig_match_prepdir_0
  69. sig_match_mkdir
  70. sig_match_add_ptp_handler
  71. sig_match_qsort
  72. sig_match_deletedirectory_fut
  73. sig_match_set_control_event
  74. sig_match_displaybusyonscreen_52
  75. sig_match_undisplaybusyonscreen_52
  76. sig_match_try_take_sem_dry_gt_58
  77. sig_match_wait_all_eventflag_strict
  78. sig_match_get_num_posted_messages
  79. sig_match_set_hp_timer_after_now
  80. sig_match_transfer_src_overlay
  81. sig_match_exmem_vars
  82. sig_match_zicokick_52
  83. sig_match_zicokick_gt52
  84. sig_match_zicokick_copy
  85. sig_match_zicokick_values
  86. sig_match_init_ex_drivers
  87. sig_match_omar_init
  88. sig_match_enable_hdmi_power
  89. sig_match_disable_hdmi_power
  90. sig_match_levent_table
  91. sig_match_flash_param_table
  92. sig_match_jpeg_count_str
  93. sig_match_misc_flag_named
  94. sig_match_cam_has_iris_diaphragm
  95. sig_match_cam_uncached_bit
  96. sig_match_physw_event_table
  97. sig_match_uiprop_count
  98. sig_match_get_canon_mode_list
  99. sig_match_zoom_busy
  100. sig_match_focus_busy
  101. sig_match_aram_size
  102. sig_match_aram_size_gt58
  103. sig_match_aram_start
  104. sig_match_aram_start2
  105. sig_match__nrflag
  106. sig_match_var_struct_get
  107. sig_match_av_over_sem
  108. sig_match_canon_menu_active
  109. sig_match_file_counter_init
  110. sig_match_file_counter_var
  111. sig_match_palette_vars
  112. sig_match_rom_ptr_get
  113. find_call_near_str
  114. sig_match_near_str
  115. sig_match_prop_string
  116. is_immediate_ret_sub
  117. sig_match_named_last
  118. sig_match_named_save_sig
  119. sig_match_named
  120. run_sig_rules
  121. add_event_proc
  122. process_reg_eventproc_call
  123. process_eventproc_table_call
  124. process_createtask_call
  125. save_ptp_handler_func
  126. process_add_ptp_handler_call
  127. add_generic_func_match
  128. add_generic_sig_match
  129. find_exception_handlers
  130. find_generic_funcs
  131. find_ctypes
  132. print_misc_val_makefile
  133. output_firmware_vals
  134. print_platform_misc_val_undef
  135. output_platform_vals
  136. output_propcases
  137. output_exmem_types
  138. print_misc_val_comment
  139. get_physw_table_entry
  140. find_physw_table_entry
  141. find_physw_table_max
  142. write_physw_event_table_dump
  143. print_kval
  144. add_kinfo
  145. add_kmval
  146. kinfo_compare
  147. print_kmvals
  148. do_km_vals
  149. output_physw_vals
  150. output_modemap
  151. compare_sig_names
  152. compare_func_addresses
  153. write_funcs
  154. write_func_lists
  155. print_other_stubs_min
  156. print_stubs_min_def
  157. find_other_stubs_min
  158. print_results
  159. write_stubs
  160. main

   1 #include <stdlib.h>
   2 #include <stdio.h>
   3 #include <stdint.h>
   4 #include <string.h>
   5 #include <time.h>
   6 #include <stdarg.h>
   7 
   8 #include <inttypes.h>
   9 
  10 #include <capstone.h>
  11 
  12 
  13 #include "stubs_load.h"
  14 #include "firmware_load_ng.h"
  15 #include "ptp_op_names.h"
  16 
  17 // arbitrary standardized constant for search "near" a string ref etc
  18 // could base on ADR etc reach
  19 #define SEARCH_NEAR_REF_RANGE 1024
  20 
  21 #define SIG_NEAR_OFFSET_MASK    0x00FF
  22 #define SIG_NEAR_COUNT_MASK     0xFF00
  23 #define SIG_NEAR_COUNT_SHIFT    8
  24 #define SIG_NEAR_REV            0x10000
  25 #define SIG_NEAR_INDIRECT       0x20000
  26 #define SIG_NEAR_JMP_SUB        0x40000
  27 #define SIG_NEAR_AFTER(max_insns,n) (((max_insns)&SIG_NEAR_OFFSET_MASK) \
  28                                 | (((n)<<SIG_NEAR_COUNT_SHIFT)&SIG_NEAR_COUNT_MASK))
  29 #define SIG_NEAR_BEFORE(max_insns,n) (SIG_NEAR_AFTER(max_insns,n)|SIG_NEAR_REV)
  30 
  31 /* copied from finsig_dryos.c */
  32 char    out_buf[32*1024] = "";
  33 int     out_len = 0;
  34 char    hdr_buf[32*1024] = "";
  35 int     hdr_len = 0;
  36 int     out_hdr = 1;
  37 
  38 FILE *out_fp;
  39 
  40 void bprintf(char *fmt, ...)
  41 {
  42     va_list argp;
  43     va_start(argp, fmt);
  44 
  45     if (out_hdr)
  46         hdr_len += vsprintf(hdr_buf+hdr_len,fmt,argp);
  47     else
  48         out_len += vsprintf(out_buf+out_len,fmt,argp);
  49 
  50     va_end(argp);
  51 }
  52 
  53 void add_blankline()
  54 {
  55     if (strcmp(hdr_buf+hdr_len-2,"\n\n") != 0)
  56     {
  57         hdr_buf[hdr_len++] = '\n';
  58         hdr_buf[hdr_len] = 0;
  59     }
  60 }
  61 
  62 void write_output()
  63 {
  64     add_blankline();
  65     if (out_fp)
  66     {
  67         fprintf(out_fp,"%s",hdr_buf);
  68         fprintf(out_fp,"%s",out_buf);
  69     }
  70 }
  71 
  72 // Master list of functions / addresses to find
  73 
  74 #define DONT_EXPORT    0x01
  75 #define OPTIONAL       0x02
  76 #define UNUSED         0x04
  77 #define BAD_MATCH      0x08
  78 #define EV_MATCH       0x10
  79 #define LIST_ALWAYS    0x20
  80 // force an arm veneer (NHSTUB2)
  81 #define ARM_STUB       0x80
  82 #define DONT_EXPORT_ILC 0x100
  83 
  84 typedef struct {
  85     char        *name;
  86     int         flags;
  87     uint32_t    val;
  88 } sig_entry_t;
  89 
  90 int next_sig_entry = 0;
  91 
  92 #define MAX_SIG_ENTRY  5000
  93 
  94 sig_entry_t  sig_names[MAX_SIG_ENTRY] =
  95 {
  96     // Order here currently has no effect on search order, but mostly copied from finsig_dryos which did
  97     { "ExportToEventProcedure_FW", UNUSED|DONT_EXPORT },
  98     { "RegisterEventProcedure", UNUSED|DONT_EXPORT },
  99     { "RegisterEventProcedure_alt1", UNUSED|DONT_EXPORT },
 100     { "RegisterEventProcedure_alt2", UNUSED|DONT_EXPORT },
 101     { "RegisterEventProcTable", UNUSED|DONT_EXPORT },
 102     { "UnRegisterEventProcTable", UNUSED|DONT_EXPORT },
 103     { "UnRegisterEventProcedure", UNUSED|DONT_EXPORT },
 104     { "PrepareDirectory_1", UNUSED|DONT_EXPORT },
 105     { "PrepareDirectory_x", UNUSED|DONT_EXPORT },
 106     { "PrepareDirectory_0", UNUSED|DONT_EXPORT },
 107     { "CreateTaskStrictly", UNUSED|DONT_EXPORT },
 108     { "CreateTaskStrictly_alt", UNUSED|DONT_EXPORT },
 109     { "CreateTask_alt", UNUSED|DONT_EXPORT },
 110     { "CreateTask_low", UNUSED | OPTIONAL },
 111     { "CreateJumptable", UNUSED },
 112     { "_uartr_req", UNUSED },
 113     { "StartRecModeMenu", UNUSED },
 114     { "LogCameraEvent", UNUSED|DONT_EXPORT },
 115     { "getImageDirName", UNUSED|DONT_EXPORT },
 116 
 117     { "AllocateMemory", UNUSED|LIST_ALWAYS },
 118     { "AllocateUncacheableMemory" },
 119     { "Close" },
 120     { "CreateBinarySemaphore" },
 121     { "CreateCountingSemaphore", UNUSED|LIST_ALWAYS },
 122     { "CreateTask" },
 123     { "DebugAssert", OPTIONAL|LIST_ALWAYS },
 124     { "DeleteDirectory_Fut" },
 125     { "DeleteFile_Fut" },
 126     { "DeleteSemaphore", UNUSED|LIST_ALWAYS },
 127     { "DoAELock" },
 128     { "DoAFLock" },
 129     { "EnterToCompensationEVF" },
 130     { "ExecuteEventProcedure", ARM_STUB },
 131     { "ExitFromCompensationEVF" },
 132     { "ExitTask" },
 133     { "ExpCtrlTool_StartContiAE" },
 134     { "ExpCtrlTool_StopContiAE" },
 135     { "Fclose_Fut" },
 136     { "Feof_Fut" },
 137     { "Fflush_Fut" },
 138     { "Fgets_Fut" },
 139     { "Fopen_Fut" },
 140     { "Fread_Fut" },
 141     { "FreeMemory", UNUSED|LIST_ALWAYS },
 142     { "FreeUncacheableMemory" },
 143     { "Fseek_Fut" },
 144     { "Fwrite_Fut" },
 145     { "GetBatteryTemperature" },
 146     { "GetCCDTemperature" },
 147 
 148     { "GetCurrentAvValue" },
 149     { "GetCurrentShutterSpeed" },
 150     { "GetUsableMaxAv", OPTIONAL },
 151     { "GetUsableMinAv", OPTIONAL },
 152     { "GetUsableAvRange", UNUSED |OPTIONAL },
 153     { "get_nd_value", OPTIONAL },
 154     { "get_current_exp", UNUSED | OPTIONAL }, // helper, underlying function of ShowCurrentExp
 155     { "get_current_nd_value", OPTIONAL },
 156     { "GetDrive_ClusterSize" },
 157     { "GetDrive_FreeClusters" },
 158     { "GetDrive_TotalClusters" },
 159     { "GetFocusLensSubjectDistance" },
 160     { "GetFocusLensSubjectDistanceFromLens" },
 161     { "GetImageFolder", OPTIONAL },
 162     { "GetKbdState" },
 163     { "GetMemInfo" },
 164     { "GetOpticalTemperature" },
 165     { "GetParameterData" },
 166     { "GetPropertyCase" },
 167     { "GetSystemTime" },
 168     { "GetVRAMHPixelsSize" },
 169     { "GetVRAMVPixelsSize" },
 170     { "GetZoomLensCurrentPoint" },
 171     { "GetZoomLensCurrentPosition" },
 172     { "GiveSemaphore", OPTIONAL|LIST_ALWAYS },
 173     { "IsStrobeChargeCompleted" },
 174     { "LEDDrive", OPTIONAL },
 175     { "LocalTime" },
 176     { "LockMainPower" },
 177     { "Lseek", UNUSED|LIST_ALWAYS },
 178     { "MakeDirectory_Fut" },
 179     { "MakeSDCardBootable", OPTIONAL },
 180     { "MoveFocusLensToDistance" },
 181     { "MoveIrisWithAv", OPTIONAL },
 182     { "MoveZoomLensWithPoint", DONT_EXPORT_ILC},
 183     { "NewTaskShell", UNUSED },
 184     { "Open" },
 185     { "PB2Rec" },
 186     { "PT_MoveDigitalZoomToWide", OPTIONAL | DONT_EXPORT_ILC},
 187     { "PT_MoveOpticalZoomAt", OPTIONAL | DONT_EXPORT_ILC },
 188     { "MoveOpticalZoomAt", OPTIONAL | DONT_EXPORT_ILC },
 189     { "PT_PlaySound" },
 190     { "PostLogicalEventForNotPowerType" },
 191     { "PostLogicalEventToUI" },
 192     { "PutInNdFilter", OPTIONAL },
 193     { "PutOutNdFilter", OPTIONAL },
 194     { "Read" },
 195     { "ReadFastDir" },
 196     { "Rec2PB" },
 197     { "Remove", OPTIONAL|UNUSED },
 198     { "RenameFile_Fut" },
 199     { "Restart" },
 200     { "screenlock_helper", UNUSED|DONT_EXPORT },
 201     { "ScreenLock" },
 202     { "ScreenUnlock" },
 203     { "SetAE_ShutterSpeed" },
 204     { "SetAutoShutdownTime" },
 205     { "SetCurrentCaptureModeType" },
 206     { "SetDate" },
 207     { "SetFileAttributes" },
 208     { "SetFileTimeStamp" },
 209     { "SetLogicalEventActive" },
 210     { "SetParameterData" },
 211     { "SetPropertyCase" },
 212     { "SetScriptMode" },
 213     { "SleepTask" },
 214     { "TakeSemaphore" },
 215     { "TurnOffBackLight" },
 216     { "TurnOnBackLight" },
 217     { "TurnOnDisplay" },
 218     { "TurnOffDisplay" },
 219     { "UIFS_WriteFirmInfoToFile", OPTIONAL|UNUSED},
 220     { "UnlockAE" },
 221     { "UnlockAF" },
 222     { "UnlockMainPower" },
 223     { "UnsetZoomForMovie", OPTIONAL | DONT_EXPORT_ILC },
 224 //    { "UpdateMBROnFlash" },
 225     { "VbattGet" },
 226     { "Write" },
 227     { "WriteSDCard" },
 228 
 229     { "_log" },
 230     { "_log10" },
 231     { "_pow" },
 232     { "_sqrt" },
 233     { "add_ptp_handler" },
 234     { "apex2us" },
 235     { "close" },
 236     { "displaybusyonscreen", OPTIONAL },
 237     { "err_init_task", OPTIONAL },
 238     { "exmem_alloc", OPTIONAL },
 239     { "exmem_free", OPTIONAL|UNUSED },
 240     { "exmem_ualloc" },
 241     { "exmem_ufree" },
 242     { "free" },
 243 
 244     { "kbd_p1_f" },
 245     { "kbd_p1_f_cont" },
 246     { "kbd_p2_f" },
 247     { "kbd_read_keys" },
 248     { "kbd_read_keys_r2" },
 249 
 250     { "kbd_pwr_off", OPTIONAL },
 251     { "kbd_pwr_on", OPTIONAL },
 252     { "lseek" },
 253     { "malloc" },
 254     { "memcmp" },
 255     { "memcpy" },
 256     { "memset" },
 257 // identical to MakeDirectory_Fut for recent cams
 258 //    { "mkdir" },
 259     { "mktime_ext" },
 260     { "open" },
 261     { "OpenFastDir" },
 262     { "closedir" },
 263     { "get_fstype", OPTIONAL|LIST_ALWAYS },
 264     { "qsort" },
 265     { "rand" },
 266     { "read", UNUSED|OPTIONAL },
 267     { "realloc", OPTIONAL|LIST_ALWAYS },
 268     { "reboot_fw_update" },
 269     { "set_control_event" },
 270     { "srand" },
 271     { "stat" },
 272     { "strcat" },
 273     { "strchr" },
 274     { "strcmp" },
 275     { "strcpy" },
 276     { "strftime" },
 277     { "strlen" },
 278     { "strncmp" },
 279     { "strncpy" },
 280     { "strrchr" },
 281     { "strtol" },
 282     { "strtolx" },
 283 
 284     { "task_CaptSeq" },
 285     { "task_DvlpSeqTask", OPTIONAL },
 286     { "task_ExpDrv" },
 287     { "task_FileWrite", OPTIONAL },
 288     { "task_InitFileModules" },
 289     { "task_MovieRecord" },
 290     { "task_PhySw", OPTIONAL },
 291     { "task_RotaryEncoder", OPTIONAL },
 292     { "task_TouchPanel", OPTIONAL },
 293     { "task_TricInitTask", OPTIONAL },
 294 
 295     { "hook_CreateTask" },
 296     { "hook_CreateTask_low", UNUSED}, // unused changed at runtime if CreateTask in ROM
 297 
 298     { "time" },
 299     { "vsprintf" },
 300     { "write", UNUSED|OPTIONAL },
 301     { "undisplaybusyonscreen", OPTIONAL },
 302 
 303     { "EngDrvIn", OPTIONAL|UNUSED|LIST_ALWAYS },
 304     { "EngDrvOut", OPTIONAL|UNUSED|LIST_ALWAYS },
 305     { "EngDrvRead" },
 306     { "EngDrvBits", OPTIONAL|UNUSED|LIST_ALWAYS },
 307 
 308     { "PTM_GetCurrentItem" },
 309     { "PTM_SetCurrentItem", UNUSED|LIST_ALWAYS },
 310     { "PTM_NextItem", OPTIONAL|UNUSED|LIST_ALWAYS },
 311     { "PTM_PrevItem", OPTIONAL|UNUSED|LIST_ALWAYS },
 312     { "PTM_SetPropertyEnable", OPTIONAL|UNUSED|LIST_ALWAYS },
 313 
 314     { "DisableISDriveError", OPTIONAL },
 315 
 316     // OS functions, mostly to aid firmware analysis. Order is important!
 317     { "_GetSystemTime", OPTIONAL|UNUSED|LIST_ALWAYS }, // only for locating timer functions
 318     { "SetTimerAfter", OPTIONAL|UNUSED|LIST_ALWAYS },
 319     { "SetTimerWhen", OPTIONAL|UNUSED|LIST_ALWAYS },
 320     { "CancelTimer", OPTIONAL|UNUSED|LIST_ALWAYS },
 321     { "CancelHPTimer" },
 322     { "SetHPTimerAfterTimeout", OPTIONAL|UNUSED|LIST_ALWAYS },
 323     { "SetHPTimerAfterNow" },
 324     { "CreateTaskStrictly", OPTIONAL|UNUSED|LIST_ALWAYS },
 325     { "CreateMessageQueue", OPTIONAL|UNUSED|LIST_ALWAYS },
 326     { "CreateRecursiveLock", OPTIONAL|UNUSED|LIST_ALWAYS },
 327     { "GetSemaphoreValue", OPTIONAL|UNUSED|LIST_ALWAYS },
 328     { "TryTakeSemaphore", OPTIONAL|UNUSED|LIST_ALWAYS },
 329     { "CreateMessageQueueStrictly", OPTIONAL|UNUSED|LIST_ALWAYS },
 330     { "CreateEventFlagStrictly", OPTIONAL|UNUSED|LIST_ALWAYS },
 331     { "CreateBinarySemaphoreStrictly", OPTIONAL|UNUSED|LIST_ALWAYS },
 332     { "CreateCountingSemaphoreStrictly", OPTIONAL|UNUSED|LIST_ALWAYS },
 333     { "CreateRecursiveLockStrictly", OPTIONAL|UNUSED|LIST_ALWAYS },
 334     { "TakeSemaphoreStrictly", OPTIONAL|UNUSED|LIST_ALWAYS }, // r23+
 335     { "ReceiveMessageQueueStrictly", OPTIONAL|UNUSED|LIST_ALWAYS }, // r23+
 336     { "PostMessageQueueStrictly", OPTIONAL|UNUSED|LIST_ALWAYS },    // r23+
 337     { "WaitForAnyEventFlagStrictly", OPTIONAL|UNUSED|LIST_ALWAYS }, // r23+
 338     { "WaitForAllEventFlagStrictly", OPTIONAL|UNUSED|LIST_ALWAYS }, // r23+
 339     { "AcquireRecursiveLockStrictly", OPTIONAL|UNUSED|LIST_ALWAYS }, // r23+
 340     { "DeleteMessageQueue", OPTIONAL|UNUSED|LIST_ALWAYS },
 341     { "PostMessageQueue", OPTIONAL|UNUSED|LIST_ALWAYS },
 342     { "ReceiveMessageQueue", OPTIONAL|UNUSED|LIST_ALWAYS },
 343     { "TryReceiveMessageQueue", OPTIONAL|UNUSED|LIST_ALWAYS },
 344     { "TryPostMessageQueue", OPTIONAL|UNUSED|LIST_ALWAYS },
 345     { "GetNumberOfPostedMessages", OPTIONAL|UNUSED|LIST_ALWAYS },
 346     { "DeleteRecursiveLock", OPTIONAL|UNUSED|LIST_ALWAYS },
 347     { "AcquireRecursiveLock", OPTIONAL|UNUSED|LIST_ALWAYS },
 348     { "ReleaseRecursiveLock", OPTIONAL|UNUSED|LIST_ALWAYS },
 349     { "WaitForAnyEventFlag", OPTIONAL|UNUSED|LIST_ALWAYS },
 350     { "WaitForAllEventFlag", OPTIONAL|UNUSED|LIST_ALWAYS },
 351     { "ClearEventFlag", OPTIONAL|UNUSED|LIST_ALWAYS },
 352     { "SetEventFlag", OPTIONAL|LIST_ALWAYS },
 353     { "GetEventFlagValue", OPTIONAL|UNUSED|LIST_ALWAYS },
 354     { "CreateEventFlag", OPTIONAL|UNUSED|LIST_ALWAYS },
 355     { "DeleteEventFlag", OPTIONAL|UNUSED|LIST_ALWAYS },
 356     { "CheckAnyEventFlag", OPTIONAL|UNUSED|LIST_ALWAYS },
 357     { "CheckAllEventFlag", OPTIONAL|UNUSED|LIST_ALWAYS },
 358     { "RegisterInterruptHandler", OPTIONAL|UNUSED|LIST_ALWAYS },
 359     { "UnregisterInterruptHandler", OPTIONAL|UNUSED|LIST_ALWAYS },
 360     { "GetSRAndDisableInterrupt", OPTIONAL|UNUSED|LIST_ALWAYS }, // disables IRQ, returns a value
 361     { "SetSR", OPTIONAL|UNUSED|LIST_ALWAYS }, // enables IRQ, puts back value returned by GetSR
 362     { "EnableInterrupt", OPTIONAL|UNUSED|LIST_ALWAYS }, // enables IRQ
 363     { "_divmod_signed_int", OPTIONAL|UNUSED|LIST_ALWAYS}, // division for signed integers, remainder is returned in r1
 364     { "_divmod_unsigned_int", OPTIONAL|UNUSED|LIST_ALWAYS}, // division for unsigned integers, remainder is returned in r1
 365     { "_dflt", OPTIONAL|UNUSED|LIST_ALWAYS}, // int -> double
 366     { "_dfltu", OPTIONAL|UNUSED|LIST_ALWAYS}, // uint -> double
 367     { "_dfix", OPTIONAL|UNUSED|LIST_ALWAYS}, // double -> int
 368     { "_dfixu", OPTIONAL|UNUSED|LIST_ALWAYS}, // double -> uint
 369     { "_dmul", OPTIONAL|UNUSED|LIST_ALWAYS}, // double precision float multiplication
 370     { "_ddiv", OPTIONAL|UNUSED|LIST_ALWAYS}, // double precision float division
 371     { "_dadd", OPTIONAL|UNUSED|LIST_ALWAYS}, // addition for doubles
 372     { "_dsub", OPTIONAL|UNUSED|LIST_ALWAYS}, // subtraction for doubles
 373     { "_drsb", OPTIONAL|UNUSED|LIST_ALWAYS}, // reverse subtraction for doubles (?)
 374     { "_dcmp", OPTIONAL|UNUSED|LIST_ALWAYS}, // comparison of 2 doubles, only updates condition flags
 375     { "_dcmp_reverse", OPTIONAL|UNUSED|LIST_ALWAYS}, // like _dcmp, but operands in reverse order, only updates condition flags
 376     { "_safe_sqrt", OPTIONAL|UNUSED|LIST_ALWAYS}, // only calls _sqrt for numbers >= 0
 377     { "_scalbn", OPTIONAL|UNUSED|LIST_ALWAYS}, // double scalbn (double x, long exp), returns x * FLT_RADIX ** exp
 378     { "_fflt", OPTIONAL|UNUSED|LIST_ALWAYS}, // int -> float
 379     { "_ffltu", OPTIONAL|UNUSED|LIST_ALWAYS}, // uint -> float
 380     { "_ffix", OPTIONAL|UNUSED|LIST_ALWAYS}, // float -> int
 381     { "_ffixu", OPTIONAL|UNUSED|LIST_ALWAYS}, // float -> uint
 382     { "_fmul", OPTIONAL|UNUSED|LIST_ALWAYS}, // single precision float multiplication
 383     { "_fdiv", OPTIONAL|UNUSED|LIST_ALWAYS}, // single precision float division
 384     { "_f2d", OPTIONAL|UNUSED|LIST_ALWAYS}, // float -> double
 385     { "DisplayBusyOnScreen", OPTIONAL|UNUSED|LIST_ALWAYS}, // displays full screen "busy" message
 386     { "UndisplayBusyOnScreen", OPTIONAL|UNUSED|LIST_ALWAYS},
 387     { "CreateDialogBox", OPTIONAL|UNUSED|LIST_ALWAYS},
 388     { "DisplayDialogBox", OPTIONAL|UNUSED|LIST_ALWAYS},
 389     { "add_ui_to_dialog", OPTIONAL|UNUSED|LIST_ALWAYS}, // name made up, assigns resources to a dialog
 390     { "get_string_by_id", OPTIONAL|UNUSED|LIST_ALWAYS}, // name made up, retrieves a localised or unlocalised string by its ID
 391     { "malloc_strictly", OPTIONAL|UNUSED|LIST_ALWAYS }, // name made up
 392     { "GetCurrentMachineTime", OPTIONAL|UNUSED|LIST_ALWAYS }, // reads usec counter, name from ixus30
 393     { "HwOcReadICAPCounter", OPTIONAL|UNUSED|LIST_ALWAYS }, // reads usec counter, name from ixus30
 394     { "transfer_src_overlay_helper",UNUSED}, // helper for other related functions
 395     { "transfer_src_overlay" },
 396     { "GraphicSystemCoreFinish_helper", OPTIONAL|UNUSED }, // function that calls GraphicSystemCoreFinish
 397     { "GraphicSystemCoreFinish", OPTIONAL|UNUSED }, // used to identify mzrm message functions
 398     { "mzrm_createmsg", OPTIONAL|UNUSED },
 399     { "mzrm_sendmsg", OPTIONAL|UNUSED },
 400     { "zicokick_start", OPTIONAL|UNUSED }, // used to identify Zico core Xtensa blobs
 401     { "zicokick_copy", OPTIONAL|UNUSED }, // used to identify Zico core Xtensa blobs
 402     { "init_ex_drivers", OPTIONAL|UNUSED }, // used to identify Omar core ARM blobs
 403     { "omar_init", OPTIONAL|UNUSED }, // used to identify Omar core ARM blobs
 404 
 405     { "createsemaphore_low", OPTIONAL|UNUSED },
 406 //    { "deletesemaphore_low", UNUSED },
 407     { "givesemaphore_low", OPTIONAL|UNUSED}, // OPT_CONSOLE_REDIR_ENABLED
 408     { "takesemaphore_low", OPTIONAL|UNUSED },
 409     { "bzero" }, // 
 410     { "memset32" }, // actually jump to 2nd instruction of bzero 
 411     { "get_dial_hw_position", OPTIONAL },
 412 
 413     // Other stuff needed for finding misc variables - don't export to stubs_entry.S
 414     { "GetSDProtect", UNUSED },
 415     { "DispCon_ShowBitmapColorBar", UNUSED },
 416     { "ResetZoomLens", OPTIONAL|UNUSED },
 417     { "ResetFocusLens", OPTIONAL|UNUSED },
 418     { "NR_GetDarkSubType", OPTIONAL|UNUSED },
 419     { "NR_SetDarkSubType", OPTIONAL|UNUSED },
 420     { "SavePaletteData", OPTIONAL|UNUSED },
 421     { "GUISrv_StartGUISystem", OPTIONAL|UNUSED|LIST_ALWAYS },
 422     { "get_resource_pointer", OPTIONAL|UNUSED|LIST_ALWAYS }, // name made up, gets a pointer to a certain resource (font, dialog, icon)
 423     { "CalcLog10", OPTIONAL|UNUSED|LIST_ALWAYS }, // helper
 424     { "CalcSqrt", OPTIONAL|UNUSED }, // helper
 425     { "dry_memcpy", OPTIONAL|UNUSED }, // helper, memcpy-like function in dryos kernel code
 426     { "get_playrec_mode", OPTIONAL|UNUSED }, // helper, made up name
 427     { "DebugAssert2", OPTIONAL|UNUSED }, // helper, made up name, two arg form of DebugAssert
 428     { "get_canon_mode_list", OPTIONAL|UNUSED }, // helper, made up name
 429     { "taskcreate_LowConsole", OPTIONAL|UNUSED }, // helper, made up name
 430     { "ImagerActivate", OPTIONAL|UNUSED }, // helper
 431     { "imager_active_callback", OPTIONAL|UNUSED }, // helper
 432     { "file_counter_var_init", OPTIONAL|UNUSED }, // helper
 433     { "get_displaytype", OPTIONAL|UNUSED }, // for camera bitmap res change
 434 
 435     { "MFOn", OPTIONAL },
 436     { "MFOff", OPTIONAL },
 437     { "PT_MFOn", OPTIONAL },
 438     { "PT_MFOff", OPTIONAL },
 439     { "SS_MFOn", OPTIONAL },
 440     { "SS_MFOff", OPTIONAL },
 441 
 442     { "GetAdChValue", OPTIONAL },
 443 
 444     // for HDMI remote support - optional, probably only present on cameras with HDMI
 445     { "EnableHDMIPower", OPTIONAL },
 446     { "DisableHDMIPower", OPTIONAL },
 447 
 448     { "SetVideoOutType", OPTIONAL },
 449     { "GetVideoOutType", OPTIONAL },
 450 
 451     {0,0,0},
 452 };
 453 
 454 typedef struct {
 455     char*   name;   // name
 456     int     id;     // propcase id, as found
 457     int     use;    // 0: informational only; 1: use for propset guess AND print as #define; 2: use for propset guess
 458     
 459     int     id_ps6; // id in propset 6
 460     int     id_ps7; // id in propset 7
 461     int     id_ps8; // id in propset 8
 462     int     id_ps9; // id in propset 9
 463     int     id_ps10;// id in propset 10
 464     int     id_ps11;// id in propset 11
 465     int     id_ps12;// id in propset 12
 466     int     id_ps13;// id in propset 13
 467 } known_prop_t;
 468 
 469 #define KNOWN_PROPSET_COUNT (13-5)
 470 
 471 known_prop_t knownprops[] =
 472 {   // name                          id  u ps6 ps7 ps8 ps9 ps10 ps11 ps12 ps13
 473     {"PROPCASE_AFSTEP"             , -1, 2, 13, 13, 13, 13,  13,  13,  13,  13},
 474     {"PROPCASE_FOCUS_STATE"        , -1, 1, 18, 18, 18, 18,  18,  18,  18,  18},
 475     {"PROPCASE_AV"                 , -1, 1, 23, 23, 23, 23,  23,  23,  23,  23},
 476     {"PROPCASE_BV"                 , -1, 1, 34, 38, 35, 38,  40,  40,  40,  40},
 477     {"PROPCASE_DELTA_DIGITALGAIN"  , -1, 2, 77, 82, 79, 82,  84,  85,  85,  84},
 478     {"PROPCASE_DELTA_SV"           , -1, 1, 79, 84, 81, 84,  86,  87,  87,  86},
 479     {"PROPCASE_DELTA_ND"           , -1, 2, 80, 85, 82, 85,  87,  88,  88,  87},
 480     {"PROPCASE_FELOCK"             , -1, 2,114,120,117,120, 122, 123, 123, 122},
 481     {"PROPCASE_FLASH_ADJUST_MODE"  , -1, 1,121,127,124,127, 129, 130, 130, 129},
 482     {"PROPCASE_FLASH_FIRE"         , -1, 1,122,128,125,128, 130, 131, 131, 130},
 483     {"PROPCASE_HSCAPTURE"          , -1, 2,138,144,141,144, 146, 147, 147, 146},
 484     {"PROPCASE_EV_CORRECTION_2"    , -1, 1,210,216,213,216, 218, 219, 220, 218},
 485     {"PROPCASE_ORIENTATION_SENSOR" , -1, 1,222,228,225,228, 230, 231, 232, 230},
 486     {"PROPCASE_SV_MARKET"          , -1, 1,249,255,252,255, 257, 259, 260, 258},
 487     {"PROPCASE_SVFIX"              , -1, 0,  0,  0,  0,  0,   0,   0,   0, 259},
 488     {"PROPCASE_TV"                 , -1, 1,265,272,269,272, 274, 276, 277, 275},
 489     {0,}
 490 };
 491 
 492 void add_prop_hit(char *name, int id)
 493 {
 494     int n = 0;
 495     while (knownprops[n].name) {
 496         if (strcmp(knownprops[n].name,name) == 0) {
 497             knownprops[n].id = id;
 498             break;
 499         }
 500         n++;
 501     }
 502 }
 503 
 504 #define MISC_BLOB_XTENSA_MAX    5
 505 #define MISC_BLOB_TYPE_NONE     0
 506 #define MISC_BLOB_TYPE_XTENSA   1
 507 #define MISC_BLOB_TYPE_OMAR     2
 508 typedef struct {
 509     int         type;
 510     uint32_t    rom_adr; // location of data in ROM, if copied
 511     uint32_t    ram_adr; // location of data in RAM
 512     uint32_t    size;
 513 } misc_blob_t;
 514 
 515 // for values that don't get a DEF etc
 516 #define MISC_VAL_NO_STUB    1
 517 // DEF_CONST instead of DEF
 518 #define MISC_VAL_DEF_CONST  2
 519 #define MISC_VAL_OPTIONAL   4
 520 // variables and constants
 521 typedef struct {
 522     char        *name;
 523     int         flags;
 524     uint32_t    val;
 525     // informational values
 526     uint32_t    base; // if stub is found as ptr + offset, record
 527     uint32_t    offset;
 528     uint32_t    ref_adr; // code address near where value found (TODO may want list)
 529     misc_blob_t *blobs; // malloc'd array of blobs if this is a blob type value, terminated with flags = 0
 530 } misc_val_t;
 531 
 532 misc_val_t misc_vals[]={
 533     // stubs_min variables / constants
 534     { "ctypes",             },
 535     { "physw_run",          },
 536     { "physw_sleep_delay",  },
 537     { "physw_status",       },
 538     { "fileio_semaphore",   },
 539     { "levent_table",       },
 540     { "FlashParamsTable",   },
 541     { "playrec_mode",       },
 542     { "jpeg_count_str",     },
 543     { "zoom_busy",          },
 544     { "focus_busy",         },
 545     { "imager_active",      },
 546     { "canon_menu_active",  },
 547     { "file_counter_var",   },
 548     { "_nrflag",            MISC_VAL_OPTIONAL},
 549     { "av_override_semaphore",MISC_VAL_OPTIONAL},
 550     { "active_bitmap_buffer",MISC_VAL_OPTIONAL},
 551     { "displaytype",        MISC_VAL_OPTIONAL},
 552     { "bitmap_buffer",      MISC_VAL_OPTIONAL},
 553     { "palette_control",    MISC_VAL_OPTIONAL},
 554     { "palette_buffer_ptr", MISC_VAL_OPTIONAL},
 555     { "active_palette_buffer",MISC_VAL_OPTIONAL},
 556     { "CAM_UNCACHED_BIT",   MISC_VAL_NO_STUB},
 557     { "physw_event_table",  MISC_VAL_NO_STUB},
 558     { "uiprop_count",       MISC_VAL_DEF_CONST},
 559     { "canon_mode_list",    MISC_VAL_NO_STUB},
 560     { "ARAM_HEAP_START",    MISC_VAL_NO_STUB},
 561     { "ARAM_HEAP_SIZE",     MISC_VAL_NO_STUB},
 562     { "zicokick_values",    MISC_VAL_NO_STUB}, // used to identify Zico core Xtensa blobs (dummy for now)
 563     { "omar_init_data",     MISC_VAL_NO_STUB}, // structure containing pointers to Omar blogs
 564     { "omar_init_values",   MISC_VAL_NO_STUB}, // Omar blobs
 565     { "CAM_HAS_ND_FILTER",  MISC_VAL_NO_STUB},
 566     { "CAM_IS_ILC",         MISC_VAL_NO_STUB}, // used for finsig code that wants to check for interchangeable lens
 567     { "CAM_HAS_IRIS_DIAPHRAGM",MISC_VAL_NO_STUB},
 568     { "exmem_alloc_table",  },
 569     { "exmem_types_table",  },
 570     { "exmem_type_count",   MISC_VAL_DEF_CONST},
 571     {0,0,0},
 572 };
 573 
 574 misc_val_t *get_misc_val(const char *name)
 575 {
 576     misc_val_t *p=misc_vals;
 577     while(p->name) {
 578         if(strcmp(name,p->name) == 0) {
 579             return p;
 580         }
 581         p++;
 582     }
 583     return NULL;
 584 }
 585 
 586 // get value of misc val, if set. Name :<
 587 uint32_t get_misc_val_value(const char *name)
 588 {
 589     misc_val_t *p=get_misc_val(name);
 590     if(!p) {
 591         printf("get_misc_val_value: invalid name %s\n",name);
 592         return 0;
 593     }
 594     return p->val;
 595 }
 596 void save_misc_val(const char *name, uint32_t base, uint32_t offset, uint32_t ref_adr)
 597 {
 598     misc_val_t *p=get_misc_val(name);
 599     if(!p) {
 600         printf("save_misc_val: invalid name %s\n",name);
 601         return;
 602     }
 603     p->val = base + offset;
 604     p->base = base;
 605     p->offset = offset;
 606     p->ref_adr = ref_adr;
 607     p->blobs = NULL;
 608 }
 609 void save_misc_val_blobs(const char *name, misc_blob_t *blobs, uint32_t ref_adr)
 610 {
 611     misc_val_t *p=get_misc_val(name);
 612     if(!p) {
 613         printf("save_misc_val: invalid name %s\n",name);
 614         return;
 615     }
 616     p->val = p->base = p->offset = 0;
 617     p->ref_adr = ref_adr;
 618     p->blobs = blobs;
 619 }
 620 
 621 // Return the array index of a named function in the array above
 622 #if 0
 623 int find_saved_sig_index(const char *name)
 624 {
 625     int i;
 626     for (i=0; sig_names[i].name != 0; i++)
 627     {
 628         if (strcmp(name,sig_names[i].name) == 0)
 629         {
 630             return i;
 631         }
 632     }
 633     return -1;
 634 }
 635 #endif
 636 
 637 sig_entry_t * find_saved_sig(const char *name)
 638 {
 639     int i;
 640     for (i=0; sig_names[i].name != 0; i++)
 641     {
 642         if (strcmp(name,sig_names[i].name) == 0)
 643         {
 644             return &sig_names[i];
 645         }
 646     }
 647     return NULL;
 648 }
 649 
 650 // return value of saved sig, or 0 if not found / doesn't exist
 651 uint32_t get_saved_sig_val(const char *name)
 652 {
 653     sig_entry_t *sig=find_saved_sig(name);
 654     if(!sig) {
 655         // printf("get_saved_sig_val: missing %s\n",name);
 656         return 0;
 657     }
 658     return sig->val;
 659 }
 660 
 661 // unused for now
 662 // Return the array index of of function with given address
 663 #if 0
 664 int find_saved_sig_index_by_adr(uint32_t adr)
 665 {
 666     if(!adr) {
 667         return  -1;
 668     }
 669     int i;
 670     for (i=0; sig_names[i].name != 0; i++)
 671     {
 672         if (sig_names[i].val == adr)
 673         {
 674             return i;
 675         }
 676     }
 677     return -1;
 678 }
 679 #endif
 680 #if 0
 681 sig_entry_t* find_saved_sig_by_val(uint32_t val)
 682 {
 683     if(!val) {
 684         return NULL;
 685     }
 686     int i;
 687     for (i=0; sig_names[i].name != 0; i++)
 688     {
 689         if (sig_names[i].val == val)
 690         {
 691             return &sig_names[i];
 692         }
 693     }
 694     return NULL;
 695 }
 696 #endif
 697 
 698 // Save the address value found for a function in the above array
 699 void save_sig(firmware *fw, const char *name, uint32_t val)
 700 {
 701     sig_entry_t *sig = find_saved_sig(name);
 702     if (!sig)
 703     {
 704         printf("save_sig: refusing to save unknown name %s\n",name);
 705         return;
 706     }
 707     // if we end up needed these, can add a flag
 708     if(!adr_is_main_fw_code(fw,val)) {
 709         printf("save_sig: refusing to save %s with out of range address 0x%08x\n",name,val);
 710         return;
 711     }
 712     if(sig->val && sig->val != val) {
 713         printf("save_sig: duplicate name %s existing 0x%08x != new 0x%08x\n",name,sig->val,val);
 714     }
 715     sig->val = val;
 716 }
 717 
 718 void add_func_name(firmware *fw, char *n, uint32_t eadr, char *suffix)
 719 {
 720     int k;
 721 
 722     char *s = n;
 723     int mallocd = 0;
 724     if (suffix != 0)
 725     {
 726         s = malloc(strlen(n) + strlen(suffix) + 1);
 727         sprintf(s, "%s%s", n, suffix);
 728         mallocd = 1;
 729     }
 730 
 731     // if we end up needed these, can add a flag
 732     if(!adr_is_main_fw_code(fw,eadr)) {
 733         printf("save_sig: refusing to save %s with out of range address 0x%08x\n",s,eadr);
 734         if(mallocd) {
 735             free(s);
 736         }
 737         return;
 738     }
 739 
 740     for (k=0; sig_names[k].name != 0; k++)
 741     {
 742         if (strcmp(sig_names[k].name, s) == 0)
 743         {
 744             if (sig_names[k].val == 0)             // same name, no address
 745             {
 746                 sig_names[k].val = eadr;
 747                 sig_names[k].flags |= EV_MATCH;
 748                 if (mallocd)
 749                     free(s);
 750                 return;
 751             }
 752             else if (sig_names[k].val == eadr)     // same name, same address
 753             {
 754                 if (mallocd)
 755                     free(s);
 756                 return;
 757             }
 758             else // same name, different address
 759             {
 760                 printf("add_func_name: duplicate name %s existing 0x%08x != new 0x%08x\n",s, sig_names[k].val, eadr);
 761             }
 762         }
 763     }
 764 
 765     sig_names[next_sig_entry].name = s;
 766     sig_names[next_sig_entry].flags = OPTIONAL|UNUSED;
 767     sig_names[next_sig_entry].val = eadr;
 768     next_sig_entry++;
 769     sig_names[next_sig_entry].name = 0;
 770 }
 771 
 772 // save sig, with up to one level veneer added as j_...
 773 int save_sig_with_j(firmware *fw, char *name, uint32_t adr)
 774 {
 775     if(!adr) {
 776         printf("save_sig_with_j: %s null adr\n",name);
 777         return 0;
 778     }
 779     // attempt to disassemble target
 780     if(!fw_disasm_iter_single(fw,adr)) {
 781         printf("save_sig_with_j: %s disassembly failed at 0x%08x\n",name,adr);
 782         return 0;
 783     }
 784     // handle functions that immediately jump
 785     // only one level of jump for now, doesn't check for conditionals, but first insn shouldn't be conditional
 786     //uint32_t b_adr=B_target(fw,fw->is->insn);
 787     uint32_t b_adr=get_direct_jump_target(fw,fw->is);
 788     if(b_adr) {
 789         char *buf=malloc(strlen(name)+6);
 790         sprintf(buf,"j_%s",name);
 791         add_func_name(fw,buf,adr,NULL); // this is the orignal named address
 792 //        adr=b_adr | fw->is->thumb; // thumb bit from iter state
 793         adr=b_adr; // thumb bit already handled by get_direct...
 794     }
 795     save_sig(fw,name,adr);
 796     return 1;
 797 }
 798 
 799 // find next call to func named "name" or j_name, up to max_offset form the current is address
 800 // TODO should have a way of dealing with more than one veneer
 801 // TODO max_offset is in bytes, unlike insn search functions that use insn counts
 802 int find_next_sig_call(firmware *fw, iter_state_t *is, uint32_t max_offset, const char *name)
 803 {
 804     uint32_t adr=get_saved_sig_val(name);
 805 
 806     if(!adr) {
 807         printf("find_next_sig_call: missing %s\n",name);
 808         return 0;
 809     }
 810 
 811     search_calls_multi_data_t match_fns[3];
 812 
 813     match_fns[0].adr=adr;
 814     match_fns[0].fn=search_calls_multi_end;
 815     char veneer[128];
 816     sprintf(veneer,"j_%s",name);
 817     adr=get_saved_sig_val(veneer);
 818     if(!adr) {
 819         match_fns[1].adr=0;
 820     } else {
 821         match_fns[1].adr=adr;
 822         match_fns[1].fn=search_calls_multi_end;
 823         match_fns[2].adr=0;
 824     }
 825     return fw_search_insn(fw,is,search_disasm_calls_multi,0,match_fns,is->adr + max_offset);
 826 }
 827 // is the insn pointed to by is a call to "name" or one of it's veneers?
 828 // note: inefficient, should not be used for large searches
 829 int is_sig_call(firmware *fw, iter_state_t *is, const char *name)
 830 {
 831     uint32_t adr=get_branch_call_insn_target(fw,is);
 832     // not a call at all
 833     // TODO could check if unknown veneer
 834     if(!adr) {
 835         return 0;
 836     }
 837     uint32_t sig_adr=get_saved_sig_val(name);
 838     osig* ostub2 = find_sig(fw->sv->stubs,name);
 839     if (ostub2 && ostub2->val)
 840         sig_adr = ostub2->val;
 841     if(!sig_adr) {
 842         printf("is_sig_call: missing %s\n",name);
 843         return 0;
 844     }
 845     if(adr == sig_adr) {
 846         return 1;
 847     }
 848     char veneer[128];
 849     sprintf(veneer,"j_%s",name);
 850     sig_adr=get_saved_sig_val(veneer);
 851     if(!sig_adr) {
 852         return 0;
 853     }
 854     return (adr == sig_adr);
 855 }
 856 
 857 typedef struct sig_rule_s sig_rule_t;
 858 typedef int (*sig_match_fn)(firmware *fw, iter_state_t *is, sig_rule_t *rule);
 859 // signature matching structure
 860 struct sig_rule_s {
 861     sig_match_fn    match_fn;       // function to locate function
 862     char        *name;              // function name used in CHDK
 863     char        *ref_name;          // event / other name to match in the firmware
 864     int         param;              // function specific param/offset
 865     int         dryos_min;          // minimum dryos rel (0 = any)
 866     int         dryos_max;          // max dryos rel to apply this sig to (0 = any)
 867     // DryOS version specific params / offsets - not used yet
 868     /*
 869     int         dryos52_param; // ***** UPDATE for new DryOS version *****
 870     int         dryos54_param;
 871     int         dryos55_param;
 872     int         dryos57_param;
 873     int         dryos58_param;
 874     */
 875 };
 876 
 877 // Get DryOS version specific param
 878 /*
 879 int dryos_param(firmware *fw, sig_rule_t *sig)
 880 {
 881     switch (fw->dryos_ver)
 882     {
 883     case 52:    return sig->dryos52_param;
 884     case 54:    return sig->dryos54_param;
 885     case 55:    return sig->dryos55_param;
 886     case 57:    return sig->dryos57_param;
 887     case 58:    return sig->dryos58_param;
 888     }
 889     return 0;
 890 }
 891 */
 892 
 893 // initialize iter state using address from ref_name, print error and return 0 if not found
 894 int init_disasm_sig_ref(firmware *fw, iter_state_t *is, sig_rule_t *rule)
 895 {
 896     if(!rule->ref_name) {
 897         printf("init_disasm_sig_ref: %s missing ref_name\n",rule->name);
 898         return 0;
 899     }
 900     uint32_t adr=get_saved_sig_val(rule->ref_name);
 901     if(!adr) {
 902         printf("init_disasm_sig_ref: %s missing %s\n",rule->name,rule->ref_name);
 903         return 0;
 904     }
 905     if(!disasm_iter_init(fw,is,adr)) {
 906         printf("init_disasm_sig_ref: %s bad address 0x%08x for %s\n",rule->name,adr,rule->ref_name);
 907         return 0;
 908     }
 909     return 1;
 910 }
 911 
 912 int sig_match_near_str(firmware *fw, iter_state_t *is, sig_rule_t *rule);
 913 
 914 // match 
 915 // r0=ref value
 916 //...
 917 // bl=<our func>
 918 int sig_match_str_r0_call(firmware *fw, iter_state_t *is, sig_rule_t *rule)
 919 {
 920     uint32_t str_adr = find_str_bytes_main_fw(fw,rule->ref_name);
 921     if(!str_adr) {
 922         printf("sig_match_str_r0_call: %s failed to find ref %s\n",rule->name,rule->ref_name);
 923         return  0;
 924     }
 925 
 926 //    printf("sig_match_str_r0_call: %s ref str %s 0x%08x\n",rule->name,rule->ref_name,str_adr);
 927 
 928     // TODO should handle multiple instances of string
 929     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
 930     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
 931         if(is->insn->detail->arm.operands[0].reg == ARM_REG_R0) {
 932             // printf("sig_match_str_r0_call: %s ref str %s ref 0x%"PRIx64"\n",rule->name,rule->ref_name,is->insn->address);
 933             // TODO should check if intervening insn nuke r0
 934             if(insn_match_find_next(fw,is,4,match_b_bl_blximm)) {
 935                 uint32_t adr=get_branch_call_insn_target(fw,is);
 936                 // printf("sig_match_str_r0_call: thumb %s call 0x%08x\n",rule->name,adr);
 937                 return save_sig_with_j(fw,rule->name,adr);
 938             }
 939         }
 940     }
 941     return 0;
 942 }
 943 
 944 // find RegisterEventProcedure
 945 int sig_match_reg_evp(firmware *fw, iter_state_t *is, __attribute__ ((unused))sig_rule_t *rule)
 946 {
 947     const insn_match_t reg_evp_match[]={
 948         {MATCH_INS(MOV,   2),  {MATCH_OP_REG(R2),  MATCH_OP_REG(R1)}},
 949         {MATCH_INS(LDR,   2),  {MATCH_OP_REG(R1),  MATCH_OP_MEM_ANY}},
 950         {MATCH_INS(B,     MATCH_OPCOUNT_IGNORE)},
 951         {ARM_INS_ENDING}
 952     };
 953 
 954     uint32_t e_to_evp=get_saved_sig_val("ExportToEventProcedure_FW");
 955     if(!e_to_evp) {
 956         printf("sig_match_reg_evp: failed to find ExportToEventProcedure, giving up\n");
 957         return 0;
 958     }
 959 
 960     //look for the underlying RegisterEventProcedure function (not currently used)
 961     uint32_t reg_evp=0;
 962     // start at identified Export..
 963     disasm_iter_init(fw,is,e_to_evp);
 964     if(insn_match_seq(fw,is,reg_evp_match)) {
 965         reg_evp=ADR_SET_THUMB(is->insn->detail->arm.operands[0].imm);
 966         //printf("RegisterEventProcedure found 0x%08x at %"PRIx64"\n",reg_evp,is->insn->address);
 967         save_sig(fw,"RegisterEventProcedure",reg_evp);
 968     }
 969     return (reg_evp != 0);
 970 }
 971 
 972 // find event proc table registration, and some other stuff
 973 // TODO this should be broken up to some generic parts
 974 int sig_match_reg_evp_table(firmware *fw, iter_state_t *is, sig_rule_t *rule)
 975 {
 976     // ref to find RegisterEventProcTable
 977     uint32_t str_adr = find_str_bytes_main_fw(fw,rule->ref_name); // note this string may appear more than once, assuming want first
 978     if(!str_adr) {
 979         printf("sig_match_reg_evp_table: failed to find %s\n",rule->ref_name);
 980         return 0;
 981     }
 982     //printf("sig_match_reg_evp_table: DispDev_EnableEventProc 0x%08x\n",str_adr);
 983     uint32_t reg_evp_alt1=0;
 984     uint32_t reg_evp_tbl=0;
 985     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
 986     uint32_t dd_enable_p=0;
 987     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
 988         if(is->insn->detail->arm.operands[0].reg != ARM_REG_R0) {
 989             continue;
 990         }
 991         if(!insn_match_find_next(fw,is,2,match_b_bl)) {
 992             continue;
 993         }
 994         reg_evp_alt1=ADR_SET_THUMB(is->insn->detail->arm.operands[0].imm);
 995         //printf("RegisterEventProcedure_alt1 found 0x%08x at %"PRIx64"\n",reg_evp_alt1,is->insn->address);
 996         save_sig(fw,"RegisterEventProcedure_alt1",reg_evp_alt1);
 997 
 998         uint32_t regs[4];
 999 
1000         // get r0 and r1, backtracking up to 4 instructions
1001         if((get_call_const_args(fw,is,4,regs)&3)==3) {
1002             // sanity check, arg0 was the original thing we were looking for
1003             if(regs[0]==str_adr) {
1004                 dd_enable_p=regs[1]; // constant value should already have correct ARM/THUMB bit
1005                 //printf("DispDev_EnableEventProc found 0x%08x at %"PRIx64"\n",dd_enable_p,is->insn->address);
1006                 add_func_name(fw,"DispDev_EnableEventProc",dd_enable_p,NULL);
1007                 break;
1008             }
1009         }
1010     } 
1011     // found candidate function
1012     if(dd_enable_p) {
1013         disasm_iter_init(fw,is,dd_enable_p); // start at found func
1014         if(insn_match_find_next(fw,is,4,match_b_bl)) { // find the first bl
1015             // sanity check, make sure we get a const in r0
1016             uint32_t regs[4];
1017             if(get_call_const_args(fw,is,4,regs)&1) {
1018                 reg_evp_tbl=ADR_SET_THUMB(is->insn->detail->arm.operands[0].imm);
1019                 // printf("RegisterEventProcTable found 0x%08x at %"PRIx64"\n",reg_evp_tbl,is->insn->address);
1020                 save_sig(fw,"RegisterEventProcTable",reg_evp_tbl);
1021             }
1022         }
1023     }
1024     return (reg_evp_tbl != 0);
1025 }
1026 
1027 // find an alternate eventproc registration call
1028 int sig_match_reg_evp_alt2(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1029 {
1030     uint32_t reg_evp_alt2=0;
1031     // TODO could make this a param for different fw variants
1032     uint32_t str_adr = find_str_bytes_main_fw(fw,rule->ref_name);
1033     if(!str_adr) {
1034         printf("sig_match_reg_evp_alt2: failed to find %s\n",rule->ref_name);
1035         return 0;
1036     }
1037     //printf("sig_match_reg_evp_alt2: EngApp.Delete 0x%08x\n",str_adr);
1038     uint32_t reg_evp_alt1=get_saved_sig_val("RegisterEventProcedure_alt1");
1039 
1040     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
1041     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
1042         if(is->insn->detail->arm.operands[0].reg != ARM_REG_R0) {
1043             continue;
1044         }
1045         if(!insn_match_find_next(fw,is,3,match_b_bl)) {
1046             continue;
1047         }
1048         uint32_t regs[4];
1049         // sanity check, constants in r0, r1 and r0 was the original thing we were looking for
1050         if((get_call_const_args(fw,is,4,regs)&3)==3) {
1051             if(regs[0]==str_adr) {
1052                 reg_evp_alt2=ADR_SET_THUMB(is->insn->detail->arm.operands[0].imm);
1053                 // TODO could keep looking
1054                 if(reg_evp_alt2 == reg_evp_alt1) {
1055                     printf("RegisterEventProcedure_alt2 == _alt1 at %"PRIx64"\n",is->insn->address);
1056                     reg_evp_alt2=0;
1057                 } else {
1058                     save_sig(fw,"RegisterEventProcedure_alt2",reg_evp_alt2);
1059                    // printf("RegisterEventProcedure_alt2 found 0x%08x at %"PRIx64"\n",reg_evp_alt2,is->insn->address);
1060                     // TODO could follow alt2 and make sure it matches expected mov r2,0, bl register..
1061                 }
1062                 break;
1063             }
1064         }
1065     }
1066     return (reg_evp_alt2 != 0);
1067 }
1068 
1069 // find UnRegisterEventProc (made up name) for reference, finding tables missed by reg_event_proc_table
1070 int sig_match_unreg_evp_table(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1071 {
1072     uint32_t str_adr = find_str_bytes_main_fw(fw,rule->ref_name);
1073     if(!str_adr) {
1074         printf("sig_match_unreg_evp_table: failed to find %s\n",rule->ref_name);
1075         return 0;
1076     }
1077     // for checks
1078     uint32_t reg_evp_alt1=get_saved_sig_val("RegisterEventProcedure_alt1");
1079     uint32_t reg_evp_alt2=get_saved_sig_val("RegisterEventProcedure_alt2");
1080 
1081     uint32_t mecha_unreg=0;
1082     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
1083     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
1084         //printf("sig_match_unreg_evp_table: found ref 0x%"PRIx64"\n",is->insn->address);
1085         if(is->insn->detail->arm.operands[0].reg != ARM_REG_R0) {
1086             continue;
1087         }
1088         if(!insn_match_find_next(fw,is,3,match_b_bl)) {
1089             continue;
1090         }
1091         uint32_t reg_call=get_branch_call_insn_target(fw,is);
1092         // wasn't a registration call (could be unreg)
1093         // TODO could check veneers
1094         if(!reg_call || (reg_call != reg_evp_alt1 && reg_call != reg_evp_alt2)) {
1095             continue;
1096         }
1097         uint32_t regs[4];
1098         if((get_call_const_args(fw,is,4,regs)&3)==3) {
1099             // sanity check we got the right string
1100             if(regs[0]==str_adr) {
1101                 mecha_unreg=ADR_SET_THUMB(regs[1]);
1102                 break;
1103             }
1104         }
1105     }
1106     if(!mecha_unreg) {
1107         return 0;
1108     }
1109     // follow
1110     disasm_iter_init(fw,is,mecha_unreg);
1111     // find first func call
1112     if(!insn_match_find_next(fw,is,7,match_b_bl)) {
1113         return 0;
1114     }
1115     // now find next ldr pc. Could follow above func, but this way picks up veneer on many fw
1116     const insn_match_t match_ldr_r0[]={
1117         {MATCH_INS(LDR, 2),   {MATCH_OP_REG(R0),  MATCH_OP_MEM_BASE(PC)}},
1118         {ARM_INS_ENDING}
1119     };
1120     if(!insn_match_find_next(fw,is,18,match_ldr_r0)) {
1121         return 0;
1122     }
1123     uint32_t tbl=LDR_PC2val(fw,is->insn);
1124     if(!tbl) {
1125         return 0;
1126     }
1127     if(!disasm_iter(fw,is)) {
1128         printf("sig_match_unreg_evp_table: disasm failed\n");
1129         return 0;
1130     }
1131     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
1132 }
1133 // find some veneers of RegisterEventProcTable/UnRegisterEventProcTable
1134 // expects b <something else>, b <ref_name>
1135 // TODO should be a generic find veneer near?
1136 int sig_match_evp_table_veneer(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1137 {
1138     uint32_t ref_adr = get_saved_sig_val(rule->ref_name);
1139     int prevb = 0;
1140     uint32_t cadr;
1141     // following should probably be done using fw_search_insn
1142     // both veneers consist of a single b instruction, always preceded by another b instruction
1143     disasm_iter_init(fw,is,ref_adr); // start at our known function
1144     while (is->adr < (ref_adr+0x800)) {
1145         cadr = is->adr;
1146         if (!disasm_iter(fw,is)) {
1147             disasm_iter_set(fw,is,(is->adr+2) | fw->thumb_default);
1148         }
1149         else {
1150             if (is->insn->id == ARM_INS_B) {
1151                 uint32_t b_adr = get_branch_call_insn_target(fw,is);
1152                 if (prevb && (b_adr == ref_adr)) {
1153                     // this doesn't use _with_j since we want identify the veneer
1154                     add_func_name(fw,rule->name,cadr | is->thumb,NULL);
1155                     return 1;
1156                 }
1157                 prevb = 1;
1158             }
1159         }
1160     }
1161     return 0;
1162 }
1163 
1164 int sig_match_get_nd_value(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1165 {
1166     // this match is only valid for cameras with both ND and Iris
1167     if(!get_misc_val_value("CAM_HAS_ND_FILTER") || !get_misc_val_value("CAM_HAS_IRIS_DIAPHRAGM")) {
1168         return 0;
1169     }
1170 
1171     if(!init_disasm_sig_ref(fw,is,rule)) {
1172         return 0;
1173     }
1174     if(!find_next_sig_call(fw,is,16,"ClearEventFlag")) {
1175         printf("sig_match_get_nd_value: no match ClearEventFlag\n");
1176         return 0;
1177     }
1178     if(!insn_match_find_next(fw,is,4,match_bl_blximm)) {
1179         printf("sig_match_get_nd_value: bl match 1 failed\n");
1180         return 0;
1181     }
1182     // follow
1183     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
1184     disasm_iter(fw,is);
1185     if (B_target(fw,is->insn))
1186         disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
1187     // first call should be either get_nd_value or GetUsableAvRange
1188     if(!insn_match_find_next(fw,is,5,match_bl_blximm)) {
1189         printf("sig_match_get_nd_value: bl match 2 failed\n");
1190         return 0;
1191     }
1192     uint32_t addr=get_branch_call_insn_target(fw,is);
1193     if(addr == get_saved_sig_val("GetUsableAvRange")) {
1194         printf("sig_match_get_nd_value: found GetUsableAvRange, iris or ND only?\n");
1195         return 0;
1196     }
1197     return save_sig_with_j(fw,rule->name,addr);
1198 }
1199 
1200 int sig_match_get_current_exp(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1201 {
1202     if(!init_disasm_sig_ref(fw,is,rule)) {
1203         return 0;
1204     }
1205     if(!insn_match_find_next(fw,is,2,match_bl_blximm)) {
1206         printf("sig_match_get_current_exp: bl match 1 failed\n");
1207         return 0;
1208     }
1209     // follow
1210     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
1211     if(!insn_match_find_next(fw,is,6,match_bl_blximm)) {
1212         printf("sig_match_get_current_exp: bl match 2 failed\n");
1213         return 0;
1214     }
1215     // follow
1216     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
1217     if(!insn_match_find_next(fw,is,6,match_bl_blximm)) {
1218         printf("sig_match_get_current_exp: bl match 3 failed\n");
1219         return 0;
1220     }
1221     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
1222 }
1223 
1224 int sig_match_get_current_nd_value(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1225 {
1226     // this match is only valid for cameras with both ND and Iris
1227     if(!get_misc_val_value("CAM_HAS_ND_FILTER") || !get_misc_val_value("CAM_HAS_IRIS_DIAPHRAGM")) {
1228         return 0;
1229     }
1230     if(!init_disasm_sig_ref(fw,is,rule)) {
1231         return 0;
1232     }
1233     if(!find_next_sig_call(fw,is,36,"GetCurrentShutterSpeed_FW")) {
1234         printf("sig_match_get_current_nd_value: no match GetCurrentShutterSpeed_FW\n");
1235         return 0;
1236     }
1237     // bl <func we want>
1238     // strh r0, [rN,8]
1239     const insn_match_t match_bl_strh[]={
1240         {MATCH_INS(BL,MATCH_OPCOUNT_IGNORE)},
1241         {MATCH_INS(STRH,2), {MATCH_OP_REG(R0),  MATCH_OP_MEM(INVALID,INVALID,0x8)}},
1242         {ARM_INS_ENDING}
1243     };
1244     if(!insn_match_find_next_seq(fw,is,10,match_bl_strh)) {
1245         printf("sig_match_get_current_nd_value: match failed\n");
1246         return 0;
1247     }
1248     // rewind one for call
1249     disasm_iter_init(fw,is,adr_hist_get(&is->ah,1));
1250     disasm_iter(fw,is);
1251     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
1252 }
1253 
1254 int sig_match_imager_active_callback(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1255 {
1256     if(!init_disasm_sig_ref(fw,is,rule)) {
1257         return 0;
1258     }
1259     const insn_match_t match_ldr_bl_mov_pop[]={
1260         {MATCH_INS(LDR, 2),   {MATCH_OP_REG(R0),  MATCH_OP_MEM_BASE(PC)}},
1261         {MATCH_INS(BL, MATCH_OPCOUNT_IGNORE)},
1262         {MATCH_INS(MOV,2),  {MATCH_OP_REG(R0),  MATCH_OP_IMM(0)}},
1263         {MATCH_INS(POP, MATCH_OPCOUNT_IGNORE)},
1264         {ARM_INS_ENDING}
1265     };
1266 
1267     if(!insn_match_find_next_seq(fw,is,28,match_ldr_bl_mov_pop)) {
1268         printf("sig_match_imager_active_callback: match failed\n");
1269         return 0;
1270     }
1271     // rewind to LDR r0,...
1272     disasm_iter_init(fw,is,adr_hist_get(&is->ah,3));
1273     // get LDR value
1274     disasm_iter(fw,is);
1275     uint32_t f1=LDR_PC2val(fw,is->insn);
1276 //    printf("f1 0x%08x\n",f1);
1277     // thumb bit should be set correctly
1278     return save_sig_with_j(fw,rule->name,f1);
1279 }
1280 int sig_match_imager_active(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1281 {
1282     if(!init_disasm_sig_ref(fw,is,rule)) {
1283         return 0;
1284     }
1285 
1286     const insn_match_t match_ldr_mov_str_pop[]={
1287         {MATCH_INS(LDR, 2), {MATCH_OP_REG_ANY,  MATCH_OP_MEM_BASE(PC)}},
1288         {MATCH_INS(MOV,2), {MATCH_OP_REG_ANY,  MATCH_OP_IMM(1)}},
1289         {MATCH_INS(STR, 2), {MATCH_OP_REG_ANY,  MATCH_OP_MEM_ANY}},
1290         {MATCH_INS(POP, MATCH_OPCOUNT_IGNORE)},
1291         {ARM_INS_ENDING}
1292     };
1293 
1294     int backtrack=3;
1295     if(!insn_match_find_next_seq(fw,is,10,match_ldr_mov_str_pop)) {
1296         // re-init and try reverse mov/ldr order
1297         init_disasm_sig_ref(fw,is,rule);
1298         const insn_match_t match_mov_ldr_str_pop[]={
1299             {MATCH_INS(MOV,2), {MATCH_OP_REG_ANY,  MATCH_OP_IMM(1)}},
1300             {MATCH_INS(LDR, 2), {MATCH_OP_REG_ANY,  MATCH_OP_MEM_BASE(PC)}},
1301             {MATCH_INS(STR, 2), {MATCH_OP_REG_ANY,  MATCH_OP_MEM_ANY}},
1302             {MATCH_INS(POP, MATCH_OPCOUNT_IGNORE)},
1303             {ARM_INS_ENDING}
1304         };
1305         if(!insn_match_find_next_seq(fw,is,10,match_mov_ldr_str_pop)) {
1306             printf("sig_match_imager_active: match failed\n");
1307             return 0;
1308         }
1309         backtrack=2;
1310     }
1311     // rewind to LDR
1312     disasm_iter_init(fw,is,adr_hist_get(&is->ah,backtrack));
1313     disasm_iter(fw,is);
1314     uint32_t base=LDR_PC2val(fw,is->insn);
1315     uint32_t reg=is->insn->detail->arm.operands[0].reg;
1316 //    printf("base 0x%08x @0x%08x\n",base,(uint32_t)is->insn->address);
1317     // skip mov if after LDR
1318     if(backtrack == 3) {
1319         disasm_iter(fw,is);
1320     }
1321     disasm_iter(fw,is);
1322     // sanity check base is the same as LDR'd to
1323     if(is->insn->detail->arm.operands[1].mem.base != reg) {
1324         printf("sig_match_imager_active: reg mismatch\n");
1325         return 0;
1326     }
1327     uint32_t off=is->insn->detail->arm.operands[1].mem.disp;
1328 //    printf("off 0x%08x @0x%08x\n",off,(uint32_t)is->insn->address);
1329     save_misc_val("imager_active",base,off,(uint32_t)is->insn->address);
1330     return 1;
1331 }
1332 
1333 int sig_match_screenlock_helper(firmware *fw, iter_state_t *is, sig_rule_t *rule) {
1334     if(!init_disasm_sig_ref(fw,is,rule)) {
1335         return 0;
1336     }
1337     uint32_t init_adr = (uint32_t)is->adr | is->thumb;
1338     // match specific instruction sequence instead of first call because g3x, g5x have different code
1339     // not by dryos rev, sx710 has same code as earlier cams
1340     const insn_match_t match_cmp_bne_bl[]={
1341         {MATCH_INS(CMP, 2), {MATCH_OP_REG(R0),  MATCH_OP_IMM(0)}},
1342         {MATCH_INS_CC(B,NE,MATCH_OPCOUNT_IGNORE)},
1343         {MATCH_INS(BL,MATCH_OPCOUNT_IGNORE)},
1344         {ARM_INS_ENDING}
1345     };
1346     const insn_match_t match_ldrpc_mov_b[]={
1347         {MATCH_INS(LDR, 2), {MATCH_OP_REG(R0),  MATCH_OP_MEM_BASE(PC)}},
1348         {MATCH_INS(MOV, 2), {MATCH_OP_REG(R1),  MATCH_OP_IMM(0)}},
1349         {MATCH_INS_CC(B,AL,MATCH_OPCOUNT_IGNORE)},
1350         {ARM_INS_ENDING}
1351     };
1352     // match first seq, this is the func we want
1353     if(insn_match_find_next_seq(fw,is,6,match_cmp_bne_bl)) {
1354         return save_sig_with_j(fw,rule->name,init_adr);
1355 
1356     }
1357     // try alternate match on newer cameras (around r57, but some variation)
1358     // this version puts a pointer to the function used in normal match in r0, 0 in R1, and jumps
1359     // go back to start
1360     disasm_iter_init(fw,is,init_adr);
1361     if(!insn_match_find_next_seq(fw,is,1,match_ldrpc_mov_b)) {
1362         printf("sig_match_screenlock_helper: match 2 failed 0x%"PRIx64"\n",is->insn->address);
1363         return 0;
1364     }
1365     disasm_iter_init(fw,is,init_adr);
1366     disasm_iter(fw,is);
1367     uint32_t adr = LDR_PC2val(fw,is->insn);
1368     if(!adr) {
1369         printf("sig_match_screenlock_helper: no match LDR PC 0x%"PRIx64"\n",is->insn->address);
1370         return 0;
1371     }
1372     disasm_iter_init(fw,is,adr);
1373     // retry original match on pointer
1374     if(!insn_match_find_next_seq(fw,is,6,match_cmp_bne_bl)) {
1375         printf("sig_match_screenlock_helper: match failed 0x%8x\n",init_adr);
1376         return 0;
1377     }
1378     return save_sig_with_j(fw,rule->name,adr);
1379 }
1380 
1381 int sig_match_screenunlock(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1382 {
1383     if(!init_disasm_sig_ref(fw,is,rule)) {
1384         return 0;
1385     }
1386     // look for ScreenLock call to verify right version of UIFS_DisplayFirmUpdateView
1387     if(!find_next_sig_call(fw,is,14,"ScreenLock")) {
1388         // printf("sig_match_screenunlock: no ScreenLock\n");
1389         return 0;
1390     }
1391     
1392     // expect tail call to ScreenUnlock
1393     const insn_match_t match_end[]={
1394         {MATCH_INS(POP, MATCH_OPCOUNT_IGNORE)},
1395         {MATCH_INS_CC(B,AL,MATCH_OPCOUNT_IGNORE)},
1396         {ARM_INS_ENDING}
1397     };
1398     if(!insn_match_find_next_seq(fw,is,38,match_end)) {
1399         // printf("sig_match_screenunlock: match failed\n");
1400         return 0;
1401     }
1402     // TODO would be nice to have some validation
1403     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
1404 }
1405 
1406 // look for f(0x60,"_Simage") at start of task_StartupImage
1407 int sig_match_log_camera_event(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1408 {
1409     if(!init_disasm_sig_ref(fw,is,rule)) {
1410         return 0;
1411     }
1412     if(!insn_match_find_next(fw,is,6,match_bl_blximm)) {
1413         // printf("sig_match_log_camera_event: bl match failed\n");
1414         return 0;
1415     }
1416     uint32_t regs[4];
1417     if((get_call_const_args(fw,is,4,regs)&3)!=3) {
1418         // printf("sig_match_log_camera_event: get args failed\n");
1419         return 0;
1420     }
1421     if(regs[0] != 0x60) {
1422         // printf("sig_match_log_camera_event: bad r0 0x%x\n",regs[0]);
1423         return 0;
1424     }
1425     const char *str=(char *)adr2ptr(fw,regs[1]);
1426     if(!str || strcmp(str,"_SImage") != 0) {
1427         // printf("sig_match_log_camera_event: bad r1 0x%x\n",regs[1]);
1428         return 0;
1429     }
1430     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
1431 }
1432 
1433 // TODO this finds multiple values in PhySwTask main function
1434 int sig_match_physw_misc(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1435 {
1436     if(!init_disasm_sig_ref(fw,is,rule)) {
1437         osig* ostub2 = find_sig(fw->sv->stubs,rule->ref_name);
1438         if (ostub2 && ostub2->val)
1439         {
1440             disasm_iter_init(fw,is,ostub2->val);
1441         }
1442         else
1443         {
1444             return 0;
1445         }
1446     }
1447     int i;
1448     uint32_t physw_run=0;
1449     for(i=0; i<3; i++) {
1450         if(!disasm_iter(fw,is)) {
1451             printf("sig_match_physw_misc: disasm failed\n");
1452             return 0;
1453         }
1454         physw_run=LDR_PC2val(fw,is->insn);
1455         if(physw_run) {
1456             if(adr_is_var(fw,physw_run)) {
1457                 save_misc_val("physw_run",physw_run,0,(uint32_t)is->insn->address);
1458                 break;
1459             } else {
1460                 printf("sig_match_physw_misc: adr not data? 0x%08x\n",physw_run);
1461                 return 0;
1462             }
1463         }
1464     }
1465     if(!physw_run) {
1466         return 0;
1467     }
1468 
1469     // look for physw_sleep_delay, offset from physw_run, loaded before SleepTask
1470     if(!insn_match_find_next(fw,is,7,match_bl_blximm)) {
1471         return 0;
1472     }
1473     uint32_t sleeptask=get_saved_sig_val("SleepTask");
1474     if(!sleeptask) {
1475         printf("sig_match_physw_misc: missing SleepTask\n");
1476         return 0;
1477     }
1478     uint32_t f=get_branch_call_insn_target(fw,is);
1479 
1480     // call wasn't direct, check for veneer
1481     if(f != sleeptask) {
1482         fw_disasm_iter_single(fw,f);
1483         uint32_t f2=get_direct_jump_target(fw,fw->is);
1484         if(f2 != sleeptask) {
1485             return 0;
1486         }
1487         // sleeptask veneer is useful for xref
1488         // re-adding existing won't hurt
1489         save_sig_with_j(fw,"SleepTask",f);
1490     }
1491     // rewind 1 for r0
1492     disasm_iter_init(fw,is,adr_hist_get(&is->ah,1));
1493     if(!disasm_iter(fw,is)) {
1494         printf("sig_match_physw_misc: disasm failed\n");
1495         return 0;
1496     }
1497     // TODO could check base is same reg physw_run was loaded into
1498     if(is->insn->id != ARM_INS_LDR
1499         || is->insn->detail->arm.operands[0].reg != ARM_REG_R0) {
1500         return 0;
1501     }
1502     save_misc_val("physw_sleep_delay",physw_run,is->insn->detail->arm.operands[1].mem.disp,(uint32_t)is->insn->address);
1503     // skip over sleeptask to next insn
1504     if(!disasm_iter(fw,is)) {
1505         printf("sig_match_physw_misc: disasm failed\n");
1506         return 0;
1507     }
1508     
1509     // look for kbd_p1_f
1510     if(!insn_match_find_next(fw,is,2,match_bl_blximm)) {
1511         return 0;
1512     }
1513     save_sig(fw,"kbd_p1_f",get_branch_call_insn_target(fw,is));
1514 
1515     // look for kbd_p2_f
1516     if(!insn_match_find_next(fw,is,4,match_bl_blximm)) {
1517         return 0;
1518     }
1519     save_sig(fw,"kbd_p2_f",get_branch_call_insn_target(fw,is));
1520     return 1;
1521 }
1522 // TODO also finds p1_f_cont, physw_status
1523 int sig_match_kbd_read_keys(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1524 {
1525     if(!init_disasm_sig_ref(fw,is,rule)) {
1526         return 0;
1527     }
1528     // look for kbd_read_keys
1529     if(!insn_match_find_next(fw,is,4,match_bl_blximm)) {
1530         return 0;
1531     }
1532     save_sig(fw,"kbd_read_keys",get_branch_call_insn_target(fw,is));
1533     if(!disasm_iter(fw,is)) {
1534         printf("sig_match_kbd_read_keys: disasm failed\n");
1535         return 0;
1536     }
1537     uint32_t physw_status=LDR_PC2val(fw,is->insn);
1538     if(physw_status) {
1539         save_misc_val("physw_status",physw_status,0,(uint32_t)is->insn->address);
1540         save_sig(fw,"kbd_p1_f_cont",(uint32_t)(is->insn->address) | is->thumb);
1541         return 1;
1542     }
1543     return 0;
1544 }
1545 
1546 // TODO also finds kbd_read_keys_r2
1547 int sig_match_get_kbd_state(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1548 {
1549     if(!init_disasm_sig_ref(fw,is,rule)) {
1550         return 0;
1551     }
1552     // instructions that zero out physw_status
1553     insn_match_t match[]={
1554         {MATCH_INS(LDR, 2), {MATCH_OP_REG(R0),  MATCH_OP_MEM_BASE(PC)}},
1555         {MATCH_INS(BL,  MATCH_OPCOUNT_IGNORE)},
1556         {ARM_INS_ENDING}
1557     };
1558 
1559     if(!insn_match_find_next_seq(fw,is,11,match)) {
1560         return 0;
1561     }
1562     save_sig_with_j(fw,"GetKbdState",get_branch_call_insn_target(fw,is));
1563     // look for kbd_read_keys_r2
1564     if(!insn_match_find_next(fw,is,5,match_b_bl_blximm)) {
1565         return 0;
1566     }
1567     save_sig_with_j(fw,"kbd_read_keys_r2",get_branch_call_insn_target(fw,is));
1568     return 1;
1569 }
1570 
1571 int sig_match_get_dial_hw_position(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1572 {
1573     if(!init_disasm_sig_ref(fw,is,rule)) {
1574         return 0;
1575     }
1576     uint32_t adr = find_last_call_from_func(fw,is,18,50);
1577     if(!adr) {
1578         // printf("sig_match_get_dial_hw_position: no match call 1 at 0x%"PRIx64"\n",is->insn->address);
1579         return 0;
1580     }
1581     // follow
1582     disasm_iter_init(fw,is,adr);
1583     adr = find_last_call_from_func(fw,is,16,32);
1584     if(!adr) {
1585         // printf("sig_match_get_dial_hw_position: no match call 2 at 0x%"PRIx64"\n",is->insn->address);
1586         return 0;
1587     }
1588     // follow
1589     disasm_iter_init(fw,is,adr);
1590     // find next function call
1591     if(!insn_match_find_next(fw,is,30,match_bl_blximm)) {
1592         // printf("sig_match_get_dial_hw_position: no match call 3 at 0x%"PRIx64"\n",is->insn->address);
1593         return 0;
1594     }
1595     uint32_t fadr = get_branch_call_insn_target(fw,is);
1596     // rewind and match instructions for sanity check
1597     disasm_iter_init(fw,is,adr_hist_get(&is->ah,4));
1598     const insn_match_t match_hw_dial_call[]={
1599         {MATCH_INS(ADD,MATCH_OPCOUNT_ANY), {MATCH_OP_REG(R0)}},
1600         {MATCH_INS(LDR, 2), {MATCH_OP_REG(R0),MATCH_OP_MEM_BASE(PC)}},
1601         {MATCH_INS(BL,MATCH_OPCOUNT_IGNORE)},
1602         {ARM_INS_ENDING}
1603     };
1604     if(!insn_match_find_next(fw,is,4,match_hw_dial_call)) {
1605         // printf("sig_match_get_dial_hw_position: no match seq 0x%"PRIx64"\n",is->insn->address);
1606         return 0;
1607     }
1608     return save_sig_with_j(fw,rule->name,fadr);
1609 }
1610 
1611 int sig_match_create_jumptable(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1612 {
1613     if(!init_disasm_sig_ref(fw,is,rule)) {
1614         return 0;
1615     }
1616     // find second function call
1617     if(!insn_match_find_nth(fw,is,20,2,match_bl_blximm)) {
1618         return 0;
1619     }
1620     // follow
1621     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
1622     if(!insn_match_find_next(fw,is,15,match_bl_blximm)) {
1623         return 0;
1624     }
1625     // TODO could verify it looks right (version string)
1626     save_sig(fw,"CreateJumptable",get_branch_call_insn_target(fw,is));
1627     return 1;
1628 }
1629 
1630 // TODO this actually finds a bunch of different stuff
1631 int sig_match_take_semaphore_strict(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1632 {
1633     if(!init_disasm_sig_ref(fw,is,rule)) {
1634         return 0;
1635     }
1636     // find first function call
1637     if(!insn_match_find_next(fw,is,6,match_bl_blximm)) {
1638         return 0;
1639     }
1640     // follow
1641     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
1642     // find second call
1643     if(!insn_match_find_nth(fw,is,10,2,match_bl_blximm)) {
1644         return 0;
1645     }
1646     // follow
1647     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
1648     // skip two calls, next should be DebugAssert
1649     if(!insn_match_find_nth(fw,is,20,3,match_bl_blximm)) {
1650         return 0;
1651     }
1652     save_sig_with_j(fw,"DebugAssert",get_branch_call_insn_target(fw,is));
1653 
1654     // next should be TakeSemaphoreStrictly
1655     if(!insn_match_find_next(fw,is,7,match_bl_blximm)) {
1656         return 0;
1657     }
1658     save_sig_with_j(fw,"TakeSemaphoreStrictly",get_branch_call_insn_target(fw,is));
1659     arm_reg ptr_reg = ARM_REG_INVALID;
1660     uint32_t sem_adr=0;
1661     int i;
1662     // iterate backwards looking for the value put in r0
1663     for(i=1; i<7; i++) {
1664         fw_disasm_iter_single(fw,adr_hist_get(&is->ah,i));
1665         cs_insn *insn=fw->is->insn;
1666         if(insn->id != ARM_INS_LDR) {
1667             continue;
1668         }
1669         if(ptr_reg == ARM_REG_INVALID
1670             && insn->detail->arm.operands[0].reg == ARM_REG_R0
1671             && insn->detail->arm.operands[1].mem.base != ARM_REG_PC) {
1672             ptr_reg = insn->detail->arm.operands[1].mem.base;
1673             continue;
1674         }
1675         if(ptr_reg == ARM_REG_INVALID || !isLDR_PC(insn) || (arm_reg)insn->detail->arm.operands[0].reg != ptr_reg) {
1676             continue;
1677         }
1678         sem_adr=LDR_PC2val(fw,insn);
1679         if(sem_adr) {
1680             break;
1681         }
1682     }
1683     if(!sem_adr) {
1684         return 0;
1685     }
1686     save_misc_val("fileio_semaphore",sem_adr,0,(uint32_t)is->insn->address);
1687     // look for next call: GetDrive_FreeClusters
1688     if(!insn_match_find_next(fw,is,10,match_bl_blximm)) {
1689         return 0;
1690     }
1691     return save_sig_with_j(fw,"GetDrive_FreeClusters",get_branch_call_insn_target(fw,is));
1692 }
1693 
1694 int sig_match_get_semaphore_value(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1695 {
1696     uint32_t str_adr = find_str_bytes_main_fw(fw,rule->ref_name);
1697     if(!str_adr) {
1698         printf("sig_get_semaphore_value: failed to find ref %s\n",rule->ref_name);
1699         return 0;
1700     }
1701 
1702     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
1703     // assume first / only ref
1704     if(!fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
1705         // printf("sig_get_semaphore_value: failed to find code ref to %s\n",rule->ref_name);
1706         return 0;
1707     }
1708     // search backwards for func call
1709     uint32_t fadr=0;
1710     int i;
1711     for(i=1; i<=5; i++) {
1712         if(!fw_disasm_iter_single(fw,adr_hist_get(&is->ah,i))) {
1713             // printf("sig_get_semaphore_value: disasm failed\n");
1714             return 0;
1715         }
1716         if(insn_match_any(fw->is->insn,match_bl_blximm)){
1717             fadr=get_branch_call_insn_target(fw,fw->is);
1718             break;
1719         }
1720     }
1721     if(!fadr) {
1722         // printf("sig_get_semaphore_value: failed to find bl 1\n");
1723         return 0;
1724     }
1725     // follow
1726     disasm_iter_init(fw,is,fadr);
1727     // look for first call
1728     if(!insn_match_find_next(fw,is,9,match_bl_blximm)) {
1729         // printf("sig_get_semaphore_value: failed to find bl 2\n");
1730         return 0;
1731     }
1732     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
1733 }
1734 // similar to sig_match_str_r0_call, but string also appears with Fopen_Fut
1735 int sig_match_stat(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1736 {
1737     uint32_t str_adr = find_str_bytes_main_fw(fw,rule->ref_name);
1738     if(!str_adr) {
1739         printf("sig_match_stat: %s failed to find ref %s\n",rule->name,rule->ref_name);
1740         return  0;
1741     }
1742 
1743     // TODO should handle multiple instances of string
1744     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
1745     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
1746         if(is->insn->detail->arm.operands[0].reg == ARM_REG_R0) {
1747             if(insn_match_find_next(fw,is,2,match_bl_blximm)) {
1748                 uint32_t adr=get_branch_call_insn_target(fw,is);
1749                 // same string ref'd by Fopen
1750                 if(is_sig_call(fw,is,"Fopen_Fut_FW")) {
1751                     continue;
1752                 }
1753                 // TODO could check r1 not a const
1754                 return save_sig_with_j(fw,rule->name,adr);
1755             }
1756         }
1757     }
1758     return 0;
1759 }
1760 static const insn_match_t match_open_mov_call[]={
1761     // 3 reg / reg movs, followed by a call
1762     {MATCH_INS(MOV, 2), {MATCH_OP_REG_ANY,  MATCH_OP_REG_ANY}},
1763     {MATCH_INS(MOV, 2), {MATCH_OP_REG_ANY,  MATCH_OP_REG_ANY}},
1764     {MATCH_INS(MOV, 2), {MATCH_OP_REG_ANY,  MATCH_OP_REG_ANY}},
1765     {MATCH_INS(BL,  MATCH_OPCOUNT_IGNORE)},
1766     {ARM_INS_ENDING}
1767 };
1768 
1769 
1770 // find low level open
1771 int sig_match_open(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1772 {
1773     if(!init_disasm_sig_ref(fw,is,rule)) {
1774         return 0;
1775     }
1776     if(!insn_match_find_next_seq(fw,is,48,match_open_mov_call)) {
1777         return 0;
1778     }
1779     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
1780 }
1781 // find low level open for dryos >= 58
1782 // TODO not verified it works as expected
1783 int sig_match_open_gt_57(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1784 {
1785     if(!init_disasm_sig_ref(fw,is,rule)) {
1786         return 0;
1787     }
1788     if(!find_next_sig_call(fw,is,38,"TakeSemaphoreStrictly")) {
1789         return 0;
1790     }
1791     // looking for next call
1792     // this should be equivalent to previous versions Open, without the extra semaphore wrapper
1793     if(!insn_match_find_next(fw,is,5,match_bl_blximm)) {
1794         return 0;
1795     }
1796     // follow
1797     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
1798     // match same pattern as normal
1799     if(!insn_match_find_next_seq(fw,is,48,match_open_mov_call)) {
1800         return 0;
1801     }
1802     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
1803 }
1804 
1805 // find low level close for dryos >= 58
1806 // TODO not verified it works as expected
1807 int sig_match_close_gt_57(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1808 {
1809     if(!init_disasm_sig_ref(fw,is,rule)) {
1810         return 0;
1811     }
1812     if(!find_next_sig_call(fw,is,34,"TakeSemaphoreStrictly")) {
1813         return 0;
1814     }
1815     // looking for next call
1816     // this should be equivalent to previous versions Close, without the extra semaphore wrapper
1817     if(!insn_match_find_next(fw,is,3,match_bl_blximm)) {
1818         return 0;
1819     }
1820     // follow
1821     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
1822     // first call
1823     if(!insn_match_find_next(fw,is,3,match_bl_blximm)) {
1824         return 0;
1825     }
1826     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
1827 }
1828 
1829 
1830 // AllocateUncacheableMemory
1831 int sig_match_umalloc(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1832 {
1833     if(!init_disasm_sig_ref(fw,is,rule)) {
1834         return 0;
1835     }
1836     // looking for 3rd call
1837     if(!insn_match_find_nth(fw,is,15,3,match_bl_blximm)) {
1838         return 0;
1839     }
1840     //follow
1841     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
1842     // looking for 3rd call again
1843     if(!insn_match_find_nth(fw,is,14,3,match_bl_blximm)) {
1844         return 0;
1845     }
1846     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
1847 }
1848 
1849 // FreeUncacheableMemory
1850 int sig_match_ufree(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1851 {
1852     if(!init_disasm_sig_ref(fw,is,rule)) {
1853         return 0;
1854     }
1855     // find the first call to strcpy
1856     if(!find_next_sig_call(fw,is,60,"strcpy_FW")) {
1857         return 0;
1858     }
1859     // find 3rd call
1860     if(!insn_match_find_nth(fw,is,12,3,match_bl_blximm)) {
1861         return 0;
1862     }
1863     // follow
1864     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
1865     // Look for Close call
1866     if(!find_next_sig_call(fw,is,40,"Close_FW")) {
1867         return 0;
1868     }
1869     // next call should be FreeUncacheableMemory
1870     if(!insn_match_find_next(fw,is,4,match_bl_blximm)) {
1871         return 0;
1872     }
1873     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
1874 }
1875 
1876 int sig_match_deletefile_fut(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1877 {
1878     uint32_t str_adr = find_str_bytes_main_fw(fw,rule->ref_name);
1879     if(!str_adr) {
1880         printf("sig_match_deletefile_fut: %s failed to find ref %s\n",rule->name,rule->ref_name);
1881         return  0;
1882     }
1883     // TODO should handle multiple instances of string
1884     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
1885     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
1886         // next call should be DeleteFile_Fut
1887         if(!insn_match_find_next(fw,is,4,match_bl_blximm)) {
1888             continue;
1889         }
1890         // follow
1891         uint32_t adr=get_branch_call_insn_target(fw,is);
1892         if(!fw_disasm_iter_single(fw,adr)) {
1893             printf("sig_match_deletefile_fut: disasm failed\n");
1894             return 0;
1895         }
1896         const insn_match_t match_mov_r1[]={
1897             {MATCH_INS(MOV,     2), {MATCH_OP_REG(R1),  MATCH_OP_IMM_ANY}},
1898 #if CS_API_MAJOR < 4
1899             {MATCH_INS(MOVS,    2), {MATCH_OP_REG(R1),  MATCH_OP_IMM_ANY}},
1900 #endif
1901             {ARM_INS_ENDING}
1902         };
1903 
1904         if(!insn_match_any(fw->is->insn,match_mov_r1)){
1905             continue;
1906         }
1907         return save_sig_with_j(fw,rule->name,adr);
1908     }
1909     return 0;
1910 }
1911 
1912 uint32_t find_call_near_str(firmware *fw, iter_state_t *is, sig_rule_t *rule);
1913 
1914 int sig_match_closedir(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1915 {
1916     uint32_t str_adr = find_str_bytes_main_fw(fw,rule->ref_name);
1917     if(!str_adr) {
1918         printf("sig_match_closedir: %s failed to find ref %s\n",rule->name,rule->ref_name);
1919         return  0;
1920     }
1921     // TODO should handle multiple instances of string
1922     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
1923     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
1924         if(!find_next_sig_call(fw,is,60,"sprintf_FW")) {
1925             continue;
1926         }
1927         if(insn_match_find_nth(fw,is,7,2,match_bl_blximm)) {
1928             return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
1929         }
1930     }
1931 
1932     uint32_t call_adr = find_call_near_str(fw,is,rule);
1933     if(call_adr) {
1934         disasm_iter_init(fw,is,call_adr); // reset to a bit before where the string was found
1935         const insn_match_t match_closedir[]={
1936             {MATCH_INS(BL,MATCH_OPCOUNT_IGNORE)},
1937             {MATCH_INS(MOV, 2), {MATCH_OP_REG(R0),  MATCH_OP_REG_ANY}},
1938             {MATCH_INS(BL,MATCH_OPCOUNT_IGNORE)},
1939             {ARM_INS_ENDING}
1940         };
1941         if(insn_match_seq(fw,is,match_closedir)){
1942             return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
1943         }
1944     }
1945 
1946     return 0;
1947 }
1948 
1949 // Save sig for function call (BL/BLX) at current instruction
1950 int save_sig_match_call(firmware* fw, sig_rule_t *rule, uint32_t call_adr)
1951 {
1952     disasm_iter_init(fw,fw->is,call_adr); // reset to a bit before where the string was found
1953     disasm_iter(fw,fw->is);
1954     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,fw->is));
1955 }
1956 
1957 int sig_match_readfastdir(firmware *fw, iter_state_t *is, sig_rule_t *rule)
1958 {
1959     uint32_t str_adr;
1960     str_adr = find_str_bytes_main_fw(fw,rule->ref_name);
1961     if(!str_adr) {
1962         printf("sig_match_readfastdir: %s failed to find ref %s\n",rule->name,rule->ref_name);
1963         return 0;
1964     }
1965     const insn_match_t match_cbnz_r0[]={
1966         {MATCH_INS(CBNZ, 2), {MATCH_OP_REG(R0), MATCH_OP_IMM_ANY}},
1967         {ARM_INS_ENDING}
1968     };
1969     const insn_match_t match_cbz_r0[]={
1970         {MATCH_INS(CBZ, 2), {MATCH_OP_REG(R0), MATCH_OP_IMM_ANY}},
1971         {ARM_INS_ENDING}
1972     };
1973     int max_insns=rule->param&SIG_NEAR_OFFSET_MASK;
1974     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
1975     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
1976         uint32_t ref_adr = iter_state_adr(is);
1977         // Check for bl followed by cbnz in previous 2 instructions
1978         fw_disasm_iter_single(fw,adr_hist_get(&is->ah,2));
1979         if(insn_match_any(fw->is->insn,match_bl_blximm)) {
1980             uint32_t call_adr = iter_state_adr(fw->is);
1981             fw_disasm_iter_single(fw,adr_hist_get(&is->ah,1));
1982             if(insn_match_any(fw->is->insn,match_cbnz_r0)) {
1983                 return save_sig_match_call(fw, rule, call_adr);
1984             }
1985         }
1986         int i;
1987         for(i=3; i<=max_insns; i++) {
1988             // Check for bl followed by cbz branching to ref_adr
1989             fw_disasm_iter_single(fw,adr_hist_get(&is->ah,i));
1990             if(insn_match_any(fw->is->insn,match_bl_blximm)) {
1991                 uint32_t call_adr = iter_state_adr(fw->is);
1992                 fw_disasm_iter_single(fw,adr_hist_get(&is->ah,i-1));
1993                 if(insn_match_any(fw->is->insn,match_cbz_r0)) {
1994                     uint32_t b_adr = get_branch_call_insn_target(fw,fw->is);
1995                     if (ref_adr == b_adr) {
1996                         return save_sig_match_call(fw, rule, call_adr);
1997                     }
1998                 }
1999             }
2000         }
2001     }
2002     printf("sig_match_readfastdir: no match %s\n",rule->name);
2003     return 0;
2004 }
2005 
2006 int sig_match_strrchr(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2007 {
2008     uint32_t sig_adr=get_saved_sig_val(rule->name);
2009     if (sig_adr == 0)
2010     {
2011         uint32_t call_adr = find_call_near_str(fw,is,rule);
2012         if(call_adr) {
2013             disasm_iter_init(fw,is,call_adr-4); // reset to a bit before where the string was found
2014             const insn_match_t match_mov_r1_imm[]={
2015                 {MATCH_INS(MOV, 2), {MATCH_OP_REG(R1),  MATCH_OP_IMM_ANY}},
2016                 {ARM_INS_ENDING}
2017             };
2018             if(insn_match_find_next(fw,is,2,match_mov_r1_imm)){
2019                 return save_sig_match_call(fw, rule, call_adr);
2020             }
2021         }
2022     }
2023     return 0;
2024 }
2025 
2026 int sig_match_time(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2027 {
2028     uint32_t str_adr = find_str_bytes_main_fw(fw,rule->ref_name);
2029     if(!str_adr) {
2030         printf("sig_match_time: %s failed to find ref %s\n",rule->name,rule->ref_name);
2031         return  0;
2032     }
2033     uint32_t fadr=0;
2034     // TODO should handle multiple instances of string
2035     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
2036     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
2037         // find second func after str ref
2038         if(insn_match_find_nth(fw,is,6,2,match_bl_blximm)) {
2039             fadr=get_branch_call_insn_target(fw,is);
2040             break;
2041         }
2042     }
2043     if(!fadr) {
2044         return 0;
2045     }
2046     // follow found func
2047     disasm_iter_init(fw,is,fadr);
2048     // find second call
2049     if(insn_match_find_nth(fw,is,11,2,match_bl_blximm)) {
2050         return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2051     }
2052     return 0;
2053 }
2054 
2055 int sig_match_strncpy(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2056 {
2057     if(!init_disasm_sig_ref(fw,is,rule)) {
2058         return 0;
2059     }
2060     if(!find_next_sig_call(fw,is,60,"strcpy_FW")) {
2061         return 0;
2062     }
2063     if(!insn_match_find_next(fw,is,6,match_bl_blximm)) {
2064         return 0;
2065     }
2066     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2067 }
2068 
2069 int sig_match_strncmp(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2070 {
2071     uint32_t str_adr = find_str_bytes_main_fw(fw,rule->ref_name);
2072     if(!str_adr) {
2073         printf("sig_match_strncmp: failed to find ref %s\n",rule->ref_name);
2074         return  0;
2075     }
2076     // TODO should handle multiple instances of string
2077     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
2078     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
2079         if(!insn_match_find_next(fw,is,3,match_bl_blximm)) {
2080             continue;
2081         }
2082         uint32_t regs[4];
2083         if((get_call_const_args(fw,is,4,regs)&6)==6) {
2084             // sanity check we got the right string
2085             if(regs[1]==str_adr &&  regs[2] == strlen(rule->ref_name)) {
2086                 return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2087             }
2088         }
2089     }
2090     return 0;
2091 }
2092 
2093 int sig_match_strtolx(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2094 {
2095     if(!init_disasm_sig_ref(fw,is,rule)) {
2096         return 0;
2097     }
2098     if(!find_next_sig_call(fw,is,130,"strncpy")) {
2099         return 0;
2100     }
2101     // find first call after strncpy
2102     if(!insn_match_find_next(fw,is,6,match_bl_blximm)) {
2103         return 0;
2104     }
2105     uint32_t adr=get_branch_call_insn_target(fw,is);
2106     if(!adr) {
2107         return 0;
2108     }
2109     // follow
2110     disasm_iter_init(fw,is,adr);
2111     if(!disasm_iter(fw,is)) {
2112         printf("sig_match_strtolx: disasm failed\n");
2113         return 0;
2114     }
2115     // expect
2116     // mov r3, #0
2117     // b ...
2118     const insn_match_t match_mov_r3_imm[]={
2119         {MATCH_INS(MOV, 2), {MATCH_OP_REG(R3),  MATCH_OP_IMM_ANY}},
2120         {ARM_INS_ENDING}
2121     };
2122     if(!insn_match(is->insn,match_mov_r3_imm)){
2123         return 0;
2124     }
2125     if(!disasm_iter(fw,is)) {
2126         printf("sig_match_strtolx: disasm failed\n");
2127         return 0;
2128     }
2129     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2130 }
2131 
2132 // find the version of ExecuteEventProcedure that doesn't assert evp isn't reg'd
2133 int sig_match_exec_evp(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2134 {
2135     uint32_t str_adr = find_str_bytes_main_fw(fw,rule->ref_name);
2136     if(!str_adr) {
2137         printf("sig_match_exec_evp: failed to find ref %s\n",rule->ref_name);
2138         return  0;
2139     }
2140     // TODO should handle multiple instances of string
2141     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
2142     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
2143         // search backwards for push {r0,...}
2144         int i;
2145         for(i=1; i<=18; i++) {
2146             if(!fw_disasm_iter_single(fw,adr_hist_get(&is->ah,i))) {
2147                 break;
2148             }
2149             if(fw->is->insn->id == ARM_INS_PUSH && fw->is->insn->detail->arm.operands[0].reg == ARM_REG_R0) {
2150                 // push should be start of func
2151                 uint32_t adr=(uint32_t)(fw->is->insn->address) | is->thumb;
2152                 // search forward in original iter_state for DebugAssert. If found, in the wrong ExecuteEventProcedure
2153                 if(find_next_sig_call(fw,is,28,"DebugAssert")) {
2154                     break;
2155                 }
2156                 return save_sig_with_j(fw,rule->name,adr);
2157             }
2158         }
2159     }
2160     return 0;
2161 }
2162 
2163 int sig_match_fgets_fut(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2164 {
2165     if(!init_disasm_sig_ref(fw,is,rule)) {
2166         return 0;
2167     }
2168     if(!find_next_sig_call(fw,is,16,"Fopen_Fut_FW")) {
2169         return 0;
2170     }
2171     disasm_iter(fw,is);
2172     disasm_iter(fw,is);
2173     if (B_target(fw,is->insn) && (is->insn->detail->arm.cc == ARM_CC_NE)) {
2174         disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
2175     } else {
2176         if (B_target(fw,is->insn) && (is->insn->detail->arm.cc == ARM_CC_NE)) {
2177             disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
2178         }
2179     }
2180     if(!insn_match_find_nth(fw,is,20,1,match_bl_blximm)) {
2181         return 0;
2182     }
2183     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2184 }
2185 
2186 int sig_match_log(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2187 {
2188     if(!init_disasm_sig_ref(fw,is,rule)) {
2189         return 0;
2190     }
2191     const insn_match_t match_pop6[]={
2192         {MATCH_INS(POP, 6), {MATCH_OP_REST_ANY}},
2193         {ARM_INS_ENDING}
2194     };
2195     // skip forward through 3x pop     {r4, r5, r6, r7, r8, lr}
2196     if(!insn_match_find_nth(fw,is,38,3,match_pop6)) {
2197         return 0;
2198     }
2199     // third call
2200     if(!insn_match_find_nth(fw,is,24,3,match_bl_blximm)) {
2201         return 0;
2202     }
2203     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2204 }
2205 
2206 // this only works on DryOS r52 cams
2207 int sig_match_pow_dry_52(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2208 {
2209     if (fw->dryos_ver != 52) {
2210         return 0;
2211     }
2212     if(!init_disasm_sig_ref(fw,is,rule)) {
2213         return 0;
2214     }
2215     const insn_match_t match_ldrd_r0_r1[]={
2216         {MATCH_INS(LDRD,    3), {MATCH_OP_REG(R0), MATCH_OP_REG(R1),    MATCH_OP_ANY}},
2217         {ARM_INS_ENDING}
2218     };
2219     // skip forward to first ldrd    r0, r1, [r...]
2220     if(!insn_match_find_next(fw,is,50,match_ldrd_r0_r1)) {
2221         return 0;
2222     }
2223     // prevent false positive
2224     if(is->insn->detail->arm.operands[2].mem.base == ARM_REG_SP) {
2225         return 0;
2226     }
2227     if(!disasm_iter(fw,is)) {
2228         printf("sig_match_pow: disasm failed\n");
2229         return 0;
2230     }
2231     uint32_t adr=get_branch_call_insn_target(fw,is);
2232     if(!adr) {
2233         return 0;
2234     }
2235     return save_sig_with_j(fw,rule->name,adr);
2236 }
2237 
2238 // match for dryos > r52 cams
2239 int sig_match_pow_dry_gt_52(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2240 {
2241     if (fw->dryos_ver <= 52) {
2242         return 0;
2243     }
2244     if(!init_disasm_sig_ref(fw,is,rule)) {
2245         return 0;
2246     }
2247     const insn_match_t match1a[]={
2248         {MATCH_INS(LDRSH,   2), {MATCH_OP_REG(R0),  MATCH_OP_MEM(SP,INVALID,0x12)}},
2249         {MATCH_INS(LDRD,    3), {MATCH_OP_REG(R2),  MATCH_OP_REG(R3), MATCH_OP_MEM(SP,INVALID,0x20)}},
2250         {MATCH_INS(STR,     2), {MATCH_OP_REG_ANY,  MATCH_OP_MEM(SP,INVALID,0)}},
2251         {MATCH_INS(BL,      MATCH_OPCOUNT_IGNORE)},
2252         {ARM_INS_ENDING}
2253     };
2254     const insn_match_t match1b[]={
2255         {MATCH_INS(MOV,     2), {MATCH_OP_REG(R2),  MATCH_OP_REG(R7)}},
2256         {MATCH_INS(LDRSH,   2), {MATCH_OP_REG(R0),  MATCH_OP_MEM(SP,INVALID,0x12)}},
2257         {MATCH_INS(MOV,     2), {MATCH_OP_REG(R3),  MATCH_OP_REG(R6)}},
2258         {MATCH_INS(STR,     2), {MATCH_OP_REG_ANY,  MATCH_OP_MEM(SP,INVALID,0)}},
2259         {MATCH_INS(BL,      MATCH_OPCOUNT_IGNORE)},
2260         {ARM_INS_ENDING}
2261     };
2262     const insn_match_t* match1[]={ match1a, match1b };
2263     int idx;
2264     for (idx = 0; idx < 2; idx += 1)
2265     {
2266         // match above sequence
2267         if(insn_match_find_next_seq(fw,is,50,match1[idx]))
2268             break;
2269         init_disasm_sig_ref(fw,is,rule);
2270     }
2271     // check for match
2272     if (idx >= 2)
2273         return 0;
2274     // match above sequence
2275     uint32_t adr=get_branch_call_insn_target(fw,is);
2276     if(!adr) {
2277         return 0;
2278     }
2279     // follow bl
2280     disasm_iter_init(fw,is,adr);
2281     const insn_match_t match2a[]={
2282         {MATCH_INS(LDRD,3), {MATCH_OP_REG(R0),  MATCH_OP_REG(R1),   MATCH_OP_MEM_ANY}},
2283         {MATCH_INS(BLX, 1), {MATCH_OP_IMM_ANY}},
2284         {ARM_INS_ENDING}
2285     };
2286     const insn_match_t match2b[]={
2287         {MATCH_INS(MOV, 2), {MATCH_OP_REG(R2),  MATCH_OP_REG(R0)}},
2288         {MATCH_INS(MOV, 2), {MATCH_OP_REG(R3),  MATCH_OP_REG(R1)}},
2289         {MATCH_INS(MOV, 2), {MATCH_OP_REG(R4),  MATCH_OP_IMM(0x40000000)}},
2290         {MATCH_INS(MOV, 2), {MATCH_OP_REG(R0),  MATCH_OP_IMM(0)}},
2291         {MATCH_INS(MOV, 2), {MATCH_OP_REG(R1),  MATCH_OP_REG(R4)}},
2292         {MATCH_INS(BL,  1), {MATCH_OP_IMM_ANY}},
2293         {ARM_INS_ENDING}
2294     };
2295     const insn_match_t* match2[]={ match2a, match2b };
2296     // match above sequence
2297     if(!insn_match_find_next_seq(fw,is,15,match2[idx])) {
2298         return 0;
2299     }
2300     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2301 }
2302 
2303 int sig_match_sqrt(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2304 {
2305     if(!init_disasm_sig_ref(fw,is,rule)) {
2306         return 0;
2307     }
2308     // third call
2309     if(!insn_match_find_nth(fw,is,12,3,match_bl_blximm)) {
2310         return 0;
2311     }
2312     // follow
2313     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
2314     if(!disasm_iter(fw,is)) {
2315         printf("sig_match_sqrt: disasm failed\n");
2316         return 0;
2317     }
2318     uint32_t j_tgt=get_direct_jump_target(fw,is);
2319     // veneer?
2320     if(j_tgt) {
2321         // follow
2322         disasm_iter_init(fw,is,j_tgt);
2323         if(!disasm_iter(fw,is)) {
2324             printf("sig_match_sqrt: disasm failed\n");
2325             return 0;
2326         }
2327     }
2328     // second call/branch (seems to be bhs)
2329     if(!insn_match_find_nth(fw,is,12,2,match_b_bl_blximm)) {
2330         return 0;
2331     }
2332     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2333 }
2334 int sig_match_get_drive_cluster_size(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2335 {
2336     if(!init_disasm_sig_ref(fw,is,rule)) {
2337         return 0;
2338     }
2339     // only handle first match, don't expect multiple refs to string
2340     if(fw_search_insn(fw,is,search_disasm_str_ref,0,"A/OpLogErr.txt",(uint32_t)is->adr+260)) {
2341         // find first call after string ref
2342         if(!insn_match_find_next(fw,is,3,match_bl_blximm)) {
2343             // printf("sig_match_get_drive_cluster_size: bl not found\n");
2344             return 0;
2345         }
2346         // follow
2347         disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
2348         // find second call
2349         if(!insn_match_find_nth(fw,is,13,2,match_bl_blximm)) {
2350             // printf("sig_match_get_drive_cluster_size: call 1 not found\n");
2351             return 0;
2352         }
2353         // follow
2354         disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
2355         disasm_iter(fw,is);
2356         if (B_target(fw, is->insn))
2357             disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
2358         // find next call
2359         if(!insn_match_find_next(fw,is,4,match_bl_blximm)) {
2360             // printf("sig_match_get_drive_cluster_size: call 2 not found\n");
2361             return 0;
2362         }
2363         return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2364     }
2365     return 0;
2366 }
2367 
2368 int sig_match_mktime_ext(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2369 {
2370     uint32_t str_adr = find_str_bytes_main_fw(fw,rule->ref_name);
2371     if(!str_adr) {
2372         printf("sig_match_mktime_ext: failed to find ref %s\n",rule->ref_name);
2373         return  0;
2374     }
2375     // TODO should handle multiple instances of string
2376     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
2377     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
2378         // expect sscanf after str
2379         if(!find_next_sig_call(fw,is,12,"sscanf_FW")) {
2380             // printf("sig_match_mktime_ext: no sscanf\n");
2381             return 0;
2382         }
2383         // find next call
2384         if(!insn_match_find_next(fw,is,22,match_bl_blximm)) {
2385             // printf("sig_match_mktime_ext: no call\n");
2386             return 0;
2387         }
2388         // follow
2389         disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
2390         if(!disasm_iter(fw,is)) {
2391             printf("sig_match_mktime_ext: disasm failed\n");
2392             return 0;
2393         }
2394         uint32_t j_tgt=get_direct_jump_target(fw,is);
2395         // veneer?
2396         if(j_tgt) {
2397             // follow
2398             disasm_iter_init(fw,is,j_tgt);
2399             if(!disasm_iter(fw,is)) {
2400                 printf("sig_match_mktime_ext: disasm failed\n");
2401                 return 0;
2402             }
2403         }
2404         const insn_match_t match_pop4[]={
2405             {MATCH_INS(POP, 4), {MATCH_OP_REST_ANY}},
2406             {MATCH_INS(POP, 6), {MATCH_OP_REST_ANY}},
2407             {ARM_INS_ENDING}
2408         };
2409 
2410         // find pop
2411         if(!insn_match_find_next(fw,is,54,match_pop4)) {
2412             // printf("sig_match_mktime_ext: no pop\n");
2413             return 0;
2414         }
2415         if(!insn_match_find_next(fw,is,1,match_b)) {
2416             // printf("sig_match_mktime_ext: no b\n");
2417             return 0;
2418         }
2419         return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2420     }
2421     return 0;
2422 }
2423 
2424 // match call after ref to "_EnrySRec" because "AC:Rec2PB" ref before push in function, hard to be sure of start
2425 int sig_match_rec2pb(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2426 {
2427     uint32_t str_adr = find_str_bytes_main_fw(fw,rule->ref_name);
2428     if(!str_adr) {
2429         printf("sig_match_mktime_ext: failed to find ref %s\n",rule->ref_name);
2430         return  0;
2431     }
2432     // TODO should handle multiple instances of string
2433     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
2434     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
2435         const insn_match_t match_ldr_cbnz_r0[]={
2436             {MATCH_INS(LDR, 2), {MATCH_OP_REG(R0), MATCH_OP_MEM_ANY}},
2437             {MATCH_INS(CBNZ, 2), {MATCH_OP_REG(R0), MATCH_OP_IMM_ANY}},
2438             {ARM_INS_ENDING}
2439         };
2440         if(!insn_match_find_next_seq(fw,is,10,match_ldr_cbnz_r0)) {
2441             // printf("sig_match_rec2pb: no cbnz\n");
2442             continue;
2443         }
2444         // follow
2445         disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
2446         if(!insn_match_find_next(fw,is,3,match_b_bl_blximm)) {
2447             // printf("sig_match_rec2pb: no call\n");
2448             // followed branch, doesn't make sense to keep searching
2449             return 0;
2450         }
2451         uint32_t adr = iter_state_adr(is);
2452         // follow for sanity check
2453         disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
2454         if(!find_next_sig_call(fw,is,16,"LogCameraEvent")) {
2455             // printf("sig_match_rec2pb: no LogCameraEvent call\n");
2456             return 0;
2457         }
2458         uint32_t regs[4];
2459         if((get_call_const_args(fw,is,4,regs)&3)!=3) {
2460             // printf("sig_match_rec2pb: failed to get args\n");
2461             return 0;
2462         }
2463         // sanity check starts with LogCameraEvent with expected number and string
2464         if(regs[0]==0x60 && adr2ptr(fw,regs[1]) && (strcmp((const char *)adr2ptr(fw,regs[1]),"AC:Rec2PB")==0)) {
2465             return save_sig_with_j(fw,rule->name,adr);
2466         } else {
2467             // printf("sig_match_rec2pb: bad args\n");
2468             return 0;
2469         }
2470     }
2471     return 0;
2472 }
2473 
2474 // could just do sig_match_named, 3rd b, but want more validation
2475 int sig_match_get_parameter_data(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2476 {
2477     if(!init_disasm_sig_ref(fw,is,rule)) {
2478         return 0;
2479     }
2480     const insn_match_t match_cmp_bhs[]={
2481         {MATCH_INS(CMP, 2), {MATCH_OP_REG_ANY, MATCH_OP_IMM_ANY}},
2482         {MATCH_INS_CC(B,HS,MATCH_OPCOUNT_IGNORE)},
2483         {ARM_INS_ENDING}
2484     };
2485     if(!insn_match_find_next_seq(fw,is,4,match_cmp_bhs)) {
2486         // printf("sig_match_get_parameter_data: no match cmp, bhs\n");
2487         return 0;
2488     }
2489     // follow
2490     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
2491     if(!insn_match_find_next(fw,is,1,match_b)) {
2492         // printf("sig_match_get_parameter_data: no match b\n");
2493         return 0;
2494     }
2495     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2496 }
2497 
2498 // PrepareDirectory (via string ref) points at something like
2499 // mov r1, 1
2500 // b PrepareDirectory_x
2501 int sig_match_prepdir_x(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2502 {
2503     if(!init_disasm_sig_ref(fw,is,rule)) {
2504         return 0;
2505     }
2506     const insn_match_t match_mov_r1_1[]={
2507         {MATCH_INS(MOV,     2), {MATCH_OP_REG(R1),  MATCH_OP_IMM(1)}},
2508 #if CS_API_MAJOR < 4
2509         {MATCH_INS(MOVS,    2), {MATCH_OP_REG(R1),  MATCH_OP_IMM(1)}},
2510 #endif
2511         {ARM_INS_ENDING}
2512     };
2513     if(!insn_match_find_next(fw,is,1,match_mov_r1_1)) {
2514         //printf("sig_match_prepdir_x: no match mov\n");
2515         return 0;
2516     }
2517     if(!insn_match_find_next(fw,is,1,match_b)) {
2518         //printf("sig_match_prepdir_x: no match b\n");
2519         return 0;
2520     }
2521     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2522 }
2523 
2524 // PrepareDirectory (via string ref) points at something like
2525 // mov r1, 1
2526 // b PrepareDirectory_x
2527 int sig_match_prepdir_1(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2528 {
2529     uint32_t call_adr = find_call_near_str(fw,is,rule);
2530     if(call_adr) {
2531         disasm_iter_init(fw,is,call_adr);
2532         disasm_iter(fw,is);
2533         disasm_iter(fw,is);
2534         if (!CBx_target(fw,is->insn))
2535         {
2536             rule->param = SIG_NEAR_BEFORE(20,5);
2537             call_adr = find_call_near_str(fw,is,rule);
2538             if(!call_adr) {
2539                 return 0;
2540             }
2541             disasm_iter_init(fw,is,call_adr);
2542             disasm_iter(fw,is);
2543             return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2544         }
2545     }
2546 
2547     rule->param = SIG_NEAR_BEFORE(7,2);
2548     call_adr = find_call_near_str(fw,is,rule);
2549     if(!call_adr) {
2550         return 0;
2551     }
2552     return save_sig_match_call(fw, rule, call_adr);
2553 }
2554 // assume this function is directly after the 2 instructions of ref
2555 int sig_match_prepdir_0(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2556 {
2557     if(!init_disasm_sig_ref(fw,is,rule)) {
2558         return 0;
2559     }
2560     uint32_t ref_pdx=get_saved_sig_val("PrepareDirectory_x");
2561     if(!ref_pdx) {
2562         printf("sig_match_prepdir_0: missing PrepareDirectory_x\n");
2563         return 0;
2564     }
2565     // skip over, assume validated previously
2566     disasm_iter(fw,is);
2567     disasm_iter(fw,is);
2568     // this should be the start address of our function
2569     uint32_t adr=(uint32_t)is->adr|is->thumb;
2570     const insn_match_t match_mov_r1_1[]={
2571         {MATCH_INS(MOV,     2), {MATCH_OP_REG(R1),  MATCH_OP_IMM(0)}},
2572 #if CS_API_MAJOR < 4
2573         {MATCH_INS(MOVS,    2), {MATCH_OP_REG(R1),  MATCH_OP_IMM(0)}},
2574 #endif
2575         {ARM_INS_ENDING}
2576     };
2577     if(!insn_match_find_next(fw,is,1,match_mov_r1_1)) {
2578         //printf("sig_match_prepdir_0: no match mov\n");
2579         return 0;
2580     }
2581     if(!insn_match_find_next(fw,is,1,match_b)) {
2582         //printf("sig_match_prepdir_0: no match b\n");
2583         return 0;
2584     }
2585     uint32_t pdx=get_branch_call_insn_target(fw,is);
2586     if(pdx != ref_pdx) {
2587         //printf("sig_match_prepdir_0: target 0x%08x != 0x%08x\n",pdx,ref_pdx);
2588         return 0;
2589     }
2590     return save_sig_with_j(fw,rule->name,adr);
2591 }
2592 int sig_match_mkdir(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2593 {
2594     if(!init_disasm_sig_ref(fw,is,rule)) {
2595         return 0;
2596     }
2597     const insn_match_t match[]={
2598         {MATCH_INS(STRB,2), {MATCH_OP_REG_ANY,  MATCH_OP_MEM_ANY}},
2599         {MATCH_INS(MOV, 2), {MATCH_OP_REG(R0),  MATCH_OP_REG(SP)}},
2600         {MATCH_INS(LDR, 2), {MATCH_OP_REG(R1),  MATCH_OP_MEM_BASE(SP)}},
2601         {MATCH_INS(BL,  MATCH_OPCOUNT_IGNORE)},
2602         {ARM_INS_ENDING}
2603     };
2604     if(insn_match_find_next_seq(fw,is,148,match)) {
2605         return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2606     }
2607 
2608     init_disasm_sig_ref(fw,is,rule);
2609     const insn_match_t match2[]={
2610         {MATCH_INS(MOV, 2), {MATCH_OP_REG(R1),  MATCH_OP_REG_ANY}},
2611         {MATCH_INS(STRB,2), {MATCH_OP_REG_ANY,  MATCH_OP_MEM_ANY}},
2612         {MATCH_INS(MOV, 2), {MATCH_OP_REG(R0),  MATCH_OP_REG(SP)}},
2613         {MATCH_INS(BL,  MATCH_OPCOUNT_IGNORE)},
2614         {ARM_INS_ENDING}
2615     };
2616     if(!insn_match_find_next_seq(fw,is,148,match2)) {
2617         //printf("sig_match_mkdir: no match\n");
2618         return 0;
2619     }
2620     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2621 }
2622 
2623 int sig_match_add_ptp_handler(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2624 {
2625     uint32_t str_adr = find_str_bytes_main_fw(fw,rule->ref_name);
2626     if(!str_adr) {
2627         printf("sig_match_add_ptp_handler: failed to find ref %s\n",rule->ref_name);
2628         return  0;
2629     }
2630     // TODO should handle multiple instances of string
2631     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
2632     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
2633         // expect CreateTaskStrictly
2634         if(!find_next_sig_call(fw,is,8,"CreateTaskStrictly")) {
2635             // printf("sig_match_add_ptp_handler: no CreateTaskStrictly\n");
2636             continue;
2637         }
2638         // expect add_ptp_handler is 3rd call after CreateTask
2639         if(!insn_match_find_nth(fw,is,13,3,match_bl_blximm)) {
2640             // printf("sig_match_add_ptp_handler: no match bl\n");
2641             return 0;
2642         }
2643         // sanity check, expect opcode, func, 0
2644         uint32_t regs[4];
2645         if((get_call_const_args(fw,is,5,regs)&7)!=7) {
2646             // printf("sig_match_add_ptp_handler: failed to get args\n");
2647             return 0;
2648         }
2649         if(regs[0] < 0x9000 || regs[0] > 0x10000 || !adr2ptr(fw,regs[1]) || regs[2] != 0) {
2650             // printf("sig_match_add_ptp_handler: bad args 0x%08x 0x%08x 0x%08x\n",regs[0],regs[1],regs[2]);
2651             return 0;
2652         }
2653         return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2654     }
2655     return 0;
2656 }
2657 int sig_match_qsort(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2658 {
2659     if(!init_disasm_sig_ref(fw,is,rule)) {
2660         return 0;
2661     }
2662     if(!find_next_sig_call(fw,is,90,"DebugAssert")) {
2663         // printf("sig_match_qsort: no DebugAssert\n");
2664         return 0;
2665     }
2666     if(!insn_match_find_nth(fw,is,38,3,match_bl_blximm)) {
2667         // printf("sig_match_qsort: no match bl\n");
2668         return 0;
2669     }
2670     // follow
2671     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
2672     // if call in first 4 insn, follow again (newer cams have an extra sub)
2673     if(insn_match_find_next(fw,is,4,match_bl_blximm)) {
2674         disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
2675     }
2676     if(!insn_match_find_next(fw,is,14,match_bl_blximm)) {
2677         // printf("sig_match_qsort: no match bl (qsort)\n");
2678         return 0;
2679     }
2680     // sanity check, expect r1-r3 to be const
2681     uint32_t regs[4];
2682     if((get_call_const_args(fw,is,5,regs)&0xe)!=0xe) {
2683         // printf("sig_match_qsort: failed to get args\n");
2684         return 0;
2685     }
2686     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2687 }
2688 // looks for sequence of calls near ref string RedEyeController.c
2689 // DeleteFile_Fut
2690 // ...
2691 // strcpy
2692 // ...
2693 // strchr
2694 // ...
2695 // DeleteDirectory_Fut
2696 int sig_match_deletedirectory_fut(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2697 {
2698     uint32_t str_adr = find_str_bytes_main_fw(fw,rule->ref_name);
2699     if(!str_adr) {
2700         printf("sig_match_deletedirectory_fut: failed to find ref %s\n",rule->ref_name);
2701         return  0;
2702     }
2703     // TODO using larger than default "near" range, needed for sx710
2704     // not looking for ref to string, just code near where the actual string is
2705     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - 2048) | fw->thumb_default); // reset to a bit before where the string was found
2706     uint32_t end_adr = ADR_ALIGN4(str_adr) + 2048;
2707     while(find_next_sig_call(fw,is,end_adr - (uint32_t)is->adr,"DeleteFile_Fut")) {
2708         if(!insn_match_find_next(fw,is,6,match_bl_blximm)) {
2709             // printf("sig_match_deletedirectory_fut: no match bl strcpy\n");
2710             continue;
2711         }
2712         if(!is_sig_call(fw,is,"strcpy")) {
2713             // printf("sig_match_deletedirectory_fut: bl not strcpy at 0x%"PRIx64"\n",is->insn->address);
2714             continue;
2715         }
2716         if(!insn_match_find_next(fw,is,4,match_bl_blximm)) {
2717             // printf("sig_match_deletedirectory_fut: no match bl strrchr at 0x%"PRIx64"\n",is->insn->address);
2718             continue;
2719         }
2720         if(!is_sig_call(fw,is,"strrchr")) {
2721             // printf("sig_match_deletedirectory_fut: bl not strrchr at 0x%"PRIx64"\n",is->insn->address);
2722             continue;
2723         }
2724         // verify that arg1 to strrch is /
2725         uint32_t regs[4];
2726         if((get_call_const_args(fw,is,2,regs)&0x2)!=0x2) {
2727             // printf("sig_match_deletedirectory_fut: failed to get strrchr r1 at 0x%"PRIx64"\n",is->insn->address);
2728             continue;
2729         }
2730         if(regs[1] != '/') {
2731             // printf("sig_match_deletedirectory_fut: strrchr r1 not '/' at 0x%"PRIx64"\n",is->insn->address);
2732             continue;
2733         }
2734         if(!insn_match_find_next(fw,is,5,match_bl_blximm)) {
2735             // printf("sig_match_deletedirectory_fut: no match bl at 0x%"PRIx64"\n",is->insn->address);
2736             continue;
2737         }
2738         return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2739     }
2740     return 0;
2741 }
2742 
2743 /*
2744 match
2745    ref "LogicalEvent:0x%04x:adr:%p,Para:%ld" (or "LogicalEvent:0x%08x:adr:%p,Para:%ld")
2746    bl      LogCameraEvent
2747    mov     r0, rN
2748    bl      <some func>
2749    bl      set_control_event (or veneer)
2750 same string is used elsewhere, so match specific sequence
2751 */
2752 int sig_match_set_control_event(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2753 {
2754     uint32_t str_adr = find_str_bytes_main_fw(fw,rule->ref_name);
2755     if(!str_adr) {
2756         // not logged, two different ref strings so failing one is normal
2757         // printf("sig_match_set_control_event: failed to find ref %s\n",rule->ref_name);
2758         return  0;
2759     }
2760     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
2761     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
2762         if(!insn_match_find_next(fw,is,4,match_bl_blximm)) {
2763             // printf("sig_match_set_control_event: no match bl at 0x%"PRIx64"\n",is->insn->address);
2764             continue;
2765         }
2766         if(!is_sig_call(fw,is,"LogCameraEvent")) {
2767             // printf("sig_match_set_control_event: not LogCameraEvent at 0x%"PRIx64"\n",is->insn->address);
2768             continue;
2769         }
2770         const insn_match_t match_seq[]={
2771             {MATCH_INS(MOV, 2), {MATCH_OP_REG(R0),  MATCH_OP_REG_ANY,}},
2772             {MATCH_INS(BL, MATCH_OPCOUNT_IGNORE)},
2773             {MATCH_INS(BL, MATCH_OPCOUNT_IGNORE)},
2774             {ARM_INS_ENDING}
2775         };
2776         if(!insn_match_find_next_seq(fw,is,1,match_seq)) {
2777             // printf("sig_match_set_control_event: no match seq\n");
2778             continue;
2779         }
2780         return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2781     }
2782     return 0;
2783 }
2784 // find displaybusyonscreen for r52 cams, later uses different code
2785 int sig_match_displaybusyonscreen_52(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2786 {
2787     if (fw->dryos_ver != 52) {
2788         return 0;
2789     }
2790     uint32_t str_adr = find_str_bytes_main_fw(fw,rule->ref_name);
2791     if(!str_adr) {
2792         printf("sig_match_displaybusyonscreen: failed to find ref %s\n",rule->ref_name);
2793         return  0;
2794     }
2795     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
2796     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
2797         if(!insn_match_find_next(fw,is,3,match_bl_blximm)) {
2798             // printf("sig_match_displaybusyonscreen: no match bl at 0x%"PRIx64"\n",is->insn->address);
2799             continue;
2800         }
2801         if(!is_sig_call(fw,is,"LogCameraEvent")) {
2802             // printf("sig_match_displaybusyonscreen: not LogCameraEvent at 0x%"PRIx64"\n",is->insn->address);
2803             continue;
2804         }
2805         if(!find_next_sig_call(fw,is,4,"GUISrv_StartGUISystem_FW")) {
2806             // printf("sig_match_displaybusyonscreen: no match GUISrv_StartGUISystem_FW\n");
2807             continue;
2808         }
2809         if(!insn_match_find_nth(fw,is,5,2,match_bl_blximm)) {
2810             // printf("sig_match_displaybusyonscreen: no match bl 0x%"PRIx64"\n",is->insn->address);
2811             continue;
2812         }
2813         return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2814     }
2815     return 0;
2816 }
2817 // find undisplaybusyonscreen for r52 cams, later uses different code
2818 int sig_match_undisplaybusyonscreen_52(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2819 {
2820     if (fw->dryos_ver != 52) {
2821         return 0;
2822     }
2823     uint32_t str_adr = find_str_bytes_main_fw(fw,rule->ref_name);
2824     if(!str_adr) {
2825         printf("sig_match_undisplaybusyonscreen: failed to find ref %s\n",rule->ref_name);
2826         return  0;
2827     }
2828     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
2829     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
2830         // assume same function display* was found in, skip to that
2831         if(!find_next_sig_call(fw,is,24,"displaybusyonscreen")) {
2832             // printf("sig_match_undisplaybusyonscreen: no match displaybusyonscreen\n");
2833             continue;
2834         }
2835         if(!find_next_sig_call(fw,is,12,"GUISrv_StartGUISystem_FW")) {
2836             // printf("sig_match_undisplaybusyonscreen: no match GUISrv_StartGUISystem_FW\n");
2837             continue;
2838         }
2839         if(!insn_match_find_nth(fw,is,6,3,match_bl_blximm)) {
2840             // printf("sig_match_undisplaybusyonscreen: no match bl 0x%"PRIx64"\n",is->insn->address);
2841             continue;
2842         }
2843         return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2844     }
2845     return 0;
2846 }
2847 
2848 int sig_match_try_take_sem_dry_gt_58(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2849 {
2850     if(!init_disasm_sig_ref(fw,is,rule)) {
2851         return 0;
2852     }
2853     if(!find_next_sig_call(fw,is,24,"ReceiveMessageQueue")) {
2854         printf("sig_match_try_take_sem_dry_gt_58: failed to find ReceiveMessageQueue\n");
2855         return 0;
2856     }
2857     if(!find_next_sig_call(fw,is,60,"bzero")) {
2858         printf("sig_match_try_take_sem_dry_gt_58: failed to find bzero\n");
2859         return 0;
2860     }
2861     if(insn_match_find_next(fw,is,3,match_bl_blximm)) {
2862         return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2863     }
2864     printf("sig_match_try_take_sem_dry_gt_58: no match\n");
2865     return 0;
2866 }
2867 
2868 int sig_match_wait_all_eventflag_strict(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2869 {
2870     if(!init_disasm_sig_ref(fw,is,rule)) {
2871         return 0;
2872     }
2873     uint32_t str_adr = find_str_bytes_main_fw(fw,"EFTool.c");
2874     if(!str_adr) {
2875         printf("sig_match_wait_all_eventflag_strict: failed to find ref EFTool.c\n");
2876         return 0;
2877     }
2878     if(!find_next_sig_call(fw,is,60,"SleepTask")) {
2879         printf("sig_match_wait_all_eventflag_strict: failed to find SleepTask\n");
2880         return 0;
2881     }
2882 
2883     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,is->adr + 60)) {
2884         if(!insn_match_find_next(fw,is,6,match_bl_blximm)) {
2885             printf("sig_match_wait_all_eventflag_strict: no match bl 0x%"PRIx64"\n",is->insn->address);
2886             return 0;
2887         }
2888         return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2889     }
2890     return 0;
2891 }
2892 
2893 int sig_match_get_num_posted_messages(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2894 {
2895     if(!init_disasm_sig_ref(fw,is,rule)) {
2896         return 0;
2897     }
2898     if(!find_next_sig_call(fw,is,50,"TakeSemaphore")) {
2899         printf("sig_match_get_num_posted_messages: failed to find TakeSemaphore\n");
2900         return 0;
2901     }
2902     // find next call
2903     if(!insn_match_find_next(fw,is,5,match_bl_blximm)) {
2904         printf("sig_match_get_num_posted_messages:  no match bl 0x%"PRIx64"\n",is->insn->address);
2905         return 0;
2906     }
2907     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2908 }
2909 
2910 int sig_match_set_hp_timer_after_now(firmware *fw, iter_state_t *is, sig_rule_t *rule)
2911 {
2912     uint32_t str_adr = find_str_bytes_main_fw(fw,rule->ref_name);
2913     if(!str_adr) {
2914         printf("sig_match_set_hp_timer_after_now: failed to find ref %s\n",rule->ref_name);
2915         return 0;
2916     }
2917     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
2918     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
2919         if(!find_next_sig_call(fw,is,20,"ClearEventFlag")) {
2920             // printf("sig_match_set_hp_timer_after_now: failed to find ClearEventFlag\n");
2921             continue;
2922         }
2923         // find 3rd call
2924         if(!insn_match_find_nth(fw,is,13,3,match_bl_blximm)) {
2925             // printf("sig_match_set_hp_timer_after_now: no match bl 0x%"PRIx64"\n",is->insn->address);
2926             continue;
2927         }
2928         // check call args, expect r0 = 70000
2929         uint32_t regs[4];
2930         uint32_t found_regs = get_call_const_args(fw,is,6,regs);
2931         if((found_regs&0x1)!=0x1) {
2932             // some cameras load r0 through a base reg, try alternate match
2933             // r3 == 3 and r2 or r1 found and in ROM
2934             if((found_regs & 0x8) && regs[3] == 4) {
2935                 if((found_regs & 0x2 && regs[1] > fw->rom_code_search_min_adr) 
2936                     || (found_regs & 0x4 && regs[2] > fw->rom_code_search_min_adr)) {
2937                     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2938                 }
2939             }
2940             // printf("sig_match_set_hp_timer_after_now: failed to match args 0x%"PRIx64"\n",is->insn->address);
2941             continue;
2942         }
2943         // r1, r2 should be func pointers but may involve reg-reg moves that get_call_const_args doesn't track
2944         if(regs[0] != 70000) {
2945             // printf("sig_match_set_hp_timer_after_now: args mismatch 0x%08x 0x%08x 0x%"PRIx64"\n",regs[0],regs[1],is->insn->address);
2946             continue;
2947         }
2948         return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
2949     }
2950     return 0;
2951 }
2952 int sig_match_transfer_src_overlay(firmware *fw, iter_state_t *is, sig_rule_t *rule) {
2953     if(!init_disasm_sig_ref(fw,is,rule)) {
2954         return 0;
2955     }
2956     // skip to debugassert
2957     if(!find_next_sig_call(fw,is,32,"DebugAssert")) {
2958         printf("sig_match_transfer_src_overlay: no match DebugAssert\n");
2959         return 0;
2960     }
2961     var_ldr_desc_t desc;
2962     if(!find_and_get_var_ldr(fw, is, 20,4, ARM_REG_R0, &desc)) {
2963         printf("sig_match_transfer_src_overlay: no match ldr\n");
2964         return 0;
2965     }
2966     // following should be call
2967     if(!insn_match_find_next(fw,is,1,match_bl_blximm)) {
2968         printf("sig_match_transfer_src_overlay: no match bl 0x%"PRIx64"\n",is->insn->address);
2969         return 0;
2970     }
2971     // main sig value
2972     uint32_t fadr = get_branch_call_insn_target(fw,is);
2973     // adding active_bitmap_buffer here
2974     // note 4 bytes after value used on many ports, but the value normally sent to transfer_src_overlay
2975     save_misc_val("active_bitmap_buffer",desc.adr_adj,desc.off,(uint32_t)is->insn->address);
2976     // pick up bitmap_buffer 
2977     // expect
2978     // ldr rx,[reg bitmap buffer]
2979     // add r0, <reg from bitmap buffer>, const
2980     const insn_match_t bm_buf_match[]={
2981         {MATCH_INS(LDR,   2),  {MATCH_OP_REG_ANY,  MATCH_OP_MEM_ANY}},
2982         {MATCH_INS(ADD,   3),  {MATCH_OP_REG(R0), MATCH_OP_REG_ANY, MATCH_OP_IMM_ANY}},
2983         {ARM_INS_ENDING}
2984     };
2985     if(insn_match_find_next_seq(fw,is,1,bm_buf_match)) {
2986         if((arm_reg)is->insn->detail->arm.operands[1].reg == desc.reg_base) {
2987             save_misc_val("bitmap_buffer",desc.adr_adj,is->insn->detail->arm.operands[2].imm,(uint32_t)is->insn->address);
2988         }
2989         /*
2990         else {
2991             printf("sig_match_transfer_src_overlay: no match bitmap_buffer add 0x%"PRIx64"\n",is->insn->address);
2992         }
2993         */
2994     }
2995     /*
2996     else {
2997         printf("sig_match_transfer_src_overlay: no match bitmap_buffer seq 0x%"PRIx64"\n",is->insn->address);
2998     }
2999     */
3000     return save_sig_with_j(fw,rule->name,fadr);
3001 }
3002 
3003 // find exmem related stuff
3004 int sig_match_exmem_vars(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3005 {
3006     uint32_t adr[2], fnd[2];
3007     if(!init_disasm_sig_ref(fw,is,rule)) {
3008         printf("sig_match_exmem_vars: missing ref\n");
3009         return 0;
3010     }
3011     // expect first LDR pc
3012     if(!insn_match_find_next(fw,is,15,match_ldr_pc)) {
3013         printf("sig_match_exmem_vars: match LDR PC failed\n");
3014         return 0;
3015     }
3016     adr[0]=LDR_PC2val(fw,is->insn);
3017     fnd[0]=(uint32_t)is->insn->address;
3018     if(!insn_match_find_next(fw,is,5,match_ldr_pc)) {
3019         printf("sig_match_exmem_vars: 2nd match LDR PC failed\n");
3020         return 0;
3021     }
3022     adr[1]=LDR_PC2val(fw,is->insn);
3023     fnd[1]=(uint32_t)is->insn->address;
3024     //printf("sig_match_exmem_vars: %x, %x\n",adr[0], adr[1]);
3025     int n;
3026     for (n=0; n<2; n++) {
3027         if (adr[n] < fw->data_start+fw->data_len) {
3028             uint32_t ladr = adr[n]-fw->data_start+fw->data_init_start;
3029             save_misc_val("exmem_types_table",ladr,0,fnd[n]);
3030             int m;
3031             int exm_typ_cnt = 0;
3032             for (m=0; m<42; m++) {
3033                 if ( (fw_u32(fw,ladr+m*4)!=0) && isASCIIstring(fw, fw_u32(fw,ladr+m*4)) )
3034                 {
3035                     char *extyp = (char*)adr2ptr(fw, fw_u32(fw,ladr+m*4));
3036                     if ( strncmp(extyp,"EXMEM",5)==0 )
3037                     {
3038                         exm_typ_cnt++;
3039                     }
3040                 }
3041                 else
3042                 {
3043                     break;
3044                 }
3045             }
3046             save_misc_val("exmem_type_count",exm_typ_cnt,0,ladr);
3047         }
3048         else if (adr[n] < fw->memisostart) {
3049             save_misc_val("exmem_alloc_table",adr[n],0,fnd[n]);
3050         }
3051     }
3052     return 1;
3053 }
3054 
3055 // find function that copies Zico Xtensa blobs to their destination (dryos 52)
3056 int sig_match_zicokick_52(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3057 {
3058     uint32_t str_adr = find_str_bytes_main_fw(fw,rule->ref_name);
3059     if(!str_adr) {
3060         printf("sig_match_zicokick_52: failed to find ref %s\n",rule->ref_name);
3061         return  0;
3062     }
3063     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
3064     
3065     // search for string ref
3066     if(!fw_search_insn(fw,is,search_disasm_str_ref,0,rule->ref_name,(uint32_t)is->adr+SEARCH_NEAR_REF_RANGE)) {
3067         printf("sig_match_zicokick_52: failed to find insn ref %s\n",rule->ref_name);
3068         return 0;
3069     }
3070     // check preceding instruction
3071     if(!fw_disasm_iter_single(fw,adr_hist_get(&is->ah,1))) {
3072         printf("sig_match_zicokick_52: disasm failed\n");
3073         return 0;
3074     }
3075     if (!(isLDR_PC(fw->is->insn) && fw->is->insn->detail->arm.operands[0].reg == ARM_REG_R0)) {
3076         printf("sig_match_zicokick_52: match ldr r0 failed\n");
3077         return 0;
3078     }
3079     // save backtracked address
3080     uint32_t adr=(uint32_t)(fw->is->insn->address) | is->thumb;
3081     // step forward one from string ref
3082     if(!disasm_iter(fw,is)) {
3083         printf("sig_match_zicokick_52: disasm failed\n");
3084         return 0;
3085     }
3086     if (is->insn->id == ARM_INS_PUSH && is->insn->detail->arm.operands[0].reg == ARM_REG_R4) {
3087         return save_sig_with_j(fw,rule->name,adr);
3088     }
3089     return 0;
3090 }
3091 // find function that copies Zico Xtensa blobs to their destination (dryos >52)
3092 int sig_match_zicokick_gt52(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3093 {
3094     uint32_t str_adr = find_str_bytes_main_fw(fw,rule->ref_name);
3095     if(!str_adr) {
3096         printf("sig_match_zicokick_gt52: failed to find ref %s\n",rule->ref_name);
3097         return  0;
3098     }
3099     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
3100     
3101     // search for string ref
3102     if(!fw_search_insn(fw,is,search_disasm_str_ref,0,rule->ref_name,(uint32_t)is->adr+SEARCH_NEAR_REF_RANGE)) {
3103         printf("sig_match_zicokick_gt52: failed to find insn ref %s\n",rule->ref_name);
3104         return 0;
3105     }
3106     int i;
3107     // search backward for
3108     // ldr r0,...
3109     // push r4,...
3110     for(i=1; i<=8; i++) {
3111         if (!fw_disasm_iter_single(fw,adr_hist_get(&is->ah,i))) {
3112             printf("sig_match_zicokick_gt52: disasm failed\n");
3113             return 0;
3114         }
3115         if (fw->is->insn->id == ARM_INS_PUSH && fw->is->insn->detail->arm.operands[0].reg == ARM_REG_R4) {
3116             if (!fw_disasm_iter_single(fw,adr_hist_get(&is->ah,i+1))) {
3117                 printf("sig_match_zicokick_gt52: disasm failed\n");
3118                 return 0;
3119             }
3120             if (isLDR_PC(fw->is->insn) && fw->is->insn->detail->arm.operands[0].reg == ARM_REG_R0) {
3121                 return save_sig_with_j(fw,rule->name,(uint32_t)(fw->is->insn->address) | is->thumb);
3122             }
3123             return 0;
3124         }
3125     }
3126     return 0;
3127 }
3128 int sig_match_zicokick_copy(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3129 {
3130     if(!init_disasm_sig_ref(fw,is,rule)) {
3131         return 0;
3132     }
3133     // TODO could be less strict on regs, 5 LDRs in a row is rare
3134     const insn_match_t match_ldrs_bl[]={
3135         {MATCH_INS(LDR, 2), {MATCH_OP_REG(R0),  MATCH_OP_MEM_BASE(PC)}},
3136         {MATCH_INS(LDR, 2), {MATCH_OP_REG(R1),  MATCH_OP_MEM_BASE(PC)}},
3137         {MATCH_INS(LDR, 2), {MATCH_OP_REG(R2),  MATCH_OP_MEM_BASE(R0)}},
3138         {MATCH_INS(LDR, 2), {MATCH_OP_REG(R0),  MATCH_OP_MEM_BASE(PC)}},
3139         {MATCH_INS(LDR, 2), {MATCH_OP_REG(R0),  MATCH_OP_MEM_BASE(R0)}},
3140         {MATCH_INS(BL,MATCH_OPCOUNT_IGNORE)},
3141         {ARM_INS_ENDING}
3142     };
3143     if(!insn_match_find_next_seq(fw,is,30,match_ldrs_bl)) {
3144         printf("sig_match_zicokick_copy no match ldr\n");
3145         return 0;
3146     }
3147     // TODO could sanity check bl target
3148     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
3149 }
3150 
3151 int sig_match_zicokick_values(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3152 {
3153     if(!init_disasm_sig_ref(fw,is,rule)) {
3154         return 0;
3155     }
3156 // get_call_const_args doesn't currently handle ldr sequence
3157 #if 0
3158     // first call is further from function start
3159     if(!find_next_sig_call(fw,is,64,"zicokick_copy")) {
3160         printf("sig_match_zicokick_values: no zicokick_copy 1\n");
3161         return 0;
3162     }
3163     while(1) {
3164         uint32_t regs[4];
3165         if((get_call_const_args(fw,is,7,regs)&0x7)==0x7) {
3166             printf("xtensa blob @ 0x%08x, loads to 0x%08x, size 0x%08x\n",regs[1],regs[0],regs[2]);
3167         } else {
3168             printf("sig_match_zicokick_values: failed to get regs\n");
3169         }
3170         if(!find_next_sig_call(fw,is,8,"zicokick_copy")) {
3171             break;
3172         }
3173     }
3174     return 1;
3175 #endif
3176     int i;
3177     uint32_t uv[3] = {0,0,0};
3178     int uvi = 0;
3179     misc_blob_t *blobs=malloc((MISC_BLOB_XTENSA_MAX + 1)*sizeof(misc_blob_t));
3180     int n_blobs = 0;
3181 
3182     for(i=1; i<=64; i++) {
3183         if (!disasm_iter(fw,is)) {
3184             free(blobs);
3185             return 0;
3186         }
3187         if (is->insn->id == ARM_INS_LDR && is->insn->detail->arm.operands[1].type == ARM_OP_MEM) {
3188             uint32_t u = LDR_PC2val(fw,is->insn);
3189             if ((u<fw->base+fw->size8) && (u>fw->rom_code_search_max_adr)) {
3190                 // address outside the main fw
3191                 if (uvi<3) {
3192                     uv[uvi] = u;
3193                     uvi++;
3194                 }
3195             }
3196         }
3197         else if (is->insn->id == ARM_INS_BL) {
3198             if (uvi==3) {
3199                 // func call, all 3 addresses are in collection
3200                 uint32_t bsize, bloadedto, badr, u;
3201                 int j;
3202                 badr = MAX(MAX(uv[0],uv[1]),uv[2]);
3203                 for (j=0; j<3; j++) {
3204                     if (uv[j]!=badr) {
3205                         u = fw_u32(fw, uv[j]);
3206                         if (u<1024*1024*2) {
3207                             bsize = u;
3208                         }
3209                         else {
3210                             bloadedto = u;
3211                         }
3212                     }
3213                 }
3214                 if (bsize) {
3215                     if(n_blobs == MISC_BLOB_XTENSA_MAX) {
3216                         printf("sig_match_zicokick_values: ignoring xtensa blobs > %d\n",MISC_BLOB_XTENSA_MAX);
3217                         blobs[n_blobs].type = MISC_BLOB_TYPE_NONE;
3218                         break;
3219                     }
3220                     // printf("xtensa blob @ 0x%08x, loads to 0x%08x, size 0x%08x\n",badr,bloadedto,bsize);
3221                     blobs[n_blobs].type = MISC_BLOB_TYPE_XTENSA;
3222                     blobs[n_blobs].rom_adr = badr;
3223                     blobs[n_blobs].ram_adr = bloadedto;
3224                     blobs[n_blobs].size = bsize;
3225                     n_blobs++;
3226                 }
3227             }
3228             uvi = 0;
3229         }
3230         else if (is->insn->id == ARM_INS_POP) {
3231             break;
3232         }
3233     }
3234     if(n_blobs > 0) {
3235         blobs[n_blobs].type = MISC_BLOB_TYPE_NONE;
3236         save_misc_val_blobs("zicokick_values",blobs,0);
3237         return 1;
3238     } else {
3239         free(blobs);
3240         return 0;
3241     }
3242 }
3243 
3244 int sig_match_init_ex_drivers(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3245 {
3246     if(!init_disasm_sig_ref(fw,is,rule)) {
3247         return 0;
3248     }
3249     int i;
3250     int b_count;
3251     // search forward 32 instructions or 14 calls
3252     for(i=0, b_count = 0; i < 32 && b_count < 14; i++) {
3253         if (!disasm_iter(fw,is)) {
3254             printf("sig_match_init_ex_drivers: disasm failed 1\n");
3255             return 0;
3256         }
3257         uint32_t b_tgt = get_branch_call_insn_target(fw,is);
3258         if(!b_tgt) {
3259             continue;
3260         }
3261         b_count++;
3262         uint64_t next_adr = is->adr | is->thumb;
3263         disasm_iter_init(fw,is,b_tgt);
3264         if (!disasm_iter(fw,is)) {
3265             printf("sig_match_init_ex_drivers: disasm failed 2\n");
3266             return 0;
3267         }
3268         // expect the function we're looking for to start with a push
3269         if(is->insn->id == ARM_INS_PUSH) {
3270             if(find_next_sig_call(fw,is,30,"DebugAssert")) {
3271                 uint32_t regs[4];
3272                 if((get_call_const_args(fw,is,5,regs)&0x2)==0x2) {
3273                     const char *str=(char *)adr2ptr(fw,regs[1]);
3274                     if(str && strcmp(str,"InitExDrivers.c") == 0) {
3275                         return save_sig_with_j(fw,rule->name,b_tgt);
3276                     }
3277                 }
3278             }
3279         }
3280         disasm_iter_init(fw,is,next_adr);
3281     }
3282     return 0;
3283 }
3284 
3285 int sig_match_omar_init(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3286 {
3287     // not present on digic 7
3288     if (fw->arch_flags & FW_ARCH_FL_VMSA) {
3289         return 0;
3290     }
3291     if(!init_disasm_sig_ref(fw,is,rule)) {
3292         return 0;
3293     }
3294     uint32_t fadr = find_last_call_from_func(fw,is,20,42);
3295     if(!fadr) {
3296         printf("sig_match_omar_init: no match call\n");
3297         return 0;
3298     }
3299     // follow
3300     disasm_iter_init(fw,is,fadr);
3301     if(!find_next_sig_call(fw,is,44,"dry_memcpy")) {
3302         printf("sig_match_omar_init: no match dry_memcpy\n");
3303         return 0;
3304     }
3305     uint32_t regs[4];
3306     // expect dry_memcpy(stack ptr,rom ptr, 0x18)
3307     if((get_call_const_args(fw,is,5,regs)&0x6)!=0x6) {
3308         printf("sig_match_omar_init: no match dry_memcpy args 1\n");
3309         return 0;
3310     }
3311     if(regs[2] != 0x18 || !adr2ptr(fw,regs[1])) {
3312         printf("sig_match_omar_init: no match dry_memcpy args 2\n");
3313         return 0;
3314     }
3315     uint32_t dadr = regs[1];
3316     save_misc_val("omar_init_data",dadr,0,(uint32_t)is->insn->address);
3317     misc_blob_t *blobs=malloc(3*sizeof(misc_blob_t));
3318     int i;
3319     for(i = 0; i<2; i++) {
3320         uint32_t dst = fw_u32(fw,dadr + i*12);
3321         uint32_t src = fw_u32(fw,dadr + i*12 + 4);
3322         uint32_t bsize = fw_u32(fw,dadr + i*12 + 8);
3323         if(src && dst && bsize) {
3324             blobs[i].type = MISC_BLOB_TYPE_OMAR;
3325             blobs[i].rom_adr = src;
3326             blobs[i].ram_adr = dst;
3327             blobs[i].size = bsize;
3328         } else {
3329             printf("sig_match_omar_init: invalid blobs\n");
3330             free(blobs);
3331             blobs = NULL;
3332             break;
3333         }
3334     }
3335     if(blobs) {
3336         blobs[2].type = MISC_BLOB_TYPE_NONE;
3337         save_misc_val_blobs("omar_init_values",blobs,0);
3338     }
3339 
3340     return save_sig_with_j(fw,rule->name,fadr);
3341 }
3342 
3343 int sig_match_enable_hdmi_power(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3344 {
3345     if(!init_disasm_sig_ref(fw,is,rule)) {
3346         return 0;
3347     }
3348     if(!find_next_sig_call(fw,is,14,"CreateEventFlagStrictly")) {
3349         printf("sig_match_enable_hdmi_power: no match CreateEventFlagStrictly\n");
3350         return 0;
3351     }
3352     const insn_match_t match_seq[]={
3353         {MATCH_INS(BL,   MATCH_OPCOUNT_IGNORE)},
3354         {MATCH_INS(CBNZ, MATCH_OPCOUNT_IGNORE)},
3355         {ARM_INS_ENDING}
3356     };
3357     if(!insn_match_find_next_seq(fw,is,4,match_seq)) {
3358         printf("sig_match_enable_hdmi_power: no match bl seq cbnz 0x%"PRIx64"\n",is->insn->address);
3359         return 0;
3360     }
3361     // function should be next call
3362     if (!disasm_iter(fw,is)) {
3363         return 0;
3364     }
3365     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
3366 }
3367 
3368 int sig_match_disable_hdmi_power(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3369 {
3370     if(!init_disasm_sig_ref(fw,is,rule)) {
3371         return 0;
3372     }
3373     if(!find_next_sig_call(fw,is,24,"EnableHDMIPower")) {
3374         printf("sig_match_disable_hdmi_power: no match EnableHDMIPower\n");
3375         return 0;
3376     }
3377     if(!find_next_sig_call(fw,is,22,"ClearEventFlag")) {
3378         printf("sig_match_disable_hdmi_power: no match ClearEventFlag\n");
3379         return 0;
3380     }
3381     const insn_match_t match_seq[]={
3382         {MATCH_INS(BL,   MATCH_OPCOUNT_IGNORE)},
3383         {MATCH_INS(MOV, 2), {MATCH_OP_REG(R0),  MATCH_OP_IMM(1)}},
3384         {MATCH_INS(POP, MATCH_OPCOUNT_IGNORE)},
3385         {ARM_INS_ENDING}
3386     };
3387     if(!insn_match_find_next_seq(fw,is,12,match_seq)) {
3388         printf("sig_match_disable_hdmi_power: no match seq bl movs pop 0x%"PRIx64"\n",is->insn->address);
3389         return 0;
3390     }
3391     // bl matched above should be func
3392     disasm_iter_init(fw,is,adr_hist_get(&is->ah,2));
3393     if (!disasm_iter(fw,is)) {
3394         return 0;
3395     }
3396     return save_sig_with_j(fw,rule->name,get_branch_call_insn_target(fw,is));
3397 }
3398 
3399 int sig_match_levent_table(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3400 {
3401     if(!init_disasm_sig_ref(fw,is,rule)) {
3402         return 0;
3403     }
3404     if(!insn_match_find_next(fw,is,4,match_bl_blximm)) {
3405         // printf("sig_match_levent_table: no match bl 0x%"PRIx64"\n",is->insn->address);
3406         return 0;
3407     }
3408     // follow
3409     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
3410 
3411     // find first call of next function
3412     if(!insn_match_find_next(fw,is,4,match_bl_blximm)) {
3413         // printf("sig_match_levent_table: no match bl 0x%"PRIx64"\n",is->insn->address);
3414         return 0;
3415     }
3416     
3417     // follow
3418     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
3419 
3420     // first instruction should load address
3421     disasm_iter(fw,is);
3422     uint32_t adr=LDR_PC2val(fw,is->insn);
3423     if(!adr) {
3424         // printf("sig_match_levent_table: no match LDR PC 0x%"PRIx64"\n",is->insn->address);
3425         return  0;
3426     }
3427     uint32_t *p=(uint32_t *)adr2ptr(fw,adr);
3428     if(!p) {
3429         printf("sig_match_levent_table: 0x%08x not a ROM adr 0x%"PRIx64"\n",adr,is->insn->address);
3430         return  0;
3431     }
3432     if(*(p+1) != 0x800) {
3433         printf("sig_match_levent_table: expected 0x800 not 0x%x at 0x%08x ref 0x%"PRIx64"\n",*(p+1),adr,is->insn->address);
3434         return  0;
3435     }
3436     // TODO saving the function might be useful for analysis
3437     save_misc_val(rule->name,adr,0,(uint32_t)is->insn->address);
3438     return 1;
3439 }
3440 int sig_match_flash_param_table(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3441 {
3442     if(!init_disasm_sig_ref(fw,is,rule)) {
3443         return 0;
3444     }
3445     // expect 3 asserts
3446     if(!insn_match_find_next(fw,is,14,match_bl_blximm)) {
3447         // printf("sig_match_flash_param_table: no match bl 1\n");
3448         return 0;
3449     }
3450     if(!is_sig_call(fw,is,"DebugAssert")) {
3451         // printf("sig_match_flash_param_table: bl 1 not DebugAssert at 0x%"PRIx64"\n",is->insn->address);
3452         return 0;
3453     }
3454     if(!insn_match_find_next(fw,is,7,match_bl_blximm)) {
3455         // printf("sig_match_flash_param_table: no match bl 2\n");
3456         return 0;
3457     }
3458     if(!is_sig_call(fw,is,"DebugAssert")) {
3459         // printf("sig_match_flash_param_table: bl 2 not DebugAssert at 0x%"PRIx64"\n",is->insn->address);
3460         return 0;
3461     }
3462     if(!insn_match_find_next(fw,is,8,match_bl_blximm)) {
3463         // printf("sig_match_flash_param_table: no match bl 3\n");
3464         return 0;
3465     }
3466     if(!is_sig_call(fw,is,"DebugAssert")) {
3467         // printf("sig_match_flash_param_table: bl 3 not DebugAssert at 0x%"PRIx64"\n",is->insn->address);
3468         return 0;
3469     }
3470     // expect AcquireRecursiveLockStrictly, func
3471     if(!insn_match_find_nth(fw,is,14,2,match_bl_blximm)) {
3472         // printf("sig_match_flash_param_table: no match sub 1\n");
3473         return 0;
3474     }
3475     // follow
3476     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
3477 
3478     // first call
3479     if(!insn_match_find_next(fw,is,8,match_bl_blximm)) {
3480         // printf("sig_match_flash_param_table: no match sub 1 bl\n");
3481         return 0;
3482     }
3483     
3484     // follow
3485     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
3486     // first instruction should load address
3487     disasm_iter(fw,is);
3488     uint32_t adr=LDR_PC2val(fw,is->insn);
3489     if(!adr) {
3490         // printf("sig_match_flash_param_table: no match LDR PC 0x%"PRIx64"\n",is->insn->address);
3491         return  0;
3492     }
3493     save_misc_val(rule->name,adr,0,(uint32_t)is->insn->address);
3494     return 1;
3495 }
3496 int sig_match_jpeg_count_str(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3497 {
3498     uint32_t str_adr = find_str_bytes_main_fw(fw,rule->ref_name);
3499     if(!str_adr) {
3500         printf("sig_match_jpeg_count_str: failed to find ref %s\n",rule->ref_name);
3501         return  0;
3502     }
3503     // TODO should handle multiple instances of string
3504     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
3505     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
3506         // printf("sig_match_jpeg_count_str: str match 0x%"PRIx64"\n",is->insn->address);
3507         if(!insn_match_find_next(fw,is,3,match_bl_blximm)) {
3508             // printf("sig_match_jpeg_count_str: no match bl\n");
3509             continue;
3510         }
3511         if(!is_sig_call(fw,is,"sprintf_FW")) {
3512             // printf("sig_match_jpeg_count_str: not sprintf_FW at 0x%"PRIx64"\n",is->insn->address);
3513             continue;
3514         }
3515         // expect ptr in r0, str in r1
3516         uint32_t regs[4];
3517         if((get_call_const_args(fw,is,5,regs)&0x3)!=0x3) {
3518             // printf("sig_match_jpeg_count_str: failed to get sprintf args 0x%"PRIx64"\n",is->insn->address);
3519             continue;
3520         }
3521         if(regs[1] != str_adr) {
3522             // printf("sig_match_jpeg_count_str: expected r1 == 0x%08x not 0x%08x at 0x%"PRIx64"\n",str_adr, regs[1],is->insn->address);
3523             return 0;
3524         }
3525         if(!adr_is_var(fw,regs[0])) {
3526             // printf("sig_match_jpeg_count_str: r0 == 0x%08x not var ptr at 0x%"PRIx64"\n",regs[0],is->insn->address);
3527             return 0;
3528         }
3529         save_misc_val(rule->name,regs[0],0,(uint32_t)is->insn->address);
3530         return 1;
3531     }
3532     return 0;
3533 }
3534 
3535 // set a boolean misc val if ref is present
3536 int sig_match_misc_flag_named(__attribute__ ((unused))firmware *fw, __attribute__ ((unused))iter_state_t *is, sig_rule_t *rule)
3537 {
3538     uint32_t ref=get_saved_sig_val(rule->ref_name);
3539     save_misc_val(rule->name,(ref)?1:0,0,ref);
3540     return 1;
3541 }
3542 
3543 int sig_match_cam_has_iris_diaphragm(__attribute__ ((unused))firmware *fw, __attribute__ ((unused))iter_state_t *is, sig_rule_t *rule)
3544 {
3545     uint32_t v;
3546     uint32_t ref=0;get_saved_sig_val(rule->ref_name);
3547     // ILC assumed to have iris
3548     if(get_misc_val_value("CAM_IS_ILC")) {
3549         v=1;
3550     } else {
3551         ref=get_saved_sig_val(rule->ref_name);
3552         v=(ref)?1:0;
3553     }
3554     save_misc_val(rule->name,v,0,ref);
3555     return 1;
3556 }
3557 
3558 int sig_match_cam_uncached_bit(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3559 {
3560     if(!init_disasm_sig_ref(fw,is,rule)) {
3561         return 0;
3562     }
3563     const insn_match_t match_bic_r0[]={
3564         {MATCH_INS(BIC, 3), {MATCH_OP_REG(R0),  MATCH_OP_REG(R0),   MATCH_OP_IMM_ANY}},
3565         {ARM_INS_ENDING}
3566     };
3567     if(insn_match_find_next(fw,is,4,match_bic_r0)) {
3568         save_misc_val(rule->name,is->insn->detail->arm.operands[2].imm,0,(uint32_t)is->insn->address);
3569         return 1;
3570     }
3571     return 0;
3572 }
3573 
3574 int sig_match_physw_event_table(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3575 {
3576     if(!init_disasm_sig_ref(fw,is,rule)) {
3577         return 0;
3578     }
3579     // expect first LDR pc
3580     if(!insn_match_find_next(fw,is,5,match_ldr_pc)) {
3581         printf("sig_match_physw_event_table: match LDR PC failed\n");
3582         return 0;
3583     }
3584     uint32_t adr=LDR_PC2val(fw,is->insn);
3585     if(!adr) {
3586         printf("sig_match_physw_event_table: no match LDR PC 0x%"PRIx64"\n",is->insn->address);
3587         return 0;
3588     }
3589     if(!adr2ptr(fw,adr)) {
3590         printf("sig_match_physw_event_table: adr not ROM 0x%08x at 0x%"PRIx64"\n",adr,is->insn->address);
3591         return 0;
3592     }
3593     save_misc_val(rule->name,adr,0,(uint32_t)is->insn->address);
3594     return 1;
3595 }
3596 int sig_match_uiprop_count(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3597 {
3598     if(!init_disasm_sig_ref(fw,is,rule)) {
3599         return 0;
3600     }
3601     if(!find_next_sig_call(fw,is,38,"DebugAssert")) {
3602         // printf("sig_match_uiprop_count: no DebugAssert 1\n");
3603         return 0;
3604     }
3605     if(!find_next_sig_call(fw,is,14,"DebugAssert")) {
3606         // printf("sig_match_uiprop_count: no DebugAssert 2\n");
3607         return 0;
3608     }
3609     const insn_match_t match_bic_cmp[]={
3610         {MATCH_INS(BIC, 3), {MATCH_OP_REG_ANY,  MATCH_OP_REG_ANY,   MATCH_OP_IMM(0x8000)}},
3611         {MATCH_INS(CMP, 2), {MATCH_OP_REG_ANY,  MATCH_OP_ANY}},
3612         {ARM_INS_ENDING}
3613     };
3614     if(!insn_match_find_next_seq(fw,is,3,match_bic_cmp)) {
3615         // printf("sig_match_uiprop_count: no bic,cmp\n");
3616         return 0;
3617     }
3618     save_misc_val(rule->name,is->insn->detail->arm.operands[1].imm,0,(uint32_t)is->insn->address);
3619     return 1;
3620 }
3621 
3622 int sig_match_get_canon_mode_list(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3623 {
3624     uint32_t str_adr = find_str_bytes_main_fw(fw,rule->ref_name);
3625     if(!str_adr) {
3626         printf("sig_match_get_canon_mode_list: failed to find ref %s\n",rule->ref_name);
3627         return  0;
3628     }
3629     uint32_t adr=0;
3630     // TODO should handle multiple instances of string
3631     disasm_iter_init(fw,is,(ADR_ALIGN4(str_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
3632     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,str_adr+SEARCH_NEAR_REF_RANGE)) {
3633         // printf("sig_match_get_canon_mode_list: str match 0x%"PRIx64"\n",is->insn->address);
3634         if(!find_next_sig_call(fw,is,4,"LogCameraEvent")) {
3635             // printf("sig_match_get_canon_mode_list: no LogCameraEvent\n");
3636             continue;
3637         }
3638         // some cameras have a mov and an extra call
3639         if(!disasm_iter(fw,is)) {
3640             // printf("sig_match_get_canon_mode_list: disasm failed\n");
3641             return 0;
3642         }
3643         const insn_match_t match_mov_r0_1[]={
3644 #if CS_API_MAJOR < 4
3645             {MATCH_INS(MOVS, 2), {MATCH_OP_REG(R0),  MATCH_OP_IMM(1)}},
3646 #endif
3647             {MATCH_INS(MOV, 2), {MATCH_OP_REG(R0),  MATCH_OP_IMM(1)}},
3648             {ARM_INS_ENDING}
3649         };
3650         if(insn_match_any(is->insn,match_mov_r0_1)) {
3651             if(!insn_match_find_nth(fw,is,2,2,match_bl_blximm)) {
3652                 // printf("sig_match_get_canon_mode_list: no match bl 1x\n");
3653                 continue;
3654             }
3655         } else {
3656             if(!insn_match_any(is->insn,match_bl_blximm)) {
3657                 // printf("sig_match_get_canon_mode_list: no match bl 1\n");
3658                 continue;
3659             }
3660         }
3661         // found something to follow, break
3662         adr=get_branch_call_insn_target(fw,is);
3663         break;
3664     }
3665     if(!adr) {
3666         return 0;
3667     }
3668     // printf("sig_match_get_canon_mode_list: sub 1 0x%08x\n",adr);
3669     disasm_iter_init(fw,is,adr);
3670     if(!find_next_sig_call(fw,is,40,"TakeSemaphoreStrictly")) {
3671         // printf("sig_match_get_canon_mode_list: no TakeSemaphoreStrictly\n");
3672         return 0;
3673     }
3674     // match second call
3675     if(!insn_match_find_nth(fw,is,12,2,match_b_bl_blximm)) {
3676         // printf("sig_match_get_canon_mode_list: no match bl 2\n");
3677         return 0;
3678     }
3679     // follow
3680     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
3681     const insn_match_t match_loop[]={
3682         {MATCH_INS(ADD, 3), {MATCH_OP_REG_ANY,  MATCH_OP_REG_ANY,   MATCH_OP_IMM(1)}},
3683         {MATCH_INS(UXTH, 2), {MATCH_OP_REG_ANY,  MATCH_OP_REG_ANY}},
3684         {MATCH_INS(CMP, 2), {MATCH_OP_REG_ANY,  MATCH_OP_IMM_ANY}},
3685         {MATCH_INS_CC(B,LO,MATCH_OPCOUNT_IGNORE)},
3686         {ARM_INS_ENDING}
3687     };
3688     if(!insn_match_find_next_seq(fw,is,64,match_loop)) {
3689         // printf("sig_match_get_canon_mode_list: match 1 failed\n");
3690         return 0;
3691     }
3692     if(!insn_match_find_next(fw,is,2,match_bl_blximm)) {
3693         // printf("sig_match_get_canon_mode_list: no match bl 3\n");
3694         return 0;
3695     }
3696     // should be func
3697     adr=get_branch_call_insn_target(fw,is);
3698     // sanity check
3699     disasm_iter_init(fw,is,adr);
3700     const insn_match_t match_ldr_r0_ret[]={
3701         {MATCH_INS(LDR, 2),   {MATCH_OP_REG(R0),  MATCH_OP_MEM_BASE(PC)}},
3702         {MATCH_INS(BX, 1),   {MATCH_OP_REG(LR)}},
3703         {ARM_INS_ENDING}
3704     };
3705     if(!insn_match_find_next_seq(fw,is,1,match_ldr_r0_ret)) {
3706         // printf("sig_match_get_canon_mode_list: match 2 failed\n");
3707         return 0;
3708     }
3709     return save_sig_with_j(fw,rule->name,adr);
3710 } 
3711 
3712 int sig_match_zoom_busy(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3713 {
3714     if(!init_disasm_sig_ref(fw,is,rule)) {
3715         return 0;
3716     }
3717     // first call
3718     if(!insn_match_find_next(fw,is,5,match_bl_blximm)) {
3719         // printf("sig_match_zoom_busy: no match bl\n");
3720         return 0;
3721     }
3722     // follow
3723     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
3724     // get base address from first LDR PC
3725     if(!insn_match_find_next(fw,is,5,match_ldr_pc)) {
3726         // printf("sig_match_zoom_busy: match LDR PC failed\n");
3727         return 0;
3728     }
3729     uint32_t base=LDR_PC2val(fw,is->insn);
3730     arm_reg rb=is->insn->detail->arm.operands[0].reg;
3731     
3732     // look for first TakeSemaphoreStrictly
3733     if(!find_next_sig_call(fw,is,40,"TakeSemaphoreStrictly")) {
3734         // printf("sig_match_zoom_busy: no match TakeSemaphoreStrictly\n");
3735         return 0;
3736     }
3737     if(!disasm_iter(fw,is)) {
3738         // printf("sig_match_zoom_busy: disasm failed\n");
3739         return 0;
3740     }
3741     // assume next instruction is ldr
3742     if(is->insn->id != ARM_INS_LDR 
3743         || is->insn->detail->arm.operands[0].reg != ARM_REG_R0
3744         || is->insn->detail->arm.operands[1].mem.base != rb) {
3745         // printf("sig_match_zoom_busy: no match LDR\n");
3746         return 0;
3747     }
3748     save_misc_val(rule->name,base,is->insn->detail->arm.operands[1].mem.disp,(uint32_t)is->insn->address);
3749     return 1;
3750 }
3751 
3752 int sig_match_focus_busy(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3753 {
3754     if(!init_disasm_sig_ref(fw,is,rule)) {
3755         return 0;
3756     }
3757     // look for first TakeSemaphore
3758     if(!find_next_sig_call(fw,is,40,"TakeSemaphore")) {
3759         // printf("sig_match_focus_busy: no match TakeSemaphore\n");
3760         return 0;
3761     }
3762     // next call
3763     if(!insn_match_find_next(fw,is,5,match_bl_blximm)) {
3764         // printf("sig_match_focus_busy: no match bl\n");
3765         return 0;
3766     }
3767     // follow
3768     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
3769     // get base address from first LDR PC
3770     if(!insn_match_find_next(fw,is,5,match_ldr_pc)) {
3771         // printf("sig_match_focus_busy: match LDR PC failed\n");
3772         return 0;
3773     }
3774     uint32_t base=LDR_PC2val(fw,is->insn);
3775     arm_reg rb=is->insn->detail->arm.operands[0].reg;
3776     
3777     // look for first TakeSemaphoreStrictly
3778     if(!find_next_sig_call(fw,is,50,"TakeSemaphoreStrictly")) {
3779         // printf("sig_match_focus_busy: no match TakeSemaphoreStrictly\n");
3780         return 0;
3781     }
3782     const insn_match_t match_ldr[]={
3783         {MATCH_INS(LDR, 2), {MATCH_OP_REG(R0), MATCH_OP_MEM_ANY}},
3784         {MATCH_INS(CBZ, 2), {MATCH_OP_REG(R0), MATCH_OP_IMM_ANY}},
3785         {ARM_INS_ENDING}
3786     };
3787     if(!insn_match_find_next_seq(fw,is,10,match_ldr)) {
3788         // printf("sig_match_focus_busy: no match LDR\n");
3789         return 0;
3790     }
3791     // rewind to LDR
3792     disasm_iter_init(fw,is,adr_hist_get(&is->ah,1));
3793     disasm_iter(fw,is);
3794     // check LDR 
3795     if(is->insn->detail->arm.operands[1].mem.base != rb) {
3796         // printf("sig_match_focus_busy: no match LDR base\n");
3797         return 0;
3798     }
3799     save_misc_val(rule->name,base,is->insn->detail->arm.operands[1].mem.disp,(uint32_t)is->insn->address);
3800     return 1;
3801 }
3802 int sig_match_aram_size(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3803 {
3804     if(!init_disasm_sig_ref(fw,is,rule)) {
3805         printf("sig_match_aram_size: missing ref\n");
3806         return 0;
3807     }
3808     const insn_match_t match_ldr_r0_sp_cmp[]={
3809         {MATCH_INS(LDR, 2), {MATCH_OP_REG(R0),MATCH_OP_MEM(SP,INVALID,0xc)}},
3810         {MATCH_INS(CMP, 2), {MATCH_OP_REG(R0),MATCH_OP_IMM_ANY}},
3811         {ARM_INS_ENDING}
3812     };
3813     if(!insn_match_find_next_seq(fw,is,15,match_ldr_r0_sp_cmp)) {
3814         printf("sig_match_aram_size: no match LDR\n");
3815         return 0;
3816     }
3817     uint32_t val=is->insn->detail->arm.operands[1].imm;
3818     if(val != 0x22000 && val != 0x32000) {
3819         printf("sig_match_aram_size: unexpected ARAM size 0x%08x\n",val);
3820     }
3821     save_misc_val(rule->name,val,0,(uint32_t)is->insn->address);
3822     return 1;
3823 }
3824 
3825 int sig_match_aram_size_gt58(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3826 {
3827     if(!init_disasm_sig_ref(fw,is,rule)) {
3828         printf("sig_match_aram_size: missing ref\n");
3829         return 0;
3830     }
3831     const insn_match_t match_ldrd_r0r1_mov[]={
3832         {MATCH_INS(LDRD, 3), {MATCH_OP_REG(R0),MATCH_OP_REG(R1),MATCH_OP_MEM(SP,INVALID,0x10)}},
3833         {MATCH_INS(MOV, 2), {MATCH_OP_REG(R2),MATCH_OP_IMM_ANY}},
3834         {ARM_INS_ENDING}
3835     };
3836     // d7? variant
3837     const insn_match_t match_ldrd_r2r1_mov[]={
3838         {MATCH_INS(LDRD, 3), {MATCH_OP_REG(R2),MATCH_OP_REG(R1),MATCH_OP_MEM(SP,INVALID,0x10)}},
3839         {MATCH_INS(MOV, 2), {MATCH_OP_REG(R3),MATCH_OP_IMM_ANY}},
3840         {ARM_INS_ENDING}
3841     };
3842     if(!insn_match_find_next_seq(fw,is,15,match_ldrd_r0r1_mov)) {
3843         init_disasm_sig_ref(fw,is,rule); // reset to start
3844         if(!insn_match_find_next_seq(fw,is,15,match_ldrd_r2r1_mov)) {
3845             printf("sig_match_aram_size: no match LDR\n");
3846         }
3847         return 0;
3848     }
3849     uint32_t val=is->insn->detail->arm.operands[1].imm;
3850     if(val != 0x22000 && val != 0x32000) {
3851         printf("sig_match_aram_size: unexpected ARAM size 0x%08x\n",val);
3852     }
3853     save_misc_val(rule->name,val,0,(uint32_t)is->insn->address);
3854     return 1;
3855 }
3856 
3857 int sig_match_aram_start(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3858 {
3859     if(!init_disasm_sig_ref(fw,is,rule)) {
3860         printf("sig_match_aram_start: missing ref\n");
3861         return 0;
3862     }
3863     if(!find_next_sig_call(fw,is,50,"DebugAssert")) {
3864         printf("sig_aram_start: no match DebugAssert\n");
3865         return 0;
3866     }
3867     const insn_match_t match_cmp_bne_ldr[]={
3868         {MATCH_INS(CMP, 2), {MATCH_OP_REG(R1),MATCH_OP_IMM(0)}},
3869         {MATCH_INS_CC(B,NE,MATCH_OPCOUNT_IGNORE)},
3870         {MATCH_INS(LDR, 2), {MATCH_OP_REG_ANY,MATCH_OP_MEM_BASE(PC)}},
3871         {ARM_INS_ENDING}
3872     };
3873     if(!insn_match_find_next_seq(fw,is,15,match_cmp_bne_ldr)) {
3874         printf("sig_match_aram_start: no match CMP\n");
3875         return 0;
3876     }
3877     uint32_t adr=LDR_PC2val(fw,is->insn);
3878     if(!adr) {
3879         printf("sig_match_aram_start: no match LDR PC 0x%"PRIx64"\n",is->insn->address);
3880         return 0;
3881     }
3882     // could sanity check that it looks like a RAM address
3883     save_misc_val(rule->name,adr,0,(uint32_t)is->insn->address);
3884     return 1;
3885 }
3886 
3887 int sig_match_aram_start2(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3888 {
3889     if (get_misc_val_value("ARAM_HEAP_START"))
3890         return 0;
3891 
3892     if(!init_disasm_sig_ref(fw,is,rule)) {
3893         printf("sig_match_aram_start: missing ref\n");
3894         return 0;
3895     }
3896     if(!find_next_sig_call(fw,is,60,"DebugAssert")) {
3897         printf("sig_aram_start2: no match DebugAssert\n");
3898         return 0;
3899     }
3900     const insn_match_t match_cmp_bne_ldr[]={
3901         {MATCH_INS(CMP, 2), {MATCH_OP_REG(R1),MATCH_OP_IMM(0)}},
3902         {MATCH_INS_CC(B,NE,MATCH_OPCOUNT_IGNORE)},
3903         {MATCH_INS(LDR, 2), {MATCH_OP_REG_ANY,MATCH_OP_MEM_BASE(SP)}},
3904         {MATCH_INS(LDR, 2), {MATCH_OP_REG_ANY,MATCH_OP_MEM_BASE(PC)}},
3905         {ARM_INS_ENDING}
3906     };
3907     if(!insn_match_find_next_seq(fw,is,15,match_cmp_bne_ldr)) {
3908         printf("sig_match_aram_start2: no match CMP\n");
3909         return 0;
3910     }
3911     uint32_t adr=LDR_PC2val(fw,is->insn);
3912     if(!adr) {
3913         printf("sig_match_aram_start2: no match LDR PC 0x%"PRIx64"\n",is->insn->address);
3914         return 0;
3915     }
3916     // could sanity check that it looks like a RAM address
3917     save_misc_val(rule->name,adr,0,(uint32_t)is->insn->address);
3918     return 1;
3919 }
3920 
3921 int sig_match__nrflag(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3922 {
3923     if(!init_disasm_sig_ref(fw,is,rule)) {
3924         return 0;
3925     }
3926     uint32_t fadr=is->adr;
3927     // find range check on input arg
3928     const insn_match_t match_cmp_b[]={
3929         {MATCH_INS(CMP, 2), {MATCH_OP_REG(R0),MATCH_OP_IMM_ANY}},
3930         {MATCH_INS(B,MATCH_OPCOUNT_IGNORE)}, // blo or blt may be used, so don't include cond
3931         {ARM_INS_ENDING}
3932     };
3933     if(!insn_match_find_next_seq(fw,is,4,match_cmp_b) || is->insn->detail->arm.cc == ARM_CC_AL) {
3934         printf("sig_match__nrflag: no match CMP\n");
3935         return 0;
3936     }
3937     // follow
3938     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
3939     if(!disasm_iter(fw,is)) {
3940         printf("sig_match__nrflag: disasm failed\n");
3941         return 0;
3942     }
3943     // assume next is base addres
3944     uint32_t adr=LDR_PC2val(fw,is->insn);
3945     if(!adr) {
3946         printf("sig_match__nrflag: no match LDR PC 0x%"PRIx64"\n",is->insn->address);
3947         return 0;
3948     }
3949     arm_reg reg_base = is->insn->detail->arm.operands[0].reg; // reg value was loaded into
3950     if(!disasm_iter(fw,is)) {
3951         printf("sig_match__nrflag: disasm failed\n");
3952         return 0;
3953     }
3954     // firmware may use add/sub to get actual firmware base address
3955     if(isADDx_imm(is->insn) || isSUBx_imm(is->insn)) {
3956         if((arm_reg)is->insn->detail->arm.operands[0].reg != reg_base) {
3957             printf("sig_match__nrflag: no match ADD/SUB\n");
3958             return 0;
3959         }
3960         if(isADDx_imm(is->insn)) {
3961             adr+=is->insn->detail->arm.operands[1].imm;
3962         } else {
3963             adr-=is->insn->detail->arm.operands[1].imm;
3964         }
3965         if(!disasm_iter(fw,is)) {
3966             printf("sig_match__nrflag: disasm failed\n");
3967             return 0;
3968         }
3969     }
3970     if(is->insn->id != ARM_INS_STR || (arm_reg)is->insn->detail->arm.operands[1].reg != reg_base) {
3971         printf("sig_match__nrflag: no match STR\n");
3972         return 0;
3973     }
3974     uint32_t disp = is->insn->detail->arm.operands[1].mem.disp;
3975     save_misc_val(rule->name,adr,disp,fadr);
3976     return 1;
3977 }
3978 // get the address used by a function that does something like
3979 // ldr rx =base
3980 // ldr r0 [rx, offset]
3981 // bx lr
3982 int sig_match_var_struct_get(firmware *fw, iter_state_t *is, sig_rule_t *rule)
3983 {
3984     if(!init_disasm_sig_ref(fw,is,rule)) {
3985         return 0;
3986     }
3987     uint32_t fadr=is->adr;
3988     var_ldr_desc_t desc;
3989     if(!find_and_get_var_ldr(fw, is, 1, 4, ARM_REG_R0, &desc)) {
3990         printf("sig_match_var_struct_get: no match ldr\n");
3991         return 0;
3992     }
3993     if(!disasm_iter(fw,is)) {
3994         printf("sig_match_var_struct_get: disasm failed\n");
3995         return 0;
3996     }
3997     // TODO could check for other RET type instructions
3998     if(!insn_match(is->insn,match_bxlr)) {
3999         printf("sig_match_var_struct_get: no match BX LR\n");
4000         return 0;
4001     }
4002     save_misc_val(rule->name,desc.adr_adj,desc.off,fadr);
4003     return 1;
4004 }
4005 
4006 int sig_match_av_over_sem(firmware *fw, iter_state_t *is, sig_rule_t *rule)
4007 {
4008     // don't bother on ND-only cams
4009     if(!get_misc_val_value("CAM_HAS_IRIS_DIAPHRAGM")) {
4010         return 0;
4011     }
4012 
4013     if(!init_disasm_sig_ref(fw,is,rule)) {
4014         return 0;
4015     }
4016     if(!find_next_sig_call(fw,is,30,"TakeSemaphore")) {
4017         printf("sig_match_av_over_sem: no match TakeSemaphore at 0x%"PRIx64"\n",is->insn->address);
4018         return 0;
4019     }
4020 
4021     // rewind 5 ins
4022     disasm_iter_init(fw,is,adr_hist_get(&is->ah,5));
4023     var_ldr_desc_t desc;
4024     if(!find_and_get_var_ldr(fw, is, 3, 4, ARM_REG_R0, &desc)) {
4025         printf("sig_match_av_over_sem: no match ldr at 0x%"PRIx64"\n",is->insn->address);
4026         return 0;
4027     }
4028 
4029     save_misc_val(rule->name,desc.adr_adj,desc.off,(uint32_t)is->insn->address);
4030     return 1;
4031 }
4032 
4033 int sig_match_canon_menu_active(firmware *fw, iter_state_t *is, sig_rule_t *rule)
4034 {
4035     if(!init_disasm_sig_ref(fw,is,rule)) {
4036         return 0;
4037     }
4038     var_ldr_desc_t desc;
4039     if(!find_and_get_var_ldr(fw, is, 2, 4, ARM_REG_R0, &desc)) {
4040         printf("sig_match_canon_menu_active: no match ldr at 0x%"PRIx64"\n",is->insn->address);
4041         return 0;
4042     }
4043     if(!disasm_iter(fw,is)) {
4044         printf("sig_match_canon_menu_active: disasm failed\n");
4045         return 0;
4046     }
4047     if(is->insn->id != ARM_INS_CMP) {
4048         printf("sig_match_canon_menu_active: no match cmp at 0x%"PRIx64"\n",is->insn->address);
4049         return 0;
4050     }
4051     save_misc_val(rule->name,desc.adr_adj,desc.off,(uint32_t)is->insn->address);
4052     return 1;
4053 }
4054 
4055 int sig_match_file_counter_init(firmware *fw, iter_state_t *is, sig_rule_t *rule)
4056 {
4057     if(!init_disasm_sig_ref(fw,is,rule)) {
4058         return 0;
4059     }
4060     // find first call
4061     if(!insn_match_find_next(fw,is,3,match_bl_blximm)) {
4062         // printf("sig_match_file_counter_init: bl match 1 failed at 0x%"PRIx64"\n",is->insn->address);
4063         return 0;
4064     }
4065     // some cameras (dry 58+?) have a nullsub before the function of interest
4066     if(check_simple_func(fw,get_branch_call_insn_target(fw,is),MATCH_SIMPLE_FUNC_NULLSUB,NULL)) {
4067         if(!insn_match_find_next(fw,is,3,match_bl_blximm)) {
4068             // printf("sig_match_file_counter_init: bl match 1a failed at 0x%"PRIx64"\n",is->insn->address);
4069             return 0;
4070         }
4071     }
4072     // follow
4073     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
4074     if(!insn_match_find_next(fw,is,3,match_bl_blximm)) {
4075         // printf("sig_match_file_counter_init: bl match 2 failed at 0x%"PRIx64"\n",is->insn->address);
4076         return 0;
4077     }
4078     uint32_t fadr = get_branch_call_insn_target(fw,is);
4079     // follow
4080     disasm_iter_init(fw,is,fadr);
4081     if(!disasm_iter(fw,is)) {
4082         // printf("sig_match_file_counter_init: disasm failed\n");
4083         return 0;
4084     }
4085     // sanity check
4086     if(!isLDR_PC(is->insn)) {
4087         // printf("sig_match_file_counter_init: no match LDR PC at 0x%"PRIx64"\n",is->insn->address);
4088         return 0;
4089     }
4090     // function we're looking for
4091     return save_sig_with_j(fw,rule->name,fadr);
4092 }
4093 int sig_match_file_counter_var(firmware *fw, iter_state_t *is, sig_rule_t *rule)
4094 {
4095     if(!init_disasm_sig_ref(fw,is,rule)) {
4096         return 0;
4097     }
4098     uint32_t adr=LDR_PC2val(fw,is->insn);
4099     if(!adr) {
4100         // printf("sig_match_file_counter_var: no match LDR PC 0x%"PRIx64"\n",is->insn->address);
4101         return 0;
4102     }
4103     if(is->insn->detail->arm.operands[0].reg != ARM_REG_R0) {
4104         // printf("sig_match_file_counter_var: not R0 0x%"PRIx64"\n",is->insn->address);
4105         return 0;
4106     }
4107     if(!adr_is_var(fw,adr)) {
4108         // printf("sig_match_file_counter_var: not a data address 0x%08x at 0x%"PRIx64"\n",adr,is->insn->address);
4109         return 0;
4110     }
4111     save_misc_val(rule->name,adr,0,(uint32_t)is->insn->address);
4112     return 1;
4113 }
4114 
4115 int sig_match_palette_vars(firmware *fw, iter_state_t *is, sig_rule_t *rule)
4116 {
4117     if(!init_disasm_sig_ref(fw,is,rule)) {
4118         return 0;
4119     }
4120     if(!find_next_sig_call(fw,is,70,"transfer_src_overlay")) {
4121         printf("sig_match_palette_vars: no match transfer_src_overlay\n");
4122         return 0;
4123     }
4124     uint32_t fadr=0;
4125     int i;
4126     // search backwards for call before transfer_src_overlay
4127     for(i=1; i<=6; i++) {
4128         if(!fw_disasm_iter_single(fw,adr_hist_get(&is->ah,i))) {
4129             printf("sig_match_palette_vars: disasm failed\n");
4130             return 0;
4131         }
4132         fadr=get_branch_call_insn_target(fw,fw->is);
4133         if(fadr) {
4134             break;
4135         }
4136    }
4137    if(!fadr) {
4138         printf("sig_match_palette_vars: no match bl 1 0x%"PRIx64"\n",fw->is->insn->address);
4139         return 0;
4140    }
4141     // follow
4142     disasm_iter_init(fw,is,fadr);
4143     // find first func call
4144     if(!insn_match_find_next(fw,is,3,match_bl)) {
4145         printf("sig_match_palette_vars: no match bl 2 0x%"PRIx64"\n",is->insn->address);
4146         return 0;
4147     }
4148     // follow
4149     disasm_iter_init(fw,is,get_branch_call_insn_target(fw,is));
4150 
4151     if(!insn_match_find_next(fw,is,3,match_ldr_pc)) {
4152         printf("sig_match_palette_vars: no match ldr pc 0x%"PRIx64"\n",is->insn->address);
4153         return 0;
4154     }
4155 
4156     uint32_t pal_base=LDR_PC2val(fw,is->insn);
4157     if(!pal_base || !adr_is_var(fw,pal_base)) {
4158         printf("sig_match_palette_vars: bad LDR PC 0x%"PRIx64"\n",is->insn->address);
4159         return 0;
4160     }
4161     // palette_control is at the start of struct, save register
4162     arm_reg ptr_reg = is->insn->detail->arm.operands[0].reg;
4163 
4164     save_misc_val(rule->name,pal_base,0,(uint32_t)is->insn->address);
4165 
4166     int found=0;
4167     // find LDR Rn  [ptr_reg +x]
4168     for(i=0; i<3; i++) {
4169         if(!disasm_iter(fw,is)) {
4170             printf("sig_match_palette_vars: disasm failed\n");
4171             return 0;
4172         }
4173         if (is->insn->id == ARM_INS_LDR && is->insn->detail->arm.operands[1].mem.base == ptr_reg) {
4174             save_misc_val("active_palette_buffer",
4175                         pal_base,
4176                         is->insn->detail->arm.operands[1].mem.disp,
4177                         (uint32_t)is->insn->address);
4178             found=1;
4179             break;
4180         }
4181     }
4182     if(!found) {
4183         printf("sig_match_palette_vars: no match active_palette_buffer 0x%"PRIx64"\n",is->insn->address);
4184         return 0;
4185     }
4186 
4187     if(!find_next_sig_call(fw,is,20,"PTM_RestoreUIProperty_FW")) {
4188         printf("sig_match_palette_vars: no match PTM_RestoreUIProperty_FW\n");
4189         return 0;
4190     }
4191     // find LDR Rn  [ptr_reg +x]
4192     for(i=0; i<6; i++) {
4193         if(!disasm_iter(fw,is)) {
4194             printf("sig_match_palette_vars: disasm failed\n");
4195             return 0;
4196         }
4197         if (is->insn->id == ARM_INS_LDR && is->insn->detail->arm.operands[1].mem.base == ptr_reg) {
4198             save_misc_val("palette_buffer_ptr",
4199                         pal_base,
4200                         is->insn->detail->arm.operands[1].mem.disp,
4201                         (uint32_t)is->insn->address);
4202             return 1;
4203         }
4204     }
4205     printf("sig_match_palette_vars: no match palette_buffer_ptr 0x%"PRIx64"\n",is->insn->address);
4206     return 0;
4207 }
4208 
4209 int sig_match_rom_ptr_get(firmware *fw, iter_state_t *is, sig_rule_t *rule)
4210 {
4211     if(!init_disasm_sig_ref(fw,is,rule)) {
4212         return 0;
4213     }
4214     uint32_t fadr=is->adr;
4215     if(!disasm_iter(fw,is)) {
4216         printf("sig_match_rom_ptr_get: disasm failed\n");
4217         return 0;
4218     }
4219     uint32_t adr=LDR_PC2val(fw,is->insn);
4220     if(!adr) {
4221         printf("sig_match_rom_ptr_get: no match LDR PC 0x%"PRIx64"\n",is->insn->address);
4222         return  0;
4223     }
4224     if(is->insn->detail->arm.operands[0].reg != ARM_REG_R0) {
4225         printf("sig_match_rom_ptr_get: not R0\n");
4226         return 0;
4227     }
4228     if(!disasm_iter(fw,is)) {
4229         printf("sig_match_rom_ptr_get: disasm failed\n");
4230         return 0;
4231     }
4232     // TODO could check for other RET type instructions
4233     if(!insn_match(is->insn,match_bxlr)) {
4234         printf("sig_match_rom_ptr_get: no match BX LR\n");
4235         return 0;
4236     }
4237     save_misc_val(rule->name,adr,0,fadr);
4238     return 1;
4239 }
4240 
4241 // find Nth function call within max_insns ins of string ref, 
4242 // returns address w/thumb bit set according to current state of call instruction
4243 // modifies is and potentially fw->is
4244 uint32_t find_call_near_str(firmware *fw, iter_state_t *is, sig_rule_t *rule)
4245 {
4246     uint32_t str_adr;
4247     if(rule->param & SIG_NEAR_INDIRECT) {
4248         str_adr = find_str_bytes(fw,rule->ref_name); // indirect string could be in data area
4249     } else {
4250         str_adr = find_str_bytes_main_fw(fw,rule->ref_name); // direct string must be near actual code
4251     }
4252     if(!str_adr) {
4253         printf("find_call_near_str: %s failed to find ref %s\n",rule->name,rule->ref_name);
4254         return 0;
4255     }
4256     uint32_t search_adr = str_adr;
4257     // looking for ref to ptr to string, not ref to string
4258     // TODO only looks for first ptr
4259     if(rule->param & SIG_NEAR_INDIRECT) {
4260         // printf("find_call_near_str: %s str 0x%08x\n",rule->name,str_adr);
4261         search_adr=find_u32_adr_range(fw,str_adr,fw->rom_code_search_min_adr,fw->rom_code_search_max_adr);
4262         if(!search_adr) {
4263             printf("find_call_near_str: %s failed to find indirect ref %s\n",rule->name,rule->ref_name);
4264             return 0;
4265         }
4266         // printf("find_call_near_str: %s indirect 0x%08x\n",rule->name,search_adr);
4267     }
4268     const insn_match_t *insn_match;
4269     if(rule->param & SIG_NEAR_JMP_SUB) {
4270         insn_match = match_b_bl_blximm;
4271     } else {
4272         insn_match = match_bl_blximm;
4273     }
4274 
4275     int max_insns=rule->param&SIG_NEAR_OFFSET_MASK;
4276     int n=(rule->param&SIG_NEAR_COUNT_MASK)>>SIG_NEAR_COUNT_SHIFT;
4277     //printf("find_call_near_str: %s max_insns %d n %d %s\n",rule->name,max_insns,n,(rule->param & SIG_NEAR_REV)?"rev":"fwd");
4278     // TODO should handle multiple instances of string
4279     disasm_iter_init(fw,is,(ADR_ALIGN4(search_adr) - SEARCH_NEAR_REF_RANGE) | fw->thumb_default); // reset to a bit before where the string was found
4280     while(fw_search_insn(fw,is,search_disasm_const_ref,str_adr,NULL,search_adr+SEARCH_NEAR_REF_RANGE)) {
4281         // bactrack looking for preceding call
4282         if(rule->param & SIG_NEAR_REV) {
4283             int i;
4284             int n_calls=0;
4285             for(i=1; i<=max_insns; i++) {
4286                 fw_disasm_iter_single(fw,adr_hist_get(&is->ah,i));
4287                 if(insn_match_any(fw->is->insn,insn_match)) {
4288                     n_calls++;
4289                 }
4290                 if(n_calls == n) {
4291                     return iter_state_adr(fw->is);
4292                 }
4293             }
4294         } else {
4295             if(insn_match_find_nth(fw,is,max_insns,n,insn_match)) {
4296                 return iter_state_adr(is);
4297             }
4298         }
4299     }
4300     printf("find_call_near_str: no match %s\n",rule->name);
4301     return 0;
4302 }
4303                                 
4304 // find Nth function call within max_insns ins of string ref
4305 int sig_match_near_str(firmware *fw, iter_state_t *is, sig_rule_t *rule)
4306 {
4307     if (!get_saved_sig_val(rule->name))
4308     {
4309         uint32_t call_adr = find_call_near_str(fw,is,rule);
4310         if(call_adr) {
4311             return save_sig_match_call(fw, rule, call_adr);
4312         }
4313     }
4314     return 0;
4315 }
4316 
4317 
4318 int sig_match_prop_string(firmware *fw, iter_state_t *is, sig_rule_t *rule)
4319 {
4320     uint32_t call_adr = find_call_near_str(fw, is, rule);
4321 
4322     if (call_adr == 0)
4323         return 0;
4324 
4325     // initialize to found address
4326     disasm_iter_init(fw,is,call_adr);
4327     disasm_iter(fw,is);
4328 
4329     uint32_t myreg;
4330 
4331     if (is_sig_call(fw,is,"GetPropertyCase")) {
4332         // looking for r0
4333         myreg = 0;
4334     }
4335     else {
4336         // semaphore version of GetPropertyCase, looking for r1
4337         myreg = 1;
4338     }
4339     
4340     // re-init 'is' to current address minus at least 8 insts
4341     const int hl = 8;
4342     disasm_iter_init(fw,is,call_adr - hl*4);
4343     // history needs to be made
4344     while (is->adr < call_adr) {
4345         if (!disasm_iter(fw,is))
4346             disasm_iter_init(fw,is,(is->adr | is->thumb)+2);
4347     }
4348     uint32_t regs[4];
4349     // get r0 or r1, backtracking up to 8 instructions
4350     if ((get_call_const_args(fw,is,hl,regs)&(1<<myreg))==(1<<myreg)) {
4351         add_prop_hit(rule->name,(int)regs[myreg]);
4352         return 1;
4353     }
4354     return 0;
4355 }
4356 
4357 // check if func is a nullsub or mov r0, x ; ret
4358 // to prevent sig_named* matches from going off the end of dummy funcs
4359 int is_immediate_ret_sub(firmware *fw,iter_state_t *is_init)
4360 {
4361     fw_disasm_iter_single(fw,is_init->adr | is_init->thumb);
4362     const insn_match_t match_mov_r0_imm[]={
4363         {MATCH_INS(MOV,   2),  {MATCH_OP_REG(R0),  MATCH_OP_IMM_ANY}},
4364 #if CS_API_MAJOR < 4
4365         {MATCH_INS(MOVS,  2),  {MATCH_OP_REG(R0),  MATCH_OP_IMM_ANY}},
4366 #endif
4367         {ARM_INS_ENDING}
4368     };
4369     // if it's a MOV, check if next is ret
4370     if(insn_match_any(fw->is->insn,match_mov_r0_imm)) {
4371         fw_disasm_iter(fw);
4372     }
4373     if(isRETx(fw->is->insn)) {
4374         return 1;
4375     }
4376     return 0;
4377 }
4378 
4379 // match last function called by already matched sig, 
4380 // either the last bl/blximmg before pop {... pc}
4381 // or b after pop {... lr}
4382 // param defines min and max number of insns
4383 // doesn't work on functions that don't push/pop since can't tell if unconditional branch is last
4384 // TODO should probably be split into a general "find last call of current func"
4385 #define SIG_NAMED_LAST_MAX_MASK     0x00000FFF
4386 #define SIG_NAMED_LAST_MIN_MASK     0x00FFF000
4387 #define SIG_NAMED_LAST_MIN_SHIFT    12
4388 #define SIG_NAMED_LAST_RANGE(min,max)   ((SIG_NAMED_LAST_MIN_MASK&((min)<<SIG_NAMED_LAST_MIN_SHIFT)) \
4389                                          | (SIG_NAMED_LAST_MAX_MASK&(max)))
4390 
4391 int sig_match_named_last(firmware *fw, iter_state_t *is, sig_rule_t *rule)
4392 {
4393     uint32_t ref_adr = get_saved_sig_val(rule->ref_name);
4394     int min = (rule->param&SIG_NAMED_LAST_MIN_MASK)>>SIG_NAMED_LAST_MIN_SHIFT;
4395     int max = (rule->param&SIG_NAMED_LAST_MAX_MASK);
4396     if(!ref_adr) {
4397         printf("sig_match_named_last: %s missing %s\n",rule->name,rule->ref_name);
4398         return 0;
4399     }
4400     disasm_iter_init(fw,is,ref_adr);
4401     if(is_immediate_ret_sub(fw,is)) {
4402         printf("sig_match_named_last: immediate return %s\n",rule->name);
4403         return 0;
4404     }
4405     uint32_t fadr = find_last_call_from_func(fw,is,min,max);
4406     if(fadr) {
4407         return save_sig_with_j(fw,rule->name,fadr);
4408     }
4409     return 0;
4410 }
4411 
4412 // default - use the named firmware function
4413 #define SIG_NAMED_ASIS          0x00000000
4414 // use the target of the first B, BX, BL, BLX etc
4415 #define SIG_NAMED_JMP_SUB       0x00000001
4416 // use the target of the first BL, BLX
4417 #define SIG_NAMED_SUB           0x00000002
4418 // match address of Nth instruction in named sub
4419 #define SIG_NAMED_INSN          0x00000003
4420 #define SIG_NAMED_TYPE_MASK     0x0000000F
4421 
4422 #define SIG_NAMED_CLEARTHUMB    0x00000010
4423 #define SIG_NAMED_FLAG_MASK     0x000000F0
4424 
4425 #define SIG_NAMED_NTH_MASK      0x00000F00
4426 #define SIG_NAMED_NTH_SHIFT     8
4427 
4428 // number of instructions to search for each Nth
4429 // default 5
4430 #define SIG_NAMED_NTH_RANGE_MASK  0x0003F000
4431 #define SIG_NAMED_NTH_RANGE_SHIFT 12
4432 
4433 //#define SIG_NAMED_NTH(n,type)   ((SIG_NAMED_NTH_MASK&((n)<<SIG_NAMED_NTH_SHIFT)) | ((SIG_NAMED_##type)&SIG_NAME_TYPE_MASK))
4434 #define SIG_NAMED_NTH(n,type)   ((SIG_NAMED_NTH_MASK&((n)<<SIG_NAMED_NTH_SHIFT)) | (SIG_NAMED_##type))
4435 
4436 #define SIG_NAMED_NTH_RANGE(n)   ((SIG_NAMED_NTH_RANGE_MASK&((n)<<SIG_NAMED_NTH_RANGE_SHIFT)))
4437 
4438 void sig_match_named_save_sig(firmware *fw,const char *name, uint32_t adr, uint32_t flags)
4439 {
4440     if(flags & SIG_NAMED_CLEARTHUMB) {
4441         adr = ADR_CLEAR_THUMB(adr);
4442     }
4443     save_sig(fw,name,adr);
4444 }
4445 // match already identified function found by name
4446 // if offset is 1, match the first called function with 20 insn instead (e.g. to avoid eventproc arg handling)
4447 // initial direct jumps (j_foo) assumed to have been handled
4448 int sig_match_named(firmware *fw, iter_state_t *is, sig_rule_t *rule)
4449 {
4450     uint32_t ref_adr = get_saved_sig_val(rule->ref_name);
4451     if(!ref_adr) {
4452         printf("sig_match_named: missing %s\n",rule->ref_name);
4453         return 0;
4454     }
4455     uint32_t sig_type = rule->param & SIG_NAMED_TYPE_MASK;
4456     uint32_t sig_flags = rule->param & SIG_NAMED_FLAG_MASK;
4457     uint32_t sig_nth = (rule->param & SIG_NAMED_NTH_MASK)>>SIG_NAMED_NTH_SHIFT;
4458     uint32_t sig_nth_range = (rule->param & SIG_NAMED_NTH_RANGE_MASK)>>SIG_NAMED_NTH_RANGE_SHIFT;
4459     if(!sig_nth) {
4460         sig_nth=1;
4461     }
4462     if(!sig_nth_range) {
4463         sig_nth_range=5;
4464     }
4465     // no offset, just save match as is
4466     // TODO might want to validate anyway
4467     if(sig_type == SIG_NAMED_ASIS) {
4468         sig_match_named_save_sig(fw,rule->name,ref_adr,sig_flags); 
4469         return 1;
4470     }
4471     const insn_match_t *insn_match;
4472     if(sig_type == SIG_NAMED_JMP_SUB) {
4473         insn_match = match_b_bl_blximm;
4474     } else if(sig_type == SIG_NAMED_SUB) {
4475         insn_match = match_bl_blximm;
4476     } else if(sig_type == SIG_NAMED_INSN) {
4477         insn_match = NULL;
4478     } else {
4479         printf("sig_match_named: %s invalid type %d\n",rule->ref_name,sig_type);
4480         return 0;
4481     }
4482 
4483     disasm_iter_init(fw,is,ref_adr);
4484     // TODO for eventprocs, may just want to use the original
4485     if(is_immediate_ret_sub(fw,is)) {
4486         printf("sig_match_named: immediate return %s\n",rule->name);
4487         return 0;
4488     }
4489     if(sig_type == SIG_NAMED_INSN) {
4490         uint32_t i;
4491         // iter starts on the address given to init
4492         for(i=0;i<=sig_nth;i++) {
4493             if(!disasm_iter(fw,is)) {
4494                 printf("sig_match_named: disasm failed %s 0x%08x\n",rule->name,(uint32_t)is->insn->address);
4495                 return 0;
4496             }
4497         }
4498         sig_match_named_save_sig(fw,rule->name,iter_state_adr(is),sig_flags);
4499         return 1;
4500     }
4501 
4502     // initial 15 is hard coded
4503     if(insn_match_find_nth(fw,is,15 + sig_nth_range*sig_nth,sig_nth,insn_match)) {
4504         uint32_t adr = B_BL_BLXimm_target(fw,is->insn);
4505         if(adr) {
4506             // BLX, set thumb bit 
4507             if(is->insn->id == ARM_INS_BLX) {
4508                 // curently not thumb, set in target
4509                 if(!is->thumb) {
4510                     adr=ADR_SET_THUMB(adr);
4511                 }
4512             } else {
4513                 // preserve current state
4514                 adr |= is->thumb;
4515             }
4516             disasm_iter_set(fw,is,adr);
4517             if(disasm_iter(fw,is)) {
4518                 // TODO only checks one level
4519                 uint32_t j_adr=get_direct_jump_target(fw,is);
4520                 if(j_adr) {
4521                     char *buf=malloc(strlen(rule->name)+3);
4522                     // add j_ for cross referencing
4523                     sprintf(buf,"j_%s",rule->name);
4524                     add_func_name(fw,buf,adr,NULL); // add the previous address as j_...
4525                     adr=j_adr;
4526                 }
4527             } else {
4528                 printf("sig_match_named: disasm failed in j_ check at %s 0x%08x\n",rule->name,adr);
4529             }
4530             sig_match_named_save_sig(fw,rule->name,adr,sig_flags); 
4531             return 1;
4532         } else {
4533             printf("sig_match_named: %s invalid branch target 0x%08x\n",rule->ref_name,adr);
4534         }
4535     } else {
4536         printf("sig_match_named: %s branch not found 0x%08x\n",rule->ref_name,ref_adr);
4537     }
4538     return 0;
4539 }
4540 
4541 #define SIG_DRY_MIN(min_rel) (min_rel),0
4542 #define SIG_DRY_MAX(max_rel) 0,(max_rel)
4543 #define SIG_DRY_RANGE(min_rel,max_rel) (min_rel),(max_rel)
4544 // bootstrap sigs:
4545 // Used to find the minimum needed to for find_generic_funcs to get generic task and eventproc matches 
4546 // order is important
4547 sig_rule_t sig_rules_initial[]={
4548 // function         CHDK name                   ref name/string         func param          dry rel
4549 // NOTE _FW is in the CHDK column, because that's how it is in sig_names
4550 {sig_match_str_r0_call, "ExportToEventProcedure_FW","ExportToEventProcedure"},
4551 {sig_match_reg_evp,     "RegisterEventProcedure",},
4552 {sig_match_reg_evp_table, "RegisterEventProcTable","DispDev_EnableEventProc"},
4553 {sig_match_reg_evp_alt2, "RegisterEventProcedure_alt2","EngApp.Delete"},
4554 {sig_match_unreg_evp_table,"UnRegisterEventProcTable","MechaUnRegisterEventProcedure"},
4555 {sig_match_evp_table_veneer,"RegisterEventProcTable_alt","RegisterEventProcTable"},
4556 {sig_match_evp_table_veneer,"UnRegisterEventProcTable_alt","UnRegisterEventProcTable"},
4557 {sig_match_str_r0_call,"CreateTaskStrictly",    "LowConsole",},
4558 {sig_match_str_r0_call,"CreateTaskStrictly_alt","HdmiCecTask",          0,                  SIG_DRY_MIN(59)},
4559 {sig_match_str_r0_call,"CreateTask",            "EvShel",},
4560 {sig_match_str_r0_call,"CreateTask_alt",        "PhySw",                0,                  SIG_DRY_MIN(58)},
4561 {sig_match_named,   "CreateTask_low",           "CreateTask",           (SIG_NAMED_NTH(2,SUB)|SIG_NAMED_NTH_RANGE(10)), SIG_DRY_MAX(52)},
4562 {sig_match_named,   "CreateTask_low",           "CreateTask",           (SIG_NAMED_NTH(3,SUB)|SIG_NAMED_NTH_RANGE(10)), SIG_DRY_MIN(54)},
4563 {sig_match_near_str,   "dry_memcpy",            "EP Slot%d",            SIG_NEAR_BEFORE(4,1)},
4564 {sig_match_add_ptp_handler,"add_ptp_handler",   "PTPtoFAPI_EventProcTask_Try",},
4565 {NULL},
4566 };
4567 
4568 // main sigs:
4569 // Run after find_generic_funcs. Order is important
4570 sig_rule_t sig_rules_main[]={
4571 // function         CHDK name                   ref name/string         func param          dry rel
4572 {sig_match_named,   "SetParameterData",         "PTM_BackupUIProperty_FW", 0, SIG_DRY_MIN(58)},
4573 {sig_match_named,   "ExitTask",                 "ExitTask_FW",},
4574 {sig_match_named,   "EngDrvRead",               "EngDrvRead_FW",        SIG_NAMED_JMP_SUB},
4575 {sig_match_named,   "CalcLog10",                "CalcLog10_FW",         SIG_NAMED_JMP_SUB},
4576 {sig_match_named,   "CalcSqrt",                 "CalcSqrt_FW",          SIG_NAMED_JMP_SUB},
4577 {sig_match_named,   "Close",                    "Close_FW",},
4578 {sig_match_named,   "close",                    "Close",                SIG_NAMED_SUB,      SIG_DRY_MAX(57)},
4579 {sig_match_named,   "DoAELock",                 "SS.DoAELock_FW",       SIG_NAMED_JMP_SUB},
4580 {sig_match_named,   "DoAFLock",                 "SS.DoAFLock_FW",       SIG_NAMED_JMP_SUB},
4581 {sig_match_named,   "Fclose_Fut",               "Fclose_Fut_FW",},
4582 {sig_match_named,   "Fopen_Fut",                "Fopen_Fut_FW",},
4583 {sig_match_named,   "Fread_Fut",                "Fread_Fut_FW",},
4584 {sig_match_named,   "Fseek_Fut",                "Fseek_Fut_FW",},
4585 {sig_match_named,   "Fwrite_Fut",               "Fwrite_Fut_FW",},
4586 {sig_match_named,   "GetAdChValue",             "GetAdChValue_FW",},
4587 {sig_match_named,   "GetCurrentAvValue",        "GetCurrentAvValue_FW",},
4588 {sig_match_named,   "GetCurrentShutterSpeed",   "GetCurrentShutterSpeed_FW",},
4589 {sig_match_named,   "GetBatteryTemperature",    "GetBatteryTemperature_FW",},
4590 {sig_match_named,   "GetCCDTemperature",        "GetCCDTemperature_FW",},
4591 {sig_match_named,   "GetFocusLensSubjectDistance","GetFocusLensSubjectDistance_FW",SIG_NAMED_JMP_SUB},
4592 {sig_match_named,   "GetOpticalTemperature",    "GetOpticalTemperature_FW",},
4593 {sig_match_named,   "GetPropertyCase",          "GetPropertyCase_FW",   SIG_NAMED_SUB},
4594 {sig_match_named,   "GetSystemTime",            "GetSystemTime_FW",},
4595 {sig_match_named,   "GetUsableMaxAv",           "GetUsableMaxAv_FW",},
4596 {sig_match_named,   "GetUsableMinAv",           "GetUsableMinAv_FW",},
4597 // a different match would be needed for older, ND only cams maybe based on "AE Result Tv Setting "
4598 {sig_match_named,   "GetUsableAvRange",         "GetUsableMinAv", SIG_NAMED_SUB},
4599 {sig_match_named,   "GetVRAMHPixelsSize",       "GetVRAMHPixelsSize_FW",},
4600 {sig_match_named,   "GetVRAMVPixelsSize",       "GetVRAMVPixelsSize_FW",},
4601 {sig_match_named,   "GetZoomLensCurrentPoint",  "GetZoomLensCurrentPoint_FW",},
4602 {sig_match_named,   "GetZoomLensCurrentPosition","GetZoomLensCurrentPosition_FW",},
4603 {sig_match_named,   "GiveSemaphore",            "GiveSemaphore_FW",},
4604 {sig_match_named,   "IsStrobeChargeCompleted",  "EF.IsChargeFull_FW",},
4605 {sig_match_named,   "Read",                     "Read_FW",},
4606 {sig_match_named,   "LEDDrive",                 "LEDDrive_FW",},
4607 {sig_match_named,   "LockMainPower",            "LockMainPower_FW",},
4608 {sig_match_named,   "MoveFocusLensToDistance",  "MoveFocusLensToDistance_FW",},
4609 {sig_match_named,   "MoveIrisWithAv",           "MoveIrisWithAv_FW",},
4610 {sig_match_named,   "MoveZoomLensWithPoint",    "MoveZoomLensWithPoint_FW",},
4611 {sig_match_named,   "Open",                     "Open_FW",},
4612 {sig_match_named,   "PostLogicalEventForNotPowerType",  "PostLogicalEventForNotPowerType_FW",},
4613 {sig_match_named,   "PostLogicalEventToUI",     "PostLogicalEventToUI_FW",},
4614 {sig_match_named,   "PT_MFOn",                  "SS.MFOn_FW",           SIG_NAMED_JMP_SUB},
4615 {sig_match_named,   "PT_MFOff",                 "SS.MFOff_FW",          SIG_NAMED_JMP_SUB},
4616 {sig_match_named,   "PT_MoveDigitalZoomToWide", "SS.MoveDigitalZoomToWide_FW", SIG_NAMED_JMP_SUB},
4617 {sig_match_named,   "PT_MoveOpticalZoomAt",     "SS.MoveOpticalZoomAt_FW",},
4618 {sig_match_named,   "PutInNdFilter",            "PutInNdFilter_FW",},
4619 {sig_match_named,   "PutOutNdFilter",           "PutOutNdFilter_FW",},
4620 {sig_match_named,   "SetAE_ShutterSpeed",       "SetAE_ShutterSpeed_FW",},
4621 {sig_match_named,   "SetAutoShutdownTime",      "SetAutoShutdownTime_FW",},
4622 {sig_match_named,   "SetCurrentCaptureModeType","SetCurrentCaptureModeType_FW",},
4623 {sig_match_named,   "SetDate",                  "SetDate_FW",},
4624 {sig_match_named,   "SetLogicalEventActive",    "UiEvnt_SetLogicalEventActive_FW",},
4625 {sig_match_named,   "SetScriptMode",            "SetScriptMode_FW",},
4626 {sig_match_named,   "SleepTask",                "SleepTask_FW",},
4627 {sig_match_named,   "SetPropertyCase",          "SetPropertyCase_FW",   SIG_NAMED_SUB},
4628 {sig_match_named,   "TakeSemaphore",            "TakeSemaphore_FW",},
4629 {sig_match_named,   "TurnOnDisplay",            "DispCon_TurnOnDisplay_FW",SIG_NAMED_SUB},
4630 {sig_match_named,   "TurnOffDisplay",           "DispCon_TurnOffDisplay_FW",SIG_NAMED_SUB},
4631 {sig_match_named,   "TurnOnBackLight",          "DispCon_TurnOnBackLight_FW",SIG_NAMED_SUB, SIG_DRY_MAX(57)},
4632 {sig_match_named,   "TurnOffBackLight",         "DispCon_TurnOffBackLight_FW",SIG_NAMED_SUB},
4633 {sig_match_named,   "UIFS_WriteFirmInfoToFile", "UIFS_WriteFirmInfoToFile_FW",},
4634 {sig_match_named,   "UnlockAE",                 "SS.UnlockAE_FW",       SIG_NAMED_JMP_SUB},
4635 {sig_match_named,   "UnlockAF",                 "SS.UnlockAF_FW",       SIG_NAMED_JMP_SUB},
4636 {sig_match_named,   "UnlockMainPower",          "UnlockMainPower_FW",},
4637 {sig_match_named,   "UnRegisterEventProcedure", "UnRegisterEventProcTable", SIG_NAMED_SUB},
4638 //{sig_match_named,   "UnsetZoomForMovie",        "UnsetZoomForMovie_FW",},
4639 {sig_match_named,   "VbattGet",                 "VbattGet_FW",},
4640 {sig_match_named,   "Write",                    "Write_FW",},
4641 {sig_match_named,   "bzero",                    "exec_FW",              SIG_NAMED_SUB},
4642 {sig_match_named,   "exmem_free",               "ExMem.FreeCacheable_FW",SIG_NAMED_JMP_SUB},
4643 {sig_match_named,   "exmem_alloc",              "ExMem.AllocCacheable_FW",SIG_NAMED_JMP_SUB},
4644 {sig_match_named,   "exmem_ufree",              "ExMem.FreeUncacheable_FW",SIG_NAMED_JMP_SUB},
4645 {sig_match_named,   "exmem_ualloc",             "ExMem.AllocUncacheable_FW",SIG_NAMED_JMP_SUB},
4646 {sig_match_named,   "free",                     "FreeMemory_FW",        SIG_NAMED_JMP_SUB},
4647 {sig_match_named,   "lseek",                    "Lseek_FW",},
4648 {sig_match_named,   "_log10",                   "CalcLog10",            SIG_NAMED_NTH(2,SUB)},
4649 {sig_match_named,   "malloc",                   "AllocateMemory_FW",    SIG_NAMED_JMP_SUB},
4650 {sig_match_named,   "memcmp",                   "memcmp_FW",},
4651 {sig_match_named,   "memcpy",                   "memcpy_FW",},
4652 {sig_match_named,   "memset",                   "memset_FW",},
4653 {sig_match_named,   "strcmp",                   "strcmp_FW",},
4654 {sig_match_named,   "strcpy",                   "strcpy_FW",},
4655 {sig_match_named,   "strlen",                   "strlen_FW",},
4656 {sig_match_named,   "task_CaptSeq",             "task_CaptSeqTask",},
4657 {sig_match_named,   "task_ExpDrv",              "task_ExpDrvTask",},
4658 {sig_match_named,   "task_FileWrite",           "task_FileWriteTask",},
4659 //{sig_match_named,   "task_MovieRecord",         "task_MovieRecord",},
4660 //{sig_match_named,   "task_PhySw",               "task_PhySw",},
4661 {sig_match_named,   "vsprintf",                 "sprintf_FW",           SIG_NAMED_SUB},
4662 {sig_match_named,   "PTM_GetCurrentItem",       "PTM_GetCurrentItem_FW",},
4663 {sig_match_named,   "DisableISDriveError",      "DisableISDriveError_FW",},
4664 {sig_match_named,   "hook_CreateTask",          "CreateTask",           SIG_NAMED_CLEARTHUMB},
4665 // alternate if CreateTask is in ROM
4666 {sig_match_named,   "hook_CreateTask_low",      "CreateTask_low",       SIG_NAMED_CLEARTHUMB},
4667 {sig_match_named,   "malloc_strictly",          "task_EvShel",          SIG_NAMED_NTH(2,SUB)},
4668 {sig_match_named,   "DebugAssert2",             "malloc_strictly",      SIG_NAMED_NTH(3,SUB)},
4669 {sig_match_named,   "AcquireRecursiveLockStrictly","StartWDT_FW",       SIG_NAMED_NTH(1,SUB)},
4670 {sig_match_named,   "CheckAllEventFlag",        "ChargeStrobeForFA_FW", SIG_NAMED_SUB},
4671 {sig_match_named,   "ClearEventFlag",           "GetAEIntegralValueWithFix_FW",SIG_NAMED_SUB},
4672 {sig_match_named,   "CheckAnyEventFlag",        "task_SynchTask",       SIG_NAMED_NTH(2,SUB)},
4673 {sig_match_named,   "taskcreate_LowConsole",    "task_EvShel",          SIG_NAMED_SUB},
4674 {sig_match_named,   "CreateMessageQueueStrictly","taskcreate_LowConsole",SIG_NAMED_SUB},
4675 {sig_match_named,   "CreateBinarySemaphoreStrictly","taskcreate_LowConsole",SIG_NAMED_NTH(2,SUB)},
4676 {sig_match_named,   "PostMessageQueue",         "GetCh_FW",             SIG_NAMED_NTH(2,SUB)},
4677 {sig_match_named,   "CreateEventFlagStrictly",  "InitializeDigicon_FW", SIG_NAMED_SUB},
4678 {sig_match_named,   "WaitForAnyEventFlag",      "task_DPOFTask",        SIG_NAMED_SUB},
4679 {sig_match_named,   "GetEventFlagValue",        "task_DPOFTask",        SIG_NAMED_NTH(2,SUB)},
4680 {sig_match_named,   "CreateBinarySemaphore",    "task_UartLog",         SIG_NAMED_SUB},
4681 {sig_match_named,   "PostMessageQueueStrictly", "EF.IsChargeFull_FW",   SIG_NAMED_SUB},
4682 {sig_match_named,   "SetEventFlag",             "StopStrobeChargeForFA_FW",SIG_NAMED_SUB},
4683 {sig_match_named,   "TryReceiveMessageQueue",   "task_DvlpSeqTask",     SIG_NAMED_NTH(3,SUB)},
4684 // Semaphore funcs found by eventproc match, but want veneers. Will warn if mismatched
4685 {sig_match_named,   "TakeSemaphore",            "task_Bye",             SIG_NAMED_SUB},
4686 {sig_match_named_last,"GiveSemaphore",          "TurnOnVideoOutMode_FW",SIG_NAMED_LAST_RANGE(10,24)},
4687 // TODO finding through veneers would be better for disassembly
4688 {sig_match_named,   "givesemaphore_low",        "GiveSemaphore",        SIG_NAMED_SUB,      SIG_DRY_MAX(52)}, // first call on dry <=52
4689 {sig_match_named,   "givesemaphore_low",        "GiveSemaphore",        SIG_NAMED_NTH(2,SUB),SIG_DRY_MIN(53)}, // 2nd call on dry >52
4690 
4691 // can't use last because func has early return POP
4692 {sig_match_named,   "ReleaseRecursiveLock",     "StartWDT_FW",          SIG_NAMED_NTH(2,SUB)},
4693 {sig_match_named,   "MoveOpticalZoomAt",        "SS.MoveOpticalZoomAt_FW",SIG_NAMED_SUB},
4694 {sig_match_named,   "SetVideoOutType",          "SetVideoOutType_FW",SIG_NAMED_SUB},
4695 {sig_match_named,   "GetVideoOutType",          "GetVideoOutType_FW"},
4696 // alternate match because "exec" lands near a literal pool on some cams
4697 {sig_match_near_str,"bzero",                    "Canon Degital Camera"/*sic*/,SIG_NEAR_AFTER(8,2)|SIG_NEAR_INDIRECT},
4698 //{sig_match_near_str,"bzero",                    "FromDate",             SIG_NEAR_BEFORE(2,1)},
4699 {sig_match_named,   "memset32",                 "bzero",                SIG_NAMED_NTH(1,INSN)},
4700 {sig_match_misc_flag_named,"CAM_IS_ILC",        "task_EFLensComTask",},
4701 {sig_match_misc_flag_named,"CAM_HAS_ND_FILTER", "task_Nd",},
4702 {sig_match_cam_has_iris_diaphragm,"CAM_HAS_IRIS_DIAPHRAGM","task_IrisEvent",},
4703 {sig_match_near_str,"ImagerActivate",           "Fail ImagerActivate(ErrorCode:%x)\r",SIG_NEAR_BEFORE(6,1)},
4704 {sig_match_screenlock_helper,"screenlock_helper","UIFS_DisplayFirmUpdateView_FW"},
4705 {sig_match_named,   "ScreenLock",               "screenlock_helper",SIG_NAMED_SUB},
4706 //{sig_match_screenlock,"ScreenLock",             "UIFS_DisplayFirmUpdateView_FW"},
4707 {sig_match_screenunlock,"ScreenUnlock",         "screenlock_helper",    0,SIG_DRY_MAX(55)},
4708 {sig_match_near_str,"ScreenUnlock",             "PB._ErrorRef",         SIG_NEAR_AFTER(8,2)|SIG_NEAR_JMP_SUB,SIG_DRY_RANGE(57,58)},
4709 {sig_match_near_str,"ScreenUnlock",             "PB._ErrorRef:SI",      SIG_NEAR_AFTER(8,2)|SIG_NEAR_JMP_SUB,SIG_DRY_MIN(59)},
4710 {sig_match_log_camera_event,"LogCameraEvent",   "task_StartupImage",},
4711 {sig_match_physw_misc, "physw_misc",            "task_PhySw"},
4712 {sig_match_kbd_read_keys, "kbd_read_keys",      "kbd_p1_f"},
4713 {sig_match_get_kbd_state, "GetKbdState",        "kbd_read_keys"},
4714 {sig_match_create_jumptable, "CreateJumptable", "InitializeAdjustmentSystem_FW"},
4715 {sig_match_take_semaphore_strict, "TakeSemaphoreStrictly","Fopen_Fut"},
4716 {sig_match_get_semaphore_value,"GetSemaphoreValue","\tRaw[%i]"},
4717 {sig_match_stat,    "stat",                     "A/uartr.req"},
4718 {sig_match_open,    "open",                     "Open_FW",              0,              SIG_DRY_MAX(57)},
4719 {sig_match_open_gt_57,"open",                   "Open_FW",              0,              SIG_DRY_MIN(58)},
4720 // match close for dryos 58 and later
4721 {sig_match_close_gt_57,"close",                 "Close_FW",             0,              SIG_DRY_MIN(58)},
4722 {sig_match_umalloc, "AllocateUncacheableMemory","Fopen_Fut_FW"},
4723 {sig_match_ufree,   "FreeUncacheableMemory",    "Fclose_Fut_FW"},
4724 {sig_match_cam_uncached_bit,"CAM_UNCACHED_BIT", "FreeUncacheableMemory"},
4725 {sig_match_deletefile_fut,"DeleteFile_Fut",     "Get Err TempPath"},
4726 {sig_match_near_str,"createsemaphore_low",      "intr_sem",             SIG_NEAR_AFTER(3,1)},
4727 // probably only matched on cameras with wifi code in firmware, but catches veneers
4728 {sig_match_near_str,"takesemaphore_low",        "sem_test_callback",    SIG_NEAR_AFTER(12,2)},
4729 // not using Strictly, to pick up veneers
4730 {sig_match_near_str,"AcquireRecursiveLock",     "not executed\n",SIG_NEAR_BEFORE(20,3)},
4731 {sig_match_near_str,"CreateCountingSemaphoreStrictly","DvlpSeqTask",    SIG_NEAR_BEFORE(18,3)},
4732 {sig_match_near_str,"CreateMessageQueue",       "CreateMessageQueue:%ld",SIG_NEAR_BEFORE(7,1)},
4733 {sig_match_near_str,"CreateEventFlag",          "CreateEventFlag:%ld",  SIG_NEAR_BEFORE(7,1),SIG_DRY_MAX(57)},
4734 {sig_match_near_str,"CreateEventFlag",          "CreateEventFlag:%ld",  SIG_NEAR_BEFORE(9,1),SIG_DRY_MIN(58)},
4735 {sig_match_near_str,"CreateRecursiveLock",      "WdtInt",               SIG_NEAR_BEFORE(9,1)},
4736 {sig_match_near_str,"CreateRecursiveLockStrictly","LoadedScript",       SIG_NEAR_AFTER(6,2)},
4737 {sig_match_near_str,"DeleteMessageQueue",       "DeleteMessageQueue(%d) is FAILURE",SIG_NEAR_BEFORE(10,1)},
4738 {sig_match_near_str,"DeleteEventFlag",          "DeleteEventFlag(%d) is FAILURE",SIG_NEAR_BEFORE(10,1)},
4739 {sig_match_near_str,"ReceiveMessageQueue",      "ReceiveMessageQue:%d", SIG_NEAR_BEFORE(9,1)},
4740 {sig_match_near_str,"RegisterInterruptHandler", "WdtInt",               SIG_NEAR_AFTER(3,1)},
4741 {sig_match_near_str,"TryPostMessageQueue",      "TryPostMessageQueue(%d)\n",SIG_NEAR_BEFORE(9,1),SIG_DRY_MAX(52)},
4742 // different string on cams newer than sx280
4743 {sig_match_near_str,"TryPostMessageQueue",      "[CWS]TryPostMessageQueue(%d) Failed\n",SIG_NEAR_BEFORE(9,1)},
4744 {sig_match_near_str,"TryTakeSemaphore",         "FileScheduleTask",     SIG_NEAR_AFTER(10,2),SIG_DRY_MAX(58)},
4745 {sig_match_try_take_sem_dry_gt_58,"TryTakeSemaphore","task_ImageStoreTask",0,SIG_DRY_MIN(59)},
4746 // pick up takesemaphore_low from TryTakeSemaphore in case not matched by earlier rule
4747 {sig_match_named,   "takesemaphore_low",        "TryTakeSemaphore",     SIG_NAMED_SUB},
4748 {sig_match_near_str,"WaitForAllEventFlag",      "Error WaitEvent PREPARE_TESTREC_EXECUTED.", SIG_NEAR_BEFORE(5,1)},
4749 {sig_match_near_str,"WaitForAnyEventFlagStrictly","_imageSensorTask",   SIG_NEAR_AFTER(10,2)},
4750 {sig_match_wait_all_eventflag_strict,"WaitForAllEventFlagStrictly","EF.StartInternalMainFlash_FW"},
4751 {sig_match_near_str,"DeleteSemaphore",          "DeleteSemaphore passed",SIG_NEAR_BEFORE(3,1)},
4752 {sig_match_get_num_posted_messages,"GetNumberOfPostedMessages","task_CtgTotalTask"},
4753 {sig_match_near_str,"LocalTime",                "%Y-%m-%dT%H:%M:%S",    SIG_NEAR_BEFORE(5,1),SIG_DRY_MAX(58)},
4754 {sig_match_near_str,"LocalTime",                "%Y.%m.%d %H:%M:%S",    SIG_NEAR_BEFORE(5,1)},
4755 {sig_match_near_str,"strftime",                 "%Y/%m/%d %H:%M:%S",    SIG_NEAR_AFTER(3,1)},
4756 {sig_match_near_str,"OpenFastDir",              "OpenFastDir_ERROR\n",  SIG_NEAR_BEFORE(5,1)},
4757 {sig_match_readfastdir,"ReadFastDir",           "ReadFast_ERROR\n",     SIG_NEAR_BEFORE(24,1)},
4758 {sig_match_near_str,"PT_PlaySound",             "BufAccBeep",           SIG_NEAR_AFTER(7,2)|SIG_NEAR_JMP_SUB,SIG_DRY_MAX(58)},
4759 // for dry 59+ the above match finds function that takes different params on some cameras (d7?)
4760 {sig_match_near_str,"PT_PlaySound",             "PB._PMenuCBR",         SIG_NEAR_BEFORE(7,3),SIG_DRY_MIN(59)},
4761 {sig_match_closedir,"closedir",                 "ReadFast_ERROR\n",     SIG_NEAR_AFTER(1,1)},
4762 {sig_match_strrchr,"strrchr",                   "ReadFast_ERROR\n",     SIG_NEAR_AFTER(9,2)},
4763 {sig_match_strrchr,"strrchr",                   "ReadFast_ERROR\n",     SIG_NEAR_BEFORE(18,4)},
4764 {sig_match_time,    "time",                     "<UseAreaSize> DataWidth : %d , DataHeight : %d\r\n",},
4765 {sig_match_near_str,"strcat",                   "String can't be displayed; no more space in buffer",SIG_NEAR_AFTER(5,2)},
4766 {sig_match_near_str,"strchr",                   "-._~",SIG_NEAR_AFTER(4,1)},
4767 {sig_match_strncpy, "strncpy",                  "UnRegisterEventProcedure",},
4768 {sig_match_strncmp, "strncmp",                  "EXFAT   ",},
4769 {sig_match_strtolx, "strtolx",                  "CheckSumAll_FW",},
4770 {sig_match_near_str,"strtol",                   "prio <task ID> <priority>\n",SIG_NEAR_AFTER(7,1)},
4771 {sig_match_exec_evp,"ExecuteEventProcedure",    "Can not Execute "},
4772 {sig_match_fgets_fut,"Fgets_Fut",               "CheckSumAll_FW",},
4773 {sig_match_log,     "_log",                     "_log10",},
4774 {sig_match_pow_dry_52,"_pow",                   "GetDefectTvAdj_FW",    0,                  SIG_DRY_MAX(52)},
4775 {sig_match_pow_dry_gt_52,"_pow",                "GetDefectTvAdj_FW",    0,                  SIG_DRY_MIN(53)},
4776 {sig_match_sqrt,    "_sqrt",                    "CalcSqrt",},
4777 {sig_match_named,   "get_fstype",               "OpenFastDir",          SIG_NAMED_NTH(2,SUB)},
4778 {sig_match_near_str,"GetMemInfo",               " -- refusing to print malloc information.\n",SIG_NEAR_AFTER(7,2)},
4779 {sig_match_get_drive_cluster_size,"GetDrive_ClusterSize","OpLog.WriteToSD_FW",},
4780 {sig_match_mktime_ext,"mktime_ext",             "%04d%02d%02dT%02d%02d%02d.%01d",},
4781 {sig_match_near_str,"PB2Rec",                   "AC:ActionP2R Fail",     SIG_NEAR_BEFORE(6,1)},
4782 {sig_match_rec2pb,  "Rec2PB",                   "_EnrySRec",},
4783 //{sig_match_named,   "GetParameterData",         "PTM_RestoreUIProperty_FW",          SIG_NAMED_NTH(3,JMP_SUB)},
4784 {sig_match_get_parameter_data,"GetParameterData","PTM_RestoreUIProperty_FW",},
4785 {sig_match_prepdir_1,"PrepareDirectory_1",      "<OpenFileWithDir> PrepareDirectory NG\r\n",SIG_NEAR_BEFORE(7,1)},
4786 {sig_match_prepdir_x,"PrepareDirectory_x",      "PrepareDirectory_1",},
4787 {sig_match_prepdir_0,"PrepareDirectory_0",      "PrepareDirectory_1",},
4788 {sig_match_mkdir,   "MakeDirectory_Fut",        "PrepareDirectory_x",},
4789 // moved to sig_rules_initial to allow auto-detecting handlers
4790 //{sig_match_add_ptp_handler,"add_ptp_handler",   "PTPtoFAPI_EventProcTask_Try",},
4791 {sig_match_qsort,   "qsort",                    "task_MetaCtg",},
4792 {sig_match_deletedirectory_fut,"DeleteDirectory_Fut","RedEyeController.c",},
4793 {sig_match_set_control_event,"set_control_event","LogicalEvent:0x%04x:adr:%p,Para:%ld",},
4794 // newer cams use %08x
4795 {sig_match_set_control_event,"set_control_event","LogicalEvent:0x%08x:adr:%p,Para:%ld",},
4796 {sig_match_displaybusyonscreen_52,"displaybusyonscreen","_PBBusyScrn",  0,                  SIG_DRY_MAX(52)},
4797 {sig_match_undisplaybusyonscreen_52,"undisplaybusyonscreen","_PBBusyScrn",0,                SIG_DRY_MAX(52)},
4798 {sig_match_near_str,"srand",                    "Canon Degital Camera"/*sic*/,SIG_NEAR_AFTER(14,4)|SIG_NEAR_INDIRECT},
4799 {sig_match_near_str,"rand",                     "Canon Degital Camera"/*sic*/,SIG_NEAR_AFTER(15,5)|SIG_NEAR_INDIRECT},
4800 {sig_match_set_hp_timer_after_now,"SetHPTimerAfterNow","MechaNC.c",},
4801 {sig_match_levent_table,"levent_table",         "ShowLogicalEventName_FW",},
4802 {sig_match_flash_param_table,"FlashParamsTable","GetParameterData",},
4803 {sig_match_named,   "get_playrec_mode",         "task_SsStartupTask",   SIG_NAMED_SUB},
4804 {sig_match_var_struct_get,"playrec_mode",       "get_playrec_mode",},
4805 {sig_match_jpeg_count_str,"jpeg_count_str",     "9999",},
4806 {sig_match_physw_event_table,"physw_event_table","kbd_read_keys_r2",},
4807 {sig_match_uiprop_count,"uiprop_count",         "PTM_SetCurrentItem_FW",},
4808 {sig_match_get_canon_mode_list,"get_canon_mode_list","AC:PTM_Init",},
4809 {sig_match_rom_ptr_get,"canon_mode_list",       "get_canon_mode_list",},
4810 {sig_match_zoom_busy,"zoom_busy",               "ResetZoomLens_FW",},
4811 {sig_match_focus_busy,"focus_busy",             "MoveFocusLensToTerminate_FW",},
4812 {sig_match_aram_size,"ARAM_HEAP_SIZE",          "AdditionAgentRAM_FW",  0,                  SIG_DRY_MAX(58)},
4813 {sig_match_aram_size_gt58,"ARAM_HEAP_SIZE",     "AdditionAgentRAM_FW",  0,                  SIG_DRY_MIN(59)},
4814 {sig_match_aram_start,"ARAM_HEAP_START",        "AdditionAgentRAM_FW",},
4815 {sig_match_aram_start2,"ARAM_HEAP_START",       "AdditionAgentRAM_FW",},
4816 {sig_match__nrflag,"_nrflag",                   "NRTBL.SetDarkSubType_FW",},
4817 {sig_match_near_str,"transfer_src_overlay_helper","Window_EmergencyRefreshPhysicalScreen",SIG_NEAR_BEFORE(6,1)},
4818 {sig_match_transfer_src_overlay,"transfer_src_overlay","transfer_src_overlay_helper",},
4819 {sig_match_named,"GraphicSystemCoreFinish_helper","transfer_src_overlay",SIG_NAMED_NTH(3,SUB),SIG_DRY_MAX(52)},
4820 {sig_match_named,"GraphicSystemCoreFinish_helper","transfer_src_overlay",SIG_NAMED_NTH(4,SUB),SIG_DRY_RANGE(53,57)},// VTMReduuce fails on M10
4821 {sig_match_named,"GraphicSystemCoreFinish_helper","transfer_src_overlay",SIG_NAMED_NTH(5,SUB),SIG_DRY_MIN(58)},// additional call
4822 //{sig_match_near_str,"GraphicSystemCoreFinish_helper","VTMReduuce"/*sic*/,SIG_NEAR_BEFORE(6,1),SIG_DRY_RANGE(58,58)}, 
4823 //{sig_match_near_str,"GraphicSystemCoreFinish_helper","VTMReduce",SIG_NEAR_BEFORE(6,1),SIG_DRY_MIN(59)},  // canon fixed the typo, works on sx730 but others have diff code
4824 {sig_match_named,"GraphicSystemCoreFinish","GraphicSystemCoreFinish_helper",SIG_NAMED_SUB},
4825 {sig_match_named,"mzrm_createmsg","GraphicSystemCoreFinish",SIG_NAMED_SUB},
4826 {sig_match_named_last,"mzrm_sendmsg","GraphicSystemCoreFinish",SIG_NAMED_LAST_RANGE(10,16)},
4827 {sig_match_zicokick_52,"zicokick_start",        "ZicoKick Start\n",0,SIG_DRY_MAX(52)},
4828 {sig_match_zicokick_gt52,"zicokick_start",      "ZicoKick Start\n",0,SIG_DRY_MIN(53)},
4829 {sig_match_zicokick_copy,"zicokick_copy",       "zicokick_start"},
4830 {sig_match_zicokick_values,"zicokick_values",   "zicokick_start"},
4831 {sig_match_init_ex_drivers,"init_ex_drivers",   "task_Startup"},
4832 {sig_match_omar_init,"omar_init",               "init_ex_drivers"},
4833 {sig_match_enable_hdmi_power,"EnableHDMIPower", "HecHdmiCecPhysicalCheckForScript_FW"},
4834 {sig_match_disable_hdmi_power,"DisableHDMIPower","HecHdmiCecPhysicalCheckForScript_FW"},
4835 {sig_match_get_nd_value,"get_nd_value",         "PutInNdFilter",},
4836 {sig_match_get_current_exp,"get_current_exp","ShowCurrentExp_FW",},
4837 {sig_match_get_current_nd_value,"get_current_nd_value","get_current_exp",},
4838 {sig_match_imager_active_callback,"imager_active_callback","ImagerActivate",},
4839 {sig_match_imager_active,"imager_active","imager_active_callback",},
4840 {sig_match_get_dial_hw_position,"get_dial_hw_position","kbd_p1_f",},
4841 {sig_match_near_str,"get_displaytype","DisplayType : %d\r\n",SIG_NEAR_BEFORE(5,1)},
4842 {sig_match_prop_string,"PROPCASE_AFSTEP", "\n\rError : GetAFStepResult",SIG_NEAR_BEFORE(7,1)},
4843 {sig_match_prop_string,"PROPCASE_FOCUS_STATE", "\n\rError : GetAFResult",SIG_NEAR_BEFORE(7,1)},
4844 {sig_match_prop_string,"PROPCASE_AV", "\n\rError : GetAvResult",SIG_NEAR_BEFORE(7,1)},
4845 {sig_match_prop_string,"PROPCASE_BV", "\n\rError : GetBvResult",SIG_NEAR_BEFORE(7,1)},
4846 {sig_match_prop_string,"PROPCASE_DELTA_DIGITALGAIN", "\n\rError : GetDeltaDigitalResult",SIG_NEAR_BEFORE(7,1)},
4847 {sig_match_prop_string,"PROPCASE_DELTA_SV", "\n\rError : GetDeltaGainResult",SIG_NEAR_BEFORE(7,1)},
4848 {sig_match_prop_string,"PROPCASE_DELTA_ND", "\n\rError : GetDeltaNdResult",SIG_NEAR_BEFORE(7,1)},
4849 {sig_match_prop_string,"PROPCASE_EV_CORRECTION_2", "\n\rError : GetRealExposureCompensationResult",SIG_NEAR_BEFORE(7,1)},
4850 {sig_match_prop_string,"PROPCASE_ORIENTATION_SENSOR", "\n\rError : GetRotationAngleResult",SIG_NEAR_BEFORE(7,1)},
4851 {sig_match_prop_string,"PROPCASE_SV_MARKET", "\n\rError : GetSvResult",SIG_NEAR_BEFORE(7,1)},
4852 {sig_match_prop_string,"PROPCASE_SVFIX", "\n\rError : GetSvFixResult",SIG_NEAR_BEFORE(7,1)},
4853 {sig_match_prop_string,"PROPCASE_TV", "\n\rError : GetTvResult",SIG_NEAR_BEFORE(7,1)},
4854 {sig_match_prop_string,"PROPCASE_HSCAPTURE", "GetPropertyFromCase Error [HSCapture]",SIG_NEAR_BEFORE(7,1)},
4855 {sig_match_prop_string,"PROPCASE_FLASH_FIRE", "FlashDecision",SIG_NEAR_BEFORE(7,1)},
4856 {sig_match_prop_string,"PROPCASE_FELOCK", "GetPropertyFromCurrentCase Error [FELock]",SIG_NEAR_BEFORE(7,1)},
4857 {sig_match_prop_string,"PROPCASE_FLASH_ADJUST_MODE", "GetPropertyFromCurrentCase Error [FlashAdjust]",SIG_NEAR_BEFORE(7,1)},
4858 {sig_match_exmem_vars,"exmem_types_table", "ExMem.View_FW"},
4859 {sig_match_av_over_sem,"av_override_semaphore", "MoveIrisWithAv_FW"},
4860 {sig_match_canon_menu_active,"canon_menu_active", "StartRecModeMenu_FW"},
4861 {sig_match_file_counter_init,"file_counter_var_init","task_InitFileModules",},
4862 {sig_match_file_counter_var,"file_counter_var","file_counter_var_init",},
4863 {sig_match_var_struct_get,"displaytype",       "get_displaytype",},
4864 {sig_match_palette_vars,"palette_control",     "transfer_src_overlay_helper",0,SIG_DRY_MAX(58)},// Dry59 code is different
4865 {NULL},
4866 };
4867 
4868 void run_sig_rules(firmware *fw, sig_rule_t *sig_rules)
4869 {
4870     sig_rule_t *rule=sig_rules;
4871     // for convenience, pass an iter_state to match fns so they don't have to manage
4872     iter_state_t *is=disasm_iter_new(fw,0);
4873     while(rule->match_fn) {
4874         if((rule->dryos_min && fw->dryos_ver < rule->dryos_min)
4875             || (rule->dryos_max && fw->dryos_ver > rule->dryos_max)) {
4876             rule++;
4877             continue;
4878         }
4879 //        printf("rule: %s ",rule->name);
4880         //int r=rule->match_fn(fw,is,rule);
4881         rule->match_fn(fw,is,rule);
4882 //        printf("%d\n",r);
4883         rule++;
4884     }
4885     disasm_iter_free(is);
4886 }
4887 
4888 void add_event_proc(firmware *fw, char *name, uint32_t adr)
4889 {
4890     // TODO - no ARM eventprocs seen so far, warn
4891     if(!ADR_IS_THUMB(adr)) {
4892         printf("add_event_proc: %s is ARM 0x%08x\n",name,adr);
4893     }
4894     // attempt to disassemble target
4895     if(!fw_disasm_iter_single(fw,adr)) {
4896         printf("add_event_proc: %s disassembly failed at 0x%08x\n",name,adr);
4897         return;
4898     }
4899     // handle functions that immediately jump
4900     // only one level of jump for now, doesn't check for conditionals, but first insn shouldn't be conditional
4901     //uint32_t b_adr=B_target(fw,fw->is->insn);
4902     uint32_t b_adr=get_direct_jump_target(fw,fw->is);
4903     if(b_adr) {
4904         char *buf=malloc(strlen(name)+6);
4905         sprintf(buf,"j_%s_FW",name);
4906         add_func_name(fw,buf,adr,NULL); // this is the orignal named address
4907 //        adr=b_adr | fw->is->thumb; // thumb bit from iter state
4908         adr=b_adr; // thumb bit already handled by get_direct...
4909     }
4910     add_func_name(fw,name,adr,"_FW");
4911 }
4912 
4913 // process a call to an 2 arg event proc registration function
4914 int process_reg_eventproc_call(firmware *fw, iter_state_t *is, __attribute__ ((unused))uint32_t unused) {
4915     uint32_t regs[4];
4916     // get r0, r1, backtracking up to 4 instructions
4917     if((get_call_const_args(fw,is,4,regs)&3)==3) {
4918         // TODO follow ptr to verify code, pick up underlying functions
4919         if(isASCIIstring(fw,regs[0])) {
4920             char *nm=(char *)adr2ptr(fw,regs[0]);
4921             add_event_proc(fw,nm,regs[1]);
4922             //add_func_name(fw,nm,regs[1],NULL);
4923             //printf("eventproc found %s 0x%08x at 0x%"PRIx64"\n",nm,regs[1],is->insn->address);
4924         } else {
4925             printf("eventproc name not string at 0x%"PRIx64"\n",is->insn->address);
4926         }
4927     } else {
4928         // check for special case: one of the 2 arg eventprocs is used in loop to register a table
4929 
4930         // using the existing 'is' iterator
4931         // first, address is backed up
4932         uint64_t adr = is->insn->address;
4933         uint32_t adr_thumb = is->thumb;
4934         uint32_t tbla = 0;
4935         int ar = -1;
4936         int found = 0;
4937         // go back a 10 instructions
4938         disasm_iter_init(fw,is,adr_hist_get(&is->ah,10));
4939         // search for ldr reg, =address where address is higher in ROM (supposed to be the eventproc table)
4940         while(1) {
4941             if (!disasm_iter(fw,is)) break;
4942             if (is->insn->address >= adr) break;
4943             if (is->insn->id == ARM_INS_LDR && is->insn->detail->arm.operands[1].type == ARM_OP_MEM) {
4944                 uint32_t u = LDR_PC2val(fw,is->insn);
4945                 if ((u<fw->base+fw->size8) && (u>adr) && (!isASCIIstring(fw,u))) {
4946                     ar = is->insn->detail->arm.operands[0].reg;
4947                     tbla = u;
4948                     break;
4949                 }
4950             }
4951         }
4952         // search for found register appearing later in an add instruction
4953         while(ar >= 0) {
4954             if (!disasm_iter(fw,is)) break;
4955             if (is->insn->address >= adr) break;
4956             if (is->insn->id == ARM_INS_ADD && is->insn->detail->arm.operands[1].reg == ar) {
4957                 found = 1;
4958                 //printf("found loop eventproc table at 0x%"PRIx64"\n",is->insn->address);
4959                 break;
4960             }
4961         }
4962         if (found) {
4963             // following is taken from process_eventproc_table_call
4964             uint32_t *p=(uint32_t*)adr2ptr_with_data(fw,tbla);
4965             if(p) {
4966                 while(*p) {
4967                     uint32_t nm_adr=*p;
4968                     // NULL name = end of table
4969                     if (!nm_adr) break;
4970                     if(!isASCIIstring(fw,nm_adr)) {
4971                         printf("eventproc name not string tbl2 0x%08x 0x%08x\n",tbla,nm_adr);
4972                         break;
4973                     }
4974                     char *nm=(char *)adr2ptr(fw,nm_adr);
4975                     p++;
4976                     uint32_t fn=*p;
4977                     p++;
4978                     add_event_proc(fw,nm,fn);
4979                 }
4980             } else {
4981                 printf("eventproc tbl2 not table 0x%08x\n",tbla);
4982             }
4983         }
4984         else {
4985             printf("failed to get export/register eventproc args at 0x%"PRIx64"\n",adr);
4986         }
4987         // restore address in 'is' to avoid infinite loop
4988         disasm_iter_init(fw,is,adr | adr_thumb);
4989         disasm_iter(fw,is);
4990     }
4991     return 0; // always keep looking
4992 }
4993 
4994 // process a call to event proc table registration
4995 int process_eventproc_table_call(firmware *fw, iter_state_t *is, __attribute__ ((unused))uint32_t unused) {
4996     uint32_t regs[4];
4997     int foundr0 = 0;
4998     // get r0, backtracking up to 4 instructions
4999     foundr0 = get_call_const_args(fw,is,4,regs) & 1;
5000     if (!foundr0) {
5001         // case 1: table memcpy'd onto stack
5002         uint32_t ca = iter_state_adr(is);
5003         uint32_t sa = adr_hist_get(&is->ah,2);
5004         uint32_t ta = adr_hist_get(&is->ah,8);
5005         disasm_iter_set(fw,is,ta);
5006         int n = 0;
5007         while(++n<=(8-2))
5008         {
5009             disasm_iter(fw,is);
5010         }
5011         fw_disasm_iter_single(fw,sa);
5012         uint32_t adr1 = get_saved_sig_val("j_dry_memcpy");
5013         uint32_t adr2 = get_branch_call_insn_target(fw,fw->is);
5014         if (fw->is->insn->id == ARM_INS_BLX && adr1 == adr2) {
5015             foundr0 = get_call_const_args(fw,is,8-2,regs) & 2;
5016             if (foundr0) {
5017                 regs[0] = regs[1];
5018                 // printf("eventproc table case1 0x%x found table 0x%x\n",ca,regs[1]);
5019             }
5020         }
5021         // restore iter address
5022         disasm_iter_init(fw,is,ca);
5023         disasm_iter(fw,is);
5024     }
5025     if(foundr0) {
5026         // include tables in RAM data
5027         uint32_t *p=(uint32_t*)adr2ptr_with_data(fw,regs[0]);
5028         //printf("found eventproc table 0x%08x\n",regs[0]);
5029         // if it was a valid address
5030         if(p) {
5031             while(*p) {
5032                 uint32_t nm_adr=*p;
5033                 if(!isASCIIstring(fw,nm_adr)) {
5034                     printf("eventproc name not string tbl 0x%08x 0x%08x\n",regs[0],nm_adr);
5035                     break;
5036                 }
5037                 char *nm=(char *)adr2ptr(fw,nm_adr);
5038                 p++;
5039                 uint32_t fn=*p;
5040                 p++;
5041                 //printf("found %s 0x%08x\n",nm,fn);
5042                 add_event_proc(fw,nm,fn);
5043                 //add_func_name(fw,nm,fn,NULL);
5044             }
5045         } else {
5046             printf("failed to get *EventProcTable arg 0x%08x at 0x%"PRIx64"\n",regs[0],is->insn->address);
5047         }
5048     } else {
5049         printf("failed to get *EventProcTable r0 at 0x%"PRIx64"\n",is->insn->address);
5050     }
5051     return 0;
5052 }
5053 
5054 int process_createtask_call(firmware *fw, iter_state_t *is, __attribute__ ((unused))uint32_t unused) {
5055     //printf("CreateTask call at %"PRIx64"\n",is->insn->address);
5056     uint32_t regs[4];
5057     // get r0 (name) and r3 (entry), backtracking up to 10 instructions
5058     if((get_call_const_args(fw,is,10,regs)&9)==9) {
5059         if(isASCIIstring(fw,regs[0])) {
5060             // TODO
5061             char *buf=malloc(64);
5062             char *nm=(char *)adr2ptr(fw,regs[0]);
5063             sprintf(buf,"task_%s",nm);
5064             //printf("found %s 0x%08x at 0x%"PRIx64"\n",buf,regs[3],is->insn->address);
5065             add_func_name(fw,buf,regs[3],NULL);
5066         } else {
5067             printf("task name name not string at 0x%"PRIx64"\n",is->insn->address);
5068         }
5069     } else {
5070         printf("failed to get CreateTask args at 0x%"PRIx64"\n",is->insn->address);
5071     }
5072     return 0;
5073 }
5074 
5075 int save_ptp_handler_func(firmware *fw,uint32_t op,uint32_t handler) {
5076     if((op >= 0x9000 && op < 0x10000) || (op >= 0x1000 && op < 0x2000)) {
5077         char *buf=malloc(64);
5078         const char *nm=get_ptp_op_name(op);
5079         if(nm) {
5080             sprintf(buf,"handle_%s",nm);
5081         } else {
5082             sprintf(buf,"handle_PTP_OC_0x%04x",op);
5083         }
5084         // TODO Canon sometimes uses the same handler for multiple opcodes
5085         add_func_name(fw,buf,handler,NULL);
5086     } else {
5087         return 0;
5088     }
5089     return 1;
5090 }
5091 int process_add_ptp_handler_call(firmware *fw, iter_state_t *is, __attribute__ ((unused))uint32_t unused) {
5092     uint32_t regs[4];
5093     // get r0 (opcode) and r1 (handler), backtracking up to 8 instructions
5094     if((get_call_const_args(fw,is,8,regs)&3)==3) {
5095         //uint32_t op=regs[0];
5096         if(!save_ptp_handler_func(fw,regs[0],regs[1])) {
5097             printf("add_ptp_handler op 0x%08x out of range 0x%"PRIx64"\n",regs[0],is->insn->address);
5098         }
5099         return 0;
5100     } else {
5101         // if above failed, check for opcode table
5102         arm_reg ptr_reg = ARM_REG_INVALID;
5103         int i;
5104         // backtrack until we get to ldrh r0, ...
5105         for(i=1; i<6; i++) {
5106             fw_disasm_iter_single(fw,adr_hist_get(&is->ah,i));
5107             cs_insn *insn=fw->is->insn;
5108             if(insn->id != ARM_INS_LDRH) {
5109                 continue;
5110             }
5111             if(insn->detail->arm.operands[0].reg != ARM_REG_R0
5112                 || insn->detail->arm.operands[1].mem.base == ARM_REG_PC
5113                 // shift isn't set correctly under capstone 3, not required for current cams
5114                 /*|| insn->detail->arm.operands[1].shift.value != 3*/) { 
5115                 continue;
5116             }
5117             ptr_reg = insn->detail->arm.operands[1].mem.base;
5118             //printf("add_ptp_handler ptr_reg %d at 0x%"PRIx64"\n",ptr_reg,insn->address);
5119             break;
5120         }
5121         // didn't find args or anything that looks like table load
5122         if(ptr_reg == ARM_REG_INVALID) {
5123             printf("failed to get add_ptp_handler args at 0x%"PRIx64"\n",is->insn->address);
5124             return 0;
5125         }
5126         uint32_t op_table=0;
5127         // backtrack looking for LDR into ptr_reg
5128         // starting from previous i
5129         for(; i<20; i++) {
5130             fw_disasm_iter_single(fw,adr_hist_get(&is->ah,i));
5131             cs_insn *insn=fw->is->insn;
5132             if(!isLDR_PC(insn)) {
5133                 continue;
5134             }
5135             if((arm_reg)insn->detail->arm.operands[0].reg != ptr_reg) {
5136                 continue;
5137             }
5138             // printf("add_ptp_handler LDR PC 0x%08x at 0x%"PRIx64"\n",LDR_PC2val(fw,insn),insn->address);
5139             uint32_t adr=LDR_PC2val(fw,insn);
5140             // check loaded address points to expected value (OC GetStorageIDs)
5141             if(fw_u32(fw,adr) == 0x1004) {
5142                 op_table=adr;
5143             }
5144             break;
5145         }
5146         if(!op_table) {
5147             printf("failed to get ptp handler table adr at 0x%"PRIx64"\n",is->insn->address);
5148             return 0;
5149         }
5150         // TODO canon firmware has count in loop that calls add_ptp_handler,
5151         // but for simplicity just checking for valid opcode with hardcoded max
5152         for(i=0; i<64; i++) {
5153             uint32_t op=fw_u32(fw,op_table+i*8);
5154             uint32_t handler=fw_u32(fw,op_table+i*8+4);
5155             // fails on op out of range
5156             if(!save_ptp_handler_func(fw,op,handler)) {
5157                 break;
5158             }
5159         }
5160         return 0;
5161     }
5162 }
5163 
5164 int add_generic_func_match(search_calls_multi_data_t *match_fns,
5165                             int *match_fn_count,
5166                             int max_funcs,
5167                             search_calls_multi_fn fn,
5168                             uint32_t adr)
5169 {
5170     if(*match_fn_count >= max_funcs-1) {
5171         printf("add_generic_func_match: ERROR max_funcs %d reached\n",max_funcs);
5172         match_fns[max_funcs-1].adr=0;
5173         match_fns[max_funcs-1].fn=NULL;
5174         return 0;
5175     }
5176     match_fns[*match_fn_count].adr=adr;
5177     match_fns[*match_fn_count].fn=fn;
5178     (*match_fn_count)++;
5179     match_fns[*match_fn_count].adr=0;
5180     match_fns[*match_fn_count].fn=NULL;
5181     return 1;
5182 }
5183 #define MAX_GENERIC_FUNCS 16
5184 void add_generic_sig_match(search_calls_multi_data_t *match_fns,
5185                                 int *match_fn_count,
5186                                 search_calls_multi_fn fn,
5187                                 const char *name)
5188 {
5189     uint32_t adr=get_saved_sig_val(name);
5190     if(!adr) {
5191         printf("add_generic_sig_match: missing %s\n",name);
5192         return;
5193     }
5194     add_generic_func_match(match_fns,match_fn_count,MAX_GENERIC_FUNCS,fn,adr);
5195     char veneer[128];
5196     sprintf(veneer,"j_%s",name);
5197     adr=get_saved_sig_val(veneer);
5198     if(adr) {
5199         add_generic_func_match(match_fns,match_fn_count,MAX_GENERIC_FUNCS,fn,adr);
5200     }
5201 }
5202 
5203 void find_exception_handlers(firmware *fw, iter_state_t *is)
5204 {
5205     uint32_t ex_vec = 0;
5206     // somewhat duplicated from to firmware_load_ng, but it's simple
5207     if (fw->arch_flags & FW_ARCH_FL_VMSA) {
5208         const insn_match_t match_mcr_vbar[]={
5209             // Vector Base Address Register MCR p15, 0, <Rt>, c12, c0, 0 - not present on PMSA
5210             {MATCH_INS(MCR, 6), {MATCH_OP_PIMM(15),MATCH_OP_IMM(0),MATCH_OP_REG_ANY,MATCH_OP_CIMM(12),MATCH_OP_CIMM(0),MATCH_OP_IMM(0)}},
5211             {ARM_INS_ENDING}
5212         };
5213         // reset to main fw start
5214         disasm_iter_init(fw, is, fw->base + fw->main_offs + 12 + fw->thumb_default);
5215         if(!insn_match_find_next(fw,is,4,match_mcr_vbar)) {
5216             return;
5217         }
5218         // back up one
5219         disasm_iter_init(fw, is, adr_hist_get(&is->ah,1));
5220         disasm_iter(fw, is);
5221         // expect ldr to be exception vector address
5222         ex_vec  = LDR_PC2val(fw,is->insn);
5223         if(!ex_vec || adr_get_range_type(fw,ex_vec) != ADR_RANGE_ROM) {
5224             return;
5225         }
5226     }
5227     // both d6 and d7 appear to have an ARM instruction in reset, and thumb in the remaining
5228     // which appears contrary to arm documentation (ARM DDI 0406C.c (ID051414) 
5229     // On digic 6, Reset appears to be an infinte loop, so must not be expected in any case
5230     disasm_iter_init(fw, is, ex_vec);
5231     disasm_iter(fw, is);
5232 
5233     char *names[]={
5234         "exception_handler_Reset",
5235         "exception_handler_UndefinedInstruction",
5236         "exception_handler_SupervisorCall",
5237         "exception_handler_PrefetchAbort",
5238         "exception_handler_DataAbort",
5239         "exception_handler_NotUsed",
5240         "exception_handler_IRQ",
5241         "exception_handler_FIQ",
5242     };
5243 
5244     uint32_t addr=LDR_PC2val(fw,is->insn);
5245     if(!addr && is->insn->id == ARM_INS_B) {
5246         addr=get_branch_call_insn_target(fw,is);
5247     }
5248     // addr may be 0 (= inf loop at reset vector) but stubs system won't recognize it anyway
5249     if(addr) {
5250         add_func_name(fw,names[0],addr,NULL);
5251     }
5252     disasm_iter_init(fw, is, ADR_SET_THUMB(ex_vec + 4));
5253     int i;
5254     for(i=1; i<8; i++) {
5255         disasm_iter(fw, is);
5256         // all seen so far use a thumb ldr.w pc,...
5257         // "NotUsed" is typically a NOP, which won't get picked up here, but would fall through to IRQ
5258         addr=LDR_PC2val(fw,is->insn);
5259         if(addr) {
5260             add_func_name(fw,names[i],addr,NULL);
5261         }
5262     }
5263 }
5264 
5265 /*
5266 collect as many calls as possible of functions identified by name, whether or not listed in funcs to find
5267 this does a full disassembly of the entire firmware, which is slow
5268 */
5269 void find_generic_funcs(firmware *fw) {
5270     search_calls_multi_data_t match_fns[MAX_GENERIC_FUNCS];
5271 
5272     int match_fn_count=0;
5273 
5274     add_generic_sig_match(match_fns,&match_fn_count,process_reg_eventproc_call,"ExportToEventProcedure_FW");
5275     add_generic_sig_match(match_fns,&match_fn_count,process_reg_eventproc_call,"RegisterEventProcedure_alt1");
5276     add_generic_sig_match(match_fns,&match_fn_count,process_reg_eventproc_call,"RegisterEventProcedure_alt2");
5277     add_generic_sig_match(match_fns,&match_fn_count,process_eventproc_table_call,"RegisterEventProcTable");
5278     add_generic_sig_match(match_fns,&match_fn_count,process_eventproc_table_call,"UnRegisterEventProcTable");
5279     add_generic_sig_match(match_fns,&match_fn_count,process_createtask_call,"CreateTaskStrictly");
5280     if(get_saved_sig_val("CreateTaskStrictly_alt")) {
5281         add_generic_sig_match(match_fns,&match_fn_count,process_createtask_call,"CreateTaskStrictly_alt");
5282     }
5283     add_generic_sig_match(match_fns,&match_fn_count,process_createtask_call,"CreateTask");
5284     if(get_saved_sig_val("CreateTask_alt")) {
5285         add_generic_sig_match(match_fns,&match_fn_count,process_createtask_call,"CreateTask_alt");
5286     }
5287     add_generic_sig_match(match_fns,&match_fn_count,process_eventproc_table_call,"RegisterEventProcTable_alt");
5288     add_generic_sig_match(match_fns,&match_fn_count,process_eventproc_table_call,"UnRegisterEventProcTable_alt");
5289     add_generic_sig_match(match_fns,&match_fn_count,process_add_ptp_handler_call,"add_ptp_handler");
5290 
5291     iter_state_t *is=disasm_iter_new(fw,0);
5292     disasm_iter_init(fw,is,fw->rom_code_search_min_adr | fw->thumb_default); // reset to start of fw
5293     fw_search_insn(fw,is,search_disasm_calls_multi,0,match_fns,0);
5294 
5295     // currently only finds SD1stInit task on a couple cams
5296     int i;
5297     for(i=0;i<fw->adr_range_count;i++) {
5298         if(fw->adr_ranges[i].type != ADR_RANGE_RAM_CODE) {
5299             continue;
5300         }
5301         disasm_iter_init(fw,is,fw->adr_ranges[i].start | fw->thumb_default); // reset to start of range
5302         fw_search_insn(fw,is,search_disasm_calls_multi,0,match_fns,0);
5303     }
5304 
5305     find_exception_handlers(fw,is);
5306 
5307     disasm_iter_free(is);
5308 }
5309 
5310 void find_ctypes(firmware *fw)
5311 {
5312     static unsigned char ctypes[] =
5313     {
5314         0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x60, 0x60, 0x60, 0x60, 0x60, 0x20, 0x20,
5315         0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
5316         0x48, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
5317         0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10,
5318         0x10, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 1, 1, 1, 1, 1, 1, 1, 1, 1,
5319         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0x10, 0x10, 0x10, 0x10, 0x10,
5320         0x10, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 2, 2, 2, 2, 2, 2, 2, 2, 2,
5321         2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0x10, 0x10, 0x10, 0x10, 0x20
5322     };
5323 
5324     uint32_t ctypes_matches[10];
5325     int match_count = find_bytes_all(fw,ctypes,sizeof(ctypes),fw->base,ctypes_matches,10);
5326     if(!match_count) {
5327         return;
5328     }
5329     if(match_count == 10) {
5330         fprintf(stderr,"WARNING found 10+ ctypes!\n");
5331     }
5332     int i;
5333     int match_i;
5334     uint32_t min_adr = 0xFFFFFFFF;
5335     for(i = 0; i< match_count; i++) {
5336         // ref should easily be in the first 1M
5337         uint32_t maxadr = (fw->rom_code_search_max_adr > fw->base + 0x400000)?fw->base + 0x100000:fw->rom_code_search_max_adr;
5338         uint32_t adr = find_u32_adr_range(fw,ctypes_matches[i],fw->rom_code_search_min_adr,maxadr);
5339         if(adr && adr < min_adr) {
5340             min_adr = adr;
5341             match_i = i;
5342         }
5343     }
5344     if(min_adr == 0xFFFFFFFF) {
5345         fprintf(stderr,"WARNING cytpes pointer not found, defaulting to first\n");
5346         match_i = 0;
5347     }
5348     save_misc_val("ctypes",ctypes_matches[match_i],0,min_adr); 
5349 }
5350 
5351 void print_misc_val_makefile(const char *name)
5352 {
5353     misc_val_t *mv=get_misc_val(name);
5354     if(!mv) {
5355         return;
5356     }
5357     // TODO legitimate 0 values might be possible, if so can add found bit
5358     if(!mv->val) {
5359         bprintf("// %s not found\n",name);
5360         return;
5361     }
5362     bprintf("//   %s = 0x%08x# ",name,mv->val);
5363     if(mv->offset) {
5364         bprintf(" (0x%x+0x%x)",mv->base,mv->offset);
5365     }
5366     if(mv->ref_adr) {
5367         bprintf(" Found @0x%08x",mv->ref_adr);
5368     }
5369     bprintf("\n");
5370 }
5371 
5372 
5373 void output_firmware_vals(firmware *fw)
5374 {
5375     bprintf("// Camera info:\n");
5376     bprintf("//   Main firmware start: 0x%08x\n",fw->base+fw->main_offs);
5377     if (fw->dryos_ver == 0)
5378     {
5379         bprintf("//   Can't find DRYOS version !!!\n\n");
5380     } else {
5381         bprintf("//   DRYOS R%d (%s) @ 0x%08x ref @ 0x%08x\n",
5382                     fw->dryos_ver,
5383                     fw->dryos_ver_str,
5384                     fw->dryos_ver_adr,
5385                     fw->dryos_ver_ref_adr);
5386     }
5387     if (fw->firmware_ver_str == 0)
5388     {
5389         bprintf("//   Can't find firmware version !!!\n\n");
5390     }
5391     else
5392     {
5393         char *c = strrchr(fw->firmware_ver_str,' ') + 1; // points after the last space char
5394         uint32_t j = ptr2adr(fw,(uint8_t *)fw->firmware_ver_str);
5395         uint32_t k = j + c - fw->firmware_ver_str;
5396         if ( (k>=j) && (k<j+32) )
5397         {
5398             bprintf("//   %s   // Found @ 0x%08x, \"%s\" @ 0x%08x\n",fw->firmware_ver_str,j,c,k);
5399         }
5400         else
5401         {
5402             // no space found in string (shouldn't happen)
5403             bprintf("//   %s   // Found @ 0x%08x, \"%s\" @ 0x%08x\n",fw->firmware_ver_str,j,fw->firmware_ver_str,j);
5404         }
5405     }
5406     if (fw->arch_flags & FW_ARCH_FL_VMSA) {
5407         bprintf("//   VMSA detected, probably digic >= 7\n");
5408     }
5409 
5410     bprintf("\n// Values for makefile.inc\n");
5411     bprintf("//   PLATFORMOSVER = %d\n",fw->dryos_ver);
5412     if (fw->arch_flags & FW_ARCH_FL_VMSA) {
5413         bprintf("//   DIGIC = 70\n");
5414     } else {
5415         // 6+ exists, but not for known powershot firmware cams
5416         bprintf("//   DIGIC = 60\n");
5417     }
5418 
5419     if (fw->memisostart) {
5420         bprintf("//   MEMISOSTART = 0x%x\n",fw->memisostart);
5421     } else {
5422         bprintf("//   MEMISOSTART not found !!!\n");
5423     }
5424     if (fw->data_init_start)
5425     {
5426         bprintf("//   MEMBASEADDR = 0x%x\n",fw->data_start);
5427     }
5428     print_misc_val_makefile("ARAM_HEAP_START");
5429     print_misc_val_makefile("ARAM_HEAP_SIZE");
5430 
5431     bprintf("\n// Detected address ranges:\n");
5432     int i;
5433     for(i=0; i<fw->adr_range_count; i++) {
5434         if(fw->adr_ranges[i].type == ADR_RANGE_ROM) {
5435             bprintf("// %-8s 0x%08x - 0x%08x (%7d bytes)\n",
5436                     adr_range_desc_str(&fw->adr_ranges[i]),
5437                     fw->adr_ranges[i].start,
5438                     fw->adr_ranges[i].start+fw->adr_ranges[i].bytes,
5439                     fw->adr_ranges[i].bytes);
5440         } else {
5441             bprintf("// %-8s 0x%08x - 0x%08x copied from 0x%08x (%7d bytes)\n",
5442                     adr_range_desc_str(&fw->adr_ranges[i]),
5443                     fw->adr_ranges[i].start,
5444                     fw->adr_ranges[i].start+fw->adr_ranges[i].bytes,
5445                     fw->adr_ranges[i].src_start,
5446                     fw->adr_ranges[i].bytes);
5447         }
5448     }
5449     misc_val_t *mv=get_misc_val("zicokick_values");
5450     if(mv->blobs) {
5451         bprintf("\n// Zico Xtensa blobs:\n");
5452         for(i=0;mv->blobs[i].type != MISC_BLOB_TYPE_NONE;i++) {
5453             bprintf("// zico_%d 0x%08x - 0x%08x copied from 0x%08x (%7d bytes)\n",
5454                     i,
5455                     mv->blobs[i].ram_adr,
5456                     mv->blobs[i].ram_adr+mv->blobs[i].size,
5457                     mv->blobs[i].rom_adr,
5458                     mv->blobs[i].size);
5459         }
5460 
5461     }
5462     mv=get_misc_val("omar_init_values");
5463     if(mv->blobs) {
5464         bprintf("\n// Omar ARM blobs:\n");
5465         for(i=0;mv->blobs[i].type != MISC_BLOB_TYPE_NONE;i++) {
5466             bprintf("// omar_%d 0x%08x - 0x%08x copied from 0x%08x (%7d bytes)\n",
5467                     i,
5468                     mv->blobs[i].ram_adr,
5469                     mv->blobs[i].ram_adr+mv->blobs[i].size,
5470                     mv->blobs[i].rom_adr,
5471                     mv->blobs[i].size);
5472         }
5473     }
5474     if(fw->dryos_ver_count) {
5475         bprintf("\n// Found DryOS versions:\n");
5476         for(i=0;i<(int)fw->dryos_ver_count;i++) {
5477             bprintf("// 0x%08x %s \"%s\"\n",
5478                 fw->dryos_ver_list[i], (fw->dryos_ver_list[i] == fw->dryos_ver_adr) ? "main ":"other",
5479                 (char *)adr2ptr(fw,fw->dryos_ver_list[i]));
5480         }
5481         add_blankline();
5482     }
5483     add_blankline();
5484 
5485     // check if CreateTask is in ROM, offer CreateTask_low if it's in RAM
5486     sig_entry_t * ct = find_saved_sig("hook_CreateTask");
5487     if(ct && adr_get_range_type(fw,ct->val) != ADR_RANGE_RAM_CODE) {
5488         bprintf("// CreateTask is not in RAM code\n");
5489         ct->flags |= UNUSED;
5490         sig_entry_t * ctl = find_saved_sig("CreateTask_low");
5491         if(ctl && adr_get_range_type(fw,ctl->val) == ADR_RANGE_RAM_CODE) {
5492             bprintf("// use hook_CreateTask_low instead\n");
5493             ctl->flags &= ~UNUSED;
5494             sig_entry_t * hctl = find_saved_sig("hook_CreateTask_low");
5495             hctl->flags &= ~UNUSED;
5496         }
5497         add_blankline();
5498     }
5499 }
5500 
5501 // print platform.h define, if not default value
5502 void print_platform_misc_val_undef(const char *name, uint32_t def)
5503 {
5504     misc_val_t *mv=get_misc_val(name);
5505     if(mv && mv->val && mv->val != def) {
5506         bprintf("//#undef  %s\n",name);
5507         bprintf("//#define %s  0x%08x // Found @0x%08x\n",name,mv->val,mv->ref_adr);
5508     }
5509 }
5510 
5511 void output_platform_vals(firmware *fw) {
5512     bprintf("// Values below go in 'platform_camera.h':\n");
5513     bprintf("//#define CAM_DRYOS         1\n");
5514     if (fw->dryos_ver >= 39)
5515         bprintf("//#define CAM_DRYOS_2_3_R39 1 // Defined for cameras with DryOS version R39 or higher\n");
5516     if (fw->dryos_ver >= 47)
5517         bprintf("//#define CAM_DRYOS_2_3_R47 1 // Defined for cameras with DryOS version R47 or higher\n");
5518     if (fw->dryos_ver >= 59)
5519         bprintf("//#define CAM_DRYOS_2_3_R59 1 // Defined for cameras with DryOS version R59 or higher\n");
5520 
5521     if(get_misc_val_value("CAM_IS_ILC")) {
5522         bprintf("//#define CAM_ILC 1 // Camera is interchangeable lens\n");
5523     }
5524 
5525 
5526     print_platform_misc_val_undef("CAM_UNCACHED_BIT",0x10000000);
5527 
5528     if(get_misc_val_value("CAM_HAS_ND_FILTER")) {
5529         bprintf("//#define CAM_HAS_ND_FILTER 1 // Camera has ND filter\n");
5530     } else {
5531         bprintf("//#undef CAM_HAS_ND_FILTER // Camera does not have an ND filter\n");
5532     }
5533     if(get_misc_val_value("CAM_HAS_IRIS_DIAPHRAGM")) {
5534         bprintf("// Camera has an iris (CAM_HAS_IRIS_DIAPHRAGM default)\n");
5535     } else {
5536         bprintf("//#undef CAM_HAS_IRIS_DIAPHRAGM // Camera does not have an iris\n");
5537     }
5538 
5539     add_blankline();
5540 }
5541 
5542 void output_propcases(firmware *fw) {
5543 
5544     uint32_t used=0;
5545     uint32_t hits[KNOWN_PROPSET_COUNT];
5546     const uint32_t ps_offset = 6;
5547     
5548     memset(hits, 0, KNOWN_PROPSET_COUNT*sizeof(uint32_t));
5549 
5550     bprintf("// Known propcases\n");
5551 
5552     int n = 0;
5553     while (knownprops[n].name) {
5554         used += knownprops[n].use>0?1:0;
5555         if (knownprops[n].id >= 0)
5556         {
5557             if (knownprops[n].use)
5558             {
5559                 if (knownprops[n].id == knownprops[n].id_ps6) hits[6-ps_offset] += 1;
5560                 if (knownprops[n].id == knownprops[n].id_ps7) hits[7-ps_offset] += 1;
5561                 if (knownprops[n].id == knownprops[n].id_ps8) hits[8-ps_offset] += 1;
5562                 if (knownprops[n].id == knownprops[n].id_ps9) hits[9-ps_offset] += 1;
5563                 if (knownprops[n].id == knownprops[n].id_ps10) hits[10-ps_offset] += 1;
5564                 if (knownprops[n].id == knownprops[n].id_ps11) hits[11-ps_offset] += 1;
5565                 if (knownprops[n].id == knownprops[n].id_ps12) hits[12-ps_offset] += 1;
5566                 if (knownprops[n].id == knownprops[n].id_ps13) hits[13-ps_offset] += 1;
5567             }
5568             if (knownprops[n].use == 1)
5569             {
5570                 bprintf("// #define %s %i\n", knownprops[n].name, knownprops[n].id);
5571             }
5572             else
5573             {
5574                 // propcases not used by CHDK, name may be made up
5575                 bprintf("// //      %s %i\n", knownprops[n].name, knownprops[n].id);
5576             }
5577         }
5578         else
5579         {
5580             bprintf("//         %s not found\n", knownprops[n].name);
5581         }
5582         n++;
5583     }
5584 
5585     bprintf("// Guessed propset: ");
5586     int m = 0;
5587     uint32_t fmax = 0;
5588     int okay = 0;
5589     for (n=0; n<KNOWN_PROPSET_COUNT; n++)
5590     {
5591         if (hits[n] == used)
5592         {
5593             if (m) bprintf(", ");
5594             bprintf("%i", n+ps_offset);
5595             if (fw->sv->propset == n+ps_offset) okay = 1; // if the propset equals to (one of) the complete propset matches
5596             m += 1;
5597         }
5598         if (hits[n] > fmax) fmax = hits[n];
5599     }
5600     if (m == 0)
5601     {
5602         bprintf("uncertain (%i of %u match), closest to ",fmax,used);
5603         for (n=0; n<KNOWN_PROPSET_COUNT; n++)
5604         {
5605             if (hits[n] == fmax)
5606             {
5607                 if (m) bprintf(", ");
5608                 bprintf("%i", n+ps_offset);
5609                 if (fw->sv->propset == n+ps_offset) okay = 1; // if the propset equals to (one of) the most complete propset matches
5610                 m += 1;
5611             }
5612         }
5613     }
5614     bprintf("\n");
5615     if (!okay && fw->sv->propset>0)
5616     {
5617         // only shown when there's a clear mismatch
5618         bprintf("// Port's propset (%i) may be set incorrectly\n", fw->sv->propset);
5619     }
5620 
5621     add_blankline();
5622 }
5623 
5624 void output_exmem_types(firmware *fw)
5625 {
5626 
5627     misc_val_t *ett=get_misc_val("exmem_types_table");
5628     misc_val_t *etc=get_misc_val("exmem_type_count");
5629     if (ett->val == 0 || etc->val == 0) {
5630         return;
5631     }
5632     bprintf("// EXMEM types:\n");
5633     uint32_t n;
5634     for (n=0; n<etc->val; n++) {
5635         char *extyp = (char*)adr2ptr(fw, fw_u32(fw,ett->val+n*4));
5636         bprintf("// %s %i\n", extyp, n);
5637     }
5638     add_blankline();
5639 }
5640 
5641 void print_misc_val_comment(const char *name)
5642 {
5643     misc_val_t *mv=get_misc_val(name);
5644     if(!mv) {
5645         return;
5646     }
5647     // TODO legitimate 0 values might be possible, if so can add found bit
5648     if(!mv->val) {
5649         bprintf("// %s not found\n",name);
5650         return;
5651     }
5652     bprintf("// %s 0x%08x",name,mv->val);
5653     if(mv->offset) {
5654         bprintf(" (0x%x+0x%x)",mv->base,mv->offset);
5655     }
5656     if(mv->ref_adr) {
5657         bprintf(" Found @0x%08x",mv->ref_adr);
5658     }
5659     bprintf("\n");
5660 }
5661 
5662 typedef struct {
5663     int reg;
5664     uint32_t bit;
5665     uint32_t ev;
5666     uint32_t raw_info;
5667     int no_invert;
5668 } physw_table_entry_t;
5669 
5670 void get_physw_table_entry(firmware *fw, uint32_t adr, physw_table_entry_t *vals)
5671 {
5672     uint32_t info=fw_u32(fw,adr);
5673     vals->raw_info=info;
5674     vals->ev=fw_u32(fw,adr+4);
5675     // taken from finsig_dryos print_physw_raw_vals
5676     vals->reg=(info >>5) & 7;
5677     vals->bit=(1 << (info & 0x1f));
5678     // vals->no_invert=(info >> 16) & 1;
5679     vals->no_invert=((info&0xff0000)==0x10000)?1:0;
5680 }
5681 uint32_t find_physw_table_entry(firmware *fw, uint32_t tadr, int tcount, uint32_t ev)
5682 {
5683     int i;
5684     for(i=0; i<tcount; i++,tadr += 8) {
5685         if(fw_u32(fw,tadr+4) == ev) {
5686             return tadr;
5687         }
5688     }
5689     return 0;
5690 }
5691 // look for the first invalid looking entry
5692 uint32_t find_physw_table_max(firmware *fw, uint32_t tadr, int max_count)
5693 {
5694     int i;
5695     for(i=0; i<max_count; i++,tadr += 8) {
5696         physw_table_entry_t v;
5697         get_physw_table_entry(fw,tadr,&v);
5698         if(v.raw_info == 0 || v.raw_info == 0xFFFFFFFF || v.reg > 2) {
5699             return i;
5700         }
5701         // TODO could check that no event numbers (except -1) are repeated
5702     }
5703     return max_count;
5704 }
5705 void write_physw_event_table_dump(firmware *fw, uint32_t tadr, int tcount)
5706 {
5707     FILE *f=fopen("physw_bits.txt","w");
5708     if(!f) {
5709         return;
5710     }
5711     fprintf(f,"physw_event_table dump (%d entries printed, may not all be valid)\n",tcount);
5712     fprintf(f,"address    info       event      index bit        non-inverted\n");
5713     int i;
5714     physw_table_entry_t v;
5715 
5716     for(i=0; i<tcount; i++,tadr += 8) {
5717         get_physw_table_entry(fw,tadr,&v);
5718         fprintf(f,"0x%08x 0x%08x 0x%08x %-5d 0x%08x %d\n",tadr,v.raw_info,v.ev,v.reg,v.bit,v.no_invert);
5719     }
5720     fclose(f);
5721 }
5722 void print_kval(firmware *fw, uint32_t tadr, int tcount, uint32_t ev, const char *name, const char *sfx)
5723 {
5724     uint32_t adr=find_physw_table_entry(fw,tadr,tcount,ev);
5725     if(!adr) {
5726         return;
5727     }
5728     physw_table_entry_t v;
5729     get_physw_table_entry(fw,adr,&v);
5730     
5731     char fn[100], rn[100];
5732     strcpy(fn,name); strcat(fn,sfx);
5733     strcpy(rn,name); strcat(rn,"_IDX");
5734 
5735     bprintf("//#define %-20s0x%08x // Found @0x%08x, levent 0x%x%s\n",fn,v.bit,adr,v.ev,v.no_invert?" (non-inverted logic)":"");
5736     bprintf("//#define %-20s%d\n",rn,v.reg);
5737 
5738 }
5739 
5740 // key stuff copied from finsig_dryos.c
5741 typedef struct {
5742     int         reg;
5743     uint32_t    bits;
5744     char        nm[32];
5745     uint32_t    fadr;
5746     uint32_t    ev;
5747     int         inv;
5748 } kinfo;
5749 
5750 int     kmask[3];
5751 kinfo   key_info[100];
5752 int     kcount = 0;
5753 uint32_t kshutter_min_bits = 0xFFFFFFFF;
5754 
5755 void add_kinfo(int r, uint32_t b, const char *nm, uint32_t adr, uint32_t ev, int inv)
5756 {
5757     key_info[kcount].reg = r;
5758     key_info[kcount].bits = b;
5759     strcpy(key_info[kcount].nm, nm);
5760     key_info[kcount].fadr = adr;
5761     key_info[kcount].ev = ev;
5762     key_info[kcount].inv = inv;
5763     kcount++;
5764     kmask[r] |= b;
5765     if ((ev <= 1) && (b < kshutter_min_bits)) kshutter_min_bits = b;
5766 }
5767 
5768 uint32_t add_kmval(firmware *fw, uint32_t tadr, __attribute__ ((unused))int tsiz, int tlen, uint32_t ev, const char *name, uint32_t xtra)
5769 {
5770     uint32_t adr=find_physw_table_entry(fw,tadr,tlen,ev);
5771     if(!adr) {
5772         return 0;
5773     }
5774     physw_table_entry_t v;
5775     get_physw_table_entry(fw,adr,&v);
5776 
5777     add_kinfo(v.reg,v.bit|xtra,name,adr,v.ev,(v.no_invert)?0:1);
5778     return v.bit;
5779 }
5780 
5781 int kinfo_compare(const kinfo *p1, const kinfo *p2)
5782 {
5783     if (p1->reg > p2->reg)
5784     {
5785         return 1;
5786     }
5787     else if (p1->reg < p2->reg)
5788     {
5789         return -1;
5790     }
5791     if ((p1->ev <= 1) && (p2->ev <= 1))    // output shutter entries in reverse order
5792     {
5793         if (p1->bits > p2->bits)
5794         {
5795             return -1;
5796         }
5797         else if (p1->bits < p2->bits)
5798         {
5799             return 1;
5800         }
5801     }
5802     // if one entry is shutter then compare to min shutter bits
5803     if (p1->ev <= 1)
5804     {
5805         if (kshutter_min_bits > p2->bits)
5806         {
5807             return 1;
5808         }
5809         else if (kshutter_min_bits < p2->bits)
5810         {
5811             return -1;
5812         }
5813     }
5814     if (p2->ev <= 1)
5815     {
5816         if (p1->bits > kshutter_min_bits)
5817         {
5818             return 1;
5819         }
5820         else if (p1->bits < kshutter_min_bits)
5821         {
5822             return -1;
5823         }
5824     }
5825     if (p1->bits > p2->bits)
5826     {
5827         return 1;
5828     }
5829     else if (p1->bits < p2->bits)
5830     {
5831         return -1;
5832     }
5833 
5834     return 0;
5835 }
5836 
5837 void print_kmvals()
5838 {
5839     qsort(key_info, kcount, sizeof(kinfo), (void*)kinfo_compare);
5840 
5841     bprintf("//KeyMap keymap[] = {\n");
5842 
5843     int k;
5844     for (k=0; k<kcount; k++)
5845     {
5846         bprintf("//    { %d, %-20s,0x%08x }, // Found @0x%08x, levent 0x%02x%s\n",key_info[k].reg,key_info[k].nm,key_info[k].bits,key_info[k].fadr,key_info[k].ev,(key_info[k].inv==0)?"":" (uses inverted logic in physw_status)");
5847     }
5848 
5849     bprintf("//    { 0, 0, 0 }\n//};\n");
5850 }
5851 
5852 void do_km_vals(firmware *fw, uint32_t tadr,int tsiz,int tlen)
5853 {
5854     uint32_t key_half = add_kmval(fw,tadr,tsiz,tlen,0,"KEY_SHOOT_HALF",0);
5855     add_kmval(fw,tadr,tsiz,tlen,1,"KEY_SHOOT_FULL",key_half);
5856     add_kmval(fw,tadr,tsiz,tlen,1,"KEY_SHOOT_FULL_ONLY",0);
5857     
5858     add_kmval(fw,tadr,tsiz,tlen,0x101,"KEY_PLAYBACK",0);
5859     add_kmval(fw,tadr,tsiz,tlen,0x100,"KEY_POWER",0);
5860 
5861     // mostly copied from finsig_dryos, with < r52 stuff removed
5862     if (fw->dryos_ver == 52)  // unclear if this applies any other ver
5863     {
5864         add_kmval(fw,tadr,tsiz,tlen,3,"KEY_ZOOM_IN",0);
5865         add_kmval(fw,tadr,tsiz,tlen,4,"KEY_ZOOM_OUT",0);
5866         add_kmval(fw,tadr,tsiz,tlen,6,"KEY_UP",0);
5867         add_kmval(fw,tadr,tsiz,tlen,7,"KEY_DOWN",0);
5868         add_kmval(fw,tadr,tsiz,tlen,8,"KEY_LEFT",0);
5869         add_kmval(fw,tadr,tsiz,tlen,9,"KEY_RIGHT",0);
5870         add_kmval(fw,tadr,tsiz,tlen,0xA,"KEY_SET",0);
5871         add_kmval(fw,tadr,tsiz,tlen,0xB,"KEY_MENU",0);
5872         add_kmval(fw,tadr,tsiz,tlen,0xC,"KEY_DISPLAY",0);
5873         add_kmval(fw,tadr,tsiz,tlen,0x12,"KEY_HELP",0);
5874         add_kmval(fw,tadr,tsiz,tlen,0x19,"KEY_ERASE",0);
5875         add_kmval(fw,tadr,tsiz,tlen,2,"KEY_VIDEO",0);
5876 //        add_kmval(fw,tadr,tsiz,tlen,18,"KEY_SHORTCUT",0);
5877     }
5878     else if (fw->dryos_ver < 54)
5879     {
5880         add_kmval(fw,tadr,tsiz,tlen,2,"KEY_ZOOM_IN",0);
5881         add_kmval(fw,tadr,tsiz,tlen,3,"KEY_ZOOM_OUT",0);
5882         add_kmval(fw,tadr,tsiz,tlen,4,"KEY_UP",0);
5883         add_kmval(fw,tadr,tsiz,tlen,5,"KEY_DOWN",0);
5884         add_kmval(fw,tadr,tsiz,tlen,6,"KEY_LEFT",0);
5885         add_kmval(fw,tadr,tsiz,tlen,7,"KEY_RIGHT",0);
5886         add_kmval(fw,tadr,tsiz,tlen,8,"KEY_SET",0);
5887         add_kmval(fw,tadr,tsiz,tlen,9,"KEY_MENU",0);
5888         add_kmval(fw,tadr,tsiz,tlen,0xA,"KEY_DISPLAY",0);
5889     }
5890     else if (fw->dryos_ver < 55)
5891     {
5892         add_kmval(fw,tadr,tsiz,tlen,3,"KEY_ZOOM_IN",0);
5893         add_kmval(fw,tadr,tsiz,tlen,4,"KEY_ZOOM_OUT",0);
5894         add_kmval(fw,tadr,tsiz,tlen,6,"KEY_UP",0);
5895         add_kmval(fw,tadr,tsiz,tlen,7,"KEY_DOWN",0);
5896         add_kmval(fw,tadr,tsiz,tlen,8,"KEY_LEFT",0);
5897         add_kmval(fw,tadr,tsiz,tlen,9,"KEY_RIGHT",0);
5898         add_kmval(fw,tadr,tsiz,tlen,0xA,"KEY_SET",0);
5899         add_kmval(fw,tadr,tsiz,tlen,0xE,"KEY_MENU",0);
5900         add_kmval(fw,tadr,tsiz,tlen,2,"KEY_VIDEO",0);
5901         add_kmval(fw,tadr,tsiz,tlen,0xD,"KEY_DISPLAY",0);
5902         add_kmval(fw,tadr,tsiz,tlen,0x103,"KEY_WIFI",0);
5903 // framing assist / shortuct
5904 //        add_kmval(fw,tadr,tsiz,tlen,0xF,"KEY_",0);
5905     }
5906     else if (fw->dryos_ver < 59)
5907     {
5908         add_kmval(fw,tadr,tsiz,tlen,3,"KEY_ZOOM_IN",0);
5909         add_kmval(fw,tadr,tsiz,tlen,4,"KEY_ZOOM_OUT",0);
5910         add_kmval(fw,tadr,tsiz,tlen,6,"KEY_UP",0);
5911         add_kmval(fw,tadr,tsiz,tlen,7,"KEY_DOWN",0);
5912         add_kmval(fw,tadr,tsiz,tlen,8,"KEY_LEFT",0);
5913         add_kmval(fw,tadr,tsiz,tlen,9,"KEY_RIGHT",0);
5914         add_kmval(fw,tadr,tsiz,tlen,0xA,"KEY_SET",0);
5915         add_kmval(fw,tadr,tsiz,tlen,0x14,"KEY_MENU",0);
5916         add_kmval(fw,tadr,tsiz,tlen,2,"KEY_VIDEO",0);
5917         add_kmval(fw,tadr,tsiz,tlen,0xD,"KEY_DISPLAY",0);
5918         add_kmval(fw,tadr,tsiz,tlen,0x103,"KEY_WIFI",0);
5919 // framing assist, shortcut
5920 //        add_kmval(fw,tadr,tsiz,tlen,0xF,"KEY_",0);
5921     }
5922     else
5923     {
5924         add_kmval(fw,tadr,tsiz,tlen,3,"KEY_ZOOM_IN",0);
5925         add_kmval(fw,tadr,tsiz,tlen,4,"KEY_ZOOM_OUT",0);
5926         add_kmval(fw,tadr,tsiz,tlen,6,"KEY_UP",0);
5927         add_kmval(fw,tadr,tsiz,tlen,7,"KEY_DOWN",0);
5928         add_kmval(fw,tadr,tsiz,tlen,8,"KEY_LEFT",0);
5929         add_kmval(fw,tadr,tsiz,tlen,9,"KEY_RIGHT",0);
5930         add_kmval(fw,tadr,tsiz,tlen,0xA,"KEY_SET",0);
5931         add_kmval(fw,tadr,tsiz,tlen,0x15,"KEY_MENU",0);
5932         add_kmval(fw,tadr,tsiz,tlen,2,"KEY_VIDEO",0);
5933         add_kmval(fw,tadr,tsiz,tlen,0xB,"KEY_ERASE",0); // also framing assist etc
5934         add_kmval(fw,tadr,tsiz,tlen,0x103,"KEY_WIFI",0);
5935     }
5936 
5937     bprintf("\n// Keymap values for kbd.c. Additional keys may be present, only common values included here.\n");
5938     bprintf("// WARNING: Key name / function may vary! Values for unknown DryOS versions should not be trusted!\n");
5939     print_kmvals();
5940 }
5941 void output_physw_vals(firmware *fw) {
5942     print_misc_val_comment("physw_event_table");
5943     uint32_t physw_tbl=get_misc_val_value("physw_event_table");
5944     if(!physw_tbl) {
5945         return;
5946     }
5947     int physw_tbl_len=find_physw_table_max(fw,physw_tbl,50);
5948     write_physw_event_table_dump(fw,physw_tbl,physw_tbl_len);
5949 
5950     bprintf("// Values below go in 'platform_kbd.h':\n");
5951     if (fw->dryos_ver >= 58) 
5952     {
5953         // Event ID's have changed in DryOS 58 **********
5954         print_kval(fw,physw_tbl,physw_tbl_len,0x30A,"SD_READONLY","_FLAG");
5955         print_kval(fw,physw_tbl,physw_tbl_len,0x302,"USB","_MASK");
5956         print_kval(fw,physw_tbl,physw_tbl_len,0x305,"BATTCOVER","_FLAG");
5957         print_kval(fw,physw_tbl,physw_tbl_len,0x304,"HOTSHOE","_FLAG");
5958         print_kval(fw,physw_tbl,physw_tbl_len,0x300,"ANALOG_AV","_FLAG");
5959     }
5960     else
5961     { 
5962         print_kval(fw,physw_tbl,physw_tbl_len,0x20A,"SD_READONLY","_FLAG");
5963         print_kval(fw,physw_tbl,physw_tbl_len,0x202,"USB","_MASK");
5964         print_kval(fw,physw_tbl,physw_tbl_len,0x205,"BATTCOVER","_FLAG");
5965         print_kval(fw,physw_tbl,physw_tbl_len,0x204,"HOTSHOE","_FLAG");
5966         print_kval(fw,physw_tbl,physw_tbl_len,0x200,"ANALOG_AV","_FLAG");
5967     }
5968     do_km_vals(fw,physw_tbl,2,physw_tbl_len);
5969 
5970     add_blankline();
5971 }
5972 
5973 /*
5974 typedef struct
5975 {
5976     uint16_t mode;
5977     char *nm;
5978 } ModeMapName;
5979 
5980 
5981 // TODO numbers changed after g1xmk2
5982 // M=32770
5983 ModeMapName mmnames[] = {
5984     { 32768,"MODE_AUTO" },
5985     { 32769,"MODE_M" },
5986     { 32770,"MODE_AV" },
5987     { 32771,"MODE_TV" },
5988     { 32772,"MODE_P" },
5989 
5990     { 65535,"" }
5991 };
5992 
5993 char* mode_name(uint16_t v)
5994 {
5995     int i;
5996     for (i=0; mmnames[i].mode != 65535; i++)
5997     {
5998         if (mmnames[i].mode == v)
5999             return mmnames[i].nm;
6000     }
6001 
6002     return "";
6003 }
6004 */
6005 
6006 
6007 void output_modemap(firmware *fw) {
6008     print_misc_val_comment("canon_mode_list");
6009     bprintf("// Check modemap values from 'platform/CAMERA/shooting.c':\n");
6010     misc_val_t *mv=get_misc_val("canon_mode_list");
6011     if(!mv) {
6012         add_blankline();
6013         return;
6014     }
6015     int i;
6016     uint32_t adr=mv->val;
6017     int bad=0;
6018     for(i=0; i<50; i++,adr+=2) {
6019         uint16_t *pv=(uint16_t*)adr2ptr(fw,adr);
6020         if(!pv) {
6021             break;
6022         }
6023         if(*pv==0xFFFF) {
6024             break;
6025         }
6026         osig *m = find_sig_val(fw->sv->modemap, *pv);
6027         if (!m) {
6028             bprintf("// %5hu  0x%04hx In firmware but not in current modemap",*pv,*pv);
6029             /*
6030             char *s = mode_name(*pv);
6031             if (strcmp(s,"") != 0) {
6032                 bprintf(" (%s)",s);
6033             }
6034             */
6035             bprintf("\n");
6036             bad++;
6037         } else {
6038 //            bprintf("// %5hu  0x%04hx %s\n", *pv,*pv,m->nm);
6039             m->pct = 100;
6040         }
6041     }
6042     osig *m = fw->sv->modemap;
6043     while (m)
6044     {
6045         if (m->pct != 100)    // not matched above?
6046         {
6047             bprintf("// Current modemap entry not found in firmware - %-24s %5d\n",m->nm,m->val);
6048             bad++;
6049         }
6050         m = m->nxt;
6051     }
6052     if (bad == 0)
6053     {
6054         bprintf("// No problems found with modemap table.\n");
6055     }
6056 
6057     add_blankline();
6058 }
6059 
6060 // copied from finsig_dryos
6061 int compare_sig_names(const sig_entry_t **p1, const sig_entry_t **p2)
6062 {
6063     int rv = strcasecmp((*p1)->name, (*p2)->name);     // Case insensitive
6064     if (rv != 0)
6065         return rv;
6066     rv = strcmp((*p1)->name, (*p2)->name);          // Case sensitive (if equal with insensitive test)
6067     if (rv != 0)
6068         return rv;
6069     if ((*p1)->val < (*p2)->val)
6070         return -1;
6071     else if ((*p1)->val > (*p2)->val)
6072         return 1;
6073     return 0;
6074 }
6075 
6076 int compare_func_addresses(const sig_entry_t **p1, const sig_entry_t **p2)
6077 {
6078     if ((*p1)->val < (*p2)->val)
6079         return -1;
6080     else if ((*p1)->val > (*p2)->val)
6081         return 1;
6082     return compare_sig_names(p1,p2);
6083 }
6084 
6085 void write_funcs(firmware *fw, char *filename, sig_entry_t *fns[], int (*compare)(const sig_entry_t **p1, const sig_entry_t **p2))
6086 {
6087     int k;
6088 
6089     qsort(fns, next_sig_entry, sizeof(sig_entry_t*), (void*)compare);
6090 
6091     FILE *out_fp = fopen(filename, "w");
6092     for (k=0; k<next_sig_entry; k++)
6093     {
6094         if (strncmp(fns[k]->name,"hook_",5) == 0) {
6095             continue;
6096         }
6097         if (fns[k]->val != 0)
6098         {
6099             if (fns[k]->flags & BAD_MATCH)
6100             {
6101                 osig* ostub2 = find_sig(fw->sv->stubs,fns[k]->name);
6102                 if (ostub2 && ostub2->val)
6103                     fprintf(out_fp, "0x%08x,%s,(stubs_entry_2.s)\n", ostub2->val, fns[k]->name);
6104             }
6105             else
6106                 fprintf(out_fp, "0x%08x,%s\n", fns[k]->val, fns[k]->name);
6107         }
6108 #ifdef LIST_IMPORTANT_FUNCTIONS
6109         else if (fns[k]->flags & LIST_ALWAYS)
6110         {
6111             // helps development by listing important functions even when not found
6112             fprintf(out_fp, "0,%s,(NOT FOUND)\n", fns[k]->name);
6113         }
6114 #endif
6115     }
6116     fclose(out_fp);
6117 }
6118 // end copy finsig_dryos
6119 void write_func_lists(firmware *fw) {
6120     sig_entry_t *fns[MAX_SIG_ENTRY];
6121     int k;
6122     for (k=0; k<next_sig_entry; k++)
6123         fns[k] = &sig_names[k];
6124 
6125     write_funcs(fw, "funcs_by_name.csv", fns, compare_sig_names);
6126     write_funcs(fw, "funcs_by_address.csv", fns, compare_func_addresses);
6127 }
6128 
6129 void print_other_stubs_min(firmware *fw, const char *name, uint32_t fadr, uint32_t atadr)
6130 {
6131     osig *o = find_sig(fw->sv->stubs_min,name);
6132     if (o)
6133     {
6134         bprintf("//DEF(%-40s,0x%08x) // Found @0x%08x",name,fadr,atadr);
6135         if (fadr != o->val)
6136         {
6137             bprintf(", ** != ** stubs_min = 0x%08x (%s)",o->val,o->sval);
6138         }
6139         else
6140         {
6141             bprintf(",          stubs_min = 0x%08x (%s)",o->val,o->sval);
6142         }
6143     }
6144     else
6145     {
6146         bprintf("DEF(%-40s,0x%08x) // Found @0x%08x",name,fadr,atadr);
6147     }
6148     bprintf("\n");
6149 }
6150 
6151 void print_stubs_min_def(firmware *fw, misc_val_t *sig)
6152 {
6153     if(sig->flags & MISC_VAL_NO_STUB) {
6154         return;
6155     }
6156     // find best match and report results
6157     osig* ostub2=find_sig(fw->sv->stubs_min,sig->name);
6158 
6159     const char *macro = "DEF";
6160     if(sig->flags & MISC_VAL_DEF_CONST) {
6161         macro="DEF_CONST";
6162     }
6163 
6164     if (ostub2)
6165     {
6166         bprintf("//%s(%-34s,0x%08x)",macro,sig->name,sig->val);
6167         if (sig->val != ostub2->val)
6168         {
6169             bprintf(", ** != ** stubs_min = 0x%08x (%s)",ostub2->val,ostub2->sval);
6170         }
6171         else
6172         {
6173             bprintf(",          stubs_min = 0x%08x (%s)",ostub2->val,ostub2->sval);
6174         }
6175     }
6176     else if(sig->base || sig->offset)
6177     {
6178         bprintf("%s(%-34s,0x%08x)",macro,sig->name,sig->val);
6179         if(sig->offset || sig->ref_adr) {
6180             bprintf(" //");
6181             if(sig->offset) {
6182                 bprintf(" (0x%x+0x%x)",sig->base,sig->offset);
6183             }
6184             if(sig->ref_adr) {
6185                 bprintf(" Found @0x%08x",sig->ref_adr);
6186             }
6187         }
6188     }
6189     else 
6190     {
6191         if (sig->flags & MISC_VAL_OPTIONAL) return;
6192         bprintf("// %s not found",sig->name);
6193     }
6194     bprintf("\n");
6195 }
6196 
6197 // Search for other things that go in 'stubs_min.S'
6198 void find_other_stubs_min(firmware *fw)
6199 {
6200     int k,k1;
6201 
6202     out_hdr = 1;
6203 
6204     // focus_len_table
6205     if (fw->sv->min_focus_len != 0)
6206     {
6207         int found = 0, pos = 0, len = 0, size = 0;
6208         for (k=0; k<fw->size32; k++)
6209         {
6210             if (fw->buf32[k] == fw->sv->min_focus_len)
6211             {
6212                 int mul = 1;
6213                 if ((fw->buf32[k+1] == 100) && (fw->buf32[k+2] == 0)) mul = 3;
6214                 if ((fw->buf32[k+1] == 100) && (fw->buf32[k+2] != 0)) mul = 2;
6215                 if ((fw->buf32[k+1] ==   0) && (fw->buf32[k+2] != 0)) mul = 2;
6216                 for (k1 = k + mul; (k1 < fw->size32) && (fw->buf32[k1] > fw->buf32[k1-mul]) && (fw->buf32[k1] > fw->sv->min_focus_len) && (fw->buf32[k1] < fw->sv->max_focus_len); k1 += mul) ;
6217                 if (fw->buf32[k1] == fw->sv->max_focus_len)
6218                 {
6219                     if ((found == 0) || ((size < mul) && (len < ((k1 - k) / mul) + 1)))
6220                     {
6221                         found = 1;
6222                         pos = k;
6223                         len = ((k1 - k) / mul) + 1;
6224                         size = mul;
6225                     }
6226                 }
6227             }
6228         }
6229         if (found == 1)
6230         {
6231             uint32_t adr = fw->base + (pos << 2);
6232             bprintf("// focus_len_table contains zoom focus lengths for use in 'get_focal_length' (main.c).\n");
6233             if (size == 1)
6234                 bprintf("// each entry contains 1 int value, which is the the zoom focus length.\n",size);
6235             else
6236                 bprintf("// each entry contains %d int value(s), the first is the zoom focus length.\n",size);
6237             bprintf("// there are %d entries in the table - set NUM_FL to %d\n",len,len);
6238             print_other_stubs_min(fw,"focus_len_table",adr,adr);
6239         }
6240     }
6241 }
6242 
6243 // Output match results for function
6244 // matches stuff butchered out for now, just using value in sig_names table
6245 void print_results(firmware *fw, sig_entry_t *sig)
6246 {
6247     int i;
6248     int err = 0;
6249     char line[500] = "";
6250 
6251     if (sig->flags & DONT_EXPORT) {
6252         return;
6253     }
6254 
6255     if ((sig->flags & DONT_EXPORT_ILC) && get_misc_val_value("CAM_IS_ILC")) {
6256         return;
6257     }
6258 
6259     // find best match and report results
6260     osig* ostub2 = find_sig(fw->sv->stubs,sig->name);
6261 
6262     if (ostub2 && (sig->val != ostub2->val))
6263     {
6264         if (ostub2->type != TYPE_IGNORE)
6265         {
6266             err = 1;
6267             sig->flags |= BAD_MATCH;
6268         }
6269     }
6270     else
6271     {
6272         if (sig->flags & UNUSED) return;
6273     }
6274 
6275     // write to header (if error) or body buffer (no error)
6276     out_hdr = err;
6277 
6278     char *macro = "NHSTUB";
6279     if (sig->flags & ARM_STUB) {
6280         macro = "NHSTUB2";
6281     }
6282     if (strncmp(sig->name,"task_",5) == 0 ||
6283         strncmp(sig->name,"hook_",5) == 0) macro = "   DEF";
6284 
6285     if (!sig->val && !ostub2)
6286     {
6287         if (sig->flags & OPTIONAL) return;
6288         char fmt[51] = "";
6289         sprintf(fmt, "// ERROR: %%s is not found. %%%ds//--- --- ", (int)(34-strlen(sig->name)));
6290         sprintf(line+strlen(line), fmt, sig->name, "");
6291     }
6292     else
6293     {
6294         if (ostub2 || (sig->flags & UNUSED))
6295             sprintf(line+strlen(line),"//%s(%-37s,0x%08x) //%3d ", macro, sig->name, sig->val, 0);
6296         else
6297             sprintf(line+strlen(line),"%s(%-39s,0x%08x) //%3d ", macro, sig->name, sig->val, 0);
6298 
6299         /*
6300         if (matches->fail > 0)
6301             sprintf(line+strlen(line),"%2d%% ", matches->success*100/(matches->success+matches->fail));
6302         else
6303             */
6304             sprintf(line+strlen(line),"    ");
6305     }
6306 
6307     if (ostub2)
6308     {
6309         if (ostub2->type == TYPE_IGNORE)
6310             sprintf(line+strlen(line),"       Overridden");
6311         else if (sig->val == ostub2->val)
6312             sprintf(line+strlen(line),"       == 0x%08x    ",ostub2->val);
6313         else {
6314             // if both have same value check if differs only by veneer
6315             if(sig->val && ostub2->val) {
6316                 fw_disasm_iter_single(fw,ostub2->val);
6317                 if(get_direct_jump_target(fw,fw->is) == sig->val) {
6318                     sprintf(line+strlen(line)," <-veneer 0x%08x    ",ostub2->val);
6319                 } else {
6320                     fw_disasm_iter_single(fw,sig->val);
6321                     if(get_direct_jump_target(fw,fw->is) == ostub2->val) {
6322                         sprintf(line+strlen(line)," veneer-> 0x%08x    ",ostub2->val);
6323                     } else {
6324                         sprintf(line+strlen(line),"   *** != 0x%08x    ",ostub2->val);
6325                     }
6326                 }
6327             } else {
6328                 sprintf(line+strlen(line),"   *** != 0x%08x    ",ostub2->val);
6329             }
6330         }
6331     }
6332     else
6333         sprintf(line+strlen(line),"                        ");
6334 
6335     for (i=strlen(line)-1; i>=0 && line[i]==' '; i--) line[i] = 0;
6336     bprintf("%s\n",line);
6337 
6338     /*
6339     for (i=1;i<count && matches[i].fail==matches[0].fail;i++)
6340     {
6341         if (matches[i].ptr != matches->ptr)
6342         {
6343             bprintf("// ALT: %s(%s, 0x%x) // %d %d/%d\n", macro, curr_name, matches[i].ptr, matches[i].sig, matches[i].success, matches[i].fail);
6344         }
6345     }
6346     */
6347 }
6348 
6349 void write_stubs(firmware *fw,int max_find_func) {
6350     int k;
6351     bprintf("// Values below can be overridden in 'stubs_min.S':\n");
6352     misc_val_t *stub_min=misc_vals;
6353     while(stub_min->name) {
6354         print_stubs_min_def(fw,stub_min);
6355         stub_min++;
6356     }
6357 
6358     find_other_stubs_min(fw);
6359 
6360     add_blankline();
6361 
6362     for (k = 0; k < max_find_func; k++)
6363     {
6364         print_results(fw,&sig_names[k]);
6365     }
6366 }
6367 
6368 int main(int argc, char **argv)
6369 {
6370     clock_t t1 = clock();
6371 
6372     firmware fw;
6373     memset(&fw,0,sizeof(firmware));
6374     if (argc < 4) {
6375         fprintf(stderr,"finsig_thumb2 <primary> <base> <outputfilename>\n");
6376         exit(1);
6377     }
6378 
6379     for (next_sig_entry = 0; sig_names[next_sig_entry].name != 0; next_sig_entry++);
6380     int max_find_sig = next_sig_entry;
6381 
6382     fw.sv = new_stub_values();
6383     load_stubs(fw.sv, "stubs_entry_2.S", 1);
6384     load_stubs_min(fw.sv);
6385     load_modemap(fw.sv);
6386     load_platform(fw.sv);
6387 
6388     bprintf("// !!! THIS FILE IS GENERATED. DO NOT EDIT. !!!\n");
6389     bprintf("#include \"stubs_asm.h\"\n\n");
6390 
6391     firmware_load(&fw,argv[1],strtoul(argv[2], NULL, 0),FW_ARCH_ARMv7);
6392     if(!firmware_init_capstone(&fw)) {
6393         exit(1);
6394     }
6395     firmware_init_data_ranges(&fw);
6396 
6397     out_fp = fopen(argv[3],"w");
6398     if (out_fp == NULL) {
6399         fprintf(stderr,"Error opening output file %s\n",argv[3]);
6400         exit(1);
6401     }
6402     
6403 
6404     // find ctypes - previously separate from regular sig matches to set code search limit
6405     find_ctypes(&fw);
6406 
6407     run_sig_rules(&fw,sig_rules_initial);
6408     find_generic_funcs(&fw);
6409     run_sig_rules(&fw,sig_rules_main);
6410 
6411     output_firmware_vals(&fw);
6412 
6413     output_platform_vals(&fw);
6414     output_physw_vals(&fw);
6415     output_modemap(&fw);
6416 
6417     output_propcases(&fw);
6418     output_exmem_types(&fw);
6419 
6420     write_stubs(&fw,max_find_sig);
6421 
6422     write_output();
6423     fclose(out_fp);
6424 
6425     write_func_lists(&fw);
6426 
6427     firmware_unload(&fw);
6428 
6429     clock_t t2 = clock();
6430 
6431     printf("Time to generate stubs %.2f seconds\n",(double)(t2-t1)/(double)CLOCKS_PER_SEC);
6432 
6433     return 0;
6434 }

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