mirror of https://gitlab.com/qemu-project/qemu
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
153 lines
4.8 KiB
Python
153 lines
4.8 KiB
Python
#!/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()
|