]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
Old com.redhat.elfutils.roland.notes branch from Monotone.
authorRoland McGrath <roland@redhat.com>
Sun, 4 Nov 2007 22:30:37 +0000 (14:30 -0800)
committerRoland McGrath <roland@redhat.com>
Thu, 20 May 2010 23:54:23 +0000 (16:54 -0700)
29 files changed:
NEWS
libdw/ChangeLog
libdw/libdw.map
libdwfl/ChangeLog
libdwfl/Makefile.am
libdwfl/argp-std.c
libdwfl/core-file-register-map.c [new file with mode: 0644]
libdwfl/core-file.c [new file with mode: 0644]
libdwfl/dwfl_module.c
libdwfl/libdwfl.h
libdwfl/libdwflP.h
libdwfl/linux-proc-maps.c
libdwfl/register-map.c [new file with mode: 0644]
libelf/ChangeLog
libelf/Makefile.am
libelf/common.h
libelf/elf_begin.c
libelf/elf_end.c
libelf/elf_readall.c
libelf/gelf.h
libelf/gelf_begin_embedded.c [new file with mode: 0644]
libelf/gelf_getdata_memory.c [new file with mode: 0644]
libelf/gelf_getdata_rawchunk.c [new file with mode: 0644]
libelf/libelf.map
libelf/libelfP.h
tests/ChangeLog
tests/Makefile.am
tests/coreregs.c [new file with mode: 0644]
tests/dwflmodtest.c

diff --git a/NEWS b/NEWS
index eb52105a6933a7e4e529663829ff020dc10f1a14..ac1aba8d3dbbfab9321a51f57a16e44cc7a930f8 100644 (file)
--- 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
index b7cd62851a72d003dc2d585af2ee4430169f02f1..1642ce85afdf1a79ad943a0a74296fbd465e7df6 100644 (file)
@@ -1,3 +1,17 @@
+2007-08-04  Roland McGrath  <roland@redhat.com>
+
+       * 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  <roland@redhat.com>
+
+       * 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  <roland@redhat.com>
 
        * libdw.h (__deprecated_attribute__): New macro.
index 8ef5f63338e757e777c9f871b8982cb3feb46360..f0edbf35b2b12592139f856ebe765fff1476f2b4 100644 (file)
@@ -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;
index ea6e4e0197eb053207c781a0a627082d96caeac1..285c7db5a11e2748de2046511abf8f2e3fc94e89 100644 (file)
@@ -1,3 +1,47 @@
+2007-08-04  Roland McGrath  <roland@redhat.com>
+
+       * 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  <roland@redhat.com>
+
+       * 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  <roland@redhat.com>
+
+       * 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  <roland@redhat.com>
+
+       * 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  <roland@redhat.com>
 
        * linux-kernel-modules.c (report_kernel_archive): Reorder the kernel
        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  <roland@redhat.com>
 
        * dwfl_module_addrsym.c: Prefer a later global symbol at the same
 
 2007-03-12  Roland McGrath  <roland@redhat.com>
 
+       * 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.
 
        * offline.c: Comment typo fix.
 
+2007-03-10  Roland McGrath  <roland@redhat.com>
+
+       * linux-proc-maps.c (find_sysinfo_ehdr): Rewritten to use
+       gelf_getdata_memory and gelf_getauxv.
+
 2007-03-04  Roland McGrath  <roland@redhat.com>
 
        * linux-kernel-modules.c (KERNEL_MODNAME): New macro for "kernel".
index 83834cdba3ee4959a30ccca240090fbd96ab7d38..6645315f670f1478fdc2ab9fbfb7bac329dccb7a 100644 (file)
@@ -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
index 1139788928c47aa246e162f7a4b3b8a30dde934c..a30b3d38270e85f714c5013b69692b81ae6aa768 100644 (file)
 #include <stdlib.h>
 #include <assert.h>
 #include <libintl.h>
+#include <fcntl.h>
+#include <unistd.h>
 
 /* 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 (file)
index 0000000..9e3af78
--- /dev/null
@@ -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
+   <http://www.openinventionnetwork.com>.  */
+
+#include <config.h>
+#include "libdwflP.h"
+
+#include <gelf.h>
+
+
+// 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 (file)
index 0000000..99f133e
--- /dev/null
@@ -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
+   <http://www.openinventionnetwork.com>.  */
+
+#include <config.h>
+#include "libdwflP.h"
+
+#include <gelf.h>
+#include <inttypes.h>
+#include <alloca.h>
+
+#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)
index 434f3ff90586644958af01a2cba2564ae0f3c48f..f6b6353a3993dd67cec9f0d9b977d4462946bb27 100644 (file)
@@ -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);
 }
