mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-12-01 23:40:04 +00:00
8ec9f456e6
* Fix export symbol test. The symbol export test does not check weak symbols, even though they are possibly exported. It also does not allow functions in the standard namespace even though it allows symbols on other namespaces. I've modified the check to look at the name of weakly defined function, and I've allowed functions in the standard namespace, functions in a local scope, and weak definitions of new and delete operators. Fixes #4250
111 lines
4.0 KiB
Python
Executable File
111 lines
4.0 KiB
Python
Executable File
#!/usr/bin/env python
|
|
# Copyright (c) 2017 Google Inc.
|
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
"""Ensures that all externally visible functions in the library have an appropriate name
|
|
|
|
Appropriate function names are:
|
|
- names starting with spv,
|
|
- anything in a namespace,
|
|
- functions added by the protobuf compiler,
|
|
- and weak definitions of new and delete."""
|
|
|
|
import os.path
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
|
|
|
|
PROG = 'check_symbol_exports'
|
|
|
|
|
|
def command_output(cmd, directory):
|
|
"""Runs a command in a directory and returns its standard output stream.
|
|
|
|
Captures the standard error stream.
|
|
|
|
Raises a RuntimeError if the command fails to launch or otherwise fails.
|
|
"""
|
|
p = subprocess.Popen(cmd,
|
|
cwd=directory,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
universal_newlines=True)
|
|
(stdout, _) = p.communicate()
|
|
if p.returncode != 0:
|
|
raise RuntimeError('Failed to run %s in %s' % (cmd, directory))
|
|
return stdout
|
|
|
|
|
|
def check_library(library):
|
|
"""Scans the given library file for global exports. If all such
|
|
exports are namespaced or begin with spv (in either C or C++ styles)
|
|
then return 0. Otherwise emit a message and return 1."""
|
|
|
|
# The pattern for an externally visible symbol record
|
|
symbol_pattern = re.compile(r'^[0-aA-Fa-f]+ +([wg]) *F \.text.*[0-9A-Fa-f]+ +(.*)')
|
|
|
|
# Ok patterns are as follows, assuming Itanium name mangling:
|
|
# spv[A-Z] : extern "C" symbol starting with spv
|
|
# _ZN : something in a namespace
|
|
# _ZSt : something in the standard namespace
|
|
# _ZZN : something in a local scope and namespace
|
|
# _Z[0-9]+spv[A-Z_] : C++ symbol starting with spv[A-Z_]
|
|
symbol_ok_pattern = re.compile(r'^(spv[A-Z]|_ZN|_ZSt|_ZZN|_Z[0-9]+spv[A-Z_])')
|
|
|
|
# In addition, the following pattern allowlists global functions that are added
|
|
# by the protobuf compiler:
|
|
# - AddDescriptors_spvtoolsfuzz_2eproto()
|
|
# - InitDefaults_spvtoolsfuzz_2eproto()
|
|
symbol_allowlist_pattern = re.compile(r'_Z[0-9]+(InitDefaults|AddDescriptors)_spvtoolsfuzz_2eprotov')
|
|
|
|
symbol_is_new_or_delete = re.compile(r'^(_Zna|_Znw|_Zdl|_Zda)')
|
|
|
|
seen = set()
|
|
result = 0
|
|
for line in command_output(['objdump', '-t', library], '.').split('\n'):
|
|
match = symbol_pattern.search(line)
|
|
if match:
|
|
linkage = match.group(1)
|
|
symbol = match.group(2)
|
|
if symbol not in seen:
|
|
seen.add(symbol)
|
|
#print("look at '{}'".format(symbol))
|
|
if not (symbol_is_new_or_delete.match(symbol) and linkage == 'w'):
|
|
if not (symbol_allowlist_pattern.match(symbol) or symbol_ok_pattern.match(symbol)):
|
|
print('{}: error: Unescaped exported symbol: {}'.format(PROG, symbol))
|
|
result = 1
|
|
return result
|
|
|
|
|
|
def main():
|
|
import argparse
|
|
parser = argparse.ArgumentParser(description='Check global names exported from a library')
|
|
parser.add_argument('library', help='The static library to examine')
|
|
args = parser.parse_args()
|
|
|
|
if not os.path.isfile(args.library):
|
|
print('{}: error: {} does not exist'.format(PROG, args.library))
|
|
sys.exit(1)
|
|
|
|
if os.name == 'posix':
|
|
status = check_library(args.library)
|
|
sys.exit(status)
|
|
else:
|
|
print('Passing test since not on Posix')
|
|
sys.exit(0)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|