1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
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)
|