Re: [PATCH v2 01/19] tools: Add gendwarfksyms

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, Aug 16, 2024 at 2:39 AM Sami Tolvanen <samitolvanen@xxxxxxxxxx> wrote:
>
> Add a basic DWARF parser, which uses libdw to traverse the debugging
> information in an object file and looks for functions and variables.
> In follow-up patches, this will be expanded to produce symbol versions
> for CONFIG_MODVERSIONS from DWARF.



I do not think it would make sense to split the patch
until this tool starts to do something useful.






>
> Signed-off-by: Sami Tolvanen <samitolvanen@xxxxxxxxxx>
> ---
>  kernel/module/Kconfig                 |   8 ++
>  scripts/Makefile                      |   1 +
>  scripts/gendwarfksyms/.gitignore      |   2 +
>  scripts/gendwarfksyms/Makefile        |   7 ++
>  scripts/gendwarfksyms/dwarf.c         |  87 +++++++++++++++
>  scripts/gendwarfksyms/gendwarfksyms.c | 146 ++++++++++++++++++++++++++
>  scripts/gendwarfksyms/gendwarfksyms.h |  78 ++++++++++++++
>  7 files changed, 329 insertions(+)
>  create mode 100644 scripts/gendwarfksyms/.gitignore
>  create mode 100644 scripts/gendwarfksyms/Makefile
>  create mode 100644 scripts/gendwarfksyms/dwarf.c
>  create mode 100644 scripts/gendwarfksyms/gendwarfksyms.c
>  create mode 100644 scripts/gendwarfksyms/gendwarfksyms.h
>
> diff --git a/kernel/module/Kconfig b/kernel/module/Kconfig
> index 4047b6d48255..a506d4ac660f 100644
> --- a/kernel/module/Kconfig
> +++ b/kernel/module/Kconfig
> @@ -168,6 +168,14 @@ config MODVERSIONS
>           make them incompatible with the kernel you are running.  If
>           unsure, say N.
>
> +config GENDWARFKSYMS
> +       bool
> +       depends on DEBUG_INFO
> +       # Requires full debugging information, split DWARF not supported.
> +       depends on !DEBUG_INFO_REDUCED && !DEBUG_INFO_SPLIT
> +       # Requires ELF object files.
> +       depends on !LTO
> +
>  config ASM_MODVERSIONS
>         bool
>         default HAVE_ASM_MODVERSIONS && MODVERSIONS
> diff --git a/scripts/Makefile b/scripts/Makefile
> index dccef663ca82..2fd0199662e9 100644
> --- a/scripts/Makefile
> +++ b/scripts/Makefile
> @@ -54,6 +54,7 @@ targets += module.lds
>
>  subdir-$(CONFIG_GCC_PLUGINS) += gcc-plugins
>  subdir-$(CONFIG_MODVERSIONS) += genksyms
> +subdir-$(CONFIG_GENDWARFKSYMS) += gendwarfksyms
>  subdir-$(CONFIG_SECURITY_SELINUX) += selinux
>
>  # Let clean descend into subdirs
> diff --git a/scripts/gendwarfksyms/.gitignore b/scripts/gendwarfksyms/.gitignore
> new file mode 100644
> index 000000000000..ab8c763b3afe
> --- /dev/null
> +++ b/scripts/gendwarfksyms/.gitignore
> @@ -0,0 +1,2 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +/gendwarfksyms
> diff --git a/scripts/gendwarfksyms/Makefile b/scripts/gendwarfksyms/Makefile
> new file mode 100644
> index 000000000000..c1389c161f9c
> --- /dev/null
> +++ b/scripts/gendwarfksyms/Makefile
> @@ -0,0 +1,7 @@
> +hostprogs-always-y += gendwarfksyms
> +
> +gendwarfksyms-objs += gendwarfksyms.o
> +gendwarfksyms-objs += dwarf.o
> +
> +HOST_EXTRACFLAGS := -I $(srctree)/tools/include
> +HOSTLDLIBS_gendwarfksyms := -ldw -lelf
> diff --git a/scripts/gendwarfksyms/dwarf.c b/scripts/gendwarfksyms/dwarf.c
> new file mode 100644
> index 000000000000..65a29d0bd8f4
> --- /dev/null
> +++ b/scripts/gendwarfksyms/dwarf.c
> @@ -0,0 +1,87 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (C) 2024 Google LLC
> + */
> +
> +#include "gendwarfksyms.h"
> +
> +/*
> + * Type string processing
> + */
> +static int process(struct state *state, const char *s)
> +{
> +       s = s ?: "<null>";
> +
> +       if (debug)
> +               fputs(s, stderr);
> +
> +       return 0;
> +}
> +
> +bool match_all(Dwarf_Die *die)
> +{
> +       return true;
> +}
> +
> +int process_die_container(struct state *state, Dwarf_Die *die,
> +                         die_callback_t func, die_match_callback_t match)
> +{
> +       Dwarf_Die current;
> +       int res;
> +
> +       res = checkp(dwarf_child(die, &current));
> +       while (!res) {
> +               if (match(&current))
> +                       check(func(state, &current));
> +               res = checkp(dwarf_siblingof(&current, &current));
> +       }
> +
> +       return 0;
> +}
> +
> +/*
> + * Symbol processing
> + */
> +static int process_subprogram(struct state *state, Dwarf_Die *die)
> +{
> +       return check(process(state, "subprogram;\n"));
> +}
> +
> +static int process_variable(struct state *state, Dwarf_Die *die)
> +{
> +       return check(process(state, "variable;\n"));
> +}
> +
> +static int process_exported_symbols(struct state *state, Dwarf_Die *die)
> +{
> +       int tag = dwarf_tag(die);
> +
> +       switch (tag) {
> +       /* Possible containers of exported symbols */
> +       case DW_TAG_namespace:
> +       case DW_TAG_class_type:
> +       case DW_TAG_structure_type:
> +               return check(process_die_container(
> +                       state, die, process_exported_symbols, match_all));
> +
> +       /* Possible exported symbols */
> +       case DW_TAG_subprogram:
> +       case DW_TAG_variable:
> +               if (tag == DW_TAG_subprogram)
> +                       check(process_subprogram(state, die));
> +               else
> +                       check(process_variable(state, die));
> +
> +               return 0;
> +       default:
> +               return 0;
> +       }
> +}
> +
> +int process_module(Dwfl_Module *mod, Dwarf *dbg, Dwarf_Die *cudie)
> +{
> +       struct state state = { .mod = mod, .dbg = dbg };
> +
> +       return check(process_die_container(
> +               &state, cudie, process_exported_symbols, match_all));
> +}
> diff --git a/scripts/gendwarfksyms/gendwarfksyms.c b/scripts/gendwarfksyms/gendwarfksyms.c
> new file mode 100644
> index 000000000000..27f2d6423c45
> --- /dev/null
> +++ b/scripts/gendwarfksyms/gendwarfksyms.c
> @@ -0,0 +1,146 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (C) 2024 Google LLC
> + */
> +
> +#include <fcntl.h>
> +#include <errno.h>
> +#include <stdarg.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include "gendwarfksyms.h"
> +
> +/*
> + * Options
> + */
> +
> +/* Print out debugging information to stderr */
> +bool debug;
> +
> +static const struct {
> +       const char *arg;
> +       bool *flag;
> +       const char **param;
> +} options[] = {
> +       { "--debug", &debug, NULL },
> +};
> +
> +static int usage(void)
> +{
> +       error("usage: gendwarfksyms [options] elf-object-file ...");



Description for each option, please.

See
scripts/genksyms/genksyms.c
as an example.


> +       return -1;
> +}
> +
> +static const char *object_files[MAX_INPUT_FILES];
> +static unsigned int object_count;
> +
> +static int parse_options(int argc, const char **argv)



