elf.py revision e5d81f57cb97b3b6b7fccc9c5610d21eb81db09d
1# Copyright 2014 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Helper script for extracting information from ELF files"""
6
7import struct
8
9
10class Error(Exception):
11  '''Local Error class for this file.'''
12  pass
13
14
15def ParseElfHeader(path):
16  """Determine properties of a nexe by parsing elf header.
17  Return tuple of architecture and boolean signalling whether
18  the executable is dynamic (has INTERP header) or static.
19  """
20  # From elf.h:
21  # typedef struct
22  # {
23  #   unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
24  #   Elf64_Half e_type; /* Object file type */
25  #   Elf64_Half e_machine; /* Architecture */
26  #   ...
27  # } Elf32_Ehdr;
28  elf_header_format = '16s2H'
29  elf_header_size = struct.calcsize(elf_header_format)
30
31  with open(path, 'rb') as f:
32    header = f.read(elf_header_size)
33
34  try:
35    header = struct.unpack(elf_header_format, header)
36  except struct.error:
37    raise Error("error parsing elf header: %s" % path)
38  e_ident, _, e_machine = header[:3]
39
40  elf_magic = '\x7fELF'
41  if e_ident[:4] != elf_magic:
42    raise Error('Not a valid NaCl executable: %s' % path)
43
44  e_machine_mapping = {
45    3 : 'x86-32',
46    8 : 'mips32',
47    40 : 'arm',
48    62 : 'x86-64'
49  }
50  if e_machine not in e_machine_mapping:
51    raise Error('Unknown machine type: %s' % e_machine)
52
53  # Set arch based on the machine type in the elf header
54  arch = e_machine_mapping[e_machine]
55
56  # Now read the full header in either 64bit or 32bit mode
57  dynamic = IsDynamicElf(path, arch == 'x86-64')
58  return arch, dynamic
59
60
61def IsDynamicElf(path, is64bit):
62  """Examine an elf file to determine if it is dynamically
63  linked or not.
64  This is determined by searching the program headers for
65  a header of type PT_INTERP.
66  """
67  if is64bit:
68    elf_header_format = '16s2HI3QI3H'
69  else:
70    elf_header_format = '16s2HI3II3H'
71
72  elf_header_size = struct.calcsize(elf_header_format)
73
74  with open(path, 'rb') as f:
75    header = f.read(elf_header_size)
76    header = struct.unpack(elf_header_format, header)
77    p_header_offset = header[5]
78    p_header_entry_size = header[9]
79    num_p_header = header[10]
80    f.seek(p_header_offset)
81    p_headers = f.read(p_header_entry_size*num_p_header)
82
83  # Read the first word of each Phdr to find out its type.
84  #
85  # typedef struct
86  # {
87  #   Elf32_Word  p_type;     /* Segment type */
88  #   ...
89  # } Elf32_Phdr;
90  elf_phdr_format = 'I'
91  PT_INTERP = 3
92
93  while p_headers:
94    p_header = p_headers[:p_header_entry_size]
95    p_headers = p_headers[p_header_entry_size:]
96    phdr_type = struct.unpack(elf_phdr_format, p_header[:4])[0]
97    if phdr_type == PT_INTERP:
98      return True
99
100  return False
101