xref: /optee_os/scripts/symbolize.py (revision 099918f6744c37ce693c38562f11466b19d573c9)
1bbaeed4dSRouven Czerwinski#!/usr/bin/env python3
21bb92983SJerome Forissier# SPDX-License-Identifier: BSD-2-Clause
3733a15f2SJerome Forissier#
4733a15f2SJerome Forissier# Copyright (c) 2017, Linaro Limited
5733a15f2SJerome Forissier#
6733a15f2SJerome Forissier
7733a15f2SJerome Forissier
8733a15f2SJerome Forissierimport argparse
9bbaeed4dSRouven Czerwinskiimport errno
10733a15f2SJerome Forissierimport glob
11157e6213SJerome Forissierimport os
12733a15f2SJerome Forissierimport re
13733a15f2SJerome Forissierimport subprocess
14733a15f2SJerome Forissierimport sys
156b4fc675SJerome Forissierimport termios
16733a15f2SJerome Forissier
17733a15f2SJerome ForissierCALL_STACK_RE = re.compile('Call stack:')
18a2b984bdSJoakim Bech# This gets the address from lines looking like this:
19a2b984bdSJoakim Bech# E/TC:0  0x001044a8
206e7c2e91SJerome ForissierSTACK_ADDR_RE = re.compile(
21531963a5SJens Wiklander    r'[UEIDFM]/(TC|LD):(\?*|[0-9]*) [0-9]* +(?P<addr>0x[0-9a-f]+)')
226e7c2e91SJerome ForissierABORT_ADDR_RE = re.compile(r'-abort at address (?P<addr>0x[0-9a-f]+)')
23444c203eSJerome ForissierREGION_RE = re.compile(r'region +[0-9]+: va (?P<addr>0x[0-9a-f]+) '
246e7c2e91SJerome Forissier                       r'pa 0x[0-9a-f]+ size (?P<size>0x[0-9a-f]+)'
25531963a5SJens Wiklander                       r'( flags .{4} (\[(?P<elf_idx>[0-9]+)\])?)?')
26ae252462SJerome ForissierELF_LIST_RE = re.compile(r'\[(?P<idx>[0-9]+)\] (?P<uuid>[0-9a-f\-]+)'
276e7c2e91SJerome Forissier                         r' @ (?P<load_addr>0x[0-9a-f\-]+)')
28c90b6663SSumit GargFUNC_GRAPH_RE = re.compile(r'Function graph')
29c90b6663SSumit GargGRAPH_ADDR_RE = re.compile(r'(?P<addr>0x[0-9a-f]+)')
30c90b6663SSumit GargGRAPH_RE = re.compile(r'}')
31733a15f2SJerome Forissier
32733a15f2SJerome Forissierepilog = '''
330c5bedb5SJerome ForissierThis scripts reads an OP-TEE abort or panic message from stdin and adds debug
340c5bedb5SJerome Forissierinformation to the output, such as '<function> at <file>:<line>' next to each
350c5bedb5SJerome Forissieraddress in the call stack. Any message generated by OP-TEE and containing a
360c5bedb5SJerome Forissiercall stack can in principle be processed by this script. This currently
370c5bedb5SJerome Forissierincludes aborts and panics from the TEE core as well as from any TA.
380c5bedb5SJerome ForissierThe paths provided on the command line are used to locate the appropriate ELF
390c5bedb5SJerome Forissierbinary (tee.elf or Trusted Application). The GNU binutils (addr2line, objdump,
40f9089765SJerome Forissiernm) are used to extract the debug info. If the CROSS_COMPILE environment
41f9089765SJerome Forissiervariable is set, it is used as a prefix to the binutils tools. That is, the
42f9089765SJerome Forissierscript will invoke $(CROSS_COMPILE)addr2line etc. If it is not set however,
43f9089765SJerome Forissierthe prefix will be determined automatically for each ELF file based on its
44f9089765SJerome Forissierarchitecture (arm-linux-gnueabihf-, aarch64-linux-gnu-). The resulting command
45f9089765SJerome Forissieris then expected to be found in the user's PATH.
46733a15f2SJerome Forissier
470c5bedb5SJerome ForissierOP-TEE abort and panic messages are sent to the secure console. They look like
480c5bedb5SJerome Forissierthe following:
49733a15f2SJerome Forissier
500c5bedb5SJerome Forissier  E/TC:0 User TA data-abort at address 0xffffdecd (alignment fault)
51733a15f2SJerome Forissier  ...
520c5bedb5SJerome Forissier  E/TC:0 Call stack:
530c5bedb5SJerome Forissier  E/TC:0  0x4000549e
540c5bedb5SJerome Forissier  E/TC:0  0x40001f4b
550c5bedb5SJerome Forissier  E/TC:0  0x4000273f
560c5bedb5SJerome Forissier  E/TC:0  0x40005da7
57733a15f2SJerome Forissier
58733a15f2SJerome ForissierInspired by a script of the same name by the Chromium project.
59733a15f2SJerome Forissier
60733a15f2SJerome ForissierSample usage:
61733a15f2SJerome Forissier
62733a15f2SJerome Forissier  $ scripts/symbolize.py -d out/arm-plat-hikey/core -d ../optee_test/out/ta/*
63733a15f2SJerome Forissier  <paste whole dump here>
64733a15f2SJerome Forissier  ^D
65c90b6663SSumit Garg
66c90b6663SSumit GargAlso, this script reads function graph generated for OP-TEE user TA from
67c90b6663SSumit Garg/tmp/ftrace-<ta_uuid>.out file and resolves function addresses to corresponding
68c90b6663SSumit Gargsymbols.
69c90b6663SSumit Garg
70c90b6663SSumit GargSample usage:
71c90b6663SSumit Garg
72c90b6663SSumit Garg  $ cat /tmp/ftrace-<ta_uuid>.out | scripts/symbolize.py -d <ta_uuid>.elf
73c90b6663SSumit Garg  <paste function graph here>
74c90b6663SSumit Garg  ^D
75733a15f2SJerome Forissier'''
76733a15f2SJerome Forissier
77ae252462SJerome Forissier
78733a15f2SJerome Forissierdef get_args():
79733a15f2SJerome Forissier    parser = argparse.ArgumentParser(
80733a15f2SJerome Forissier        formatter_class=argparse.RawDescriptionHelpFormatter,
81c90b6663SSumit Garg        description='Symbolizes OP-TEE abort dumps or function graphs',
82733a15f2SJerome Forissier        epilog=epilog)
83733a15f2SJerome Forissier    parser.add_argument('-d', '--dir', action='append', nargs='+',
841d8c2a48SJerome Forissier                        help='Search for ELF file in DIR. tee.elf is needed '
851d8c2a48SJerome Forissier                        'to decode a TEE Core or pseudo-TA abort, while '
861d8c2a48SJerome Forissier                        '<TA_uuid>.elf is required if a user-mode TA has '
871d8c2a48SJerome Forissier                        'crashed. For convenience, ELF files may also be '
881d8c2a48SJerome Forissier                        'given.')
895f7df507SJerome Forissier    parser.add_argument('-s', '--strip_path', nargs='?',
901d8c2a48SJerome Forissier                        help='Strip STRIP_PATH from file paths (default: '
911d8c2a48SJerome Forissier                        'current directory, use -s with no argument to show '
921d8c2a48SJerome Forissier                        'full paths)', default=os.getcwd())
93733a15f2SJerome Forissier
94733a15f2SJerome Forissier    return parser.parse_args()
95733a15f2SJerome Forissier
96ae252462SJerome Forissier
97733a15f2SJerome Forissierclass Symbolizer(object):
98733a15f2SJerome Forissier    def __init__(self, out, dirs, strip_path):
99733a15f2SJerome Forissier        self._out = out
100733a15f2SJerome Forissier        self._dirs = dirs
101733a15f2SJerome Forissier        self._strip_path = strip_path
102733a15f2SJerome Forissier        self._addr2line = None
103733a15f2SJerome Forissier        self.reset()
104733a15f2SJerome Forissier
1051cbf777bSJerome Forissier    def my_Popen(self, cmd):
1061cbf777bSJerome Forissier        try:
1071cbf777bSJerome Forissier            return subprocess.Popen(cmd, stdin=subprocess.PIPE,
108bbaeed4dSRouven Czerwinski                                    stdout=subprocess.PIPE, text=True,
109bbaeed4dSRouven Czerwinski                                    bufsize=1)
1101cbf777bSJerome Forissier        except OSError as e:
111bbaeed4dSRouven Czerwinski            if e.errno == errno.ENOENT:
112bbaeed4dSRouven Czerwinski                print("*** Error:{}: command not found".format(cmd[0]),
113bbaeed4dSRouven Czerwinski                      file=sys.stderr)
1141cbf777bSJerome Forissier                sys.exit(1)
1151cbf777bSJerome Forissier
116733a15f2SJerome Forissier    def get_elf(self, elf_or_uuid):
117733a15f2SJerome Forissier        if not elf_or_uuid.endswith('.elf'):
118733a15f2SJerome Forissier            elf_or_uuid += '.elf'
119733a15f2SJerome Forissier        for d in self._dirs:
120157e6213SJerome Forissier            if d.endswith(elf_or_uuid) and os.path.isfile(d):
121157e6213SJerome Forissier                return d
122733a15f2SJerome Forissier            elf = glob.glob(d + '/' + elf_or_uuid)
123733a15f2SJerome Forissier            if elf:
124733a15f2SJerome Forissier                return elf[0]
125733a15f2SJerome Forissier
126d720431cSJerome Forissier    def set_arch(self):
127d720431cSJerome Forissier        if self._arch:
128d720431cSJerome Forissier            return
1296e7c2e91SJerome Forissier        self._arch = os.getenv('CROSS_COMPILE')
1308a6d4a8bSEtienne Carriere        if self._arch:
1318a6d4a8bSEtienne Carriere            return
132ae252462SJerome Forissier        elf = self.get_elf(self._elfs[0][0])
133ae252462SJerome Forissier        if elf is None:
134ae252462SJerome Forissier            return
135ae252462SJerome Forissier        p = subprocess.Popen(['file', self.get_elf(self._elfs[0][0])],
136d720431cSJerome Forissier                             stdout=subprocess.PIPE)
137d720431cSJerome Forissier        output = p.stdout.readlines()
138d720431cSJerome Forissier        p.terminate()
139bbaeed4dSRouven Czerwinski        if b'ARM aarch64,' in output[0]:
140d720431cSJerome Forissier            self._arch = 'aarch64-linux-gnu-'
141bbaeed4dSRouven Czerwinski        elif b'ARM,' in output[0]:
142d720431cSJerome Forissier            self._arch = 'arm-linux-gnueabihf-'
143d720431cSJerome Forissier
144142c5cccSJerome Forissier    def arch_prefix(self, cmd):
145d720431cSJerome Forissier        self.set_arch()
146ae252462SJerome Forissier        if self._arch is None:
147ae252462SJerome Forissier            return ''
148d720431cSJerome Forissier        return self._arch + cmd
149142c5cccSJerome Forissier
150ae252462SJerome Forissier    def spawn_addr2line(self, elf_name):
151ae252462SJerome Forissier        if elf_name is None:
152ae252462SJerome Forissier            return
153ae252462SJerome Forissier        if self._addr2line_elf_name is elf_name:
154ae252462SJerome Forissier            return
155ae252462SJerome Forissier        if self._addr2line:
156ae252462SJerome Forissier            self._addr2line.terminate
157ae252462SJerome Forissier            self._addr2line = None
158ae252462SJerome Forissier        elf = self.get_elf(elf_name)
159733a15f2SJerome Forissier        if not elf:
160733a15f2SJerome Forissier            return
161142c5cccSJerome Forissier        cmd = self.arch_prefix('addr2line')
162142c5cccSJerome Forissier        if not cmd:
163733a15f2SJerome Forissier            return
1641cbf777bSJerome Forissier        self._addr2line = self.my_Popen([cmd, '-f', '-p', '-e', elf])
165ae252462SJerome Forissier        self._addr2line_elf_name = elf_name
166ae252462SJerome Forissier
167ae252462SJerome Forissier    # If addr falls into a region that maps a TA ELF file, return the load
168ae252462SJerome Forissier    # address of that file.
169ae252462SJerome Forissier    def elf_load_addr(self, addr):
170ae252462SJerome Forissier        if self._regions:
171ae252462SJerome Forissier            for r in self._regions:
172ae252462SJerome Forissier                r_addr = int(r[0], 16)
173ae252462SJerome Forissier                r_size = int(r[1], 16)
174ae252462SJerome Forissier                i_addr = int(addr, 16)
175ae252462SJerome Forissier                if (i_addr >= r_addr and i_addr < (r_addr + r_size)):
176ae252462SJerome Forissier                    # Found region
177ae252462SJerome Forissier                    elf_idx = r[2]
178ae252462SJerome Forissier                    if elf_idx is not None:
179ae252462SJerome Forissier                        return self._elfs[int(elf_idx)][1]
180*099918f6SSumit Garg            # In case address is not found in TA ELF file, fallback to tee.elf
181*099918f6SSumit Garg            # especially to symbolize mixed (user-space and kernel) addresses
182*099918f6SSumit Garg            # which is true when syscall ftrace is enabled along with TA
183*099918f6SSumit Garg            # ftrace.
184*099918f6SSumit Garg            return '0x0'
185ae252462SJerome Forissier        else:
186ae252462SJerome Forissier            # tee.elf
187ae252462SJerome Forissier            return '0x0'
188ae252462SJerome Forissier
189ae252462SJerome Forissier    def elf_for_addr(self, addr):
190ae252462SJerome Forissier        l_addr = self.elf_load_addr(addr)
191ae252462SJerome Forissier        if l_addr is None:
192ae252462SJerome Forissier            return None
193ae252462SJerome Forissier        if l_addr is '0x0':
194ae252462SJerome Forissier            return 'tee.elf'
195ae252462SJerome Forissier        for k in self._elfs:
196ae252462SJerome Forissier            e = self._elfs[k]
197ae252462SJerome Forissier            if int(e[1], 16) == int(l_addr, 16):
198ae252462SJerome Forissier                return e[0]
199ae252462SJerome Forissier        return None
200733a15f2SJerome Forissier
201142c5cccSJerome Forissier    def subtract_load_addr(self, addr):
202ae252462SJerome Forissier        l_addr = self.elf_load_addr(addr)
203ae252462SJerome Forissier        if l_addr is None:
204ae252462SJerome Forissier            return None
205ae252462SJerome Forissier        if int(l_addr, 16) > int(addr, 16):
206142c5cccSJerome Forissier            return ''
207ae252462SJerome Forissier        return '0x{:x}'.format(int(addr, 16) - int(l_addr, 16))
208142c5cccSJerome Forissier
209142c5cccSJerome Forissier    def resolve(self, addr):
210142c5cccSJerome Forissier        reladdr = self.subtract_load_addr(addr)
211ae252462SJerome Forissier        self.spawn_addr2line(self.elf_for_addr(addr))
212142c5cccSJerome Forissier        if not reladdr or not self._addr2line:
213733a15f2SJerome Forissier            return '???'
214733a15f2SJerome Forissier        try:
215bbaeed4dSRouven Czerwinski            print(reladdr, file=self._addr2line.stdin)
216733a15f2SJerome Forissier            ret = self._addr2line.stdout.readline().rstrip('\n')
217733a15f2SJerome Forissier        except IOError:
218733a15f2SJerome Forissier            ret = '!!!'
219733a15f2SJerome Forissier        return ret
220733a15f2SJerome Forissier
221142c5cccSJerome Forissier    def symbol_plus_offset(self, addr):
222142c5cccSJerome Forissier        ret = ''
223142c5cccSJerome Forissier        prevsize = 0
224142c5cccSJerome Forissier        reladdr = self.subtract_load_addr(addr)
225ae252462SJerome Forissier        elf_name = self.elf_for_addr(addr)
226ae252462SJerome Forissier        if elf_name is None:
227ae252462SJerome Forissier            return ''
228ae252462SJerome Forissier        elf = self.get_elf(elf_name)
229142c5cccSJerome Forissier        cmd = self.arch_prefix('nm')
230142c5cccSJerome Forissier        if not reladdr or not elf or not cmd:
231142c5cccSJerome Forissier            return ''
23230999126SJerome Forissier        ireladdr = int(reladdr, 16)
2331cbf777bSJerome Forissier        nm = self.my_Popen([cmd, '--numeric-sort', '--print-size', elf])
234142c5cccSJerome Forissier        for line in iter(nm.stdout.readline, ''):
235142c5cccSJerome Forissier            try:
236142c5cccSJerome Forissier                addr, size, _, name = line.split()
2371d8c2a48SJerome Forissier            except ValueError:
238142c5cccSJerome Forissier                # Size is missing
239b4815427SJerome Forissier                try:
240142c5cccSJerome Forissier                    addr, _, name = line.split()
241142c5cccSJerome Forissier                    size = '0'
2421d8c2a48SJerome Forissier                except ValueError:
243b4815427SJerome Forissier                    # E.g., undefined (external) symbols (line = "U symbol")
244b4815427SJerome Forissier                    continue
245142c5cccSJerome Forissier            iaddr = int(addr, 16)
246142c5cccSJerome Forissier            isize = int(size, 16)
247142c5cccSJerome Forissier            if iaddr == ireladdr:
248142c5cccSJerome Forissier                ret = name
249142c5cccSJerome Forissier                break
250142c5cccSJerome Forissier            if iaddr < ireladdr and iaddr + isize >= ireladdr:
251142c5cccSJerome Forissier                offs = ireladdr - iaddr
252142c5cccSJerome Forissier                ret = name + '+' + str(offs)
253142c5cccSJerome Forissier                break
254142c5cccSJerome Forissier            if iaddr > ireladdr and prevsize == 0:
255142c5cccSJerome Forissier                offs = iaddr + ireladdr
256142c5cccSJerome Forissier                ret = prevname + '+' + str(offs)
257142c5cccSJerome Forissier                break
258142c5cccSJerome Forissier            prevsize = size
259142c5cccSJerome Forissier            prevname = name
260142c5cccSJerome Forissier        nm.terminate()
261142c5cccSJerome Forissier        return ret
262142c5cccSJerome Forissier
263142c5cccSJerome Forissier    def section_plus_offset(self, addr):
264142c5cccSJerome Forissier        ret = ''
265142c5cccSJerome Forissier        reladdr = self.subtract_load_addr(addr)
266ae252462SJerome Forissier        elf_name = self.elf_for_addr(addr)
267ae252462SJerome Forissier        if elf_name is None:
268ae252462SJerome Forissier            return ''
269ae252462SJerome Forissier        elf = self.get_elf(elf_name)
270142c5cccSJerome Forissier        cmd = self.arch_prefix('objdump')
271142c5cccSJerome Forissier        if not reladdr or not elf or not cmd:
272142c5cccSJerome Forissier            return ''
27330999126SJerome Forissier        iaddr = int(reladdr, 16)
2741cbf777bSJerome Forissier        objdump = self.my_Popen([cmd, '--section-headers', elf])
275142c5cccSJerome Forissier        for line in iter(objdump.stdout.readline, ''):
276142c5cccSJerome Forissier            try:
277142c5cccSJerome Forissier                idx, name, size, vma, lma, offs, algn = line.split()
2781d8c2a48SJerome Forissier            except ValueError:
279ae252462SJerome Forissier                continue
280142c5cccSJerome Forissier            ivma = int(vma, 16)
281142c5cccSJerome Forissier            isize = int(size, 16)
282142c5cccSJerome Forissier            if ivma == iaddr:
283142c5cccSJerome Forissier                ret = name
284142c5cccSJerome Forissier                break
285142c5cccSJerome Forissier            if ivma < iaddr and ivma + isize >= iaddr:
286142c5cccSJerome Forissier                offs = iaddr - ivma
287142c5cccSJerome Forissier                ret = name + '+' + str(offs)
288142c5cccSJerome Forissier                break
289142c5cccSJerome Forissier        objdump.terminate()
290142c5cccSJerome Forissier        return ret
291142c5cccSJerome Forissier
292142c5cccSJerome Forissier    def process_abort(self, line):
293142c5cccSJerome Forissier        ret = ''
294142c5cccSJerome Forissier        match = re.search(ABORT_ADDR_RE, line)
295142c5cccSJerome Forissier        addr = match.group('addr')
296142c5cccSJerome Forissier        pre = match.start('addr')
297142c5cccSJerome Forissier        post = match.end('addr')
298142c5cccSJerome Forissier        sym = self.symbol_plus_offset(addr)
299142c5cccSJerome Forissier        sec = self.section_plus_offset(addr)
300142c5cccSJerome Forissier        if sym or sec:
301142c5cccSJerome Forissier            ret += line[:pre]
302142c5cccSJerome Forissier            ret += addr
303142c5cccSJerome Forissier            if sym:
304142c5cccSJerome Forissier                ret += ' ' + sym
305142c5cccSJerome Forissier            if sec:
306142c5cccSJerome Forissier                ret += ' ' + sec
307142c5cccSJerome Forissier            ret += line[post:]
308142c5cccSJerome Forissier        return ret
309142c5cccSJerome Forissier
31030999126SJerome Forissier    # Return all ELF sections with the ALLOC flag
311ae252462SJerome Forissier    def read_sections(self, elf_name):
312ae252462SJerome Forissier        if elf_name is None:
31330999126SJerome Forissier            return
314ae252462SJerome Forissier        if elf_name in self._sections:
315ae252462SJerome Forissier            return
316ae252462SJerome Forissier        elf = self.get_elf(elf_name)
31730999126SJerome Forissier        cmd = self.arch_prefix('objdump')
31830999126SJerome Forissier        if not elf or not cmd:
31930999126SJerome Forissier            return
320ae252462SJerome Forissier        self._sections[elf_name] = []
3211cbf777bSJerome Forissier        objdump = self.my_Popen([cmd, '--section-headers', elf])
32230999126SJerome Forissier        for line in iter(objdump.stdout.readline, ''):
32330999126SJerome Forissier            try:
32430999126SJerome Forissier                _, name, size, vma, _, _, _ = line.split()
3251d8c2a48SJerome Forissier            except ValueError:
32630999126SJerome Forissier                if 'ALLOC' in line:
327ae252462SJerome Forissier                    self._sections[elf_name].append([name, int(vma, 16),
328ae252462SJerome Forissier                                                     int(size, 16)])
32930999126SJerome Forissier
33030999126SJerome Forissier    def overlaps(self, section, addr, size):
33130999126SJerome Forissier        sec_addr = section[1]
33230999126SJerome Forissier        sec_size = section[2]
33330999126SJerome Forissier        if not size or not sec_size:
33430999126SJerome Forissier            return False
335ae252462SJerome Forissier        return ((addr <= (sec_addr + sec_size - 1)) and
336ae252462SJerome Forissier                ((addr + size - 1) >= sec_addr))
33730999126SJerome Forissier
338ae252462SJerome Forissier    def sections_in_region(self, addr, size, elf_idx):
33930999126SJerome Forissier        ret = ''
34030999126SJerome Forissier        addr = self.subtract_load_addr(addr)
34130999126SJerome Forissier        if not addr:
34230999126SJerome Forissier            return ''
34330999126SJerome Forissier        iaddr = int(addr, 16)
34430999126SJerome Forissier        isize = int(size, 16)
345ae252462SJerome Forissier        elf = self._elfs[int(elf_idx)][0]
346ae252462SJerome Forissier        if elf is None:
347ae252462SJerome Forissier            return ''
348ae252462SJerome Forissier        self.read_sections(elf)
349ae252462SJerome Forissier        if elf not in self._sections:
350ae252462SJerome Forissier            return ''
351ae252462SJerome Forissier        for s in self._sections[elf]:
35230999126SJerome Forissier            if self.overlaps(s, iaddr, isize):
35330999126SJerome Forissier                ret += ' ' + s[0]
35430999126SJerome Forissier        return ret
35530999126SJerome Forissier
356733a15f2SJerome Forissier    def reset(self):
357733a15f2SJerome Forissier        self._call_stack_found = False
358733a15f2SJerome Forissier        if self._addr2line:
359733a15f2SJerome Forissier            self._addr2line.terminate()
360733a15f2SJerome Forissier            self._addr2line = None
361ae252462SJerome Forissier        self._addr2line_elf_name = None
362d720431cSJerome Forissier        self._arch = None
363142c5cccSJerome Forissier        self._saved_abort_line = ''
364ae252462SJerome Forissier        self._sections = {}  # {elf_name: [[name, addr, size], ...], ...}
365ae252462SJerome Forissier        self._regions = []   # [[addr, size, elf_idx, saved line], ...]
366ae252462SJerome Forissier        self._elfs = {0: ["tee.elf", 0]}  # {idx: [uuid, load_addr], ...}
367c90b6663SSumit Garg        self._func_graph_found = False
368c90b6663SSumit Garg        self._func_graph_skip_line = True
369733a15f2SJerome Forissier
370095567e5SJerome Forissier    def pretty_print_path(self, path):
371095567e5SJerome Forissier        if self._strip_path:
372095567e5SJerome Forissier            return re.sub(re.escape(self._strip_path) + '/*', '', path)
373095567e5SJerome Forissier        return path
374095567e5SJerome Forissier
375733a15f2SJerome Forissier    def write(self, line):
376733a15f2SJerome Forissier        if self._call_stack_found:
377733a15f2SJerome Forissier            match = re.search(STACK_ADDR_RE, line)
378733a15f2SJerome Forissier            if match:
379733a15f2SJerome Forissier                addr = match.group('addr')
380733a15f2SJerome Forissier                pre = match.start('addr')
381733a15f2SJerome Forissier                post = match.end('addr')
382733a15f2SJerome Forissier                self._out.write(line[:pre])
383733a15f2SJerome Forissier                self._out.write(addr)
384733a15f2SJerome Forissier                res = self.resolve(addr)
385095567e5SJerome Forissier                res = self.pretty_print_path(res)
386733a15f2SJerome Forissier                self._out.write(' ' + res)
387733a15f2SJerome Forissier                self._out.write(line[post:])
388733a15f2SJerome Forissier                return
389733a15f2SJerome Forissier            else:
390733a15f2SJerome Forissier                self.reset()
391c90b6663SSumit Garg        if self._func_graph_found:
392c90b6663SSumit Garg            match = re.search(GRAPH_ADDR_RE, line)
393c90b6663SSumit Garg            match_re = re.search(GRAPH_RE, line)
394c90b6663SSumit Garg            if match:
395c90b6663SSumit Garg                addr = match.group('addr')
396c90b6663SSumit Garg                pre = match.start('addr')
397c90b6663SSumit Garg                post = match.end('addr')
398c90b6663SSumit Garg                self._out.write(line[:pre])
399c90b6663SSumit Garg                res = self.resolve(addr)
400c90b6663SSumit Garg                res_arr = re.split(' ', res)
401c90b6663SSumit Garg                self._out.write(res_arr[0])
402c90b6663SSumit Garg                self._out.write(line[post:])
403c90b6663SSumit Garg                self._func_graph_skip_line = False
404c90b6663SSumit Garg                return
405c90b6663SSumit Garg            elif match_re:
406c90b6663SSumit Garg                self._out.write(line)
407c90b6663SSumit Garg                return
408c90b6663SSumit Garg            elif self._func_graph_skip_line:
409c90b6663SSumit Garg                return
410c90b6663SSumit Garg            else:
411c90b6663SSumit Garg                self.reset()
41230999126SJerome Forissier        match = re.search(REGION_RE, line)
41330999126SJerome Forissier        if match:
414ae252462SJerome Forissier            # Region table: save info for later processing once
415ae252462SJerome Forissier            # we know which UUID corresponds to which ELF index
41630999126SJerome Forissier            addr = match.group('addr')
41730999126SJerome Forissier            size = match.group('size')
418ae252462SJerome Forissier            elf_idx = match.group('elf_idx')
419ae252462SJerome Forissier            self._regions.append([addr, size, elf_idx, line])
420ae252462SJerome Forissier            return
421ae252462SJerome Forissier        match = re.search(ELF_LIST_RE, line)
422ae252462SJerome Forissier        if match:
423ae252462SJerome Forissier            # ELF list: save info for later. Region table and ELF list
424ae252462SJerome Forissier            # will be displayed when the call stack is reached
425ae252462SJerome Forissier            i = int(match.group('idx'))
426ae252462SJerome Forissier            self._elfs[i] = [match.group('uuid'), match.group('load_addr'),
427ae252462SJerome Forissier                             line]
42830999126SJerome Forissier            return
429733a15f2SJerome Forissier        match = re.search(CALL_STACK_RE, line)
430733a15f2SJerome Forissier        if match:
431733a15f2SJerome Forissier            self._call_stack_found = True
432ae252462SJerome Forissier            if self._regions:
433ae252462SJerome Forissier                for r in self._regions:
434ae252462SJerome Forissier                    r_addr = r[0]
435ae252462SJerome Forissier                    r_size = r[1]
436ae252462SJerome Forissier                    elf_idx = r[2]
437ae252462SJerome Forissier                    saved_line = r[3]
438ae252462SJerome Forissier                    if elf_idx is None:
439ae252462SJerome Forissier                        self._out.write(saved_line)
440ae252462SJerome Forissier                    else:
441ae252462SJerome Forissier                        self._out.write(saved_line.strip() +
442ae252462SJerome Forissier                                        self.sections_in_region(r_addr,
443ae252462SJerome Forissier                                                                r_size,
444ae252462SJerome Forissier                                                                elf_idx) +
445ae252462SJerome Forissier                                        '\n')
446ae252462SJerome Forissier            if self._elfs:
447ae252462SJerome Forissier                for k in self._elfs:
448ae252462SJerome Forissier                    e = self._elfs[k]
449ae252462SJerome Forissier                    if (len(e) >= 3):
4501e6f2ea0SJerome Forissier                        # TA executable or library
451095567e5SJerome Forissier                        self._out.write(e[2].strip())
452095567e5SJerome Forissier                        elf = self.get_elf(e[0])
453095567e5SJerome Forissier                        if elf:
454095567e5SJerome Forissier                            rpath = os.path.realpath(elf)
455095567e5SJerome Forissier                            path = self.pretty_print_path(rpath)
456095567e5SJerome Forissier                            self._out.write(' (' + path + ')')
457095567e5SJerome Forissier                        self._out.write('\n')
458142c5cccSJerome Forissier            # Here is a good place to resolve the abort address because we
459142c5cccSJerome Forissier            # have all the information we need
460142c5cccSJerome Forissier            if self._saved_abort_line:
461142c5cccSJerome Forissier                self._out.write(self.process_abort(self._saved_abort_line))
462c90b6663SSumit Garg        match = re.search(FUNC_GRAPH_RE, line)
463c90b6663SSumit Garg        if match:
464c90b6663SSumit Garg            self._func_graph_found = True
465142c5cccSJerome Forissier        match = re.search(ABORT_ADDR_RE, line)
466142c5cccSJerome Forissier        if match:
46727b83ad2SJerome Forissier            self.reset()
468142c5cccSJerome Forissier            # At this point the arch and TA load address are unknown.
469142c5cccSJerome Forissier            # Save the line so We can translate the abort address later.
470142c5cccSJerome Forissier            self._saved_abort_line = line
471733a15f2SJerome Forissier        self._out.write(line)
472733a15f2SJerome Forissier
473733a15f2SJerome Forissier    def flush(self):
474733a15f2SJerome Forissier        self._out.flush()
475733a15f2SJerome Forissier
476ae252462SJerome Forissier
477733a15f2SJerome Forissierdef main():
478733a15f2SJerome Forissier    args = get_args()
479733a15f2SJerome Forissier    if args.dir:
480733a15f2SJerome Forissier        # Flatten list in case -d is used several times *and* with multiple
481733a15f2SJerome Forissier        # arguments
482733a15f2SJerome Forissier        args.dirs = [item for sublist in args.dir for item in sublist]
483733a15f2SJerome Forissier    else:
484733a15f2SJerome Forissier        args.dirs = []
485733a15f2SJerome Forissier    symbolizer = Symbolizer(sys.stdout, args.dirs, args.strip_path)
486733a15f2SJerome Forissier
4876b4fc675SJerome Forissier    fd = sys.stdin.fileno()
48820d152b8SJerome Forissier    isatty = os.isatty(fd)
48920d152b8SJerome Forissier    if isatty:
4906b4fc675SJerome Forissier        old = termios.tcgetattr(fd)
4916b4fc675SJerome Forissier        new = termios.tcgetattr(fd)
4926b4fc675SJerome Forissier        new[3] = new[3] & ~termios.ECHO  # lflags
4936b4fc675SJerome Forissier    try:
49420d152b8SJerome Forissier        if isatty:
4956b4fc675SJerome Forissier            termios.tcsetattr(fd, termios.TCSADRAIN, new)
496733a15f2SJerome Forissier        for line in sys.stdin:
497733a15f2SJerome Forissier            symbolizer.write(line)
4986b4fc675SJerome Forissier    finally:
499733a15f2SJerome Forissier        symbolizer.flush()
50020d152b8SJerome Forissier        if isatty:
5016b4fc675SJerome Forissier            termios.tcsetattr(fd, termios.TCSADRAIN, old)
502733a15f2SJerome Forissier
5031d8c2a48SJerome Forissier
504733a15f2SJerome Forissierif __name__ == "__main__":
505733a15f2SJerome Forissier    main()
506