# 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
import subprocess
from io
import BytesIO
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
ELF_SIGNATURE = 0x7F454C46
# Elf binary
UNKNOWN = 0
MACHO = 1
ELF = 2
def get_type(path_or_fileobj):
"""
Check the signature of the give file
and returns what kind of executable
matches.
"""
if hasattr(path_or_fileobj,
"peek"):
f = BytesIO(path_or_fileobj.peek(8))
elif hasattr(path_or_fileobj,
"read"):
f = path_or_fileobj
else:
f = open(path_or_fileobj,
"rb")
signature = f.read(4)
if len(signature) < 4:
return UNKNOWN
signature = struct.unpack(
">L", signature)[0]
if signature == ELF_SIGNATURE:
return ELF
if signature
in MACHO_SIGNATURES:
return MACHO
if signature != FAT_SIGNATURE:
return UNKNOWN
# 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 UNKNOWN
num = struct.unpack(
">L", num)[0]
if num < 20:
return MACHO
return UNKNOWN
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
and WINNT
- 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, ELF
and WINNT (
in GCC build) systems, we don
't bother
detecting other kind of executables.
"""
from buildconfig
import substs
if not os.path.exists(path):
return False
if substs[
"OS_ARCH"] ==
"WINNT":
return path.lower().endswith((substs[
"DLL_SUFFIX"], substs[
"BIN_SUFFIX"]))
return get_type(path) != UNKNOWN
def may_strip(path):
"""
Return whether strip() should be called
"""
from buildconfig
import substs
return bool(substs.get(
"PKG_STRIP"))
def strip(path):
"""
Execute the STRIP command
with STRIP_FLAGS on the given path.
"""
from buildconfig
import substs
strip = substs[
"STRIP"]
flags = substs.get(
"STRIP_FLAGS", [])
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.
from buildconfig
import substs
return (
"USE_ELF_HACK" in substs
and substs[
"USE_ELF_HACK"]
and path.endswith(substs[
"DLL_SUFFIX"])
and "COMPILE_ENVIRONMENT" in substs
and substs[
"COMPILE_ENVIRONMENT"]
)
def elfhack(path):
"""
Execute the elfhack command on the given path.
"""
from buildconfig
import topobjdir
cmd = [os.path.join(topobjdir,
"build/unix/elfhack/elfhack"), path]
if subprocess.call(cmd) != 0:
errors.fatal(
"Error executing " +
" ".join(cmd))