From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: <37cEpYwYKDjgppppppaiiafY.WigYfZoncfm-XYpYfmiolWYqUlY.ila@flex--vvvvvv.bounces.google.com> Received: from mail-wr1-x44a.google.com (mail-wr1-x44a.google.com [IPv6:2a00:1450:4864:20::44a]) by sourceware.org (Postfix) with ESMTPS id 063F53858C74 for ; Tue, 20 Sep 2022 13:36:47 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 063F53858C74 Authentication-Results: sourceware.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=flex--vvvvvv.bounces.google.com Received: by mail-wr1-x44a.google.com with SMTP id x1-20020adfbb41000000b0022b113add45so998374wrg.10 for ; Tue, 20 Sep 2022 06:36:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20210112; h=cc:to:from:subject:message-id:mime-version:date:from:to:cc:subject :date; bh=p/MFdrpflyF3qr1niBoNJbcfL1D5jkynRqCvDmq1beo=; b=YkVYgeU8eiPSyLAWUEnKS8chXQZJjDxGTUCN5TMF4v12FI/DvWapTZ66n5bjTbxTYb T4N7LtNOK6O1vV1KYXg/svWWQm9uiGt4r1t58Ew026M6UuQ0uh74KNBHtUZkXtB3Pmii bOcpxQBYcWpUxUCS2lkDphRS8oEUEYEiSEII5jMfprpWGrs39ozVwtab0CRHHaykgkIg +anaNlQD+fIBW7fVOw3h3lYFrj2E2l1yciVTmaX2FF7FS0chNm2ChojD5mBvptppyPzi /rwIuiQIIsmx6vFhxUeoVgjJ059pivmBwkb1fgyRZcpfRrKjiw1yJvYVO9xE4d9gZgOa McJA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=cc:to:from:subject:message-id:mime-version:date:x-gm-message-state :from:to:cc:subject:date; bh=p/MFdrpflyF3qr1niBoNJbcfL1D5jkynRqCvDmq1beo=; b=hI64zNDT52cpAEryIpAnUJFjcV/og8jWLW8h5klNJjxb3ReprPTGSx3yscMHJvnAJo ded91TEzH0Ih8MNQwPX6CXyIuMHokjYdJ9jwZ9jYi6oAU3Qg5uN6ztYdt8XIh5NcPmih y38GoWaGlWMayhZ5x/1jL3MHzQWZegz3h1Qe1GmXi9WDP/vnWnSujt8hV7l4LwYQVPPQ 1KM+n2NQbIYjzf+H4i2i69y/yf20K+TTHwGkWQDhexOdSp5IIFdYaij/oTIiW5ggjPNC QePhFQeaxMjW+u6vFyREx02gSUUWmd401h8x79mlWwo72MjD2j3D6lymhtMcDbsQjGI+ txog== X-Gm-Message-State: ACrzQf0e1rNAnbc+LQoKAwAoszs9HwixrgrTY0xD9ztNHL918tvNuIHw +UAXbpwhyBoLRSGFX4JqKqOA4jFG7wISUiTySKZg6IpNB73k/duIIJgeA62pNKew+0YaQG2QLxf QW7vXGODFn0aB7OqAIfouACl/0B0dDTBvsbCU5Ij1/KWDPDfY/yAgilfJ6LQlxDm06JcukeE= X-Google-Smtp-Source: AMsMyM6okrOf+UkPnP4GXIWkTkm3QjsOqZfgbZJ+H+Qup0enDh3UzRr/sTonAzhjKdHvTGhS7+fbEGEw9nc= X-Received: from vvvvvv-dev.c.googlers.com ([fda3:e722:ac3:cc00:28:9cb1:c0a8:472]) (user=vvvvvv job=sendgmr) by 2002:a05:600c:1d94:b0:3b4:7b91:7056 with SMTP id p20-20020a05600c1d9400b003b47b917056mr2449163wms.18.1663681005544; Tue, 20 Sep 2022 06:36:45 -0700 (PDT) Date: Tue, 20 Sep 2022 13:36:37 +0000 Mime-Version: 1.0 X-Mailer: git-send-email 2.37.3.968.ga6b4b080e4-goog Message-ID: <20220920133637.2639873-1-vvvvvv@google.com> Subject: [PATCH] libdwfl: add dwfl_report_offline_memory From: vvvvvv@google.com To: elfutils-devel@sourceware.org Cc: kernel-team@android.com, maennich@google.com, vvvvvv@google.com Content-Type: text/plain; charset="UTF-8" X-Spam-Status: No, score=-20.6 required=5.0 tests=BAYES_00,DKIMWL_WL_MED,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,HK_RANDOM_FROM,KAM_SHORT,RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS,TXREP,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: From: Aleksei Vetrov 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 --- ChangeLog | 4 + libdw/libdw.map | 1 + libdwfl/libdwfl.h | 4 + libdwfl/libdwflP.h | 6 ++ libdwfl/offline.c | 26 +++++++ libdwfl/open.c | 30 +++++++- tests/Makefile.am | 4 + tests/dwfl-report-offline-memory.c | 97 +++++++++++++++++++++++++ tests/run-dwfl-report-offline-memory.sh | 26 +++++++ 9 files changed, 194 insertions(+), 4 deletions(-) create mode 100644 tests/dwfl-report-offline-memory.c create mode 100755 tests/run-dwfl-report-offline-memory.sh diff --git a/ChangeLog b/ChangeLog index 5421f5b8..c3a1948a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2022-09-13 Aleksei Vetrov + + * NEWS (libdwfl): Add dwfl_report_offline_memory. + 2022-04-28 Di Chen * NEWS: Add readefl -D, --use-dynamic. diff --git a/libdw/libdw.map b/libdw/libdw.map index 8f393438..ff3b7d20 100644 --- a/libdw/libdw.map +++ b/libdw/libdw.map @@ -137,6 +137,7 @@ ELFUTILS_0.122 { dwfl_report_end; dwfl_report_module; dwfl_report_offline; + dwfl_report_offline_memory; dwfl_standard_argp; dwfl_standard_find_debuginfo; dwfl_version; 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 + 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/Makefile.am b/tests/Makefile.am index 87988fb9..0cd98add 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 \ @@ -148,6 +149,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 \ @@ -411,6 +413,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 \ @@ -709,6 +712,7 @@ test_elf_cntl_gelf_getshdr_LDADD = $(libelf) dwflsyms_LDADD = $(libdw) $(libelf) $(argp_LDADD) dwfllines_LDADD = $(libdw) $(libelf) $(argp_LDADD) dwfl_report_elf_align_LDADD = $(libdw) +dwfl_report_offline_memory_LDADD = $(libdw) dwfl_report_segment_contiguous_LDADD = $(libdw) $(libebl) $(libelf) varlocs_LDADD = $(libdw) $(libelf) $(argp_LDADD) backtrace_LDADD = $(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 . */ + +#include +#include +#include +#include +#include +#include +#include +#include +#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 . + +. $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 -- 2.37.3.968.ga6b4b080e4-goog