1 #-------------------------------------------------------------------------------
2 # elftools: elf/structs.py
4 # Encapsulation of Construct structs for parsing an ELF file, adjusted for
5 # correct endianness and word-size.
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
16 from ..common
.construct_utils
import ULEB128
20 class ELFStructs(object):
21 """ Accessible attributes:
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.
40 Entries in relocation sections
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
48 self
.e_ident_osabi
= None
50 def __getstate__(self
):
51 return self
.little_endian
, self
.elfclass
, self
.e_type
, self
.e_machine
, self
.e_ident_osabi
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
)
58 def create_basic_structs(self
):
59 """ Create word-size related structs and ehdr struct needed for
60 initial determining of ELF type.
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
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
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.
91 self
.e_machine
= e_machine
92 self
.e_ident_osabi
= e_ident_osabi
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
)
107 self
._create
_arm
_attributes
()
108 self
._create
_elf
_hash
()
109 self
._create
_gnu
_hash
()
111 #-------------------------------- PRIVATE --------------------------------#
113 def _create_ehdr(self
):
114 self
.Elf_Ehdr
= Struct('Elf_Ehdr',
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'),
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'),
139 def _create_leb128(self
):
140 self
.Elf_uleb128
= ULEB128
142 def _create_ntbs(self
):
143 self
.Elf_ntbs
= CString
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
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'),
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'),
177 def _create_shdr(self
):
178 """Section header parsing.
180 Depends on e_machine because of machine-specific values in sh_type.
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
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'),
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
209 Enum(self
.Elf_word('ch_type'), **ENUM_ELFCOMPRESS_TYPE
),
210 self
.Elf_xword('ch_size'),
211 self
.Elf_xword('ch_addralign'),
213 if self
.elfclass
== 64:
214 fields
.insert(1, self
.Elf_word('ch_reserved'))
215 self
.Elf_Chdr
= Struct('Elf_Chdr', *fields
)
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'),
223 lambda ctx
: (ctx
['r_info'] >> 8) & 0xFFFFFF),
225 lambda ctx
: ctx
['r_info'] & 0xFF)]
226 elif self
.e_machine
== 'EM_MIPS': # ELF64 MIPS
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'),
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
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']),
247 lambda ctx
: (ctx
['r_sym'] << 32)
248 |
(ctx
['r_ssym'] << 24)
249 |
(ctx
['r_type3'] << 16)
250 |
(ctx
['r_type2'] << 8)
253 else: # Other 64 ELFs
254 fields
= [self
.Elf_xword('r_info'),
256 lambda ctx
: (ctx
['r_info'] >> 32) & 0xFFFFFFFF),
258 lambda ctx
: ctx
['r_info'] & 0xFFFFFFFF)]
260 self
.Elf_Rel
= Struct('Elf_Rel',
261 self
.Elf_addr('r_offset'),
264 fields_and_addend
= fields
+ [self
.Elf_sxword('r_addend')]
265 self
.Elf_Rela
= Struct('Elf_Rela',
266 self
.Elf_addr('r_offset'),
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
)
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']),
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 # https://openpowerfoundation.org/wp-content/uploads/2016/03/ABI64BitOpenPOWERv1.1_16July2015_pub4.pdf
293 # See 3.4.1 Symbol Values.
294 Enum(BitField('local', 3), **ENUM_ST_LOCAL
),
296 Enum(BitField('visibility', 3), **ENUM_ST_VISIBILITY
))
297 if self
.elfclass
== 32:
298 self
.Elf_Sym
= Struct('Elf_Sym',
299 self
.Elf_word('st_name'),
300 self
.Elf_addr('st_value'),
301 self
.Elf_word('st_size'),
304 Enum(self
.Elf_half('st_shndx'), **ENUM_ST_SHNDX
),
307 self
.Elf_Sym
= Struct('Elf_Sym',
308 self
.Elf_word('st_name'),
311 Enum(self
.Elf_half('st_shndx'), **ENUM_ST_SHNDX
),
312 self
.Elf_addr('st_value'),
313 self
.Elf_xword('st_size'),
316 def _create_sunw_syminfo(self
):
317 self
.Elf_Sunw_Syminfo
= Struct('Elf_Sunw_Syminfo',
318 Enum(self
.Elf_half('si_boundto'), **ENUM_SUNW_SYMINFO_BOUNDTO
),
319 self
.Elf_half('si_flags'),
322 def _create_gnu_verneed(self
):
323 # Structure of "version needed" entries is documented in
324 # Oracle "Linker and Libraries Guide", Chapter 13 Object File Format
325 self
.Elf_Verneed
= Struct('Elf_Verneed',
326 self
.Elf_half('vn_version'),
327 self
.Elf_half('vn_cnt'),
328 self
.Elf_word('vn_file'),
329 self
.Elf_word('vn_aux'),
330 self
.Elf_word('vn_next'),
332 self
.Elf_Vernaux
= Struct('Elf_Vernaux',
333 self
.Elf_word('vna_hash'),
334 self
.Elf_half('vna_flags'),
335 self
.Elf_half('vna_other'),
336 self
.Elf_word('vna_name'),
337 self
.Elf_word('vna_next'),
340 def _create_gnu_verdef(self
):
341 # Structure of "version definition" entries are documented in
342 # Oracle "Linker and Libraries Guide", Chapter 13 Object File Format
343 self
.Elf_Verdef
= Struct('Elf_Verdef',
344 self
.Elf_half('vd_version'),
345 self
.Elf_half('vd_flags'),
346 self
.Elf_half('vd_ndx'),
347 self
.Elf_half('vd_cnt'),
348 self
.Elf_word('vd_hash'),
349 self
.Elf_word('vd_aux'),
350 self
.Elf_word('vd_next'),
352 self
.Elf_Verdaux
= Struct('Elf_Verdaux',
353 self
.Elf_word('vda_name'),
354 self
.Elf_word('vda_next'),
357 def _create_gnu_versym(self
):
358 # Structure of "version symbol" entries are documented in
359 # Oracle "Linker and Libraries Guide", Chapter 13 Object File Format
360 self
.Elf_Versym
= Struct('Elf_Versym',
361 Enum(self
.Elf_half('ndx'), **ENUM_VERSYM
),
364 def _create_gnu_abi(self
):
365 # Structure of GNU ABI notes is documented in
366 # https://code.woboq.org/userspace/glibc/csu/abi-note.S.html
367 self
.Elf_abi
= Struct('Elf_abi',
368 Enum(self
.Elf_word('abi_os'), **ENUM_NOTE_ABI_TAG_OS
),
369 self
.Elf_word('abi_major'),
370 self
.Elf_word('abi_minor'),
371 self
.Elf_word('abi_tiny'),
374 def _create_note(self
, e_type
=None):
375 # Structure of "PT_NOTE" section
376 self
.Elf_Nhdr
= Struct('Elf_Nhdr',
377 self
.Elf_word('n_namesz'),
378 self
.Elf_word('n_descsz'),
379 Enum(self
.Elf_word('n_type'),
380 **(ENUM_NOTE_N_TYPE
if e_type
!= "ET_CORE"
381 else ENUM_CORE_NOTE_N_TYPE
)),
384 # A process psinfo structure according to
385 # http://elixir.free-electrons.com/linux/v2.6.35/source/include/linux/elfcore.h#L84
386 if self
.elfclass
== 32:
387 self
.Elf_Prpsinfo
= Struct('Elf_Prpsinfo',
388 self
.Elf_byte('pr_state'),
389 String('pr_sname', 1),
390 self
.Elf_byte('pr_zomb'),
391 self
.Elf_byte('pr_nice'),
392 self
.Elf_xword('pr_flag'),
393 self
.Elf_half('pr_uid'),
394 self
.Elf_half('pr_gid'),
395 self
.Elf_word('pr_pid'),
396 self
.Elf_word('pr_ppid'),
397 self
.Elf_word('pr_pgrp'),
398 self
.Elf_word('pr_sid'),
399 String('pr_fname', 16),
400 String('pr_psargs', 80),
403 self
.Elf_Prpsinfo
= Struct('Elf_Prpsinfo',
404 self
.Elf_byte('pr_state'),
405 String('pr_sname', 1),
406 self
.Elf_byte('pr_zomb'),
407 self
.Elf_byte('pr_nice'),
409 self
.Elf_xword('pr_flag'),
410 self
.Elf_word('pr_uid'),
411 self
.Elf_word('pr_gid'),
412 self
.Elf_word('pr_pid'),
413 self
.Elf_word('pr_ppid'),
414 self
.Elf_word('pr_pgrp'),
415 self
.Elf_word('pr_sid'),
416 String('pr_fname', 16),
417 String('pr_psargs', 80),
420 # A PT_NOTE of type NT_FILE matching the definition in
421 # https://chromium.googlesource.com/
422 # native_client/nacl-binutils/+/upstream/master/binutils/readelf.c
424 self
.Elf_Nt_File
= Struct('Elf_Nt_File',
425 self
.Elf_xword("num_map_entries"),
426 self
.Elf_xword("page_size"),
427 Array(lambda ctx
: ctx
.num_map_entries
,
428 Struct('Elf_Nt_File_Entry',
429 self
.Elf_addr('vm_start'),
430 self
.Elf_addr('vm_end'),
431 self
.Elf_offset('page_offset'))),
432 Array(lambda ctx
: ctx
.num_map_entries
,
433 CString('filename')))
435 def _create_stabs(self
):
436 # Structure of one stabs entry, see binutils/bfd/stabs.c
437 # Names taken from https://sourceware.org/gdb/current/onlinedocs/stabs.html#Overview
438 self
.Elf_Stabs
= Struct('Elf_Stabs',
439 self
.Elf_word('n_strx'),
440 self
.Elf_byte('n_type'),
441 self
.Elf_byte('n_other'),
442 self
.Elf_half('n_desc'),
443 self
.Elf_word('n_value'),
446 def _create_arm_attributes(self
):
447 # Structure of a build attributes subsection header. A subsection is
448 # either public to all tools that process the ELF file or private to
449 # the vendor's tools.
450 self
.Elf_Attr_Subsection_Header
= Struct('Elf_Attr_Subsection',
451 self
.Elf_word('length'),
452 self
.Elf_ntbs('vendor_name',
456 # Structure of a build attribute tag.
457 self
.Elf_Attribute_Tag
= Struct('Elf_Attribute_Tag',
458 Enum(self
.Elf_uleb128('tag'),
462 def _create_elf_hash(self
):
463 # Structure of the old SYSV-style hash table header. It is documented
464 # in the Oracle "Linker and Libraries Guide", Part IV ELF Application
465 # Binary Interface, Chapter 14 Object File Format, Section Hash Table
467 # https://docs.oracle.com/cd/E53394_01/html/E54813/chapter6-48031.html
469 self
.Elf_Hash
= Struct('Elf_Hash',
470 self
.Elf_word('nbuckets'),
471 self
.Elf_word('nchains'),
472 Array(lambda ctx
: ctx
['nbuckets'], self
.Elf_word('buckets')),
473 Array(lambda ctx
: ctx
['nchains'], self
.Elf_word('chains')))
475 def _create_gnu_hash(self
):
476 # Structure of the GNU-style hash table header. Documentation for this
477 # table is mostly in the GLIBC source code, a good explanation of the
478 # format can be found in this blog post:
479 # https://flapenguin.me/2017/05/10/elf-lookup-dt-gnu-hash/
480 self
.Gnu_Hash
= Struct('Gnu_Hash',
481 self
.Elf_word('nbuckets'),
482 self
.Elf_word('symoffset'),
483 self
.Elf_word('bloom_size'),
484 self
.Elf_word('bloom_shift'),
485 Array(lambda ctx
: ctx
['bloom_size'], self
.Elf_xword('bloom')),
486 Array(lambda ctx
: ctx
['nbuckets'], self
.Elf_word('buckets')))