diff options
author | Aleksei Vetrov <vvvvvv@google.com> | 2022-09-20 13:36:37 +0000 |
---|---|---|
committer | Mark Wielaard <mark@klomp.org> | 2022-10-16 17:09:42 +0200 |
commit | 64ee2cb792e7b6ba6ad2a5759bff7ce8714e4668 (patch) | |
tree | ac911024367364c26a5a557b6aed95aaed3245e9 | |
parent | 6284f4d12ccbc6405e986fd84ac6d4d72dc9c2a7 (diff) | |
download | elfutils-64ee2cb792e7b6ba6ad2a5759bff7ce8714e4668.tar.gz |
libdwfl: add dwfl_report_offline_memory
This method allows to read and report ELF from memory instead of opening
a file. That way arbitrary memory can be worked with, e.g. when coming
from a stream without the need to persist.
Another useful application is for fuzzing, because fuzzers might be able
to track accesses to the memory and change the fuzzer input to cover
more edge cases through more targeted input. Hence, add a new function
along with a test case.
Signed-off-by: Aleksei Vetrov <vvvvvv@google.com>
-rw-r--r-- | ChangeLog | 4 | ||||
-rw-r--r-- | NEWS | 1 | ||||
-rw-r--r-- | libdw/ChangeLog | 4 | ||||
-rw-r--r-- | libdw/libdw.map | 1 | ||||
-rw-r--r-- | libdwfl/ChangeLog | 13 | ||||
-rw-r--r-- | libdwfl/libdwfl.h | 4 | ||||
-rw-r--r-- | libdwfl/libdwflP.h | 6 | ||||
-rw-r--r-- | libdwfl/offline.c | 26 | ||||
-rw-r--r-- | libdwfl/open.c | 30 | ||||
-rw-r--r-- | tests/ChangeLog | 9 | ||||
-rw-r--r-- | tests/Makefile.am | 4 | ||||
-rw-r--r-- | tests/dwfl-report-offline-memory.c | 97 | ||||
-rwxr-xr-x | tests/run-dwfl-report-offline-memory.sh | 26 |
13 files changed, 221 insertions, 4 deletions
@@ -1,3 +1,7 @@ +2022-09-13 Aleksei Vetrov <vvvvvv@google.com> + + * NEWS (libdwfl): Add dwfl_report_offline_memory. + 2022-09-27 Taketo Kabe <kabe@sra-tohoku.co.jp> * debuginfod/debuginfod-client.c: Correctly get timestamp when @@ -6,6 +6,7 @@ debuginfod: Add --disable-source-scan option. libdwfl: Add new function dwfl_get_debuginfod_client. Add new function dwfl_frame_reg. + Add new function dwfl_report_offline_memory. Version 0.187 diff --git a/libdw/ChangeLog b/libdw/ChangeLog index dd54afc2..9a798ff9 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,3 +1,7 @@ +2022-09-13 Aleksei Vetrov <vvvvvv@google.com> + + * libdw.map (ELFUTILS_0.188): Add dwfl_report_offline_memory. + 2022-08-09 Ulrich Drepper <drepper@redhat.com> * dwarf_next_cfi.c (dwarf_next_cfi): Don't skip processing the diff --git a/libdw/libdw.map b/libdw/libdw.map index 8f393438..5331ad45 100644 --- a/libdw/libdw.map +++ b/libdw/libdw.map @@ -371,4 +371,5 @@ ELFUTILS_0.188 { global: dwfl_get_debuginfod_client; dwfl_frame_reg; + dwfl_report_offline_memory; } ELFUTILS_0.186; diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog index 30f30b14..942f05d5 100644 --- a/libdwfl/ChangeLog +++ b/libdwfl/ChangeLog @@ -1,3 +1,16 @@ +2022-09-13 Aleksei Vetrov <vvvvvv@google.com> + + * libdwfl.h (dwfl_report_offline_memory): New function. + * libdwflP.h (__libdw_open_elf_memory): New internal function. + (dwfl_report_offline_memory): INTDECL. + * offline.c (dwfl_report_offline_memory): New function. + * open.c (decompress): Return DWFL_E_BADELF when fd is -1. + (libdw_open_elf): New argument use_elfp. Adding *elfp to elf if + true. + (__libdw_open_file): Pass false to libdw_open_elf. + (__libdw_open_elf_memory): New function. + (__libdw_open_elf): Pass false for libdw_open_elf. + 2022-07-28 Di Chen <dichen@redhat.com> * libdwfl.h (dwfl_frame_reg): New function. diff --git a/libdwfl/libdwfl.h b/libdwfl/libdwfl.h index edb537c2..9114f7f0 100644 --- a/libdwfl/libdwfl.h +++ b/libdwfl/libdwfl.h @@ -159,6 +159,10 @@ extern Dwfl_Module *dwfl_report_elf (Dwfl *dwfl, const char *name, extern Dwfl_Module *dwfl_report_offline (Dwfl *dwfl, const char *name, const char *file_name, int fd); +/* Similar, but report ELF from memory region. */ +extern Dwfl_Module *dwfl_report_offline_memory (Dwfl *dwfl, const char *name, + const char *file_name, + char *data, size_t size); /* Finish reporting the current set of modules to the library. If REMOVED is not null, it's called for each module that diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h index a2949e74..011b5de9 100644 --- a/libdwfl/libdwflP.h +++ b/libdwfl/libdwflP.h @@ -631,6 +631,11 @@ extern Dwfl_Error __libdw_open_file (int *fdp, Elf **elfp, bool close_on_fail, bool archive_ok) internal_function; +/* Same as __libdw_open_file, but opens Elf handle from memory region. */ +extern Dwfl_Error __libdw_open_elf_memory (char *data, size_t size, Elf **elfp, + bool archive_ok) + internal_function; + /* Same as __libdw_open_file, but never closes the given file descriptor and ELF_K_AR is always an acceptable type. */ extern Dwfl_Error __libdw_open_elf (int fd, Elf **elfp) internal_function; @@ -760,6 +765,7 @@ INTDECL (dwfl_report_begin_add) INTDECL (dwfl_report_module) INTDECL (dwfl_report_segment) INTDECL (dwfl_report_offline) +INTDECL (dwfl_report_offline_memory) INTDECL (dwfl_report_end) INTDECL (dwfl_build_id_find_elf) INTDECL (dwfl_build_id_find_debuginfo) diff --git a/libdwfl/offline.c b/libdwfl/offline.c index 58ba4c36..499663e3 100644 --- a/libdwfl/offline.c +++ b/libdwfl/offline.c @@ -1,6 +1,7 @@ /* Recover relocatibility for addresses computed from debug information. Copyright (C) 2005-2009, 2012 Red Hat, Inc. Copyright (C) 2022 Mark J. Wielaard <mark@klomp.org> + Copyright (C) 2022 Google LLC This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -252,6 +253,7 @@ process_archive (Dwfl *dwfl, const char *name, const char *file_name, int fd, { Dwfl_Module *mod = NULL; + /* elf_begin supports opening archives even with fd == -1 passed. */ Elf *member = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, archive); if (unlikely (member == NULL)) /* Empty archive. */ { @@ -320,3 +322,27 @@ dwfl_report_offline (Dwfl *dwfl, const char *name, return __libdwfl_report_offline (dwfl, name, file_name, fd, closefd, NULL); } INTDEF (dwfl_report_offline) + +Dwfl_Module * +dwfl_report_offline_memory (Dwfl *dwfl, const char *name, + const char *file_name, char *data, size_t size) +{ + if (dwfl == NULL) + return NULL; + + Elf *elf; + Dwfl_Error error = __libdw_open_elf_memory (data, size, &elf, true); + if (error != DWFL_E_NOERROR) + { + __libdwfl_seterrno (error); + return NULL; + } + /* It is ok to pass fd == -1 here, because libelf uses it as a value for + "no file opened" and supports working with files without fd, thanks to + the existence of the elf_memory function. */ + Dwfl_Module *mod = process_file (dwfl, name, file_name, -1, elf, NULL); + if (mod == NULL) + elf_end (elf); + return mod; +} +INTDEF (dwfl_report_offline_memory) diff --git a/libdwfl/open.c b/libdwfl/open.c index 77bd2bd9..da8b59a3 100644 --- a/libdwfl/open.c +++ b/libdwfl/open.c @@ -1,5 +1,6 @@ /* Decompression support for libdwfl: zlib (gzip), bzlib (bzip2) or lzma (xz). Copyright (C) 2009, 2016 Red Hat, Inc. + Copyright (C) 2022 Google LLC This file is part of elfutils. This file is free software; you can redistribute it and/or modify @@ -53,6 +54,9 @@ static Dwfl_Error decompress (int fd __attribute__ ((unused)), Elf **elf) { Dwfl_Error error = DWFL_E_BADELF; + /* ELF cannot be decompressed, if there is no file descriptor. */ + if (fd == -1) + return error; void *buffer = NULL; size_t size = 0; @@ -124,11 +128,12 @@ what_kind (int fd, Elf **elfp, Elf_Kind *kind, bool *may_close_fd) static Dwfl_Error libdw_open_elf (int *fdp, Elf **elfp, bool close_on_fail, bool archive_ok, - bool never_close_fd, bool bad_elf_ok) + bool never_close_fd, bool bad_elf_ok, bool use_elfp) { bool may_close_fd = false; - Elf *elf = elf_begin (*fdp, ELF_C_READ_MMAP_PRIVATE, NULL); + Elf *elf = + use_elfp ? *elfp : elf_begin (*fdp, ELF_C_READ_MMAP_PRIVATE, NULL); Elf_Kind kind; Dwfl_Error error = what_kind (*fdp, &elf, &kind, &may_close_fd); @@ -194,11 +199,28 @@ libdw_open_elf (int *fdp, Elf **elfp, bool close_on_fail, bool archive_ok, Dwfl_Error internal_function __libdw_open_file (int *fdp, Elf **elfp, bool close_on_fail, bool archive_ok) { - return libdw_open_elf (fdp, elfp, close_on_fail, archive_ok, false, false); + return libdw_open_elf (fdp, elfp, close_on_fail, archive_ok, false, false, + false); +} + +Dwfl_Error internal_function +__libdw_open_elf_memory (char *data, size_t size, Elf **elfp, bool archive_ok) +{ + /* It is ok to use `fd == -1` here, because libelf uses it as a value for + "no file opened" and code supports working with this value, and also + `never_close_fd == false` is passed to prevent closing non-existant file. + The only caveat is in `decompress` method, which doesn't support + decompressing from memory, so reading compressed zImage using this method + won't work. */ + int fd = -1; + *elfp = elf_memory (data, size); + /* Allow using this ELF as reference for subsequent elf_begin calls. */ + (*elfp)->cmd = ELF_C_READ_MMAP_PRIVATE; + return libdw_open_elf (&fd, elfp, false, archive_ok, true, false, true); } Dwfl_Error internal_function __libdw_open_elf (int fd, Elf **elfp) { - return libdw_open_elf (&fd, elfp, false, true, true, true); + return libdw_open_elf (&fd, elfp, false, true, true, true, false); } diff --git a/tests/ChangeLog b/tests/ChangeLog index d10acfa0..6ac2c1e8 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,12 @@ +2022-09-13 Aleksei Vetrov <vvvvvv@google.com> + + * Makefile.am (check_PROGRAMS): Add dwfl-report-offline-memory. + (TESTS): Add run-dwfl-report-offline-memory.sh. + (EXTRA_DIST): Likewise. + (dwfl_report_offline_memory_LDADD): New variable. + * dwfl-report-offline-memory.c: New file. + * run-dwfl-report-offline-memory.sh: Likewise. + 2022-09-13 Khem Raj <raj.khem@gmail.com> * Makefile.am (*_LDADD): Add libeu if needed for error. diff --git a/tests/Makefile.am b/tests/Makefile.am index 142b9c30..f680d3e1 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -47,6 +47,7 @@ check_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \ alldts typeiter typeiter2 low_high_pc \ test-elf_cntl_gelf_getshdr dwflsyms dwfllines \ dwfl-report-elf-align dwfl-report-segment-contiguous \ + dwfl-report-offline-memory \ varlocs backtrace backtrace-child \ backtrace-data backtrace-dwarf debuglink debugaltlink \ buildid deleted deleted-lib.so aggregate_size peel_type \ @@ -149,6 +150,7 @@ TESTS = run-arextract.sh run-arsymtest.sh run-ar.sh newfile test-nlist \ run-readelf-mixed-corenote.sh run-dwfllines.sh \ run-readelf-variant.sh run-readelf-fat-lto.sh \ run-dwfl-report-elf-align.sh run-addr2line-test.sh \ + run-dwfl-report-offline-memory.sh \ run-addr2line-i-test.sh run-addr2line-i-lex-test.sh \ run-addr2line-i-demangle-test.sh run-addr2line-alt-debugpath.sh \ run-varlocs.sh run-exprlocs.sh run-varlocs-vars.sh run-funcretval.sh \ @@ -413,6 +415,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \ testfile69.core.bz2 testfile69.so.bz2 \ testfile70.core.bz2 testfile70.exec.bz2 testfile71.bz2 \ run-dwfllines.sh run-dwfl-report-elf-align.sh \ + run-dwfl-report-offline-memory.sh \ testfile-dwfl-report-elf-align-shlib.so.bz2 \ testfilenolines.bz2 test-core-lib.so.bz2 test-core.core.bz2 \ test-core.exec.bz2 run-addr2line-test.sh \ @@ -711,6 +714,7 @@ test_elf_cntl_gelf_getshdr_LDADD = $(libelf) dwflsyms_LDADD = $(libdw) $(libelf) $(argp_LDADD) dwfllines_LDADD = $(libeu) $(libdw) $(libelf) $(argp_LDADD) dwfl_report_elf_align_LDADD = $(libeu) $(libdw) +dwfl_report_offline_memory_LDADD = $(libeu) $(libdw) dwfl_report_segment_contiguous_LDADD = $(libdw) $(libebl) $(libelf) varlocs_LDADD = $(libeu) $(libdw) $(libelf) $(argp_LDADD) backtrace_LDADD = $(libeu) $(libdw) $(libelf) $(argp_LDADD) diff --git a/tests/dwfl-report-offline-memory.c b/tests/dwfl-report-offline-memory.c new file mode 100644 index 00000000..837aca5e --- /dev/null +++ b/tests/dwfl-report-offline-memory.c @@ -0,0 +1,97 @@ +/* Test program for dwfl_report_offline_memory. + Copyright (C) 2022 Google LLC + 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/>. */ + +#include <assert.h> +#include <config.h> +#include <fcntl.h> +#include <locale.h> +#include <stdio.h> +#include <stdio_ext.h> +#include <stdlib.h> +#include <unistd.h> +#include ELFUTILS_HEADER(dwfl) +#include "system.h" + + +static const Dwfl_Callbacks offline_callbacks = + { + .find_debuginfo = INTUSE(dwfl_standard_find_debuginfo), + .section_address = INTUSE(dwfl_offline_section_address), + }; + +static int +count_modules (Dwfl_Module *mod __attribute__ ((unused)), + void **userdata __attribute__ ((unused)), + const char *name __attribute__ ((unused)), + Dwarf_Addr base __attribute__ ((unused)), void *arg) +{ + unsigned long long *counter = arg; + ++(*counter); + return DWARF_CB_OK; +} + +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, ""); + + if (argc != 3) + error (-1, 0, + "usage: dwfl_report_offline_memory [filename] " + "[expected number of modules]"); + + const char *fname = argv[1]; + int fd = open (fname, O_RDONLY); + if (fd < 0) + error (-1, 0, "can't open file %s: %s", fname, strerror (errno)); + size_t size = lseek (fd, 0, SEEK_END); + lseek (fd, 0, SEEK_SET); + char *data = malloc (size); + size_t bytes_read = read (fd, data, size); + assert (bytes_read == size); + close (fd); + + Dwfl *dwfl = dwfl_begin (&offline_callbacks); + assert (dwfl != NULL); + + Dwfl_Module *mod = + dwfl_report_offline_memory (dwfl, argv[1], argv[1], data, size); + assert (mod != NULL); + dwfl_report_end (dwfl, NULL, NULL); + + unsigned long long number_of_modules = 0; + ptrdiff_t offset = + dwfl_getmodules (dwfl, &count_modules, &number_of_modules, 0); + if (offset < 0) + error (1, 0, "dwfl_getmodules: %s", dwfl_errmsg (-1)); + assert (offset == 0); + + char *endptr; + unsigned long long expected_number_of_modules = + strtoull (argv[2], &endptr, 0); + assert (endptr && !*endptr); + assert (number_of_modules == expected_number_of_modules); + + dwfl_end (dwfl); + free (data); + + return 0; +} diff --git a/tests/run-dwfl-report-offline-memory.sh b/tests/run-dwfl-report-offline-memory.sh new file mode 100755 index 00000000..644a45dc --- /dev/null +++ b/tests/run-dwfl-report-offline-memory.sh @@ -0,0 +1,26 @@ +#! /bin/sh +# Copyright (C) 2022 Google LLC +# 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 testfile-dwfl-report-elf-align-shlib.so +testfiles testarchive64.a + +testrun ${abs_builddir}/dwfl-report-offline-memory ./testfile-dwfl-report-elf-align-shlib.so 1 +testrun ${abs_builddir}/dwfl-report-offline-memory ./testarchive64.a 3 + +exit 0 |