root/scripts/gendwarfksyms/gendwarfksyms.c
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2024 Google LLC
 */

#include <fcntl.h>
#include <getopt.h>
#include <errno.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include "gendwarfksyms.h"

/*
 * Options
 */

/* Print debugging information to stderr */
int debug;
/* Dump DIE contents */
int dump_dies;
/* Print debugging information about die_map changes */
int dump_die_map;
/* Print out type strings (i.e. type_map) */
int dump_types;
/* Print out expanded type strings used for symbol versions */
int dump_versions;
/* Support kABI stability features */
int stable;
/* Write a symtypes file */
int symtypes;
static const char *symtypes_file;

static void usage(void)
{
        fputs("Usage: gendwarfksyms [options] elf-object-file ... < symbol-list\n\n"
              "Options:\n"
              "  -d, --debug          Print debugging information\n"
              "      --dump-dies      Dump DWARF DIE contents\n"
              "      --dump-die-map   Print debugging information about die_map changes\n"
              "      --dump-types     Dump type strings\n"
              "      --dump-versions  Dump expanded type strings used for symbol versions\n"
              "  -s, --stable         Support kABI stability features\n"
              "  -T, --symtypes file  Write a symtypes file\n"
              "  -h, --help           Print this message\n"
              "\n",
              stderr);
}

static int process_module(Dwfl_Module *mod, void **userdata, const char *name,
                          Dwarf_Addr base, void *arg)
{
        Dwarf_Addr dwbias;
        Dwarf_Die cudie;
        Dwarf_CU *cu = NULL;
        Dwarf *dbg;
        FILE *symfile = arg;
        int res;

        debug("%s", name);
        dbg = dwfl_module_getdwarf(mod, &dwbias);

        /*
         * Look for exported symbols in each CU, follow the DIE tree, and add
         * the entries to die_map.
         */
        do {
                res = dwarf_get_units(dbg, cu, &cu, NULL, NULL, &cudie, NULL);
                if (res < 0)
                        error("dwarf_get_units failed: no debugging information?");
                if (res == 1)
                        break; /* No more units */

                process_cu(&cudie);
        } while (cu);

        /*
         * Use die_map to expand type strings, write them to `symfile`, and
         * calculate symbol versions.
         */
        generate_symtypes_and_versions(symfile);
        die_map_free();

        return DWARF_CB_OK;
}

static const Dwfl_Callbacks callbacks = {
        .section_address = dwfl_offline_section_address,
        .find_debuginfo = dwfl_standard_find_debuginfo,
};

int main(int argc, char **argv)
{
        FILE *symfile = NULL;
        unsigned int n;
        int opt;

        static const struct option opts[] = {
                { "debug", 0, NULL, 'd' },
                { "dump-dies", 0, &dump_dies, 1 },
                { "dump-die-map", 0, &dump_die_map, 1 },
                { "dump-types", 0, &dump_types, 1 },
                { "dump-versions", 0, &dump_versions, 1 },
                { "stable", 0, NULL, 's' },
                { "symtypes", 1, NULL, 'T' },
                { "help", 0, NULL, 'h' },
                { 0, 0, NULL, 0 }
        };

        while ((opt = getopt_long(argc, argv, "dsT:h", opts, NULL)) != EOF) {
                switch (opt) {
                case 0:
                        break;
                case 'd':
                        debug = 1;
                        break;
                case 's':
                        stable = 1;
                        break;
                case 'T':
                        symtypes = 1;
                        symtypes_file = optarg;
                        break;
                case 'h':
                        usage();
                        return 0;
                default:
                        usage();
                        return 1;
                }
        }

        if (dump_die_map)
                dump_dies = 1;

        if (optind >= argc) {
                usage();
                error("no input files?");
        }

        if (!symbol_read_exports(stdin))
                return 0;

        if (symtypes_file) {
                symfile = fopen(symtypes_file, "w");
                if (!symfile)
                        error("fopen failed for '%s': %s", symtypes_file,
                              strerror(errno));
        }

        for (n = optind; n < argc; n++) {
                Dwfl *dwfl;
                int fd;

                fd = open(argv[n], O_RDONLY);
                if (fd == -1)
                        error("open failed for '%s': %s", argv[n],
                              strerror(errno));

                symbol_read_symtab(fd);
                kabi_read_rules(fd);

                dwfl = dwfl_begin(&callbacks);
                if (!dwfl)
                        error("dwfl_begin failed for '%s': %s", argv[n],
                              dwarf_errmsg(-1));

                if (!dwfl_report_offline(dwfl, argv[n], argv[n], fd))
                        error("dwfl_report_offline failed for '%s': %s",
                              argv[n], dwarf_errmsg(-1));

                dwfl_report_end(dwfl, NULL, NULL);

                if (dwfl_getmodules(dwfl, &process_module, symfile, 0))
                        error("dwfl_getmodules failed for '%s'", argv[n]);

                dwfl_end(dwfl);
                kabi_free();
        }

        if (symfile)
                check(fclose(symfile));

        symbol_print_versions();
        symbol_free();

        return 0;
}