Merge "Fix freopen() where the path is null."
diff --git a/libc/Android.bp b/libc/Android.bp
index 1bced2e..6ba60ca 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -1579,6 +1579,7 @@
         "bionic/NetdClient.cpp",
         "arch-common/bionic/crtend_so.S",
     ],
+    bazel_module: { bp2build_available: true },
 }
 
 filegroup {
@@ -1589,6 +1590,7 @@
         "bionic/malloc_common.cpp",
         "bionic/malloc_limit.cpp",
     ],
+    bazel_module: { bp2build_available: true },
 }
 
 filegroup {
@@ -1597,6 +1599,7 @@
         "arch-arm/bionic/exidx_dynamic.c",
         "arch-arm/bionic/atexit_legacy.c",
     ],
+    bazel_module: { bp2build_available: true },
 }
 
 // ========================================================
@@ -1754,6 +1757,7 @@
     srcs: ["libc.map.txt"],
     tool_files: [":bionic-generate-version-script"],
     cmd: "$(location :bionic-generate-version-script) arm $(in) $(out)",
+    bazel_module: { bp2build_available: true },
 }
 
 genrule {
@@ -1762,6 +1766,7 @@
     srcs: ["libc.map.txt"],
     tool_files: [":bionic-generate-version-script"],
     cmd: "$(location :bionic-generate-version-script) arm64 $(in) $(out)",
+    bazel_module: { bp2build_available: true },
 }
 
 genrule {
@@ -1770,6 +1775,7 @@
     srcs: ["libc.map.txt"],
     tool_files: [":bionic-generate-version-script"],
     cmd: "$(location :bionic-generate-version-script) x86 $(in) $(out)",
+    bazel_module: { bp2build_available: true },
 }
 
 genrule {
@@ -1778,6 +1784,7 @@
     srcs: ["libc.map.txt"],
     tool_files: [":bionic-generate-version-script"],
     cmd: "$(location :bionic-generate-version-script) x86_64 $(in) $(out)",
+    bazel_module: { bp2build_available: true },
 }
 
 // Headers that only other parts of the platform can include.
@@ -2356,53 +2363,11 @@
     ],
 }
 
