# 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 argparse import os import re import subprocess import sys
import buildconfig from mozpack.executables import ELF, UNKNOWN, get_type from packaging.version import Version
def at_least_one(iter):
saw_one = False for item in iter:
saw_one = True yield item ifnot saw_one: raise Empty()
# Iterates the symbol table on ELF binaries. def iter_elf_symbols(binary, all=False):
ty = get_type(binary) # Static libraries are ar archives. Assume they are ELF. if ty == UNKNOWN and open(binary, "rb").read(8) == b"!<arch>\n":
ty = ELF assert ty == ELF
def looks_like_readelf_data(data): return len(data) >= 8and data[0].endswith(":") and data[0][:-1].isdigit()
for line in get_output(
READELF, "--wide", "--syms"if all else"--dyn-syms", binary
):
data = line.split() ifnot looks_like_readelf_data(data): # Older versions of llvm-readelf would use .hash for --dyn-syms, # which would add an extra column at the beginning.
data = data[1:] ifnot looks_like_readelf_data(data): continue
n, addr, size, type, bind, vis, index, name = data[:8]
if"@"in name:
name, ver = name.rsplit("@", 1) while name.endswith("@"):
name = name[:-1] else:
ver = None yield { "addr": int(addr, 16), # readelf output may contain decimal values or hexadecimal # values prefixed with 0x for the size. Let python autodetect. "size": int(size, 0), "name": name, "version": ver,
}
def iter_readelf_dynamic(binary): for line in get_output(READELF, "-d", binary):
data = line.split(None, 2) if data and len(data) == 3and data[0].startswith("0x"): yield data[1].rstrip(")").lstrip("("), data[2]
unwanted = {} try: for sym in at_least_one(iter_elf_symbols(binary)): # Only check versions on undefined symbols if sym["addr"] != 0: continue
# No version to check ifnot sym["version"]: continue
for _, prefix, max_version in checks: if sym["version"].startswith(prefix):
version = Version(sym["version"][len(prefix) :]) if version > max_version:
unwanted.setdefault(prefix, []).append(sym) except Empty: raise RuntimeError("Could not parse readelf output?") if unwanted:
error = [] for lib, prefix, _ in checks: if prefix in unwanted:
error.append( "We do not want these {} symbol versions to be used:".format(lib)
)
error.extend( " {} ({})".format(s["name"], s["version"]) for s in unwanted[prefix]
) raise RuntimeError("\n".join(error))
def check_textrel(binary): if get_type(binary) != ELF: raise Skip() try: for tag, value in at_least_one(iter_readelf_dynamic(binary)): if tag == "TEXTREL"or (tag == "FLAGS"and"TEXTREL"in value): raise RuntimeError( "We do not want text relocations in libraries and programs"
) except Empty: raise RuntimeError("Could not parse readelf output?")
def check_pt_load(binary): if get_type(binary) != ELF ornot is_libxul(binary): raise Skip()
count = 0 for line in get_output(READELF, "-l", binary):
data = line.split() if data and data[0] == "LOAD":
count += 1 if count <= 1: raise RuntimeError("Expected more than one PT_LOAD segment")
def check_mozglue_order(binary): if PLATFORM != "Android": raise Skip() # While this is very unlikely (libc being added by the compiler at the end # of the linker command line), if libmozglue.so ends up after libc.so, all # hell breaks loose, so better safe than sorry, and check it's actually the # case. try:
mozglue = libc = None for n, (tag, value) in enumerate(at_least_one(iter_readelf_dynamic(binary))): if tag == "NEEDED": if"[libmozglue.so]"in value:
mozglue = n elif"[libc.so]"in value:
libc = n if libc isNone: raise RuntimeError("libc.so is not linked?") if mozglue isnotNoneand libc < mozglue: raise RuntimeError("libmozglue.so must be linked before libc.so") except Empty: raise RuntimeError("Could not parse readelf output?")
def check_networking(binary):
retcode = 0
networking_functions = set(
[ # socketpair is not concerning; it is restricted to AF_UNIX "connect", "accept", "listen", "recv", "send", # We would be concerned by recvmsg and sendmsg; but we believe # they are okay as documented in 1376621#c23 "gethostbyname", "gethostbyaddr", "gethostent", "sethostent", "endhostent", "gethostent_r", "gethostbyname2", "gethostbyaddr_r", "gethostbyname_r", "gethostbyname2_r", "getservent", "getservbyname", "getservbyport", "setservent", "getprotoent", "getprotobyname", "getprotobynumber", "setprotoent", "endprotoent",
]
)
bad_occurences_names = set()
try: for sym in at_least_one(iter_elf_symbols(binary, all=True)): if sym["addr"] == 0and sym["name"] in networking_functions:
bad_occurences_names.add(sym["name"]) except Empty: raise RuntimeError("Could not parse readelf output?")
basename = os.path.basename(binary) if bad_occurences_names:
s = ( "TEST-UNEXPECTED-FAIL | check_networking | {} | Identified {} "
+ "networking function(s) being imported in the rust static library ({})"
)
print(
s.format(
basename,
len(bad_occurences_names), ",".join(sorted(bad_occurences_names)),
),
file=sys.stderr,
)
retcode = 1 elif buildconfig.substs.get("MOZ_AUTOMATION"):
print("TEST-PASS | check_networking | {}".format(basename)) return retcode
def checks(binary): # The clang-plugin is built as target but is really a host binary. # Cheat and pretend we weren't called. if"clang-plugin"in binary: return0
checks = [] if buildconfig.substs.get("MOZ_STDCXX_COMPAT") and PLATFORM == "Linux":
checks.append(check_binary_compat)
# Disabled for local builds because of readelf performance: See bug 1472496 ifnot buildconfig.substs.get("DEVELOPER_OPTIONS"):
checks.append(check_textrel)
checks.append(check_pt_load)
checks.append(check_mozglue_order)
retcode = 0
basename = os.path.basename(binary) for c in checks: try:
name = c.__name__
c(binary) if buildconfig.substs.get("MOZ_AUTOMATION"):
print("TEST-PASS | {} | {}".format(name, basename)) except Skip: pass except RuntimeError as e:
print( "TEST-UNEXPECTED-FAIL | {} | {} | {}".format(name, basename, str(e)),
file=sys.stderr,
)
retcode = 1 return retcode
def main(args):
parser = argparse.ArgumentParser(description="Check built binaries")
parser.add_argument( "--networking",
action="store_true",
help="Perform checks for networking functions",
)
parser.add_argument( "binary", metavar="PATH", help="Location of the binary to check"
)
options = parser.parse_args(args)
if options.networking: return check_networking(options.binary) return checks(options.binary)
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-06-04)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.