tor-browser

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

assert_static_initializers.py (3685B)


      1 #!/usr/bin/env python3
      2 # Copyright 2017 The Chromium Authors
      3 # Use of this source code is governed by a BSD-style license that can be
      4 # found in the LICENSE file.
      5 
      6 """Checks the number of static initializers in an APK's library."""
      7 
      8 
      9 import argparse
     10 import os
     11 import re
     12 import subprocess
     13 import sys
     14 
     15 from util import build_utils
     16 
     17 _DUMP_STATIC_INITIALIZERS_PATH = os.path.join(build_utils.DIR_SOURCE_ROOT,
     18                                              'tools', 'linux',
     19                                              'dump-static-initializers.py')
     20 
     21 
     22 def _RunReadelf(so_path, options, tool_prefix=''):
     23  return subprocess.check_output(
     24      [tool_prefix + 'readobj', '--elf-output-style=GNU'] + options +
     25      [so_path]).decode('utf8')
     26 
     27 
     28 def _DumpStaticInitializers(so_path):
     29  subprocess.check_call([_DUMP_STATIC_INITIALIZERS_PATH, so_path])
     30 
     31 
     32 def _ReadInitArray(so_path, tool_prefix):
     33  stdout = _RunReadelf(so_path, ['-SW'], tool_prefix)
     34  # Matches: .init_array INIT_ARRAY 000000000516add0 5169dd0 000010 00 WA 0 0 8
     35  match = re.search(r'\.init_array.*$', stdout, re.MULTILINE)
     36  if not match:
     37    raise Exception('Did not find section: .init_array in {}:\n{}'.format(
     38        so_path, stdout))
     39  size_str = re.split(r'\W+', match.group(0))[5]
     40  return int(size_str, 16)
     41 
     42 
     43 def _CountStaticInitializers(so_path, tool_prefix):
     44  # Find the number of files with at least one static initializer.
     45  # First determine if we're 32 or 64 bit
     46  stdout = _RunReadelf(so_path, ['-h'], tool_prefix)
     47  elf_class_line = re.search('Class:.*$', stdout, re.MULTILINE).group(0)
     48  elf_class = re.split(r'\W+', elf_class_line)[1]
     49  if elf_class == 'ELF32':
     50    word_size = 4
     51  else:
     52    word_size = 8
     53 
     54  # Then find the number of files with global static initializers.
     55  # NOTE: this is very implementation-specific and makes assumptions
     56  # about how compiler and linker implement global static initializers.
     57  init_array_size = _ReadInitArray(so_path, tool_prefix)
     58  assert init_array_size % word_size == 0
     59  return init_array_size // word_size
     60 
     61 
     62 def main():
     63  parser = argparse.ArgumentParser()
     64  parser.add_argument('--touch', help='File to touch upon success')
     65  parser.add_argument('--tool-prefix', required=True,
     66                      help='Prefix for nm and friends')
     67  parser.add_argument('--expected-count', required=True, type=int,
     68                      help='Fail if number of static initializers is not '
     69                           'equal to this value.')
     70  parser.add_argument('--unstripped-so-path',
     71                      help='Path to the unstripped version of the .so '
     72                      'file if needed for better dumps.')
     73  parser.add_argument('so_path', help='Path to .so file.')
     74  args = parser.parse_args()
     75 
     76  si_count = _CountStaticInitializers(args.so_path, args.tool_prefix)
     77  if si_count != args.expected_count:
     78    print('Expected {} static initializers, but found {}.'.format(
     79        args.expected_count, si_count))
     80    if args.expected_count > si_count:
     81      print('You have removed one or more static initializers. Thanks!')
     82      print('To fix the build, update the expectation in:')
     83      print('    //chrome/android/static_initializers.gni')
     84      print()
     85 
     86    print('Dumping static initializers via dump-static-initializers.py:')
     87    sys.stdout.flush()
     88    dump_so_path = args.so_path
     89    if args.unstripped_so_path:
     90      dump_so_path = args.unstripped_so_path
     91    _DumpStaticInitializers(dump_so_path)
     92    print()
     93    print('For more information:')
     94    print('    https://chromium.googlesource.com/chromium/src/+/main/docs/'
     95          'static_initializers.md')
     96    sys.exit(1)
     97 
     98  if args.touch:
     99    open(args.touch, 'w')
    100 
    101 
    102 if __name__ == '__main__':
    103  main()