1 #-------------------------------------------------------------------------------
2 # elftools: elf/elffile.py
4 # ELFFile - main class for accessing ELF files
6 # Eli Bendersky (eliben@gmail.com)
7 # This code is in the public domain
8 #-------------------------------------------------------------------------------
9 from ..common.py3compat import BytesIO
10 from ..common.exceptions import ELFError
11 from ..common.utils import struct_parse, elf_assert
12 from ..construct import ConstructError
13 from .structs import ELFStructs
14 from .sections import (
15 Section, StringTableSection, SymbolTableSection, NullSection)
16 from .dynamic import DynamicSection, DynamicSegment
17 from .relocation import RelocationSection, RelocationHandler
18 from .segments import Segment, InterpSegment
19 from .enums import ENUM_RELOC_TYPE_i386, ENUM_RELOC_TYPE_x64
20 from ..dwarf.dwarfinfo import DWARFInfo, DebugSectionDescriptor, DwarfConfig
23 class ELFFile(object):
24 """ Creation: the constructor accepts a stream (file-like object) with the
25 contents of an ELF file.
27 Accessible attributes:
30 The stream holding the data of the file - must be a binary
31 stream (bytes, not string).
34 32 or 64 - specifies the word size of the target machine
37 boolean - specifies the target machine's endianness
40 the complete ELF file header
43 the raw e_ident field of the header
45 def __init__(self, stream):
48 self.structs = ELFStructs(
49 little_endian=self.little_endian,
50 elfclass=self.elfclass)
51 self.header = self._parse_elf_header()
54 self.e_ident_raw = self.stream.read(16)
56 self._file_stringtable_section = self._get_file_stringtable()
57 self._section_name_map = None
59 def num_sections(self):
60 """ Number of sections in the file
62 return self['e_shnum']
64 def get_section(self, n):
65 """ Get the section at index #n from the file (Section object or a
68 section_header = self._get_section_header(n)
69 return self._make_section(section_header)
71 def get_section_by_name(self, name):
72 """ Get a section from the file, by name. Return None if no such
75 # The first time this method is called, construct a name to number
78 if self._section_name_map is None:
79 self._section_name_map = {}
80 for i, sec in enumerate(self.iter_sections()):
81 self._section_name_map[sec.name] = i
82 secnum = self._section_name_map.get(name, None)
83 return None if secnum is None else self.get_section(secnum)
85 def iter_sections(self):
86 """ Yield all the sections in the file
88 for i in range(self.num_sections()):
89 yield self.get_section(i)
91 def num_segments(self):
92 """ Number of segments in the file
94 return self['e_phnum']
96 def get_segment(self, n):
97 """ Get the segment at index #n from the file (Segment object)
99 segment_header = self._get_segment_header(n)
100 return self._make_segment(segment_header)
102 def iter_segments(self):
103 """ Yield all the segments in the file
105 for i in range(self.num_segments()):
106 yield self.get_segment(i)
108 def has_dwarf_info(self):
109 """ Check whether this file appears to have debugging information.
110 We assume that if it has the debug_info section, it has all theother
111 required sections as well.
113 return bool(self.get_section_by_name(b'.debug_info'))
115 def get_dwarf_info(self, relocate_dwarf_sections=True):
116 """ Return a DWARFInfo object representing the debugging information in
119 If relocate_dwarf_sections is True, relocations for DWARF sections
120 are looked up and applied.
122 # Expect that has_dwarf_info was called, so at least .debug_info is
124 # Sections that aren't found will be passed as None to DWARFInfo.
127 for secname in (b'.debug_info', b'.debug_abbrev', b'.debug_str',
128 b'.debug_line', b'.debug_frame', b'.debug_loc',
130 section = self.get_section_by_name(secname)
132 debug_sections[secname] = None
134 debug_sections[secname] = self._read_dwarf_section(
136 relocate_dwarf_sections)
140 little_endian=self.little_endian,
141 default_address_size=self.elfclass / 8,
142 machine_arch=self.get_machine_arch()),
143 debug_info_sec=debug_sections[b'.debug_info'],
144 debug_abbrev_sec=debug_sections[b'.debug_abbrev'],
145 debug_frame_sec=debug_sections[b'.debug_frame'],
146 debug_str_sec=debug_sections[b'.debug_str'],
147 debug_loc_sec=debug_sections[b'.debug_loc'],
148 debug_ranges_sec=debug_sections[b'.debug_ranges'],
149 debug_line_sec=debug_sections[b'.debug_line'])
151 def get_machine_arch(self):
152 """ Return the machine architecture, as detected from the ELF header.
153 At the moment the only supported architectures are x86 and x64.
155 if self['e_machine'] == 'EM_X86_64':
157 elif self['e_machine'] in ('EM_386', 'EM_486'):
162 #-------------------------------- PRIVATE --------------------------------#
164 def __getitem__(self, name):
165 """ Implement dict-like access to header entries
167 return self.header[name]
169 def _identify_file(self):
170 """ Verify the ELF file and identify its class and endianness.
172 # Note: this code reads the stream directly, without using ELFStructs,
173 # since we don't yet know its exact format. ELF was designed to be
174 # read like this - its e_ident field is word-size and endian agnostic.
177 magic = self.stream.read(4)
178 elf_assert(magic == b'\x7fELF', 'Magic number does not match')
180 ei_class = self.stream.read(1)
181 if ei_class == b'\x01':
183 elif ei_class == b'\x02':
186 raise ELFError('Invalid EI_CLASS %s' % repr(ei_class))
188 ei_data = self.stream.read(1)
189 if ei_data == b'\x01':
190 self.little_endian = True
191 elif ei_data == b'\x02':
192 self.little_endian = False
194 raise ELFError('Invalid EI_DATA %s' % repr(ei_data))
196 def _section_offset(self, n):
197 """ Compute the offset of section #n in the file
199 return self['e_shoff'] + n * self['e_shentsize']
201 def _segment_offset(self, n):
202 """ Compute the offset of segment #n in the file
204 return self['e_phoff'] + n * self['e_phentsize']
206 def _make_segment(self, segment_header):
207 """ Create a Segment object of the appropriate type
209 segtype = segment_header['p_type']
210 if segtype == 'PT_INTERP':
211 return InterpSegment(segment_header, self.stream)
212 elif segtype == 'PT_DYNAMIC':
213 return DynamicSegment(segment_header, self.stream, self)
215 return Segment(segment_header, self.stream)
217 def _get_section_header(self, n):
218 """ Find the header of section #n, parse it and return the struct
221 self.structs.Elf_Shdr,
223 stream_pos=self._section_offset(n))
225 def _get_section_name(self, section_header):
226 """ Given a section header, find this section's name in the file's
229 name_offset = section_header['sh_name']
230 return self._file_stringtable_section.get_string(name_offset)
232 def _make_section(self, section_header):
233 """ Create a section object of the appropriate type
235 name = self._get_section_name(section_header)
236 sectype = section_header['sh_type']
238 if sectype == 'SHT_STRTAB':
239 return StringTableSection(section_header, name, self.stream)
240 elif sectype == 'SHT_NULL':
241 return NullSection(section_header, name, self.stream)
242 elif sectype in ('SHT_SYMTAB', 'SHT_DYNSYM'):
243 return self._make_symbol_table_section(section_header, name)
244 elif sectype in ('SHT_REL', 'SHT_RELA'):
245 return RelocationSection(
246 section_header, name, self.stream, self)
247 elif sectype == 'SHT_DYNAMIC':
248 return DynamicSection(section_header, name, self.stream, self)
250 return Section(section_header, name, self.stream)
252 def _make_symbol_table_section(self, section_header, name):
253 """ Create a SymbolTableSection
255 linked_strtab_index = section_header['sh_link']
256 strtab_section = self.get_section(linked_strtab_index)
257 return SymbolTableSection(
258 section_header, name, self.stream,
260 stringtable=strtab_section)
262 def _get_segment_header(self, n):
263 """ Find the header of segment #n, parse it and return the struct
266 self.structs.Elf_Phdr,
268 stream_pos=self._segment_offset(n))
270 def _get_file_stringtable(self):
271 """ Find the file's string table section
273 stringtable_section_num = self['e_shstrndx']
274 return StringTableSection(
275 header=self._get_section_header(stringtable_section_num),
279 def _parse_elf_header(self):
280 """ Parses the ELF file header and assigns the result to attributes
283 return struct_parse(self.structs.Elf_Ehdr, self.stream, stream_pos=0)
285 def _read_dwarf_section(self, section, relocate_dwarf_sections):
286 """ Read the contents of a DWARF section from the stream and return a
287 DebugSectionDescriptor. Apply relocations if asked to.
289 self.stream.seek(section['sh_offset'])
290 # The section data is read into a new stream, for processing
291 section_stream = BytesIO()
292 section_stream.write(self.stream.read(section['sh_size']))
294 if relocate_dwarf_sections:
295 reloc_handler = RelocationHandler(self)
296 reloc_section = reloc_handler.find_relocations_for_section(section)
297 if reloc_section is not None:
298 reloc_handler.apply_section_relocations(
299 section_stream, reloc_section)
301 return DebugSectionDescriptor(
302 stream=section_stream,
304 global_offset=section['sh_offset'],
305 size=section['sh_size'])