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