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