| # Copyright 2014 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Helper script for extracting information from ELF files""" |
| |
| import struct |
| |
| |
| class Error(Exception): |
| '''Local Error class for this file.''' |
| pass |
| |
| |
| def ParseElfHeader(path): |
| """Determine properties of a nexe by parsing elf header. |
| Return tuple of architecture and boolean signalling whether |
| the executable is dynamic (has INTERP header) or static. |
| """ |
| # From elf.h: |
| # typedef struct |
| # { |
| # unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ |
| # Elf64_Half e_type; /* Object file type */ |
| # Elf64_Half e_machine; /* Architecture */ |
| # ... |
| # } Elf32_Ehdr; |
| elf_header_format = '16s2H' |
| elf_header_size = struct.calcsize(elf_header_format) |
| |
| with open(path, 'rb') as f: |
| header = f.read(elf_header_size) |
| |
| try: |
| header = struct.unpack(elf_header_format, header) |
| except struct.error: |
| raise Error("error parsing elf header: %s" % path) |
| e_ident, _, e_machine = header[:3] |
| |
| elf_magic = '\x7fELF' |
| if e_ident[:4] != elf_magic: |
| raise Error('Not a valid NaCl executable: %s' % path) |
| |
| e_machine_mapping = { |
| 3 : 'x86-32', |
| 8 : 'mips32', |
| 40 : 'arm', |
| 62 : 'x86-64' |
| } |
| if e_machine not in e_machine_mapping: |
| raise Error('Unknown machine type: %s' % e_machine) |
| |
| # Set arch based on the machine type in the elf header |
| arch = e_machine_mapping[e_machine] |
| |
| # Now read the full header in either 64bit or 32bit mode |
| dynamic = IsDynamicElf(path, arch == 'x86-64') |
| return arch, dynamic |
| |
| |
| def IsDynamicElf(path, is64bit): |
| """Examine an elf file to determine if it is dynamically |
| linked or not. |
| This is determined by searching the program headers for |
| a header of type PT_INTERP. |
| """ |
| if is64bit: |
| elf_header_format = '16s2HI3QI3H' |
| else: |
| elf_header_format = '16s2HI3II3H' |
| |
| elf_header_size = struct.calcsize(elf_header_format) |
| |
| with open(path, 'rb') as f: |
| header = f.read(elf_header_size) |
| header = struct.unpack(elf_header_format, header) |
| p_header_offset = header[5] |
| p_header_entry_size = header[9] |
| num_p_header = header[10] |
| f.seek(p_header_offset) |
| p_headers = f.read(p_header_entry_size*num_p_header) |
| |
| # Read the first word of each Phdr to find out its type. |
| # |
| # typedef struct |
| # { |
| # Elf32_Word p_type; /* Segment type */ |
| # ... |
| # } Elf32_Phdr; |
| elf_phdr_format = 'I' |
| PT_INTERP = 3 |
| |
| while p_headers: |
| p_header = p_headers[:p_header_entry_size] |
| p_headers = p_headers[p_header_entry_size:] |
| phdr_type = struct.unpack(elf_phdr_format, p_header[:4])[0] |
| if phdr_type == PT_INTERP: |
| return True |
| |
| return False |