2 # ELF support functionality for Python.
3 # Copyright (C) 2022 Free Software Foundation, Inc.
4 # This file is part of the GNU C Library.
6 # The GNU C Library is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU Lesser General Public
8 # License as published by the Free Software Foundation; either
9 # version 2.1 of the License, or (at your option) any later version.
11 # The GNU C Library is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # Lesser General Public License for more details.
16 # You should have received a copy of the GNU Lesser General Public
17 # License along with the GNU C Library; if not, see
18 # <https://www.gnu.org/licenses/>.
22 Use Image.readfile(path) to read an ELF file into memory and begin
34 class _MetaNamedValue(type):
35 """Used to set up _NamedValue subclasses."""
38 def __prepare__(metacls
, cls
, bases
, **kwds
):
39 # Indicates an int-based class. Needed for types like Shn.
42 if issubclass(base
, int):
45 return dict(by_value
={},
50 def __contains__(self
, other
):
51 return other
in self
.by_value
53 class _NamedValue(metaclass
=_MetaNamedValue
):
54 """Typed, named integer constants.
56 Constants have the following instance attributes:
58 name: The full name of the constant (e.g., "PT_NULL").
59 short_name: The name with of the constant without the prefix ("NULL").
60 value: The integer value of the constant.
62 The following class attributes are available:
64 by_value: A dict mapping integers to constants.
65 by_name: A dict mapping strings to constants.
66 prefix: A string that is removed from the start of short names, or None.
70 def __new__(cls
, arg0
, arg1
=None):
73 For the one-argument form, the argument must be a string, an
74 int, or an instance of this class. Strings are looked up via
75 by_name. Values are looked up via by_value; if value lookup
76 fails, a new unnamed instance is returned. Instances of this
77 class a re returned as-is.
79 The two-argument form expects the name (a string) and the
80 value (an integer). A new instance is created in this case.
81 The instance is not registered in the by_value/by_name
82 dictionaries (but the caller can do that).
88 if isinstance(typ0
, cls
):
89 # Re-use the existing object.
92 by_value
= cls
.by_value
96 # Create a new object of the requested value.
98 result
= int.__new
__(cls
, arg0
)
100 result
= object.__new
__(cls
)
105 by_name
= cls
.by_name
109 raise ValueError('unknown {} constant: {!r}'.format(
112 # Types for the two-argument form are rigid.
113 if typ0
is not str and typ0
is not None:
114 raise ValueError('type {} of name {!r} should be str'.format(
115 typ0
.__name__
, arg0
))
116 if type(arg1
) is not int:
117 raise ValueError('type {} of value {!r} should be int'.format(
118 type(arg1
).__name
__, arg1
))
119 # Create a new named constants.
121 result
= int.__new
__(cls
, arg1
)
123 result
= object.__new
__(cls
)
126 # Set up the short_name attribute.
128 if prefix
and arg0
.startswith(prefix
):
129 result
.short_name
= arg0
[len(prefix
):]
131 result
.short_name
= arg0
139 return str(self
.value
)
146 return '{}({})'.format(self
.__class
__.__name
__, self
.value
)
148 def __setattr__(self
, name
, value
):
149 # Prevent modification of the critical attributes once they
151 if name
in ('name', 'value', 'short_name') and hasattr(self
, name
):
152 raise AttributeError('can\'t set attribute {}'.format(name
))
153 object.__setattr
__(self
, name
, value
)
155 @functools.total_ordering
156 class _TypedConstant(_NamedValue
):
157 """Base class for integer-valued optionally named constants.
159 This type is not an integer type.
163 def __eq__(self
, other
):
164 return isinstance(other
, self
.__class
__) and self
.value
== other
.value
166 def __lt__(self
, other
):
167 return isinstance(other
, self
.__class
__) and self
.value
<= other
.value
170 return hash(self
.value
)
172 class _IntConstant(_NamedValue
, int):
173 """Base class for integer-like optionally named constants.
175 Instances compare equal to the integer of the same value, and can
176 be used in integer arithmetic.
182 class _FlagConstant(_TypedConstant
, int):
186 """Read ../elf/elf.h and return a dict with the constants in it."""
188 path
= os
.path
.join(os
.path
.dirname(os
.path
.realpath(__file__
)),
189 '..', 'elf', 'elf.h')
190 class TokenizerReporter
:
191 """Report tokenizer errors to standard output."""
196 def error(self
, token
, message
):
198 print('{}:{}:{}: error: {}'.format(
199 path
, token
.line
, token
.column
, message
))
201 reporter
= TokenizerReporter()
202 with
open(path
) as inp
:
203 tokens
= glibcpp
.tokenize_c(inp
.read(), reporter
)
205 raise IOError('parse error in elf.h')
208 """Report macro errors to standard output."""
213 def error(self
, line
, message
):
215 print('{}:{}: error: {}'.format(path
, line
, message
))
217 def note(self
, line
, message
):
218 print('{}:{}: note: {}'.format(path
, line
, message
))
220 reporter
= MacroReporter()
221 result
= glibcpp
.macro_eval(glibcpp
.macro_definitions(tokens
), reporter
)
223 raise IOError('parse error in elf.h')
226 _elf_h
= _parse_elf_h()
228 _elf_h_processed
= set()
230 def _register_elf_h(cls
, prefix
=None, skip
=(), ranges
=False, parent
=None):
231 prefix
= prefix
or cls
.prefix
233 raise ValueError('missing prefix for {}'.format(cls
.__name
__))
234 by_value
= cls
.by_value
235 by_name
= cls
.by_name
236 processed
= _elf_h_processed
239 skip
.add(prefix
+ 'NUM')
241 skip
.add(prefix
+ 'LOOS')
242 skip
.add(prefix
+ 'HIOS')
243 skip
.add(prefix
+ 'LOPROC')
244 skip
.add(prefix
+ 'HIPROC')
245 cls
.os_range
= (_elf_h
[prefix
+ 'LOOS'], _elf_h
[prefix
+ 'HIOS'])
246 cls
.proc_range
= (_elf_h
[prefix
+ 'LOPROC'], _elf_h
[prefix
+ 'HIPROC'])
248 # Inherit the prefix from the parent if not set.
249 if parent
and cls
.prefix
is None and parent
.prefix
is not None:
250 cls
.prefix
= parent
.prefix
252 processed_len_start
= len(processed
)
253 for name
, value
in _elf_h
.items():
254 if name
in skip
or name
in processed
:
256 if name
.startswith(prefix
):
258 if value
in by_value
:
259 raise ValueError('duplicate value {}: {}, {}'.format(
260 value
, name
, by_value
[value
]))
261 obj
= cls(name
, value
)
262 by_value
[value
] = obj
264 setattr(cls
, name
, obj
)
266 # Make the symbolic name available through the parent as well.
267 parent
.by_name
[name
] = obj
268 setattr(parent
, name
, obj
)
270 if len(processed
) == processed_len_start
:
271 raise ValueError('nothing matched prefix {!r}'.format(prefix
))
273 class ElfClass(_TypedConstant
):
274 """ELF word size. Type of EI_CLASS values."""
275 _register_elf_h(ElfClass
, prefix
='ELFCLASS')
277 class ElfData(_TypedConstant
):
278 """ELF endianess. Type of EI_DATA values."""
279 _register_elf_h(ElfData
, prefix
='ELFDATA')
281 class Machine(_TypedConstant
):
282 """ELF machine type. Type of values in Ehdr.e_machine field."""
284 _register_elf_h(Machine
, skip
=('EM_ARC_A5',))
286 class Et(_TypedConstant
):
287 """ELF file type. Type of ET_* values and the Ehdr.e_type field."""
289 _register_elf_h(Et
, ranges
=True)
291 class Shn(_IntConstant
):
292 """ELF reserved section indices."""
295 """Supplemental SHN_* constants for EM_MIPS."""
296 class ShnPARISC(Shn
):
297 """Supplemental SHN_* constants for EM_PARISC."""
298 _register_elf_h(ShnMIPS
, prefix
='SHN_MIPS_', parent
=Shn
)
299 _register_elf_h(ShnPARISC
, prefix
='SHN_PARISC_', parent
=Shn
)
300 _register_elf_h(Shn
, skip
='SHN_LORESERVE SHN_HIRESERVE'.split(), ranges
=True)
302 class Sht(_TypedConstant
):
303 """ELF section types. Type of SHT_* values."""
306 """Supplemental SHT_* constants for EM_ALPHA."""
308 """Supplemental SHT_* constants for EM_ARM."""
310 """Supplemental SHT_* constants for EM_CSKY."""
312 """Supplemental SHT_* constants for EM_IA_64."""
314 """Supplemental SHT_* constants for EM_MIPS."""
315 class ShtPARISC(Sht
):
316 """Supplemental SHT_* constants for EM_PARISC."""
318 """Supplemental SHT_* constants for EM_RISCV."""
319 _register_elf_h(ShtALPHA
, prefix
='SHT_ALPHA_', parent
=Sht
)
320 _register_elf_h(ShtARM
, prefix
='SHT_ARM_', parent
=Sht
)
321 _register_elf_h(ShtCSKY
, prefix
='SHT_CSKY_', parent
=Sht
)
322 _register_elf_h(ShtIA_64
, prefix
='SHT_IA_64_', parent
=Sht
)
323 _register_elf_h(ShtMIPS
, prefix
='SHT_MIPS_', parent
=Sht
)
324 _register_elf_h(ShtPARISC
, prefix
='SHT_PARISC_', parent
=Sht
)
325 _register_elf_h(ShtRISCV
, prefix
='SHT_RISCV_', parent
=Sht
)
326 _register_elf_h(Sht
, ranges
=True,
327 skip
='SHT_LOSUNW SHT_HISUNW SHT_LOUSER SHT_HIUSER'.split())
329 class Pf(_FlagConstant
):
330 """Program header flags. Type of Phdr.p_flags values."""
333 """Supplemental PF_* flags for EM_ARM."""
335 """Supplemental PF_* flags for HP-UX."""
337 """Supplemental PF_* flags for EM_IA_64."""
339 """Supplemental PF_* flags for EM_MIPS."""
341 """Supplemental PF_* flags for EM_PARISC."""
342 _register_elf_h(PfARM
, prefix
='PF_ARM_', parent
=Pf
)
343 _register_elf_h(PfHP
, prefix
='PF_HP_', parent
=Pf
)
344 _register_elf_h(PfIA_64
, prefix
='PF_IA_64_', parent
=Pf
)
345 _register_elf_h(PfMIPS
, prefix
='PF_MIPS_', parent
=Pf
)
346 _register_elf_h(PfPARISC
, prefix
='PF_PARISC_', parent
=Pf
)
347 _register_elf_h(Pf
, skip
='PF_MASKOS PF_MASKPROC'.split())
349 class Shf(_FlagConstant
):
350 """Section flags. Type of Shdr.sh_type values."""
353 """Supplemental SHF_* constants for EM_ALPHA."""
355 """Supplemental SHF_* constants for EM_ARM."""
357 """Supplemental SHF_* constants for EM_IA_64."""
359 """Supplemental SHF_* constants for EM_MIPS."""
360 class ShfPARISC(Shf
):
361 """Supplemental SHF_* constants for EM_PARISC."""
362 _register_elf_h(ShfALPHA
, prefix
='SHF_ALPHA_', parent
=Shf
)
363 _register_elf_h(ShfARM
, prefix
='SHF_ARM_', parent
=Shf
)
364 _register_elf_h(ShfIA_64
, prefix
='SHF_IA_64_', parent
=Shf
)
365 _register_elf_h(ShfMIPS
, prefix
='SHF_MIPS_', parent
=Shf
)
366 _register_elf_h(ShfPARISC
, prefix
='SHF_PARISC_', parent
=Shf
)
367 _register_elf_h(Shf
, skip
='SHF_MASKOS SHF_MASKPROC'.split())
369 class Stb(_TypedConstant
):
370 """ELF symbol binding type."""
372 _register_elf_h(Stb
, ranges
=True)
374 class Stt(_TypedConstant
):
375 """ELF symbol type."""
378 """Supplemental STT_* constants for EM_ARM."""
379 class SttPARISC(Sht
):
380 """Supplemental STT_* constants for EM_PARISC."""
382 """Supplemental STT_* constants for EM_SPARC."""
383 STT_SPARC_REGISTER
= 13
384 class SttX86_64(Sht
):
385 """Supplemental STT_* constants for EM_X86_64."""
386 _register_elf_h(SttARM
, prefix
='STT_ARM_', parent
=Stt
)
387 _register_elf_h(SttPARISC
, prefix
='STT_PARISC_', parent
=Stt
)
388 _register_elf_h(SttSPARC
, prefix
='STT_SPARC_', parent
=Stt
)
389 _register_elf_h(Stt
, ranges
=True)
392 class Pt(_TypedConstant
):
393 """ELF program header types. Type of Phdr.p_type."""
396 """Supplemental PT_* constants for EM_AARCH64."""
398 """Supplemental PT_* constants for EM_ARM."""
400 """Supplemental PT_* constants for HP-U."""
402 """Supplemental PT_* constants for EM_IA_64."""
404 """Supplemental PT_* constants for EM_MIPS."""
406 """Supplemental PT_* constants for EM_PARISC."""
408 """Supplemental PT_* constants for EM_RISCV."""
409 _register_elf_h(PtAARCH64
, prefix
='PT_AARCH64_', parent
=Pt
)
410 _register_elf_h(PtARM
, prefix
='PT_ARM_', parent
=Pt
)
411 _register_elf_h(PtHP
, prefix
='PT_HP_', parent
=Pt
)
412 _register_elf_h(PtIA_64
, prefix
='PT_IA_64_', parent
=Pt
)
413 _register_elf_h(PtMIPS
, prefix
='PT_MIPS_', parent
=Pt
)
414 _register_elf_h(PtPARISC
, prefix
='PT_PARISC_', parent
=Pt
)
415 _register_elf_h(PtRISCV
, prefix
='PT_RISCV_', parent
=Pt
)
416 _register_elf_h(Pt
, skip
='PT_LOSUNW PT_HISUNW'.split(), ranges
=True)
418 class Dt(_TypedConstant
):
419 """ELF dynamic segment tags. Type of Dyn.d_val."""
422 """Supplemental DT_* constants for EM_AARCH64."""
424 """Supplemental DT_* constants for EM_ALPHA."""
425 class DtALTERA_NIOS2(Dt
):
426 """Supplemental DT_* constants for EM_ALTERA_NIOS2."""
428 """Supplemental DT_* constants for EM_IA_64."""
430 """Supplemental DT_* constants for EM_MIPS."""
432 """Supplemental DT_* constants for EM_PPC."""
434 """Supplemental DT_* constants for EM_PPC64."""
436 """Supplemental DT_* constants for EM_RISCV."""
438 """Supplemental DT_* constants for EM_SPARC."""
440 DT_ENCODING DT_PROCNUM
441 DT_ADDRRNGLO DT_ADDRRNGHI DT_ADDRNUM
442 DT_VALRNGLO DT_VALRNGHI DT_VALNUM
443 DT_VERSIONTAGNUM DT_EXTRANUM
452 _register_elf_h(DtAARCH64
, prefix
='DT_AARCH64_', skip
=_dt_skip
, parent
=Dt
)
453 _register_elf_h(DtALPHA
, prefix
='DT_ALPHA_', skip
=_dt_skip
, parent
=Dt
)
454 _register_elf_h(DtALTERA_NIOS2
, prefix
='DT_NIOS2_', skip
=_dt_skip
, parent
=Dt
)
455 _register_elf_h(DtIA_64
, prefix
='DT_IA_64_', skip
=_dt_skip
, parent
=Dt
)
456 _register_elf_h(DtMIPS
, prefix
='DT_MIPS_', skip
=_dt_skip
, parent
=Dt
)
457 _register_elf_h(DtPPC
, prefix
='DT_PPC_', skip
=_dt_skip
, parent
=Dt
)
458 _register_elf_h(DtPPC64
, prefix
='DT_PPC64_', skip
=_dt_skip
, parent
=Dt
)
459 _register_elf_h(DtRISCV
, prefix
='DT_RISCV_', skip
=_dt_skip
, parent
=Dt
)
460 _register_elf_h(DtSPARC
, prefix
='DT_SPARC_', skip
=_dt_skip
, parent
=Dt
)
461 _register_elf_h(Dt
, skip
=_dt_skip
, ranges
=True)
464 # Constant extraction is complete.
469 """ELF symbol binding and type. Type of the Sym.st_info field."""
470 def __init__(self
, arg0
, arg1
=None):
471 if isinstance(arg0
, int) and arg1
is None:
472 self
.bind
= Stb(arg0
>> 4)
473 self
.type = Stt(arg0
& 15)
475 self
.bind
= Stb(arg0
)
476 self
.type = Stt(arg1
)
479 """Returns the raw value for the bind/type combination."""
480 return (self
.bind
.value() << 4) |
(self
.type.value())
482 # Type in an ELF file. Used for deserialization.
483 _Layout
= collections
.namedtuple('_Layout', 'unpack size')
485 def _define_layouts(baseclass
: type, layout32
: str, layout64
: str,
486 types
=None, fields32
=None):
487 """Assign variants dict to baseclass.
489 The variants dict is indexed by (ElfClass, ElfData) pairs, and its
490 values are _Layout instances.
493 struct32
= struct
.Struct(layout32
)
494 struct64
= struct
.Struct(layout64
)
496 # Check that the struct formats yield the right number of components.
497 for s
in (struct32
, struct64
):
498 example
= s
.unpack(b
' ' * s
.size
)
499 if len(example
) != len(baseclass
._fields
):
500 raise ValueError('{!r} yields wrong field count: {} != {}'.format(
501 s
.format
, len(example
), len(baseclass
._fields
)))
503 # Check that field names in types are correct.
507 if n
not in baseclass
._fields
:
508 raise ValueError('{} does not have field {!r}'.format(
509 baseclass
.__name
__, n
))
511 if fields32
is not None \
512 and set(fields32
) != set(baseclass
._fields
):
513 raise ValueError('{!r} is not a permutation of the fields {!r}'.format(
514 fields32
, baseclass
._fields
))
516 def unique_name(name
, used_names
= (set((baseclass
.__name
__,))
517 |
set(baseclass
._fields
)
519 for n
in (types
or {}).values()})):
520 """Find a name that is not used for a class or field name."""
523 while candidate
in used_names
:
525 candidate
= '{}{}'.format(name
, n
)
526 used_names
.add(candidate
)
529 blob_name
= unique_name('blob')
530 struct_unpack_name
= unique_name('struct_unpack')
531 comps_name
= unique_name('comps')
534 for (bits
, elfclass
, layout
, fields
) in (
535 (32, ElfClass
.ELFCLASS32
, layout32
, fields32
),
536 (64, ElfClass
.ELFCLASS64
, layout64
, None),
538 for (elfdata
, structprefix
, funcsuffix
) in (
539 (ElfData
.ELFDATA2LSB
, '<', 'LE'),
540 (ElfData
.ELFDATA2MSB
, '>', 'BE'),
543 baseclass
.__name
__: baseclass
,
544 struct_unpack_name
: struct
.unpack
,
547 # Add the type converters.
549 for cls
in types
.values():
550 env
[cls
.__name
__] = cls
553 ('unpack_', baseclass
.__name
__, str(bits
), funcsuffix
))
556 def {funcname}({blob_name}):
557 '''.format(funcname
=funcname
, blob_name
=blob_name
)
560 unpack_call
= '{}({!r}, {})'.format(
561 struct_unpack_name
, structprefix
+ layout
, blob_name
)
562 field_names
= ', '.join(baseclass
._fields
)
563 if types
is None and fields
is None:
564 code
+= '{}return {}({})\n'.format(
565 indent
, baseclass
.__name
__, unpack_call
)
567 # Destructuring tuple assignment.
569 code
+= '{}{} = {}\n'.format(
570 indent
, field_names
, unpack_call
)
572 # Use custom field order.
573 code
+= '{}{} = {}\n'.format(
574 indent
, ', '.join(fields
), unpack_call
)
576 # Perform the type conversions.
577 for n
in baseclass
._fields
:
579 code
+= '{}{} = {}({})\n'.format(
580 indent
, n
, types
[n
].__name
__, n
)
581 # Create the named tuple.
582 code
+= '{}return {}({})\n'.format(
583 indent
, baseclass
.__name
__, field_names
)
586 layouts
[(elfclass
, elfdata
)] = _Layout(
587 env
[funcname
], struct
.calcsize(layout
))
588 baseclass
.layouts
= layouts
591 # Corresponds to EI_* indices into Elf*_Ehdr.e_indent.
592 class Ident(collections
.namedtuple('Ident',
593 'ei_mag ei_class ei_data ei_version ei_osabi ei_abiversion ei_pad')):
595 def __new__(cls
, *args
):
596 """Construct an object from a blob or its constituent fields."""
598 return cls
.unpack(args
[0])
599 return cls
.__base
__.__new
__(cls
, *args
)
602 def unpack(blob
: memoryview
) -> 'Ident':
603 """Parse raws data into a tuple."""
604 ei_mag
, ei_class
, ei_data
, ei_version
, ei_osabi
, ei_abiversion
, \
605 ei_pad
= struct
.unpack('4s5B7s', blob
)
606 return Ident(ei_mag
, ElfClass(ei_class
), ElfData(ei_data
),
607 ei_version
, ei_osabi
, ei_abiversion
, ei_pad
)
610 # Corresponds to Elf32_Ehdr and Elf64_Ehdr.
611 Ehdr
= collections
.namedtuple('Ehdr',
612 'e_ident e_type e_machine e_version e_entry e_phoff e_shoff e_flags'
613 + ' e_ehsize e_phentsize e_phnum e_shentsize e_shnum e_shstrndx')
614 _define_layouts(Ehdr
,
615 layout32
='16s2H5I6H',
616 layout64
='16s2HI3QI6H',
617 types
=dict(e_ident
=Ident
,
622 # Corresponds to Elf32_Phdr and Elf64_Pdhr. Order follows the latter.
623 Phdr
= collections
.namedtuple('Phdr',
624 'p_type p_flags p_offset p_vaddr p_paddr p_filesz p_memsz p_align')
625 _define_layouts(Phdr
,
627 fields32
=('p_type', 'p_offset', 'p_vaddr', 'p_paddr',
628 'p_filesz', 'p_memsz', 'p_flags', 'p_align'),
630 types
=dict(p_type
=Pt
, p_flags
=Pf
))
633 # Corresponds to Elf32_Shdr and Elf64_Shdr.
634 class Shdr(collections
.namedtuple('Shdr',
635 'sh_name sh_type sh_flags sh_addr sh_offset sh_size sh_link sh_info'
636 + ' sh_addralign sh_entsize')):
637 def resolve(self
, strtab
: 'StringTable') -> 'Shdr':
638 """Resolve sh_name using a string table."""
639 return self
.__class
__(strtab
.get(self
[0]), *self
[1:])
640 _define_layouts(Shdr
,
643 types
=dict(sh_type
=Sht
,
647 # Corresponds to Elf32_Dyn and Elf64_Dyn. The nesting through the
648 # d_un union is skipped, and d_ptr is missing (its representation in
649 # Python would be identical to d_val).
650 Dyn
= collections
.namedtuple('Dyn', 'd_tag d_val')
654 types
=dict(d_tag
=Dt
))
656 # Corresponds to Elf32_Sym and Elf64_Sym.
657 class Sym(collections
.namedtuple('Sym',
658 'st_name st_info st_other st_shndx st_value st_size')):
659 def resolve(self
, strtab
: 'StringTable') -> 'Sym':
660 """Resolve st_name using a string table."""
661 return self
.__class
__(strtab
.get(self
[0]), *self
[1:])
665 fields32
=('st_name', 'st_value', 'st_size', 'st_info',
666 'st_other', 'st_shndx'),
667 types
=dict(st_shndx
=Shn
,
670 # Corresponds to Elf32_Rel and Elf64_Rel.
671 Rel
= collections
.namedtuple('Rel', 'r_offset r_info')
676 # Corresponds to Elf32_Rel and Elf64_Rel.
677 Rela
= collections
.namedtuple('Rela', 'r_offset r_info r_addend')
678 _define_layouts(Rela
,
683 """ELF string table."""
684 def __init__(self
, blob
):
685 """Create a new string table backed by the data in the blob.
687 blob: a memoryview-like object
692 def get(self
, index
) -> bytes
:
693 """Returns the null-terminated byte string at the index."""
697 if blob
[endindex
] == 0:
698 return bytes(blob
[index
:endindex
])
702 """ELF image parser."""
703 def __init__(self
, image
):
704 """Create an ELF image from binary image data.
706 image: a memoryview-like object that supports efficient range
711 ident
= self
.read(Ident
, 0)
712 classdata
= (ident
.ei_class
, ident
.ei_data
)
713 # Set self.Ehdr etc. to the subtypes with the right parsers.
714 for typ
in (Ehdr
, Phdr
, Shdr
, Dyn
, Sym
, Rel
, Rela
):
715 setattr(self
, typ
.__name
__, typ
.layouts
.get(classdata
, None))
717 if self
.Ehdr
is not None:
718 self
.ehdr
= self
.read(self
.Ehdr
, 0)
719 self
._shdr
_num
= self
._compute
_shdr
_num
()
727 if self
._shdr
_num
> 0:
728 self
._shdr
_strtab
= self
._find
_shdr
_strtab
()
730 self
._shdr
_strtab
= None
733 def readfile(path
: str) -> 'Image':
734 """Reads the ELF file at the specified path."""
735 with
open(path
, 'rb') as inp
:
736 return Image(memoryview(inp
.read()))
738 def _compute_shdr_num(self
) -> int:
739 """Computes the actual number of section headers."""
740 shnum
= self
.ehdr
.e_shnum
742 if self
.ehdr
.e_shoff
== 0 or self
.ehdr
.e_shentsize
== 0:
743 # No section headers.
745 # Otherwise the extension mechanism is used (which may be
746 # needed because e_shnum is just 16 bits).
747 return self
.read(self
.Shdr
, self
.ehdr
.e_shoff
).sh_size
750 def _find_shdr_strtab(self
) -> StringTable
:
751 """Finds the section header string table (maybe via extensions)."""
752 shstrndx
= self
.ehdr
.e_shstrndx
753 if shstrndx
== Shn
.SHN_XINDEX
:
754 shstrndx
= self
.read(self
.Shdr
, self
.ehdr
.e_shoff
).sh_link
755 return self
._find
_stringtab
(shstrndx
)
757 def read(self
, typ
: type, offset
:int ):
758 """Reads an object at a specific offset.
760 The type must have been enhanced using _define_variants.
763 return typ
.unpack(self
.image
[offset
: offset
+ typ
.size
])
765 def phdrs(self
) -> Phdr
:
766 """Generator iterating over the program headers."""
767 if self
.ehdr
is None:
769 size
= self
.ehdr
.e_phentsize
770 if size
!= self
.Phdr
.size
:
771 raise ValueError('Unexpected Phdr size in ELF header: {} != {}'
772 .format(size
, self
.Phdr
.size
))
774 offset
= self
.ehdr
.e_phoff
775 for _
in range(self
.ehdr
.e_phnum
):
776 yield self
.read(self
.Phdr
, offset
)
779 def shdrs(self
, resolve
: bool=True) -> Shdr
:
780 """Generator iterating over the section headers.
782 If resolve, section names are automatically translated
783 using the section header string table.
786 if self
._shdr
_num
== 0:
789 size
= self
.ehdr
.e_shentsize
790 if size
!= self
.Shdr
.size
:
791 raise ValueError('Unexpected Shdr size in ELF header: {} != {}'
792 .format(size
, self
.Shdr
.size
))
794 offset
= self
.ehdr
.e_shoff
795 for _
in range(self
._shdr
_num
):
796 shdr
= self
.read(self
.Shdr
, offset
)
798 shdr
= shdr
.resolve(self
._shdr
_strtab
)
802 def dynamic(self
) -> Dyn
:
803 """Generator iterating over the dynamic segment."""
804 for phdr
in self
.phdrs():
805 if phdr
.p_type
== Pt
.PT_DYNAMIC
:
806 # Pick the first dynamic segment, like the loader.
807 if phdr
.p_filesz
== 0:
808 # Probably separated debuginfo.
810 offset
= phdr
.p_offset
811 end
= offset
+ phdr
.p_memsz
814 next_offset
= offset
+ size
815 if next_offset
> end
:
817 'Dynamic segment size {} is not a multiple of Dyn size {}'.format(
819 yield self
.read(self
.Dyn
, offset
)
820 if next_offset
== end
:
824 def syms(self
, shdr
: Shdr
, resolve
: bool=True) -> Sym
:
825 """A generator iterating over a symbol table.
827 If resolve, symbol names are automatically translated using
828 the string table for the symbol table.
831 assert shdr
.sh_type
== Sht
.SHT_SYMTAB
832 size
= shdr
.sh_entsize
833 if size
!= self
.Sym
.size
:
834 raise ValueError('Invalid symbol table entry size {}'.format(size
))
835 offset
= shdr
.sh_offset
836 end
= shdr
.sh_offset
+ shdr
.sh_size
838 strtab
= self
._find
_stringtab
(shdr
.sh_link
)
840 sym
= self
.read(self
.Sym
, offset
)
842 sym
= sym
.resolve(strtab
)
846 raise ValueError('Symbol table is not a multiple of entry size')
848 def lookup_string(self
, strtab_index
: int, strtab_offset
: int) -> bytes
:
849 """Looks up a string in a string table identified by its link index."""
851 strtab
= self
._stringtab
[strtab_index
]
853 strtab
= self
._find
_stringtab
(strtab_index
)
854 return strtab
.get(strtab_offset
)
856 def find_section(self
, shndx
: Shn
) -> Shdr
:
857 """Returns the section header for the indexed section.
859 The section name is not resolved.
862 return self
._section
[shndx
]
866 raise ValueError('Reserved section index {}'.format(shndx
))
868 if idx
< 0 or idx
> self
._shdr
_num
:
869 raise ValueError('Section index {} out of range [0, {})'.format(
870 idx
, self
._shdr
_num
))
872 self
.Shdr
, self
.ehdr
.e_shoff
+ idx
* self
.Shdr
.size
)
873 self
._section
[shndx
] = shdr
876 def _find_stringtab(self
, sh_link
: int) -> StringTable
:
877 if sh_link
in self
._stringtab
:
878 return self
._stringtab
879 if sh_link
< 0 or sh_link
>= self
._shdr
_num
:
880 raise ValueError('Section index {} out of range [0, {})'.format(
881 sh_link
, self
._shdr
_num
))
883 self
.Shdr
, self
.ehdr
.e_shoff
+ sh_link
* self
.Shdr
.size
)
884 if shdr
.sh_type
!= Sht
.SHT_STRTAB
:
886 'Section {} is not a string table: {}'.format(
887 sh_link
, shdr
.sh_type
))
888 strtab
= StringTable(
889 self
.image
[shdr
.sh_offset
:shdr
.sh_offset
+ shdr
.sh_size
])
890 # This could retrain essentially arbitrary amounts of data,
891 # but caching string tables seems important for performance.
892 self
._stringtab
[sh_link
] = strtab
896 """Computes the ELF hash of the string."""
899 if type(ch
) is not int:
901 acc
= ((acc
<< 4) + ch
) & 0xffffffff
902 top
= acc
& 0xf0000000
903 acc
= (acc ^
(top
>> 24)) & ~top
907 """Computes the GNU hash of the string."""
910 if type(ch
) is not int:
912 h
= (h
* 33 + ch
) & 0xffffffff
915 __all__
= [name
for name
in dir() if name
[0].isupper()]