1#!/usr/bin/env python3 2# 3# Copyright (c) 2024 Meta Platforms 4# 5# SPDX-License-Identifier: Apache-2.0 6 7import argparse 8import os 9import re 10import sys 11 12from elftools.elf.descriptions import ( 13 describe_symbol_type, 14) 15from elftools.elf.elffile import ELFFile 16 17 18class gen_symtab_log: 19 def __init__(self, debug=False): 20 self.__debug = debug 21 22 def debug(self, text): 23 """Print debug message if debugging is enabled. 24 25 Note - this function requires config global variable to be initialized. 26 """ 27 if self.__debug: 28 sys.stdout.write(os.path.basename(sys.argv[0]) + ": " + text + "\n") 29 30 @staticmethod 31 def error(text): 32 sys.exit(os.path.basename(sys.argv[0]) + ": error: " + text + "\n") 33 34 def set_debug(self, state): 35 self.__debug = state 36 37 38log = gen_symtab_log() 39 40 41def parse_args(): 42 parser = argparse.ArgumentParser( 43 description=__doc__, 44 formatter_class=argparse.RawDescriptionHelpFormatter, 45 allow_abbrev=False, 46 ) 47 48 parser.add_argument("-k", "--kernel", required=True, help="Zephyr kernel image") 49 parser.add_argument("-o", "--output", required=True, help="Output source file") 50 parser.add_argument( 51 "-d", "--debug", action="store_true", help="Print additional debugging information" 52 ) 53 54 return parser.parse_args() 55 56 57class symtab_entry: 58 def __init__(self, addr, size, offset, name): 59 self.addr = addr 60 self.size = size 61 self.offset = offset 62 self.name = name 63 64 def __eq__(self, other): 65 return self.addr == other.addr 66 67 68first_addr = 0 69symtab_list = [] 70 71 72def sanitize_func_name(name): 73 pattern = r'(^[a-zA-Z_][a-zA-Z0-9_]*)' 74 match = re.match(pattern, name) 75 if match: 76 return match.group(0) 77 else: 78 log.error(f"Failed to sanitize function name: {name}") 79 80 return name 81 82 83def main(): 84 args = parse_args() 85 log.set_debug(args.debug) 86 87 with open(args.kernel, "rb") as rf: 88 elf = ELFFile(rf) 89 90 # Find the symbol table. 91 symtab = elf.get_section_by_name('.symtab') 92 93 for symbol in symtab.iter_symbols(): 94 symbol_type = describe_symbol_type(symbol['st_info']['type']) 95 symbol_addr = symbol['st_value'] 96 symbol_size = symbol['st_size'] 97 98 if symbol_type == 'FUNC' and symbol_addr != 0: 99 symbol_name = sanitize_func_name(symbol.name) 100 dummy_offset = 0 # offsets will be calculated later after we know the first address 101 entry = symtab_entry(symbol_addr, symbol_size, dummy_offset, symbol_name) 102 # Prevent entries with duplicated addresses 103 if entry not in symtab_list: 104 symtab_list.append(entry) 105 106 # Sort the address in ascending order 107 symtab_list.sort(key=lambda x: x.addr, reverse=False) 108 109 # Get the address of the first symbol 110 first_addr = symtab_list[0].addr 111 112 for i, entry in enumerate(symtab_list): 113 # Offset is calculated here 114 entry.offset = entry.addr - first_addr 115 116 # Debug print 117 log.debug(f'{i:6d}: {hex(entry.addr)} {hex(entry.size)} {entry.name:.25s}') 118 119 with open(args.output, 'w') as wf: 120 print("/* AUTO-GENERATED by gen_symtab.py, do not edit! */", file=wf) 121 print("", file=wf) 122 print("#include <zephyr/linker/sections.h>", file=wf) 123 print("#include <zephyr/debug/symtab.h>", file=wf) 124 print("", file=wf) 125 num = len(symtab_list) + 1 126 print( 127 f"const struct z_symtab_entry __symtab_entry z_symtab_entries[{num}] = {{", 128 file=wf, 129 ) 130 for i, entry in enumerate(symtab_list): 131 print(f"\t/* ADDR: {hex(entry.addr)} SIZE: {hex(entry.size)} */", file=wf) 132 print( 133 f"\t[{i}] = {{.offset = {hex(entry.offset)}, .name = \"{entry.name}\"}},", file=wf 134 ) 135 136 # Append a dummy entry at the end to facilitate the binary search 137 if symtab_list[-1].size == 0: 138 dummy_offset = f"{hex(symtab_list[-1].offset)} + sizeof(uintptr_t)" 139 else: 140 dummy_offset = f"{hex(symtab_list[-1].offset + symtab_list[-1].size)}" 141 print("\t/* dummy entry */", file=wf) 142 print(f"\t[{len(symtab_list)}] = {{.offset = {dummy_offset}, .name = \"?\"}},", file=wf) 143 print("};\n", file=wf) 144 145 print("const struct symtab_info __symtab_info z_symtab = {", file=wf) 146 print(f"\t.first_addr = {hex(first_addr)},", file=wf) 147 print(f"\t.length = {len(symtab_list)},", file=wf) 148 print("\t.entries = z_symtab_entries,", file=wf) 149 print("};\n", file=wf) 150 151 152if __name__ == "__main__": 153 main() 154