[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <CAK7LNATKWnkdmxvPBaBLYThLk0Voh7UVh5V4_eKHs9g-40qUTQ@mail.gmail.com>
Date: Thu, 29 Aug 2024 02:45:03 +0900
From: Masahiro Yamada <masahiroy@...nel.org>
To: Sami Tolvanen <samitolvanen@...gle.com>
Cc: Luis Chamberlain <mcgrof@...nel.org>, Miguel Ojeda <ojeda@...nel.org>,
Greg Kroah-Hartman <gregkh@...uxfoundation.org>, Matthew Maurer <mmaurer@...gle.com>,
Alex Gaynor <alex.gaynor@...il.com>, Wedson Almeida Filho <wedsonaf@...il.com>, Gary Guo <gary@...yguo.net>,
Petr Pavlu <petr.pavlu@...e.com>, Neal Gompa <neal@...pa.dev>, Hector Martin <marcan@...can.st>,
Janne Grunau <j@...nau.net>, Asahi Linux <asahi@...ts.linux.dev>, linux-kbuild@...r.kernel.org,
linux-kernel@...r.kernel.org, linux-modules@...r.kernel.org,
rust-for-linux@...r.kernel.org
Subject: Re: [PATCH v2 01/19] tools: Add gendwarfksyms
On Fri, Aug 16, 2024 at 2:39 AM Sami Tolvanen <samitolvanen@...gle.com> 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@...gle.com>
> ---
> 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
Powered by blists - more mailing lists