hilda 3.0.1__py3-none-any.whl → 3.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hilda/_version.py +16 -3
- hilda/breakpoints.py +30 -7
- hilda/decorators.py +46 -0
- hilda/exceptions.py +1 -6
- hilda/hilda_client.py +21 -70
- hilda/ipython_extensions/events.py +14 -18
- hilda/objective_c_class.py +86 -29
- hilda/objective_c_symbol.py +5 -16
- hilda/snippets/libmalloc.py +9 -9
- hilda/symbol.py +48 -12
- hilda/symbols.py +595 -0
- hilda/watchpoints.py +41 -9
- {hilda-3.0.1.dist-info → hilda-3.2.1.dist-info}/METADATA +107 -183
- {hilda-3.0.1.dist-info → hilda-3.2.1.dist-info}/RECORD +18 -18
- {hilda-3.0.1.dist-info → hilda-3.2.1.dist-info}/WHEEL +1 -1
- hilda/hilda_ascii_art.html +0 -40
- hilda/symbols_jar.py +0 -208
- {hilda-3.0.1.dist-info → hilda-3.2.1.dist-info}/entry_points.txt +0 -0
- {hilda-3.0.1.dist-info → hilda-3.2.1.dist-info/licenses}/LICENSE +0 -0
- {hilda-3.0.1.dist-info → hilda-3.2.1.dist-info}/top_level.txt +0 -0
hilda/symbol.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import struct
|
|
3
3
|
from contextlib import contextmanager
|
|
4
|
-
from
|
|
4
|
+
from functools import cached_property
|
|
5
|
+
from typing import Any, Optional, Tuple
|
|
5
6
|
|
|
6
7
|
from construct import FormatField
|
|
7
8
|
|
|
@@ -25,16 +26,36 @@ class SymbolFormatField(FormatField):
|
|
|
25
26
|
return self._client.symbol(FormatField._parse(self, stream, context, path))
|
|
26
27
|
|
|
27
28
|
|
|
29
|
+
"""
|
|
30
|
+
A value identifying a `HildaSymbol`.
|
|
31
|
+
|
|
32
|
+
`HildaSymbol`s are either regular (i.e., named) symbols or anonymous symbols.
|
|
33
|
+
|
|
34
|
+
Regular symbols are uniquely identified by a `HildaSymbolId` (i.e., no two
|
|
35
|
+
instances of `HildaSymbol` have the same ID).
|
|
36
|
+
Note that several regular symbols may have the same address (with different names).
|
|
37
|
+
|
|
38
|
+
Anonymous symbols are not uniquely identified by a `HildaSymbolId`.
|
|
39
|
+
"""
|
|
40
|
+
HildaSymbolId = Tuple[Optional[str], int]
|
|
41
|
+
|
|
42
|
+
|
|
28
43
|
class Symbol(int):
|
|
44
|
+
"""
|
|
45
|
+
Hilda's class representing a symbol (not necessarily an LLDB symbol).
|
|
46
|
+
"""
|
|
47
|
+
|
|
29
48
|
PROXY_METHODS = ['peek', 'poke', 'peek_str', 'monitor', 'bp',
|
|
30
49
|
'disass', 'po', 'objc_call']
|
|
31
50
|
|
|
32
51
|
@classmethod
|
|
33
|
-
def create(cls, value: int, client
|
|
52
|
+
def create(cls, value: int, client, lldb_symbol: Optional[lldb.SBSymbol] = None,
|
|
53
|
+
lldb_address: Optional[lldb.SBAddress] = None, lldb_type: Optional[int] = None) -> None:
|
|
34
54
|
"""
|
|
35
55
|
Create a Symbol object.
|
|
36
56
|
:param value: Symbol address.
|
|
37
57
|
:param hilda.hilda_client.HildaClient client: Hilda client.
|
|
58
|
+
:param lldb.SBSymbol lldb_symbol: LLDB symbol.
|
|
38
59
|
:return: Symbol object.
|
|
39
60
|
:rtype: Symbol
|
|
40
61
|
"""
|
|
@@ -56,14 +77,12 @@ class Symbol(int):
|
|
|
56
77
|
symbol._file_address = None
|
|
57
78
|
|
|
58
79
|
# getting more data out from lldb
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
symbol.
|
|
65
|
-
symbol.type_ = type_
|
|
66
|
-
symbol.filename = filename
|
|
80
|
+
if lldb_address is None:
|
|
81
|
+
lldb_address = client.target.ResolveLoadAddress(int(symbol) & 0xFFFFFFFFFFFFFFFF)
|
|
82
|
+
if lldb_type is None:
|
|
83
|
+
lldb_type = lldb_address.symbol.type
|
|
84
|
+
symbol.type_ = lldb_type
|
|
85
|
+
symbol.lldb_address = lldb_address
|
|
67
86
|
symbol.lldb_symbol = lldb_symbol
|
|
68
87
|
|
|
69
88
|
for method_name in Symbol.PROXY_METHODS:
|
|
@@ -73,12 +92,24 @@ class Symbol(int):
|
|
|
73
92
|
return symbol
|
|
74
93
|
|
|
75
94
|
@property
|
|
95
|
+
def id(self) -> HildaSymbolId:
|
|
96
|
+
return (self.lldb_name, int(self))
|
|
97
|
+
|
|
98
|
+
@property
|
|
99
|
+
def lldb_name(self) -> Optional[str]:
|
|
100
|
+
return self.lldb_symbol.GetName() if self.lldb_symbol is not None else None
|
|
101
|
+
|
|
102
|
+
@cached_property
|
|
76
103
|
def file_address(self) -> int:
|
|
77
104
|
"""
|
|
78
105
|
Get symbol file address (address without ASLR)
|
|
79
106
|
:return: File address
|
|
80
107
|
"""
|
|
81
|
-
return self.
|
|
108
|
+
return self.lldb_address.file_addr
|
|
109
|
+
|
|
110
|
+
@cached_property
|
|
111
|
+
def filename(self):
|
|
112
|
+
return self.lldb_address.module.file.basename
|
|
82
113
|
|
|
83
114
|
@property
|
|
84
115
|
def objc_class(self) -> Class:
|
|
@@ -249,7 +280,12 @@ class Symbol(int):
|
|
|
249
280
|
self._client.poke(self + item * self.item_size, value)
|
|
250
281
|
|
|
251
282
|
def __repr__(self):
|
|
252
|
-
|
|
283
|
+
address = int(self)
|
|
284
|
+
name = self.lldb_name
|
|
285
|
+
if name is not None:
|
|
286
|
+
return f'Symbol({name}, 0x{address:016X})'
|
|
287
|
+
else:
|
|
288
|
+
return f'AnonymousSymbol(0x{address:016X})'
|
|
253
289
|
|
|
254
290
|
def __str__(self):
|
|
255
291
|
return hex(self)
|
hilda/symbols.py
ADDED
|
@@ -0,0 +1,595 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import re
|
|
3
|
+
import shlex
|
|
4
|
+
from itertools import chain
|
|
5
|
+
from tempfile import NamedTemporaryFile
|
|
6
|
+
from typing import Iterator, Optional, Tuple, Union
|
|
7
|
+
|
|
8
|
+
from tqdm import tqdm
|
|
9
|
+
|
|
10
|
+
from hilda.exceptions import SymbolAbsentError
|
|
11
|
+
from hilda.lldb_importer import lldb
|
|
12
|
+
from hilda.symbol import HildaSymbolId, Symbol
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class SymbolList:
|
|
16
|
+
"""
|
|
17
|
+
Manager for `Symbol` objects, each one representing a symbol.
|
|
18
|
+
|
|
19
|
+
`Symbol`s are either regular (i.e., named) symbols or anonymous symbols.
|
|
20
|
+
Only regular symbols are managed by this class, though anonymous
|
|
21
|
+
symbols can be created by this class (using the function `add`).
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(self, hilda) -> None:
|
|
25
|
+
"""
|
|
26
|
+
Initialize a symbol list.
|
|
27
|
+
|
|
28
|
+
:param hilda.hilda_client.HildaClient hilda: Hilda client
|
|
29
|
+
"""
|
|
30
|
+
self._hilda = hilda
|
|
31
|
+
self._modules = set()
|
|
32
|
+
self._symbols = {}
|
|
33
|
+
self._symbols_by_name = {}
|
|
34
|
+
|
|
35
|
+
# There should be only one "global" symbol list instance, and it should be referenced by the HildaClient class.
|
|
36
|
+
# The global symbols list contains (lazily) all symbols (from all modules).
|
|
37
|
+
if not hasattr(hilda, 'symbols'):
|
|
38
|
+
self._global = self
|
|
39
|
+
else:
|
|
40
|
+
self._global = hilda.symbols
|
|
41
|
+
|
|
42
|
+
def __iter__(self) -> Iterator[Symbol]:
|
|
43
|
+
self._populate_cache()
|
|
44
|
+
|
|
45
|
+
for symbol in self._symbols.values():
|
|
46
|
+
yield symbol
|
|
47
|
+
|
|
48
|
+
def __len__(self) -> int:
|
|
49
|
+
return sum(1 for _ in self)
|
|
50
|
+
|
|
51
|
+
def __contains__(self,
|
|
52
|
+
address_or_name_or_id_or_symbol: Union[int, str, HildaSymbolId, lldb.SBSymbol, Symbol]) -> bool:
|
|
53
|
+
return self.get(address_or_name_or_id_or_symbol) is not None
|
|
54
|
+
|
|
55
|
+
def __getitem__(self,
|
|
56
|
+
address_or_name_or_id_or_symbol: Union[int, str, HildaSymbolId, lldb.SBSymbol, Symbol]) -> Symbol:
|
|
57
|
+
"""
|
|
58
|
+
Get a symbol by address or name or ID (or the symbol itself, though it usually makes little sense)
|
|
59
|
+
|
|
60
|
+
:param address_or_name_or_id_or_symbol: Address or name or ID (or the symbol itself)
|
|
61
|
+
"""
|
|
62
|
+
symbol = self.get(address_or_name_or_id_or_symbol)
|
|
63
|
+
if symbol is None:
|
|
64
|
+
raise SymbolAbsentError(f'no such symbol: {address_or_name_or_id_or_symbol}')
|
|
65
|
+
# raise KeyError(address_or_name_or_id_or_symbol)
|
|
66
|
+
return symbol
|
|
67
|
+
|
|
68
|
+
def __delitem__(self,
|
|
69
|
+
address_or_name_or_id_or_symbol: Union[int, str, HildaSymbolId, lldb.SBSymbol, Symbol]) -> None:
|
|
70
|
+
"""
|
|
71
|
+
Remove a symbol (unless this is the global symbol list - see remove())
|
|
72
|
+
|
|
73
|
+
:param address_or_name_or_id_or_symbol: Address or name or ID (or the symbol itself)
|
|
74
|
+
"""
|
|
75
|
+
self.remove(address_or_name_or_id_or_symbol)
|
|
76
|
+
|
|
77
|
+
def __repr__(self) -> str:
|
|
78
|
+
if self._global is self:
|
|
79
|
+
return (f'<{self.__class__.__name__} GLOBAL>')
|
|
80
|
+
else:
|
|
81
|
+
return repr(list(self))
|
|
82
|
+
|
|
83
|
+
def __str__(self) -> str:
|
|
84
|
+
return repr(self)
|
|
85
|
+
|
|
86
|
+
def get(self, address_or_name_or_id_or_symbol: Union[int, str, HildaSymbolId, lldb.SBSymbol, Symbol]) \
|
|
87
|
+
-> Optional[Symbol]:
|
|
88
|
+
"""
|
|
89
|
+
Get a symbol by address or name or ID (or the symbol itself, though it usually makes little sense)
|
|
90
|
+
|
|
91
|
+
:param address_or_name_or_id_or_symbol: Address or name or ID (or the symbol itself)
|
|
92
|
+
:return: `Symbol` if one exists, or `None` otherwise
|
|
93
|
+
"""
|
|
94
|
+
symbol = self._get_lldb_symbol(address_or_name_or_id_or_symbol)
|
|
95
|
+
if symbol is None:
|
|
96
|
+
return None
|
|
97
|
+
|
|
98
|
+
lldb_symbol, lldb_address, name, address, type_ = symbol
|
|
99
|
+
sym_id = (name, address)
|
|
100
|
+
if sym_id not in self._symbols and self._global is self:
|
|
101
|
+
symbol = Symbol.create(address, self._hilda, lldb_symbol, lldb_address, type_)
|
|
102
|
+
self._add(sym_id, symbol)
|
|
103
|
+
return symbol
|
|
104
|
+
|
|
105
|
+
return self._symbols.get(sym_id)
|
|
106
|
+
|
|
107
|
+
def _populate_cache(self, module_uuid_filter=None) -> None:
|
|
108
|
+
if self._global is self:
|
|
109
|
+
modules = self._hilda.target.modules
|
|
110
|
+
modules_not_cached = [module for module in modules if module.GetUUIDString() not in self._modules]
|
|
111
|
+
if module_uuid_filter is not None:
|
|
112
|
+
modules_not_cached = [module for module in modules if module.GetUUIDString() == module_uuid_filter]
|
|
113
|
+
if len(modules_not_cached) != 0:
|
|
114
|
+
for lldb_module in tqdm(modules_not_cached, desc='Populating Hilda symbols cache'):
|
|
115
|
+
for lldb_symbol in lldb_module.symbols:
|
|
116
|
+
_ = self.get(lldb_symbol)
|
|
117
|
+
self._modules.add(lldb_module.GetUUIDString())
|
|
118
|
+
|
|
119
|
+
def force_refresh(self, module_range=None, module_filename_filter=''):
|
|
120
|
+
"""
|
|
121
|
+
Force a refresh of symbols
|
|
122
|
+
:param module_range: index range for images to load in the form of [start, end]
|
|
123
|
+
:param module_filename_filter: filter only images containing given expression
|
|
124
|
+
"""
|
|
125
|
+
self.log_debug('Force symbols')
|
|
126
|
+
|
|
127
|
+
if self._global is not self:
|
|
128
|
+
self._hilda.log_error('Cannot refresh a non-global symbol list')
|
|
129
|
+
return
|
|
130
|
+
|
|
131
|
+
for i, lldb_module in enumerate(tqdm(self._hilda.target.modules)):
|
|
132
|
+
filename = lldb_module.file.basename
|
|
133
|
+
|
|
134
|
+
if module_filename_filter not in filename:
|
|
135
|
+
continue
|
|
136
|
+
|
|
137
|
+
if module_range is not None and (i < module_range[0] or i > module_range[1]):
|
|
138
|
+
continue
|
|
139
|
+
|
|
140
|
+
for lldb_symbol in lldb_module.symbols:
|
|
141
|
+
# Getting the symbol would insert it if it does not exist
|
|
142
|
+
_ = self.get(lldb_symbol)
|
|
143
|
+
|
|
144
|
+
def _add(self, sym_id: HildaSymbolId, symbol: Symbol) -> None:
|
|
145
|
+
self._symbols[sym_id] = symbol
|
|
146
|
+
name, address = sym_id
|
|
147
|
+
if name is not None and re.match(r'^[a-zA-Z0-9_]+$', name):
|
|
148
|
+
ids = self._symbols_by_name.get(name)
|
|
149
|
+
if ids is not None:
|
|
150
|
+
ids.append(sym_id)
|
|
151
|
+
else:
|
|
152
|
+
self._symbols_by_name[name] = [sym_id]
|
|
153
|
+
|
|
154
|
+
def add(self, value: Union[int, Symbol], symbol_name: Optional[str] = None, symbol_type: Optional[str] = None,
|
|
155
|
+
symbol_size: Optional[int] = None) -> Symbol:
|
|
156
|
+
"""
|
|
157
|
+
Add a symbol.
|
|
158
|
+
Returns existing symbol if a matching regular (i.e., non-anonymous) symbol exists.
|
|
159
|
+
|
|
160
|
+
:param value: The address of the symbol (in memory) or and existing `Symbol`.
|
|
161
|
+
:param symbol_name: The name of the symbol.
|
|
162
|
+
:param symbol_type: The type of the symbol (either 'code' of 'data', defaults to 'code').
|
|
163
|
+
:param symbol_size: The size of the symbol (defaults to 8 bytes).
|
|
164
|
+
:return: The symbol
|
|
165
|
+
"""
|
|
166
|
+
# Check if we already created the symbol
|
|
167
|
+
if (isinstance(value, Symbol) and
|
|
168
|
+
(symbol_type, symbol_size) == (None, None) and
|
|
169
|
+
value.lldb_symbol is not None and
|
|
170
|
+
|
|
171
|
+
# TODO: Is it an error to add again, providing the same name?
|
|
172
|
+
(symbol_name is None or symbol_name == value.lldb_name)):
|
|
173
|
+
self._add(value.id, value)
|
|
174
|
+
return value
|
|
175
|
+
|
|
176
|
+
# Adding an existing anonymous symbol. Ignore the fact that this is actually a symbol.
|
|
177
|
+
if isinstance(value, Symbol) and value.lldb_name is None:
|
|
178
|
+
value = int(value)
|
|
179
|
+
|
|
180
|
+
# Adding an existing symbol. Do not provide any other arguments.
|
|
181
|
+
if isinstance(value, Symbol) and (symbol_name, symbol_type, symbol_size) != (None, None, None):
|
|
182
|
+
raise ValueError()
|
|
183
|
+
|
|
184
|
+
# Adding a new symbol without specifying a name. Symbol type and size are not (currently) supported.
|
|
185
|
+
if isinstance(value, int) and symbol_name is None and (symbol_type, symbol_size) != (None, None):
|
|
186
|
+
raise ValueError()
|
|
187
|
+
|
|
188
|
+
# Add
|
|
189
|
+
|
|
190
|
+
# Check if we can get a global symbol
|
|
191
|
+
symbol_address = value
|
|
192
|
+
global_symbol = self._global.get(symbol_address) if symbol_name is None else (
|
|
193
|
+
self._global.get((symbol_name, symbol_address)))
|
|
194
|
+
if global_symbol is not None:
|
|
195
|
+
if (symbol_type, symbol_size) != (None, None):
|
|
196
|
+
raise ValueError()
|
|
197
|
+
return self.add(global_symbol)
|
|
198
|
+
|
|
199
|
+
# Check if this is an anonymous symbol
|
|
200
|
+
if symbol_name is None:
|
|
201
|
+
# Anonymous symbols need not be added to _symbols.
|
|
202
|
+
return Symbol.create(symbol_address, self._hilda, None)
|
|
203
|
+
|
|
204
|
+
# Add a new global symbol
|
|
205
|
+
symbol = self._global._add_lldb_symbol(
|
|
206
|
+
symbol_name,
|
|
207
|
+
symbol_address,
|
|
208
|
+
symbol_type if symbol_type is not None else 'code',
|
|
209
|
+
symbol_size if symbol_size is not None else 8)
|
|
210
|
+
return self.add(symbol)
|
|
211
|
+
|
|
212
|
+
def _remove(self, sym_id: HildaSymbolId) -> None:
|
|
213
|
+
del self._symbols[sym_id]
|
|
214
|
+
name, address = sym_id
|
|
215
|
+
if name is not None and re.match(r'^[a-zA-Z0-9_]+$', name):
|
|
216
|
+
ids = self._symbols_by_name[name]
|
|
217
|
+
if len(ids) == 1:
|
|
218
|
+
ids.remove(sym_id)
|
|
219
|
+
else:
|
|
220
|
+
del self._symbols_by_name[name]
|
|
221
|
+
|
|
222
|
+
def remove(self, address_or_name_or_id_or_symbol: Union[int, str, HildaSymbolId, lldb.SBSymbol, Symbol]) -> None:
|
|
223
|
+
"""
|
|
224
|
+
Remove a symbol.
|
|
225
|
+
|
|
226
|
+
:param address_or_name_or_id_or_symbol: Address or name or ID (or the symbol itself)
|
|
227
|
+
"""
|
|
228
|
+
if self._global is self:
|
|
229
|
+
raise Exception('Cannot remove from the global symbols list')
|
|
230
|
+
|
|
231
|
+
symbol = self[address_or_name_or_id_or_symbol]
|
|
232
|
+
self._remove(symbol.id)
|
|
233
|
+
|
|
234
|
+
def items(self) -> Iterator[Tuple[HildaSymbolId, Symbol]]:
|
|
235
|
+
"""
|
|
236
|
+
Get a symbol ID and symbol object tuple for every symbol
|
|
237
|
+
"""
|
|
238
|
+
return ((symbol.id, symbol) for symbol in self)
|
|
239
|
+
|
|
240
|
+
def keys(self) -> Iterator[HildaSymbolId]:
|
|
241
|
+
"""
|
|
242
|
+
Get the symbol ID for every symbol
|
|
243
|
+
"""
|
|
244
|
+
return (symbol.id for symbol in self)
|
|
245
|
+
|
|
246
|
+
def values(self) -> Iterator[Symbol]:
|
|
247
|
+
"""
|
|
248
|
+
Get the symbol object for every symbol
|
|
249
|
+
"""
|
|
250
|
+
return (symbol for symbol in self)
|
|
251
|
+
|
|
252
|
+
def __getattr__(self, attribute_name: str) -> Symbol:
|
|
253
|
+
"""
|
|
254
|
+
Returns a symbol appropriate to the attribute requested.
|
|
255
|
+
|
|
256
|
+
For example:
|
|
257
|
+
support a `symbols.malloc()` syntax.
|
|
258
|
+
support a `symbols.x0x11223344` syntax.
|
|
259
|
+
support a `symbols.x11223344` syntax.
|
|
260
|
+
"""
|
|
261
|
+
match = re.fullmatch(r'x(?:0x)?([0-9a-fA-F]{6,16})', attribute_name)
|
|
262
|
+
if match:
|
|
263
|
+
address = int(match[1], base=0x10)
|
|
264
|
+
return self.add(address)
|
|
265
|
+
value = self.get(attribute_name)
|
|
266
|
+
if value is None:
|
|
267
|
+
raise SymbolAbsentError(f"SymbolList object has no attribute '{attribute_name}'")
|
|
268
|
+
return value
|
|
269
|
+
|
|
270
|
+
def __dir__(self):
|
|
271
|
+
self._populate_cache()
|
|
272
|
+
|
|
273
|
+
# Return normal attributes and symbol names
|
|
274
|
+
return chain(super().__dir__(), self._symbols_by_name.keys())
|
|
275
|
+
|
|
276
|
+
def _get_lldb_symbol_from_name(self, name: str, address: Optional[int] = None) \
|
|
277
|
+
-> Optional[Tuple[lldb.SBSymbol, lldb.SBAddress, str, int, int]]:
|
|
278
|
+
lldb_symbol_context_list = list(self._hilda.target.FindSymbols(name))
|
|
279
|
+
|
|
280
|
+
for lldb_symbol_context in list(lldb_symbol_context_list):
|
|
281
|
+
# Verify because FindSymbols finds `_Z3foov` when looking for `foo`
|
|
282
|
+
if lldb_symbol_context.symbol.name != name:
|
|
283
|
+
lldb_symbol_context_list.remove(lldb_symbol_context)
|
|
284
|
+
self._hilda.log_debug(f'Ignoring symbol {lldb_symbol_context.symbol.name} (similar to {name})')
|
|
285
|
+
|
|
286
|
+
if address is not None:
|
|
287
|
+
for lldb_symbol_context in list(lldb_symbol_context_list):
|
|
288
|
+
lldb_symbol_context_address = lldb_symbol_context.symbol.GetStartAddress().GetLoadAddress(
|
|
289
|
+
self._hilda.target)
|
|
290
|
+
if lldb_symbol_context_address != address:
|
|
291
|
+
lldb_symbol_context_list.remove(lldb_symbol_context)
|
|
292
|
+
self._hilda.log_debug(f'Ignoring symbol {name}@0x{lldb_symbol_context_address:016X} '
|
|
293
|
+
f'(beacause address is not 0x{address:016X})')
|
|
294
|
+
|
|
295
|
+
symbols = []
|
|
296
|
+
for lldb_symbol_context in lldb_symbol_context_list:
|
|
297
|
+
symbol = self._get_lldb_symbol(lldb_symbol_context.symbol)
|
|
298
|
+
if symbol is None:
|
|
299
|
+
self._hilda.log_debug(f'Ignoring symbol {lldb_symbol_context} (failed to convert)')
|
|
300
|
+
continue
|
|
301
|
+
|
|
302
|
+
if address is not None:
|
|
303
|
+
lldb_symbol, lldb_address, symbol_name, symbol_address, symbol_type = symbol
|
|
304
|
+
if address != symbol_address:
|
|
305
|
+
continue
|
|
306
|
+
|
|
307
|
+
symbols.append(symbol)
|
|
308
|
+
|
|
309
|
+
if len(symbols) == 0:
|
|
310
|
+
return None
|
|
311
|
+
|
|
312
|
+
# TODO: Should we really pick the first? Maybe the last? Something else?
|
|
313
|
+
# if len(lldb_symbols) != 1:
|
|
314
|
+
# # Error out if we found multiple symbols with the same name and same address
|
|
315
|
+
# raise KeyError((name, address))
|
|
316
|
+
|
|
317
|
+
return symbols[0]
|
|
318
|
+
|
|
319
|
+
def _get_lldb_symbol(self, value: Union[int, str, HildaSymbolId, Symbol, lldb.SBAddress, lldb.SBSymbol]) \
|
|
320
|
+
-> Optional[Tuple[lldb.SBSymbol, lldb.SBAddress, str, int, int]]:
|
|
321
|
+
if isinstance(value, Symbol):
|
|
322
|
+
symbol = value
|
|
323
|
+
return self._get_lldb_symbol(symbol.id)
|
|
324
|
+
elif isinstance(value, int):
|
|
325
|
+
address = value & 0xFFFFFFFFFFFFFFFF
|
|
326
|
+
lldb_address = self._hilda.target.ResolveLoadAddress(address)
|
|
327
|
+
return self._get_lldb_symbol(lldb_address)
|
|
328
|
+
elif isinstance(value, tuple): # HildaSymbolId
|
|
329
|
+
if len(value) != 2:
|
|
330
|
+
raise TypeError()
|
|
331
|
+
name, address = value
|
|
332
|
+
if not (name is None or isinstance(name, str)):
|
|
333
|
+
raise TypeError()
|
|
334
|
+
if not (isinstance(address, int)):
|
|
335
|
+
raise TypeError()
|
|
336
|
+
|
|
337
|
+
if name is None:
|
|
338
|
+
return self._get_lldb_symbol(address)
|
|
339
|
+
else:
|
|
340
|
+
return self._get_lldb_symbol_from_name(name, address)
|
|
341
|
+
elif isinstance(value, str):
|
|
342
|
+
name = value
|
|
343
|
+
return self._get_lldb_symbol_from_name(name)
|
|
344
|
+
elif isinstance(value, lldb.SBAddress):
|
|
345
|
+
lldb_address = value
|
|
346
|
+
lldb_symbol_context = self._hilda.target.ResolveSymbolContextForAddress(lldb_address,
|
|
347
|
+
lldb.eSymbolContextEverything)
|
|
348
|
+
lldb_symbol = lldb_symbol_context.symbol
|
|
349
|
+
|
|
350
|
+
address = lldb_address.GetLoadAddress(self._hilda.target)
|
|
351
|
+
lldb_symbol_address = lldb_symbol.GetStartAddress().GetLoadAddress(self._hilda.target)
|
|
352
|
+
if address != lldb_symbol_address:
|
|
353
|
+
return None
|
|
354
|
+
|
|
355
|
+
return self._get_lldb_symbol(lldb_symbol)
|
|
356
|
+
elif isinstance(value, lldb.SBSymbol):
|
|
357
|
+
lldb_symbol = value
|
|
358
|
+
|
|
359
|
+
# Ignore symbols not having a real name
|
|
360
|
+
symbol_name = lldb_symbol.GetName()
|
|
361
|
+
if symbol_name in ('<redacted>',):
|
|
362
|
+
return None
|
|
363
|
+
|
|
364
|
+
# Ignore symbols not having a real address
|
|
365
|
+
lldb_address = lldb_symbol.GetStartAddress()
|
|
366
|
+
symbol_address = lldb_address.GetLoadAddress(self._hilda.target)
|
|
367
|
+
if symbol_address == 0xffffffffffffffff:
|
|
368
|
+
return None
|
|
369
|
+
|
|
370
|
+
# Ignore symbols not having a useful type
|
|
371
|
+
symbol_type = lldb_symbol.GetType()
|
|
372
|
+
if symbol_type not in (lldb.eSymbolTypeCode,
|
|
373
|
+
lldb.eSymbolTypeRuntime,
|
|
374
|
+
lldb.eSymbolTypeData,
|
|
375
|
+
lldb.eSymbolTypeObjCMetaClass):
|
|
376
|
+
return None
|
|
377
|
+
|
|
378
|
+
return (lldb_symbol, lldb_address, symbol_name, symbol_address, symbol_type)
|
|
379
|
+
else:
|
|
380
|
+
raise TypeError()
|
|
381
|
+
|
|
382
|
+
def _add_lldb_symbol(self, symbol_name: str, symbol_address: int, symbol_type: str, symbol_size) -> lldb.SBSymbol:
|
|
383
|
+
with NamedTemporaryFile(mode='w+', suffix='.json') as symbols_file:
|
|
384
|
+
lldb_address = self._hilda.target.ResolveLoadAddress(symbol_address)
|
|
385
|
+
lldb_module = lldb_address.module
|
|
386
|
+
symbol_file_address = lldb_address.GetFileAddress()
|
|
387
|
+
|
|
388
|
+
# Create symbol file
|
|
389
|
+
data = {
|
|
390
|
+
"triple": lldb_module.GetTriple(),
|
|
391
|
+
"uuid": lldb_module.GetUUIDString(),
|
|
392
|
+
"symbols": [{
|
|
393
|
+
"name": symbol_name,
|
|
394
|
+
"type": symbol_type,
|
|
395
|
+
"size": symbol_size,
|
|
396
|
+
"address": symbol_file_address,
|
|
397
|
+
}],
|
|
398
|
+
}
|
|
399
|
+
json.dump(data, symbols_file)
|
|
400
|
+
symbols_file.flush()
|
|
401
|
+
|
|
402
|
+
# Add symbol from file
|
|
403
|
+
symbols_before = lldb_module.FindSymbols(symbol_name)
|
|
404
|
+
if len(symbols_before) != 0:
|
|
405
|
+
raise Exception(
|
|
406
|
+
f'Failed to add symbol {symbol_name} to {lldb_module.file}'
|
|
407
|
+
f' (symbol already exists {symbols_before})')
|
|
408
|
+
|
|
409
|
+
result = self._hilda.lldb_handle_command(f'target symbols add {shlex.quote(symbols_file.name)}',
|
|
410
|
+
capture_output=True)
|
|
411
|
+
|
|
412
|
+
# Verify command executed as expected
|
|
413
|
+
if result is None:
|
|
414
|
+
raise Exception(f'Failed to add symbol {symbol_name} to {lldb_module.file}')
|
|
415
|
+
expected_result = f"symbol file '{symbols_file.name}' has been added to '{lldb_module.file}'\n"
|
|
416
|
+
if expected_result != result:
|
|
417
|
+
raise Exception(
|
|
418
|
+
f'Failed to add symbol {symbol_name} to {lldb_module.file}'
|
|
419
|
+
f' (expected: {json.dumps(expected_result)}, output: {json.dumps(result)})')
|
|
420
|
+
|
|
421
|
+
# Verify the symbol was added
|
|
422
|
+
symbols_after = lldb_module.FindSymbols(symbol_name)
|
|
423
|
+
if len(symbols_after) != 1:
|
|
424
|
+
raise Exception(f'Failed to add symbol {symbol_name} to {lldb_module.file}')
|
|
425
|
+
|
|
426
|
+
return self.get((symbol_name, symbol_address))
|
|
427
|
+
|
|
428
|
+
# Actions
|
|
429
|
+
|
|
430
|
+
def bp(self, callback=None, **args):
|
|
431
|
+
"""
|
|
432
|
+
Place a breakpoint on all symbols in current list.
|
|
433
|
+
Look for the bp command for more details.
|
|
434
|
+
:param callback: callback function to be executed upon an hit
|
|
435
|
+
:param args: optional args for the bp command
|
|
436
|
+
"""
|
|
437
|
+
for v in self.values():
|
|
438
|
+
v.bp(callback, **args)
|
|
439
|
+
|
|
440
|
+
def monitor(self, **args):
|
|
441
|
+
"""
|
|
442
|
+
Perform monitor for all symbols in current list.
|
|
443
|
+
See monitor command for more details.
|
|
444
|
+
:param args: given arguments for monitor command
|
|
445
|
+
"""
|
|
446
|
+
for (name, address), symbol in self.items():
|
|
447
|
+
options = args.copy()
|
|
448
|
+
if name is None:
|
|
449
|
+
continue
|
|
450
|
+
if self._hilda.configs.objc_verbose_monitor:
|
|
451
|
+
arg_count = name.count(':')
|
|
452
|
+
if arg_count > 0:
|
|
453
|
+
arg_count = min(6, arg_count)
|
|
454
|
+
options['expr'] = {f'$arg{i + 3}': 'po' for i in range(arg_count)}
|
|
455
|
+
name = options.get('name', name)
|
|
456
|
+
address.monitor(name=name, **options)
|
|
457
|
+
|
|
458
|
+
# Filters
|
|
459
|
+
|
|
460
|
+
def __sub__(self, other: 'SymbolList') -> 'SymbolList':
|
|
461
|
+
retval = SymbolList(self._hilda)
|
|
462
|
+
for v in self.values():
|
|
463
|
+
if v not in other:
|
|
464
|
+
retval.add(v)
|
|
465
|
+
return retval
|
|
466
|
+
|
|
467
|
+
def __add__(self, other: 'SymbolList') -> 'SymbolList':
|
|
468
|
+
retval = SymbolList(self._hilda)
|
|
469
|
+
for v in other.values():
|
|
470
|
+
retval.add(v)
|
|
471
|
+
for v in self.values():
|
|
472
|
+
retval.add(v)
|
|
473
|
+
return retval
|
|
474
|
+
|
|
475
|
+
def filter_by_module(self, substring: str) -> 'SymbolList':
|
|
476
|
+
"""
|
|
477
|
+
Filter symbols who's module name contains the provided substring
|
|
478
|
+
:return: reduced symbol list
|
|
479
|
+
"""
|
|
480
|
+
|
|
481
|
+
def optimized_iter():
|
|
482
|
+
if self._global is self:
|
|
483
|
+
for lldb_module in self._hilda.target.modules:
|
|
484
|
+
if substring not in lldb_module.file.basename:
|
|
485
|
+
continue
|
|
486
|
+
|
|
487
|
+
for lldb_symbol in lldb_module.symbols:
|
|
488
|
+
symbol = self.get(lldb_symbol)
|
|
489
|
+
|
|
490
|
+
if symbol is None:
|
|
491
|
+
# This should only happen if we do not want to expose certain symbols
|
|
492
|
+
continue
|
|
493
|
+
|
|
494
|
+
yield symbol
|
|
495
|
+
else:
|
|
496
|
+
for symbol in self:
|
|
497
|
+
yield symbol
|
|
498
|
+
|
|
499
|
+
retval = SymbolList(self._hilda)
|
|
500
|
+
for symbol in optimized_iter():
|
|
501
|
+
if substring in symbol.filename:
|
|
502
|
+
retval.add(symbol)
|
|
503
|
+
|
|
504
|
+
return retval
|
|
505
|
+
|
|
506
|
+
def filter_symbol_type(self, lldb_type) -> 'SymbolList':
|
|
507
|
+
"""
|
|
508
|
+
Filter by LLDB symbol types (for example: lldb.eSymbolTypeCode,
|
|
509
|
+
lldb.eSymbolTypeData, ...)
|
|
510
|
+
:param lldb_type: symbol type from LLDB consts
|
|
511
|
+
:return: symbols matching the type filter
|
|
512
|
+
"""
|
|
513
|
+
retval = SymbolList(self._hilda)
|
|
514
|
+
for v in self.values():
|
|
515
|
+
if v.type_ == lldb_type:
|
|
516
|
+
retval.add(v)
|
|
517
|
+
return retval
|
|
518
|
+
|
|
519
|
+
def filter_code_symbols(self) -> 'SymbolList':
|
|
520
|
+
"""
|
|
521
|
+
Filter only code symbols
|
|
522
|
+
:return: symbols with type lldb.eSymbolTypeCode
|
|
523
|
+
"""
|
|
524
|
+
return self.filter_symbol_type(lldb.eSymbolTypeCode)
|
|
525
|
+
|
|
526
|
+
def filter_data_symbols(self):
|
|
527
|
+
"""
|
|
528
|
+
Filter only data symbols
|
|
529
|
+
:return: symbols with type lldb.eSymbolTypeCode
|
|
530
|
+
"""
|
|
531
|
+
return self.filter_symbol_type(lldb.eSymbolTypeData)
|
|
532
|
+
|
|
533
|
+
def filter_objc_classes(self):
|
|
534
|
+
"""
|
|
535
|
+
Filter only objc meta classes
|
|
536
|
+
:return: symbols with type lldb.eSymbolTypeObjCMetaClass
|
|
537
|
+
"""
|
|
538
|
+
return self.filter_symbol_type(lldb.eSymbolTypeObjCMetaClass)
|
|
539
|
+
|
|
540
|
+
def filter_startswith(self, exp, case_sensitive=True):
|
|
541
|
+
"""
|
|
542
|
+
Filter only symbols with given prefix
|
|
543
|
+
:param exp: prefix
|
|
544
|
+
:param case_sensitive: is case sensitive
|
|
545
|
+
:return: reduced symbol list
|
|
546
|
+
"""
|
|
547
|
+
if not case_sensitive:
|
|
548
|
+
exp = exp.lower()
|
|
549
|
+
|
|
550
|
+
retval = SymbolList(self._hilda)
|
|
551
|
+
for v in self.values():
|
|
552
|
+
name = v.lldb_name
|
|
553
|
+
if not case_sensitive:
|
|
554
|
+
name = name.lower()
|
|
555
|
+
if name.startswith(exp):
|
|
556
|
+
retval.add(v)
|
|
557
|
+
return retval
|
|
558
|
+
|
|
559
|
+
def filter_endswith(self, exp, case_sensitive=True):
|
|
560
|
+
"""
|
|
561
|
+
Filter only symbols with given prefix
|
|
562
|
+
:param exp: prefix
|
|
563
|
+
:param case_sensitive: is case sensitive
|
|
564
|
+
:return: reduced symbol list
|
|
565
|
+
"""
|
|
566
|
+
if not case_sensitive:
|
|
567
|
+
exp = exp.lower()
|
|
568
|
+
|
|
569
|
+
retval = SymbolList(self._hilda)
|
|
570
|
+
for v in self.values():
|
|
571
|
+
name = v.lldb_name
|
|
572
|
+
if not case_sensitive:
|
|
573
|
+
name = name.lower()
|
|
574
|
+
if name.endswith(exp):
|
|
575
|
+
retval.add(v)
|
|
576
|
+
return retval
|
|
577
|
+
|
|
578
|
+
def filter_name_contains(self, exp, case_sensitive=True):
|
|
579
|
+
"""
|
|
580
|
+
Filter symbols containing a given expression
|
|
581
|
+
:param exp: given expression
|
|
582
|
+
:param case_sensitive: is case sensitive
|
|
583
|
+
:return: reduced symbol list
|
|
584
|
+
"""
|
|
585
|
+
if not case_sensitive:
|
|
586
|
+
exp = exp.lower()
|
|
587
|
+
|
|
588
|
+
retval = SymbolList(self._hilda)
|
|
589
|
+
for v in self.values():
|
|
590
|
+
name = v.lldb_name
|
|
591
|
+
if not case_sensitive:
|
|
592
|
+
name = name.lower()
|
|
593
|
+
if exp in name:
|
|
594
|
+
retval.add(v)
|
|
595
|
+
return retval
|