blob: 87b2466034fabf7cd01de0e349256fe083f7ddbc [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 Wiklander17a66902023-11-10 17:57:58 +010022 r'[UEIDFM]/(TC|LD):([0-9]+ )?(\?*|[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
Alvin Chang0309f582023-05-29 17:35:42 +080046architecture. The resulting command is then expected to be found in the user's
47PATH.
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-'
Alvin Chang0309f582023-05-29 17:35:42 +0800173 elif b'RISC-V,' in output[0]:
174 if b'32-bit' in output[0]:
175 self._arch = 'riscv32-unknown-linux-gnu-'
176 elif b'64-bit' in output[0]:
177 self._arch = 'riscv64-unknown-linux-gnu-'
Jerome Forissierd7204312017-09-04 17:58:52 +0200178
Jerome Forissier24778de2020-02-12 14:48:03 +0100179 def arch_prefix(self, cmd, elf):
180 self.set_arch(elf)
Jerome Forissierae252462018-05-25 15:07:28 +0200181 if self._arch is None:
182 return ''
Jerome Forissierd7204312017-09-04 17:58:52 +0200183 return self._arch + cmd
Jerome Forissier142c5cc2017-08-24 15:07:17 +0200184
Jerome Forissierae252462018-05-25 15:07:28 +0200185 def spawn_addr2line(self, elf_name):
186 if elf_name is None:
187 return
188 if self._addr2line_elf_name is elf_name:
189 return
190 if self._addr2line:
191 self._addr2line.terminate
192 self._addr2line = None
193 elf = self.get_elf(elf_name)
194 if not elf:
195 return
Jerome Forissier24778de2020-02-12 14:48:03 +0100196 cmd = self.arch_prefix('addr2line', elf)
Jerome Forissierae252462018-05-25 15:07:28 +0200197 if not cmd:
198 return
Jerome Forissierc0c57c82020-01-08 14:57:32 +0100199 self._addr2line = self.my_Popen([cmd, '-f', '-p', '-e', elf])
Jerome Forissierba84a3f2020-02-12 14:44:03 +0100200 self._addr2line_elf_name = elf_name
Jerome Forissierae252462018-05-25 15:07:28 +0200201
202 # If addr falls into a region that maps a TA ELF file, return the load
203 # address of that file.
204 def elf_load_addr(self, addr):
205 if self._regions:
206 for r in self._regions:
207 r_addr = int(r[0], 16)
208 r_size = int(r[1], 16)
209 i_addr = int(addr, 16)
210 if (i_addr >= r_addr and i_addr < (r_addr + r_size)):
211 # Found region
212 elf_idx = r[2]
213 if elf_idx is not None:
214 return self._elfs[int(elf_idx)][1]
Sumit Garg099918f2019-09-05 13:23:01 +0530215 # In case address is not found in TA ELF file, fallback to tee.elf
216 # especially to symbolize mixed (user-space and kernel) addresses
217 # which is true when syscall ftrace is enabled along with TA
218 # ftrace.
Jerome Forissier91068f82019-11-26 10:51:45 +0100219 return self._tee_load_addr
Jerome Forissierae252462018-05-25 15:07:28 +0200220 else:
221 # tee.elf
Jerome Forissier105e09c2019-10-16 16:59:35 +0200222 return self._tee_load_addr
Jerome Forissierae252462018-05-25 15:07:28 +0200223
224 def elf_for_addr(self, addr):
225 l_addr = self.elf_load_addr(addr)
Jerome Forissier91068f82019-11-26 10:51:45 +0100226 if l_addr == self._tee_load_addr:
227 return 'tee.elf'
Jerome Forissierae252462018-05-25 15:07:28 +0200228 for k in self._elfs:
229 e = self._elfs[k]
230 if int(e[1], 16) == int(l_addr, 16):
231 return e[0]
232 return None
Jerome Forissier733a15f2017-05-19 17:40:17 +0200233
Jerome Forissier142c5cc2017-08-24 15:07:17 +0200234 def subtract_load_addr(self, addr):
Jerome Forissierae252462018-05-25 15:07:28 +0200235 l_addr = self.elf_load_addr(addr)
236 if l_addr is None:
237 return None
238 if int(l_addr, 16) > int(addr, 16):
Jerome Forissier142c5cc2017-08-24 15:07:17 +0200239 return ''
Jerome Forissierae252462018-05-25 15:07:28 +0200240 return '0x{:x}'.format(int(addr, 16) - int(l_addr, 16))
Jerome Forissier142c5cc2017-08-24 15:07:17 +0200241
242 def resolve(self, addr):
243 reladdr = self.subtract_load_addr(addr)
Jerome Forissierae252462018-05-25 15:07:28 +0200244 self.spawn_addr2line(self.elf_for_addr(addr))
Jerome Forissier142c5cc2017-08-24 15:07:17 +0200245 if not reladdr or not self._addr2line:
Jerome Forissier733a15f2017-05-19 17:40:17 +0200246 return '???'
Jerome Forissierc0c57c82020-01-08 14:57:32 +0100247 if self.elf_for_addr(addr) == 'tee.elf':
248 reladdr = '0x{:x}'.format(int(reladdr, 16) +
249 int(self.first_vma('tee.elf'), 16))
Jerome Forissier733a15f2017-05-19 17:40:17 +0200250 try:
Rouven Czerwinskibbaeed42019-08-07 20:07:00 +0200251 print(reladdr, file=self._addr2line.stdin)
Jerome Forissier733a15f2017-05-19 17:40:17 +0200252 ret = self._addr2line.stdout.readline().rstrip('\n')
253 except IOError:
254 ret = '!!!'
255 return ret
256
Jerome Forissier142c5cc2017-08-24 15:07:17 +0200257 def symbol_plus_offset(self, addr):
258 ret = ''
259 prevsize = 0
260 reladdr = self.subtract_load_addr(addr)
Jerome Forissierae252462018-05-25 15:07:28 +0200261 elf_name = self.elf_for_addr(addr)
262 if elf_name is None:
263 return ''
264 elf = self.get_elf(elf_name)
Kun Laid33360e2023-06-15 20:38:07 +0800265 if elf is None:
266 return ''
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)
Kun Laid33360e2023-06-15 20:38:07 +0800308 if elf is None:
309 return ''
Jerome Forissier24778de2020-02-12 14:48:03 +0100310 cmd = self.arch_prefix('objdump', elf)
Jerome Forissier142c5cc2017-08-24 15:07:17 +0200311 if not reladdr or not elf or not cmd:
312 return ''
Jerome Forissier30999122017-08-25 18:42:58 +0200313 iaddr = int(reladdr, 16)
Jerome Forissier1cbf7772018-09-07 17:08:23 +0200314 objdump = self.my_Popen([cmd, '--section-headers', elf])
Jerome Forissier142c5cc2017-08-24 15:07:17 +0200315 for line in iter(objdump.stdout.readline, ''):
316 try:
317 idx, name, size, vma, lma, offs, algn = line.split()
Jerome Forissier1d8c2a42018-09-07 17:05:21 +0200318 except ValueError:
Jerome Forissierae252462018-05-25 15:07:28 +0200319 continue
Jerome Forissier142c5cc2017-08-24 15:07:17 +0200320 ivma = int(vma, 16)
321 isize = int(size, 16)
322 if ivma == iaddr:
323 ret = name
324 break
325 if ivma < iaddr and ivma + isize >= iaddr:
326 offs = iaddr - ivma
327 ret = name + '+' + str(offs)
328 break
329 objdump.terminate()
330 return ret
331
332 def process_abort(self, line):
333 ret = ''
334 match = re.search(ABORT_ADDR_RE, line)
335 addr = match.group('addr')
336 pre = match.start('addr')
337 post = match.end('addr')
338 sym = self.symbol_plus_offset(addr)
339 sec = self.section_plus_offset(addr)
340 if sym or sec:
341 ret += line[:pre]
342 ret += addr
343 if sym:
344 ret += ' ' + sym
345 if sec:
346 ret += ' ' + sec
347 ret += line[post:]
348 return ret
349
Jerome Forissier30999122017-08-25 18:42:58 +0200350 # Return all ELF sections with the ALLOC flag
Jerome Forissierae252462018-05-25 15:07:28 +0200351 def read_sections(self, elf_name):
352 if elf_name is None:
Jerome Forissier30999122017-08-25 18:42:58 +0200353 return
Jerome Forissierae252462018-05-25 15:07:28 +0200354 if elf_name in self._sections:
355 return
356 elf = self.get_elf(elf_name)
Jerome Forissierd7c22ac2020-06-22 14:07:38 +0200357 if not elf:
358 return
Jerome Forissier24778de2020-02-12 14:48:03 +0100359 cmd = self.arch_prefix('objdump', elf)
Jerome Forissier30999122017-08-25 18:42:58 +0200360 if not elf or not cmd:
361 return
Jerome Forissierae252462018-05-25 15:07:28 +0200362 self._sections[elf_name] = []
Jerome Forissier1cbf7772018-09-07 17:08:23 +0200363 objdump = self.my_Popen([cmd, '--section-headers', elf])
Jerome Forissier30999122017-08-25 18:42:58 +0200364 for line in iter(objdump.stdout.readline, ''):
365 try:
366 _, name, size, vma, _, _, _ = line.split()
Jerome Forissier1d8c2a42018-09-07 17:05:21 +0200367 except ValueError:
Jerome Forissier30999122017-08-25 18:42:58 +0200368 if 'ALLOC' in line:
Jerome Forissierae252462018-05-25 15:07:28 +0200369 self._sections[elf_name].append([name, int(vma, 16),
370 int(size, 16)])
Jerome Forissier30999122017-08-25 18:42:58 +0200371
Jerome Forissierc0c57c82020-01-08 14:57:32 +0100372 def first_vma(self, elf_name):
373 self.read_sections(elf_name)
374 return '0x{:x}'.format(self._sections[elf_name][0][1])
375
Jerome Forissier30999122017-08-25 18:42:58 +0200376 def overlaps(self, section, addr, size):
377 sec_addr = section[1]
378 sec_size = section[2]
379 if not size or not sec_size:
380 return False
Jerome Forissierae252462018-05-25 15:07:28 +0200381 return ((addr <= (sec_addr + sec_size - 1)) and
382 ((addr + size - 1) >= sec_addr))
Jerome Forissier30999122017-08-25 18:42:58 +0200383
Jerome Forissierae252462018-05-25 15:07:28 +0200384 def sections_in_region(self, addr, size, elf_idx):
Jerome Forissier30999122017-08-25 18:42:58 +0200385 ret = ''
386 addr = self.subtract_load_addr(addr)
387 if not addr:
388 return ''
389 iaddr = int(addr, 16)
390 isize = int(size, 16)
Jerome Forissierae252462018-05-25 15:07:28 +0200391 elf = self._elfs[int(elf_idx)][0]
392 if elf is None:
393 return ''
394 self.read_sections(elf)
395 if elf not in self._sections:
396 return ''
397 for s in self._sections[elf]:
Jerome Forissier30999122017-08-25 18:42:58 +0200398 if self.overlaps(s, iaddr, isize):
399 ret += ' ' + s[0]
400 return ret
401
Jerome Forissier733a15f2017-05-19 17:40:17 +0200402 def reset(self):
403 self._call_stack_found = False
Jerome Forissier733a15f2017-05-19 17:40:17 +0200404 if self._addr2line:
405 self._addr2line.terminate()
406 self._addr2line = None
Jerome Forissierae252462018-05-25 15:07:28 +0200407 self._addr2line_elf_name = None
Jerome Forissierd7204312017-09-04 17:58:52 +0200408 self._arch = None
Jerome Forissier142c5cc2017-08-24 15:07:17 +0200409 self._saved_abort_line = ''
Jerome Forissierae252462018-05-25 15:07:28 +0200410 self._sections = {} # {elf_name: [[name, addr, size], ...], ...}
411 self._regions = [] # [[addr, size, elf_idx, saved line], ...]
412 self._elfs = {0: ["tee.elf", 0]} # {idx: [uuid, load_addr], ...}
Jerome Forissier91068f82019-11-26 10:51:45 +0100413 self._tee_load_addr = '0x0'
Sumit Gargc90b6662019-03-28 18:24:21 +0530414 self._func_graph_found = False
415 self._func_graph_skip_line = True
Jerome Forissier733a15f2017-05-19 17:40:17 +0200416
Jerome Forissier095567e2018-05-29 17:42:34 +0200417 def pretty_print_path(self, path):
418 if self._strip_path:
419 return re.sub(re.escape(self._strip_path) + '/*', '', path)
420 return path
421
Jerome Forissier733a15f2017-05-19 17:40:17 +0200422 def write(self, line):
Jerome Forissier6e7c2e92018-11-14 11:02:49 +0100423 if self._call_stack_found:
424 match = re.search(STACK_ADDR_RE, line)
Jerome Forissier30999122017-08-25 18:42:58 +0200425 if match:
426 addr = match.group('addr')
Jerome Forissier6e7c2e92018-11-14 11:02:49 +0100427 pre = match.start('addr')
428 post = match.end('addr')
429 self._out.write(line[:pre])
430 self._out.write(addr)
Jerome Forissier5500d702020-07-31 11:30:54 +0200431 # The call stack contains return addresses (LR/ELR values).
432 # Heuristic: subtract 2 to obtain the call site of the function
433 # or the location of the exception. This value works for A64,
434 # A32 as well as Thumb.
435 pc = 0
436 lr = int(addr, 16)
437 if lr:
438 pc = lr - 2
439 res = self.resolve('0x{:x}'.format(pc))
Jerome Forissier6e7c2e92018-11-14 11:02:49 +0100440 res = self.pretty_print_path(res)
441 self._out.write(' ' + res)
442 self._out.write(line[post:])
Jerome Forissierae252462018-05-25 15:07:28 +0200443 return
Jerome Forissier6e7c2e92018-11-14 11:02:49 +0100444 else:
Jerome Forissier27b83ad2017-10-06 13:37:35 +0200445 self.reset()
Sumit Gargc90b6662019-03-28 18:24:21 +0530446 if self._func_graph_found:
447 match = re.search(GRAPH_ADDR_RE, line)
448 match_re = re.search(GRAPH_RE, line)
449 if match:
450 addr = match.group('addr')
451 pre = match.start('addr')
452 post = match.end('addr')
453 self._out.write(line[:pre])
454 res = self.resolve(addr)
455 res_arr = re.split(' ', res)
456 self._out.write(res_arr[0])
457 self._out.write(line[post:])
458 self._func_graph_skip_line = False
459 return
460 elif match_re:
461 self._out.write(line)
462 return
463 elif self._func_graph_skip_line:
464 return
465 else:
466 self.reset()
Jerome Forissier6e7c2e92018-11-14 11:02:49 +0100467 match = re.search(REGION_RE, line)
468 if match:
469 # Region table: save info for later processing once
470 # we know which UUID corresponds to which ELF index
471 addr = match.group('addr')
472 size = match.group('size')
473 elf_idx = match.group('elf_idx')
474 self._regions.append([addr, size, elf_idx, line])
475 return
476 match = re.search(ELF_LIST_RE, line)
477 if match:
478 # ELF list: save info for later. Region table and ELF list
479 # will be displayed when the call stack is reached
480 i = int(match.group('idx'))
481 self._elfs[i] = [match.group('uuid'), match.group('load_addr'),
482 line]
483 return
Jerome Forissier99e82b12022-05-05 10:19:22 +0200484 match = re.search(TA_PANIC_RE, line)
485 if match:
486 code = match.group('code')
487 if code in tee_result_names:
488 line = line.strip() + ' (' + tee_result_names[code] + ')\n'
489 self._out.write(line)
490 return
Jerome Forissier105e09c2019-10-16 16:59:35 +0200491 match = re.search(TEE_LOAD_ADDR_RE, line)
492 if match:
493 self._tee_load_addr = match.group('load_addr')
Jerome Forissier6e7c2e92018-11-14 11:02:49 +0100494 match = re.search(CALL_STACK_RE, line)
495 if match:
496 self._call_stack_found = True
497 if self._regions:
498 for r in self._regions:
499 r_addr = r[0]
500 r_size = r[1]
501 elf_idx = r[2]
502 saved_line = r[3]
503 if elf_idx is None:
504 self._out.write(saved_line)
505 else:
506 self._out.write(saved_line.strip() +
507 self.sections_in_region(r_addr,
508 r_size,
509 elf_idx) +
510 '\n')
511 if self._elfs:
512 for k in self._elfs:
513 e = self._elfs[k]
514 if (len(e) >= 3):
515 # TA executable or library
516 self._out.write(e[2].strip())
517 elf = self.get_elf(e[0])
518 if elf:
519 rpath = os.path.realpath(elf)
520 path = self.pretty_print_path(rpath)
521 self._out.write(' (' + path + ')')
522 self._out.write('\n')
523 # Here is a good place to resolve the abort address because we
524 # have all the information we need
525 if self._saved_abort_line:
526 self._out.write(self.process_abort(self._saved_abort_line))
Sumit Gargc90b6662019-03-28 18:24:21 +0530527 match = re.search(FUNC_GRAPH_RE, line)
528 if match:
529 self._func_graph_found = True
Jerome Forissier6e7c2e92018-11-14 11:02:49 +0100530 match = re.search(ABORT_ADDR_RE, line)
531 if match:
532 self.reset()
533 # At this point the arch and TA load address are unknown.
534 # Save the line so We can translate the abort address later.
535 self._saved_abort_line = line
536 self._out.write(line)
Jerome Forissier733a15f2017-05-19 17:40:17 +0200537
538 def flush(self):
539 self._out.flush()
540
Jerome Forissierae252462018-05-25 15:07:28 +0200541
Jerome Forissier733a15f2017-05-19 17:40:17 +0200542def main():
543 args = get_args()
544 if args.dir:
545 # Flatten list in case -d is used several times *and* with multiple
546 # arguments
547 args.dirs = [item for sublist in args.dir for item in sublist]
548 else:
549 args.dirs = []
550 symbolizer = Symbolizer(sys.stdout, args.dirs, args.strip_path)
551
Jerome Forissier6b4fc672019-09-23 09:49:32 +0200552 fd = sys.stdin.fileno()
Jerome Forissier20d152b2019-09-25 20:28:33 +0200553 isatty = os.isatty(fd)
554 if isatty:
555 old = termios.tcgetattr(fd)
556 new = termios.tcgetattr(fd)
557 new[3] = new[3] & ~termios.ECHO # lflags
Jerome Forissier6b4fc672019-09-23 09:49:32 +0200558 try:
Jerome Forissier20d152b2019-09-25 20:28:33 +0200559 if isatty:
560 termios.tcsetattr(fd, termios.TCSADRAIN, new)
Jerome Forissier6b4fc672019-09-23 09:49:32 +0200561 for line in sys.stdin:
562 symbolizer.write(line)
563 finally:
564 symbolizer.flush()
Jerome Forissier20d152b2019-09-25 20:28:33 +0200565 if isatty:
566 termios.tcsetattr(fd, termios.TCSADRAIN, old)
Jerome Forissier733a15f2017-05-19 17:40:17 +0200567
Jerome Forissier1d8c2a42018-09-07 17:05:21 +0200568
Jerome Forissier733a15f2017-05-19 17:40:17 +0200569if __name__ == "__main__":
570 main()