nuttx/tools/mkallsyms.py
Marco Casaroli 1f166a970e tools/mkallsyms.py: let the dependency error actually reach the user
mkallsyms.py prints "Please execute the following command to install
dependencies: pip install pyelftools cxxfilt" and then calls
os._exit(errno.EINVAL).  os._exit() terminates without flushing stdio, so
when stdout is a pipe -- which it always is under make -- the message sits in
the buffer and is discarded.  What the developer sees is:

  make[1]: *** [Makefile:65: nuttx] Error 22

with no indication of the cause anywhere in the build output.  Error 22 is
just errno.EINVAL leaking out as an exit status.  The same applies to
usage(), which exits ENOENT the same way.

Use sys.exit() in both places, which raises SystemExit and lets the
interpreter flush on the way out.  Nothing else changes:  the exit statuses
are the same, and os is no longer referenced, so the import goes too.

Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>

Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 01:37:36 +08:00

160 lines
5.4 KiB
Python
Executable file

#!/usr/bin/env python3
############################################################################
# tools/mkallsyms.py
#
# SPDX-License-Identifier: Apache-2.0
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership. The
# ASF licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the
# License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
############################################################################
import argparse
import errno
import re
import sys
try:
import cxxfilt
from elftools import __version__
from elftools.elf.elffile import ELFFile
from elftools.elf.sections import SymbolTableSection
except ModuleNotFoundError:
print("Please execute the following command to install dependencies:")
print("pip install pyelftools cxxfilt")
sys.exit(errno.EINVAL)
class SymbolTables(object):
def __init__(self, elffile, output):
try:
file = open(elffile, "rb")
self.elffile = ELFFile(file)
except FileNotFoundError:
self.elffile = None
self.output = output
self.symbol_list = []
def symbol_filter(self, symbol):
if symbol["st_info"]["type"] != "STT_FUNC":
return None
if symbol["st_info"]["bind"] == "STB_WEAK":
return None
if symbol["st_shndx"] == "SHN_UNDEF":
return None
return symbol
def print_symbol_tables(self, isnoconst=False):
noconst = "const"
if not isnoconst:
noconst = ""
self.emitline("#include <nuttx/compiler.h>")
self.emitline("#include <nuttx/symtab.h>\n")
self.emitline("extern int g_nallsyms;\n")
self.emitline(
"extern struct symtab_s g_allsyms[%d + 2];\n" % len(self.symbol_list)
)
self.emitline("%s int g_nallsyms = %d + 2;" % (noconst, len(self.symbol_list)))
self.emitline(
"%s struct symtab_s g_allsyms[%d + 2] =\n{"
% (noconst, len(self.symbol_list))
)
self.emitline(' { "Unknown", (FAR %s void *)0x00000000 },' % (noconst))
for symbol in self.symbol_list:
self.emitline(
' { "%s", (FAR %s void *)%s },' % (symbol[1], noconst, hex(symbol[0]))
)
self.emitline(' { "Unknown", (FAR %s void *)0xffffffff }\n};' % (noconst))
def get_symtable(self):
symbol_tables = [
(idx, s)
for idx, s in enumerate(self.elffile.iter_sections())
if isinstance(s, SymbolTableSection)
]
if not symbol_tables and self.elffile.num_sections() == 0:
self.emitline("")
return
for section_index, section in symbol_tables:
if not isinstance(section, SymbolTableSection):
continue
if section["sh_entsize"] == 0 or section.name != ".symtab":
continue
return section
def parse_symbol(self, orderbyname=False):
if self.elffile is None:
return
symtable = self.get_symtable()
for nsym, symbol in enumerate(symtable.iter_symbols()):
if self.symbol_filter(symbol) is not None:
try:
symbol_name = cxxfilt.demangle(symbol.name)
func_name = re.sub(r"\(.*$", "", symbol_name)
except cxxfilt.InvalidName:
symbol_name = symbol.name
self.symbol_list.append((symbol["st_value"], func_name))
if orderbyname:
self.symbol_list = sorted(self.symbol_list, key=lambda item: item[1])
else:
self.symbol_list = sorted(self.symbol_list, key=lambda item: item[0])
def emitline(self, s=""):
self.output.write(str(s) + "\n")
def usage():
print(
"Usage: mkallsyms.py [noconst] <ELFBIN> [output file] [order symbols by name]"
)
sys.exit(errno.ENOENT)
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Process ELF binary to extract symbols."
)
parser.add_argument("elffile", help="Path to the ELF binary file.")
parser.add_argument(
"outfile",
nargs="?",
type=argparse.FileType("w"),
default=sys.stdout,
help="Output file to write symbols to (default: stdout).",
)
parser.add_argument("--noconst", action="store_true", help="Exclude const symbols.")
parser.add_argument(
"--version",
action="version",
version="mkallsyms.py: based on pyelftools %s" % __version__,
)
parser.add_argument(
"--orderbyname",
nargs="?",
const=False,
default=False,
help='Order symbols by name (specify "y" to enable, default: False).',
)
args = parser.parse_args()
readelf = SymbolTables(args.elffile, args.outfile)
readelf.parse_symbol(args.orderbyname)
readelf.print_symbol_tables(args.noconst)