Skip to content

Commit a073d11

Browse files
pbo-linarostsquad
authored andcommitted
contrib/plugins/uftrace_symbols.py
usage: contrib/plugins/uftrace_symbols.py \ --prefix-symbols \ arm-trusted-firmware/build/qemu/debug/bl1/bl1.elf \ arm-trusted-firmware/build/qemu/debug/bl2/bl2.elf \ arm-trusted-firmware/build/qemu/debug/bl31/bl31.elf \ u-boot/u-boot:0x60000000 \ u-boot/u-boot.relocated:0x000000023f6b6000 \ linux/vmlinux Will generate symbols and memory mapping files for uftrace, allowing to have an enhanced trace, instead of raw addresses. It takes a collection of elf files, and automatically find all their symbols, and generate an ordered memory map based on that. This script uses the python (native) pyelftools module. Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Pierrick Bouvier <pierrick.bouvier@linaro.org> Acked-by: Alex Bennée <alex.bennee@linaro.org> Message-ID: <20250902075042.223990-9-pierrick.bouvier@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-ID: <20250922093711.2768983-25-alex.bennee@linaro.org>
1 parent b860d96 commit a073d11

File tree

1 file changed

+152
-0
lines changed

1 file changed

+152
-0
lines changed

contrib/plugins/uftrace_symbols.py

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
#
4+
# Create symbols and mapping files for uftrace.
5+
#
6+
# Copyright 2025 Linaro Ltd
7+
# Author: Pierrick Bouvier <pierrick.bouvier@linaro.org>
8+
#
9+
# SPDX-License-Identifier: GPL-2.0-or-later
10+
11+
import argparse
12+
import elftools # pip install pyelftools
13+
import os
14+
15+
from elftools.elf.elffile import ELFFile
16+
from elftools.elf.sections import SymbolTableSection
17+
18+
def elf_func_symbols(elf):
19+
symbol_tables = [(idx, s) for idx, s in enumerate(elf.iter_sections())
20+
if isinstance(s, SymbolTableSection)]
21+
symbols = []
22+
for _, section in symbol_tables:
23+
for _, symbol in enumerate(section.iter_symbols()):
24+
if symbol_size(symbol) == 0:
25+
continue
26+
type = symbol['st_info']['type']
27+
if type == 'STT_FUNC' or type == 'STT_NOTYPE':
28+
symbols.append(symbol)
29+
symbols.sort(key = lambda x: symbol_addr(x))
30+
return symbols
31+
32+
def symbol_size(symbol):
33+
return symbol['st_size']
34+
35+
def symbol_addr(symbol):
36+
addr = symbol['st_value']
37+
# clamp addr to 48 bits, like uftrace entries
38+
return addr & 0xffffffffffff
39+
40+
def symbol_name(symbol):
41+
return symbol.name
42+
43+
class BinaryFile:
44+
def __init__(self, path, map_offset):
45+
self.fullpath = os.path.realpath(path)
46+
self.map_offset = map_offset
47+
with open(path, 'rb') as f:
48+
self.elf = ELFFile(f)
49+
self.symbols = elf_func_symbols(self.elf)
50+
51+
def path(self):
52+
return self.fullpath
53+
54+
def addr_start(self):
55+
return self.map_offset
56+
57+
def addr_end(self):
58+
last_sym = self.symbols[-1]
59+
return symbol_addr(last_sym) + symbol_size(last_sym) + self.map_offset
60+
61+
def generate_symbol_file(self, prefix_symbols):
62+
binary_name = os.path.basename(self.fullpath)
63+
sym_file_path = f'./uftrace.data/{binary_name}.sym'
64+
print(f'{sym_file_path} ({len(self.symbols)} symbols)')
65+
with open(sym_file_path, 'w') as sym_file:
66+
# print hexadecimal addresses on 48 bits
67+
addrx = "0>12x"
68+
for s in self.symbols:
69+
addr = symbol_addr(s)
70+
addr = f'{addr:{addrx}}'
71+
size = f'{symbol_size(s):{addrx}}'
72+
name = symbol_name(s)
73+
if prefix_symbols:
74+
name = f'{binary_name}:{name}'
75+
print(addr, size, 'T', name, file=sym_file)
76+
77+
def parse_parameter(p):
78+
s = p.split(":")
79+
path = s[0]
80+
if len(s) == 1:
81+
return path, 0
82+
if len(s) > 2:
83+
raise ValueError('only one offset can be set')
84+
offset = s[1]
85+
if not offset.startswith('0x'):
86+
err = f'offset "{offset}" is not an hexadecimal constant. '
87+
err += 'It should starts with "0x".'
88+
raise ValueError(err)
89+
offset = int(offset, 16)
90+
return path, offset
91+
92+
def is_from_user_mode(map_file_path):
93+
if os.path.exists(map_file_path):
94+
with open(map_file_path, 'r') as map_file:
95+
if not map_file.readline().startswith('# map stack on'):
96+
return True
97+
return False
98+
99+
def generate_map(binaries):
100+
map_file_path = './uftrace.data/sid-0.map'
101+
102+
if is_from_user_mode(map_file_path):
103+
print(f'do not overwrite {map_file_path} generated from qemu-user')
104+
return
105+
106+
mappings = []
107+
108+
# print hexadecimal addresses on 48 bits
109+
addrx = "0>12x"
110+
111+
mappings += ['# map stack on highest address possible, to prevent uftrace']
112+
mappings += ['# from considering any kernel address']
113+
mappings += ['ffffffffffff-ffffffffffff rw-p 00000000 00:00 0 [stack]']
114+
115+
for b in binaries:
116+
m = f'{b.addr_start():{addrx}}-{b.addr_end():{addrx}}'
117+
m += f' r--p 00000000 00:00 0 {b.path()}'
118+
mappings.append(m)
119+
120+
with open(map_file_path, 'w') as map_file:
121+
print('\n'.join(mappings), file=map_file)
122+
print(f'{map_file_path}')
123+
print('\n'.join(mappings))
124+
125+
def main():
126+
parser = argparse.ArgumentParser(description=
127+
'generate symbol files for uftrace')
128+
parser.add_argument('elf_file', nargs='+',
129+
help='path to an ELF file. '
130+
'Use /path/to/file:0xdeadbeef to add a mapping offset.')
131+
parser.add_argument('--prefix-symbols',
132+
help='prepend binary name to symbols',
133+
action=argparse.BooleanOptionalAction)
134+
args = parser.parse_args()
135+
136+
if not os.path.exists('./uftrace.data'):
137+
os.mkdir('./uftrace.data')
138+
139+
binaries = []
140+
for file in args.elf_file:
141+
path, offset = parse_parameter(file)
142+
b = BinaryFile(path, offset)
143+
binaries.append(b)
144+
binaries.sort(key = lambda b: b.addr_end());
145+
146+
for b in binaries:
147+
b.generate_symbol_file(args.prefix_symbols)
148+
149+
generate_map(binaries)
150+
151+
if __name__ == '__main__':
152+
main()

0 commit comments

Comments
 (0)