xref: /optee_os/scripts/symbolize.py (revision 17a66904a791447da1356331f01e7c1ca25329be)
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(
22*17a66904SJens Wiklander    r'[UEIDFM]/(TC|LD):([0-9]+ )?(\?*|[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
460309f58dSAlvin Changarchitecture. The resulting command is then expected to be found in the user's
470309f58dSAlvin 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-'
1730309f58dSAlvin Chang        elif b'RISC-V,' in output[0]:
1740309f58dSAlvin Chang            if b'32-bit' in output[0]:
1750309f58dSAlvin Chang                self._arch = 'riscv32-unknown-linux-gnu-'
1760309f58dSAlvin Chang            elif b'64-bit' in output[0]:
1770309f58dSAlvin 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)
265d33360e5SKun Lai        if elf is None:
266d33360e5SKun Lai            return ''
26724778dedSJerome Forissier        cmd = self.arch_prefix('nm', elf)
268142c5cccSJerome Forissier        if not reladdr or not elf or not cmd:
269142c5cccSJerome Forissier            return ''
27030999126SJerome Forissier        ireladdr = int(reladdr, 16)
2711cbf777bSJerome Forissier        nm = self.my_Popen([cmd, '--numeric-sort', '--print-size', elf])
272142c5cccSJerome Forissier        for line in iter(nm.stdout.readline, ''):
273142c5cccSJerome Forissier            try:
274142c5cccSJerome Forissier                addr, size, _, name = line.split()
2751d8c2a48SJerome Forissier            except ValueError:
276142c5cccSJerome Forissier                # Size is missing
277b4815427SJerome Forissier                try:
278142c5cccSJerome Forissier                    addr, _, name = line.split()
279142c5cccSJerome Forissier                    size = '0'
2801d8c2a48SJerome Forissier                except ValueError:
281b4815427SJerome Forissier                    # E.g., undefined (external) symbols (line = "U symbol")
282b4815427SJerome Forissier                    continue
283142c5cccSJerome Forissier            iaddr = int(addr, 16)
284142c5cccSJerome Forissier            isize = int(size, 16)
285142c5cccSJerome Forissier            if iaddr == ireladdr:
286142c5cccSJerome Forissier                ret = name
287142c5cccSJerome Forissier                break
288142c5cccSJerome Forissier            if iaddr < ireladdr and iaddr + isize >= ireladdr:
289142c5cccSJerome Forissier                offs = ireladdr - iaddr
290142c5cccSJerome Forissier                ret = name + '+' + str(offs)
291142c5cccSJerome Forissier                break
292142c5cccSJerome Forissier            if iaddr > ireladdr and prevsize == 0:
293142c5cccSJerome Forissier                offs = iaddr + ireladdr
294142c5cccSJerome Forissier                ret = prevname + '+' + str(offs)
295142c5cccSJerome Forissier                break
296142c5cccSJerome Forissier            prevsize = size
297142c5cccSJerome Forissier            prevname = name
298142c5cccSJerome Forissier        nm.terminate()
299142c5cccSJerome Forissier        return ret
300142c5cccSJerome Forissier
301142c5cccSJerome Forissier    def section_plus_offset(self, addr):
302142c5cccSJerome Forissier        ret = ''
303142c5cccSJerome Forissier        reladdr = self.subtract_load_addr(addr)
304ae252462SJerome Forissier        elf_name = self.elf_for_addr(addr)
305ae252462SJerome Forissier        if elf_name is None:
306ae252462SJerome Forissier            return ''
307ae252462SJerome Forissier        elf = self.get_elf(elf_name)
308d33360e5SKun Lai        if elf is None:
309d33360e5SKun Lai            return ''
31024778dedSJerome Forissier        cmd = self.arch_prefix('objdump', elf)
311142c5cccSJerome Forissier        if not reladdr or not elf or not cmd:
312142c5cccSJerome Forissier            return ''
31330999126SJerome Forissier        iaddr = int(reladdr, 16)
3141cbf777bSJerome Forissier        objdump = self.my_Popen([cmd, '--section-headers', elf])
315142c5cccSJerome Forissier        for line in iter(objdump.stdout.readline, ''):
316142c5cccSJerome Forissier            try:
317142c5cccSJerome Forissier                idx, name, size, vma, lma, offs, algn = line.split()
3181d8c2a48SJerome Forissier            except ValueError:
319ae252462SJerome Forissier                continue
320142c5cccSJerome Forissier            ivma = int(vma, 16)
321142c5cccSJerome Forissier            isize = int(size, 16)
322142c5cccSJerome Forissier            if ivma == iaddr:
323142c5cccSJerome Forissier                ret = name
324142c5cccSJerome Forissier                break
325142c5cccSJerome Forissier            if ivma < iaddr and ivma + isize >= iaddr:
326142c5cccSJerome Forissier                offs = iaddr - ivma
327142c5cccSJerome Forissier                ret = name + '+' + str(offs)
328142c5cccSJerome Forissier                break
329142c5cccSJerome Forissier        objdump.terminate()
330142c5cccSJerome Forissier        return ret
331142c5cccSJerome Forissier
332142c5cccSJerome Forissier    def process_abort(self, line):
333142c5cccSJerome Forissier        ret = ''
334142c5cccSJerome Forissier        match = re.search(ABORT_ADDR_RE, line)
335142c5cccSJerome Forissier        addr = match.group('addr')
336142c5cccSJerome Forissier        pre = match.start('addr')
337142c5cccSJerome Forissier        post = match.end('addr')
338142c5cccSJerome Forissier        sym = self.symbol_plus_offset(addr)
339142c5cccSJerome Forissier        sec = self.section_plus_offset(addr)
340142c5cccSJerome Forissier        if sym or sec:
341142c5cccSJerome Forissier            ret += line[:pre]
342142c5cccSJerome Forissier            ret += addr
343142c5cccSJerome Forissier            if sym:
344142c5cccSJerome Forissier                ret += ' ' + sym
345142c5cccSJerome Forissier            if sec:
346142c5cccSJerome Forissier                ret += ' ' + sec
347142c5cccSJerome Forissier            ret += line[post:]
348142c5cccSJerome Forissier        return ret
349142c5cccSJerome Forissier
35030999126SJerome Forissier    # Return all ELF sections with the ALLOC flag
351ae252462SJerome Forissier    def read_sections(self, elf_name):
352ae252462SJerome Forissier        if elf_name is None:
35330999126SJerome Forissier            return
354ae252462SJerome Forissier        if elf_name in self._sections:
355ae252462SJerome Forissier            return
356ae252462SJerome Forissier        elf = self.get_elf(elf_name)
357d7c22aceSJerome Forissier        if not elf:
358d7c22aceSJerome Forissier            return
35924778dedSJerome Forissier        cmd = self.arch_prefix('objdump', elf)
36030999126SJerome Forissier        if not elf or not cmd:
36130999126SJerome Forissier            return
362ae252462SJerome Forissier        self._sections[elf_name] = []
3631cbf777bSJerome Forissier        objdump = self.my_Popen([cmd, '--section-headers', elf])
36430999126SJerome Forissier        for line in iter(objdump.stdout.readline, ''):
36530999126SJerome Forissier            try:
36630999126SJerome Forissier                _, name, size, vma, _, _, _ = line.split()
3671d8c2a48SJerome Forissier            except ValueError:
36830999126SJerome Forissier                if 'ALLOC' in line:
369ae252462SJerome Forissier                    self._sections[elf_name].append([name, int(vma, 16),
370ae252462SJerome Forissier                                                     int(size, 16)])
37130999126SJerome Forissier
372c0c57c8fSJerome Forissier    def first_vma(self, elf_name):
373c0c57c8fSJerome Forissier        self.read_sections(elf_name)
374c0c57c8fSJerome Forissier        return '0x{:x}'.format(self._sections[elf_name][0][1])
375c0c57c8fSJerome Forissier
37630999126SJerome Forissier    def overlaps(self, section, addr, size):
37730999126SJerome Forissier        sec_addr = section[1]
37830999126SJerome Forissier        sec_size = section[2]
37930999126SJerome Forissier        if not size or not sec_size:
38030999126SJerome Forissier            return False
381ae252462SJerome Forissier        return ((addr <= (sec_addr + sec_size - 1)) and
382ae252462SJerome Forissier                ((addr + size - 1) >= sec_addr))
38330999126SJerome Forissier
384ae252462SJerome Forissier    def sections_in_region(self, addr, size, elf_idx):
38530999126SJerome Forissier        ret = ''
38630999126SJerome Forissier        addr = self.subtract_load_addr(addr)
38730999126SJerome Forissier        if not addr:
38830999126SJerome Forissier            return ''
38930999126SJerome Forissier        iaddr = int(addr, 16)
39030999126SJerome Forissier        isize = int(size, 16)
391ae252462SJerome Forissier        elf = self._elfs[int(elf_idx)][0]
392ae252462SJerome Forissier        if elf is None:
393ae252462SJerome Forissier            return ''
394ae252462SJerome Forissier        self.read_sections(elf)
395ae252462SJerome Forissier        if elf not in self._sections:
396ae252462SJerome Forissier            return ''
397ae252462SJerome Forissier        for s in self._sections[elf]:
39830999126SJerome Forissier            if self.overlaps(s, iaddr, isize):
39930999126SJerome Forissier                ret += ' ' + s[0]
40030999126SJerome Forissier        return ret
40130999126SJerome Forissier
402733a15f2SJerome Forissier    def reset(self):
403733a15f2SJerome Forissier        self._call_stack_found = False
404733a15f2SJerome Forissier        if self._addr2line:
405733a15f2SJerome Forissier            self._addr2line.terminate()
406733a15f2SJerome Forissier            self._addr2line = None
407ae252462SJerome Forissier        self._addr2line_elf_name = None
408d720431cSJerome Forissier        self._arch = None
409142c5cccSJerome Forissier        self._saved_abort_line = ''
410ae252462SJerome Forissier        self._sections = {}  # {elf_name: [[name, addr, size], ...], ...}
411ae252462SJerome Forissier        self._regions = []   # [[addr, size, elf_idx, saved line], ...]
412ae252462SJerome Forissier        self._elfs = {0: ["tee.elf", 0]}  # {idx: [uuid, load_addr], ...}
41391068f86SJerome Forissier        self._tee_load_addr = '0x0'
414c90b6663SSumit Garg        self._func_graph_found = False
415c90b6663SSumit Garg        self._func_graph_skip_line = True
416733a15f2SJerome Forissier
417095567e5SJerome Forissier    def pretty_print_path(self, path):
418095567e5SJerome Forissier        if self._strip_path:
419095567e5SJerome Forissier            return re.sub(re.escape(self._strip_path) + '/*', '', path)
420095567e5SJerome Forissier        return path
421095567e5SJerome Forissier
422733a15f2SJerome Forissier    def write(self, line):
423733a15f2SJerome Forissier        if self._call_stack_found:
424733a15f2SJerome Forissier            match = re.search(STACK_ADDR_RE, line)
425733a15f2SJerome Forissier            if match:
426733a15f2SJerome Forissier                addr = match.group('addr')
427733a15f2SJerome Forissier                pre = match.start('addr')
428733a15f2SJerome Forissier                post = match.end('addr')
429733a15f2SJerome Forissier                self._out.write(line[:pre])
430733a15f2SJerome Forissier                self._out.write(addr)
4315500d703SJerome Forissier                # The call stack contains return addresses (LR/ELR values).
4325500d703SJerome Forissier                # Heuristic: subtract 2 to obtain the call site of the function
4335500d703SJerome Forissier                # or the location of the exception. This value works for A64,
4345500d703SJerome Forissier                # A32 as well as Thumb.
4355500d703SJerome Forissier                pc = 0
4365500d703SJerome Forissier                lr = int(addr, 16)
4375500d703SJerome Forissier                if lr:
4385500d703SJerome Forissier                    pc = lr - 2
4395500d703SJerome Forissier                res = self.resolve('0x{:x}'.format(pc))
440095567e5SJerome Forissier                res = self.pretty_print_path(res)
441733a15f2SJerome Forissier                self._out.write(' ' + res)
442733a15f2SJerome Forissier                self._out.write(line[post:])
443733a15f2SJerome Forissier                return
444733a15f2SJerome Forissier            else:
445733a15f2SJerome Forissier                self.reset()
446c90b6663SSumit Garg        if self._func_graph_found:
447c90b6663SSumit Garg            match = re.search(GRAPH_ADDR_RE, line)
448c90b6663SSumit Garg            match_re = re.search(GRAPH_RE, line)
449c90b6663SSumit Garg            if match:
450c90b6663SSumit Garg                addr = match.group('addr')
451c90b6663SSumit Garg                pre = match.start('addr')
452c90b6663SSumit Garg                post = match.end('addr')
453c90b6663SSumit Garg                self._out.write(line[:pre])
454c90b6663SSumit Garg                res = self.resolve(addr)
455c90b6663SSumit Garg                res_arr = re.split(' ', res)
456c90b6663SSumit Garg                self._out.write(res_arr[0])
457c90b6663SSumit Garg                self._out.write(line[post:])
458c90b6663SSumit Garg                self._func_graph_skip_line = False
459c90b6663SSumit Garg                return
460c90b6663SSumit Garg            elif match_re:
461c90b6663SSumit Garg                self._out.write(line)
462c90b6663SSumit Garg                return
463c90b6663SSumit Garg            elif self._func_graph_skip_line:
464c90b6663SSumit Garg                return
465c90b6663SSumit Garg            else:
466c90b6663SSumit Garg                self.reset()
46730999126SJerome Forissier        match = re.search(REGION_RE, line)
46830999126SJerome Forissier        if match:
469ae252462SJerome Forissier            # Region table: save info for later processing once
470ae252462SJerome Forissier            # we know which UUID corresponds to which ELF index
47130999126SJerome Forissier            addr = match.group('addr')
47230999126SJerome Forissier            size = match.group('size')
473ae252462SJerome Forissier            elf_idx = match.group('elf_idx')
474ae252462SJerome Forissier            self._regions.append([addr, size, elf_idx, line])
475ae252462SJerome Forissier            return
476ae252462SJerome Forissier        match = re.search(ELF_LIST_RE, line)
477ae252462SJerome Forissier        if match:
478ae252462SJerome Forissier            # ELF list: save info for later. Region table and ELF list
479ae252462SJerome Forissier            # will be displayed when the call stack is reached
480ae252462SJerome Forissier            i = int(match.group('idx'))
481ae252462SJerome Forissier            self._elfs[i] = [match.group('uuid'), match.group('load_addr'),
482ae252462SJerome Forissier                             line]
48330999126SJerome Forissier            return
48499e82b1fSJerome Forissier        match = re.search(TA_PANIC_RE, line)
48599e82b1fSJerome Forissier        if match:
48699e82b1fSJerome Forissier            code = match.group('code')
48799e82b1fSJerome Forissier            if code in tee_result_names:
48899e82b1fSJerome Forissier                line = line.strip() + ' (' + tee_result_names[code] + ')\n'
48999e82b1fSJerome Forissier            self._out.write(line)
49099e82b1fSJerome Forissier            return
491105e09c2SJerome Forissier        match = re.search(TEE_LOAD_ADDR_RE, line)
492105e09c2SJerome Forissier        if match:
493105e09c2SJerome Forissier            self._tee_load_addr = match.group('load_addr')
494733a15f2SJerome Forissier        match = re.search(CALL_STACK_RE, line)
495733a15f2SJerome Forissier        if match:
496733a15f2SJerome Forissier            self._call_stack_found = True
497ae252462SJerome Forissier            if self._regions:
498ae252462SJerome Forissier                for r in self._regions:
499ae252462SJerome Forissier                    r_addr = r[0]
500ae252462SJerome Forissier                    r_size = r[1]
501ae252462SJerome Forissier                    elf_idx = r[2]
502ae252462SJerome Forissier                    saved_line = r[3]
503ae252462SJerome Forissier                    if elf_idx is None:
504ae252462SJerome Forissier                        self._out.write(saved_line)
505ae252462SJerome Forissier                    else:
506ae252462SJerome Forissier                        self._out.write(saved_line.strip() +
507ae252462SJerome Forissier                                        self.sections_in_region(r_addr,
508ae252462SJerome Forissier                                                                r_size,
509ae252462SJerome Forissier                                                                elf_idx) +
510ae252462SJerome Forissier                                        '\n')
511ae252462SJerome Forissier            if self._elfs:
512ae252462SJerome Forissier                for k in self._elfs:
513ae252462SJerome Forissier                    e = self._elfs[k]
514ae252462SJerome Forissier                    if (len(e) >= 3):
5151e6f2ea0SJerome Forissier                        # TA executable or library
516095567e5SJerome Forissier                        self._out.write(e[2].strip())
517095567e5SJerome Forissier                        elf = self.get_elf(e[0])
518095567e5SJerome Forissier                        if elf:
519095567e5SJerome Forissier                            rpath = os.path.realpath(elf)
520095567e5SJerome Forissier                            path = self.pretty_print_path(rpath)
521095567e5SJerome Forissier                            self._out.write(' (' + path + ')')
522095567e5SJerome Forissier                        self._out.write('\n')
523142c5cccSJerome Forissier            # Here is a good place to resolve the abort address because we
524142c5cccSJerome Forissier            # have all the information we need
525142c5cccSJerome Forissier            if self._saved_abort_line:
526142c5cccSJerome Forissier                self._out.write(self.process_abort(self._saved_abort_line))
527c90b6663SSumit Garg        match = re.search(FUNC_GRAPH_RE, line)
528c90b6663SSumit Garg        if match:
529c90b6663SSumit Garg            self._func_graph_found = True
530142c5cccSJerome Forissier        match = re.search(ABORT_ADDR_RE, line)
531142c5cccSJerome Forissier        if match:
53227b83ad2SJerome Forissier            self.reset()
533142c5cccSJerome Forissier            # At this point the arch and TA load address are unknown.
534142c5cccSJerome Forissier            # Save the line so We can translate the abort address later.
535142c5cccSJerome Forissier            self._saved_abort_line = line
536733a15f2SJerome Forissier        self._out.write(line)
537733a15f2SJerome Forissier
538733a15f2SJerome Forissier    def flush(self):
539733a15f2SJerome Forissier        self._out.flush()
540733a15f2SJerome Forissier
541ae252462SJerome Forissier
542733a15f2SJerome Forissierdef main():
543733a15f2SJerome Forissier    args = get_args()
544733a15f2SJerome Forissier    if args.dir:
545733a15f2SJerome Forissier        # Flatten list in case -d is used several times *and* with multiple
546733a15f2SJerome Forissier        # arguments
547733a15f2SJerome Forissier        args.dirs = [item for sublist in args.dir for item in sublist]
548733a15f2SJerome Forissier    else:
549733a15f2SJerome Forissier        args.dirs = []
550733a15f2SJerome Forissier    symbolizer = Symbolizer(sys.stdout, args.dirs, args.strip_path)
551733a15f2SJerome Forissier
5526b4fc675SJerome Forissier    fd = sys.stdin.fileno()
55320d152b8SJerome Forissier    isatty = os.isatty(fd)
55420d152b8SJerome Forissier    if isatty:
5556b4fc675SJerome Forissier        old = termios.tcgetattr(fd)
5566b4fc675SJerome Forissier        new = termios.tcgetattr(fd)
5576b4fc675SJerome Forissier        new[3] = new[3] & ~termios.ECHO  # lflags
5586b4fc675SJerome Forissier    try:
55920d152b8SJerome Forissier        if isatty:
5606b4fc675SJerome Forissier            termios.tcsetattr(fd, termios.TCSADRAIN, new)
561733a15f2SJerome Forissier        for line in sys.stdin:
562733a15f2SJerome Forissier            symbolizer.write(line)
5636b4fc675SJerome Forissier    finally:
564733a15f2SJerome Forissier        symbolizer.flush()
56520d152b8SJerome Forissier        if isatty:
5666b4fc675SJerome Forissier            termios.tcsetattr(fd, termios.TCSADRAIN, old)
567733a15f2SJerome Forissier
5681d8c2a48SJerome Forissier
569733a15f2SJerome Forissierif __name__ == "__main__":
570733a15f2SJerome Forissier    main()
571