1from abc import ABCMeta, abstractmethod
2import six
3
4import lldb
5
6@six.add_metaclass(ABCMeta)
7class ScriptedProcess:
8
9    """
10    The base class for a scripted process.
11
12    Most of the base class methods are `@abstractmethod` that need to be
13    overwritten by the inheriting class.
14
15    DISCLAIMER: THIS INTERFACE IS STILL UNDER DEVELOPMENT AND NOT STABLE.
16                THE METHODS EXPOSED MIGHT CHANGE IN THE FUTURE.
17    """
18
19    @abstractmethod
20    def __init__(self, target, args):
21        """ Construct a scripted process.
22
23        Args:
24            target (lldb.SBTarget): The target launching the scripted process.
25            args (lldb.SBStructuredData): A Dictionary holding arbitrary
26                key/value pairs used by the scripted process.
27        """
28        self.target = None
29        self.args = None
30        if isinstance(target, lldb.SBTarget) and target.IsValid():
31            self.target = target
32        if isinstance(args, lldb.SBStructuredData) and args.IsValid():
33            self.args = args
34
35    @abstractmethod
36    def get_memory_region_containing_address(addr):
37        """ Get the memory region for the scripted process, containing a
38            specific address.
39
40        Args:
41            addr (int): Address to look for in the scripted process memory
42                regions.
43
44        Returns:
45            lldb.SBMemoryRegionInfo: The memory region containing the address.
46                None if out of bounds.
47        """
48        pass
49
50    @abstractmethod
51    def get_thread_with_id(tid):
52        """ Get the scripted process thread with a specific ID.
53
54        Args:
55            tid (int): Thread ID to look for in the scripted process.
56
57        Returns:
58            Dict: The thread represented as a dictionary, withr the
59                tid thread ID. None if tid doesn't match any of the scripted
60                process threads.
61        """
62        pass
63
64    @abstractmethod
65    def get_registers_for_thread(tid):
66        """ Get the register context dictionary for a certain thread of
67            the scripted process.
68
69        Args:
70            tid (int): Thread ID for the thread's register context.
71
72        Returns:
73            Dict: The register context represented as a dictionary, for the
74                tid thread. None if tid doesn't match any of the scripted
75                process threads.
76        """
77        pass
78
79    @abstractmethod
80    def read_memory_at_address(addr, size):
81        """ Get a memory buffer from the scripted process at a certain address,
82            of a certain size.
83
84        Args:
85            addr (int): Address from which we should start reading.
86            size (int): Size of the memory to read.
87
88        Returns:
89            lldb.SBData: An `lldb.SBData` buffer with the target byte size and
90                byte order storing the memory read.
91        """
92        pass
93
94    @abstractmethod
95    def get_loaded_images(self):
96        """ Get the list of loaded images for the scripted process.
97
98        ```
99        class ScriptedProcessImage:
100            def __init__(name, file_spec, uuid, load_address):
101              self.name = name
102              self.file_spec = file_spec
103              self.uuid = uuid
104              self.load_address = load_address
105        ```
106
107        Returns:
108            List[ScriptedProcessImage]: A list of `ScriptedProcessImage`
109                containing for each entry, the name of the library, a UUID,
110                an `lldb.SBFileSpec` and a load address.
111                None if the list is empty.
112        """
113        pass
114
115    def get_process_id(self):
116        """ Get the scripted process identifier.
117
118        Returns:
119            int: The scripted process identifier.
120        """
121        return 0
122
123
124    def launch(self):
125        """ Simulate the scripted process launch.
126
127        Returns:
128            lldb.SBError: An `lldb.SBError` with error code 0.
129        """
130        return lldb.SBError()
131
132    def resume(self):
133        """ Simulate the scripted process resume.
134
135        Returns:
136            lldb.SBError: An `lldb.SBError` with error code 0.
137        """
138        return lldb.SBError()
139
140    @abstractmethod
141    def should_stop(self):
142        """ Check if the scripted process plugin should produce the stop event.
143
144        Returns:
145            bool: True if scripted process should broadcast a stop event.
146                  False otherwise.
147        """
148        pass
149
150    def stop(self):
151        """ Trigger the scripted process stop.
152
153        Returns:
154            lldb.SBError: An `lldb.SBError` with error code 0.
155        """
156        return lldb.SBError()
157
158    @abstractmethod
159    def is_alive(self):
160        """ Check if the scripted process is alive.
161
162        Returns:
163            bool: True if scripted process is alive. False otherwise.
164        """
165        pass
166
167    @abstractmethod
168    def get_scripted_thread_plugin(self):
169        """ Get scripted thread plugin name.
170
171        Returns:
172            str: Name of the scripted thread plugin.
173        """
174        return None
175
176@six.add_metaclass(ABCMeta)
177class ScriptedThread:
178
179    """
180    The base class for a scripted thread.
181
182    Most of the base class methods are `@abstractmethod` that need to be
183    overwritten by the inheriting class.
184
185    DISCLAIMER: THIS INTERFACE IS STILL UNDER DEVELOPMENT AND NOT STABLE.
186                THE METHODS EXPOSED MIGHT CHANGE IN THE FUTURE.
187    """
188
189    @abstractmethod
190    def __init__(self, target):
191        """ Construct a scripted thread.
192
193        Args:
194            target (lldb.SBTarget): The target launching the scripted process.
195            args (lldb.SBStructuredData): A Dictionary holding arbitrary
196                key/value pairs used by the scripted process.
197        """
198        self.target = None
199        self.args = None
200        if isinstance(target, lldb.SBTarget) and target.IsValid():
201            self.target = target
202
203        self.id = None
204        self.name = None
205        self.queue = None
206        self.state = None
207        self.stop_reason = None
208        self.register_info = None
209        self.register_ctx = []
210        self.frames = []
211
212    @abstractmethod
213    def get_thread_id(self):
214        """ Get the scripted thread identifier.
215
216        Returns:
217            int: The identifier of the scripted thread.
218        """
219        pass
220
221    @abstractmethod
222    def get_name(self):
223        """ Get the scripted thread name.
224
225        Returns:
226            str: The name of the scripted thread.
227        """
228        pass
229
230    def get_state(self):
231        """ Get the scripted thread state type.
232
233            eStateStopped,   ///< Process or thread is stopped and can be examined.
234            eStateRunning,   ///< Process or thread is running and can't be examined.
235            eStateStepping,  ///< Process or thread is in the process of stepping and can
236                             /// not be examined.
237
238        Returns:
239            int: The state type of the scripted thread.
240                 Returns lldb.eStateStopped by default.
241        """
242        return lldb.eStateStopped
243
244    def get_queue(self):
245        """ Get the scripted thread associated queue name.
246            This method is optional.
247
248        Returns:
249            str: The queue name associated with the scripted thread.
250        """
251        pass
252
253    @abstractmethod
254    def get_stop_reason(self):
255        """ Get the dictionary describing the stop reason type with some data.
256            This method is optional.
257
258        Returns:
259            Dict: The dictionary holding the stop reason type and the possibly
260            the stop reason data.
261        """
262        pass
263
264    def get_stackframes(self):
265        """ Get the list of stack frames for the scripted thread.
266
267        ```
268        class ScriptedStackFrame:
269            def __init__(idx, cfa, pc, symbol_ctx):
270                self.idx = idx
271                self.cfa = cfa
272                self.pc = pc
273                self.symbol_ctx = symbol_ctx
274        ```
275
276        Returns:
277            List[ScriptedFrame]: A list of `ScriptedStackFrame`
278                containing for each entry, the frame index, the canonical
279                frame address, the program counter value for that frame
280                and a symbol context.
281                None if the list is empty.
282        """
283        return 0
284
285    def get_register_info(self):
286        if self.register_info is None:
287            self.register_info = dict()
288            triple = self.target.triple
289            if triple:
290                arch = triple.split('-')[0]
291                if arch == 'x86_64':
292                    self.register_info['sets'] = ['GPR', 'FPU', 'EXC']
293                    self.register_info['registers'] = [
294                        {'name': 'rax', 'bitsize': 64, 'offset': 0, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 0, 'dwarf': 0},
295                        {'name': 'rbx', 'bitsize': 64, 'offset': 8, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 3, 'dwarf': 3},
296                        {'name': 'rcx', 'bitsize': 64, 'offset': 16, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 2, 'dwarf': 2, 'generic': 'arg4', 'alt-name': 'arg4', },
297                        {'name': 'rdx', 'bitsize': 64, 'offset': 24, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 1, 'dwarf': 1, 'generic': 'arg3', 'alt-name': 'arg3', },
298                        {'name': 'rdi', 'bitsize': 64, 'offset': 32, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 5, 'dwarf': 5, 'generic': 'arg1', 'alt-name': 'arg1', },
299                        {'name': 'rsi', 'bitsize': 64, 'offset': 40, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 4, 'dwarf': 4, 'generic': 'arg2', 'alt-name': 'arg2', },
300                        {'name': 'rbp', 'bitsize': 64, 'offset': 48, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 6, 'dwarf': 6, 'generic': 'fp', 'alt-name': 'fp', },
301                        {'name': 'rsp', 'bitsize': 64, 'offset': 56, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 7, 'dwarf': 7, 'generic': 'sp', 'alt-name': 'sp', },
302                        {'name': 'r8', 'bitsize': 64, 'offset': 64, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 8, 'dwarf': 8, 'generic': 'arg5', 'alt-name': 'arg5', },
303                        {'name': 'r9', 'bitsize': 64, 'offset': 72, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 9, 'dwarf': 9, 'generic': 'arg6', 'alt-name': 'arg6', },
304                        {'name': 'r10', 'bitsize': 64, 'offset': 80, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 10, 'dwarf': 10},
305                        {'name': 'r11', 'bitsize': 64, 'offset': 88, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 11, 'dwarf': 11},
306                        {'name': 'r12', 'bitsize': 64, 'offset': 96, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 12, 'dwarf': 12},
307                        {'name': 'r13', 'bitsize': 64, 'offset': 104, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 13, 'dwarf': 13},
308                        {'name': 'r14', 'bitsize': 64, 'offset': 112, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 14, 'dwarf': 14},
309                        {'name': 'r15', 'bitsize': 64, 'offset': 120, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 15, 'dwarf': 15},
310                        {'name': 'rip', 'bitsize': 64, 'offset': 128, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'gcc': 16, 'dwarf': 16, 'generic': 'pc', 'alt-name': 'pc'},
311                        {'name': 'rflags', 'bitsize': 64, 'offset': 136, 'encoding': 'uint', 'format': 'hex', 'set': 0, 'generic': 'flags', 'alt-name': 'flags'},
312                        {'name': 'cs', 'bitsize': 64, 'offset': 144, 'encoding': 'uint', 'format': 'hex', 'set': 0},
313                        {'name': 'fs', 'bitsize': 64, 'offset': 152, 'encoding': 'uint', 'format': 'hex', 'set': 0},
314                        {'name': 'gs', 'bitsize': 64, 'offset': 160, 'encoding': 'uint', 'format': 'hex', 'set': 0},
315                    ]
316        return self.register_info
317
318    @abstractmethod
319    def get_register_context(self):
320        """ Get the scripted thread register context
321
322        Returns:
323            str: A byte representing all register's value.
324        """
325        pass
326