shlib-compat.py revision 291036
1248693Sgleb#!/usr/bin/env python
2248693Sgleb#-
3248693Sgleb# Copyright (c) 2010 Gleb Kurtsou
4248693Sgleb# All rights reserved.
5248693Sgleb#
6248693Sgleb# Redistribution and use in source and binary forms, with or without
7248693Sgleb# modification, are permitted provided that the following conditions
8248693Sgleb# are met:
9248693Sgleb# 1. Redistributions of source code must retain the above copyright
10248693Sgleb#    notice, this list of conditions and the following disclaimer.
11248693Sgleb# 2. Redistributions in binary form must reproduce the above copyright
12248693Sgleb#    notice, this list of conditions and the following disclaimer in the
13248693Sgleb#    documentation and/or other materials provided with the distribution.
14248693Sgleb#
15248693Sgleb# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16248693Sgleb# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17248693Sgleb# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18248693Sgleb# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19248693Sgleb# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20248693Sgleb# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21248693Sgleb# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22248693Sgleb# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23248693Sgleb# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24248693Sgleb# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25248693Sgleb# SUCH DAMAGE.
26248693Sgleb#
27248693Sgleb# $FreeBSD: head/tools/tools/shlib-compat/shlib-compat.py 291036 2015-11-18 23:32:29Z rodrigc $
28248693Sgleb
29248693Sglebimport os
30248693Sglebimport sys
31248693Sglebimport re
32248693Sglebimport optparse
33248693Sgleb
34248693Sglebclass Config(object):
35248693Sgleb    version = '0.1'
36248693Sgleb    # controlled by user
37248693Sgleb    verbose = 0
38248693Sgleb    dump = False
39248693Sgleb    no_dump = False
40248693Sgleb    version_filter = None
41248693Sgleb    symbol_filter = None
42248693Sgleb    alias_prefixes = []
43248693Sgleb    # misc opts
44248693Sgleb    objdump = 'objdump'
45248693Sgleb    dwarfdump = 'dwarfdump'
46248693Sgleb    # debug
47248693Sgleb    cmpcache_enabled = True
48248693Sgleb    dwarfcache_enabled = True
49248693Sgleb    w_alias = True
50248693Sgleb    w_cached = False
51248693Sgleb    w_symbol = True
52248693Sgleb
53248693Sgleb    class FileConfig(object):
54248693Sgleb        filename = None
55248693Sgleb        out = sys.stdout
56248693Sgleb        def init(self, outname):
57248693Sgleb            if outname and outname != '-':
58248693Sgleb                self.out = open(outname, "w")
59248693Sgleb
60248693Sgleb    origfile = FileConfig()
61248693Sgleb    newfile = FileConfig()
62248693Sgleb
63275354Sgleb    exclude_sym_default = [
64275354Sgleb            '^__bss_start$',
65275354Sgleb            '^_edata$',
66275354Sgleb            '^_end$',
67275354Sgleb            '^_fini$',
68275354Sgleb            '^_init$',
69275354Sgleb            ]
70275354Sgleb
71248693Sgleb    @classmethod
72248693Sgleb    def init(cls):
73248693Sgleb        cls.version_filter = StrFilter()
74248693Sgleb        cls.symbol_filter = StrFilter()
75248693Sgleb
76248693Sglebclass App(object):
77248693Sgleb    result_code = 0
78248693Sgleb
79248693Sglebdef warn(cond, msg):
80248693Sgleb    if cond:
81248693Sgleb        print >> sys.stderr, "WARN: " + msg
82248693Sgleb
83248693Sgleb# {{{ misc
84248693Sgleb
85248693Sglebclass StrFilter(object):
86248693Sgleb    def __init__(self):
87248693Sgleb        self.exclude = []
88248693Sgleb        self.include = []
89248693Sgleb
90248693Sgleb    def compile(self):
91248693Sgleb        self.re_exclude = [ re.compile(x) for x in self.exclude ]
92248693Sgleb        self.re_include = [ re.compile(x) for x in self.include ]
93248693Sgleb
94248693Sgleb    def match(self, s):
95248693Sgleb        if len(self.re_include):
96248693Sgleb            matched = False
97248693Sgleb            for r in self.re_include:
98248693Sgleb                if r.match(s):
99248693Sgleb                    matched = True
100248693Sgleb                    break
101248693Sgleb            if not matched:
102248693Sgleb                return False
103248693Sgleb        for r in self.re_exclude:
104248693Sgleb            if r.match(s):
105248693Sgleb                return False
106248693Sgleb        return True
107248693Sgleb
108248693Sglebclass Cache(object):
109248693Sgleb
110248693Sgleb    class CacheStats(object):
111248693Sgleb        def __init__(self):
112248693Sgleb            self.hit = 0
113248693Sgleb            self.miss = 0
114248693Sgleb
115248693Sgleb        def show(self, name):
116248693Sgleb            total = self.hit + self.miss
117248693Sgleb            if total == 0:
118248693Sgleb                ratio = '(undef)'
119248693Sgleb            else:
120248693Sgleb                ratio = '%f' % (self.hit/float(total))
121248693Sgleb            return '%s cache stats: hit: %d; miss: %d; ratio: %s' % \
122248693Sgleb                    (name, self.hit, self.miss, ratio)
123248693Sgleb
124248693Sgleb    def __init__(self, enabled=True, stats=None):
125248693Sgleb        self.enabled = enabled
126248693Sgleb        self.items = {}
127248693Sgleb        if stats == None:
128248693Sgleb            self.stats = Cache.CacheStats()
129248693Sgleb        else:
130248693Sgleb            self.stats = stats
131248693Sgleb
132248693Sgleb    def get(self, id):
133291036Srodrigc        if self.enabled and id in self.items:
134248693Sgleb            self.stats.hit += 1
135248693Sgleb            return self.items[id]
136248693Sgleb        else:
137248693Sgleb            self.stats.miss += 1
138248693Sgleb            return None
139248693Sgleb
140248693Sgleb    def put(self, id, obj):
141248693Sgleb        if self.enabled:
142291036Srodrigc            if id in self.items and obj is not self.items[id]:
143248693Sgleb                #raise ValueError("Item is already cached: %d (%s, %s)" %
144248693Sgleb                #        (id, self.items[id], obj))
145248693Sgleb                warn(Config.w_cached, "Item is already cached: %d (%s, %s)" % \
146248693Sgleb                        (id, self.items[id], obj))
147248693Sgleb            self.items[id] = obj
148248693Sgleb
149248693Sgleb    def replace(self, id, obj):
150248693Sgleb        if self.enabled:
151291036Srodrigc            assert id in self.items
152248693Sgleb            self.items[id] = obj
153248693Sgleb
154248693Sglebclass ListDiff(object):
155248693Sgleb    def __init__(self, orig, new):
156248693Sgleb        self.orig = set(orig)
157248693Sgleb        self.new = set(new)
158248693Sgleb        self.common = self.orig & self.new
159248693Sgleb        self.added = self.new - self.common
160248693Sgleb        self.removed = self.orig - self.common
161248693Sgleb
162248693Sglebclass PrettyPrinter(object):
163248693Sgleb    def __init__(self):
164248693Sgleb        self.stack = []
165248693Sgleb
166248693Sgleb    def run_nested(self, obj):
167248693Sgleb        ex = obj._pp_ex(self)
168248693Sgleb        self.stack.append(ex)
169248693Sgleb
170248693Sgleb    def run(self, obj):
171248693Sgleb        self._result = obj._pp(self)
172248693Sgleb        return self._result
173248693Sgleb
174248693Sgleb    def nested(self):
175248693Sgleb        return sorted(set(self.stack))
176248693Sgleb
177248693Sgleb    def result(self):
178248693Sgleb        return self._result;
179248693Sgleb
180248693Sgleb# }}}
181248693Sgleb
182248693Sgleb#{{{ symbols and version maps
183248693Sgleb
184248693Sglebclass Symbol(object):
185248693Sgleb    def __init__(self, name, offset, version, lib):
186248693Sgleb        self.name = name
187248693Sgleb        self.offset = offset
188248693Sgleb        self.version = version
189248693Sgleb        self.lib = lib
190248693Sgleb        self.definition = None
191248693Sgleb
192248693Sgleb    @property
193248693Sgleb    def name_ver(self):
194248693Sgleb        return self.name + '@' + self.version
195248693Sgleb
196248693Sgleb    def __repr__(self):
197248693Sgleb        return "Symbol(%s, 0x%x, %s)" % (self.name, self.offset, self.version)
198248693Sgleb
199248693Sglebclass CommonSymbol(object):
200248693Sgleb    def __init__(self, origsym, newsym):
201248693Sgleb        if origsym.name != newsym.name or origsym.version != newsym.version:
202248693Sgleb            raise RuntimeError("Symbols have different names: %s",
203248693Sgleb                    [origsym, newsym])
204248693Sgleb        self.origsym = origsym
205248693Sgleb        self.newsym = newsym
206248693Sgleb        self.name = newsym.name
207248693Sgleb        self.version = newsym.version
208248693Sgleb
209248693Sgleb    def __repr__(self):
210248693Sgleb        return "CommonSymbol(%s, %s)" % (self.name, self.version)
211248693Sgleb
212248693Sglebclass SymbolAlias(object):
213248693Sgleb    def __init__(self, alias, prefix, offset):
214248693Sgleb        assert alias.startswith(prefix)
215248693Sgleb        self.alias = alias
216248693Sgleb        self.name = alias[len(prefix):]
217248693Sgleb        self.offset = offset
218248693Sgleb
219248693Sgleb    def __repr__(self):
220248693Sgleb        return "SymbolAlias(%s, 0x%x)" % (self.alias, self.offset)
221248693Sgleb
222248693Sgleb
223248693Sglebclass VersionMap(object):
224248693Sgleb    def __init__(self, name):
225248693Sgleb        self.name = name
226248693Sgleb        self.symbols = {}
227248693Sgleb
228248693Sgleb    def append(self, symbol):
229291036Srodrigc        if (symbol.name in self.symbols):
230248693Sgleb            raise ValueError("Symbol is already defined %s@%s" %
231248693Sgleb                    (symbol.name, self.name))
232248693Sgleb        self.symbols[symbol.name] = symbol
233248693Sgleb
234248693Sgleb    def names(self):
235248693Sgleb        return self.symbols.keys()
236248693Sgleb
237248693Sgleb    def __repr__(self):
238248693Sgleb        return repr(self.symbols.values())
239248693Sgleb
240248693Sgleb# }}}
241248693Sgleb
242248693Sgleb# {{{ types and definitions
243248693Sgleb
244248693Sglebclass Def(object):
245248693Sgleb    _is_alias = False
246248693Sgleb
247248693Sgleb    def __init__(self, id, name, **kwargs):
248248693Sgleb        self.id = id
249248693Sgleb        self.name = name
250248693Sgleb        self.attrs = kwargs
251248693Sgleb
252248693Sgleb    def __getattr__(self, attr):
253291036Srodrigc        if attr not in self.attrs:
254248693Sgleb            raise AttributeError('%s in %s' % (attr, str(self)))
255248693Sgleb        return self.attrs[attr]
256248693Sgleb
257248693Sgleb    def _name_opt(self, default=''):
258248693Sgleb        if not self.name:
259248693Sgleb            return default
260248693Sgleb        return self.name
261248693Sgleb
262248693Sgleb    def _alias(self):
263248693Sgleb        if self._is_alias:
264248693Sgleb            return self.type._alias()
265248693Sgleb        return self
266248693Sgleb
267248693Sgleb    def __cmp__(self, other):
268248693Sgleb        # TODO assert 'self' and 'other' belong to different libraries
269248693Sgleb        #print 'cmp defs: %s, %s' % (self, other)
270248693Sgleb        a = self._alias()
271248693Sgleb        try:
272248693Sgleb            b = other._alias()
273248693Sgleb        except AttributeError:
274248693Sgleb            return 1
275248693Sgleb        r = cmp(a.__class__, b.__class__)
276248693Sgleb        if r == 0:
277248693Sgleb            if a.id != 0 and b.id != 0:
278248693Sgleb                ind = (long(a.id) << 32) + b.id
279248693Sgleb                r = Dwarf.cmpcache.get(ind)
280248693Sgleb                if r != None:
281248693Sgleb                    return r
282248693Sgleb            else:
283248693Sgleb                ind = 0
284248693Sgleb            r = cmp(a.attrs, b.attrs)
285248693Sgleb            if ind != 0:
286248693Sgleb                Dwarf.cmpcache.put(ind, r)
287248693Sgleb        else:
288248693Sgleb            r = 0
289248693Sgleb            #raise RuntimeError('Comparing different classes: %s, %s' %
290248693Sgleb            #        (a.__class__.__name__, b.__class__.__name__))
291248693Sgleb        return r
292248693Sgleb
293248693Sgleb    def __repr__(self):
294248693Sgleb        p = []
295248693Sgleb        if hasattr(self, 'name'):
296248693Sgleb            p.append("name=%s" % self.name)
297248693Sgleb        for (k, v) in self.attrs.items():
298248693Sgleb            if isinstance(v, Def):
299248693Sgleb                v = v.__class__.__name__ + '(...)'
300248693Sgleb            p.append("%s=%s" % (k, v))
301248693Sgleb        return self.__class__.__name__ + '(' + ', '.join(p) + ')'
302248693Sgleb
303248693Sgleb    def _mapval(self, param, vals):
304248693Sgleb        if param not in vals.keys():
305248693Sgleb            raise NotImplementedError("Invalid value '%s': %s" %
306248693Sgleb                    (param, str(self)))
307248693Sgleb        return vals[param]
308248693Sgleb
309248693Sgleb    def _pp_ex(self, pp):
310248693Sgleb        raise NotImplementedError('Extended pretty print not implemeted: %s' %
311248693Sgleb                str(self))
312248693Sgleb
313248693Sgleb    def _pp(self, pp):
314248693Sgleb        raise NotImplementedError('Pretty print not implemeted: %s' % str(self))
315248693Sgleb
316248693Sglebclass AnonymousDef(Def):
317248693Sgleb    def __init__(self, id, **kwargs):
318248693Sgleb        Def.__init__(self, id, None, **kwargs)
319248693Sgleb
320248693Sglebclass Void(AnonymousDef):
321248693Sgleb    _instance = None
322248693Sgleb
323248693Sgleb    def __new__(cls, *args, **kwargs):
324248693Sgleb        if not cls._instance:
325248693Sgleb            cls._instance = super(Void, cls).__new__(
326248693Sgleb                    cls, *args, **kwargs)
327248693Sgleb        return cls._instance
328248693Sgleb
329248693Sgleb    def __init__(self):
330248693Sgleb        AnonymousDef.__init__(self, 0)
331248693Sgleb
332248693Sgleb    def _pp(self, pp):
333248693Sgleb        return "void"
334248693Sgleb
335248693Sglebclass VarArgs(AnonymousDef):
336248693Sgleb    def _pp(self, pp):
337248693Sgleb        return "..."
338248693Sgleb
339248693Sglebclass PointerDef(AnonymousDef):
340248693Sgleb    def _pp(self, pp):
341248693Sgleb        t = pp.run(self.type)
342248693Sgleb        return "%s*" % (t,)
343248693Sgleb
344248693Sglebclass BaseTypeDef(Def):
345248693Sgleb    inttypes = ['DW_ATE_signed', 'DW_ATE_unsigned', 'DW_ATE_unsigned_char']
346248693Sgleb    def _pp(self, pp):
347248693Sgleb        if self.encoding in self.inttypes:
348248693Sgleb            sign = '' if self.encoding == 'DW_ATE_signed' else 'u'
349275354Sgleb            bits = int(self.byte_size, 0) * 8
350248693Sgleb            return '%sint%s_t' % (sign, bits)
351275354Sgleb        elif self.encoding == 'DW_ATE_signed_char' and int(self.byte_size, 0) == 1:
352248693Sgleb            return 'char';
353275354Sgleb        elif self.encoding == 'DW_ATE_boolean' and int(self.byte_size, 0) == 1:
354275354Sgleb            return 'bool';
355248693Sgleb        elif self.encoding == 'DW_ATE_float':
356275354Sgleb            return self._mapval(int(self.byte_size, 0), {
357275354Sgleb                16: 'long double',
358275354Sgleb                8: 'double',
359275354Sgleb                4: 'float',
360248693Sgleb            })
361248693Sgleb        raise NotImplementedError('Invalid encoding: %s' % self)
362248693Sgleb
363248693Sglebclass TypeAliasDef(Def):
364248693Sgleb    _is_alias = True
365248693Sgleb    def _pp(self, pp):
366248693Sgleb        alias = self._alias()
367248693Sgleb        # push typedef name
368248693Sgleb        if self.name and not alias.name:
369248693Sgleb            alias.name = 'T(%s)' % self.name
370248693Sgleb        # return type with modifiers
371248693Sgleb        return self.type._pp(pp)
372248693Sgleb
373248693Sglebclass EnumerationTypeDef(Def):
374248693Sgleb    def _pp(self, pp):
375248693Sgleb        return 'enum ' + self._name_opt('UNKNOWN')
376248693Sgleb
377248693Sglebclass ConstTypeDef(AnonymousDef):
378248693Sgleb    _is_alias = True
379248693Sgleb    def _pp(self, pp):
380248693Sgleb        return 'const ' + self.type._pp(pp)
381248693Sgleb
382248693Sglebclass VolatileTypeDef(AnonymousDef):
383248693Sgleb    _is_alias = True
384248693Sgleb    def _pp(self, pp):
385248693Sgleb        return 'volatile ' + self.type._pp(pp)
386248693Sgleb
387275354Sglebclass RestrictTypeDef(AnonymousDef):
388275354Sgleb    _is_alias = True
389275354Sgleb    def _pp(self, pp):
390275354Sgleb        return 'restrict ' + self.type._pp(pp)
391275354Sgleb
392248693Sglebclass ArrayDef(AnonymousDef):
393248693Sgleb    def _pp(self, pp):
394248693Sgleb        t = pp.run(self.type)
395248693Sgleb        assert len(self.subranges) == 1
396248693Sgleb        try:
397248693Sgleb            sz = int(self.subranges[0].upper_bound) + 1
398248693Sgleb        except ValueError:
399248693Sgleb            s = re.sub(r'\(.+\)', '', self.subranges[0].upper_bound)
400248693Sgleb            sz = int(s) + 1
401248693Sgleb        return '%s[%s]' % (t, sz)
402248693Sgleb
403248693Sglebclass ArraySubrangeDef(AnonymousDef):
404248693Sgleb    pass
405248693Sgleb
406248693Sglebclass FunctionDef(Def):
407248693Sgleb    def _pp(self, pp):
408248693Sgleb        result = pp.run(self.result)
409248693Sgleb        if not self.params:
410248693Sgleb            params = "void"
411248693Sgleb        else:
412248693Sgleb            params = ', '.join([ pp.run(x) for x in self.params ])
413248693Sgleb        return "%s %s(%s);" % (result, self.name, params)
414248693Sgleb
415248693Sglebclass FunctionTypeDef(Def):
416248693Sgleb    def _pp(self, pp):
417248693Sgleb        result = pp.run(self.result)
418248693Sgleb        if not self.params:
419248693Sgleb            params = "void"
420248693Sgleb        else:
421248693Sgleb            params = ', '.join([ pp.run(x) for x in self.params ])
422248693Sgleb        return "F(%s, %s, (%s))" % (self._name_opt(), result, params)
423248693Sgleb
424248693Sglebclass ParameterDef(Def):
425248693Sgleb    def _pp(self, pp):
426248693Sgleb        t = pp.run(self.type)
427248693Sgleb        return "%s %s" % (t, self._name_opt())
428248693Sgleb
429275354Sglebclass VariableDef(Def):
430275354Sgleb    def _pp(self, pp):
431275354Sgleb        t = pp.run(self.type)
432275354Sgleb        return "%s %s" % (t, self._name_opt())
433275354Sgleb
434248693Sgleb# TODO
435248693Sglebclass StructForwardDef(Def):
436248693Sgleb    pass
437248693Sgleb
438248693Sglebclass IncompleteDef(Def):
439248693Sgleb    def update(self, complete, cache=None):
440248693Sgleb        self.complete = complete
441248693Sgleb        complete.incomplete = self
442248693Sgleb        if cache != None:
443248693Sgleb            cached = cache.get(self.id)
444248693Sgleb            if cached != None and isinstance(cached, IncompleteDef):
445248693Sgleb                cache.replace(self.id, complete)
446248693Sgleb
447248693Sglebclass StructIncompleteDef(IncompleteDef):
448248693Sgleb    def _pp(self, pp):
449248693Sgleb        return "struct %s" % (self.name,)
450248693Sgleb
451248693Sglebclass UnionIncompleteDef(IncompleteDef):
452248693Sgleb    def _pp(self, pp):
453248693Sgleb        return "union %s" % (self.name,)
454248693Sgleb
455248693Sglebclass StructDef(Def):
456248693Sgleb    def _pp_ex(self, pp, suffix=';'):
457248693Sgleb        members = [ pp.run(x) for x in self.members ]
458248693Sgleb        return "struct %s { %s }%s" % \
459248693Sgleb                (self._name_opt(), ' '.join(members), suffix)
460248693Sgleb    def _pp(self, pp):
461248693Sgleb        if self.name:
462248693Sgleb            pp.run_nested(self)
463248693Sgleb            return "struct %s" % (self.name,)
464248693Sgleb        else:
465248693Sgleb            return self._pp_ex(pp, suffix='')
466248693Sgleb
467248693Sglebclass UnionDef(Def):
468248693Sgleb    def _pp_ex(self, pp, suffix=';'):
469248693Sgleb        members = [ pp.run(x) for x in self.members ]
470248693Sgleb        return "union %s { %s }%s" % \
471248693Sgleb                (self._name_opt(), ' '.join(members), suffix)
472248693Sgleb    def _pp(self, pp):
473248693Sgleb        if self.name:
474248693Sgleb            pp.run_nested(self)
475248693Sgleb            return "union %s" % (self.name,)
476248693Sgleb        else:
477248693Sgleb            return self._pp_ex(pp, suffix='')
478248693Sgleb
479248693Sglebclass MemberDef(Def):
480248693Sgleb    def _pp(self, pp):
481248693Sgleb        t = pp.run(self.type)
482248693Sgleb        if self.bit_size:
483248693Sgleb            bits = ":%s" % self.bit_size
484248693Sgleb        else:
485248693Sgleb            bits = ""
486248693Sgleb        return "%s %s%s;" % (t, self._name_opt(), bits)
487248693Sgleb
488248693Sglebclass Dwarf(object):
489248693Sgleb
490248693Sgleb    cmpcache = Cache(enabled=Config.cmpcache_enabled)
491248693Sgleb
492248693Sgleb    def __init__(self, dump):
493248693Sgleb        self.dump = dump
494248693Sgleb
495248693Sgleb    def _build_optarg_type(self, praw):
496248693Sgleb        type = praw.optarg('type', Void())
497248693Sgleb        if type != Void():
498248693Sgleb            type = self.buildref(praw.unit, type)
499248693Sgleb        return type
500248693Sgleb
501248693Sgleb    def build_subprogram(self, raw):
502248693Sgleb        if raw.optname == None:
503248693Sgleb            raw.setname('SUBPROGRAM_NONAME_' + raw.arg('low_pc'));
504248693Sgleb        params = [ self.build(x) for x in raw.nested ]
505248693Sgleb        result = self._build_optarg_type(raw)
506248693Sgleb        return FunctionDef(raw.id, raw.name, params=params, result=result)
507248693Sgleb
508275354Sgleb    def build_variable(self, raw):
509275354Sgleb        type = self._build_optarg_type(raw)
510275354Sgleb        return VariableDef(raw.id, raw.optname, type=type)
511275354Sgleb
512248693Sgleb    def build_subroutine_type(self, raw):
513248693Sgleb        params = [ self.build(x) for x in raw.nested ]
514248693Sgleb        result = self._build_optarg_type(raw)
515248693Sgleb        return FunctionTypeDef(raw.id, raw.optname, params=params, result=result)
516248693Sgleb
517248693Sgleb    def build_formal_parameter(self, raw):
518248693Sgleb        type = self._build_optarg_type(raw)
519248693Sgleb        return ParameterDef(raw.id, raw.optname, type=type)
520248693Sgleb
521248693Sgleb    def build_pointer_type(self, raw):
522248693Sgleb        type = self._build_optarg_type(raw)
523248693Sgleb        return PointerDef(raw.id, type=type)
524248693Sgleb
525248693Sgleb    def build_member(self, raw):
526248693Sgleb        type = self.buildref(raw.unit, raw.arg('type'))
527248693Sgleb        return MemberDef(raw.id, raw.name, type=type,
528248693Sgleb                bit_size=raw.optarg('bit_size', None))
529248693Sgleb
530248693Sgleb    def build_structure_type(self, raw):
531248693Sgleb        incomplete = raw.unit.incomplete.get(raw.id)
532248693Sgleb        if incomplete == None:
533248693Sgleb            incomplete = StructIncompleteDef(raw.id, raw.optname)
534248693Sgleb            raw.unit.incomplete.put(raw.id, incomplete)
535248693Sgleb        else:
536248693Sgleb            return incomplete
537248693Sgleb        members = [ self.build(x) for x in raw.nested ]
538248693Sgleb        byte_size = raw.optarg('byte_size', None)
539248693Sgleb        if byte_size == None:
540248693Sgleb            obj = StructForwardDef(raw.id, raw.name, members=members,
541248693Sgleb                    forcename=raw.name)
542248693Sgleb        obj = StructDef(raw.id, raw.optname, members=members,
543248693Sgleb                byte_size=byte_size)
544248693Sgleb        incomplete.update(obj, cache=raw.unit.cache)
545248693Sgleb        return obj
546248693Sgleb
547248693Sgleb    def build_union_type(self, raw):
548248693Sgleb        incomplete = raw.unit.incomplete.get(raw.id)
549248693Sgleb        if incomplete == None:
550248693Sgleb            incomplete = UnionIncompleteDef(raw.id, raw.optname)
551248693Sgleb            raw.unit.incomplete.put(raw.id, incomplete)
552248693Sgleb        else:
553248693Sgleb            return incomplete
554248693Sgleb        members = [ self.build(x) for x in raw.nested ]
555248693Sgleb        byte_size = raw.optarg('byte_size', None)
556248693Sgleb        obj = UnionDef(raw.id, raw.optname, members=members,
557248693Sgleb                byte_size=byte_size)
558248693Sgleb        obj.incomplete = incomplete
559248693Sgleb        incomplete.complete = obj
560248693Sgleb        return obj
561248693Sgleb
562248693Sgleb    def build_typedef(self, raw):
563248693Sgleb        type = self._build_optarg_type(raw)
564248693Sgleb        return TypeAliasDef(raw.id, raw.name, type=type)
565248693Sgleb
566248693Sgleb    def build_const_type(self, raw):
567248693Sgleb        type = self._build_optarg_type(raw)
568248693Sgleb        return ConstTypeDef(raw.id, type=type)
569248693Sgleb
570248693Sgleb    def build_volatile_type(self, raw):
571248693Sgleb        type = self._build_optarg_type(raw)
572248693Sgleb        return VolatileTypeDef(raw.id, type=type)
573248693Sgleb
574275354Sgleb    def build_restrict_type(self, raw):
575275354Sgleb        type = self._build_optarg_type(raw)
576275354Sgleb        return RestrictTypeDef(raw.id, type=type)
577275354Sgleb
578248693Sgleb    def build_enumeration_type(self, raw):
579248693Sgleb        # TODO handle DW_TAG_enumerator ???
580248693Sgleb        return EnumerationTypeDef(raw.id, name=raw.optname,
581248693Sgleb                byte_size=raw.arg('byte_size'))
582248693Sgleb
583248693Sgleb    def build_base_type(self, raw):
584248693Sgleb        return BaseTypeDef(raw.id, raw.optname,
585248693Sgleb                byte_size=raw.arg('byte_size'), encoding=raw.arg('encoding'))
586248693Sgleb
587248693Sgleb    def build_array_type(self, raw):
588248693Sgleb        type = self.buildref(raw.unit, raw.arg('type'))
589248693Sgleb        subranges = [ self.build(x) for x in raw.nested ]
590248693Sgleb        return ArrayDef(raw.id, type=type, subranges=subranges)
591248693Sgleb
592248693Sgleb    def build_subrange_type(self, raw):
593248693Sgleb        type = self.buildref(raw.unit, raw.arg('type'))
594248693Sgleb        return ArraySubrangeDef(raw.id, type=type,
595248693Sgleb                upper_bound=raw.optarg('upper_bound', 0))
596248693Sgleb
597248693Sgleb    def build_unspecified_parameters(self, raw):
598248693Sgleb        return VarArgs(raw.id)
599248693Sgleb
600248693Sgleb    def _get_id(self, id):
601248693Sgleb        try:
602248693Sgleb            return int(id)
603248693Sgleb        except ValueError:
604248693Sgleb            if (id.startswith('<') and id.endswith('>')):
605275354Sgleb                return int(id[1:-1], 0)
606248693Sgleb            else:
607248693Sgleb                raise ValueError("Invalid dwarf id: %s" % id)
608248693Sgleb
609248693Sgleb    def build(self, raw):
610248693Sgleb        obj = raw.unit.cache.get(raw.id)
611248693Sgleb        if obj != None:
612248693Sgleb            return obj
613248693Sgleb        builder_name = raw.tag.replace('DW_TAG_', 'build_')
614248693Sgleb        try:
615248693Sgleb            builder = getattr(self, builder_name)
616248693Sgleb        except AttributeError:
617248693Sgleb            raise AttributeError("Unknown dwarf tag: %s" % raw)
618248693Sgleb        obj = builder(raw)
619248693Sgleb        raw.unit.cache.put(obj.id, obj)
620248693Sgleb        return obj
621248693Sgleb
622248693Sgleb    def buildref(self, unit, id):
623248693Sgleb        id = self._get_id(id)
624248693Sgleb        raw = unit.tags[id]
625248693Sgleb        obj = self.build(raw)
626248693Sgleb        return obj
627248693Sgleb
628248693Sgleb# }}}
629248693Sgleb
630248693Sglebclass Shlib(object):
631248693Sgleb    def __init__(self, libfile):
632248693Sgleb        self.libfile = libfile
633248693Sgleb        self.versions = {}
634248693Sgleb        self.alias_syms = {}
635248693Sgleb
636248693Sgleb    def parse_objdump(self):
637248693Sgleb        objdump = ObjdumpParser(self.libfile)
638248693Sgleb        objdump.run()
639248693Sgleb        for p in objdump.dynamic_symbols:
640248693Sgleb            vername = p['ver']
641248693Sgleb            if vername.startswith('(') and vername.endswith(')'):
642248693Sgleb                vername = vername[1:-1]
643248693Sgleb            if not Config.version_filter.match(vername):
644248693Sgleb                continue
645248693Sgleb            if not Config.symbol_filter.match(p['symbol']):
646248693Sgleb                continue
647248693Sgleb            sym = Symbol(p['symbol'], p['offset'], vername, self)
648291036Srodrigc            if vername not in self.versions:
649248693Sgleb                self.versions[vername] = VersionMap(vername)
650248693Sgleb            self.versions[vername].append(sym)
651248693Sgleb        if Config.alias_prefixes:
652248693Sgleb            self.local_offsetmap = objdump.local_offsetmap
653248693Sgleb            for p in objdump.local_symbols:
654248693Sgleb                for prefix in Config.alias_prefixes:
655248693Sgleb                    if not p['symbol'].startswith(prefix):
656248693Sgleb                        continue
657248693Sgleb                    alias = SymbolAlias(p['symbol'], prefix, p['offset'])
658291036Srodrigc                    if alias.name in self.alias_syms:
659248693Sgleb                        prevalias = self.alias_syms[alias.name]
660248693Sgleb                        if alias.name != prevalias.name or \
661248693Sgleb                                alias.offset != prevalias.offset:
662248693Sgleb                            warn(Config.w_alias, "Symbol alias is " \
663248693Sgleb                                    "already defined: %s: %s at %08x -- %s at %08x" % \
664248693Sgleb                                    (alias.alias, alias.name,  alias.offset,
665248693Sgleb                                            prevalias.name, prevalias.offset))
666248693Sgleb                    self.alias_syms[alias.name] = alias
667248693Sgleb
668248693Sgleb    def parse_dwarfdump(self):
669248693Sgleb        dwarfdump = DwarfdumpParser(self.libfile)
670248693Sgleb        def lookup(sym):
671248693Sgleb            raw = None
672248693Sgleb            try:
673248693Sgleb                raw = dwarfdump.offsetmap[sym.offset]
674248693Sgleb            except:
675248693Sgleb                try:
676248693Sgleb                    localnames = self.local_offsetmap[sym.offset]
677248693Sgleb                    localnames.sort(key=lambda x: -len(x))
678248693Sgleb                    for localname in localnames:
679291036Srodrigc                        if localname not in self.alias_syms:
680248693Sgleb                            continue
681248693Sgleb                        alias = self.alias_syms[localname]
682248693Sgleb                        raw = dwarfdump.offsetmap[alias.offset]
683248693Sgleb                        break
684248693Sgleb                except:
685248693Sgleb                    pass
686248693Sgleb            return raw
687248693Sgleb        dwarfdump.run()
688248693Sgleb        dwarf = Dwarf(dwarfdump)
689248693Sgleb        for ver in self.versions.values():
690248693Sgleb            for sym in ver.symbols.values():
691248693Sgleb                raw = lookup(sym);
692248693Sgleb                if not raw:
693248693Sgleb                    warn(Config.w_symbol, "Symbol %s (%s) not found at offset 0x%x" % \
694248693Sgleb                            (sym.name_ver, self.libfile, sym.offset))
695248693Sgleb                    continue
696248693Sgleb                if Config.verbose >= 3:
697248693Sgleb                    print "Parsing symbol %s (%s)" % (sym.name_ver, self.libfile)
698248693Sgleb                sym.definition = dwarf.build(raw)
699248693Sgleb
700248693Sgleb    def parse(self):
701248693Sgleb        if not os.path.isfile(self.libfile):
702248693Sgleb            print >> sys.stderr, ("No such file: %s" % self.libfile)
703248693Sgleb            sys.exit(1)
704248693Sgleb        self.parse_objdump()
705248693Sgleb        self.parse_dwarfdump()
706248693Sgleb
707248693Sgleb# {{{ parsers
708248693Sgleb
709248693Sglebclass Parser(object):
710248693Sgleb    def __init__(self, proc):
711248693Sgleb        self.proc = proc
712248693Sgleb        self.parser = self.parse_begin
713248693Sgleb
714248693Sgleb    def run(self):
715248693Sgleb        fd = os.popen(self.proc, 'r')
716248693Sgleb        while True:
717248693Sgleb            line = fd.readline()
718248693Sgleb            if (not line):
719248693Sgleb                break
720248693Sgleb            line = line.strip()
721248693Sgleb            if (line):
722248693Sgleb                self.parser(line)
723248693Sgleb        err = fd.close()
724248693Sgleb        if err:
725248693Sgleb            print >> sys.stderr, ("Execution failed: %s" % self.proc)
726248693Sgleb            sys.exit(2)
727248693Sgleb
728248693Sgleb    def parse_begin(self, line):
729248693Sgleb        print(line)
730248693Sgleb
731248693Sglebclass ObjdumpParser(Parser):
732248693Sgleb
733248693Sgleb    re_header = re.compile('(?P<table>\w*)\s*SYMBOL TABLE:')
734248693Sgleb
735248693Sgleb    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]*)')
736248693Sgleb    re_lame_symbol = re.compile('(?P<offset>[0-9a-fA-F]+)\s+(?P<bind>\w+)\s+\*[A-Z]+\*')
737248693Sgleb
738248693Sgleb    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]*)')
739248693Sgleb
740248693Sgleb    def __init__(self, libfile):
741248693Sgleb        Parser.__init__(self, "%s -wtT %s" % (Config.objdump, libfile))
742248693Sgleb        self.dynamic_symbols = []
743248693Sgleb        self.local_symbols = []
744248693Sgleb        self.local_offsetmap = {}
745248693Sgleb
746248693Sgleb    def parse_begin(self, line):
747248693Sgleb        self.parse_header(line)
748248693Sgleb
749248693Sgleb    def add_symbol(self, table, symbol, offsetmap = None):
750248693Sgleb        offset = int(symbol['offset'], 16);
751248693Sgleb        symbol['offset'] = offset
752248693Sgleb        if (offset == 0):
753248693Sgleb            return
754248693Sgleb        table.append(symbol)
755248693Sgleb        if offsetmap != None:
756291036Srodrigc            if offset not in offsetmap:
757248693Sgleb                offsetmap[offset] = [symbol['symbol']]
758248693Sgleb            else:
759248693Sgleb                offsetmap[offset].append(symbol['symbol'])
760248693Sgleb
761248693Sgleb    def parse_header(self, line):
762248693Sgleb        m = self.re_header.match(line)
763248693Sgleb        if (m):
764248693Sgleb            table = m.group('table')
765248693Sgleb            if (table == "DYNAMIC"):
766248693Sgleb                self.parser = self.parse_dynamic
767248693Sgleb            elif table == '':
768248693Sgleb                self.parser = self.parse_local
769248693Sgleb            else:
770248693Sgleb                raise ValueError("Invalid symbol table: %s" % table)
771248693Sgleb            return True
772248693Sgleb        return False
773248693Sgleb
774248693Sgleb    def parse_local(self, line):
775248693Sgleb        if (self.parse_header(line)):
776248693Sgleb            return
777248693Sgleb        if (self.re_lame_symbol.match(line)):
778248693Sgleb            return
779248693Sgleb        m = self.re_local_symbol.match(line)
780248693Sgleb        if (not m):
781248693Sgleb            return
782248693Sgleb            #raise ValueError("Invalid symbol definition: %s" % line)
783248693Sgleb        p = m.groupdict()
784248693Sgleb        if (p['symbol'] and p['symbol'].find('@') == -1):
785248693Sgleb            self.add_symbol(self.local_symbols, p, self.local_offsetmap);
786248693Sgleb
787248693Sgleb    def parse_dynamic(self, line):
788248693Sgleb        if (self.parse_header(line)):
789248693Sgleb            return
790248693Sgleb        if (self.re_lame_symbol.match(line)):
791248693Sgleb            return
792248693Sgleb        m = self.re_dynamic_symbol.match(line)
793248693Sgleb        if (not m):
794248693Sgleb            raise ValueError("Invalid symbol definition: %s" % line)
795248693Sgleb        p = m.groupdict()
796248693Sgleb        if (p['symbol'] and p['ver']):
797248693Sgleb            self.add_symbol(self.dynamic_symbols, p);
798248693Sgleb
799248693Sglebclass DwarfdumpParser(Parser):
800248693Sgleb
801248693Sgleb    tagcache_stats = Cache.CacheStats()
802248693Sgleb
803248693Sgleb    class Unit(object):
804248693Sgleb        def __init__(self):
805248693Sgleb            self.cache = Cache(enabled=Config.dwarfcache_enabled,
806248693Sgleb                    stats=DwarfdumpParser.tagcache_stats)
807248693Sgleb            self.incomplete = Cache()
808248693Sgleb            self.tags = {}
809248693Sgleb
810248693Sgleb    class Tag(object):
811248693Sgleb        def __init__(self, unit, data):
812248693Sgleb            self.unit = unit
813275354Sgleb            self.id = int(data['id'], 0)
814248693Sgleb            self.level = int(data['level'])
815248693Sgleb            self.tag = data['tag']
816248693Sgleb            self.args = {}
817248693Sgleb            self.nested = []
818248693Sgleb
819248693Sgleb        @property
820248693Sgleb        def name(self):
821248693Sgleb            return self.arg('name')
822248693Sgleb
823248693Sgleb        @property
824248693Sgleb        def optname(self):
825248693Sgleb            return self.optarg('name', None)
826248693Sgleb
827248693Sgleb        def setname(self, name):
828248693Sgleb            self.args['DW_AT_name'] = name
829248693Sgleb
830248693Sgleb        def arg(self, a):
831248693Sgleb            name = 'DW_AT_' + a
832248693Sgleb            try:
833248693Sgleb                return self.args[name]
834248693Sgleb            except KeyError:
835248693Sgleb                raise KeyError("Argument '%s' not found in %s: %s" %
836248693Sgleb                        (name, self, self.args))
837248693Sgleb
838248693Sgleb        def optarg(self, a, default):
839248693Sgleb            try:
840248693Sgleb                return self.arg(a)
841248693Sgleb            except KeyError:
842248693Sgleb                return default
843248693Sgleb
844248693Sgleb        def __repr__(self):
845248693Sgleb            return "Tag(%d, %d, %s)" % (self.level, self.id, self.tag)
846248693Sgleb
847275354Sgleb    re_header = re.compile('<(?P<level>\d+)><(?P<id>[0xX0-9a-fA-F]+(?:\+(0[xX])?[0-9a-fA-F]+)?)><(?P<tag>\w+)>')
848248693Sgleb    re_argname = re.compile('(?P<arg>\w+)<')
849248693Sgleb    re_argunknown = re.compile('<Unknown AT value \w+><[^<>]+>')
850248693Sgleb
851248693Sgleb    skip_tags = set([
852248693Sgleb        'DW_TAG_lexical_block',
853248693Sgleb        'DW_TAG_inlined_subroutine',
854248693Sgleb        'DW_TAG_label',
855248693Sgleb        'DW_TAG_variable',
856248693Sgleb        ])
857248693Sgleb
858275354Sgleb    external_tags = set([
859275354Sgleb        'DW_TAG_variable',
860275354Sgleb        ])
861275354Sgleb
862248693Sgleb    def __init__(self, libfile):
863248693Sgleb        Parser.__init__(self, "%s -di %s" % (Config.dwarfdump, libfile))
864248693Sgleb        self.current_unit = None
865248693Sgleb        self.offsetmap = {}
866248693Sgleb        self.stack = []
867248693Sgleb
868248693Sgleb    def parse_begin(self, line):
869248693Sgleb        if line == '.debug_info':
870248693Sgleb            self.parser = self.parse_debuginfo
871248693Sgleb        else:
872248693Sgleb            raise ValueError("Invalid dwarfdump header: %s" % line)
873248693Sgleb
874248693Sgleb    def parse_argvalue(self, args):
875248693Sgleb        assert args.startswith('<')
876248693Sgleb        i = 1
877248693Sgleb        cnt = 1
878248693Sgleb        while i < len(args) and args[i]:
879248693Sgleb            if args[i] == '<':
880248693Sgleb                cnt += 1
881248693Sgleb            elif args[i] == '>':
882248693Sgleb                cnt -= 1
883248693Sgleb                if cnt == 0:
884248693Sgleb                    break
885248693Sgleb            i = i + 1
886248693Sgleb        value = args[1:i]
887248693Sgleb        args = args[i+1:]
888248693Sgleb        return (args, value)
889248693Sgleb
890248693Sgleb    def parse_arg(self, tag, args):
891248693Sgleb        m = self.re_argname.match(args)
892248693Sgleb        if not m:
893248693Sgleb            m = self.re_argunknown.match(args)
894248693Sgleb            if not m:
895248693Sgleb                raise ValueError("Invalid dwarfdump: couldn't parse arguments: %s" %
896248693Sgleb                        args)
897248693Sgleb            args = args[len(m.group(0)):].lstrip()
898248693Sgleb            return args
899248693Sgleb        argname = m.group('arg')
900248693Sgleb        args = args[len(argname):]
901248693Sgleb        value = []
902248693Sgleb        while len(args) > 0 and args.startswith('<'):
903248693Sgleb            (args, v) = self.parse_argvalue(args)
904248693Sgleb            value.append(v)
905248693Sgleb        args = args.lstrip()
906248693Sgleb        if len(value) == 1:
907248693Sgleb            value = value[0]
908248693Sgleb        tag.args[argname] = value
909248693Sgleb        return args
910248693Sgleb
911248693Sgleb    def parse_debuginfo(self, line):
912248693Sgleb        m = self.re_header.match(line)
913248693Sgleb        if not m:
914248693Sgleb            raise ValueError("Invalid dwarfdump: %s" % line)
915248693Sgleb        if m.group('level') == '0':
916248693Sgleb            self.current_unit = DwarfdumpParser.Unit()
917248693Sgleb            return
918248693Sgleb        tag = DwarfdumpParser.Tag(self.current_unit, m.groupdict())
919248693Sgleb        args = line[len(m.group(0)):].lstrip()
920248693Sgleb        while args:
921248693Sgleb            args = self.parse_arg(tag, args)
922248693Sgleb        tag.unit.tags[tag.id] = tag
923275354Sgleb        def parse_offset(tag):
924291036Srodrigc            if 'DW_AT_low_pc' in tag.args:
925275354Sgleb                return int(tag.args['DW_AT_low_pc'], 16)
926291036Srodrigc            elif 'DW_AT_location' in tag.args:
927275354Sgleb                location = tag.args['DW_AT_location']
928275354Sgleb                if location.startswith('DW_OP_addr'):
929275354Sgleb                    return int(location.replace('DW_OP_addr', ''), 16)
930275354Sgleb            return None
931275354Sgleb        offset = parse_offset(tag)
932275354Sgleb        if offset is not None and \
933275354Sgleb                (tag.tag not in DwarfdumpParser.skip_tags or \
934291036Srodrigc                ('DW_AT_external' in tag.args and \
935275354Sgleb                tag.tag in DwarfdumpParser.external_tags)):
936291036Srodrigc            if offset in self.offsetmap:
937248693Sgleb                raise ValueError("Dwarf dump parse error: " +
938248693Sgleb                        "symbol is aleady defined at offset 0x%x" % offset)
939248693Sgleb            self.offsetmap[offset] = tag
940248693Sgleb        if len(self.stack) > 0:
941248693Sgleb            prev = self.stack.pop()
942248693Sgleb            while prev.level >= tag.level and len(self.stack) > 0:
943248693Sgleb                prev = self.stack.pop()
944248693Sgleb            if prev.level < tag.level:
945248693Sgleb                assert prev.level == tag.level - 1
946248693Sgleb                # TODO check DW_AT_sibling ???
947248693Sgleb                if tag.tag not in DwarfdumpParser.skip_tags:
948248693Sgleb                    prev.nested.append(tag)
949248693Sgleb                self.stack.append(prev)
950248693Sgleb        self.stack.append(tag)
951248693Sgleb        assert len(self.stack) == tag.level
952248693Sgleb
953248693Sgleb# }}}
954248693Sgleb
955248693Sglebdef list_str(l):
956248693Sgleb    l = [ str(x) for x in l ]
957248693Sgleb    l.sort()
958248693Sgleb    return ', '.join(l)
959248693Sgleb
960248693Sglebdef names_ver_str(vername, names):
961248693Sgleb    return list_str([ x + "@" + vername for x in names ])
962248693Sgleb
963248693Sglebdef common_symbols(origlib, newlib):
964248693Sgleb    result = []
965248693Sgleb    verdiff = ListDiff(origlib.versions.keys(), newlib.versions.keys())
966248693Sgleb    if Config.verbose >= 1:
967248693Sgleb        print 'Original versions:   ', list_str(verdiff.orig)
968248693Sgleb        print 'New versions:        ', list_str(verdiff.new)
969248693Sgleb    for vername in verdiff.added:
970248693Sgleb        print 'Added version:       ', vername
971248693Sgleb        print '    Added symbols:   ', \
972248693Sgleb                names_ver_str(vername, newlib.versions[vername].names())
973248693Sgleb    for vername in verdiff.removed:
974248693Sgleb        print 'Removed version:     ', vername
975248693Sgleb        print '    Removed symbols: ', \
976248693Sgleb                names_ver_str(vername, origlib.versions[vername].names())
977248693Sgleb    added = []
978248693Sgleb    removed = []
979248693Sgleb    for vername in verdiff.common:
980248693Sgleb        origver = origlib.versions[vername]
981248693Sgleb        newver = newlib.versions[vername]
982248693Sgleb        namediff = ListDiff(origver.names(), newver.names())
983248693Sgleb        if namediff.added:
984248693Sgleb            added.append(names_ver_str(vername, namediff.added))
985248693Sgleb        if namediff.removed:
986248693Sgleb            removed.append(names_ver_str(vername, namediff.removed))
987248693Sgleb        commonver = VersionMap(vername)
988248693Sgleb        result.append(commonver)
989248693Sgleb        for n in namediff.common:
990248693Sgleb            sym = CommonSymbol(origver.symbols[n], newver.symbols[n])
991248693Sgleb            commonver.append(sym)
992248693Sgleb    if added:
993248693Sgleb        print 'Added symbols:'
994248693Sgleb        for i in added:
995248693Sgleb            print '    ', i
996248693Sgleb    if removed:
997248693Sgleb        print 'Removed symbols:'
998248693Sgleb        for i in removed:
999248693Sgleb            print '    ', i
1000248693Sgleb    return result
1001248693Sgleb
1002248693Sglebdef cmp_symbols(commonver):
1003248693Sgleb    for ver in commonver:
1004248693Sgleb        names = ver.names();
1005248693Sgleb        names.sort()
1006248693Sgleb        for symname in names:
1007248693Sgleb            sym = ver.symbols[symname]
1008275354Sgleb            missing = sym.origsym.definition is None or sym.newsym.definition is None
1009275354Sgleb            match = not missing and sym.origsym.definition == sym.newsym.definition
1010248693Sgleb            if not match:
1011248693Sgleb                App.result_code = 1
1012248693Sgleb            if Config.verbose >= 1 or not match:
1013275354Sgleb                if missing:
1014275354Sgleb                    print '%s: missing definition' % \
1015275354Sgleb                            (sym.origsym.name_ver,)
1016275354Sgleb                    continue
1017248693Sgleb                print '%s: definitions %smatch' % \
1018248693Sgleb                        (sym.origsym.name_ver, "" if match else "mis")
1019248693Sgleb                if Config.dump or (not match and not Config.no_dump):
1020248693Sgleb                    for x in [(sym.origsym, Config.origfile),
1021248693Sgleb                            (sym.newsym, Config.newfile)]:
1022248693Sgleb                        xsym = x[0]
1023248693Sgleb                        xout = x[1].out
1024248693Sgleb                        if not xsym.definition:
1025248693Sgleb                            print >> xout, '\n// Definition not found: %s %s' % \
1026248693Sgleb                                    (xsym.name_ver, xsym.lib.libfile)
1027248693Sgleb                            continue
1028248693Sgleb                        print >> xout, '\n// Definitions mismatch: %s %s' % \
1029248693Sgleb                                (xsym.name_ver, xsym.lib.libfile)
1030248693Sgleb                        pp = PrettyPrinter()
1031248693Sgleb                        pp.run(xsym.definition)
1032248693Sgleb                        for i in pp.nested():
1033248693Sgleb                            print >> xout, i
1034248693Sgleb                        print >> xout, pp.result()
1035248693Sgleb
1036248693Sglebdef dump_symbols(commonver):
1037248693Sgleb    class SymbolDump(object):
1038248693Sgleb        def __init__(self, io_conf):
1039248693Sgleb            self.io_conf = io_conf
1040248693Sgleb            self.pp = PrettyPrinter()
1041248693Sgleb            self.res = []
1042248693Sgleb        def run(self, sym):
1043248693Sgleb            r = self.pp.run(sym.definition)
1044248693Sgleb            self.res.append('/* %s@%s */ %s' % (sym.name, sym.version, r))
1045248693Sgleb        def finish(self):
1046248693Sgleb            print >> self.io_conf.out, '\n// Symbol dump: version %s, library %s' % \
1047248693Sgleb                    (ver.name, self.io_conf.filename)
1048248693Sgleb            for i in self.pp.nested():
1049248693Sgleb                print >> self.io_conf.out, i
1050248693Sgleb            print >> self.io_conf.out, ''
1051248693Sgleb            for i in self.res:
1052248693Sgleb                print >> self.io_conf.out, i
1053248693Sgleb    for ver in commonver:
1054248693Sgleb        names = sorted(ver.names());
1055248693Sgleb        d_orig = SymbolDump(Config.origfile)
1056248693Sgleb        d_new = SymbolDump(Config.newfile)
1057248693Sgleb        for symname in names:
1058248693Sgleb            sym = ver.symbols[symname]
1059248693Sgleb            if not sym.origsym.definition or not sym.newsym.definition:
1060248693Sgleb                # XXX
1061248693Sgleb                warn(Config.w_symbol, 'Missing symbol definition: %s@%s' % \
1062248693Sgleb                        (symname, ver.name))
1063248693Sgleb                continue
1064248693Sgleb            d_orig.run(sym.origsym)
1065248693Sgleb            d_new.run(sym.newsym)
1066248693Sgleb        d_orig.finish()
1067248693Sgleb        d_new.finish()
1068248693Sgleb
1069248693Sglebif __name__ == '__main__':
1070248693Sgleb    Config.init()
1071248693Sgleb    parser = optparse.OptionParser(usage="usage: %prog origlib newlib",
1072248693Sgleb            version="%prog " + Config.version)
1073248693Sgleb    parser.add_option('-v', '--verbose', action='count',
1074248693Sgleb            help="verbose mode, may be specified several times")
1075248693Sgleb    parser.add_option('--alias-prefix', action='append',
1076248693Sgleb            help="name prefix to try for symbol alias lookup", metavar="STR")
1077248693Sgleb    parser.add_option('--dump', action='store_true',
1078248693Sgleb            help="dump symbol definitions")
1079248693Sgleb    parser.add_option('--no-dump', action='store_true',
1080248693Sgleb            help="disable dump for mismatched symbols")
1081248693Sgleb    parser.add_option('--out-orig', action='store',
1082248693Sgleb            help="result output file for original library", metavar="ORIGFILE")
1083248693Sgleb    parser.add_option('--out-new', action='store',
1084248693Sgleb            help="result output file for new library", metavar="NEWFILE")
1085275354Sgleb    parser.add_option('--dwarfdump', action='store',
1086275354Sgleb            help="path to dwarfdump executable", metavar="DWARFDUMP")
1087275354Sgleb    parser.add_option('--objdump', action='store',
1088275354Sgleb            help="path to objdump executable", metavar="OBJDUMP")
1089248693Sgleb    parser.add_option('--exclude-ver', action='append', metavar="RE")
1090248693Sgleb    parser.add_option('--include-ver', action='append', metavar="RE")
1091248693Sgleb    parser.add_option('--exclude-sym', action='append', metavar="RE")
1092248693Sgleb    parser.add_option('--include-sym', action='append', metavar="RE")
1093275354Sgleb    parser.add_option('--no-exclude-sym-default', action='store_true',
1094275354Sgleb            help="don't exclude special symbols like _init, _end, __bss_start")
1095248693Sgleb    for opt in ['alias', 'cached', 'symbol']:
1096248693Sgleb        parser.add_option("--w-" + opt,
1097248693Sgleb                action="store_true", dest="w_" + opt)
1098248693Sgleb        parser.add_option("--w-no-" + opt,
1099248693Sgleb                action="store_false", dest="w_" + opt)
1100248693Sgleb    (opts, args) = parser.parse_args()
1101248693Sgleb
1102248693Sgleb    if len(args) != 2:
1103248693Sgleb        parser.print_help()
1104248693Sgleb        sys.exit(-1)
1105275354Sgleb    if opts.dwarfdump:
1106275354Sgleb        Config.dwarfdump = opts.dwarfdump
1107275354Sgleb    if opts.objdump:
1108275354Sgleb        Config.objdump = opts.objdump
1109248693Sgleb    if opts.out_orig:
1110248693Sgleb        Config.origfile.init(opts.out_orig)
1111248693Sgleb    if opts.out_new:
1112248693Sgleb        Config.newfile.init(opts.out_new)
1113248693Sgleb    if opts.no_dump:
1114248693Sgleb        Config.dump = False
1115248693Sgleb        Config.no_dump = True
1116248693Sgleb    if opts.dump:
1117248693Sgleb        Config.dump = True
1118248693Sgleb        Config.no_dump = False
1119248693Sgleb        Config.verbose = 1
1120248693Sgleb    if opts.verbose:
1121248693Sgleb        Config.verbose = opts.verbose
1122248693Sgleb    if opts.alias_prefix:
1123248693Sgleb        Config.alias_prefixes = opts.alias_prefix
1124248693Sgleb        Config.alias_prefixes.sort(key=lambda x: -len(x))
1125248693Sgleb    for (k, v) in ({ '_sym': Config.symbol_filter,
1126248693Sgleb            '_ver': Config.version_filter }).items():
1127248693Sgleb        for a in [ 'exclude', 'include' ]:
1128248693Sgleb            opt = getattr(opts, a + k)
1129248693Sgleb            if opt:
1130248693Sgleb                getattr(v, a).extend(opt)
1131275354Sgleb    if not opts.no_exclude_sym_default:
1132275354Sgleb        Config.symbol_filter.exclude.extend(Config.exclude_sym_default)
1133248693Sgleb    Config.version_filter.compile()
1134248693Sgleb    Config.symbol_filter.compile()
1135248693Sgleb    for w in ['w_alias', 'w_cached', 'w_symbol']:
1136248693Sgleb        if hasattr(opts, w):
1137248693Sgleb            v = getattr(opts, w)
1138248693Sgleb            if v != None:
1139248693Sgleb                setattr(Config, w, v)
1140248693Sgleb
1141248693Sgleb    (Config.origfile.filename, Config.newfile.filename) = (args[0], args[1])
1142248693Sgleb
1143248693Sgleb    origlib = Shlib(Config.origfile.filename)
1144248693Sgleb    origlib.parse()
1145248693Sgleb    newlib = Shlib(Config.newfile.filename)
1146248693Sgleb    newlib.parse()
1147248693Sgleb
1148248693Sgleb    commonver = common_symbols(origlib, newlib)
1149248693Sgleb    if Config.dump:
1150248693Sgleb        dump_symbols(commonver)
1151248693Sgleb    cmp_symbols(commonver)
1152248693Sgleb    if Config.verbose >= 4:
1153248693Sgleb        print Dwarf.cmpcache.stats.show('Cmp')
1154248693Sgleb        print DwarfdumpParser.tagcache_stats.show('Dwarf tag')
1155248693Sgleb
1156248693Sgleb    sys.exit(App.result_code)
1157