michael@0: # This Source Code Form is subject to the terms of the Mozilla Public michael@0: # License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: # file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: michael@0: import os michael@0: import struct michael@0: import subprocess michael@0: from mozpack.errors import errors michael@0: michael@0: MACHO_SIGNATURES = [ michael@0: 0xfeedface, # mach-o 32-bits big endian michael@0: 0xcefaedfe, # mach-o 32-bits little endian michael@0: 0xfeedfacf, # mach-o 64-bits big endian michael@0: 0xcffaedfe, # mach-o 64-bits little endian michael@0: ] michael@0: michael@0: FAT_SIGNATURE = 0xcafebabe # mach-o FAT binary michael@0: michael@0: ELF_SIGNATURE = 0x7f454c46 # Elf binary michael@0: michael@0: UNKNOWN = 0 michael@0: MACHO = 1 michael@0: ELF = 2 michael@0: michael@0: def get_type(path): michael@0: ''' michael@0: Check the signature of the give file and returns what kind of executable michael@0: matches. michael@0: ''' michael@0: with open(path, 'rb') as f: michael@0: signature = f.read(4) michael@0: if len(signature) < 4: michael@0: return UNKNOWN michael@0: signature = struct.unpack('>L', signature)[0] michael@0: if signature == ELF_SIGNATURE: michael@0: return ELF michael@0: if signature in MACHO_SIGNATURES: michael@0: return MACHO michael@0: if signature != FAT_SIGNATURE: michael@0: return UNKNOWN michael@0: # We have to sanity check the second four bytes, because Java class michael@0: # files use the same magic number as Mach-O fat binaries. michael@0: # This logic is adapted from file(1), which says that Mach-O uses michael@0: # these bytes to count the number of architectures within, while michael@0: # Java uses it for a version number. Conveniently, there are only michael@0: # 18 labelled Mach-O architectures, and Java's first released michael@0: # class format used the version 43.0. michael@0: num = f.read(4) michael@0: if len(num) < 4: michael@0: return UNKNOWN michael@0: num = struct.unpack('>L', num)[0] michael@0: if num < 20: michael@0: return MACHO michael@0: return UNKNOWN michael@0: michael@0: michael@0: def is_executable(path): michael@0: ''' michael@0: Return whether a given file path points to an executable or a library, michael@0: where an executable or library is identified by: michael@0: - the file extension on OS/2 and WINNT michael@0: - the file signature on OS/X and ELF systems (GNU/Linux, Android, BSD, michael@0: Solaris) michael@0: michael@0: As this function is intended for use to choose between the ExecutableFile michael@0: and File classes in FileFinder, and choosing ExecutableFile only matters michael@0: on OS/2, OS/X, ELF and WINNT (in GCC build) systems, we don't bother michael@0: detecting other kind of executables. michael@0: ''' michael@0: from buildconfig import substs michael@0: if not os.path.exists(path): michael@0: return False michael@0: michael@0: if substs['OS_ARCH'] == 'WINNT': michael@0: return path.lower().endswith((substs['DLL_SUFFIX'], michael@0: substs['BIN_SUFFIX'])) michael@0: michael@0: return get_type(path) != UNKNOWN michael@0: michael@0: michael@0: def may_strip(path): michael@0: ''' michael@0: Return whether strip() should be called michael@0: ''' michael@0: from buildconfig import substs michael@0: return not substs['PKG_SKIP_STRIP'] michael@0: michael@0: michael@0: def strip(path): michael@0: ''' michael@0: Execute the STRIP command with STRIP_FLAGS on the given path. michael@0: ''' michael@0: from buildconfig import substs michael@0: strip = substs['STRIP'] michael@0: flags = substs['STRIP_FLAGS'].split() if 'STRIP_FLAGS' in substs else [] michael@0: cmd = [strip] + flags + [path] michael@0: if subprocess.call(cmd) != 0: michael@0: errors.fatal('Error executing ' + ' '.join(cmd)) michael@0: michael@0: michael@0: def may_elfhack(path): michael@0: ''' michael@0: Return whether elfhack() should be called michael@0: ''' michael@0: # elfhack only supports libraries. We should check the ELF header for michael@0: # the right flag, but checking the file extension works too. michael@0: from buildconfig import substs michael@0: return 'USE_ELF_HACK' in substs and substs['USE_ELF_HACK'] and \ michael@0: path.endswith(substs['DLL_SUFFIX']) michael@0: michael@0: michael@0: def elfhack(path): michael@0: ''' michael@0: Execute the elfhack command on the given path. michael@0: ''' michael@0: from buildconfig import topobjdir michael@0: cmd = [os.path.join(topobjdir, 'build/unix/elfhack/elfhack'), path] michael@0: if 'ELF_HACK_FLAGS' in os.environ: michael@0: cmd[1:0] = os.environ['ELF_HACK_FLAGS'].split() michael@0: if subprocess.call(cmd) != 0: michael@0: errors.fatal('Error executing ' + ' '.join(cmd))