1bbaeed4dSRouven Czerwinski#!/usr/bin/env python3 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 9bbaeed4dSRouven Czerwinskiimport errno 10733a15f2SJerome Forissierimport glob 11157e6213SJerome Forissierimport os 12733a15f2SJerome Forissierimport re 13733a15f2SJerome Forissierimport subprocess 14733a15f2SJerome Forissierimport sys 156b4fc675SJerome Forissierimport termios 16733a15f2SJerome Forissier 17733a15f2SJerome ForissierCALL_STACK_RE = re.compile('Call stack:') 18d77929ecSSumit GargTEE_LOAD_ADDR_RE = re.compile(r'TEE load address @ (?P<load_addr>0x[0-9a-f]+)') 19a2b984bdSJoakim Bech# This gets the address from lines looking like this: 20a2b984bdSJoakim Bech# E/TC:0 0x001044a8 216e7c2e91SJerome ForissierSTACK_ADDR_RE = re.compile( 22531963a5SJens Wiklander r'[UEIDFM]/(TC|LD):(\?*|[0-9]*) [0-9]* +(?P<addr>0x[0-9a-f]+)') 236e7c2e91SJerome ForissierABORT_ADDR_RE = re.compile(r'-abort at address (?P<addr>0x[0-9a-f]+)') 24444c203eSJerome ForissierREGION_RE = re.compile(r'region +[0-9]+: va (?P<addr>0x[0-9a-f]+) ' 256e7c2e91SJerome Forissier r'pa 0x[0-9a-f]+ size (?P<size>0x[0-9a-f]+)' 26531963a5SJens Wiklander r'( flags .{4} (\[(?P<elf_idx>[0-9]+)\])?)?') 27ae252462SJerome ForissierELF_LIST_RE = re.compile(r'\[(?P<idx>[0-9]+)\] (?P<uuid>[0-9a-f\-]+)' 286e7c2e91SJerome Forissier r' @ (?P<load_addr>0x[0-9a-f\-]+)') 29c90b6663SSumit GargFUNC_GRAPH_RE = re.compile(r'Function graph') 30c90b6663SSumit GargGRAPH_ADDR_RE = re.compile(r'(?P<addr>0x[0-9a-f]+)') 31c90b6663SSumit GargGRAPH_RE = re.compile(r'}') 32733a15f2SJerome Forissier 33733a15f2SJerome Forissierepilog = ''' 340c5bedb5SJerome ForissierThis scripts reads an OP-TEE abort or panic message from stdin and adds debug 350c5bedb5SJerome Forissierinformation to the output, such as '<function> at <file>:<line>' next to each 360c5bedb5SJerome Forissieraddress in the call stack. Any message generated by OP-TEE and containing a 370c5bedb5SJerome Forissiercall stack can in principle be processed by this script. This currently 380c5bedb5SJerome Forissierincludes aborts and panics from the TEE core as well as from any TA. 390c5bedb5SJerome ForissierThe paths provided on the command line are used to locate the appropriate ELF 400c5bedb5SJerome Forissierbinary (tee.elf or Trusted Application). The GNU binutils (addr2line, objdump, 41f9089765SJerome Forissiernm) are used to extract the debug info. If the CROSS_COMPILE environment 42f9089765SJerome Forissiervariable is set, it is used as a prefix to the binutils tools. That is, the 43f9089765SJerome Forissierscript will invoke $(CROSS_COMPILE)addr2line etc. If it is not set however, 44f9089765SJerome Forissierthe prefix will be determined automatically for each ELF file based on its 45f9089765SJerome Forissierarchitecture (arm-linux-gnueabihf-, aarch64-linux-gnu-). The resulting command 46f9089765SJerome Forissieris then expected to be found in the user's PATH. 47733a15f2SJerome Forissier 480c5bedb5SJerome ForissierOP-TEE abort and panic messages are sent to the secure console. They look like 490c5bedb5SJerome Forissierthe following: 50733a15f2SJerome Forissier 510c5bedb5SJerome Forissier E/TC:0 User TA data-abort at address 0xffffdecd (alignment fault) 52733a15f2SJerome Forissier ... 530c5bedb5SJerome Forissier E/TC:0 Call stack: 540c5bedb5SJerome Forissier E/TC:0 0x4000549e 550c5bedb5SJerome Forissier E/TC:0 0x40001f4b 560c5bedb5SJerome Forissier E/TC:0 0x4000273f 570c5bedb5SJerome Forissier E/TC:0 0x40005da7 58733a15f2SJerome Forissier 59733a15f2SJerome ForissierInspired by a script of the same name by the Chromium project. 60733a15f2SJerome Forissier 61733a15f2SJerome ForissierSample usage: 62733a15f2SJerome Forissier 63733a15f2SJerome Forissier $ scripts/symbolize.py -d out/arm-plat-hikey/core -d ../optee_test/out/ta/* 64733a15f2SJerome Forissier <paste whole dump here> 65733a15f2SJerome Forissier ^D 66c90b6663SSumit Garg 67c90b6663SSumit GargAlso, this script reads function graph generated for OP-TEE user TA from 68c90b6663SSumit Garg/tmp/ftrace-<ta_uuid>.out file and resolves function addresses to corresponding 69c90b6663SSumit Gargsymbols. 70c90b6663SSumit Garg 71c90b6663SSumit GargSample usage: 72c90b6663SSumit Garg 73c90b6663SSumit Garg $ cat /tmp/ftrace-<ta_uuid>.out | scripts/symbolize.py -d <ta_uuid>.elf 74c90b6663SSumit Garg <paste function graph here> 75c90b6663SSumit Garg ^D 76733a15f2SJerome Forissier''' 77733a15f2SJerome Forissier 78ae252462SJerome Forissier 79733a15f2SJerome Forissierdef get_args(): 80733a15f2SJerome Forissier parser = argparse.ArgumentParser( 81733a15f2SJerome Forissier formatter_class=argparse.RawDescriptionHelpFormatter, 82c90b6663SSumit Garg description='Symbolizes OP-TEE abort dumps or function graphs', 83733a15f2SJerome Forissier epilog=epilog) 84733a15f2SJerome Forissier parser.add_argument('-d', '--dir', action='append', nargs='+', 851d8c2a48SJerome Forissier help='Search for ELF file in DIR. tee.elf is needed ' 861d8c2a48SJerome Forissier 'to decode a TEE Core or pseudo-TA abort, while ' 871d8c2a48SJerome Forissier '<TA_uuid>.elf is required if a user-mode TA has ' 881d8c2a48SJerome Forissier 'crashed. For convenience, ELF files may also be ' 891d8c2a48SJerome Forissier 'given.') 905f7df507SJerome Forissier parser.add_argument('-s', '--strip_path', nargs='?', 911d8c2a48SJerome Forissier help='Strip STRIP_PATH from file paths (default: ' 921d8c2a48SJerome Forissier 'current directory, use -s with no argument to show ' 931d8c2a48SJerome Forissier 'full paths)', default=os.getcwd()) 94733a15f2SJerome Forissier 95733a15f2SJerome Forissier return parser.parse_args() 96733a15f2SJerome Forissier 97ae252462SJerome Forissier 98733a15f2SJerome Forissierclass Symbolizer(object): 99733a15f2SJerome Forissier def __init__(self, out, dirs, strip_path): 100733a15f2SJerome Forissier self._out = out 101733a15f2SJerome Forissier self._dirs = dirs 102733a15f2SJerome Forissier self._strip_path = strip_path 103733a15f2SJerome Forissier self._addr2line = None 104733a15f2SJerome Forissier self.reset() 105733a15f2SJerome Forissier 1061cbf777bSJerome Forissier def my_Popen(self, cmd): 1071cbf777bSJerome Forissier try: 1081cbf777bSJerome Forissier return subprocess.Popen(cmd, stdin=subprocess.PIPE, 10917be223aSJerome Forissier stdout=subprocess.PIPE, 11017be223aSJerome Forissier universal_newlines=True, 111bbaeed4dSRouven Czerwinski bufsize=1) 1121cbf777bSJerome Forissier except OSError as e: 113bbaeed4dSRouven Czerwinski if e.errno == errno.ENOENT: 114bbaeed4dSRouven Czerwinski print("*** Error:{}: command not found".format(cmd[0]), 115bbaeed4dSRouven Czerwinski file=sys.stderr) 1161cbf777bSJerome Forissier sys.exit(1) 1171cbf777bSJerome Forissier 118733a15f2SJerome Forissier def get_elf(self, elf_or_uuid): 119733a15f2SJerome Forissier if not elf_or_uuid.endswith('.elf'): 120733a15f2SJerome Forissier elf_or_uuid += '.elf' 121733a15f2SJerome Forissier for d in self._dirs: 122157e6213SJerome Forissier if d.endswith(elf_or_uuid) and os.path.isfile(d): 123157e6213SJerome Forissier return d 124733a15f2SJerome Forissier elf = glob.glob(d + '/' + elf_or_uuid) 125733a15f2SJerome Forissier if elf: 126733a15f2SJerome Forissier return elf[0] 127733a15f2SJerome Forissier 12824778dedSJerome Forissier def set_arch(self, elf): 1296e7c2e91SJerome Forissier self._arch = os.getenv('CROSS_COMPILE') 1308a6d4a8bSEtienne Carriere if self._arch: 1318a6d4a8bSEtienne Carriere return 1329bb9f377SJerome Forissier p = subprocess.Popen(['file', '-L', elf], stdout=subprocess.PIPE) 133d720431cSJerome Forissier output = p.stdout.readlines() 134d720431cSJerome Forissier p.terminate() 135bbaeed4dSRouven Czerwinski if b'ARM aarch64,' in output[0]: 136d720431cSJerome Forissier self._arch = 'aarch64-linux-gnu-' 137bbaeed4dSRouven Czerwinski elif b'ARM,' in output[0]: 138d720431cSJerome Forissier self._arch = 'arm-linux-gnueabihf-' 139d720431cSJerome Forissier 14024778dedSJerome Forissier def arch_prefix(self, cmd, elf): 14124778dedSJerome Forissier self.set_arch(elf) 142ae252462SJerome Forissier if self._arch is None: 143ae252462SJerome Forissier return '' 144d720431cSJerome Forissier return self._arch + cmd 145142c5cccSJerome Forissier 146ae252462SJerome Forissier def spawn_addr2line(self, elf_name): 147ae252462SJerome Forissier if elf_name is None: 148ae252462SJerome Forissier return 149ae252462SJerome Forissier if self._addr2line_elf_name is elf_name: 150ae252462SJerome Forissier return 151ae252462SJerome Forissier if self._addr2line: 152ae252462SJerome Forissier self._addr2line.terminate 153ae252462SJerome Forissier self._addr2line = None 154ae252462SJerome Forissier elf = self.get_elf(elf_name) 155733a15f2SJerome Forissier if not elf: 156733a15f2SJerome Forissier return 15724778dedSJerome Forissier cmd = self.arch_prefix('addr2line', elf) 158142c5cccSJerome Forissier if not cmd: 159733a15f2SJerome Forissier return 160c0c57c8fSJerome Forissier self._addr2line = self.my_Popen([cmd, '-f', '-p', '-e', elf]) 161ba84a3f5SJerome Forissier self._addr2line_elf_name = elf_name 162ae252462SJerome Forissier 163ae252462SJerome Forissier # If addr falls into a region that maps a TA ELF file, return the load 164ae252462SJerome Forissier # address of that file. 165ae252462SJerome Forissier def elf_load_addr(self, addr): 166ae252462SJerome Forissier if self._regions: 167ae252462SJerome Forissier for r in self._regions: 168ae252462SJerome Forissier r_addr = int(r[0], 16) 169ae252462SJerome Forissier r_size = int(r[1], 16) 170ae252462SJerome Forissier i_addr = int(addr, 16) 171ae252462SJerome Forissier if (i_addr >= r_addr and i_addr < (r_addr + r_size)): 172ae252462SJerome Forissier # Found region 173ae252462SJerome Forissier elf_idx = r[2] 174ae252462SJerome Forissier if elf_idx is not None: 175ae252462SJerome Forissier return self._elfs[int(elf_idx)][1] 176099918f6SSumit Garg # In case address is not found in TA ELF file, fallback to tee.elf 177099918f6SSumit Garg # especially to symbolize mixed (user-space and kernel) addresses 178099918f6SSumit Garg # which is true when syscall ftrace is enabled along with TA 179099918f6SSumit Garg # ftrace. 18091068f86SJerome Forissier return self._tee_load_addr 181ae252462SJerome Forissier else: 182ae252462SJerome Forissier # tee.elf 183105e09c2SJerome Forissier return self._tee_load_addr 184ae252462SJerome Forissier 185ae252462SJerome Forissier def elf_for_addr(self, addr): 186ae252462SJerome Forissier l_addr = self.elf_load_addr(addr) 18791068f86SJerome Forissier if l_addr == self._tee_load_addr: 18891068f86SJerome Forissier return 'tee.elf' 189ae252462SJerome Forissier for k in self._elfs: 190ae252462SJerome Forissier e = self._elfs[k] 191ae252462SJerome Forissier if int(e[1], 16) == int(l_addr, 16): 192ae252462SJerome Forissier return e[0] 193ae252462SJerome Forissier return None 194733a15f2SJerome Forissier 195142c5cccSJerome Forissier def subtract_load_addr(self, addr): 196ae252462SJerome Forissier l_addr = self.elf_load_addr(addr) 197ae252462SJerome Forissier if l_addr is None: 198ae252462SJerome Forissier return None 199ae252462SJerome Forissier if int(l_addr, 16) > int(addr, 16): 200142c5cccSJerome Forissier return '' 201ae252462SJerome Forissier return '0x{:x}'.format(int(addr, 16) - int(l_addr, 16)) 202142c5cccSJerome Forissier 203142c5cccSJerome Forissier def resolve(self, addr): 204142c5cccSJerome Forissier reladdr = self.subtract_load_addr(addr) 205ae252462SJerome Forissier self.spawn_addr2line(self.elf_for_addr(addr)) 206142c5cccSJerome Forissier if not reladdr or not self._addr2line: 207733a15f2SJerome Forissier return '???' 208c0c57c8fSJerome Forissier if self.elf_for_addr(addr) == 'tee.elf': 209c0c57c8fSJerome Forissier reladdr = '0x{:x}'.format(int(reladdr, 16) + 210c0c57c8fSJerome Forissier int(self.first_vma('tee.elf'), 16)) 211733a15f2SJerome Forissier try: 212bbaeed4dSRouven Czerwinski print(reladdr, file=self._addr2line.stdin) 213733a15f2SJerome Forissier ret = self._addr2line.stdout.readline().rstrip('\n') 214733a15f2SJerome Forissier except IOError: 215733a15f2SJerome Forissier ret = '!!!' 216733a15f2SJerome Forissier return ret 217733a15f2SJerome Forissier 218*2a0d456fSJerome Forissier # Armv8.5 with Memory Tagging Extension (MTE) 219*2a0d456fSJerome Forissier def strip_armv85_mte_tag(self, addr): 220*2a0d456fSJerome Forissier i_addr = int(addr, 16) 221*2a0d456fSJerome Forissier i_addr &= ~(0xf << 56) 222*2a0d456fSJerome Forissier return '0x{:x}'.format(i_addr) 223*2a0d456fSJerome Forissier 224142c5cccSJerome Forissier def symbol_plus_offset(self, addr): 225142c5cccSJerome Forissier ret = '' 226142c5cccSJerome Forissier prevsize = 0 227*2a0d456fSJerome Forissier addr = self.strip_armv85_mte_tag(addr) 228142c5cccSJerome Forissier reladdr = self.subtract_load_addr(addr) 229ae252462SJerome Forissier elf_name = self.elf_for_addr(addr) 230ae252462SJerome Forissier if elf_name is None: 231ae252462SJerome Forissier return '' 232ae252462SJerome Forissier elf = self.get_elf(elf_name) 23324778dedSJerome Forissier cmd = self.arch_prefix('nm', elf) 234142c5cccSJerome Forissier if not reladdr or not elf or not cmd: 235142c5cccSJerome Forissier return '' 23630999126SJerome Forissier ireladdr = int(reladdr, 16) 2371cbf777bSJerome Forissier nm = self.my_Popen([cmd, '--numeric-sort', '--print-size', elf]) 238142c5cccSJerome Forissier for line in iter(nm.stdout.readline, ''): 239142c5cccSJerome Forissier try: 240142c5cccSJerome Forissier addr, size, _, name = line.split() 2411d8c2a48SJerome Forissier except ValueError: 242142c5cccSJerome Forissier # Size is missing 243b4815427SJerome Forissier try: 244142c5cccSJerome Forissier addr, _, name = line.split() 245142c5cccSJerome Forissier size = '0' 2461d8c2a48SJerome Forissier except ValueError: 247b4815427SJerome Forissier # E.g., undefined (external) symbols (line = "U symbol") 248b4815427SJerome Forissier continue 249142c5cccSJerome Forissier iaddr = int(addr, 16) 250142c5cccSJerome Forissier isize = int(size, 16) 251142c5cccSJerome Forissier if iaddr == ireladdr: 252142c5cccSJerome Forissier ret = name 253142c5cccSJerome Forissier break 254142c5cccSJerome Forissier if iaddr < ireladdr and iaddr + isize >= ireladdr: 255142c5cccSJerome Forissier offs = ireladdr - iaddr 256142c5cccSJerome Forissier ret = name + '+' + str(offs) 257142c5cccSJerome Forissier break 258142c5cccSJerome Forissier if iaddr > ireladdr and prevsize == 0: 259142c5cccSJerome Forissier offs = iaddr + ireladdr 260142c5cccSJerome Forissier ret = prevname + '+' + str(offs) 261142c5cccSJerome Forissier break 262142c5cccSJerome Forissier prevsize = size 263142c5cccSJerome Forissier prevname = name 264142c5cccSJerome Forissier nm.terminate() 265142c5cccSJerome Forissier return ret 266142c5cccSJerome Forissier 267142c5cccSJerome Forissier def section_plus_offset(self, addr): 268142c5cccSJerome Forissier ret = '' 269142c5cccSJerome Forissier reladdr = self.subtract_load_addr(addr) 270ae252462SJerome Forissier elf_name = self.elf_for_addr(addr) 271ae252462SJerome Forissier if elf_name is None: 272ae252462SJerome Forissier return '' 273ae252462SJerome Forissier elf = self.get_elf(elf_name) 27424778dedSJerome Forissier cmd = self.arch_prefix('objdump', elf) 275142c5cccSJerome Forissier if not reladdr or not elf or not cmd: 276142c5cccSJerome Forissier return '' 27730999126SJerome Forissier iaddr = int(reladdr, 16) 2781cbf777bSJerome Forissier objdump = self.my_Popen([cmd, '--section-headers', elf]) 279142c5cccSJerome Forissier for line in iter(objdump.stdout.readline, ''): 280142c5cccSJerome Forissier try: 281142c5cccSJerome Forissier idx, name, size, vma, lma, offs, algn = line.split() 2821d8c2a48SJerome Forissier except ValueError: 283ae252462SJerome Forissier continue 284142c5cccSJerome Forissier ivma = int(vma, 16) 285142c5cccSJerome Forissier isize = int(size, 16) 286142c5cccSJerome Forissier if ivma == iaddr: 287142c5cccSJerome Forissier ret = name 288142c5cccSJerome Forissier break 289142c5cccSJerome Forissier if ivma < iaddr and ivma + isize >= iaddr: 290142c5cccSJerome Forissier offs = iaddr - ivma 291142c5cccSJerome Forissier ret = name + '+' + str(offs) 292142c5cccSJerome Forissier break 293142c5cccSJerome Forissier objdump.terminate() 294142c5cccSJerome Forissier return ret 295142c5cccSJerome Forissier 296142c5cccSJerome Forissier def process_abort(self, line): 297142c5cccSJerome Forissier ret = '' 298142c5cccSJerome Forissier match = re.search(ABORT_ADDR_RE, line) 299142c5cccSJerome Forissier addr = match.group('addr') 300142c5cccSJerome Forissier pre = match.start('addr') 301142c5cccSJerome Forissier post = match.end('addr') 302142c5cccSJerome Forissier sym = self.symbol_plus_offset(addr) 303142c5cccSJerome Forissier sec = self.section_plus_offset(addr) 304142c5cccSJerome Forissier if sym or sec: 305142c5cccSJerome Forissier ret += line[:pre] 306142c5cccSJerome Forissier ret += addr 307142c5cccSJerome Forissier if sym: 308142c5cccSJerome Forissier ret += ' ' + sym 309142c5cccSJerome Forissier if sec: 310142c5cccSJerome Forissier ret += ' ' + sec 311142c5cccSJerome Forissier ret += line[post:] 312142c5cccSJerome Forissier return ret 313142c5cccSJerome Forissier 31430999126SJerome Forissier # Return all ELF sections with the ALLOC flag 315ae252462SJerome Forissier def read_sections(self, elf_name): 316ae252462SJerome Forissier if elf_name is None: 31730999126SJerome Forissier return 318ae252462SJerome Forissier if elf_name in self._sections: 319ae252462SJerome Forissier return 320ae252462SJerome Forissier elf = self.get_elf(elf_name) 321d7c22aceSJerome Forissier if not elf: 322d7c22aceSJerome Forissier return 32324778dedSJerome Forissier cmd = self.arch_prefix('objdump', elf) 32430999126SJerome Forissier if not elf or not cmd: 32530999126SJerome Forissier return 326ae252462SJerome Forissier self._sections[elf_name] = [] 3271cbf777bSJerome Forissier objdump = self.my_Popen([cmd, '--section-headers', elf]) 32830999126SJerome Forissier for line in iter(objdump.stdout.readline, ''): 32930999126SJerome Forissier try: 33030999126SJerome Forissier _, name, size, vma, _, _, _ = line.split() 3311d8c2a48SJerome Forissier except ValueError: 33230999126SJerome Forissier if 'ALLOC' in line: 333ae252462SJerome Forissier self._sections[elf_name].append([name, int(vma, 16), 334ae252462SJerome Forissier int(size, 16)]) 33530999126SJerome Forissier 336c0c57c8fSJerome Forissier def first_vma(self, elf_name): 337c0c57c8fSJerome Forissier self.read_sections(elf_name) 338c0c57c8fSJerome Forissier return '0x{:x}'.format(self._sections[elf_name][0][1]) 339c0c57c8fSJerome Forissier 34030999126SJerome Forissier def overlaps(self, section, addr, size): 34130999126SJerome Forissier sec_addr = section[1] 34230999126SJerome Forissier sec_size = section[2] 34330999126SJerome Forissier if not size or not sec_size: 34430999126SJerome Forissier return False 345ae252462SJerome Forissier return ((addr <= (sec_addr + sec_size - 1)) and 346ae252462SJerome Forissier ((addr + size - 1) >= sec_addr)) 34730999126SJerome Forissier 348ae252462SJerome Forissier def sections_in_region(self, addr, size, elf_idx): 34930999126SJerome Forissier ret = '' 35030999126SJerome Forissier addr = self.subtract_load_addr(addr) 35130999126SJerome Forissier if not addr: 35230999126SJerome Forissier return '' 35330999126SJerome Forissier iaddr = int(addr, 16) 35430999126SJerome Forissier isize = int(size, 16) 355ae252462SJerome Forissier elf = self._elfs[int(elf_idx)][0] 356ae252462SJerome Forissier if elf is None: 357ae252462SJerome Forissier return '' 358ae252462SJerome Forissier self.read_sections(elf) 359ae252462SJerome Forissier if elf not in self._sections: 360ae252462SJerome Forissier return '' 361ae252462SJerome Forissier for s in self._sections[elf]: 36230999126SJerome Forissier if self.overlaps(s, iaddr, isize): 36330999126SJerome Forissier ret += ' ' + s[0] 36430999126SJerome Forissier return ret 36530999126SJerome Forissier 366733a15f2SJerome Forissier def reset(self): 367733a15f2SJerome Forissier self._call_stack_found = False 368733a15f2SJerome Forissier if self._addr2line: 369733a15f2SJerome Forissier self._addr2line.terminate() 370733a15f2SJerome Forissier self._addr2line = None 371ae252462SJerome Forissier self._addr2line_elf_name = None 372d720431cSJerome Forissier self._arch = None 373142c5cccSJerome Forissier self._saved_abort_line = '' 374ae252462SJerome Forissier self._sections = {} # {elf_name: [[name, addr, size], ...], ...} 375ae252462SJerome Forissier self._regions = [] # [[addr, size, elf_idx, saved line], ...] 376ae252462SJerome Forissier self._elfs = {0: ["tee.elf", 0]} # {idx: [uuid, load_addr], ...} 37791068f86SJerome Forissier self._tee_load_addr = '0x0' 378c90b6663SSumit Garg self._func_graph_found = False 379c90b6663SSumit Garg self._func_graph_skip_line = True 380733a15f2SJerome Forissier 381095567e5SJerome Forissier def pretty_print_path(self, path): 382095567e5SJerome Forissier if self._strip_path: 383095567e5SJerome Forissier return re.sub(re.escape(self._strip_path) + '/*', '', path) 384095567e5SJerome Forissier return path 385095567e5SJerome Forissier 386733a15f2SJerome Forissier def write(self, line): 387733a15f2SJerome Forissier if self._call_stack_found: 388733a15f2SJerome Forissier match = re.search(STACK_ADDR_RE, line) 389733a15f2SJerome Forissier if match: 390733a15f2SJerome Forissier addr = match.group('addr') 391733a15f2SJerome Forissier pre = match.start('addr') 392733a15f2SJerome Forissier post = match.end('addr') 393733a15f2SJerome Forissier self._out.write(line[:pre]) 394733a15f2SJerome Forissier self._out.write(addr) 3955500d703SJerome Forissier # The call stack contains return addresses (LR/ELR values). 3965500d703SJerome Forissier # Heuristic: subtract 2 to obtain the call site of the function 3975500d703SJerome Forissier # or the location of the exception. This value works for A64, 3985500d703SJerome Forissier # A32 as well as Thumb. 3995500d703SJerome Forissier pc = 0 4005500d703SJerome Forissier lr = int(addr, 16) 4015500d703SJerome Forissier if lr: 4025500d703SJerome Forissier pc = lr - 2 4035500d703SJerome Forissier res = self.resolve('0x{:x}'.format(pc)) 404095567e5SJerome Forissier res = self.pretty_print_path(res) 405733a15f2SJerome Forissier self._out.write(' ' + res) 406733a15f2SJerome Forissier self._out.write(line[post:]) 407733a15f2SJerome Forissier return 408733a15f2SJerome Forissier else: 409733a15f2SJerome Forissier self.reset() 410c90b6663SSumit Garg if self._func_graph_found: 411c90b6663SSumit Garg match = re.search(GRAPH_ADDR_RE, line) 412c90b6663SSumit Garg match_re = re.search(GRAPH_RE, line) 413c90b6663SSumit Garg if match: 414c90b6663SSumit Garg addr = match.group('addr') 415c90b6663SSumit Garg pre = match.start('addr') 416c90b6663SSumit Garg post = match.end('addr') 417c90b6663SSumit Garg self._out.write(line[:pre]) 418c90b6663SSumit Garg res = self.resolve(addr) 419c90b6663SSumit Garg res_arr = re.split(' ', res) 420c90b6663SSumit Garg self._out.write(res_arr[0]) 421c90b6663SSumit Garg self._out.write(line[post:]) 422c90b6663SSumit Garg self._func_graph_skip_line = False 423c90b6663SSumit Garg return 424c90b6663SSumit Garg elif match_re: 425c90b6663SSumit Garg self._out.write(line) 426c90b6663SSumit Garg return 427c90b6663SSumit Garg elif self._func_graph_skip_line: 428c90b6663SSumit Garg return 429c90b6663SSumit Garg else: 430c90b6663SSumit Garg self.reset() 43130999126SJerome Forissier match = re.search(REGION_RE, line) 43230999126SJerome Forissier if match: 433ae252462SJerome Forissier # Region table: save info for later processing once 434ae252462SJerome Forissier # we know which UUID corresponds to which ELF index 43530999126SJerome Forissier addr = match.group('addr') 43630999126SJerome Forissier size = match.group('size') 437ae252462SJerome Forissier elf_idx = match.group('elf_idx') 438ae252462SJerome Forissier self._regions.append([addr, size, elf_idx, line]) 439ae252462SJerome Forissier return 440ae252462SJerome Forissier match = re.search(ELF_LIST_RE, line) 441ae252462SJerome Forissier if match: 442ae252462SJerome Forissier # ELF list: save info for later. Region table and ELF list 443ae252462SJerome Forissier # will be displayed when the call stack is reached 444ae252462SJerome Forissier i = int(match.group('idx')) 445ae252462SJerome Forissier self._elfs[i] = [match.group('uuid'), match.group('load_addr'), 446ae252462SJerome Forissier line] 44730999126SJerome Forissier return 448105e09c2SJerome Forissier match = re.search(TEE_LOAD_ADDR_RE, line) 449105e09c2SJerome Forissier if match: 450105e09c2SJerome Forissier self._tee_load_addr = match.group('load_addr') 451733a15f2SJerome Forissier match = re.search(CALL_STACK_RE, line) 452733a15f2SJerome Forissier if match: 453733a15f2SJerome Forissier self._call_stack_found = True 454ae252462SJerome Forissier if self._regions: 455ae252462SJerome Forissier for r in self._regions: 456ae252462SJerome Forissier r_addr = r[0] 457ae252462SJerome Forissier r_size = r[1] 458ae252462SJerome Forissier elf_idx = r[2] 459ae252462SJerome Forissier saved_line = r[3] 460ae252462SJerome Forissier if elf_idx is None: 461ae252462SJerome Forissier self._out.write(saved_line) 462ae252462SJerome Forissier else: 463ae252462SJerome Forissier self._out.write(saved_line.strip() + 464ae252462SJerome Forissier self.sections_in_region(r_addr, 465ae252462SJerome Forissier r_size, 466ae252462SJerome Forissier elf_idx) + 467ae252462SJerome Forissier '\n') 468ae252462SJerome Forissier if self._elfs: 469ae252462SJerome Forissier for k in self._elfs: 470ae252462SJerome Forissier e = self._elfs[k] 471ae252462SJerome Forissier if (len(e) >= 3): 4721e6f2ea0SJerome Forissier # TA executable or library 473095567e5SJerome Forissier self._out.write(e[2].strip()) 474095567e5SJerome Forissier elf = self.get_elf(e[0]) 475095567e5SJerome Forissier if elf: 476095567e5SJerome Forissier rpath = os.path.realpath(elf) 477095567e5SJerome Forissier path = self.pretty_print_path(rpath) 478095567e5SJerome Forissier self._out.write(' (' + path + ')') 479095567e5SJerome Forissier self._out.write('\n') 480142c5cccSJerome Forissier # Here is a good place to resolve the abort address because we 481142c5cccSJerome Forissier # have all the information we need 482142c5cccSJerome Forissier if self._saved_abort_line: 483142c5cccSJerome Forissier self._out.write(self.process_abort(self._saved_abort_line)) 484c90b6663SSumit Garg match = re.search(FUNC_GRAPH_RE, line) 485c90b6663SSumit Garg if match: 486c90b6663SSumit Garg self._func_graph_found = True 487142c5cccSJerome Forissier match = re.search(ABORT_ADDR_RE, line) 488142c5cccSJerome Forissier if match: 48927b83ad2SJerome Forissier self.reset() 490142c5cccSJerome Forissier # At this point the arch and TA load address are unknown. 491142c5cccSJerome Forissier # Save the line so We can translate the abort address later. 492142c5cccSJerome Forissier self._saved_abort_line = line 493733a15f2SJerome Forissier self._out.write(line) 494733a15f2SJerome Forissier 495733a15f2SJerome Forissier def flush(self): 496733a15f2SJerome Forissier self._out.flush() 497733a15f2SJerome Forissier 498ae252462SJerome Forissier 499733a15f2SJerome Forissierdef main(): 500733a15f2SJerome Forissier args = get_args() 501733a15f2SJerome Forissier if args.dir: 502733a15f2SJerome Forissier # Flatten list in case -d is used several times *and* with multiple 503733a15f2SJerome Forissier # arguments 504733a15f2SJerome Forissier args.dirs = [item for sublist in args.dir for item in sublist] 505733a15f2SJerome Forissier else: 506733a15f2SJerome Forissier args.dirs = [] 507733a15f2SJerome Forissier symbolizer = Symbolizer(sys.stdout, args.dirs, args.strip_path) 508733a15f2SJerome Forissier 5096b4fc675SJerome Forissier fd = sys.stdin.fileno() 51020d152b8SJerome Forissier isatty = os.isatty(fd) 51120d152b8SJerome Forissier if isatty: 5126b4fc675SJerome Forissier old = termios.tcgetattr(fd) 5136b4fc675SJerome Forissier new = termios.tcgetattr(fd) 5146b4fc675SJerome Forissier new[3] = new[3] & ~termios.ECHO # lflags 5156b4fc675SJerome Forissier try: 51620d152b8SJerome Forissier if isatty: 5176b4fc675SJerome Forissier termios.tcsetattr(fd, termios.TCSADRAIN, new) 518733a15f2SJerome Forissier for line in sys.stdin: 519733a15f2SJerome Forissier symbolizer.write(line) 5206b4fc675SJerome Forissier finally: 521733a15f2SJerome Forissier symbolizer.flush() 52220d152b8SJerome Forissier if isatty: 5236b4fc675SJerome Forissier termios.tcsetattr(fd, termios.TCSADRAIN, old) 524733a15f2SJerome Forissier 5251d8c2a48SJerome Forissier 526733a15f2SJerome Forissierif __name__ == "__main__": 527733a15f2SJerome Forissier main() 528