-// Generate the C++ policy sources for app and system seccomp-bpf filters.
-python_binary_host {
-    name: "genseccomp",
-    main: "tools/genseccomp.py",
-
-    srcs: [
-        "tools/genseccomp.py",
-        "tools/gensyscalls.py",
-    ],
-
-    data: [
-        "kernel/uapi/**/*.h",
-    ],
-
-    version: {
-        py2: {
-            enabled: true,
-        },
-        py3: {
-            enabled: false,
-        },
-    },
+filegroup {
+    name: "all_kernel_uapi_headers",
+    srcs: ["kernel/uapi/**/*.h"],
 }
 
-python_binary_host {
-    name: "genfunctosyscallnrs",
-    main: "tools/genfunctosyscallnrs.py",
-
-    srcs: [
-        "tools/genseccomp.py",
-        "tools/genfunctosyscallnrs.py",
-        "tools/gensyscalls.py",
-    ],
-
-    data: [
-        "kernel/uapi/**/*.h",
-    ],
-
-    version: {
-        py2: {
-            enabled: true,
-        },
-        py3: {
-            enabled: false,
-        },
-    },
-}
 
 cc_genrule {
     name: "func_to_syscall_nrs",
diff --git a/libc/arch-arm/generic/bionic/strlen.c b/libc/arch-arm/generic/bionic/strlen.c
index 43d9e51..a6fde8b 100644
--- a/libc/arch-arm/generic/bionic/strlen.c
+++ b/libc/arch-arm/generic/bionic/strlen.c
@@ -116,8 +116,8 @@
         "beq     2f                         \n"
         "add     %[l], %[l], #1             \n"
         "tst     %[v], #0xFF0000            \n"
-        "it      ne                         \n"
-        "addne   %[l], %[l], #1             \n"
+        "beq     2f                         \n"
+        "add     %[l], %[l], #1             \n"
         "2:                                 \n"
         : [l]"=&r"(l), [v]"=&r"(v), [t]"=&r"(t), [s]"=&r"(u.b)
         : "%[l]"(l), "%[s]"(u.b), [mask]"r"(0x80808080UL)
diff --git a/libc/tools/Android.bp b/libc/tools/Android.bp
index 13179a0..bf515ca 100644
--- a/libc/tools/Android.bp
+++ b/libc/tools/Android.bp
@@ -2,3 +2,33 @@
     name: "bionic-gensyscalls",
     srcs: ["gensyscalls.py"]
 }
+
+// Generate the C++ policy sources for app and system seccomp-bpf filters.
+python_binary_host {
+    name: "genseccomp",
+    main: "genseccomp.py",
+
+    srcs: [
+        "genseccomp.py",
+        "gensyscalls.py",
+    ],
+
+    data: [
+        ":all_kernel_uapi_headers",
+    ],
+}
+
+python_binary_host {
+    name: "genfunctosyscallnrs",
+    main: "genfunctosyscallnrs.py",
+
+    srcs: [
+        "genseccomp.py",
+        "genfunctosyscallnrs.py",
+        "gensyscalls.py",
+    ],
+
+    data: [
+        ":all_kernel_uapi_headers",
+    ],
+}
diff --git a/libc/tools/generate-NOTICE.py b/libc/tools/generate_notice.py
similarity index 60%
rename from libc/tools/generate-NOTICE.py
rename to libc/tools/generate_notice.py
index b6deb9c..e0e6b32 100755
--- a/libc/tools/generate-NOTICE.py
+++ b/libc/tools/generate_notice.py
@@ -1,28 +1,29 @@
 #!/usr/bin/env python
-# Run with directory arguments from any directory, with no special setup required.
+# Run with directory arguments from any directory, with no special setup
+# required.
 
-import ftplib
-import hashlib
 import os
+from pathlib import Path
 import re
-import shutil
-import string
-import subprocess
 import sys
-import tarfile
-import tempfile
+from typing import Sequence
 
 VERBOSE = False
 
+copyrights = set()
+
+
 def warn(s):
     sys.stderr.write("warning: %s\n" % s)
 
+
 def warn_verbose(s):
     if VERBOSE:
         warn(s)
 
-def is_interesting(path):
-    path = path.lower()
+
+def is_interesting(path_str: str) -> bool:
+    path = Path(path_str.lower())
     uninteresting_extensions = [
         ".bp",
         ".map",
@@ -33,12 +34,13 @@
         ".swp",
         ".txt",
     ]
-    if os.path.splitext(path)[1] in uninteresting_extensions:
+    if path.suffix in uninteresting_extensions:
         return False
-    if path.endswith("/notice") or path.endswith("/readme") or path.endswith("/pylintrc"):
+    if path.name in {"notice", "readme", "pylintrc"}:
         return False
     return True
 
+
 def is_auto_generated(content):
     if "Generated by gensyscalls.py" in content or "generated by genserv.py" in content:
         return True
@@ -46,14 +48,40 @@
         return True
     return False
 
-copyrights = set()
 
-def extract_copyright_at(lines, i):
-    hash = lines[i].startswith("#")
+def is_copyright_end(line: str, first_line_was_hash: bool) -> bool:
+    endings = [
+        " $FreeBSD: ",
+        "$Citrus$",
+        "$FreeBSD$",
+        "*/",
+        "From: @(#)",
+        # OpenBSD likes to say where stuff originally came from:
+        "Original version ID:",
+        "\t$Citrus: ",
+        "\t$NetBSD: ",
+        "\t$OpenBSD: ",
+        "\t@(#)",
+        "\tcitrus Id: ",
+        "\tfrom: @(#)",
+        "from OpenBSD:",
+    ]
+    if first_line_was_hash and not line:
+        return True
+
+    for ending in endings:
+        if ending in line:
+            return True
+
+    return False
+
+
+def extract_copyright_at(lines: Sequence[str], i: int) -> int:
+    first_line_was_hash = lines[i].startswith("#")
 
     # Do we need to back up to find the start of the copyright header?
     start = i
-    if not hash:
+    if not first_line_was_hash:
         while start > 0:
             if "/*" in lines[start - 1]:
                 break
@@ -62,20 +90,7 @@
     # Read comment lines until we hit something that terminates a
     # copyright header.
     while i < len(lines):
-        if "*/" in lines[i]:
-            break
-        if hash and len(lines[i]) == 0:
-            break
-        if "\t@(#)" in lines[i] or "\tfrom: @(#)" in lines[i] or "From: @(#)" in lines[i] or "from OpenBSD:" in lines[i]:
-            break
-        if "\tcitrus Id: " in lines[i]:
-            break
-        if "\t$Citrus: " in lines[i] or "\t$OpenBSD: " in lines[i] or " $FreeBSD: " in lines[i] or "\t$NetBSD: " in lines[i]:
-            break
-        if "$FreeBSD$" in lines[i] or "$Citrus$" in lines[i]:
-            break
-        # OpenBSD likes to say where stuff originally came from:
-        if "Original version ID:" in lines[i]:
+        if is_copyright_end(lines[i], first_line_was_hash):
             break
         i += 1
 
@@ -83,7 +98,10 @@
 
     # Trim trailing cruft.
     while end > 0:
-        if lines[end - 1] != " *" and lines[end - 1] != " * ====================================================":
+        line = lines[end - 1]
+        if line not in {
+                " *", " * ===================================================="
+        }:
             break
         end -= 1
 
@@ -92,7 +110,7 @@
     for line in lines[start:end]:
         line = line.replace("\t", "    ")
         line = line.replace("/* ", "")
-        line = re.sub("^ \* ", "", line)
+        line = re.sub(r"^ \* ", "", line)
         line = line.replace("** ", "")
         line = line.replace("# ", "")
         if "SPDX-License-Identifier:" in line:
@@ -102,7 +120,7 @@
         line = line.replace("--Copyright--", "")
         line = line.rstrip()
         # These come last and take care of "blank" comment lines.
-        if line == "#" or line == " *" or line == "**" or line == "-":
+        if line in {"#", " *", "**", "-"}:
             line = ""
         clean_lines.append(line)
 
@@ -112,19 +130,18 @@
     while clean_lines[len(clean_lines) - 1] == "":
         clean_lines = clean_lines[0:(len(clean_lines) - 1)]
 
-    copyright = "\n".join(clean_lines)
-    copyrights.add(copyright)
+    copyrights.add("\n".join(clean_lines))
 
     return i
 
 
-def do_file(path):
-    with open(path, "r") as the_file:
-        try:
-            content = open(path, "r").read().decode("utf-8")
-        except UnicodeDecodeError:
-            warn("bad UTF-8 in %s" % path)
-            content = open(path, "r").read().decode("iso-8859-1")
+def do_file(path: str) -> None:
+    raw = Path(path).read_bytes()
+    try:
+        content = raw.decode("utf-8")
+    except UnicodeDecodeError:
+        warn("bad UTF-8 in %s" % path)
+        content = raw.decode("iso-8859-1")
 
     lines = content.split("\n")
 
@@ -140,10 +157,12 @@
         if "public domain" in content.lower():
             warn_verbose("ignoring public domain file %s" % path)
             return
-        warn('no copyright notice found in "%s" (%d lines)' % (path, len(lines)))
+        warn('no copyright notice found in "%s" (%d lines)' %
+             (path, len(lines)))
         return
 
-    # Manually iterate because extract_copyright_at tells us how many lines to skip.
+    # Manually iterate because extract_copyright_at tells us how many lines to
+    # skip.
     i = 0
     while i < len(lines):
         if "Copyright" in lines[i] and not "@(#) Copyright" in lines[i]:
@@ -152,7 +171,7 @@
             i += 1
 
 
-def do_dir(path):
+def do_dir(arg):
     for directory, sub_directories, filenames in os.walk(arg):
         if ".git" in sub_directories:
             sub_directories.remove(".git")
@@ -164,20 +183,23 @@
                 do_file(path)
 
 
-args = sys.argv[1:]
-if len(args) == 0:
-    args = [ "." ]
+def main() -> None:
+    args = sys.argv[1:]
+    if len(args) == 0:
+        args = ["."]
 
-for arg in args:
-    if os.path.isdir(arg):
-        do_dir(arg)
-    else:
-        do_file(arg)
+    for arg in args:
+        if os.path.isdir(arg):
+            do_dir(arg)
+        else:
+            do_file(arg)
 
-for copyright in sorted(copyrights):
-    print copyright.encode("utf-8")
-    print
-    print "-------------------------------------------------------------------"
-    print
+    for notice in sorted(copyrights):
+        print(notice)
+        print()
+        print("-" * 67)
+        print()
 
-sys.exit(0)
+
+if __name__ == "__main__":
+    main()
diff --git a/libc/tools/genfunctosyscallnrs.py b/libc/tools/genfunctosyscallnrs.py
index ecfc8ab..26642f9 100755
--- a/libc/tools/genfunctosyscallnrs.py
+++ b/libc/tools/genfunctosyscallnrs.py
@@ -1,60 +1,71 @@
 #!/usr/bin/env python
 
 import argparse
-import collections
 import logging
 import os
 import re
-import subprocess
-import textwrap
 
 from gensyscalls import SupportedArchitectures, SysCallsTxtParser
 from genseccomp import parse_syscall_NRs
 
-def load_syscall_names_from_file(file_path, architecture):
-  parser = SysCallsTxtParser()
-  parser.parse_open_file(open(file_path))
-  arch_map = {}
-  for syscall in parser.syscalls:
-    if syscall.get(architecture):
-      arch_map[syscall["func"]] = syscall["name"];
 
-  return arch_map
+def load_syscall_names_from_file(file_path, architecture):
+    parser = SysCallsTxtParser()
+    parser.parse_open_file(open(file_path))
+    arch_map = {}
+    for syscall in parser.syscalls:
+        if syscall.get(architecture):
+            arch_map[syscall["func"]] = syscall["name"]
+
+    return arch_map
+
 
 def gen_syscall_nrs(out_file, base_syscall_file, syscall_NRs):
-  for arch in SupportedArchitectures:
-    base_names = load_syscall_names_from_file(base_syscall_file, arch)
+    for arch in SupportedArchitectures:
+        base_names = load_syscall_names_from_file(base_syscall_file, arch)
 
-    for func,syscall in base_names.iteritems():
-      out_file.write("#define __" + arch + "_" + func + " " + str(syscall_NRs[arch][syscall]) + ";\n")
+        for func, syscall in base_names.items():
+            out_file.write("#define __" + arch + "_" + func + " " +
+                           str(syscall_NRs[arch][syscall]) + ";\n")
+
 
 def main():
-  parser = argparse.ArgumentParser(
-      description="Generates a mapping of bionic functions to system call numbers per architecture.")
-  parser.add_argument("--verbose", "-v", help="Enables verbose logging.")
-  parser.add_argument("--out-dir",
-                      help="The output directory for the output files")
-  parser.add_argument("base_file", metavar="base-file", type=str,
-                      help="The path of the base syscall list (SYSCALLS.TXT).")
-  parser.add_argument("files", metavar="FILE", type=str, nargs="+",
-                      help=("A syscall name-number mapping file for an architecture.\n"))
-  args = parser.parse_args()
+    parser = argparse.ArgumentParser(
+        description=
+        "Generates a mapping of bionic functions to system call numbers per architecture."
+    )
+    parser.add_argument("--verbose", "-v", help="Enables verbose logging.")
+    parser.add_argument("--out-dir",
+                        help="The output directory for the output files")
+    parser.add_argument(
+        "base_file",
+        metavar="base-file",
+        type=str,
+        help="The path of the base syscall list (SYSCALLS.TXT).")
+    parser.add_argument(
+        "files",
+        metavar="FILE",
+        type=str,
+        nargs="+",
+        help=("A syscall name-number mapping file for an architecture.\n"))
+    args = parser.parse_args()
 
-  if args.verbose:
-    logging.basicConfig(level=logging.DEBUG)
-  else:
-    logging.basicConfig(level=logging.INFO)
+    if args.verbose:
+        logging.basicConfig(level=logging.DEBUG)
+    else:
+        logging.basicConfig(level=logging.INFO)
 
-  syscall_files = []
-  syscall_NRs = {}
-  for filename in args.files:
-    m = re.search(r"libseccomp_gen_syscall_nrs_([^/]+)", filename)
-    syscall_NRs[m.group(1)] = parse_syscall_NRs(filename)
+    syscall_NRs = {}
+    for filename in args.files:
+        m = re.search(r"libseccomp_gen_syscall_nrs_([^/]+)", filename)
+        syscall_NRs[m.group(1)] = parse_syscall_NRs(filename)
 
-  output_path = os.path.join(args.out_dir, "func_to_syscall_nrs.h")
-  with open(output_path, "w") as output_file:
-    gen_syscall_nrs(out_file=output_file,
-             syscall_NRs=syscall_NRs, base_syscall_file=args.base_file)
+    output_path = os.path.join(args.out_dir, "func_to_syscall_nrs.h")
+    with open(output_path, "w") as output_file:
+        gen_syscall_nrs(out_file=output_file,
+                        syscall_NRs=syscall_NRs,
+                        base_syscall_file=args.base_file)
+
 
 if __name__ == "__main__":
-  main()
+    main()
diff --git a/libc/tools/genseccomp.py b/libc/tools/genseccomp.py
index 89eeb44..a78f6c1 100755
--- a/libc/tools/genseccomp.py
+++ b/libc/tools/genseccomp.py
@@ -1,11 +1,10 @@
 #!/usr/bin/env python
 
 import argparse
-import collections
 import logging
+import operator
 import os
 import re
-import subprocess
 import textwrap
 
 from gensyscalls import SupportedArchitectures, SysCallsTxtParser
@@ -16,7 +15,7 @@
 BPF_ALLOW = "BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW)"
 
 
-class SyscallRange(object):
+class SyscallRange:
   def __init__(self, name, value):
     self.names = [name]
     self.begin = value
@@ -35,23 +34,23 @@
 def load_syscall_names_from_file(file_path, architecture):
   parser = SysCallsTxtParser()
   parser.parse_open_file(open(file_path))
-  return set([x["name"] for x in parser.syscalls if x.get(architecture)])
+  return {x["name"] for x in parser.syscalls if x.get(architecture)}
 
 
 def load_syscall_priorities_from_file(file_path):
   format_re = re.compile(r'^\s*([A-Za-z_][A-Za-z0-9_]+)\s*$')
   priorities = []
-  with open(file_path) as f:
-    for line in f:
-      m = format_re.match(line)
-      if not m:
+  with open(file_path) as priority_file:
+    for line in priority_file:
+      match = format_re.match(line)
+      if match is None:
         continue
       try:
-        name = m.group(1)
+        name = match.group(1)
         priorities.append(name)
-      except:
-        logging.debug('Failed to parse %s from %s', (line, file_path))
-        pass
+      except IndexError:
+        # TODO: This should be impossible becauase it wouldn't have matched?
+        logging.exception('Failed to parse %s from %s', line, file_path)
 
   return priorities
 
@@ -93,7 +92,7 @@
   with open(names_path) as f:
     for line in f:
       m = constant_re.match(line)
-      if not m:
+      if m is None:
         continue
       try:
         name = m.group(1)
@@ -102,12 +101,21 @@
                                   m.group(2)))
 
         constants[name] = value
