1*733a15f2SJerome Forissier#!/usr/bin/env python 2*733a15f2SJerome Forissier# 3*733a15f2SJerome Forissier# Copyright (c) 2017, Linaro Limited 4*733a15f2SJerome Forissier# All rights reserved. 5*733a15f2SJerome Forissier# 6*733a15f2SJerome Forissier# Redistribution and use in source and binary forms, with or without 7*733a15f2SJerome Forissier# modification, are permitted provided that the following conditions are met: 8*733a15f2SJerome Forissier# 9*733a15f2SJerome Forissier# 1. Redistributions of source code must retain the above copyright notice, 10*733a15f2SJerome Forissier# this list of conditions and the following disclaimer. 11*733a15f2SJerome Forissier# 12*733a15f2SJerome Forissier# 2. Redistributions in binary form must reproduce the above copyright notice, 13*733a15f2SJerome Forissier# this list of conditions and the following disclaimer in the documentation 14*733a15f2SJerome Forissier# and/or other materials provided with the distribution. 15*733a15f2SJerome Forissier# 16*733a15f2SJerome Forissier# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17*733a15f2SJerome Forissier# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18*733a15f2SJerome Forissier# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19*733a15f2SJerome Forissier# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20*733a15f2SJerome Forissier# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21*733a15f2SJerome Forissier# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22*733a15f2SJerome Forissier# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23*733a15f2SJerome Forissier# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24*733a15f2SJerome Forissier# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25*733a15f2SJerome Forissier# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26*733a15f2SJerome Forissier# POSSIBILITY OF SUCH DAMAGE. 27*733a15f2SJerome Forissier# 28*733a15f2SJerome Forissier 29*733a15f2SJerome Forissier 30*733a15f2SJerome Forissierimport argparse 31*733a15f2SJerome Forissierimport glob 32*733a15f2SJerome Forissierimport re 33*733a15f2SJerome Forissierimport subprocess 34*733a15f2SJerome Forissierimport sys 35*733a15f2SJerome Forissier 36*733a15f2SJerome ForissierTA_UUID_RE = re.compile(r'Status of TA (?P<uuid>[0-9a-f\-]+)') 37*733a15f2SJerome ForissierTA_INFO_RE = re.compile(': arch: (?P<arch>\w+) ' 38*733a15f2SJerome Forissier 'load address: (?P<load_addr>0x[0-9a-f]+)') 39*733a15f2SJerome ForissierCALL_STACK_RE = re.compile('Call stack:') 40*733a15f2SJerome ForissierSTACK_ADDR_RE = re.compile(r': (?P<addr>0x[0-9a-f]+)') 41*733a15f2SJerome ForissierX64_REGS_RE = re.compile(': x0 [0-9a-f]{16} x1 [0-9a-f]{16}') 42*733a15f2SJerome Forissier 43*733a15f2SJerome Forissierepilog = ''' 44*733a15f2SJerome ForissierThis scripts reads an OP-TEE abort message from stdin and adds debug 45*733a15f2SJerome Forissierinformation ('function at file:line') next to each address in the call stack. 46*733a15f2SJerome ForissierIt uses the paths provided on the command line to locate the appropriate ELF 47*733a15f2SJerome Forissierbinary (tee.elf or Trusted Application) and runs arm-linux-gnueabihf-addr2line 48*733a15f2SJerome Forissieror aarch64-linux-gnu-addr2line to process the addresses. 49*733a15f2SJerome Forissier 50*733a15f2SJerome ForissierOP-TEE abort messages are sent to the secure console. They look like the 51*733a15f2SJerome Forissierfollowing: 52*733a15f2SJerome Forissier 53*733a15f2SJerome Forissier ERROR: TEE-CORE: User TA data-abort at address 0xffffdecd (alignment fault) 54*733a15f2SJerome Forissier ... 55*733a15f2SJerome Forissier ERROR: TEE-CORE: Call stack: 56*733a15f2SJerome Forissier ERROR: TEE-CORE: 0x4000549e 57*733a15f2SJerome Forissier ERROR: TEE-CORE: 0x40001f4b 58*733a15f2SJerome Forissier ERROR: TEE-CORE: 0x4000273f 59*733a15f2SJerome Forissier ERROR: TEE-CORE: 0x40005da7 60*733a15f2SJerome Forissier 61*733a15f2SJerome ForissierInspired by a script of the same name by the Chromium project. 62*733a15f2SJerome Forissier 63*733a15f2SJerome ForissierSample usage: 64*733a15f2SJerome Forissier 65*733a15f2SJerome Forissier $ scripts/symbolize.py -d out/arm-plat-hikey/core -d ../optee_test/out/ta/* 66*733a15f2SJerome Forissier <paste whole dump here> 67*733a15f2SJerome Forissier ^D 68*733a15f2SJerome Forissier''' 69*733a15f2SJerome Forissier 70*733a15f2SJerome Forissierdef get_args(): 71*733a15f2SJerome Forissier parser = argparse.ArgumentParser( 72*733a15f2SJerome Forissier formatter_class=argparse.RawDescriptionHelpFormatter, 73*733a15f2SJerome Forissier description='Symbolizes OP-TEE abort dumps', 74*733a15f2SJerome Forissier epilog=epilog) 75*733a15f2SJerome Forissier parser.add_argument('-d', '--dir', action='append', nargs='+', 76*733a15f2SJerome Forissier help='Search for ELF file in DIR. tee.elf is needed to decode ' 77*733a15f2SJerome Forissier 'a TEE Core or pseudo-TA abort, while <TA_uuid>.elf is required ' 78*733a15f2SJerome Forissier 'if a user-mode TA has crashed.') 79*733a15f2SJerome Forissier parser.add_argument('-s', '--strip_path', 80*733a15f2SJerome Forissier help='Strip STRIP_PATH from file paths') 81*733a15f2SJerome Forissier 82*733a15f2SJerome Forissier return parser.parse_args() 83*733a15f2SJerome Forissier 84*733a15f2SJerome Forissierclass Symbolizer(object): 85*733a15f2SJerome Forissier def __init__(self, out, dirs, strip_path): 86*733a15f2SJerome Forissier self._out = out 87*733a15f2SJerome Forissier self._dirs = dirs 88*733a15f2SJerome Forissier self._strip_path = strip_path 89*733a15f2SJerome Forissier self._addr2line = None 90*733a15f2SJerome Forissier self._bin = 'tee.elf' 91*733a15f2SJerome Forissier self.reset() 92*733a15f2SJerome Forissier 93*733a15f2SJerome Forissier def get_elf(self, elf_or_uuid): 94*733a15f2SJerome Forissier if not elf_or_uuid.endswith('.elf'): 95*733a15f2SJerome Forissier elf_or_uuid += '.elf' 96*733a15f2SJerome Forissier for d in self._dirs: 97*733a15f2SJerome Forissier elf = glob.glob(d + '/' + elf_or_uuid) 98*733a15f2SJerome Forissier if elf: 99*733a15f2SJerome Forissier return elf[0] 100*733a15f2SJerome Forissier 101*733a15f2SJerome Forissier def spawn_addr2line(self): 102*733a15f2SJerome Forissier if not self._addr2line: 103*733a15f2SJerome Forissier elf = self.get_elf(self._bin) 104*733a15f2SJerome Forissier if not elf: 105*733a15f2SJerome Forissier return 106*733a15f2SJerome Forissier if self._arch == 'arm': 107*733a15f2SJerome Forissier cmd = 'arm-linux-gnueabihf-addr2line' 108*733a15f2SJerome Forissier elif self._arch == 'aarch64': 109*733a15f2SJerome Forissier cmd = 'aarch64-linux-gnu-addr2line' 110*733a15f2SJerome Forissier else: 111*733a15f2SJerome Forissier return 112*733a15f2SJerome Forissier self._addr2line = subprocess.Popen([cmd, '-f', '-p', '-e', elf], 113*733a15f2SJerome Forissier stdin = subprocess.PIPE, 114*733a15f2SJerome Forissier stdout = subprocess.PIPE) 115*733a15f2SJerome Forissier 116*733a15f2SJerome Forissier def resolve(self, addr): 117*733a15f2SJerome Forissier offs = self._load_addr 118*733a15f2SJerome Forissier if int(offs, 0) > int(addr, 0): 119*733a15f2SJerome Forissier return '???' 120*733a15f2SJerome Forissier reladdr = '0x{:x}'.format(int(addr, 0) - int(offs, 0)) 121*733a15f2SJerome Forissier self.spawn_addr2line() 122*733a15f2SJerome Forissier if not self._addr2line: 123*733a15f2SJerome Forissier return '???' 124*733a15f2SJerome Forissier try: 125*733a15f2SJerome Forissier print >> self._addr2line.stdin, reladdr 126*733a15f2SJerome Forissier ret = self._addr2line.stdout.readline().rstrip('\n') 127*733a15f2SJerome Forissier except IOError: 128*733a15f2SJerome Forissier ret = '!!!' 129*733a15f2SJerome Forissier return ret 130*733a15f2SJerome Forissier 131*733a15f2SJerome Forissier def reset(self): 132*733a15f2SJerome Forissier self._call_stack_found = False 133*733a15f2SJerome Forissier self._load_addr = '0' 134*733a15f2SJerome Forissier if self._addr2line: 135*733a15f2SJerome Forissier self._addr2line.terminate() 136*733a15f2SJerome Forissier self._addr2line = None 137*733a15f2SJerome Forissier self._arch = 'arm' 138*733a15f2SJerome Forissier 139*733a15f2SJerome Forissier def write(self, line): 140*733a15f2SJerome Forissier if self._call_stack_found: 141*733a15f2SJerome Forissier match = re.search(STACK_ADDR_RE, line) 142*733a15f2SJerome Forissier if match: 143*733a15f2SJerome Forissier addr = match.group('addr') 144*733a15f2SJerome Forissier pre = match.start('addr') 145*733a15f2SJerome Forissier post = match.end('addr') 146*733a15f2SJerome Forissier self._out.write(line[:pre]) 147*733a15f2SJerome Forissier self._out.write(addr) 148*733a15f2SJerome Forissier res = self.resolve(addr) 149*733a15f2SJerome Forissier if self._strip_path: 150*733a15f2SJerome Forissier res = re.sub(re.escape(self._strip_path) + '/*', '', 151*733a15f2SJerome Forissier res) 152*733a15f2SJerome Forissier self._out.write(' ' + res) 153*733a15f2SJerome Forissier self._out.write(line[post:]) 154*733a15f2SJerome Forissier return 155*733a15f2SJerome Forissier else: 156*733a15f2SJerome Forissier self.reset() 157*733a15f2SJerome Forissier match = re.search(CALL_STACK_RE, line) 158*733a15f2SJerome Forissier if match: 159*733a15f2SJerome Forissier self._call_stack_found = True 160*733a15f2SJerome Forissier match = re.search(TA_UUID_RE, line) 161*733a15f2SJerome Forissier if match: 162*733a15f2SJerome Forissier self._bin = match.group('uuid') 163*733a15f2SJerome Forissier match = re.search(TA_INFO_RE, line) 164*733a15f2SJerome Forissier if match: 165*733a15f2SJerome Forissier self._arch = match.group('arch') 166*733a15f2SJerome Forissier self._load_addr = match.group('load_addr') 167*733a15f2SJerome Forissier match = re.search(X64_REGS_RE, line) 168*733a15f2SJerome Forissier if match: 169*733a15f2SJerome Forissier # Assume _arch represents the TEE core. If we have a TA dump, 170*733a15f2SJerome Forissier # it will be overwritten later 171*733a15f2SJerome Forissier self._arch = 'aarch64' 172*733a15f2SJerome Forissier self._out.write(line) 173*733a15f2SJerome Forissier 174*733a15f2SJerome Forissier def flush(self): 175*733a15f2SJerome Forissier self._out.flush() 176*733a15f2SJerome Forissier 177*733a15f2SJerome Forissierdef main(): 178*733a15f2SJerome Forissier args = get_args() 179*733a15f2SJerome Forissier if args.dir: 180*733a15f2SJerome Forissier # Flatten list in case -d is used several times *and* with multiple 181*733a15f2SJerome Forissier # arguments 182*733a15f2SJerome Forissier args.dirs = [item for sublist in args.dir for item in sublist] 183*733a15f2SJerome Forissier else: 184*733a15f2SJerome Forissier args.dirs = [] 185*733a15f2SJerome Forissier symbolizer = Symbolizer(sys.stdout, args.dirs, args.strip_path) 186*733a15f2SJerome Forissier 187*733a15f2SJerome Forissier for line in sys.stdin: 188*733a15f2SJerome Forissier symbolizer.write(line) 189*733a15f2SJerome Forissier symbolizer.flush() 190*733a15f2SJerome Forissier 191*733a15f2SJerome Forissierif __name__ == "__main__": 192*733a15f2SJerome Forissier main() 193