diff options
Diffstat (limited to 'chromium/buildtools/third_party/libc++/trunk/utils')
12 files changed, 708 insertions, 0 deletions
diff --git a/chromium/buildtools/third_party/libc++/trunk/utils/gen_link_script/gen_link_script.py b/chromium/buildtools/third_party/libc++/trunk/utils/gen_link_script/gen_link_script.py new file mode 100755 index 00000000000..5de18f9129c --- /dev/null +++ b/chromium/buildtools/third_party/libc++/trunk/utils/gen_link_script/gen_link_script.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +import os +import sys + +def print_and_exit(msg): + sys.stderr.write(msg + '\n') + sys.exit(1) + +def usage_and_exit(): + print_and_exit("Usage: ./gen_link_script.py [--help] [--dryrun] <path/to/libcxx.so> <abi_libname>") + +def help_and_exit(): + help_msg = \ +"""Usage + + gen_link_script.py [--help] [--dryrun] <path/to/libcxx.so> <abi_libname> + + Generate a linker script that links libc++ to the proper ABI library. + The script replaces the specified libc++ symlink. + An example script for c++abi would look like "INPUT(libc++.so.1 -lc++abi)". + +Arguments + <path/to/libcxx.so> - The top level symlink to the versioned libc++ shared + library. This file is replaced with a linker script. + <abi_libname> - The name of the ABI library to use in the linker script. + The name must be one of [c++abi, stdc++, supc++, cxxrt]. + +Exit Status: + 0 if OK, + 1 if the action failed. +""" + print_and_exit(help_msg) + +def parse_args(): + args = list(sys.argv) + del args[0] + if len(args) == 0: + usage_and_exit() + if args[0] == '--help': + help_and_exit() + dryrun = '--dryrun' == args[0] + if dryrun: + del args[0] + if len(args) != 2: + usage_and_exit() + symlink_file = args[0] + abi_libname = args[1] + return dryrun, symlink_file, abi_libname + +def main(): + dryrun, symlink_file, abi_libname = parse_args() + + # Check that the given libc++.so file is a valid symlink. + if not os.path.islink(symlink_file): + print_and_exit("symlink file %s is not a symlink" % symlink_file) + + # Read the symlink so we know what libc++ to link to in the linker script. + linked_libcxx = os.readlink(symlink_file) + + # Check that the abi_libname is one of the supported values. + supported_abi_list = ['c++abi', 'stdc++', 'supc++', 'cxxrt'] + if abi_libname not in supported_abi_list: + print_and_exit("abi name '%s' is not supported: Use one of %r" % + (abi_libname, supported_abi_list)) + + # Generate the linker script contents and print the script and destination + # information. + contents = "INPUT(%s -l%s)" % (linked_libcxx, abi_libname) + print("GENERATING SCRIPT: '%s' as file %s" % (contents, symlink_file)) + + # Remove the existing libc++ symlink and replace it with the script. + if not dryrun: + os.unlink(symlink_file) + with open(symlink_file, 'w') as f: + f.write(contents + "\n") + + +if __name__ == '__main__': + main() diff --git a/chromium/buildtools/third_party/libc++/trunk/utils/not/not.py b/chromium/buildtools/third_party/libc++/trunk/utils/not/not.py new file mode 100644 index 00000000000..96e4ea7eee1 --- /dev/null +++ b/chromium/buildtools/third_party/libc++/trunk/utils/not/not.py @@ -0,0 +1,35 @@ +"""not.py is a utility for inverting the return code of commands. +It acts similar to llvm/utils/not. +ex: python /path/to/not.py ' echo hello + echo $? // (prints 1) +""" + +import distutils.spawn +import subprocess +import sys + + +def main(): + argv = list(sys.argv) + del argv[0] + if len(argv) > 0 and argv[0] == '--crash': + del argv[0] + expectCrash = True + else: + expectCrash = False + if len(argv) == 0: + return 1 + prog = distutils.spawn.find_executable(argv[0]) + if prog is None: + sys.stderr.write('Failed to find program %s' % argv[0]) + return 1 + rc = subprocess.call(argv) + if rc < 0: + return 0 if expectCrash else 1 + if expectCrash: + return 1 + return rc == 0 + + +if __name__ == '__main__': + exit(main()) diff --git a/chromium/buildtools/third_party/libc++/trunk/utils/sym_check/linux_blacklist.txt b/chromium/buildtools/third_party/libc++/trunk/utils/sym_check/linux_blacklist.txt new file mode 100644 index 00000000000..4d9d1d4b9e3 --- /dev/null +++ b/chromium/buildtools/third_party/libc++/trunk/utils/sym_check/linux_blacklist.txt @@ -0,0 +1,19 @@ +# all guard variables +_ZGVNSt3__ +# all vtables +_ZTV +# all VTT +_ZTT +# all non-virtual thunks +_ZTh +# all virtual thunks +_ZTv +# typeinfo for std::__1::__types +# There are no std::__types +_ZTINSt3__1[0-9][0-9]*__ +# typeinfo name for std::__1::__types +_ZTSNSt3__1[0-9][0-9]*__ +# anything using __hidden_allocator +.*__hidden_allocator +# anything using __sso_allocator +.*__sso_allocator diff --git a/chromium/buildtools/third_party/libc++/trunk/utils/sym_check/osx_blacklist.txt b/chromium/buildtools/third_party/libc++/trunk/utils/sym_check/osx_blacklist.txt new file mode 100644 index 00000000000..cfa911ed74e --- /dev/null +++ b/chromium/buildtools/third_party/libc++/trunk/utils/sym_check/osx_blacklist.txt @@ -0,0 +1,19 @@ +# all guard variables +__ZGVNSt3__ +# all vtables +__ZTV +# all VTT +__ZTT +# all non-virtual thunks +__ZTh +# all virtual thunks +__ZTv +# typeinfo for std::__1::__types +# There are no std::__types +__ZTINSt3__1[0-9][0-9]*__ +# typeinfo name for std::__1::__types +__ZTSNSt3__1[0-9][0-9]*__ +# anything using __hidden_allocator +.*__hidden_allocator +# anything using __sso_allocator +.*__sso_allocator diff --git a/chromium/buildtools/third_party/libc++/trunk/utils/sym_check/sym_check/__init__.py b/chromium/buildtools/third_party/libc++/trunk/utils/sym_check/sym_check/__init__.py new file mode 100644 index 00000000000..3c668036e47 --- /dev/null +++ b/chromium/buildtools/third_party/libc++/trunk/utils/sym_check/sym_check/__init__.py @@ -0,0 +1,8 @@ +"""libcxx abi symbol checker""" + +__author__ = 'Eric Fiselier' +__email__ = 'eric@efcs.ca' +__versioninfo__ = (0, 1, 0) +__version__ = ' '.join(str(v) for v in __versioninfo__) + 'dev' + +__all__ = ['diff', 'extract', 'util'] diff --git a/chromium/buildtools/third_party/libc++/trunk/utils/sym_check/sym_check/diff.py b/chromium/buildtools/third_party/libc++/trunk/utils/sym_check/sym_check/diff.py new file mode 100644 index 00000000000..a16e54d34e0 --- /dev/null +++ b/chromium/buildtools/third_party/libc++/trunk/utils/sym_check/sym_check/diff.py @@ -0,0 +1,93 @@ +# -*- Python -*- vim: set syntax=python tabstop=4 expandtab cc=80: +""" +diff - A set of functions for diff-ing two symbol lists. +""" + +from sym_check import util + + +def _symbol_difference(lhs, rhs): + lhs_names = set((n['name'] for n in lhs)) + rhs_names = set((n['name'] for n in rhs)) + diff_names = lhs_names - rhs_names + return [n for n in lhs if n['name'] in diff_names] + + +def _find_by_key(sym_list, k): + for sym in sym_list: + if sym['name'] == k: + return sym + return None + + +def added_symbols(old, new): + return _symbol_difference(new, old) + + +def removed_symbols(old, new): + return _symbol_difference(old, new) + + +def changed_symbols(old, new): + changed = [] + for old_sym in old: + if old_sym in new: + continue + new_sym = _find_by_key(new, old_sym['name']) + if (new_sym is not None and not new_sym in old + and cmp(old_sym, new_sym) != 0): + changed += [(old_sym, new_sym)] + return changed + + +def diff(old, new): + added = added_symbols(old, new) + removed = removed_symbols(old, new) + changed = changed_symbols(old, new) + return added, removed, changed + + +def report_diff(added_syms, removed_syms, changed_syms, names_only=False, + demangle=True): + def maybe_demangle(name): + return util.demangle_symbol(name) if demangle else name + + report = '' + for sym in added_syms: + report += 'Symbol added: %s\n' % maybe_demangle(sym['name']) + if not names_only: + report += ' %s\n\n' % sym + if added_syms and names_only: + report += '\n' + for sym in removed_syms: + report += 'SYMBOL REMOVED: %s\n' % maybe_demangle(sym['name']) + if not names_only: + report += ' %s\n\n' % sym + if removed_syms and names_only: + report += '\n' + if not names_only: + for sym_pair in changed_syms: + old_sym, new_sym = sym_pair + old_str = '\n OLD SYMBOL: %s' % old_sym + new_str = '\n NEW SYMBOL: %s' % new_sym + report += ('SYMBOL CHANGED: %s%s%s\n\n' % + (maybe_demangle(old_sym['name']), + old_str, new_str)) + + added = bool(len(added_syms) != 0) + abi_break = bool(len(removed_syms)) + if not names_only: + abi_break = abi_break or len(changed_syms) + if added or abi_break: + report += 'Summary\n' + report += ' Added: %d\n' % len(added_syms) + report += ' Removed: %d\n' % len(removed_syms) + if not names_only: + report += ' Changed: %d\n' % len(changed_syms) + if not abi_break: + report += 'Symbols added.' + else: + report += 'ABI BREAKAGE: SYMBOLS ADDED OR REMOVED!' + else: + report += 'Symbols match.' + return report, int(abi_break) diff --git a/chromium/buildtools/third_party/libc++/trunk/utils/sym_check/sym_check/extract.py b/chromium/buildtools/third_party/libc++/trunk/utils/sym_check/sym_check/extract.py new file mode 100644 index 00000000000..b4f4cee2dc5 --- /dev/null +++ b/chromium/buildtools/third_party/libc++/trunk/utils/sym_check/sym_check/extract.py @@ -0,0 +1,177 @@ +# -*- Python -*- vim: set syntax=python tabstop=4 expandtab cc=80: +""" +extract - A set of function that extract symbol lists from shared libraries. +""" +import distutils.spawn +import sys + +from sym_check import util + + +class NMExtractor(object): + """ + NMExtractor - Extract symbol lists from libraries using nm. + """ + + @staticmethod + def find_tool(): + """ + Search for the nm executable and return the path. + """ + return distutils.spawn.find_executable('nm') + + def __init__(self): + """ + Initialize the nm executable and flags that will be used to extract + symbols from shared libraries. + """ + self.nm_exe = self.find_tool() + if self.nm_exe is None: + # ERROR no NM found + print("ERROR: Could not find nm") + sys.exit(1) + self.flags = ['-P', '-g'] + + def extract(self, lib): + """ + Extract symbols from a library and return the results as a dict of + parsed symbols. + """ + cmd = [self.nm_exe] + self.flags + [lib] + out, _, exit_code = util.execute_command_verbose(cmd) + if exit_code != 0: + raise RuntimeError('Failed to run %s on %s' % (self.nm_exe, lib)) + fmt_syms = (self._extract_sym(l) + for l in out.splitlines() if l.strip()) + # Cast symbol to string. + final_syms = (repr(s) for s in fmt_syms if self._want_sym(s)) + # Make unique and sort strings. + tmp_list = list(sorted(set(final_syms))) + # Cast string back to symbol. + return util.read_syms_from_list(tmp_list) + + def _extract_sym(self, sym_str): + bits = sym_str.split() + # Everything we want has at least two columns. + if len(bits) < 2: + return None + new_sym = { + 'name': bits[0], + 'type': bits[1] + } + new_sym['name'] = new_sym['name'].replace('@@', '@') + new_sym = self._transform_sym_type(new_sym) + # NM types which we want to save the size for. + if new_sym['type'] == 'OBJECT' and len(bits) > 3: + new_sym['size'] = int(bits[3], 16) + return new_sym + + @staticmethod + def _want_sym(sym): + """ + Check that s is a valid symbol that we want to keep. + """ + if sym is None or len(sym) < 2: + return False + bad_types = ['t', 'b', 'r', 'd', 'w'] + return (sym['type'] not in bad_types + and sym['name'] not in ['__bss_start', '_end', '_edata']) + + @staticmethod + def _transform_sym_type(sym): + """ + Map the nm single letter output for type to either FUNC or OBJECT. + If the type is not recognized it is left unchanged. + """ + func_types = ['T', 'W'] + obj_types = ['B', 'D', 'R', 'V', 'S'] + if sym['type'] in func_types: + sym['type'] = 'FUNC' + elif sym['type'] in obj_types: + sym['type'] = 'OBJECT' + return sym + +class ReadElfExtractor(object): + """ + ReadElfExtractor - Extract symbol lists from libraries using readelf. + """ + + @staticmethod + def find_tool(): + """ + Search for the readelf executable and return the path. + """ + return distutils.spawn.find_executable('readelf') + + def __init__(self): + """ + Initialize the readelf executable and flags that will be used to + extract symbols from shared libraries. + """ + self.tool = self.find_tool() + if self.tool is None: + # ERROR no NM found + print("ERROR: Could not find readelf") + sys.exit(1) + self.flags = ['--wide', '--symbols'] + + def extract(self, lib): + """ + Extract symbols from a library and return the results as a dict of + parsed symbols. + """ + cmd = [self.tool] + self.flags + [lib] + out, _, exit_code = util.execute_command_verbose(cmd) + if exit_code != 0: + raise RuntimeError('Failed to run %s on %s' % (self.nm_exe, lib)) + dyn_syms = self.get_dynsym_table(out) + return self.process_syms(dyn_syms) + + def process_syms(self, sym_list): + new_syms = [] + for s in sym_list: + parts = s.split() + if not parts: + continue + assert len(parts) == 7 or len(parts) == 8 or len(parts) == 9 + if len(parts) == 7: + continue + new_sym = { + 'name': parts[7], + 'size': int(parts[2]), + 'type': parts[3], + } + assert new_sym['type'] in ['OBJECT', 'FUNC', 'NOTYPE'] + if new_sym['type'] == 'NOTYPE': + continue + if new_sym['type'] == 'FUNC': + del new_sym['size'] + new_syms += [new_sym] + return new_syms + + def get_dynsym_table(self, out): + lines = out.splitlines() + start = -1 + end = -1 + for i in range(len(lines)): + if lines[i].startswith("Symbol table '.dynsym'"): + start = i + 2 + if start != -1 and end == -1 and not lines[i].strip(): + end = i + 1 + assert start != -1 + if end == -1: + end = len(lines) + return lines[start:end] + + +def extract_symbols(lib_file): + """ + 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. + """ + if ReadElfExtractor.find_tool(): + extractor = ReadElfExtractor() + else: + extractor = NMExtractor() + return extractor.extract(lib_file) diff --git a/chromium/buildtools/third_party/libc++/trunk/utils/sym_check/sym_check/match.py b/chromium/buildtools/third_party/libc++/trunk/utils/sym_check/sym_check/match.py new file mode 100644 index 00000000000..9f496427e37 --- /dev/null +++ b/chromium/buildtools/third_party/libc++/trunk/utils/sym_check/sym_check/match.py @@ -0,0 +1,32 @@ +# -*- Python -*- vim: set syntax=python tabstop=4 expandtab cc=80: +""" +match - A set of functions for matching symbols in a list to a list of regexs +""" + +import re + + +def find_and_report_matching(symbol_list, regex_list): + report = '' + found_count = 0 + for regex_str in regex_list: + report += 'Matching regex "%s":\n' % regex_str + matching_list = find_matching_symbols(symbol_list, regex_str) + if not matching_list: + report += ' No matches found\n\n' + continue + # else + found_count += len(matching_list) + for m in matching_list: + report += ' MATCHES: %s\n' % m['name'] + report += '\n' + return found_count, report + + +def find_matching_symbols(symbol_list, regex_str): + regex = re.compile(regex_str) + matching_list = [] + for s in symbol_list: + if regex.match(s['name']): + matching_list += [s] + return matching_list diff --git a/chromium/buildtools/third_party/libc++/trunk/utils/sym_check/sym_check/util.py b/chromium/buildtools/third_party/libc++/trunk/utils/sym_check/sym_check/util.py new file mode 100644 index 00000000000..1d3b424f6f3 --- /dev/null +++ b/chromium/buildtools/third_party/libc++/trunk/utils/sym_check/sym_check/util.py @@ -0,0 +1,128 @@ +import ast +import distutils.spawn +import signal +import subprocess +import sys + + +def execute_command(cmd, input_str=None): + """ + Execute a command, capture and return its output. + """ + kwargs = { + 'stdin': subprocess.PIPE, + 'stdout': subprocess.PIPE, + 'stderr': subprocess.PIPE, + } + p = subprocess.Popen(cmd, **kwargs) + out, err = p.communicate(input=input_str) + exitCode = p.wait() + if exitCode == -signal.SIGINT: + raise KeyboardInterrupt + return out, err, exitCode + + +def execute_command_verbose(cmd, input_str=None): + """ + Execute a command and print its output on failure. + """ + out, err, exitCode = execute_command(cmd, input_str=input_str) + if exitCode != 0: + report = "Command: %s\n" % ' '.join(["'%s'" % a for a in cmd]) + report += "Exit Code: %d\n" % exitCode + if out: + report += "Standard Output:\n--\n%s--" % out + if err: + report += "Standard Error:\n--\n%s--" % err + report += "\n\nFailed!" + sys.stderr.write('%s\n' % report) + return out, err, exitCode + + +def read_syms_from_list(slist): + """ + Read a list of symbols from a list of strings. + Each string is one symbol. + """ + return [ast.literal_eval(l) for l in slist] + + +def read_syms_from_file(filename): + """ + Read a list of symbols in from a file. + """ + with open(filename, 'r') as f: + data = f.read() + return read_syms_from_list(data.splitlines()) + + +def read_blacklist(filename): + with open(filename, 'r') as f: + data = f.read() + lines = [l.strip() for l in data.splitlines() if l.strip()] + lines = [l for l in lines if not l.startswith('#')] + return lines + + +def write_syms(sym_list, out=None, names_only=False): + """ + Write a list of symbols to the file named by out. + """ + out_str = '' + out_list = sym_list + if names_only: + out_list = [sym['name'] for sym in sym_list] + out_list.sort() + for sym in out_list: + out_str += '%s\n' % sym + if out is None: + sys.stdout.write(out_str) + else: + with open(out, 'w') as f: + f.write(out_str) + + +_cppfilt_exe = distutils.spawn.find_executable('c++filt') + + +def demangle_symbol(symbol): + if _cppfilt_exe is None: + return symbol + out, _, exit_code = execute_command_verbose( + [_cppfilt_exe], input_str=symbol) + if exit_code != 0: + return symbol + return out + + +def is_elf(filename): + with open(filename, 'r') as f: + magic_bytes = f.read(4) + return magic_bytes == '\x7fELF' + + +def is_mach_o(filename): + with open(filename, 'r') as f: + magic_bytes = f.read(4) + return magic_bytes in [ + '\xfe\xed\xfa\xce', # MH_MAGIC + '\xce\xfa\xed\xfe', # MH_CIGAM + '\xfe\xed\xfa\xcf', # MH_MAGIC_64 + '\xcf\xfa\xed\xfe', # MH_CIGAM_64 + '\xca\xfe\xba\xbe', # FAT_MAGIC + '\xbe\xba\xfe\xca' # FAT_CIGAM + ] + + +def is_library_file(filename): + if sys.platform == 'darwin': + return is_mach_o(filename) + else: + return is_elf(filename) + + +def extract_or_load(filename): + import sym_check.extract + if is_library_file(filename): + return sym_check.extract.extract_symbols(filename) + return read_syms_from_file(filename) diff --git a/chromium/buildtools/third_party/libc++/trunk/utils/sym_check/sym_diff.py b/chromium/buildtools/third_party/libc++/trunk/utils/sym_check/sym_diff.py new file mode 100755 index 00000000000..054c6c18e3e --- /dev/null +++ b/chromium/buildtools/third_party/libc++/trunk/utils/sym_check/sym_diff.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python +""" +sym_diff - Compare two symbol lists and output the differences. +""" +from argparse import ArgumentParser +import sys +from sym_check import diff, util + + +def main(): + parser = ArgumentParser( + description='Extract a list of symbols from a shared library.') + parser.add_argument( + '--names-only', dest='names_only', + help='Only print symbol names', + action='store_true', default=False) + parser.add_argument( + '-o', '--output', dest='output', + help='The output file. stdout is used if not given', + type=str, action='store', default=None) + parser.add_argument( + '--demangle', dest='demangle', action='store_true', default=False) + parser.add_argument( + 'old_syms', metavar='old-syms', type=str, + help='The file containing the old symbol list or a library') + parser.add_argument( + 'new_syms', metavar='new-syms', type=str, + help='The file containing the new symbol list or a library') + args = parser.parse_args() + + old_syms_list = util.extract_or_load(args.old_syms) + new_syms_list = util.extract_or_load(args.new_syms) + + added, removed, changed = diff.diff(old_syms_list, new_syms_list) + report, is_break = diff.report_diff(added, removed, changed, + names_only=args.names_only, + demangle=args.demangle) + if args.output is None: + print(report) + else: + with open(args.output, 'w') as f: + f.write(report + '\n') + sys.exit(is_break) + + +if __name__ == '__main__': + main() diff --git a/chromium/buildtools/third_party/libc++/trunk/utils/sym_check/sym_extract.py b/chromium/buildtools/third_party/libc++/trunk/utils/sym_check/sym_extract.py new file mode 100755 index 00000000000..5d8953986b2 --- /dev/null +++ b/chromium/buildtools/third_party/libc++/trunk/utils/sym_check/sym_extract.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python +""" +sym_extract - Extract and output a list of symbols from a shared library. +""" +from argparse import ArgumentParser +from sym_check import extract, util + + +def main(): + parser = ArgumentParser( + description='Extract a list of symbols from a shared library.') + parser.add_argument('library', metavar='shared-lib', type=str, + help='The library to extract symbols from') + parser.add_argument('-o', '--output', dest='output', + help='The output file. stdout is used if not given', + type=str, action='store', default=None) + parser.add_argument('--names-only', dest='names_only', + help='Output only the name of the symbol', + action='store_true', default=False) + args = parser.parse_args() + if args.output is not None: + print('Extracting symbols from %s to %s.' + % (args.library, args.output)) + syms = extract.extract_symbols(args.library) + util.write_syms(syms, out=args.output, names_only=args.names_only) + + +if __name__ == '__main__': + main() diff --git a/chromium/buildtools/third_party/libc++/trunk/utils/sym_check/sym_match.py b/chromium/buildtools/third_party/libc++/trunk/utils/sym_check/sym_match.py new file mode 100755 index 00000000000..c60b2462935 --- /dev/null +++ b/chromium/buildtools/third_party/libc++/trunk/utils/sym_check/sym_match.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python +""" +sym_match - Match all symbols in a list against a list of regexes. +""" +from argparse import ArgumentParser +import sys +from sym_check import util, match, extract + + +def main(): + parser = ArgumentParser( + description='Extract a list of symbols from a shared library.') + parser.add_argument( + '--blacklist', dest='blacklist', + type=str, action='store', default=None) + parser.add_argument( + 'symbol_list', metavar='symbol_list', type=str, + help='The file containing the old symbol list') + parser.add_argument( + 'regexes', metavar='regexes', default=[], nargs='*', + help='The file containing the new symbol list or a library') + args = parser.parse_args() + + if not args.regexes and args.blacklist is None: + sys.stderr.write('Either a regex or a blacklist must be specified.\n') + sys.exit(1) + if args.blacklist: + search_list = util.read_blacklist(args.blacklist) + else: + search_list = args.regexes + + symbol_list = util.extract_or_load(args.symbol_list) + + matching_count, report = match.find_and_report_matching( + symbol_list, search_list) + sys.stdout.write(report) + if matching_count != 0: + print('%d matching symbols found...' % matching_count) + + +if __name__ == '__main__': + main() |