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.
This commit is contained in:
Omar Sandoval 2019-07-15 00:51:30 -07:00
parent a9a2cb7cac
commit e5874ad18a
12 changed files with 1625 additions and 1526 deletions

File diff suppressed because it is too large Load Diff

View File

@ -12,7 +12,7 @@
#ifndef DRGN_DWARF_INDEX_H #ifndef DRGN_DWARF_INDEX_H
#define DRGN_DWARF_INDEX_H #define DRGN_DWARF_INDEX_H
#include <elfutils/libdw.h> #include <elfutils/libdwfl.h>
#include <libelf.h> #include <libelf.h>
#include <omp.h> #include <omp.h>
#include <stddef.h> #include <stddef.h>
@ -20,6 +20,7 @@
#include "drgn.h" #include "drgn.h"
#include "hash_table.h" #include "hash_table.h"
#include "string_builder.h"
#include "vector.h" #include "vector.h"
/** /**
@ -44,38 +45,53 @@
* @{ * @{
*/ */
enum { /**
SECTION_SYMTAB, * drgn-specific data for the @c Dwfl_Module userdata pointer.
SECTION_DEBUG_ABBREV, *
SECTION_DEBUG_INFO, * For a newly created userdata, @c indexed is @c false and @c err is @c NULL.
SECTION_DEBUG_LINE, * They are updated by @ref drgn_dwarf_index_update(). @c err may be set with
SECTION_DEBUG_STR, * @ref drgn_dwfl_module_userdata_set_error() before @ref
DRGN_DWARF_INDEX_NUM_SECTIONS, * drgn_dwarf_index_update() is called to skip indexing for that module; the
}; * error message will be added to the @ref DRGN_ERROR_MISSING_DEBUG_INFO error.
*
struct drgn_dwarf_index_file { * @sa drgn_dwfl_find_elf(), drgn_dwfl_section_address()
Elf_Data *sections[DRGN_DWARF_INDEX_NUM_SECTIONS]; */
bool failed; struct drgn_dwfl_module_userdata {
/** Whether the module is indexed in a @ref drgn_dwarf_index. */
bool indexed;
/** File descriptor of @ref drgn_dwfl_module_userdata::elf. */
int fd; int fd;
/* /** Error encountered while indexing. */
* If this is NULL, then we didn't open the file and don't own the Elf struct drgn_error *err;
* handle. /** Path of @ref drgn_dwfl_module_userdata::elf. */
*/ char *path;
const char *path; /** ELF handle to use. */
Elf *elf; Elf *elf;
Dwarf *dwarf;
Elf_Data *rela_sections[DRGN_DWARF_INDEX_NUM_SECTIONS];
struct drgn_dwarf_index_file *next;
}; };
static inline const char * struct drgn_dwfl_module_userdata *drgn_dwfl_module_userdata_create(void);
drgn_dwarf_index_file_to_key(struct drgn_dwarf_index_file * const *entry)
void
drgn_dwfl_module_userdata_destroy(struct drgn_dwfl_module_userdata *userdata);
/* This takes ownership of err. */
void
drgn_dwfl_module_userdata_set_error(struct drgn_dwfl_module_userdata *userdata,
const char *message,
struct drgn_error *err);
extern const Dwfl_Callbacks drgn_dwfl_callbacks;
/** Get the @ref drgn_dwfl_module_userdata for a @c Dwfl_Module. */
static inline struct drgn_dwfl_module_userdata *
drgn_dwfl_module_userdata(Dwfl_Module *module)
{ {
return (*entry)->path; void **userdatap;
dwfl_module_info(module, &userdatap, NULL, NULL, NULL, NULL, NULL,
NULL);
return *userdatap;
} }
DEFINE_HASH_TABLE_TYPE(drgn_dwarf_index_file_table,
struct drgn_dwarf_index_file *,
drgn_dwarf_index_file_to_key)
struct drgn_dwarf_index_die; struct drgn_dwarf_index_die;
DEFINE_HASH_MAP_TYPE(drgn_dwarf_index_die_map, struct string, size_t) DEFINE_HASH_MAP_TYPE(drgn_dwarf_index_die_map, struct string, size_t)
@ -102,23 +118,14 @@ struct drgn_dwarf_index_shard {
* files. It is much faster for this task than other generic DWARF parsing * files. It is much faster for this task than other generic DWARF parsing
* libraries. * libraries.
* *
* A new DWARF index is created by @ref drgn_dwarf_index_create(). It is freed
* by @ref drgn_dwarf_index_destroy().
*
* Indexing happens in two steps: the files to index are opened using @ref
* drgn_dwarf_index_open(), then they all are parsed and indexed by @ref
* drgn_dwarf_index_update(). The update step is parallelized across CPUs, so it
* is most efficient to open as many files as possible before indexing them all
* at once in parallel.
*
* Searches in the index are done with a @ref drgn_dwarf_index_iterator. * Searches in the index are done with a @ref drgn_dwarf_index_iterator.
*/ */
struct drgn_dwarf_index { struct drgn_dwarf_index {
/** @privatesection */ /**
struct drgn_dwarf_index_file_table files; * Index shards.
struct drgn_dwarf_index_file *opened_first, *opened_last; *
struct drgn_dwarf_index_file *indexed_first, *indexed_last; * This is sharded to reduce lock contention.
/* The index is sharded to reduce lock contention. */ */
struct drgn_dwarf_index_shard shards[1 << DRGN_DWARF_INDEX_SHARD_BITS]; struct drgn_dwarf_index_shard shards[1 << DRGN_DWARF_INDEX_SHARD_BITS];
}; };
@ -134,44 +141,36 @@ void drgn_dwarf_index_init(struct drgn_dwarf_index *dindex);
void drgn_dwarf_index_deinit(struct drgn_dwarf_index *dindex); void drgn_dwarf_index_deinit(struct drgn_dwarf_index *dindex);
/** /**
* Open a file and add it to a DWARF index. * Index new DWARF information.
* *
* This function does the first part of indexing a file: it opens the file, * This parses and indexes the debugging information for all modules in @p dwfl
* reads or maps it, and checks that it contains the required debugging * that have not yet been indexed.
* information. However, it does not actually parse the debugging information.
* To do so, call drgn_dwarf_index_update() once all of the files to index have
* been opened.
* *
* If this fails, the file is not opened, but previously opened files are not * On success, @ref drgn_dwfl_module_userdata::indexed is set to @c true for all
* affected. * modules that we were able to index, and @ref drgn_dwfl_module_userdata::err
* is set to non-@c NULL for all other modules.
*
* If debug information was not available for one or more modules, a @ref
* DRGN_ERROR_MISSING_DEBUG_INFO error is returned.
*
* On any other error, no new debugging information is indexed.
* *
* @param[in] dindex DWARF index.
* @param[in] path Path to open.
* @param[out] elf If not @c NULL, the opened ELF file. It is valid until @ref
* drgn_dwarf_index_destroy() is called.
* @return @c NULL on success, non-@c NULL on error. * @return @c NULL on success, non-@c NULL on error.
*/ */
struct drgn_error *drgn_dwarf_index_open(struct drgn_dwarf_index *dindex, struct drgn_error *drgn_dwarf_index_update(struct drgn_dwarf_index *dindex,
const char *path, Elf **elf); Dwfl *dwfl);
/** Close any files which haven't been indexed yet. */
void drgn_dwarf_index_close_unindexed(struct drgn_dwarf_index *dindex);
/** /**
* Index newly opened files. * Remove all @c Dwfl_Modules that aren't indexed (see @ref
* drgn_dwfl_module_userdata::indexed) from @p dwfl.
* *
* This function does the second part of indexing a file: it applies ELF * This should be called if @ref drgn_dwarf_index_update() returned an error or
* relocations, then parses and indexes the debugging information in all of the * if modules were reported and @ref drgn_dwarf_index_update() was not called.
* files opened by @ref drgn_dwarf_index_open() since the last call to @ref
* drgn_dwarf_index_update() or @ref drgn_dwarf_index_create().
*
* If this fails, no new debugging information is indexed and all opened files
* which were not already indexed are closed.
*
* @param[in] dindex DWARF index.
* @return @c NULL on success, non-@c NULL on error.
*/ */
struct drgn_error *drgn_dwarf_index_update(struct drgn_dwarf_index *dindex); void drgn_remove_unindexed_dwfl_modules(Dwfl *dwfl);
/** Remove all @Dwfl_Modules from @p dwfl. */
void drgn_remove_all_dwfl_modules(Dwfl *dwfl);
/** /**
* Iterator over DWARF debugging information. * Iterator over DWARF debugging information.
@ -215,6 +214,9 @@ void drgn_dwarf_index_iterator_init(struct drgn_dwarf_index_iterator *it,
* *
* @param[in] it DWARF index iterator. * @param[in] it DWARF index iterator.
* @param[out] die_ret Returned DIE. * @param[out] die_ret Returned DIE.
* @param[out] bias_ret Returned difference between addresses in the loaded
* module and addresses in the debugging information. This may be @c NULL if it
* is not needed.
* @return @c NULL on success, non-@c NULL on error. In particular, when there * @return @c NULL on success, non-@c NULL on error. In particular, when there
* are no more matching DIEs, @p die_ret is not modified and an error with code * are no more matching DIEs, @p die_ret is not modified and an error with code
* @ref DRGN_ERROR_STOP is returned; this @ref DRGN_ERROR_STOP error does not * @ref DRGN_ERROR_STOP is returned; this @ref DRGN_ERROR_STOP error does not
@ -222,7 +224,7 @@ void drgn_dwarf_index_iterator_init(struct drgn_dwarf_index_iterator *it,
*/ */
struct drgn_error * struct drgn_error *
drgn_dwarf_index_iterator_next(struct drgn_dwarf_index_iterator *it, drgn_dwarf_index_iterator_next(struct drgn_dwarf_index_iterator *it,
Dwarf_Die *die_ret); Dwarf_Die *die_ret, uint64_t *bias_ret);
/** @} */ /** @} */

View File

@ -369,14 +369,14 @@ drgn_dwarf_info_cache_find_complete(struct drgn_dwarf_info_cache *dicache,
* Find a matching DIE. Note that drgn_dwarf_index does not contain DIEs * Find a matching DIE. Note that drgn_dwarf_index does not contain DIEs
* with DW_AT_declaration, so this will always be a complete type. * with DW_AT_declaration, so this will always be a complete type.
*/ */
err = drgn_dwarf_index_iterator_next(&it, &die); err = drgn_dwarf_index_iterator_next(&it, &die, NULL);
if (err) if (err)
return err; return err;
/* /*
* Look for another matching DIE. If there is one, then we can't be sure * Look for another matching DIE. If there is one, then we can't be sure
* which type this is, so leave it incomplete rather than guessing. * which type this is, so leave it incomplete rather than guessing.
*/ */
err = drgn_dwarf_index_iterator_next(&it, &die); err = drgn_dwarf_index_iterator_next(&it, &die, NULL);
if (!err) if (!err)
return &drgn_stop; return &drgn_stop;
else if (err->code != DRGN_ERROR_STOP) else if (err->code != DRGN_ERROR_STOP)
@ -1387,7 +1387,7 @@ struct drgn_error *drgn_dwarf_type_find(enum drgn_type_kind kind,
drgn_dwarf_index_iterator_init(&it, &dicache->dindex, name, name_len, drgn_dwarf_index_iterator_init(&it, &dicache->dindex, name, name_len,
&tag, 1); &tag, 1);
while (!(err = drgn_dwarf_index_iterator_next(&it, &die))) { while (!(err = drgn_dwarf_index_iterator_next(&it, &die, NULL))) {
if (die_matches_filename(&die, filename)) { if (die_matches_filename(&die, filename)) {
err = drgn_type_from_dwarf(dicache, &die, ret); err = drgn_type_from_dwarf(dicache, &die, ret);
if (err) if (err)
@ -1408,8 +1408,8 @@ struct drgn_error *drgn_dwarf_type_find(enum drgn_type_kind kind,
static struct drgn_error * static struct drgn_error *
drgn_symbol_from_dwarf_subprogram(struct drgn_dwarf_info_cache *dicache, drgn_symbol_from_dwarf_subprogram(struct drgn_dwarf_info_cache *dicache,
Dwarf_Die *die, const char *name, Dwarf_Die *die, uint64_t bias,
struct drgn_symbol *ret) const char *name, struct drgn_symbol *ret)
{ {
struct drgn_error *err; struct drgn_error *err;
struct drgn_qualified_type qualified_type; struct drgn_qualified_type qualified_type;
@ -1427,20 +1427,14 @@ drgn_symbol_from_dwarf_subprogram(struct drgn_dwarf_info_cache *dicache,
"could not find address of '%s'", "could not find address of '%s'",
name); name);
} }
ret->address = low_pc; ret->address = low_pc + bias;
ret->little_endian = dwarf_die_is_little_endian(die); ret->little_endian = dwarf_die_is_little_endian(die);
if (dicache->relocation_hook) {
err = dicache->relocation_hook(name, die, ret,
dicache->relocation_arg);
if (err)
return err;
}
return NULL; return NULL;
} }
static struct drgn_error * static struct drgn_error *
drgn_symbol_from_dwarf_variable(struct drgn_dwarf_info_cache *dicache, drgn_symbol_from_dwarf_variable(struct drgn_dwarf_info_cache *dicache,
Dwarf_Die *die, const char *name, Dwarf_Die *die, uint64_t bias, const char *name,
struct drgn_symbol *ret) struct drgn_symbol *ret)
{ {
struct drgn_error *err; struct drgn_error *err;
@ -1470,14 +1464,8 @@ drgn_symbol_from_dwarf_variable(struct drgn_dwarf_info_cache *dicache,
return drgn_error_create(DRGN_ERROR_DWARF_FORMAT, return drgn_error_create(DRGN_ERROR_DWARF_FORMAT,
"DW_AT_location has unimplemented operation"); "DW_AT_location has unimplemented operation");
} }
ret->address = loc[0].number; ret->address = loc[0].number + bias;
ret->little_endian = dwarf_die_is_little_endian(die); ret->little_endian = dwarf_die_is_little_endian(die);
if (dicache->relocation_hook) {
err = dicache->relocation_hook(name, die, ret,
dicache->relocation_arg);
if (err)
return err;
}
return NULL; return NULL;
} }
@ -1492,6 +1480,7 @@ drgn_dwarf_symbol_find(const char *name, size_t name_len, const char *filename,
size_t num_tags; size_t num_tags;
struct drgn_dwarf_index_iterator it; struct drgn_dwarf_index_iterator it;
Dwarf_Die die; Dwarf_Die die;
uint64_t bias;
num_tags = 0; num_tags = 0;
if (flags & DRGN_FIND_OBJECT_CONSTANT) if (flags & DRGN_FIND_OBJECT_CONSTANT)
@ -1503,7 +1492,7 @@ drgn_dwarf_symbol_find(const char *name, size_t name_len, const char *filename,
drgn_dwarf_index_iterator_init(&it, &dicache->dindex, name, drgn_dwarf_index_iterator_init(&it, &dicache->dindex, name,
strlen(name), tags, num_tags); strlen(name), tags, num_tags);
while (!(err = drgn_dwarf_index_iterator_next(&it, &die))) { while (!(err = drgn_dwarf_index_iterator_next(&it, &die, &bias))) {
if (!die_matches_filename(&die, filename)) if (!die_matches_filename(&die, filename))
continue; continue;
switch (dwarf_tag(&die)) { switch (dwarf_tag(&die)) {
@ -1521,10 +1510,11 @@ drgn_dwarf_symbol_find(const char *name, size_t name_len, const char *filename,
} }
case DW_TAG_subprogram: case DW_TAG_subprogram:
return drgn_symbol_from_dwarf_subprogram(dicache, &die, return drgn_symbol_from_dwarf_subprogram(dicache, &die,
name, ret); bias, name,
ret);
case DW_TAG_variable: case DW_TAG_variable:
return drgn_symbol_from_dwarf_variable(dicache, &die, return drgn_symbol_from_dwarf_variable(dicache, &die,
name, ret); bias, name, ret);
default: default:
DRGN_UNREACHABLE(); DRGN_UNREACHABLE();
} }
@ -1549,8 +1539,6 @@ drgn_dwarf_info_cache_create(struct drgn_type_index *tindex,
dwarf_type_map_init(&dicache->cant_be_incomplete_array_map); dwarf_type_map_init(&dicache->cant_be_incomplete_array_map);
dicache->depth = 0; dicache->depth = 0;
dicache->tindex = tindex; dicache->tindex = tindex;
dicache->relocation_hook = NULL;
dicache->relocation_arg = NULL;
*ret = dicache; *ret = dicache;
return NULL; return NULL;
} }

View File

@ -77,28 +77,6 @@ struct drgn_dwarf_info_cache {
int depth; int depth;
/** Type index. */ /** Type index. */
struct drgn_type_index *tindex; struct drgn_type_index *tindex;
/**
* Relocation callback.
*
* Objects in an ELF file are often relocated when they are loaded into
* a program (e.g., shared libraries or position-independent
* executables). This callback can be used to adjust the address which
* was found in the DWARF debugging information entry for the symbol.
*
* On entry, @p sym is fully initialized and not a constant. This
* should look at @c sym->address and modify it as appropriate.
*
* @param[in] prog @ref drgn_dwarf_symbol_index::prog.
* @param[in] name Name of the symbol.
* @param[in] die DWARF DIE of the symbol.
* @param[in,out] sym Symbol to relocate.
* @return @c NULL on success, non-@c NULL on error.
*/
struct drgn_error *(*relocation_hook)(const char *name, Dwarf_Die *die,
struct drgn_symbol *sym,
void *arg);
/* Argument to pass to @ref drgn_dwarf_info_cache::relocation_hook. */
void *relocation_arg;
}; };
/** Create a @ref drgn_dwarf_info_cache. */ /** Create a @ref drgn_dwarf_info_cache. */

View File

@ -2,7 +2,7 @@
// SPDX-License-Identifier: GPL-3.0+ // SPDX-License-Identifier: GPL-3.0+
#include <errno.h> #include <errno.h>
#include <elfutils/libdw.h> #include <elfutils/libdwfl.h>
#include <libelf.h> #include <libelf.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
@ -22,11 +22,6 @@ struct drgn_error drgn_stop = {
.message = "stop iteration", .message = "stop iteration",
}; };
struct drgn_error drgn_not_elf = {
.code = DRGN_ERROR_ELF_FORMAT,
.message = "not an ELF file",
};
static struct drgn_error *drgn_error_create_nodup(enum drgn_error_code code, static struct drgn_error *drgn_error_create_nodup(enum drgn_error_code code,
char *message) char *message)
{ {
@ -194,3 +189,9 @@ struct drgn_error *drgn_error_libdw(void)
return drgn_error_format(DRGN_ERROR_DWARF_FORMAT, "libdw error: %s", return drgn_error_format(DRGN_ERROR_DWARF_FORMAT, "libdw error: %s",
dwarf_errmsg(-1)); dwarf_errmsg(-1));
} }
struct drgn_error *drgn_error_libdwfl(void)
{
return drgn_error_format(DRGN_ERROR_DWARF_FORMAT, "libdwfl error: %s",
dwfl_errmsg(-1));
}

View File

@ -37,9 +37,6 @@ struct drgn_object_type;
*/ */
extern struct drgn_error drgn_stop; extern struct drgn_error drgn_stop;
/** Not an ELF file. */
extern struct drgn_error drgn_not_elf;
struct string_builder; struct string_builder;
/** /**
@ -67,6 +64,10 @@ struct drgn_error *drgn_error_libelf(void)
struct drgn_error *drgn_error_libdw(void) struct drgn_error *drgn_error_libdw(void)
__attribute__((returns_nonnull)); __attribute__((returns_nonnull));
/** Create a @ref drgn_error from the libdwfl error indicator. */
struct drgn_error *drgn_error_libdwfl(void)
__attribute__((returns_nonnull));
/** /**
* Create a @ref drgn_error with a type name. * Create a @ref drgn_error with a type name.
* *

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,7 @@
#ifndef DRGN_LINUX_KERNEL_H #ifndef DRGN_LINUX_KERNEL_H
#define DRGN_LINUX_KERNEL_H #define DRGN_LINUX_KERNEL_H
#include <elfutils/libdw.h> #include <elfutils/libdwfl.h>
#include "drgn.h" #include "drgn.h"
@ -18,10 +18,11 @@ struct drgn_error *read_vmcoreinfo_fallback(struct drgn_memory_reader *reader,
bool have_non_zero_phys_addr, bool have_non_zero_phys_addr,
struct vmcoreinfo *ret); struct vmcoreinfo *ret);
struct drgn_error *kernel_relocation_hook(struct drgn_program *prog, struct drgn_error *
const char *name, Dwarf_Die *die, linux_kernel_load_debug_info(struct drgn_program *prog, const char **paths,
struct drgn_symbol *sym); size_t n);
struct drgn_error *load_kernel_debug_info(struct drgn_program *prog); struct drgn_error *
linux_kernel_load_default_debug_info(struct drgn_program *prog);
#endif /* DRGN_LINUX_KERNEL_H */ #endif /* DRGN_LINUX_KERNEL_H */

View File

@ -65,41 +65,32 @@ static void drgn_program_update_arch(struct drgn_program *prog,
void drgn_program_init(struct drgn_program *prog, void drgn_program_init(struct drgn_program *prog,
enum drgn_architecture_flags arch) enum drgn_architecture_flags arch)
{ {
memset(prog, 0, sizeof(*prog));
drgn_memory_reader_init(&prog->reader); drgn_memory_reader_init(&prog->reader);
drgn_type_index_init(&prog->tindex); drgn_type_index_init(&prog->tindex);
drgn_symbol_index_init(&prog->sindex); drgn_symbol_index_init(&prog->sindex);
prog->file_segments = NULL;
prog->num_file_segments = 0;
prog->mappings = NULL;
prog->num_mappings = 0;
memset(&prog->vmcoreinfo, 0, sizeof(prog->vmcoreinfo));
prog->dicache = NULL;
prog->core_fd = -1; prog->core_fd = -1;
prog->flags = 0;
prog->arch = DRGN_ARCH_AUTO; prog->arch = DRGN_ARCH_AUTO;
if (arch != DRGN_ARCH_AUTO) if (arch != DRGN_ARCH_AUTO)
drgn_program_update_arch(prog, arch); drgn_program_update_arch(prog, arch);
prog->added_vmcoreinfo_symbol_finder = false;
} }
void drgn_program_deinit(struct drgn_program *prog) void drgn_program_deinit(struct drgn_program *prog)
{ {
size_t i;
drgn_symbol_index_deinit(&prog->sindex); drgn_symbol_index_deinit(&prog->sindex);
drgn_type_index_deinit(&prog->tindex); drgn_type_index_deinit(&prog->tindex);
drgn_memory_reader_deinit(&prog->reader); drgn_memory_reader_deinit(&prog->reader);
free(prog->file_segments); free(prog->file_segments);
for (i = 0; i < prog->num_mappings; i++)
free(prog->mappings[i].path);
free(prog->mappings);
if (prog->core_fd != -1) if (prog->core_fd != -1)
close(prog->core_fd); close(prog->core_fd);
drgn_dwarf_info_cache_destroy(prog->dicache); drgn_dwarf_info_cache_destroy(prog->dicache);
if (prog->_dwfl) {
drgn_remove_all_dwfl_modules(prog->_dwfl);
dwfl_end(prog->_dwfl);
}
} }
LIBDRGN_PUBLIC struct drgn_error * LIBDRGN_PUBLIC struct drgn_error *
@ -151,135 +142,6 @@ drgn_program_add_symbol_finder(struct drgn_program *prog,
return drgn_symbol_index_add_finder(&prog->sindex, fn, arg); return drgn_symbol_index_add_finder(&prog->sindex, fn, arg);
} }
/*
* Returns NULL if a mapping was appended, &drgn_stop if the mapping was merged,
* non-NULL on error.
*/
static struct drgn_error *append_file_mapping(uint64_t start, uint64_t end,
uint64_t file_offset, char *path,
struct file_mapping **mappings,
size_t *num_mappings,
size_t *capacity)
{
struct file_mapping *mapping;
if (start > end) {
return drgn_error_create(DRGN_ERROR_OTHER,
"file memory mapping has negative length");
} else if (start == end) {
return NULL;
}
/*
* There may be separate mappings for adjacent areas of a file (e.g., if
* the mappings have different permissions). Make sure to merge those.
*/
if (*num_mappings) {
uint64_t length;
mapping = &(*mappings)[*num_mappings - 1];
length = mapping->end - mapping->start;
if (mapping->end == start &&
mapping->file_offset + length == file_offset &&
strcmp(mapping->path, path) == 0) {
mapping->end = end;
return &drgn_stop;
}
}
if (*num_mappings >= *capacity) {
size_t new_capacity;
if (*capacity == 0)
new_capacity = 1;
else
new_capacity = *capacity * 2;
if (!resize_array(mappings, new_capacity))
return &drgn_enomem;
*capacity = new_capacity;
}
mapping = &(*mappings)[(*num_mappings)++];
mapping->start = start;
mapping->end = end;
mapping->file_offset = file_offset;
mapping->path = path;
mapping->elf = NULL;
return NULL;
}
static struct drgn_error *parse_nt_file(const char *desc, size_t descsz,
bool is_64_bit,
struct file_mapping **mappings,
size_t *num_mappings,
size_t *mappings_capacity)
{
struct drgn_error *err;
uint64_t count, page_size, i;
const char *p = desc, *q, *end = &desc[descsz];
size_t paths_offset;
bool bswap = false;
if (is_64_bit) {
if (!read_u64(&p, end, bswap, &count) ||
!read_u64(&p, end, bswap, &page_size) ||
__builtin_mul_overflow(count, 24U, &paths_offset))
goto invalid;
} else {
if (!read_u32_into_u64(&p, end, bswap, &count) ||
!read_u32_into_u64(&p, end, bswap, &page_size) ||
__builtin_mul_overflow(count, 12U, &paths_offset))
goto invalid;
}
if (!read_in_bounds(p, end, paths_offset))
goto invalid;
q = p + paths_offset;
for (i = 0; i < count; i++) {
uint64_t mapping_start, mapping_end, file_offset;
const char *path;
size_t len;
/* We already did the bounds check above. */
if (is_64_bit) {
read_u64_nocheck(&p, bswap, &mapping_start);
read_u64_nocheck(&p, bswap, &mapping_end);
read_u64_nocheck(&p, bswap, &file_offset);
} else {
read_u32_into_u64_nocheck(&p, bswap, &mapping_start);
read_u32_into_u64_nocheck(&p, bswap, &mapping_end);
read_u32_into_u64_nocheck(&p, bswap, &file_offset);
}
file_offset *= page_size;
if (!read_string(&q, end, &path, &len))
goto invalid;
err = append_file_mapping(mapping_start, mapping_end, file_offset,
(char *)path, mappings, num_mappings,
mappings_capacity);
if (!err) {
struct file_mapping *mapping;
/*
* The mapping wasn't merged, so actually allocate the
* path now.
*/
mapping = &(*mappings)[*num_mappings - 1];
mapping->path = malloc(len + 1);
if (!mapping->path)
return &drgn_enomem;
memcpy(mapping->path, path, len + 1);
} else if (err->code != DRGN_ERROR_STOP) {
return err;
}
}
return NULL;
invalid:
return drgn_error_create(DRGN_ERROR_ELF_FORMAT, "invalid NT_FILE note");
}
static enum drgn_architecture_flags drgn_architecture_from_elf(Elf *elf) static enum drgn_architecture_flags drgn_architecture_from_elf(Elf *elf)
{ {
char *e_ident = elf_getident(elf, NULL); char *e_ident = elf_getident(elf, NULL);
@ -310,7 +172,7 @@ drgn_program_set_core_dump(struct drgn_program *prog, const char *path)
GElf_Ehdr ehdr_mem, *ehdr; GElf_Ehdr ehdr_mem, *ehdr;
enum drgn_architecture_flags arch; enum drgn_architecture_flags arch;
bool is_64_bit; bool is_64_bit;
size_t phnum, i, mappings_capacity = 0; size_t phnum, i;
bool have_non_zero_phys_addr = false; bool have_non_zero_phys_addr = false;
struct drgn_memory_file_segment *current_file_segment; struct drgn_memory_file_segment *current_file_segment;
bool have_nt_taskstruct = false, have_vmcoreinfo = false, is_proc_kcore; bool have_nt_taskstruct = false, have_vmcoreinfo = false, is_proc_kcore;
@ -332,12 +194,7 @@ drgn_program_set_core_dump(struct drgn_program *prog, const char *path)
} }
ehdr = gelf_getehdr(elf, &ehdr_mem); ehdr = gelf_getehdr(elf, &ehdr_mem);
if (!ehdr) { if (!ehdr || ehdr->e_type != ET_CORE) {
err = &drgn_not_elf;
goto out_elf;
}
if (ehdr->e_type != ET_CORE) {
err = drgn_error_format(DRGN_ERROR_INVALID_ARGUMENT, err = drgn_error_format(DRGN_ERROR_INVALID_ARGUMENT,
"not an ELF core file"); "not an ELF core file");
goto out_elf; goto out_elf;
@ -387,7 +244,7 @@ drgn_program_set_core_dump(struct drgn_program *prog, const char *path)
phdr = gelf_getphdr(elf, i, &phdr_mem); phdr = gelf_getphdr(elf, i, &phdr_mem);
if (!phdr) { if (!phdr) {
err = drgn_error_libelf(); err = drgn_error_libelf();
goto out_mappings; goto out_segments;
} }
if (phdr->p_type == PT_LOAD) { if (phdr->p_type == PT_LOAD) {
@ -409,7 +266,7 @@ drgn_program_set_core_dump(struct drgn_program *prog, const char *path)
current_file_segment, current_file_segment,
false); false);
if (err) if (err)
goto out_mappings; goto out_segments;
if (have_non_zero_phys_addr && if (have_non_zero_phys_addr &&
phdr->p_paddr != phdr->p_paddr !=
(is_64_bit ? UINT64_MAX : UINT32_MAX)) { (is_64_bit ? UINT64_MAX : UINT32_MAX)) {
@ -420,7 +277,7 @@ drgn_program_set_core_dump(struct drgn_program *prog, const char *path)
current_file_segment, current_file_segment,
true); true);
if (err) if (err)
goto out_mappings; goto out_segments;
} }
current_file_segment++; current_file_segment++;
} else if (phdr->p_type == PT_NOTE) { } else if (phdr->p_type == PT_NOTE) {
@ -434,7 +291,7 @@ drgn_program_set_core_dump(struct drgn_program *prog, const char *path)
note_header_type(phdr)); note_header_type(phdr));
if (!data) { if (!data) {
err = drgn_error_libelf(); err = drgn_error_libelf();
goto out_mappings; goto out_segments;
} }
offset = 0; offset = 0;
@ -447,25 +304,15 @@ drgn_program_set_core_dump(struct drgn_program *prog, const char *path)
name = (char *)data->d_buf + name_offset; name = (char *)data->d_buf + name_offset;
desc = (char *)data->d_buf + desc_offset; desc = (char *)data->d_buf + desc_offset;
if (strncmp(name, "CORE", nhdr.n_namesz) == 0) { if (strncmp(name, "CORE", nhdr.n_namesz) == 0) {
if (nhdr.n_type == NT_FILE) { if (nhdr.n_type == NT_TASKSTRUCT)
err = parse_nt_file(desc,
nhdr.n_descsz,
is_64_bit,
&prog->mappings,
&prog->num_mappings,
&mappings_capacity);
if (err)
goto out_mappings;
} else if (nhdr.n_type == NT_TASKSTRUCT) {
have_nt_taskstruct = true; have_nt_taskstruct = true;
}
} else if (strncmp(name, "VMCOREINFO", } else if (strncmp(name, "VMCOREINFO",
nhdr.n_namesz) == 0) { nhdr.n_namesz) == 0) {
err = parse_vmcoreinfo(desc, err = parse_vmcoreinfo(desc,
nhdr.n_descsz, nhdr.n_descsz,
&prog->vmcoreinfo); &prog->vmcoreinfo);
if (err) if (err)
goto out_mappings; goto out_segments;
have_vmcoreinfo = true; have_vmcoreinfo = true;
} }
} }
@ -474,11 +321,6 @@ drgn_program_set_core_dump(struct drgn_program *prog, const char *path)
elf_end(elf); elf_end(elf);
elf = NULL; elf = NULL;
if (mappings_capacity > prog->num_mappings) {
/* We don't care if this fails. */
resize_array(&prog->mappings, prog->num_mappings);
}
if (have_nt_taskstruct) { if (have_nt_taskstruct) {
/* /*
* If the core file has an NT_TASKSTRUCT note and is in /proc, * If the core file has an NT_TASKSTRUCT note and is in /proc,
@ -489,7 +331,7 @@ drgn_program_set_core_dump(struct drgn_program *prog, const char *path)
if (fstatfs(prog->core_fd, &fs) == -1) { if (fstatfs(prog->core_fd, &fs) == -1) {
err = drgn_error_create_os("fstatfs", errno, path); err = drgn_error_create_os("fstatfs", errno, path);
if (err) if (err)
goto out_mappings; goto out_segments;
} }
is_proc_kcore = fs.f_type == 0x9fa0; /* PROC_SUPER_MAGIC */ is_proc_kcore = fs.f_type == 0x9fa0; /* PROC_SUPER_MAGIC */
} else { } else {
@ -501,7 +343,7 @@ drgn_program_set_core_dump(struct drgn_program *prog, const char *path)
have_non_zero_phys_addr, have_non_zero_phys_addr,
&prog->vmcoreinfo); &prog->vmcoreinfo);
if (err) if (err)
goto out_mappings; goto out_segments;
have_vmcoreinfo = true; have_vmcoreinfo = true;
} }
@ -512,10 +354,6 @@ drgn_program_set_core_dump(struct drgn_program *prog, const char *path)
drgn_program_update_arch(prog, arch); drgn_program_update_arch(prog, arch);
return NULL; return NULL;
out_mappings:
free(prog->mappings);
prog->mappings = NULL;
prog->num_mappings = 0;
out_segments: out_segments:
drgn_memory_reader_deinit(&prog->reader); drgn_memory_reader_deinit(&prog->reader);
drgn_memory_reader_init(&prog->reader); drgn_memory_reader_init(&prog->reader);
@ -536,59 +374,6 @@ drgn_program_set_kernel(struct drgn_program *prog)
return drgn_program_set_core_dump(prog, "/proc/kcore"); return drgn_program_set_core_dump(prog, "/proc/kcore");
} }
static struct drgn_error *parse_proc_maps(const char *maps_path,
struct file_mapping **mappings,
size_t *num_mappings)
{
struct drgn_error *err;
FILE *file;
size_t capacity = 0;
file = fopen(maps_path, "r");
if (!file)
return drgn_error_create_os("fopen", errno, maps_path);
for (;;) {
unsigned long mapping_start, mapping_end;
uint64_t file_offset;
char *path;
int ret;
ret = fscanf(file, "%lx-%lx %*c%*c%*c%*c %" SCNx64 " "
"%*x:%*x %*d%*[ ]%m[^\n]", &mapping_start,
&mapping_end, &file_offset, &path);
if (ret == EOF) {
break;
} else if (ret == 3) {
/* This is an anonymous mapping; skip it. */
continue;
} else if (ret != 4) {
err = drgn_error_format(DRGN_ERROR_OTHER,
"could not parse %s", maps_path);
goto out;
}
err = append_file_mapping(mapping_start, mapping_end,
file_offset, path, mappings,
num_mappings, &capacity);
if (err && err->code == DRGN_ERROR_STOP) {
/* The mapping was merged, so free the path. */
free(path);
} else if (err) {
goto out;
}
}
if (capacity > *num_mappings) {
/* We don't care if this fails. */
resize_array(mappings, *num_mappings);
}
err = NULL;
out:
fclose(file);
return err;
}
LIBDRGN_PUBLIC struct drgn_error * LIBDRGN_PUBLIC struct drgn_error *
drgn_program_set_pid(struct drgn_program *prog, pid_t pid) drgn_program_set_pid(struct drgn_program *prog, pid_t pid)
{ {
@ -619,19 +404,11 @@ drgn_program_set_pid(struct drgn_program *prog, pid_t pid)
if (err) if (err)
goto out_segments; goto out_segments;
sprintf(buf, "/proc/%ld/maps", (long)pid); prog->pid = pid;
err = parse_proc_maps(buf, &prog->mappings, &prog->num_mappings);
if (err)
goto out_mappings;
prog->flags |= DRGN_PROGRAM_IS_LIVE; prog->flags |= DRGN_PROGRAM_IS_LIVE;
drgn_program_update_arch(prog, DRGN_ARCH_HOST); drgn_program_update_arch(prog, DRGN_ARCH_HOST);
return NULL; return NULL;
out_mappings:
free(prog->mappings);
prog->mappings = NULL;
prog->num_mappings = 0;
out_segments: out_segments:
drgn_memory_reader_deinit(&prog->reader); drgn_memory_reader_deinit(&prog->reader);
drgn_memory_reader_init(&prog->reader); drgn_memory_reader_init(&prog->reader);
@ -644,84 +421,38 @@ out_fd:
return err; return err;
} }
static const Dwfl_Callbacks linux_proc_dwfl_callbacks = {
.find_elf = dwfl_linux_proc_find_elf,
.find_debuginfo = dwfl_standard_find_debuginfo,
};
static struct drgn_error * static const Dwfl_Callbacks userspace_core_dump_dwfl_callbacks = {
userspace_relocation_hook(struct drgn_program *prog, const char *name, .find_elf = dwfl_build_id_find_elf,
Dwarf_Die *die, struct drgn_symbol *sym) .find_debuginfo = dwfl_standard_find_debuginfo,
{ };
Elf *elf;
size_t phnum, i;
uint64_t file_offset;
elf = dwarf_getelf(dwarf_cu_getdwarf(die->cu)); struct drgn_error *drgn_program_get_dwarf(struct drgn_program *prog,
if (elf_getphdrnum(elf, &phnum) != 0) Dwfl **dwfl_ret,
return drgn_error_libelf(); struct drgn_dwarf_index **dindex_ret)
for (i = 0; i < phnum; i++) {
GElf_Phdr phdr_mem, *phdr;
phdr = gelf_getphdr(elf, i, &phdr_mem);
if (!phdr)
return drgn_error_libelf();
if (phdr->p_type == PT_LOAD &&
phdr->p_vaddr <= sym->address &&
sym->address < phdr->p_vaddr + phdr->p_memsz) {
file_offset = (phdr->p_offset + sym->address -
phdr->p_vaddr);
break;
}
}
if (i >= phnum) {
return drgn_error_format(DRGN_ERROR_LOOKUP,
"could not find segment containing %s",
name);
}
for (i = 0; i < prog->num_mappings; i++) {
struct file_mapping *mapping = &prog->mappings[i];
uint64_t mapping_size;
mapping_size = mapping->end - mapping->start;
if (mapping->elf == elf &&
mapping->file_offset <= file_offset &&
file_offset < mapping->file_offset + mapping_size) {
sym->address = (mapping->start + file_offset -
mapping->file_offset);
return NULL;
}
}
return drgn_error_format(DRGN_ERROR_LOOKUP,
"could not find file mapping containing %s",
name);
}
static struct drgn_error *drgn_program_relocation_hook(const char *name,
Dwarf_Die *die,
struct drgn_symbol *sym,
void *arg)
{
struct drgn_program *prog = arg;
if (prog->flags & DRGN_PROGRAM_IS_LINUX_KERNEL)
return kernel_relocation_hook(prog, name, die, sym);
else if (prog->num_mappings)
return userspace_relocation_hook(prog, name, die, sym);
else
return NULL;
}
struct drgn_error *drgn_program_open_debug_info(struct drgn_program *prog,
const char *path, Elf **elf_ret)
{ {
struct drgn_error *err; struct drgn_error *err;
Elf *elf; const Dwfl_Callbacks *dwfl_callbacks;
if (!prog->_dwfl) {
if (prog->flags & DRGN_PROGRAM_IS_LINUX_KERNEL)
dwfl_callbacks = &drgn_dwfl_callbacks;
else if (prog->flags & DRGN_PROGRAM_IS_LIVE)
dwfl_callbacks = &linux_proc_dwfl_callbacks;
else
dwfl_callbacks = &userspace_core_dump_dwfl_callbacks;
prog->_dwfl = dwfl_begin(dwfl_callbacks);
if (!prog->_dwfl)
return drgn_error_libdwfl();
}
if (!prog->dicache) { if (!prog->dicache) {
struct drgn_dwarf_info_cache *dicache; struct drgn_dwarf_info_cache *dicache;
err = drgn_dwarf_info_cache_create(&prog->tindex, err = drgn_dwarf_info_cache_create(&prog->tindex, &dicache);
&dicache);
if (err) if (err)
return err; return err;
err = drgn_program_add_type_finder(prog, drgn_dwarf_type_find, err = drgn_program_add_type_finder(prog, drgn_dwarf_type_find,
@ -739,30 +470,25 @@ struct drgn_error *drgn_program_open_debug_info(struct drgn_program *prog,
return err; return err;
} }
prog->dicache = dicache; prog->dicache = dicache;
dicache->relocation_hook = drgn_program_relocation_hook;
dicache->relocation_arg = prog;
} }
*dwfl_ret = prog->_dwfl;
err = drgn_dwarf_index_open(&prog->dicache->dindex, path, &elf); *dindex_ret = &prog->dicache->dindex;
if (err)
return err;
drgn_program_update_arch(prog, drgn_architecture_from_elf(elf));
if (elf_ret)
*elf_ret = elf;
return NULL; return NULL;
} }
void drgn_program_close_unindexed_debug_info(struct drgn_program *prog) static int drgn_architecture_from_dwfl_module(Dwfl_Module *module,
void **userdatap,
const char *name, Dwarf_Addr base,
void *arg)
{ {
if (prog->dicache) Elf *elf;
drgn_dwarf_index_close_unindexed(&prog->dicache->dindex); GElf_Addr load_base;
}
struct drgn_error *drgn_program_update_debug_info(struct drgn_program *prog) elf = dwfl_module_getelf(module, &load_base);
{ if (!elf)
if (!prog->dicache) return DWARF_CB_OK;
return NULL; drgn_program_update_arch(arg, drgn_architecture_from_elf(elf));
return drgn_dwarf_index_update(&prog->dicache->dindex); return DWARF_CB_ABORT;
} }
LIBDRGN_PUBLIC struct drgn_error * LIBDRGN_PUBLIC struct drgn_error *
@ -770,60 +496,90 @@ drgn_program_load_debug_info(struct drgn_program *prog, const char **paths,
size_t n) size_t n)
{ {
struct drgn_error *err; struct drgn_error *err;
size_t i; Dwfl *dwfl;
struct drgn_dwarf_index *dindex;
for (i = 0; i < n; i++) { err = drgn_program_get_dwarf(prog, &dwfl, &dindex);
err = drgn_program_open_debug_info(prog, paths[i], NULL); if (err)
if (err) { return err;
drgn_program_close_unindexed_debug_info(prog);
if (prog->flags & DRGN_PROGRAM_IS_LINUX_KERNEL) {
err = linux_kernel_load_debug_info(prog, paths, n);
if (err)
return err; return err;
} } else {
} size_t i;
return drgn_program_update_debug_info(prog);
}
static struct drgn_error *load_userspace_debug_info(struct drgn_program *prog) for (i = 0; i < n; i++) {
{ if (!dwfl_report_elf(dwfl, paths[i], paths[i], -1, 0,
struct drgn_error *err; true)) {
struct file_mapping *mappings; err = drgn_error_libdwfl();
size_t i, num_mappings; drgn_remove_unindexed_dwfl_modules(dwfl);
bool success = false; return err;
mappings = prog->mappings;
num_mappings = prog->num_mappings;
for (i = 0; i < num_mappings; i++) {
if (prog->mappings[i].elf)
continue;
err = drgn_program_open_debug_info(prog, mappings[i].path,
&mappings[i].elf);
if (err) {
mappings[i].elf = NULL;
if ((err->code == DRGN_ERROR_OS &&
err->errnum == ENOENT) ||
err == &drgn_not_elf ||
err->code == DRGN_ERROR_MISSING_DEBUG_INFO) {
drgn_error_destroy(err);
continue;
} }
drgn_dwarf_index_close_unindexed(&prog->dicache->dindex); }
err = drgn_dwarf_index_update(dindex, dwfl);
if (err) {
drgn_remove_unindexed_dwfl_modules(dwfl);
return err; return err;
} }
success = true;
} }
if (!success) { if (prog->arch == DRGN_ARCH_AUTO) {
return drgn_error_create(DRGN_ERROR_MISSING_DEBUG_INFO, dwfl_getmodules(dwfl, drgn_architecture_from_dwfl_module, prog,
"no debug information found"); 0);
} }
return drgn_program_update_debug_info(prog); return NULL;
} }
LIBDRGN_PUBLIC struct drgn_error * LIBDRGN_PUBLIC struct drgn_error *
drgn_program_load_default_debug_info(struct drgn_program *prog) drgn_program_load_default_debug_info(struct drgn_program *prog)
{ {
struct drgn_error *err;
Dwfl *dwfl;
struct drgn_dwarf_index *dindex;
if (prog->flags & DRGN_PROGRAM_IS_LINUX_KERNEL) if (prog->flags & DRGN_PROGRAM_IS_LINUX_KERNEL)
return load_kernel_debug_info(prog); return linux_kernel_load_default_debug_info(prog);
else
return load_userspace_debug_info(prog); err = drgn_program_get_dwarf(prog, &dwfl, &dindex);
if (err)
return err;
dwfl_report_begin_add(dwfl);
if (prog->flags & DRGN_PROGRAM_IS_LIVE) {
int ret;
ret = dwfl_linux_proc_report(dwfl, prog->pid);
if (ret == -1) {
err = drgn_error_libdwfl();
} else if (ret) {
err = drgn_error_create_os("dwfl_linux_proc_report",
ret, NULL);
} else {
err = NULL;
}
} else {
Elf *elf;
elf = elf_begin(prog->core_fd, ELF_C_READ, NULL);
if (elf) {
if (dwfl_core_file_report(dwfl, elf, NULL) == -1)
err = drgn_error_libdwfl();
else
err = NULL;
elf_end(elf);
} else {
err = drgn_error_libelf();
}
}
dwfl_report_end(dwfl, NULL, NULL);
if (err)
goto out;
err = drgn_dwarf_index_update(dindex, dwfl);
out:
if (err)
drgn_remove_unindexed_dwfl_modules(dwfl);
return err;
} }
struct drgn_error *drgn_program_init_core_dump(struct drgn_program *prog, struct drgn_error *drgn_program_init_core_dump(struct drgn_program *prog,

View File

@ -12,6 +12,8 @@
#ifndef DRGN_PROGRAM_H #ifndef DRGN_PROGRAM_H
#define DRGN_PROGRAM_H #define DRGN_PROGRAM_H
#include <elfutils/libdwfl.h>
#include "memory_reader.h" #include "memory_reader.h"
#include "symbol_index.h" #include "symbol_index.h"
#include "type_index.h" #include "type_index.h"
@ -42,29 +44,8 @@ struct vmcoreinfo {
uint64_t kaslr_offset; uint64_t kaslr_offset;
}; };
/**
* An ELF file which is mapped into a program.
*
* This is parsed from the @c NT_FILE note of a crash dump or
* <tt>/proc/$pid/maps</tt> of a running program.
*/
struct file_mapping {
/** Path of the file. */
char *path;
/** ELF handle. */
Elf *elf;
/** Starting virtual address in the program's address space. */
uint64_t start;
/**
* One byte after the last virtual address in the program's address
* space.
*/
uint64_t end;
/** Starting offset in the file. */
uint64_t file_offset;
};
struct drgn_dwarf_info_cache; struct drgn_dwarf_info_cache;
struct drgn_dwarf_index;
struct drgn_program { struct drgn_program {
/** @privatesection */ /** @privatesection */
@ -73,9 +54,17 @@ struct drgn_program {
struct drgn_symbol_index sindex; struct drgn_symbol_index sindex;
struct drgn_memory_file_segment *file_segments; struct drgn_memory_file_segment *file_segments;
size_t num_file_segments; size_t num_file_segments;
struct file_mapping *mappings; /*
size_t num_mappings; * Valid iff <tt>flags & DRGN_PROGRAM_IS_LINUX_KERNEL</tt>.
*/
struct vmcoreinfo vmcoreinfo; struct vmcoreinfo vmcoreinfo;
/*
* Valid iff
* <tt>(flags & (DRGN_PROGRAM_IS_LINUX_KERNEL | DRGN_PROGRAM_IS_LIVE)) ==
* DRGN_PROGRAM_IS_LIVE</tt>.
*/
pid_t pid;
Dwfl *_dwfl;
struct drgn_dwarf_info_cache *dicache; struct drgn_dwarf_info_cache *dicache;
int core_fd; int core_fd;
enum drgn_program_flags flags; enum drgn_program_flags flags;
@ -114,12 +103,14 @@ static inline uint64_t drgn_program_word_mask(struct drgn_program *prog)
return prog->arch & DRGN_ARCH_IS_64_BIT ? UINT64_MAX : UINT32_MAX; return prog->arch & DRGN_ARCH_IS_64_BIT ? UINT64_MAX : UINT32_MAX;
} }
struct drgn_error *drgn_program_open_debug_info(struct drgn_program *prog, /*
const char *path, Elf **elf_ret); * Get the @c Dwfl handle and @ref drgn_dwarf_index for a @ref drgn_program.
*
void drgn_program_close_unindexed_debug_info(struct drgn_program *prog); * These are created the first time that this is called.
*/
struct drgn_error *drgn_program_update_debug_info(struct drgn_program *prog); struct drgn_error *drgn_program_get_dwarf(struct drgn_program *prog,
Dwfl **dwfl_ret,
struct drgn_dwarf_index **dindex_ret);
/** @} */ /** @} */

View File

@ -1,7 +1,7 @@
from collections import namedtuple from collections import namedtuple
import os.path import os.path
from tests.elf import ET, SHT from tests.elf import ET, PT, SHT
from tests.elfwriter import ElfSection, create_elf_file from tests.elfwriter import ElfSection, create_elf_file
from tests.dwarf import DW_AT, DW_FORM, DW_TAG from tests.dwarf import DW_AT, DW_FORM, DW_TAG
@ -187,6 +187,11 @@ def compile_dwarf(dies, little_endian=True, bits=64):
], dies) ], dies)
return create_elf_file(ET.EXEC, [ return create_elf_file(ET.EXEC, [
ElfSection(
p_type=PT.LOAD,
vaddr=0xffff0000,
data=b'',
),
ElfSection( ElfSection(
name='.debug_abbrev', name='.debug_abbrev',
sh_type=SHT.PROGBITS, sh_type=SHT.PROGBITS,

View File

@ -578,13 +578,10 @@ class TestSymbols(unittest.TestCase):
class TestCoreDump(unittest.TestCase): class TestCoreDump(unittest.TestCase):
def test_not_elf(self):
prog = Program()
self.assertRaisesRegex(FileFormatError, 'not an ELF file',
prog.set_core_dump, '/dev/null')
def test_not_core_dump(self): def test_not_core_dump(self):
prog = Program() prog = Program()
self.assertRaisesRegex(ValueError, 'not an ELF core file',
prog.set_core_dump, '/dev/null')
with tempfile.NamedTemporaryFile() as f: with tempfile.NamedTemporaryFile() as f:
f.write(create_elf_file(ET.EXEC, [])) f.write(create_elf_file(ET.EXEC, []))
f.flush() f.flush()