python/mozbuild/mozpack/executables.py
author Hannes Verschore <hv1989@gmail.com>
Mon, 18 Feb 2013 18:32:10 +0100
changeset 122248 95e67683fe4e17e4b8c2321251fe6e7b0c28956d
parent 119472 1bbce6c7e0069e732090dcb88adb771965ee8039
child 125681 1da324d86a59599e04de6f6fadf6f1aaf73fc7c4
permissions -rw-r--r--
Bug 839315: IonMonkey: Fix fault in TypePolicy of MCompare for CompareType_Boolean and CompareType_String, r=jandem

# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

import os
import struct
from buildconfig import (
    substs,
    topobjdir,
)
import subprocess
from mozpack.errors import errors

MACHO_SIGNATURES = [
    0xfeedface,  # mach-o 32-bits big endian
    0xcefaedfe,  # mach-o 32-bits little endian
    0xfeedfacf,  # mach-o 64-bits big endian
    0xcffaedfe,  # mach-o 64-bits little endian
]

FAT_SIGNATURE = 0xcafebabe  # mach-o FAT binary

EXECUTABLE_SIGNATURES = [
    0x7f454c46,  # Elf
] + MACHO_SIGNATURES


def is_executable(path):
    '''
    Return whether a given file path points to an executable or a library,
    where an executable or library is identified by:
        - the file extension on OS/2
        - the file signature on OS/X and ELF systems (GNU/Linux, Android, BSD,
          Solaris)

    As this function is intended for use to choose between the ExecutableFile
    and File classes in FileFinder, and choosing ExecutableFile only matters
    on OS/2, OS/X and ELF systems, we don't bother detecting other kind of
    executables.
    '''
    if not os.path.exists(path):
        return False

    if substs['OS_ARCH'] == 'OS2':
        return path.lower().endswith((substs['DLL_SUFFIX'],
                                      substs['BIN_SUFFIX']))

    with open(path, 'rb') as f:
        signature = f.read(4)
        if len(signature) < 4:
            return False
        signature = struct.unpack('>L', signature)[0]
        if signature in EXECUTABLE_SIGNATURES:
            return True
        if signature != FAT_SIGNATURE:
            return False
        # We have to sanity check the second four bytes, because Java class
        # files use the same magic number as Mach-O fat binaries.
        # This logic is adapted from file(1), which says that Mach-O uses
        # these bytes to count the number of architectures within, while
        # Java uses it for a version number. Conveniently, there are only
        # 18 labelled Mach-O architectures, and Java's first released
        # class format used the version 43.0.
        num = f.read(4)
        if len(num) < 4:
            return False
        num = struct.unpack('>L', num)[0]
        return num < 20


def may_strip(path):
    '''
    Return whether strip() should be called
    '''
    return not substs['PKG_SKIP_STRIP']


def strip(path):
    '''
    Execute the STRIP command with STRIP_FLAGS on the given path.
    '''
    strip = substs['STRIP']
    flags = substs['STRIP_FLAGS'].split() if 'STRIP_FLAGS' in substs else []
    cmd = [strip] + flags + [path]
    if subprocess.call(cmd) != 0:
        errors.fatal('Error executing ' + ' '.join(cmd))


def may_elfhack(path):
    '''
    Return whether elfhack() should be called
    '''
    # elfhack only supports libraries. We should check the ELF header for
    # the right flag, but checking the file extension works too.
    return 'USE_ELF_HACK' in substs and substs['USE_ELF_HACK'] and \
           path.endswith(substs['DLL_SUFFIX'])


def elfhack(path):
    '''
    Execute the elfhack command on the given path.
    '''
    cmd = [os.path.join(topobjdir, 'build/unix/elfhack/elfhack'), path]
    if 'ELF_HACK_FLAGS' in os.environ:
        cmd[1:0] = os.environ['ELF_HACK_FLAGS'].split()
    if subprocess.call(cmd) != 0:
        errors.fatal('Error executing ' + ' '.join(cmd))