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