1"""Defines the behavior for LLDB local at-desk GDB session.""" 2import contextlib 3import functools 4import typing 5from pathlib import Path 6 7import lldb 8 9 10class AtDeskLLDBGdbSession: 11 def __init__(self, interpreter): 12 self._command_interpreter = interpreter 13 14 def refresh(self): 15 macros_base_path = Path(__file__).parent.parent.parent 16 self.exec('settings set target.load-script-from-symbol-file false') 17 self.exec(f'settings set target.process.python-os-plugin-path {macros_base_path}/core/operating_system.py') 18 self.exec(f'command script import {macros_base_path}/xnu.py') 19 return self 20 21 @classmethod 22 @contextlib.contextmanager 23 def create(cls, gdb_remote: typing.Optional[str]) -> 'AtDeskLLDBGdbSession': 24 debugger = lldb.SBDebugger.Create() 25 command_interpreter = debugger.GetCommandInterpreter() 26 27 session = AtDeskLLDBGdbSession(command_interpreter) 28 session.exec('settings set plugin.dynamic-loader.darwin-kernel.load-kexts false') 29 30 session.refresh() 31 with session._gdb(gdb_remote): 32 yield session 33 34 lldb.SBDebugger.Destroy(debugger) 35 36 @functools.lru_cache(maxsize=5096) 37 def exec(self, cmd) -> str: 38 # TODO: consider logging. 39 print(f'LLDBSession running command: `{cmd}`') 40 res = lldb.SBCommandReturnObject() 41 self._command_interpreter.HandleCommand(cmd, res) 42 if res.Succeeded(): 43 return res.GetOutput() 44 raise RuntimeError(res.GetError()) 45 46 @contextlib.contextmanager 47 def _gdb(self, remote_gdb: typing.Optional[str] = None) -> 'AtDeskLLDBGdbSession': 48 if remote_gdb is None: 49 yield self 50 return 51 52 self.exec(f'gdb {remote_gdb}') 53 yield self 54 self.exec('detach') 55