zephyr/scripts/build/gen_symtab.py

158 lines
4.8 KiB
Python

#!/usr/bin/env python3
#
# Copyright (c) 2024 Meta Platforms
#
# SPDX-License-Identifier: Apache-2.0
import argparse
import sys
import os
import re
from elftools.elf.elffile import ELFFile
from elftools.elf.descriptions import (
describe_symbol_type,
)
class gen_symtab_log:
def __init__(self, debug=False):
self.__debug = debug
def debug(self, text):
"""Print debug message if debugging is enabled.
Note - this function requires config global variable to be initialized.
"""
if self.__debug:
sys.stdout.write(os.path.basename(
sys.argv[0]) + ": " + text + "\n")
@staticmethod
def error(text):
sys.exit(os.path.basename(sys.argv[0]) + ": error: " + text + "\n")
def set_debug(self, state):
self.__debug = state
log = gen_symtab_log()
def parse_args():
parser = argparse.ArgumentParser(description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter, allow_abbrev=False)
parser.add_argument("-k", "--kernel", required=True,
help="Zephyr kernel image")
parser.add_argument("-o", "--output", required=True,
help="Output source file")
parser.add_argument("-d", "--debug", action="store_true",
help="Print additional debugging information")
return parser.parse_args()
class symtab_entry:
def __init__(self, addr, size, offset, name):
self.addr = addr
self.size = size
self.offset = offset
self.name = name
def __eq__(self, other):
return self.addr == other.addr
first_addr = 0
symtab_list = []
def sanitize_func_name(name):
pattern = r'(^[a-zA-Z_][a-zA-Z0-9_]*)'
match = re.match(pattern, name)
if match:
return match.group(0)
else:
log.error(f"Failed to sanitize function name: {name}")
return name
def main():
args = parse_args()
log.set_debug(args.debug)
with open(args.kernel, "rb") as rf:
elf = ELFFile(rf)
# Find the symbol table.
symtab = elf.get_section_by_name('.symtab')
i = 1
for nsym, symbol in enumerate(symtab.iter_symbols()): # pylint: disable=unused-variable
symbol_type = describe_symbol_type(symbol['st_info']['type'])
symbol_addr = symbol['st_value']
symbol_size = symbol['st_size']
if symbol_type == 'FUNC' and symbol_addr != 0:
symbol_name = sanitize_func_name(symbol.name)
dummy_offset = 0 # offsets will be calculated later after we know the first address
entry = symtab_entry(
symbol_addr, symbol_size, dummy_offset, symbol_name)
# Prevent entries with duplicated addresses
if entry not in symtab_list:
symtab_list.append(entry)
# Sort the address in ascending order
symtab_list.sort(key=lambda x: x.addr, reverse=False)
# Get the address of the first symbol
first_addr = symtab_list[0].addr
for i, entry in enumerate(symtab_list):
# Offset is calculated here
entry.offset = entry.addr - first_addr
# Debug print
log.debug('%6d: %s %s %.25s' % (
i,
hex(entry.addr),
hex(entry.size),
entry.name))
with open(args.output, 'w') as wf:
print("/* AUTO-GENERATED by gen_symtab.py, do not edit! */", file=wf)
print("", file=wf)
print("#include <zephyr/linker/sections.h>", file=wf)
print("#include <zephyr/debug/symtab.h>", file=wf)
print("", file=wf)
print(
f"const struct z_symtab_entry __symtab_entry z_symtab_entries[{len(symtab_list) + 1}] = {{", file=wf)
for i, entry in enumerate(symtab_list):
print(
f"\t/* ADDR: {hex(entry.addr)} SIZE: {hex(entry.size)} */", file=wf)
print(
f"\t[{i}] = {{.offset = {hex(entry.offset)}, .name = \"{entry.name}\"}},", file=wf)
# Append a dummy entry at the end to facilitate the binary search
if symtab_list[-1].size == 0:
dummy_offset = f"{hex(symtab_list[-1].offset)} + sizeof(uintptr_t)"
else:
dummy_offset = f"{hex(symtab_list[-1].offset + symtab_list[-1].size)}"
print("\t/* dummy entry */", file=wf)
print(
f"\t[{len(symtab_list)}] = {{.offset = {dummy_offset}, .name = \"?\"}},", file=wf)
print(f"}};\n", file=wf)
print(f"const struct symtab_info __symtab_info z_symtab = {{", file=wf)
print(f"\t.first_addr = {hex(first_addr)},", file=wf)
print(f"\t.length = {len(symtab_list)},", file=wf)
print(f"\t.entries = z_symtab_entries,", file=wf)
print(f"}};\n", file=wf)
if __name__ == "__main__":
main()