1#!/usr/bin/env python 2# 3# Copyright (C) 2016 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17"""Builds a database of symbol version introductions.""" 18import argparse 19import json 20import logging 21import os 22 23 24THIS_DIR = os.path.realpath(os.path.dirname(__file__)) 25 26 27ALL_ARCHITECTURES = ( 28 'arm', 29 'arm64', 30 'mips', 31 'mips64', 32 'x86', 33 'x86_64', 34) 35 36 37def logger(): 38 """Returns the default logger for this module.""" 39 return logging.getLogger(__name__) 40 41 42def get_platform_versions(): 43 """Returns a list of the platform versions we have data for.""" 44 versions = [] 45 platforms_dir = os.path.join(THIS_DIR, 'platforms') 46 logger().debug('Getting platform versions from %s', platforms_dir) 47 for name in os.listdir(platforms_dir): 48 if name.startswith('android-'): 49 versions.append(int(name.split('-')[1])) 50 return versions 51 52 53def add_symbols(symbols, symbol_file_path, version, arch, is_var): 54 """Adds symbols from a file to the symbol dict.""" 55 with open(symbol_file_path) as symbol_file: 56 names = symbol_file.readlines() 57 58 for name in names: 59 name = name.strip() 60 if not name: 61 continue 62 introduced_tag = 'introduced-' + arch 63 if name in symbols: 64 assert symbols[name]['is_var'] == is_var 65 if introduced_tag in symbols[name]: 66 continue 67 symbols[name][introduced_tag] = version 68 else: 69 symbols[name] = {} 70 symbols[name]['is_var'] = is_var 71 symbols[name][introduced_tag] = version 72 73 74def build_symbol_db(lib_name): 75 """Returns a dict of symbols and their version information. 76 77 Args: 78 lib_name: Name of the library to return file mapping for. 79 80 Returns: dict of symbol information in the following format: 81 { 82 "symbol_name": { 83 "is_var": "true", 84 "introduced-arm": 9, 85 "introduced-x86": 14, 86 "introduced-mips": 16, 87 "introduced-arm64": 21, 88 "introduced-mips64": 21, 89 "introduced-x86_64": 21, 90 }, 91 ... 92 } 93 """ 94 symbols = {} 95 versions = sorted(get_platform_versions()) 96 for version in versions: 97 for arch in ALL_ARCHITECTURES: 98 symbols_dir = os.path.join( 99 THIS_DIR, 'platforms', 'android-' + str(version), 100 'arch-' + arch, 'symbols') 101 if not os.path.exists(symbols_dir): 102 logger().debug('Skipping non-existent %s', symbols_dir) 103 continue 104 105 logger().info('Processing android-%d arch-%s', version, arch) 106 107 funcs_file_name = lib_name + '.so.functions.txt' 108 funcs_file = os.path.join(symbols_dir, funcs_file_name) 109 if os.path.exists(funcs_file): 110 add_symbols(symbols, funcs_file, version, arch, is_var='false') 111 112 vars_file_name = lib_name + '.so.variables.txt' 113 vars_file = os.path.join(symbols_dir, vars_file_name) 114 if os.path.exists(vars_file): 115 add_symbols(symbols, vars_file, version, arch, is_var='true') 116 return symbols 117 118 119def parse_args(): 120 """Returns parsed command line arguments.""" 121 parser = argparse.ArgumentParser() 122 123 parser.add_argument('-v', '--verbose', action='count', default=0) 124 125 parser.add_argument( 126 'library_name', metavar='LIBRARY_NAME', 127 help='Name of the library to create a database for.') 128 129 return parser.parse_args() 130 131 132def main(): 133 """Program entry point.""" 134 args = parse_args() 135 os.chdir(THIS_DIR) 136 137 verbose_map = (logging.WARNING, logging.INFO, logging.DEBUG) 138 verbosity = args.verbose 139 if verbosity > 2: 140 verbosity = 2 141 logging.basicConfig(level=verbose_map[verbosity]) 142 143 symbol_db = build_symbol_db(args.library_name) 144 with open(args.library_name + '.so.json', 'w') as db_file: 145 json.dump(symbol_db, db_file, indent=4, separators=(',', ': '), 146 sort_keys=True) 147 148 149if __name__ == '__main__': 150 main() 151