• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2# Copyright (c) 2017 Google Inc.
3
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#     http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15"""Checks names of global exports from a library."""
16
17from __future__ import print_function
18
19import os.path
20import re
21import subprocess
22import sys
23
24
25PROG = 'check_symbol_exports'
26
27
28def command_output(cmd, directory):
29    """Runs a command in a directory and returns its standard output stream.
30
31    Captures the standard error stream.
32
33    Raises a RuntimeError if the command fails to launch or otherwise fails.
34    """
35    p = subprocess.Popen(cmd,
36                         cwd=directory,
37                         stdout=subprocess.PIPE,
38                         stderr=subprocess.PIPE,
39                         universal_newlines=True)
40    (stdout, _) = p.communicate()
41    if p.returncode != 0:
42        raise RuntimeError('Failed to run %s in %s' % (cmd, directory))
43    return stdout
44
45
46def check_library(library):
47    """Scans the given library file for global exports.  If all such
48    exports are namespaced or begin with spv (in either C or C++ styles)
49    then return 0.  Otherwise emit a message and return 1."""
50
51    # The pattern for a global symbol record
52    symbol_pattern = re.compile(r'^[0-aA-Fa-f]+ g *F \.text.*[0-9A-Fa-f]+ +(.*)')
53
54    # Ok patterns are as follows, assuming Itanium name mangling:
55    #   spv[A-Z]          :  extern "C" symbol starting with spv
56    #   _ZN               :  something in a namespace
57    #   _Z[0-9]+spv[A-Z_] :  C++ symbol starting with spv[A-Z_]
58    symbol_ok_pattern = re.compile(r'^(spv[A-Z]|_ZN|_Z[0-9]+spv[A-Z_])')
59    seen = set()
60    result = 0
61    for line in command_output(['objdump', '-t', library], '.').split('\n'):
62        match = symbol_pattern.search(line)
63        if match:
64            symbol = match.group(1)
65            if symbol not in seen:
66                seen.add(symbol)
67                #print("look at '{}'".format(symbol))
68                if not symbol_ok_pattern.match(symbol):
69                    print('{}: error: Unescaped exported symbol: {}'.format(PROG, symbol))
70                    result = 1
71    return result
72
73
74def main():
75    import argparse
76    parser = argparse.ArgumentParser(description='Check global names exported from a library')
77    parser.add_argument('library', help='The static library to examine')
78    args = parser.parse_args()
79
80    if not os.path.isfile(args.library):
81        print('{}: error: {} does not exist'.format(PROG, args.library))
82        sys.exit(1)
83
84    if os.name is 'posix':
85        status = check_library(args.library)
86        sys.exit(status)
87    else:
88        print('Passing test since not on Posix')
89        sys.exit(0)
90
91
92if __name__ == '__main__':
93    main()
94