xref: /optee_os/scripts/symbolize.py (revision 2a0d456fa09fba195ab615420d510a041398dbe5)
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
218*2a0d456fSJerome Forissier    # Armv8.5 with Memory Tagging Extension (MTE)
219*2a0d456fSJerome Forissier    def strip_armv85_mte_tag(self, addr):
220*2a0d456fSJerome Forissier        i_addr = int(addr, 16)
221*2a0d456fSJerome Forissier        i_addr &= ~(0xf << 56)
222*2a0d456fSJerome Forissier        return '0x{:x}'.format(i_addr)
223*2a0d456fSJerome Forissier
224142c5cccSJerome Forissier    def symbol_plus_offset(self, addr):
225142c5cccSJerome Forissier        ret = ''
226142c5cccSJerome Forissier        prevsize = 0
227*2a0d456fSJerome Forissier        addr = self.strip_armv85_mte_tag(addr)
228142c5cccSJerome Forissier        reladdr = self.subtract_load_addr(addr)
229ae252462SJerome Forissier        elf_name = self.elf_for_addr(addr)
230ae252462SJerome Forissier        if elf_name is None:
231ae252462SJerome Forissier            return ''
232ae252462SJerome Forissier        elf = self.get_elf(elf_name)
23324778dedSJerome Forissier        cmd = self.arch_prefix('nm', elf)
234142c5cccSJerome Forissier        if not reladdr or not elf or not cmd:
235142c5cccSJerome Forissier            return ''
23630999126SJerome Forissier        ireladdr = int(reladdr, 16)
2371cbf777bSJerome Forissier        nm = self.my_Popen([cmd, '--numeric-sort', '--print-size', elf])
238142c5cccSJerome Forissier        for line in iter(nm.stdout.readline, ''):
239142c5cccSJerome Forissier            try:
240142c5cccSJerome Forissier                addr, size, _, name = line.split()
2411d8c2a48SJerome Forissier            except ValueError:
242142c5cccSJerome Forissier                # Size is missing
243b4815427SJerome Forissier                try:
244142c5cccSJerome Forissier                    addr, _, name = line.split()
245142c5cccSJerome Forissier                    size = '0'
2461d8c2a48SJerome Forissier                except ValueError:
247b4815427SJerome Forissier                    # E.g., undefined (external) symbols (line = "U symbol")
248b4815427SJerome Forissier                    continue
249142c5cccSJerome Forissier            iaddr = int(addr, 16)
250142c5cccSJerome Forissier            isize = int(size, 16)
251142c5cccSJerome Forissier            if iaddr == ireladdr:
252142c5cccSJerome Forissier                ret = name
253142c5cccSJerome Forissier                break
254142c5cccSJerome Forissier            if iaddr < ireladdr and iaddr + isize >= ireladdr:
255142c5cccSJerome Forissier                offs = ireladdr - iaddr
256142c5cccSJerome Forissier                ret = name + '+' + str(offs)
257142c5cccSJerome Forissier                break
258142c5cccSJerome Forissier            if iaddr > ireladdr and prevsize == 0:
259142c5cccSJerome Forissier                offs = iaddr + ireladdr
260142c5cccSJerome Forissier                ret = prevname + '+' + str(offs)
261142c5cccSJerome Forissier                break
262142c5cccSJerome Forissier            prevsize = size
263142c5cccSJerome Forissier            prevname = name
264142c5cccSJerome Forissier        nm.terminate()
265142c5cccSJerome Forissier        return ret
266142c5cccSJerome Forissier
267142c5cccSJerome Forissier    def section_plus_offset(self, addr):
268142c5cccSJerome Forissier        ret = ''
269142c5cccSJerome Forissier        reladdr = self.subtract_load_addr(addr)
270ae252462SJerome Forissier        elf_name = self.elf_for_addr(addr)
271ae252462SJerome Forissier        if elf_name is None:
272ae252462SJerome Forissier            return ''
273ae252462SJerome Forissier        elf = self.get_elf(elf_name)
27424778dedSJerome Forissier        cmd = self.arch_prefix('objdump', elf)
275142c5cccSJerome Forissier        if not reladdr or not elf or not cmd:
276142c5cccSJerome Forissier            return ''
27730999126SJerome Forissier        iaddr = int(reladdr, 16)
2781cbf777bSJerome Forissier        objdump = self.my_Popen([cmd, '--section-headers', elf])
279142c5cccSJerome Forissier        for line in iter(objdump.stdout.readline, ''):
280142c5cccSJerome Forissier            try:
281142c5cccSJerome Forissier                idx, name, size, vma, lma, offs, algn = line.split()
2821d8c2a48SJerome Forissier            except ValueError:
283ae252462SJerome Forissier                continue
284142c5cccSJerome Forissier            ivma = int(vma, 16)
285142c5cccSJerome Forissier            isize = int(size, 16)
286142c5cccSJerome Forissier            if ivma == iaddr:
287142c5cccSJerome Forissier                ret = name
288142c5cccSJerome Forissier                break
289142c5cccSJerome Forissier            if ivma < iaddr and ivma + isize >= iaddr:
290142c5cccSJerome Forissier                offs = iaddr - ivma
291142c5cccSJerome Forissier                ret = name + '+' + str(offs)
292142c5cccSJerome Forissier                break
293142c5cccSJerome Forissier        objdump.terminate()
294142c5cccSJerome Forissier        return ret
295142c5cccSJerome Forissier
296142c5cccSJerome Forissier    def process_abort(self, line):
297142c5cccSJerome Forissier        ret = ''
298142c5cccSJerome Forissier        match = re.search(ABORT_ADDR_RE, line)
299142c5cccSJerome Forissier        addr = match.group('addr')
300142c5cccSJerome Forissier        pre = match.start('addr')
301142c5cccSJerome Forissier        post = match.end('addr')
302142c5cccSJerome Forissier        sym = self.symbol_plus_offset(addr)
303142c5cccSJerome Forissier        sec = self.section_plus_offset(addr)
304142c5cccSJerome Forissier        if sym or sec:
305142c5cccSJerome Forissier            ret += line[:pre]
306142c5cccSJerome Forissier            ret += addr
307142c5cccSJerome Forissier            if sym:
308142c5cccSJerome Forissier                ret += ' ' + sym
309142c5cccSJerome Forissier            if sec:
310142c5cccSJerome Forissier                ret += ' ' + sec
311142c5cccSJerome Forissier            ret += line[post:]
312142c5cccSJerome Forissier        return ret
313142c5cccSJerome Forissier
31430999126SJerome Forissier    # Return all ELF sections with the ALLOC flag
315ae252462SJerome Forissier    def read_sections(self, elf_name):
316ae252462SJerome Forissier        if elf_name is None:
31730999126SJerome Forissier            return
318ae252462SJerome Forissier        if elf_name in self._sections:
319ae252462SJerome Forissier            return
320ae252462SJerome Forissier        elf = self.get_elf(elf_name)
321d7c22aceSJerome Forissier        if not elf:
322d7c22aceSJerome Forissier            return
32324778dedSJerome Forissier        cmd = self.arch_prefix('objdump', elf)
32430999126SJerome Forissier        if not elf or not cmd:
32530999126SJerome Forissier            return
326ae252462SJerome Forissier        self._sections[elf_name] = []
3271cbf777bSJerome Forissier        objdump = self.my_Popen([cmd, '--section-headers', elf])
32830999126SJerome Forissier        for line in iter(objdump.stdout.readline, ''):
32930999126SJerome Forissier            try:
33030999126SJerome Forissier                _, name, size, vma, _, _, _ = line.split()
3311d8c2a48SJerome Forissier            except ValueError:
33230999126SJerome Forissier                if 'ALLOC' in line:
333ae252462SJerome Forissier                    self._sections[elf_name].append([name, int(vma, 16),
334ae252462SJerome Forissier                                                     int(size, 16)])
33530999126SJerome Forissier
336c0c57c8fSJerome Forissier    def first_vma(self, elf_name):
337c0c57c8fSJerome Forissier        self.read_sections(elf_name)
338c0c57c8fSJerome Forissier        return '0x{:x}'.format(self._sections[elf_name][0][1])
339c0c57c8fSJerome Forissier
34030999126SJerome Forissier    def overlaps(self, section, addr, size):
34130999126SJerome Forissier        sec_addr = section[1]
34230999126SJerome Forissier        sec_size = section[2]
34330999126SJerome Forissier        if not size or not sec_size:
34430999126SJerome Forissier            return False
345ae252462SJerome Forissier        return ((addr <= (sec_addr + sec_size - 1)) and
346ae252462SJerome Forissier                ((addr + size - 1) >= sec_addr))
34730999126SJerome Forissier
348ae252462SJerome Forissier    def sections_in_region(self, addr, size, elf_idx):
34930999126SJerome Forissier        ret = ''
35030999126SJerome Forissier        addr = self.subtract_load_addr(addr)
35130999126SJerome Forissier        if not addr:
35230999126SJerome Forissier            return ''
35330999126SJerome Forissier        iaddr = int(addr, 16)
35430999126SJerome Forissier        isize = int(size, 16)
355ae252462SJerome Forissier        elf = self._elfs[int(elf_idx)][0]
356ae252462SJerome Forissier        if elf is None:
357ae252462SJerome Forissier            return ''
358ae252462SJerome Forissier        self.read_sections(elf)
359ae252462SJerome Forissier        if elf not in self._sections:
360ae252462SJerome Forissier            return ''
361ae252462SJerome Forissier        for s in self._sections[elf]:
36230999126SJerome Forissier            if self.overlaps(s, iaddr, isize):
36330999126SJerome Forissier                ret += ' ' + s[0]
36430999126SJerome Forissier        return ret
36530999126SJerome Forissier
366733a15f2SJerome Forissier    def reset(self):
367733a15f2SJerome Forissier        self._call_stack_found = False
368733a15f2SJerome Forissier        if self._addr2line:
369733a15f2SJerome Forissier            self._addr2line.terminate()
370733a15f2SJerome Forissier            self._addr2line = None
371ae252462SJerome Forissier        self._addr2line_elf_name = None
372d720431cSJerome Forissier        self._arch = None
373142c5cccSJerome Forissier        self._saved_abort_line = ''
374ae252462SJerome Forissier        self._sections = {}  # {elf_name: [[name, addr, size], ...], ...}
375ae252462SJerome Forissier        self._regions = []   # [[addr, size, elf_idx, saved line], ...]
376ae252462SJerome Forissier        self._elfs = {0: ["tee.elf", 0]}  # {idx: [uuid, load_addr], ...}
37791068f86SJerome Forissier        self._tee_load_addr = '0x0'
378c90b6663SSumit Garg        self._func_graph_found = False
379c90b6663SSumit Garg        self._func_graph_skip_line = True
380733a15f2SJerome Forissier
381095567e5SJerome Forissier    def pretty_print_path(self, path):
382095567e5SJerome Forissier        if self._strip_path:
383095567e5SJerome Forissier            return re.sub(re.escape(self._strip_path) + '/*', '', path)
384095567e5SJerome Forissier        return path
385095567e5SJerome Forissier
386733a15f2SJerome Forissier    def write(self, line):
387733a15f2SJerome Forissier        if self._call_stack_found:
388733a15f2SJerome Forissier            match = re.search(STACK_ADDR_RE, line)
389733a15f2SJerome Forissier            if match:
390733a15f2SJerome Forissier                addr = match.group('addr')
391733a15f2SJerome Forissier                pre = match.start('addr')
392733a15f2SJerome Forissier                post = match.end('addr')
393733a15f2SJerome Forissier                self._out.write(line[:pre])
394733a15f2SJerome Forissier                self._out.write(addr)
3955500d703SJerome Forissier                # The call stack contains return addresses (LR/ELR values).
3965500d703SJerome Forissier                # Heuristic: subtract 2 to obtain the call site of the function
3975500d703SJerome Forissier                # or the location of the exception. This value works for A64,
3985500d703SJerome Forissier                # A32 as well as Thumb.
3995500d703SJerome Forissier                pc = 0
4005500d703SJerome Forissier                lr = int(addr, 16)
4015500d703SJerome Forissier                if lr:
4025500d703SJerome Forissier                    pc = lr - 2
4035500d703SJerome Forissier                res = self.resolve('0x{:x}'.format(pc))
404095567e5SJerome Forissier                res = self.pretty_print_path(res)
405733a15f2SJerome Forissier                self._out.write(' ' + res)
406733a15f2SJerome Forissier                self._out.write(line[post:])
407733a15f2SJerome Forissier                return
408733a15f2SJerome Forissier            else:
409733a15f2SJerome Forissier                self.reset()
410c90b6663SSumit Garg        if self._func_graph_found:
411c90b6663SSumit Garg            match = re.search(GRAPH_ADDR_RE, line)
412c90b6663SSumit Garg            match_re = re.search(GRAPH_RE, line)
413c90b6663SSumit Garg            if match:
414c90b6663SSumit Garg                addr = match.group('addr')
415c90b6663SSumit Garg                pre = match.start('addr')
416c90b6663SSumit Garg                post = match.end('addr')
417c90b6663SSumit Garg                self._out.write(line[:pre])
418c90b6663SSumit Garg                res = self.resolve(addr)
419c90b6663SSumit Garg                res_arr = re.split(' ', res)
420c90b6663SSumit Garg                self._out.write(res_arr[0])
421c90b6663SSumit Garg                self._out.write(line[post:])
422c90b6663SSumit Garg                self._func_graph_skip_line = False
423c90b6663SSumit Garg                return
424c90b6663SSumit Garg            elif match_re:
425c90b6663SSumit Garg                self._out.write(line)
426c90b6663SSumit Garg                return
427c90b6663SSumit Garg            elif self._func_graph_skip_line:
428c90b6663SSumit Garg                return
429c90b6663SSumit Garg            else:
430c90b6663SSumit Garg                self.reset()
43130999126SJerome Forissier        match = re.search(REGION_RE, line)
43230999126SJerome Forissier        if match:
433ae252462SJerome Forissier            # Region table: save info for later processing once
434ae252462SJerome Forissier            # we know which UUID corresponds to which ELF index
43530999126SJerome Forissier            addr = match.group('addr')
43630999126SJerome Forissier            size = match.group('size')
437ae252462SJerome Forissier            elf_idx = match.group('elf_idx')
438ae252462SJerome Forissier            self._regions.append([addr, size, elf_idx, line])
439ae252462SJerome Forissier            return
440ae252462SJerome Forissier        match = re.search(ELF_LIST_RE, line)
441ae252462SJerome Forissier        if match:
442ae252462SJerome Forissier            # ELF list: save info for later. Region table and ELF list
443ae252462SJerome Forissier            # will be displayed when the call stack is reached
444ae252462SJerome Forissier            i = int(match.group('idx'))
445ae252462SJerome Forissier            self._elfs[i] = [match.group('uuid'), match.group('load_addr'),
446ae252462SJerome Forissier                             line]
44730999126SJerome Forissier            return
448105e09c2SJerome Forissier        match = re.search(TEE_LOAD_ADDR_RE, line)
449105e09c2SJerome Forissier        if match:
450105e09c2SJerome Forissier            self._tee_load_addr = match.group('load_addr')
451733a15f2SJerome Forissier        match = re.search(CALL_STACK_RE, line)
452733a15f2SJerome Forissier        if match:
453733a15f2SJerome Forissier            self._call_stack_found = True
454ae252462SJerome Forissier            if self._regions:
455ae252462SJerome Forissier                for r in self._regions:
456ae252462SJerome Forissier                    r_addr = r[0]
457ae252462SJerome Forissier                    r_size = r[1]
458ae252462SJerome Forissier                    elf_idx = r[2]
459ae252462SJerome Forissier                    saved_line = r[3]
460ae252462SJerome Forissier                    if elf_idx is None:
461ae252462SJerome Forissier                        self._out.write(saved_line)
462ae252462SJerome Forissier                    else:
463ae252462SJerome Forissier                        self._out.write(saved_line.strip() +
464ae252462SJerome Forissier                                        self.sections_in_region(r_addr,
465ae252462SJerome Forissier                                                                r_size,
466ae252462SJerome Forissier                                                                elf_idx) +
467ae252462SJerome Forissier                                        '\n')
468ae252462SJerome Forissier            if self._elfs:
469ae252462SJerome Forissier                for k in self._elfs:
470ae252462SJerome Forissier                    e = self._elfs[k]
471ae252462SJerome Forissier                    if (len(e) >= 3):
4721e6f2ea0SJerome Forissier                        # TA executable or library
473095567e5SJerome Forissier                        self._out.write(e[2].strip())
474095567e5SJerome Forissier                        elf = self.get_elf(e[0])
475095567e5SJerome Forissier                        if elf:
476095567e5SJerome Forissier                            rpath = os.path.realpath(elf)
477095567e5SJerome Forissier                            path = self.pretty_print_path(rpath)
478095567e5SJerome Forissier                            self._out.write(' (' + path + ')')
479095567e5SJerome Forissier                        self._out.write('\n')
480142c5cccSJerome Forissier            # Here is a good place to resolve the abort address because we
481142c5cccSJerome Forissier            # have all the information we need
482142c5cccSJerome Forissier            if self._saved_abort_line:
483142c5cccSJerome Forissier                self._out.write(self.process_abort(self._saved_abort_line))
484c90b6663SSumit Garg        match = re.search(FUNC_GRAPH_RE, line)
485c90b6663SSumit Garg        if match:
486c90b6663SSumit Garg            self._func_graph_found = True
487142c5cccSJerome Forissier        match = re.search(ABORT_ADDR_RE, line)
488142c5cccSJerome Forissier        if match:
48927b83ad2SJerome Forissier            self.reset()
490142c5cccSJerome Forissier            # At this point the arch and TA load address are unknown.
491142c5cccSJerome Forissier            # Save the line so We can translate the abort address later.
492142c5cccSJerome Forissier            self._saved_abort_line = line
493733a15f2SJerome Forissier        self._out.write(line)
494733a15f2SJerome Forissier
495733a15f2SJerome Forissier    def flush(self):
496733a15f2SJerome Forissier        self._out.flush()
497733a15f2SJerome Forissier
498ae252462SJerome Forissier
499733a15f2SJerome Forissierdef main():
500733a15f2SJerome Forissier    args = get_args()
501733a15f2SJerome Forissier    if args.dir:
502733a15f2SJerome Forissier        # Flatten list in case -d is used several times *and* with multiple
503733a15f2SJerome Forissier        # arguments
504733a15f2SJerome Forissier        args.dirs = [item for sublist in args.dir for item in sublist]
505733a15f2SJerome Forissier    else:
506733a15f2SJerome Forissier        args.dirs = []
507733a15f2SJerome Forissier    symbolizer = Symbolizer(sys.stdout, args.dirs, args.strip_path)
508733a15f2SJerome Forissier
5096b4fc675SJerome Forissier    fd = sys.stdin.fileno()
51020d152b8SJerome Forissier    isatty = os.isatty(fd)
51120d152b8SJerome Forissier    if isatty:
5126b4fc675SJerome Forissier        old = termios.tcgetattr(fd)
5136b4fc675SJerome Forissier        new = termios.tcgetattr(fd)
5146b4fc675SJerome Forissier        new[3] = new[3] & ~termios.ECHO  # lflags
5156b4fc675SJerome Forissier    try:
51620d152b8SJerome Forissier        if isatty:
5176b4fc675SJerome Forissier            termios.tcsetattr(fd, termios.TCSADRAIN, new)
518733a15f2SJerome Forissier        for line in sys.stdin:
519733a15f2SJerome Forissier            symbolizer.write(line)
5206b4fc675SJerome Forissier    finally:
521733a15f2SJerome Forissier        symbolizer.flush()
52220d152b8SJerome Forissier        if isatty:
5236b4fc675SJerome Forissier            termios.tcsetattr(fd, termios.TCSADRAIN, old)
524733a15f2SJerome Forissier
5251d8c2a48SJerome Forissier
526733a15f2SJerome Forissierif __name__ == "__main__":
527733a15f2SJerome Forissier    main()
528