index 5438ee2dfb1f69340fe68ee83123b6448148ba5a..cd1d97129cfade9b7ec7563accdcedfd06ae0e7a 100644 (file)
@@ -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
index bbb56aac54a4233bc41ca8d74c44e4a2e93af369..54a3d727a71f2092dc42afa6732094ee9d5c6b6b 100644 (file)
@@ -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
index 6eaed39ad0597cbcb796b2aec7c04a1d3851f2b8..60e9b58dbbc636fa04046bf5504c062cefb06b3a 100644 (file)
@@ -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 (file)
index 0000000..ad15051
--- /dev/null
@@ -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
+   <http://www.openinventionnetwork.com>.  */
+
+#include <config.h>
+#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 (&regs[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, &regs_offset,
+                             &nregloc, &reglocs, &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 = &reglocs[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 = &reglocs[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.  */
+}
index 6860fa71eef52c17756a2fdcfa069dfd351014de..364eb82c40c38d8ed921a5205ab2b9a1c0bab18d 100644 (file)
@@ -1,3 +1,28 @@
+2007-03-12  Roland McGrath  <roland@redhat.com>
+
+       * 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  <roland@redhat.com>
+
+       * 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  <roland@redhat.com>
 
        * libelf.h (Elf_Data): Comment fix.
index 58c9b5a8042ef94ac08bfa1d93b8211a36c7da87..acd063e67be7ec2e7a44730752da89abee24378a 100644 (file)
@@ -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 =
index 22fcfab6f99bc50771fcf8652ffc397d3664c926..af1fce54bdcbd35ab9b94c07a3da5011796b693f 100644 (file)
@@ -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 <drepper@redhat.com>, 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);
index 13f965f7c978358e790fcf7d5abb1988c8a1c206..b6e7e74382943c4f3fb66878cbaaf3845ad0119e 100644 (file)
@@ -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.  */
index 5112eaea41157c11f3206f149042411094bf3bfb..7a813e9737867b0ff585f89fe98187270ee09e74 100644 (file)
@@ -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;
index 8f171b21c494a6da672f78b71a918c90ba064757..47d0da1e53da5bdde4f2d173f4e4f6b1985133bd 100644 (file)
@@ -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 <drepper@redhat.com>, 1998.
 
 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;
     }
 }
 
index 533e15a9391862ac98420902234b090768bd1cd3..50851e4b76e03b75ec2230b6f24ba0f191f21a85 100644 (file)
@@ -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 (file)
index 0000000..a7c0066
--- /dev/null
@@ -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
+   <http://www.openinventionnetwork.com>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stddef.h>
+
+#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 (file)
index 0000000..5821257
--- /dev/null
@@ -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
+   <http://www.openinventionnetwork.com>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <errno.h>
+#include <stddef.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "libelfP.h"
+#include <system.h>
+#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 (file)
index 0000000..dba492f
--- /dev/null
@@ -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
+   <http://www.openinventionnetwork.com>.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#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;
+}
index aaaf9164b2787d092b75d58739d46c913546e632..ec149b43bdd732a7214eb9fb9767432cce5d8a22 100644 (file)
@@ -122,4 +122,7 @@ ELFUTILS_1.3 {
     gelf_getauxv;
     gelf_update_auxv;
     gelf_getnote;
+    gelf_getdata_memory;
+    gelf_getdata_rawchunk;
+    gelf_begin_embedded;
 };
index 7e6305cdd725eb6c1ce16b1512c5a7db86914db5..378d9c32c8419ecae1df2d9ca5765948c7d07ac2 100644 (file)
@@ -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.  */
index f029f15666ddcf481cec25465dc8a497c1a803e9..dc801f370b5ebdf7092146b61d6213acda8ac073 100644 (file)
@@ -1,3 +1,18 @@
+2007-08-04  Roland McGrath  <roland@redhat.com>
+
+       * dwflmodtest.c (print_module_build_id): New function.
+       (list_module, print_module): Call it.
+
+2007-05-03  Roland McGrath  <roland@redhat.com>
+
+       * coreregs.c (handle_core_file): Close FD.
+
+2007-04-08  Roland McGrath  <roland@redhat.com>
+
+       * coreregs.c: New file.
+       * Makefile.am (noinst_PROGRAMS): Add it.
+       (coreregs_LDADD): New variable.
+
 2007-10-20  Roland McGrath  <roland@redhat.com>
 
        * run-dwfl-addr-sect.sh: Change expected output, no errors.
index 4556078889537daf746faffd546827679530964e..339b1b9797b00273916afa5e2a1374ec81269c9d 100644 (file)
@@ -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 (file)
index 0000000..0683a3d
--- /dev/null
@@ -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
+   <http://www.openinventionnetwork.com>.  */
+
+#include <config.h>
+#include <assert.h>
+#include <inttypes.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdio_ext.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <error.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <gelf.h>
+#include <dwarf.h>
+#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, &note_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;
+}
index 94f960faffda0b6cb0fa41d9502597de3495f9fb..ad5356b503e9d245719dfaa8683ff7c86bdcb5d5 100644 (file)
@@ -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)
     {