1733a15f2SJerome Forissier#!/usr/bin/env python 21bb92983SJerome Forissier# SPDX-License-Identifier: BSD-2-Clause 3733a15f2SJerome Forissier# 4733a15f2SJerome Forissier# Copyright (c) 2017, Linaro Limited 5733a15f2SJerome Forissier# 6733a15f2SJerome Forissier 7733a15f2SJerome Forissier 8733a15f2SJerome Forissierimport argparse 9733a15f2SJerome Forissierimport glob 10157e6213SJerome Forissierimport os 11733a15f2SJerome Forissierimport re 12733a15f2SJerome Forissierimport subprocess 13733a15f2SJerome Forissierimport sys 14733a15f2SJerome Forissier 15733a15f2SJerome ForissierTA_UUID_RE = re.compile(r'Status of TA (?P<uuid>[0-9a-f\-]+)') 16a2b984bdSJoakim BechTA_INFO_RE = re.compile(' arch: (?P<arch>\w+) ' 17733a15f2SJerome Forissier 'load address: (?P<load_addr>0x[0-9a-f]+)') 18733a15f2SJerome ForissierCALL_STACK_RE = re.compile('Call stack:') 19a2b984bdSJoakim Bech 20a2b984bdSJoakim Bech# This gets the address from lines looking like this: 21a2b984bdSJoakim Bech# E/TC:0 0x001044a8 22a2b984bdSJoakim BechSTACK_ADDR_RE = re.compile(r'[UEIDFM]/T[AC]:.*(?P<addr>0x[0-9a-f]+)') 23142c5cccSJerome ForissierABORT_ADDR_RE = re.compile('-abort at address (?P<addr>0x[0-9a-f]+)') 2430999126SJerome ForissierREGION_RE = re.compile('region [0-9]+: va (?P<addr>0x[0-9a-f]+) ' 2530999126SJerome Forissier 'pa 0x[0-9a-f]+ size (?P<size>0x[0-9a-f]+)') 26733a15f2SJerome Forissier 27733a15f2SJerome Forissierepilog = ''' 28733a15f2SJerome ForissierThis scripts reads an OP-TEE abort message from stdin and adds debug 29733a15f2SJerome Forissierinformation ('function at file:line') next to each address in the call stack. 30733a15f2SJerome ForissierIt uses the paths provided on the command line to locate the appropriate ELF 31733a15f2SJerome Forissierbinary (tee.elf or Trusted Application) and runs arm-linux-gnueabihf-addr2line 32733a15f2SJerome Forissieror aarch64-linux-gnu-addr2line to process the addresses. 33733a15f2SJerome Forissier 34733a15f2SJerome ForissierOP-TEE abort messages are sent to the secure console. They look like the 35733a15f2SJerome Forissierfollowing: 36733a15f2SJerome Forissier 37733a15f2SJerome Forissier ERROR: TEE-CORE: User TA data-abort at address 0xffffdecd (alignment fault) 38733a15f2SJerome Forissier ... 39733a15f2SJerome Forissier ERROR: TEE-CORE: Call stack: 40733a15f2SJerome Forissier ERROR: TEE-CORE: 0x4000549e 41733a15f2SJerome Forissier ERROR: TEE-CORE: 0x40001f4b 42733a15f2SJerome Forissier ERROR: TEE-CORE: 0x4000273f 43733a15f2SJerome Forissier ERROR: TEE-CORE: 0x40005da7 44733a15f2SJerome Forissier 45733a15f2SJerome ForissierInspired by a script of the same name by the Chromium project. 46733a15f2SJerome Forissier 47733a15f2SJerome ForissierSample usage: 48733a15f2SJerome Forissier 49733a15f2SJerome Forissier $ scripts/symbolize.py -d out/arm-plat-hikey/core -d ../optee_test/out/ta/* 50733a15f2SJerome Forissier <paste whole dump here> 51733a15f2SJerome Forissier ^D 52733a15f2SJerome Forissier''' 53733a15f2SJerome Forissier 54733a15f2SJerome Forissierdef get_args(): 55733a15f2SJerome Forissier parser = argparse.ArgumentParser( 56733a15f2SJerome Forissier formatter_class=argparse.RawDescriptionHelpFormatter, 57733a15f2SJerome Forissier description='Symbolizes OP-TEE abort dumps', 58733a15f2SJerome Forissier epilog=epilog) 59733a15f2SJerome Forissier parser.add_argument('-d', '--dir', action='append', nargs='+', 60733a15f2SJerome Forissier help='Search for ELF file in DIR. tee.elf is needed to decode ' 61733a15f2SJerome Forissier 'a TEE Core or pseudo-TA abort, while <TA_uuid>.elf is required ' 62157e6213SJerome Forissier 'if a user-mode TA has crashed. For convenience, ELF files ' 63157e6213SJerome Forissier 'may also be given.') 64*5f7df507SJerome Forissier parser.add_argument('-s', '--strip_path', nargs='?', 65*5f7df507SJerome Forissier help='Strip STRIP_PATH from file paths (default: current directory, ' 66*5f7df507SJerome Forissier 'use -s with no argument to show full paths)', 67*5f7df507SJerome Forissier default=os.getcwd()) 68733a15f2SJerome Forissier 69733a15f2SJerome Forissier return parser.parse_args() 70733a15f2SJerome Forissier 71733a15f2SJerome Forissierclass Symbolizer(object): 72733a15f2SJerome Forissier def __init__(self, out, dirs, strip_path): 73733a15f2SJerome Forissier self._out = out 74733a15f2SJerome Forissier self._dirs = dirs 75733a15f2SJerome Forissier self._strip_path = strip_path 76733a15f2SJerome Forissier self._addr2line = None 77733a15f2SJerome Forissier self._bin = 'tee.elf' 78733a15f2SJerome Forissier self.reset() 79733a15f2SJerome Forissier 80733a15f2SJerome Forissier def get_elf(self, elf_or_uuid): 81733a15f2SJerome Forissier if not elf_or_uuid.endswith('.elf'): 82733a15f2SJerome Forissier elf_or_uuid += '.elf' 83733a15f2SJerome Forissier for d in self._dirs: 84157e6213SJerome Forissier if d.endswith(elf_or_uuid) and os.path.isfile(d): 85157e6213SJerome Forissier return d 86733a15f2SJerome Forissier elf = glob.glob(d + '/' + elf_or_uuid) 87733a15f2SJerome Forissier if elf: 88733a15f2SJerome Forissier return elf[0] 89733a15f2SJerome Forissier 90d720431cSJerome Forissier def set_arch(self): 91d720431cSJerome Forissier if self._arch: 92d720431cSJerome Forissier return 93d720431cSJerome Forissier if self._bin: 94d720431cSJerome Forissier p = subprocess.Popen([ 'file', self.get_elf(self._bin) ], 95d720431cSJerome Forissier stdout=subprocess.PIPE) 96d720431cSJerome Forissier output = p.stdout.readlines() 97d720431cSJerome Forissier p.terminate() 98d720431cSJerome Forissier if 'ARM aarch64,' in output[0]: 99d720431cSJerome Forissier self._arch = 'aarch64-linux-gnu-' 100d720431cSJerome Forissier elif 'ARM,' in output[0]: 101d720431cSJerome Forissier self._arch = 'arm-linux-gnueabihf-' 102d720431cSJerome Forissier 103142c5cccSJerome Forissier def arch_prefix(self, cmd): 104d720431cSJerome Forissier self.set_arch() 105d720431cSJerome Forissier return self._arch + cmd 106142c5cccSJerome Forissier 107733a15f2SJerome Forissier def spawn_addr2line(self): 108733a15f2SJerome Forissier if not self._addr2line: 109733a15f2SJerome Forissier elf = self.get_elf(self._bin) 110733a15f2SJerome Forissier if not elf: 111733a15f2SJerome Forissier return 112142c5cccSJerome Forissier cmd = self.arch_prefix('addr2line') 113142c5cccSJerome Forissier if not cmd: 114733a15f2SJerome Forissier return 115733a15f2SJerome Forissier self._addr2line = subprocess.Popen([cmd, '-f', '-p', '-e', elf], 116733a15f2SJerome Forissier stdin = subprocess.PIPE, 117733a15f2SJerome Forissier stdout = subprocess.PIPE) 118733a15f2SJerome Forissier 119142c5cccSJerome Forissier def subtract_load_addr(self, addr): 120733a15f2SJerome Forissier offs = self._load_addr 121fd5d0622SJerome Forissier if int(offs, 16) > int(addr, 16): 122142c5cccSJerome Forissier return '' 123142c5cccSJerome Forissier return '0x{:x}'.format(int(addr, 16) - int(offs, 16)) 124142c5cccSJerome Forissier 125142c5cccSJerome Forissier def resolve(self, addr): 126142c5cccSJerome Forissier reladdr = self.subtract_load_addr(addr) 127733a15f2SJerome Forissier self.spawn_addr2line() 128142c5cccSJerome Forissier if not reladdr or not self._addr2line: 129733a15f2SJerome Forissier return '???' 130733a15f2SJerome Forissier try: 131733a15f2SJerome Forissier print >> self._addr2line.stdin, reladdr 132733a15f2SJerome Forissier ret = self._addr2line.stdout.readline().rstrip('\n') 133733a15f2SJerome Forissier except IOError: 134733a15f2SJerome Forissier ret = '!!!' 135733a15f2SJerome Forissier return ret 136733a15f2SJerome Forissier 137142c5cccSJerome Forissier def symbol_plus_offset(self, addr): 138142c5cccSJerome Forissier ret = '' 139142c5cccSJerome Forissier prevsize = 0 140142c5cccSJerome Forissier reladdr = self.subtract_load_addr(addr) 141142c5cccSJerome Forissier elf = self.get_elf(self._bin) 142142c5cccSJerome Forissier cmd = self.arch_prefix('nm') 143142c5cccSJerome Forissier if not reladdr or not elf or not cmd: 144142c5cccSJerome Forissier return '' 14530999126SJerome Forissier ireladdr = int(reladdr, 16) 146142c5cccSJerome Forissier nm = subprocess.Popen([cmd, '--numeric-sort', '--print-size', elf], 147142c5cccSJerome Forissier stdin = subprocess.PIPE, 148142c5cccSJerome Forissier stdout = subprocess.PIPE) 149142c5cccSJerome Forissier for line in iter(nm.stdout.readline, ''): 150142c5cccSJerome Forissier try: 151142c5cccSJerome Forissier addr, size, _, name = line.split() 152142c5cccSJerome Forissier except: 153142c5cccSJerome Forissier # Size is missing 154142c5cccSJerome Forissier addr, _, name = line.split() 155142c5cccSJerome Forissier size = '0' 156142c5cccSJerome Forissier iaddr = int(addr, 16) 157142c5cccSJerome Forissier isize = int(size, 16) 158142c5cccSJerome Forissier if iaddr == ireladdr: 159142c5cccSJerome Forissier ret = name 160142c5cccSJerome Forissier break 161142c5cccSJerome Forissier if iaddr < ireladdr and iaddr + isize >= ireladdr: 162142c5cccSJerome Forissier offs = ireladdr - iaddr 163142c5cccSJerome Forissier ret = name + '+' + str(offs) 164142c5cccSJerome Forissier break 165142c5cccSJerome Forissier if iaddr > ireladdr and prevsize == 0: 166142c5cccSJerome Forissier offs = iaddr + ireladdr 167142c5cccSJerome Forissier ret = prevname + '+' + str(offs) 168142c5cccSJerome Forissier break 169142c5cccSJerome Forissier prevsize = size 170142c5cccSJerome Forissier prevname = name 171142c5cccSJerome Forissier nm.terminate() 172142c5cccSJerome Forissier return ret 173142c5cccSJerome Forissier 174142c5cccSJerome Forissier def section_plus_offset(self, addr): 175142c5cccSJerome Forissier ret = '' 176142c5cccSJerome Forissier reladdr = self.subtract_load_addr(addr) 177142c5cccSJerome Forissier elf = self.get_elf(self._bin) 178142c5cccSJerome Forissier cmd = self.arch_prefix('objdump') 179142c5cccSJerome Forissier if not reladdr or not elf or not cmd: 180142c5cccSJerome Forissier return '' 18130999126SJerome Forissier iaddr = int(reladdr, 16) 182142c5cccSJerome Forissier objdump = subprocess.Popen([cmd, '--section-headers', elf], 183142c5cccSJerome Forissier stdin = subprocess.PIPE, 184142c5cccSJerome Forissier stdout = subprocess.PIPE) 185142c5cccSJerome Forissier for line in iter(objdump.stdout.readline, ''): 186142c5cccSJerome Forissier try: 187142c5cccSJerome Forissier idx, name, size, vma, lma, offs, algn = line.split() 188142c5cccSJerome Forissier except: 189142c5cccSJerome Forissier continue; 190142c5cccSJerome Forissier ivma = int(vma, 16) 191142c5cccSJerome Forissier isize = int(size, 16) 192142c5cccSJerome Forissier if ivma == iaddr: 193142c5cccSJerome Forissier ret = name 194142c5cccSJerome Forissier break 195142c5cccSJerome Forissier if ivma < iaddr and ivma + isize >= iaddr: 196142c5cccSJerome Forissier offs = iaddr - ivma 197142c5cccSJerome Forissier ret = name + '+' + str(offs) 198142c5cccSJerome Forissier break 199142c5cccSJerome Forissier objdump.terminate() 200142c5cccSJerome Forissier return ret 201142c5cccSJerome Forissier 202142c5cccSJerome Forissier def process_abort(self, line): 203142c5cccSJerome Forissier ret = '' 204142c5cccSJerome Forissier match = re.search(ABORT_ADDR_RE, line) 205142c5cccSJerome Forissier addr = match.group('addr') 206142c5cccSJerome Forissier pre = match.start('addr') 207142c5cccSJerome Forissier post = match.end('addr') 208142c5cccSJerome Forissier sym = self.symbol_plus_offset(addr) 209142c5cccSJerome Forissier sec = self.section_plus_offset(addr) 210142c5cccSJerome Forissier if sym or sec: 211142c5cccSJerome Forissier ret += line[:pre] 212142c5cccSJerome Forissier ret += addr 213142c5cccSJerome Forissier if sym: 214142c5cccSJerome Forissier ret += ' ' + sym 215142c5cccSJerome Forissier if sec: 216142c5cccSJerome Forissier ret += ' ' + sec 217142c5cccSJerome Forissier ret += line[post:] 218142c5cccSJerome Forissier return ret 219142c5cccSJerome Forissier 22030999126SJerome Forissier # Return all ELF sections with the ALLOC flag 22130999126SJerome Forissier def read_sections(self): 22230999126SJerome Forissier if self._sections: 22330999126SJerome Forissier return 22430999126SJerome Forissier elf = self.get_elf(self._bin) 22530999126SJerome Forissier cmd = self.arch_prefix('objdump') 22630999126SJerome Forissier if not elf or not cmd: 22730999126SJerome Forissier return 22830999126SJerome Forissier objdump = subprocess.Popen([cmd, '--section-headers', elf], 22930999126SJerome Forissier stdin = subprocess.PIPE, 23030999126SJerome Forissier stdout = subprocess.PIPE) 23130999126SJerome Forissier for line in iter(objdump.stdout.readline, ''): 23230999126SJerome Forissier try: 23330999126SJerome Forissier _, name, size, vma, _, _, _ = line.split() 23430999126SJerome Forissier except: 23530999126SJerome Forissier if 'ALLOC' in line: 23630999126SJerome Forissier self._sections.append([name, int(vma, 16), int(size, 16)]) 23730999126SJerome Forissier 23830999126SJerome Forissier def overlaps(self, section, addr, size): 23930999126SJerome Forissier sec_addr = section[1] 24030999126SJerome Forissier sec_size = section[2] 24130999126SJerome Forissier if not size or not sec_size: 24230999126SJerome Forissier return False 24330999126SJerome Forissier return (addr <= (sec_addr + sec_size - 1)) and ((addr + size - 1) >= sec_addr) 24430999126SJerome Forissier 24530999126SJerome Forissier def sections_in_region(self, addr, size): 24630999126SJerome Forissier ret = '' 24730999126SJerome Forissier addr = self.subtract_load_addr(addr) 24830999126SJerome Forissier if not addr: 24930999126SJerome Forissier return '' 25030999126SJerome Forissier iaddr = int(addr, 16) 25130999126SJerome Forissier isize = int(size, 16) 25230999126SJerome Forissier self.read_sections() 25330999126SJerome Forissier for s in self._sections: 25430999126SJerome Forissier if self.overlaps(s, iaddr, isize): 25530999126SJerome Forissier ret += ' ' + s[0] 25630999126SJerome Forissier return ret 25730999126SJerome Forissier 258733a15f2SJerome Forissier def reset(self): 259733a15f2SJerome Forissier self._call_stack_found = False 260733a15f2SJerome Forissier self._load_addr = '0' 261733a15f2SJerome Forissier if self._addr2line: 262733a15f2SJerome Forissier self._addr2line.terminate() 263733a15f2SJerome Forissier self._addr2line = None 264d720431cSJerome Forissier self._arch = None 265142c5cccSJerome Forissier self._saved_abort_line = '' 26630999126SJerome Forissier self._sections = [] 26727b83ad2SJerome Forissier self._bin = "tee.elf" 268733a15f2SJerome Forissier 269733a15f2SJerome Forissier def write(self, line): 270733a15f2SJerome Forissier if self._call_stack_found: 271733a15f2SJerome Forissier match = re.search(STACK_ADDR_RE, line) 272733a15f2SJerome Forissier if match: 273733a15f2SJerome Forissier addr = match.group('addr') 274733a15f2SJerome Forissier pre = match.start('addr') 275733a15f2SJerome Forissier post = match.end('addr') 276733a15f2SJerome Forissier self._out.write(line[:pre]) 277733a15f2SJerome Forissier self._out.write(addr) 278733a15f2SJerome Forissier res = self.resolve(addr) 279733a15f2SJerome Forissier if self._strip_path: 280733a15f2SJerome Forissier res = re.sub(re.escape(self._strip_path) + '/*', '', 281733a15f2SJerome Forissier res) 282733a15f2SJerome Forissier self._out.write(' ' + res) 283733a15f2SJerome Forissier self._out.write(line[post:]) 284733a15f2SJerome Forissier return 285733a15f2SJerome Forissier else: 286733a15f2SJerome Forissier self.reset() 28730999126SJerome Forissier match = re.search(REGION_RE, line) 28830999126SJerome Forissier if match: 28930999126SJerome Forissier addr = match.group('addr') 29030999126SJerome Forissier size = match.group('size') 29130999126SJerome Forissier self._out.write(line.strip() + 29230999126SJerome Forissier self.sections_in_region(addr, size) + '\n'); 29330999126SJerome Forissier return 294733a15f2SJerome Forissier match = re.search(CALL_STACK_RE, line) 295733a15f2SJerome Forissier if match: 296733a15f2SJerome Forissier self._call_stack_found = True 297142c5cccSJerome Forissier # Here is a good place to resolve the abort address because we 298142c5cccSJerome Forissier # have all the information we need 299142c5cccSJerome Forissier if self._saved_abort_line: 300142c5cccSJerome Forissier self._out.write(self.process_abort(self._saved_abort_line)) 301733a15f2SJerome Forissier match = re.search(TA_UUID_RE, line) 302733a15f2SJerome Forissier if match: 303733a15f2SJerome Forissier self._bin = match.group('uuid') 304733a15f2SJerome Forissier match = re.search(TA_INFO_RE, line) 305733a15f2SJerome Forissier if match: 306733a15f2SJerome Forissier self._load_addr = match.group('load_addr') 307142c5cccSJerome Forissier match = re.search(ABORT_ADDR_RE, line) 308142c5cccSJerome Forissier if match: 30927b83ad2SJerome Forissier self.reset() 310142c5cccSJerome Forissier # At this point the arch and TA load address are unknown. 311142c5cccSJerome Forissier # Save the line so We can translate the abort address later. 312142c5cccSJerome Forissier self._saved_abort_line = line 313733a15f2SJerome Forissier self._out.write(line) 314733a15f2SJerome Forissier 315733a15f2SJerome Forissier def flush(self): 316733a15f2SJerome Forissier self._out.flush() 317733a15f2SJerome Forissier 318733a15f2SJerome Forissierdef main(): 319733a15f2SJerome Forissier args = get_args() 320733a15f2SJerome Forissier if args.dir: 321733a15f2SJerome Forissier # Flatten list in case -d is used several times *and* with multiple 322733a15f2SJerome Forissier # arguments 323733a15f2SJerome Forissier args.dirs = [item for sublist in args.dir for item in sublist] 324733a15f2SJerome Forissier else: 325733a15f2SJerome Forissier args.dirs = [] 326733a15f2SJerome Forissier symbolizer = Symbolizer(sys.stdout, args.dirs, args.strip_path) 327733a15f2SJerome Forissier 328733a15f2SJerome Forissier for line in sys.stdin: 329733a15f2SJerome Forissier symbolizer.write(line) 330733a15f2SJerome Forissier symbolizer.flush() 331733a15f2SJerome Forissier 332733a15f2SJerome Forissierif __name__ == "__main__": 333733a15f2SJerome Forissier main() 334