tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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))