blob: e924de74134393006c3db3d17b97bfe2d9dfcfba [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
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)
Jerome Forissier24778de2020-02-12 14:48:03 +0100265 cmd = self.arch_prefix('nm', elf)
Jerome Forissier142c5cc2017-08-24 15:07:17 +0200266 if not reladdr or not elf or not cmd:
267 return ''
Jerome Forissier30999122017-08-25 18:42:58 +0200268 ireladdr = int(reladdr, 16)
Jerome Forissier1cbf7772018-09-07 17:08:23 +0200269 nm = self.my_Popen([cmd, '--numeric-sort', '--print-size', elf])
Jerome Forissier142c5cc2017-08-24 15:07:17 +0200270 for line in iter(nm.stdout.readline, ''):
271 try:
272 addr, size, _, name = line.split()
Jerome Forissier1d8c2a42018-09-07 17:05:21 +0200273 except ValueError:
Jerome Forissier142c5cc2017-08-24 15:07:17 +0200274 # Size is missing
Jerome Forissierb4815422018-06-20 09:43:33 +0200275 try:
276 addr, _, name = line.split()
277 size = '0'
Jerome Forissier1d8c2a42018-09-07 17:05:21 +0200278 except ValueError:
Jerome Forissierb4815422018-06-20 09:43:33 +0200279 # E.g., undefined (external) symbols (line = "U symbol")
280 continue
Jerome Forissier142c5cc2017-08-24 15:07:17 +0200281 iaddr = int(addr, 16)
282 isize = int(size, 16)
283 if iaddr == ireladdr:
284 ret = name
285 break
286 if iaddr < ireladdr and iaddr + isize >= ireladdr:
287 offs = ireladdr - iaddr
288 ret = name + '+' + str(offs)
289 break
290 if iaddr > ireladdr and prevsize == 0:
291 offs = iaddr + ireladdr
292 ret = prevname + '+' + str(offs)
293 break
294 prevsize = size
295 prevname = name
296 nm.terminate()
297 return ret
298
299 def section_plus_offset(self, addr):
300 ret = ''
301 reladdr = self.subtract_load_addr(addr)
Jerome Forissierae252462018-05-25 15:07:28 +0200302 elf_name = self.elf_for_addr(addr)
303 if elf_name is None:
304 return ''
305 elf = self.get_elf(elf_name)
Jerome Forissier24778de2020-02-12 14:48:03 +0100306 cmd = self.arch_prefix('objdump', elf)
Jerome Forissier142c5cc2017-08-24 15:07:17 +0200307 if not reladdr or not elf or not cmd:
308 return ''
Jerome Forissier30999122017-08-25 18:42:58 +0200309 iaddr = int(reladdr, 16)
Jerome Forissier1cbf7772018-09-07 17:08:23 +0200310 objdump = self.my_Popen([cmd, '--section-headers', elf])
Jerome Forissier142c5cc2017-08-24 15:07:17 +0200311 for line in iter(objdump.stdout.readline, ''):
312 try:
313 idx, name, size, vma, lma, offs, algn = line.split()
Jerome Forissier1d8c2a42018-09-07 17:05:21 +0200314 except ValueError:
Jerome Forissierae252462018-05-25 15:07:28 +0200315 continue
Jerome Forissier142c5cc2017-08-24 15:07:17 +0200316 ivma = int(vma, 16)
317 isize = int(size, 16)
318 if ivma == iaddr:
319 ret = name
320 break
321 if ivma < iaddr and ivma + isize >= iaddr:
322 offs = iaddr - ivma
323 ret = name + '+' + str(offs)
324 break
325 objdump.terminate()
326 return ret
327
328 def process_abort(self, line):
329 ret = ''
330 match = re.search(ABORT_ADDR_RE, line)
331 addr = match.group('addr')
332 pre = match.start('addr')
333 post = match.end('addr')
334 sym = self.symbol_plus_offset(addr)
335 sec = self.section_plus_offset(addr)
336 if sym or sec:
337 ret += line[:pre]
338 ret += addr
339 if sym:
340 ret += ' ' + sym
341 if sec:
342 ret += ' ' + sec
343 ret += line[post:]
344 return ret
345
Jerome Forissier30999122017-08-25 18:42:58 +0200346 # Return all ELF sections with the ALLOC flag
Jerome Forissierae252462018-05-25 15:07:28 +0200347 def read_sections(self, elf_name):
348 if elf_name is None:
Jerome Forissier30999122017-08-25 18:42:58 +0200349 return
Jerome Forissierae252462018-05-25 15:07:28 +0200350 if elf_name in self._sections:
351 return
352 elf = self.get_elf(elf_name)
Jerome Forissierd7c22ac2020-06-22 14:07:38 +0200353 if not elf:
354 return
Jerome Forissier24778de2020-02-12 14:48:03 +0100355 cmd = self.arch_prefix('objdump', elf)
Jerome Forissier30999122017-08-25 18:42:58 +0200356 if not elf or not cmd:
357 return
Jerome Forissierae252462018-05-25 15:07:28 +0200358 self._sections[elf_name] = []
Jerome Forissier1cbf7772018-09-07 17:08:23 +0200359 objdump = self.my_Popen([cmd, '--section-headers', elf])
Jerome Forissier30999122017-08-25 18:42:58 +0200360 for line in iter(objdump.stdout.readline, ''):
361 try:
362 _, name, size, vma, _, _, _ = line.split()
Jerome Forissier1d8c2a42018-09-07 17:05:21 +0200363 except ValueError:
Jerome Forissier30999122017-08-25 18:42:58 +0200364 if 'ALLOC' in line:
Jerome Forissierae252462018-05-25 15:07:28 +0200365 self._sections[elf_name].append([name, int(vma, 16),
366 int(size, 16)])
Jerome Forissier30999122017-08-25 18:42:58 +0200367
Jerome Forissierc0c57c82020-01-08 14:57:32 +0100368 def first_vma(self, elf_name):
369 self.read_sections(elf_name)
370 return '0x{:x}'.format(self._sections[elf_name][0][1])
371
Jerome Forissier30999122017-08-25 18:42:58 +0200372 def overlaps(self, section, addr, size):
373 sec_addr = section[1]
374 sec_size = section[2]
375 if not size or not sec_size:
376 return False
Jerome Forissierae252462018-05-25 15:07:28 +0200377 return ((addr <= (sec_addr + sec_size - 1)) and
378 ((addr + size - 1) >= sec_addr))
Jerome Forissier30999122017-08-25 18:42:58 +0200379
Jerome Forissierae252462018-05-25 15:07:28 +0200380 def sections_in_region(self, addr, size, elf_idx):
Jerome Forissier30999122017-08-25 18:42:58 +0200381 ret = ''
382 addr = self.subtract_load_addr(addr)
383 if not addr:
384 return ''
385 iaddr = int(addr, 16)
386 isize = int(size, 16)
Jerome Forissierae252462018-05-25 15:07:28 +0200387 elf = self._elfs[int(elf_idx)][0]
388 if elf is None:
389 return ''
390 self.read_sections(elf)
391 if elf not in self._sections:
392 return ''
393 for s in self._sections[elf]:
Jerome Forissier30999122017-08-25 18:42:58 +0200394 if self.overlaps(s, iaddr, isize):
395 ret += ' ' + s[0]
396 return ret
397
Jerome Forissier733a15f2017-05-19 17:40:17 +0200398 def reset(self):
399 self._call_stack_found = False
Jerome Forissier733a15f2017-05-19 17:40:17 +0200400 if self._addr2line:
401 self._addr2line.terminate()
402 self._addr2line = None
Jerome Forissierae252462018-05-25 15:07:28 +0200403 self._addr2line_elf_name = None
Jerome Forissierd7204312017-09-04 17:58:52 +0200404 self._arch = None
Jerome Forissier142c5cc2017-08-24 15:07:17 +0200405 self._saved_abort_line = ''
Jerome Forissierae252462018-05-25 15:07:28 +0200406 self._sections = {} # {elf_name: [[name, addr, size], ...], ...}
407 self._regions = [] # [[addr, size, elf_idx, saved line], ...]
408 self._elfs = {0: ["tee.elf", 0]} # {idx: [uuid, load_addr], ...}
Jerome Forissier91068f82019-11-26 10:51:45 +0100409 self._tee_load_addr = '0x0'
Sumit Gargc90b6662019-03-28 18:24:21 +0530410 self._func_graph_found = False
411 self._func_graph_skip_line = True
Jerome Forissier733a15f2017-05-19 17:40:17 +0200412
Jerome Forissier095567e2018-05-29 17:42:34 +0200413 def pretty_print_path(self, path):
414 if self._strip_path:
415 return re.sub(re.escape(self._strip_path) + '/*', '', path)
416 return path
417
Jerome Forissier733a15f2017-05-19 17:40:17 +0200418 def write(self, line):
Jerome Forissier6e7c2e92018-11-14 11:02:49 +0100419 if self._call_stack_found:
420 match = re.search(STACK_ADDR_RE, line)
Jerome Forissier30999122017-08-25 18:42:58 +0200421 if match:
422 addr = match.group('addr')
Jerome Forissier6e7c2e92018-11-14 11:02:49 +0100423 pre = match.start('addr')
424 post = match.end('addr')
425 self._out.write(line[:pre])
426 self._out.write(addr)
Jerome Forissier5500d702020-07-31 11:30:54 +0200427 # The call stack contains return addresses (LR/ELR values).
428 # Heuristic: subtract 2 to obtain the call site of the function
429 # or the location of the exception. This value works for A64,
430 # A32 as well as Thumb.
431 pc = 0
432 lr = int(addr, 16)
433 if lr:
434 pc = lr - 2
435 res = self.resolve('0x{:x}'.format(pc))
Jerome Forissier6e7c2e92018-11-14 11:02:49 +0100436 res = self.pretty_print_path(res)
437 self._out.write(' ' + res)
438 self._out.write(line[post:])
Jerome Forissierae252462018-05-25 15:07:28 +0200439 return
Jerome Forissier6e7c2e92018-11-14 11:02:49 +0100440 else:
Jerome Forissier27b83ad2017-10-06 13:37:35 +0200441 self.reset()
Sumit Gargc90b6662019-03-28 18:24:21 +0530442 if self._func_graph_found:
443 match = re.search(GRAPH_ADDR_RE, line)
444 match_re = re.search(GRAPH_RE, line)
445 if match:
446 addr = match.group('addr')
447 pre = match.start('addr')
448 post = match.end('addr')
449 self._out.write(line[:pre])
450 res = self.resolve(addr)
451 res_arr = re.split(' ', res)
452 self._out.write(res_arr[0])
453 self._out.write(line[post:])
454 self._func_graph_skip_line = False
455 return
456 elif match_re:
457 self._out.write(line)
458 return
459 elif self._func_graph_skip_line:
460 return
461 else:
462 self.reset()
Jerome Forissier6e7c2e92018-11-14 11:02:49 +0100463 match = re.search(REGION_RE, line)
464 if match:
465 # Region table: save info for later processing once
466 # we know which UUID corresponds to which ELF index
467 addr = match.group('addr')
468 size = match.group('size')
469 elf_idx = match.group('elf_idx')
470 self._regions.append([addr, size, elf_idx, line])
471 return
472 match = re.search(ELF_LIST_RE, line)
473 if match:
474 # ELF list: save info for later. Region table and ELF list
475 # will be displayed when the call stack is reached
476 i = int(match.group('idx'))
477 self._elfs[i] = [match.group('uuid'), match.group('load_addr'),
478 line]
479 return
Jerome Forissier99e82b12022-05-05 10:19:22 +0200480 match = re.search(TA_PANIC_RE, line)
481 if match:
482 code = match.group('code')
483 if code in tee_result_names:
484 line = line.strip() + ' (' + tee_result_names[code] + ')\n'
485 self._out.write(line)
486 return
Jerome Forissier105e09c2019-10-16 16:59:35 +0200487 match = re.search(TEE_LOAD_ADDR_RE, line)
488 if match:
489 self._tee_load_addr = match.group('load_addr')
Jerome Forissier6e7c2e92018-11-14 11:02:49 +0100490 match = re.search(CALL_STACK_RE, line)
491 if match:
492 self._call_stack_found = True
493 if self._regions:
494 for r in self._regions:
495 r_addr = r[0]
496 r_size = r[1]
497 elf_idx = r[2]
498 saved_line = r[3]
499 if elf_idx is None:
500 self._out.write(saved_line)
501 else:
502 self._out.write(saved_line.strip() +
503 self.sections_in_region(r_addr,
504 r_size,
505 elf_idx) +
506 '\n')
507 if self._elfs:
508 for k in self._elfs:
509 e = self._elfs[k]
510 if (len(e) >= 3):
511 # TA executable or library
512 self._out.write(e[2].strip())
513 elf = self.get_elf(e[0])
514 if elf:
515 rpath = os.path.realpath(elf)
516 path = self.pretty_print_path(rpath)
517 self._out.write(' (' + path + ')')
518 self._out.write('\n')
519 # Here is a good place to resolve the abort address because we
520 # have all the information we need
521 if self._saved_abort_line:
522 self._out.write(self.process_abort(self._saved_abort_line))
Sumit Gargc90b6662019-03-28 18:24:21 +0530523 match = re.search(FUNC_GRAPH_RE, line)
524 if match:
525 self._func_graph_found = True
Jerome Forissier6e7c2e92018-11-14 11:02:49 +0100526 match = re.search(ABORT_ADDR_RE, line)
527 if match:
528 self.reset()
529 # At this point the arch and TA load address are unknown.
530 # Save the line so We can translate the abort address later.
531 self._saved_abort_line = line
532 self._out.write(line)
Jerome Forissier733a15f2017-05-19 17:40:17 +0200533
534 def flush(self):
535 self._out.flush()
536
Jerome Forissierae252462018-05-25 15:07:28 +0200537
Jerome Forissier733a15f2017-05-19 17:40:17 +0200538def main():
539 args = get_args()
540 if args.dir:
541 # Flatten list in case -d is used several times *and* with multiple
542 # arguments
543 args.dirs = [item for sublist in args.dir for item in sublist]
544 else:
545 args.dirs = []
546 symbolizer = Symbolizer(sys.stdout, args.dirs, args.strip_path)
547
Jerome Forissier6b4fc672019-09-23 09:49:32 +0200548 fd = sys.stdin.fileno()
Jerome Forissier20d152b2019-09-25 20:28:33 +0200549 isatty = os.isatty(fd)
550 if isatty:
551 old = termios.tcgetattr(fd)
552 new = termios.tcgetattr(fd)
553 new[3] = new[3] & ~termios.ECHO # lflags
Jerome Forissier6b4fc672019-09-23 09:49:32 +0200554 try:
Jerome Forissier20d152b2019-09-25 20:28:33 +0200555 if isatty:
556 termios.tcsetattr(fd, termios.TCSADRAIN, new)
Jerome Forissier6b4fc672019-09-23 09:49:32 +0200557 for line in sys.stdin:
558 symbolizer.write(line)
559 finally:
560 symbolizer.flush()
Jerome Forissier20d152b2019-09-25 20:28:33 +0200561 if isatty:
562 termios.tcsetattr(fd, termios.TCSADRAIN, old)
Jerome Forissier733a15f2017-05-19 17:40:17 +0200563
Jerome Forissier1d8c2a42018-09-07 17:05:21 +0200564
Jerome Forissier733a15f2017-05-19 17:40:17 +0200565if __name__ == "__main__":
566 main()