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()
63 exclude_sym_default = [
73 cls.version_filter = StrFilter()
74 cls.symbol_filter = StrFilter()
81 print >> sys.stderr, "WARN: " + msg
85 class StrFilter(object):
91 self.re_exclude = [ re.compile(x) for x in self.exclude ]
92 self.re_include = [ re.compile(x) for x in self.include ]
95 if len(self.re_include):
97 for r in self.re_include:
103 for r in self.re_exclude:
110 class CacheStats(object):
115 def show(self, name):
116 total = self.hit + self.miss
120 ratio = '%f' % (self.hit/float(total))
121 return '%s cache stats: hit: %d; miss: %d; ratio: %s' % \
122 (name, self.hit, self.miss, ratio)
124 def __init__(self, enabled=True, stats=None):
125 self.enabled = enabled
128 self.stats = Cache.CacheStats()
133 if self.enabled and self.items.has_key(id):
135 return self.items[id]
140 def put(self, id, obj):
142 if self.items.has_key(id) and obj is not self.items[id]:
143 #raise ValueError("Item is already cached: %d (%s, %s)" %
144 # (id, self.items[id], obj))
145 warn(Config.w_cached, "Item is already cached: %d (%s, %s)" % \
146 (id, self.items[id], obj))
149 def replace(self, id, obj):
151 assert self.items.has_key(id)
154 class ListDiff(object):
155 def __init__(self, orig, new):
156 self.orig = set(orig)
158 self.common = self.orig & self.new
159 self.added = self.new - self.common
160 self.removed = self.orig - self.common
162 class PrettyPrinter(object):
166 def run_nested(self, obj):
167 ex = obj._pp_ex(self)
168 self.stack.append(ex)
171 self._result = obj._pp(self)
175 return sorted(set(self.stack))
182 #{{{ symbols and version maps
184 class Symbol(object):
185 def __init__(self, name, offset, version, lib):
188 self.version = version
190 self.definition = None
194 return self.name + '@' + self.version
197 return "Symbol(%s, 0x%x, %s)" % (self.name, self.offset, self.version)
199 class CommonSymbol(object):
200 def __init__(self, origsym, newsym):
201 if origsym.name != newsym.name or origsym.version != newsym.version:
202 raise RuntimeError("Symbols have different names: %s",
204 self.origsym = origsym
206 self.name = newsym.name
207 self.version = newsym.version
210 return "CommonSymbol(%s, %s)" % (self.name, self.version)
212 class SymbolAlias(object):
213 def __init__(self, alias, prefix, offset):
214 assert alias.startswith(prefix)
216 self.name = alias[len(prefix):]
220 return "SymbolAlias(%s, 0x%x)" % (self.alias, self.offset)
223 class VersionMap(object):
224 def __init__(self, name):
228 def append(self, symbol):
229 if (self.symbols.has_key(symbol.name)):
230 raise ValueError("Symbol is already defined %s@%s" %
231 (symbol.name, self.name))
232 self.symbols[symbol.name] = symbol
235 return self.symbols.keys()
238 return repr(self.symbols.values())
242 # {{{ types and definitions
247 def __init__(self, id, name, **kwargs):
252 def __getattr__(self, attr):
253 if not self.attrs.has_key(attr):
254 raise AttributeError('%s in %s' % (attr, str(self)))
255 return self.attrs[attr]
257 def _name_opt(self, default=''):
264 return self.type._alias()
267 def __cmp__(self, other):
268 # TODO assert 'self' and 'other' belong to different libraries
269 #print 'cmp defs: %s, %s' % (self, other)
273 except AttributeError:
275 r = cmp(a.__class__, b.__class__)
277 if a.id != 0 and b.id != 0:
278 ind = (long(a.id) << 32) + b.id
279 r = Dwarf.cmpcache.get(ind)
284 r = cmp(a.attrs, b.attrs)
286 Dwarf.cmpcache.put(ind, r)
289 #raise RuntimeError('Comparing different classes: %s, %s' %
290 # (a.__class__.__name__, b.__class__.__name__))
295 if hasattr(self, 'name'):
296 p.append("name=%s" % self.name)
297 for (k, v) in self.attrs.items():
298 if isinstance(v, Def):
299 v = v.__class__.__name__ + '(...)'
300 p.append("%s=%s" % (k, v))
301 return self.__class__.__name__ + '(' + ', '.join(p) + ')'
303 def _mapval(self, param, vals):
304 if param not in vals.keys():
305 raise NotImplementedError("Invalid value '%s': %s" %
309 def _pp_ex(self, pp):
310 raise NotImplementedError('Extended pretty print not implemeted: %s' %
314 raise NotImplementedError('Pretty print not implemeted: %s' % str(self))
316 class AnonymousDef(Def):
317 def __init__(self, id, **kwargs):
318 Def.__init__(self, id, None, **kwargs)
320 class Void(AnonymousDef):
323 def __new__(cls, *args, **kwargs):
324 if not cls._instance:
325 cls._instance = super(Void, cls).__new__(
326 cls, *args, **kwargs)
330 AnonymousDef.__init__(self, 0)
335 class VarArgs(AnonymousDef):
339 class PointerDef(AnonymousDef):
341 t = pp.run(self.type)
344 class BaseTypeDef(Def):
345 inttypes = ['DW_ATE_signed', 'DW_ATE_unsigned', 'DW_ATE_unsigned_char']
347 if self.encoding in self.inttypes:
348 sign = '' if self.encoding == 'DW_ATE_signed' else 'u'
349 bits = int(self.byte_size, 0) * 8
350 return '%sint%s_t' % (sign, bits)
351 elif self.encoding == 'DW_ATE_signed_char' and int(self.byte_size, 0) == 1:
353 elif self.encoding == 'DW_ATE_boolean' and int(self.byte_size, 0) == 1:
355 elif self.encoding == 'DW_ATE_float':
356 return self._mapval(int(self.byte_size, 0), {
361 raise NotImplementedError('Invalid encoding: %s' % self)
363 class TypeAliasDef(Def):
366 alias = self._alias()
368 if self.name and not alias.name:
369 alias.name = 'T(%s)' % self.name
370 # return type with modifiers
371 return self.type._pp(pp)
373 class EnumerationTypeDef(Def):
375 return 'enum ' + self._name_opt('UNKNOWN')
377 class ConstTypeDef(AnonymousDef):
380 return 'const ' + self.type._pp(pp)
382 class VolatileTypeDef(AnonymousDef):
385 return 'volatile ' + self.type._pp(pp)
387 class RestrictTypeDef(AnonymousDef):
390 return 'restrict ' + self.type._pp(pp)
392 class ArrayDef(AnonymousDef):
394 t = pp.run(self.type)
395 assert len(self.subranges) == 1
397 sz = int(self.subranges[0].upper_bound) + 1
399 s = re.sub(r'\(.+\)', '', self.subranges[0].upper_bound)
401 return '%s[%s]' % (t, sz)
403 class ArraySubrangeDef(AnonymousDef):
406 class FunctionDef(Def):
408 result = pp.run(self.result)
412 params = ', '.join([ pp.run(x) for x in self.params ])
413 return "%s %s(%s);" % (result, self.name, params)
415 class FunctionTypeDef(Def):
417 result = pp.run(self.result)
421 params = ', '.join([ pp.run(x) for x in self.params ])
422 return "F(%s, %s, (%s))" % (self._name_opt(), result, params)
424 class ParameterDef(Def):
426 t = pp.run(self.type)
427 return "%s %s" % (t, self._name_opt())
429 class VariableDef(Def):
431 t = pp.run(self.type)
432 return "%s %s" % (t, self._name_opt())
435 class StructForwardDef(Def):
438 class IncompleteDef(Def):
439 def update(self, complete, cache=None):
440 self.complete = complete
441 complete.incomplete = self
443 cached = cache.get(self.id)
444 if cached != None and isinstance(cached, IncompleteDef):
445 cache.replace(self.id, complete)
447 class StructIncompleteDef(IncompleteDef):
449 return "struct %s" % (self.name,)
451 class UnionIncompleteDef(IncompleteDef):
453 return "union %s" % (self.name,)
455 class StructDef(Def):
456 def _pp_ex(self, pp, suffix=';'):
457 members = [ pp.run(x) for x in self.members ]
458 return "struct %s { %s }%s" % \
459 (self._name_opt(), ' '.join(members), suffix)
463 return "struct %s" % (self.name,)
465 return self._pp_ex(pp, suffix='')
468 def _pp_ex(self, pp, suffix=';'):
469 members = [ pp.run(x) for x in self.members ]
470 return "union %s { %s }%s" % \
471 (self._name_opt(), ' '.join(members), suffix)
475 return "union %s" % (self.name,)
477 return self._pp_ex(pp, suffix='')
479 class MemberDef(Def):
481 t = pp.run(self.type)
483 bits = ":%s" % self.bit_size
486 return "%s %s%s;" % (t, self._name_opt(), bits)
490 cmpcache = Cache(enabled=Config.cmpcache_enabled)
492 def __init__(self, dump):
495 def _build_optarg_type(self, praw):
496 type = praw.optarg('type', Void())
498 type = self.buildref(praw.unit, type)
501 def build_subprogram(self, raw):
502 if raw.optname == None:
503 raw.setname('SUBPROGRAM_NONAME_' + raw.arg('low_pc'));
504 params = [ self.build(x) for x in raw.nested ]
505 result = self._build_optarg_type(raw)
506 return FunctionDef(raw.id, raw.name, params=params, result=result)
508 def build_variable(self, raw):
509 type = self._build_optarg_type(raw)
510 return VariableDef(raw.id, raw.optname, type=type)
512 def build_subroutine_type(self, raw):
513 params = [ self.build(x) for x in raw.nested ]
514 result = self._build_optarg_type(raw)
515 return FunctionTypeDef(raw.id, raw.optname, params=params, result=result)
517 def build_formal_parameter(self, raw):
518 type = self._build_optarg_type(raw)
519 return ParameterDef(raw.id, raw.optname, type=type)
521 def build_pointer_type(self, raw):
522 type = self._build_optarg_type(raw)
523 return PointerDef(raw.id, type=type)
525 def build_member(self, raw):
526 type = self.buildref(raw.unit, raw.arg('type'))
527 return MemberDef(raw.id, raw.name, type=type,
528 bit_size=raw.optarg('bit_size', None))
530 def build_structure_type(self, raw):
531 incomplete = raw.unit.incomplete.get(raw.id)
532 if incomplete == None:
533 incomplete = StructIncompleteDef(raw.id, raw.optname)
534 raw.unit.incomplete.put(raw.id, incomplete)
537 members = [ self.build(x) for x in raw.nested ]
538 byte_size = raw.optarg('byte_size', None)
539 if byte_size == None:
540 obj = StructForwardDef(raw.id, raw.name, members=members,
542 obj = StructDef(raw.id, raw.optname, members=members,
544 incomplete.update(obj, cache=raw.unit.cache)
547 def build_union_type(self, raw):
548 incomplete = raw.unit.incomplete.get(raw.id)
549 if incomplete == None:
550 incomplete = UnionIncompleteDef(raw.id, raw.optname)
551 raw.unit.incomplete.put(raw.id, incomplete)
554 members = [ self.build(x) for x in raw.nested ]
555 byte_size = raw.optarg('byte_size', None)
556 obj = UnionDef(raw.id, raw.optname, members=members,
558 obj.incomplete = incomplete
559 incomplete.complete = obj
562 def build_typedef(self, raw):
563 type = self._build_optarg_type(raw)
564 return TypeAliasDef(raw.id, raw.name, type=type)
566 def build_const_type(self, raw):
567 type = self._build_optarg_type(raw)
568 return ConstTypeDef(raw.id, type=type)
570 def build_volatile_type(self, raw):
571 type = self._build_optarg_type(raw)
572 return VolatileTypeDef(raw.id, type=type)
574 def build_restrict_type(self, raw):
575 type = self._build_optarg_type(raw)
576 return RestrictTypeDef(raw.id, type=type)
578 def build_enumeration_type(self, raw):
579 # TODO handle DW_TAG_enumerator ???
580 return EnumerationTypeDef(raw.id, name=raw.optname,
581 byte_size=raw.arg('byte_size'))
583 def build_base_type(self, raw):
584 return BaseTypeDef(raw.id, raw.optname,
585 byte_size=raw.arg('byte_size'), encoding=raw.arg('encoding'))
587 def build_array_type(self, raw):
588 type = self.buildref(raw.unit, raw.arg('type'))
589 subranges = [ self.build(x) for x in raw.nested ]
590 return ArrayDef(raw.id, type=type, subranges=subranges)
592 def build_subrange_type(self, raw):
593 type = self.buildref(raw.unit, raw.arg('type'))
594 return ArraySubrangeDef(raw.id, type=type,
595 upper_bound=raw.optarg('upper_bound', 0))
597 def build_unspecified_parameters(self, raw):
598 return VarArgs(raw.id)
600 def _get_id(self, id):
604 if (id.startswith('<') and id.endswith('>')):
605 return int(id[1:-1], 0)
607 raise ValueError("Invalid dwarf id: %s" % id)
609 def build(self, raw):
610 obj = raw.unit.cache.get(raw.id)
613 builder_name = raw.tag.replace('DW_TAG_', 'build_')
615 builder = getattr(self, builder_name)
616 except AttributeError:
617 raise AttributeError("Unknown dwarf tag: %s" % raw)
619 raw.unit.cache.put(obj.id, obj)
622 def buildref(self, unit, id):
623 id = self._get_id(id)
625 obj = self.build(raw)
631 def __init__(self, libfile):
632 self.libfile = libfile
636 def parse_objdump(self):
637 objdump = ObjdumpParser(self.libfile)
639 for p in objdump.dynamic_symbols:
641 if vername.startswith('(') and vername.endswith(')'):
642 vername = vername[1:-1]
643 if not Config.version_filter.match(vername):
645 if not Config.symbol_filter.match(p['symbol']):
647 sym = Symbol(p['symbol'], p['offset'], vername, self)
648 if not self.versions.has_key(vername):
649 self.versions[vername] = VersionMap(vername)
650 self.versions[vername].append(sym)
651 if Config.alias_prefixes:
652 self.local_offsetmap = objdump.local_offsetmap
653 for p in objdump.local_symbols:
654 for prefix in Config.alias_prefixes:
655 if not p['symbol'].startswith(prefix):
657 alias = SymbolAlias(p['symbol'], prefix, p['offset'])
658 if self.alias_syms.has_key(alias.name):
659 prevalias = self.alias_syms[alias.name]
660 if alias.name != prevalias.name or \
661 alias.offset != prevalias.offset:
662 warn(Config.w_alias, "Symbol alias is " \
663 "already defined: %s: %s at %08x -- %s at %08x" % \
664 (alias.alias, alias.name, alias.offset,
665 prevalias.name, prevalias.offset))
666 self.alias_syms[alias.name] = alias
668 def parse_dwarfdump(self):
669 dwarfdump = DwarfdumpParser(self.libfile)
673 raw = dwarfdump.offsetmap[sym.offset]
676 localnames = self.local_offsetmap[sym.offset]
677 localnames.sort(key=lambda x: -len(x))
678 for localname in localnames:
679 if not self.alias_syms.has_key(localname):
681 alias = self.alias_syms[localname]
682 raw = dwarfdump.offsetmap[alias.offset]
688 dwarf = Dwarf(dwarfdump)
689 for ver in self.versions.values():
690 for sym in ver.symbols.values():
693 warn(Config.w_symbol, "Symbol %s (%s) not found at offset 0x%x" % \
694 (sym.name_ver, self.libfile, sym.offset))
696 if Config.verbose >= 3:
697 print "Parsing symbol %s (%s)" % (sym.name_ver, self.libfile)
698 sym.definition = dwarf.build(raw)
701 if not os.path.isfile(self.libfile):
702 print >> sys.stderr, ("No such file: %s" % self.libfile)
705 self.parse_dwarfdump()
709 class Parser(object):
710 def __init__(self, proc):
712 self.parser = self.parse_begin
715 fd = os.popen(self.proc, 'r')
725 print >> sys.stderr, ("Execution failed: %s" % self.proc)
728 def parse_begin(self, line):
731 class ObjdumpParser(Parser):
733 re_header = re.compile('(?P<table>\w*)\s*SYMBOL TABLE:')
735 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]*)')
736 re_lame_symbol = re.compile('(?P<offset>[0-9a-fA-F]+)\s+(?P<bind>\w+)\s+\*[A-Z]+\*')
738 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]*)')
740 def __init__(self, libfile):
741 Parser.__init__(self, "%s -wtT %s" % (Config.objdump, libfile))
742 self.dynamic_symbols = []
743 self.local_symbols = []
744 self.local_offsetmap = {}
746 def parse_begin(self, line):
747 self.parse_header(line)
749 def add_symbol(self, table, symbol, offsetmap = None):
750 offset = int(symbol['offset'], 16);
751 symbol['offset'] = offset
755 if offsetmap != None:
756 if not offsetmap.has_key(offset):
757 offsetmap[offset] = [symbol['symbol']]
759 offsetmap[offset].append(symbol['symbol'])
761 def parse_header(self, line):
762 m = self.re_header.match(line)
764 table = m.group('table')
765 if (table == "DYNAMIC"):
766 self.parser = self.parse_dynamic
768 self.parser = self.parse_local
770 raise ValueError("Invalid symbol table: %s" % table)
774 def parse_local(self, line):
775 if (self.parse_header(line)):
777 if (self.re_lame_symbol.match(line)):
779 m = self.re_local_symbol.match(line)
782 #raise ValueError("Invalid symbol definition: %s" % line)
784 if (p['symbol'] and p['symbol'].find('@') == -1):
785 self.add_symbol(self.local_symbols, p, self.local_offsetmap);
787 def parse_dynamic(self, line):
788 if (self.parse_header(line)):
790 if (self.re_lame_symbol.match(line)):
792 m = self.re_dynamic_symbol.match(line)
794 raise ValueError("Invalid symbol definition: %s" % line)
796 if (p['symbol'] and p['ver']):
797 self.add_symbol(self.dynamic_symbols, p);
799 class DwarfdumpParser(Parser):
801 tagcache_stats = Cache.CacheStats()
805 self.cache = Cache(enabled=Config.dwarfcache_enabled,
806 stats=DwarfdumpParser.tagcache_stats)
807 self.incomplete = Cache()
811 def __init__(self, unit, data):
813 self.id = int(data['id'], 0)
814 self.level = int(data['level'])
815 self.tag = data['tag']
821 return self.arg('name')
825 return self.optarg('name', None)
827 def setname(self, name):
828 self.args['DW_AT_name'] = name
833 return self.args[name]
835 raise KeyError("Argument '%s' not found in %s: %s" %
836 (name, self, self.args))
838 def optarg(self, a, default):
845 return "Tag(%d, %d, %s)" % (self.level, self.id, self.tag)
847 re_header = re.compile('<(?P<level>\d+)><(?P<id>[0xX0-9a-fA-F]+(?:\+(0[xX])?[0-9a-fA-F]+)?)><(?P<tag>\w+)>')
848 re_argname = re.compile('(?P<arg>\w+)<')
849 re_argunknown = re.compile('<Unknown AT value \w+><[^<>]+>')
852 'DW_TAG_lexical_block',
853 'DW_TAG_inlined_subroutine',
858 external_tags = set([
862 def __init__(self, libfile):
863 Parser.__init__(self, "%s -di %s" % (Config.dwarfdump, libfile))
864 self.current_unit = None
868 def parse_begin(self, line):
869 if line == '.debug_info':
870 self.parser = self.parse_debuginfo
872 raise ValueError("Invalid dwarfdump header: %s" % line)
874 def parse_argvalue(self, args):
875 assert args.startswith('<')
878 while i < len(args) and args[i]:
890 def parse_arg(self, tag, args):
891 m = self.re_argname.match(args)
893 m = self.re_argunknown.match(args)
895 raise ValueError("Invalid dwarfdump: couldn't parse arguments: %s" %
897 args = args[len(m.group(0)):].lstrip()
899 argname = m.group('arg')
900 args = args[len(argname):]
902 while len(args) > 0 and args.startswith('<'):
903 (args, v) = self.parse_argvalue(args)
908 tag.args[argname] = value
911 def parse_debuginfo(self, line):
912 m = self.re_header.match(line)
914 raise ValueError("Invalid dwarfdump: %s" % line)
915 if m.group('level') == '0':
916 self.current_unit = DwarfdumpParser.Unit()
918 tag = DwarfdumpParser.Tag(self.current_unit, m.groupdict())
919 args = line[len(m.group(0)):].lstrip()
921 args = self.parse_arg(tag, args)
922 tag.unit.tags[tag.id] = tag
923 def parse_offset(tag):
924 if tag.args.has_key('DW_AT_low_pc'):
925 return int(tag.args['DW_AT_low_pc'], 16)
926 elif tag.args.has_key('DW_AT_location'):
927 location = tag.args['DW_AT_location']
928 if location.startswith('DW_OP_addr'):
929 return int(location.replace('DW_OP_addr', ''), 16)
931 offset = parse_offset(tag)
932 if offset is not None and \
933 (tag.tag not in DwarfdumpParser.skip_tags or \
934 (tag.args.has_key('DW_AT_external') and \
935 tag.tag in DwarfdumpParser.external_tags)):
936 if self.offsetmap.has_key(offset):
937 raise ValueError("Dwarf dump parse error: " +
938 "symbol is aleady defined at offset 0x%x" % offset)
939 self.offsetmap[offset] = tag
940 if len(self.stack) > 0:
941 prev = self.stack.pop()
942 while prev.level >= tag.level and len(self.stack) > 0:
943 prev = self.stack.pop()
944 if prev.level < tag.level:
945 assert prev.level == tag.level - 1
946 # TODO check DW_AT_sibling ???
947 if tag.tag not in DwarfdumpParser.skip_tags:
948 prev.nested.append(tag)
949 self.stack.append(prev)
950 self.stack.append(tag)
951 assert len(self.stack) == tag.level
956 l = [ str(x) for x in l ]
960 def names_ver_str(vername, names):
961 return list_str([ x + "@" + vername for x in names ])
963 def common_symbols(origlib, newlib):
965 verdiff = ListDiff(origlib.versions.keys(), newlib.versions.keys())
966 if Config.verbose >= 1:
967 print 'Original versions: ', list_str(verdiff.orig)
968 print 'New versions: ', list_str(verdiff.new)
969 for vername in verdiff.added:
970 print 'Added version: ', vername
971 print ' Added symbols: ', \
972 names_ver_str(vername, newlib.versions[vername].names())
973 for vername in verdiff.removed:
974 print 'Removed version: ', vername
975 print ' Removed symbols: ', \
976 names_ver_str(vername, origlib.versions[vername].names())
979 for vername in verdiff.common:
980 origver = origlib.versions[vername]
981 newver = newlib.versions[vername]
982 namediff = ListDiff(origver.names(), newver.names())
984 added.append(names_ver_str(vername, namediff.added))
986 removed.append(names_ver_str(vername, namediff.removed))
987 commonver = VersionMap(vername)
988 result.append(commonver)
989 for n in namediff.common:
990 sym = CommonSymbol(origver.symbols[n], newver.symbols[n])
991 commonver.append(sym)
993 print 'Added symbols:'
997 print 'Removed symbols:'
1002 def cmp_symbols(commonver):
1003 for ver in commonver:
1004 names = ver.names();
1006 for symname in names:
1007 sym = ver.symbols[symname]
1008 missing = sym.origsym.definition is None or sym.newsym.definition is None
1009 match = not missing and sym.origsym.definition == sym.newsym.definition
1012 if Config.verbose >= 1 or not match:
1014 print '%s: missing definition' % \
1015 (sym.origsym.name_ver,)
1017 print '%s: definitions %smatch' % \
1018 (sym.origsym.name_ver, "" if match else "mis")
1019 if Config.dump or (not match and not Config.no_dump):
1020 for x in [(sym.origsym, Config.origfile),
1021 (sym.newsym, Config.newfile)]:
1024 if not xsym.definition:
1025 print >> xout, '\n// Definition not found: %s %s' % \
1026 (xsym.name_ver, xsym.lib.libfile)
1028 print >> xout, '\n// Definitions mismatch: %s %s' % \
1029 (xsym.name_ver, xsym.lib.libfile)
1030 pp = PrettyPrinter()
1031 pp.run(xsym.definition)
1032 for i in pp.nested():
1034 print >> xout, pp.result()
1036 def dump_symbols(commonver):
1037 class SymbolDump(object):
1038 def __init__(self, io_conf):
1039 self.io_conf = io_conf
1040 self.pp = PrettyPrinter()
1043 r = self.pp.run(sym.definition)
1044 self.res.append('/* %s@%s */ %s' % (sym.name, sym.version, r))
1046 print >> self.io_conf.out, '\n// Symbol dump: version %s, library %s' % \
1047 (ver.name, self.io_conf.filename)
1048 for i in self.pp.nested():
1049 print >> self.io_conf.out, i
1050 print >> self.io_conf.out, ''
1052 print >> self.io_conf.out, i
1053 for ver in commonver:
1054 names = sorted(ver.names());
1055 d_orig = SymbolDump(Config.origfile)
1056 d_new = SymbolDump(Config.newfile)
1057 for symname in names:
1058 sym = ver.symbols[symname]
1059 if not sym.origsym.definition or not sym.newsym.definition:
1061 warn(Config.w_symbol, 'Missing symbol definition: %s@%s' % \
1062 (symname, ver.name))
1064 d_orig.run(sym.origsym)
1065 d_new.run(sym.newsym)
1069 if __name__ == '__main__':
1071 parser = optparse.OptionParser(usage="usage: %prog origlib newlib",
1072 version="%prog " + Config.version)
1073 parser.add_option('-v', '--verbose', action='count',
1074 help="verbose mode, may be specified several times")
1075 parser.add_option('--alias-prefix', action='append',
1076 help="name prefix to try for symbol alias lookup", metavar="STR")
1077 parser.add_option('--dump', action='store_true',
1078 help="dump symbol definitions")
1079 parser.add_option('--no-dump', action='store_true',
1080 help="disable dump for mismatched symbols")
1081 parser.add_option('--out-orig', action='store',
1082 help="result output file for original library", metavar="ORIGFILE")
1083 parser.add_option('--out-new', action='store',
1084 help="result output file for new library", metavar="NEWFILE")
1085 parser.add_option('--dwarfdump', action='store',
1086 help="path to dwarfdump executable", metavar="DWARFDUMP")
1087 parser.add_option('--objdump', action='store',
1088 help="path to objdump executable", metavar="OBJDUMP")
1089 parser.add_option('--exclude-ver', action='append', metavar="RE")
1090 parser.add_option('--include-ver', action='append', metavar="RE")
1091 parser.add_option('--exclude-sym', action='append', metavar="RE")
1092 parser.add_option('--include-sym', action='append', metavar="RE")
1093 parser.add_option('--no-exclude-sym-default', action='store_true',
1094 help="don't exclude special symbols like _init, _end, __bss_start")
1095 for opt in ['alias', 'cached', 'symbol']:
1096 parser.add_option("--w-" + opt,
1097 action="store_true", dest="w_" + opt)
1098 parser.add_option("--w-no-" + opt,
1099 action="store_false", dest="w_" + opt)
1100 (opts, args) = parser.parse_args()
1106 Config.dwarfdump = opts.dwarfdump
1108 Config.objdump = opts.objdump
1110 Config.origfile.init(opts.out_orig)
1112 Config.newfile.init(opts.out_new)
1115 Config.no_dump = True
1118 Config.no_dump = False
1121 Config.verbose = opts.verbose
1122 if opts.alias_prefix:
1123 Config.alias_prefixes = opts.alias_prefix
1124 Config.alias_prefixes.sort(key=lambda x: -len(x))
1125 for (k, v) in ({ '_sym': Config.symbol_filter,
1126 '_ver': Config.version_filter }).items():
1127 for a in [ 'exclude', 'include' ]:
1128 opt = getattr(opts, a + k)
1130 getattr(v, a).extend(opt)
1131 if not opts.no_exclude_sym_default:
1132 Config.symbol_filter.exclude.extend(Config.exclude_sym_default)
1133 Config.version_filter.compile()
1134 Config.symbol_filter.compile()
1135 for w in ['w_alias', 'w_cached', 'w_symbol']:
1136 if hasattr(opts, w):
1137 v = getattr(opts, w)
1139 setattr(Config, w, v)
1141 (Config.origfile.filename, Config.newfile.filename) = (args[0], args[1])
1143 origlib = Shlib(Config.origfile.filename)
1145 newlib = Shlib(Config.newfile.filename)
1148 commonver = common_symbols(origlib, newlib)
1150 dump_symbols(commonver)
1151 cmp_symbols(commonver)
1152 if Config.verbose >= 4:
1153 print Dwarf.cmpcache.stats.show('Cmp')
1154 print DwarfdumpParser.tagcache_stats.show('Dwarf tag')
1156 sys.exit(App.result_code)