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}') 42*142c5cccSJerome ForissierABORT_ADDR_RE = re.compile('-abort at address (?P<addr>0x[0-9a-f]+)') 43733a15f2SJerome Forissier 44733a15f2SJerome Forissierepilog = ''' 45733a15f2SJerome ForissierThis scripts reads an OP-TEE abort message from stdin and adds debug 46733a15f2SJerome Forissierinformation ('function at file:line') next to each address in the call stack. 47733a15f2SJerome ForissierIt uses the paths provided on the command line to locate the appropriate ELF 48733a15f2SJerome Forissierbinary (tee.elf or Trusted Application) and runs arm-linux-gnueabihf-addr2line 49733a15f2SJerome Forissieror aarch64-linux-gnu-addr2line to process the addresses. 50733a15f2SJerome Forissier 51733a15f2SJerome ForissierOP-TEE abort messages are sent to the secure console. They look like the 52733a15f2SJerome Forissierfollowing: 53733a15f2SJerome Forissier 54733a15f2SJerome Forissier ERROR: TEE-CORE: User TA data-abort at address 0xffffdecd (alignment fault) 55733a15f2SJerome Forissier ... 56733a15f2SJerome Forissier ERROR: TEE-CORE: Call stack: 57733a15f2SJerome Forissier ERROR: TEE-CORE: 0x4000549e 58733a15f2SJerome Forissier ERROR: TEE-CORE: 0x40001f4b 59733a15f2SJerome Forissier ERROR: TEE-CORE: 0x4000273f 60733a15f2SJerome Forissier ERROR: TEE-CORE: 0x40005da7 61733a15f2SJerome Forissier 62733a15f2SJerome ForissierInspired by a script of the same name by the Chromium project. 63733a15f2SJerome Forissier 64733a15f2SJerome ForissierSample usage: 65733a15f2SJerome Forissier 66733a15f2SJerome Forissier $ scripts/symbolize.py -d out/arm-plat-hikey/core -d ../optee_test/out/ta/* 67733a15f2SJerome Forissier <paste whole dump here> 68733a15f2SJerome Forissier ^D 69733a15f2SJerome Forissier''' 70733a15f2SJerome Forissier 71733a15f2SJerome Forissierdef get_args(): 72733a15f2SJerome Forissier parser = argparse.ArgumentParser( 73733a15f2SJerome Forissier formatter_class=argparse.RawDescriptionHelpFormatter, 74733a15f2SJerome Forissier description='Symbolizes OP-TEE abort dumps', 75733a15f2SJerome Forissier epilog=epilog) 76733a15f2SJerome Forissier parser.add_argument('-d', '--dir', action='append', nargs='+', 77733a15f2SJerome Forissier help='Search for ELF file in DIR. tee.elf is needed to decode ' 78733a15f2SJerome Forissier 'a TEE Core or pseudo-TA abort, while <TA_uuid>.elf is required ' 79733a15f2SJerome Forissier 'if a user-mode TA has crashed.') 80733a15f2SJerome Forissier parser.add_argument('-s', '--strip_path', 81733a15f2SJerome Forissier help='Strip STRIP_PATH from file paths') 82733a15f2SJerome Forissier 83733a15f2SJerome Forissier return parser.parse_args() 84733a15f2SJerome Forissier 85733a15f2SJerome Forissierclass Symbolizer(object): 86733a15f2SJerome Forissier def __init__(self, out, dirs, strip_path): 87733a15f2SJerome Forissier self._out = out 88733a15f2SJerome Forissier self._dirs = dirs 89733a15f2SJerome Forissier self._strip_path = strip_path 90733a15f2SJerome Forissier self._addr2line = None 91733a15f2SJerome Forissier self._bin = 'tee.elf' 92733a15f2SJerome Forissier self.reset() 93733a15f2SJerome Forissier 94733a15f2SJerome Forissier def get_elf(self, elf_or_uuid): 95733a15f2SJerome Forissier if not elf_or_uuid.endswith('.elf'): 96733a15f2SJerome Forissier elf_or_uuid += '.elf' 97733a15f2SJerome Forissier for d in self._dirs: 98733a15f2SJerome Forissier elf = glob.glob(d + '/' + elf_or_uuid) 99733a15f2SJerome Forissier if elf: 100733a15f2SJerome Forissier return elf[0] 101733a15f2SJerome Forissier 102*142c5cccSJerome Forissier def arch_prefix(self, cmd): 103*142c5cccSJerome Forissier if self._arch == 'arm': 104*142c5cccSJerome Forissier return 'arm-linux-gnueabihf-' + cmd 105*142c5cccSJerome Forissier elif self._arch == 'aarch64': 106*142c5cccSJerome Forissier return 'aarch64-linux-gnu-' + cmd 107*142c5cccSJerome Forissier else: 108*142c5cccSJerome Forissier return '' 109*142c5cccSJerome Forissier 110733a15f2SJerome Forissier def spawn_addr2line(self): 111733a15f2SJerome Forissier if not self._addr2line: 112733a15f2SJerome Forissier elf = self.get_elf(self._bin) 113733a15f2SJerome Forissier if not elf: 114733a15f2SJerome Forissier return 115*142c5cccSJerome Forissier cmd = self.arch_prefix('addr2line') 116*142c5cccSJerome Forissier if not cmd: 117733a15f2SJerome Forissier return 118733a15f2SJerome Forissier self._addr2line = subprocess.Popen([cmd, '-f', '-p', '-e', elf], 119733a15f2SJerome Forissier stdin = subprocess.PIPE, 120733a15f2SJerome Forissier stdout = subprocess.PIPE) 121733a15f2SJerome Forissier 122*142c5cccSJerome Forissier def subtract_load_addr(self, addr): 123733a15f2SJerome Forissier offs = self._load_addr 124fd5d0622SJerome Forissier if int(offs, 16) > int(addr, 16): 125*142c5cccSJerome Forissier return '' 126*142c5cccSJerome Forissier return '0x{:x}'.format(int(addr, 16) - int(offs, 16)) 127*142c5cccSJerome Forissier 128*142c5cccSJerome Forissier def resolve(self, addr): 129*142c5cccSJerome Forissier reladdr = self.subtract_load_addr(addr) 130733a15f2SJerome Forissier self.spawn_addr2line() 131*142c5cccSJerome Forissier if not reladdr or not self._addr2line: 132733a15f2SJerome Forissier return '???' 133733a15f2SJerome Forissier try: 134733a15f2SJerome Forissier print >> self._addr2line.stdin, reladdr 135733a15f2SJerome Forissier ret = self._addr2line.stdout.readline().rstrip('\n') 136733a15f2SJerome Forissier except IOError: 137733a15f2SJerome Forissier ret = '!!!' 138733a15f2SJerome Forissier return ret 139733a15f2SJerome Forissier 140*142c5cccSJerome Forissier def symbol_plus_offset(self, addr): 141*142c5cccSJerome Forissier ret = '' 142*142c5cccSJerome Forissier prevsize = 0 143*142c5cccSJerome Forissier reladdr = self.subtract_load_addr(addr) 144*142c5cccSJerome Forissier elf = self.get_elf(self._bin) 145*142c5cccSJerome Forissier cmd = self.arch_prefix('nm') 146*142c5cccSJerome Forissier if not reladdr or not elf or not cmd: 147*142c5cccSJerome Forissier return '' 148*142c5cccSJerome Forissier ireladdr = int(reladdr, 0) 149*142c5cccSJerome Forissier nm = subprocess.Popen([cmd, '--numeric-sort', '--print-size', elf], 150*142c5cccSJerome Forissier stdin = subprocess.PIPE, 151*142c5cccSJerome Forissier stdout = subprocess.PIPE) 152*142c5cccSJerome Forissier for line in iter(nm.stdout.readline, ''): 153*142c5cccSJerome Forissier try: 154*142c5cccSJerome Forissier addr, size, _, name = line.split() 155*142c5cccSJerome Forissier except: 156*142c5cccSJerome Forissier # Size is missing 157*142c5cccSJerome Forissier addr, _, name = line.split() 158*142c5cccSJerome Forissier size = '0' 159*142c5cccSJerome Forissier iaddr = int(addr, 16) 160*142c5cccSJerome Forissier isize = int(size, 16) 161*142c5cccSJerome Forissier if iaddr == ireladdr: 162*142c5cccSJerome Forissier ret = name 163*142c5cccSJerome Forissier break 164*142c5cccSJerome Forissier if iaddr < ireladdr and iaddr + isize >= ireladdr: 165*142c5cccSJerome Forissier offs = ireladdr - iaddr 166*142c5cccSJerome Forissier ret = name + '+' + str(offs) 167*142c5cccSJerome Forissier break 168*142c5cccSJerome Forissier if iaddr > ireladdr and prevsize == 0: 169*142c5cccSJerome Forissier offs = iaddr + ireladdr 170*142c5cccSJerome Forissier ret = prevname + '+' + str(offs) 171*142c5cccSJerome Forissier break 172*142c5cccSJerome Forissier prevsize = size 173*142c5cccSJerome Forissier prevname = name 174*142c5cccSJerome Forissier nm.terminate() 175*142c5cccSJerome Forissier return ret 176*142c5cccSJerome Forissier 177*142c5cccSJerome Forissier def section_plus_offset(self, addr): 178*142c5cccSJerome Forissier ret = '' 179*142c5cccSJerome Forissier reladdr = self.subtract_load_addr(addr) 180*142c5cccSJerome Forissier elf = self.get_elf(self._bin) 181*142c5cccSJerome Forissier cmd = self.arch_prefix('objdump') 182*142c5cccSJerome Forissier if not reladdr or not elf or not cmd: 183*142c5cccSJerome Forissier return '' 184*142c5cccSJerome Forissier iaddr = int(reladdr, 0) 185*142c5cccSJerome Forissier objdump = subprocess.Popen([cmd, '--section-headers', elf], 186*142c5cccSJerome Forissier stdin = subprocess.PIPE, 187*142c5cccSJerome Forissier stdout = subprocess.PIPE) 188*142c5cccSJerome Forissier for line in iter(objdump.stdout.readline, ''): 189*142c5cccSJerome Forissier try: 190*142c5cccSJerome Forissier idx, name, size, vma, lma, offs, algn = line.split() 191*142c5cccSJerome Forissier except: 192*142c5cccSJerome Forissier continue; 193*142c5cccSJerome Forissier ivma = int(vma, 16) 194*142c5cccSJerome Forissier isize = int(size, 16) 195*142c5cccSJerome Forissier if ivma == iaddr: 196*142c5cccSJerome Forissier ret = name 197*142c5cccSJerome Forissier break 198*142c5cccSJerome Forissier if ivma < iaddr and ivma + isize >= iaddr: 199*142c5cccSJerome Forissier offs = iaddr - ivma 200*142c5cccSJerome Forissier ret = name + '+' + str(offs) 201*142c5cccSJerome Forissier break 202*142c5cccSJerome Forissier objdump.terminate() 203*142c5cccSJerome Forissier return ret 204*142c5cccSJerome Forissier 205*142c5cccSJerome Forissier def process_abort(self, line): 206*142c5cccSJerome Forissier ret = '' 207*142c5cccSJerome Forissier match = re.search(ABORT_ADDR_RE, line) 208*142c5cccSJerome Forissier addr = match.group('addr') 209*142c5cccSJerome Forissier pre = match.start('addr') 210*142c5cccSJerome Forissier post = match.end('addr') 211*142c5cccSJerome Forissier sym = self.symbol_plus_offset(addr) 212*142c5cccSJerome Forissier sec = self.section_plus_offset(addr) 213*142c5cccSJerome Forissier if sym or sec: 214*142c5cccSJerome Forissier ret += line[:pre] 215*142c5cccSJerome Forissier ret += addr 216*142c5cccSJerome Forissier if sym: 217*142c5cccSJerome Forissier ret += ' ' + sym 218*142c5cccSJerome Forissier if sec: 219*142c5cccSJerome Forissier ret += ' ' + sec 220*142c5cccSJerome Forissier ret += line[post:] 221*142c5cccSJerome Forissier return ret 222*142c5cccSJerome Forissier 223733a15f2SJerome Forissier def reset(self): 224733a15f2SJerome Forissier self._call_stack_found = False 225733a15f2SJerome Forissier self._load_addr = '0' 226733a15f2SJerome Forissier if self._addr2line: 227733a15f2SJerome Forissier self._addr2line.terminate() 228733a15f2SJerome Forissier self._addr2line = None 229733a15f2SJerome Forissier self._arch = 'arm' 230*142c5cccSJerome Forissier self._saved_abort_line = '' 231733a15f2SJerome Forissier 232733a15f2SJerome Forissier def write(self, line): 233733a15f2SJerome Forissier if self._call_stack_found: 234733a15f2SJerome Forissier match = re.search(STACK_ADDR_RE, line) 235733a15f2SJerome Forissier if match: 236733a15f2SJerome Forissier addr = match.group('addr') 237733a15f2SJerome Forissier pre = match.start('addr') 238733a15f2SJerome Forissier post = match.end('addr') 239733a15f2SJerome Forissier self._out.write(line[:pre]) 240733a15f2SJerome Forissier self._out.write(addr) 241733a15f2SJerome Forissier res = self.resolve(addr) 242733a15f2SJerome Forissier if self._strip_path: 243733a15f2SJerome Forissier res = re.sub(re.escape(self._strip_path) + '/*', '', 244733a15f2SJerome Forissier res) 245733a15f2SJerome Forissier self._out.write(' ' + res) 246733a15f2SJerome Forissier self._out.write(line[post:]) 247733a15f2SJerome Forissier return 248733a15f2SJerome Forissier else: 249733a15f2SJerome Forissier self.reset() 250733a15f2SJerome Forissier match = re.search(CALL_STACK_RE, line) 251733a15f2SJerome Forissier if match: 252733a15f2SJerome Forissier self._call_stack_found = True 253*142c5cccSJerome Forissier # Here is a good place to resolve the abort address because we 254*142c5cccSJerome Forissier # have all the information we need 255*142c5cccSJerome Forissier if self._saved_abort_line: 256*142c5cccSJerome Forissier self._out.write(self.process_abort(self._saved_abort_line)) 257733a15f2SJerome Forissier match = re.search(TA_UUID_RE, line) 258733a15f2SJerome Forissier if match: 259733a15f2SJerome Forissier self._bin = match.group('uuid') 260733a15f2SJerome Forissier match = re.search(TA_INFO_RE, line) 261733a15f2SJerome Forissier if match: 262733a15f2SJerome Forissier self._arch = match.group('arch') 263733a15f2SJerome Forissier self._load_addr = match.group('load_addr') 264733a15f2SJerome Forissier match = re.search(X64_REGS_RE, line) 265733a15f2SJerome Forissier if match: 266733a15f2SJerome Forissier # Assume _arch represents the TEE core. If we have a TA dump, 267733a15f2SJerome Forissier # it will be overwritten later 268733a15f2SJerome Forissier self._arch = 'aarch64' 269*142c5cccSJerome Forissier match = re.search(ABORT_ADDR_RE, line) 270*142c5cccSJerome Forissier if match: 271*142c5cccSJerome Forissier # At this point the arch and TA load address are unknown. 272*142c5cccSJerome Forissier # Save the line so We can translate the abort address later. 273*142c5cccSJerome Forissier self._saved_abort_line = line 274733a15f2SJerome Forissier self._out.write(line) 275733a15f2SJerome Forissier 276733a15f2SJerome Forissier def flush(self): 277733a15f2SJerome Forissier self._out.flush() 278733a15f2SJerome Forissier 279733a15f2SJerome Forissierdef main(): 280733a15f2SJerome Forissier args = get_args() 281733a15f2SJerome Forissier if args.dir: 282733a15f2SJerome Forissier # Flatten list in case -d is used several times *and* with multiple 283733a15f2SJerome Forissier # arguments 284733a15f2SJerome Forissier args.dirs = [item for sublist in args.dir for item in sublist] 285733a15f2SJerome Forissier else: 286733a15f2SJerome Forissier args.dirs = [] 287733a15f2SJerome Forissier symbolizer = Symbolizer(sys.stdout, args.dirs, args.strip_path) 288733a15f2SJerome Forissier 289733a15f2SJerome Forissier for line in sys.stdin: 290733a15f2SJerome Forissier symbolizer.write(line) 291733a15f2SJerome Forissier symbolizer.flush() 292733a15f2SJerome Forissier 293733a15f2SJerome Forissierif __name__ == "__main__": 294733a15f2SJerome Forissier main() 295