xref: /optee_os/scripts/symbolize.py (revision 0309f58d09c131fd4a75c257541207c78c8cc81d)
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]+)')
2499e82b1fSJerome ForissierTA_PANIC_RE = re.compile(r'TA panicked with code (?P<code>0x[0-9a-f]+)')
25444c203eSJerome ForissierREGION_RE = re.compile(r'region +[0-9]+: va (?P<addr>0x[0-9a-f]+) '
266e7c2e91SJerome Forissier                       r'pa 0x[0-9a-f]+ size (?P<size>0x[0-9a-f]+)'
27531963a5SJens Wiklander                       r'( flags .{4} (\[(?P<elf_idx>[0-9]+)\])?)?')
28ae252462SJerome ForissierELF_LIST_RE = re.compile(r'\[(?P<idx>[0-9]+)\] (?P<uuid>[0-9a-f\-]+)'
296e7c2e91SJerome Forissier                         r' @ (?P<load_addr>0x[0-9a-f\-]+)')
30c90b6663SSumit GargFUNC_GRAPH_RE = re.compile(r'Function graph')
31c90b6663SSumit GargGRAPH_ADDR_RE = re.compile(r'(?P<addr>0x[0-9a-f]+)')
32c90b6663SSumit GargGRAPH_RE = re.compile(r'}')
33733a15f2SJerome Forissier
34733a15f2SJerome Forissierepilog = '''
350c5bedb5SJerome ForissierThis scripts reads an OP-TEE abort or panic message from stdin and adds debug
360c5bedb5SJerome Forissierinformation to the output, such as '<function> at <file>:<line>' next to each
370c5bedb5SJerome Forissieraddress in the call stack. Any message generated by OP-TEE and containing a
380c5bedb5SJerome Forissiercall stack can in principle be processed by this script. This currently
390c5bedb5SJerome Forissierincludes aborts and panics from the TEE core as well as from any TA.
400c5bedb5SJerome ForissierThe paths provided on the command line are used to locate the appropriate ELF
410c5bedb5SJerome Forissierbinary (tee.elf or Trusted Application). The GNU binutils (addr2line, objdump,
42f9089765SJerome Forissiernm) are used to extract the debug info. If the CROSS_COMPILE environment
43f9089765SJerome Forissiervariable is set, it is used as a prefix to the binutils tools. That is, the
44f9089765SJerome Forissierscript will invoke $(CROSS_COMPILE)addr2line etc. If it is not set however,
45f9089765SJerome Forissierthe prefix will be determined automatically for each ELF file based on its
46*0309f58dSAlvin Changarchitecture. The resulting command is then expected to be found in the user's
47*0309f58dSAlvin ChangPATH.
48733a15f2SJerome Forissier
490c5bedb5SJerome ForissierOP-TEE abort and panic messages are sent to the secure console. They look like
500c5bedb5SJerome Forissierthe following:
51733a15f2SJerome Forissier
520c5bedb5SJerome Forissier  E/TC:0 User TA data-abort at address 0xffffdecd (alignment fault)
53733a15f2SJerome Forissier  ...
540c5bedb5SJerome Forissier  E/TC:0 Call stack:
550c5bedb5SJerome Forissier  E/TC:0  0x4000549e
560c5bedb5SJerome Forissier  E/TC:0  0x40001f4b
570c5bedb5SJerome Forissier  E/TC:0  0x4000273f
580c5bedb5SJerome Forissier  E/TC:0  0x40005da7
59733a15f2SJerome Forissier
60733a15f2SJerome ForissierInspired by a script of the same name by the Chromium project.
61733a15f2SJerome Forissier
62733a15f2SJerome ForissierSample usage:
63733a15f2SJerome Forissier
64733a15f2SJerome Forissier  $ scripts/symbolize.py -d out/arm-plat-hikey/core -d ../optee_test/out/ta/*
65733a15f2SJerome Forissier  <paste whole dump here>
66733a15f2SJerome Forissier  ^D
67c90b6663SSumit Garg
68c90b6663SSumit GargAlso, this script reads function graph generated for OP-TEE user TA from
69c90b6663SSumit Garg/tmp/ftrace-<ta_uuid>.out file and resolves function addresses to corresponding
70c90b6663SSumit Gargsymbols.
71c90b6663SSumit Garg
72c90b6663SSumit GargSample usage:
73c90b6663SSumit Garg
74c90b6663SSumit Garg  $ cat /tmp/ftrace-<ta_uuid>.out | scripts/symbolize.py -d <ta_uuid>.elf
75c90b6663SSumit Garg  <paste function graph here>
76c90b6663SSumit Garg  ^D
77733a15f2SJerome Forissier'''
78733a15f2SJerome Forissier
7999e82b1fSJerome Forissiertee_result_names = {
8099e82b1fSJerome Forissier        '0xf0100001': 'TEE_ERROR_CORRUPT_OBJECT',
8199e82b1fSJerome Forissier        '0xf0100002': 'TEE_ERROR_CORRUPT_OBJECT_2',
8299e82b1fSJerome Forissier        '0xf0100003': 'TEE_ERROR_STORAGE_NOT_AVAILABLE',
8399e82b1fSJerome Forissier        '0xf0100004': 'TEE_ERROR_STORAGE_NOT_AVAILABLE_2',
8499e82b1fSJerome Forissier        '0xf0100006': 'TEE_ERROR_CIPHERTEXT_INVALID ',
8599e82b1fSJerome Forissier        '0xffff0000': 'TEE_ERROR_GENERIC',
8699e82b1fSJerome Forissier        '0xffff0001': 'TEE_ERROR_ACCESS_DENIED',
8799e82b1fSJerome Forissier        '0xffff0002': 'TEE_ERROR_CANCEL',
8899e82b1fSJerome Forissier        '0xffff0003': 'TEE_ERROR_ACCESS_CONFLICT',
8999e82b1fSJerome Forissier        '0xffff0004': 'TEE_ERROR_EXCESS_DATA',
9099e82b1fSJerome Forissier        '0xffff0005': 'TEE_ERROR_BAD_FORMAT',
9199e82b1fSJerome Forissier        '0xffff0006': 'TEE_ERROR_BAD_PARAMETERS',
9299e82b1fSJerome Forissier        '0xffff0007': 'TEE_ERROR_BAD_STATE',
9399e82b1fSJerome Forissier        '0xffff0008': 'TEE_ERROR_ITEM_NOT_FOUND',
9499e82b1fSJerome Forissier        '0xffff0009': 'TEE_ERROR_NOT_IMPLEMENTED',
9599e82b1fSJerome Forissier        '0xffff000a': 'TEE_ERROR_NOT_SUPPORTED',
9699e82b1fSJerome Forissier        '0xffff000b': 'TEE_ERROR_NO_DATA',
9799e82b1fSJerome Forissier        '0xffff000c': 'TEE_ERROR_OUT_OF_MEMORY',
9899e82b1fSJerome Forissier        '0xffff000d': 'TEE_ERROR_BUSY',
9999e82b1fSJerome Forissier        '0xffff000e': 'TEE_ERROR_COMMUNICATION',
10099e82b1fSJerome Forissier        '0xffff000f': 'TEE_ERROR_SECURITY',
10199e82b1fSJerome Forissier        '0xffff0010': 'TEE_ERROR_SHORT_BUFFER',
10299e82b1fSJerome Forissier        '0xffff0011': 'TEE_ERROR_EXTERNAL_CANCEL',
10399e82b1fSJerome Forissier        '0xffff300f': 'TEE_ERROR_OVERFLOW',
10499e82b1fSJerome Forissier        '0xffff3024': 'TEE_ERROR_TARGET_DEAD',
10599e82b1fSJerome Forissier        '0xffff3041': 'TEE_ERROR_STORAGE_NO_SPACE',
10699e82b1fSJerome Forissier        '0xffff3071': 'TEE_ERROR_MAC_INVALID',
10799e82b1fSJerome Forissier        '0xffff3072': 'TEE_ERROR_SIGNATURE_INVALID',
10899e82b1fSJerome Forissier        '0xffff5000': 'TEE_ERROR_TIME_NOT_SET',
10999e82b1fSJerome Forissier        '0xffff5001': 'TEE_ERROR_TIME_NEEDS_RESET',
11099e82b1fSJerome Forissier    }
11199e82b1fSJerome Forissier
112ae252462SJerome Forissier
113733a15f2SJerome Forissierdef get_args():
114733a15f2SJerome Forissier    parser = argparse.ArgumentParser(
115733a15f2SJerome Forissier        formatter_class=argparse.RawDescriptionHelpFormatter,
116c90b6663SSumit Garg        description='Symbolizes OP-TEE abort dumps or function graphs',
117733a15f2SJerome Forissier        epilog=epilog)
118733a15f2SJerome Forissier    parser.add_argument('-d', '--dir', action='append', nargs='+',
1191d8c2a48SJerome Forissier                        help='Search for ELF file in DIR. tee.elf is needed '
1201d8c2a48SJerome Forissier                        'to decode a TEE Core or pseudo-TA abort, while '
1211d8c2a48SJerome Forissier                        '<TA_uuid>.elf is required if a user-mode TA has '
1221d8c2a48SJerome Forissier                        'crashed. For convenience, ELF files may also be '
1231d8c2a48SJerome Forissier                        'given.')
1245f7df507SJerome Forissier    parser.add_argument('-s', '--strip_path', nargs='?',
1251d8c2a48SJerome Forissier                        help='Strip STRIP_PATH from file paths (default: '
1261d8c2a48SJerome Forissier                        'current directory, use -s with no argument to show '
1271d8c2a48SJerome Forissier                        'full paths)', default=os.getcwd())
128733a15f2SJerome Forissier
129733a15f2SJerome Forissier    return parser.parse_args()
130733a15f2SJerome Forissier
131ae252462SJerome Forissier
132733a15f2SJerome Forissierclass Symbolizer(object):
133733a15f2SJerome Forissier    def __init__(self, out, dirs, strip_path):
134733a15f2SJerome Forissier        self._out = out
135733a15f2SJerome Forissier        self._dirs = dirs
136733a15f2SJerome Forissier        self._strip_path = strip_path
137733a15f2SJerome Forissier        self._addr2line = None
138733a15f2SJerome Forissier        self.reset()
139733a15f2SJerome Forissier
1401cbf777bSJerome Forissier    def my_Popen(self, cmd):
1411cbf777bSJerome Forissier        try:
1421cbf777bSJerome Forissier            return subprocess.Popen(cmd, stdin=subprocess.PIPE,
14317be223aSJerome Forissier                                    stdout=subprocess.PIPE,
14417be223aSJerome Forissier                                    universal_newlines=True,
145bbaeed4dSRouven Czerwinski                                    bufsize=1)
1461cbf777bSJerome Forissier        except OSError as e:
147bbaeed4dSRouven Czerwinski            if e.errno == errno.ENOENT:
148bbaeed4dSRouven Czerwinski                print("*** Error:{}: command not found".format(cmd[0]),
149bbaeed4dSRouven Czerwinski                      file=sys.stderr)
1501cbf777bSJerome Forissier                sys.exit(1)
1511cbf777bSJerome Forissier
152733a15f2SJerome Forissier    def get_elf(self, elf_or_uuid):
153733a15f2SJerome Forissier        if not elf_or_uuid.endswith('.elf'):
154733a15f2SJerome Forissier            elf_or_uuid += '.elf'
155733a15f2SJerome Forissier        for d in self._dirs:
156157e6213SJerome Forissier            if d.endswith(elf_or_uuid) and os.path.isfile(d):
157157e6213SJerome Forissier                return d
158733a15f2SJerome Forissier            elf = glob.glob(d + '/' + elf_or_uuid)
159733a15f2SJerome Forissier            if elf:
160733a15f2SJerome Forissier                return elf[0]
161733a15f2SJerome Forissier
16224778dedSJerome Forissier    def set_arch(self, elf):
1636e7c2e91SJerome Forissier        self._arch = os.getenv('CROSS_COMPILE')
1648a6d4a8bSEtienne Carriere        if self._arch:
1658a6d4a8bSEtienne Carriere            return
1669bb9f377SJerome Forissier        p = subprocess.Popen(['file', '-L', elf], stdout=subprocess.PIPE)
167d720431cSJerome Forissier        output = p.stdout.readlines()
168d720431cSJerome Forissier        p.terminate()
169bbaeed4dSRouven Czerwinski        if b'ARM aarch64,' in output[0]:
170d720431cSJerome Forissier            self._arch = 'aarch64-linux-gnu-'
171bbaeed4dSRouven Czerwinski        elif b'ARM,' in output[0]:
172d720431cSJerome Forissier            self._arch = 'arm-linux-gnueabihf-'
173*0309f58dSAlvin Chang        elif b'RISC-V,' in output[0]:
174*0309f58dSAlvin Chang            if b'32-bit' in output[0]:
175*0309f58dSAlvin Chang                self._arch = 'riscv32-unknown-linux-gnu-'
176*0309f58dSAlvin Chang            elif b'64-bit' in output[0]:
177*0309f58dSAlvin Chang                self._arch = 'riscv64-unknown-linux-gnu-'
178d720431cSJerome Forissier
17924778dedSJerome Forissier    def arch_prefix(self, cmd, elf):
18024778dedSJerome Forissier        self.set_arch(elf)
181ae252462SJerome Forissier        if self._arch is None:
182ae252462SJerome Forissier            return ''
183d720431cSJerome Forissier        return self._arch + cmd
184142c5cccSJerome Forissier
185ae252462SJerome Forissier    def spawn_addr2line(self, elf_name):
186ae252462SJerome Forissier        if elf_name is None:
187ae252462SJerome Forissier            return
188ae252462SJerome Forissier        if self._addr2line_elf_name is elf_name:
189ae252462SJerome Forissier            return
190ae252462SJerome Forissier        if self._addr2line:
191ae252462SJerome Forissier            self._addr2line.terminate
192ae252462SJerome Forissier            self._addr2line = None
193ae252462SJerome Forissier        elf = self.get_elf(elf_name)
194733a15f2SJerome Forissier        if not elf:
195733a15f2SJerome Forissier            return
19624778dedSJerome Forissier        cmd = self.arch_prefix('addr2line', elf)
197142c5cccSJerome Forissier        if not cmd:
198733a15f2SJerome Forissier            return
199c0c57c8fSJerome Forissier        self._addr2line = self.my_Popen([cmd, '-f', '-p', '-e', elf])
200ba84a3f5SJerome Forissier        self._addr2line_elf_name = elf_name
201ae252462SJerome Forissier
202ae252462SJerome Forissier    # If addr falls into a region that maps a TA ELF file, return the load
203ae252462SJerome Forissier    # address of that file.
204ae252462SJerome Forissier    def elf_load_addr(self, addr):
205ae252462SJerome Forissier        if self._regions:
206ae252462SJerome Forissier            for r in self._regions:
207ae252462SJerome Forissier                r_addr = int(r[0], 16)
208ae252462SJerome Forissier                r_size = int(r[1], 16)
209ae252462SJerome Forissier                i_addr = int(addr, 16)
210ae252462SJerome Forissier                if (i_addr >= r_addr and i_addr < (r_addr + r_size)):
211ae252462SJerome Forissier                    # Found region
212ae252462SJerome Forissier                    elf_idx = r[2]
213ae252462SJerome Forissier                    if elf_idx is not None:
214ae252462SJerome Forissier                        return self._elfs[int(elf_idx)][1]
215099918f6SSumit Garg            # In case address is not found in TA ELF file, fallback to tee.elf
216099918f6SSumit Garg            # especially to symbolize mixed (user-space and kernel) addresses
217099918f6SSumit Garg            # which is true when syscall ftrace is enabled along with TA
218099918f6SSumit Garg            # ftrace.
21991068f86SJerome Forissier            return self._tee_load_addr
220ae252462SJerome Forissier        else:
221ae252462SJerome Forissier            # tee.elf
222105e09c2SJerome Forissier            return self._tee_load_addr
223ae252462SJerome Forissier
224ae252462SJerome Forissier    def elf_for_addr(self, addr):
225ae252462SJerome Forissier        l_addr = self.elf_load_addr(addr)
22691068f86SJerome Forissier        if l_addr == self._tee_load_addr:
22791068f86SJerome Forissier            return 'tee.elf'
228ae252462SJerome Forissier        for k in self._elfs:
229ae252462SJerome Forissier            e = self._elfs[k]
230ae252462SJerome Forissier            if int(e[1], 16) == int(l_addr, 16):
231ae252462SJerome Forissier                return e[0]
232ae252462SJerome Forissier        return None
233733a15f2SJerome Forissier
234142c5cccSJerome Forissier    def subtract_load_addr(self, addr):
235ae252462SJerome Forissier        l_addr = self.elf_load_addr(addr)
236ae252462SJerome Forissier        if l_addr is None:
237ae252462SJerome Forissier            return None
238ae252462SJerome Forissier        if int(l_addr, 16) > int(addr, 16):
239142c5cccSJerome Forissier            return ''
240ae252462SJerome Forissier        return '0x{:x}'.format(int(addr, 16) - int(l_addr, 16))
241142c5cccSJerome Forissier
242142c5cccSJerome Forissier    def resolve(self, addr):
243142c5cccSJerome Forissier        reladdr = self.subtract_load_addr(addr)
244ae252462SJerome Forissier        self.spawn_addr2line(self.elf_for_addr(addr))
245142c5cccSJerome Forissier        if not reladdr or not self._addr2line:
246733a15f2SJerome Forissier            return '???'
247c0c57c8fSJerome Forissier        if self.elf_for_addr(addr) == 'tee.elf':
248c0c57c8fSJerome Forissier            reladdr = '0x{:x}'.format(int(reladdr, 16) +
249c0c57c8fSJerome Forissier                                      int(self.first_vma('tee.elf'), 16))
250733a15f2SJerome Forissier        try:
251bbaeed4dSRouven Czerwinski            print(reladdr, file=self._addr2line.stdin)
252733a15f2SJerome Forissier            ret = self._addr2line.stdout.readline().rstrip('\n')
253733a15f2SJerome Forissier        except IOError:
254733a15f2SJerome Forissier            ret = '!!!'
255733a15f2SJerome Forissier        return ret
256733a15f2SJerome Forissier
257142c5cccSJerome Forissier    def symbol_plus_offset(self, addr):
258142c5cccSJerome Forissier        ret = ''
259142c5cccSJerome Forissier        prevsize = 0
260142c5cccSJerome Forissier        reladdr = self.subtract_load_addr(addr)
261ae252462SJerome Forissier        elf_name = self.elf_for_addr(addr)
262ae252462SJerome Forissier        if elf_name is None:
263ae252462SJerome Forissier            return ''
264ae252462SJerome Forissier        elf = self.get_elf(elf_name)
26524778dedSJerome Forissier        cmd = self.arch_prefix('nm', elf)
266142c5cccSJerome Forissier        if not reladdr or not elf or not cmd:
267142c5cccSJerome Forissier            return ''
26830999126SJerome Forissier        ireladdr = int(reladdr, 16)
2691cbf777bSJerome Forissier        nm = self.my_Popen([cmd, '--numeric-sort', '--print-size', elf])
270142c5cccSJerome Forissier        for line in iter(nm.stdout.readline, ''):
271142c5cccSJerome Forissier            try:
272142c5cccSJerome Forissier                addr, size, _, name = line.split()
2731d8c2a48SJerome Forissier            except ValueError:
274142c5cccSJerome Forissier                # Size is missing
275b4815427SJerome Forissier                try:
276142c5cccSJerome Forissier                    addr, _, name = line.split()
277142c5cccSJerome Forissier                    size = '0'
2781d8c2a48SJerome Forissier                except ValueError:
279b4815427SJerome Forissier                    # E.g., undefined (external) symbols (line = "U symbol")
280b4815427SJerome Forissier                    continue
281142c5cccSJerome Forissier            iaddr = int(addr, 16)
282142c5cccSJerome Forissier            isize = int(size, 16)
283142c5cccSJerome Forissier            if iaddr == ireladdr:
284142c5cccSJerome Forissier                ret = name
285142c5cccSJerome Forissier                break
286142c5cccSJerome Forissier            if iaddr < ireladdr and iaddr + isize >= ireladdr:
287142c5cccSJerome Forissier                offs = ireladdr - iaddr
288142c5cccSJerome Forissier                ret = name + '+' + str(offs)
289142c5cccSJerome Forissier                break
290142c5cccSJerome Forissier            if iaddr > ireladdr and prevsize == 0:
291142c5cccSJerome Forissier                offs = iaddr + ireladdr
292142c5cccSJerome Forissier                ret = prevname + '+' + str(offs)
293142c5cccSJerome Forissier                break
294142c5cccSJerome Forissier            prevsize = size
295142c5cccSJerome Forissier            prevname = name
296142c5cccSJerome Forissier        nm.terminate()
297142c5cccSJerome Forissier        return ret
298142c5cccSJerome Forissier
299142c5cccSJerome Forissier    def section_plus_offset(self, addr):
300142c5cccSJerome Forissier        ret = ''
301142c5cccSJerome Forissier        reladdr = self.subtract_load_addr(addr)
302ae252462SJerome Forissier        elf_name = self.elf_for_addr(addr)
303ae252462SJerome Forissier        if elf_name is None:
304ae252462SJerome Forissier            return ''
305ae252462SJerome Forissier        elf = self.get_elf(elf_name)
30624778dedSJerome Forissier        cmd = self.arch_prefix('objdump', elf)
307142c5cccSJerome Forissier        if not reladdr or not elf or not cmd:
308142c5cccSJerome Forissier            return ''
30930999126SJerome Forissier        iaddr = int(reladdr, 16)
3101cbf777bSJerome Forissier        objdump = self.my_Popen([cmd, '--section-headers', elf])
311142c5cccSJerome Forissier        for line in iter(objdump.stdout.readline, ''):
312142c5cccSJerome Forissier            try:
313142c5cccSJerome Forissier                idx, name, size, vma, lma, offs, algn = line.split()
3141d8c2a48SJerome Forissier            except ValueError:
315ae252462SJerome Forissier                continue
316142c5cccSJerome Forissier            ivma = int(vma, 16)
317142c5cccSJerome Forissier            isize = int(size, 16)
318142c5cccSJerome Forissier            if ivma == iaddr:
319142c5cccSJerome Forissier                ret = name
320142c5cccSJerome Forissier                break
321142c5cccSJerome Forissier            if ivma < iaddr and ivma + isize >= iaddr:
322142c5cccSJerome Forissier                offs = iaddr - ivma
323142c5cccSJerome Forissier                ret = name + '+' + str(offs)
324142c5cccSJerome Forissier                break
325142c5cccSJerome Forissier        objdump.terminate()
326142c5cccSJerome Forissier        return ret
327142c5cccSJerome Forissier
328142c5cccSJerome Forissier    def process_abort(self, line):
329142c5cccSJerome Forissier        ret = ''
330142c5cccSJerome Forissier        match = re.search(ABORT_ADDR_RE, line)
331142c5cccSJerome Forissier        addr = match.group('addr')
332142c5cccSJerome Forissier        pre = match.start('addr')
333142c5cccSJerome Forissier        post = match.end('addr')
334142c5cccSJerome Forissier        sym = self.symbol_plus_offset(addr)
335142c5cccSJerome Forissier        sec = self.section_plus_offset(addr)
336142c5cccSJerome Forissier        if sym or sec:
337142c5cccSJerome Forissier            ret += line[:pre]
338142c5cccSJerome Forissier            ret += addr
339142c5cccSJerome Forissier            if sym:
340142c5cccSJerome Forissier                ret += ' ' + sym
341142c5cccSJerome Forissier            if sec:
342142c5cccSJerome Forissier                ret += ' ' + sec
343142c5cccSJerome Forissier            ret += line[post:]
344142c5cccSJerome Forissier        return ret
345142c5cccSJerome Forissier
34630999126SJerome Forissier    # Return all ELF sections with the ALLOC flag
347ae252462SJerome Forissier    def read_sections(self, elf_name):
348ae252462SJerome Forissier        if elf_name is None:
34930999126SJerome Forissier            return
350ae252462SJerome Forissier        if elf_name in self._sections:
351ae252462SJerome Forissier            return
352ae252462SJerome Forissier        elf = self.get_elf(elf_name)
353d7c22aceSJerome Forissier        if not elf:
354d7c22aceSJerome Forissier            return
35524778dedSJerome Forissier        cmd = self.arch_prefix('objdump', elf)
35630999126SJerome Forissier        if not elf or not cmd:
35730999126SJerome Forissier            return
358ae252462SJerome Forissier        self._sections[elf_name] = []
3591cbf777bSJerome Forissier        objdump = self.my_Popen([cmd, '--section-headers', elf])
36030999126SJerome Forissier        for line in iter(objdump.stdout.readline, ''):
36130999126SJerome Forissier            try:
36230999126SJerome Forissier                _, name, size, vma, _, _, _ = line.split()
3631d8c2a48SJerome Forissier            except ValueError:
36430999126SJerome Forissier                if 'ALLOC' in line:
365ae252462SJerome Forissier                    self._sections[elf_name].append([name, int(vma, 16),
366ae252462SJerome Forissier                                                     int(size, 16)])
36730999126SJerome Forissier
368c0c57c8fSJerome Forissier    def first_vma(self, elf_name):
369c0c57c8fSJerome Forissier        self.read_sections(elf_name)
370c0c57c8fSJerome Forissier        return '0x{:x}'.format(self._sections[elf_name][0][1])
371c0c57c8fSJerome Forissier
37230999126SJerome Forissier    def overlaps(self, section, addr, size):
37330999126SJerome Forissier        sec_addr = section[1]
37430999126SJerome Forissier        sec_size = section[2]
37530999126SJerome Forissier        if not size or not sec_size:
37630999126SJerome Forissier            return False
377ae252462SJerome Forissier        return ((addr <= (sec_addr + sec_size - 1)) and
378ae252462SJerome Forissier                ((addr + size - 1) >= sec_addr))
37930999126SJerome Forissier
380ae252462SJerome Forissier    def sections_in_region(self, addr, size, elf_idx):
38130999126SJerome Forissier        ret = ''
38230999126SJerome Forissier        addr = self.subtract_load_addr(addr)
38330999126SJerome Forissier        if not addr:
38430999126SJerome Forissier            return ''
38530999126SJerome Forissier        iaddr = int(addr, 16)
38630999126SJerome Forissier        isize = int(size, 16)
387ae252462SJerome Forissier        elf = self._elfs[int(elf_idx)][0]
388ae252462SJerome Forissier        if elf is None:
389ae252462SJerome Forissier            return ''
390ae252462SJerome Forissier        self.read_sections(elf)
391ae252462SJerome Forissier        if elf not in self._sections:
392ae252462SJerome Forissier            return ''
393ae252462SJerome Forissier        for s in self._sections[elf]:
39430999126SJerome Forissier            if self.overlaps(s, iaddr, isize):
39530999126SJerome Forissier                ret += ' ' + s[0]
39630999126SJerome Forissier        return ret
39730999126SJerome Forissier
398733a15f2SJerome Forissier    def reset(self):
399733a15f2SJerome Forissier        self._call_stack_found = False
400733a15f2SJerome Forissier        if self._addr2line:
401733a15f2SJerome Forissier            self._addr2line.terminate()
402733a15f2SJerome Forissier            self._addr2line = None
403ae252462SJerome Forissier        self._addr2line_elf_name = None
404d720431cSJerome Forissier        self._arch = None
405142c5cccSJerome Forissier        self._saved_abort_line = ''
406ae252462SJerome Forissier        self._sections = {}  # {elf_name: [[name, addr, size], ...], ...}
407ae252462SJerome Forissier        self._regions = []   # [[addr, size, elf_idx, saved line], ...]
408ae252462SJerome Forissier        self._elfs = {0: ["tee.elf", 0]}  # {idx: [uuid, load_addr], ...}
40991068f86SJerome Forissier        self._tee_load_addr = '0x0'
410c90b6663SSumit Garg        self._func_graph_found = False
411c90b6663SSumit Garg        self._func_graph_skip_line = True
412733a15f2SJerome Forissier
413095567e5SJerome Forissier    def pretty_print_path(self, path):
414095567e5SJerome Forissier        if self._strip_path:
415095567e5SJerome Forissier            return re.sub(re.escape(self._strip_path) + '/*', '', path)
416095567e5SJerome Forissier        return path
417095567e5SJerome Forissier
418733a15f2SJerome Forissier    def write(self, line):
419733a15f2SJerome Forissier        if self._call_stack_found:
420733a15f2SJerome Forissier            match = re.search(STACK_ADDR_RE, line)
421733a15f2SJerome Forissier            if match:
422733a15f2SJerome Forissier                addr = match.group('addr')
423733a15f2SJerome Forissier                pre = match.start('addr')
424733a15f2SJerome Forissier                post = match.end('addr')
425733a15f2SJerome Forissier                self._out.write(line[:pre])
426733a15f2SJerome Forissier                self._out.write(addr)
4275500d703SJerome Forissier                # The call stack contains return addresses (LR/ELR values).
4285500d703SJerome Forissier                # Heuristic: subtract 2 to obtain the call site of the function
4295500d703SJerome Forissier                # or the location of the exception. This value works for A64,
4305500d703SJerome Forissier                # A32 as well as Thumb.
4315500d703SJerome Forissier                pc = 0
4325500d703SJerome Forissier                lr = int(addr, 16)
4335500d703SJerome Forissier                if lr:
4345500d703SJerome Forissier                    pc = lr - 2
4355500d703SJerome Forissier                res = self.resolve('0x{:x}'.format(pc))
436095567e5SJerome Forissier                res = self.pretty_print_path(res)
437733a15f2SJerome Forissier                self._out.write(' ' + res)
438733a15f2SJerome Forissier                self._out.write(line[post:])
439733a15f2SJerome Forissier                return
440733a15f2SJerome Forissier            else:
441733a15f2SJerome Forissier                self.reset()
442c90b6663SSumit Garg        if self._func_graph_found:
443c90b6663SSumit Garg            match = re.search(GRAPH_ADDR_RE, line)
444c90b6663SSumit Garg            match_re = re.search(GRAPH_RE, line)
445c90b6663SSumit Garg            if match:
446c90b6663SSumit Garg                addr = match.group('addr')
447c90b6663SSumit Garg                pre = match.start('addr')
448c90b6663SSumit Garg                post = match.end('addr')
449c90b6663SSumit Garg                self._out.write(line[:pre])
450c90b6663SSumit Garg                res = self.resolve(addr)
451c90b6663SSumit Garg                res_arr = re.split(' ', res)
452c90b6663SSumit Garg                self._out.write(res_arr[0])
453c90b6663SSumit Garg                self._out.write(line[post:])
454c90b6663SSumit Garg                self._func_graph_skip_line = False
455c90b6663SSumit Garg                return
456c90b6663SSumit Garg            elif match_re:
457c90b6663SSumit Garg                self._out.write(line)
458c90b6663SSumit Garg                return
459c90b6663SSumit Garg            elif self._func_graph_skip_line:
460c90b6663SSumit Garg                return
461c90b6663SSumit Garg            else:
462c90b6663SSumit Garg                self.reset()
46330999126SJerome Forissier        match = re.search(REGION_RE, line)
46430999126SJerome Forissier        if match:
465ae252462SJerome Forissier            # Region table: save info for later processing once
466ae252462SJerome Forissier            # we know which UUID corresponds to which ELF index
46730999126SJerome Forissier            addr = match.group('addr')
46830999126SJerome Forissier            size = match.group('size')
469ae252462SJerome Forissier            elf_idx = match.group('elf_idx')
470ae252462SJerome Forissier            self._regions.append([addr, size, elf_idx, line])
471ae252462SJerome Forissier            return
472ae252462SJerome Forissier        match = re.search(ELF_LIST_RE, line)
473ae252462SJerome Forissier        if match:
474ae252462SJerome Forissier            # ELF list: save info for later. Region table and ELF list
475ae252462SJerome Forissier            # will be displayed when the call stack is reached
476ae252462SJerome Forissier            i = int(match.group('idx'))
477ae252462SJerome Forissier            self._elfs[i] = [match.group('uuid'), match.group('load_addr'),
478ae252462SJerome Forissier                             line]
47930999126SJerome Forissier            return
48099e82b1fSJerome Forissier        match = re.search(TA_PANIC_RE, line)
48199e82b1fSJerome Forissier        if match:
48299e82b1fSJerome Forissier            code = match.group('code')
48399e82b1fSJerome Forissier            if code in tee_result_names:
48499e82b1fSJerome Forissier                line = line.strip() + ' (' + tee_result_names[code] + ')\n'
48599e82b1fSJerome Forissier            self._out.write(line)
48699e82b1fSJerome Forissier            return
487105e09c2SJerome Forissier        match = re.search(TEE_LOAD_ADDR_RE, line)
488105e09c2SJerome Forissier        if match:
489105e09c2SJerome Forissier            self._tee_load_addr = match.group('load_addr')
490733a15f2SJerome Forissier        match = re.search(CALL_STACK_RE, line)
491733a15f2SJerome Forissier        if match:
492733a15f2SJerome Forissier            self._call_stack_found = True
493ae252462SJerome Forissier            if self._regions:
494ae252462SJerome Forissier                for r in self._regions:
495ae252462SJerome Forissier                    r_addr = r[0]
496ae252462SJerome Forissier                    r_size = r[1]
497ae252462SJerome Forissier                    elf_idx = r[2]
498ae252462SJerome Forissier                    saved_line = r[3]
499ae252462SJerome Forissier                    if elf_idx is None:
500ae252462SJerome Forissier                        self._out.write(saved_line)
501ae252462SJerome Forissier                    else:
502ae252462SJerome Forissier                        self._out.write(saved_line.strip() +
503ae252462SJerome Forissier                                        self.sections_in_region(r_addr,
504ae252462SJerome Forissier                                                                r_size,
505ae252462SJerome Forissier                                                                elf_idx) +
506ae252462SJerome Forissier                                        '\n')
507ae252462SJerome Forissier            if self._elfs:
508ae252462SJerome Forissier                for k in self._elfs:
509ae252462SJerome Forissier                    e = self._elfs[k]
510ae252462SJerome Forissier                    if (len(e) >= 3):
5111e6f2ea0SJerome Forissier                        # TA executable or library
512095567e5SJerome Forissier                        self._out.write(e[2].strip())
513095567e5SJerome Forissier                        elf = self.get_elf(e[0])
514095567e5SJerome Forissier                        if elf:
515095567e5SJerome Forissier                            rpath = os.path.realpath(elf)
516095567e5SJerome Forissier                            path = self.pretty_print_path(rpath)
517095567e5SJerome Forissier                            self._out.write(' (' + path + ')')
518095567e5SJerome Forissier                        self._out.write('\n')
519142c5cccSJerome Forissier            # Here is a good place to resolve the abort address because we
520142c5cccSJerome Forissier            # have all the information we need
521142c5cccSJerome Forissier            if self._saved_abort_line:
522142c5cccSJerome Forissier                self._out.write(self.process_abort(self._saved_abort_line))
523c90b6663SSumit Garg        match = re.search(FUNC_GRAPH_RE, line)
524c90b6663SSumit Garg        if match:
525c90b6663SSumit Garg            self._func_graph_found = True
526142c5cccSJerome Forissier        match = re.search(ABORT_ADDR_RE, line)
527142c5cccSJerome Forissier        if match:
52827b83ad2SJerome Forissier            self.reset()
529142c5cccSJerome Forissier            # At this point the arch and TA load address are unknown.
530142c5cccSJerome Forissier            # Save the line so We can translate the abort address later.
531142c5cccSJerome Forissier            self._saved_abort_line = line
532733a15f2SJerome Forissier        self._out.write(line)
533733a15f2SJerome Forissier
534733a15f2SJerome Forissier    def flush(self):
535733a15f2SJerome Forissier        self._out.flush()
536733a15f2SJerome Forissier
537ae252462SJerome Forissier
538733a15f2SJerome Forissierdef main():
539733a15f2SJerome Forissier    args = get_args()
540733a15f2SJerome Forissier    if args.dir:
541733a15f2SJerome Forissier        # Flatten list in case -d is used several times *and* with multiple
542733a15f2SJerome Forissier        # arguments
543733a15f2SJerome Forissier        args.dirs = [item for sublist in args.dir for item in sublist]
544733a15f2SJerome Forissier    else:
545733a15f2SJerome Forissier        args.dirs = []
546733a15f2SJerome Forissier    symbolizer = Symbolizer(sys.stdout, args.dirs, args.strip_path)
547733a15f2SJerome Forissier
5486b4fc675SJerome Forissier    fd = sys.stdin.fileno()
54920d152b8SJerome Forissier    isatty = os.isatty(fd)
55020d152b8SJerome Forissier    if isatty:
5516b4fc675SJerome Forissier        old = termios.tcgetattr(fd)
5526b4fc675SJerome Forissier        new = termios.tcgetattr(fd)
5536b4fc675SJerome Forissier        new[3] = new[3] & ~termios.ECHO  # lflags
5546b4fc675SJerome Forissier    try:
55520d152b8SJerome Forissier        if isatty:
5566b4fc675SJerome Forissier            termios.tcsetattr(fd, termios.TCSADRAIN, new)
557733a15f2SJerome Forissier        for line in sys.stdin:
558733a15f2SJerome Forissier            symbolizer.write(line)
5596b4fc675SJerome Forissier    finally:
560733a15f2SJerome Forissier        symbolizer.flush()
56120d152b8SJerome Forissier        if isatty:
5626b4fc675SJerome Forissier            termios.tcsetattr(fd, termios.TCSADRAIN, old)
563733a15f2SJerome Forissier
5641d8c2a48SJerome Forissier
565733a15f2SJerome Forissierif __name__ == "__main__":
566733a15f2SJerome Forissier    main()
567