Make the sym_diff utilities more useful.

In particular when working with static libraries and libstdc++.

git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@353772 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Eric Fiselier
2019-02-12 00:00:43 +00:00
parent ecc2c089fd
commit 199f01c55f
3 changed files with 40 additions and 17 deletions

View File

@@ -10,6 +10,7 @@
extract - A set of function that extract symbol lists from shared libraries. extract - A set of function that extract symbol lists from shared libraries.
""" """
import distutils.spawn import distutils.spawn
import os.path
import sys import sys
import re import re
@@ -30,7 +31,7 @@ class NMExtractor(object):
""" """
return distutils.spawn.find_executable('nm') return distutils.spawn.find_executable('nm')
def __init__(self): def __init__(self, static_lib):
""" """
Initialize the nm executable and flags that will be used to extract Initialize the nm executable and flags that will be used to extract
symbols from shared libraries. symbols from shared libraries.
@@ -40,8 +41,10 @@ class NMExtractor(object):
# ERROR no NM found # ERROR no NM found
print("ERROR: Could not find nm") print("ERROR: Could not find nm")
sys.exit(1) sys.exit(1)
self.static_lib = static_lib
self.flags = ['-P', '-g'] self.flags = ['-P', '-g']
def extract(self, lib): def extract(self, lib):
""" """
Extract symbols from a library and return the results as a dict of Extract symbols from a library and return the results as a dict of
@@ -53,7 +56,7 @@ class NMExtractor(object):
raise RuntimeError('Failed to run %s on %s' % (self.nm_exe, lib)) raise RuntimeError('Failed to run %s on %s' % (self.nm_exe, lib))
fmt_syms = (self._extract_sym(l) fmt_syms = (self._extract_sym(l)
for l in out.splitlines() if l.strip()) for l in out.splitlines() if l.strip())
# Cast symbol to string. # Cast symbol to string.
final_syms = (repr(s) for s in fmt_syms if self._want_sym(s)) final_syms = (repr(s) for s in fmt_syms if self._want_sym(s))
# Make unique and sort strings. # Make unique and sort strings.
tmp_list = list(sorted(set(final_syms))) tmp_list = list(sorted(set(final_syms)))
@@ -116,7 +119,7 @@ class ReadElfExtractor(object):
""" """
return distutils.spawn.find_executable('readelf') return distutils.spawn.find_executable('readelf')
def __init__(self): def __init__(self, static_lib):
""" """
Initialize the readelf executable and flags that will be used to Initialize the readelf executable and flags that will be used to
extract symbols from shared libraries. extract symbols from shared libraries.
@@ -126,6 +129,8 @@ class ReadElfExtractor(object):
# ERROR no NM found # ERROR no NM found
print("ERROR: Could not find readelf") print("ERROR: Could not find readelf")
sys.exit(1) sys.exit(1)
# TODO: Support readelf for reading symbols from archives
assert not static_lib and "RealElf does not yet support static libs"
self.flags = ['--wide', '--symbols'] self.flags = ['--wide', '--symbols']
def extract(self, lib): def extract(self, lib):
@@ -180,14 +185,17 @@ class ReadElfExtractor(object):
return lines[start:end] return lines[start:end]
def extract_symbols(lib_file): def extract_symbols(lib_file, static_lib=None):
""" """
Extract and return a list of symbols extracted from a dynamic library. Extract and return a list of symbols extracted from a static or dynamic
The symbols are extracted using NM. They are then filtered and formated. library. The symbols are extracted using NM or readelf. They are then
Finally they symbols are made unique. filtered and formated. Finally they symbols are made unique.
""" """
if ReadElfExtractor.find_tool(): if static_lib is None:
extractor = ReadElfExtractor() _, ext = os.path.splitext(lib_file)
static_lib = True if ext in ['.a'] else False
if ReadElfExtractor.find_tool() and not static_lib:
extractor = ReadElfExtractor(static_lib=static_lib)
else: else:
extractor = NMExtractor() extractor = NMExtractor(static_lib=static_lib)
return extractor.extract(lib_file) return extractor.extract(lib_file)

