Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tools: Add gendwarfksyms

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.

Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
Reviewed-by: Petr Pavlu <petr.pavlu@suse.com>
Signed-off-by: Masahiro Yamada <masahiroy@kernel.org>

authored by

Sami Tolvanen and committed by
Masahiro Yamada
f2856884 a56fece7

+513
+7
MAINTAINERS
··· 9550 9550 T: git git://linuxtv.org/media.git 9551 9551 F: drivers/media/radio/radio-gemtek* 9552 9552 9553 + GENDWARFKSYMS 9554 + M: Sami Tolvanen <samitolvanen@google.com> 9555 + L: linux-modules@vger.kernel.org 9556 + L: linux-kbuild@vger.kernel.org 9557 + S: Maintained 9558 + F: scripts/gendwarfksyms/ 9559 + 9553 9560 GENERIC ARCHITECTURE TOPOLOGY 9554 9561 M: Sudeep Holla <sudeep.holla@arm.com> 9555 9562 L: linux-kernel@vger.kernel.org
+8
kernel/module/Kconfig
··· 169 169 make them incompatible with the kernel you are running. If 170 170 unsure, say N. 171 171 172 + config GENDWARFKSYMS 173 + bool "gendwarfksyms (from debugging information)" 174 + depends on DEBUG_INFO 175 + # Requires full debugging information, split DWARF not supported. 176 + depends on !DEBUG_INFO_REDUCED && !DEBUG_INFO_SPLIT 177 + # Requires ELF object files. 178 + depends on !LTO 179 + 172 180 config ASM_MODVERSIONS 173 181 bool 174 182 default HAVE_ASM_MODVERSIONS && MODVERSIONS
+1
scripts/Makefile
··· 54 54 55 55 subdir-$(CONFIG_GCC_PLUGINS) += gcc-plugins 56 56 subdir-$(CONFIG_MODVERSIONS) += genksyms 57 + subdir-$(CONFIG_GENDWARFKSYMS) += gendwarfksyms 57 58 subdir-$(CONFIG_SECURITY_SELINUX) += selinux 58 59 subdir-$(CONFIG_SECURITY_IPE) += ipe 59 60
+2
scripts/gendwarfksyms/.gitignore
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + /gendwarfksyms
+8
scripts/gendwarfksyms/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + hostprogs-always-y += gendwarfksyms 3 + 4 + gendwarfksyms-objs += gendwarfksyms.o 5 + gendwarfksyms-objs += dwarf.o 6 + gendwarfksyms-objs += symbols.o 7 + 8 + HOSTLDLIBS_gendwarfksyms := -ldw -lelf
+166
scripts/gendwarfksyms/dwarf.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2024 Google LLC 4 + */ 5 + 6 + #include "gendwarfksyms.h" 7 + 8 + static bool get_ref_die_attr(Dwarf_Die *die, unsigned int id, Dwarf_Die *value) 9 + { 10 + Dwarf_Attribute da; 11 + 12 + /* dwarf_formref_die returns a pointer instead of an error value. */ 13 + return dwarf_attr(die, id, &da) && dwarf_formref_die(&da, value); 14 + } 15 + 16 + #define DEFINE_GET_STRING_ATTR(attr) \ 17 + static const char *get_##attr##_attr(Dwarf_Die *die) \ 18 + { \ 19 + Dwarf_Attribute da; \ 20 + if (dwarf_attr(die, DW_AT_##attr, &da)) \ 21 + return dwarf_formstring(&da); \ 22 + return NULL; \ 23 + } 24 + 25 + DEFINE_GET_STRING_ATTR(name) 26 + DEFINE_GET_STRING_ATTR(linkage_name) 27 + 28 + static const char *get_symbol_name(Dwarf_Die *die) 29 + { 30 + const char *name; 31 + 32 + /* rustc uses DW_AT_linkage_name for exported symbols */ 33 + name = get_linkage_name_attr(die); 34 + if (!name) 35 + name = get_name_attr(die); 36 + 37 + return name; 38 + } 39 + 40 + static bool match_export_symbol(struct state *state, Dwarf_Die *die) 41 + { 42 + Dwarf_Die *source = die; 43 + Dwarf_Die origin; 44 + 45 + /* If the DIE has an abstract origin, use it for type information. */ 46 + if (get_ref_die_attr(die, DW_AT_abstract_origin, &origin)) 47 + source = &origin; 48 + 49 + state->sym = symbol_get(get_symbol_name(die)); 50 + 51 + /* Look up using the origin name if there are no matches. */ 52 + if (!state->sym && source != die) 53 + state->sym = symbol_get(get_symbol_name(source)); 54 + 55 + state->die = *source; 56 + return !!state->sym; 57 + } 58 + 59 + /* 60 + * Type string processing 61 + */ 62 + static void process(const char *s) 63 + { 64 + s = s ?: "<null>"; 65 + 66 + if (dump_dies) 67 + fputs(s, stderr); 68 + } 69 + 70 + bool match_all(Dwarf_Die *die) 71 + { 72 + return true; 73 + } 74 + 75 + int process_die_container(struct state *state, Dwarf_Die *die, 76 + die_callback_t func, die_match_callback_t match) 77 + { 78 + Dwarf_Die current; 79 + int res; 80 + 81 + res = checkp(dwarf_child(die, &current)); 82 + while (!res) { 83 + if (match(&current)) { 84 + /* <0 = error, 0 = continue, >0 = stop */ 85 + res = checkp(func(state, &current)); 86 + if (res) 87 + return res; 88 + } 89 + 90 + res = checkp(dwarf_siblingof(&current, &current)); 91 + } 92 + 93 + return 0; 94 + } 95 + 96 + /* 97 + * Exported symbol processing 98 + */ 99 + static void process_symbol(struct state *state, Dwarf_Die *die, 100 + die_callback_t process_func) 101 + { 102 + debug("%s", state->sym->name); 103 + check(process_func(state, die)); 104 + if (dump_dies) 105 + fputs("\n", stderr); 106 + } 107 + 108 + static int __process_subprogram(struct state *state, Dwarf_Die *die) 109 + { 110 + process("subprogram"); 111 + return 0; 112 + } 113 + 114 + static void process_subprogram(struct state *state, Dwarf_Die *die) 115 + { 116 + process_symbol(state, die, __process_subprogram); 117 + } 118 + 119 + static int __process_variable(struct state *state, Dwarf_Die *die) 120 + { 121 + process("variable "); 122 + return 0; 123 + } 124 + 125 + static void process_variable(struct state *state, Dwarf_Die *die) 126 + { 127 + process_symbol(state, die, __process_variable); 128 + } 129 + 130 + static int process_exported_symbols(struct state *unused, Dwarf_Die *die) 131 + { 132 + int tag = dwarf_tag(die); 133 + 134 + switch (tag) { 135 + /* Possible containers of exported symbols */ 136 + case DW_TAG_namespace: 137 + case DW_TAG_class_type: 138 + case DW_TAG_structure_type: 139 + return check(process_die_container( 140 + NULL, die, process_exported_symbols, match_all)); 141 + 142 + /* Possible exported symbols */ 143 + case DW_TAG_subprogram: 144 + case DW_TAG_variable: { 145 + struct state state; 146 + 147 + if (!match_export_symbol(&state, die)) 148 + return 0; 149 + 150 + if (tag == DW_TAG_subprogram) 151 + process_subprogram(&state, &state.die); 152 + else 153 + process_variable(&state, &state.die); 154 + 155 + return 0; 156 + } 157 + default: 158 + return 0; 159 + } 160 + } 161 + 162 + void process_cu(Dwarf_Die *cudie) 163 + { 164 + check(process_die_container(NULL, cudie, process_exported_symbols, 165 + match_all)); 166 + }
+128
scripts/gendwarfksyms/gendwarfksyms.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2024 Google LLC 4 + */ 5 + 6 + #include <fcntl.h> 7 + #include <getopt.h> 8 + #include <errno.h> 9 + #include <stdarg.h> 10 + #include <string.h> 11 + #include <unistd.h> 12 + #include "gendwarfksyms.h" 13 + 14 + /* 15 + * Options 16 + */ 17 + 18 + /* Print debugging information to stderr */ 19 + int debug; 20 + /* Dump DIE contents */ 21 + int dump_dies; 22 + 23 + static void usage(void) 24 + { 25 + fputs("Usage: gendwarfksyms [options] elf-object-file ... < symbol-list\n\n" 26 + "Options:\n" 27 + " -d, --debug Print debugging information\n" 28 + " --dump-dies Dump DWARF DIE contents\n" 29 + " -h, --help Print this message\n" 30 + "\n", 31 + stderr); 32 + } 33 + 34 + static int process_module(Dwfl_Module *mod, void **userdata, const char *name, 35 + Dwarf_Addr base, void *arg) 36 + { 37 + Dwarf_Addr dwbias; 38 + Dwarf_Die cudie; 39 + Dwarf_CU *cu = NULL; 40 + Dwarf *dbg; 41 + int res; 42 + 43 + debug("%s", name); 44 + dbg = dwfl_module_getdwarf(mod, &dwbias); 45 + 46 + do { 47 + res = dwarf_get_units(dbg, cu, &cu, NULL, NULL, &cudie, NULL); 48 + if (res < 0) 49 + error("dwarf_get_units failed: no debugging information?"); 50 + if (res == 1) 51 + break; /* No more units */ 52 + 53 + process_cu(&cudie); 54 + } while (cu); 55 + 56 + return DWARF_CB_OK; 57 + } 58 + 59 + static const Dwfl_Callbacks callbacks = { 60 + .section_address = dwfl_offline_section_address, 61 + .find_debuginfo = dwfl_standard_find_debuginfo, 62 + }; 63 + 64 + int main(int argc, char **argv) 65 + { 66 + unsigned int n; 67 + int opt; 68 + 69 + static const struct option opts[] = { 70 + { "debug", 0, NULL, 'd' }, 71 + { "dump-dies", 0, &dump_dies, 1 }, 72 + { "help", 0, NULL, 'h' }, 73 + { 0, 0, NULL, 0 } 74 + }; 75 + 76 + while ((opt = getopt_long(argc, argv, "dh", opts, NULL)) != EOF) { 77 + switch (opt) { 78 + case 0: 79 + break; 80 + case 'd': 81 + debug = 1; 82 + break; 83 + case 'h': 84 + usage(); 85 + return 0; 86 + default: 87 + usage(); 88 + return 1; 89 + } 90 + } 91 + 92 + if (optind >= argc) { 93 + usage(); 94 + error("no input files?"); 95 + } 96 + 97 + symbol_read_exports(stdin); 98 + 99 + for (n = optind; n < argc; n++) { 100 + Dwfl *dwfl; 101 + int fd; 102 + 103 + fd = open(argv[n], O_RDONLY); 104 + if (fd == -1) 105 + error("open failed for '%s': %s", argv[n], 106 + strerror(errno)); 107 + 108 + dwfl = dwfl_begin(&callbacks); 109 + if (!dwfl) 110 + error("dwfl_begin failed for '%s': %s", argv[n], 111 + dwarf_errmsg(-1)); 112 + 113 + if (!dwfl_report_offline(dwfl, argv[n], argv[n], fd)) 114 + error("dwfl_report_offline failed for '%s': %s", 115 + argv[n], dwarf_errmsg(-1)); 116 + 117 + dwfl_report_end(dwfl, NULL, NULL); 118 + 119 + if (dwfl_getmodules(dwfl, &process_module, NULL, 0)) 120 + error("dwfl_getmodules failed for '%s'", argv[n]); 121 + 122 + dwfl_end(dwfl); 123 + } 124 + 125 + symbol_free(); 126 + 127 + return 0; 128 + }
+95
scripts/gendwarfksyms/gendwarfksyms.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Copyright (C) 2024 Google LLC 4 + */ 5 + 6 + #include <dwarf.h> 7 + #include <elfutils/libdw.h> 8 + #include <elfutils/libdwfl.h> 9 + #include <stdlib.h> 10 + #include <stdio.h> 11 + 12 + #include <hash.h> 13 + #include <hashtable.h> 14 + #include <xalloc.h> 15 + 16 + #ifndef __GENDWARFKSYMS_H 17 + #define __GENDWARFKSYMS_H 18 + 19 + /* 20 + * Options -- in gendwarfksyms.c 21 + */ 22 + extern int debug; 23 + extern int dump_dies; 24 + 25 + /* 26 + * Output helpers 27 + */ 28 + #define __PREFIX "gendwarfksyms: " 29 + #define __println(prefix, format, ...) \ 30 + fprintf(stderr, prefix __PREFIX "%s: " format "\n", __func__, \ 31 + ##__VA_ARGS__) 32 + 33 + #define debug(format, ...) \ 34 + do { \ 35 + if (debug) \ 36 + __println("", format, ##__VA_ARGS__); \ 37 + } while (0) 38 + 39 + #define warn(format, ...) __println("warning: ", format, ##__VA_ARGS__) 40 + #define error(format, ...) \ 41 + do { \ 42 + __println("error: ", format, ##__VA_ARGS__); \ 43 + exit(1); \ 44 + } while (0) 45 + 46 + /* 47 + * Error handling helpers 48 + */ 49 + #define __check(expr, test) \ 50 + ({ \ 51 + int __res = expr; \ 52 + if (test) \ 53 + error("`%s` failed: %d", #expr, __res); \ 54 + __res; \ 55 + }) 56 + 57 + /* Error == non-zero values */ 58 + #define check(expr) __check(expr, __res) 59 + /* Error == negative values */ 60 + #define checkp(expr) __check(expr, __res < 0) 61 + 62 + /* 63 + * symbols.c 64 + */ 65 + 66 + struct symbol { 67 + const char *name; 68 + struct hlist_node name_hash; 69 + }; 70 + 71 + typedef void (*symbol_callback_t)(struct symbol *, void *arg); 72 + 73 + void symbol_read_exports(FILE *file); 74 + struct symbol *symbol_get(const char *name); 75 + void symbol_free(void); 76 + 77 + /* 78 + * dwarf.c 79 + */ 80 + 81 + struct state { 82 + struct symbol *sym; 83 + Dwarf_Die die; 84 + }; 85 + 86 + typedef int (*die_callback_t)(struct state *state, Dwarf_Die *die); 87 + typedef bool (*die_match_callback_t)(Dwarf_Die *die); 88 + bool match_all(Dwarf_Die *die); 89 + 90 + int process_die_container(struct state *state, Dwarf_Die *die, 91 + die_callback_t func, die_match_callback_t match); 92 + 93 + void process_cu(Dwarf_Die *cudie); 94 + 95 + #endif /* __GENDWARFKSYMS_H */
+98
scripts/gendwarfksyms/symbols.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2024 Google LLC 4 + */ 5 + 6 + #include "gendwarfksyms.h" 7 + 8 + #define SYMBOL_HASH_BITS 12 9 + 10 + /* name -> struct symbol */ 11 + static HASHTABLE_DEFINE(symbol_names, 1 << SYMBOL_HASH_BITS); 12 + 13 + static unsigned int for_each(const char *name, symbol_callback_t func, 14 + void *data) 15 + { 16 + struct hlist_node *tmp; 17 + struct symbol *match; 18 + 19 + if (!name || !*name) 20 + return 0; 21 + 22 + hash_for_each_possible_safe(symbol_names, match, tmp, name_hash, 23 + hash_str(name)) { 24 + if (strcmp(match->name, name)) 25 + continue; 26 + 27 + if (func) 28 + func(match, data); 29 + 30 + return 1; 31 + } 32 + 33 + return 0; 34 + } 35 + 36 + static bool is_exported(const char *name) 37 + { 38 + return for_each(name, NULL, NULL) > 0; 39 + } 40 + 41 + void symbol_read_exports(FILE *file) 42 + { 43 + struct symbol *sym; 44 + char *line = NULL; 45 + char *name = NULL; 46 + size_t size = 0; 47 + int nsym = 0; 48 + 49 + while (getline(&line, &size, file) > 0) { 50 + if (sscanf(line, "%ms\n", &name) != 1) 51 + error("malformed input line: %s", line); 52 + 53 + if (is_exported(name)) { 54 + /* Ignore duplicates */ 55 + free(name); 56 + continue; 57 + } 58 + 59 + sym = xcalloc(1, sizeof(struct symbol)); 60 + sym->name = name; 61 + 62 + hash_add(symbol_names, &sym->name_hash, hash_str(sym->name)); 63 + ++nsym; 64 + 65 + debug("%s", sym->name); 66 + } 67 + 68 + free(line); 69 + debug("%d exported symbols", nsym); 70 + } 71 + 72 + static void get_symbol(struct symbol *sym, void *arg) 73 + { 74 + struct symbol **res = arg; 75 + 76 + *res = sym; 77 + } 78 + 79 + struct symbol *symbol_get(const char *name) 80 + { 81 + struct symbol *sym = NULL; 82 + 83 + for_each(name, get_symbol, &sym); 84 + return sym; 85 + } 86 + 87 + void symbol_free(void) 88 + { 89 + struct hlist_node *tmp; 90 + struct symbol *sym; 91 + 92 + hash_for_each_safe(symbol_names, sym, tmp, name_hash) { 93 + free((void *)sym->name); 94 + free(sym); 95 + } 96 + 97 + hash_init(symbol_names); 98 + }