libdrgn: debug_info: get address range of reported ET_EXEC files

When explicitly reporting a debugging information file for a userspace
program, userspace_report_debug_info() currently always reports it with
a load address range of [0, 0) (i.e., not actually loaded into the
program). This is because for ET_DYN and ET_REL files, we have to
determine the address range by inspecting the core dump or program
state, which is a bit involved.

However, ET_EXEC is much easier: we can get the address range from the
segment headers. In fact, we already implemented this for vmlinux files,
so we can reuse that with a modification to make it more permissive.

ET_CORE debug info files don't make much sense, but libdwfl seems to
treat a reported ET_CORE file the same as ET_EXEC (see
dwfl_report_elf()), so we do, too.

Unfortunately, most executables on modern Linux distributions are
ET_DYN, but this will at least make testing easier.

Signed-off-by: Omar Sandoval <osandov@osandov.com>
This commit is contained in:
Omar Sandoval 2021-11-19 14:19:32 -08:00
parent c3f31e28f9
commit 4808ef72ee

View File

@ -581,28 +581,52 @@ err:
return DWARF_CB_ABORT; return DWARF_CB_ABORT;
} }
static struct drgn_error *
userspace_report_elf_file(struct drgn_debug_info_load_state *load,
const char *path)
{
struct drgn_error *err;
int fd;
Elf *elf;
err = open_elf_file(path, &fd, &elf);
if (err)
goto err;
GElf_Ehdr ehdr_mem, *ehdr;
ehdr = gelf_getehdr(elf, &ehdr_mem);
if (!ehdr) {
err = drgn_error_libelf();
goto err_close;
}
/*
* We haven't implemented a way to get the load address for dynamically
* loaded or relocatable files, so for now we report those as unloaded.
*/
uint64_t start = 0, end = 0;
if (ehdr->e_type == ET_EXEC || ehdr->e_type == ET_CORE) {
err = elf_address_range(elf, 0, &start, &end);
if (err)
goto err_close;
}
return drgn_debug_info_report_elf(load, path, fd, elf, start, end, NULL,
NULL);
err_close:
elf_end(elf);
close(fd);
err:
return drgn_debug_info_report_error(load, path, NULL, err);
}
static struct drgn_error * static struct drgn_error *
userspace_report_debug_info(struct drgn_debug_info_load_state *load) userspace_report_debug_info(struct drgn_debug_info_load_state *load)
{ {
struct drgn_error *err; struct drgn_error *err;
for (size_t i = 0; i < load->num_paths; i++) { for (size_t i = 0; i < load->num_paths; i++) {
int fd; err = userspace_report_elf_file(load, load->paths[i]);
Elf *elf;
err = open_elf_file(load->paths[i], &fd, &elf);
if (err) {
err = drgn_debug_info_report_error(load, load->paths[i],
NULL, err);
if (err)
return err;
continue;
}
/*
* We haven't implemented a way to get the load address for
* anything reported here, so for now we report it as unloaded.
*/
err = drgn_debug_info_report_elf(load, load->paths[i], fd, elf,
0, 0, NULL, NULL);
if (err) if (err)
return err; return err;
} }
@ -1338,10 +1362,9 @@ struct drgn_error *elf_address_range(Elf *elf, uint64_t bias,
end = segment_end; end = segment_end;
} }
} }
if (start >= end) { /* There were no loadable segments. */
return drgn_error_create(DRGN_ERROR_OTHER, if (start >= end)
"ELF file has no loadable segments"); start = end = 0;
}
*start_ret = start; *start_ret = start;
*end_ret = end; *end_ret = end;
return NULL; return NULL;