-      except:
+      except:  # pylint: disable=bare-except
+        # TODO: This seems wrong.
+        # Key error doesn't seem like the error the original author was trying
+        # to catch. It looks like the intent was to catch IndexError from
+        # match.group() for non-matching lines, but that's impossible because
+        # the match object is checked and continued if not matched. What
+        # actually happens is that KeyError is thrown by constants[x.group(0)]
+        # on at least the first run because the dict is empty.
+        #
+        # It's also matching syntax errors because not all C integer literals
+        # are valid Python integer literals, e.g. 10L.
         logging.debug('Failed to parse %s', line)
-        pass
 
   syscalls = {}
-  for name, value in constants.iteritems():
+  for name, value in constants.items():
     if not name.startswith("__NR_") and not name.startswith("__ARM_NR"):
       continue
     if name.startswith("__NR_"):
@@ -120,7 +128,7 @@
 
 def convert_NRs_to_ranges(syscalls):
   # Sort the values so we convert to ranges and binary chop
-  syscalls = sorted(syscalls, lambda x, y: cmp(x[1], y[1]))
+  syscalls = sorted(syscalls, key=operator.itemgetter(1))
 
   # Turn into a list of ranges. Keep the names for the comments
   ranges = []
@@ -148,12 +156,12 @@
     # We will replace {fail} and {allow} with appropriate range jumps later
     return [BPF_JGE.format(ranges[0].end, "{fail}", "{allow}") +
             ", //" + "|".join(ranges[0].names)]
