tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

check-symbols.py (3545B)


      1 #!/usr/bin/env python3
      2 
      3 import difflib
      4 import os
      5 import re
      6 import shutil
      7 import subprocess
      8 import sys
      9 
     10 os.environ["LC_ALL"] = "C"  # otherwise 'nm' prints in wrong order
     11 
     12 srcdir = sys.argv[1]
     13 base_srcdir = sys.argv[2]
     14 builddir = sys.argv[3]
     15 
     16 IGNORED_SYMBOLS = [
     17    "_fini",
     18    "_init",
     19    "_fdata",
     20    "_ftext",
     21    "_fbss",
     22    "__bss_start",
     23    "__bss_start__",
     24    "__bss_end__",
     25    "_edata",
     26    "_end",
     27    "_bss_end__",
     28    "__end__",
     29    "__gcov_.*",
     30    "llvm_.*",
     31    "flush_fn_list",
     32    "writeout_fn_list",
     33    "mangle_path",
     34    "lprofDirMode",
     35    "reset_fn_list",
     36 ]
     37 
     38 # Rust
     39 IGNORED_SYMBOLS += [
     40    "rust_eh_personality",
     41    "_.*3std9panicking11EMPTY_PANIC.*",  # 'std.*::panicking::EMPTY_PANIC::.*'
     42    "_.*3std3sys3pal4unix4args3imp15ARGV_INIT_ARRAY.*",  # 'std.*::sys::pal::unix::args::imp::ARGV_INIT_ARRAY::.*'
     43    "_.*3std3sys4args4unix3imp15ARGV_INIT_ARRAY.*",  # std.*::sys::args::unix::imp::ARGV_INIT_ARRAY::.*
     44    "_.*17compiler_builtins.*",  # 'compiler_builtins.*::.*'
     45    ".*__rustc.*",  # '.*__rustc.*' # Eg. _RNvCsgSLETaxrkfn_7___rustc17___rust_probestack
     46    '_hb_harfrust_.*_rs',
     47 ]
     48 
     49 IGNORED_SYMBOLS = "|".join(IGNORED_SYMBOLS)
     50 
     51 nm = os.getenv("NM", shutil.which("nm"))
     52 if not nm:
     53    print("check-symbols.py: 'nm' not found; skipping test")
     54    sys.exit(77)
     55 
     56 tested = False
     57 stat = 0
     58 
     59 for soname in [
     60    "harfbuzz",
     61    "harfbuzz-subset",
     62    "harfbuzz-icu",
     63    "harfbuzz-gobject",
     64    "harfbuzz-cairo",
     65 ]:
     66    for suffix in ["so", "dylib"]:
     67        so = os.path.join(builddir, "lib%s.%s" % (soname, suffix))
     68        if not os.path.exists(so):
     69            continue
     70 
     71        # On macOS, C symbols are prefixed with _
     72        symprefix = "_" if suffix == "dylib" else ""
     73 
     74        EXPORTED_SYMBOLS = [
     75            s.split()[2]
     76            for s in re.findall(
     77                r"^.+ [BCDGIRSTu] .+$",
     78                subprocess.check_output(nm.split() + [so]).decode("utf-8"),
     79                re.MULTILINE,
     80            )
     81            if not re.match(r".* %s(%s)\b" % (symprefix, IGNORED_SYMBOLS), s)
     82        ]
     83 
     84        prefix = (
     85            (symprefix + os.path.basename(so))
     86            .replace("libharfbuzz", "hb")
     87            .replace("-", "_")
     88            .split(".")[0]
     89        )
     90 
     91        print("Checking that %s does not expose internal symbols" % so)
     92        suspicious_symbols = [
     93            x for x in EXPORTED_SYMBOLS if not re.match(r"^%s(_|$)" % prefix, x)
     94        ]
     95        if suspicious_symbols:
     96            print("Ouch, internal symbols exposed:", suspicious_symbols)
     97            stat = 1
     98 
     99        def_path = os.path.join(builddir, soname + ".def")
    100        if not os.path.exists(def_path):
    101            print("'%s' not found; skipping" % def_path)
    102        else:
    103            print("Checking that %s has the same symbol list as %s" % (so, def_path))
    104            with open(def_path, "r", encoding="utf-8") as f:
    105                def_file = f.read()
    106            diff_result = list(
    107                difflib.context_diff(
    108                    def_file.splitlines(),
    109                    ["EXPORTS"]
    110                    + [re.sub("^%shb" % symprefix, "hb", x) for x in EXPORTED_SYMBOLS]
    111                    +
    112                    # cheat: copy the last line from the def file!
    113                    [def_file.splitlines()[-1]],
    114                )
    115            )
    116 
    117            if diff_result:
    118                print("\n".join(diff_result))
    119                stat = 1
    120 
    121            tested = True
    122 
    123 if not tested:
    124    print("check-symbols.py: no shared libraries found; skipping test")
    125    sys.exit(77)
    126 
    127 sys.exit(stat)