summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libdwfl/ChangeLog16
-rw-r--r--libdwfl/core-file.c19
-rw-r--r--libdwfl/dwfl_segment_report_module.c217
-rw-r--r--libdwfl/libdwflP.h2
-rw-r--r--tests/ChangeLog12
-rw-r--r--tests/Makefile.am6
-rw-r--r--tests/linkmap-cut-lib.so.bz2bin0 -> 2151 bytes
-rw-r--r--tests/linkmap-cut.bz2bin0 -> 2633 bytes
-rw-r--r--tests/linkmap-cut.core.bz2bin0 -> 21343 bytes
-rwxr-xr-xtests/run-linkmap-cut.sh27
-rw-r--r--tests/test-core.core.bz2bin14165 -> 14205 bytes
11 files changed, 263 insertions, 36 deletions
diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog
index bfbc1f77..99963f8d 100644
--- a/libdwfl/ChangeLog
+++ b/libdwfl/ChangeLog
@@ -1,5 +1,21 @@
2014-09-18 Jan Kratochvil <jan.kratochvil@redhat.com>
+ Support NT_FILE for locating files.
+ * core-file.c (dwfl_core_file_report): New variables note_file and
+ note_file_size, set them and pass them to dwfl_segment_report_module.
+ * dwfl_segment_report_module.c: Include common.h and fcntl.h.
+ (buf_has_data, buf_read_ulong, handle_file_note): New functions.
+ (invalid_elf): New function from code of dwfl_segment_report_module.
+ (dwfl_segment_report_module): Add parameters note_file and
+ note_file_size. New variables elf and fd, clean them up in finish.
+ Move some code to invalid_elf. Call handle_file_note, if it found
+ a name verify the file by invalid_elf. Protect elf and fd against
+ cleanup by finish if we found the file for new Dwfl_Module.
+ * libdwflP.h (dwfl_segment_report_module): Add parameters note_file and
+ note_file_size.
+
+2014-09-18 Jan Kratochvil <jan.kratochvil@redhat.com>
+
* dwfl_build_id_find_elf.c (dwfl_build_id_find_elf): Use IS_EXECUTABLE.
* dwfl_segment_report_module.c (dwfl_segment_report_module): Set
IS_EXECUTABLE.
diff --git a/libdwfl/core-file.c b/libdwfl/core-file.c
index 4ce63c4e..50031aed 100644
--- a/libdwfl/core-file.c
+++ b/libdwfl/core-file.c
@@ -451,7 +451,9 @@ dwfl_core_file_report (Dwfl *dwfl, Elf *elf, const char *executable)
/* Next, we should follow the chain from DT_DEBUG. */
const void *auxv = NULL;
+ const void *note_file = NULL;
size_t auxv_size = 0;
+ size_t note_file_size = 0;
if (likely (notes_phdr.p_type == PT_NOTE))
{
/* PT_NOTE -> NT_AUXV -> AT_PHDR -> PT_DYNAMIC -> DT_DEBUG */
@@ -468,13 +470,19 @@ dwfl_core_file_report (Dwfl *dwfl, Elf *elf, const char *executable)
size_t desc_pos;
while ((pos = gelf_getnote (notes, pos, &nhdr,
&name_pos, &desc_pos)) > 0)
- if (nhdr.n_type == NT_AUXV
- && nhdr.n_namesz == sizeof "CORE"
+ if (nhdr.n_namesz == sizeof "CORE"
&& !memcmp (notes->d_buf + name_pos, "CORE", sizeof "CORE"))
{
- auxv = notes->d_buf + desc_pos;
- auxv_size = nhdr.n_descsz;
- break;
+ if (nhdr.n_type == NT_AUXV)
+ {
+ auxv = notes->d_buf + desc_pos;
+ auxv_size = nhdr.n_descsz;
+ }
+ if (nhdr.n_type == NT_FILE)
+ {
+ note_file = notes->d_buf + desc_pos;
+ note_file_size = nhdr.n_descsz;
+ }
}
}
}
@@ -498,6 +506,7 @@ dwfl_core_file_report (Dwfl *dwfl, Elf *elf, const char *executable)
int seg = dwfl_segment_report_module (dwfl, ndx, NULL,
&dwfl_elf_phdr_memory_callback, elf,
core_file_read_eagerly, elf,
+ note_file, note_file_size,
&r_debug_info);
if (unlikely (seg < 0))
{
diff --git a/libdwfl/dwfl_segment_report_module.c b/libdwfl/dwfl_segment_report_module.c
index 3393b087..3822e323 100644
--- a/libdwfl/dwfl_segment_report_module.c
+++ b/libdwfl/dwfl_segment_report_module.c
@@ -30,6 +30,7 @@
#include "../libelf/libelfP.h" /* For NOTE_ALIGN. */
#undef _
#include "libdwflP.h"
+#include "common.h"
#include <elf.h>
#include <gelf.h>
@@ -38,6 +39,7 @@
#include <alloca.h>
#include <endian.h>
#include <unistd.h>
+#include <fcntl.h>
/* A good size for the initial read from memory, if it's not too costly.
@@ -79,12 +81,161 @@ addr_segndx (Dwfl *dwfl, size_t segment, GElf_Addr addr, bool next)
return ndx;
}
+/* Return whether there is SZ bytes available at PTR till END.
+ Function comes from src/readelf.c . */
+
+static bool
+buf_has_data (const void *ptr, const void *end, size_t sz)
+{
+ return ptr < end && (size_t) (end - ptr) >= sz;
+}
+
+/* Read SZ bytes into *RETP from *PTRP (limited by END) in format E32.
+ Function comes from src/readelf.c . */
+
+static bool
+buf_read_ulong (const Elf32_Ehdr *e32, size_t sz,
+ const void **ptrp, const void *end, uint64_t *retp)
+{
+ if (! buf_has_data (*ptrp, end, sz))
+ return false;
+
+ union
+ {
+ uint64_t u64;
+ uint32_t u32;
+ } u;
+
+ memcpy (&u, *ptrp, sz);
+ (*ptrp) += sz;
+
+ if (retp == NULL)
+ return true;
+
+ if (MY_ELFDATA != e32->e_ident[EI_DATA])
+ {
+ if (sz == 4)
+ CONVERT (u.u32);
+ else
+ CONVERT (u.u64);
+ }
+ if (sz == 4)
+ *retp = u.u32;
+ else
+ *retp = u.u64;
+ return true;
+}
+
+/* Try to find matching entry for module from address MODULE_START to
+ MODULE_END in NT_FILE note located at NOTE_FILE of NOTE_FILE_SIZE
+ bytes in format E32. */
+
+static const char *
+handle_file_note (GElf_Addr module_start, GElf_Addr module_end,
+ const Elf32_Ehdr *e32,
+ const void *note_file, size_t note_file_size)
+{
+ if (note_file == NULL)
+ return NULL;
+
+ size_t sz;
+ switch (e32->e_ident[EI_CLASS])
+ {
+ case ELFCLASS32:
+ sz = 4;
+ break;
+ case ELFCLASS64:
+ sz = 8;
+ break;
+ default:
+ return NULL;
+ }
+
+ const void *ptr = note_file;
+ const void *end = note_file + note_file_size;
+ uint64_t count;
+ if (! buf_read_ulong (e32, sz, &ptr, end, &count))
+ return NULL;
+ if (! buf_read_ulong (e32, sz, &ptr, end, NULL)) // page_size
+ return NULL;
+
+ /* Where file names are stored. */
+ const char *fptr = ptr + 3 * count * sz;
+
+ ssize_t firstix = -1;
+ ssize_t lastix = -1;
+ for (size_t mix = 0; mix < count; mix++)
+ {
+ uint64_t mstart, mend, moffset;
+ if (! buf_read_ulong (e32, sz, &ptr, fptr, &mstart)
+ || ! buf_read_ulong (e32, sz, &ptr, fptr, &mend)
+ || ! buf_read_ulong (e32, sz, &ptr, fptr, &moffset))
+ return NULL;
+ if (mstart == module_start && moffset == 0)
+ firstix = lastix = mix;
+ if (firstix != -1 && mstart < module_end)
+ lastix = mix;
+ if (mend >= module_end)
+ break;
+ }
+ if (firstix == -1)
+ return NULL;
+
+ const char *retval = NULL;
+ for (ssize_t mix = 0; mix <= lastix; mix++)
+ {
+ const char *fnext = memchr (fptr, 0, (const char *) end - fptr);
+ if (fnext == NULL)
+ return NULL;
+ if (mix == firstix)
+ retval = fptr;
+ if (firstix < mix && mix <= lastix && strcmp (fptr, retval) != 0)
+ return NULL;
+ fptr = fnext + 1;
+ }
+ return retval;
+}
+
+/* Return true iff we are certain ELF cannot match BUILD_ID of
+ BUILD_ID_LEN bytes. Pass DISK_FILE_HAS_BUILD_ID as false if it is
+ certain ELF does not contain build-id (it is only a performance hit
+ to pass it always as true). */
+
+static bool
+invalid_elf (Elf *elf, bool disk_file_has_build_id,
+ const void *build_id, size_t build_id_len)
+{
+ if (! disk_file_has_build_id && build_id_len > 0)
+ {
+ /* Module found in segments with build-id is more reliable
+ than a module found via DT_DEBUG on disk without any
+ build-id. */
+ return true;
+ }
+ if (disk_file_has_build_id && build_id_len > 0)
+ {
+ const void *elf_build_id;
+ ssize_t elf_build_id_len;
+
+ /* If there is a build id in the elf file, check it. */
+ elf_build_id_len = INTUSE(dwelf_elf_gnu_build_id) (elf, &elf_build_id);
+ if (elf_build_id_len > 0)
+ {
+ if (build_id_len != (size_t) elf_build_id_len
+ || memcmp (build_id, elf_build_id, build_id_len) != 0)
+ return true;
+ }
+ }
+ return false;
+}
+
int
dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
Dwfl_Memory_Callback *memory_callback,
void *memory_callback_arg,
Dwfl_Module_Callback *read_eagerly,
void *read_eagerly_arg,
+ const void *note_file, size_t note_file_size,
const struct r_debug_info *r_debug_info)
{
size_t segment = ndx;
@@ -121,10 +272,16 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
void *buffer = NULL;
size_t buffer_available = INITIAL_READ;
+ Elf *elf = NULL;
+ int fd = -1;
inline int finish (void)
{
release_buffer (&buffer, &buffer_available);
+ if (elf != NULL)
+ elf_end (elf);
+ if (fd != -1)
+ close (fd);
return ndx;
}
@@ -481,32 +638,9 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
if ((module_end > module->start && module_start < module->end)
|| dyn_vaddr == module->l_ld)
{
- bool close_elf = false;
- if (! module->disk_file_has_build_id && build_id_len > 0)
- {
- /* Module found in segments with build-id is more reliable
- than a module found via DT_DEBUG on disk without any
- build-id. */
- if (module->elf != NULL)
- close_elf = true;
- }
if (module->elf != NULL
- && module->disk_file_has_build_id && build_id_len > 0)
- {
- const void *elf_build_id;
- ssize_t elf_build_id_len;
-
- /* If there is a build id in the elf file, check it. */
- elf_build_id_len = INTUSE(dwelf_elf_gnu_build_id) (module->elf,
- &elf_build_id);
- if (elf_build_id_len > 0)
- {
- if (build_id_len != (size_t) elf_build_id_len
- || memcmp (build_id, elf_build_id, build_id_len) != 0)
- close_elf = true;
- }
- }
- if (close_elf)
+ && invalid_elf (module->elf, module->disk_file_has_build_id,
+ build_id, build_id_len))
{
elf_end (module->elf);
close (module->fd);
@@ -527,6 +661,29 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
}
}
+ const char *file_note_name = handle_file_note (module_start, module_end,
+ &ehdr.e32,
+ note_file, note_file_size);
+ if (file_note_name)
+ {
+ name = file_note_name;
+ name_is_final = true;
+ bool invalid = false;
+ fd = open64 (name, O_RDONLY);
+ if (fd >= 0)
+ {
+ Dwfl_Error error = __libdw_open_file (&fd, &elf, true, false);
+ if (error == DWFL_E_NOERROR)
+ invalid = invalid_elf (elf, true /* disk_file_has_build_id */,
+ build_id, build_id_len);
+ }
+ if (invalid)
+ {
+ free (build_id);
+ return finish ();
+ }
+ }
+
/* Our return value now says to skip the segments contained
within the module. */
ndx = addr_segndx (dwfl, segment, module_end, true);
@@ -683,10 +840,10 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
: dynstr_vaddr + dynstrsz - start);
const GElf_Off whole = MAX (file_trimmed_end, shdrs_end);
- Elf *elf = NULL;
- if ((*read_eagerly) (MODCB_ARGS (mod), &buffer, &buffer_available,
- cost, worthwhile, whole, contiguous,
- read_eagerly_arg, &elf)
+ if (elf == NULL
+ && (*read_eagerly) (MODCB_ARGS (mod), &buffer, &buffer_available,
+ cost, worthwhile, whole, contiguous,
+ read_eagerly_arg, &elf)
&& elf == NULL)
{
/* The caller wants to read the whole file in right now, but hasn't
@@ -748,6 +905,8 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
{
/* Install the file in the module. */
mod->main.elf = elf;
+ elf = NULL;
+ fd = -1;
mod->main.vaddr = module_start - bias;
mod->main.address_sync = module_address_sync;
mod->main_bias = bias;
diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h
index 735b920a..12ee116e 100644
--- a/libdwfl/libdwflP.h
+++ b/libdwfl/libdwflP.h
@@ -660,6 +660,8 @@ extern int dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name,
void *memory_callback_arg,
Dwfl_Module_Callback *read_eagerly,
void *read_eagerly_arg,
+ const void *note_file,
+ size_t note_file_size,
const struct r_debug_info *r_debug_info);
/* Report a module for entry in the dynamic linker's struct link_map list.
diff --git a/tests/ChangeLog b/tests/ChangeLog
index 3f127285..71163ee3 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,3 +1,15 @@
+2014-09-18 Jan Kratochvil <jan.kratochvil@redhat.com>
+
+ Support NT_FILE for locating files.
+ * Makefile.am (TESTS): Add run-linkmap-cut.sh.
+ (EXTRA_DIST): Add run-linkmap-cut.sh, linkmap-cut-lib.so.bz2,
+ linkmap-cut.bz2 and linkmap-cut.core.bz2 .
+ * linkmap-cut-lib.so.bz2: New file.
+ * linkmap-cut.bz2: New file.
+ * linkmap-cut.core.bz2: New file.
+ * run-linkmap-cut.sh: New file.
+ * test-core.core.bz2: Disable its NT_FILE note.
+
2014-08-28 Jan Kratochvil <jan.kratochvil@redhat.com>
* Makefile.am (check_PROGRAMS): Add deleted and deleted-lib.so.
diff --git a/tests/Makefile.am b/tests/Makefile.am
index be3494da..ebf4f71f 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -109,7 +109,8 @@ TESTS = run-arextract.sh run-arsymtest.sh newfile test-nlist \
run-backtrace-core-s390x.sh run-backtrace-core-s390.sh \
run-backtrace-core-aarch64.sh \
run-backtrace-demangle.sh run-stack-d-test.sh run-stack-i-test.sh \
- run-readelf-dwz-multi.sh run-allfcts-multi.sh run-deleted.sh
+ run-readelf-dwz-multi.sh run-allfcts-multi.sh run-deleted.sh \
+ run-linkmap-cut.sh
if !BIARCH
export ELFUTILS_DISABLE_BIARCH = 1
@@ -272,7 +273,8 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh \
run-stack-d-test.sh run-stack-i-test.sh \
testfiledwarfinlines.bz2 testfiledwarfinlines.core.bz2 \
run-readelf-zdebug.sh testfile-debug.bz2 testfile-zdebug.bz2 \
- run-deleted.sh
+ run-deleted.sh run-linkmap-cut.sh linkmap-cut-lib.so.bz2 \
+ linkmap-cut.bz2 linkmap-cut.core.bz2
if USE_VALGRIND
valgrind_cmd='valgrind -q --error-exitcode=1 --run-libc-freeres=no'
diff --git a/tests/linkmap-cut-lib.so.bz2 b/tests/linkmap-cut-lib.so.bz2
new file mode 100644
index 00000000..a1bda5c0
--- /dev/null
+++ b/tests/linkmap-cut-lib.so.bz2
Binary files differ
diff --git a/tests/linkmap-cut.bz2 b/tests/linkmap-cut.bz2
new file mode 100644
index 00000000..f2ccd7ca
--- /dev/null
+++ b/tests/linkmap-cut.bz2
Binary files differ
diff --git a/tests/linkmap-cut.core.bz2 b/tests/linkmap-cut.core.bz2
new file mode 100644
index 00000000..b55b2f21
--- /dev/null
+++ b/tests/linkmap-cut.core.bz2
Binary files differ
diff --git a/tests/run-linkmap-cut.sh b/tests/run-linkmap-cut.sh
new file mode 100755
index 00000000..7b1e7977
--- /dev/null
+++ b/tests/run-linkmap-cut.sh
@@ -0,0 +1,27 @@
+#! /bin/bash
+# Copyright (C) 2014 Red Hat, Inc.
+# This file is part of elfutils.
+#
+# This file 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; either version 3 of the License, or
+# (at your option) any later version.
+#
+# 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 this program. If not, see <http://www.gnu.org/licenses/>.
+
+. $srcdir/test-subr.sh
+
+testfiles linkmap-cut-lib.so linkmap-cut linkmap-cut.core
+tempfiles bt
+# It may have non-zero exit code with:
+# .../elfutils/src/stack: dwfl_thread_getframes tid 3130 at 0x3fdf821d64 in /usr/lib64/libc-2.18.so: no matching address range
+testrun ${abs_top_builddir}/src/stack --core=linkmap-cut.core -e linkmap-cut >bt || true
+cat bt
+grep -qw libfunc bt
+grep -qw main bt
diff --git a/tests/test-core.core.bz2 b/tests/test-core.core.bz2
index 4d4346b9..dfaecc75 100644
--- a/tests/test-core.core.bz2
+++ b/tests/test-core.core.bz2
Binary files differ