-  else:
-    half = (len(ranges) + 1) / 2
-    first = convert_to_intermediate_bpf(ranges[:half])
-    second = convert_to_intermediate_bpf(ranges[half:])
-    jump = [BPF_JGE.format(ranges[half].begin, len(first), 0) + ","]
-    return jump + first + second
+
+  half = (len(ranges) + 1) // 2
+  first = convert_to_intermediate_bpf(ranges[:half])
+  second = convert_to_intermediate_bpf(ranges[half:])
+  jump = [BPF_JGE.format(ranges[half].begin, len(first), 0) + ","]
+  return jump + first + second
 
 
 # Converts the prioritized syscalls to a bpf list that  is prepended to the
@@ -162,7 +170,7 @@
 # immediately
 def convert_priority_to_intermediate_bpf(priority_syscalls):
   result = []
-  for i, syscall in enumerate(priority_syscalls):
+  for syscall in priority_syscalls:
     result.append(BPF_JEQ.format(syscall[1], "{allow}", 0) +
                   ", //" + syscall[0])
   return result
@@ -227,7 +235,8 @@
   return convert_bpf_to_output(bpf, architecture, name_modifier)
 
 
-def gen_policy(name_modifier, out_dir, base_syscall_file, syscall_files, syscall_NRs, priority_file):
+def gen_policy(name_modifier, out_dir, base_syscall_file, syscall_files,
+               syscall_NRs, priority_file):
   for arch in SupportedArchitectures:
     base_names = load_syscall_names_from_file(base_syscall_file, arch)
     allowlist_names = set()
