77f0bea7创建于 4月21日历史提交
#!/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 os
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")
    os._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]"
    )
    os._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)