debug: implement symtab generation
Use pyelftools to extract the symbol table from the link stage executable. Then, filter out the function names and sort them based on their offsets before writing into the `symtab.c`, this is similar to how the `isr_tables` works. To access the structure, simply include the new header: ```c #include <zephyr/debug/symtab.h> ``` Signed-off-by: Yong Cong Sin <ycsin@meta.com>
This commit is contained in:
parent
0af906a533
commit
e1ce0aefff
9 changed files with 276 additions and 0 deletions
|
@ -1297,6 +1297,20 @@ if(CONFIG_GEN_ISR_TABLES)
|
||||||
set_property(GLOBAL APPEND PROPERTY GENERATED_KERNEL_SOURCE_FILES isr_tables.c)
|
set_property(GLOBAL APPEND PROPERTY GENERATED_KERNEL_SOURCE_FILES isr_tables.c)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(CONFIG_SYMTAB)
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT symtab.c
|
||||||
|
COMMAND
|
||||||
|
${PYTHON_EXECUTABLE}
|
||||||
|
${ZEPHYR_BASE}/scripts/build/gen_symtab.py
|
||||||
|
-k $<TARGET_FILE:${ZEPHYR_LINK_STAGE_EXECUTABLE}>
|
||||||
|
-o symtab.c
|
||||||
|
DEPENDS ${ZEPHYR_LINK_STAGE_EXECUTABLE}
|
||||||
|
COMMAND_EXPAND_LISTS
|
||||||
|
)
|
||||||
|
set_property(GLOBAL APPEND PROPERTY GENERATED_KERNEL_SOURCE_FILES symtab.c)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(CONFIG_USERSPACE)
|
if(CONFIG_USERSPACE)
|
||||||
set(KOBJECT_HASH_LIST kobject_hash.gperf)
|
set(KOBJECT_HASH_LIST kobject_hash.gperf)
|
||||||
set(KOBJECT_HASH_OUTPUT_SRC_PRE kobject_hash_preprocessed.c)
|
set(KOBJECT_HASH_OUTPUT_SRC_PRE kobject_hash_preprocessed.c)
|
||||||
|
|
|
@ -323,6 +323,13 @@ Devicetree
|
||||||
Libraries / Subsystems
|
Libraries / Subsystems
|
||||||
**********************
|
**********************
|
||||||
|
|
||||||
|
* Debug
|
||||||
|
|
||||||
|
* symtab
|
||||||
|
|
||||||
|
* By enabling :kconfig:option:`CONFIG_SYMTAB`, the symbol table will be
|
||||||
|
generated with Zephyr link stage executable on supported architectures.
|
||||||
|
|
||||||
* Management
|
* Management
|
||||||
|
|
||||||
* hawkBit
|
* hawkBit
|
||||||
|
|
52
include/zephyr/debug/symtab.h
Normal file
52
include/zephyr/debug/symtab.h
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Meta Platforms
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ZEPHYR_INCLUDE_DEBUG_SYMTAB_H_
|
||||||
|
#define ZEPHYR_INCLUDE_DEBUG_SYMTAB_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @cond INTERNAL_HIDDEN
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct z_symtab_entry {
|
||||||
|
const uint32_t offset;
|
||||||
|
const char *const name;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* INTERNAL_HIDDEN @endcond
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct symtab_info {
|
||||||
|
/* Absolute address of the first symbol */
|
||||||
|
const uintptr_t start_addr;
|
||||||
|
/* Number of symbol entries */
|
||||||
|
const uint32_t length;
|
||||||
|
/* Symbol entries */
|
||||||
|
const struct z_symtab_entry *const entries;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the pointer to the symbol table.
|
||||||
|
*
|
||||||
|
* @return Pointer to the symbol table.
|
||||||
|
*/
|
||||||
|
const struct symtab_info *const symtab_get(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Find the symbol name with a binary search
|
||||||
|
*
|
||||||
|
* @param[in] addr Address of the symbol to find
|
||||||
|
* @param[out] offset Offset of the symbol from the nearest symbol. If the symbol can't be found,
|
||||||
|
* this will be 0.
|
||||||
|
*
|
||||||
|
* @return Name of the nearest symbol if found, otherwise "?" is returned.
|
||||||
|
*/
|
||||||
|
const char *const symtab_find_symbol_name(uintptr_t addr, uint32_t *offset);
|
||||||
|
|
||||||
|
#endif /* ZEPHYR_INCLUDE_DEBUG_SYMTAB_H_ */
|
119
scripts/build/gen_symtab.py
Normal file
119
scripts/build/gen_symtab.py
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# Copyright (c) 2024 Meta Platforms
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
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, offset, name):
|
||||||
|
self.addr = addr
|
||||||
|
self.offset = offset
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
|
||||||
|
start_addr = 0
|
||||||
|
symtab_list = []
|
||||||
|
|
||||||
|
|
||||||
|
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']
|
||||||
|
|
||||||
|
if symbol_type == 'FUNC' and symbol_addr != 0:
|
||||||
|
symtab_list.append(symtab_entry(
|
||||||
|
symbol_addr, symbol_addr, symbol.name))
|
||||||
|
log.debug('%6d: %s %.25s' % (
|
||||||
|
i,
|
||||||
|
hex(symbol_addr),
|
||||||
|
symbol.name))
|
||||||
|
i = i + 1
|
||||||
|
|
||||||
|
symtab_list.sort(key=lambda x: x.addr, reverse=False)
|
||||||
|
|
||||||
|
# Get the address of the first symbol
|
||||||
|
start_addr = symtab_list[0].addr
|
||||||
|
# Use that to calculate the offset of other symbols relative to the first one
|
||||||
|
for i, entry in enumerate(symtab_list):
|
||||||
|
entry.offset = entry.addr - start_addr
|
||||||
|
|
||||||
|
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/debug/symtab.h>", file=wf)
|
||||||
|
print("", file=wf)
|
||||||
|
print(
|
||||||
|
f"const struct z_symtab_entry z_symtab_entries[{len(symtab_list)}] = {{", file=wf)
|
||||||
|
for i, entry in enumerate(symtab_list):
|
||||||
|
print(
|
||||||
|
f"\t[{i}] = {{.offset = {hex(entry.offset)}, .name = \"{entry.name}\"}}, /* {hex(entry.addr)} */", file=wf)
|
||||||
|
print(f"}};\n", file=wf)
|
||||||
|
|
||||||
|
print(f"const struct symtab_info z_symtab = {{", file=wf)
|
||||||
|
print(f"\t.start_addr = {hex(start_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()
|
|
@ -34,3 +34,8 @@ zephyr_sources_ifdef(
|
||||||
CONFIG_MIPI_STP_DECODER
|
CONFIG_MIPI_STP_DECODER
|
||||||
mipi_stp_decoder.c
|
mipi_stp_decoder.c
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_subdirectory_ifdef(
|
||||||
|
CONFIG_SYMTAB
|
||||||
|
symtab
|
||||||
|
)
|
||||||
|
|
|
@ -368,6 +368,16 @@ config DEBUG_INFO
|
||||||
used by debuggers in debugging the system, or enable additional
|
used by debuggers in debugging the system, or enable additional
|
||||||
debugging information to be reported at runtime.
|
debugging information to be reported at runtime.
|
||||||
|
|
||||||
|
config SYMTAB
|
||||||
|
bool "Generate symbol table"
|
||||||
|
help
|
||||||
|
Generate the symbol table with the offset and name of every
|
||||||
|
function.
|
||||||
|
The symbol table can be accessed by including the
|
||||||
|
<zephyr/debug/symtab.h> header.
|
||||||
|
|
||||||
|
Choose N if you have no idea what is this.
|
||||||
|
|
||||||
config EXCEPTION_STACK_TRACE
|
config EXCEPTION_STACK_TRACE
|
||||||
bool "Attempt to print stack traces upon exceptions"
|
bool "Attempt to print stack traces upon exceptions"
|
||||||
default y
|
default y
|
||||||
|
|
9
subsys/debug/symtab/CMakeLists.txt
Normal file
9
subsys/debug/symtab/CMakeLists.txt
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
add_library(symtab
|
||||||
|
symtab.c
|
||||||
|
symtab_stub.c
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(symtab zephyr_interface)
|
||||||
|
zephyr_library_link_libraries(symtab)
|
46
subsys/debug/symtab/symtab.c
Normal file
46
subsys/debug/symtab/symtab.c
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Meta Platforms
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <zephyr/debug/symtab.h>
|
||||||
|
|
||||||
|
const struct symtab_info *const symtab_get(void)
|
||||||
|
{
|
||||||
|
extern const struct symtab_info z_symtab;
|
||||||
|
|
||||||
|
return &z_symtab;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *const symtab_find_symbol_name(uintptr_t addr, uint32_t *offset)
|
||||||
|
{
|
||||||
|
const struct symtab_info *const symtab = symtab_get();
|
||||||
|
const uint32_t symbol_offset = addr - symtab->start_addr;
|
||||||
|
uint32_t left = 0, right = symtab->length - 1;
|
||||||
|
uint32_t ret_offset = 0;
|
||||||
|
const char *ret_name = "?";
|
||||||
|
|
||||||
|
while (left <= right) {
|
||||||
|
uint32_t mid = left + (right - left) / 2;
|
||||||
|
|
||||||
|
if ((symbol_offset >= symtab->entries[mid].offset) &&
|
||||||
|
(symbol_offset < symtab->entries[mid + 1].offset)) {
|
||||||
|
ret_offset = symbol_offset - symtab->entries[mid].offset;
|
||||||
|
ret_name = symtab->entries[mid].name;
|
||||||
|
break;
|
||||||
|
} else if (symbol_offset < symtab->entries[mid].offset) {
|
||||||
|
right = mid - 1;
|
||||||
|
} else {
|
||||||
|
left = mid + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset != NULL) {
|
||||||
|
*offset = ret_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret_name;
|
||||||
|
}
|
14
subsys/debug/symtab/symtab_stub.c
Normal file
14
subsys/debug/symtab/symtab_stub.c
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2024 Meta Platforms
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr/debug/symtab.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These are placeholder variables. They will be replaced by the real ones
|
||||||
|
* generated by `gen_symtab.py`.
|
||||||
|
*/
|
||||||
|
const struct z_symtab_entry *z_symtab_entries;
|
||||||
|
const struct symtab_info z_symtab;
|
Loading…
Add table
Add a link
Reference in a new issue