@@ -251,7 +260,6 @@
     output = construct_bpf(allowed_syscalls, arch, name_modifier, priorities)
 
     # And output policy
-    existing = ""
     filename_modifier = "_" + name_modifier if name_modifier else ""
     output_path = os.path.join(out_dir,
                                "{}{}_policy.cpp".format(arch, filename_modifier))
@@ -274,8 +282,8 @@
                       help=("The path of the input files. In order to "
                             "simplify the build rules, it can take any of the "
                             "following files: \n"
-                            "* /blocklist.*\.txt$/ syscall blocklist.\n"
-                            "* /allowlist.*\.txt$/ syscall allowlist.\n"
+                            "* /blocklist.*\\.txt$/ syscall blocklist.\n"
+                            "* /allowlist.*\\.txt$/ syscall allowlist.\n"
                             "* /priority.txt$/ priorities for bpf rules.\n"
                             "* otherwise, syscall name-number mapping.\n"))
   args = parser.parse_args()
diff --git a/libc/tools/gensyscalls.py b/libc/tools/gensyscalls.py
index 0e0e25f..d8d4302 100755
--- a/libc/tools/gensyscalls.py
+++ b/libc/tools/gensyscalls.py
@@ -5,7 +5,6 @@
 # makefiles used to build all the stubs.
 
 import atexit
-import commands
 import filecmp
 import glob
 import re
@@ -315,7 +314,7 @@
         self.lineno   = 0
 
     def E(self, msg):
