xref: /optee_os/scripts/symbolize.py (revision 99e82b1f64201fc84489235473f3ffd499a056c1)
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]+)')
24*99e82b1fSJerome 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
46f9089765SJerome Forissierarchitecture (arm-linux-gnueabihf-, aarch64-linux-gnu-). The resulting command
47f9089765SJerome Forissieris then expected to be found in the user's PATH.
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
79*99e82b1fSJerome Forissiertee_result_names = {
80*99e82b1fSJerome Forissier        '0xf0100001': 'TEE_ERROR_CORRUPT_OBJECT',
81*99e82b1fSJerome Forissier        '0xf0100002': 'TEE_ERROR_CORRUPT_OBJECT_2',
82*99e82b1fSJerome Forissier        '0xf0100003': 'TEE_ERROR_STORAGE_NOT_AVAILABLE',
83*99e82b1fSJerome Forissier        '0xf0100004': 'TEE_ERROR_STORAGE_NOT_AVAILABLE_2',
84*99e82b1fSJerome Forissier        '0xf0100006': 'TEE_ERROR_CIPHERTEXT_INVALID ',
85*99e82b1fSJerome Forissier        '0xffff0000': 'TEE_ERROR_GENERIC',
86*99e82b1fSJerome Forissier        '0xffff0001': 'TEE_ERROR_ACCESS_DENIED',
87*99e82b1fSJerome Forissier        '0xffff0002': 'TEE_ERROR_CANCEL',
88*99e82b1fSJerome Forissier        '0xffff0003': 'TEE_ERROR_ACCESS_CONFLICT',
89*99e82b1fSJerome Forissier        '0xffff0004': 'TEE_ERROR_EXCESS_DATA',
90*99e82b1fSJerome Forissier        '0xffff0005': 'TEE_ERROR_BAD_FORMAT',
91*99e82b1fSJerome Forissier        '0xffff0006': 'TEE_ERROR_BAD_PARAMETERS',
92*99e82b1fSJerome Forissier        '0xffff0007': 'TEE_ERROR_BAD_STATE',
93*99e82b1fSJerome Forissier        '0xffff0008': 'TEE_ERROR_ITEM_NOT_FOUND',
94*99e82b1fSJerome Forissier        '0xffff0009': 'TEE_ERROR_NOT_IMPLEMENTED',
95*99e82b1fSJerome Forissier        '0xffff000a': 'TEE_ERROR_NOT_SUPPORTED',
96*99e82b1fSJerome Forissier        '0xffff000b': 'TEE_ERROR_NO_DATA',
97*99e82b1fSJerome Forissier        '0xffff000c': 'TEE_ERROR_OUT_OF_MEMORY',
98*99e82b1fSJerome Forissier        '0xffff000d': 'TEE_ERROR_BUSY',
99*99e82b1fSJerome Forissier        '0xffff000e': 'TEE_ERROR_COMMUNICATION',
100*99e82b1fSJerome Forissier        '0xffff000f': 'TEE_ERROR_SECURITY',
101*99e82b1fSJerome Forissier        '0xffff0010': 'TEE_ERROR_SHORT_BUFFER',
102*99e82b1fSJerome Forissier        '0xffff0011': 'TEE_ERROR_EXTERNAL_CANCEL',
103*99e82b1fSJerome Forissier        '0xffff300f': 'TEE_ERROR_OVERFLOW',
104*99e82b1fSJerome Forissier        '0xffff3024': 'TEE_ERROR_TARGET_DEAD',
105*99e82b1fSJerome Forissier        '0xffff3041': 'TEE_ERROR_STORAGE_NO_SPACE',
106*99e82b1fSJerome Forissier        '0xffff3071': 'TEE_ERROR_MAC_INVALID',
107*99e82b1fSJerome Forissier        '0xffff3072': 'TEE_ERROR_SIGNATURE_INVALID',
108*99e82b1fSJerome Forissier        '0xffff5000': 'TEE_ERROR_TIME_NOT_SET',
109*99e82b1fSJerome Forissier        '0xffff5001': 'TEE_ERROR_TIME_NEEDS_RESET',
110*99e82b1fSJerome Forissier    }
111*99e82b1fSJerome 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-'
173d720431cSJerome Forissier
17424778dedSJerome Forissier    def arch_prefix(self, cmd, elf):
17524778dedSJerome Forissier        self.set_arch(elf)
176ae252462SJerome Forissier        if self._arch is None:
177ae252462SJerome Forissier            return ''
178d720431cSJerome Forissier        return self._arch + cmd
179142c5cccSJerome Forissier
180ae252462SJerome Forissier    def spawn_addr2line(self, elf_name):
181ae252462SJerome Forissier        if elf_name is None:
182ae252462SJerome Forissier            return
183ae252462SJerome Forissier        if self._addr2line_elf_name is elf_name:
184ae252462SJerome Forissier            return
185ae252462SJerome Forissier        if self._addr2line:
186ae252462SJerome Forissier            self._addr2line.terminate
187ae252462SJerome Forissier            self._addr2line = None
188ae252462SJerome Forissier        elf = self.get_elf(elf_name)
189733a15f2SJerome Forissier        if not elf:
190733a15f2SJerome Forissier            return
19124778dedSJerome Forissier        cmd = self.arch_prefix('addr2line', elf)
192142c5cccSJerome Forissier        if not cmd:
193733a15f2SJerome Forissier            return
194c0c57c8fSJerome Forissier        self._addr2line = self.my_Popen([cmd, '-f', '-p', '-e', elf])
195ba84a3f5SJerome Forissier        self._addr2line_elf_name = elf_name
196ae252462SJerome Forissier
197ae252462SJerome Forissier    # If addr falls into a region that maps a TA ELF file, return the load
198ae252462SJerome Forissier    # address of that file.
199ae252462SJerome Forissier    def elf_load_addr(self, addr):
200ae252462SJerome Forissier        if self._regions:
201ae252462SJerome Forissier            for r in self._regions:
202ae252462SJerome Forissier                r_addr = int(r[0], 16)
203ae252462SJerome Forissier                r_size = int(r[1], 16)
204ae252462SJerome Forissier                i_addr = int(addr, 16)
205ae252462SJerome Forissier                if (i_addr >= r_addr and i_addr < (r_addr + r_size)):
206ae252462SJerome Forissier                    # Found region
207ae252462SJerome Forissier                    elf_idx = r[2]
208ae252462SJerome Forissier                    if elf_idx is not None:
209ae252462SJerome Forissier                        return self._elfs[int(elf_idx)][1]
210099918f6SSumit Garg            # In case address is not found in TA ELF file, fallback to tee.elf
211099918f6SSumit Garg            # especially to symbolize mixed (user-space and kernel) addresses
212099918f6SSumit Garg            # which is true when syscall ftrace is enabled along with TA
213099918f6SSumit Garg            # ftrace.
21491068f86SJerome Forissier            return self._tee_load_addr
215ae252462SJerome Forissier        else:
216ae252462SJerome Forissier            # tee.elf
217105e09c2SJerome Forissier            return self._tee_load_addr
218ae252462SJerome Forissier
219ae252462SJerome Forissier    def elf_for_addr(self, addr):
220ae252462SJerome Forissier        l_addr = self.elf_load_addr(addr)
22191068f86SJerome Forissier        if l_addr == self._tee_load_addr:
22291068f86SJerome Forissier            return 'tee.elf'
223ae252462SJerome Forissier        for k in self._elfs:
224ae252462SJerome Forissier            e = self._elfs[k]
225ae252462SJerome Forissier            if int(e[1], 16) == int(l_addr, 16):
226ae252462SJerome Forissier                return e[0]
227ae252462SJerome Forissier        return None
228733a15f2SJerome Forissier
229142c5cccSJerome Forissier    def subtract_load_addr(self, addr):
230ae252462SJerome Forissier        l_addr = self.elf_load_addr(addr)
231ae252462SJerome Forissier        if l_addr is None:
232ae252462SJerome Forissier            return None
233ae252462SJerome Forissier        if int(l_addr, 16) > int(addr, 16):
234142c5cccSJerome Forissier            return ''
235ae252462SJerome Forissier        return '0x{:x}'.format(int(addr, 16) - int(l_addr, 16))
236142c5cccSJerome Forissier
237142c5cccSJerome Forissier    def resolve(self, addr):
238142c5cccSJerome Forissier        reladdr = self.subtract_load_addr(addr)
239ae252462SJerome Forissier        self.spawn_addr2line(self.elf_for_addr(addr))
240142c5cccSJerome Forissier        if not reladdr or not self._addr2line:
241733a15f2SJerome Forissier            return '???'
242c0c57c8fSJerome Forissier        if self.elf_for_addr(addr) == 'tee.elf':
243c0c57c8fSJerome Forissier            reladdr = '0x{:x}'.format(int(reladdr, 16) +
244c0c57c8fSJerome Forissier                                      int(self.first_vma('tee.elf'), 16))
245733a15f2SJerome Forissier        try:
246bbaeed4dSRouven Czerwinski            print(reladdr, file=self._addr2line.stdin)
247733a15f2SJerome Forissier            ret = self._addr2line.stdout.readline().rstrip('\n')
248733a15f2SJerome Forissier        except IOError:
249733a15f2SJerome Forissier            ret = '!!!'
250733a15f2SJerome Forissier        return ret
251733a15f2SJerome Forissier
2522a0d456fSJerome Forissier    # Armv8.5 with Memory Tagging Extension (MTE)
2532a0d456fSJerome Forissier    def strip_armv85_mte_tag(self, addr):
2542a0d456fSJerome Forissier        i_addr = int(addr, 16)
2552a0d456fSJerome Forissier        i_addr &= ~(0xf << 56)
2562a0d456fSJerome Forissier        return '0x{:x}'.format(i_addr)
2572a0d456fSJerome Forissier
258142c5cccSJerome Forissier    def symbol_plus_offset(self, addr):
259142c5cccSJerome Forissier        ret = ''
260142c5cccSJerome Forissier        prevsize = 0
2612a0d456fSJerome Forissier        addr = self.strip_armv85_mte_tag(addr)
262142c5cccSJerome Forissier        reladdr = self.subtract_load_addr(addr)
263ae252462SJerome Forissier        elf_name = self.elf_for_addr(addr)
264ae252462SJerome Forissier        if elf_name is None:
265ae252462SJerome Forissier            return ''
266ae252462SJerome Forissier        elf = self.get_elf(elf_name)
26724778dedSJerome Forissier        cmd = self.arch_prefix('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)
30824778dedSJerome Forissier        cmd = self.arch_prefix('objdump', elf)
309142c5cccSJerome Forissier        if not reladdr or not elf or not cmd:
310142c5cccSJerome Forissier            return ''
31130999126SJerome Forissier        iaddr = int(reladdr, 16)
3121cbf777bSJerome Forissier        objdump = self.my_Popen([cmd, '--section-headers', elf])
313142c5cccSJerome Forissier        for line in iter(objdump.stdout.readline, ''):
314142c5cccSJerome Forissier            try:
315142c5cccSJerome Forissier                idx, name, size, vma, lma, offs, algn = line.split()
3161d8c2a48SJerome Forissier            except ValueError:
317ae252462SJerome Forissier                continue
318142c5cccSJerome Forissier            ivma = int(vma, 16)
319142c5cccSJerome Forissier            isize = int(size, 16)
320142c5cccSJerome Forissier            if ivma == iaddr:
321142c5cccSJerome Forissier                ret = name
322142c5cccSJerome Forissier                break
323142c5cccSJerome Forissier            if ivma < iaddr and ivma + isize >= iaddr:
324142c5cccSJerome Forissier                offs = iaddr - ivma
325142c5cccSJerome Forissier                ret = name + '+' + str(offs)
326142c5cccSJerome Forissier                break
327142c5cccSJerome Forissier        objdump.terminate()
328142c5cccSJerome Forissier        return ret
329142c5cccSJerome Forissier
330142c5cccSJerome Forissier    def process_abort(self, line):
331142c5cccSJerome Forissier        ret = ''
332142c5cccSJerome Forissier        match = re.search(ABORT_ADDR_RE, line)
333142c5cccSJerome Forissier        addr = match.group('addr')
334142c5cccSJerome Forissier        pre = match.start('addr')
335142c5cccSJerome Forissier        post = match.end('addr')
336142c5cccSJerome Forissier        sym = self.symbol_plus_offset(addr)
337142c5cccSJerome Forissier        sec = self.section_plus_offset(addr)
338142c5cccSJerome Forissier        if sym or sec:
339142c5cccSJerome Forissier            ret += line[:pre]
340142c5cccSJerome Forissier            ret += addr
341142c5cccSJerome Forissier            if sym:
342142c5cccSJerome Forissier                ret += ' ' + sym
343142c5cccSJerome Forissier            if sec:
344142c5cccSJerome Forissier                ret += ' ' + sec
345142c5cccSJerome Forissier            ret += line[post:]
346142c5cccSJerome Forissier        return ret
347142c5cccSJerome Forissier
34830999126SJerome Forissier    # Return all ELF sections with the ALLOC flag
349ae252462SJerome Forissier    def read_sections(self, elf_name):
350ae252462SJerome Forissier        if elf_name is None:
35130999126SJerome Forissier            return
352ae252462SJerome Forissier        if elf_name in self._sections:
353ae252462SJerome Forissier            return
354ae252462SJerome Forissier        elf = self.get_elf(elf_name)
355d7c22aceSJerome Forissier        if not elf:
356d7c22aceSJerome Forissier            return
35724778dedSJerome Forissier        cmd = self.arch_prefix('objdump', elf)
35830999126SJerome Forissier        if not elf or not cmd:
35930999126SJerome Forissier            return
360ae252462SJerome Forissier        self._sections[elf_name] = []
3611cbf777bSJerome Forissier        objdump = self.my_Popen([cmd, '--section-headers', elf])
36230999126SJerome Forissier        for line in iter(objdump.stdout.readline, ''):
36330999126SJerome Forissier            try:
36430999126SJerome Forissier                _, name, size, vma, _, _, _ = line.split()
3651d8c2a48SJerome Forissier            except ValueError:
36630999126SJerome Forissier                if 'ALLOC' in line:
367ae252462SJerome Forissier                    self._sections[elf_name].append([name, int(vma, 16),
368ae252462SJerome Forissier                                                     int(size, 16)])
36930999126SJerome Forissier
370c0c57c8fSJerome Forissier    def first_vma(self, elf_name):
371c0c57c8fSJerome Forissier        self.read_sections(elf_name)
372c0c57c8fSJerome Forissier        return '0x{:x}'.format(self._sections[elf_name][0][1])
373c0c57c8fSJerome Forissier
37430999126SJerome Forissier    def overlaps(self, section, addr, size):
37530999126SJerome Forissier        sec_addr = section[1]
37630999126SJerome Forissier        sec_size = section[2]
37730999126SJerome Forissier        if not size or not sec_size:
37830999126SJerome Forissier            return False
379ae252462SJerome Forissier        return ((addr <= (sec_addr + sec_size - 1)) and
380ae252462SJerome Forissier                ((addr + size - 1) >= sec_addr))
38130999126SJerome Forissier
382ae252462SJerome Forissier    def sections_in_region(self, addr, size, elf_idx):
38330999126SJerome Forissier        ret = ''
38430999126SJerome Forissier        addr = self.subtract_load_addr(addr)
38530999126SJerome Forissier        if not addr:
38630999126SJerome Forissier            return ''
38730999126SJerome Forissier        iaddr = int(addr, 16)
38830999126SJerome Forissier        isize = int(size, 16)
389ae252462SJerome Forissier        elf = self._elfs[int(elf_idx)][0]
390ae252462SJerome Forissier        if elf is None:
391ae252462SJerome Forissier            return ''
392ae252462SJerome Forissier        self.read_sections(elf)
393ae252462SJerome Forissier        if elf not in self._sections:
394ae252462SJerome Forissier            return ''
395ae252462SJerome Forissier        for s in self._sections[elf]:
39630999126SJerome Forissier            if self.overlaps(s, iaddr, isize):
39730999126SJerome Forissier                ret += ' ' + s[0]
39830999126SJerome Forissier        return ret
39930999126SJerome Forissier
400733a15f2SJerome Forissier    def reset(self):
401733a15f2SJerome Forissier        self._call_stack_found = False
402733a15f2SJerome Forissier        if self._addr2line:
403733a15f2SJerome Forissier            self._addr2line.terminate()
404733a15f2SJerome Forissier            self._addr2line = None
405ae252462SJerome Forissier        self._addr2line_elf_name = None
406d720431cSJerome Forissier        self._arch = None
407142c5cccSJerome Forissier        self._saved_abort_line = ''
408ae252462SJerome Forissier        self._sections = {}  # {elf_name: [[name, addr, size], ...], ...}
409ae252462SJerome Forissier        self._regions = []   # [[addr, size, elf_idx, saved line], ...]
410ae252462SJerome Forissier        self._elfs = {0: ["tee.elf", 0]}  # {idx: [uuid, load_addr], ...}
41191068f86SJerome Forissier        self._tee_load_addr = '0x0'
412c90b6663SSumit Garg        self._func_graph_found = False
413c90b6663SSumit Garg        self._func_graph_skip_line = True
414733a15f2SJerome Forissier
415095567e5SJerome Forissier    def pretty_print_path(self, path):
416095567e5SJerome Forissier        if self._strip_path:
417095567e5SJerome Forissier            return re.sub(re.escape(self._strip_path) + '/*', '', path)
418095567e5SJerome Forissier        return path
419095567e5SJerome Forissier
420733a15f2SJerome Forissier    def write(self, line):
421733a15f2SJerome Forissier        if self._call_stack_found:
422733a15f2SJerome Forissier            match = re.search(STACK_ADDR_RE, line)
423733a15f2SJerome Forissier            if match:
424733a15f2SJerome Forissier                addr = match.group('addr')
425733a15f2SJerome Forissier                pre = match.start('addr')
426733a15f2SJerome Forissier                post = match.end('addr')
427733a15f2SJerome Forissier                self._out.write(line[:pre])
428733a15f2SJerome Forissier                self._out.write(addr)
4295500d703SJerome Forissier                # The call stack contains return addresses (LR/ELR values).
4305500d703SJerome Forissier                # Heuristic: subtract 2 to obtain the call site of the function
4315500d703SJerome Forissier                # or the location of the exception. This value works for A64,
4325500d703SJerome Forissier                # A32 as well as Thumb.
4335500d703SJerome Forissier                pc = 0
4345500d703SJerome Forissier                lr = int(addr, 16)
4355500d703SJerome Forissier                if lr:
4365500d703SJerome Forissier                    pc = lr - 2
4375500d703SJerome Forissier                res = self.resolve('0x{:x}'.format(pc))
438095567e5SJerome Forissier                res = self.pretty_print_path(res)
439733a15f2SJerome Forissier                self._out.write(' ' + res)
440733a15f2SJerome Forissier                self._out.write(line[post:])
441733a15f2SJerome Forissier                return
442733a15f2SJerome Forissier            else:
443733a15f2SJerome Forissier                self.reset()
444c90b6663SSumit Garg        if self._func_graph_found:
445c90b6663SSumit Garg            match = re.search(GRAPH_ADDR_RE, line)
446c90b6663SSumit Garg            match_re = re.search(GRAPH_RE, line)
447c90b6663SSumit Garg            if match:
448c90b6663SSumit Garg                addr = match.group('addr')
449c90b6663SSumit Garg                pre = match.start('addr')
450c90b6663SSumit Garg                post = match.end('addr')
451c90b6663SSumit Garg                self._out.write(line[:pre])
452c90b6663SSumit Garg                res = self.resolve(addr)
453c90b6663SSumit Garg                res_arr = re.split(' ', res)
454c90b6663SSumit Garg                self._out.write(res_arr[0])
455c90b6663SSumit Garg                self._out.write(line[post:])
456c90b6663SSumit Garg                self._func_graph_skip_line = False
457c90b6663SSumit Garg                return
458c90b6663SSumit Garg            elif match_re:
459c90b6663SSumit Garg                self._out.write(line)
460c90b6663SSumit Garg                return
461c90b6663SSumit Garg            elif self._func_graph_skip_line:
462c90b6663SSumit Garg                return
463c90b6663SSumit Garg            else:
464c90b6663SSumit Garg                self.reset()
46530999126SJerome Forissier        match = re.search(REGION_RE, line)
46630999126SJerome Forissier        if match:
467ae252462SJerome Forissier            # Region table: save info for later processing once
468ae252462SJerome Forissier            # we know which UUID corresponds to which ELF index
46930999126SJerome Forissier            addr = match.group('addr')
47030999126SJerome Forissier            size = match.group('size')
471ae252462SJerome Forissier            elf_idx = match.group('elf_idx')
472ae252462SJerome Forissier            self._regions.append([addr, size, elf_idx, line])
473ae252462SJerome Forissier            return
474ae252462SJerome Forissier        match = re.search(ELF_LIST_RE, line)
475ae252462SJerome Forissier        if match:
476ae252462SJerome Forissier            # ELF list: save info for later. Region table and ELF list
477ae252462SJerome Forissier            # will be displayed when the call stack is reached
478ae252462SJerome Forissier            i = int(match.group('idx'))
479ae252462SJerome Forissier            self._elfs[i] = [match.group('uuid'), match.group('load_addr'),
480ae252462SJerome Forissier                             line]
48130999126SJerome Forissier            return
482*99e82b1fSJerome Forissier        match = re.search(TA_PANIC_RE, line)
483*99e82b1fSJerome Forissier        if match:
484*99e82b1fSJerome Forissier            code = match.group('code')
485*99e82b1fSJerome Forissier            if code in tee_result_names:
486*99e82b1fSJerome Forissier                line = line.strip() + ' (' + tee_result_names[code] + ')\n'
487*99e82b1fSJerome Forissier            self._out.write(line)
488*99e82b1fSJerome Forissier            return
489105e09c2SJerome Forissier        match = re.search(TEE_LOAD_ADDR_RE, line)
490105e09c2SJerome Forissier        if match:
491105e09c2SJerome Forissier            self._tee_load_addr = match.group('load_addr')
492733a15f2SJerome Forissier        match = re.search(CALL_STACK_RE, line)
493733a15f2SJerome Forissier        if match:
494733a15f2SJerome Forissier            self._call_stack_found = True
495ae252462SJerome Forissier            if self._regions:
496ae252462SJerome Forissier                for r in self._regions:
497ae252462SJerome Forissier                    r_addr = r[0]
498ae252462SJerome Forissier                    r_size = r[1]
499ae252462SJerome Forissier                    elf_idx = r[2]
500ae252462SJerome Forissier                    saved_line = r[3]
501ae252462SJerome Forissier                    if elf_idx is None:
502ae252462SJerome Forissier                        self._out.write(saved_line)
503ae252462SJerome Forissier                    else:
504ae252462SJerome Forissier                        self._out.write(saved_line.strip() +
505ae252462SJerome Forissier                                        self.sections_in_region(r_addr,
506ae252462SJerome Forissier                                                                r_size,
507ae252462SJerome Forissier                                                                elf_idx) +
508ae252462SJerome Forissier                                        '\n')
509ae252462SJerome Forissier            if self._elfs:
510ae252462SJerome Forissier                for k in self._elfs:
511ae252462SJerome Forissier                    e = self._elfs[k]
512ae252462SJerome Forissier                    if (len(e) >= 3):
5131e6f2ea0SJerome Forissier                        # TA executable or library
514095567e5SJerome Forissier                        self._out.write(e[2].strip())
515095567e5SJerome Forissier                        elf = self.get_elf(e[0])
516095567e5SJerome Forissier                        if elf:
517095567e5SJerome Forissier                            rpath = os.path.realpath(elf)
518095567e5SJerome Forissier                            path = self.pretty_print_path(rpath)
519095567e5SJerome Forissier                            self._out.write(' (' + path + ')')
520095567e5SJerome Forissier                        self._out.write('\n')
521142c5cccSJerome Forissier            # Here is a good place to resolve the abort address because we
522142c5cccSJerome Forissier            # have all the information we need
523142c5cccSJerome Forissier            if self._saved_abort_line:
524142c5cccSJerome Forissier                self._out.write(self.process_abort(self._saved_abort_line))
525c90b6663SSumit Garg        match = re.search(FUNC_GRAPH_RE, line)
526c90b6663SSumit Garg        if match:
527c90b6663SSumit Garg            self._func_graph_found = True
528142c5cccSJerome Forissier        match = re.search(ABORT_ADDR_RE, line)
529142c5cccSJerome Forissier        if match:
53027b83ad2SJerome Forissier            self.reset()
531142c5cccSJerome Forissier            # At this point the arch and TA load address are unknown.
532142c5cccSJerome Forissier            # Save the line so We can translate the abort address later.
533142c5cccSJerome Forissier            self._saved_abort_line = line
534733a15f2SJerome Forissier        self._out.write(line)
535733a15f2SJerome Forissier
536733a15f2SJerome Forissier    def flush(self):
537733a15f2SJerome Forissier        self._out.flush()
538733a15f2SJerome Forissier
539ae252462SJerome Forissier
540733a15f2SJerome Forissierdef main():
541733a15f2SJerome Forissier    args = get_args()
542733a15f2SJerome Forissier    if args.dir:
543733a15f2SJerome Forissier        # Flatten list in case -d is used several times *and* with multiple
544733a15f2SJerome Forissier        # arguments
545733a15f2SJerome Forissier        args.dirs = [item for sublist in args.dir for item in sublist]
546733a15f2SJerome Forissier    else:
547733a15f2SJerome Forissier        args.dirs = []
548733a15f2SJerome Forissier    symbolizer = Symbolizer(sys.stdout, args.dirs, args.strip_path)
549733a15f2SJerome Forissier
5506b4fc675SJerome Forissier    fd = sys.stdin.fileno()
55120d152b8SJerome Forissier    isatty = os.isatty(fd)
55220d152b8SJerome Forissier    if isatty:
5536b4fc675SJerome Forissier        old = termios.tcgetattr(fd)
5546b4fc675SJerome Forissier        new = termios.tcgetattr(fd)
5556b4fc675SJerome Forissier        new[3] = new[3] & ~termios.ECHO  # lflags
5566b4fc675SJerome Forissier    try:
55720d152b8SJerome Forissier        if isatty:
5586b4fc675SJerome Forissier            termios.tcsetattr(fd, termios.TCSADRAIN, new)
559733a15f2SJerome Forissier        for line in sys.stdin:
560733a15f2SJerome Forissier            symbolizer.write(line)
5616b4fc675SJerome Forissier    finally:
562733a15f2SJerome Forissier        symbolizer.flush()
56320d152b8SJerome Forissier        if isatty:
5646b4fc675SJerome Forissier            termios.tcsetattr(fd, termios.TCSADRAIN, old)
565733a15f2SJerome Forissier
5661d8c2a48SJerome Forissier
567733a15f2SJerome Forissierif __name__ == "__main__":
568733a15f2SJerome Forissier    main()
569