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