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 32157e6213SJerome Forissierimport os 33733a15f2SJerome Forissierimport re 34733a15f2SJerome Forissierimport subprocess 35733a15f2SJerome Forissierimport sys 36733a15f2SJerome Forissier 37733a15f2SJerome ForissierTA_UUID_RE = re.compile(r'Status of TA (?P<uuid>[0-9a-f\-]+)') 38733a15f2SJerome ForissierTA_INFO_RE = re.compile(': arch: (?P<arch>\w+) ' 39733a15f2SJerome Forissier 'load address: (?P<load_addr>0x[0-9a-f]+)') 40733a15f2SJerome ForissierCALL_STACK_RE = re.compile('Call stack:') 41733a15f2SJerome ForissierSTACK_ADDR_RE = re.compile(r': (?P<addr>0x[0-9a-f]+)') 42142c5cccSJerome ForissierABORT_ADDR_RE = re.compile('-abort at address (?P<addr>0x[0-9a-f]+)') 4330999126SJerome ForissierREGION_RE = re.compile('region [0-9]+: va (?P<addr>0x[0-9a-f]+) ' 4430999126SJerome Forissier 'pa 0x[0-9a-f]+ size (?P<size>0x[0-9a-f]+)') 45733a15f2SJerome Forissier 46733a15f2SJerome Forissierepilog = ''' 47733a15f2SJerome ForissierThis scripts reads an OP-TEE abort message from stdin and adds debug 48733a15f2SJerome Forissierinformation ('function at file:line') next to each address in the call stack. 49733a15f2SJerome ForissierIt uses the paths provided on the command line to locate the appropriate ELF 50733a15f2SJerome Forissierbinary (tee.elf or Trusted Application) and runs arm-linux-gnueabihf-addr2line 51733a15f2SJerome Forissieror aarch64-linux-gnu-addr2line to process the addresses. 52733a15f2SJerome Forissier 53733a15f2SJerome ForissierOP-TEE abort messages are sent to the secure console. They look like the 54733a15f2SJerome Forissierfollowing: 55733a15f2SJerome Forissier 56733a15f2SJerome Forissier ERROR: TEE-CORE: User TA data-abort at address 0xffffdecd (alignment fault) 57733a15f2SJerome Forissier ... 58733a15f2SJerome Forissier ERROR: TEE-CORE: Call stack: 59733a15f2SJerome Forissier ERROR: TEE-CORE: 0x4000549e 60733a15f2SJerome Forissier ERROR: TEE-CORE: 0x40001f4b 61733a15f2SJerome Forissier ERROR: TEE-CORE: 0x4000273f 62733a15f2SJerome Forissier ERROR: TEE-CORE: 0x40005da7 63733a15f2SJerome Forissier 64733a15f2SJerome ForissierInspired by a script of the same name by the Chromium project. 65733a15f2SJerome Forissier 66733a15f2SJerome ForissierSample usage: 67733a15f2SJerome Forissier 68733a15f2SJerome Forissier $ scripts/symbolize.py -d out/arm-plat-hikey/core -d ../optee_test/out/ta/* 69733a15f2SJerome Forissier <paste whole dump here> 70733a15f2SJerome Forissier ^D 71733a15f2SJerome Forissier''' 72733a15f2SJerome Forissier 73733a15f2SJerome Forissierdef get_args(): 74733a15f2SJerome Forissier parser = argparse.ArgumentParser( 75733a15f2SJerome Forissier formatter_class=argparse.RawDescriptionHelpFormatter, 76733a15f2SJerome Forissier description='Symbolizes OP-TEE abort dumps', 77733a15f2SJerome Forissier epilog=epilog) 78733a15f2SJerome Forissier parser.add_argument('-d', '--dir', action='append', nargs='+', 79733a15f2SJerome Forissier help='Search for ELF file in DIR. tee.elf is needed to decode ' 80733a15f2SJerome Forissier 'a TEE Core or pseudo-TA abort, while <TA_uuid>.elf is required ' 81157e6213SJerome Forissier 'if a user-mode TA has crashed. For convenience, ELF files ' 82157e6213SJerome Forissier 'may also be given.') 83733a15f2SJerome Forissier parser.add_argument('-s', '--strip_path', 84733a15f2SJerome Forissier help='Strip STRIP_PATH from file paths') 85733a15f2SJerome Forissier 86733a15f2SJerome Forissier return parser.parse_args() 87733a15f2SJerome Forissier 88733a15f2SJerome Forissierclass Symbolizer(object): 89733a15f2SJerome Forissier def __init__(self, out, dirs, strip_path): 90733a15f2SJerome Forissier self._out = out 91733a15f2SJerome Forissier self._dirs = dirs 92733a15f2SJerome Forissier self._strip_path = strip_path 93733a15f2SJerome Forissier self._addr2line = None 94733a15f2SJerome Forissier self._bin = 'tee.elf' 95733a15f2SJerome Forissier self.reset() 96733a15f2SJerome Forissier 97733a15f2SJerome Forissier def get_elf(self, elf_or_uuid): 98733a15f2SJerome Forissier if not elf_or_uuid.endswith('.elf'): 99733a15f2SJerome Forissier elf_or_uuid += '.elf' 100733a15f2SJerome Forissier for d in self._dirs: 101157e6213SJerome Forissier if d.endswith(elf_or_uuid) and os.path.isfile(d): 102157e6213SJerome Forissier return d 103733a15f2SJerome Forissier elf = glob.glob(d + '/' + elf_or_uuid) 104733a15f2SJerome Forissier if elf: 105733a15f2SJerome Forissier return elf[0] 106733a15f2SJerome Forissier 107d720431cSJerome Forissier def set_arch(self): 108d720431cSJerome Forissier if self._arch: 109d720431cSJerome Forissier return 110d720431cSJerome Forissier if self._bin: 111d720431cSJerome Forissier p = subprocess.Popen([ 'file', self.get_elf(self._bin) ], 112d720431cSJerome Forissier stdout=subprocess.PIPE) 113d720431cSJerome Forissier output = p.stdout.readlines() 114d720431cSJerome Forissier p.terminate() 115d720431cSJerome Forissier if 'ARM aarch64,' in output[0]: 116d720431cSJerome Forissier self._arch = 'aarch64-linux-gnu-' 117d720431cSJerome Forissier elif 'ARM,' in output[0]: 118d720431cSJerome Forissier self._arch = 'arm-linux-gnueabihf-' 119d720431cSJerome Forissier 120142c5cccSJerome Forissier def arch_prefix(self, cmd): 121d720431cSJerome Forissier self.set_arch() 122d720431cSJerome Forissier return self._arch + cmd 123142c5cccSJerome Forissier 124733a15f2SJerome Forissier def spawn_addr2line(self): 125733a15f2SJerome Forissier if not self._addr2line: 126733a15f2SJerome Forissier elf = self.get_elf(self._bin) 127733a15f2SJerome Forissier if not elf: 128733a15f2SJerome Forissier return 129142c5cccSJerome Forissier cmd = self.arch_prefix('addr2line') 130142c5cccSJerome Forissier if not cmd: 131733a15f2SJerome Forissier return 132733a15f2SJerome Forissier self._addr2line = subprocess.Popen([cmd, '-f', '-p', '-e', elf], 133733a15f2SJerome Forissier stdin = subprocess.PIPE, 134733a15f2SJerome Forissier stdout = subprocess.PIPE) 135733a15f2SJerome Forissier 136142c5cccSJerome Forissier def subtract_load_addr(self, addr): 137733a15f2SJerome Forissier offs = self._load_addr 138fd5d0622SJerome Forissier if int(offs, 16) > int(addr, 16): 139142c5cccSJerome Forissier return '' 140142c5cccSJerome Forissier return '0x{:x}'.format(int(addr, 16) - int(offs, 16)) 141142c5cccSJerome Forissier 142142c5cccSJerome Forissier def resolve(self, addr): 143142c5cccSJerome Forissier reladdr = self.subtract_load_addr(addr) 144733a15f2SJerome Forissier self.spawn_addr2line() 145142c5cccSJerome Forissier if not reladdr or not self._addr2line: 146733a15f2SJerome Forissier return '???' 147733a15f2SJerome Forissier try: 148733a15f2SJerome Forissier print >> self._addr2line.stdin, reladdr 149733a15f2SJerome Forissier ret = self._addr2line.stdout.readline().rstrip('\n') 150733a15f2SJerome Forissier except IOError: 151733a15f2SJerome Forissier ret = '!!!' 152733a15f2SJerome Forissier return ret 153733a15f2SJerome Forissier 154142c5cccSJerome Forissier def symbol_plus_offset(self, addr): 155142c5cccSJerome Forissier ret = '' 156142c5cccSJerome Forissier prevsize = 0 157142c5cccSJerome Forissier reladdr = self.subtract_load_addr(addr) 158142c5cccSJerome Forissier elf = self.get_elf(self._bin) 159142c5cccSJerome Forissier cmd = self.arch_prefix('nm') 160142c5cccSJerome Forissier if not reladdr or not elf or not cmd: 161142c5cccSJerome Forissier return '' 16230999126SJerome Forissier ireladdr = int(reladdr, 16) 163142c5cccSJerome Forissier nm = subprocess.Popen([cmd, '--numeric-sort', '--print-size', elf], 164142c5cccSJerome Forissier stdin = subprocess.PIPE, 165142c5cccSJerome Forissier stdout = subprocess.PIPE) 166142c5cccSJerome Forissier for line in iter(nm.stdout.readline, ''): 167142c5cccSJerome Forissier try: 168142c5cccSJerome Forissier addr, size, _, name = line.split() 169142c5cccSJerome Forissier except: 170142c5cccSJerome Forissier # Size is missing 171142c5cccSJerome Forissier addr, _, name = line.split() 172142c5cccSJerome Forissier size = '0' 173142c5cccSJerome Forissier iaddr = int(addr, 16) 174142c5cccSJerome Forissier isize = int(size, 16) 175142c5cccSJerome Forissier if iaddr == ireladdr: 176142c5cccSJerome Forissier ret = name 177142c5cccSJerome Forissier break 178142c5cccSJerome Forissier if iaddr < ireladdr and iaddr + isize >= ireladdr: 179142c5cccSJerome Forissier offs = ireladdr - iaddr 180142c5cccSJerome Forissier ret = name + '+' + str(offs) 181142c5cccSJerome Forissier break 182142c5cccSJerome Forissier if iaddr > ireladdr and prevsize == 0: 183142c5cccSJerome Forissier offs = iaddr + ireladdr 184142c5cccSJerome Forissier ret = prevname + '+' + str(offs) 185142c5cccSJerome Forissier break 186142c5cccSJerome Forissier prevsize = size 187142c5cccSJerome Forissier prevname = name 188142c5cccSJerome Forissier nm.terminate() 189142c5cccSJerome Forissier return ret 190142c5cccSJerome Forissier 191142c5cccSJerome Forissier def section_plus_offset(self, addr): 192142c5cccSJerome Forissier ret = '' 193142c5cccSJerome Forissier reladdr = self.subtract_load_addr(addr) 194142c5cccSJerome Forissier elf = self.get_elf(self._bin) 195142c5cccSJerome Forissier cmd = self.arch_prefix('objdump') 196142c5cccSJerome Forissier if not reladdr or not elf or not cmd: 197142c5cccSJerome Forissier return '' 19830999126SJerome Forissier iaddr = int(reladdr, 16) 199142c5cccSJerome Forissier objdump = subprocess.Popen([cmd, '--section-headers', elf], 200142c5cccSJerome Forissier stdin = subprocess.PIPE, 201142c5cccSJerome Forissier stdout = subprocess.PIPE) 202142c5cccSJerome Forissier for line in iter(objdump.stdout.readline, ''): 203142c5cccSJerome Forissier try: 204142c5cccSJerome Forissier idx, name, size, vma, lma, offs, algn = line.split() 205142c5cccSJerome Forissier except: 206142c5cccSJerome Forissier continue; 207142c5cccSJerome Forissier ivma = int(vma, 16) 208142c5cccSJerome Forissier isize = int(size, 16) 209142c5cccSJerome Forissier if ivma == iaddr: 210142c5cccSJerome Forissier ret = name 211142c5cccSJerome Forissier break 212142c5cccSJerome Forissier if ivma < iaddr and ivma + isize >= iaddr: 213142c5cccSJerome Forissier offs = iaddr - ivma 214142c5cccSJerome Forissier ret = name + '+' + str(offs) 215142c5cccSJerome Forissier break 216142c5cccSJerome Forissier objdump.terminate() 217142c5cccSJerome Forissier return ret 218142c5cccSJerome Forissier 219142c5cccSJerome Forissier def process_abort(self, line): 220142c5cccSJerome Forissier ret = '' 221142c5cccSJerome Forissier match = re.search(ABORT_ADDR_RE, line) 222142c5cccSJerome Forissier addr = match.group('addr') 223142c5cccSJerome Forissier pre = match.start('addr') 224142c5cccSJerome Forissier post = match.end('addr') 225142c5cccSJerome Forissier sym = self.symbol_plus_offset(addr) 226142c5cccSJerome Forissier sec = self.section_plus_offset(addr) 227142c5cccSJerome Forissier if sym or sec: 228142c5cccSJerome Forissier ret += line[:pre] 229142c5cccSJerome Forissier ret += addr 230142c5cccSJerome Forissier if sym: 231142c5cccSJerome Forissier ret += ' ' + sym 232142c5cccSJerome Forissier if sec: 233142c5cccSJerome Forissier ret += ' ' + sec 234142c5cccSJerome Forissier ret += line[post:] 235142c5cccSJerome Forissier return ret 236142c5cccSJerome Forissier 23730999126SJerome Forissier # Return all ELF sections with the ALLOC flag 23830999126SJerome Forissier def read_sections(self): 23930999126SJerome Forissier if self._sections: 24030999126SJerome Forissier return 24130999126SJerome Forissier elf = self.get_elf(self._bin) 24230999126SJerome Forissier cmd = self.arch_prefix('objdump') 24330999126SJerome Forissier if not elf or not cmd: 24430999126SJerome Forissier return 24530999126SJerome Forissier objdump = subprocess.Popen([cmd, '--section-headers', elf], 24630999126SJerome Forissier stdin = subprocess.PIPE, 24730999126SJerome Forissier stdout = subprocess.PIPE) 24830999126SJerome Forissier for line in iter(objdump.stdout.readline, ''): 24930999126SJerome Forissier try: 25030999126SJerome Forissier _, name, size, vma, _, _, _ = line.split() 25130999126SJerome Forissier except: 25230999126SJerome Forissier if 'ALLOC' in line: 25330999126SJerome Forissier self._sections.append([name, int(vma, 16), int(size, 16)]) 25430999126SJerome Forissier 25530999126SJerome Forissier def overlaps(self, section, addr, size): 25630999126SJerome Forissier sec_addr = section[1] 25730999126SJerome Forissier sec_size = section[2] 25830999126SJerome Forissier if not size or not sec_size: 25930999126SJerome Forissier return False 26030999126SJerome Forissier return (addr <= (sec_addr + sec_size - 1)) and ((addr + size - 1) >= sec_addr) 26130999126SJerome Forissier 26230999126SJerome Forissier def sections_in_region(self, addr, size): 26330999126SJerome Forissier ret = '' 26430999126SJerome Forissier addr = self.subtract_load_addr(addr) 26530999126SJerome Forissier if not addr: 26630999126SJerome Forissier return '' 26730999126SJerome Forissier iaddr = int(addr, 16) 26830999126SJerome Forissier isize = int(size, 16) 26930999126SJerome Forissier self.read_sections() 27030999126SJerome Forissier for s in self._sections: 27130999126SJerome Forissier if self.overlaps(s, iaddr, isize): 27230999126SJerome Forissier ret += ' ' + s[0] 27330999126SJerome Forissier return ret 27430999126SJerome Forissier 275733a15f2SJerome Forissier def reset(self): 276733a15f2SJerome Forissier self._call_stack_found = False 277733a15f2SJerome Forissier self._load_addr = '0' 278733a15f2SJerome Forissier if self._addr2line: 279733a15f2SJerome Forissier self._addr2line.terminate() 280733a15f2SJerome Forissier self._addr2line = None 281d720431cSJerome Forissier self._arch = None 282142c5cccSJerome Forissier self._saved_abort_line = '' 28330999126SJerome Forissier self._sections = [] 284*27b83ad2SJerome Forissier self._bin = "tee.elf" 285733a15f2SJerome Forissier 286733a15f2SJerome Forissier def write(self, line): 287733a15f2SJerome Forissier if self._call_stack_found: 288733a15f2SJerome Forissier match = re.search(STACK_ADDR_RE, line) 289733a15f2SJerome Forissier if match: 290733a15f2SJerome Forissier addr = match.group('addr') 291733a15f2SJerome Forissier pre = match.start('addr') 292733a15f2SJerome Forissier post = match.end('addr') 293733a15f2SJerome Forissier self._out.write(line[:pre]) 294733a15f2SJerome Forissier self._out.write(addr) 295733a15f2SJerome Forissier res = self.resolve(addr) 296733a15f2SJerome Forissier if self._strip_path: 297733a15f2SJerome Forissier res = re.sub(re.escape(self._strip_path) + '/*', '', 298733a15f2SJerome Forissier res) 299733a15f2SJerome Forissier self._out.write(' ' + res) 300733a15f2SJerome Forissier self._out.write(line[post:]) 301733a15f2SJerome Forissier return 302733a15f2SJerome Forissier else: 303733a15f2SJerome Forissier self.reset() 30430999126SJerome Forissier match = re.search(REGION_RE, line) 30530999126SJerome Forissier if match: 30630999126SJerome Forissier addr = match.group('addr') 30730999126SJerome Forissier size = match.group('size') 30830999126SJerome Forissier self._out.write(line.strip() + 30930999126SJerome Forissier self.sections_in_region(addr, size) + '\n'); 31030999126SJerome Forissier return 311733a15f2SJerome Forissier match = re.search(CALL_STACK_RE, line) 312733a15f2SJerome Forissier if match: 313733a15f2SJerome Forissier self._call_stack_found = True 314142c5cccSJerome Forissier # Here is a good place to resolve the abort address because we 315142c5cccSJerome Forissier # have all the information we need 316142c5cccSJerome Forissier if self._saved_abort_line: 317142c5cccSJerome Forissier self._out.write(self.process_abort(self._saved_abort_line)) 318733a15f2SJerome Forissier match = re.search(TA_UUID_RE, line) 319733a15f2SJerome Forissier if match: 320733a15f2SJerome Forissier self._bin = match.group('uuid') 321733a15f2SJerome Forissier match = re.search(TA_INFO_RE, line) 322733a15f2SJerome Forissier if match: 323733a15f2SJerome Forissier self._load_addr = match.group('load_addr') 324142c5cccSJerome Forissier match = re.search(ABORT_ADDR_RE, line) 325142c5cccSJerome Forissier if match: 326*27b83ad2SJerome Forissier self.reset() 327142c5cccSJerome Forissier # At this point the arch and TA load address are unknown. 328142c5cccSJerome Forissier # Save the line so We can translate the abort address later. 329142c5cccSJerome Forissier self._saved_abort_line = line 330733a15f2SJerome Forissier self._out.write(line) 331733a15f2SJerome Forissier 332733a15f2SJerome Forissier def flush(self): 333733a15f2SJerome Forissier self._out.flush() 334733a15f2SJerome Forissier 335733a15f2SJerome Forissierdef main(): 336733a15f2SJerome Forissier args = get_args() 337733a15f2SJerome Forissier if args.dir: 338733a15f2SJerome Forissier # Flatten list in case -d is used several times *and* with multiple 339733a15f2SJerome Forissier # arguments 340733a15f2SJerome Forissier args.dirs = [item for sublist in args.dir for item in sublist] 341733a15f2SJerome Forissier else: 342733a15f2SJerome Forissier args.dirs = [] 343733a15f2SJerome Forissier symbolizer = Symbolizer(sys.stdout, args.dirs, args.strip_path) 344733a15f2SJerome Forissier 345733a15f2SJerome Forissier for line in sys.stdin: 346733a15f2SJerome Forissier symbolizer.write(line) 347733a15f2SJerome Forissier symbolizer.flush() 348733a15f2SJerome Forissier 349733a15f2SJerome Forissierif __name__ == "__main__": 350733a15f2SJerome Forissier main() 351