Upstream version 8.36.161.0
[platform/framework/web/crosswalk.git] / src / third_party / pyelftools / elftools / elf / elffile.py
1 #-------------------------------------------------------------------------------
2 # elftools: elf/elffile.py
3 #
4 # ELFFile - main class for accessing ELF files
5 #
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
21
22
23 class ELFFile(object):
24     """ Creation: the constructor accepts a stream (file-like object) with the
25         contents of an ELF file.
26     
27         Accessible attributes:
28
29             stream:
30                 The stream holding the data of the file - must be a binary
31                 stream (bytes, not string).
32
33             elfclass: 
34                 32 or 64 - specifies the word size of the target machine
35             
36             little_endian:
37                 boolean - specifies the target machine's endianness     
38
39             header:
40                 the complete ELF file header
41
42             e_ident_raw:
43                 the raw e_ident field of the header
44     """
45     def __init__(self, stream):
46         self.stream = stream
47         self._identify_file()
48         self.structs = ELFStructs(
49             little_endian=self.little_endian,
50             elfclass=self.elfclass)
51         self.header = self._parse_elf_header()
52
53         self.stream.seek(0)
54         self.e_ident_raw = self.stream.read(16)
55         
56         self._file_stringtable_section = self._get_file_stringtable()
57         self._section_name_map = None
58     
59     def num_sections(self):
60         """ Number of sections in the file
61         """
62         return self['e_shnum']
63     
64     def get_section(self, n):
65         """ Get the section at index #n from the file (Section object or a
66             subclass)
67         """
68         section_header = self._get_section_header(n)
69         return self._make_section(section_header)
70     
71     def get_section_by_name(self, name):
72         """ Get a section from the file, by name. Return None if no such 
73             section exists.
74         """
75         # The first time this method is called, construct a name to number
76         # mapping
77         #
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)
84     
85     def iter_sections(self):
86         """ Yield all the sections in the file
87         """
88         for i in range(self.num_sections()):
89             yield self.get_section(i)
90     
91     def num_segments(self):
92         """ Number of segments in the file
93         """
94         return self['e_phnum']
95     
96     def get_segment(self, n):
97         """ Get the segment at index #n from the file (Segment object)
98         """
99         segment_header = self._get_segment_header(n)
100         return self._make_segment(segment_header)
101
102     def iter_segments(self):
103         """ Yield all the segments in the file
104         """
105         for i in range(self.num_segments()):
106             yield self.get_segment(i)
107
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.
112         """
113         return bool(self.get_section_by_name(b'.debug_info'))
114
115     def get_dwarf_info(self, relocate_dwarf_sections=True):
116         """ Return a DWARFInfo object representing the debugging information in
117             this file.
118
119             If relocate_dwarf_sections is True, relocations for DWARF sections
120             are looked up and applied.
121         """
122         # Expect that has_dwarf_info was called, so at least .debug_info is 
123         # present. 
124         # Sections that aren't found will be passed as None to DWARFInfo.
125         #
126         debug_sections = {}
127         for secname in (b'.debug_info', b'.debug_abbrev', b'.debug_str', 
128                         b'.debug_line', b'.debug_frame', b'.debug_loc',
129                         b'.debug_ranges'):
130             section = self.get_section_by_name(secname)
131             if section is None:
132                 debug_sections[secname] = None
133             else:
134                 debug_sections[secname] = self._read_dwarf_section(
135                         section,
136                         relocate_dwarf_sections)
137
138         return DWARFInfo(
139                 config=DwarfConfig(
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'])
150
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.
154         """
155         if self['e_machine'] == 'EM_X86_64':
156             return 'x64'
157         elif self['e_machine'] in ('EM_386', 'EM_486'):
158             return 'x86'
159         else:
160             return '<unknown>'
161
162     #-------------------------------- PRIVATE --------------------------------#
163
164     def __getitem__(self, name):
165         """ Implement dict-like access to header entries
166         """
167         return self.header[name]
168
169     def _identify_file(self):
170         """ Verify the ELF file and identify its class and endianness.
171         """
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.
175         #
176         self.stream.seek(0)
177         magic = self.stream.read(4)
178         elf_assert(magic == b'\x7fELF', 'Magic number does not match')
179
180         ei_class = self.stream.read(1)
181         if ei_class == b'\x01':
182             self.elfclass = 32
183         elif ei_class == b'\x02':
184             self.elfclass = 64
185         else:
186             raise ELFError('Invalid EI_CLASS %s' % repr(ei_class))
187
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
193         else:
194             raise ELFError('Invalid EI_DATA %s' % repr(ei_data))
195     
196     def _section_offset(self, n):
197         """ Compute the offset of section #n in the file
198         """
199         return self['e_shoff'] + n * self['e_shentsize']
200     
201     def _segment_offset(self, n):
202         """ Compute the offset of segment #n in the file
203         """
204         return self['e_phoff'] + n * self['e_phentsize']
205     
206     def _make_segment(self, segment_header):
207         """ Create a Segment object of the appropriate type
208         """
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)
214         else:
215             return Segment(segment_header, self.stream)
216
217     def _get_section_header(self, n):
218         """ Find the header of section #n, parse it and return the struct 
219         """
220         return struct_parse(
221             self.structs.Elf_Shdr,
222             self.stream,
223             stream_pos=self._section_offset(n))
224     
225     def _get_section_name(self, section_header):
226         """ Given a section header, find this section's name in the file's
227             string table
228         """
229         name_offset = section_header['sh_name']
230         return self._file_stringtable_section.get_string(name_offset)
231
232     def _make_section(self, section_header):
233         """ Create a section object of the appropriate type
234         """
235         name = self._get_section_name(section_header)
236         sectype = section_header['sh_type']
237         
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)
249         else:
250             return Section(section_header, name, self.stream)
251
252     def _make_symbol_table_section(self, section_header, name):
253         """ Create a SymbolTableSection
254         """
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,
259             elffile=self,
260             stringtable=strtab_section)
261
262     def _get_segment_header(self, n):
263         """ Find the header of segment #n, parse it and return the struct
264         """
265         return struct_parse(
266             self.structs.Elf_Phdr,
267             self.stream,
268             stream_pos=self._segment_offset(n))
269     
270     def _get_file_stringtable(self):
271         """ Find the file's string table section
272         """
273         stringtable_section_num = self['e_shstrndx']
274         return StringTableSection(
275                 header=self._get_section_header(stringtable_section_num),
276                 name='',
277                 stream=self.stream)
278
279     def _parse_elf_header(self):
280         """ Parses the ELF file header and assigns the result to attributes
281             of this object.
282         """
283         return struct_parse(self.structs.Elf_Ehdr, self.stream, stream_pos=0)
284
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.
288         """
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']))
293
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)
300
301         return DebugSectionDescriptor(
302                 stream=section_stream,
303                 name=section.name,
304                 global_offset=section['sh_offset'],
305                 size=section['sh_size'])
306
307