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$
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
63248693Sgleb    @classmethod
64248693Sgleb    def init(cls):
65248693Sgleb        cls.version_filter = StrFilter()
66248693Sgleb        cls.symbol_filter = StrFilter()
67248693Sgleb
68248693Sglebclass App(object):
69248693Sgleb    result_code = 0
70248693Sgleb
71248693Sglebdef warn(cond, msg):
72248693Sgleb    if cond:
73248693Sgleb        print >> sys.stderr, "WARN: " + msg
74248693Sgleb
75248693Sgleb# {{{ misc
76248693Sgleb
77248693Sglebclass StrFilter(object):
78248693Sgleb    def __init__(self):
79248693Sgleb        self.exclude = []
80248693Sgleb        self.include = []
81248693Sgleb
82248693Sgleb    def compile(self):
83248693Sgleb        self.re_exclude = [ re.compile(x) for x in self.exclude ]
84248693Sgleb        self.re_include = [ re.compile(x) for x in self.include ]
85248693Sgleb
86248693Sgleb    def match(self, s):
87248693Sgleb        if len(self.re_include):
88248693Sgleb            matched = False
89248693Sgleb            for r in self.re_include:
90248693Sgleb                if r.match(s):
91248693Sgleb                    matched = True
92248693Sgleb                    break
93248693Sgleb            if not matched:
94248693Sgleb                return False
95248693Sgleb        for r in self.re_exclude:
96248693Sgleb            if r.match(s):
97248693Sgleb                return False
98248693Sgleb        return True
99248693Sgleb
100248693Sglebclass Cache(object):
101248693Sgleb
102248693Sgleb    class CacheStats(object):
103248693Sgleb        def __init__(self):
104248693Sgleb            self.hit = 0
105248693Sgleb            self.miss = 0
106248693Sgleb
107248693Sgleb        def show(self, name):
108248693Sgleb            total = self.hit + self.miss
109248693Sgleb            if total == 0:
110248693Sgleb                ratio = '(undef)'
111248693Sgleb            else:
112248693Sgleb                ratio = '%f' % (self.hit/float(total))
113248693Sgleb            return '%s cache stats: hit: %d; miss: %d; ratio: %s' % \
114248693Sgleb                    (name, self.hit, self.miss, ratio)
115248693Sgleb
116248693Sgleb    def __init__(self, enabled=True, stats=None):
117248693Sgleb        self.enabled = enabled
118248693Sgleb        self.items = {}
119248693Sgleb        if stats == None:
120248693Sgleb            self.stats = Cache.CacheStats()
121248693Sgleb        else:
122248693Sgleb            self.stats = stats
123248693Sgleb
124248693Sgleb    def get(self, id):
125248693Sgleb        if self.enabled and self.items.has_key(id):
126248693Sgleb            self.stats.hit += 1
127248693Sgleb            return self.items[id]
128248693Sgleb        else:
129248693Sgleb            self.stats.miss += 1
130248693Sgleb            return None
131248693Sgleb
132248693Sgleb    def put(self, id, obj):
133248693Sgleb        if self.enabled:
134248693Sgleb            if self.items.has_key(id) and obj is not self.items[id]:
135248693Sgleb                #raise ValueError("Item is already cached: %d (%s, %s)" %
136248693Sgleb                #        (id, self.items[id], obj))
137248693Sgleb                warn(Config.w_cached, "Item is already cached: %d (%s, %s)" % \
138248693Sgleb                        (id, self.items[id], obj))
139248693Sgleb            self.items[id] = obj
140248693Sgleb
141248693Sgleb    def replace(self, id, obj):
142248693Sgleb        if self.enabled:
143248693Sgleb            assert self.items.has_key(id)
144248693Sgleb            self.items[id] = obj
145248693Sgleb
146248693Sglebclass ListDiff(object):
147248693Sgleb    def __init__(self, orig, new):
148248693Sgleb        self.orig = set(orig)
149248693Sgleb        self.new = set(new)
150248693Sgleb        self.common = self.orig & self.new
151248693Sgleb        self.added = self.new - self.common
152248693Sgleb        self.removed = self.orig - self.common
153248693Sgleb
154248693Sglebclass PrettyPrinter(object):
155248693Sgleb    def __init__(self):
156248693Sgleb        self.stack = []
157248693Sgleb
158248693Sgleb    def run_nested(self, obj):
159248693Sgleb        ex = obj._pp_ex(self)
160248693Sgleb        self.stack.append(ex)
161248693Sgleb
162248693Sgleb    def run(self, obj):
163248693Sgleb        self._result = obj._pp(self)
164248693Sgleb        return self._result
165248693Sgleb
166248693Sgleb    def nested(self):
167248693Sgleb        return sorted(set(self.stack))
168248693Sgleb
169248693Sgleb    def result(self):
170248693Sgleb        return self._result;
171248693Sgleb
172248693Sgleb# }}}
173248693Sgleb
174248693Sgleb#{{{ symbols and version maps
175248693Sgleb
176248693Sglebclass Symbol(object):
177248693Sgleb    def __init__(self, name, offset, version, lib):
178248693Sgleb        self.name = name
179248693Sgleb        self.offset = offset
180248693Sgleb        self.version = version
181248693Sgleb        self.lib = lib
182248693Sgleb        self.definition = None
183248693Sgleb
184248693Sgleb    @property
185248693Sgleb    def name_ver(self):
186248693Sgleb        return self.name + '@' + self.version
187248693Sgleb
188248693Sgleb    def __repr__(self):
189248693Sgleb        return "Symbol(%s, 0x%x, %s)" % (self.name, self.offset, self.version)
190248693Sgleb
191248693Sglebclass CommonSymbol(object):
192248693Sgleb    def __init__(self, origsym, newsym):
193248693Sgleb        if origsym.name != newsym.name or origsym.version != newsym.version:
194248693Sgleb            raise RuntimeError("Symbols have different names: %s",
195248693Sgleb                    [origsym, newsym])
196248693Sgleb        self.origsym = origsym
197248693Sgleb        self.newsym = newsym
198248693Sgleb        self.name = newsym.name
199248693Sgleb        self.version = newsym.version
200248693Sgleb
201248693Sgleb    def __repr__(self):
202248693Sgleb        return "CommonSymbol(%s, %s)" % (self.name, self.version)
203248693Sgleb
204248693Sglebclass SymbolAlias(object):
205248693Sgleb    def __init__(self, alias, prefix, offset):
206248693Sgleb        assert alias.startswith(prefix)
207248693Sgleb        self.alias = alias
208248693Sgleb        self.name = alias[len(prefix):]
209248693Sgleb        self.offset = offset
210248693Sgleb
211248693Sgleb    def __repr__(self):
212248693Sgleb        return "SymbolAlias(%s, 0x%x)" % (self.alias, self.offset)
213248693Sgleb
214248693Sgleb
215248693Sglebclass VersionMap(object):
216248693Sgleb    def __init__(self, name):
217248693Sgleb        self.name = name
218248693Sgleb        self.symbols = {}
219248693Sgleb
220248693Sgleb    def append(self, symbol):
221248693Sgleb        if (self.symbols.has_key(symbol.name)):
222248693Sgleb            raise ValueError("Symbol is already defined %s@%s" %
223248693Sgleb                    (symbol.name, self.name))
224248693Sgleb        self.symbols[symbol.name] = symbol
225248693Sgleb
226248693Sgleb    def names(self):
227248693Sgleb        return self.symbols.keys()
228248693Sgleb
229248693Sgleb    def __repr__(self):
230248693Sgleb        return repr(self.symbols.values())
231248693Sgleb
232248693Sgleb# }}}
233248693Sgleb
234248693Sgleb# {{{ types and definitions
235248693Sgleb
236248693Sglebclass Def(object):
237248693Sgleb    _is_alias = False
238248693Sgleb
239248693Sgleb    def __init__(self, id, name, **kwargs):
240248693Sgleb        self.id = id
241248693Sgleb        self.name = name
242248693Sgleb        self.attrs = kwargs
243248693Sgleb
244248693Sgleb    def __getattr__(self, attr):
245248693Sgleb        if not self.attrs.has_key(attr):
246248693Sgleb            raise AttributeError('%s in %s' % (attr, str(self)))
247248693Sgleb        return self.attrs[attr]
248248693Sgleb
249248693Sgleb    def _name_opt(self, default=''):
250248693Sgleb        if not self.name:
251248693Sgleb            return default
252248693Sgleb        return self.name
253248693Sgleb
254248693Sgleb    def _alias(self):
255248693Sgleb        if self._is_alias:
256248693Sgleb            return self.type._alias()
257248693Sgleb        return self
258248693Sgleb
259248693Sgleb    def __cmp__(self, other):
260248693Sgleb        # TODO assert 'self' and 'other' belong to different libraries
261248693Sgleb        #print 'cmp defs: %s, %s' % (self, other)
262248693Sgleb        a = self._alias()
263248693Sgleb        try:
264248693Sgleb            b = other._alias()
265248693Sgleb        except AttributeError:
266248693Sgleb            return 1
267248693Sgleb        r = cmp(a.__class__, b.__class__)
268248693Sgleb        if r == 0:
269248693Sgleb            if a.id != 0 and b.id != 0:
270248693Sgleb                ind = (long(a.id) << 32) + b.id
271248693Sgleb                r = Dwarf.cmpcache.get(ind)
272248693Sgleb                if r != None:
273248693Sgleb                    return r
274248693Sgleb            else:
275248693Sgleb                ind = 0
276248693Sgleb            r = cmp(a.attrs, b.attrs)
277248693Sgleb            if ind != 0:
278248693Sgleb                Dwarf.cmpcache.put(ind, r)
279248693Sgleb        else:
280248693Sgleb            r = 0
281248693Sgleb            #raise RuntimeError('Comparing different classes: %s, %s' %
282248693Sgleb            #        (a.__class__.__name__, b.__class__.__name__))
283248693Sgleb        return r
284248693Sgleb
285248693Sgleb    def __repr__(self):
286248693Sgleb        p = []
287248693Sgleb        if hasattr(self, 'name'):
288248693Sgleb            p.append("name=%s" % self.name)
289248693Sgleb        for (k, v) in self.attrs.items():
290248693Sgleb            if isinstance(v, Def):
291248693Sgleb                v = v.__class__.__name__ + '(...)'
292248693Sgleb            p.append("%s=%s" % (k, v))
293248693Sgleb        return self.__class__.__name__ + '(' + ', '.join(p) + ')'
294248693Sgleb
295248693Sgleb    def _mapval(self, param, vals):
296248693Sgleb        if param not in vals.keys():
297248693Sgleb            raise NotImplementedError("Invalid value '%s': %s" %
298248693Sgleb                    (param, str(self)))
299248693Sgleb        return vals[param]
300248693Sgleb
301248693Sgleb    def _pp_ex(self, pp):
302248693Sgleb        raise NotImplementedError('Extended pretty print not implemeted: %s' %
303248693Sgleb                str(self))
304248693Sgleb
305248693Sgleb    def _pp(self, pp):
306248693Sgleb        raise NotImplementedError('Pretty print not implemeted: %s' % str(self))
307248693Sgleb
308248693Sglebclass AnonymousDef(Def):
309248693Sgleb    def __init__(self, id, **kwargs):
310248693Sgleb        Def.__init__(self, id, None, **kwargs)
311248693Sgleb
312248693Sglebclass Void(AnonymousDef):
313248693Sgleb    _instance = None
314248693Sgleb
315248693Sgleb    def __new__(cls, *args, **kwargs):
316248693Sgleb        if not cls._instance:
317248693Sgleb            cls._instance = super(Void, cls).__new__(
318248693Sgleb                    cls, *args, **kwargs)
319248693Sgleb        return cls._instance
320248693Sgleb
321248693Sgleb    def __init__(self):
322248693Sgleb        AnonymousDef.__init__(self, 0)
323248693Sgleb
324248693Sgleb    def _pp(self, pp):
325248693Sgleb        return "void"
326248693Sgleb
327248693Sglebclass VarArgs(AnonymousDef):
328248693Sgleb    def _pp(self, pp):
329248693Sgleb        return "..."
330248693Sgleb
331248693Sglebclass PointerDef(AnonymousDef):
332248693Sgleb    def _pp(self, pp):
333248693Sgleb        t = pp.run(self.type)
334248693Sgleb        return "%s*" % (t,)
335248693Sgleb
336248693Sglebclass BaseTypeDef(Def):
337248693Sgleb    inttypes = ['DW_ATE_signed', 'DW_ATE_unsigned', 'DW_ATE_unsigned_char']
338248693Sgleb    def _pp(self, pp):
339248693Sgleb        if self.encoding in self.inttypes:
340248693Sgleb            sign = '' if self.encoding == 'DW_ATE_signed' else 'u'
341248693Sgleb            bits = int(self.byte_size) * 8
342248693Sgleb            return '%sint%s_t' % (sign, bits)
343248693Sgleb        elif self.encoding == 'DW_ATE_signed_char' and int(self.byte_size) == 1:
344248693Sgleb            return 'char';
345248693Sgleb        elif self.encoding == 'DW_ATE_float':
346248693Sgleb            return self._mapval(self.byte_size, {
347248693Sgleb                '16': 'long double',
348248693Sgleb                '8': 'double',
349248693Sgleb                '4': 'float',
350248693Sgleb            })
351248693Sgleb        raise NotImplementedError('Invalid encoding: %s' % self)
352248693Sgleb
353248693Sglebclass TypeAliasDef(Def):
354248693Sgleb    _is_alias = True
355248693Sgleb    def _pp(self, pp):
356248693Sgleb        alias = self._alias()
357248693Sgleb        # push typedef name
358248693Sgleb        if self.name and not alias.name:
359248693Sgleb            alias.name = 'T(%s)' % self.name
360248693Sgleb        # return type with modifiers
361248693Sgleb        return self.type._pp(pp)
362248693Sgleb
363248693Sglebclass EnumerationTypeDef(Def):
364248693Sgleb    def _pp(self, pp):
365248693Sgleb        return 'enum ' + self._name_opt('UNKNOWN')
366248693Sgleb
367248693Sglebclass ConstTypeDef(AnonymousDef):
368248693Sgleb    _is_alias = True
369248693Sgleb    def _pp(self, pp):
370248693Sgleb        return 'const ' + self.type._pp(pp)
371248693Sgleb
372248693Sglebclass VolatileTypeDef(AnonymousDef):
373248693Sgleb    _is_alias = True
374248693Sgleb    def _pp(self, pp):
375248693Sgleb        return 'volatile ' + self.type._pp(pp)
376248693Sgleb
377248693Sglebclass ArrayDef(AnonymousDef):
378248693Sgleb    def _pp(self, pp):
379248693Sgleb        t = pp.run(self.type)
380248693Sgleb        assert len(self.subranges) == 1
381248693Sgleb        try:
382248693Sgleb            sz = int(self.subranges[0].upper_bound) + 1
383248693Sgleb        except ValueError:
384248693Sgleb            s = re.sub(r'\(.+\)', '', self.subranges[0].upper_bound)
385248693Sgleb            sz = int(s) + 1
386248693Sgleb        return '%s[%s]' % (t, sz)
387248693Sgleb
388248693Sglebclass ArraySubrangeDef(AnonymousDef):
389248693Sgleb    pass
390248693Sgleb
391248693Sglebclass FunctionDef(Def):
392248693Sgleb    def _pp(self, pp):
393248693Sgleb        result = pp.run(self.result)
394248693Sgleb        if not self.params:
395248693Sgleb            params = "void"
396248693Sgleb        else:
397248693Sgleb            params = ', '.join([ pp.run(x) for x in self.params ])
398248693Sgleb        return "%s %s(%s);" % (result, self.name, params)
399248693Sgleb
400248693Sglebclass FunctionTypeDef(Def):
401248693Sgleb    def _pp(self, pp):
402248693Sgleb        result = pp.run(self.result)
403248693Sgleb        if not self.params:
404248693Sgleb            params = "void"
405248693Sgleb        else:
406248693Sgleb            params = ', '.join([ pp.run(x) for x in self.params ])
407248693Sgleb        return "F(%s, %s, (%s))" % (self._name_opt(), result, params)
408248693Sgleb
409248693Sglebclass ParameterDef(Def):
410248693Sgleb    def _pp(self, pp):
411248693Sgleb        t = pp.run(self.type)
412248693Sgleb        return "%s %s" % (t, self._name_opt())
413248693Sgleb
414248693Sgleb# TODO
415248693Sglebclass StructForwardDef(Def):
416248693Sgleb    pass
417248693Sgleb
418248693Sglebclass IncompleteDef(Def):
419248693Sgleb    def update(self, complete, cache=None):
420248693Sgleb        self.complete = complete
421248693Sgleb        complete.incomplete = self
422248693Sgleb        if cache != None:
423248693Sgleb            cached = cache.get(self.id)
424248693Sgleb            if cached != None and isinstance(cached, IncompleteDef):
425248693Sgleb                cache.replace(self.id, complete)
426248693Sgleb
427248693Sglebclass StructIncompleteDef(IncompleteDef):
428248693Sgleb    def _pp(self, pp):
429248693Sgleb        return "struct %s" % (self.name,)
430248693Sgleb
431248693Sglebclass UnionIncompleteDef(IncompleteDef):
432248693Sgleb    def _pp(self, pp):
433248693Sgleb        return "union %s" % (self.name,)
434248693Sgleb
435248693Sglebclass StructDef(Def):
436248693Sgleb    def _pp_ex(self, pp, suffix=';'):
437248693Sgleb        members = [ pp.run(x) for x in self.members ]
438248693Sgleb        return "struct %s { %s }%s" % \
439248693Sgleb                (self._name_opt(), ' '.join(members), suffix)
440248693Sgleb    def _pp(self, pp):
441248693Sgleb        if self.name:
442248693Sgleb            pp.run_nested(self)
443248693Sgleb            return "struct %s" % (self.name,)
444248693Sgleb        else:
445248693Sgleb            return self._pp_ex(pp, suffix='')
446248693Sgleb
447248693Sglebclass UnionDef(Def):
448248693Sgleb    def _pp_ex(self, pp, suffix=';'):
449248693Sgleb        members = [ pp.run(x) for x in self.members ]
450248693Sgleb        return "union %s { %s }%s" % \
451248693Sgleb                (self._name_opt(), ' '.join(members), suffix)
452248693Sgleb    def _pp(self, pp):
453248693Sgleb        if self.name:
454248693Sgleb            pp.run_nested(self)
455248693Sgleb            return "union %s" % (self.name,)
456248693Sgleb        else:
457248693Sgleb            return self._pp_ex(pp, suffix='')
458248693Sgleb
459248693Sglebclass MemberDef(Def):
460248693Sgleb    def _pp(self, pp):
461248693Sgleb        t = pp.run(self.type)
462248693Sgleb        if self.bit_size:
463248693Sgleb            bits = ":%s" % self.bit_size
464248693Sgleb        else:
465248693Sgleb            bits = ""
466248693Sgleb        return "%s %s%s;" % (t, self._name_opt(), bits)
467248693Sgleb
468248693Sglebclass Dwarf(object):
469248693Sgleb
470248693Sgleb    cmpcache = Cache(enabled=Config.cmpcache_enabled)
471248693Sgleb
472248693Sgleb    def __init__(self, dump):
473248693Sgleb        self.dump = dump
474248693Sgleb
475248693Sgleb    def _build_optarg_type(self, praw):
476248693Sgleb        type = praw.optarg('type', Void())
477248693Sgleb        if type != Void():
478248693Sgleb            type = self.buildref(praw.unit, type)
479248693Sgleb        return type
480248693Sgleb
481248693Sgleb    def build_subprogram(self, raw):
482248693Sgleb        if raw.optname == None:
483248693Sgleb            raw.setname('SUBPROGRAM_NONAME_' + raw.arg('low_pc'));
484248693Sgleb        params = [ self.build(x) for x in raw.nested ]
485248693Sgleb        result = self._build_optarg_type(raw)
486248693Sgleb        return FunctionDef(raw.id, raw.name, params=params, result=result)
487248693Sgleb
488248693Sgleb    def build_subroutine_type(self, raw):
489248693Sgleb        params = [ self.build(x) for x in raw.nested ]
490248693Sgleb        result = self._build_optarg_type(raw)
491248693Sgleb        return FunctionTypeDef(raw.id, raw.optname, params=params, result=result)
492248693Sgleb
493248693Sgleb    def build_formal_parameter(self, raw):
494248693Sgleb        type = self._build_optarg_type(raw)
495248693Sgleb        return ParameterDef(raw.id, raw.optname, type=type)
496248693Sgleb
497248693Sgleb    def build_pointer_type(self, raw):
498248693Sgleb        type = self._build_optarg_type(raw)
499248693Sgleb        return PointerDef(raw.id, type=type)
500248693Sgleb
501248693Sgleb    def build_member(self, raw):
502248693Sgleb        type = self.buildref(raw.unit, raw.arg('type'))
503248693Sgleb        return MemberDef(raw.id, raw.name, type=type,
504248693Sgleb                bit_size=raw.optarg('bit_size', None))
505248693Sgleb
506248693Sgleb    def build_structure_type(self, raw):
507248693Sgleb        incomplete = raw.unit.incomplete.get(raw.id)
508248693Sgleb        if incomplete == None:
509248693Sgleb            incomplete = StructIncompleteDef(raw.id, raw.optname)
510248693Sgleb            raw.unit.incomplete.put(raw.id, incomplete)
511248693Sgleb        else:
512248693Sgleb            return incomplete
513248693Sgleb        members = [ self.build(x) for x in raw.nested ]
514248693Sgleb        byte_size = raw.optarg('byte_size', None)
515248693Sgleb        if byte_size == None:
516248693Sgleb            obj = StructForwardDef(raw.id, raw.name, members=members,
517248693Sgleb                    forcename=raw.name)
518248693Sgleb        obj = StructDef(raw.id, raw.optname, members=members,
519248693Sgleb                byte_size=byte_size)
520248693Sgleb        incomplete.update(obj, cache=raw.unit.cache)
521248693Sgleb        return obj
522248693Sgleb
523248693Sgleb    def build_union_type(self, raw):
524248693Sgleb        incomplete = raw.unit.incomplete.get(raw.id)
525248693Sgleb        if incomplete == None:
526248693Sgleb            incomplete = UnionIncompleteDef(raw.id, raw.optname)
527248693Sgleb            raw.unit.incomplete.put(raw.id, incomplete)
528248693Sgleb        else:
529248693Sgleb            return incomplete
530248693Sgleb        members = [ self.build(x) for x in raw.nested ]
531248693Sgleb        byte_size = raw.optarg('byte_size', None)
532248693Sgleb        obj = UnionDef(raw.id, raw.optname, members=members,
533248693Sgleb                byte_size=byte_size)
534248693Sgleb        obj.incomplete = incomplete
535248693Sgleb        incomplete.complete = obj
536248693Sgleb        return obj
537248693Sgleb
538248693Sgleb    def build_typedef(self, raw):
539248693Sgleb        type = self._build_optarg_type(raw)
540248693Sgleb        return TypeAliasDef(raw.id, raw.name, type=type)
541248693Sgleb
542248693Sgleb    def build_const_type(self, raw):
543248693Sgleb        type = self._build_optarg_type(raw)
544248693Sgleb        return ConstTypeDef(raw.id, type=type)
545248693Sgleb
546248693Sgleb    def build_volatile_type(self, raw):
547248693Sgleb        type = self._build_optarg_type(raw)
548248693Sgleb        return VolatileTypeDef(raw.id, type=type)
549248693Sgleb
550248693Sgleb    def build_enumeration_type(self, raw):
551248693Sgleb        # TODO handle DW_TAG_enumerator ???
552248693Sgleb        return EnumerationTypeDef(raw.id, name=raw.optname,
553248693Sgleb                byte_size=raw.arg('byte_size'))
554248693Sgleb
555248693Sgleb    def build_base_type(self, raw):
556248693Sgleb        return BaseTypeDef(raw.id, raw.optname,
557248693Sgleb                byte_size=raw.arg('byte_size'), encoding=raw.arg('encoding'))
558248693Sgleb
559248693Sgleb    def build_array_type(self, raw):
560248693Sgleb        type = self.buildref(raw.unit, raw.arg('type'))
561248693Sgleb        subranges = [ self.build(x) for x in raw.nested ]
562248693Sgleb        return ArrayDef(raw.id, type=type, subranges=subranges)
563248693Sgleb
564248693Sgleb    def build_subrange_type(self, raw):
565248693Sgleb        type = self.buildref(raw.unit, raw.arg('type'))
566248693Sgleb        return ArraySubrangeDef(raw.id, type=type,
567248693Sgleb                upper_bound=raw.optarg('upper_bound', 0))
568248693Sgleb
569248693Sgleb    def build_unspecified_parameters(self, raw):
570248693Sgleb        return VarArgs(raw.id)
571248693Sgleb
572248693Sgleb    def _get_id(self, id):
573248693Sgleb        try:
574248693Sgleb            return int(id)
575248693Sgleb        except ValueError:
576248693Sgleb            if (id.startswith('<') and id.endswith('>')):
577248693Sgleb                return int(id[1:-1])
578248693Sgleb            else:
579248693Sgleb                raise ValueError("Invalid dwarf id: %s" % id)
580248693Sgleb
581248693Sgleb    def build(self, raw):
582248693Sgleb        obj = raw.unit.cache.get(raw.id)
583248693Sgleb        if obj != None:
584248693Sgleb            return obj
585248693Sgleb        builder_name = raw.tag.replace('DW_TAG_', 'build_')
586248693Sgleb        try:
587248693Sgleb            builder = getattr(self, builder_name)
588248693Sgleb        except AttributeError:
589248693Sgleb            raise AttributeError("Unknown dwarf tag: %s" % raw)
590248693Sgleb        obj = builder(raw)
591248693Sgleb        raw.unit.cache.put(obj.id, obj)
592248693Sgleb        return obj
593248693Sgleb
594248693Sgleb    def buildref(self, unit, id):
595248693Sgleb        id = self._get_id(id)
596248693Sgleb        raw = unit.tags[id]
597248693Sgleb        obj = self.build(raw)
598248693Sgleb        return obj
599248693Sgleb
600248693Sgleb# }}}
601248693Sgleb
602248693Sglebclass Shlib(object):
603248693Sgleb    def __init__(self, libfile):
604248693Sgleb        self.libfile = libfile
605248693Sgleb        self.versions = {}
606248693Sgleb        self.alias_syms = {}
607248693Sgleb
608248693Sgleb    def parse_objdump(self):
609248693Sgleb        objdump = ObjdumpParser(self.libfile)
610248693Sgleb        objdump.run()
611248693Sgleb        for p in objdump.dynamic_symbols:
612248693Sgleb            vername = p['ver']
613248693Sgleb            if vername.startswith('(') and vername.endswith(')'):
614248693Sgleb                vername = vername[1:-1]
615248693Sgleb            if not Config.version_filter.match(vername):
616248693Sgleb                continue
617248693Sgleb            if not Config.symbol_filter.match(p['symbol']):
618248693Sgleb                continue
619248693Sgleb            sym = Symbol(p['symbol'], p['offset'], vername, self)
620248693Sgleb            if not self.versions.has_key(vername):
621248693Sgleb                self.versions[vername] = VersionMap(vername)
622248693Sgleb            self.versions[vername].append(sym)
623248693Sgleb        if Config.alias_prefixes:
624248693Sgleb            self.local_offsetmap = objdump.local_offsetmap
625248693Sgleb            for p in objdump.local_symbols:
626248693Sgleb                for prefix in Config.alias_prefixes:
627248693Sgleb                    if not p['symbol'].startswith(prefix):
628248693Sgleb                        continue
629248693Sgleb                    alias = SymbolAlias(p['symbol'], prefix, p['offset'])
630248693Sgleb                    if self.alias_syms.has_key(alias.name):
631248693Sgleb                        prevalias = self.alias_syms[alias.name]
632248693Sgleb                        if alias.name != prevalias.name or \
633248693Sgleb                                alias.offset != prevalias.offset:
634248693Sgleb                            warn(Config.w_alias, "Symbol alias is " \
635248693Sgleb                                    "already defined: %s: %s at %08x -- %s at %08x" % \
636248693Sgleb                                    (alias.alias, alias.name,  alias.offset,
637248693Sgleb                                            prevalias.name, prevalias.offset))
638248693Sgleb                    self.alias_syms[alias.name] = alias
639248693Sgleb
640248693Sgleb    def parse_dwarfdump(self):
641248693Sgleb        dwarfdump = DwarfdumpParser(self.libfile)
642248693Sgleb        def lookup(sym):
643248693Sgleb            raw = None
644248693Sgleb            try:
645248693Sgleb                raw = dwarfdump.offsetmap[sym.offset]
646248693Sgleb            except:
647248693Sgleb                try:
648248693Sgleb                    localnames = self.local_offsetmap[sym.offset]
649248693Sgleb                    localnames.sort(key=lambda x: -len(x))
650248693Sgleb                    for localname in localnames:
651248693Sgleb                        if not self.alias_syms.has_key(localname):
652248693Sgleb                            continue
653248693Sgleb                        alias = self.alias_syms[localname]
654248693Sgleb                        raw = dwarfdump.offsetmap[alias.offset]
655248693Sgleb                        break
656248693Sgleb                except:
657248693Sgleb                    pass
658248693Sgleb            return raw
659248693Sgleb        dwarfdump.run()
660248693Sgleb        dwarf = Dwarf(dwarfdump)
661248693Sgleb        for ver in self.versions.values():
662248693Sgleb            for sym in ver.symbols.values():
663248693Sgleb                raw = lookup(sym);
664248693Sgleb                if not raw:
665248693Sgleb                    warn(Config.w_symbol, "Symbol %s (%s) not found at offset 0x%x" % \
666248693Sgleb                            (sym.name_ver, self.libfile, sym.offset))
667248693Sgleb                    continue
668248693Sgleb                if Config.verbose >= 3:
669248693Sgleb                    print "Parsing symbol %s (%s)" % (sym.name_ver, self.libfile)
670248693Sgleb                sym.definition = dwarf.build(raw)
671248693Sgleb
672248693Sgleb    def parse(self):
673248693Sgleb        if not os.path.isfile(self.libfile):
674248693Sgleb            print >> sys.stderr, ("No such file: %s" % self.libfile)
675248693Sgleb            sys.exit(1)
676248693Sgleb        self.parse_objdump()
677248693Sgleb        self.parse_dwarfdump()
678248693Sgleb
679248693Sgleb# {{{ parsers
680248693Sgleb
681248693Sglebclass Parser(object):
682248693Sgleb    def __init__(self, proc):
683248693Sgleb        self.proc = proc
684248693Sgleb        self.parser = self.parse_begin
685248693Sgleb
686248693Sgleb    def run(self):
687248693Sgleb        fd = os.popen(self.proc, 'r')
688248693Sgleb        while True:
689248693Sgleb            line = fd.readline()
690248693Sgleb            if (not line):
691248693Sgleb                break
692248693Sgleb            line = line.strip()
693248693Sgleb            if (line):
694248693Sgleb                self.parser(line)
695248693Sgleb        err = fd.close()
696248693Sgleb        if err:
697248693Sgleb            print >> sys.stderr, ("Execution failed: %s" % self.proc)
698248693Sgleb            sys.exit(2)
699248693Sgleb
700248693Sgleb    def parse_begin(self, line):
701248693Sgleb        print(line)
702248693Sgleb
703248693Sglebclass ObjdumpParser(Parser):
704248693Sgleb
705248693Sgleb    re_header = re.compile('(?P<table>\w*)\s*SYMBOL TABLE:')
706248693Sgleb
707248693Sgleb    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]*)')
708248693Sgleb    re_lame_symbol = re.compile('(?P<offset>[0-9a-fA-F]+)\s+(?P<bind>\w+)\s+\*[A-Z]+\*')
709248693Sgleb
710248693Sgleb    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]*)')
711248693Sgleb
712248693Sgleb    def __init__(self, libfile):
713248693Sgleb        Parser.__init__(self, "%s -wtT %s" % (Config.objdump, libfile))
714248693Sgleb        self.dynamic_symbols = []
715248693Sgleb        self.local_symbols = []
716248693Sgleb        self.local_offsetmap = {}
717248693Sgleb
718248693Sgleb    def parse_begin(self, line):
719248693Sgleb        self.parse_header(line)
720248693Sgleb
721248693Sgleb    def add_symbol(self, table, symbol, offsetmap = None):
722248693Sgleb        offset = int(symbol['offset'], 16);
723248693Sgleb        symbol['offset'] = offset
724248693Sgleb        if (offset == 0):
725248693Sgleb            return
726248693Sgleb        table.append(symbol)
727248693Sgleb        if offsetmap != None:
728248693Sgleb            if not offsetmap.has_key(offset):
729248693Sgleb                offsetmap[offset] = [symbol['symbol']]
730248693Sgleb            else:
731248693Sgleb                offsetmap[offset].append(symbol['symbol'])
732248693Sgleb
733248693Sgleb    def parse_header(self, line):
734248693Sgleb        m = self.re_header.match(line)
735248693Sgleb        if (m):
736248693Sgleb            table = m.group('table')
737248693Sgleb            if (table == "DYNAMIC"):
738248693Sgleb                self.parser = self.parse_dynamic
739248693Sgleb            elif table == '':
740248693Sgleb                self.parser = self.parse_local
741248693Sgleb            else:
742248693Sgleb                raise ValueError("Invalid symbol table: %s" % table)
743248693Sgleb            return True
744248693Sgleb        return False
745248693Sgleb
746248693Sgleb    def parse_local(self, line):
747248693Sgleb        if (self.parse_header(line)):
748248693Sgleb            return
749248693Sgleb        if (self.re_lame_symbol.match(line)):
750248693Sgleb            return
751248693Sgleb        m = self.re_local_symbol.match(line)
752248693Sgleb        if (not m):
753248693Sgleb            return
754248693Sgleb            #raise ValueError("Invalid symbol definition: %s" % line)
755248693Sgleb        p = m.groupdict()
756248693Sgleb        if (p['symbol'] and p['symbol'].find('@') == -1):
757248693Sgleb            self.add_symbol(self.local_symbols, p, self.local_offsetmap);
758248693Sgleb
759248693Sgleb    def parse_dynamic(self, line):
760248693Sgleb        if (self.parse_header(line)):
761248693Sgleb            return
762248693Sgleb        if (self.re_lame_symbol.match(line)):
763248693Sgleb            return
764248693Sgleb        m = self.re_dynamic_symbol.match(line)
765248693Sgleb        if (not m):
766248693Sgleb            raise ValueError("Invalid symbol definition: %s" % line)
767248693Sgleb        p = m.groupdict()
768248693Sgleb        if (p['symbol'] and p['ver']):
769248693Sgleb            self.add_symbol(self.dynamic_symbols, p);
770248693Sgleb
771248693Sglebclass DwarfdumpParser(Parser):
772248693Sgleb
773248693Sgleb    tagcache_stats = Cache.CacheStats()
774248693Sgleb
775248693Sgleb    class Unit(object):
776248693Sgleb        def __init__(self):
777248693Sgleb            self.cache = Cache(enabled=Config.dwarfcache_enabled,
778248693Sgleb                    stats=DwarfdumpParser.tagcache_stats)
779248693Sgleb            self.incomplete = Cache()
780248693Sgleb            self.tags = {}
781248693Sgleb
782248693Sgleb    class Tag(object):
783248693Sgleb        def __init__(self, unit, data):
784248693Sgleb            self.unit = unit
785248693Sgleb            self.id = int(data['id'])
786248693Sgleb            self.level = int(data['level'])
787248693Sgleb            self.tag = data['tag']
788248693Sgleb            self.args = {}
789248693Sgleb            self.nested = []
790248693Sgleb
791248693Sgleb        @property
792248693Sgleb        def name(self):
793248693Sgleb            return self.arg('name')
794248693Sgleb
795248693Sgleb        @property
796248693Sgleb        def optname(self):
797248693Sgleb            return self.optarg('name', None)
798248693Sgleb
799248693Sgleb        def setname(self, name):
800248693Sgleb            self.args['DW_AT_name'] = name
801248693Sgleb
802248693Sgleb        def arg(self, a):
803248693Sgleb            name = 'DW_AT_' + a
804248693Sgleb            try:
805248693Sgleb                return self.args[name]
806248693Sgleb            except KeyError:
807248693Sgleb                raise KeyError("Argument '%s' not found in %s: %s" %
808248693Sgleb                        (name, self, self.args))
809248693Sgleb
810248693Sgleb        def optarg(self, a, default):
811248693Sgleb            try:
812248693Sgleb                return self.arg(a)
813248693Sgleb            except KeyError:
814248693Sgleb                return default
815248693Sgleb
816248693Sgleb        def __repr__(self):
817248693Sgleb            return "Tag(%d, %d, %s)" % (self.level, self.id, self.tag)
818248693Sgleb
819248693Sgleb    re_header = re.compile('<(?P<level>\d+)><(?P<id>\d+\+*\d*)><(?P<tag>\w+)>')
820248693Sgleb    re_argname = re.compile('(?P<arg>\w+)<')
821248693Sgleb    re_argunknown = re.compile('<Unknown AT value \w+><[^<>]+>')
822248693Sgleb
823248693Sgleb    skip_tags = set([
824248693Sgleb        'DW_TAG_lexical_block',
825248693Sgleb        'DW_TAG_inlined_subroutine',
826248693Sgleb        'DW_TAG_label',
827248693Sgleb        'DW_TAG_variable',
828248693Sgleb        ])
829248693Sgleb
830248693Sgleb    def __init__(self, libfile):
831248693Sgleb        Parser.__init__(self, "%s -di %s" % (Config.dwarfdump, libfile))
832248693Sgleb        self.current_unit = None
833248693Sgleb        self.offsetmap = {}
834248693Sgleb        self.stack = []
835248693Sgleb
836248693Sgleb    def parse_begin(self, line):
837248693Sgleb        if line == '.debug_info':
838248693Sgleb            self.parser = self.parse_debuginfo
839248693Sgleb        else:
840248693Sgleb            raise ValueError("Invalid dwarfdump header: %s" % line)
841248693Sgleb
842248693Sgleb    def parse_argvalue(self, args):
843248693Sgleb        assert args.startswith('<')
844248693Sgleb        i = 1
845248693Sgleb        cnt = 1
846248693Sgleb        while i < len(args) and args[i]:
847248693Sgleb            if args[i] == '<':
848248693Sgleb                cnt += 1
849248693Sgleb            elif args[i] == '>':
850248693Sgleb                cnt -= 1
851248693Sgleb                if cnt == 0:
852248693Sgleb                    break
853248693Sgleb            i = i + 1
854248693Sgleb        value = args[1:i]
855248693Sgleb        args = args[i+1:]
856248693Sgleb        return (args, value)
857248693Sgleb
858248693Sgleb    def parse_arg(self, tag, args):
859248693Sgleb        m = self.re_argname.match(args)
860248693Sgleb        if not m:
861248693Sgleb            m = self.re_argunknown.match(args)
862248693Sgleb            if not m:
863248693Sgleb                raise ValueError("Invalid dwarfdump: couldn't parse arguments: %s" %
864248693Sgleb                        args)
865248693Sgleb            args = args[len(m.group(0)):].lstrip()
866248693Sgleb            return args
867248693Sgleb        argname = m.group('arg')
868248693Sgleb        args = args[len(argname):]
869248693Sgleb        value = []
870248693Sgleb        while len(args) > 0 and args.startswith('<'):
871248693Sgleb            (args, v) = self.parse_argvalue(args)
872248693Sgleb            value.append(v)
873248693Sgleb        args = args.lstrip()
874248693Sgleb        if len(value) == 1:
875248693Sgleb            value = value[0]
876248693Sgleb        tag.args[argname] = value
877248693Sgleb        return args
878248693Sgleb
879248693Sgleb    def parse_debuginfo(self, line):
880248693Sgleb        m = self.re_header.match(line)
881248693Sgleb        if not m:
882248693Sgleb            raise ValueError("Invalid dwarfdump: %s" % line)
883248693Sgleb        if m.group('level') == '0':
884248693Sgleb            self.current_unit = DwarfdumpParser.Unit()
885248693Sgleb            return
886248693Sgleb        tag = DwarfdumpParser.Tag(self.current_unit, m.groupdict())
887248693Sgleb        args = line[len(m.group(0)):].lstrip()
888248693Sgleb        while args:
889248693Sgleb            args = self.parse_arg(tag, args)
890248693Sgleb        tag.unit.tags[tag.id] = tag
891248693Sgleb        if tag.args.has_key('DW_AT_low_pc') and \
892248693Sgleb                tag.tag not in DwarfdumpParser.skip_tags:
893248693Sgleb            offset = int(tag.args['DW_AT_low_pc'], 16)
894248693Sgleb            if self.offsetmap.has_key(offset):
895248693Sgleb                raise ValueError("Dwarf dump parse error: " +
896248693Sgleb                        "symbol is aleady defined at offset 0x%x" % offset)
897248693Sgleb            self.offsetmap[offset] = tag
898248693Sgleb        if len(self.stack) > 0:
899248693Sgleb            prev = self.stack.pop()
900248693Sgleb            while prev.level >= tag.level and len(self.stack) > 0:
901248693Sgleb                prev = self.stack.pop()
902248693Sgleb            if prev.level < tag.level:
903248693Sgleb                assert prev.level == tag.level - 1
904248693Sgleb                # TODO check DW_AT_sibling ???
905248693Sgleb                if tag.tag not in DwarfdumpParser.skip_tags:
906248693Sgleb                    prev.nested.append(tag)
907248693Sgleb                self.stack.append(prev)
908248693Sgleb        self.stack.append(tag)
909248693Sgleb        assert len(self.stack) == tag.level
910248693Sgleb
911248693Sgleb# }}}
912248693Sgleb
913248693Sglebdef list_str(l):
914248693Sgleb    l = [ str(x) for x in l ]
915248693Sgleb    l.sort()
916248693Sgleb    return ', '.join(l)
917248693Sgleb
918248693Sglebdef names_ver_str(vername, names):
919248693Sgleb    return list_str([ x + "@" + vername for x in names ])
920248693Sgleb
921248693Sglebdef common_symbols(origlib, newlib):
922248693Sgleb    result = []
923248693Sgleb    verdiff = ListDiff(origlib.versions.keys(), newlib.versions.keys())
924248693Sgleb    if Config.verbose >= 1:
925248693Sgleb        print 'Original versions:   ', list_str(verdiff.orig)
926248693Sgleb        print 'New versions:        ', list_str(verdiff.new)
927248693Sgleb    for vername in verdiff.added:
928248693Sgleb        print 'Added version:       ', vername
929248693Sgleb        print '    Added symbols:   ', \
930248693Sgleb                names_ver_str(vername, newlib.versions[vername].names())
931248693Sgleb    for vername in verdiff.removed:
932248693Sgleb        print 'Removed version:     ', vername
933248693Sgleb        print '    Removed symbols: ', \
934248693Sgleb                names_ver_str(vername, origlib.versions[vername].names())
935248693Sgleb    added = []
936248693Sgleb    removed = []
937248693Sgleb    for vername in verdiff.common:
938248693Sgleb        origver = origlib.versions[vername]
939248693Sgleb        newver = newlib.versions[vername]
940248693Sgleb        namediff = ListDiff(origver.names(), newver.names())
941248693Sgleb        if namediff.added:
942248693Sgleb            added.append(names_ver_str(vername, namediff.added))
943248693Sgleb        if namediff.removed:
944248693Sgleb            removed.append(names_ver_str(vername, namediff.removed))
945248693Sgleb        commonver = VersionMap(vername)
946248693Sgleb        result.append(commonver)
947248693Sgleb        for n in namediff.common:
948248693Sgleb            sym = CommonSymbol(origver.symbols[n], newver.symbols[n])
949248693Sgleb            commonver.append(sym)
950248693Sgleb    if added:
951248693Sgleb        print 'Added symbols:'
952248693Sgleb        for i in added:
953248693Sgleb            print '    ', i
954248693Sgleb    if removed:
955248693Sgleb        print 'Removed symbols:'
956248693Sgleb        for i in removed:
957248693Sgleb            print '    ', i
958248693Sgleb    return result
959248693Sgleb
960248693Sglebdef cmp_symbols(commonver):
961248693Sgleb    for ver in commonver:
962248693Sgleb        names = ver.names();
963248693Sgleb        names.sort()
964248693Sgleb        for symname in names:
965248693Sgleb            sym = ver.symbols[symname]
966248693Sgleb            match = sym.origsym.definition == sym.newsym.definition
967248693Sgleb            if not match:
968248693Sgleb                App.result_code = 1
969248693Sgleb            if Config.verbose >= 1 or not match:
970248693Sgleb                print '%s: definitions %smatch' % \
971248693Sgleb                        (sym.origsym.name_ver, "" if match else "mis")
972248693Sgleb                if Config.dump or (not match and not Config.no_dump):
973248693Sgleb                    for x in [(sym.origsym, Config.origfile),
974248693Sgleb                            (sym.newsym, Config.newfile)]:
975248693Sgleb                        xsym = x[0]
976248693Sgleb                        xout = x[1].out
977248693Sgleb                        if not xsym.definition:
978248693Sgleb                            print >> xout, '\n// Definition not found: %s %s' % \
979248693Sgleb                                    (xsym.name_ver, xsym.lib.libfile)
980248693Sgleb                            continue
981248693Sgleb                        print >> xout, '\n// Definitions mismatch: %s %s' % \
982248693Sgleb                                (xsym.name_ver, xsym.lib.libfile)
983248693Sgleb                        pp = PrettyPrinter()
984248693Sgleb                        pp.run(xsym.definition)
985248693Sgleb                        for i in pp.nested():
986248693Sgleb                            print >> xout, i
987248693Sgleb                        print >> xout, pp.result()
988248693Sgleb
989248693Sglebdef dump_symbols(commonver):
990248693Sgleb    class SymbolDump(object):
991248693Sgleb        def __init__(self, io_conf):
992248693Sgleb            self.io_conf = io_conf
993248693Sgleb            self.pp = PrettyPrinter()
994248693Sgleb            self.res = []
995248693Sgleb        def run(self, sym):
996248693Sgleb            r = self.pp.run(sym.definition)
997248693Sgleb            self.res.append('/* %s@%s */ %s' % (sym.name, sym.version, r))
998248693Sgleb        def finish(self):
999248693Sgleb            print >> self.io_conf.out, '\n// Symbol dump: version %s, library %s' % \
1000248693Sgleb                    (ver.name, self.io_conf.filename)
1001248693Sgleb            for i in self.pp.nested():
1002248693Sgleb                print >> self.io_conf.out, i
1003248693Sgleb            print >> self.io_conf.out, ''
1004248693Sgleb            for i in self.res:
1005248693Sgleb                print >> self.io_conf.out, i
1006248693Sgleb    for ver in commonver:
1007248693Sgleb        names = sorted(ver.names());
1008248693Sgleb        d_orig = SymbolDump(Config.origfile)
1009248693Sgleb        d_new = SymbolDump(Config.newfile)
1010248693Sgleb        for symname in names:
1011248693Sgleb            sym = ver.symbols[symname]
1012248693Sgleb            if not sym.origsym.definition or not sym.newsym.definition:
1013248693Sgleb                # XXX
1014248693Sgleb                warn(Config.w_symbol, 'Missing symbol definition: %s@%s' % \
1015248693Sgleb                        (symname, ver.name))
1016248693Sgleb                continue
1017248693Sgleb            d_orig.run(sym.origsym)
1018248693Sgleb            d_new.run(sym.newsym)
1019248693Sgleb        d_orig.finish()
1020248693Sgleb        d_new.finish()
1021248693Sgleb
1022248693Sglebif __name__ == '__main__':
1023248693Sgleb    Config.init()
1024248693Sgleb    parser = optparse.OptionParser(usage="usage: %prog origlib newlib",
1025248693Sgleb            version="%prog " + Config.version)
1026248693Sgleb    parser.add_option('-v', '--verbose', action='count',
1027248693Sgleb            help="verbose mode, may be specified several times")
1028248693Sgleb    parser.add_option('--alias-prefix', action='append',
1029248693Sgleb            help="name prefix to try for symbol alias lookup", metavar="STR")
1030248693Sgleb    parser.add_option('--dump', action='store_true',
1031248693Sgleb            help="dump symbol definitions")
1032248693Sgleb    parser.add_option('--no-dump', action='store_true',
1033248693Sgleb            help="disable dump for mismatched symbols")
1034248693Sgleb    parser.add_option('--out-orig', action='store',
1035248693Sgleb            help="result output file for original library", metavar="ORIGFILE")
1036248693Sgleb    parser.add_option('--out-new', action='store',
1037248693Sgleb            help="result output file for new library", metavar="NEWFILE")
1038248693Sgleb    parser.add_option('--exclude-ver', action='append', metavar="RE")
1039248693Sgleb    parser.add_option('--include-ver', action='append', metavar="RE")
1040248693Sgleb    parser.add_option('--exclude-sym', action='append', metavar="RE")
1041248693Sgleb    parser.add_option('--include-sym', action='append', metavar="RE")
1042248693Sgleb    for opt in ['alias', 'cached', 'symbol']:
1043248693Sgleb        parser.add_option("--w-" + opt,
1044248693Sgleb                action="store_true", dest="w_" + opt)
1045248693Sgleb        parser.add_option("--w-no-" + opt,
1046248693Sgleb                action="store_false", dest="w_" + opt)
1047248693Sgleb    (opts, args) = parser.parse_args()
1048248693Sgleb
1049248693Sgleb    if len(args) != 2:
1050248693Sgleb        parser.print_help()
1051248693Sgleb        sys.exit(-1)
1052248693Sgleb    if opts.out_orig:
1053248693Sgleb        Config.origfile.init(opts.out_orig)
1054248693Sgleb    if opts.out_new:
1055248693Sgleb        Config.newfile.init(opts.out_new)
1056248693Sgleb    if opts.no_dump:
1057248693Sgleb        Config.dump = False
1058248693Sgleb        Config.no_dump = True
1059248693Sgleb    if opts.dump:
1060248693Sgleb        Config.dump = True
1061248693Sgleb        Config.no_dump = False
1062248693Sgleb        Config.verbose = 1
1063248693Sgleb    if opts.verbose:
1064248693Sgleb        Config.verbose = opts.verbose
1065248693Sgleb    if opts.alias_prefix:
1066248693Sgleb        Config.alias_prefixes = opts.alias_prefix
1067248693Sgleb        Config.alias_prefixes.sort(key=lambda x: -len(x))
1068248693Sgleb    for (k, v) in ({ '_sym': Config.symbol_filter,
1069248693Sgleb            '_ver': Config.version_filter }).items():
1070248693Sgleb        for a in [ 'exclude', 'include' ]:
1071248693Sgleb            opt = getattr(opts, a + k)
1072248693Sgleb            if opt:
1073248693Sgleb                getattr(v, a).extend(opt)
1074248693Sgleb    Config.version_filter.compile()
1075248693Sgleb    Config.symbol_filter.compile()
1076248693Sgleb    for w in ['w_alias', 'w_cached', 'w_symbol']:
1077248693Sgleb        if hasattr(opts, w):
1078248693Sgleb            v = getattr(opts, w)
1079248693Sgleb            if v != None:
1080248693Sgleb                setattr(Config, w, v)
1081248693Sgleb
1082248693Sgleb    (Config.origfile.filename, Config.newfile.filename) = (args[0], args[1])
1083248693Sgleb
1084248693Sgleb    origlib = Shlib(Config.origfile.filename)
1085248693Sgleb    origlib.parse()
1086248693Sgleb    newlib = Shlib(Config.newfile.filename)
1087248693Sgleb    newlib.parse()
1088248693Sgleb
1089248693Sgleb    commonver = common_symbols(origlib, newlib)
1090248693Sgleb    if Config.dump:
1091248693Sgleb        dump_symbols(commonver)
1092248693Sgleb    cmp_symbols(commonver)
1093248693Sgleb    if Config.verbose >= 4:
1094248693Sgleb        print Dwarf.cmpcache.stats.show('Cmp')
1095248693Sgleb        print DwarfdumpParser.tagcache_stats.show('Dwarf tag')
1096248693Sgleb
1097248693Sgleb    sys.exit(App.result_code)
1098