Skip to content

Commit f285688

Browse files
samitolvanenmasahir0y
authored andcommitted
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 <[email protected]> Reviewed-by: Petr Pavlu <[email protected]> Signed-off-by: Masahiro Yamada <[email protected]>
1 parent a56fece commit f285688

File tree

9 files changed

+513
-0
lines changed

9 files changed

+513
-0
lines changed

MAINTAINERS

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9550,6 +9550,13 @@ W: https://linuxtv.org
95509550
T: git git://linuxtv.org/media.git
95519551
F: drivers/media/radio/radio-gemtek*
95529552

9553+
GENDWARFKSYMS
9554+
M: Sami Tolvanen <[email protected]>
9555+
9556+
9557+
S: Maintained
9558+
F: scripts/gendwarfksyms/
9559+
95539560
GENERIC ARCHITECTURE TOPOLOGY
95549561
M: Sudeep Holla <[email protected]>
95559562

kernel/module/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,14 @@ config MODVERSIONS
169169
make them incompatible with the kernel you are running. If
170170
unsure, say N.
171171

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+
172180
config ASM_MODVERSIONS
173181
bool
174182
default HAVE_ASM_MODVERSIONS && MODVERSIONS

scripts/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ targets += module.lds
5454

5555
subdir-$(CONFIG_GCC_PLUGINS) += gcc-plugins
5656
subdir-$(CONFIG_MODVERSIONS) += genksyms
57+
subdir-$(CONFIG_GENDWARFKSYMS) += gendwarfksyms
5758
subdir-$(CONFIG_SECURITY_SELINUX) += selinux
5859
subdir-$(CONFIG_SECURITY_IPE) += ipe
5960

scripts/gendwarfksyms/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# SPDX-License-Identifier: GPL-2.0
2+
/gendwarfksyms

scripts/gendwarfksyms/Makefile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
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

scripts/gendwarfksyms/dwarf.c

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
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+
}

scripts/gendwarfksyms/gendwarfksyms.c

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
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+
}

0 commit comments

Comments
 (0)