1fd4e5da5Sopenharmony_ci#!/usr/bin/env python 2fd4e5da5Sopenharmony_ci# Copyright (c) 2017 Google Inc. 3fd4e5da5Sopenharmony_ci 4fd4e5da5Sopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License"); 5fd4e5da5Sopenharmony_ci# you may not use this file except in compliance with the License. 6fd4e5da5Sopenharmony_ci# You may obtain a copy of the License at 7fd4e5da5Sopenharmony_ci# 8fd4e5da5Sopenharmony_ci# http://www.apache.org/licenses/LICENSE-2.0 9fd4e5da5Sopenharmony_ci# 10fd4e5da5Sopenharmony_ci# Unless required by applicable law or agreed to in writing, software 11fd4e5da5Sopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS, 12fd4e5da5Sopenharmony_ci# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13fd4e5da5Sopenharmony_ci# See the License for the specific language governing permissions and 14fd4e5da5Sopenharmony_ci# limitations under the License. 15fd4e5da5Sopenharmony_ci"""Ensures that all externally visible functions in the library have an appropriate name 16fd4e5da5Sopenharmony_ci 17fd4e5da5Sopenharmony_ciAppropriate function names are: 18fd4e5da5Sopenharmony_ci - names starting with spv, 19fd4e5da5Sopenharmony_ci - anything in a namespace, 20fd4e5da5Sopenharmony_ci - functions added by the protobuf compiler, 21fd4e5da5Sopenharmony_ci - and weak definitions of new and delete.""" 22fd4e5da5Sopenharmony_ci 23fd4e5da5Sopenharmony_ciimport os.path 24fd4e5da5Sopenharmony_ciimport re 25fd4e5da5Sopenharmony_ciimport subprocess 26fd4e5da5Sopenharmony_ciimport sys 27fd4e5da5Sopenharmony_ci 28fd4e5da5Sopenharmony_ci 29fd4e5da5Sopenharmony_ciPROG = 'check_symbol_exports' 30fd4e5da5Sopenharmony_ci 31fd4e5da5Sopenharmony_ci 32fd4e5da5Sopenharmony_cidef command_output(cmd, directory): 33fd4e5da5Sopenharmony_ci """Runs a command in a directory and returns its standard output stream. 34fd4e5da5Sopenharmony_ci 35fd4e5da5Sopenharmony_ci Captures the standard error stream. 36fd4e5da5Sopenharmony_ci 37fd4e5da5Sopenharmony_ci Raises a RuntimeError if the command fails to launch or otherwise fails. 38fd4e5da5Sopenharmony_ci """ 39fd4e5da5Sopenharmony_ci p = subprocess.Popen(cmd, 40fd4e5da5Sopenharmony_ci cwd=directory, 41fd4e5da5Sopenharmony_ci stdout=subprocess.PIPE, 42fd4e5da5Sopenharmony_ci stderr=subprocess.PIPE, 43fd4e5da5Sopenharmony_ci universal_newlines=True) 44fd4e5da5Sopenharmony_ci (stdout, _) = p.communicate() 45fd4e5da5Sopenharmony_ci if p.returncode != 0: 46fd4e5da5Sopenharmony_ci raise RuntimeError('Failed to run %s in %s' % (cmd, directory)) 47fd4e5da5Sopenharmony_ci return stdout 48fd4e5da5Sopenharmony_ci 49fd4e5da5Sopenharmony_ci 50fd4e5da5Sopenharmony_cidef check_library(library): 51fd4e5da5Sopenharmony_ci """Scans the given library file for global exports. If all such 52fd4e5da5Sopenharmony_ci exports are namespaced or begin with spv (in either C or C++ styles) 53fd4e5da5Sopenharmony_ci then return 0. Otherwise emit a message and return 1.""" 54fd4e5da5Sopenharmony_ci 55fd4e5da5Sopenharmony_ci # The pattern for an externally visible symbol record 56fd4e5da5Sopenharmony_ci symbol_pattern = re.compile(r'^[0-aA-Fa-f]+ +([wg]) *F \.text.*[0-9A-Fa-f]+ +(.*)') 57fd4e5da5Sopenharmony_ci 58fd4e5da5Sopenharmony_ci # Ok patterns are as follows, assuming Itanium name mangling: 59fd4e5da5Sopenharmony_ci # spv[A-Z] : extern "C" symbol starting with spv 60fd4e5da5Sopenharmony_ci # _ZN : something in a namespace 61fd4e5da5Sopenharmony_ci # _ZSt : something in the standard namespace 62fd4e5da5Sopenharmony_ci # _ZZN : something in a local scope and namespace 63fd4e5da5Sopenharmony_ci # _Z[0-9]+spv[A-Z_] : C++ symbol starting with spv[A-Z_] 64fd4e5da5Sopenharmony_ci symbol_ok_pattern = re.compile(r'^(spv[A-Z]|_ZN|_ZSt|_ZZN|_Z[0-9]+spv[A-Z_])') 65fd4e5da5Sopenharmony_ci 66fd4e5da5Sopenharmony_ci # In addition, the following pattern allowlists global functions that are added 67fd4e5da5Sopenharmony_ci # by the protobuf compiler: 68fd4e5da5Sopenharmony_ci # - AddDescriptors_spvtoolsfuzz_2eproto() 69fd4e5da5Sopenharmony_ci # - InitDefaults_spvtoolsfuzz_2eproto() 70fd4e5da5Sopenharmony_ci symbol_allowlist_pattern = re.compile(r'_Z[0-9]+.*spvtoolsfuzz_2eproto.*') 71fd4e5da5Sopenharmony_ci 72fd4e5da5Sopenharmony_ci symbol_is_new_or_delete = re.compile(r'^(_Zna|_Znw|_Zdl|_Zda)') 73fd4e5da5Sopenharmony_ci # Compilaion for Arm has various thunks for constructors, destructors, vtables. 74fd4e5da5Sopenharmony_ci # They are weak. 75fd4e5da5Sopenharmony_ci symbol_is_thunk = re.compile(r'^_ZT') 76fd4e5da5Sopenharmony_ci 77fd4e5da5Sopenharmony_ci # This occurs in NDK builds. 78fd4e5da5Sopenharmony_ci symbol_is_hidden = re.compile(r'^\.hidden ') 79fd4e5da5Sopenharmony_ci 80fd4e5da5Sopenharmony_ci seen = set() 81fd4e5da5Sopenharmony_ci result = 0 82fd4e5da5Sopenharmony_ci for line in command_output(['objdump', '-t', library], '.').split('\n'): 83fd4e5da5Sopenharmony_ci match = symbol_pattern.search(line) 84fd4e5da5Sopenharmony_ci if match: 85fd4e5da5Sopenharmony_ci linkage = match.group(1) 86fd4e5da5Sopenharmony_ci symbol = match.group(2) 87fd4e5da5Sopenharmony_ci if symbol not in seen: 88fd4e5da5Sopenharmony_ci seen.add(symbol) 89fd4e5da5Sopenharmony_ci #print("look at '{}'".format(symbol)) 90fd4e5da5Sopenharmony_ci if not (symbol_is_new_or_delete.match(symbol) and linkage == 'w'): 91fd4e5da5Sopenharmony_ci if not (symbol_is_thunk.match(symbol) and linkage == 'w'): 92fd4e5da5Sopenharmony_ci if not (symbol_allowlist_pattern.match(symbol) or 93fd4e5da5Sopenharmony_ci symbol_ok_pattern.match(symbol) or 94fd4e5da5Sopenharmony_ci symbol_is_hidden.match(symbol)): 95fd4e5da5Sopenharmony_ci print('{}: error: Unescaped exported symbol: {}'.format(PROG, symbol)) 96fd4e5da5Sopenharmony_ci result = 1 97fd4e5da5Sopenharmony_ci return result 98fd4e5da5Sopenharmony_ci 99fd4e5da5Sopenharmony_ci 100fd4e5da5Sopenharmony_cidef main(): 101fd4e5da5Sopenharmony_ci import argparse 102fd4e5da5Sopenharmony_ci parser = argparse.ArgumentParser(description='Check global names exported from a library') 103fd4e5da5Sopenharmony_ci parser.add_argument('library', help='The static library to examine') 104fd4e5da5Sopenharmony_ci args = parser.parse_args() 105fd4e5da5Sopenharmony_ci 106fd4e5da5Sopenharmony_ci if not os.path.isfile(args.library): 107fd4e5da5Sopenharmony_ci print('{}: error: {} does not exist'.format(PROG, args.library)) 108fd4e5da5Sopenharmony_ci sys.exit(1) 109fd4e5da5Sopenharmony_ci 110fd4e5da5Sopenharmony_ci if os.name == 'posix': 111fd4e5da5Sopenharmony_ci status = check_library(args.library) 112fd4e5da5Sopenharmony_ci sys.exit(status) 113fd4e5da5Sopenharmony_ci else: 114fd4e5da5Sopenharmony_ci print('Passing test since not on Posix') 115fd4e5da5Sopenharmony_ci sys.exit(0) 116fd4e5da5Sopenharmony_ci 117fd4e5da5Sopenharmony_ci 118fd4e5da5Sopenharmony_ciif __name__ == '__main__': 119fd4e5da5Sopenharmony_ci main() 120