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