pelfy 1.0.0__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.
- pelfy/__init__.py +563 -0
- pelfy/fields_data.py +577 -0
- pelfy/output_formatter.py +60 -0
- pelfy-1.0.0.dist-info/LICENSE +21 -0
- pelfy-1.0.0.dist-info/METADATA +123 -0
- pelfy-1.0.0.dist-info/RECORD +8 -0
- pelfy-1.0.0.dist-info/WHEEL +5 -0
- pelfy-1.0.0.dist-info/top_level.txt +1 -0
pelfy/__init__.py
ADDED
|
@@ -0,0 +1,563 @@
|
|
|
1
|
+
from . import fields_data as fdat
|
|
2
|
+
from . import output_formatter
|
|
3
|
+
from typing import TypeVar, Literal, Iterable, Generic, Iterator, Generator
|
|
4
|
+
|
|
5
|
+
_T = TypeVar('_T')
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def open_elf_file(file_path: str) -> 'elf_file':
|
|
9
|
+
"""Reads ELF data from file
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
file_path: path of the ELF file
|
|
13
|
+
|
|
14
|
+
Returns:
|
|
15
|
+
elf_file object
|
|
16
|
+
"""
|
|
17
|
+
with open(file_path, mode='rb') as f:
|
|
18
|
+
return elf_file(f.read())
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class elf_symbol():
|
|
22
|
+
"""A class for representing data of an ELF symbol
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
file: ELF file object
|
|
26
|
+
|
|
27
|
+
fields: symbol header fields
|
|
28
|
+
|
|
29
|
+
index: Absolut index in the symbol table
|
|
30
|
+
|
|
31
|
+
Attributes:
|
|
32
|
+
file: Points to the parent ELF file object.
|
|
33
|
+
|
|
34
|
+
name: Name of the symbol
|
|
35
|
+
|
|
36
|
+
index: Absolut index in the symbol table
|
|
37
|
+
|
|
38
|
+
info: Type of the symbol
|
|
39
|
+
|
|
40
|
+
description: Description of the symbol type
|
|
41
|
+
|
|
42
|
+
stb: visibility of the symbol (local, global, etc.)
|
|
43
|
+
|
|
44
|
+
stb_description: Description of the symbol visibility
|
|
45
|
+
|
|
46
|
+
fields: All symbol header fields as dict
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
def __init__(self, file: 'elf_file', fields: dict[str, int], index: int):
|
|
50
|
+
self.fields = fields
|
|
51
|
+
self.file = file
|
|
52
|
+
|
|
53
|
+
if file.string_table_section:
|
|
54
|
+
self.name = file.read_string(file.string_table_section['sh_offset'] + fields['st_name'])
|
|
55
|
+
else:
|
|
56
|
+
self.name = ''
|
|
57
|
+
|
|
58
|
+
self.index = index
|
|
59
|
+
|
|
60
|
+
self.info, self.description = fdat.st_info_values[fields['st_info'] & 0x0F]
|
|
61
|
+
self.stb, self.stb_description = fdat.stb_values[fields['st_info'] >> 4]
|
|
62
|
+
|
|
63
|
+
def read_data(self) -> bytes:
|
|
64
|
+
"""Returns the binary data the symbol is pointing to.
|
|
65
|
+
The offset in the ELF file is calculated by:
|
|
66
|
+
sections[symbol.st_shndx].sh_offset + symbol.st_value
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
Symbol data
|
|
70
|
+
"""
|
|
71
|
+
offset = self.file.sections[self['st_shndx']]['sh_offset'] + self['st_value']
|
|
72
|
+
return self.file.read_bytes(offset, self['st_size'])
|
|
73
|
+
|
|
74
|
+
def read_data_hex(self) -> str:
|
|
75
|
+
return ' '.join(f'{d:02X}' for d in self.read_data())
|
|
76
|
+
|
|
77
|
+
def get_relocations(self) -> 'relocation_list':
|
|
78
|
+
"""List all relocations that are pointing to this symbol.
|
|
79
|
+
The symbol must be of type SHT_PROGBITS (program code). Therefore
|
|
80
|
+
this function lists typically all relocations that will be
|
|
81
|
+
applied to the function represented by the symbol.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
List of relocations
|
|
85
|
+
"""
|
|
86
|
+
ret: list[elf_relocation] = list()
|
|
87
|
+
section = self.file.sections[self.fields['st_shndx']]
|
|
88
|
+
assert section.type == 'SHT_PROGBITS'
|
|
89
|
+
for reloc in self.file.get_relocations():
|
|
90
|
+
if reloc.target_section == section:
|
|
91
|
+
offset = reloc['r_offset'] - self['st_value']
|
|
92
|
+
if 0 <= offset < self['st_size']:
|
|
93
|
+
ret.append(reloc)
|
|
94
|
+
return relocation_list(ret)
|
|
95
|
+
|
|
96
|
+
def __getitem__(self, key: str | int) -> int:
|
|
97
|
+
if isinstance(key, str):
|
|
98
|
+
assert key in self.fields, f'Unknown field name: {key}'
|
|
99
|
+
return self.fields[key]
|
|
100
|
+
else:
|
|
101
|
+
return list(self.fields.values())[key]
|
|
102
|
+
|
|
103
|
+
def __repr__(self) -> str:
|
|
104
|
+
return f'index {self.index}\n' +\
|
|
105
|
+
f'name {self.name}\n' +\
|
|
106
|
+
f'stb {self.stb} ({self.stb_description})\n' +\
|
|
107
|
+
f'info {self.info} ({self.description})\n' +\
|
|
108
|
+
'\n'.join(f'{k:18} {v:4}' for k, v in self.fields.items()) + '\n'
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class elf_section():
|
|
112
|
+
"""A class for representing data of an ELF section
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
file: ELF file object
|
|
116
|
+
|
|
117
|
+
fields: Section header fields
|
|
118
|
+
|
|
119
|
+
name: Name of the section
|
|
120
|
+
|
|
121
|
+
index: Absolut index in the symbol table
|
|
122
|
+
|
|
123
|
+
Attributes:
|
|
124
|
+
file: Points to the parent ELF file object.
|
|
125
|
+
|
|
126
|
+
name: Name of the section
|
|
127
|
+
|
|
128
|
+
index: Absolut index of the section
|
|
129
|
+
|
|
130
|
+
type: Type of the section
|
|
131
|
+
|
|
132
|
+
description: Description of the section type
|
|
133
|
+
|
|
134
|
+
fields: All symbol header fields as dict
|
|
135
|
+
"""
|
|
136
|
+
def __init__(self, file: 'elf_file', fields: dict[str, int], name: str, index: int):
|
|
137
|
+
self.fields = fields
|
|
138
|
+
self.file = file
|
|
139
|
+
self.index = index
|
|
140
|
+
self.name = name
|
|
141
|
+
|
|
142
|
+
if fields['sh_type'] > 0x60000000:
|
|
143
|
+
# Range for OS, compiler and application specific types
|
|
144
|
+
self.description = [v for k, v in fdat.section_header_types_ex.items() if k >= fields['sh_type']][0]
|
|
145
|
+
self.type = str(hex(fields['sh_type']))
|
|
146
|
+
elif fields['sh_type'] in fdat.section_header_types:
|
|
147
|
+
self.type, self.description = fdat.section_header_types[fields['sh_type']]
|
|
148
|
+
else:
|
|
149
|
+
self.description = ''
|
|
150
|
+
self.type = str(hex(fields['sh_type']))
|
|
151
|
+
|
|
152
|
+
def read_data(self) -> bytes:
|
|
153
|
+
"""Returns the binary data from the section.
|
|
154
|
+
The offset in the ELF file is given by: section.sh_offset
|
|
155
|
+
|
|
156
|
+
Returns:
|
|
157
|
+
Data of the section
|
|
158
|
+
"""
|
|
159
|
+
return self.file.read_bytes(self['sh_offset'], self['sh_size'])
|
|
160
|
+
|
|
161
|
+
def get_data_hex(self) -> str:
|
|
162
|
+
data = self.file.read_bytes(self['sh_offset'], self['sh_size'])
|
|
163
|
+
return ' '.join(f'{d:02X}' for d in data)
|
|
164
|
+
|
|
165
|
+
def __getitem__(self, key: str | int) -> int:
|
|
166
|
+
if isinstance(key, str):
|
|
167
|
+
assert key in self.fields, f'Unknown field name: {key}'
|
|
168
|
+
return self.fields[key]
|
|
169
|
+
else:
|
|
170
|
+
return list(self.fields.values())[key]
|
|
171
|
+
|
|
172
|
+
def __repr__(self) -> str:
|
|
173
|
+
return f'index {self.index}\n' +\
|
|
174
|
+
f'name {self.name}\n' +\
|
|
175
|
+
f'type {self.type} ({self.description})\n' +\
|
|
176
|
+
'\n'.join(f"{k:18} {v:4} {fdat.section_header[k]['description']}" for k, v in self.fields.items()) + '\n'
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
class elf_relocation():
|
|
180
|
+
"""A class for representing data of a relocation
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
file: ELF file object
|
|
184
|
+
|
|
185
|
+
fields: Relocation header fields
|
|
186
|
+
|
|
187
|
+
symbol_index: Index of the symbol to relocate in the symbol table
|
|
188
|
+
|
|
189
|
+
relocation_type: Type of the relocation (numeric)
|
|
190
|
+
|
|
191
|
+
sh_info: Index of the section this relocation applies to
|
|
192
|
+
|
|
193
|
+
index: Absolut index of the relocation in the associated relocation section
|
|
194
|
+
|
|
195
|
+
Attributes:
|
|
196
|
+
file: Points to the parent ELF file object.
|
|
197
|
+
|
|
198
|
+
index: Absolut index of the relocation the associated relocation section
|
|
199
|
+
|
|
200
|
+
type: Type of the relocation
|
|
201
|
+
|
|
202
|
+
calculation: Description of the relocation calculation
|
|
203
|
+
|
|
204
|
+
target_section: Pointing to the section that is relocation applies to
|
|
205
|
+
|
|
206
|
+
fields: All relocation header fields as dict
|
|
207
|
+
"""
|
|
208
|
+
def __init__(self, file: 'elf_file', fields: dict[str, int], symbol_index: int,
|
|
209
|
+
relocation_type: int, sh_info: int, index: int):
|
|
210
|
+
self.fields = fields
|
|
211
|
+
self.file = file
|
|
212
|
+
self.index = index
|
|
213
|
+
self.symbol = file.symbols[symbol_index]
|
|
214
|
+
reloc_types = fdat.relocation_table_types.get(file.architecture)
|
|
215
|
+
if reloc_types and relocation_type in reloc_types:
|
|
216
|
+
self.calculation = reloc_types[relocation_type][2]
|
|
217
|
+
self.type = reloc_types[relocation_type][0]
|
|
218
|
+
else:
|
|
219
|
+
self.calculation = ''
|
|
220
|
+
self.type = str(relocation_type)
|
|
221
|
+
self.target_section: elf_section = file.sections[sh_info]
|
|
222
|
+
|
|
223
|
+
def __getitem__(self, key: str | int) -> int:
|
|
224
|
+
if isinstance(key, str):
|
|
225
|
+
assert key in self.fields, f'Unknown field name: {key}'
|
|
226
|
+
return self.fields[key]
|
|
227
|
+
else:
|
|
228
|
+
return list(self.fields.values())[key]
|
|
229
|
+
|
|
230
|
+
def __repr__(self) -> str:
|
|
231
|
+
return f'index {self.symbol.index}\n' +\
|
|
232
|
+
f'symbol {self.symbol.name}\n' +\
|
|
233
|
+
f'relocation type {self.type} ({self.calculation})\n' +\
|
|
234
|
+
'\n'.join(f'{k:18} {v:4}' for k, v in self.fields.items()) + '\n'
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
class elf_list(Generic[_T]):
|
|
238
|
+
"""A generic class for representing a list of ELF data items
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
data: Iterable of ELF data items
|
|
242
|
+
"""
|
|
243
|
+
def __init__(self, data: Iterable[_T]):
|
|
244
|
+
self._data = list(data)
|
|
245
|
+
|
|
246
|
+
def __getitem__(self, key: int | str) -> _T:
|
|
247
|
+
if isinstance(key, str):
|
|
248
|
+
elements = [el for el in self._data if getattr(el, 'name', '') == key]
|
|
249
|
+
assert elements, f'Unknown name: {key}'
|
|
250
|
+
return elements[0]
|
|
251
|
+
else:
|
|
252
|
+
return self._data.__getitem__(key)
|
|
253
|
+
|
|
254
|
+
def __len__(self) -> int:
|
|
255
|
+
return len(self._data)
|
|
256
|
+
|
|
257
|
+
def __iter__(self) -> Iterator[_T]:
|
|
258
|
+
return iter(self._data)
|
|
259
|
+
|
|
260
|
+
def _compact_table(self) -> tuple[list[str], list[list[str | int]], list[str]]:
|
|
261
|
+
return [], [[]], []
|
|
262
|
+
|
|
263
|
+
def _repr_table(self, format: output_formatter.table_format, raw_data: bool = False) -> str:
|
|
264
|
+
if raw_data and len(self):
|
|
265
|
+
table_dict: list[dict[str, int]] = [el.__dict__.get('fields', {' ': 0}) for el in self]
|
|
266
|
+
columns = list(table_dict[0].keys())
|
|
267
|
+
data: list[list[str | int]] = [list(el.values()) for el in table_dict]
|
|
268
|
+
radj = columns
|
|
269
|
+
else:
|
|
270
|
+
columns, data, radj = self._compact_table()
|
|
271
|
+
return output_formatter.generate_table(data, columns, right_adj_col=radj, format=format)
|
|
272
|
+
|
|
273
|
+
def to_dict_list(self) -> list[dict[str, str | int]]:
|
|
274
|
+
"""Exporting the ELF item data table to a list of dicts. It can be used with pandas:
|
|
275
|
+
df = pandas.DataFrame(elements.to_dict_list())
|
|
276
|
+
|
|
277
|
+
Returns:
|
|
278
|
+
Table data
|
|
279
|
+
"""
|
|
280
|
+
columns, data, _ = self._compact_table()
|
|
281
|
+
return [{k: v for k, v in zip(columns, row)} for row in data]
|
|
282
|
+
|
|
283
|
+
def to_html(self) -> str:
|
|
284
|
+
"""Exporting the ELF item data table to HTML.
|
|
285
|
+
|
|
286
|
+
Returns:
|
|
287
|
+
HTML table
|
|
288
|
+
"""
|
|
289
|
+
return self._repr_table('html')
|
|
290
|
+
|
|
291
|
+
def to_markdown(self) -> str:
|
|
292
|
+
"""Exporting the ELF item data table to markdown.
|
|
293
|
+
|
|
294
|
+
Returns:
|
|
295
|
+
Markdown table
|
|
296
|
+
"""
|
|
297
|
+
return self._repr_table('markdown')
|
|
298
|
+
|
|
299
|
+
def __repr__(self) -> str:
|
|
300
|
+
return self._repr_table('text')
|
|
301
|
+
|
|
302
|
+
def _repr_html_(self) -> str:
|
|
303
|
+
return self._repr_table('html')
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
class section_list(elf_list[elf_section]):
|
|
307
|
+
"""A class for representing a list of ELF section
|
|
308
|
+
"""
|
|
309
|
+
def _compact_table(self) -> tuple[list[str], list[list[int | str]], list[str]]:
|
|
310
|
+
columns = ['index', 'name', 'type', 'description']
|
|
311
|
+
data: list[list[str | int]] = [[item.index, item.name, item.type,
|
|
312
|
+
item.description] for item in self]
|
|
313
|
+
return columns, data, ['index']
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
class symbol_list(elf_list[elf_symbol]):
|
|
317
|
+
"""A class for representing a list of ELF symbols
|
|
318
|
+
"""
|
|
319
|
+
def _compact_table(self) -> tuple[list[str], list[list[int | str]], list[str]]:
|
|
320
|
+
columns = ['index', 'name', 'info', 'size', 'stb', 'description']
|
|
321
|
+
data: list[list[str | int]] = [[item.index, item.name, item.info, item.fields['st_size'],
|
|
322
|
+
item.stb, item.description] for item in self]
|
|
323
|
+
return columns, data, ['index', 'size']
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
class relocation_list(elf_list[elf_relocation]):
|
|
327
|
+
"""A class for representing a list of ELF relocations
|
|
328
|
+
"""
|
|
329
|
+
def _compact_table(self) -> tuple[list[str], list[list[int | str]], list[str]]:
|
|
330
|
+
columns = ['index', 'symbol name', 'type', 'calculation']
|
|
331
|
+
data: list[list[str | int]] = [[item.index, item.symbol.name, item.type,
|
|
332
|
+
item.calculation] for item in self]
|
|
333
|
+
return columns, data, ['index']
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
class elf_file:
|
|
337
|
+
"""A class for representing data of an ELF file in a structured form
|
|
338
|
+
|
|
339
|
+
Args:
|
|
340
|
+
data: binary ELF data
|
|
341
|
+
|
|
342
|
+
Attributes:
|
|
343
|
+
byteorder: Byte order of the architecture 'little' or 'big'
|
|
344
|
+
(based on e_ident[EI_DATA])
|
|
345
|
+
|
|
346
|
+
bit_width: Bit with of the architecture: 32 or 64 (based on
|
|
347
|
+
e_ident[EI_CLASS])
|
|
348
|
+
|
|
349
|
+
architecture: Name of the architecture (based on e_machine)
|
|
350
|
+
|
|
351
|
+
fields: All ELF header fields as dict
|
|
352
|
+
|
|
353
|
+
sections: A list of all ELF sections
|
|
354
|
+
|
|
355
|
+
symbols: A list of all ELF symbols
|
|
356
|
+
|
|
357
|
+
functions: A list of all function symbols (STT_FUNC)
|
|
358
|
+
|
|
359
|
+
objects: A list of all variable/object symbols (STT_OBJECT)
|
|
360
|
+
|
|
361
|
+
code_relocations: A list of all code relocations (.rela.text and .rel.text)
|
|
362
|
+
|
|
363
|
+
symbol_table_section: The symbol table section (first section with
|
|
364
|
+
the type SHT_SYMTAB)
|
|
365
|
+
|
|
366
|
+
string_table_section: The string table section (first section with
|
|
367
|
+
the name .strtab)
|
|
368
|
+
"""
|
|
369
|
+
def __init__(self, data: bytes):
|
|
370
|
+
self._data = data
|
|
371
|
+
|
|
372
|
+
# Defaults required for function _read_int_from_elf_field
|
|
373
|
+
self.bit_width = 32
|
|
374
|
+
self.byteorder: Literal['little', 'big'] = 'little'
|
|
375
|
+
|
|
376
|
+
assert self._read_bytes_from_elf_field('e_ident[EI_MAG]') == bytes([0x7F, 0x45, 0x4c, 0x46]), 'Not an ELF file'
|
|
377
|
+
|
|
378
|
+
self.bit_width = {1: 32, 2: 64}[self._read_int_from_elf_field('e_ident[EI_CLASS]')]
|
|
379
|
+
|
|
380
|
+
byte_order = self._read_int_from_elf_field('e_ident[EI_DATA]')
|
|
381
|
+
assert byte_order in [1, 2], 'Invalid byte order value e_ident[EI_DATA]'
|
|
382
|
+
self.byteorder = 'little' if byte_order == 1 else 'big'
|
|
383
|
+
|
|
384
|
+
self.fields = {fn: self._read_int_from_elf_field(fn) for fn in fdat.elf_header_field.keys()}
|
|
385
|
+
|
|
386
|
+
arch_entr = fdat.e_machine_dict.get(self.fields['e_machine'])
|
|
387
|
+
self.architecture = arch_entr[0] if arch_entr else str(self.fields['e_machine'])
|
|
388
|
+
|
|
389
|
+
section_data = list(self._list_sections())
|
|
390
|
+
name_addr: dict[str, int] = section_data[self.fields['e_shstrndx']] if section_data else dict()
|
|
391
|
+
section_names = (self.read_string(name_addr['sh_offset'] + f['sh_name']) for f in section_data)
|
|
392
|
+
|
|
393
|
+
self.sections = section_list(elf_section(self, sd, sn, i)
|
|
394
|
+
for i, (sd, sn) in enumerate(zip(section_data, section_names)))
|
|
395
|
+
|
|
396
|
+
ret_sections = [sh for sh in self.sections if sh.type == 'SHT_SYMTAB']
|
|
397
|
+
self.symbol_table_section = ret_sections[0] if ret_sections else None
|
|
398
|
+
|
|
399
|
+
ret_sections = [sh for sh in self.sections if sh.name == '.strtab']
|
|
400
|
+
self.string_table_section = ret_sections[0] if ret_sections else None
|
|
401
|
+
|
|
402
|
+
self.symbols = symbol_list(self._list_symbols())
|
|
403
|
+
|
|
404
|
+
self.functions = symbol_list(s for s in self.symbols if s.info == 'STT_FUNC')
|
|
405
|
+
self.objects = symbol_list(s for s in self.symbols if s.info == 'STT_OBJECT')
|
|
406
|
+
|
|
407
|
+
self.code_relocations = self.get_relocations(['.rela.text', '.rel.text'])
|
|
408
|
+
|
|
409
|
+
def _list_sections(self) -> Generator[dict[str, int], None, None]:
|
|
410
|
+
for i in range(self.fields['e_shnum']):
|
|
411
|
+
offs = self.fields['e_shoff'] + i * self.fields['e_shentsize']
|
|
412
|
+
yield {fn: self._read_from_sh_field(offs, fn) for fn in fdat.section_header.keys()}
|
|
413
|
+
|
|
414
|
+
def _list_symbols(self) -> Generator[elf_symbol, None, None]:
|
|
415
|
+
if self.symbol_table_section:
|
|
416
|
+
offs = self.symbol_table_section['sh_offset']
|
|
417
|
+
|
|
418
|
+
for j, i in enumerate(range(offs, self.symbol_table_section['sh_size'] + offs, self.symbol_table_section['sh_entsize'])):
|
|
419
|
+
ret = {'st_name': self.read_int(i, 4)}
|
|
420
|
+
|
|
421
|
+
if self.bit_width == 32:
|
|
422
|
+
ret['st_value'] = self.read_int(i + 4, 4)
|
|
423
|
+
ret['st_size'] = self.read_int(i + 8, 4)
|
|
424
|
+
ret['st_info'] = self.read_int(i + 12, 1)
|
|
425
|
+
ret['st_other'] = self.read_int(i + 13, 1)
|
|
426
|
+
ret['st_shndx'] = self.read_int(i + 14, 2)
|
|
427
|
+
elif self.bit_width == 64:
|
|
428
|
+
ret['st_info'] = self.read_int(i + 4, 1)
|
|
429
|
+
ret['st_other'] = self.read_int(i + 5, 1)
|
|
430
|
+
ret['st_shndx'] = self.read_int(i + 6, 2)
|
|
431
|
+
ret['st_value'] = self.read_int(i + 8, 8)
|
|
432
|
+
ret['st_size'] = self.read_int(i + 16, 8)
|
|
433
|
+
|
|
434
|
+
yield elf_symbol(self, ret, j)
|
|
435
|
+
|
|
436
|
+
def get_relocations(self, reloc_section: elf_section | str | list[str] | None = None) -> relocation_list:
|
|
437
|
+
"""List relocations.
|
|
438
|
+
|
|
439
|
+
Args:
|
|
440
|
+
reloc_section: Specifies the relocation section from which the
|
|
441
|
+
relocations should be listed. It can be provided as
|
|
442
|
+
elf_section object or by its name. If not provided
|
|
443
|
+
(reloc_section=None) relocations from all relocation
|
|
444
|
+
sections are returned.
|
|
445
|
+
|
|
446
|
+
Returns:
|
|
447
|
+
List of relocations
|
|
448
|
+
"""
|
|
449
|
+
if isinstance(reloc_section, elf_section):
|
|
450
|
+
assert reloc_section.type in ('SHT_REL', 'SHT_RELA'), f'{reloc_section.name} is not a relocation section'
|
|
451
|
+
return relocation_list(self._list_relocations(reloc_section))
|
|
452
|
+
else:
|
|
453
|
+
relocations: list[elf_relocation] = list()
|
|
454
|
+
for sh in self.sections:
|
|
455
|
+
if sh.type in ('SHT_REL', 'SHT_RELA'):
|
|
456
|
+
if reloc_section is None or \
|
|
457
|
+
(isinstance(reloc_section, str) and sh.name == reloc_section) or \
|
|
458
|
+
(isinstance(reloc_section, list) and sh.name in reloc_section):
|
|
459
|
+
relocations += relocation_list(self._list_relocations(sh))
|
|
460
|
+
|
|
461
|
+
return relocation_list(relocations)
|
|
462
|
+
|
|
463
|
+
def _list_relocations(self, sh: elf_section) -> Generator[elf_relocation, None, None]:
|
|
464
|
+
"""List relocations for a elf_section.
|
|
465
|
+
|
|
466
|
+
Args:
|
|
467
|
+
elf_section: Specifies the relocation section from which the
|
|
468
|
+
relocations should be listed.
|
|
469
|
+
|
|
470
|
+
Returns:
|
|
471
|
+
Relocations from specified elf_section
|
|
472
|
+
"""
|
|
473
|
+
offs = sh['sh_offset']
|
|
474
|
+
for i, el_off in enumerate(range(offs, sh['sh_size'] + offs, sh['sh_entsize'])):
|
|
475
|
+
ret: dict[str, int] = dict()
|
|
476
|
+
|
|
477
|
+
if self.bit_width == 32:
|
|
478
|
+
ret['r_offset'] = self.read_int(el_off, 4)
|
|
479
|
+
r_info = self.read_int(el_off + 4, 4)
|
|
480
|
+
ret['r_info'] = r_info
|
|
481
|
+
ret['r_addend'] = self.read_int(el_off + 8, 4, True) if sh.type == 'SHT_RELA' else 0
|
|
482
|
+
yield elf_relocation(self, ret, r_info >> 8, r_info & 0xFF, sh['sh_info'], i)
|
|
483
|
+
elif self.bit_width == 64:
|
|
484
|
+
ret['r_offset'] = self.read_int(el_off, 8)
|
|
485
|
+
r_info = self.read_int(el_off + 8, 8)
|
|
486
|
+
ret['r_info'] = r_info
|
|
487
|
+
ret['r_addend'] = self.read_int(el_off + 16, 8, True) if sh.type == 'SHT_RELA' else 0
|
|
488
|
+
yield elf_relocation(self, ret, r_info >> 32, r_info & 0xFFFFFFFF, sh['sh_info'], i)
|
|
489
|
+
|
|
490
|
+
def read_bytes(self, offset: int, num_bytes: int) -> bytes:
|
|
491
|
+
"""Read bytes from ELF file.
|
|
492
|
+
|
|
493
|
+
Args:
|
|
494
|
+
offset: Specify first byte relative to the start of
|
|
495
|
+
the ELF file.
|
|
496
|
+
|
|
497
|
+
num_bytes: Specify the number of bytes to read.
|
|
498
|
+
|
|
499
|
+
Returns:
|
|
500
|
+
Binary data as bytes
|
|
501
|
+
"""
|
|
502
|
+
return self._data[offset:offset + num_bytes]
|
|
503
|
+
|
|
504
|
+
def read_int(self, offset: int, num_bytes: int, signed: bool = False) -> int:
|
|
505
|
+
"""Read an integer from the ELF file. Byte order is
|
|
506
|
+
selected according to the architecture (e_ident[EI_DATA]).
|
|
507
|
+
|
|
508
|
+
Args:
|
|
509
|
+
offset: Specify first byte of the integer relative to
|
|
510
|
+
the start of the ELF file.
|
|
511
|
+
|
|
512
|
+
num_bytes: Specify the size of the integer in bytes.
|
|
513
|
+
|
|
514
|
+
signed: Select if the integer is a signed integer.
|
|
515
|
+
|
|
516
|
+
Returns:
|
|
517
|
+
Integer value
|
|
518
|
+
"""
|
|
519
|
+
return int.from_bytes(self._data[offset:offset + num_bytes], self.byteorder, signed=signed)
|
|
520
|
+
|
|
521
|
+
# def int_to_bytes(self, value: int, num_bytes: int = 4, signed: bool = False) -> int:
|
|
522
|
+
# return value.to_bytes(length=num_bytes, byteorder=self.byteorder, signed=signed)
|
|
523
|
+
|
|
524
|
+
def read_string(self, offset: int, encoding: str = 'utf-8') -> str:
|
|
525
|
+
"""Read a zero-terminated text string from the ELF file.
|
|
526
|
+
|
|
527
|
+
Args:
|
|
528
|
+
offset: Specify first byte of the string relative to
|
|
529
|
+
the start of the ELF file.
|
|
530
|
+
|
|
531
|
+
encoding: Encoding used for text decoding.
|
|
532
|
+
|
|
533
|
+
Returns:
|
|
534
|
+
Text string
|
|
535
|
+
"""
|
|
536
|
+
str_end = self._data.find(b'\x00', offset)
|
|
537
|
+
return self._data[offset:str_end].decode(encoding)
|
|
538
|
+
|
|
539
|
+
def _read_int_from_elf_field(self, field_name: str) -> int:
|
|
540
|
+
field = fdat.elf_header_field[field_name]
|
|
541
|
+
offs = int(field[str(self.bit_width)], base=16)
|
|
542
|
+
byte_len = int(field['size' + str(self.bit_width)])
|
|
543
|
+
return self.read_int(offs, byte_len)
|
|
544
|
+
|
|
545
|
+
def _read_bytes_from_elf_field(self, field_name: str) -> bytes:
|
|
546
|
+
field = fdat.elf_header_field[field_name]
|
|
547
|
+
offs = int(field[str(self.bit_width)], base=16)
|
|
548
|
+
byte_len = int(field['size' + str(self.bit_width)])
|
|
549
|
+
return self.read_bytes(offs, byte_len)
|
|
550
|
+
|
|
551
|
+
def _read_from_sh_field(self, offset: int, field_name: str) -> int:
|
|
552
|
+
field = fdat.section_header[field_name]
|
|
553
|
+
offs = int(field[str(self.bit_width)], base=16) + offset
|
|
554
|
+
byte_len = int(field['size' + str(self.bit_width)])
|
|
555
|
+
return self.read_int(offs, byte_len)
|
|
556
|
+
|
|
557
|
+
def __repr__(self) -> str:
|
|
558
|
+
hf_list = ((hf, self.fields[hf['field_name']]) for hf in fdat.elf_header_field.values())
|
|
559
|
+
return '\n'.join(f"{hf['field_name']:24} {v:4} {hf['description']}" for hf, v in hf_list) + '\n'
|
|
560
|
+
|
|
561
|
+
def __getitem__(self, key: str) -> int:
|
|
562
|
+
assert key in self.fields, f'Unknown field name: {key}'
|
|
563
|
+
return self.fields[key]
|