From patchwork Thu Mar 5 00:42:42 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kevin Buettner X-Patchwork-Id: 38418 Received: (qmail 65735 invoked by alias); 5 Mar 2020 00:43:44 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 65659 invoked by uid 89); 5 Mar 2020 00:43:44 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-16.9 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 spammy=layers, examining X-HELO: us-smtp-delivery-1.mimecast.com Received: from us-smtp-1.mimecast.com (HELO us-smtp-delivery-1.mimecast.com) (207.211.31.81) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Thu, 05 Mar 2020 00:43:41 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1583369020; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=HCb/Fz05C7atVg0JkpCl8AdwARix+CoF+p5Hh7ii7G0=; b=gRK7RNtxc7d53ziC0pp4CsKxyoKDxLLvTGrFC4zoib7tYXUNqkkSVHOhKTXHlETCDRZzi8 p4/60GUHKMPkta/HPqDssOlfSGnzEMBm7LbBXsysL6SatkRhcUwrZHve1rAf2wLtsnSywk VxfuW4c96dr/v0tZTQyGTALsH7DbM+A= Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-321-k5cP0qyIOZiSjMtvSZMuog-1; Wed, 04 Mar 2020 19:43:35 -0500 Received: from smtp.corp.redhat.com (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.23]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id B8A9A7A1B2 for ; Thu, 5 Mar 2020 00:43:34 +0000 (UTC) Received: from f31-1.lan (ovpn-116-156.phx2.redhat.com [10.3.116.156]) by smtp.corp.redhat.com (Postfix) with ESMTP id 7FDF7272A1; Thu, 5 Mar 2020 00:43:34 +0000 (UTC) From: Kevin Buettner To: gdb-patches@sourceware.org Cc: Kevin Buettner Subject: [PATCH 3/4] Provide access to non SEC_HAS_CONTENTS core file sections Date: Wed, 4 Mar 2020 17:42:42 -0700 Message-Id: <20200305004243.334607-4-kevinb@redhat.com> In-Reply-To: <20200305004243.334607-1-kevinb@redhat.com> References: <20200305004243.334607-1-kevinb@redhat.com> MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-IsSubscribed: yes Consider the following program: Change-Id: I1adbb4e9047baad7cae7eab9c72e6d2b16f87d73 --- mkmmapcore.c --- static char *buf; int main (int argc, char **argv) { buf = mmap (NULL, 8192, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); abort (); } --- end mkmmapcore.c --- Compile it like this: gcc -g -o mkmmapcore mkmmapcore.c Now let's run it from GDB. I've already placed a breakpoint on the line with the abort() call and have run to that breakpoint. Breakpoint 1, main (argc=1, argv=0x7fffffffd678) at mkmmapcore.c:11 11 abort (); (gdb) x/x buf 0x7ffff7fcb000: 0x00000000 Note that we can examine the memory allocated via the call to mmap(). Now let's try debugging a core file created by running this program. Depending on your system, in order to make a core file, you may have to run the following as root (or using sudo): echo core > /proc/sys/kernel/core_pattern It may also be necessary to do: ulimit -c unlimited I'm using Fedora 31. YMMV if you're using one of the BSDs or some other (non-Linux) system. This is what things look like when we debug the core file: [kev@f31-1 tmp]$ gdb -q ./mkmmapcore core.304767 Reading symbols from ./mkmmapcore... [New LWP 304767] Core was generated by `/tmp/mkmmapcore'. Program terminated with signal SIGABRT, Aborted. #0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50 50 return ret; (gdb) x/x buf 0x7ffff7fcb000: Cannot access memory at address 0x7ffff7fcb000 Note that we can no longer access the memory region allocated by mmap(). Back in 2007, a hack for GDB was added to _bfd_elf_make_section_from_phdr() in bfd/elf.c: /* Hack for gdb. Segments that have not been modified do not have their contents written to a core file, on the assumption that a debugger can find the contents in the executable. We flag this case by setting the fake section size to zero. Note that "real" bss sections will always have their contents dumped to the core file. */ if (bfd_get_format (abfd) == bfd_core) newsect->size = 0; You can find the entire patch plus links to other discussion starting here: https://sourceware.org/ml/binutils/2007-08/msg00047.html This hack sets the size of certain BFD sections to 0, which effectively causes GDB to ignore them. I think it's likely that the bug described above existed even before this hack was added, but I have no easy way to test this now. The output from objdump -h shows the result of this hack: 25 load13 00000000 00007ffff7fcb000 0000000000000000 00013000 2**12 ALLOC (The first field, after load13, shows the size of 0.) Once the hack is removed, the output from objdump -h shows the correct size: 25 load13 00002000 00007ffff7fcb000 0000000000000000 00013000 2**12 ALLOC (This is a digression, but I think it's good that objdump will now show the correct size.) If we remove the hack from bfd/elf.c, but do nothing to GDB, we'll see the following regression: FAIL: gdb.base/corefile.exp: print coremaker_ro The reason for this is that all sections which have the BFD flag SEC_ALLOC set, but for which SEC_HAS_CONTENTS is not set no longer have zero size. Some of these sections have data that can (and should) be read from the executable. But, due to the way that the target strata are traversed when attempting to access memory, the non-SEC_HAS_CONTENTS sections will be read as zeroes from the process_stratum (which in this case is the core file stratum) without first checking the file stratum, which is where the data might actually be found. What we should be doing is this: - Attempt to access core file data for SEC_HAS_CONTENTS sections. - Attempt to access executable file data if the above fails. - Attempt to access core file data for non SEC_HAS_CONTENTS sections, if both of the above fail. That's what this commit does. See the comments in the patch for additional details. gdb/ChangeLog: * corelow.c (class core_target): Add new field m_core_no_contents_section_table. (core_target::core_target): Initialize m_core_no_contents_section_table. (core_target::~core_target): Free data structure associated with m_core_no_contents_section_table. (core_target::files_info): Print section info associated with m_core_no_contents_section_table. (core_target:xfer_partial): Revise TARGET_OBJECT_MEMORY case to consider the stratum beneath the core target as well as m_core_no_contents_section_table. --- gdb/corelow.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 73 insertions(+), 7 deletions(-) diff --git a/gdb/corelow.c b/gdb/corelow.c index 5cd058d599..7a71174062 100644 --- a/gdb/corelow.c +++ b/gdb/corelow.c @@ -122,9 +122,21 @@ private: /* per-core data */ sections --- those should come only from pure executable or shared library bfds. The core bfd sections are an implementation detail of the core target, just like ptrace is for unix child - targets. */ + targets. + + Immediately after being read, sections with no contents will be + removed from this table and placed in the "no contents" table; + see below. */ target_section_table m_core_section_table {}; + /* The core's "no contents" section table. These sections represent + regions of memory which were not modified while the process was + live. Data in these sections are either found in the exec file + or represent regions of memory which were allocated, but never + written to. */ + + target_section_table m_core_no_contents_section_table {}; + /* The core_fns for a core file handler that is prepared to read the core file currently open on core_bfd. */ core_fns *m_core_vec = NULL; @@ -147,11 +159,24 @@ core_target::core_target () &m_core_section_table.sections_end)) error (_("\"%s\": Can't find sections: %s"), bfd_get_filename (core_bfd), bfd_errmsg (bfd_get_error ())); + + /* Place sections which have the BFD flag SEC_ALLOC set, but + for which SEC_HAS_CONTENTS is not set into a separate section + table. */ + split_section_table (&m_core_section_table, + &m_core_no_contents_section_table, + [](struct target_section *s) -> bool + { + flagword flags = s->the_bfd_section->flags; + return ((flags & SEC_ALLOC) + && !(flags & SEC_HAS_CONTENTS)); + }); } core_target::~core_target () { xfree (m_core_section_table.sections); + xfree (m_core_no_contents_section_table.sections); } /* List of all available core_fns. On gdb startup, each core file @@ -731,6 +756,7 @@ void core_target::files_info () { print_section_info (&m_core_section_table, core_bfd); + print_section_info (&m_core_no_contents_section_table, core_bfd); } enum target_xfer_status @@ -741,12 +767,52 @@ core_target::xfer_partial (enum target_object object, const char *annex, switch (object) { case TARGET_OBJECT_MEMORY: - return (section_table_xfer_memory_partial - (readbuf, writebuf, - offset, len, xfered_len, - m_core_section_table.sections, - m_core_section_table.sections_end, - NULL)); + enum target_xfer_status xfer_status; + + /* Try accessing memory contents from core file data, + restricting consideration to those sections in + m_core_section_table. Due to the partitioning (splitting) + which occurs after the core's section table has been read in, + this table will consist only of sections for which the BFD + section flag SEC_HAS_CONTENTS is set. */ + xfer_status = section_table_xfer_memory_partial + (readbuf, writebuf, + offset, len, xfered_len, + m_core_section_table.sections, + m_core_section_table.sections_end, + NULL); + if (xfer_status == TARGET_XFER_OK) + return TARGET_XFER_OK; + + /* If the above failed, we need to see if memory contents are + available from exec file prior to examining those sections + in the core file for which we know only the size of the + section. */ + xfer_status = this->beneath ()->xfer_partial (object, annex, readbuf, + writebuf, offset, len, + xfered_len); + if (xfer_status == TARGET_XFER_OK) + return TARGET_XFER_OK; + + /* Finally, attempt to access data in core file sections with + no contents. These will typically read as all zero, but we + leave it to the underlying layers to decide what to do. */ + xfer_status = section_table_xfer_memory_partial + (readbuf, writebuf, + offset, len, xfered_len, + m_core_no_contents_section_table.sections, + m_core_no_contents_section_table.sections_end, + NULL); + + /* If none of the above attempts worked to access the memory in + question, return TARGET_XFER_UNAVAILABLE. Due to the fact + that the exec file stratum has already been considered, we + want to prevent it from being examined yet again (at a higher + level). */ + if (xfer_status == TARGET_XFER_OK) + return TARGET_XFER_OK; + else + return TARGET_XFER_UNAVAILABLE; case TARGET_OBJECT_AUXV: if (readbuf)