| #!/usr/bin/env python3 |
| # |
| # Copyright (c) 2024 Meta Platforms |
| # |
| # SPDX-License-Identifier: Apache-2.0 |
| |
| import argparse |
| import os |
| import re |
| import sys |
| |
| from elftools.elf.descriptions import ( |
| describe_symbol_type, |
| ) |
| from elftools.elf.elffile import ELFFile |
| |
| |
| 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') |
| |
| for symbol in symtab.iter_symbols(): |
| 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(f'{i:6d}: {hex(entry.addr)} {hex(entry.size)} {entry.name:.25s}') |
| |
| 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) |
| num = len(symtab_list) + 1 |
| print( |
| f"const struct z_symtab_entry __symtab_entry z_symtab_entries[{num}] = {{", |
| 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("};\n", file=wf) |
| |
| print("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("\t.entries = z_symtab_entries,", file=wf) |
| print("};\n", file=wf) |
| |
| |
| if __name__ == "__main__": |
| main() |