Why not getopt_long()?


See
scripts/kallsyms.c
scripts/genksyms/genksyms.c
as examples.









> +{
> +       for (int i = 1; i < argc; i++) {
> +               bool flag = false;
> +
> +               for (int j = 0; j < ARRAY_SIZE(options); j++) {
> +                       if (strcmp(argv[i], options[j].arg))
> +                               continue;
> +
> +                       *options[j].flag = true;
> +
> +                       if (options[j].param) {
> +                               if (++i >= argc) {
> +                                       error("%s needs an argument",
> +                                             options[j].arg);
> +                                       return -1;
> +                               }
> +
> +                               *options[j].param = argv[i];
> +                       }
> +
> +                       flag = true;
> +                       break;
> +               }
> +
> +               if (!flag)
> +                       object_files[object_count++] = argv[i];
> +       }
> +
> +       return object_count ? 0 : -1;
> +}
> +
> +static int process_modules(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;
> +       int res;
> +
> +       debug("%s", name);
> +       dbg = dwfl_module_getdwarf(mod, &dwbias);
> +
> +       do {
> +               res = dwarf_get_units(dbg, cu, &cu, NULL, NULL, &cudie, NULL);
> +               if (res < 0) {
> +                       error("dwarf_get_units failed: no debugging information?");
> +                       return -1;
> +               } else if (res == 1) {
> +                       break; /* No more units */
> +               }
> +
> +               check(process_module(mod, dbg, &cudie));
> +       } while (cu);
> +
> +       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, const char **argv)
> +{
> +       unsigned int n;
> +
> +       if (parse_options(argc, argv) < 0)
> +               return usage();
> +
> +       for (n = 0; n < object_count; n++) {


When does  "object_count >= 2" happen ?










> +               Dwfl *dwfl;
> +               int fd;
> +
> +               fd = open(object_files[n], O_RDONLY);
> +               if (fd == -1) {
> +                       error("open failed for '%s': %s", object_files[n],
> +                             strerror(errno));
> +                       return -1;
> +               }
> +
> +               dwfl = dwfl_begin(&callbacks);
> +               if (!dwfl) {
> +                       error("dwfl_begin failed for '%s': %s", object_files[n],
> +                             dwarf_errmsg(-1));
> +                       return -1;
> +               }
> +
> +               if (!dwfl_report_offline(dwfl, object_files[n], object_files[n],
> +                                        fd)) {
> +                       error("dwfl_report_offline failed for '%s': %s",
> +                             object_files[n], dwarf_errmsg(-1));
> +                       return -1;
> +               }
> +
> +               dwfl_report_end(dwfl, NULL, NULL);
> +
> +               if (dwfl_getmodules(dwfl, &process_modules, NULL, 0)) {
> +                       error("dwfl_getmodules failed for '%s'",
> +                             object_files[n]);
> +                       return -1;
> +               }
> +
> +               dwfl_end(dwfl);
> +               close(fd);
> +       }
> +
> +       return 0;
> +}
> diff --git a/scripts/gendwarfksyms/gendwarfksyms.h b/scripts/gendwarfksyms/gendwarfksyms.h
> new file mode 100644
> index 000000000000..5ab7ce7d4efb
> --- /dev/null
> +++ b/scripts/gendwarfksyms/gendwarfksyms.h
> @@ -0,0 +1,78 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (C) 2024 Google LLC
> + */
> +
> +#include <dwarf.h>
> +#include <elfutils/libdw.h>
> +#include <elfutils/libdwfl.h>
> +#include <linux/hashtable.h>
> +#include <inttypes.h>
> +#include <stdlib.h>
> +#include <stdio.h>
> +
> +#ifndef __GENDWARFKSYMS_H
> +#define __GENDWARFKSYMS_H
> +
> +/*
> + * Options -- in gendwarfksyms.c
> + */
> +extern bool debug;
> +
> +#define MAX_INPUT_FILES 128
> +
> +/*
> + * Output helpers
> + */
> +#define __PREFIX "gendwarfksyms: "
> +#define __println(prefix, format, ...)                                \
> +       fprintf(stderr, prefix __PREFIX "%s: " format "\n", __func__, \
> +               ##__VA_ARGS__)
> +
> +#define debug(format, ...)                                    \
> +       do {                                                  \
> +               if (debug)                                    \
> +                       __println("", format, ##__VA_ARGS__); \
> +       } while (0)
> +
> +#define warn(format, ...) __println("warning: ", format, ##__VA_ARGS__)
> +#define error(format, ...) __println("error: ", format, ##__VA_ARGS__)
> +
> +/*
> + * Error handling helpers
> + */
> +#define __check(expr, test, rv)                                 \
> +       ({                                                      \
> +               int __res = expr;                               \
> +               if (test) {                                     \
> +                       error("`%s` failed: %d", #expr, __res); \
> +                       return rv;                              \
> +               }                                               \
> +               __res;                                          \
> +       })
> +
> +/* Error == non-zero values */
> +#define check(expr) __check(expr, __res, -1)
> +/* Error == negative values */
> +#define checkp(expr) __check(expr, __res < 0, __res)
> +
> +/*
> + * dwarf.c
> + */
> +
> +struct state {
> +       Dwfl_Module *mod;
> +       Dwarf *dbg;
> +};
> +
> +typedef int (*die_callback_t)(struct state *state, Dwarf_Die *die);
> +typedef bool (*die_match_callback_t)(Dwarf_Die *die);
> +extern bool match_all(Dwarf_Die *die);
> +
> +extern int process_die_container(struct state *state, Dwarf_Die *die,
> +                                die_callback_t func,
> +                                die_match_callback_t match);
> +
> +extern int process_module(Dwfl_Module *mod, Dwarf *dbg, Dwarf_Die *cudie);


No 'extern' for function declarations.





> +
> +#endif /* __GENDWARFKSYMS_H */
> --
> 2.46.0.184.g6999bdac58-goog
>


--
Best Regards

Masahiro Yamada





[Index of Archives]     [Linux&nblp;USB Development]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite Secrets]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux