Skip to content

Commit e5874ad

Browse files
committed
libdrgn: use libdwfl
libdwfl is the elfutils "DWARF frontend library". It has high-level functionality for looking up symbols, walking stack traces, etc. In order to use this functionality, we need to report our debugging information through libdwfl. For userspace programs, libdwfl has a much better implementation than drgn for automatically finding debug information from a core dump or PID. However, for the kernel, libdwfl has a few issues: - It only supports finding debug information for the running kernel, not vmcores. - It determines the vmlinux address range by reading /proc/kallsyms, which is slow (~70ms on my machine). - If separate debug information isn't available for a kernel module, it finds it by walking /lib/modules/$(uname -r)/kernel; this is repeated for every module. - It doesn't find kernel modules with names containing both dashes and underscores (e.g., aes-x86_64). Luckily, drgn already solved all of these problems, and with some effort, we can keep doing it ourselves and report it to libdwfl. The conversion replaces a bunch of code for dealing with userspace core dump notes, /proc/$pid/maps, and relocations.
1 parent a9a2cb7 commit e5874ad

12 files changed

+1568
-1469
lines changed

libdrgn/dwarf_index.c

Lines changed: 405 additions & 444 deletions
Large diffs are not rendered by default.

libdrgn/dwarf_index.h

Lines changed: 73 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,15 @@
1212
#ifndef DRGN_DWARF_INDEX_H
1313
#define DRGN_DWARF_INDEX_H
1414

15-
#include <elfutils/libdw.h>
15+
#include <elfutils/libdwfl.h>
1616
#include <libelf.h>
1717
#include <omp.h>
1818
#include <stddef.h>
1919
#include <stdint.h>
2020

2121
#include "drgn.h"
2222
#include "hash_table.h"
23+
#include "string_builder.h"
2324
#include "vector.h"
2425

2526
/**
@@ -44,38 +45,53 @@
4445
* @{
4546
*/
4647

