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