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