17c54ffdcSMed Ismail Bennaniimport os,json,struct,signal 27c54ffdcSMed Ismail Bennani 37c54ffdcSMed Ismail Bennanifrom typing import Any, Dict 47c54ffdcSMed Ismail Bennani 57c54ffdcSMed Ismail Bennaniimport lldb 67c54ffdcSMed Ismail Bennanifrom lldb.plugins.scripted_process import ScriptedProcess 77c54ffdcSMed Ismail Bennanifrom lldb.plugins.scripted_process import ScriptedThread 87c54ffdcSMed Ismail Bennani 97c54ffdcSMed Ismail Bennanifrom lldb.macosx.crashlog import CrashLog,CrashLogParser 107c54ffdcSMed Ismail Bennani 117c54ffdcSMed Ismail Bennaniclass CrashLogScriptedProcess(ScriptedProcess): 127c54ffdcSMed Ismail Bennani def parse_crashlog(self): 137c54ffdcSMed Ismail Bennani try: 147c54ffdcSMed Ismail Bennani crash_log = CrashLogParser().parse(self.dbg, self.crashlog_path, False) 157c54ffdcSMed Ismail Bennani except Exception as e: 167c54ffdcSMed Ismail Bennani return 177c54ffdcSMed Ismail Bennani 187c54ffdcSMed Ismail Bennani self.pid = crash_log.process_id 19*3e54ea0cSMed Ismail Bennani self.addr_mask = crash_log.addr_mask 207c54ffdcSMed Ismail Bennani self.crashed_thread_idx = crash_log.crashed_thread_idx 217c54ffdcSMed Ismail Bennani self.loaded_images = [] 227c54ffdcSMed Ismail Bennani 230a65112cSMed Ismail Bennani def load_images(self, images): 240a65112cSMed Ismail Bennani #TODO: Add to self.loaded_images and load images in lldb 257c54ffdcSMed Ismail Bennani if images: 267c54ffdcSMed Ismail Bennani for image in images: 270a65112cSMed Ismail Bennani if image not in self.loaded_images: 287c54ffdcSMed Ismail Bennani err = image.add_module(self.target) 297c54ffdcSMed Ismail Bennani if err: 307c54ffdcSMed Ismail Bennani print(err) 317c54ffdcSMed Ismail Bennani else: 327c54ffdcSMed Ismail Bennani self.loaded_images.append(image) 330a65112cSMed Ismail Bennani 340a65112cSMed Ismail Bennani for thread in crash_log.threads: 350a65112cSMed Ismail Bennani if self.load_all_images: 360a65112cSMed Ismail Bennani load_images(self, crash_log.images) 370a65112cSMed Ismail Bennani elif thread.did_crash(): 380a65112cSMed Ismail Bennani for ident in thread.idents: 390a65112cSMed Ismail Bennani load_images(self, crash_log.find_images_with_identifier(ident)) 400a65112cSMed Ismail Bennani 417c54ffdcSMed Ismail Bennani self.threads[thread.index] = CrashLogScriptedThread(self, None, thread) 427c54ffdcSMed Ismail Bennani 437c54ffdcSMed Ismail Bennani def __init__(self, target: lldb.SBTarget, args : lldb.SBStructuredData): 447c54ffdcSMed Ismail Bennani super().__init__(target, args) 457c54ffdcSMed Ismail Bennani 467c54ffdcSMed Ismail Bennani if not self.target or not self.target.IsValid(): 477c54ffdcSMed Ismail Bennani return 487c54ffdcSMed Ismail Bennani 497c54ffdcSMed Ismail Bennani self.crashlog_path = None 507c54ffdcSMed Ismail Bennani 517c54ffdcSMed Ismail Bennani crashlog_path = args.GetValueForKey("crashlog_path") 527c54ffdcSMed Ismail Bennani if crashlog_path and crashlog_path.IsValid(): 537c54ffdcSMed Ismail Bennani if crashlog_path.GetType() == lldb.eStructuredDataTypeString: 547c54ffdcSMed Ismail Bennani self.crashlog_path = crashlog_path.GetStringValue(4096) 557c54ffdcSMed Ismail Bennani 567c54ffdcSMed Ismail Bennani if not self.crashlog_path: 577c54ffdcSMed Ismail Bennani return 587c54ffdcSMed Ismail Bennani 590a65112cSMed Ismail Bennani load_all_images = args.GetValueForKey("load_all_images") 600a65112cSMed Ismail Bennani if load_all_images and load_all_images.IsValid(): 610a65112cSMed Ismail Bennani if load_all_images.GetType() == lldb.eStructuredDataTypeBoolean: 620a65112cSMed Ismail Bennani self.load_all_images = load_all_images.GetBooleanValue() 630a65112cSMed Ismail Bennani 640a65112cSMed Ismail Bennani if not self.load_all_images: 650a65112cSMed Ismail Bennani self.load_all_images = False 660a65112cSMed Ismail Bennani 677c54ffdcSMed Ismail Bennani self.pid = super().get_process_id() 687c54ffdcSMed Ismail Bennani self.crashed_thread_idx = 0 697c54ffdcSMed Ismail Bennani self.parse_crashlog() 707c54ffdcSMed Ismail Bennani 717c54ffdcSMed Ismail Bennani def get_memory_region_containing_address(self, addr: int) -> lldb.SBMemoryRegionInfo: 727c54ffdcSMed Ismail Bennani return None 737c54ffdcSMed Ismail Bennani 747c54ffdcSMed Ismail Bennani def get_thread_with_id(self, tid: int): 757c54ffdcSMed Ismail Bennani return {} 767c54ffdcSMed Ismail Bennani 777c54ffdcSMed Ismail Bennani def get_registers_for_thread(self, tid: int): 787c54ffdcSMed Ismail Bennani return {} 797c54ffdcSMed Ismail Bennani 807c54ffdcSMed Ismail Bennani def read_memory_at_address(self, addr: int, size: int) -> lldb.SBData: 817c54ffdcSMed Ismail Bennani # NOTE: CrashLogs don't contain any memory. 827c54ffdcSMed Ismail Bennani return lldb.SBData() 837c54ffdcSMed Ismail Bennani 847c54ffdcSMed Ismail Bennani def get_loaded_images(self): 857c54ffdcSMed Ismail Bennani # TODO: Iterate over corefile_target modules and build a data structure 867c54ffdcSMed Ismail Bennani # from it. 877c54ffdcSMed Ismail Bennani return self.loaded_images 887c54ffdcSMed Ismail Bennani 897c54ffdcSMed Ismail Bennani def get_process_id(self) -> int: 907c54ffdcSMed Ismail Bennani return self.pid 917c54ffdcSMed Ismail Bennani 927c54ffdcSMed Ismail Bennani def should_stop(self) -> bool: 937c54ffdcSMed Ismail Bennani return True 947c54ffdcSMed Ismail Bennani 957c54ffdcSMed Ismail Bennani def is_alive(self) -> bool: 967c54ffdcSMed Ismail Bennani return True 977c54ffdcSMed Ismail Bennani 987c54ffdcSMed Ismail Bennani def get_scripted_thread_plugin(self): 997c54ffdcSMed Ismail Bennani return CrashLogScriptedThread.__module__ + "." + CrashLogScriptedThread.__name__ 1007c54ffdcSMed Ismail Bennani 1017c54ffdcSMed Ismail Bennaniclass CrashLogScriptedThread(ScriptedThread): 1027c54ffdcSMed Ismail Bennani def create_register_ctx(self): 1037c54ffdcSMed Ismail Bennani if not self.has_crashed: 1047c54ffdcSMed Ismail Bennani return dict.fromkeys([*map(lambda reg: reg['name'], self.register_info['registers'])] , 0) 1057c54ffdcSMed Ismail Bennani 1067c54ffdcSMed Ismail Bennani if not self.backing_thread or not len(self.backing_thread.registers): 1077c54ffdcSMed Ismail Bennani return dict.fromkeys([*map(lambda reg: reg['name'], self.register_info['registers'])] , 0) 1087c54ffdcSMed Ismail Bennani 1097c54ffdcSMed Ismail Bennani for reg in self.register_info['registers']: 1107c54ffdcSMed Ismail Bennani reg_name = reg['name'] 1117c54ffdcSMed Ismail Bennani if reg_name in self.backing_thread.registers: 1127c54ffdcSMed Ismail Bennani self.register_ctx[reg_name] = self.backing_thread.registers[reg_name] 1137c54ffdcSMed Ismail Bennani else: 1147c54ffdcSMed Ismail Bennani self.register_ctx[reg_name] = 0 1157c54ffdcSMed Ismail Bennani 1167c54ffdcSMed Ismail Bennani return self.register_ctx 1177c54ffdcSMed Ismail Bennani 1187c54ffdcSMed Ismail Bennani def create_stackframes(self): 1190a65112cSMed Ismail Bennani if not (self.scripted_process.load_all_images or self.has_crashed): 1207c54ffdcSMed Ismail Bennani return None 1217c54ffdcSMed Ismail Bennani 1227c54ffdcSMed Ismail Bennani if not self.backing_thread or not len(self.backing_thread.frames): 1237c54ffdcSMed Ismail Bennani return None 1247c54ffdcSMed Ismail Bennani 1257c54ffdcSMed Ismail Bennani for frame in self.backing_thread.frames: 126*3e54ea0cSMed Ismail Bennani frame_pc = frame.pc & self.scripted_process.addr_mask 127*3e54ea0cSMed Ismail Bennani pc = frame_pc if frame.index == 0 or frame_pc == 0 else frame_pc - 1 1287c54ffdcSMed Ismail Bennani sym_addr = lldb.SBAddress() 129*3e54ea0cSMed Ismail Bennani sym_addr.SetLoadAddress(pc, self.target) 1307c54ffdcSMed Ismail Bennani if not sym_addr.IsValid(): 1317c54ffdcSMed Ismail Bennani continue 132*3e54ea0cSMed Ismail Bennani self.frames.append({"idx": frame.index, "pc": pc}) 1337c54ffdcSMed Ismail Bennani 1347c54ffdcSMed Ismail Bennani return self.frames 1357c54ffdcSMed Ismail Bennani 1367c54ffdcSMed Ismail Bennani def __init__(self, process, args, crashlog_thread): 1377c54ffdcSMed Ismail Bennani super().__init__(process, args) 1387c54ffdcSMed Ismail Bennani 1397c54ffdcSMed Ismail Bennani self.backing_thread = crashlog_thread 1407c54ffdcSMed Ismail Bennani self.idx = self.backing_thread.index 14112301d61SMed Ismail Bennani self.tid = self.backing_thread.id 14212301d61SMed Ismail Bennani self.name = self.backing_thread.name 14312301d61SMed Ismail Bennani self.queue = self.backing_thread.queue 1447c54ffdcSMed Ismail Bennani self.has_crashed = (self.scripted_process.crashed_thread_idx == self.idx) 1457c54ffdcSMed Ismail Bennani self.create_stackframes() 1467c54ffdcSMed Ismail Bennani 1477c54ffdcSMed Ismail Bennani def get_state(self): 1487c54ffdcSMed Ismail Bennani if not self.has_crashed: 1497c54ffdcSMed Ismail Bennani return lldb.eStateStopped 1507c54ffdcSMed Ismail Bennani return lldb.eStateCrashed 1517c54ffdcSMed Ismail Bennani 1527c54ffdcSMed Ismail Bennani def get_stop_reason(self) -> Dict[str, Any]: 1537c54ffdcSMed Ismail Bennani if not self.has_crashed: 1547c54ffdcSMed Ismail Bennani return { "type": lldb.eStopReasonNone, "data": { }} 1557c54ffdcSMed Ismail Bennani # TODO: Investigate what stop reason should be reported when crashed 1567c54ffdcSMed Ismail Bennani return { "type": lldb.eStopReasonException, "data": { "desc": "EXC_BAD_ACCESS" }} 1577c54ffdcSMed Ismail Bennani 1587c54ffdcSMed Ismail Bennani def get_register_context(self) -> str: 1597c54ffdcSMed Ismail Bennani if not self.register_ctx: 1607c54ffdcSMed Ismail Bennani self.register_ctx = self.create_register_ctx() 1617c54ffdcSMed Ismail Bennani 1627c54ffdcSMed Ismail Bennani return struct.pack("{}Q".format(len(self.register_ctx)), *self.register_ctx.values()) 163