xref: /optee_os/scripts/symbolize.py (revision fd5d0622932a0bcd82d79af19205852a0e63ebf8)
1733a15f2SJerome Forissier#!/usr/bin/env python
2733a15f2SJerome Forissier#
3733a15f2SJerome Forissier# Copyright (c) 2017, Linaro Limited
4733a15f2SJerome Forissier# All rights reserved.
5733a15f2SJerome Forissier#
6733a15f2SJerome Forissier# Redistribution and use in source and binary forms, with or without
7733a15f2SJerome Forissier# modification, are permitted provided that the following conditions are met:
8733a15f2SJerome Forissier#
9733a15f2SJerome Forissier# 1. Redistributions of source code must retain the above copyright notice,
10733a15f2SJerome Forissier# this list of conditions and the following disclaimer.
11733a15f2SJerome Forissier#
12733a15f2SJerome Forissier# 2. Redistributions in binary form must reproduce the above copyright notice,
13733a15f2SJerome Forissier# this list of conditions and the following disclaimer in the documentation
14733a15f2SJerome Forissier# and/or other materials provided with the distribution.
15733a15f2SJerome Forissier#
16733a15f2SJerome Forissier# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17733a15f2SJerome Forissier# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18733a15f2SJerome Forissier# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19733a15f2SJerome Forissier# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20733a15f2SJerome Forissier# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21733a15f2SJerome Forissier# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22733a15f2SJerome Forissier# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23733a15f2SJerome Forissier# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24733a15f2SJerome Forissier# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25733a15f2SJerome Forissier# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26733a15f2SJerome Forissier# POSSIBILITY OF SUCH DAMAGE.
27733a15f2SJerome Forissier#
28733a15f2SJerome Forissier
29733a15f2SJerome Forissier
30733a15f2SJerome Forissierimport argparse
31733a15f2SJerome Forissierimport glob
32733a15f2SJerome Forissierimport re
33733a15f2SJerome Forissierimport subprocess
34733a15f2SJerome Forissierimport sys
35733a15f2SJerome Forissier
36733a15f2SJerome ForissierTA_UUID_RE = re.compile(r'Status of TA (?P<uuid>[0-9a-f\-]+)')
37733a15f2SJerome ForissierTA_INFO_RE = re.compile(':  arch: (?P<arch>\w+)  '
38733a15f2SJerome Forissier                        'load address: (?P<load_addr>0x[0-9a-f]+)')
39733a15f2SJerome ForissierCALL_STACK_RE = re.compile('Call stack:')
40733a15f2SJerome ForissierSTACK_ADDR_RE = re.compile(r':  (?P<addr>0x[0-9a-f]+)')
41733a15f2SJerome ForissierX64_REGS_RE = re.compile(':  x0  [0-9a-f]{16} x1  [0-9a-f]{16}')
42733a15f2SJerome Forissier
43733a15f2SJerome Forissierepilog = '''
44733a15f2SJerome ForissierThis scripts reads an OP-TEE abort message from stdin and adds debug
45733a15f2SJerome Forissierinformation ('function at file:line') next to each address in the call stack.
46733a15f2SJerome ForissierIt uses the paths provided on the command line to locate the appropriate ELF
47733a15f2SJerome Forissierbinary (tee.elf or Trusted Application) and runs arm-linux-gnueabihf-addr2line
48733a15f2SJerome Forissieror aarch64-linux-gnu-addr2line to process the addresses.
49733a15f2SJerome Forissier
50733a15f2SJerome ForissierOP-TEE abort messages are sent to the secure console. They look like the
51733a15f2SJerome Forissierfollowing:
52733a15f2SJerome Forissier
53733a15f2SJerome Forissier  ERROR:   TEE-CORE: User TA data-abort at address 0xffffdecd (alignment fault)
54733a15f2SJerome Forissier  ...
55733a15f2SJerome Forissier  ERROR:   TEE-CORE: Call stack:
56733a15f2SJerome Forissier  ERROR:   TEE-CORE:  0x4000549e
57733a15f2SJerome Forissier  ERROR:   TEE-CORE:  0x40001f4b
58733a15f2SJerome Forissier  ERROR:   TEE-CORE:  0x4000273f
59733a15f2SJerome Forissier  ERROR:   TEE-CORE:  0x40005da7
60733a15f2SJerome Forissier
61733a15f2SJerome ForissierInspired by a script of the same name by the Chromium project.
62733a15f2SJerome Forissier
63733a15f2SJerome ForissierSample usage:
64733a15f2SJerome Forissier
65733a15f2SJerome Forissier  $ scripts/symbolize.py -d out/arm-plat-hikey/core -d ../optee_test/out/ta/*
66733a15f2SJerome Forissier  <paste whole dump here>
67733a15f2SJerome Forissier  ^D
68733a15f2SJerome Forissier'''
69733a15f2SJerome Forissier
70733a15f2SJerome Forissierdef get_args():
71733a15f2SJerome Forissier    parser = argparse.ArgumentParser(
72733a15f2SJerome Forissier                formatter_class=argparse.RawDescriptionHelpFormatter,
73733a15f2SJerome Forissier                description='Symbolizes OP-TEE abort dumps',
74733a15f2SJerome Forissier                epilog=epilog)
75733a15f2SJerome Forissier    parser.add_argument('-d', '--dir', action='append', nargs='+',
76733a15f2SJerome Forissier        help='Search for ELF file in DIR. tee.elf is needed to decode '
77733a15f2SJerome Forissier             'a TEE Core or pseudo-TA abort, while <TA_uuid>.elf is required '
78733a15f2SJerome Forissier             'if a user-mode TA has crashed.')
79733a15f2SJerome Forissier    parser.add_argument('-s', '--strip_path',
80733a15f2SJerome Forissier        help='Strip STRIP_PATH from file paths')
81733a15f2SJerome Forissier
82733a15f2SJerome Forissier    return parser.parse_args()
83733a15f2SJerome Forissier
84733a15f2SJerome Forissierclass Symbolizer(object):
85733a15f2SJerome Forissier    def __init__(self, out, dirs, strip_path):
86733a15f2SJerome Forissier        self._out = out
87733a15f2SJerome Forissier        self._dirs = dirs
88733a15f2SJerome Forissier        self._strip_path = strip_path
89733a15f2SJerome Forissier        self._addr2line = None
90733a15f2SJerome Forissier        self._bin = 'tee.elf'
91733a15f2SJerome Forissier        self.reset()
92733a15f2SJerome Forissier
93733a15f2SJerome Forissier    def get_elf(self, elf_or_uuid):
94733a15f2SJerome Forissier        if not elf_or_uuid.endswith('.elf'):
95733a15f2SJerome Forissier            elf_or_uuid += '.elf'
96733a15f2SJerome Forissier        for d in self._dirs:
97733a15f2SJerome Forissier            elf = glob.glob(d + '/' + elf_or_uuid)
98733a15f2SJerome Forissier            if elf:
99733a15f2SJerome Forissier                return elf[0]
100733a15f2SJerome Forissier
101733a15f2SJerome Forissier    def spawn_addr2line(self):
102733a15f2SJerome Forissier        if not self._addr2line:
103733a15f2SJerome Forissier            elf = self.get_elf(self._bin)
104733a15f2SJerome Forissier            if not elf:
105733a15f2SJerome Forissier                return
106733a15f2SJerome Forissier            if self._arch == 'arm':
107733a15f2SJerome Forissier                cmd = 'arm-linux-gnueabihf-addr2line'
108733a15f2SJerome Forissier            elif self._arch == 'aarch64':
109733a15f2SJerome Forissier                cmd = 'aarch64-linux-gnu-addr2line'
110733a15f2SJerome Forissier            else:
111733a15f2SJerome Forissier                return
112733a15f2SJerome Forissier            self._addr2line = subprocess.Popen([cmd, '-f', '-p', '-e', elf],
113733a15f2SJerome Forissier                                                stdin = subprocess.PIPE,
114733a15f2SJerome Forissier                                                stdout = subprocess.PIPE)
115733a15f2SJerome Forissier
116733a15f2SJerome Forissier    def resolve(self, addr):
117733a15f2SJerome Forissier        offs = self._load_addr
118*fd5d0622SJerome Forissier        if int(offs, 16) > int(addr, 16):
119733a15f2SJerome Forissier            return '???'
120*fd5d0622SJerome Forissier        reladdr = '0x{:x}'.format(int(addr, 16) - int(offs, 16))
121733a15f2SJerome Forissier        self.spawn_addr2line()
122733a15f2SJerome Forissier        if not self._addr2line:
123733a15f2SJerome Forissier            return '???'
124733a15f2SJerome Forissier        try:
125733a15f2SJerome Forissier            print >> self._addr2line.stdin, reladdr
126733a15f2SJerome Forissier            ret = self._addr2line.stdout.readline().rstrip('\n')
127733a15f2SJerome Forissier        except IOError:
128733a15f2SJerome Forissier            ret = '!!!'
129733a15f2SJerome Forissier        return ret
130733a15f2SJerome Forissier
131733a15f2SJerome Forissier    def reset(self):
132733a15f2SJerome Forissier        self._call_stack_found = False
133733a15f2SJerome Forissier        self._load_addr = '0'
134733a15f2SJerome Forissier        if self._addr2line:
135733a15f2SJerome Forissier            self._addr2line.terminate()
136733a15f2SJerome Forissier            self._addr2line = None
137733a15f2SJerome Forissier        self._arch = 'arm'
138733a15f2SJerome Forissier
139733a15f2SJerome Forissier    def write(self, line):
140733a15f2SJerome Forissier            if self._call_stack_found:
141733a15f2SJerome Forissier                match = re.search(STACK_ADDR_RE, line)
142733a15f2SJerome Forissier                if match:
143733a15f2SJerome Forissier                    addr = match.group('addr')
144733a15f2SJerome Forissier                    pre = match.start('addr')
145733a15f2SJerome Forissier                    post = match.end('addr')
146733a15f2SJerome Forissier                    self._out.write(line[:pre])
147733a15f2SJerome Forissier                    self._out.write(addr)
148733a15f2SJerome Forissier                    res = self.resolve(addr)
149733a15f2SJerome Forissier                    if self._strip_path:
150733a15f2SJerome Forissier                        res = re.sub(re.escape(self._strip_path) + '/*', '',
151733a15f2SJerome Forissier                              res)
152733a15f2SJerome Forissier                    self._out.write(' ' + res)
153733a15f2SJerome Forissier                    self._out.write(line[post:])
154733a15f2SJerome Forissier                    return
155733a15f2SJerome Forissier                else:
156733a15f2SJerome Forissier                    self.reset()
157733a15f2SJerome Forissier            match = re.search(CALL_STACK_RE, line)
158733a15f2SJerome Forissier            if match:
159733a15f2SJerome Forissier                self._call_stack_found = True
160733a15f2SJerome Forissier            match = re.search(TA_UUID_RE, line)
161733a15f2SJerome Forissier            if match:
162733a15f2SJerome Forissier                self._bin = match.group('uuid')
163733a15f2SJerome Forissier            match = re.search(TA_INFO_RE, line)
164733a15f2SJerome Forissier            if match:
165733a15f2SJerome Forissier                self._arch = match.group('arch')
166733a15f2SJerome Forissier                self._load_addr = match.group('load_addr')
167733a15f2SJerome Forissier            match = re.search(X64_REGS_RE, line)
168733a15f2SJerome Forissier            if match:
169733a15f2SJerome Forissier                # Assume _arch represents the TEE core. If we have a TA dump,
170733a15f2SJerome Forissier                # it will be overwritten later
171733a15f2SJerome Forissier                self._arch = 'aarch64'
172733a15f2SJerome Forissier            self._out.write(line)
173733a15f2SJerome Forissier
174733a15f2SJerome Forissier    def flush(self):
175733a15f2SJerome Forissier        self._out.flush()
176733a15f2SJerome Forissier
177733a15f2SJerome Forissierdef main():
178733a15f2SJerome Forissier    args = get_args()
179733a15f2SJerome Forissier    if args.dir:
180733a15f2SJerome Forissier        # Flatten list in case -d is used several times *and* with multiple
181733a15f2SJerome Forissier        # arguments
182733a15f2SJerome Forissier        args.dirs = [item for sublist in args.dir for item in sublist]
183733a15f2SJerome Forissier    else:
184733a15f2SJerome Forissier        args.dirs = []
185733a15f2SJerome Forissier    symbolizer = Symbolizer(sys.stdout, args.dirs, args.strip_path)
186733a15f2SJerome Forissier
187733a15f2SJerome Forissier    for line in sys.stdin:
188733a15f2SJerome Forissier        symbolizer.write(line)
189733a15f2SJerome Forissier    symbolizer.flush()
190733a15f2SJerome Forissier
191733a15f2SJerome Forissierif __name__ == "__main__":
192733a15f2SJerome Forissier    main()
193