blob: 998bada658ddf6484079110000c99c391d541d78 [file] [log] [blame]
Rouven Czerwinskibbaeed42019-08-07 20:07:00 +02001#!/usr/bin/env python3
Jerome Forissier1bb92982017-12-15 14:27:02 +01002# SPDX-License-Identifier: BSD-2-Clause
Jerome Forissier733a15f2017-05-19 17:40:17 +02003#
4# Copyright (c) 2017, Linaro Limited
Jerome Forissier733a15f2017-05-19 17:40:17 +02005#
Jerome Forissier733a15f2017-05-19 17:40:17 +02006
7
8import argparse
Rouven Czerwinskibbaeed42019-08-07 20:07:00 +02009import errno
Jerome Forissier733a15f2017-05-19 17:40:17 +020010import glob
Jerome Forissier157e6212017-08-24 15:49:16 +020011import os
Jerome Forissier733a15f2017-05-19 17:40:17 +020012import re
13import subprocess
14import sys
Jerome Forissier6b4fc672019-09-23 09:49:32 +020015import termios
Jerome Forissier733a15f2017-05-19 17:40:17 +020016
Jerome Forissier733a15f2017-05-19 17:40:17 +020017CALL_STACK_RE = re.compile('Call stack:')
Sumit Gargd77929e2019-11-27 21:01:27 +053018TEE_LOAD_ADDR_RE = re.compile(r'TEE load address @ (?P<load_addr>0x[0-9a-f]+)')
Joakim Becha2b984b2017-12-15 14:34:56 +010019# This gets the address from lines looking like this:
20# E/TC:0 0x001044a8
Jerome Forissier6e7c2e92018-11-14 11:02:49 +010021STACK_ADDR_RE = re.compile(
Jens Wiklander531963a2019-05-23 17:42:15 +020022 r'[UEIDFM]/(TC|LD):(\?*|[0-9]*) [0-9]* +(?P<addr>0x[0-9a-f]+)')
Jerome Forissier6e7c2e92018-11-14 11:02:49 +010023ABORT_ADDR_RE = re.compile(r'-abort at address (?P<addr>0x[0-9a-f]+)')
Jerome Forissier99e82b12022-05-05 10:19:22 +020024TA_PANIC_RE = re.compile(r'TA panicked with code (?P<code>0x[0-9a-f]+)')
Jerome Forissier444c2032019-03-13 18:00:56 +010025REGION_RE = re.compile(r'region +[0-9]+: va (?P<addr>0x[0-9a-f]+) '
Jerome Forissier6e7c2e92018-11-14 11:02:49 +010026 r'pa 0x[0-9a-f]+ size (?P<size>0x[0-9a-f]+)'
Jens Wiklander531963a2019-05-23 17:42:15 +020027 r'( flags .{4} (\[(?P<elf_idx>[0-9]+)\])?)?')
Jerome Forissierae252462018-05-25 15:07:28 +020028ELF_LIST_RE = re.compile(r'\[(?P<idx>[0-9]+)\] (?P<uuid>[0-9a-f\-]+)'
Jerome Forissier6e7c2e92018-11-14 11:02:49 +010029 r' @ (?P<load_addr>0x[0-9a-f\-]+)')
Sumit Gargc90b6662019-03-28 18:24:21 +053030FUNC_GRAPH_RE = re.compile(r'Function graph')
31GRAPH_ADDR_RE = re.compile(r'(?P<addr>0x[0-9a-f]+)')
32GRAPH_RE = re.compile(r'}')
Jerome Forissier733a15f2017-05-19 17:40:17 +020033
34epilog = '''
Jerome Forissier0c5bedb2018-02-15 17:20:36 +010035This scripts reads an OP-TEE abort or panic message from stdin and adds debug
36information to the output, such as '<function> at <file>:<line>' next to each
37address in the call stack. Any message generated by OP-TEE and containing a
38call stack can in principle be processed by this script. This currently
39includes aborts and panics from the TEE core as well as from any TA.
40The paths provided on the command line are used to locate the appropriate ELF
41binary (tee.elf or Trusted Application). The GNU binutils (addr2line, objdump,
Jerome Forissierf9089762018-10-02 10:12:32 +020042nm) are used to extract the debug info. If the CROSS_COMPILE environment
43variable is set, it is used as a prefix to the binutils tools. That is, the
44script will invoke $(CROSS_COMPILE)addr2line etc. If it is not set however,
45the prefix will be determined automatically for each ELF file based on its
46architecture (arm-linux-gnueabihf-, aarch64-linux-gnu-). The resulting command
47is then expected to be found in the user's PATH.
Jerome Forissier733a15f2017-05-19 17:40:17 +020048
Jerome Forissier0c5bedb2018-02-15 17:20:36 +010049OP-TEE abort and panic messages are sent to the secure console. They look like
50the following:
Jerome Forissier733a15f2017-05-19 17:40:17 +020051
Jerome Forissier0c5bedb2018-02-15 17:20:36 +010052 E/TC:0 User TA data-abort at address 0xffffdecd (alignment fault)
Jerome Forissier733a15f2017-05-19 17:40:17 +020053 ...
Jerome Forissier0c5bedb2018-02-15 17:20:36 +010054 E/TC:0 Call stack:
55 E/TC:0 0x4000549e
56 E/TC:0 0x40001f4b
57 E/TC:0 0x4000273f
58 E/TC:0 0x40005da7
Jerome Forissier733a15f2017-05-19 17:40:17 +020059
60Inspired by a script of the same name by the Chromium project.
61
62Sample usage:
63
64 $ scripts/symbolize.py -d out/arm-plat-hikey/core -d ../optee_test/out/ta/*
65 <paste whole dump here>
66 ^D
Sumit Gargc90b6662019-03-28 18:24:21 +053067
68Also, this script reads function graph generated for OP-TEE user TA from
69/tmp/ftrace-<ta_uuid>.out file and resolves function addresses to corresponding
70symbols.
71
72Sample usage:
73
74 $ cat /tmp/ftrace-<ta_uuid>.out | scripts/symbolize.py -d <ta_uuid>.elf
75 <paste function graph here>
76 ^D
Jerome Forissier733a15f2017-05-19 17:40:17 +020077'''
78
Jerome Forissier99e82b12022-05-05 10:19:22 +020079tee_result_names = {
80 '0xf0100001': 'TEE_ERROR_CORRUPT_OBJECT',
81 '0xf0100002': 'TEE_ERROR_CORRUPT_OBJECT_2',
82 '0xf0100003': 'TEE_ERROR_STORAGE_NOT_AVAILABLE',
83 '0xf0100004': 'TEE_ERROR_STORAGE_NOT_AVAILABLE_2',
84 '0xf0100006': 'TEE_ERROR_CIPHERTEXT_INVALID ',
85 '0xffff0000': 'TEE_ERROR_GENERIC',
86 '0xffff0001': 'TEE_ERROR_ACCESS_DENIED',
87 '0xffff0002': 'TEE_ERROR_CANCEL',
88 '0xffff0003': 'TEE_ERROR_ACCESS_CONFLICT',
89 '0xffff0004': 'TEE_ERROR_EXCESS_DATA',
90 '0xffff0005': 'TEE_ERROR_BAD_FORMAT',
91 '0xffff0006': 'TEE_ERROR_BAD_PARAMETERS',
92 '0xffff0007': 'TEE_ERROR_BAD_STATE',
93 '0xffff0008': 'TEE_ERROR_ITEM_NOT_FOUND',
94 '0xffff0009': 'TEE_ERROR_NOT_IMPLEMENTED',
95 '0xffff000a': 'TEE_ERROR_NOT_SUPPORTED',
96 '0xffff000b': 'TEE_ERROR_NO_DATA',
97 '0xffff000c': 'TEE_ERROR_OUT_OF_MEMORY',
98 '0xffff000d': 'TEE_ERROR_BUSY',
99 '0xffff000e': 'TEE_ERROR_COMMUNICATION',
100 '0xffff000f': 'TEE_ERROR_SECURITY',
101 '0xffff0010': 'TEE_ERROR_SHORT_BUFFER',
102 '0xffff0011': 'TEE_ERROR_EXTERNAL_CANCEL',
103 '0xffff300f': 'TEE_ERROR_OVERFLOW',
104 '0xffff3024': 'TEE_ERROR_TARGET_DEAD',
105 '0xffff3041': 'TEE_ERROR_STORAGE_NO_SPACE',
106 '0xffff3071': 'TEE_ERROR_MAC_INVALID',
107 '0xffff3072': 'TEE_ERROR_SIGNATURE_INVALID',
108 '0xffff5000': 'TEE_ERROR_TIME_NOT_SET',
109 '0xffff5001': 'TEE_ERROR_TIME_NEEDS_RESET',
110 }
111
Jerome Forissierae252462018-05-25 15:07:28 +0200112
Jerome Forissier733a15f2017-05-19 17:40:17 +0200113def get_args():
114 parser = argparse.ArgumentParser(
Jerome Forissier6e7c2e92018-11-14 11:02:49 +0100115 formatter_class=argparse.RawDescriptionHelpFormatter,
Sumit Gargc90b6662019-03-28 18:24:21 +0530116 description='Symbolizes OP-TEE abort dumps or function graphs',
Jerome Forissier6e7c2e92018-11-14 11:02:49 +0100117 epilog=epilog)
Jerome Forissier733a15f2017-05-19 17:40:17 +0200118 parser.add_argument('-d', '--dir', action='append', nargs='+',
Jerome Forissier1d8c2a42018-09-07 17:05:21 +0200119 help='Search for ELF file in DIR. tee.elf is needed '
120 'to decode a TEE Core or pseudo-TA abort, while '
121 '<TA_uuid>.elf is required if a user-mode TA has '
122 'crashed. For convenience, ELF files may also be '
123 'given.')
Jerome Forissier5f7df502018-02-15 16:57:55 +0100124 parser.add_argument('-s', '--strip_path', nargs='?',
Jerome Forissier1d8c2a42018-09-07 17:05:21 +0200125 help='Strip STRIP_PATH from file paths (default: '
126 'current directory, use -s with no argument to show '
127 'full paths)', default=os.getcwd())
Jerome Forissier733a15f2017-05-19 17:40:17 +0200128
129 return parser.parse_args()
130
Jerome Forissierae252462018-05-25 15:07:28 +0200131
Jerome Forissier733a15f2017-05-19 17:40:17 +0200132class Symbolizer(object):
133 def __init__(self, out, dirs, strip_path):
134 self._out = out
135 self._dirs = dirs
136 self._strip_path = strip_path
137 self._addr2line = None
Jerome Forissier733a15f2017-05-19 17:40:17 +0200138 self.reset()
139
Jerome Forissier1cbf7772018-09-07 17:08:23 +0200140 def my_Popen(self, cmd):
141 try:
142 return subprocess.Popen(cmd, stdin=subprocess.PIPE,
Jerome Forissier17be2232020-01-29 14:21:06 +0100143 stdout=subprocess.PIPE,
144 universal_newlines=True,
Rouven Czerwinskibbaeed42019-08-07 20:07:00 +0200145 bufsize=1)
Jerome Forissier1cbf7772018-09-07 17:08:23 +0200146 except OSError as e:
Rouven Czerwinskibbaeed42019-08-07 20:07:00 +0200147 if e.errno == errno.ENOENT:
148 print("*** Error:{}: command not found".format(cmd[0]),
149 file=sys.stderr)
Jerome Forissier1cbf7772018-09-07 17:08:23 +0200150 sys.exit(1)
151
Jerome Forissier733a15f2017-05-19 17:40:17 +0200152 def get_elf(self, elf_or_uuid):
153 if not elf_or_uuid.endswith('.elf'):
154 elf_or_uuid += '.elf'
155 for d in self._dirs:
Jerome Forissier157e6212017-08-24 15:49:16 +0200156 if d.endswith(elf_or_uuid) and os.path.isfile(d):
157 return d
Jerome Forissier733a15f2017-05-19 17:40:17 +0200158 elf = glob.glob(d + '/' + elf_or_uuid)
159 if elf:
160 return elf[0]
161
Jerome Forissier24778de2020-02-12 14:48:03 +0100162 def set_arch(self, elf):
Jerome Forissier6e7c2e92018-11-14 11:02:49 +0100163 self._arch = os.getenv('CROSS_COMPILE')
Etienne Carriere8a6d4a82018-10-01 09:58:31 +0200164 if self._arch:
165 return
Jerome Forissier9bb9f372020-02-18 18:12:31 +0100166 p = subprocess.Popen(['file', '-L', elf], stdout=subprocess.PIPE)
Jerome Forissierae252462018-05-25 15:07:28 +0200167 output = p.stdout.readlines()
168 p.terminate()
Rouven Czerwinskibbaeed42019-08-07 20:07:00 +0200169 if b'ARM aarch64,' in output[0]:
Jerome Forissierae252462018-05-25 15:07:28 +0200170 self._arch = 'aarch64-linux-gnu-'
Rouven Czerwinskibbaeed42019-08-07 20:07:00 +0200171 elif b'ARM,' in output[0]:
Jerome Forissierae252462018-05-25 15:07:28 +0200172 self._arch = 'arm-linux-gnueabihf-'
Jerome Forissierd7204312017-09-04 17:58:52 +0200173
Jerome Forissier24778de2020-02-12 14:48:03 +0100174 def arch_prefix(self, cmd, elf):
175 self.set_arch(elf)
Jerome Forissierae252462018-05-25 15:07:28 +0200176 if self._arch is None:
177 return ''
Jerome Forissierd7204312017-09-04 17:58:52 +0200178 return self._arch + cmd
Jerome Forissier142c5cc2017-08-24 15:07:17 +0200179
Jerome Forissierae252462018-05-25 15:07:28 +0200180 def spawn_addr2line(self, elf_name):
181 if elf_name is None:
182 return
183 if self._addr2line_elf_name is elf_name:
184 return
185 if self._addr2line:
186 self._addr2line.terminate
187 self._addr2line = None
188 elf = self.get_elf(elf_name)
189 if not elf:
190 return
Jerome Forissier24778de2020-02-12 14:48:03 +0100191 cmd = self.arch_prefix('addr2line', elf)
Jerome Forissierae252462018-05-25 15:07:28 +0200192 if not cmd:
193 return
Jerome Forissierc0c57c82020-01-08 14:57:32 +0100194 self._addr2line = self.my_Popen([cmd, '-f', '-p', '-e', elf])
Jerome Forissierba84a3f2020-02-12 14:44:03 +0100195 self._addr2line_elf_name = elf_name
Jerome Forissierae252462018-05-25 15:07:28 +0200196
197 # If addr falls into a region that maps a TA ELF file, return the load
198 # address of that file.
199 def elf_load_addr(self, addr):
200 if self._regions:
201 for r in self._regions:
202 r_addr = int(r[0], 16)
203 r_size = int(r[1], 16)
204 i_addr = int(addr, 16)
205 if (i_addr >= r_addr and i_addr < (r_addr + r_size)):
206 # Found region
207 elf_idx = r[2]
208 if elf_idx is not None:
209 return self._elfs[int(elf_idx)][1]
Sumit Garg099918f2019-09-05 13:23:01 +0530210 # In case address is not found in TA ELF file, fallback to tee.elf
211 # especially to symbolize mixed (user-space and kernel) addresses
212 # which is true when syscall ftrace is enabled along with TA
213 # ftrace.
Jerome Forissier91068f82019-11-26 10:51:45 +0100214 return self._tee_load_addr
Jerome Forissierae252462018-05-25 15:07:28 +0200215 else:
216 # tee.elf
Jerome Forissier105e09c2019-10-16 16:59:35 +0200217 return self._tee_load_addr
Jerome Forissierae252462018-05-25 15:07:28 +0200218
219 def elf_for_addr(self, addr):
220 l_addr = self.elf_load_addr(addr)
Jerome Forissier91068f82019-11-26 10:51:45 +0100221 if l_addr == self._tee_load_addr:
222 return 'tee.elf'
Jerome Forissierae252462018-05-25 15:07:28 +0200223 for k in self._elfs:
224 e = self._elfs[k]
225 if int(e[1], 16) == int(l_addr, 16):
226 return e[0]
227 return None
Jerome Forissier733a15f2017-05-19 17:40:17 +0200228
Jerome Forissier142c5cc2017-08-24 15:07:17 +0200229 def subtract_load_addr(self, addr):
Jerome Forissierae252462018-05-25 15:07:28 +0200230 l_addr = self.elf_load_addr(addr)
231 if l_addr is None:
232 return None
233 if int(l_addr, 16) > int(addr, 16):
Jerome Forissier142c5cc2017-08-24 15:07:17 +0200234 return ''
Jerome Forissierae252462018-05-25 15:07:28 +0200235 return '0x{:x}'.format(int(addr, 16) - int(l_addr, 16))
Jerome Forissier142c5cc2017-08-24 15:07:17 +0200236
237 def resolve(self, addr):
238 reladdr = self.subtract_load_addr(addr)
Jerome Forissierae252462018-05-25 15:07:28 +0200239 self.spawn_addr2line(self.elf_for_addr(addr))
Jerome Forissier142c5cc2017-08-24 15:07:17 +0200240 if not reladdr or not self._addr2line:
Jerome Forissier733a15f2017-05-19 17:40:17 +0200241 return '???'
Jerome Forissierc0c57c82020-01-08 14:57:32 +0100242 if self.elf_for_addr(addr) == 'tee.elf':
243 reladdr = '0x{:x}'.format(int(reladdr, 16) +
244 int(self.first_vma('tee.elf'), 16))
Jerome Forissier733a15f2017-05-19 17:40:17 +0200245 try:
Rouven Czerwinskibbaeed42019-08-07 20:07:00 +0200246 print(reladdr, file=self._addr2line.stdin)
Jerome Forissier733a15f2017-05-19 17:40:17 +0200247 ret = self._addr2line.stdout.readline().rstrip('\n')
248 except IOError:
249 ret = '!!!'
250 return ret
251
Jerome Forissier2a0d4562022-04-29 11:11:29 +0200252 # Armv8.5 with Memory Tagging Extension (MTE)
253 def strip_armv85_mte_tag(self, addr):
254 i_addr = int(addr, 16)
255 i_addr &= ~(0xf << 56)
256 return '0x{:x}'.format(i_addr)
257
Jerome Forissier142c5cc2017-08-24 15:07:17 +0200258 def symbol_plus_offset(self, addr):
259 ret = ''
260 prevsize = 0
Jerome Forissier2a0d4562022-04-29 11:11:29 +0200261 addr = self.strip_armv85_mte_tag(addr)
Jerome Forissier142c5cc2017-08-24 15:07:17 +0200262 reladdr = self.subtract_load_addr(addr)
Jerome Forissierae252462018-05-25 15:07:28 +0200263 elf_name = self.elf_for_addr(addr)
264 if elf_name is None:
265 return ''
266 elf = self.get_elf(elf_name)
Jerome Forissier24778de2020-02-12 14:48:03 +0100267 cmd = self.arch_prefix('nm', elf)
Jerome Forissier142c5cc2017-08-24 15:07:17 +0200268 if not reladdr or not elf or not cmd:
269 return ''
Jerome Forissier30999122017-08-25 18:42:58 +0200270 ireladdr = int(reladdr, 16)
Jerome Forissier1cbf7772018-09-07 17:08:23 +0200271 nm = self.my_Popen([cmd, '--numeric-sort', '--print-size', elf])
Jerome Forissier142c5cc2017-08-24 15:07:17 +0200272 for line in iter(nm.stdout.readline, ''):
273 try:
274 addr, size, _, name = line.split()
Jerome Forissier1d8c2a42018-09-07 17:05:21 +0200275 except ValueError:
Jerome Forissier142c5cc2017-08-24 15:07:17 +0200276 # Size is missing
Jerome Forissierb4815422018-06-20 09:43:33 +0200277 try:
278 addr, _, name = line.split()
279 size = '0'
Jerome Forissier1d8c2a42018-09-07 17:05:21 +0200280 except ValueError:
Jerome Forissierb4815422018-06-20 09:43:33 +0200281 # E.g., undefined (external) symbols (line = "U symbol")
282 continue
Jerome Forissier142c5cc2017-08-24 15:07:17 +0200283 iaddr = int(addr, 16)
284 isize = int(size, 16)
285 if iaddr == ireladdr:
286 ret = name
287 break
288 if iaddr < ireladdr and iaddr + isize >= ireladdr:
289 offs = ireladdr - iaddr
290 ret = name + '+' + str(offs)
291 break
292 if iaddr > ireladdr and prevsize == 0:
293 offs = iaddr + ireladdr
294 ret = prevname + '+' + str(offs)
295 break
296 prevsize = size
297 prevname = name
298 nm.terminate()
299 return ret
300
301 def section_plus_offset(self, addr):
302 ret = ''
303 reladdr = self.subtract_load_addr(addr)
Jerome Forissierae252462018-05-25 15:07:28 +0200304 elf_name = self.elf_for_addr(addr)
305 if elf_name is None:
306 return ''
307 elf = self.get_elf(elf_name)
Jerome Forissier24778de2020-02-12 14:48:03 +0100308 cmd = self.arch_prefix('objdump', elf)
Jerome Forissier142c5cc2017-08-24 15:07:17 +0200309 if not reladdr or not elf or not cmd:
310 return ''
Jerome Forissier30999122017-08-25 18:42:58 +0200311 iaddr = int(reladdr, 16)
Jerome Forissier1cbf7772018-09-07 17:08:23 +0200312 objdump = self.my_Popen([cmd, '--section-headers', elf])
Jerome Forissier142c5cc2017-08-24 15:07:17 +0200313 for line in iter(objdump.stdout.readline, ''):
314 try:
315 idx, name, size, vma, lma, offs, algn = line.split()
Jerome Forissier1d8c2a42018-09-07 17:05:21 +0200316 except ValueError:
Jerome Forissierae252462018-05-25 15:07:28 +0200317 continue
Jerome Forissier142c5cc2017-08-24 15:07:17 +0200318 ivma = int(vma, 16)
319 isize = int(size, 16)
320 if ivma == iaddr:
321 ret = name
322 break
323 if ivma < iaddr and ivma + isize >= iaddr:
324 offs = iaddr - ivma
325 ret = name + '+' + str(offs)
326 break
327 objdump.terminate()
328 return ret
329
330 def process_abort(self, line):
331 ret = ''
332 match = re.search(ABORT_ADDR_RE, line)
333 addr = match.group('addr')
334 pre = match.start('addr')
335 post = match.end('addr')
336 sym = self.symbol_plus_offset(addr)
337 sec = self.section_plus_offset(addr)
338 if sym or sec:
339 ret += line[:pre]
340 ret += addr
341 if sym:
342 ret += ' ' + sym
343 if sec:
344 ret += ' ' + sec
345 ret += line[post:]
346 return ret
347
Jerome Forissier30999122017-08-25 18:42:58 +0200348 # Return all ELF sections with the ALLOC flag
Jerome Forissierae252462018-05-25 15:07:28 +0200349 def read_sections(self, elf_name):
350 if elf_name is None:
Jerome Forissier30999122017-08-25 18:42:58 +0200351 return
Jerome Forissierae252462018-05-25 15:07:28 +0200352 if elf_name in self._sections:
353 return
354 elf = self.get_elf(elf_name)
Jerome Forissierd7c22ac2020-06-22 14:07:38 +0200355 if not elf:
356 return
Jerome Forissier24778de2020-02-12 14:48:03 +0100357 cmd = self.arch_prefix('objdump', elf)
Jerome Forissier30999122017-08-25 18:42:58 +0200358 if not elf or not cmd:
359 return
Jerome Forissierae252462018-05-25 15:07:28 +0200360 self._sections[elf_name] = []
Jerome Forissier1cbf7772018-09-07 17:08:23 +0200361 objdump = self.my_Popen([cmd, '--section-headers', elf])
Jerome Forissier30999122017-08-25 18:42:58 +0200362 for line in iter(objdump.stdout.readline, ''):
363 try:
364 _, name, size, vma, _, _, _ = line.split()
Jerome Forissier1d8c2a42018-09-07 17:05:21 +0200365 except ValueError:
Jerome Forissier30999122017-08-25 18:42:58 +0200366 if 'ALLOC' in line:
Jerome Forissierae252462018-05-25 15:07:28 +0200367 self._sections[elf_name].append([name, int(vma, 16),
368 int(size, 16)])
Jerome Forissier30999122017-08-25 18:42:58 +0200369
Jerome Forissierc0c57c82020-01-08 14:57:32 +0100370 def first_vma(self, elf_name):
371 self.read_sections(elf_name)
372 return '0x{:x}'.format(self._sections[elf_name][0][1])
373
Jerome Forissier30999122017-08-25 18:42:58 +0200374 def overlaps(self, section, addr, size):
375 sec_addr = section[1]
376 sec_size = section[2]
377 if not size or not sec_size:
378 return False
Jerome Forissierae252462018-05-25 15:07:28 +0200379 return ((addr <= (sec_addr + sec_size - 1)) and
380 ((addr + size - 1) >= sec_addr))
Jerome Forissier30999122017-08-25 18:42:58 +0200381
Jerome Forissierae252462018-05-25 15:07:28 +0200382 def sections_in_region(self, addr, size, elf_idx):
Jerome Forissier30999122017-08-25 18:42:58 +0200383 ret = ''
384 addr = self.subtract_load_addr(addr)
385 if not addr:
386 return ''
387 iaddr = int(addr, 16)
388 isize = int(size, 16)
Jerome Forissierae252462018-05-25 15:07:28 +0200389 elf = self._elfs[int(elf_idx)][0]
390 if elf is None:
391 return ''
392 self.read_sections(elf)
393 if elf not in self._sections:
394 return ''
395 for s in self._sections[elf]:
Jerome Forissier30999122017-08-25 18:42:58 +0200396 if self.overlaps(s, iaddr, isize):
397 ret += ' ' + s[0]
398 return ret
399
Jerome Forissier733a15f2017-05-19 17:40:17 +0200400 def reset(self):
401 self._call_stack_found = False
Jerome Forissier733a15f2017-05-19 17:40:17 +0200402 if self._addr2line:
403 self._addr2line.terminate()
404 self._addr2line = None
Jerome Forissierae252462018-05-25 15:07:28 +0200405 self._addr2line_elf_name = None
Jerome Forissierd7204312017-09-04 17:58:52 +0200406 self._arch = None
Jerome Forissier142c5cc2017-08-24 15:07:17 +0200407 self._saved_abort_line = ''
Jerome Forissierae252462018-05-25 15:07:28 +0200408 self._sections = {} # {elf_name: [[name, addr, size], ...], ...}
409 self._regions = [] # [[addr, size, elf_idx, saved line], ...]
410 self._elfs = {0: ["tee.elf", 0]} # {idx: [uuid, load_addr], ...}
Jerome Forissier91068f82019-11-26 10:51:45 +0100411 self._tee_load_addr = '0x0'
Sumit Gargc90b6662019-03-28 18:24:21 +0530412 self._func_graph_found = False
413 self._func_graph_skip_line = True
Jerome Forissier733a15f2017-05-19 17:40:17 +0200414
Jerome Forissier095567e2018-05-29 17:42:34 +0200415 def pretty_print_path(self, path):
416 if self._strip_path:
417 return re.sub(re.escape(self._strip_path) + '/*', '', path)
418 return path
419
Jerome Forissier733a15f2017-05-19 17:40:17 +0200420 def write(self, line):
Jerome Forissier6e7c2e92018-11-14 11:02:49 +0100421 if self._call_stack_found:
422 match = re.search(STACK_ADDR_RE, line)
Jerome Forissier30999122017-08-25 18:42:58 +0200423 if match:
424 addr = match.group('addr')
Jerome Forissier6e7c2e92018-11-14 11:02:49 +0100425 pre = match.start('addr')
426 post = match.end('addr')
427 self._out.write(line[:pre])
428 self._out.write(addr)
Jerome Forissier5500d702020-07-31 11:30:54 +0200429 # The call stack contains return addresses (LR/ELR values).
430 # Heuristic: subtract 2 to obtain the call site of the function
431 # or the location of the exception. This value works for A64,
432 # A32 as well as Thumb.
433 pc = 0
434 lr = int(addr, 16)
435 if lr:
436 pc = lr - 2
437 res = self.resolve('0x{:x}'.format(pc))
Jerome Forissier6e7c2e92018-11-14 11:02:49 +0100438 res = self.pretty_print_path(res)
439 self._out.write(' ' + res)
440 self._out.write(line[post:])
Jerome Forissierae252462018-05-25 15:07:28 +0200441 return
Jerome Forissier6e7c2e92018-11-14 11:02:49 +0100442 else:
Jerome Forissier27b83ad2017-10-06 13:37:35 +0200443 self.reset()
Sumit Gargc90b6662019-03-28 18:24:21 +0530444 if self._func_graph_found:
445 match = re.search(GRAPH_ADDR_RE, line)
446 match_re = re.search(GRAPH_RE, line)
447 if match:
448 addr = match.group('addr')
449 pre = match.start('addr')
450 post = match.end('addr')
451 self._out.write(line[:pre])
452 res = self.resolve(addr)
453 res_arr = re.split(' ', res)
454 self._out.write(res_arr[0])
455 self._out.write(line[post:])
456 self._func_graph_skip_line = False
457 return
458 elif match_re:
459 self._out.write(line)
460 return
461 elif self._func_graph_skip_line:
462 return
463 else:
464 self.reset()
Jerome Forissier6e7c2e92018-11-14 11:02:49 +0100465 match = re.search(REGION_RE, line)
466 if match:
467 # Region table: save info for later processing once
468 # we know which UUID corresponds to which ELF index
469 addr = match.group('addr')
470 size = match.group('size')
471 elf_idx = match.group('elf_idx')
472 self._regions.append([addr, size, elf_idx, line])
473 return
474 match = re.search(ELF_LIST_RE, line)
475 if match:
476 # ELF list: save info for later. Region table and ELF list
477 # will be displayed when the call stack is reached
478 i = int(match.group('idx'))
479 self._elfs[i] = [match.group('uuid'), match.group('load_addr'),
480 line]
481 return
Jerome Forissier99e82b12022-05-05 10:19:22 +0200482 match = re.search(TA_PANIC_RE, line)
483 if match:
484 code = match.group('code')
485 if code in tee_result_names:
486 line = line.strip() + ' (' + tee_result_names[code] + ')\n'
487 self._out.write(line)
488 return
Jerome Forissier105e09c2019-10-16 16:59:35 +0200489 match = re.search(TEE_LOAD_ADDR_RE, line)
490 if match:
491 self._tee_load_addr = match.group('load_addr')
Jerome Forissier6e7c2e92018-11-14 11:02:49 +0100492 match = re.search(CALL_STACK_RE, line)
493 if match:
494 self._call_stack_found = True
495 if self._regions:
496 for r in self._regions:
497 r_addr = r[0]
498 r_size = r[1]
499 elf_idx = r[2]
500 saved_line = r[3]
501 if elf_idx is None:
502 self._out.write(saved_line)
503 else:
504 self._out.write(saved_line.strip() +
505 self.sections_in_region(r_addr,
506 r_size,
507 elf_idx) +
508 '\n')
509 if self._elfs:
510 for k in self._elfs:
511 e = self._elfs[k]
512 if (len(e) >= 3):
513 # TA executable or library
514 self._out.write(e[2].strip())
515 elf = self.get_elf(e[0])
516 if elf:
517 rpath = os.path.realpath(elf)
518 path = self.pretty_print_path(rpath)
519 self._out.write(' (' + path + ')')
520 self._out.write('\n')
521 # Here is a good place to resolve the abort address because we
522 # have all the information we need
523 if self._saved_abort_line:
524 self._out.write(self.process_abort(self._saved_abort_line))
Sumit Gargc90b6662019-03-28 18:24:21 +0530525 match = re.search(FUNC_GRAPH_RE, line)
526 if match:
527 self._func_graph_found = True
Jerome Forissier6e7c2e92018-11-14 11:02:49 +0100528 match = re.search(ABORT_ADDR_RE, line)
529 if match:
530 self.reset()
531 # At this point the arch and TA load address are unknown.
532 # Save the line so We can translate the abort address later.
533 self._saved_abort_line = line
534 self._out.write(line)
Jerome Forissier733a15f2017-05-19 17:40:17 +0200535
536 def flush(self):
537 self._out.flush()
538
Jerome Forissierae252462018-05-25 15:07:28 +0200539
Jerome Forissier733a15f2017-05-19 17:40:17 +0200540def main():
541 args = get_args()
542 if args.dir:
543 # Flatten list in case -d is used several times *and* with multiple
544 # arguments
545 args.dirs = [item for sublist in args.dir for item in sublist]
546 else:
547 args.dirs = []
548 symbolizer = Symbolizer(sys.stdout, args.dirs, args.strip_path)
549
Jerome Forissier6b4fc672019-09-23 09:49:32 +0200550 fd = sys.stdin.fileno()
Jerome Forissier20d152b2019-09-25 20:28:33 +0200551 isatty = os.isatty(fd)
552 if isatty:
553 old = termios.tcgetattr(fd)
554 new = termios.tcgetattr(fd)
555 new[3] = new[3] & ~termios.ECHO # lflags
Jerome Forissier6b4fc672019-09-23 09:49:32 +0200556 try:
Jerome Forissier20d152b2019-09-25 20:28:33 +0200557 if isatty:
558 termios.tcsetattr(fd, termios.TCSADRAIN, new)
Jerome Forissier6b4fc672019-09-23 09:49:32 +0200559 for line in sys.stdin:
560 symbolizer.write(line)
561 finally:
562 symbolizer.flush()
Jerome Forissier20d152b2019-09-25 20:28:33 +0200563 if isatty:
564 termios.tcsetattr(fd, termios.TCSADRAIN, old)
Jerome Forissier733a15f2017-05-19 17:40:17 +0200565
Jerome Forissier1d8c2a42018-09-07 17:05:21 +0200566
Jerome Forissier733a15f2017-05-19 17:40:17 +0200567if __name__ == "__main__":
568 main()