From: Roland McGrath Date: Sun, 4 Nov 2007 22:30:37 +0000 (-0800) Subject: Old com.redhat.elfutils.roland.notes branch from Monotone. X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=fb65cc365e5d51f2434128467dab554f6921727b;p=thirdparty%2Felfutils.git Old com.redhat.elfutils.roland.notes branch from Monotone. --- diff --git a/NEWS b/NEWS index eb52105a6..ac1aba8d3 100644 --- a/NEWS +++ b/NEWS @@ -27,6 +27,14 @@ readelf: new options --hex-dump (or -x), --strings (or -p) addr2line: new option --symbols (or -S) +libelf: new functions gelf_begin_embedded, gelf_rawnote, + gelf_getdata_memory, gelf_getdata_rawchunk, gelf_getdata_freedata, + gelf_getauxv, gelf_update_auxv; new Elf_Type value ELF_T_AUXV + +libdwfl: new functions dwfl_core_file_report, dwfl_core_file_find_elf; + dwfl_standard_argp supports new option --core-file; + support dynamic symbol tables found via phdrs + Version 0.128: new program: unstrip diff --git a/libdw/ChangeLog b/libdw/ChangeLog index b7cd62851..1642ce85a 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,3 +1,17 @@ +2007-08-04 Roland McGrath + + * libdw.map (ELFUTILS_0.131): Add dwfl_module_build_id, + dwfl_module_report_build_id, dwfl_build_id_find_elf, + dwfl_build_id_find_debuginfo. + +2007-04-19 Roland McGrath + + * libdw.map (ELFUTILS_0.131): New set, inherits from ELFUTILS_0.130. + Add dwfl_register_map, dwfl_register_map_begin, dwfl_register_map_end, + dwfl_register_map_populate, dwfl_core_file_report, + dwfl_core_file_find_elf, dwfl_core_file_register_map, + dwfl_core_file_read_note. + 2007-10-17 Roland McGrath * libdw.h (__deprecated_attribute__): New macro. diff --git a/libdw/libdw.map b/libdw/libdw.map index 8ef5f6333..f0edbf35b 100644 --- a/libdw/libdw.map +++ b/libdw/libdw.map @@ -176,3 +176,19 @@ ELFUTILS_0.130 { local: *; } ELFUTILS_0.127; + +ELFUTILS_0.131 { + global: + dwfl_register_map; + dwfl_register_map_begin; + dwfl_register_map_end; + dwfl_register_map_populate; + + dwfl_core_file_report; + dwfl_core_file_find_elf; + dwfl_core_file_register_map; + dwfl_core_file_read_note; + + local: + *; +} ELFUTILS_0.130; diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog index ea6e4e019..285c7db5a 100644 --- a/libdwfl/ChangeLog +++ b/libdwfl/ChangeLog @@ -1,3 +1,47 @@ +2007-08-04 Roland McGrath + + * core-file.c (dwfl_core_file_report): Use dwfl_module_report_build_id. + (dwfl_core_file_find_elf): Use dwfl_build_id_find_elf. + +2007-05-03 Roland McGrath + + * core-file.c (no_module_overlaps): Renamed to ... + (module_overlapping): ... here. Return pointer instead of bool. + Take new bool argument REORDER; if true, move found module to end. + (offset_from_addr): New function. + (string_from_offset): New function. + (string_from_memory): New function. + (getdata_core): New function. + (addr_from_memory): New function. + (report_link_map, report_r_debug, find_dt_debug): New functions. + (dwfl_core_file_report): Use string_from_offset for soname extraction. + Notice DT_DEBUG in found PT_DYNAMIC sections. + Failing that, look for it in modules previously reported explicitly. + Use report_r_debug to follow link_map list from DT_DEBUG. + (dwfl_find_elf_by_build_id): Free old *FILE_NAME before setting it. + +2007-04-29 Roland McGrath + + * core-file.c (no_module_overlaps): New function. + (dwfl_core_file_report): Use it to exclude duplicate modules. + + * dwfl_module.c (__libdwfl_module_free): Free MOD->cb_data. + + * argp-std.c (corefile_callbacks): Variable removed. + (offline_callbacks): Use its .find_elf here. + (parse_opt): Permit -e and --core together. + +2007-04-08 Roland McGrath + + * register-map.c: New file. + * core-file-register-map.c: New file. + * Makefile.am (libdwfl_a_SOURCES): Add them. + * libdwfl.h (Dwfl_Register_Map): New opaque typedef. + Declare dwfl_register_map_* functions, dwfl_core_file_register_map, + and dwfl_core_file_read_note. + * libdwflP.h (struct Dwfl_Register_Map): New type. + (struct map_register): New type. + 2007-10-23 Roland McGrath * linux-kernel-modules.c (report_kernel_archive): Reorder the kernel @@ -197,10 +241,6 @@ build_id_len, build_id_vaddr. Declare __libdwfl_find_build_id. * dwfl_module.c (__libdwfl_module_free): Free MOD->build_id_bits. - * dwfl_module_getdwarf.c (find_offsets): New function. - (find_dynsym): New function, calls that. - (find_symtab): Call it. - 2007-09-11 Roland McGrath * dwfl_module_addrsym.c: Prefer a later global symbol at the same @@ -286,6 +326,18 @@ 2007-03-12 Roland McGrath + * argp-std.c (OPT_COREFILE): New macro. + (corefile_callbacks): New const variable. + (parse_opt, options): Add --core option. + + * core-file.c: New file. + * Makefile.am (libdwfl_a_SOURCES): Add it. + * libdwfl.h: Declare dwfl_core_file_report, dwfl_core_file_find_elf. + * libdwflP.h: Add INTDECLs. + + * libdwflP.h (struct Dwfl): New member `cb_data'. + (struct Dwfl_Module): Likewise. + * dwfl_module.c (dwfl_report_begin_add): New function broken out of ... (dwfl_report_begin): ... here. Call it. * libdwfl.h: Declare it. @@ -295,6 +347,11 @@ * offline.c: Comment typo fix. +2007-03-10 Roland McGrath + + * linux-proc-maps.c (find_sysinfo_ehdr): Rewritten to use + gelf_getdata_memory and gelf_getauxv. + 2007-03-04 Roland McGrath * linux-kernel-modules.c (KERNEL_MODNAME): New macro for "kernel". diff --git a/libdwfl/Makefile.am b/libdwfl/Makefile.am index 83834cdba..6645315f6 100644 --- a/libdwfl/Makefile.am +++ b/libdwfl/Makefile.am @@ -58,7 +58,9 @@ libdwfl_a_SOURCES = dwfl_begin.c dwfl_end.c dwfl_error.c dwfl_version.c \ argp-std.c find-debuginfo.c \ dwfl_build_id_find_elf.c \ dwfl_build_id_find_debuginfo.c \ - linux-kernel-modules.c linux-proc-maps.c \ + dwfl_build_id_find_elf.c \ + dwfl_build_id_find_debuginfo.c \ + linux-kernel-modules.c linux-proc-maps.c core-file.c \ dwfl_addrmodule.c dwfl_addrdwarf.c \ cu.c dwfl_module_nextcu.c dwfl_nextcu.c dwfl_cumodule.c \ dwfl_module_addrdie.c dwfl_addrdie.c \ @@ -72,7 +74,8 @@ libdwfl_a_SOURCES = dwfl_begin.c dwfl_end.c dwfl_error.c dwfl_version.c \ dwfl_module_getsym.c \ dwfl_module_addrname.c dwfl_module_addrsym.c \ dwfl_module_return_value_location.c \ - dwfl_module_register_names.c + dwfl_module_register_names.c \ + register-map.c core-file-register-map.c if MUDFLAP diff --git a/libdwfl/argp-std.c b/libdwfl/argp-std.c index 113978892..a30b3d382 100644 --- a/libdwfl/argp-std.c +++ b/libdwfl/argp-std.c @@ -52,17 +52,22 @@ #include #include #include +#include +#include /* gettext helper macros. */ #define _(Str) dgettext ("elfutils", Str) -#define OPT_DEBUGINFO 0x100 +#define OPT_DEBUGINFO 0x100 +#define OPT_COREFILE 0x101 static const struct argp_option options[] = { { NULL, 0, NULL, 0, N_("Input selection options:"), 0 }, { "executable", 'e', "FILE", 0, N_("Find addresses in FILE"), 0 }, + { "core", OPT_COREFILE, "COREFILE", 0, + N_("Find addresses from signatures found in COREFILE"), 0 }, { "pid", 'p', "PID", 0, N_("Find addresses in files mapped into process PID"), 0 }, { "linux-process-map", 'M', "FILE", 0, @@ -84,6 +89,9 @@ static const Dwfl_Callbacks offline_callbacks = .debuginfo_path = &debuginfo_path, .section_address = INTUSE(dwfl_offline_section_address), + + /* We use this table for core files too. */ + .find_elf = INTUSE(dwfl_core_file_find_elf), }; static const Dwfl_Callbacks proc_callbacks = @@ -151,8 +159,8 @@ parse_opt (int key, char *arg, struct argp_state *state) else { toomany: - argp_error (state, - "%s", _("only one of -e, -p, -k, or -K allowed")); + argp_error (state, "%s", + _("only one of -e, -p, -k, -K, or --core allowed")); return EINVAL; } } @@ -176,6 +184,7 @@ parse_opt (int key, char *arg, struct argp_state *state) { FILE *f = fopen (arg, "r"); if (f == NULL) + nofile: { int code = errno; argp_failure (state, EXIT_FAILURE, code, @@ -193,6 +202,49 @@ parse_opt (int key, char *arg, struct argp_state *state) goto toomany; break; + case OPT_COREFILE: + { + Dwfl *dwfl = state->hook; + if (dwfl == NULL) + state->hook = dwfl = INTUSE(dwfl_begin) (&offline_callbacks); + /* Permit -e and --core together. */ + else if (dwfl->callbacks != &offline_callbacks + || dwfl->cb_data != NULL) + goto toomany; + + int fd = open64 (arg, O_RDONLY); + if (fd < 0) + goto nofile; + + Elf *core = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, NULL); + if (core == NULL) + { + close (fd); + argp_failure (state, EXIT_FAILURE, 0, + _("cannot read ELF core file: %s"), + elf_errmsg (-1)); + return EIO; + } + + int result = INTUSE(dwfl_core_file_report) (dwfl, core); + if (result < 0) + { + elf_end (core); + close (fd); + return fail (dwfl, result, arg); + } + + /* From now we leak FD and CORE. */ + + if (result == 0) + { + argp_failure (state, EXIT_FAILURE, 0, + _("No modules recognized in core file")); + return ENOENT; + } + } + break; + case 'k': if (state->hook == NULL) { diff --git a/libdwfl/core-file-register-map.c b/libdwfl/core-file-register-map.c new file mode 100644 index 000000000..9e3af7810 --- /dev/null +++ b/libdwfl/core-file-register-map.c @@ -0,0 +1,255 @@ +/* Examine a core file for notes describing register data. + Copyright (C) 2007 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + . */ + +#include +#include "libdwflP.h" + +#include + + +// XXX +static inline bool +ebl_core_note_p (Elf *core __attribute__ ((unused)), + const GElf_Nhdr *nhdr, const char *name) +{ + inline bool check (const char *core_name, size_t core_namesz) + { + if (nhdr->n_namesz == core_namesz) + return !memcmp (name, core_name, core_namesz); + + /* Also cater to buggy old Linux kernels. */ + if (nhdr->n_namesz == core_namesz - 1) + return !memcmp (name, core_name, core_namesz - 1); + + return false; + } + +#define NAME1 "CORE" +#define NAME2 "LINUX" + + return check (NAME1, sizeof NAME1) || check (NAME2, sizeof NAME2); +} + + +/* We've found a PT_NOTE segment. Look at each note. */ +static inline int +handle_note (Dwfl *dwfl, Elf *core, const GElf_Phdr *phdr, + Dwfl_Register_Map *map, int *setno, + GElf_Off *start, GElf_Off *end) +{ + Elf_Data *data = gelf_getdata_rawchunk (core, phdr->p_offset, phdr->p_filesz, + ELF_T_NHDR); + if (data == NULL) + return -1; + + *start = phdr->p_offset; + *end = phdr->p_offset + data->d_size; + + int result = 0; + size_t offset = 0; + GElf_Nhdr nhdr; + size_t name_offset; + size_t desc_offset; + while (offset < data->d_size + && (offset = gelf_getnote (data, offset, + &nhdr, &name_offset, &desc_offset)) > 0) + { + result = ebl_core_note_p (core, &nhdr, data->d_buf + name_offset); + if (result < 0) + break; + + if (result == 0) + { + *start = phdr->p_offset + offset; + continue; + } + + result = dwfl_register_map_populate (map, dwfl, *setno, + nhdr.n_type, 0, nhdr.n_descsz); + if (result < 0) + break; + if (result > 0) + ++*setno; + } + + return result; +} + +// XXX change this interface: return Elf_Data of notes? +int +dwfl_core_file_register_map (dwfl, map, offset, limit) + Dwfl *dwfl; + Dwfl_Register_Map **map; + GElf_Off *offset; + GElf_Off *limit; +{ + if (dwfl == NULL) + return -1; + + Elf *core = dwfl->cb_data; + + GElf_Ehdr ehdr_mem; + GElf_Ehdr *ehdr = gelf_getehdr (core, &ehdr_mem); + if (ehdr == NULL) + { + elf_error: + __libdwfl_seterrno (DWFL_E_LIBELF); + return -1; + } + + *map = dwfl_register_map_begin (); + + int setno = 0; + for (uint_fast16_t i = 0; i < ehdr->e_phnum; ++i) + { + GElf_Phdr phdr_mem; + GElf_Phdr *phdr = gelf_getphdr (core, i, &phdr_mem); + if (phdr == NULL) + goto elf_error; + if (phdr->p_type == PT_NOTE) + { + int result = handle_note (dwfl, core, phdr, *map, + &setno, offset, limit); + if (result < 0) + setno = -1; + if (result != 0) + break; + } + } + + if (setno <= 0) + { + dwfl_register_map_end (*map); + *map = NULL; + } + + return setno; +} + +int +dwfl_core_file_read_note (dwfl, map, offset, limit, + nsets, offsets, sizes, + ident_setno, ident_pos, ident_type, + new_offset, next, out_desc_offset) + Dwfl *dwfl; + Dwfl_Register_Map *map; + GElf_Off offset; + GElf_Off limit; + int nsets; + GElf_Off offsets[nsets]; + GElf_Word sizes[nsets]; + int *ident_setno; + GElf_Word *ident_pos; + Elf_Type *ident_type; + GElf_Off *new_offset; + GElf_Nhdr *next; + GElf_Off *out_desc_offset; +{ + if (dwfl == NULL || map == NULL || nsets <= map->ident_setno - 1) + return -1; + + Elf *core = dwfl->cb_data; + + memset (offsets, 0, nsets * sizeof offsets[0]); + memset (sizes, 0, nsets * sizeof sizes[0]); + *ident_setno = -1; + *ident_pos = 0; + *ident_type = ELF_T_NUM; + + Elf_Data *data = gelf_getdata_rawchunk (core, offset, limit - offset, + ELF_T_NHDR); + if (data == NULL) + return -1; + + int result = 0; + size_t pos = 0; + size_t name_offset; + size_t desc_offset; + while (pos < data->d_size + && (pos = gelf_getnote (data, pos, + next, &name_offset, &desc_offset)) > 0 + && ebl_core_note_p (core, next, data->d_buf + name_offset)) + { + int i; + + for (i = 0; i < map->nsets; ++i) + if (map->types[i] == next->n_type) + break; + if (i == map->nsets) + break; + + if (i == *ident_setno || (i < nsets && sizes[0] != 0)) + { + /* This is a repeat set, must be the next thread. */ + result = 1; + break; + } + + if (likely (i < nsets)) + { + offsets[i] = offset + desc_offset; + sizes[i] = next->n_descsz; + } + + if (i == map->ident_setno - 1) + { + *ident_setno = i; + *ident_pos = map->ident_pos; + *ident_type = map->ident_type; + } + } + + *out_desc_offset = offset + desc_offset; + + if (offset == 0 && limit != 0) + return -1; + + *new_offset = offset + pos; + return result; +} diff --git a/libdwfl/core-file.c b/libdwfl/core-file.c new file mode 100644 index 000000000..99f133ec2 --- /dev/null +++ b/libdwfl/core-file.c @@ -0,0 +1,891 @@ +/* Examine a core file to guess the modules used in the crashed process. + Copyright (C) 2007 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + . */ + +#include +#include "libdwflP.h" + +#include +#include +#include + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + + +/* Determine whether a module already reported in this reporting phase + overlaps START..END. If REORDER, move it to the end of the list + as dwfl_report_module on an existing module would do. */ +static Dwfl_Module * +module_overlapping (Dwfl *dwfl, GElf_Addr start, GElf_Addr end, bool reorder) +{ + Dwfl_Module **tailp = &dwfl->modulelist, **prevp = tailp; + for (Dwfl_Module *mod = *prevp; mod != NULL; mod = *(prevp = &mod->next)) + if (! mod->gc) + { + if ((start >= mod->low_addr && start < mod->high_addr) + || (end >= mod->low_addr && end < mod->high_addr) + || (mod->low_addr >= start && mod->low_addr < end)) + { + if (reorder) + { + *prevp = mod->next; + mod->next = *tailp; + *tailp = mod; + } + return mod; + } + + tailp = &mod->next; + } + + return NULL; +} + + +/* Collected ideas about each module, stored in Dwfl_Module.cb_data. */ +struct core_module_info +{ + GElf_Off offset; /* Start position in the core file. */ + GElf_Word whole_size; /* Zero or size of whole image at offset. */ + GElf_Addr l_name_vaddr; /* l_name file name from dynamic linker. */ +}; + + +static GElf_Off +offset_from_addr (Elf *elf, GElf_Addr addr, GElf_Off *limit) +{ + // XXX optimize with binary search of cached merged regions + + GElf_Ehdr ehdr_mem; + GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem); + if (ehdr == NULL) + return 0; + + GElf_Addr end = 0; + GElf_Off offset = 0; + for (uint_fast16_t i = 0; i < ehdr->e_phnum; ++i) + { + GElf_Phdr phdr_mem; + GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem); + if (phdr == NULL) + break; + if (phdr->p_type != PT_LOAD || phdr->p_vaddr + phdr->p_memsz <= addr) + continue; + if (offset == 0) + offset = addr - phdr->p_vaddr + phdr->p_offset; + else if ((phdr->p_vaddr & -phdr->p_align) > end) + break; + end = phdr->p_vaddr + phdr->p_filesz; + } + + *limit = end - addr + offset; + return offset; +} + +/* Do gelf_rawchunk to get a '\0'-terminated string at + the given offset in the core file. Ignore an empty string. */ +static char * +string_from_offset (Elf *core, GElf_Off string_offset, GElf_Off limit) +{ + GElf_Off offset = string_offset; + +#define STRING_BUF_SIZE 64 + while (offset < limit) + { + const size_t sample_size = MIN (limit - offset, STRING_BUF_SIZE); + char *sample = gelf_rawchunk (core, offset, sample_size); + if (sample == NULL) + break; + char *end = memchr (sample, '\0', sample_size); + if (end == NULL) + { + gelf_freechunk (core, sample); + offset += sample_size; + } + else if (offset == string_offset) + { + if (end != sample) + return sample; + /* It's the empty string. */ + gelf_freechunk (core, sample); + return NULL; + } + else + { + gelf_freechunk (core, sample); + return gelf_rawchunk (core, string_offset, + (offset - string_offset) + (end - sample)); + } + } + + return NULL; +} + +// XXX l_name can be in elided text (.interp), needs module-integrated read here +/* Do gelf_rawchunk to get a '\0'-terminated string at + the given address in the core file memory image. */ +static char * +string_from_memory (Elf *core, GElf_Addr addr) +{ + GElf_Off limit; + GElf_Off offset = offset_from_addr (core, addr, &limit); + return offset == 0 ? NULL : string_from_offset (core, offset, limit); +} + +/* Do gelf_getdata_rawchunk given an address in the core file memory image. */ +static Elf_Data * +getdata_core (Elf *core, GElf_Addr addr, GElf_Word size, Elf_Type type) +{ + GElf_Off limit; + GElf_Off offset = offset_from_addr (core, addr, &limit); + return (limit - offset < size ? NULL + : gelf_getdata_rawchunk (core, offset, size, type)); +} + +/* Fetch one address word from the core file memory image. */ +static GElf_Addr +addr_from_memory (Elf *core, GElf_Addr addr) +{ + const size_t addrsize = gelf_fsize (core, ELF_T_ADDR, 1, EV_CURRENT); + Elf_Data *data = getdata_core (core, addr, addrsize, ELF_T_ADDR); + if (data == NULL) + return 0; + return (addrsize == 4 + ? *(const Elf32_Addr *) data->d_buf + : *(const Elf64_Addr *) data->d_buf); +} + +/* Process a struct link_map extracted from the core file. + Report a module if it describes one we can figure out. */ +static int +report_link_map (int result, Elf *core, Dwfl *dwfl, + GElf_Addr l_addr, GElf_Addr l_name, GElf_Addr l_ld) +{ + /* The l_ld address is a runtime address inside the module, + so we can use that alone to see if we already know this module. + This moves the one found to the end of the order as a side effect. */ + Dwfl_Module *mod = module_overlapping (dwfl, l_ld, l_ld + 1, true); + + if (mod == NULL) + { + /* We have to find the file's phdrs to compute along with l_addr + what its runtime address boundaries are. */ + + char *file_name = string_from_memory (core, l_name); + if (file_name != NULL) + { + mod = INTUSE(dwfl_report_elf) (dwfl, basename (file_name), + file_name, -1, l_addr); + if (mod != NULL) + result = 1; + gelf_freechunk (core, file_name); + } + + return result; + } + + if (mod->cb_data != NULL) + { + /* This is a module we recognized before from the core contents. */ + struct core_module_info *info = mod->cb_data; + info->l_name_vaddr = l_name; + if (mod->name[0] == '[') + { + /* We gave it a boring synthetic name. + Use the basename of its l_name string instead. */ + char *chunk = string_from_memory (core, info->l_name_vaddr); + if (chunk != NULL) + { + char *newname = strdup (basename (chunk)); + if (newname != NULL) + { + free (mod->name); + mod->name = newname; + } + gelf_freechunk (core, chunk); + } + } + } + + return result; +} + +/* Call report_link_map for each struct link_map in the linked list at r_map + in the struct r_debug at R_DEBUG_VADDR. */ +static int +report_r_debug (int result, Elf *core, Dwfl *dwfl, GElf_Addr r_debug_vaddr) +{ + const size_t addrsize = gelf_fsize (core, ELF_T_ADDR, 1, EV_CURRENT); + + /* Skip r_version, to aligned r_map field. */ + GElf_Addr next = addr_from_memory (core, r_debug_vaddr + addrsize); + + while (result >= 0 && next != 0) + { + Elf_Data *data = getdata_core (core, next, addrsize * 4, ELF_T_ADDR); + if (unlikely (data == NULL)) + result = -1; + else + { + GElf_Addr addr; + GElf_Addr name; + GElf_Addr ld; + + if (addrsize == 4) + { + const Elf32_Addr *map = data->d_buf; + addr = map[0]; + name = map[1]; + ld = map[2]; + next = map[3]; + } + else + { + const Elf64_Addr *map = data->d_buf; + addr = map[0]; + name = map[1]; + ld = map[2]; + next = map[3]; + } + + result = report_link_map (result, core, dwfl, addr, name, ld); + } + } + + return result; +} + +/* Find the vaddr of the DT_DEBUG's d_ptr. This is the memory address + where &r_debug was written at runtime. */ +static GElf_Addr +find_dt_debug (Elf *elf, GElf_Addr bias) +{ + GElf_Ehdr ehdr_mem; + GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem); + if (ehdr == NULL) + return 0; + + for (uint_fast16_t i = 0; i < ehdr->e_phnum; ++i) + { + GElf_Phdr phdr_mem; + GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem); + if (phdr == NULL) + break; + if (phdr->p_type == PT_DYNAMIC) + { + Elf_Data *data = gelf_getdata_rawchunk (elf, phdr->p_offset, + phdr->p_filesz, ELF_T_DYN); + if (data == NULL) + continue; + const size_t entsize = gelf_fsize (elf, ELF_T_DYN, 1, EV_CURRENT); + const size_t n = data->d_size / entsize; + for (size_t j = 0; j < n; ++j) + { + GElf_Dyn dyn_mem; + GElf_Dyn *dyn = gelf_getdyn (data, j, &dyn_mem); + if (dyn != NULL && dyn->d_tag == DT_DEBUG) + return phdr->p_vaddr + bias + entsize * j + entsize / 2; + } + } + } + + return 0; +} + + +int +dwfl_core_file_report (Dwfl *dwfl, Elf *core) +{ + if (dwfl == NULL) + return -1; + + int result = 0; + const size_t ehdr_size = gelf_fsize (core, ELF_T_EHDR, 1, EV_CURRENT); + + /* Record when we find a GNU build-ID note. */ + GElf_Off build_id_offset; + GElf_Word build_id_size; + GElf_Addr build_id_vaddr; + inline void handle_build_id (GElf_Off offset, GElf_Word size, GElf_Addr vaddr) + { + build_id_offset = offset; + build_id_size = size; + build_id_vaddr = vaddr; + } + + /* We've found a PT_NOTE segment inside an ELF image. Investigate. */ + inline void handle_note (GElf_Off offset, GElf_Xword filesz, GElf_Addr vaddr) + { + Elf_Data *data = gelf_getdata_rawchunk (core, offset, filesz, ELF_T_NHDR); + if (data == NULL) + return; + size_t pos = 0; + GElf_Nhdr nhdr; + size_t name_offset; + size_t desc_offset; + while (pos < data->d_size + && (pos = gelf_getnote (data, pos, + &nhdr, &name_offset, &desc_offset)) > 0) + if (nhdr.n_type == NT_GNU_BUILD_ID + && nhdr.n_namesz == sizeof "GNU" + && !memcmp (data->d_buf + name_offset, "GNU", sizeof "GNU")) + handle_build_id (offset + desc_offset, nhdr.n_descsz, + vaddr + desc_offset); + } + + /* We've found the PT_DYNAMIC segment inside an ELF image. + Return the absolute vaddr of the SONAME string if we find one. */ + inline GElf_Addr handle_dyn (GElf_Off offset, GElf_Word filesz, + GElf_Addr loadbase, GElf_Addr *strtab_end, + GElf_Addr *r_debug) + { + GElf_Xword soname = 0; + GElf_Addr strtab = 0; + GElf_Xword strsz = 0; + + Elf_Data *data = gelf_getdata_rawchunk (core, offset, filesz, ELF_T_DYN); + if (data == NULL) + return 0; + size_t n = data->d_size / gelf_fsize (core, ELF_T_DYN, 1, EV_CURRENT); + for (size_t i = 0; + i < n && (soname == 0 || strtab == 0 || strsz == 0); + ++i) + { + GElf_Dyn dyn_mem; + GElf_Dyn *dyn = gelf_getdyn (data, i, &dyn_mem); + if (dyn != NULL) + switch (dyn->d_tag) + { + case DT_STRTAB: + strtab = dyn->d_un.d_ptr; + continue; + + case DT_STRSZ: + strsz = dyn->d_un.d_val; + continue; + + case DT_SONAME: + soname = dyn->d_un.d_val; + continue; + + case DT_DEBUG: + if (*r_debug == 0) + *r_debug = dyn->d_un.d_ptr; + continue; + + default: + continue; + + case DT_NULL: + break; + } + break; + } + + if (strtab != 0) + { + *strtab_end = loadbase + strtab + strsz; + if (soname != 0) + return loadbase + strtab + soname; + } + return 0; + } + + /* We think this PT_LOAD segment starts with an ELF header. Investigate. */ + inline GElf_Half consider_segment (GElf_Phdr *phdr, char *header, + GElf_Addr *vaddr_end, + GElf_Off *file_size_available, + GElf_Off *file_size_total, + GElf_Addr *loadbase, + GElf_Addr *dyn_vaddr, + GElf_Word *dyn_filesz) + { + union + { + Elf32_Ehdr e32; + Elf64_Ehdr e64; + } ehdr; + + Elf_Data xlatefrom = + { + .d_type = ELF_T_EHDR, + .d_buf = header, + .d_version = EV_CURRENT, + }; + Elf_Data xlateto = + { + .d_type = ELF_T_EHDR, + .d_buf = &ehdr, + .d_size = sizeof ehdr, + .d_version = EV_CURRENT, + }; + GElf_Half e_type = ET_NONE; + GElf_Off phoff = 0; + GElf_Off shdrs_end = 0; + GElf_Half phnum = 0; + GElf_Half phentsize = 0; + switch (header[EI_CLASS]) + { + case ELFCLASS32: + xlatefrom.d_size = sizeof (Elf32_Ehdr); + if (elf32_xlatetom (&xlateto, &xlatefrom, header[EI_DATA]) != NULL) + { + e_type = ehdr.e32.e_type; + phoff = ehdr.e32.e_phoff; + phnum = ehdr.e32.e_phnum; + phentsize = ehdr.e32.e_phentsize; + shdrs_end = ehdr.e32.e_shoff; + shdrs_end += ehdr.e32.e_shnum * ehdr.e32.e_shentsize; + } + break; + + case ELFCLASS64: + xlatefrom.d_size = sizeof (Elf64_Ehdr); + if (elf64_xlatetom (&xlateto, &xlatefrom, header[EI_DATA]) != NULL) + { + e_type = ehdr.e64.e_type; + phoff = ehdr.e64.e_phoff; + phnum = ehdr.e64.e_phnum; + phentsize = ehdr.e64.e_phentsize; + shdrs_end = ehdr.e64.e_shoff; + shdrs_end += ehdr.e64.e_shnum * ehdr.e64.e_shentsize; + } + break; + } + + /* We're done with the original header we read in. */ + gelf_freechunk (core, header); + + /* We see if we actually have phdrs to look at. */ + if ((e_type != ET_EXEC && e_type != ET_DYN) + || phnum == 0 || phoff < ehdr_size + || phdr->p_filesz < phoff + phnum * phentsize + || phentsize != gelf_fsize (core, ELF_T_PHDR, 1, EV_CURRENT)) + return ET_NONE; + + /* Fetch the raw program headers to translate and examine. */ + char *rawphdrs = gelf_rawchunk (core, phdr->p_offset + phoff, + phnum * phentsize); + if (rawphdrs == NULL) + { + __libdwfl_seterrno (DWFL_E_LIBELF); + result = -1; + return ET_NONE; + } + xlatefrom.d_buf = rawphdrs; + xlatefrom.d_size = phnum * phentsize; + xlatefrom.d_type = ELF_T_PHDR; + union + { + Elf32_Phdr p32[phnum]; + Elf64_Phdr p64[phnum]; + } phdrs; + xlateto.d_buf = &phdrs; + xlateto.d_size = sizeof phdrs; + bool phdrs_ok = false; + switch (ehdr.e32.e_ident[EI_CLASS]) + { + case ELFCLASS32: + phdrs_ok = elf32_xlatetom (&xlateto, &xlatefrom, + ehdr.e32.e_ident[EI_DATA]) != NULL; + break; + + case ELFCLASS64: + phdrs_ok = elf64_xlatetom (&xlateto, &xlatefrom, + ehdr.e64.e_ident[EI_DATA]) != NULL; + break; + } + gelf_freechunk (core, rawphdrs); + + if (!phdrs_ok) + return ET_NONE; + + /* The p_align of a core file PT_LOAD segment gives the ELF page size + of the process that dumped the core. This is what controlled the + interpretation of p_offset and p_vaddr values in PT_LOAD headers + of objects it loaded. */ + const GElf_Xword pagesz = phdr->p_align; + + /* Consider each phdr of the embedded image. */ + *loadbase = phdr->p_vaddr; + bool found_base = false; + GElf_Addr vaddr_limit = 0; + GElf_Off file_should_end = 0; + GElf_Off file_end = 0; + GElf_Off file_end_aligned = 0; + inline void handle_segment (GElf_Word type, + GElf_Addr vaddr, GElf_Off offset, + GElf_Xword filesz, GElf_Xword memsz) + { + switch (type) + { + case PT_LOAD: + /* For load segments, keep track of the bounds of the image. */ + file_should_end = offset + filesz; + if (!found_base && (offset & -pagesz) == 0) + { + *loadbase = phdr->p_vaddr - (vaddr & -pagesz); + found_base = true; + } + vaddr_limit = (*loadbase + vaddr + memsz + pagesz - 1) & -pagesz; + + /* If this segment starts contiguous with the previous one, + it extends the verbatim file image we have to use. */ + if (file_end_aligned == 0 + || (offset & -pagesz) <= file_end_aligned) + { + file_end = offset + filesz; + file_end_aligned = (offset + filesz + pagesz - 1) & -pagesz; + } + break; + + case PT_NOTE: + /* For note segments, inspect the contents if they are within + this segment of the core file. */ + if (offset < phdr->p_filesz && phdr->p_filesz - offset >= filesz) + handle_note (vaddr - phdr->p_vaddr + phdr->p_offset, filesz, + vaddr); + break; + + case PT_DYNAMIC: + /* Save the address of the dynamic section. */ + *dyn_vaddr = *loadbase + vaddr; + *dyn_filesz = filesz; + break; + } + } + + switch (ehdr.e32.e_ident[EI_CLASS]) + { + case ELFCLASS32: + for (uint_fast16_t i = 0; i < phnum && result >= 0; ++i) + handle_segment (phdrs.p32[i].p_type, + phdrs.p32[i].p_vaddr, phdrs.p32[i].p_offset, + phdrs.p32[i].p_filesz, phdrs.p32[i].p_memsz); + break; + + case ELFCLASS64: + for (uint_fast16_t i = 0; i < phnum && result >= 0; ++i) + handle_segment (phdrs.p64[i].p_type, + phdrs.p64[i].p_vaddr, phdrs.p64[i].p_offset, + phdrs.p64[i].p_filesz, phdrs.p64[i].p_memsz); + break; + + default: + abort (); + break; + } + + /* Trim the last segment so we don't bother with zeros in the last page + that are off the end of the file. However, if the extra bit in that + page includes the section headers, keep them. */ + if (file_end < shdrs_end && shdrs_end <= file_end_aligned) + file_end = shdrs_end; + + /* If there were section headers in the file, we'd like to have them. */ + if (shdrs_end != 0 && shdrs_end <= file_end + && shdrs_end > file_should_end) + file_should_end = shdrs_end; + + *file_size_available = file_end; + *file_size_total = file_should_end; + *vaddr_end = vaddr_limit; + return e_type; + } + + GElf_Ehdr ehdr_mem; + GElf_Ehdr *ehdr = gelf_getehdr (core, &ehdr_mem); + if (ehdr == NULL) + { + elf_error: + __libdwfl_seterrno (DWFL_E_LIBELF); + return -1; + } + + size_t earlier_modules = dwfl->nmodules; + GElf_Addr r_debug_vaddr = 0; + for (uint_fast16_t i = 0; i < ehdr->e_phnum; ++i) + { + GElf_Phdr phdr_mem; + GElf_Phdr *phdr = gelf_getphdr (core, i, &phdr_mem); + if (phdr == NULL) + goto elf_error; + + /* Consider read-only segments where we have enough to look at. */ + if (phdr->p_type == PT_LOAD + && (phdr->p_flags & (PF_R|PF_W)) == PF_R + && phdr->p_filesz > ehdr_size) + { + /* Look at the ELF ident bytes to see if this might be an ELF file + image in the format of the core file. */ + char *header = gelf_rawchunk (core, phdr->p_offset, ehdr_size); + if (header == NULL) + goto elf_error; + if (memcmp (header, ehdr->e_ident, EI_VERSION)) + { + /* Doesn't look like the right header. */ + gelf_freechunk (core, header); + continue; + } + + /* Consider this segment. Bail if we get an unexpected error. */ + build_id_offset = 0; + build_id_size = 0; + build_id_vaddr = 0; + GElf_Addr dyn_vaddr = 0; + GElf_Word dyn_filesz = 0; + GElf_Addr vaddr_end; + GElf_Off available_size; + GElf_Off whole_size; + GElf_Addr loadbase; + GElf_Half type = consider_segment (phdr, header, + &vaddr_end, + &available_size, &whole_size, + &loadbase, + &dyn_vaddr, &dyn_filesz); + if (result < 0) + break; + + if (type == ET_NONE) /* Nothing there. */ + continue; + + /* Check if this segment contains the dynamic section. */ + GElf_Addr soname_vaddr = 0; + GElf_Addr dynstr_end = 0; + inline void check_dyn (void) + { + if (soname_vaddr == 0 && dyn_vaddr != 0 + && dyn_vaddr >= phdr->p_vaddr + && dyn_vaddr - phdr->p_vaddr + dyn_filesz <= phdr->p_filesz) + soname_vaddr = handle_dyn (dyn_vaddr - phdr->p_vaddr + + phdr->p_offset, + dyn_filesz, loadbase, &dynstr_end, + &r_debug_vaddr); + } + + check_dyn (); + + /* Skip some following segments if the object we found + has phdrs that say they are part of its segments. */ + const uint_fast16_t considering = i; + GElf_Addr vaddr_start = phdr->p_vaddr & -phdr->p_align; + GElf_Off file_start = phdr->p_offset & -phdr->p_align; + GElf_Off file_end = (phdr->p_offset + phdr->p_filesz + + phdr->p_align - 1) & -phdr->p_align; + while ((phdr->p_type != PT_LOAD + || vaddr_end > phdr->p_vaddr + phdr->p_memsz) + && ++i < ehdr->e_phnum) + { + phdr = gelf_getphdr (core, i, &phdr_mem); + if (phdr == NULL) + goto elf_error; + + if (phdr->p_type != PT_LOAD) + continue; + + check_dyn (); + + /* If we had all of the previous segment, we have this + segment as part of the contiguous file image. */ + GElf_Off segment_start = phdr->p_offset & -phdr->p_align; + GElf_Off segment_end = (phdr->p_offset + phdr->p_filesz + + phdr->p_align - 1) & -phdr->p_align; + if (file_end == segment_start) + file_end = segment_end; + } + if (i == ehdr->e_phnum) + { + /* Somehing is amiss. Punt this supposed object we found. */ + i = considering; + continue; + } + + /* We have as much of the file as the dumped segments contain. */ + available_size = MIN (file_end - file_start, available_size); + + /* We found an object that goes from VADDR_START to VADDR_END. */ + result = 1; + + /* A dumped partial ELF file is only useful to us if it + contained a dynamic segment and a string table. */ + if (available_size < whole_size + && (dynstr_end == 0 + || available_size < dynstr_end - vaddr_start + file_start)) + available_size = 0; + + char *soname = NULL; + if (dynstr_end == 0) + dynstr_end = vaddr_end; + if (soname_vaddr >= vaddr_start && soname_vaddr < dynstr_end) + { + GElf_Off soname_offset = soname_vaddr - vaddr_start + file_start; + GElf_Off limit = dynstr_end - vaddr_start + file_start; + if (limit > file_end) + limit = file_end; + soname = string_from_offset (core, soname_offset, limit); + } + + // XXX maybe record or verify build_id against explicit exe? + if (module_overlapping (dwfl, vaddr_start, vaddr_end, false) == NULL) + { + /* Record what we've learned, for find_elf to use. */ + struct core_module_info *mod_data = malloc (sizeof *mod_data); + if (mod_data == NULL) + result = -1; + else + { + mod_data->offset = file_start; + mod_data->whole_size = available_size; + mod_data->l_name_vaddr = 0; + + Dwfl_Module *mod = INTUSE(dwfl_report_module) + (dwfl, + soname ?: type == ET_EXEC ? "[exe]" + : available_size == 0 ? "[dso]" : "[dumped-dso]", + vaddr_start, vaddr_end); + + if (mod == NULL) + { + free (mod_data); + result = -1; + } + else + { + /* We already eliminated duplicates. */ + assert (mod->cb_data == NULL); + mod->cb_data = mod_data; + } + + if (build_id_size != 0) + { + void *build_id = gelf_rawchunk (core, build_id_offset, + build_id_size); + if (build_id != NULL) + INTUSE(dwfl_module_report_build_id) (mod, build_id, + build_id_size, + build_id_vaddr); + gelf_freechunk (core, build_id); + } + } + } + + if (soname != NULL) + gelf_freechunk (core, soname); + + if (result < 0) + break; + } + } + + if (result >= 0 && r_debug_vaddr == 0 && earlier_modules > 0) + /* Try to find an existing executable module with a DT_DEBUG. */ + for (Dwfl_Module *mod = dwfl->modulelist; + earlier_modules-- > 0 && r_debug_vaddr == 0; + mod = mod->next) + if (mod->main.elf != NULL) + { + GElf_Addr dt_debug = find_dt_debug (mod->main.elf, mod->main.bias); + if (dt_debug != 0) + r_debug_vaddr = addr_from_memory (core, dt_debug); + } + + if (result >= 0 && r_debug_vaddr != 0) + /* Now we can try to find the dynamic linker's library list. */ + result = report_r_debug (result, core, dwfl, r_debug_vaddr); + + if (result >= 0) + dwfl->cb_data = core; + + return result; +} +INTDEF (dwfl_core_file_report) + + +/* Dwfl_Callbacks.find_elf */ + +int +dwfl_core_file_find_elf (Dwfl_Module *mod, + void **userdata __attribute__ ((unused)), + const char *module_name __attribute__ ((unused)), + Dwarf_Addr base __attribute__ ((unused)), + char **file_name, Elf **elfp) +{ + Elf *core = mod->dwfl->cb_data; + struct core_module_info *info = mod->cb_data; + + int fd = -1; + *file_name = NULL; + + /* If we have the whole image in the core file, just use it directly. */ + if (info != NULL && info->whole_size != 0) + *elfp = gelf_begin_embedded (ELF_C_READ_MMAP_PRIVATE, core, + info->offset, info->whole_size); + + /* If we found a build ID, try to follow that. */ + if (*elfp == NULL && mod->build_id_len > 0) + { + fd = INTUSE(dwfl_build_id_find_elf) (mod, NULL, NULL, 0, + file_name, elfp); + if (fd >= 0) + return fd; + } + + /* If we found the dynamic linker's idea of the file name, report that. */ + if (info != NULL && info->l_name_vaddr != 0) + { + char *chunk = string_from_memory (core, info->l_name_vaddr); + if (chunk != NULL) + { + *file_name = strdup (chunk); + gelf_freechunk (core, chunk); + } + } + + return fd; +} +INTDEF (dwfl_core_file_find_elf) diff --git a/libdwfl/dwfl_module.c b/libdwfl/dwfl_module.c index 434f3ff90..f6b6353a3 100644 --- a/libdwfl/dwfl_module.c +++ b/libdwfl/dwfl_module.c @@ -104,6 +104,12 @@ __libdwfl_module_free (Dwfl_Module *mod) if (mod->build_id_bits != NULL) free (mod->build_id_bits); + + if (mod->build_id_bits != NULL) + free (mod->build_id_bits); + + if (mod->cb_data != NULL) + free (mod->cb_data); free (mod->name); free (mod); } diff --git a/libdwfl/libdwfl.h b/libdwfl/libdwfl.h index 5438ee2df..cd1d97129 100644 --- a/libdwfl/libdwfl.h +++ b/libdwfl/libdwfl.h @@ -62,6 +62,9 @@ typedef struct Dwfl_Module Dwfl_Module; /* Handle describing a line record. */ typedef struct Dwfl_Line Dwfl_Line; +/* Handle for a register map. */ +typedef struct Dwfl_Register_Map Dwfl_Register_Map; + /* Callbacks. */ typedef struct { @@ -337,6 +340,21 @@ extern int dwfl_linux_proc_find_elf (Dwfl_Module *mod, void **userdata, const char *module_name, Dwarf_Addr base, char **file_name, Elf **); + +/* Examine an open ET_CORE file to guess the modules used in the crashed + process. When the core file appears to contain whole or partial images + of loaded ELF files, those are identified as modules. When the core + image contains enough information, module names may match DSO SONAMEs. */ +extern int dwfl_core_file_report (Dwfl *dwfl, Elf *core); + +/* Special find_elf callback for use with dwfl_core_file_report. When the + core file contains a complete ELF image, this will use it directly. + Otherwise, it may find enough information to offer a file name. */ +extern int dwfl_core_file_find_elf (Dwfl_Module *mod, void **userdata, + const char *module_name, Dwarf_Addr base, + char **file_name, Elf **); + + /* Standard argument parsing for using a standard callback set. */ struct argp; extern const struct argp *dwfl_standard_argp (void) __attribute__ ((const)); @@ -512,6 +530,84 @@ extern int dwfl_module_register_names (Dwfl_Module *mod, void *arg); +/*** Register map handling functions ***/ + +/* Create an empty register map object. */ +extern Dwfl_Register_Map *dwfl_register_map_begin (void); + +/* Clean up and free a register map object. */ +extern void dwfl_register_map_end (Dwfl_Register_Map *); + +/* Populate the given register map with one set of registers you + have access to. REF supplies the machine backend that recognizes + the note formats. N_TYPE is the field from GElf_Nhdr for a core + file note that would contain this register data. OFFSET is the + byte offset into the note contents corresponding to the register + data you have, and SIZE is the number of bytes of that data. + + Returns -1 for unexpected errors. Returns 0 if N_TYPE is + recognized but has no DWARF registers or is wholly redundant. + Otherwise, returns one more than the highest DWARF register number + now described in MAP. SETNO will be returned by dwfl_register_map + to refer to this register set. */ + +extern int dwfl_register_map_populate (Dwfl_Register_Map *map, Dwfl *ref, + int setno, + GElf_Word n_type, + GElf_Word offset, + GElf_Word size); + +/* Look up a DWARF register number in the given register map. + + Returns -1 if REGNO is not described in MAP. Otherwise, returns + the register set number containing REGNO and sets *OFFSET to its + byte position within that register set's data. */ + +extern int dwfl_register_map (Dwfl_Register_Map *map, int regno, + GElf_Word *offset) + __nonnull_attribute__ (3); + + +/* Create and populate a register map from note types found in a core file, + previously opened using dwfl_core_file_report. Returns the number of + register sets used in the map, or -1 for errors. + + On success, OFFSET is filled with the location in the core file + of the first note providing thread register information, and + LIMIT is filled with the location after the last such note. */ + +extern int dwfl_core_file_register_map (Dwfl *dwfl, Dwfl_Register_Map **result, + GElf_Off *offset, GElf_Off *limit) + __nonnull_attribute__ (2, 3, 4); + +/* Examine notes starting at OFFSET and not exceeding LIMIT that + provide register data for one thread. Returns -1 for errors. + + On success, OFFSETS[] and SIZES[] are filled with the file + locations of the note data for the NSETS register sets described + by MAP. IDENT_SETNO, IDENT_POS, and IDENT_TYPE are filled to + describe where in register set data to find a moniker for this + thread. NEW_OFFSET is filled with the file position following + those notes. NEXT and DESC_OFFSET are filled to describe the + next note at *NEW_OFFSET. Returns 1 if there may be additional + threads in following notes. Returns 0 if following notes (if + any) have only non-thread data. */ + +extern int dwfl_core_file_read_note (Dwfl *dwfl, Dwfl_Register_Map *map, + GElf_Off offset, GElf_Off limit, + int nsets, + GElf_Off offsets[nsets], + GElf_Word sizes[nsets], + int *ident_setno, + GElf_Word *ident_pos, + Elf_Type *ident_type, + // XXX non-reg info? + GElf_Off *new_offset, + GElf_Nhdr *next, GElf_Off *desc_offset) + __nonnull_attribute__ (6, 7, 8, 9, 10, 11, 12, 13); + + + #ifdef __cplusplus } #endif diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h index bbb56aac5..54a3d727a 100644 --- a/libdwfl/libdwflP.h +++ b/libdwfl/libdwflP.h @@ -106,6 +106,9 @@ struct Dwfl { const Dwfl_Callbacks *callbacks; + /* Data hook for library-supplied reporting calls and find_elf hooks. */ + void *cb_data; + Dwfl_Module *modulelist; /* List in order used by full traversals. */ Dwfl_Module **modules; @@ -132,6 +135,7 @@ struct Dwfl_Module Dwfl *dwfl; struct Dwfl_Module *next; /* Link on Dwfl.modulelist. */ + void *cb_data; /* For reporting calls' find_elf hooks. */ void *userdata; char *name; /* Iterator name for this module. */ @@ -223,6 +227,26 @@ struct dwfl_arange }; +struct Dwfl_Register_Map +{ + int ident_setno; /* Biased by 1. */ + GElf_Word ident_pos; + Elf_Type ident_type; + + int nsets; + GElf_Word *types; + + int first; + int limit; + struct map_register *regs; +}; + +struct map_register +{ + Dwarf_Half setno:13; /* Biased by 1. */ + Dwarf_Half offset; +}; + extern void __libdwfl_module_free (Dwfl_Module *mod) internal_function; @@ -337,6 +361,8 @@ INTDECL (dwfl_linux_kernel_report_modules) INTDECL (dwfl_linux_kernel_report_offline) INTDECL (dwfl_offline_section_address) INTDECL (dwfl_module_relocate_address) +INTDECL (dwfl_core_file_report) +INTDECL (dwfl_core_file_find_elf) /* Leading arguments standard to callbacks passed a Dwfl_Module. */ #define MODCB_ARGS(mod) (mod), &(mod)->userdata, (mod)->name, (mod)->low_addr diff --git a/libdwfl/linux-proc-maps.c b/libdwfl/linux-proc-maps.c index 6eaed39ad..60e9b58db 100644 --- a/libdwfl/linux-proc-maps.c +++ b/libdwfl/linux-proc-maps.c @@ -65,6 +65,7 @@ #define PROCMAPSFMT "/proc/%d/maps" #define PROCMEMFMT "/proc/%d/mem" #define PROCAUXVFMT "/proc/%d/auxv" +#define PROCEXEFMT "/proc/%d/exe" /* Search /proc/PID/auxv for the AT_SYSINFO_EHDR tag. */ @@ -81,47 +82,87 @@ find_sysinfo_ehdr (pid_t pid, GElf_Addr *sysinfo_ehdr) if (fd < 0) return errno == ENOENT ? 0 : errno; - ssize_t nread; - do + /* Read the whole file. */ + char buffer[1024]; + ssize_t nread = read (fd, buffer, sizeof buffer); + char *buf = buffer; + if (nread == sizeof buffer) + { + size_t size = sizeof buffer; + buf = NULL; + do + { + free (buf); + size *= 2; + buf = malloc (size); + if (buf == NULL) + nread = -1; + else + nread = pread64 (fd, buf, size, 0); + } while ((size_t) nread == size); + } + close (fd); + + if (nread > 0) { union { - char buffer[sizeof (long int) * 2 * 64]; - Elf64_auxv_t a64[sizeof (long int) * 2 * 64 / sizeof (Elf64_auxv_t)]; - Elf32_auxv_t a32[sizeof (long int) * 2 * 32 / sizeof (Elf32_auxv_t)]; - } d; - nread = read (fd, &d, sizeof d); - if (nread > 0) + Elf64_Ehdr e64; + Elf32_Ehdr e32; + char ident[EI_NIDENT]; + } u; + Elf *elf; + + /* We need a representative ELF header to set the format of things. */ + if (asprintf (&fname, PROCEXEFMT, pid) < 0) + elf = NULL; + else { - switch (sizeof (long int)) + fd = open64 (fname, O_RDONLY); + free (fname); + if (fd < 0) + elf = NULL; + memset (&u, 0, sizeof u); + ssize_t hdr_read = read (fd, u.ident, sizeof u.ident); + close (fd); + if (hdr_read < (ssize_t) sizeof u.ident) + elf = NULL; + else + elf = elf_memory (u.ident, sizeof u); + } + + if (elf == NULL) + nread = -1; + else + { + Elf_Data *data = gelf_getdata_memory (elf, buf, nread, + ELF_T_AUXV, NULL); + if (data == NULL) + nread = -1; + else { - case 4: - for (size_t i = 0; (char *) &d.a32[i] < &d.buffer[nread]; ++i) - if (d.a32[i].a_type == AT_SYSINFO_EHDR) - { - *sysinfo_ehdr = d.a32[i].a_un.a_val; - nread = 0; - break; - } - break; - case 8: - for (size_t i = 0; (char *) &d.a64[i] < &d.buffer[nread]; ++i) - if (d.a64[i].a_type == AT_SYSINFO_EHDR) - { - *sysinfo_ehdr = d.a64[i].a_un.a_val; - nread = 0; - break; - } - break; - default: - abort (); - break; + size_t nauxv = data->d_size / gelf_fsize (elf, ELF_T_AUXV, 1, + EV_CURRENT); + for (size_t i = 0; i < nauxv; ++i) + { + GElf_auxv_t auxv_mem; + GElf_auxv_t *auxv = gelf_getauxv (data, i, &auxv_mem); + if (auxv != NULL && auxv->a_type == AT_SYSINFO_EHDR) + { + *sysinfo_ehdr = auxv->a_un.a_val; + break; + } + } + + //gelf_freedata (data); } + + elf_end (elf); } } - while (nread > 0); - close (fd); + if (buf != buffer) + free (buf); return nread < 0 ? errno : 0; } diff --git a/libdwfl/register-map.c b/libdwfl/register-map.c new file mode 100644 index 000000000..ad1505192 --- /dev/null +++ b/libdwfl/register-map.c @@ -0,0 +1,266 @@ +/* Handle register maps. + Copyright (C) 2007 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + . */ + +#include +#include "libdwflP.h" + + +Dwfl_Register_Map * +dwfl_register_map_begin (void) +{ + Dwfl_Register_Map *map = calloc (1, sizeof *map); + + if (map == NULL) + __libdwfl_seterrno (DWFL_E_NOMEM); + + return map; +} + + +void +dwfl_register_map_end (map) + Dwfl_Register_Map *map; +{ + if (map != NULL) + { + free (map->types); + free (map->regs); + free (map); + } +} + +static int +expand_map (Dwfl_Register_Map *map, int first, int limit) +{ + if (map->regs == NULL) + { + map->regs = malloc (sizeof map->regs[0]); + if (unlikely (map->regs == NULL)) + return -1; + map->first = first; + map->limit = limit; + } + else if (first < map->first) + { + struct map_register *regs + = realloc (map->regs, (map->limit - first) * sizeof regs[0]); + if (unlikely (regs == NULL)) + return -1; + map->regs = memset (regs, 0, (map->first - first) * sizeof regs[0]); + map->first = first; + } + else if (limit > map->limit) + { + struct map_register *regs + = realloc (map->regs, (limit - map->first) * sizeof regs[0]); + if (unlikely (regs == NULL)) + return -1; + memset (®s[map->limit - map->first], 0, + (limit - map->limit) * sizeof regs[0]); + map->limit = limit; + map->regs = regs; + } + return 0; +} + +int +dwfl_register_map_populate (map, ref, setno, type, offset, size) + Dwfl_Register_Map *map; + Dwfl *ref; + int setno; + GElf_Word type; + GElf_Word offset; + GElf_Word size; +{ + if (map == NULL || ref == NULL) + return -1; + + Dwfl_Module *mod = ref->modulelist; /* XXX */ + GElf_Addr base; + while (dwfl_module_getelf (mod, &base) == NULL) + mod = mod->next; + Dwfl_Error error = __libdwfl_module_getebl (mod); /* XXX */ + Ebl *ebl = mod->ebl; + if (error != DWFL_E_NOERROR) + { + __libdwfl_seterrno (error); + return -1; + } + + size_t nregloc; + size_t nitem; + const Ebl_Register_Location *reglocs; + const Ebl_Core_Item *items; + GElf_Word regs_offset; + int result = ebl_core_note (ebl, type, offset + size, ®s_offset, + &nregloc, ®locs, &nitem, &items); + if (result < 0) + { + __libdwfl_seterrno (DWFL_E_LIBEBL); + return -1; + } + + inline void install_reg (struct map_register *reg, + const Ebl_Register_Location *loc, uint_fast16_t j) + { + reg->setno = setno + 1; + reg->offset = regs_offset + loc->offset - offset; + if (loc->bits % 8 == 0) + reg->offset += (loc->bits / 8 + loc->pad) * j; + else + abort (); /* XXX ia64 pr 1-bit */ + } + + if (result > 0) + { + result = 0; + + int overlap_setno = -1; + size_t noverlap = 0; + size_t total_regs = 0; + for (size_t i = 0; i < nregloc; ++i) + { + const Ebl_Register_Location *loc = ®locs[i]; + + int first = loc->regno; + int limit = first + loc->count; + assert (first < limit); + result = expand_map (map, first, limit); + if (result < 0) + break; + + for (uint_fast16_t j = 0; j < loc->count; ++j) + { + struct map_register *reg + = &map->regs[loc->regno + j - map->first]; + + if (reg->setno != 0 && (overlap_setno < 0 + || overlap_setno == reg->setno)) + { + ++noverlap; + overlap_setno = reg->setno; + } + else + install_reg (reg, loc, j); + } + + total_regs += loc->count; + result = map->limit; + } + + if (result > 0 && noverlap > 0) + { + /* We overlapped with an existing set. + See if either the old or the new set is redundant. */ + + if (noverlap == total_regs) + /* The new set is redundant. Leave it out. */ + result = 0; + else + /* Install the new set, overriding the old. */ + for (size_t i = 0; i < nregloc; ++i) + { + const Ebl_Register_Location *loc = ®locs[i]; + for (uint_fast16_t j = 0; j < loc->count; ++j) + install_reg (&map->regs[loc->regno + j - map->first], + loc, j); + } + } + + if (result >= 0 && map->ident_setno == 0) + /* Look for the moniker item. */ + for (size_t i = 0; i < nitem; ++i) + if (items[i].thread_identifier && offset <= items[i].offset) + { + map->ident_setno = setno + 1; + map->ident_type = items[i].type; + map->ident_pos = items[i].offset - offset; + if (result == 0) + result = map->limit ?: 1; + break; + } + } + + if (result > 0) + { + /* Record the set number and n_type value. */ + + if (map->nsets <= setno) + { + GElf_Word *types = realloc (map->types, + (setno + 1) * sizeof types[0]); + if (unlikely (types == NULL)) + { + __libdwfl_seterrno (DWFL_E_NOMEM); + return -1; + } + map->nsets = setno + 1; + map->types = memset (types, 0xff, setno * sizeof types[0]); + } + + map->types[setno] = type; + } + + return result; +} + +int +dwfl_register_map (map, regno, offset) + Dwfl_Register_Map *map; + int regno; + GElf_Word *offset; +{ + if (unlikely (map == NULL || regno < map->first || regno >= map->limit)) + return -1; + + const struct map_register *reg = &map->regs[regno - map->first]; + + *offset = reg->offset; + return reg->setno - 1; /* Unused slot is 0 => -1. */ +} diff --git a/libelf/ChangeLog b/libelf/ChangeLog index 6860fa71e..364eb82c4 100644 --- a/libelf/ChangeLog +++ b/libelf/ChangeLog @@ -1,3 +1,28 @@ +2007-03-12 Roland McGrath + + * gelf_begin_embedded.c: New file. + * Makefile.am (libelf_a_SOURCES): Add it. + * libelf.map (ELFUTILS_1.3): Add gelf_begin_embedded. + * gelf.h: Declare it. + + * elf_begin.c (dup_elf): Renamed to ... + (__libelf_dup_elf): ... here, made global. + Take two new args with offset, size of image embedded in REF. + (elf_begin): Update callers. + * libelfP.h: Declare it. + + * libelfP.h (struct Elf): Move ar.children member out of union. + * elf_readall.c (set_address): Work on nonarchives with children. + * common.h (libelf_acquire_all, libelf_release_all): Likewise. + * elf_end.c (elf_end): Likewise, and handle non-archive parent. + +2007-03-09 Roland McGrath + + * gelf_getdata_memory.c: New file. + * Makefile.am (libelf_a_SOURCES): Add them. + * gelf.h: Declare those functions. + * libelf.map (ELFUTILS_1.3): Add them. + 2007-11-03 Roland McGrath * libelf.h (Elf_Data): Comment fix. diff --git a/libelf/Makefile.am b/libelf/Makefile.am index 58c9b5a80..acd063e67 100644 --- a/libelf/Makefile.am +++ b/libelf/Makefile.am @@ -94,12 +94,14 @@ libelf_a_SOURCES = elf_version.c elf_hash.c elf_error.c elf_fill.c \ gelf_update_verdaux.c \ elf_getshnum.c elf_getshstrndx.c \ gelf_checksum.c elf32_checksum.c elf64_checksum.c \ + gelf_getdata_memory.c gelf_getdata_rawchunk.c \ libelf_crc32.c libelf_next_prime.c \ elf_clone.c \ gelf_getlib.c gelf_update_lib.c \ elf32_offscn.c elf64_offscn.c gelf_offscn.c \ elf_getaroff.c \ - elf_gnu_hash.c + elf_gnu_hash.c \ + gelf_begin_embedded.c if !MUDFLAP libelf_pic_a_SOURCES = diff --git a/libelf/common.h b/libelf/common.h index 22fcfab6f..af1fce54b 100644 --- a/libelf/common.h +++ b/libelf/common.h @@ -1,5 +1,5 @@ /* Common definitions for handling files in memory or only on disk. - Copyright (C) 1998, 1999, 2000, 2002, 2005 Red Hat, Inc. + Copyright (C) 1998, 1999, 2000, 2002, 2005, 2007 Red Hat, Inc. This file is part of Red Hat elfutils. Written by Ulrich Drepper , 1998. @@ -116,16 +116,13 @@ libelf_acquire_all (Elf *elf) { rwlock_wrlock (elf->lock); - if (elf->kind == ELF_K_AR) + Elf *child = elf->children; + + while (child != NULL) { - Elf *child = elf->state.ar.children; - - while (child != NULL) - { - if (child->ref_count != 0) - libelf_acquire_all (child); - child = child->next; - } + if (child->ref_count != 0) + libelf_acquire_all (child); + child = child->next; } } @@ -133,16 +130,13 @@ libelf_acquire_all (Elf *elf) static void libelf_release_all (Elf *elf) { - if (elf->kind == ELF_K_AR) + Elf *child = elf->children; + + while (child != NULL) { - Elf *child = elf->state.ar.children; - - while (child != NULL) - { - if (child->ref_count != 0) - libelf_release_all (child); - child = child->next; - } + if (child->ref_count != 0) + libelf_release_all (child); + child = child->next; } rwlock_unlock (elf->lock); diff --git a/libelf/elf_begin.c b/libelf/elf_begin.c index 13f965f7c..b6e7e7438 100644 --- a/libelf/elf_begin.c +++ b/libelf/elf_begin.c @@ -938,8 +938,10 @@ __libelf_next_arhdr (elf) /* We were asked to return a clone of an existing descriptor. This function must be called with the lock on the parent descriptor being held. */ -static Elf * -dup_elf (int fildes, Elf_Cmd cmd, Elf *ref) +Elf * +internal_function +__libelf_dup_elf (int fildes, Elf_Cmd cmd, Elf *ref, + GElf_Off start_offset, GElf_Off maximum_size) { struct Elf *result; @@ -969,31 +971,36 @@ dup_elf (int fildes, Elf_Cmd cmd, Elf *ref) /* Now it is time to distinguish between reading normal files and archives. Normal files can easily be handled be incrementing the reference counter and return the same descriptor. */ - if (ref->kind != ELF_K_AR) + if (ref->kind != ELF_K_AR && start_offset == 0) { ++ref->ref_count; return ref; } - /* This is an archive. We must create a descriptor for the archive - member the internal pointer of the archive file desriptor is - pointing to. First read the header of the next member if this - has not happened already. */ - if (ref->state.ar.elf_ar_hdr.ar_name == NULL - && __libelf_next_arhdr (ref) != 0) - /* Something went wrong. Maybe there is no member left. */ - return NULL; + if (ref->kind == ELF_K_AR) + { + /* This is an archive. We must create a descriptor for the archive + member the internal pointer of the archive file desriptor is + pointing to. First read the header of the next member if this + has not happened already. */ + if (ref->state.ar.elf_ar_hdr.ar_name == NULL + && __libelf_next_arhdr (ref) != 0) + /* Something went wrong. Maybe there is no member left. */ + return NULL; + + start_offset = ref->state.ar.offset + sizeof (struct ar_hdr); + maximum_size = ref->state.ar.elf_ar_hdr.ar_size; + } /* We have all the information we need about the next archive member. Now create a descriptor for it. */ - result = read_file (fildes, ref->state.ar.offset + sizeof (struct ar_hdr), - ref->state.ar.elf_ar_hdr.ar_size, cmd, ref); + result = read_file (fildes, start_offset, maximum_size, cmd, ref); /* Enlist this new descriptor in the list of children. */ if (result != NULL) { - result->next = ref->state.ar.children; - ref->state.ar.children = result; + result->next = ref->children; + ref->children = result; } return result; @@ -1075,7 +1082,7 @@ elf_begin (fildes, cmd, ref) case ELF_C_READ_MMAP: if (ref != NULL) /* Duplicate the descriptor. */ - retval = dup_elf (fildes, cmd, ref); + retval = __libelf_dup_elf (fildes, cmd, ref, 0, 0); else /* Create descriptor for existing file. */ retval = read_file (fildes, 0, ~((size_t) 0), cmd, NULL); @@ -1097,7 +1104,7 @@ elf_begin (fildes, cmd, ref) } else /* Duplicate this descriptor. */ - retval = dup_elf (fildes, cmd, ref); + retval = __libelf_dup_elf (fildes, cmd, ref, 0, 0); } else /* Create descriptor for existing file. */ diff --git a/libelf/elf_end.c b/libelf/elf_end.c index 5112eaea4..7a813e973 100644 --- a/libelf/elf_end.c +++ b/libelf/elf_end.c @@ -92,11 +92,11 @@ elf_end (elf) if (elf->state.ar.ar_sym != (Elf_Arsym *) -1l) free (elf->state.ar.ar_sym); elf->state.ar.ar_sym = NULL; - - if (elf->state.ar.children != NULL) - return 0; } + if (elf->children != NULL) + return 0; + /* Remove this structure from the children list. */ parent = elf->parent; if (parent != NULL) @@ -110,11 +110,11 @@ elf_end (elf) rwlock_rdlock (parent->lock); rwlock_wrlock (elf->lock); - if (parent->state.ar.children == elf) - parent->state.ar.children = elf->next; + if (parent->children == elf) + parent->children = elf->next; else { - struct Elf *child = parent->state.ar.children; + struct Elf *child = parent->children; while (child->next != elf) child = child->next; diff --git a/libelf/elf_readall.c b/libelf/elf_readall.c index 8f171b21c..47d0da1e5 100644 --- a/libelf/elf_readall.c +++ b/libelf/elf_readall.c @@ -1,5 +1,5 @@ /* Read all of the file associated with the descriptor. - Copyright (C) 1998, 1999, 2000, 2001, 2002, 2005 Red Hat, Inc. + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2005, 2007 Red Hat, Inc. This file is part of Red Hat elfutils. Contributed by Ulrich Drepper , 1998. @@ -63,24 +63,21 @@ static void set_address (Elf *elf, size_t offset) { - if (elf->kind == ELF_K_AR) - { - Elf *child = elf->state.ar.children; + Elf *child = elf->children; - while (child != NULL) + while (child != NULL) + { + if (child->map_address == NULL) { - if (child->map_address == NULL) - { - child->map_address = elf->map_address; - child->start_offset -= offset; - if (child->kind == ELF_K_AR) - child->state.ar.offset -= offset; + child->map_address = elf->map_address; + child->start_offset -= offset; + if (child->kind == ELF_K_AR) + child->state.ar.offset -= offset; - set_address (child, offset); - } - - child = child->next; + set_address (child, offset); } + + child = child->next; } } diff --git a/libelf/gelf.h b/libelf/gelf.h index 533e15a93..50851e4b7 100644 --- a/libelf/gelf.h +++ b/libelf/gelf.h @@ -343,6 +343,21 @@ extern size_t gelf_getnote (Elf_Data *__data, size_t __offset, size_t *__name_offset, size_t *__desc_offset); +/* Return descriptor for ELF file found embedded at OFFSET for SIZE bytes + in another open ELF file, to work according to CMD. */ +extern Elf *gelf_begin_embedded (Elf_Cmd __cmd, Elf *__elf, + GElf_Off __offset, GElf_Off __size); + + +/* Get data of SIZE bytes from RAWCHUNK, translated as section data would + be for TYPE. If BUFFER is nonnull, it will be used for the translated + copy if necessary. If the result's d_buf != BUFFER, it was not used. + The resulting Elf_Data pointer is valid until elf_end (ELF) is called. */ +extern Elf_Data *gelf_getdata_memory (Elf *__elf, + const char *__rawchunk, size_t __size, + Elf_Type __type, void *__buffer); + + /* Compute simple checksum from permanent parts of the ELF file. */ extern long int gelf_checksum (Elf *__elf); diff --git a/libelf/gelf_begin_embedded.c b/libelf/gelf_begin_embedded.c new file mode 100644 index 000000000..a7c006672 --- /dev/null +++ b/libelf/gelf_begin_embedded.c @@ -0,0 +1,77 @@ +/* Create Elf descriptor from image embedded in an ELF file. + Copyright (C) 2007 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include "libelfP.h" + + +Elf * +gelf_begin_embedded (cmd, ref, offset, size) + Elf_Cmd cmd; + Elf *ref; + GElf_Off offset; + GElf_Off size; +{ + if (ref == NULL) + return NULL; + + if (offset == 0 || size == 0 + || offset >= ref->maximum_size || ref->maximum_size - offset < size) + { + __libelf_seterrno (ELF_E_INVALID_OPERAND); + return NULL; + } + + return __libelf_dup_elf (-1, cmd, ref, offset, size); +} diff --git a/libelf/gelf_getdata_memory.c b/libelf/gelf_getdata_memory.c new file mode 100644 index 000000000..58212577d --- /dev/null +++ b/libelf/gelf_getdata_memory.c @@ -0,0 +1,142 @@ +/* Return converted data from raw chunk supplied in memory. + Copyright (C) 2007 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include + +#include "libelfP.h" +#include +#include "common.h" +#include "elf-knowledge.h" + +Elf_Data * +gelf_getdata_memory (elf, rawchunk, size, type, buffer) + Elf *elf; + const char *rawchunk; + size_t size; + Elf_Type type; + void *buffer; +{ + if (elf == NULL || elf->kind != ELF_K_ELF) + return NULL; + + if (type >= ELF_T_NUM) + { + __libelf_seterrno (ELF_E_UNKNOWN_TYPE); + return NULL; + } + + size_t align = __libelf_type_align (elf->class, type); + + int flags = 0; + inline bool check_buffer (void) + { + if (buffer == NULL) + { + buffer = malloc (size); + if (buffer == NULL) + return true; + flags = ELF_F_MALLOCED; + } + return false; + } + + if (elf->state.elf32.ehdr->e_ident[EI_DATA] == MY_ELFDATA) + { + if (((uintptr_t) rawchunk & (align - 1)) == 0) + /* No need to copy, we can use the raw data. */ + buffer = (void *) rawchunk; + else + { + if (check_buffer ()) + goto nomem; + + /* The copy will be appropriately aligned for direct access. */ + memcpy (buffer, rawchunk, size); + } + } + else + { + if (check_buffer ()) + goto nomem; + + /* Call the conversion function. */ + (*__elf_xfctstom[LIBELF_EV_IDX][LIBELF_EV_IDX][elf->class - 1][type]) + (buffer, rawchunk, size, 0); + } + + Elf_Data_Chunk *chunk = calloc (1, sizeof *chunk); + if (chunk == NULL) + { + nomem: + __libelf_seterrno (ELF_E_NOMEM); + return NULL; + } + + chunk->dummy_scn.elf = elf; + chunk->dummy_scn.flags = flags; + chunk->data.s = &chunk->dummy_scn; + chunk->data.d.d_buf = buffer; + chunk->data.d.d_size = size; + chunk->data.d.d_type = type; + chunk->data.d.d_align = align; + chunk->data.d.d_version = __libelf_version; + + chunk->next = elf->state.elf.rawchunks; + elf->state.elf.rawchunks = chunk; + + return &chunk->data.d; +} +INTDEF (gelf_getdata_memory) diff --git a/libelf/gelf_getdata_rawchunk.c b/libelf/gelf_getdata_rawchunk.c new file mode 100644 index 000000000..dba492fb2 --- /dev/null +++ b/libelf/gelf_getdata_rawchunk.c @@ -0,0 +1,94 @@ +/* Return converted data from raw chunk of ELF file. + Copyright (C) 2007 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include "libelfP.h" + +Elf_Data * +gelf_getdata_rawchunk (elf, offset, size, type) + Elf *elf; + GElf_Off offset; + GElf_Word size; + Elf_Type type; +{ + /* Get the raw bytes from the file. */ + char *rawchunk = INTUSE(gelf_rawchunk) (elf, offset, size); + if (rawchunk == NULL) + return NULL; + + /* We'll reuse the buffer if we didn't map the file directly. */ + bool alloced = (rawchunk < (char *) elf->map_address + elf->start_offset + || rawchunk >= ((char *) elf->map_address + elf->start_offset + + elf->maximum_size)); + + Elf_Data *data = INTUSE(gelf_getdata_memory) (elf, rawchunk, size, type, + alloced ? rawchunk : NULL); + + if (data != NULL) + { + Elf_Data_Chunk *chunk = (Elf_Data_Chunk *) data; + if (alloced) + { + /* It should have been converted in place. + elf_end will free our original RAWCHUNK pointer. */ + assert (chunk->dummy_scn.flags == 0); + chunk->dummy_scn.flags = ELF_F_MALLOCED; + } + } + else if (alloced) + free (rawchunk); + + return data; +} diff --git a/libelf/libelf.map b/libelf/libelf.map index aaaf9164b..ec149b43b 100644 --- a/libelf/libelf.map +++ b/libelf/libelf.map @@ -122,4 +122,7 @@ ELFUTILS_1.3 { gelf_getauxv; gelf_update_auxv; gelf_getnote; + gelf_getdata_memory; + gelf_getdata_rawchunk; + gelf_begin_embedded; }; diff --git a/libelf/libelfP.h b/libelf/libelfP.h index 7e6305cdd..378d9c32c 100644 --- a/libelf/libelfP.h +++ b/libelf/libelfP.h @@ -308,6 +308,7 @@ struct Elf int ref_count; struct Elf *next; /* Used in list of archive descriptors. */ + struct Elf *children; /* List of all descriptors pointing to this one. */ union { @@ -400,7 +401,6 @@ struct Elf char ar_name[16]; /* NUL terminated ar_name of elf_ar_hdr. */ char raw_name[17]; /* This is a buffer for the NUL terminated named raw_name used in the elf_ar_hdr. */ - struct Elf *children; /* List of all descriptors for this archive. */ } ar; } state; @@ -466,6 +466,11 @@ extern size_t __elf64_msize (Elf_Type __type, size_t __count, unsigned int __version); +/* Create Elf descriptor from image embedded in reference file. */ +extern Elf *__libelf_dup_elf (int fildes, Elf_Cmd cmd, Elf *ref, + GElf_Off start_offset, GElf_Off maximum_size) + internal_function; + /* Create Elf descriptor from memory image. */ extern Elf *__libelf_read_mmaped_file (int fildes, void *map_address, off_t offset, size_t maxsize, @@ -575,6 +580,10 @@ extern GElf_Sym *__gelf_getsym_internal (Elf_Data *__data, int __ndx, extern uint32_t __libelf_crc32 (uint32_t crc, unsigned char *buf, size_t len) attribute_hidden; +INTDECL (gelf_rawchunk); +INTDECL (gelf_freechunk); +INTDECL (gelf_getdata_memory); + /* We often have to update a flag iff a value changed. Make this convenient. */ diff --git a/tests/ChangeLog b/tests/ChangeLog index f029f1566..dc801f370 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,18 @@ +2007-08-04 Roland McGrath + + * dwflmodtest.c (print_module_build_id): New function. + (list_module, print_module): Call it. + +2007-05-03 Roland McGrath + + * coreregs.c (handle_core_file): Close FD. + +2007-04-08 Roland McGrath + + * coreregs.c: New file. + * Makefile.am (noinst_PROGRAMS): Add it. + (coreregs_LDADD): New variable. + 2007-10-20 Roland McGrath * run-dwfl-addr-sect.sh: Change expected output, no errors. diff --git a/tests/Makefile.am b/tests/Makefile.am index 455607888..339b1b979 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -59,7 +59,7 @@ noinst_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \ show-abbrev hash newscn ecp dwflmodtest \ find-prologues funcretval allregs rdwrmmap \ dwfl-bug-addr-overflow arls dwfl-bug-fd-leak \ - dwfl-addr-sect dwfl-bug-report + dwfl-addr-sect dwfl-bug-report coreregs # get-ciefde asm_TESTS = asm-tst1 asm-tst2 asm-tst3 asm-tst4 asm-tst5 \ asm-tst6 asm-tst7 asm-tst8 asm-tst9 @@ -221,6 +221,7 @@ arls_LDADD = $(libelf) $(libmudflap) dwfl_bug_fd_leak_LDADD = $(libdw) $(libebl) $(libelf) $(libmudflap) -ldl dwfl_bug_report_LDADD = $(libdw) $(libebl) $(libelf) $(libmudflap) -ldl dwfl_addr_sect_LDADD = $(libdw) $(libebl) $(libelf) $(libmudflap) -ldl +coreregs_LDADD = $(libdw) $(libebl) $(libelf) $(libmudflap) -ldl CLEANFILES = xxx *.gcno *.gcda *gconv diff --git a/tests/coreregs.c b/tests/coreregs.c new file mode 100644 index 000000000..0683a3d34 --- /dev/null +++ b/tests/coreregs.c @@ -0,0 +1,327 @@ +/* Test program for libdwfl core file handling. + Copyright (C) 2007 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by the + Free Software Foundation; version 2 of the License. + + Red Hat elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + . */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include ELFUTILS_HEADER(dwfl) +#include "../libdwfl/libdwflP.h" /* XXX */ + + +typedef uint8_t GElf_Byte; + +static void +convert (Elf *core, Elf_Type type, void *value, void *data) +{ + Elf_Data valuedata = + { + .d_type = type, + .d_buf = value, + .d_size = gelf_fsize (core, type, 1, EV_CURRENT), + .d_version = EV_CURRENT, + }; + Elf_Data indata = + { + .d_type = type, + .d_buf = data, + .d_size = valuedata.d_size, + .d_version = EV_CURRENT, + }; + + Elf_Data *d = (gelf_getclass (core) == ELFCLASS32 + ? elf32_xlatetom : elf64_xlatetom) + (&valuedata, &indata, elf_getident (core, NULL)[EI_DATA]); + if (d == NULL) + error (2, 0, "elf_xlatetom: %s", elf_errmsg (-1)); +} + +static void +handle_thread_identifier (Elf *core, Elf_Type type, void *data) +{ +#define TYPES \ + DO_TYPE (BYTE, Byte, "0x%.2" PRIx8); DO_TYPE (HALF, Half, "0x%.4" PRIx16); \ + DO_TYPE (WORD, Word, "0x%.8" PRIx32); DO_TYPE (SWORD, Sword, "%" PRId32); \ + DO_TYPE (XWORD, Xword, "0x%.16" PRIx64); DO_TYPE (SXWORD, Sxword, "%" PRId64) + +#define DO_TYPE(NAME, Name, fmt) GElf_##Name Name + union { TYPES; } value; +#undef DO_TYPE + + convert (core, type, &value, data); + + printf (" thread identifier: "); + switch (type) + { + default: + abort (); + break; + +#define DO_TYPE(NAME, Name, fmt) \ + case ELF_T_##NAME: \ + printf (fmt, value.Name); \ + break + TYPES; +#undef DO_TYPE + } + putchar_unlocked ('\n'); + +#undef TYPES +} + +static void +handle_register_data (Dwfl *dwfl, Elf *core, + int regno, const char *regname, int bits, int type, + void *data) +{ +#define TYPES \ + BITS (8, BYTE, "%" PRId8, "0x%.2" PRIx8); \ + BITS (16, HALF, "%" PRId16, "0x%.4" PRIx16); \ + BITS (32, WORD, "%" PRId32, "0x%.8" PRIx32); \ + BITS (64, XWORD, "%" PRId64, "0x%.16" PRIx64) + +#define BITS(bits, xtype, sfmt, ufmt) uint##bits##_t b##bits + union { TYPES; } value; +#undef BITS + + printf ("%9s (%2d): ", regname, regno); + + Dwarf_Addr addr = 0; + switch (type) + { + case DW_ATE_unsigned: + case DW_ATE_signed: + case DW_ATE_address: + switch (bits) + { +#define BITS(bits, xtype, sfmt, ufmt) \ + case bits: \ + convert (core, ELF_T_##xtype, &value, data); \ + if (type == DW_ATE_signed) \ + printf (sfmt, value.b##bits); \ + else \ + printf (ufmt, value.b##bits); \ + addr = value.b##bits; \ + break + + TYPES; + + default: + abort (); +#undef BITS + } + break; + + default: + assert (bits % 8 == 0); + break; + } + + if (type == DW_ATE_address) + { + GElf_Sym sym; + const char *name = dwfl_module_addrsym (dwfl_addrmodule (dwfl, addr), + addr, &sym, NULL); + if (name != NULL) + { + if (addr == sym.st_value) + printf ("\t<%s>", name); + else + printf ("\t<%s%+" PRId64 ">", name, addr - sym.st_value); + } + } + + putchar_unlocked ('\n'); + +#undef TYPES +} + +static void +handle_thread (Dwfl *dwfl, Elf *core, Dwfl_Register_Map *map, + int nsets, GElf_Off offsets[], GElf_Word sizes[], + int idset, GElf_Word idpos, Elf_Type idtype) +{ + void *sets[nsets]; + memset (sets, 0, sizeof sets); + inline int establish (int setno) + { + if (sets[setno] == NULL) + { + if (sizes[setno] == 0) + return 1; + sets[setno] = gelf_rawchunk (core, offsets[setno], sizes[setno]); + if (sets[setno] == NULL) + return -1; + } + return 0; + } + int handle_register (void *arg __attribute__ ((unused)), + int regno, + const char *setname __attribute__ ((unused)), + const char *prefix __attribute__ ((unused)), + const char *regname, + int bits, int type) + { + GElf_Word offset; + int setno = dwfl_register_map (map, regno, &offset); + if (setno >= 0) + { + int result = establish (setno); + if (result == 0) + handle_register_data (dwfl, core, regno, regname, bits, type, + sets[setno] + offset); + else if (result < 0) + error (2, 0, "gelf_rawchunk: %s", elf_errmsg (-1)); + } + return 0; + } + + if (idset < 0) + puts (" no thread identifier!"); + else + { + int result = establish (idset); + if (result < 0) + error (2, 0, "gelf_rawchunk: %s", elf_errmsg (-1)); + assert (result == 0); + + handle_thread_identifier (core, idtype, sets[idset] + idpos); + } + + Dwfl_Module *mod = dwfl->modulelist; /* XXX */ + GElf_Addr base; + while (dwfl_module_getelf (mod, &base) == NULL) + mod = mod->next; + int result = dwfl_module_register_names (mod, &handle_register, NULL); + assert (result == 0); + + for (int i = 0; i < nsets; ++i) + if (sets[i] != NULL) + gelf_freechunk (core, sets[i]); +} + +static void +find_registers (Dwfl *dwfl, Elf *core) +{ + GElf_Off offset; + GElf_Off limit; + Dwfl_Register_Map *map; + int nsets = dwfl_core_file_register_map (dwfl, &map, &offset, &limit); + if (nsets < 0) + error (2, 0, "dwfl_core_file_register_map: %s", dwfl_errmsg (-1)); + + if (nsets == 0) + { + puts (" no register information"); + return; + } + + GElf_Off offsets[nsets]; + GElf_Word sizes[nsets]; + int result; + do + { + int idset; + GElf_Word idpos; + Elf_Type idtype; + GElf_Nhdr nhdr; + GElf_Off note_offset; + result = dwfl_core_file_read_note (dwfl, map, offset, limit, nsets, + offsets, sizes, + &idset, &idpos, &idtype, + &offset, &nhdr, ¬e_offset); + if (result >= 0) + handle_thread (dwfl, core, map, nsets, + offsets, sizes, idset, idpos, idtype); + if (result == 0) + { + /* Non-thread note. */ + offset = note_offset + nhdr.n_descsz; + break; + } + } while (result >= 0 && offset < limit); + + if (result < 0) + error (2, 0, "dwfl_core_file_read_note: %s", dwfl_errmsg (-1)); + + dwfl_register_map_end (map); +} + +static const Dwfl_Callbacks corefile_callbacks = + { + .find_debuginfo = INTUSE(dwfl_standard_find_debuginfo), + .find_elf = INTUSE(dwfl_core_file_find_elf), + }; + +static void +handle_core_file (const char *name) +{ + int fd = open64 (name, O_RDONLY); + if (fd < 0) + error (2, errno, "cannot open '%s'", name); + + elf_version (EV_CURRENT); + Elf *core = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, NULL); + if (core == NULL) + error (2, 0, "cannot read ELF core file: %s", elf_errmsg (-1)); + + Dwfl *dwfl = dwfl_begin (&corefile_callbacks); + int result = dwfl_core_file_report (dwfl, core); + if (result < 0) + error (2, 0, "%s: %s", name, dwfl_errmsg (-1)); + dwfl_report_end (dwfl, NULL, NULL); + + printf ("%s:\n", name); + find_registers (dwfl, core); + + dwfl_end (dwfl); + elf_end (core); + close (fd); +} + +int +main (int argc, char **argv) +{ + /* We use no threads here which can interfere with handling a stream. */ + (void) __fsetlocking (stdout, FSETLOCKING_BYCALLER); + + /* Set locale. */ + (void) setlocale (LC_ALL, ""); + + for (int i = 1; i < argc; ++i) + handle_core_file (argv[i]); + + return 0; +} diff --git a/tests/dwflmodtest.c b/tests/dwflmodtest.c index 94f960faf..ad5356b50 100644 --- a/tests/dwflmodtest.c +++ b/tests/dwflmodtest.c @@ -162,8 +162,28 @@ print_func (Dwarf_Die *func, void *arg) return DWARF_CB_OK; } +static void +print_module_build_id (Dwfl_Module *mod, const char *name) +{ + const unsigned char *bits; + GElf_Addr vaddr; + int len = dwfl_module_build_id (mod, &bits, &vaddr); + if (len < 0) + error (0, 0, "dwfl_module_build_id: %s: %s", name, dwfl_errmsg (-1)); + if (len > 0) + { + fputs ("\tID: ", stdout); + while (len-- > 0) + printf ("%02" PRIx8, *bits++); + if (vaddr == 0) + putchar ('\n'); + else + printf (" @ %#" PRIx64 "\n", vaddr); + } +} + static int -list_module (Dwfl_Module *mod __attribute__ ((unused)), +list_module (Dwfl_Module *mod, void **userdata __attribute__ ((unused)), const char *name, Dwarf_Addr base, void *arg __attribute__ ((unused))) @@ -178,11 +198,12 @@ list_module (Dwfl_Module *mod __attribute__ ((unused)), abort (); printf ("module: %30s %08" PRIx64 "..%08" PRIx64 " %s %s\n", name, start, end, file, debug); + print_module_build_id (mod, name); return DWARF_CB_OK; } static int -print_module (Dwfl_Module *mod __attribute__ ((unused)), +print_module (Dwfl_Module *mod, void **userdata __attribute__ ((unused)), const char *name, Dwarf_Addr base, Dwarf *dw, Dwarf_Addr bias, @@ -190,6 +211,7 @@ print_module (Dwfl_Module *mod __attribute__ ((unused)), { printf ("module: %30s %08" PRIx64 " %s %" PRIx64 " (%s)\n", name, base, dw == NULL ? "no" : "DWARF", bias, dwfl_errmsg (-1)); + print_module_build_id (mod, name); if (dw != NULL && *(const bool *) arg) {