1# This Source Code Form is subject to the terms of the Mozilla Public 2# License, v. 2.0. If a copy of the MPL was not distributed with this 3# file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 5from __future__ import absolute_import 6 7import os 8import struct 9import subprocess 10from mozpack.errors import errors 11 12MACHO_SIGNATURES = [ 13 0xfeedface, # mach-o 32-bits big endian 14 0xcefaedfe, # mach-o 32-bits little endian 15 0xfeedfacf, # mach-o 64-bits big endian 16 0xcffaedfe, # mach-o 64-bits little endian 17] 18 19FAT_SIGNATURE = 0xcafebabe # mach-o FAT binary 20 21ELF_SIGNATURE = 0x7f454c46 # Elf binary 22 23UNKNOWN = 0 24MACHO = 1 25ELF = 2 26 27def get_type(path): 28 ''' 29 Check the signature of the give file and returns what kind of executable 30 matches. 31 ''' 32 with open(path, 'rb') as f: 33 signature = f.read(4) 34 if len(signature) < 4: 35 return UNKNOWN 36 signature = struct.unpack('>L', signature)[0] 37 if signature == ELF_SIGNATURE: 38 return ELF 39 if signature in MACHO_SIGNATURES: 40 return MACHO 41 if signature != FAT_SIGNATURE: 42 return UNKNOWN 43 # We have to sanity check the second four bytes, because Java class 44 # files use the same magic number as Mach-O fat binaries. 45 # This logic is adapted from file(1), which says that Mach-O uses 46 # these bytes to count the number of architectures within, while 47 # Java uses it for a version number. Conveniently, there are only 48 # 18 labelled Mach-O architectures, and Java's first released 49 # class format used the version 43.0. 50 num = f.read(4) 51 if len(num) < 4: 52 return UNKNOWN 53 num = struct.unpack('>L', num)[0] 54 if num < 20: 55 return MACHO 56 return UNKNOWN 57 58 59def is_executable(path): 60 ''' 61 Return whether a given file path points to an executable or a library, 62 where an executable or library is identified by: 63 - the file extension on OS/2 and WINNT 64 - the file signature on OS/X and ELF systems (GNU/Linux, Android, BSD, 65 Solaris) 66 67 As this function is intended for use to choose between the ExecutableFile 68 and File classes in FileFinder, and choosing ExecutableFile only matters 69 on OS/2, OS/X, ELF and WINNT (in GCC build) systems, we don't bother 70 detecting other kind of executables. 71 ''' 72 from buildconfig import substs 73 if not os.path.exists(path): 74 return False 75 76 if substs['OS_ARCH'] == 'WINNT': 77 return path.lower().endswith((substs['DLL_SUFFIX'], 78 substs['BIN_SUFFIX'])) 79 80 return get_type(path) != UNKNOWN 81 82 83def may_strip(path): 84 ''' 85 Return whether strip() should be called 86 ''' 87 from buildconfig import substs 88 return not substs['PKG_SKIP_STRIP'] 89 90 91def strip(path): 92 ''' 93 Execute the STRIP command with STRIP_FLAGS on the given path. 94 ''' 95 from buildconfig import substs 96 strip = substs['STRIP'] 97 flags = substs['STRIP_FLAGS'].split() if 'STRIP_FLAGS' in substs else [] 98 cmd = [strip] + flags + [path] 99 if subprocess.call(cmd) != 0: 100 errors.fatal('Error executing ' + ' '.join(cmd)) 101 102 103def may_elfhack(path): 104 ''' 105 Return whether elfhack() should be called 106 ''' 107 # elfhack only supports libraries. We should check the ELF header for 108 # the right flag, but checking the file extension works too. 109 from buildconfig import substs 110 return ('USE_ELF_HACK' in substs and substs['USE_ELF_HACK'] and 111 path.endswith(substs['DLL_SUFFIX']) and 112 'COMPILE_ENVIRONMENT' in substs and substs['COMPILE_ENVIRONMENT']) 113 114 115def elfhack(path): 116 ''' 117 Execute the elfhack command on the given path. 118 ''' 119 from buildconfig import topobjdir 120 cmd = [os.path.join(topobjdir, 'build/unix/elfhack/elfhack'), path] 121 if 'ELF_HACK_FLAGS' in os.environ: 122 cmd[1:0] = os.environ['ELF_HACK_FLAGS'].split() 123 if subprocess.call(cmd) != 0: 124 errors.fatal('Error executing ' + ' '.join(cmd)) 125