0c6a8090ce713e25a09318247fd20445038c950d
[pyelftools.git] / elftools / elf / structs.py
1 #-------------------------------------------------------------------------------
2 # elftools: elf/structs.py
3 #
4 # Encapsulation of Construct structs for parsing an ELF file, adjusted for
5 # correct endianness and word-size.
6 #
7 # Eli Bendersky (eliben@gmail.com)
8 # This code is in the public domain
9 #-------------------------------------------------------------------------------
10 from ..construct import (
11 UBInt8, UBInt16, UBInt32, UBInt64,
12 ULInt8, ULInt16, ULInt32, ULInt64,
13 SBInt32, SLInt32, SBInt64, SLInt64,
14 Struct, Array, Enum, Padding, BitStruct, BitField, Value, String, CString
15 )
16 from ..common.construct_utils import ULEB128
17 from .enums import *
18
19
20 class ELFStructs(object):
21 """ Accessible attributes:
22
23 Elf_{byte|half|word|word64|addr|offset|sword|xword|xsword}:
24 Data chunks, as specified by the ELF standard, adjusted for
25 correct endianness and word-size.
26
27 Elf_Ehdr:
28 ELF file header
29
30 Elf_Phdr:
31 Program header
32
33 Elf_Shdr:
34 Section header
35
36 Elf_Sym:
37 Symbol table entry
38
39 Elf_Rel, Elf_Rela:
40 Entries in relocation sections
41 """
42 def __init__(self, little_endian=True, elfclass=32):
43 assert elfclass == 32 or elfclass == 64
44 self.little_endian = little_endian
45 self.elfclass = elfclass
46 self.e_type = None
47 self.e_machine = None
48 self.e_ident_osabi = None
49
50 def __getstate__(self):
51 return self.little_endian, self.elfclass, self.e_type, self.e_machine, self.e_ident_osabi
52
53 def __setstate__(self, state):
54 self.little_endian, self.elfclass, e_type, e_machine, e_osabi = state
55 self.create_basic_structs()
56 self.create_advanced_structs(e_type, e_machine, e_osabi)
57
58 def create_basic_structs(self):
59 """ Create word-size related structs and ehdr struct needed for
60 initial determining of ELF type.
61 """
62 if self.little_endian:
63 self.Elf_byte = ULInt8
64 self.Elf_half = ULInt16
65 self.Elf_word = ULInt32
66 self.Elf_word64 = ULInt64
67 self.Elf_addr = ULInt32 if self.elfclass == 32 else ULInt64
68 self.Elf_offset = self.Elf_addr
69 self.Elf_sword = SLInt32
70 self.Elf_xword = ULInt32 if self.elfclass == 32 else ULInt64
71 self.Elf_sxword = SLInt32 if self.elfclass == 32 else SLInt64
72 else:
73 self.Elf_byte = UBInt8
74 self.Elf_half = UBInt16
75 self.Elf_word = UBInt32
76 self.Elf_word64 = UBInt64
77 self.Elf_addr = UBInt32 if self.elfclass == 32 else UBInt64
78 self.Elf_offset = self.Elf_addr
79 self.Elf_sword = SBInt32
80 self.Elf_xword = UBInt32 if self.elfclass == 32 else UBInt64
81 self.Elf_sxword = SBInt32 if self.elfclass == 32 else SBInt64
82 self._create_ehdr()
83 self._create_leb128()
84 self._create_ntbs()
85
86 def create_advanced_structs(self, e_type=None, e_machine=None, e_ident_osabi=None):
87 """ Create all ELF structs except the ehdr. They may possibly depend
88 on provided e_type and/or e_machine parsed from ehdr.
89 """
90 self.e_type = e_type
91 self.e_machine = e_machine
92 self.e_ident_osabi = e_ident_osabi
93
94 self._create_phdr()
95 self._create_shdr()
96 self._create_chdr()
97 self._create_sym()
98 self._create_rel()
99 self._create_dyn()
100 self._create_sunw_syminfo()
101 self._create_gnu_verneed()
102 self._create_gnu_verdef()
103 self._create_gnu_versym()
104 self._create_gnu_abi()
105 self._create_note(e_type)
106 self._create_stabs()
107 self._create_arm_attributes()
108 self._create_elf_hash()
109 self._create_gnu_hash()
110
111 #-------------------------------- PRIVATE --------------------------------#
112
113 def _create_ehdr(self):
114 self.Elf_Ehdr = Struct('Elf_Ehdr',
115 Struct('e_ident',
116 Array(4, self.Elf_byte('EI_MAG')),
117 Enum(self.Elf_byte('EI_CLASS'), **ENUM_EI_CLASS),
118 Enum(self.Elf_byte('EI_DATA'), **ENUM_EI_DATA),
119 Enum(self.Elf_byte('EI_VERSION'), **ENUM_E_VERSION),
120 Enum(self.Elf_byte('EI_OSABI'), **ENUM_EI_OSABI),
121 self.Elf_byte('EI_ABIVERSION'),
122 Padding(7)
123 ),
124 Enum(self.Elf_half('e_type'), **ENUM_E_TYPE),
125 Enum(self.Elf_half('e_machine'), **ENUM_E_MACHINE),
126 Enum(self.Elf_word('e_version'), **ENUM_E_VERSION),
127 self.Elf_addr('e_entry'),
128 self.Elf_offset('e_phoff'),
129 self.Elf_offset('e_shoff'),
130 self.Elf_word('e_flags'),
131 self.Elf_half('e_ehsize'),
132 self.Elf_half('e_phentsize'),
133 self.Elf_half('e_phnum'),
134 self.Elf_half('e_shentsize'),
135 self.Elf_half('e_shnum'),
136 self.Elf_half('e_shstrndx'),
137 )
138
139 def _create_leb128(self):
140 self.Elf_uleb128 = ULEB128
141
142 def _create_ntbs(self):
143 self.Elf_ntbs = CString
144
145 def _create_phdr(self):
146 p_type_dict = ENUM_P_TYPE_BASE
147 if self.e_machine == 'EM_ARM':
148 p_type_dict = ENUM_P_TYPE_ARM
149 elif self.e_machine == 'EM_AARCH64':
150 p_type_dict = ENUM_P_TYPE_AARCH64
151 elif self.e_machine == 'EM_MIPS':
152 p_type_dict = ENUM_P_TYPE_MIPS
153
154 if self.elfclass == 32:
155 self.Elf_Phdr = Struct('Elf_Phdr',
156 Enum(self.Elf_word('p_type'), **p_type_dict),
157 self.Elf_offset('p_offset'),
158 self.Elf_addr('p_vaddr'),
159 self.Elf_addr('p_paddr'),
160 self.Elf_word('p_filesz'),
161 self.Elf_word('p_memsz'),
162 self.Elf_word('p_flags'),
163 self.Elf_word('p_align'),
164 )
165 else: # 64
166 self.Elf_Phdr = Struct('Elf_Phdr',
167 Enum(self.Elf_word('p_type'), **p_type_dict),
168 self.Elf_word('p_flags'),
169 self.Elf_offset('p_offset'),
170 self.Elf_addr('p_vaddr'),
171 self.Elf_addr('p_paddr'),
172 self.Elf_xword('p_filesz'),
173 self.Elf_xword('p_memsz'),
174 self.Elf_xword('p_align'),
175 )
176
177 def _create_shdr(self):
178 """Section header parsing.
179
180 Depends on e_machine because of machine-specific values in sh_type.
181 """
182 sh_type_dict = ENUM_SH_TYPE_BASE
183 if self.e_machine == 'EM_ARM':
184 sh_type_dict = ENUM_SH_TYPE_ARM
185 elif self.e_machine == 'EM_X86_64':
186 sh_type_dict = ENUM_SH_TYPE_AMD64
187 elif self.e_machine == 'EM_MIPS':
188 sh_type_dict = ENUM_SH_TYPE_MIPS
189
190 self.Elf_Shdr = Struct('Elf_Shdr',
191 self.Elf_word('sh_name'),
192 Enum(self.Elf_word('sh_type'), **sh_type_dict),
193 self.Elf_xword('sh_flags'),
194 self.Elf_addr('sh_addr'),
195 self.Elf_offset('sh_offset'),
196 self.Elf_xword('sh_size'),
197 self.Elf_word('sh_link'),
198 self.Elf_word('sh_info'),
199 self.Elf_xword('sh_addralign'),
200 self.Elf_xword('sh_entsize'),
201 )
202
203 def _create_chdr(self):
204 # Structure of compressed sections header. It is documented in Oracle
205 # "Linker and Libraries Guide", Part IV ELF Application Binary
206 # Interface, Chapter 13 Object File Format, Section Compression:
207 # https://docs.oracle.com/cd/E53394_01/html/E54813/section_compression.html
208 fields = [
209 Enum(self.Elf_word('ch_type'), **ENUM_ELFCOMPRESS_TYPE),
210 self.Elf_xword('ch_size'),
211 self.Elf_xword('ch_addralign'),
212 ]
213 if self.elfclass == 64:
214 fields.insert(1, self.Elf_word('ch_reserved'))
215 self.Elf_Chdr = Struct('Elf_Chdr', *fields)
216
217 def _create_rel(self):
218 # r_info is also taken apart into r_info_sym and r_info_type. This is
219 # done in Value to avoid endianity issues while parsing.
220 if self.elfclass == 32:
221 fields = [self.Elf_xword('r_info'),
222 Value('r_info_sym',
223 lambda ctx: (ctx['r_info'] >> 8) & 0xFFFFFF),
224 Value('r_info_type',
225 lambda ctx: ctx['r_info'] & 0xFF)]
226 elif self.e_machine == 'EM_MIPS': # ELF64 MIPS
227 fields = [
228 # The MIPS ELF64 specification
229 # (https://www.linux-mips.org/pub/linux/mips/doc/ABI/elf64-2.4.pdf)
230 # provides a non-standard relocation structure definition.
231 self.Elf_word('r_sym'),
232 self.Elf_byte('r_ssym'),
233 self.Elf_byte('r_type3'),
234 self.Elf_byte('r_type2'),
235 self.Elf_byte('r_type'),
236
237 # Synthetize usual fields for compatibility with other
238 # architectures. This allows relocation consumers (including
239 # our readelf tests) to work without worrying about MIPS64
240 # oddities.
241 Value('r_info_sym', lambda ctx: ctx['r_sym']),
242 Value('r_info_ssym', lambda ctx: ctx['r_ssym']),
243 Value('r_info_type', lambda ctx: ctx['r_type']),
244 Value('r_info_type2', lambda ctx: ctx['r_type2']),
245 Value('r_info_type3', lambda ctx: ctx['r_type3']),
246 Value('r_info',
247 lambda ctx: (ctx['r_sym'] << 32)
248 | (ctx['r_ssym'] << 24)
249 | (ctx['r_type3'] << 16)
250 | (ctx['r_type2'] << 8)
251 | ctx['r_type']),
252 ]
253 else: # Other 64 ELFs
254 fields = [self.Elf_xword('r_info'),
255 Value('r_info_sym',
256 lambda ctx: (ctx['r_info'] >> 32) & 0xFFFFFFFF),
257 Value('r_info_type',
258 lambda ctx: ctx['r_info'] & 0xFFFFFFFF)]
259
260 self.Elf_Rel = Struct('Elf_Rel',
261 self.Elf_addr('r_offset'),
262 *fields)
263
264 fields_and_addend = fields + [self.Elf_sxword('r_addend')]
265 self.Elf_Rela = Struct('Elf_Rela',
266 self.Elf_addr('r_offset'),
267 *fields_and_addend
268 )
269
270 def _create_dyn(self):
271 d_tag_dict = dict(ENUM_D_TAG_COMMON)
272 if self.e_machine in ENUMMAP_EXTRA_D_TAG_MACHINE:
273 d_tag_dict.update(ENUMMAP_EXTRA_D_TAG_MACHINE[self.e_machine])
274 elif self.e_ident_osabi == 'ELFOSABI_SOLARIS':
275 d_tag_dict.update(ENUM_D_TAG_SOLARIS)
276
277 self.Elf_Dyn = Struct('Elf_Dyn',
278 Enum(self.Elf_sxword('d_tag'), **d_tag_dict),
279 self.Elf_xword('d_val'),
280 Value('d_ptr', lambda ctx: ctx['d_val']),
281 )
282
283 def _create_sym(self):
284 # st_info is hierarchical. To access the type, use
285 # container['st_info']['type']
286 st_info_struct = BitStruct('st_info',
287 Enum(BitField('bind', 4), **ENUM_ST_INFO_BIND),
288 Enum(BitField('type', 4), **ENUM_ST_INFO_TYPE))
289 # st_other is hierarchical. To access the visibility,
290 # use container['st_other']['visibility']
291 st_other_struct = BitStruct('st_other',
292 Padding(5),
293 Enum(BitField('visibility', 3), **ENUM_ST_VISIBILITY))
294 if self.elfclass == 32:
295 self.Elf_Sym = Struct('Elf_Sym',
296 self.Elf_word('st_name'),
297 self.Elf_addr('st_value'),
298 self.Elf_word('st_size'),
299 st_info_struct,
300 st_other_struct,
301 Enum(self.Elf_half('st_shndx'), **ENUM_ST_SHNDX),
302 )
303 else:
304 self.Elf_Sym = Struct('Elf_Sym',
305 self.Elf_word('st_name'),
306 st_info_struct,
307 st_other_struct,
308 Enum(self.Elf_half('st_shndx'), **ENUM_ST_SHNDX),
309 self.Elf_addr('st_value'),
310 self.Elf_xword('st_size'),
311 )
312
313 def _create_sunw_syminfo(self):
314 self.Elf_Sunw_Syminfo = Struct('Elf_Sunw_Syminfo',
315 Enum(self.Elf_half('si_boundto'), **ENUM_SUNW_SYMINFO_BOUNDTO),
316 self.Elf_half('si_flags'),
317 )
318
319 def _create_gnu_verneed(self):
320 # Structure of "version needed" entries is documented in
321 # Oracle "Linker and Libraries Guide", Chapter 13 Object File Format
322 self.Elf_Verneed = Struct('Elf_Verneed',
323 self.Elf_half('vn_version'),
324 self.Elf_half('vn_cnt'),
325 self.Elf_word('vn_file'),
326 self.Elf_word('vn_aux'),
327 self.Elf_word('vn_next'),
328 )
329 self.Elf_Vernaux = Struct('Elf_Vernaux',
330 self.Elf_word('vna_hash'),
331 self.Elf_half('vna_flags'),
332 self.Elf_half('vna_other'),
333 self.Elf_word('vna_name'),
334 self.Elf_word('vna_next'),
335 )
336
337 def _create_gnu_verdef(self):
338 # Structure of "version definition" entries are documented in
339 # Oracle "Linker and Libraries Guide", Chapter 13 Object File Format
340 self.Elf_Verdef = Struct('Elf_Verdef',
341 self.Elf_half('vd_version'),
342 self.Elf_half('vd_flags'),
343 self.Elf_half('vd_ndx'),
344 self.Elf_half('vd_cnt'),
345 self.Elf_word('vd_hash'),
346 self.Elf_word('vd_aux'),
347 self.Elf_word('vd_next'),
348 )
349 self.Elf_Verdaux = Struct('Elf_Verdaux',
350 self.Elf_word('vda_name'),
351 self.Elf_word('vda_next'),
352 )
353
354 def _create_gnu_versym(self):
355 # Structure of "version symbol" entries are documented in
356 # Oracle "Linker and Libraries Guide", Chapter 13 Object File Format
357 self.Elf_Versym = Struct('Elf_Versym',
358 Enum(self.Elf_half('ndx'), **ENUM_VERSYM),
359 )
360
361 def _create_gnu_abi(self):
362 # Structure of GNU ABI notes is documented in
363 # https://code.woboq.org/userspace/glibc/csu/abi-note.S.html
364 self.Elf_abi = Struct('Elf_abi',
365 Enum(self.Elf_word('abi_os'), **ENUM_NOTE_ABI_TAG_OS),
366 self.Elf_word('abi_major'),
367 self.Elf_word('abi_minor'),
368 self.Elf_word('abi_tiny'),
369 )
370
371 def _create_note(self, e_type=None):
372 # Structure of "PT_NOTE" section
373 self.Elf_Nhdr = Struct('Elf_Nhdr',
374 self.Elf_word('n_namesz'),
375 self.Elf_word('n_descsz'),
376 Enum(self.Elf_word('n_type'),
377 **(ENUM_NOTE_N_TYPE if e_type != "ET_CORE"
378 else ENUM_CORE_NOTE_N_TYPE)),
379 )
380
381 # A process psinfo structure according to
382 # http://elixir.free-electrons.com/linux/v2.6.35/source/include/linux/elfcore.h#L84
383 if self.elfclass == 32:
384 self.Elf_Prpsinfo = Struct('Elf_Prpsinfo',
385 self.Elf_byte('pr_state'),
386 String('pr_sname', 1),
387 self.Elf_byte('pr_zomb'),
388 self.Elf_byte('pr_nice'),
389 self.Elf_xword('pr_flag'),
390 self.Elf_half('pr_uid'),
391 self.Elf_half('pr_gid'),
392 self.Elf_word('pr_pid'),
393 self.Elf_word('pr_ppid'),
394 self.Elf_word('pr_pgrp'),
395 self.Elf_word('pr_sid'),
396 String('pr_fname', 16),
397 String('pr_psargs', 80),
398 )
399 else: # 64
400 self.Elf_Prpsinfo = Struct('Elf_Prpsinfo',
401 self.Elf_byte('pr_state'),
402 String('pr_sname', 1),
403 self.Elf_byte('pr_zomb'),
404 self.Elf_byte('pr_nice'),
405 Padding(4),
406 self.Elf_xword('pr_flag'),
407 self.Elf_word('pr_uid'),
408 self.Elf_word('pr_gid'),
409 self.Elf_word('pr_pid'),
410 self.Elf_word('pr_ppid'),
411 self.Elf_word('pr_pgrp'),
412 self.Elf_word('pr_sid'),
413 String('pr_fname', 16),
414 String('pr_psargs', 80),
415 )
416
417 # A PT_NOTE of type NT_FILE matching the definition in
418 # https://chromium.googlesource.com/
419 # native_client/nacl-binutils/+/upstream/master/binutils/readelf.c
420 # Line 15121
421 self.Elf_Nt_File = Struct('Elf_Nt_File',
422 self.Elf_xword("num_map_entries"),
423 self.Elf_xword("page_size"),
424 Array(lambda ctx: ctx.num_map_entries,
425 Struct('Elf_Nt_File_Entry',
426 self.Elf_addr('vm_start'),
427 self.Elf_addr('vm_end'),
428 self.Elf_offset('page_offset'))),
429 Array(lambda ctx: ctx.num_map_entries,
430 CString('filename')))
431
432 def _create_stabs(self):
433 # Structure of one stabs entry, see binutils/bfd/stabs.c
434 # Names taken from https://sourceware.org/gdb/current/onlinedocs/stabs.html#Overview
435 self.Elf_Stabs = Struct('Elf_Stabs',
436 self.Elf_word('n_strx'),
437 self.Elf_byte('n_type'),
438 self.Elf_byte('n_other'),
439 self.Elf_half('n_desc'),
440 self.Elf_word('n_value'),
441 )
442
443 def _create_arm_attributes(self):
444 # Structure of a build attributes subsection header. A subsection is
445 # either public to all tools that process the ELF file or private to
446 # the vendor's tools.
447 self.Elf_Attr_Subsection_Header = Struct('Elf_Attr_Subsection',
448 self.Elf_word('length'),
449 self.Elf_ntbs('vendor_name',
450 encoding='utf-8')
451 )
452
453 # Structure of a build attribute tag.
454 self.Elf_Attribute_Tag = Struct('Elf_Attribute_Tag',
455 Enum(self.Elf_uleb128('tag'),
456 **ENUM_ATTR_TAG_ARM)
457 )
458
459 def _create_elf_hash(self):
460 # Structure of the old SYSV-style hash table header. It is documented
461 # in the Oracle "Linker and Libraries Guide", Part IV ELF Application
462 # Binary Interface, Chapter 14 Object File Format, Section Hash Table
463 # Section:
464 # https://docs.oracle.com/cd/E53394_01/html/E54813/chapter6-48031.html
465
466 self.Elf_Hash = Struct('Elf_Hash',
467 self.Elf_word('nbuckets'),
468 self.Elf_word('nchains'),
469 Array(lambda ctx: ctx['nbuckets'], self.Elf_word('buckets')),
470 Array(lambda ctx: ctx['nchains'], self.Elf_word('chains')))
471
472 def _create_gnu_hash(self):
473 # Structure of the GNU-style hash table header. Documentation for this
474 # table is mostly in the GLIBC source code, a good explanation of the
475 # format can be found in this blog post:
476 # https://flapenguin.me/2017/05/10/elf-lookup-dt-gnu-hash/
477 self.Gnu_Hash = Struct('Gnu_Hash',
478 self.Elf_word('nbuckets'),
479 self.Elf_word('symoffset'),
480 self.Elf_word('bloom_size'),
481 self.Elf_word('bloom_shift'),
482 Array(lambda ctx: ctx['bloom_size'], self.Elf_xword('bloom')),
483 Array(lambda ctx: ctx['nbuckets'], self.Elf_word('buckets')))