-        print "%d: %s" % (self.lineno, msg)
+        print("%d: %s" % (self.lineno, msg))
 
     def parse_line(self, line):
         """ parse a syscall spec line.
@@ -340,7 +339,7 @@
             return
 
         syscall_func = return_type[-1]
-        return_type  = string.join(return_type[:-1],' ')
+        return_type  = ' '.join(return_type[:-1])
         socketcall_id = -1
 
         pos_colon = syscall_func.find(':')
@@ -372,13 +371,13 @@
             alias_delim = syscall_name.find('|')
             if alias_delim > 0:
                 syscall_name = syscall_name[:alias_delim]
-            syscall_aliases = string.split(alias_list, ',')
+            syscall_aliases = alias_list.split(',')
         else:
             syscall_aliases = []
 
         if pos_rparen > pos_lparen+1:
             syscall_params = line[pos_lparen+1:pos_rparen].split(',')
-            params         = string.join(syscall_params,',')
+            params         = ','.join(syscall_params)
         else:
             syscall_params = []
             params         = "void"
@@ -398,7 +397,7 @@
             for arch in SupportedArchitectures:
                 t[arch] = True
         else:
-            for arch in string.split(arch_list, ','):
+            for arch in arch_list.split(','):
                 if arch == "lp32":
                     for arch in SupportedArchitectures:
                         if "64" not in arch:
@@ -464,7 +463,7 @@
 
 if __name__ == "__main__":
     if len(sys.argv) < 2:
-      print "Usage: gensyscalls.py ARCH SOURCE_FILE"
+      print("Usage: gensyscalls.py ARCH SOURCE_FILE")
       sys.exit(1)
 
     arch = sys.argv[1]
diff --git a/libc/tools/mypy.ini b/libc/tools/mypy.ini
new file mode 100644
index 0000000..0269354
--- /dev/null
+++ b/libc/tools/mypy.ini
@@ -0,0 +1,3 @@
+[mypy]
+# TODO: Enable.
+# disallow_untyped_defs = True
diff --git a/libc/tools/pylintrc b/libc/tools/pylintrc
new file mode 100644
index 0000000..df319e3
--- /dev/null
+++ b/libc/tools/pylintrc
@@ -0,0 +1,8 @@
+[MESSAGES CONTROL]
+disable=
+    eval-used,
+    design,
+    fixme,
+    invalid-name,
+    logging-fstring-interpolation,
+    missing-docstring
diff --git a/libc/tools/test_genseccomp.py b/libc/tools/test_genseccomp.py
index 812218e..8bd3517 100755
--- a/libc/tools/test_genseccomp.py
+++ b/libc/tools/test_genseccomp.py
@@ -1,176 +1,65 @@
 #!/usr/bin/env python
 # Unit tests for genseccomp.py
 
-import cStringIO
 import textwrap
 import unittest
 
 import genseccomp
 
 class TestGenseccomp(unittest.TestCase):
-  def setUp(self):
-    genseccomp.set_dir()
-
-  def get_config(self, arch):
-    for i in genseccomp.POLICY_CONFIGS:
-      if i[0] == arch:
-        return i
-    self.fail("No such architecture")
-
-  def get_headers(self, arch):
-    return self.get_config(arch)[1]
-
-  def get_switches(self, arch):
-    return self.get_config(arch)[2]
-
-  def test_get_names(self):
-    bionic = cStringIO.StringIO(textwrap.dedent("""\
-int __llseek:_llseek(int, unsigned long, unsigned long, off64_t*, int) arm,x86
-int         fchown:fchown(int, uid_t, gid_t)    arm64,x86_64
-    """))
-
-    allowlist = cStringIO.StringIO(textwrap.dedent("""\
-ssize_t     read(int, void*, size_t)        all
-    """))
-
-    empty = cStringIO.StringIO(textwrap.dedent("""\
-    """))
-
-    names = genseccomp.get_names([bionic, allowlist, empty], "arm")
-    bionic.seek(0)
-    allowlist.seek(0)
-    empty.seek(0)
-    names64 = genseccomp.get_names([bionic, allowlist, empty], "arm64")
-    bionic.seek(0)
-    allowlist.seek(0)
-    empty.seek(0)
-
-    self.assertIn("fchown", names64)
-    self.assertNotIn("fchown", names)
-    self.assertIn("_llseek", names)
-    self.assertNotIn("_llseek", names64)
-    self.assertIn("read", names)
-    self.assertIn("read", names64)
-
-    # Blocklist item must be in bionic
-    blocklist = cStringIO.StringIO(textwrap.dedent("""\
-int         fchown2:fchown2(int, uid_t, gid_t)    arm64,x86_64
-    """))
-    with self.assertRaises(RuntimeError):
-      genseccomp.get_names([bionic, allowlist, blocklist], "arm")
-    bionic.seek(0)
-    allowlist.seek(0)
-    blocklist.seek(0)
-
-    # Test blocklist item is removed
-    blocklist = cStringIO.StringIO(textwrap.dedent("""\
-int         fchown:fchown(int, uid_t, gid_t)    arm64,x86_64
-    """))
-    names = genseccomp.get_names([bionic, allowlist, blocklist], "arm64")
-    bionic.seek(0)
-    allowlist.seek(0)
-    blocklist.seek(0)
-    self.assertIn("read", names)
-    self.assertNotIn("fchown", names)
-
-    # Blocklist item must not be in allowlist
-    allowlist = cStringIO.StringIO(textwrap.dedent("""\
-int         fchown:fchown(int, uid_t, gid_t)    arm64,x86_64
-    """))
-    with self.assertRaises(RuntimeError):
-      genseccomp.get_names([empty, allowlist, blocklist], "arm")
-    empty.seek(0)
-    allowlist.seek(0)
-    blocklist.seek(0)
-
-    # No dups in bionic and allowlist
-    allowlist = cStringIO.StringIO(textwrap.dedent("""\
-int __llseek:_llseek(int, unsigned long, unsigned long, off64_t*, int) arm,x86
-    """))
-    with self.assertRaises(RuntimeError):
-      genseccomp.get_names([bionic, allowlist, empty], "arm")
-    bionic.seek(0)
-    allowlist.seek(0)
-    empty.seek(0)
-
-  def test_convert_names_to_NRs(self):
-    self.assertEquals(genseccomp.convert_names_to_NRs(["open"],
-                                                      self.get_headers("arm"),
-                                                      self.get_switches("arm")),
-                      [("open", 5)])
-
-    self.assertEquals(genseccomp.convert_names_to_NRs(["__ARM_NR_set_tls"],
-                                                      self.get_headers("arm"),
-                                                      self.get_switches("arm")),
-                      [('__ARM_NR_set_tls', 983045)])
-
-    self.assertEquals(genseccomp.convert_names_to_NRs(["openat"],
-                                                      self.get_headers("arm64"),
-                                                      self.get_switches("arm64")),
-                      [("openat", 56)])
-
-    self.assertEquals(genseccomp.convert_names_to_NRs(["openat"],
-                                                      self.get_headers("x86"),
-                                                      self.get_switches("x86")),
-                      [("openat", 295)])
-
-    self.assertEquals(genseccomp.convert_names_to_NRs(["openat"],
-                                                      self.get_headers("x86_64"),
-                                                      self.get_switches("x86_64")),
-                      [("openat", 257)])
-
-
   def test_convert_NRs_to_ranges(self):
     ranges = genseccomp.convert_NRs_to_ranges([("b", 2), ("a", 1)])
-    self.assertEquals(len(ranges), 1)
-    self.assertEquals(ranges[0].begin, 1)
-    self.assertEquals(ranges[0].end, 3)
-    self.assertItemsEqual(ranges[0].names, ["a", "b"])
+    self.assertEqual(len(ranges), 1)
+    self.assertEqual(ranges[0].begin, 1)
+    self.assertEqual(ranges[0].end, 3)
+    self.assertEqual(set(ranges[0].names), {"a", "b"})
 
     ranges = genseccomp.convert_NRs_to_ranges([("b", 3), ("a", 1)])
-    self.assertEquals(len(ranges), 2)
-    self.assertEquals(ranges[0].begin, 1)
-    self.assertEquals(ranges[0].end, 2)
-    self.assertItemsEqual(ranges[0].names, ["a"])
-    self.assertEquals(ranges[1].begin, 3)
-    self.assertEquals(ranges[1].end, 4)
-    self.assertItemsEqual(ranges[1].names, ["b"])
+    self.assertEqual(len(ranges), 2)
+    self.assertEqual(ranges[0].begin, 1)
+    self.assertEqual(ranges[0].end, 2)
+    self.assertEqual(set(ranges[0].names), {"a"})
+    self.assertEqual(ranges[1].begin, 3)
+    self.assertEqual(ranges[1].end, 4)
+    self.assertEqual(set(ranges[1].names), {"b"})
 
   def test_convert_to_intermediate_bpf(self):
     ranges = genseccomp.convert_NRs_to_ranges([("b", 2), ("a", 1)])
     bpf = genseccomp.convert_to_intermediate_bpf(ranges)
-    self.assertEquals(bpf, ['BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 3, {fail}, {allow}), //a|b'])
+    self.assertEqual(bpf, ['BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 3, {fail}, {allow}), //a|b'])
 
     ranges = genseccomp.convert_NRs_to_ranges([("b", 3), ("a", 1)])
     bpf = genseccomp.convert_to_intermediate_bpf(ranges)
-    self.assertEquals(bpf, ['BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 3, 1, 0),',
+    self.assertEqual(bpf, ['BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 3, 1, 0),',
                             'BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 2, {fail}, {allow}), //a',
                             'BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4, {fail}, {allow}), //b'])
 
   def test_convert_ranges_to_bpf(self):
     ranges = genseccomp.convert_NRs_to_ranges([("b", 2), ("a", 1)])
-    bpf = genseccomp.convert_ranges_to_bpf(ranges)
-    self.assertEquals(bpf, ['BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 1, 0, 2),',
+    bpf = genseccomp.convert_ranges_to_bpf(ranges, priority_syscalls=[])
+    self.assertEqual(bpf, ['BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 1, 0, 2),',
                             'BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 3, 1, 0), //a|b',
                             'BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),'])
 
     ranges = genseccomp.convert_NRs_to_ranges([("b", 3), ("a", 1)])
-    bpf = genseccomp.convert_ranges_to_bpf(ranges)
-    self.assertEquals(bpf, ['BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 1, 0, 4),',
+    bpf = genseccomp.convert_ranges_to_bpf(ranges, priority_syscalls=[])
+    self.assertEqual(bpf, ['BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 1, 0, 4),',
                             'BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 3, 1, 0),',
                             'BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 2, 2, 1), //a',
                             'BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4, 1, 0), //b',
                             'BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),'])
 
   def test_convert_bpf_to_output(self):
-    output = genseccomp.convert_bpf_to_output(["line1", "line2"], "arm")
+    output = genseccomp.convert_bpf_to_output(["line1", "line2"],
+                                              "arm",
+                                              name_modifier="")
     expected_output = textwrap.dedent("""\
