/* Pebble libpebble.a stub resolver for radare2/r2js. * * Usage: * r2 -i examples/pebble.r2.js hello-world/build/aplite/pebble-app.elf * * The per-API stubs linked from libpebble.a look like this in Thumb bytes: * 0f b4 push {r0, r1, r2, r3} * 01 49 ldr r1, [pc, #4] * ff f7 ?? ?? b.w jump_to_pbl_function * ?? ?? ?? ?? little-endian firmware jump-table byte offset * * Objdump prints the same pattern as halfwords: * b40f 4901 f7ff ???? * * The shared trampoline usually looks like this in file/memory bytes: * 03 a3 18 68 08 44 02 68 94 46 0f bc 60 47 00 bf ?? ?? ?? ?? * where the last word is pbl_table_addr and is patched by the firmware loader. */ const STUB_PATTERN = "0fb40149fff7............"; const TRAMPOLINE_PATTERN = "03a318680844026894460fbc604700bf........"; const PEBBLE_API_BY_INDEX = { 0: "accel_data_service_subscribe__deprecated", 1: "accel_data_service_unsubscribe", 2: "accel_service_peek", 3: "accel_service_set_samples_per_update", 4: "accel_service_set_sampling_rate", 5: "accel_tap_service_subscribe", 6: "accel_tap_service_unsubscribe", 7: "action_bar_layer_legacy2_add_to_window", 8: "action_bar_layer_legacy2_clear_icon", 9: "action_bar_layer_legacy2_create", 10: "action_bar_layer_legacy2_destroy", 11: "action_bar_layer_legacy2_get_layer", 12: "action_bar_layer_legacy2_remove_from_window", 13: "action_bar_layer_legacy2_set_background_color_2bit", 14: "action_bar_layer_legacy2_set_click_config_provider", 15: "action_bar_layer_legacy2_set_context", 16: "action_bar_layer_legacy2_set_icon", 17: "animation_legacy2_create", 18: "animation_legacy2_destroy", 19: "animation_legacy2_get_context", 20: "animation_legacy2_is_scheduled", 21: "animation_legacy2_schedule", 22: "animation_legacy2_set_curve", 23: "animation_legacy2_set_delay", 24: "animation_legacy2_set_duration", 25: "animation_legacy2_set_handlers", 26: "animation_legacy2_set_implementation", 27: "animation_legacy2_unschedule", 28: "animation_legacy2_unschedule_all", 29: "app_comm_get_sniff_interval", 30: "app_comm_set_sniff_interval", 31: "app_event_loop", 34: "app_log", 35: "app_message_deregister_callbacks", 36: "app_message_open", 43: "app_sync_deinit", 44: "app_sync_get", 45: "app_sync_init", 46: "app_sync_set", 47: "app_timer_cancel", 48: "app_timer_register", 49: "app_timer_reschedule", 50: "atan2_lookup", 51: "atoi", 52: "atol", 53: "battery_state_service_peek", 54: "battery_state_service_subscribe", 55: "battery_state_service_unsubscribe", 56: "bitmap_layer_create", 57: "bitmap_layer_destroy", 58: "bitmap_layer_get_layer", 59: "bitmap_layer_set_alignment", 60: "bitmap_layer_set_background_color_2bit", 61: "bitmap_layer_set_bitmap", 62: "bitmap_layer_set_compositing_mode", 63: "bluetooth_connection_service_peek", 64: "bluetooth_connection_service_subscribe", 65: "bluetooth_connection_service_unsubscribe", 66: "click_number_of_clicks_counted", 67: "click_recognizer_get_button_id", 68: "clock_copy_time_string", 69: "clock_is_24h_style", 70: "cos_lookup", 71: "data_logging_create", 72: "data_logging_finish", 73: "data_logging_log", 74: "dict_calc_buffer_size", 75: "dict_calc_buffer_size_from_tuplets", 76: "dict_find", 77: "dict_merge", 78: "dict_read_begin_from_buffer", 79: "dict_read_first", 80: "dict_read_next", 81: "dict_serialize_tuplets", 82: "dict_serialize_tuplets_to_buffer__deprecated", 83: "dict_serialize_tuplets_to_buffer_with_iter", 84: "dict_write_begin", 85: "dict_write_cstring", 86: "dict_write_data", 87: "dict_write_end", 88: "dict_write_int", 89: "dict_write_int16", 90: "dict_write_int32", 91: "dict_write_int8", 92: "dict_write_tuplet", 93: "dict_write_uint16", 94: "dict_write_uint32", 95: "dict_write_uint8", 96: "fonts_get_system_font", 97: "fonts_load_custom_font", 98: "fonts_unload_custom_font", 99: "free", 100: "gbitmap_create_as_sub_bitmap", 101: "gbitmap_create_with_data", 102: "gbitmap_create_with_resource", 103: "gbitmap_destroy", 104: "gmtime", 105: "gpath_create", 106: "gpath_destroy", 107: "gpath_draw_filled_legacy", 108: "gpath_draw_outline", 109: "gpath_move_to", 110: "gpath_rotate_to", 111: "gpoint_equal", 112: "graphics_context_set_compositing_mode", 113: "graphics_context_set_fill_color_2bit", 114: "graphics_context_set_stroke_color_2bit", 115: "graphics_context_set_text_color_2bit", 116: "graphics_draw_bitmap_in_rect", 117: "graphics_draw_circle", 118: "graphics_draw_line", 119: "graphics_draw_pixel", 120: "graphics_draw_rect", 121: "graphics_draw_round_rect", 122: "graphics_fill_circle", 123: "graphics_fill_rect", 125: "graphics_text_layout_get_max_used_size", 126: "grect_align", 127: "grect_center_point", 128: "grect_clip", 129: "grect_contains_point", 130: "grect_crop", 131: "grect_equal", 132: "grect_is_empty", 133: "grect_standardize", 134: "gsize_equal", 135: "inverter_layer_create", 136: "inverter_layer_destroy", 137: "inverter_layer_get_layer", 138: "layer_add_child", 139: "layer_create", 140: "layer_create_with_data", 141: "layer_destroy", 142: "layer_get_bounds", 143: "layer_get_clips", 144: "layer_get_data", 145: "layer_get_frame", 146: "layer_get_hidden", 147: "layer_get_window", 148: "layer_insert_above_sibling", 149: "layer_insert_below_sibling", 150: "layer_mark_dirty", 151: "layer_remove_child_layers", 152: "layer_remove_from_parent", 153: "layer_set_bounds", 154: "layer_set_clips", 155: "layer_set_frame", 156: "layer_set_hidden", 157: "layer_set_update_proc", 158: "light_enable", 159: "light_enable_interaction", 160: "localtime__deprecated", 161: "malloc", 162: "memcpy", 163: "memmove", 164: "memset", 165: "menu_cell_basic_draw", 166: "menu_cell_basic_header_draw", 167: "menu_cell_title_draw", 168: "menu_index_compare", 169: "menu_layer_legacy2_create", 170: "menu_layer_destroy", 171: "menu_layer_get_layer", 172: "menu_layer_get_scroll_layer", 173: "menu_layer_get_selected_index", 174: "menu_layer_reload_data", 175: "menu_layer_legacy2_set_callbacks__deprecated", 176: "menu_layer_set_click_config_onto_window", 177: "menu_layer_set_selected_index", 178: "menu_layer_set_selected_next", 179: "number_window_create", 180: "number_window_destroy", 181: "number_window_get_value", 182: "number_window_set_label", 183: "number_window_set_max", 184: "number_window_set_min", 185: "number_window_set_step_size", 186: "number_window_set_value", 187: "persist_delete", 188: "persist_exists", 189: "persist_get_size", 190: "persist_read_bool", 191: "persist_read_data__deprecated", 192: "persist_read_int", 193: "persist_read_string__deprecated", 194: "persist_write_bool", 195: "persist_write_data__deprecated", 196: "persist_write_int", 197: "persist_write_string", 198: "property_animation_legacy2_create", 199: "property_animation_legacy2_create_layer_frame", 200: "property_animation_legacy2_destroy", 201: "property_animation_legacy2_update_gpoint", 202: "property_animation_legacy2_update_grect", 203: "property_animation_legacy2_update_int16", 204: "psleep", 205: "rand", 206: "resource_get_handle", 207: "resource_load", 208: "resource_load_byte_range", 209: "resource_size", 210: "rot_bitmap_layer_create", 211: "rot_bitmap_layer_destroy", 212: "rot_bitmap_layer_increment_angle", 213: "rot_bitmap_layer_set_angle", 214: "rot_bitmap_layer_set_corner_clip_color_2bit", 215: "rot_bitmap_set_compositing_mode", 216: "rot_bitmap_set_src_ic", 217: "scroll_layer_add_child", 218: "scroll_layer_create", 219: "scroll_layer_destroy", 220: "scroll_layer_get_content_offset", 221: "scroll_layer_get_content_size", 222: "scroll_layer_get_layer", 223: "scroll_layer_get_shadow_hidden", 224: "scroll_layer_scroll_down_click_handler", 225: "scroll_layer_scroll_up_click_handler", 226: "scroll_layer_set_callbacks", 227: "scroll_layer_set_click_config_onto_window", 228: "scroll_layer_set_content_offset", 229: "scroll_layer_set_content_size", 230: "scroll_layer_set_context", 231: "scroll_layer_set_frame", 232: "scroll_layer_set_shadow_hidden", 233: "simple_menu_layer_create", 234: "simple_menu_layer_destroy", 235: "simple_menu_layer_get_layer", 236: "simple_menu_layer_get_selected_index", 237: "simple_menu_layer_set_selected_index", 238: "sin_lookup", 239: "snprintf", 240: "srand", 241: "strcat", 242: "strcmp", 243: "strcpy", 244: "strftime", 245: "strlen", 246: "strncat", 247: "strncmp", 248: "strncpy", 249: "text_layer_legacy2_create", 250: "text_layer_legacy2_destroy", 251: "text_layer_legacy2_get_content_size", 252: "text_layer_legacy2_get_layer", 253: "text_layer_legacy2_get_text", 254: "text_layer_legacy2_set_background_color_2bit", 255: "text_layer_legacy2_set_font", 256: "text_layer_legacy2_set_overflow_mode", 257: "text_layer_legacy2_set_size", 258: "text_layer_legacy2_set_text", 259: "text_layer_legacy2_set_text_alignment", 260: "text_layer_legacy2_set_text_color_2bit", 262: "tick_timer_service_subscribe", 263: "tick_timer_service_unsubscribe", 264: "time__deprecated", 265: "time_ms_deprecated", 266: "vibes_cancel", 267: "vibes_double_pulse", 268: "vibes_enqueue_custom_pattern", 269: "vibes_long_pulse", 270: "vibes_short_pulse", 271: "window_create", 272: "window_destroy", 273: "window_get_click_config_provider", 274: "window_get_fullscreen", 275: "window_get_root_layer", 276: "window_is_loaded", 277: "window_set_background_color_2bit", 278: "window_set_click_config_provider", 279: "window_set_click_config_provider_with_context", 280: "window_set_fullscreen", 281: "window_set_status_bar_icon", 282: "window_set_window_handlers", 283: "window_stack_contains_window", 284: "window_stack_get_top_window", 285: "window_stack_pop", 286: "window_stack_pop_all", 287: "window_stack_push", 288: "window_stack_remove", 289: "app_focus_service_subscribe", 290: "app_focus_service_unsubscribe", 291: "window_get_user_data", 292: "window_set_user_data", 293: "app_message_get_context", 294: "app_message_inbox_size_maximum", 295: "app_message_outbox_begin", 296: "app_message_outbox_send", 297: "app_message_outbox_size_maximum", 298: "app_message_register_inbox_dropped", 299: "app_message_register_inbox_received", 300: "app_message_register_outbox_failed", 301: "app_message_register_outbox_sent", 302: "app_message_set_context", 303: "window_long_click_subscribe", 304: "window_multi_click_subscribe", 305: "window_raw_click_subscribe", 306: "window_set_click_context", 307: "window_single_click_subscribe", 308: "window_single_repeating_click_subscribe", 309: "graphics_draw_text", 310: "dict_serialize_tuplets_to_buffer", 311: "persist_read_data", 312: "persist_read_string", 313: "persist_write_data", 314: "dict_size", 315: "graphics_text_layout_get_content_size", 316: "simple_menu_layer_get_menu_layer", 317: "accel_data_service_subscribe", 318: "calloc", 319: "bitmap_layer_get_bitmap", 320: "menu_layer_legacy2_set_callbacks", 321: "window_get_click_config_context", 322: "number_window_get_window", 323: "realloc", 324: "gbitmap_create_blank_2bit", 325: "click_recognizer_is_repeating", 326: "accel_raw_data_service_subscribe", 327: "app_worker_is_running", 328: "app_worker_kill", 329: "app_worker_launch", 330: "app_worker_message_subscribe", 331: "app_worker_message_unsubscribe", 332: "app_worker_send_message", 333: "worker_event_loop", 334: "worker_launch_app", 335: "heap_bytes_free", 336: "heap_bytes_used", 337: "compass_service_peek", 338: "compass_service_set_heading_filter", 339: "compass_service_subscribe", 340: "compass_service_unsubscribe", 341: "uuid_equal", 342: "uuid_to_string", 343: "gpath_draw_filled", 344: "animation_legacy2_set_custom_curve", 345: "watch_info_get_color", 346: "watch_info_get_firmware_version", 347: "watch_info_get_model", 348: "graphics_capture_frame_buffer_2bit", 349: "graphics_frame_buffer_is_captured", 350: "graphics_release_frame_buffer", 351: "clock_to_timestamp", 352: "launch_reason", 353: "wakeup_cancel", 354: "wakeup_cancel_all", 355: "wakeup_get_launch_event", 356: "wakeup_query", 357: "wakeup_schedule", 358: "wakeup_service_subscribe", 359: "clock_is_timezone_set", 360: "i18n_get_system_locale", 361: "_localeconv_r", 362: "setlocale", 363: "mktime", 364: "gcolor_equal", 365: "__profiler_init", 366: "__profiler_print_stats", 367: "__profiler_start", 368: "__profiler_stop", 370: "bitmap_layer_set_background_color", 371: "graphics_context_set_fill_color", 372: "graphics_context_set_stroke_color", 373: "graphics_context_set_text_color", 374: "rot_bitmap_layer_set_corner_clip_color", 377: "window_set_background_color", 378: "clock_get_timezone", 379: "localtime", 380: "animation_create", 381: "animation_destroy", 382: "animation_get_context", 383: "animation_is_scheduled", 384: "animation_schedule", 385: "animation_set_curve", 386: "animation_set_custom_curve", 387: "animation_set_delay", 388: "animation_set_duration", 389: "animation_set_handlers", 390: "animation_set_implementation", 391: "animation_unschedule", 392: "animation_unschedule_all", 393: "gbitmap_create_blank", 394: "graphics_capture_frame_buffer", 395: "graphics_capture_frame_buffer_format", 396: "property_animation_create", 397: "property_animation_create_layer_frame", 398: "property_animation_destroy", 399: "property_animation_from", 400: "property_animation_get_animation", 401: "property_animation_subject", 402: "property_animation_to", 403: "property_animation_update_gpoint", 404: "property_animation_update_grect", 405: "property_animation_update_int16", 406: "gbitmap_create_blank_with_palette", 407: "gbitmap_get_bounds", 408: "gbitmap_get_bytes_per_row", 409: "gbitmap_get_data", 410: "gbitmap_get_format", 411: "gbitmap_get_palette", 412: "gbitmap_set_bounds", 413: "gbitmap_set_data", 414: "gbitmap_set_palette", 415: "gbitmap_sequence_create_with_resource", 416: "gbitmap_sequence_destroy", 417: "gbitmap_sequence_get_bitmap_size", 418: "gbitmap_sequence_get_current_frame_idx", 419: "gbitmap_sequence_get_total_num_frames", 420: "gbitmap_sequence_update_bitmap_next_frame", 421: "gbitmap_create_from_png_data", 422: "animation_clone", 423: "animation_get_delay", 424: "animation_get_duration", 425: "animation_get_play_count", 426: "animation_get_elapsed", 427: "animation_get_reverse", 428: "animation_sequence_create", 429: "animation_sequence_create_from_array", 430: "animation_set_play_count", 431: "animation_set_elapsed", 432: "animation_set_reverse", 433: "animation_spawn_create", 434: "animation_spawn_create_from_array", 435: "animation_get_curve", 436: "animation_get_custom_curve", 437: "animation_get_implementation", 438: "launch_get_args", 439: "menu_layer_create", 441: "gbitmap_sequence_get_play_count", 442: "gbitmap_sequence_restart", 443: "gbitmap_sequence_set_play_count", 444: "graphics_context_set_antialiased", 445: "graphics_context_set_stroke_width", 446: "action_bar_layer_add_to_window", 447: "action_bar_layer_clear_icon", 448: "action_bar_layer_create", 449: "action_bar_layer_destroy", 450: "action_bar_layer_get_layer", 451: "action_bar_layer_remove_from_window", 452: "action_bar_layer_set_background_color", 453: "action_bar_layer_set_click_config_provider", 454: "action_bar_layer_set_context", 455: "action_bar_layer_set_icon", 456: "action_bar_layer_set_icon_animated", 457: "gbitmap_sequence_update_bitmap_by_elapsed", 458: "gbitmap_create_palettized_from_1bit", 459: "menu_cell_layer_is_highlighted", 460: "graphics_draw_rotated_bitmap", 461: "action_bar_layer_set_icon_press_animation", 462: "text_layer_create", 463: "text_layer_destroy", 464: "text_layer_get_content_size", 465: "text_layer_get_layer", 466: "text_layer_get_text", 467: "text_layer_set_background_color", 468: "text_layer_set_font", 469: "text_layer_set_overflow_mode", 470: "text_layer_set_size", 471: "text_layer_set_text", 472: "text_layer_set_text_alignment", 473: "text_layer_set_text_color", 474: "gdraw_command_draw", 475: "gdraw_command_frame_draw", 476: "gdraw_command_frame_get_duration", 477: "gdraw_command_frame_set_duration", 478: "gdraw_command_get_fill_color", 479: "gdraw_command_get_hidden", 480: "gdraw_command_get_num_points", 481: "gdraw_command_get_path_open", 482: "gdraw_command_get_point", 483: "gdraw_command_get_radius", 484: "gdraw_command_get_stroke_color", 485: "gdraw_command_get_stroke_width", 486: "gdraw_command_get_type", 487: "gdraw_command_image_clone", 488: "gdraw_command_image_create_with_resource", 489: "gdraw_command_image_destroy", 490: "gdraw_command_image_draw", 491: "gdraw_command_image_get_bounds_size", 492: "gdraw_command_image_get_command_list", 493: "gdraw_command_image_set_bounds_size", 494: "gdraw_command_list_draw", 495: "gdraw_command_list_get_command", 496: "gdraw_command_list_get_num_commands", 497: "gdraw_command_list_iterate", 498: "gdraw_command_sequence_clone", 499: "gdraw_command_sequence_create_with_resource", 500: "gdraw_command_sequence_destroy", 501: "gdraw_command_sequence_get_bounds_size", 502: "gdraw_command_sequence_get_frame_by_elapsed", 503: "gdraw_command_sequence_get_frame_by_index", 504: "gdraw_command_sequence_get_num_frames", 505: "gdraw_command_sequence_get_play_count", 506: "gdraw_command_sequence_get_total_duration", 507: "gdraw_command_sequence_set_bounds_size", 508: "gdraw_command_sequence_set_play_count", 509: "gdraw_command_set_fill_color", 510: "gdraw_command_set_hidden", 511: "gdraw_command_set_path_open", 512: "gdraw_command_set_point", 513: "gdraw_command_set_radius", 514: "gdraw_command_set_stroke_color", 515: "gdraw_command_set_stroke_width", 516: "property_animation_create_bounds_origin", 517: "property_animation_update_uint32", 518: "gpath_draw_outline_open", 519: "time", 520: "menu_layer_set_highlight_colors", 521: "menu_layer_set_normal_colors", 522: "menu_layer_set_callbacks", 523: "menu_layer_pad_bottom_enable", 524: "status_bar_layer_create", 525: "status_bar_layer_destroy", 526: "status_bar_layer_get_background_color", 527: "status_bar_layer_get_foreground_color", 528: "status_bar_layer_get_layer", 529: "status_bar_layer_set_colors", 530: "status_bar_layer_set_separator_mode", 531: "difftime", 532: "time_ms", 533: "gcolor_legible_over", 534: "property_animation_update_gcolor8", 535: "app_focus_service_subscribe_handlers", 536: "action_menu_close", 537: "action_menu_freeze", 538: "action_menu_get_context", 539: "action_menu_get_root_level", 540: "action_menu_hierarchy_destroy", 541: "action_menu_item_get_action_data", 542: "action_menu_item_get_label", 543: "action_menu_level_add_action", 544: "action_menu_level_add_child", 545: "action_menu_level_create", 546: "action_menu_level_set_display_mode", 547: "action_menu_open", 548: "action_menu_set_result_window", 549: "action_menu_unfreeze", 550: "dictation_session_create", 551: "dictation_session_destroy", 552: "dictation_session_enable_confirmation", 553: "dictation_session_start", 554: "dictation_session_stop", 555: "smartstrap_attribute_begin_write", 556: "smartstrap_attribute_create", 557: "smartstrap_attribute_destroy", 558: "smartstrap_attribute_end_write", 559: "smartstrap_attribute_get_attribute_id", 560: "smartstrap_attribute_get_service_id", 561: "smartstrap_attribute_read", 562: "smartstrap_service_is_available", 563: "smartstrap_set_timeout", 564: "smartstrap_subscribe", 565: "smartstrap_unsubscribe", 566: "connection_service_peek_pebble_app_connection", 567: "connection_service_peek_pebblekit_connection", 568: "connection_service_subscribe", 569: "connection_service_unsubscribe", 570: "dictation_session_enable_error_dialogs", 571: "gbitmap_get_data_row_info", 572: "content_indicator_configure_direction", 573: "content_indicator_create", 574: "content_indicator_destroy", 575: "content_indicator_get_content_available", 576: "content_indicator_set_content_available", 577: "scroll_layer_get_content_indicator", 578: "menu_layer_get_center_focused", 579: "menu_layer_set_center_focused", 580: "grect_inset", 581: "gpoint_from_polar", 582: "graphics_draw_arc", 583: "graphics_fill_radial", 584: "grect_centered_from_polar", 585: "graphics_text_attributes_create", 586: "graphics_text_attributes_destroy", 587: "graphics_text_attributes_enable_paging", 588: "graphics_text_attributes_enable_screen_text_flow", 589: "graphics_text_attributes_restore_default_paging", 590: "graphics_text_attributes_restore_default_text_flow", 591: "graphics_text_layout_get_content_size_with_attributes", 592: "layer_convert_point_to_screen", 593: "layer_convert_rect_to_screen", 594: "scroll_layer_get_paging", 595: "scroll_layer_set_paging", 596: "text_layer_enable_screen_text_flow_and_paging", 597: "text_layer_restore_default_text_flow_and_paging", 598: "menu_layer_is_index_selected", 599: "health_service_activities_iterate", 600: "health_service_any_activity_accessible", 601: "health_service_events_subscribe", 602: "health_service_events_unsubscribe", 603: "health_service_get_minute_history", 604: "health_service_metric_accessible", 605: "health_service_peek_current_activities", 606: "health_service_sum", 607: "health_service_sum_today", 608: "time_start_of_today", 609: "health_service_metric_averaged_accessible", 610: "health_service_sum_averaged", 611: "health_service_get_measurement_system_for_display", 612: "gdraw_command_frame_get_command_list", }; function hex(addr) { return "0x" + (addr >>> 0).toString(16); } function cleanName(name) { return name.replace(/[^A-Za-z0-9_]/g, "_"); } function u32leFromHex(hexstr, byteOffset) { const p = byteOffset * 2; return parseInt(hexstr.slice(p + 6, p + 8) + hexstr.slice(p + 4, p + 6) + hexstr.slice(p + 2, p + 4) + hexstr.slice(p, p + 2), 16) >>> 0; } function searchJson(pattern) { const out = r2.cmd("/xj " + pattern).trim(); if (!out) { return []; } try { return JSON.parse(out); } catch (e) { console.log("Cannot parse /xj output for " + pattern + ": " + e); return []; } } function nameTrampoline() { const hits = searchJson(TRAMPOLINE_PATTERN); for (const hit of hits) { const addr = hit.addr >>> 0; r2.cmd("f sym.pebble.jump_to_pbl_function 16 @ " + hex(addr)); r2.cmd("f sym.pebble.pbl_table_addr 4 @ " + hex(addr + 16)); r2.cmd("CCu Pebble firmware API trampoline, pbl_table_addr is patched by loader @ " + hex(addr)); } return hits.length; } function nameStubs() { r2.cmd("e search.align=4"); const hits = searchJson(STUB_PATTERN); let named = 0; let unknown = 0; for (const hit of hits) { const addr = hit.addr >>> 0; const data = (hit.data || "").replace(/[^0-9a-fA-F]/g, "").toLowerCase(); if (data.length < 24) { continue; } const byteOffset = u32leFromHex(data, 8); if ((byteOffset & 3) !== 0) { continue; } const index = byteOffset >>> 2; const apiName = PEBBLE_API_BY_INDEX[index] || ("unknown_" + index); const flagName = "sym.pebble." + cleanName(apiName); r2.cmd("f " + flagName + " 12 @ " + hex(addr)); r2.cmd("af @ " + hex(addr)); r2.cmd("afn " + flagName + " @ " + hex(addr)); r2.cmd("CCu Pebble API stub " + apiName + " index=" + index + " offset=0x" + byteOffset.toString(16) + " @ " + hex(addr)); if (PEBBLE_API_BY_INDEX[index]) { named++; } else { unknown++; } } console.log("Pebble stubs: named=" + named + " unknown=" + unknown + " total=" + hits.length); return named; } r2.cmd("e search.align=4"); const trampolines = nameTrampoline(); const stubs = nameStubs(); console.log("Pebble trampoline hits: " + trampolines); console.log("Pebble stub resolver complete: " + stubs + " named stubs");