Browse Source

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
Eric Fiselier 6 years ago
parent
commit
199f01c55f
3 changed files with 40 additions and 17 deletions
  1. 18 10
      utils/libcxx/sym_check/extract.py
  2. 8 6
      utils/libcxx/sym_check/util.py
  3. 14 1
      utils/sym_extract.py

+ 18 - 10
utils/libcxx/sym_check/extract.py

@@ -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.
-    The symbols are extracted using NM. They are then filtered and formated.
-    Finally they symbols are made unique.
+    Extract and return a list of symbols extracted from a static or dynamic
+    library. The symbols are extracted using NM or readelf. They are then
+    filtered and formated. Finally they symbols are made unique.
     """
     """
-    if ReadElfExtractor.find_tool():
-        extractor = ReadElfExtractor()
+    if static_lib is None:
+        _, 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)

+ 8 - 6
utils/libcxx/sym_check/util.py

@@ -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):
-            assert not s['is_defined'] and "found defined non-std symbol"
+        if not is_stdlib_symbol_name(canon_name, s):
             other_symbols += [s]
             other_symbols += [s]
         else:
         else:
             stdlib_symbols += [s]
             stdlib_symbols += [s]

+ 14 - 1
utils/sym_extract.py

@@ -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__':