47-
enum {
48-
SECTION_SYMTAB,
49-
SECTION_DEBUG_ABBREV,
50-
SECTION_DEBUG_INFO,
51-
SECTION_DEBUG_LINE,
52-
SECTION_DEBUG_STR,
53-
DRGN_DWARF_INDEX_NUM_SECTIONS,
54-
};
55-
56-
struct drgn_dwarf_index_file {
57-
Elf_Data *sections[DRGN_DWARF_INDEX_NUM_SECTIONS];
58-
bool failed;
48+
/**
49+
* drgn-specific data for the @c Dwfl_Module userdata pointer.
50+
*
51+
* For a newly created userdata, @c indexed is @c false and @c err is @c NULL.
52+
* They are updated by @ref drgn_dwarf_index_update(). @c err may be set with
53+
* @ref drgn_dwfl_module_userdata_set_error() before @ref
54+
* drgn_dwarf_index_update() is called to skip indexing for that module; the
55+
* error message will be added to the @ref DRGN_ERROR_MISSING_DEBUG_INFO error.
56+
*
57+
* @sa drgn_dwfl_find_elf(), drgn_dwfl_section_address()
58+
*/
59+
struct drgn_dwfl_module_userdata {
60+
/** Whether the module is indexed in a @ref drgn_dwarf_index. */
61+
bool indexed;
62+
/** File descriptor of @ref drgn_dwfl_module_userdata::elf. */
5963
int fd;
60-
/*
61-
* If this is NULL, then we didn't open the file and don't own the Elf
62-
* handle.
63-
*/
64-
const char *path;
64+
/** Error encountered while indexing. */
65+
struct drgn_error *err;
66+
/** Path of @ref drgn_dwfl_module_userdata::elf. */
67+
char *path;
68+
/** ELF handle to use. */
6569
Elf *elf;
66-
Dwarf *dwarf;
67-
Elf_Data *rela_sections[DRGN_DWARF_INDEX_NUM_SECTIONS];
68-
struct drgn_dwarf_index_file *next;
6970
};
7071

71-
static inline const char *
72-
drgn_dwarf_index_file_to_key(struct drgn_dwarf_index_file * const *entry)
72+
struct drgn_dwfl_module_userdata *drgn_dwfl_module_userdata_create(void);
73+
74+
void
75+
drgn_dwfl_module_userdata_destroy(struct drgn_dwfl_module_userdata *userdata);
76+
77+
/* This takes ownership of err. */
78+
void
79+
drgn_dwfl_module_userdata_set_error(struct drgn_dwfl_module_userdata *userdata,
80+
const char *message,
81+
struct drgn_error *err);
82+
83+
extern const Dwfl_Callbacks drgn_dwfl_callbacks;
84+
85+
/** Get the @ref drgn_dwfl_module_userdata for a @c Dwfl_Module. */
86+
static inline struct drgn_dwfl_module_userdata *
87+
drgn_dwfl_module_userdata(Dwfl_Module *module)
7388
{
74-
return (*entry)->path;
89+
void **userdatap;
90+
91+
dwfl_module_info(module, &userdatap, NULL, NULL, NULL, NULL, NULL,
92+
NULL);
93+
return *userdatap;
7594
}
76-
DEFINE_HASH_TABLE_TYPE(drgn_dwarf_index_file_table,
77-
struct drgn_dwarf_index_file *,
78-
drgn_dwarf_index_file_to_key)
7995

8096
struct drgn_dwarf_index_die;
8197
DEFINE_HASH_MAP_TYPE(drgn_dwarf_index_die_map, struct string, size_t)
@@ -102,23 +118,14 @@ struct drgn_dwarf_index_shard {
102118
* files. It is much faster for this task than other generic DWARF parsing
103119
* libraries.
104120
*
105-
* A new DWARF index is created by @ref drgn_dwarf_index_create(). It is freed
106-
* by @ref drgn_dwarf_index_destroy().
107-
*
108-
* Indexing happens in two steps: the files to index are opened using @ref
109-
* drgn_dwarf_index_open(), then they all are parsed and indexed by @ref
110-
* drgn_dwarf_index_update(). The update step is parallelized across CPUs, so it
111-
* is most efficient to open as many files as possible before indexing them all
112-
* at once in parallel.
113-
*
114121
* Searches in the index are done with a @ref drgn_dwarf_index_iterator.
115122
*/
116123
struct drgn_dwarf_index {
117-
/** @privatesection */
118-
struct drgn_dwarf_index_file_table files;
119-
struct drgn_dwarf_index_file *opened_first, *opened_last;
120-
struct drgn_dwarf_index_file *indexed_first, *indexed_last;
121-
/* The index is sharded to reduce lock contention. */
124+
/**
125+
* Index shards.
126+
*
127+
* This is sharded to reduce lock contention.
128+
*/
122129
struct drgn_dwarf_index_shard shards[1 << DRGN_DWARF_INDEX_SHARD_BITS];
123130
};
124131

@@ -134,44 +141,36 @@ void drgn_dwarf_index_init(struct drgn_dwarf_index *dindex);
134141
void drgn_dwarf_index_deinit(struct drgn_dwarf_index *dindex);
135142

136143
/**
137-
* Open a file and add it to a DWARF index.
144+
* Index new DWARF information.
138145
*
139-
* This function does the first part of indexing a file: it opens the file,
140-
* reads or maps it, and checks that it contains the required debugging
141-
* information. However, it does not actually parse the debugging information.
142-
* To do so, call drgn_dwarf_index_update() once all of the files to index have
143-
* been opened.
146+
* This parses and indexes the debugging information for all modules in @p dwfl
147+
* that have not yet been indexed.
144148
*
145-
* If this fails, the file is not opened, but previously opened files are not
146-
* affected.
149+
* On success, @ref drgn_dwfl_module_userdata::indexed is set to @c true for all
150+
* modules that we were able to index, and @ref drgn_dwfl_module_userdata::err
151+
* is set to non-@c NULL for all other modules.
152+
*
153+
* If debug information was not available for one or more modules, a @ref
154+
* DRGN_ERROR_MISSING_DEBUG_INFO error is returned.
155+
*
156+
* On any other error, no new debugging information is indexed.
147157
*
148-
* @param[in] dindex DWARF index.
149-
* @param[in] path Path to open.
150-
* @param[out] elf If not @c NULL, the opened ELF file. It is valid until @ref
151-
* drgn_dwarf_index_destroy() is called.
152158
* @return @c NULL on success, non-@c NULL on error.
153159
*/
154-
struct drgn_error *drgn_dwarf_index_open(struct drgn_dwarf_index *dindex,
155-
const char *path, Elf **elf);
156-
157-
/** Close any files which haven't been indexed yet. */
158-
void drgn_dwarf_index_close_unindexed(struct drgn_dwarf_index *dindex);
160+
struct drgn_error *drgn_dwarf_index_update(struct drgn_dwarf_index *dindex,
161+
Dwfl *dwfl);
159162

160163
/**
161-
* Index newly opened files.
162-
*
163-
* This function does the second part of indexing a file: it applies ELF
164-
* relocations, then parses and indexes the debugging information in all of the
165-
* files opened by @ref drgn_dwarf_index_open() since the last call to @ref
166-
* drgn_dwarf_index_update() or @ref drgn_dwarf_index_create().
167-
*
168-
* If this fails, no new debugging information is indexed and all opened files
169-
* which were not already indexed are closed.
164+
* Remove all @c Dwfl_Modules that aren't indexed (see @ref
165+
* drgn_dwfl_module_userdata::indexed) from @p dwfl.
170166
*
171-
* @param[in] dindex DWARF index.
172-
* @return @c NULL on success, non-@c NULL on error.
167+
* This should be called if @ref drgn_dwarf_index_update() returned an error or
168+
* if modules were reported and @ref drgn_dwarf_index_update() was not called.
173169
*/
174-
struct drgn_error *drgn_dwarf_index_update(struct drgn_dwarf_index *dindex);
170+
void drgn_remove_unindexed_dwfl_modules(Dwfl *dwfl);
171+
172+
/** Remove all @Dwfl_Modules from @p dwfl. */
173+
void drgn_remove_all_dwfl_modules(Dwfl *dwfl);
175174

176175
/**
177176
* Iterator over DWARF debugging information.
@@ -215,14 +214,17 @@ void drgn_dwarf_index_iterator_init(struct drgn_dwarf_index_iterator *it,
215214
*
216215
* @param[in] it DWARF index iterator.
217216
* @param[out] die_ret Returned DIE.
217+
* @param[out] bias_ret Returned difference between addresses in the loaded
218+
* module and addresses in the debugging information. This may be @c NULL if it
219+
* is not needed.
218220
* @return @c NULL on success, non-@c NULL on error. In particular, when there
219221
* are no more matching DIEs, @p die_ret is not modified and an error with code
220222
* @ref DRGN_ERROR_STOP is returned; this @ref DRGN_ERROR_STOP error does not
221223
* have to be passed to @ref drgn_error_destroy().
222224
*/
223225
struct drgn_error *
224226
drgn_dwarf_index_iterator_next(struct drgn_dwarf_index_iterator *it,
225-
Dwarf_Die *die_ret);
227+
Dwarf_Die *die_ret, uint64_t *bias_ret);
226228

227229
/** @} */
228230

libdrgn/dwarf_info_cache.c

Lines changed: 13 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -369,14 +369,14 @@ drgn_dwarf_info_cache_find_complete(struct drgn_dwarf_info_cache *dicache,
369369
* Find a matching DIE. Note that drgn_dwarf_index does not contain DIEs
370370
* with DW_AT_declaration, so this will always be a complete type.
371371
*/
372-
err = drgn_dwarf_index_iterator_next(&it, &die);
372+
err = drgn_dwarf_index_iterator_next(&it, &die, NULL);
373373
if (err)
374374
return err;
375375
/*
376376
* Look for another matching DIE. If there is one, then we can't be sure
377377
* which type this is, so leave it incomplete rather than guessing.
378378
*/
379-
err = drgn_dwarf_index_iterator_next(&it, &die);
379+
err = drgn_dwarf_index_iterator_next(&it, &die, NULL);
380380
if (!err)
381381
return &drgn_stop;
382382
else if (err->code != DRGN_ERROR_STOP)
@@ -1387,7 +1387,7 @@ struct drgn_error *drgn_dwarf_type_find(enum drgn_type_kind kind,
13871387

13881388
drgn_dwarf_index_iterator_init(&it, &dicache->dindex, name, name_len,
13891389
&tag, 1);
1390-
while (!(err = drgn_dwarf_index_iterator_next(&it, &die))) {
1390+
while (!(err = drgn_dwarf_index_iterator_next(&it, &die, NULL))) {
13911391
if (die_matches_filename(&die, filename)) {
13921392
err = drgn_type_from_dwarf(dicache, &die, ret);
13931393
if (err)
@@ -1408,8 +1408,8 @@ struct drgn_error *drgn_dwarf_type_find(enum drgn_type_kind kind,
14081408

14091409
static struct drgn_error *
14101410
drgn_symbol_from_dwarf_subprogram(struct drgn_dwarf_info_cache *dicache,
1411-
Dwarf_Die *die, const char *name,
1412-
struct drgn_symbol *ret)
1411+
Dwarf_Die *die, uint64_t bias,
1412+
const char *name, struct drgn_symbol *ret)
14131413
{
14141414
struct drgn_error *err;
14151415
struct drgn_qualified_type qualified_type;
@@ -1427,20 +1427,14 @@ drgn_symbol_from_dwarf_subprogram(struct drgn_dwarf_info_cache *dicache,
14271427
"could not find address of '%s'",
14281428
name);
14291429
}
1430-
ret->address = low_pc;
1430+
ret->address = low_pc + bias;
14311431
ret->little_endian = dwarf_die_is_little_endian(die);
1432-
if (dicache->relocation_hook) {
1433-
err = dicache->relocation_hook(name, die, ret,
1434-
dicache->relocation_arg);
1435-
if (err)
1436-
return err;
1437-
}
14381432
return NULL;
14391433
}
14401434

14411435
static struct drgn_error *
14421436
drgn_symbol_from_dwarf_variable(struct drgn_dwarf_info_cache *dicache,
1443-
Dwarf_Die *die, const char *name,
1437+
Dwarf_Die *die, uint64_t bias, const char *name,
14441438
struct drgn_symbol *ret)
14451439
{
14461440
struct drgn_error *err;
@@ -1470,14 +1464,8 @@ drgn_symbol_from_dwarf_variable(struct drgn_dwarf_info_cache *dicache,
14701464
return drgn_error_create(DRGN_ERROR_DWARF_FORMAT,
14711465
"DW_AT_location has unimplemented operation");
14721466
}
1473-
ret->address = loc[0].number;
1467+
ret->address = loc[0].number + bias;
14741468
ret->little_endian = dwarf_die_is_little_endian(die);
1475-
if (dicache->relocation_hook) {
1476-
err = dicache->relocation_hook(name, die, ret,
1477-
dicache->relocation_arg);
1478-
if (err)
1479-
return err;
1480-
}
14811469
return NULL;
14821470
}
14831471

@@ -1492,6 +1480,7 @@ drgn_dwarf_symbol_find(const char *name, size_t name_len, const char *filename,
14921480
size_t num_tags;
14931481
struct drgn_dwarf_index_iterator it;
14941482
Dwarf_Die die;
1483+
uint64_t bias;
14951484

14961485
num_tags = 0;
14971486
if (flags & DRGN_FIND_OBJECT_CONSTANT)
@@ -1503,7 +1492,7 @@ drgn_dwarf_symbol_find(const char *name, size_t name_len, const char *filename,
15031492

15041493
drgn_dwarf_index_iterator_init(&it, &dicache->dindex, name,
15051494
strlen(name), tags, num_tags);
1506-
while (!(err = drgn_dwarf_index_iterator_next(&it, &die))) {
1495+
while (!(err = drgn_dwarf_index_iterator_next(&it, &die, &bias))) {
15071496
if (!die_matches_filename(&die, filename))
15081497
continue;
15091498
switch (dwarf_tag(&die)) {
@@ -1521,10 +1510,11 @@ drgn_dwarf_symbol_find(const char *name, size_t name_len, const char *filename,
15211510
}
15221511
case DW_TAG_subprogram:
15231512
return drgn_symbol_from_dwarf_subprogram(dicache, &die,
1524-
name, ret);
1513+
bias, name,
1514+
ret);
15251515
case DW_TAG_variable:
15261516
return drgn_symbol_from_dwarf_variable(dicache, &die,
1527-
name, ret);
1517+
bias, name, ret);
15281518
default:
15291519
DRGN_UNREACHABLE();
15301520
}
@@ -1549,8 +1539,6 @@ drgn_dwarf_info_cache_create(struct drgn_type_index *tindex,
15491539
dwarf_type_map_init(&dicache->cant_be_incomplete_array_map);
15501540
dicache->depth = 0;
15511541
dicache->tindex = tindex;
1552-
dicache->relocation_hook = NULL;
1553-
dicache->relocation_arg = NULL;
15541542
*ret = dicache;
15551543
return NULL;
15561544
}

libdrgn/dwarf_info_cache.h

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -77,28 +77,6 @@ struct drgn_dwarf_info_cache {
7777
int depth;
7878
/** Type index. */
7979
struct drgn_type_index *tindex;
80-
/**
81-
* Relocation callback.
82-
*
83-
* Objects in an ELF file are often relocated when they are loaded into
84-
* a program (e.g., shared libraries or position-independent
85-
* executables). This callback can be used to adjust the address which
86-
* was found in the DWARF debugging information entry for the symbol.
87-
*
88-
* On entry, @p sym is fully initialized and not a constant. This
89-
* should look at @c sym->address and modify it as appropriate.
90-
*
91-
* @param[in] prog @ref drgn_dwarf_symbol_index::prog.
92-
* @param[in] name Name of the symbol.
93-
* @param[in] die DWARF DIE of the symbol.
94-
* @param[in,out] sym Symbol to relocate.
95-
* @return @c NULL on success, non-@c NULL on error.
96-
*/
97-
struct drgn_error *(*relocation_hook)(const char *name, Dwarf_Die *die,
98-
struct drgn_symbol *sym,
99-
void *arg);
100-
/* Argument to pass to @ref drgn_dwarf_info_cache::relocation_hook. */
101-
void *relocation_arg;
10280
};
10381

10482
/** Create a @ref drgn_dwarf_info_cache. */

libdrgn/error.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// SPDX-License-Identifier: GPL-3.0+
33

44
#include <errno.h>
5-
#include <elfutils/libdw.h>
5+
#include <elfutils/libdwfl.h>
66
#include <libelf.h>
77
#include <stdarg.h>
88
#include <stdio.h>
@@ -22,11 +22,6 @@ struct drgn_error drgn_stop = {
2222
.message = "stop iteration",
2323
};
2424

25-
struct drgn_error drgn_not_elf = {
26-
.code = DRGN_ERROR_ELF_FORMAT,
27-
.message = "not an ELF file",
28-
};
29-
3025
static struct drgn_error *drgn_error_create_nodup(enum drgn_error_code code,
3126
char *message)
3227
{
@@ -194,3 +189,9 @@ struct drgn_error *drgn_error_libdw(void)
194189
return drgn_error_format(DRGN_ERROR_DWARF_FORMAT, "libdw error: %s",
195190
dwarf_errmsg(-1));
196191
}
192+
193+
struct drgn_error *drgn_error_libdwfl(void)
194+
{
195+
return drgn_error_format(DRGN_ERROR_DWARF_FORMAT, "libdwfl error: %s",
196+
dwfl_errmsg(-1));
197+
}

0 commit comments

Comments
 (0)