blob: 42adbbec010707632ed77d18f9a78e47e0e24fae [file] [edit]
#!/usr/bin/env python3
#
# Copyright (C) 2021 The Android Open Source Project
#
# 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.
"""Validates and filters kernel symbol lists."""
import argparse
import os
import re
import sys
_TRACE_POINT = '__tracepoint_'
_TRACE_ITER = '__traceiter_'
def _validate_symbols(symbol_list, symbols):
"""Validates Tracepoints consistenty in a given symbol list."""
missing = []
for symbol in symbols:
if not symbol.startswith((_TRACE_POINT, _TRACE_ITER)):
continue
if symbol.startswith(_TRACE_POINT):
other = symbol.replace(_TRACE_POINT, _TRACE_ITER)
if other not in symbols:
missing.append(other)
if symbol.startswith(_TRACE_ITER):
other = symbol.replace(_TRACE_ITER, _TRACE_POINT)
if other not in symbols:
missing.append(other)
if missing:
print(
'ERROR: Missing symbols: ',
missing,
'in ',
os.path.basename(symbol_list),
file=sys.stderr,
)
sys.exit(1)
def _read_denied_symbols_config(deny_file):
"""Reads denied symbols configuration file."""
denied_symbols = {}
with open(deny_file) as file:
for line in file:
fields = line.rstrip('\n').split(None, 1)
if not fields:
continue
symbol = fields[0]
if symbol.startswith('#'):
continue
reason = ''
if len(fields) > 1:
reason = fields[1]
if symbol in denied_symbols:
print(f"symbol '{symbol}' duplicate configuration", file=sys.stderr)
continue
denied_symbols[symbol] = reason
return denied_symbols
def _read_symbol_lists(symbol_lists):
"""Reads libabigail symbol list files as a list of lines."""
all_lines = []
for symbol_list in symbol_lists:
with open(symbol_list) as sl:
lines = sl.read().splitlines(keepends=True)
all_lines.extend(lines)
# Separate files or at least protect against missing final newlines.
all_lines.append('\n')
# validate symbols by file
_validate_symbols(symbol_list, _get_symbols(lines))
return all_lines
def _get_symbols(lines):
"""Gets symbols from symbol list lines."""
symbols = set()
for line in lines:
stripped = line.strip()
if stripped and not stripped.startswith(('#', '[')):
symbols.add(stripped)
return symbols
def main():
dir = os.path.dirname(sys.argv[0])
deny_file = os.path.join(dir, 'symbols.deny')
parser = argparse.ArgumentParser()
parser.add_argument(
'symbol_lists',
metavar='FILE',
type=str,
nargs='+',
help='a symbol list file',
)
parser.add_argument(
'--in-dir', required=True, help='where to find the symbol list files'
)
parser.add_argument(
'--out-dir',
required=True,
help='where to put the combined symbol list and report',
)
parser.add_argument(
'--out-file', required=True, help='combined symbol list file name'
)
parser.add_argument(
'--verbose', action='store_true', help='increase verbosity of the output'
)
args = parser.parse_args()
in_directory = args.in_dir
out_directory = args.out_dir
symbol_lists = [os.path.join(in_directory, s) for s in args.symbol_lists]
out_file = os.path.join(out_directory, args.out_file)
denied_symbols = _read_denied_symbols_config(deny_file)
lines = _read_symbol_lists(symbol_lists)
symbols = _get_symbols(lines)
if args.verbose:
print('========================================================')
print(f'Generating ABI symbol list definition in {out_file}')
with open(out_file, 'w') as sl:
sl.writelines(lines)
exit_status = 0
if args.verbose:
print('Checking symbols are not forbidden')
for symbol in symbols:
reason = None
if symbol in denied_symbols:
reason = denied_symbols[symbol]
elif re.fullmatch(r'_R.*(4core|5alloc|6kernel|8bindings).*', symbol):
reason = 'appears to be a Rust symbol'
if reason is not None:
print(f"symbol '{symbol}' is not allowed: {reason}", file=sys.stderr)
exit_status = 1
return exit_status
if __name__ == '__main__':
sys.exit(main())