Home | History | Annotate | Download | only in lib
      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 
      7 import struct
      8 
      9 
     10 class Error(Exception):
     11   '''Local Error class for this file.'''
     12   pass
     13 
     14 
     15 def 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 
     61 def 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