File: verify_order.py

package info (click to toggle)
chromium 139.0.7258.127-2
  • links: PTS, VCS
  • area: main
  • in suites: forky
  • size: 6,122,156 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (89 lines) | stat: -rw-r--r-- 3,357 bytes parent folder | download | duplicates (7)
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
# Copyright 2020 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

# Verifies that no global text symbols are present in a Mach-O file
# that is not in a given allowlist. Also verifies the order of global symbols
# matches the order in the allowlist.

from __future__ import print_function

import argparse
import os.path
import re
import sys
import subprocess


def has_duplicates(x):
  return len(x) != len(set(x))


def parse_order_file(filename):
  """Parses a file containing symbols like passed to ld64's -order_file.

  Doesn't support arch or object file prefixes.
  """
  strip_comments = re.compile('#.*$')
  symbols = [strip_comments.sub('', line).strip() for line in open(filename)]
  symbols = list(filter(None, symbols))
  if has_duplicates(symbols):
    print('order file "%s" contains duplicate symbols' % filename,
          file=sys.stderr)
    sys.exit(1)
  return symbols


def check_symbol_table(binary, allowed_symbols, nm, symbol_file):
  actual_symbols = subprocess.check_output([nm, '-jUng', binary]).decode('utf8')
  actual_symbols = [s.rstrip() for s in actual_symbols.splitlines()]
  def print_syms(syms):
    print('\n'.join(['  ' + s for s in syms]), file=sys.stderr)
  try:
    # Check that actual_symbols is a sublist of allowed_symbols.
    # Every exported symbol must be in allowed_symbols, and the order of the
    # exported symbols must match the order in allowed_symbols. Not all
    # symbols in allowed_symbols need to be present in the actual exports.
    # The approach here is O(m*n), but allowed_symbols is very small so that's
    # fine. Order matters, so can't use set().
    symbol_indices = [allowed_symbols.index(s) for s in actual_symbols]
    if symbol_indices != sorted(symbol_indices):
      print('"%s" exports symbols in order different from order file %s' %
            (binary, symbol_file),
            file=sys.stderr)
      print('actual order:', file=sys.stderr)
      print_syms(actual_symbols)
      print('expected order:', file=sys.stderr)
      print_syms(allowed_symbols)
      sys.exit(1)
    # If we get here, every symbol in actual_symbols is in allowed_symbols, in
    # the right order. nm's output doesn't contain duplicates, so we are done.
    assert not has_duplicates(allowed_symbols)
  except ValueError as e:
    print('unexpected export: %s' % e, file=sys.stderr)
    print('allowed exports from %s:' % symbol_file, file=sys.stderr)
    print_syms(allowed_symbols)
    sys.exit(1)


def main():
  parser = argparse.ArgumentParser(
    description='Check order of exported symbols of a given binary.')
  parser.add_argument('--stamp', required=True,
    help='Touch this stamp file on success.')
  parser.add_argument('--binary', required=True, help='Check this binary.')
  parser.add_argument('--nm-path', required=True, help='Path to nm')
  parser.add_argument('--symbol-file', required=True,
    help='Order file listing expected public symbols.')
  args = parser.parse_args()

  allowed_symbols = parse_order_file(args.symbol_file)
  check_symbol_table(
      args.binary, allowed_symbols, args.nm_path, args.symbol_file)
  # TODO(thakis): Also verify global symbols in the binary's export trie.

  open(args.stamp, 'w').close()  # Update mtime on stamp file.


if __name__ == '__main__':
  main()