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, ¤t)); > + while (!res) { > + if (match(¤t)) > + check(func(state, ¤t)); > + res = checkp(dwarf_siblingof(¤t, ¤t)); > + } > + > + 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