xref: /optee_os/scripts/symbolize.py (revision 9bb9f377846e53a376e8c458411287c092c05411)
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:')
18d77929ecSSumit GargTEE_LOAD_ADDR_RE = re.compile(r'TEE load address @ (?P<load_addr>0x[0-9a-f]+)')
19a2b984bdSJoakim Bech# This gets the address from lines looking like this:
20a2b984bdSJoakim Bech# E/TC:0  0x001044a8
216e7c2e91SJerome ForissierSTACK_ADDR_RE = re.compile(
22531963a5SJens Wiklander    r'[UEIDFM]/(TC|LD):(\?*|[0-9]*) [0-9]* +(?P<addr>0x[0-9a-f]+)')
236e7c2e91SJerome ForissierABORT_ADDR_RE = re.compile(r'-abort at address (?P<addr>0x[0-9a-f]+)')
24444c203eSJerome ForissierREGION_RE = re.compile(r'region +[0-9]+: va (?P<addr>0x[0-9a-f]+) '
256e7c2e91SJerome Forissier                       r'pa 0x[0-9a-f]+ size (?P<size>0x[0-9a-f]+)'
26531963a5SJens Wiklander                       r'( flags .{4} (\[(?P<elf_idx>[0-9]+)\])?)?')
27ae252462SJerome ForissierELF_LIST_RE = re.compile(r'\[(?P<idx>[0-9]+)\] (?P<uuid>[0-9a-f\-]+)'
286e7c2e91SJerome Forissier                         r' @ (?P<load_addr>0x[0-9a-f\-]+)')
29c90b6663SSumit GargFUNC_GRAPH_RE = re.compile(r'Function graph')
30c90b6663SSumit GargGRAPH_ADDR_RE = re.compile(r'(?P<addr>0x[0-9a-f]+)')
31c90b6663SSumit GargGRAPH_RE = re.compile(r'}')
32733a15f2SJerome Forissier
33733a15f2SJerome Forissierepilog = '''
340c5bedb5SJerome ForissierThis scripts reads an OP-TEE abort or panic message from stdin and adds debug
350c5bedb5SJerome Forissierinformation to the output, such as '<function> at <file>:<line>' next to each
360c5bedb5SJerome Forissieraddress in the call stack. Any message generated by OP-TEE and containing a
370c5bedb5SJerome Forissiercall stack can in principle be processed by this script. This currently
380c5bedb5SJerome Forissierincludes aborts and panics from the TEE core as well as from any TA.
390c5bedb5SJerome ForissierThe paths provided on the command line are used to locate the appropriate ELF
400c5bedb5SJerome Forissierbinary (tee.elf or Trusted Application). The GNU binutils (addr2line, objdump,
41f9089765SJerome Forissiernm) are used to extract the debug info. If the CROSS_COMPILE environment
42f9089765SJerome Forissiervariable is set, it is used as a prefix to the binutils tools. That is, the
43f9089765SJerome Forissierscript will invoke $(CROSS_COMPILE)addr2line etc. If it is not set however,
44f9089765SJerome Forissierthe prefix will be determined automatically for each ELF file based on its
45f9089765SJerome Forissierarchitecture (arm-linux-gnueabihf-, aarch64-linux-gnu-). The resulting command
46f9089765SJerome Forissieris then expected to be found in the user's PATH.
47733a15f2SJerome Forissier
480c5bedb5SJerome ForissierOP-TEE abort and panic messages are sent to the secure console. They look like
490c5bedb5SJerome Forissierthe following:
50733a15f2SJerome Forissier
510c5bedb5SJerome Forissier  E/TC:0 User TA data-abort at address 0xffffdecd (alignment fault)
52733a15f2SJerome Forissier  ...
530c5bedb5SJerome Forissier  E/TC:0 Call stack:
540c5bedb5SJerome Forissier  E/TC:0  0x4000549e
550c5bedb5SJerome Forissier  E/TC:0  0x40001f4b
560c5bedb5SJerome Forissier  E/TC:0  0x4000273f
570c5bedb5SJerome Forissier  E/TC:0  0x40005da7
58733a15f2SJerome Forissier
59733a15f2SJerome ForissierInspired by a script of the same name by the Chromium project.
60733a15f2SJerome Forissier
61733a15f2SJerome ForissierSample usage:
62733a15f2SJerome Forissier
63733a15f2SJerome Forissier  $ scripts/symbolize.py -d out/arm-plat-hikey/core -d ../optee_test/out/ta/*
64733a15f2SJerome Forissier  <paste whole dump here>
65733a15f2SJerome Forissier  ^D
66c90b6663SSumit Garg
67c90b6663SSumit GargAlso, this script reads function graph generated for OP-TEE user TA from
68c90b6663SSumit Garg/tmp/ftrace-<ta_uuid>.out file and resolves function addresses to corresponding
69c90b6663SSumit Gargsymbols.
70c90b6663SSumit Garg
71c90b6663SSumit GargSample usage:
72c90b6663SSumit Garg
73c90b6663SSumit Garg  $ cat /tmp/ftrace-<ta_uuid>.out | scripts/symbolize.py -d <ta_uuid>.elf
74c90b6663SSumit Garg  <paste function graph here>
75c90b6663SSumit Garg  ^D
76733a15f2SJerome Forissier'''
77733a15f2SJerome Forissier
78ae252462SJerome Forissier
79733a15f2SJerome Forissierdef get_args():
80733a15f2SJerome Forissier    parser = argparse.ArgumentParser(
81733a15f2SJerome Forissier        formatter_class=argparse.RawDescriptionHelpFormatter,
82c90b6663SSumit Garg        description='Symbolizes OP-TEE abort dumps or function graphs',
83733a15f2SJerome Forissier        epilog=epilog)
84733a15f2SJerome Forissier    parser.add_argument('-d', '--dir', action='append', nargs='+',
851d8c2a48SJerome Forissier                        help='Search for ELF file in DIR. tee.elf is needed '
861d8c2a48SJerome Forissier                        'to decode a TEE Core or pseudo-TA abort, while '
871d8c2a48SJerome Forissier                        '<TA_uuid>.elf is required if a user-mode TA has '
881d8c2a48SJerome Forissier                        'crashed. For convenience, ELF files may also be '
891d8c2a48SJerome Forissier                        'given.')
905f7df507SJerome Forissier    parser.add_argument('-s', '--strip_path', nargs='?',
911d8c2a48SJerome Forissier                        help='Strip STRIP_PATH from file paths (default: '
921d8c2a48SJerome Forissier                        'current directory, use -s with no argument to show '
931d8c2a48SJerome Forissier                        'full paths)', default=os.getcwd())
94733a15f2SJerome Forissier
95733a15f2SJerome Forissier    return parser.parse_args()
96733a15f2SJerome Forissier
97ae252462SJerome Forissier
98733a15f2SJerome Forissierclass Symbolizer(object):
99733a15f2SJerome Forissier    def __init__(self, out, dirs, strip_path):
100733a15f2SJerome Forissier        self._out = out
101733a15f2SJerome Forissier        self._dirs = dirs
102733a15f2SJerome Forissier        self._strip_path = strip_path
103733a15f2SJerome Forissier        self._addr2line = None
104733a15f2SJerome Forissier        self.reset()
105733a15f2SJerome Forissier
1061cbf777bSJerome Forissier    def my_Popen(self, cmd):
1071cbf777bSJerome Forissier        try:
1081cbf777bSJerome Forissier            return subprocess.Popen(cmd, stdin=subprocess.PIPE,
10917be223aSJerome Forissier                                    stdout=subprocess.PIPE,
11017be223aSJerome Forissier                                    universal_newlines=True,
111bbaeed4dSRouven Czerwinski                                    bufsize=1)
1121cbf777bSJerome Forissier        except OSError as e:
113bbaeed4dSRouven Czerwinski            if e.errno == errno.ENOENT:
114bbaeed4dSRouven Czerwinski                print("*** Error:{}: command not found".format(cmd[0]),
115bbaeed4dSRouven Czerwinski                      file=sys.stderr)
1161cbf777bSJerome Forissier                sys.exit(1)
1171cbf777bSJerome Forissier
118733a15f2SJerome Forissier    def get_elf(self, elf_or_uuid):
119733a15f2SJerome Forissier        if not elf_or_uuid.endswith('.elf'):
120733a15f2SJerome Forissier            elf_or_uuid += '.elf'
121733a15f2SJerome Forissier        for d in self._dirs:
122157e6213SJerome Forissier            if d.endswith(elf_or_uuid) and os.path.isfile(d):
123157e6213SJerome Forissier                return d
124733a15f2SJerome Forissier            elf = glob.glob(d + '/' + elf_or_uuid)
125733a15f2SJerome Forissier            if elf:
126733a15f2SJerome Forissier                return elf[0]
127733a15f2SJerome Forissier
12824778dedSJerome Forissier    def set_arch(self, elf):
1296e7c2e91SJerome Forissier        self._arch = os.getenv('CROSS_COMPILE')
1308a6d4a8bSEtienne Carriere        if self._arch:
1318a6d4a8bSEtienne Carriere            return
132*9bb9f377SJerome Forissier        p = subprocess.Popen(['file', '-L', elf], stdout=subprocess.PIPE)
133d720431cSJerome Forissier        output = p.stdout.readlines()
134d720431cSJerome Forissier        p.terminate()
135bbaeed4dSRouven Czerwinski        if b'ARM aarch64,' in output[0]:
136d720431cSJerome Forissier            self._arch = 'aarch64-linux-gnu-'
137bbaeed4dSRouven Czerwinski        elif b'ARM,' in output[0]:
138d720431cSJerome Forissier            self._arch = 'arm-linux-gnueabihf-'
139d720431cSJerome Forissier
14024778dedSJerome Forissier    def arch_prefix(self, cmd, elf):
14124778dedSJerome Forissier        self.set_arch(elf)
142ae252462SJerome Forissier        if self._arch is None:
143ae252462SJerome Forissier            return ''
144d720431cSJerome Forissier        return self._arch + cmd
145142c5cccSJerome Forissier
146ae252462SJerome Forissier    def spawn_addr2line(self, elf_name):
147ae252462SJerome Forissier        if elf_name is None:
148ae252462SJerome Forissier            return
149ae252462SJerome Forissier        if self._addr2line_elf_name is elf_name:
150ae252462SJerome Forissier            return
151ae252462SJerome Forissier        if self._addr2line:
152ae252462SJerome Forissier            self._addr2line.terminate
153ae252462SJerome Forissier            self._addr2line = None
154ae252462SJerome Forissier        elf = self.get_elf(elf_name)
155733a15f2SJerome Forissier        if not elf:
156733a15f2SJerome Forissier            return
15724778dedSJerome Forissier        cmd = self.arch_prefix('addr2line', elf)
158142c5cccSJerome Forissier        if not cmd:
159733a15f2SJerome Forissier            return
160c0c57c8fSJerome Forissier        self._addr2line = self.my_Popen([cmd, '-f', '-p', '-e', elf])
161ba84a3f5SJerome Forissier        self._addr2line_elf_name = elf_name
162ae252462SJerome Forissier
163ae252462SJerome Forissier    # If addr falls into a region that maps a TA ELF file, return the load
164ae252462SJerome Forissier    # address of that file.
165ae252462SJerome Forissier    def elf_load_addr(self, addr):
166ae252462SJerome Forissier        if self._regions:
167ae252462SJerome Forissier            for r in self._regions:
168ae252462SJerome Forissier                r_addr = int(r[0], 16)
169ae252462SJerome Forissier                r_size = int(r[1], 16)
170ae252462SJerome Forissier                i_addr = int(addr, 16)
171ae252462SJerome Forissier                if (i_addr >= r_addr and i_addr < (r_addr + r_size)):
172ae252462SJerome Forissier                    # Found region
173ae252462SJerome Forissier                    elf_idx = r[2]
174ae252462SJerome Forissier                    if elf_idx is not None:
175ae252462SJerome Forissier                        return self._elfs[int(elf_idx)][1]
176099918f6SSumit Garg            # In case address is not found in TA ELF file, fallback to tee.elf
177099918f6SSumit Garg            # especially to symbolize mixed (user-space and kernel) addresses
178099918f6SSumit Garg            # which is true when syscall ftrace is enabled along with TA
179099918f6SSumit Garg            # ftrace.
18091068f86SJerome Forissier            return self._tee_load_addr
181ae252462SJerome Forissier        else:
182ae252462SJerome Forissier            # tee.elf
183105e09c2SJerome Forissier            return self._tee_load_addr
184ae252462SJerome Forissier
185ae252462SJerome Forissier    def elf_for_addr(self, addr):
186ae252462SJerome Forissier        l_addr = self.elf_load_addr(addr)
18791068f86SJerome Forissier        if l_addr == self._tee_load_addr:
18891068f86SJerome Forissier            return 'tee.elf'
189ae252462SJerome Forissier        for k in self._elfs:
190ae252462SJerome Forissier            e = self._elfs[k]
191ae252462SJerome Forissier            if int(e[1], 16) == int(l_addr, 16):
192ae252462SJerome Forissier                return e[0]
193ae252462SJerome Forissier        return None
194733a15f2SJerome Forissier
195142c5cccSJerome Forissier    def subtract_load_addr(self, addr):
196ae252462SJerome Forissier        l_addr = self.elf_load_addr(addr)
197ae252462SJerome Forissier        if l_addr is None:
198ae252462SJerome Forissier            return None
199ae252462SJerome Forissier        if int(l_addr, 16) > int(addr, 16):
200142c5cccSJerome Forissier            return ''
201ae252462SJerome Forissier        return '0x{:x}'.format(int(addr, 16) - int(l_addr, 16))
202142c5cccSJerome Forissier
203142c5cccSJerome Forissier    def resolve(self, addr):
204142c5cccSJerome Forissier        reladdr = self.subtract_load_addr(addr)
205ae252462SJerome Forissier        self.spawn_addr2line(self.elf_for_addr(addr))
206142c5cccSJerome Forissier        if not reladdr or not self._addr2line:
207733a15f2SJerome Forissier            return '???'
208c0c57c8fSJerome Forissier        if self.elf_for_addr(addr) == 'tee.elf':
209c0c57c8fSJerome Forissier            reladdr = '0x{:x}'.format(int(reladdr, 16) +
210c0c57c8fSJerome Forissier                                      int(self.first_vma('tee.elf'), 16))
211733a15f2SJerome Forissier        try:
212bbaeed4dSRouven Czerwinski            print(reladdr, file=self._addr2line.stdin)
213733a15f2SJerome Forissier            ret = self._addr2line.stdout.readline().rstrip('\n')
214733a15f2SJerome Forissier        except IOError:
215733a15f2SJerome Forissier            ret = '!!!'
216733a15f2SJerome Forissier        return ret
217733a15f2SJerome Forissier
218142c5cccSJerome Forissier    def symbol_plus_offset(self, addr):
219142c5cccSJerome Forissier        ret = ''
220142c5cccSJerome Forissier        prevsize = 0
221142c5cccSJerome Forissier        reladdr = self.subtract_load_addr(addr)
222ae252462SJerome Forissier        elf_name = self.elf_for_addr(addr)
223ae252462SJerome Forissier        if elf_name is None:
224ae252462SJerome Forissier            return ''
225ae252462SJerome Forissier        elf = self.get_elf(elf_name)
22624778dedSJerome Forissier        cmd = self.arch_prefix('nm', elf)
227142c5cccSJerome Forissier        if not reladdr or not elf or not cmd:
228142c5cccSJerome Forissier            return ''
22930999126SJerome Forissier        ireladdr = int(reladdr, 16)
2301cbf777bSJerome Forissier        nm = self.my_Popen([cmd, '--numeric-sort', '--print-size', elf])
231142c5cccSJerome Forissier        for line in iter(nm.stdout.readline, ''):
232142c5cccSJerome Forissier            try:
233142c5cccSJerome Forissier                addr, size, _, name = line.split()
2341d8c2a48SJerome Forissier            except ValueError:
235142c5cccSJerome Forissier                # Size is missing
236b4815427SJerome Forissier                try:
237142c5cccSJerome Forissier                    addr, _, name = line.split()
238142c5cccSJerome Forissier                    size = '0'
2391d8c2a48SJerome Forissier                except ValueError:
240b4815427SJerome Forissier                    # E.g., undefined (external) symbols (line = "U symbol")
241b4815427SJerome Forissier                    continue
242142c5cccSJerome Forissier            iaddr = int(addr, 16)
243142c5cccSJerome Forissier            isize = int(size, 16)
244142c5cccSJerome Forissier            if iaddr == ireladdr:
245142c5cccSJerome Forissier                ret = name
246142c5cccSJerome Forissier                break
247142c5cccSJerome Forissier            if iaddr < ireladdr and iaddr + isize >= ireladdr:
248142c5cccSJerome Forissier                offs = ireladdr - iaddr
249142c5cccSJerome Forissier                ret = name + '+' + str(offs)
250142c5cccSJerome Forissier                break
251142c5cccSJerome Forissier            if iaddr > ireladdr and prevsize == 0:
252142c5cccSJerome Forissier                offs = iaddr + ireladdr
253142c5cccSJerome Forissier                ret = prevname + '+' + str(offs)
254142c5cccSJerome Forissier                break
255142c5cccSJerome Forissier            prevsize = size
256142c5cccSJerome Forissier            prevname = name
257142c5cccSJerome Forissier        nm.terminate()
258142c5cccSJerome Forissier        return ret
259142c5cccSJerome Forissier
260142c5cccSJerome Forissier    def section_plus_offset(self, addr):
261142c5cccSJerome Forissier        ret = ''
262142c5cccSJerome Forissier        reladdr = self.subtract_load_addr(addr)
263ae252462SJerome Forissier        elf_name = self.elf_for_addr(addr)
264ae252462SJerome Forissier        if elf_name is None:
265ae252462SJerome Forissier            return ''
266ae252462SJerome Forissier        elf = self.get_elf(elf_name)
26724778dedSJerome Forissier        cmd = self.arch_prefix('objdump', elf)
268142c5cccSJerome Forissier        if not reladdr or not elf or not cmd:
269142c5cccSJerome Forissier            return ''
27030999126SJerome Forissier        iaddr = int(reladdr, 16)
2711cbf777bSJerome Forissier        objdump = self.my_Popen([cmd, '--section-headers', elf])
272142c5cccSJerome Forissier        for line in iter(objdump.stdout.readline, ''):
273142c5cccSJerome Forissier            try:
274142c5cccSJerome Forissier                idx, name, size, vma, lma, offs, algn = line.split()
2751d8c2a48SJerome Forissier            except ValueError:
276ae252462SJerome Forissier                continue
277142c5cccSJerome Forissier            ivma = int(vma, 16)
278142c5cccSJerome Forissier            isize = int(size, 16)
279142c5cccSJerome Forissier            if ivma == iaddr:
280142c5cccSJerome Forissier                ret = name
281142c5cccSJerome Forissier                break
282142c5cccSJerome Forissier            if ivma < iaddr and ivma + isize >= iaddr:
283142c5cccSJerome Forissier                offs = iaddr - ivma
284142c5cccSJerome Forissier                ret = name + '+' + str(offs)
285142c5cccSJerome Forissier                break
286142c5cccSJerome Forissier        objdump.terminate()
287142c5cccSJerome Forissier        return ret
288142c5cccSJerome Forissier
289142c5cccSJerome Forissier    def process_abort(self, line):
290142c5cccSJerome Forissier        ret = ''
291142c5cccSJerome Forissier        match = re.search(ABORT_ADDR_RE, line)
292142c5cccSJerome Forissier        addr = match.group('addr')
293142c5cccSJerome Forissier        pre = match.start('addr')
294142c5cccSJerome Forissier        post = match.end('addr')
295142c5cccSJerome Forissier        sym = self.symbol_plus_offset(addr)
296142c5cccSJerome Forissier        sec = self.section_plus_offset(addr)
297142c5cccSJerome Forissier        if sym or sec:
298142c5cccSJerome Forissier            ret += line[:pre]
299142c5cccSJerome Forissier            ret += addr
300142c5cccSJerome Forissier            if sym:
301142c5cccSJerome Forissier                ret += ' ' + sym
302142c5cccSJerome Forissier            if sec:
303142c5cccSJerome Forissier                ret += ' ' + sec
304142c5cccSJerome Forissier            ret += line[post:]
305142c5cccSJerome Forissier        return ret
306142c5cccSJerome Forissier
30730999126SJerome Forissier    # Return all ELF sections with the ALLOC flag
308ae252462SJerome Forissier    def read_sections(self, elf_name):
309ae252462SJerome Forissier        if elf_name is None:
31030999126SJerome Forissier            return
311ae252462SJerome Forissier        if elf_name in self._sections:
312ae252462SJerome Forissier            return
313ae252462SJerome Forissier        elf = self.get_elf(elf_name)
31424778dedSJerome Forissier        cmd = self.arch_prefix('objdump', elf)
31530999126SJerome Forissier        if not elf or not cmd:
31630999126SJerome Forissier            return
317ae252462SJerome Forissier        self._sections[elf_name] = []
3181cbf777bSJerome Forissier        objdump = self.my_Popen([cmd, '--section-headers', elf])
31930999126SJerome Forissier        for line in iter(objdump.stdout.readline, ''):
32030999126SJerome Forissier            try:
32130999126SJerome Forissier                _, name, size, vma, _, _, _ = line.split()
3221d8c2a48SJerome Forissier            except ValueError:
32330999126SJerome Forissier                if 'ALLOC' in line:
324ae252462SJerome Forissier                    self._sections[elf_name].append([name, int(vma, 16),
325ae252462SJerome Forissier                                                     int(size, 16)])
32630999126SJerome Forissier
327c0c57c8fSJerome Forissier    def first_vma(self, elf_name):
328c0c57c8fSJerome Forissier        self.read_sections(elf_name)
329c0c57c8fSJerome Forissier        return '0x{:x}'.format(self._sections[elf_name][0][1])
330c0c57c8fSJerome Forissier
33130999126SJerome Forissier    def overlaps(self, section, addr, size):
33230999126SJerome Forissier        sec_addr = section[1]
33330999126SJerome Forissier        sec_size = section[2]
33430999126SJerome Forissier        if not size or not sec_size:
33530999126SJerome Forissier            return False
336ae252462SJerome Forissier        return ((addr <= (sec_addr + sec_size - 1)) and
337ae252462SJerome Forissier                ((addr + size - 1) >= sec_addr))
33830999126SJerome Forissier
339ae252462SJerome Forissier    def sections_in_region(self, addr, size, elf_idx):
34030999126SJerome Forissier        ret = ''
34130999126SJerome Forissier        addr = self.subtract_load_addr(addr)
34230999126SJerome Forissier        if not addr:
34330999126SJerome Forissier            return ''
34430999126SJerome Forissier        iaddr = int(addr, 16)
34530999126SJerome Forissier        isize = int(size, 16)
346ae252462SJerome Forissier        elf = self._elfs[int(elf_idx)][0]
347ae252462SJerome Forissier        if elf is None:
348ae252462SJerome Forissier            return ''
349ae252462SJerome Forissier        self.read_sections(elf)
350ae252462SJerome Forissier        if elf not in self._sections:
351ae252462SJerome Forissier            return ''
352ae252462SJerome Forissier        for s in self._sections[elf]:
35330999126SJerome Forissier            if self.overlaps(s, iaddr, isize):
35430999126SJerome Forissier                ret += ' ' + s[0]
35530999126SJerome Forissier        return ret
35630999126SJerome Forissier
357733a15f2SJerome Forissier    def reset(self):
358733a15f2SJerome Forissier        self._call_stack_found = False
359733a15f2SJerome Forissier        if self._addr2line:
360733a15f2SJerome Forissier            self._addr2line.terminate()
361733a15f2SJerome Forissier            self._addr2line = None
362ae252462SJerome Forissier        self._addr2line_elf_name = None
363d720431cSJerome Forissier        self._arch = None
364142c5cccSJerome Forissier        self._saved_abort_line = ''
365ae252462SJerome Forissier        self._sections = {}  # {elf_name: [[name, addr, size], ...], ...}
366ae252462SJerome Forissier        self._regions = []   # [[addr, size, elf_idx, saved line], ...]
367ae252462SJerome Forissier        self._elfs = {0: ["tee.elf", 0]}  # {idx: [uuid, load_addr], ...}
36891068f86SJerome Forissier        self._tee_load_addr = '0x0'
369c90b6663SSumit Garg        self._func_graph_found = False
370c90b6663SSumit Garg        self._func_graph_skip_line = True
371733a15f2SJerome Forissier
372095567e5SJerome Forissier    def pretty_print_path(self, path):
373095567e5SJerome Forissier        if self._strip_path:
374095567e5SJerome Forissier            return re.sub(re.escape(self._strip_path) + '/*', '', path)
375095567e5SJerome Forissier        return path
376095567e5SJerome Forissier
377733a15f2SJerome Forissier    def write(self, line):
378733a15f2SJerome Forissier        if self._call_stack_found:
379733a15f2SJerome Forissier            match = re.search(STACK_ADDR_RE, line)
380733a15f2SJerome Forissier            if match:
381733a15f2SJerome Forissier                addr = match.group('addr')
382733a15f2SJerome Forissier                pre = match.start('addr')
383733a15f2SJerome Forissier                post = match.end('addr')
384733a15f2SJerome Forissier                self._out.write(line[:pre])
385733a15f2SJerome Forissier                self._out.write(addr)
386733a15f2SJerome Forissier                res = self.resolve(addr)
387095567e5SJerome Forissier                res = self.pretty_print_path(res)
388733a15f2SJerome Forissier                self._out.write(' ' + res)
389733a15f2SJerome Forissier                self._out.write(line[post:])
390733a15f2SJerome Forissier                return
391733a15f2SJerome Forissier            else:
392733a15f2SJerome Forissier                self.reset()
393c90b6663SSumit Garg        if self._func_graph_found:
394c90b6663SSumit Garg            match = re.search(GRAPH_ADDR_RE, line)
395c90b6663SSumit Garg            match_re = re.search(GRAPH_RE, line)
396c90b6663SSumit Garg            if match:
397c90b6663SSumit Garg                addr = match.group('addr')
398c90b6663SSumit Garg                pre = match.start('addr')
399c90b6663SSumit Garg                post = match.end('addr')
400c90b6663SSumit Garg                self._out.write(line[:pre])
401c90b6663SSumit Garg                res = self.resolve(addr)
402c90b6663SSumit Garg                res_arr = re.split(' ', res)
403c90b6663SSumit Garg                self._out.write(res_arr[0])
404c90b6663SSumit Garg                self._out.write(line[post:])
405c90b6663SSumit Garg                self._func_graph_skip_line = False
406c90b6663SSumit Garg                return
407c90b6663SSumit Garg            elif match_re:
408c90b6663SSumit Garg                self._out.write(line)
409c90b6663SSumit Garg                return
410c90b6663SSumit Garg            elif self._func_graph_skip_line:
411c90b6663SSumit Garg                return
412c90b6663SSumit Garg            else:
413c90b6663SSumit Garg                self.reset()
41430999126SJerome Forissier        match = re.search(REGION_RE, line)
41530999126SJerome Forissier        if match:
416ae252462SJerome Forissier            # Region table: save info for later processing once
417ae252462SJerome Forissier            # we know which UUID corresponds to which ELF index
41830999126SJerome Forissier            addr = match.group('addr')
41930999126SJerome Forissier            size = match.group('size')
420ae252462SJerome Forissier            elf_idx = match.group('elf_idx')
421ae252462SJerome Forissier            self._regions.append([addr, size, elf_idx, line])
422ae252462SJerome Forissier            return
423ae252462SJerome Forissier        match = re.search(ELF_LIST_RE, line)
424ae252462SJerome Forissier        if match:
425ae252462SJerome Forissier            # ELF list: save info for later. Region table and ELF list
426ae252462SJerome Forissier            # will be displayed when the call stack is reached
427ae252462SJerome Forissier            i = int(match.group('idx'))
428ae252462SJerome Forissier            self._elfs[i] = [match.group('uuid'), match.group('load_addr'),
429ae252462SJerome Forissier                             line]
43030999126SJerome Forissier            return
431105e09c2SJerome Forissier        match = re.search(TEE_LOAD_ADDR_RE, line)
432105e09c2SJerome Forissier        if match:
433105e09c2SJerome Forissier            self._tee_load_addr = match.group('load_addr')
434733a15f2SJerome Forissier        match = re.search(CALL_STACK_RE, line)
435733a15f2SJerome Forissier        if match:
436733a15f2SJerome Forissier            self._call_stack_found = True
437ae252462SJerome Forissier            if self._regions:
438ae252462SJerome Forissier                for r in self._regions:
439ae252462SJerome Forissier                    r_addr = r[0]
440ae252462SJerome Forissier                    r_size = r[1]
441ae252462SJerome Forissier                    elf_idx = r[2]
442ae252462SJerome Forissier                    saved_line = r[3]
443ae252462SJerome Forissier                    if elf_idx is None:
444ae252462SJerome Forissier                        self._out.write(saved_line)
445ae252462SJerome Forissier                    else:
446ae252462SJerome Forissier                        self._out.write(saved_line.strip() +
447ae252462SJerome Forissier                                        self.sections_in_region(r_addr,
448ae252462SJerome Forissier                                                                r_size,
449ae252462SJerome Forissier                                                                elf_idx) +
450ae252462SJerome Forissier                                        '\n')
451ae252462SJerome Forissier            if self._elfs:
452ae252462SJerome Forissier                for k in self._elfs:
453ae252462SJerome Forissier                    e = self._elfs[k]
454ae252462SJerome Forissier                    if (len(e) >= 3):
4551e6f2ea0SJerome Forissier                        # TA executable or library
456095567e5SJerome Forissier                        self._out.write(e[2].strip())
457095567e5SJerome Forissier                        elf = self.get_elf(e[0])
458095567e5SJerome Forissier                        if elf:
459095567e5SJerome Forissier                            rpath = os.path.realpath(elf)
460095567e5SJerome Forissier                            path = self.pretty_print_path(rpath)
461095567e5SJerome Forissier                            self._out.write(' (' + path + ')')
462095567e5SJerome Forissier                        self._out.write('\n')
463142c5cccSJerome Forissier            # Here is a good place to resolve the abort address because we
464142c5cccSJerome Forissier            # have all the information we need
465142c5cccSJerome Forissier            if self._saved_abort_line:
466142c5cccSJerome Forissier                self._out.write(self.process_abort(self._saved_abort_line))
467c90b6663SSumit Garg        match = re.search(FUNC_GRAPH_RE, line)
468c90b6663SSumit Garg        if match:
469c90b6663SSumit Garg            self._func_graph_found = True
470142c5cccSJerome Forissier        match = re.search(ABORT_ADDR_RE, line)
471142c5cccSJerome Forissier        if match:
47227b83ad2SJerome Forissier            self.reset()
473142c5cccSJerome Forissier            # At this point the arch and TA load address are unknown.
474142c5cccSJerome Forissier            # Save the line so We can translate the abort address later.
475142c5cccSJerome Forissier            self._saved_abort_line = line
476733a15f2SJerome Forissier        self._out.write(line)
477733a15f2SJerome Forissier
478733a15f2SJerome Forissier    def flush(self):
479733a15f2SJerome Forissier        self._out.flush()
480733a15f2SJerome Forissier
481ae252462SJerome Forissier
482733a15f2SJerome Forissierdef main():
483733a15f2SJerome Forissier    args = get_args()
484733a15f2SJerome Forissier    if args.dir:
485733a15f2SJerome Forissier        # Flatten list in case -d is used several times *and* with multiple
486733a15f2SJerome Forissier        # arguments
487733a15f2SJerome Forissier        args.dirs = [item for sublist in args.dir for item in sublist]
488733a15f2SJerome Forissier    else:
489733a15f2SJerome Forissier        args.dirs = []
490733a15f2SJerome Forissier    symbolizer = Symbolizer(sys.stdout, args.dirs, args.strip_path)
491733a15f2SJerome Forissier
4926b4fc675SJerome Forissier    fd = sys.stdin.fileno()
49320d152b8SJerome Forissier    isatty = os.isatty(fd)
49420d152b8SJerome Forissier    if isatty:
4956b4fc675SJerome Forissier        old = termios.tcgetattr(fd)
4966b4fc675SJerome Forissier        new = termios.tcgetattr(fd)
4976b4fc675SJerome Forissier        new[3] = new[3] & ~termios.ECHO  # lflags
4986b4fc675SJerome Forissier    try:
49920d152b8SJerome Forissier        if isatty:
5006b4fc675SJerome Forissier            termios.tcsetattr(fd, termios.TCSADRAIN, new)
501733a15f2SJerome Forissier        for line in sys.stdin:
502733a15f2SJerome Forissier            symbolizer.write(line)
5036b4fc675SJerome Forissier    finally:
504733a15f2SJerome Forissier        symbolizer.flush()
50520d152b8SJerome Forissier        if isatty:
5066b4fc675SJerome Forissier            termios.tcsetattr(fd, termios.TCSADRAIN, old)
507733a15f2SJerome Forissier
5081d8c2a48SJerome Forissier
509733a15f2SJerome Forissierif __name__ == "__main__":
510733a15f2SJerome Forissier    main()
511