-    // Autogenerated file - edit at your peril!!
+    // File autogenerated by genseccomp.py - edit at your peril!!
 
     #include <linux/filter.h>
     #include <errno.h>
 
-    #include "seccomp_bpfs.h"
+    #include "seccomp/seccomp_bpfs.h"
     const sock_filter arm_filter[] = {
     line1
     line2
@@ -178,43 +67,7 @@
 
     const size_t arm_filter_size = sizeof(arm_filter) / sizeof(struct sock_filter);
     """)
-    self.assertEquals(output, expected_output)
-
-  def test_construct_bpf(self):
-    syscalls = cStringIO.StringIO(textwrap.dedent("""\
-    int __llseek:_llseek(int, unsigned long, unsigned long, off64_t*, int) arm,x86
-    int         fchown:fchown(int, uid_t, gid_t)    arm64,x86_64
-    """))
-
-    allowlist = cStringIO.StringIO(textwrap.dedent("""\
-    ssize_t     read(int, void*, size_t)        all
-    """))
-
-    blocklist = cStringIO.StringIO(textwrap.dedent("""\
-    """))
-
-    syscall_files = [syscalls, allowlist, blocklist]
-    output = genseccomp.construct_bpf(syscall_files, "arm", self.get_headers("arm"),
-                                      self.get_switches("arm"))
-
-    expected_output = textwrap.dedent("""\
-    // Autogenerated file - edit at your peril!!
-
-    #include <linux/filter.h>
-    #include <errno.h>
-
-    #include "seccomp_bpfs.h"
-    const sock_filter arm_filter[] = {
-    BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 3, 0, 4),
-    BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 140, 1, 0),
-    BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 4, 2, 1), //read
-    BPF_JUMP(BPF_JMP|BPF_JGE|BPF_K, 141, 1, 0), //_llseek
-    BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
-    };
-
-    const size_t arm_filter_size = sizeof(arm_filter) / sizeof(struct sock_filter);
-    """)
-    self.assertEquals(output, expected_output)
+    self.assertEqual(output, expected_output)
 
 
 if __name__ == '__main__':
diff --git a/libm/Android.bp b/libm/Android.bp
index 735b1cf..723d126 100644
--- a/libm/Android.bp
+++ b/libm/Android.bp
@@ -475,6 +475,7 @@
         "-fno-math-errno",
         "-Wall",
         "-Werror",
+        "-Wno-ignored-pragmas",
         "-Wno-missing-braces",
         "-Wno-parentheses",
         "-Wno-sign-compare",
diff --git a/tests/Android.bp b/tests/Android.bp
index a6a930e..5840018 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -17,7 +17,6 @@
 cc_defaults {
     name: "bionic_tests_defaults",
     host_supported: true,
-    cpp_std: "experimental",
     target: {
         darwin: {
             enabled: false,
@@ -1017,6 +1016,10 @@
 
     // For now, these tests run forever, so do not use the isolation framework.
     isolated: false,
+    // Running forever, do not consider unit test.
+    test_options: {
+        unit_test: false,
+    },
 
     srcs: [
         "malloc_stress_test.cpp",
diff --git a/tools/Android.bp b/tools/Android.bp
index c540c3c..dfeea19 100644
--- a/tools/Android.bp
+++ b/tools/Android.bp
@@ -3,4 +3,5 @@
 filegroup {
     name: "bionic-generate-version-script",
     srcs: ["generate-version-script.py"],
+    bazel_module: { bp2build_available: true },
 }
diff --git a/tools/update_notice.sh b/tools/update_notice.sh
index a309bc2..302974f 100755
--- a/tools/update_notice.sh
+++ b/tools/update_notice.sh
@@ -1,7 +1,11 @@
 #!/bin/bash
 DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
 cd $DIR/..
-./libc/tools/generate-NOTICE.py libc libm > libc/NOTICE
+python3 ./libc/tools/generate_notice.py libc libm > libc/NOTICE
+if [ $? -ne 0 ]; then
+  >&2 echo NOTICE file generation failed
+  exit 1
+fi
 
 git diff --exit-code HEAD libc/NOTICE
 exit $?