summaryrefslogtreecommitdiffstats
path: root/python/mozbuild/mozpack/executables.py
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--python/mozbuild/mozpack/executables.py182
1 files changed, 182 insertions, 0 deletions
diff --git a/python/mozbuild/mozpack/executables.py b/python/mozbuild/mozpack/executables.py
new file mode 100644
index 0000000000..0130e089e5
--- /dev/null
+++ b/python/mozbuild/mozpack/executables.py
@@ -0,0 +1,182 @@
+# 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/.
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import os
+import struct
+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
+
+ELF_SIGNATURE = 0x7F454C46 # Elf binary
+
+UNKNOWN = 0
+MACHO = 1
+ELF = 2
+
+
+def get_type(path):
+ """
+ Check the signature of the give file and returns what kind of executable
+ matches.
+ """
+ with open(path, "rb") as f:
+ 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
+
+ # Bug 1658632: clang-11-based strip complains about d3dcompiler_47.dll.
+ # It's not clear why this happens, but as a quick fix just avoid stripping
+ # this DLL. It's not from our build anyway.
+ if "d3dcompiler" in path:
+ return False
+ 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))
+
+
+def xz_compress(path):
+ """
+ Execute xz to compress the given path.
+ """
+ if open(path, "rb").read(5)[1:] == "7zXZ":
+ print("%s is already compressed" % path)
+ return
+
+ from buildconfig import substs
+
+ xz = substs.get("XZ")
+ cmd = [xz, "-zkf", path]
+
+ # For now, the mozglue XZStream ELF loader can only support xz files
+ # with a single stream that contains a single block. In xz, there is no
+ # explicit option to set the max block count. Instead, we force xz to use
+ # single thread mode, which results in a single block.
+ cmd.extend(["--threads=1"])
+
+ bcj = None
+ if substs.get("MOZ_THUMB2"):
+ bcj = "--armthumb"
+ elif substs.get("CPU_ARCH") == "arm":
+ bcj = "--arm"
+ elif substs.get("CPU_ARCH") == "x86":
+ bcj = "--x86"
+
+ if bcj:
+ cmd.extend([bcj])
+
+ # We need to explicitly specify the LZMA filter chain to ensure consistent builds
+ # across platforms. Note that the dict size must be less then 16MiB per the hardcoded
+ # value in mozglue/linker/XZStream.cpp. This is the default LZMA filter chain for for
+ # xz-utils version 5.0. See:
+ # https://github.com/xz-mirror/xz/blob/v5.0.0/src/liblzma/lzma/lzma_encoder_presets.c
+ # https://github.com/xz-mirror/xz/blob/v5.0.0/src/liblzma/api/lzma/container.h#L31
+ cmd.extend(["--lzma2=dict=8MiB,lc=3,lp=0,pb=2,mode=normal,nice=64,mf=bt4,depth=0"])
+ print("xz-compressing %s with %s" % (path, " ".join(cmd)))
+
+ if subprocess.call(cmd) != 0:
+ errors.fatal("Error executing " + " ".join(cmd))
+ return
+
+ os.rename(path + ".xz", path)