3 # Copyright (c) 2010 Gleb Kurtsou
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
9 # 1. Redistributions of source code must retain the above copyright
10 # notice, this list of conditions and the following disclaimer.
11 # 2. Redistributions in binary form must reproduce the above copyright
12 # notice, this list of conditions and the following disclaimer in the
13 # documentation and/or other materials provided with the distribution.
15 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
45 dwarfdump = 'dwarfdump'
47 cmpcache_enabled = True
48 dwarfcache_enabled = True
53 class FileConfig(object):
56 def init(self, outname):
57 if outname and outname != '-':
58 self.out = open(outname, "w")
60 origfile = FileConfig()
61 newfile = FileConfig()
65 cls.version_filter = StrFilter()
66 cls.symbol_filter = StrFilter()
73 print >> sys.stderr, "WARN: " + msg
77 class StrFilter(object):
83 self.re_exclude = [ re.compile(x) for x in self.exclude ]
84 self.re_include = [ re.compile(x) for x in self.include ]
87 if len(self.re_include):
89 for r in self.re_include:
95 for r in self.re_exclude:
102 class CacheStats(object):
107 def show(self, name):
108 total = self.hit + self.miss
112 ratio = '%f' % (self.hit/float(total))
113 return '%s cache stats: hit: %d; miss: %d; ratio: %s' % \
114 (name, self.hit, self.miss, ratio)
116 def __init__(self, enabled=True, stats=None):
117 self.enabled = enabled
120 self.stats = Cache.CacheStats()
125 if self.enabled and self.items.has_key(id):
127 return self.items[id]
132 def put(self, id, obj):
134 if self.items.has_key(id) and obj is not self.items[id]:
135 #raise ValueError("Item is already cached: %d (%s, %s)" %
136 # (id, self.items[id], obj))
137 warn(Config.w_cached, "Item is already cached: %d (%s, %s)" % \
138 (id, self.items[id], obj))
141 def replace(self, id, obj):
143 assert self.items.has_key(id)
146 class ListDiff(object):
147 def __init__(self, orig, new):
148 self.orig = set(orig)
150 self.common = self.orig & self.new
151 self.added = self.new - self.common
152 self.removed = self.orig - self.common
154 class PrettyPrinter(object):
158 def run_nested(self, obj):
159 ex = obj._pp_ex(self)
160 self.stack.append(ex)
163 self._result = obj._pp(self)
167 return sorted(set(self.stack))
174 #{{{ symbols and version maps
176 class Symbol(object):
177 def __init__(self, name, offset, version, lib):
180 self.version = version
182 self.definition = None
186 return self.name + '@' + self.version
189 return "Symbol(%s, 0x%x, %s)" % (self.name, self.offset, self.version)
191 class CommonSymbol(object):
192 def __init__(self, origsym, newsym):
193 if origsym.name != newsym.name or origsym.version != newsym.version:
194 raise RuntimeError("Symbols have different names: %s",
196 self.origsym = origsym
198 self.name = newsym.name
199 self.version = newsym.version
202 return "CommonSymbol(%s, %s)" % (self.name, self.version)
204 class SymbolAlias(object):
205 def __init__(self, alias, prefix, offset):
206 assert alias.startswith(prefix)
208 self.name = alias[len(prefix):]
212 return "SymbolAlias(%s, 0x%x)" % (self.alias, self.offset)
215 class VersionMap(object):
216 def __init__(self, name):
220 def append(self, symbol):
221 if (self.symbols.has_key(symbol.name)):
222 raise ValueError("Symbol is already defined %s@%s" %
223 (symbol.name, self.name))
224 self.symbols[symbol.name] = symbol
227 return self.symbols.keys()
230 return repr(self.symbols.values())
234 # {{{ types and definitions
239 def __init__(self, id, name, **kwargs):
244 def __getattr__(self, attr):
245 if not self.attrs.has_key(attr):
246 raise AttributeError('%s in %s' % (attr, str(self)))
247 return self.attrs[attr]
249 def _name_opt(self, default=''):
256 return self.type._alias()
259 def __cmp__(self, other):
260 # TODO assert 'self' and 'other' belong to different libraries
261 #print 'cmp defs: %s, %s' % (self, other)
265 except AttributeError:
267 r = cmp(a.__class__, b.__class__)
269 if a.id != 0 and b.id != 0:
270 ind = (long(a.id) << 32) + b.id
271 r = Dwarf.cmpcache.get(ind)
276 r = cmp(a.attrs, b.attrs)
278 Dwarf.cmpcache.put(ind, r)
281 #raise RuntimeError('Comparing different classes: %s, %s' %
282 # (a.__class__.__name__, b.__class__.__name__))
287 if hasattr(self, 'name'):
288 p.append("name=%s" % self.name)
289 for (k, v) in self.attrs.items():
290 if isinstance(v, Def):
291 v = v.__class__.__name__ + '(...)'
292 p.append("%s=%s" % (k, v))
293 return self.__class__.__name__ + '(' + ', '.join(p) + ')'
295 def _mapval(self, param, vals):
296 if param not in vals.keys():
297 raise NotImplementedError("Invalid value '%s': %s" %
301 def _pp_ex(self, pp):
302 raise NotImplementedError('Extended pretty print not implemeted: %s' %
306 raise NotImplementedError('Pretty print not implemeted: %s' % str(self))
308 class AnonymousDef(Def):
309 def __init__(self, id, **kwargs):
310 Def.__init__(self, id, None, **kwargs)
312 class Void(AnonymousDef):
315 def __new__(cls, *args, **kwargs):
316 if not cls._instance:
317 cls._instance = super(Void, cls).__new__(
318 cls, *args, **kwargs)
322 AnonymousDef.__init__(self, 0)
327 class VarArgs(AnonymousDef):
331 class PointerDef(AnonymousDef):
333 t = pp.run(self.type)
336 class BaseTypeDef(Def):
337 inttypes = ['DW_ATE_signed', 'DW_ATE_unsigned', 'DW_ATE_unsigned_char']
339 if self.encoding in self.inttypes:
340 sign = '' if self.encoding == 'DW_ATE_signed' else 'u'
341 bits = int(self.byte_size) * 8
342 return '%sint%s_t' % (sign, bits)
343 elif self.encoding == 'DW_ATE_signed_char' and int(self.byte_size) == 1:
345 elif self.encoding == 'DW_ATE_float':
346 return self._mapval(self.byte_size, {
351 raise NotImplementedError('Invalid encoding: %s' % self)
353 class TypeAliasDef(Def):
356 alias = self._alias()
358 if self.name and not alias.name:
359 alias.name = 'T(%s)' % self.name
360 # return type with modifiers
361 return self.type._pp(pp)
363 class EnumerationTypeDef(Def):
365 return 'enum ' + self._name_opt('UNKNOWN')
367 class ConstTypeDef(AnonymousDef):
370 return 'const ' + self.type._pp(pp)
372 class VolatileTypeDef(AnonymousDef):
375 return 'volatile ' + self.type._pp(pp)
377 class ArrayDef(AnonymousDef):
379 t = pp.run(self.type)
380 assert len(self.subranges) == 1
382 sz = int(self.subranges[0].upper_bound) + 1
384 s = re.sub(r'\(.+\)', '', self.subranges[0].upper_bound)
386 return '%s[%s]' % (t, sz)
388 class ArraySubrangeDef(AnonymousDef):
391 class FunctionDef(Def):
393 result = pp.run(self.result)
397 params = ', '.join([ pp.run(x) for x in self.params ])
398 return "%s %s(%s);" % (result, self.name, params)
400 class FunctionTypeDef(Def):
402 result = pp.run(self.result)
406 params = ', '.join([ pp.run(x) for x in self.params ])
407 return "F(%s, %s, (%s))" % (self._name_opt(), result, params)
409 class ParameterDef(Def):
411 t = pp.run(self.type)
412 return "%s %s" % (t, self._name_opt())
415 class StructForwardDef(Def):
418 class IncompleteDef(Def):
419 def update(self, complete, cache=None):
420 self.complete = complete
421 complete.incomplete = self
423 cached = cache.get(self.id)
424 if cached != None and isinstance(cached, IncompleteDef):
425 cache.replace(self.id, complete)
427 class StructIncompleteDef(IncompleteDef):
429 return "struct %s" % (self.name,)
431 class UnionIncompleteDef(IncompleteDef):
433 return "union %s" % (self.name,)
435 class StructDef(Def):
436 def _pp_ex(self, pp, suffix=';'):
437 members = [ pp.run(x) for x in self.members ]
438 return "struct %s { %s }%s" % \
439 (self._name_opt(), ' '.join(members), suffix)
443 return "struct %s" % (self.name,)
445 return self._pp_ex(pp, suffix='')
448 def _pp_ex(self, pp, suffix=';'):
449 members = [ pp.run(x) for x in self.members ]
450 return "union %s { %s }%s" % \
451 (self._name_opt(), ' '.join(members), suffix)
455 return "union %s" % (self.name,)
457 return self._pp_ex(pp, suffix='')
459 class MemberDef(Def):
461 t = pp.run(self.type)
463 bits = ":%s" % self.bit_size
466 return "%s %s%s;" % (t, self._name_opt(), bits)
470 cmpcache = Cache(enabled=Config.cmpcache_enabled)
472 def __init__(self, dump):
475 def _build_optarg_type(self, praw):
476 type = praw.optarg('type', Void())
478 type = self.buildref(praw.unit, type)
481 def build_subprogram(self, raw):
482 if raw.optname == None:
483 raw.setname('SUBPROGRAM_NONAME_' + raw.arg('low_pc'));
484 params = [ self.build(x) for x in raw.nested ]
485 result = self._build_optarg_type(raw)
486 return FunctionDef(raw.id, raw.name, params=params, result=result)
488 def build_subroutine_type(self, raw):
489 params = [ self.build(x) for x in raw.nested ]
490 result = self._build_optarg_type(raw)
491 return FunctionTypeDef(raw.id, raw.optname, params=params, result=result)
493 def build_formal_parameter(self, raw):
494 type = self._build_optarg_type(raw)
495 return ParameterDef(raw.id, raw.optname, type=type)
497 def build_pointer_type(self, raw):
498 type = self._build_optarg_type(raw)
499 return PointerDef(raw.id, type=type)
501 def build_member(self, raw):
502 type = self.buildref(raw.unit, raw.arg('type'))
503 return MemberDef(raw.id, raw.name, type=type,
504 bit_size=raw.optarg('bit_size', None))
506 def build_structure_type(self, raw):
507 incomplete = raw.unit.incomplete.get(raw.id)
508 if incomplete == None:
509 incomplete = StructIncompleteDef(raw.id, raw.optname)
510 raw.unit.incomplete.put(raw.id, incomplete)
513 members = [ self.build(x) for x in raw.nested ]
514 byte_size = raw.optarg('byte_size', None)
515 if byte_size == None:
516 obj = StructForwardDef(raw.id, raw.name, members=members,
518 obj = StructDef(raw.id, raw.optname, members=members,
520 incomplete.update(obj, cache=raw.unit.cache)
523 def build_union_type(self, raw):
524 incomplete = raw.unit.incomplete.get(raw.id)
525 if incomplete == None:
526 incomplete = UnionIncompleteDef(raw.id, raw.optname)
527 raw.unit.incomplete.put(raw.id, incomplete)
530 members = [ self.build(x) for x in raw.nested ]
531 byte_size = raw.optarg('byte_size', None)
532 obj = UnionDef(raw.id, raw.optname, members=members,
534 obj.incomplete = incomplete
535 incomplete.complete = obj
538 def build_typedef(self, raw):
539 type = self._build_optarg_type(raw)
540 return TypeAliasDef(raw.id, raw.name, type=type)
542 def build_const_type(self, raw):
543 type = self._build_optarg_type(raw)
544 return ConstTypeDef(raw.id, type=type)
546 def build_volatile_type(self, raw):
547 type = self._build_optarg_type(raw)
548 return VolatileTypeDef(raw.id, type=type)
550 def build_enumeration_type(self, raw):
551 # TODO handle DW_TAG_enumerator ???
552 return EnumerationTypeDef(raw.id, name=raw.optname,
553 byte_size=raw.arg('byte_size'))
555 def build_base_type(self, raw):
556 return BaseTypeDef(raw.id, raw.optname,
557 byte_size=raw.arg('byte_size'), encoding=raw.arg('encoding'))
559 def build_array_type(self, raw):
560 type = self.buildref(raw.unit, raw.arg('type'))
561 subranges = [ self.build(x) for x in raw.nested ]
562 return ArrayDef(raw.id, type=type, subranges=subranges)
564 def build_subrange_type(self, raw):
565 type = self.buildref(raw.unit, raw.arg('type'))
566 return ArraySubrangeDef(raw.id, type=type,
567 upper_bound=raw.optarg('upper_bound', 0))
569 def build_unspecified_parameters(self, raw):
570 return VarArgs(raw.id)
572 def _get_id(self, id):
576 if (id.startswith('<') and id.endswith('>')):
579 raise ValueError("Invalid dwarf id: %s" % id)
581 def build(self, raw):
582 obj = raw.unit.cache.get(raw.id)
585 builder_name = raw.tag.replace('DW_TAG_', 'build_')
587 builder = getattr(self, builder_name)
588 except AttributeError:
589 raise AttributeError("Unknown dwarf tag: %s" % raw)
591 raw.unit.cache.put(obj.id, obj)
594 def buildref(self, unit, id):
595 id = self._get_id(id)
597 obj = self.build(raw)
603 def __init__(self, libfile):
604 self.libfile = libfile
608 def parse_objdump(self):
609 objdump = ObjdumpParser(self.libfile)
611 for p in objdump.dynamic_symbols:
613 if vername.startswith('(') and vername.endswith(')'):
614 vername = vername[1:-1]
615 if not Config.version_filter.match(vername):
617 if not Config.symbol_filter.match(p['symbol']):
619 sym = Symbol(p['symbol'], p['offset'], vername, self)
620 if not self.versions.has_key(vername):
621 self.versions[vername] = VersionMap(vername)
622 self.versions[vername].append(sym)
623 if Config.alias_prefixes:
624 self.local_offsetmap = objdump.local_offsetmap
625 for p in objdump.local_symbols:
626 for prefix in Config.alias_prefixes:
627 if not p['symbol'].startswith(prefix):
629 alias = SymbolAlias(p['symbol'], prefix, p['offset'])
630 if self.alias_syms.has_key(alias.name):
631 prevalias = self.alias_syms[alias.name]
632 if alias.name != prevalias.name or \
633 alias.offset != prevalias.offset:
634 warn(Config.w_alias, "Symbol alias is " \
635 "already defined: %s: %s at %08x -- %s at %08x" % \
636 (alias.alias, alias.name, alias.offset,
637 prevalias.name, prevalias.offset))
638 self.alias_syms[alias.name] = alias
640 def parse_dwarfdump(self):
641 dwarfdump = DwarfdumpParser(self.libfile)
645 raw = dwarfdump.offsetmap[sym.offset]
648 localnames = self.local_offsetmap[sym.offset]
649 localnames.sort(key=lambda x: -len(x))
650 for localname in localnames:
651 if not self.alias_syms.has_key(localname):
653 alias = self.alias_syms[localname]
654 raw = dwarfdump.offsetmap[alias.offset]
660 dwarf = Dwarf(dwarfdump)
661 for ver in self.versions.values():
662 for sym in ver.symbols.values():
665 warn(Config.w_symbol, "Symbol %s (%s) not found at offset 0x%x" % \
666 (sym.name_ver, self.libfile, sym.offset))
668 if Config.verbose >= 3:
669 print "Parsing symbol %s (%s)" % (sym.name_ver, self.libfile)
670 sym.definition = dwarf.build(raw)
673 if not os.path.isfile(self.libfile):
674 print >> sys.stderr, ("No such file: %s" % self.libfile)
677 self.parse_dwarfdump()
681 class Parser(object):
682 def __init__(self, proc):
684 self.parser = self.parse_begin
687 fd = os.popen(self.proc, 'r')
697 print >> sys.stderr, ("Execution failed: %s" % self.proc)
700 def parse_begin(self, line):
703 class ObjdumpParser(Parser):
705 re_header = re.compile('(?P<table>\w*)\s*SYMBOL TABLE:')
707 re_local_symbol = re.compile('(?P<offset>[0-9a-fA-F]+)\s+(?P<bind>\w+)\s+(?P<type>\w+)\s+(?P<section>[^\s]+)\s+(?P<foffset>[0-9a-fA-F]+)\s*(?P<symbol>[^\s]*)')
708 re_lame_symbol = re.compile('(?P<offset>[0-9a-fA-F]+)\s+(?P<bind>\w+)\s+\*[A-Z]+\*')
710 re_dynamic_symbol = re.compile('(?P<offset>[0-9a-fA-F]+)\s+(?P<bind>\w+)\s+(?P<type>\w+)\s+(?P<section>[^\s]+)\s+(?P<foffset>[0-9a-fA-F]+)\s*(?P<ver>[^\s]*)\s*(?P<symbol>[^\s]*)')
712 def __init__(self, libfile):
713 Parser.__init__(self, "%s -wtT %s" % (Config.objdump, libfile))
714 self.dynamic_symbols = []
715 self.local_symbols = []
716 self.local_offsetmap = {}
718 def parse_begin(self, line):
719 self.parse_header(line)
721 def add_symbol(self, table, symbol, offsetmap = None):
722 offset = int(symbol['offset'], 16);
723 symbol['offset'] = offset
727 if offsetmap != None:
728 if not offsetmap.has_key(offset):
729 offsetmap[offset] = [symbol['symbol']]
731 offsetmap[offset].append(symbol['symbol'])
733 def parse_header(self, line):
734 m = self.re_header.match(line)
736 table = m.group('table')
737 if (table == "DYNAMIC"):
738 self.parser = self.parse_dynamic
740 self.parser = self.parse_local
742 raise ValueError("Invalid symbol table: %s" % table)
746 def parse_local(self, line):
747 if (self.parse_header(line)):
749 if (self.re_lame_symbol.match(line)):
751 m = self.re_local_symbol.match(line)
754 #raise ValueError("Invalid symbol definition: %s" % line)
756 if (p['symbol'] and p['symbol'].find('@') == -1):
757 self.add_symbol(self.local_symbols, p, self.local_offsetmap);
759 def parse_dynamic(self, line):
760 if (self.parse_header(line)):
762 if (self.re_lame_symbol.match(line)):
764 m = self.re_dynamic_symbol.match(line)
766 raise ValueError("Invalid symbol definition: %s" % line)
768 if (p['symbol'] and p['ver']):
769 self.add_symbol(self.dynamic_symbols, p);
771 class DwarfdumpParser(Parser):
773 tagcache_stats = Cache.CacheStats()
777 self.cache = Cache(enabled=Config.dwarfcache_enabled,
778 stats=DwarfdumpParser.tagcache_stats)
779 self.incomplete = Cache()
783 def __init__(self, unit, data):
785 self.id = int(data['id'])
786 self.level = int(data['level'])
787 self.tag = data['tag']
793 return self.arg('name')
797 return self.optarg('name', None)
799 def setname(self, name):
800 self.args['DW_AT_name'] = name
805 return self.args[name]
807 raise KeyError("Argument '%s' not found in %s: %s" %
808 (name, self, self.args))
810 def optarg(self, a, default):
817 return "Tag(%d, %d, %s)" % (self.level, self.id, self.tag)
819 re_header = re.compile('<(?P<level>\d+)><(?P<id>\d+\+*\d*)><(?P<tag>\w+)>')
820 re_argname = re.compile('(?P<arg>\w+)<')
821 re_argunknown = re.compile('<Unknown AT value \w+><[^<>]+>')
824 'DW_TAG_lexical_block',
825 'DW_TAG_inlined_subroutine',
830 def __init__(self, libfile):
831 Parser.__init__(self, "%s -di %s" % (Config.dwarfdump, libfile))
832 self.current_unit = None
836 def parse_begin(self, line):
837 if line == '.debug_info':
838 self.parser = self.parse_debuginfo
840 raise ValueError("Invalid dwarfdump header: %s" % line)
842 def parse_argvalue(self, args):
843 assert args.startswith('<')
846 while i < len(args) and args[i]:
858 def parse_arg(self, tag, args):
859 m = self.re_argname.match(args)
861 m = self.re_argunknown.match(args)
863 raise ValueError("Invalid dwarfdump: couldn't parse arguments: %s" %
865 args = args[len(m.group(0)):].lstrip()
867 argname = m.group('arg')
868 args = args[len(argname):]
870 while len(args) > 0 and args.startswith('<'):
871 (args, v) = self.parse_argvalue(args)
876 tag.args[argname] = value
879 def parse_debuginfo(self, line):
880 m = self.re_header.match(line)
882 raise ValueError("Invalid dwarfdump: %s" % line)
883 if m.group('level') == '0':
884 self.current_unit = DwarfdumpParser.Unit()
886 tag = DwarfdumpParser.Tag(self.current_unit, m.groupdict())
887 args = line[len(m.group(0)):].lstrip()
889 args = self.parse_arg(tag, args)
890 tag.unit.tags[tag.id] = tag
891 if tag.args.has_key('DW_AT_low_pc') and \
892 tag.tag not in DwarfdumpParser.skip_tags:
893 offset = int(tag.args['DW_AT_low_pc'], 16)
894 if self.offsetmap.has_key(offset):
895 raise ValueError("Dwarf dump parse error: " +
896 "symbol is aleady defined at offset 0x%x" % offset)
897 self.offsetmap[offset] = tag
898 if len(self.stack) > 0:
899 prev = self.stack.pop()
900 while prev.level >= tag.level and len(self.stack) > 0:
901 prev = self.stack.pop()
902 if prev.level < tag.level:
903 assert prev.level == tag.level - 1
904 # TODO check DW_AT_sibling ???
905 if tag.tag not in DwarfdumpParser.skip_tags:
906 prev.nested.append(tag)
907 self.stack.append(prev)
908 self.stack.append(tag)
909 assert len(self.stack) == tag.level
914 l = [ str(x) for x in l ]
918 def names_ver_str(vername, names):
919 return list_str([ x + "@" + vername for x in names ])
921 def common_symbols(origlib, newlib):
923 verdiff = ListDiff(origlib.versions.keys(), newlib.versions.keys())
924 if Config.verbose >= 1:
925 print 'Original versions: ', list_str(verdiff.orig)
926 print 'New versions: ', list_str(verdiff.new)
927 for vername in verdiff.added:
928 print 'Added version: ', vername
929 print ' Added symbols: ', \
930 names_ver_str(vername, newlib.versions[vername].names())
931 for vername in verdiff.removed:
932 print 'Removed version: ', vername
933 print ' Removed symbols: ', \
934 names_ver_str(vername, origlib.versions[vername].names())
937 for vername in verdiff.common:
938 origver = origlib.versions[vername]
939 newver = newlib.versions[vername]
940 namediff = ListDiff(origver.names(), newver.names())
942 added.append(names_ver_str(vername, namediff.added))
944 removed.append(names_ver_str(vername, namediff.removed))
945 commonver = VersionMap(vername)
946 result.append(commonver)
947 for n in namediff.common:
948 sym = CommonSymbol(origver.symbols[n], newver.symbols[n])
949 commonver.append(sym)
951 print 'Added symbols:'
955 print 'Removed symbols:'
960 def cmp_symbols(commonver):
961 for ver in commonver:
964 for symname in names:
965 sym = ver.symbols[symname]
966 match = sym.origsym.definition == sym.newsym.definition
969 if Config.verbose >= 1 or not match:
970 print '%s: definitions %smatch' % \
971 (sym.origsym.name_ver, "" if match else "mis")
972 if Config.dump or (not match and not Config.no_dump):
973 for x in [(sym.origsym, Config.origfile),
974 (sym.newsym, Config.newfile)]:
977 if not xsym.definition:
978 print >> xout, '\n// Definition not found: %s %s' % \
979 (xsym.name_ver, xsym.lib.libfile)
981 print >> xout, '\n// Definitions mismatch: %s %s' % \
982 (xsym.name_ver, xsym.lib.libfile)
984 pp.run(xsym.definition)
985 for i in pp.nested():
987 print >> xout, pp.result()
989 def dump_symbols(commonver):
990 class SymbolDump(object):
991 def __init__(self, io_conf):
992 self.io_conf = io_conf
993 self.pp = PrettyPrinter()
996 r = self.pp.run(sym.definition)
997 self.res.append('/* %s@%s */ %s' % (sym.name, sym.version, r))
999 print >> self.io_conf.out, '\n// Symbol dump: version %s, library %s' % \
1000 (ver.name, self.io_conf.filename)
1001 for i in self.pp.nested():
1002 print >> self.io_conf.out, i
1003 print >> self.io_conf.out, ''
1005 print >> self.io_conf.out, i
1006 for ver in commonver:
1007 names = sorted(ver.names());
1008 d_orig = SymbolDump(Config.origfile)
1009 d_new = SymbolDump(Config.newfile)
1010 for symname in names:
1011 sym = ver.symbols[symname]
1012 if not sym.origsym.definition or not sym.newsym.definition:
1014 warn(Config.w_symbol, 'Missing symbol definition: %s@%s' % \
1015 (symname, ver.name))
1017 d_orig.run(sym.origsym)
1018 d_new.run(sym.newsym)
1022 if __name__ == '__main__':
1024 parser = optparse.OptionParser(usage="usage: %prog origlib newlib",
1025 version="%prog " + Config.version)
1026 parser.add_option('-v', '--verbose', action='count',
1027 help="verbose mode, may be specified several times")
1028 parser.add_option('--alias-prefix', action='append',
1029 help="name prefix to try for symbol alias lookup", metavar="STR")
1030 parser.add_option('--dump', action='store_true',
1031 help="dump symbol definitions")
1032 parser.add_option('--no-dump', action='store_true',
1033 help="disable dump for mismatched symbols")
1034 parser.add_option('--out-orig', action='store',
1035 help="result output file for original library", metavar="ORIGFILE")
1036 parser.add_option('--out-new', action='store',
1037 help="result output file for new library", metavar="NEWFILE")
1038 parser.add_option('--exclude-ver', action='append', metavar="RE")
1039 parser.add_option('--include-ver', action='append', metavar="RE")
1040 parser.add_option('--exclude-sym', action='append', metavar="RE")
1041 parser.add_option('--include-sym', action='append', metavar="RE")
1042 for opt in ['alias', 'cached', 'symbol']:
1043 parser.add_option("--w-" + opt,
1044 action="store_true", dest="w_" + opt)
1045 parser.add_option("--w-no-" + opt,
1046 action="store_false", dest="w_" + opt)
1047 (opts, args) = parser.parse_args()
1053 Config.origfile.init(opts.out_orig)
1055 Config.newfile.init(opts.out_new)
1058 Config.no_dump = True
1061 Config.no_dump = False
1064 Config.verbose = opts.verbose
1065 if opts.alias_prefix:
1066 Config.alias_prefixes = opts.alias_prefix
1067 Config.alias_prefixes.sort(key=lambda x: -len(x))
1068 for (k, v) in ({ '_sym': Config.symbol_filter,
1069 '_ver': Config.version_filter }).items():
1070 for a in [ 'exclude', 'include' ]:
1071 opt = getattr(opts, a + k)
1073 getattr(v, a).extend(opt)
1074 Config.version_filter.compile()
1075 Config.symbol_filter.compile()
1076 for w in ['w_alias', 'w_cached', 'w_symbol']:
1077 if hasattr(opts, w):
1078 v = getattr(opts, w)
1080 setattr(Config, w, v)
1082 (Config.origfile.filename, Config.newfile.filename) = (args[0], args[1])
1084 origlib = Shlib(Config.origfile.filename)
1086 newlib = Shlib(Config.newfile.filename)
1089 commonver = common_symbols(origlib, newlib)
1091 dump_symbols(commonver)
1092 cmp_symbols(commonver)
1093 if Config.verbose >= 4:
1094 print Dwarf.cmpcache.stats.show('Cmp')
1095 print DwarfdumpParser.tagcache_stats.show('Dwarf tag')
1097 sys.exit(App.result_code)