mirror of
https://github.com/JakeHillion/drgn.git
synced 2024-12-22 17:23:06 +00:00
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:
parent
a9a2cb7cac
commit
e5874ad18a
File diff suppressed because it is too large
Load Diff
@ -12,7 +12,7 @@
|
||||
#ifndef DRGN_DWARF_INDEX_H
|
||||
#define DRGN_DWARF_INDEX_H
|
||||
|
||||
#include <elfutils/libdw.h>
|
||||
#include <elfutils/libdwfl.h>
|
||||
#include <libelf.h>
|
||||
#include <omp.h>
|
||||
#include <stddef.h>
|
||||
@ -20,6 +20,7 @@
|
||||
|
||||
#include "drgn.h"
|
||||
#include "hash_table.h"
|
||||
#include "string_builder.h"
|
||||
#include "vector.h"
|
||||
|
||||
/**
|
||||
@ -44,38 +45,53 @@
|
||||
* @{
|
||||
*/
|
||||
|
||||
enum {
|
||||
SECTION_SYMTAB,
|
||||
SECTION_DEBUG_ABBREV,
|
||||
SECTION_DEBUG_INFO,
|
||||
SECTION_DEBUG_LINE,
|
||||
SECTION_DEBUG_STR,
|
||||
DRGN_DWARF_INDEX_NUM_SECTIONS,
|
||||
};
|
||||
|
||||
struct drgn_dwarf_index_file {
|
||||
Elf_Data *sections[DRGN_DWARF_INDEX_NUM_SECTIONS];
|
||||
bool failed;
|
||||
/**
|
||||
* drgn-specific data for the @c Dwfl_Module userdata pointer.
|
||||
*
|
||||
* For a newly created userdata, @c indexed is @c false and @c err is @c NULL.
|
||||
* They are updated by @ref drgn_dwarf_index_update(). @c err may be set with
|
||||
* @ref drgn_dwfl_module_userdata_set_error() before @ref
|
||||
* 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.
|
||||
*
|
||||
* @sa drgn_dwfl_find_elf(), drgn_dwfl_section_address()
|
||||
*/
|
||||
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;
|
||||
/*
|
||||
* If this is NULL, then we didn't open the file and don't own the Elf
|
||||
* handle.
|
||||
*/
|
||||
const char *path;
|
||||
/** Error encountered while indexing. */
|
||||
struct drgn_error *err;
|
||||
/** Path of @ref drgn_dwfl_module_userdata::elf. */
|
||||
char *path;
|
||||
/** ELF handle to use. */
|
||||
Elf *elf;
|
||||
Dwarf *dwarf;
|
||||
Elf_Data *rela_sections[DRGN_DWARF_INDEX_NUM_SECTIONS];
|
||||
struct drgn_dwarf_index_file *next;
|
||||
};
|
||||
|
||||
static inline const char *
|
||||
drgn_dwarf_index_file_to_key(struct drgn_dwarf_index_file * const *entry)
|
||||
struct drgn_dwfl_module_userdata *drgn_dwfl_module_userdata_create(void);
|
||||
|
||||
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;
|
||||
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
|
||||
* 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.
|
||||
*/
|
||||
struct drgn_dwarf_index {
|
||||
/** @privatesection */
|
||||
struct drgn_dwarf_index_file_table files;
|
||||
struct drgn_dwarf_index_file *opened_first, *opened_last;
|
||||
struct drgn_dwarf_index_file *indexed_first, *indexed_last;
|
||||
/* The index is sharded to reduce lock contention. */
|
||||
/**
|
||||
* Index shards.
|
||||
*
|
||||
* This is sharded to reduce lock contention.
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* 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,
|
||||
* reads or maps it, and checks that it contains the required debugging
|
||||
* 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.
|
||||
* This parses and indexes the debugging information for all modules in @p dwfl
|
||||
* that have not yet been indexed.
|
||||
*
|
||||
* If this fails, the file is not opened, but previously opened files are not
|
||||
* affected.
|
||||
* On success, @ref drgn_dwfl_module_userdata::indexed is set to @c true for all
|
||||
* 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.
|
||||
*/
|
||||
struct drgn_error *drgn_dwarf_index_open(struct drgn_dwarf_index *dindex,
|
||||
const char *path, Elf **elf);
|
||||
|
||||
/** Close any files which haven't been indexed yet. */
|
||||
void drgn_dwarf_index_close_unindexed(struct drgn_dwarf_index *dindex);
|
||||
struct drgn_error *drgn_dwarf_index_update(struct drgn_dwarf_index *dindex,
|
||||
Dwfl *dwfl);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* relocations, then parses and indexes the debugging information in all of the
|
||||
* 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.
|
||||
* This should be called if @ref drgn_dwarf_index_update() returned an error or
|
||||
* if modules were reported and @ref drgn_dwarf_index_update() was not called.
|
||||
*/
|
||||
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.
|
||||
@ -215,6 +214,9 @@ void drgn_dwarf_index_iterator_init(struct drgn_dwarf_index_iterator *it,
|
||||
*
|
||||
* @param[in] it DWARF index iterator.
|
||||
* @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
|
||||
* 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
|
||||
@ -222,7 +224,7 @@ void drgn_dwarf_index_iterator_init(struct drgn_dwarf_index_iterator *it,
|
||||
*/
|
||||
struct drgn_error *
|
||||
drgn_dwarf_index_iterator_next(struct drgn_dwarf_index_iterator *it,
|
||||
Dwarf_Die *die_ret);
|
||||
Dwarf_Die *die_ret, uint64_t *bias_ret);
|
||||
|
||||
/** @} */
|
||||
|
||||
|
@ -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
|
||||
* 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)
|
||||
return err;
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
err = drgn_dwarf_index_iterator_next(&it, &die);
|
||||
err = drgn_dwarf_index_iterator_next(&it, &die, NULL);
|
||||
if (!err)
|
||||
return &drgn_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,
|
||||
&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)) {
|
||||
err = drgn_type_from_dwarf(dicache, &die, ret);
|
||||
if (err)
|
||||
@ -1408,8 +1408,8 @@ struct drgn_error *drgn_dwarf_type_find(enum drgn_type_kind kind,
|
||||
|
||||
static struct drgn_error *
|
||||
drgn_symbol_from_dwarf_subprogram(struct drgn_dwarf_info_cache *dicache,
|
||||
Dwarf_Die *die, const char *name,
|
||||
struct drgn_symbol *ret)
|
||||
Dwarf_Die *die, uint64_t bias,
|
||||
const char *name, struct drgn_symbol *ret)
|
||||
{
|
||||
struct drgn_error *err;
|
||||
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'",
|
||||
name);
|
||||
}
|
||||
ret->address = low_pc;
|
||||
ret->address = low_pc + bias;
|
||||
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;
|
||||
}
|
||||
|
||||
static struct drgn_error *
|
||||
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_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,
|
||||
"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);
|
||||
if (dicache->relocation_hook) {
|
||||
err = dicache->relocation_hook(name, die, ret,
|
||||
dicache->relocation_arg);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -1492,6 +1480,7 @@ drgn_dwarf_symbol_find(const char *name, size_t name_len, const char *filename,
|
||||
size_t num_tags;
|
||||
struct drgn_dwarf_index_iterator it;
|
||||
Dwarf_Die die;
|
||||
uint64_t bias;
|
||||
|
||||
num_tags = 0;
|
||||
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,
|
||||
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))
|
||||
continue;
|
||||
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:
|
||||
return drgn_symbol_from_dwarf_subprogram(dicache, &die,
|
||||
name, ret);
|
||||
bias, name,
|
||||
ret);
|
||||
case DW_TAG_variable:
|
||||
return drgn_symbol_from_dwarf_variable(dicache, &die,
|
||||
name, ret);
|
||||
bias, name, ret);
|
||||
default:
|
||||
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);
|
||||
dicache->depth = 0;
|
||||
dicache->tindex = tindex;
|
||||
dicache->relocation_hook = NULL;
|
||||
dicache->relocation_arg = NULL;
|
||||
*ret = dicache;
|
||||
return NULL;
|
||||
}
|
||||
|
@ -77,28 +77,6 @@ struct drgn_dwarf_info_cache {
|
||||
int depth;
|
||||
/** Type index. */
|
||||
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. */
|
||||
|
@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#include <errno.h>
|
||||
#include <elfutils/libdw.h>
|
||||
#include <elfutils/libdwfl.h>
|
||||
#include <libelf.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
@ -22,11 +22,6 @@ struct drgn_error drgn_stop = {
|
||||
.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,
|
||||
char *message)
|
||||
{
|
||||
@ -194,3 +189,9 @@ struct drgn_error *drgn_error_libdw(void)
|
||||
return drgn_error_format(DRGN_ERROR_DWARF_FORMAT, "libdw error: %s",
|
||||
dwarf_errmsg(-1));
|
||||
}
|
||||
|
||||
struct drgn_error *drgn_error_libdwfl(void)
|
||||
{
|
||||
return drgn_error_format(DRGN_ERROR_DWARF_FORMAT, "libdwfl error: %s",
|
||||
dwfl_errmsg(-1));
|
||||
}
|
||||
|
@ -37,9 +37,6 @@ struct drgn_object_type;
|
||||
*/
|
||||
extern struct drgn_error drgn_stop;
|
||||
|
||||
/** Not an ELF file. */
|
||||
extern struct drgn_error drgn_not_elf;
|
||||
|
||||
struct string_builder;
|
||||
|
||||
/**
|
||||
@ -67,6 +64,10 @@ struct drgn_error *drgn_error_libelf(void)
|
||||
struct drgn_error *drgn_error_libdw(void)
|
||||
__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.
|
||||
*
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -4,7 +4,7 @@
|
||||
#ifndef DRGN_LINUX_KERNEL_H
|
||||
#define DRGN_LINUX_KERNEL_H
|
||||
|
||||
#include <elfutils/libdw.h>
|
||||
#include <elfutils/libdwfl.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,
|
||||
struct vmcoreinfo *ret);
|
||||
|
||||
struct drgn_error *kernel_relocation_hook(struct drgn_program *prog,
|
||||
const char *name, Dwarf_Die *die,
|
||||
struct drgn_symbol *sym);
|
||||
struct drgn_error *
|
||||
linux_kernel_load_debug_info(struct drgn_program *prog, const char **paths,
|
||||
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 */
|
||||
|
@ -65,41 +65,32 @@ static void drgn_program_update_arch(struct drgn_program *prog,
|
||||
void drgn_program_init(struct drgn_program *prog,
|
||||
enum drgn_architecture_flags arch)
|
||||
{
|
||||
memset(prog, 0, sizeof(*prog));
|
||||
drgn_memory_reader_init(&prog->reader);
|
||||
drgn_type_index_init(&prog->tindex);
|
||||
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->flags = 0;
|
||||
prog->arch = DRGN_ARCH_AUTO;
|
||||
if (arch != DRGN_ARCH_AUTO)
|
||||
drgn_program_update_arch(prog, arch);
|
||||
prog->added_vmcoreinfo_symbol_finder = false;
|
||||
}
|
||||
|
||||
void drgn_program_deinit(struct drgn_program *prog)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
drgn_symbol_index_deinit(&prog->sindex);
|
||||
drgn_type_index_deinit(&prog->tindex);
|
||||
drgn_memory_reader_deinit(&prog->reader);
|
||||
|
||||
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)
|
||||
close(prog->core_fd);
|
||||
|
||||
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 *
|
||||
@ -151,135 +142,6 @@ drgn_program_add_symbol_finder(struct drgn_program *prog,
|
||||
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)
|
||||
{
|
||||
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;
|
||||
enum drgn_architecture_flags arch;
|
||||
bool is_64_bit;
|
||||
size_t phnum, i, mappings_capacity = 0;
|
||||
size_t phnum, i;
|
||||
bool have_non_zero_phys_addr = false;
|
||||
struct drgn_memory_file_segment *current_file_segment;
|
||||
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);
|
||||
if (!ehdr) {
|
||||
err = &drgn_not_elf;
|
||||
goto out_elf;
|
||||
}
|
||||
|
||||
if (ehdr->e_type != ET_CORE) {
|
||||
if (!ehdr || ehdr->e_type != ET_CORE) {
|
||||
err = drgn_error_format(DRGN_ERROR_INVALID_ARGUMENT,
|
||||
"not an ELF core file");
|
||||
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);
|
||||
if (!phdr) {
|
||||
err = drgn_error_libelf();
|
||||
goto out_mappings;
|
||||
goto out_segments;
|
||||
}
|
||||
|
||||
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,
|
||||
false);
|
||||
if (err)
|
||||
goto out_mappings;
|
||||
goto out_segments;
|
||||
if (have_non_zero_phys_addr &&
|
||||
phdr->p_paddr !=
|
||||
(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,
|
||||
true);
|
||||
if (err)
|
||||
goto out_mappings;
|
||||
goto out_segments;
|
||||
}
|
||||
current_file_segment++;
|
||||
} 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));
|
||||
if (!data) {
|
||||
err = drgn_error_libelf();
|
||||
goto out_mappings;
|
||||
goto out_segments;
|
||||
}
|
||||
|
||||
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;
|
||||
desc = (char *)data->d_buf + desc_offset;
|
||||
if (strncmp(name, "CORE", nhdr.n_namesz) == 0) {
|
||||
if (nhdr.n_type == NT_FILE) {
|
||||
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) {
|
||||
if (nhdr.n_type == NT_TASKSTRUCT)
|
||||
have_nt_taskstruct = true;
|
||||
}
|
||||
} else if (strncmp(name, "VMCOREINFO",
|
||||
nhdr.n_namesz) == 0) {
|
||||
err = parse_vmcoreinfo(desc,
|
||||
nhdr.n_descsz,
|
||||
&prog->vmcoreinfo);
|
||||
if (err)
|
||||
goto out_mappings;
|
||||
goto out_segments;
|
||||
have_vmcoreinfo = true;
|
||||
}
|
||||
}
|
||||
@ -474,11 +321,6 @@ drgn_program_set_core_dump(struct drgn_program *prog, const char *path)
|
||||
elf_end(elf);
|
||||
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 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) {
|
||||
err = drgn_error_create_os("fstatfs", errno, path);
|
||||
if (err)
|
||||
goto out_mappings;
|
||||
goto out_segments;
|
||||
}
|
||||
is_proc_kcore = fs.f_type == 0x9fa0; /* PROC_SUPER_MAGIC */
|
||||
} else {
|
||||
@ -501,7 +343,7 @@ drgn_program_set_core_dump(struct drgn_program *prog, const char *path)
|
||||
have_non_zero_phys_addr,
|
||||
&prog->vmcoreinfo);
|
||||
if (err)
|
||||
goto out_mappings;
|
||||
goto out_segments;
|
||||
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);
|
||||
return NULL;
|
||||
|
||||
out_mappings:
|
||||
free(prog->mappings);
|
||||
prog->mappings = NULL;
|
||||
prog->num_mappings = 0;
|
||||
out_segments:
|
||||
drgn_memory_reader_deinit(&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");
|
||||
}
|
||||
|
||||
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 *
|
||||
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)
|
||||
goto out_segments;
|
||||
|
||||
sprintf(buf, "/proc/%ld/maps", (long)pid);
|
||||
err = parse_proc_maps(buf, &prog->mappings, &prog->num_mappings);
|
||||
if (err)
|
||||
goto out_mappings;
|
||||
|
||||
prog->pid = pid;
|
||||
prog->flags |= DRGN_PROGRAM_IS_LIVE;
|
||||
drgn_program_update_arch(prog, DRGN_ARCH_HOST);
|
||||
return NULL;
|
||||
|
||||
out_mappings:
|
||||
free(prog->mappings);
|
||||
prog->mappings = NULL;
|
||||
prog->num_mappings = 0;
|
||||
out_segments:
|
||||
drgn_memory_reader_deinit(&prog->reader);
|
||||
drgn_memory_reader_init(&prog->reader);
|
||||
@ -644,84 +421,38 @@ out_fd:
|
||||
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 *
|
||||
userspace_relocation_hook(struct drgn_program *prog, const char *name,
|
||||
Dwarf_Die *die, struct drgn_symbol *sym)
|
||||
{
|
||||
Elf *elf;
|
||||
size_t phnum, i;
|
||||
uint64_t file_offset;
|
||||
static const Dwfl_Callbacks userspace_core_dump_dwfl_callbacks = {
|
||||
.find_elf = dwfl_build_id_find_elf,
|
||||
.find_debuginfo = dwfl_standard_find_debuginfo,
|
||||
};
|
||||
|
||||
elf = dwarf_getelf(dwarf_cu_getdwarf(die->cu));
|
||||
if (elf_getphdrnum(elf, &phnum) != 0)
|
||||
return drgn_error_libelf();
|
||||
|
||||
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 *drgn_program_get_dwarf(struct drgn_program *prog,
|
||||
Dwfl **dwfl_ret,
|
||||
struct drgn_dwarf_index **dindex_ret)
|
||||
{
|
||||
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) {
|
||||
struct drgn_dwarf_info_cache *dicache;
|
||||
|
||||
err = drgn_dwarf_info_cache_create(&prog->tindex,
|
||||
&dicache);
|
||||
err = drgn_dwarf_info_cache_create(&prog->tindex, &dicache);
|
||||
if (err)
|
||||
return err;
|
||||
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;
|
||||
}
|
||||
prog->dicache = dicache;
|
||||
dicache->relocation_hook = drgn_program_relocation_hook;
|
||||
dicache->relocation_arg = prog;
|
||||
}
|
||||
|
||||
err = drgn_dwarf_index_open(&prog->dicache->dindex, path, &elf);
|
||||
if (err)
|
||||
return err;
|
||||
drgn_program_update_arch(prog, drgn_architecture_from_elf(elf));
|
||||
if (elf_ret)
|
||||
*elf_ret = elf;
|
||||
*dwfl_ret = prog->_dwfl;
|
||||
*dindex_ret = &prog->dicache->dindex;
|
||||
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)
|
||||
drgn_dwarf_index_close_unindexed(&prog->dicache->dindex);
|
||||
}
|
||||
Elf *elf;
|
||||
GElf_Addr load_base;
|
||||
|
||||
struct drgn_error *drgn_program_update_debug_info(struct drgn_program *prog)
|
||||
{
|
||||
if (!prog->dicache)
|
||||
return NULL;
|
||||
return drgn_dwarf_index_update(&prog->dicache->dindex);
|
||||
elf = dwfl_module_getelf(module, &load_base);
|
||||
if (!elf)
|
||||
return DWARF_CB_OK;
|
||||
drgn_program_update_arch(arg, drgn_architecture_from_elf(elf));
|
||||
return DWARF_CB_ABORT;
|
||||
}
|
||||
|
||||
LIBDRGN_PUBLIC struct drgn_error *
|
||||
@ -770,60 +496,90 @@ drgn_program_load_debug_info(struct drgn_program *prog, const char **paths,
|
||||
size_t n)
|
||||
{
|
||||
struct drgn_error *err;
|
||||
size_t i;
|
||||
Dwfl *dwfl;
|
||||
struct drgn_dwarf_index *dindex;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
err = drgn_program_open_debug_info(prog, paths[i], NULL);
|
||||
if (err) {
|
||||
drgn_program_close_unindexed_debug_info(prog);
|
||||
err = drgn_program_get_dwarf(prog, &dwfl, &dindex);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (prog->flags & DRGN_PROGRAM_IS_LINUX_KERNEL) {
|
||||
err = linux_kernel_load_debug_info(prog, paths, n);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return drgn_program_update_debug_info(prog);
|
||||
}
|
||||
} else {
|
||||
size_t i;
|
||||
|
||||
static struct drgn_error *load_userspace_debug_info(struct drgn_program *prog)
|
||||
{
|
||||
struct drgn_error *err;
|
||||
struct file_mapping *mappings;
|
||||
size_t i, num_mappings;
|
||||
bool success = false;
|
||||
|
||||
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;
|
||||
for (i = 0; i < n; i++) {
|
||||
if (!dwfl_report_elf(dwfl, paths[i], paths[i], -1, 0,
|
||||
true)) {
|
||||
err = drgn_error_libdwfl();
|
||||
drgn_remove_unindexed_dwfl_modules(dwfl);
|
||||
return err;
|
||||
}
|
||||
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;
|
||||
}
|
||||
success = true;
|
||||
}
|
||||
if (!success) {
|
||||
return drgn_error_create(DRGN_ERROR_MISSING_DEBUG_INFO,
|
||||
"no debug information found");
|
||||
if (prog->arch == DRGN_ARCH_AUTO) {
|
||||
dwfl_getmodules(dwfl, drgn_architecture_from_dwfl_module, prog,
|
||||
0);
|
||||
}
|
||||
return drgn_program_update_debug_info(prog);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LIBDRGN_PUBLIC struct drgn_error *
|
||||
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)
|
||||
return load_kernel_debug_info(prog);
|
||||
else
|
||||
return load_userspace_debug_info(prog);
|
||||
return linux_kernel_load_default_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,
|
||||
|
@ -12,6 +12,8 @@
|
||||
#ifndef DRGN_PROGRAM_H
|
||||
#define DRGN_PROGRAM_H
|
||||
|
||||
#include <elfutils/libdwfl.h>
|
||||
|
||||
#include "memory_reader.h"
|
||||
#include "symbol_index.h"
|
||||
#include "type_index.h"
|
||||
@ -42,29 +44,8 @@ struct vmcoreinfo {
|
||||
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_index;
|
||||
|
||||
struct drgn_program {
|
||||
/** @privatesection */
|
||||
@ -73,9 +54,17 @@ struct drgn_program {
|
||||
struct drgn_symbol_index sindex;
|
||||
struct drgn_memory_file_segment *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;
|
||||
/*
|
||||
* 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;
|
||||
int core_fd;
|
||||
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;
|
||||
}
|
||||
|
||||
struct drgn_error *drgn_program_open_debug_info(struct drgn_program *prog,
|
||||
const char *path, Elf **elf_ret);
|
||||
|
||||
void drgn_program_close_unindexed_debug_info(struct drgn_program *prog);
|
||||
|
||||
struct drgn_error *drgn_program_update_debug_info(struct drgn_program *prog);
|
||||
/*
|
||||
* Get the @c Dwfl handle and @ref drgn_dwarf_index for a @ref drgn_program.
|
||||
*
|
||||
* These are created the first time that this is called.
|
||||
*/
|
||||
struct drgn_error *drgn_program_get_dwarf(struct drgn_program *prog,
|
||||
Dwfl **dwfl_ret,
|
||||
struct drgn_dwarf_index **dindex_ret);
|
||||
|
||||
/** @} */
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
from collections import namedtuple
|
||||
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.dwarf import DW_AT, DW_FORM, DW_TAG
|
||||
|
||||
@ -187,6 +187,11 @@ def compile_dwarf(dies, little_endian=True, bits=64):
|
||||
], dies)
|
||||
|
||||
return create_elf_file(ET.EXEC, [
|
||||
ElfSection(
|
||||
p_type=PT.LOAD,
|
||||
vaddr=0xffff0000,
|
||||
data=b'',
|
||||
),
|
||||
ElfSection(
|
||||
name='.debug_abbrev',
|
||||
sh_type=SHT.PROGBITS,
|
||||
|
@ -578,13 +578,10 @@ class TestSymbols(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):
|
||||
prog = Program()
|
||||
self.assertRaisesRegex(ValueError, 'not an ELF core file',
|
||||
prog.set_core_dump, '/dev/null')
|
||||
with tempfile.NamedTemporaryFile() as f:
|
||||
f.write(create_elf_file(ET.EXEC, []))
|
||||
f.flush()
|
||||
|
Loading…
Reference in New Issue
Block a user