xref: /optee_os/scripts/symbolize.py (revision 5500d70387c7d8097417cf391ea66f82fce605c0)
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
1329bb9f377SJerome 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)
314d7c22aceSJerome Forissier        if not elf:
315d7c22aceSJerome Forissier            return
31624778dedSJerome Forissier        cmd = self.arch_prefix('objdump', elf)
31730999126SJerome Forissier        if not elf or not cmd:
31830999126SJerome Forissier            return
319ae252462SJerome Forissier        self._sections[elf_name] = []
3201cbf777bSJerome Forissier        objdump = self.my_Popen([cmd, '--section-headers', elf])
32130999126SJerome Forissier        for line in iter(objdump.stdout.readline, ''):
32230999126SJerome Forissier            try:
32330999126SJerome Forissier                _, name, size, vma, _, _, _ = line.split()
3241d8c2a48SJerome Forissier            except ValueError:
32530999126SJerome Forissier                if 'ALLOC' in line:
326ae252462SJerome Forissier                    self._sections[elf_name].append([name, int(vma, 16),
327ae252462SJerome Forissier                                                     int(size, 16)])
32830999126SJerome Forissier
329c0c57c8fSJerome Forissier    def first_vma(self, elf_name):
330c0c57c8fSJerome Forissier        self.read_sections(elf_name)
331c0c57c8fSJerome Forissier        return '0x{:x}'.format(self._sections[elf_name][0][1])
332c0c57c8fSJerome Forissier
33330999126SJerome Forissier    def overlaps(self, section, addr, size):
33430999126SJerome Forissier        sec_addr = section[1]
33530999126SJerome Forissier        sec_size = section[2]
33630999126SJerome Forissier        if not size or not sec_size:
33730999126SJerome Forissier            return False
338ae252462SJerome Forissier        return ((addr <= (sec_addr + sec_size - 1)) and
339ae252462SJerome Forissier                ((addr + size - 1) >= sec_addr))
34030999126SJerome Forissier
341ae252462SJerome Forissier    def sections_in_region(self, addr, size, elf_idx):
34230999126SJerome Forissier        ret = ''
34330999126SJerome Forissier        addr = self.subtract_load_addr(addr)
34430999126SJerome Forissier        if not addr:
34530999126SJerome Forissier            return ''
34630999126SJerome Forissier        iaddr = int(addr, 16)
34730999126SJerome Forissier        isize = int(size, 16)
348ae252462SJerome Forissier        elf = self._elfs[int(elf_idx)][0]
349ae252462SJerome Forissier        if elf is None:
350ae252462SJerome Forissier            return ''
351ae252462SJerome Forissier        self.read_sections(elf)
352ae252462SJerome Forissier        if elf not in self._sections:
353ae252462SJerome Forissier            return ''
354ae252462SJerome Forissier        for s in self._sections[elf]:
35530999126SJerome Forissier            if self.overlaps(s, iaddr, isize):
35630999126SJerome Forissier                ret += ' ' + s[0]
35730999126SJerome Forissier        return ret
35830999126SJerome Forissier
359733a15f2SJerome Forissier    def reset(self):
360733a15f2SJerome Forissier        self._call_stack_found = False
361733a15f2SJerome Forissier        if self._addr2line:
362733a15f2SJerome Forissier            self._addr2line.terminate()
363733a15f2SJerome Forissier            self._addr2line = None
364ae252462SJerome Forissier        self._addr2line_elf_name = None
365d720431cSJerome Forissier        self._arch = None
366142c5cccSJerome Forissier        self._saved_abort_line = ''
367ae252462SJerome Forissier        self._sections = {}  # {elf_name: [[name, addr, size], ...], ...}
368ae252462SJerome Forissier        self._regions = []   # [[addr, size, elf_idx, saved line], ...]
369ae252462SJerome Forissier        self._elfs = {0: ["tee.elf", 0]}  # {idx: [uuid, load_addr], ...}
37091068f86SJerome Forissier        self._tee_load_addr = '0x0'
371c90b6663SSumit Garg        self._func_graph_found = False
372c90b6663SSumit Garg        self._func_graph_skip_line = True
373733a15f2SJerome Forissier
374095567e5SJerome Forissier    def pretty_print_path(self, path):
375095567e5SJerome Forissier        if self._strip_path:
376095567e5SJerome Forissier            return re.sub(re.escape(self._strip_path) + '/*', '', path)
377095567e5SJerome Forissier        return path
378095567e5SJerome Forissier
379733a15f2SJerome Forissier    def write(self, line):
380733a15f2SJerome Forissier        if self._call_stack_found:
381733a15f2SJerome Forissier            match = re.search(STACK_ADDR_RE, line)
382733a15f2SJerome Forissier            if match:
383733a15f2SJerome Forissier                addr = match.group('addr')
384733a15f2SJerome Forissier                pre = match.start('addr')
385733a15f2SJerome Forissier                post = match.end('addr')
386733a15f2SJerome Forissier                self._out.write(line[:pre])
387733a15f2SJerome Forissier                self._out.write(addr)
388*5500d703SJerome Forissier                # The call stack contains return addresses (LR/ELR values).
389*5500d703SJerome Forissier                # Heuristic: subtract 2 to obtain the call site of the function
390*5500d703SJerome Forissier                # or the location of the exception. This value works for A64,
391*5500d703SJerome Forissier                # A32 as well as Thumb.
392*5500d703SJerome Forissier                pc = 0
393*5500d703SJerome Forissier                lr = int(addr, 16)
394*5500d703SJerome Forissier                if lr:
395*5500d703SJerome Forissier                    pc = lr - 2
396*5500d703SJerome Forissier                res = self.resolve('0x{:x}'.format(pc))
397095567e5SJerome Forissier                res = self.pretty_print_path(res)
398733a15f2SJerome Forissier                self._out.write(' ' + res)
399733a15f2SJerome Forissier                self._out.write(line[post:])
400733a15f2SJerome Forissier                return
401733a15f2SJerome Forissier            else:
402733a15f2SJerome Forissier                self.reset()
403c90b6663SSumit Garg        if self._func_graph_found:
404c90b6663SSumit Garg            match = re.search(GRAPH_ADDR_RE, line)
405c90b6663SSumit Garg            match_re = re.search(GRAPH_RE, line)
406c90b6663SSumit Garg            if match:
407c90b6663SSumit Garg                addr = match.group('addr')
408c90b6663SSumit Garg                pre = match.start('addr')
409c90b6663SSumit Garg                post = match.end('addr')
410c90b6663SSumit Garg                self._out.write(line[:pre])
411c90b6663SSumit Garg                res = self.resolve(addr)
412c90b6663SSumit Garg                res_arr = re.split(' ', res)
413c90b6663SSumit Garg                self._out.write(res_arr[0])
414c90b6663SSumit Garg                self._out.write(line[post:])
415c90b6663SSumit Garg                self._func_graph_skip_line = False
416c90b6663SSumit Garg                return
417c90b6663SSumit Garg            elif match_re:
418c90b6663SSumit Garg                self._out.write(line)
419c90b6663SSumit Garg                return
420c90b6663SSumit Garg            elif self._func_graph_skip_line:
421c90b6663SSumit Garg                return
422c90b6663SSumit Garg            else:
423c90b6663SSumit Garg                self.reset()
42430999126SJerome Forissier        match = re.search(REGION_RE, line)
42530999126SJerome Forissier        if match:
426ae252462SJerome Forissier            # Region table: save info for later processing once
427ae252462SJerome Forissier            # we know which UUID corresponds to which ELF index
42830999126SJerome Forissier            addr = match.group('addr')
42930999126SJerome Forissier            size = match.group('size')
430ae252462SJerome Forissier            elf_idx = match.group('elf_idx')
431ae252462SJerome Forissier            self._regions.append([addr, size, elf_idx, line])
432ae252462SJerome Forissier            return
433ae252462SJerome Forissier        match = re.search(ELF_LIST_RE, line)
434ae252462SJerome Forissier        if match:
435ae252462SJerome Forissier            # ELF list: save info for later. Region table and ELF list
436ae252462SJerome Forissier            # will be displayed when the call stack is reached
437ae252462SJerome Forissier            i = int(match.group('idx'))
438ae252462SJerome Forissier            self._elfs[i] = [match.group('uuid'), match.group('load_addr'),
439ae252462SJerome Forissier                             line]
44030999126SJerome Forissier            return
441105e09c2SJerome Forissier        match = re.search(TEE_LOAD_ADDR_RE, line)
442105e09c2SJerome Forissier        if match:
443105e09c2SJerome Forissier            self._tee_load_addr = match.group('load_addr')
444733a15f2SJerome Forissier        match = re.search(CALL_STACK_RE, line)
445733a15f2SJerome Forissier        if match:
446733a15f2SJerome Forissier            self._call_stack_found = True
447ae252462SJerome Forissier            if self._regions:
448ae252462SJerome Forissier                for r in self._regions:
449ae252462SJerome Forissier                    r_addr = r[0]
450ae252462SJerome Forissier                    r_size = r[1]
451ae252462SJerome Forissier                    elf_idx = r[2]
452ae252462SJerome Forissier                    saved_line = r[3]
453ae252462SJerome Forissier                    if elf_idx is None:
454ae252462SJerome Forissier                        self._out.write(saved_line)
455ae252462SJerome Forissier                    else:
456ae252462SJerome Forissier                        self._out.write(saved_line.strip() +
457ae252462SJerome Forissier                                        self.sections_in_region(r_addr,
458ae252462SJerome Forissier                                                                r_size,
459ae252462SJerome Forissier                                                                elf_idx) +
460ae252462SJerome Forissier                                        '\n')
461ae252462SJerome Forissier            if self._elfs:
462ae252462SJerome Forissier                for k in self._elfs:
463ae252462SJerome Forissier                    e = self._elfs[k]
464ae252462SJerome Forissier                    if (len(e) >= 3):
4651e6f2ea0SJerome Forissier                        # TA executable or library
466095567e5SJerome Forissier                        self._out.write(e[2].strip())
467095567e5SJerome Forissier                        elf = self.get_elf(e[0])
468095567e5SJerome Forissier                        if elf:
469095567e5SJerome Forissier                            rpath = os.path.realpath(elf)
470095567e5SJerome Forissier                            path = self.pretty_print_path(rpath)
471095567e5SJerome Forissier                            self._out.write(' (' + path + ')')
472095567e5SJerome Forissier                        self._out.write('\n')
473142c5cccSJerome Forissier            # Here is a good place to resolve the abort address because we
474142c5cccSJerome Forissier            # have all the information we need
475142c5cccSJerome Forissier            if self._saved_abort_line:
476142c5cccSJerome Forissier                self._out.write(self.process_abort(self._saved_abort_line))
477c90b6663SSumit Garg        match = re.search(FUNC_GRAPH_RE, line)
478c90b6663SSumit Garg        if match:
479c90b6663SSumit Garg            self._func_graph_found = True
480142c5cccSJerome Forissier        match = re.search(ABORT_ADDR_RE, line)
481142c5cccSJerome Forissier        if match:
48227b83ad2SJerome Forissier            self.reset()
483142c5cccSJerome Forissier            # At this point the arch and TA load address are unknown.
484142c5cccSJerome Forissier            # Save the line so We can translate the abort address later.
485142c5cccSJerome Forissier            self._saved_abort_line = line
486733a15f2SJerome Forissier        self._out.write(line)
487733a15f2SJerome Forissier
488733a15f2SJerome Forissier    def flush(self):
489733a15f2SJerome Forissier        self._out.flush()
490733a15f2SJerome Forissier
491ae252462SJerome Forissier
492733a15f2SJerome Forissierdef main():
493733a15f2SJerome Forissier    args = get_args()
494733a15f2SJerome Forissier    if args.dir:
495733a15f2SJerome Forissier        # Flatten list in case -d is used several times *and* with multiple
496733a15f2SJerome Forissier        # arguments
497733a15f2SJerome Forissier        args.dirs = [item for sublist in args.dir for item in sublist]
498733a15f2SJerome Forissier    else:
499733a15f2SJerome Forissier        args.dirs = []
500733a15f2SJerome Forissier    symbolizer = Symbolizer(sys.stdout, args.dirs, args.strip_path)
501733a15f2SJerome Forissier
5026b4fc675SJerome Forissier    fd = sys.stdin.fileno()
50320d152b8SJerome Forissier    isatty = os.isatty(fd)
50420d152b8SJerome Forissier    if isatty:
5056b4fc675SJerome Forissier        old = termios.tcgetattr(fd)
5066b4fc675SJerome Forissier        new = termios.tcgetattr(fd)
5076b4fc675SJerome Forissier        new[3] = new[3] & ~termios.ECHO  # lflags
5086b4fc675SJerome Forissier    try:
50920d152b8SJerome Forissier        if isatty:
5106b4fc675SJerome Forissier            termios.tcsetattr(fd, termios.TCSADRAIN, new)
511733a15f2SJerome Forissier        for line in sys.stdin:
512733a15f2SJerome Forissier            symbolizer.write(line)
5136b4fc675SJerome Forissier    finally:
514733a15f2SJerome Forissier        symbolizer.flush()
51520d152b8SJerome Forissier        if isatty:
5166b4fc675SJerome Forissier            termios.tcsetattr(fd, termios.TCSADRAIN, old)
517733a15f2SJerome Forissier
5181d8c2a48SJerome Forissier
519733a15f2SJerome Forissierif __name__ == "__main__":
520733a15f2SJerome Forissier    main()
521