1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/python/mozbuild/mozpack/executables.py Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,121 @@ 1.4 +# This Source Code Form is subject to the terms of the Mozilla Public 1.5 +# License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 +# file, You can obtain one at http://mozilla.org/MPL/2.0/. 1.7 + 1.8 +import os 1.9 +import struct 1.10 +import subprocess 1.11 +from mozpack.errors import errors 1.12 + 1.13 +MACHO_SIGNATURES = [ 1.14 + 0xfeedface, # mach-o 32-bits big endian 1.15 + 0xcefaedfe, # mach-o 32-bits little endian 1.16 + 0xfeedfacf, # mach-o 64-bits big endian 1.17 + 0xcffaedfe, # mach-o 64-bits little endian 1.18 +] 1.19 + 1.20 +FAT_SIGNATURE = 0xcafebabe # mach-o FAT binary 1.21 + 1.22 +ELF_SIGNATURE = 0x7f454c46 # Elf binary 1.23 + 1.24 +UNKNOWN = 0 1.25 +MACHO = 1 1.26 +ELF = 2 1.27 + 1.28 +def get_type(path): 1.29 + ''' 1.30 + Check the signature of the give file and returns what kind of executable 1.31 + matches. 1.32 + ''' 1.33 + with open(path, 'rb') as f: 1.34 + signature = f.read(4) 1.35 + if len(signature) < 4: 1.36 + return UNKNOWN 1.37 + signature = struct.unpack('>L', signature)[0] 1.38 + if signature == ELF_SIGNATURE: 1.39 + return ELF 1.40 + if signature in MACHO_SIGNATURES: 1.41 + return MACHO 1.42 + if signature != FAT_SIGNATURE: 1.43 + return UNKNOWN 1.44 + # We have to sanity check the second four bytes, because Java class 1.45 + # files use the same magic number as Mach-O fat binaries. 1.46 + # This logic is adapted from file(1), which says that Mach-O uses 1.47 + # these bytes to count the number of architectures within, while 1.48 + # Java uses it for a version number. Conveniently, there are only 1.49 + # 18 labelled Mach-O architectures, and Java's first released 1.50 + # class format used the version 43.0. 1.51 + num = f.read(4) 1.52 + if len(num) < 4: 1.53 + return UNKNOWN 1.54 + num = struct.unpack('>L', num)[0] 1.55 + if num < 20: 1.56 + return MACHO 1.57 + return UNKNOWN 1.58 + 1.59 + 1.60 +def is_executable(path): 1.61 + ''' 1.62 + Return whether a given file path points to an executable or a library, 1.63 + where an executable or library is identified by: 1.64 + - the file extension on OS/2 and WINNT 1.65 + - the file signature on OS/X and ELF systems (GNU/Linux, Android, BSD, 1.66 + Solaris) 1.67 + 1.68 + As this function is intended for use to choose between the ExecutableFile 1.69 + and File classes in FileFinder, and choosing ExecutableFile only matters 1.70 + on OS/2, OS/X, ELF and WINNT (in GCC build) systems, we don't bother 1.71 + detecting other kind of executables. 1.72 + ''' 1.73 + from buildconfig import substs 1.74 + if not os.path.exists(path): 1.75 + return False 1.76 + 1.77 + if substs['OS_ARCH'] == 'WINNT': 1.78 + return path.lower().endswith((substs['DLL_SUFFIX'], 1.79 + substs['BIN_SUFFIX'])) 1.80 + 1.81 + return get_type(path) != UNKNOWN 1.82 + 1.83 + 1.84 +def may_strip(path): 1.85 + ''' 1.86 + Return whether strip() should be called 1.87 + ''' 1.88 + from buildconfig import substs 1.89 + return not substs['PKG_SKIP_STRIP'] 1.90 + 1.91 + 1.92 +def strip(path): 1.93 + ''' 1.94 + Execute the STRIP command with STRIP_FLAGS on the given path. 1.95 + ''' 1.96 + from buildconfig import substs 1.97 + strip = substs['STRIP'] 1.98 + flags = substs['STRIP_FLAGS'].split() if 'STRIP_FLAGS' in substs else [] 1.99 + cmd = [strip] + flags + [path] 1.100 + if subprocess.call(cmd) != 0: 1.101 + errors.fatal('Error executing ' + ' '.join(cmd)) 1.102 + 1.103 + 1.104 +def may_elfhack(path): 1.105 + ''' 1.106 + Return whether elfhack() should be called 1.107 + ''' 1.108 + # elfhack only supports libraries. We should check the ELF header for 1.109 + # the right flag, but checking the file extension works too. 1.110 + from buildconfig import substs 1.111 + return 'USE_ELF_HACK' in substs and substs['USE_ELF_HACK'] and \ 1.112 + path.endswith(substs['DLL_SUFFIX']) 1.113 + 1.114 + 1.115 +def elfhack(path): 1.116 + ''' 1.117 + Execute the elfhack command on the given path. 1.118 + ''' 1.119 + from buildconfig import topobjdir 1.120 + cmd = [os.path.join(topobjdir, 'build/unix/elfhack/elfhack'), path] 1.121 + if 'ELF_HACK_FLAGS' in os.environ: 1.122 + cmd[1:0] = os.environ['ELF_HACK_FLAGS'].split() 1.123 + if subprocess.call(cmd) != 0: 1.124 + errors.fatal('Error executing ' + ' '.join(cmd))