View File

@@ -39,15 +39,17 @@ def read_blacklist(filename):
return lines return lines
def write_syms(sym_list, out=None, names_only=False): def write_syms(sym_list, out=None, names_only=False, filter=None):
""" """
Write a list of symbols to the file named by out. Write a list of symbols to the file named by out.
""" """
out_str = '' out_str = ''
out_list = sym_list out_list = sym_list
out_list.sort(key=lambda x: x['name']) out_list.sort(key=lambda x: x['name'])
if filter is not None:
out_list = filter(out_list)
if names_only: if names_only:
out_list = [sym['name'] for sym in sym_list] out_list = [sym['name'] for sym in out_list]
for sym in out_list: for sym in out_list:
# Use pformat for consistent ordering of keys. # Use pformat for consistent ordering of keys.
out_str += pformat(sym, width=100000) + '\n' out_str += pformat(sym, width=100000) + '\n'
@@ -242,10 +244,11 @@ cxxabi_symbols = [
'_ZTSy' '_ZTSy'
] ]
def is_stdlib_symbol_name(name): def is_stdlib_symbol_name(name, sym):
name = adjust_mangled_name(name) name = adjust_mangled_name(name)
if re.search("@GLIBC|@GCC", name): if re.search("@GLIBC|@GCC", name):
return False # Only when symbol is defined do we consider it ours
return sym['is_defined']
if re.search('(St[0-9])|(__cxa)|(__cxxabi)', name): if re.search('(St[0-9])|(__cxa)|(__cxxabi)', name):
return True return True
if name in new_delete_std_symbols: if name in new_delete_std_symbols:
@@ -261,8 +264,7 @@ def filter_stdlib_symbols(syms):
other_symbols = [] other_symbols = []
for s in syms: for s in syms:
canon_name = adjust_mangled_name(s['name']) canon_name = adjust_mangled_name(s['name'])
if not is_stdlib_symbol_name(canon_name): if not is_stdlib_symbol_name(canon_name, s):
assert not s['is_defined'] and "found defined non-std symbol"
other_symbols += [s] other_symbols += [s]
else: else:
stdlib_symbols += [s] stdlib_symbols += [s]

View File

@@ -27,14 +27,27 @@ def main():
parser.add_argument('--only-stdlib-symbols', dest='only_stdlib', parser.add_argument('--only-stdlib-symbols', dest='only_stdlib',
help="Filter all symbols not related to the stdlib", help="Filter all symbols not related to the stdlib",
action='store_true', default=False) action='store_true', default=False)
parser.add_argument('--defined-only', dest='defined_only',
help="Filter all symbols that are not defined",
action='store_true', default=False)
parser.add_argument('--undefined-only', dest='undefined_only',
help="Filter all symbols that are defined",
action='store_true', default=False)
args = parser.parse_args() args = parser.parse_args()
assert not (args.undefined_only and args.defined_only)
if args.output is not None: if args.output is not None:
print('Extracting symbols from %s to %s.' print('Extracting symbols from %s to %s.'
% (args.library, args.output)) % (args.library, args.output))
syms = extract.extract_symbols(args.library) syms = extract.extract_symbols(args.library)
if args.only_stdlib: if args.only_stdlib:
syms, other_syms = util.filter_stdlib_symbols(syms) syms, other_syms = util.filter_stdlib_symbols(syms)
util.write_syms(syms, out=args.output, names_only=args.names_only) filter = lambda x: x
if args.defined_only:
filter = lambda l: list([x for x in l if x['is_defined']])
if args.undefined_only:
filter = lambda l: list([x for x in l if not x['is_defined']])
util.write_syms(syms, out=args.output, names_only=args.names_only, filter=filter)
if __name__ == '__main__': if __name__ == '__main__':