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