[PATCH 3/4] Provide access to non SEC_HAS_CONTENTS core file sections

Kevin Buettner kevinb@redhat.com
Tue May 12 08:40:32 GMT 2020


On Sun, 29 Mar 2020 14:18:46 +0100
Pedro Alves <palves@redhat.com> wrote:

> Hi Kevin,
> 
> On 3/5/20 12:42 AM, Kevin Buettner wrote:
> > Consider the following program:
> > 
> > Change-Id: I1adbb4e9047baad7cae7eab9c72e6d2b16f87d73
> >   
> 
> This Change-Id line should be at the bottom of the commit log.
> Or removed entirely since we're not relying on it anymore.

I think the Change-Id might have been placed there due to the use of
"---" as the scissors line demarcating the test program.  Git may have
thought it was the start of a diff.  But that's just a guess.

> > --- mkmmapcore.c ---

I think I have this fixed now - In the v2 patch draft, I'm using "- - -"
instead.

[...]
> Removing the bfd hack alone fixes your new test for me.

I've addressed this point in earlier email.
 
> > 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.  
> 
> I've applied your patch #1 only, and ran the corefile.exp test, but
> it still passes cleanly for me.  I don't see any "print coremaker_ro"
> FAIL here.  :-/  That makes it a bit harder for me to understand all
> of this.  I'm on Fedora 27.
> 
> Can you expand a bit more on this following part?
> 
> > Some of these sections have data that can (and should) be read
> > from the executable.  
> 
> I'd like to understand and explore this a little bit better.

I've revised my draft commit log comments as follows:

    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.  (Sections for which SEC_HAS_CONTENTS
    is set should be read from the core file; sections which do not have
    this flag set need to either be read from the executable or, failing
    that, from the core file using whatever BFD decides is the best value
    to present to the user - it uses zeros.)

    At present, 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.

I basically split the paragraph and added the parenthetical remark
to the end of the first paragraph.

> Are these cases truly indistinguishable from the cases where data
> shouldn't be read from the executable?  I don't mean from the current
> bfd data structures, but from the data in the core file and the executable.
> It's kind of fascinating that that's the case, and if so, it would sound
> like a nasty bug in either the core format or in the Linux kernel for
> producing such cores with which we have to apply heuristics.

The core file shows a "FileSiz" of 0 for cases when we shouldn't
look first at the core file.  Here's an example using a core file
produced by the corefile.exp test case.

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
...
  LOAD           0x0000000000026000 0x00007f498a801000 0x0000000000000000
                 0x0000000000001000 0x00000000001b5000  R E    0x1000
  LOAD           0x0000000000027000 0x00007f498a9b6000 0x0000000000000000
                 0x0000000000000000 0x00000000001ff000         0x1000

The first header show a file size of 1000.  For this one, GDB should
first look at the contents provided by the core file.  The second
header has a size of 0.  There's no data there to look at, so GDB
needs to look at the exec file.  If that fails, the memory in question
was probably dynamically allocated, so we let BFD provide us with
something reasonable to present to the user.  It's possible (as I
discuss later) that a live process has non-zero contents which aren't
captured by the core file.  This might be considered a bug, but in the
case that I looked at the memory in question wasn't especially
relevant to execution of the program once it was loaded. 

By the way, objdump -h (using the one with my hack-removing patch
applied) shows the above headers / sections as follows:

Idx Name          Size      VMA               LMA               File off  Algn
 16 load5a        00001000  00007f498a801000  0000000000000000  00026000  2**12
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 17 load5b        001b4000  00007f498a802000  0000000000001000  00027000  2**12
                  ALLOC, READONLY, CODE

Note that there's no longer any numerical indication that the size
in the file is 0.  But we can discern that by the lack of a CONTENTS
flag.

This is the objdump output without the hack removed:

Idx Name          Size      VMA               LMA               File off  Algn
 16 load5a        00001000  00007f498a801000  0000000000000000  00026000  2**12
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 17 load5b        00000000  00007f498a802000  0000000000001000  00027000  2**12
                  ALLOC, READONLY, CODE

The "old" objdump shows a file size of 0, but contains no indication
of the size of the memory region.  The new (hack-removed) way is better
because it tells us the size of the memory region.  The lack of a CONTENTS
flag lets us know that the file size was 0

I don't know the reason for the choice of names (load5a and load5b).
According to the readelf output, these started out as separate program
headers.  As such, I'd have thought that they'd be given names not
needing to be distinguished by an "a" or "b" suffix.

> For the NON-split fake sections case (by split I mean the loadXXXa/loadXXXb
> sections that map to a single segment), how come we end up with such sections
> in the core in the first place if they weren't modified at run time?

Well, we still need to know that they were there, right?  It doesn't
take much space to output a header that has no corresponding file
contents.

> Diffing "objdump -h" results from before/after the hack removal, on the corefile.exp
> core dump, I see, this case for example:
> 
>  - 18 load6         00000000  00007fd61476a000  0000000000000000  00027000  2**12
>  + 18 load6         001ff000  00007fd61476a000  0000000000000000  00027000  2**12
>                     ALLOC, READONLY
> 
> This is a case of a segment that is not split in two sections like some
> others (note no trailing "a" and "b").  So this is a "!split" case in
> _bfd_elf_make_section_from_phdr.  Trying to disassemble that address, with
> the whole patch series applied, results in:
> 
> (gdb) disassemble 0x00007fd61476a000,+10
> Dump of assembler code from 0x7fd61476a000 to 0x7fd61476a00a:
>    0x00007fd61476a000:  /home/pedro/gdb/binutils-gdb/src/gdb/target.c:1271: internal-error: target_xfer_status target_xfer_partial(target_ops*, target_object, const char*, gdb_byte*, const gdb_byte*, ULONGEST, ULONGEST, ULONGEST*): Assertion `*xfered_len > 0' failed.
> A problem internal to GDB has been detected,
> further debugging may prove unreliable.
> Quit this debugging session? (y or n) 

Unfortunately, I wasn't able to reproduce this problem.  I tried
on F27, F28, and F31.  However, I think I fixed the bug while
fixing a testsuite regression after the rebase.

> I'd be good to also cover this in the testsuite somehow.

Hmmm... I'd need to reproduce it first, which might be done by undoing
the bug fix mentioned earlier.  It doesn't sound like an easy test
case to write.  Plus there's no guarantee that the test would fail
as expected (for a buggy) gdb in modern linux releases.  (I.e. I'm
not terribly enthusiastic about pursuing this.)

> Using "info proc mappings" and "readelf -l" we can see that the address
> belongs to a file-backed mapping with p_filesz=0.  I'm puzzled about
> why we ended up with a p_filesz=0 load segment in the core for
> this memory range (load6).

I don't think that the kernel tries to distinguish these cases; it just
dumps them.

> > 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.
> >   
> 
> This seems to end up in line with Daniel's suggestion back in 2007 at:
> 
>   https://sourceware.org/legacy-ml/binutils/2007-08/msg00045.html
> 
> Except it uses the section flags as proxy for the p_filesz/p_memsz
> checks.

Yes.  I'ved added that link to my v2 commit remarks.  I've also added
your note about the section flags.

> I'm still not fully sure this is the right thing to do given I'm not
> clear on all the details, but if there truly is no other way to
> distinguish the segments that need to be read from the executable
> compared to segments that need to be read from the core, I suppose
> this is the way to go.  

I don't know of a better way.

> I'm not fully convinced on the splitting the sections though, compared to
> just walking the core sections list twice with a predicate.
> section_table_xfer_memory_partial already has a predicate parameter,
> the 'section_name' parameter, we would just need to generalize
> it to a gdb::function_view callback instead.

I think that changing section_name to a predicate parameter is good
idea.  This is what I've done for the v2 patch set.

> Or alternatively, if you prefer two separate lists, then I don't
> understand why build a single list, and then split in two right after.
> Wouldn't it be preferable to make core_target::core_target() build the
> two separate lists from the get go, rather that build a single list and
> then split it in two lists immediately after?

I've removed the splitting stuff entirely.

> BTW, that TARGET_OBJECT_MEMORY case in core_target::xfer_partial
> is getting largish, might be worth it to move that chunk to a
> separate core_target::xfer_memory method.

I didn't do this; it doesn't appear to me to be that much bigger
than some of the other largish cases in the same function.

> But really just walking the single sections list in place would
> be simpler, I think.  I don't think this is a bottleneck.

Yes, I like the way it turned out after making the changes you
suggested.

> >  enum target_xfer_status
> > @@ -741,12 +767,52 @@ core_target::xfer_partial (enum target_object object, const char *annex,  
> 
> > +      /* 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;  
> 
> This returning ...UNAVAILABLE seems like the wrong thing to do.  If
> we want to prevent continuing to the next layer, then we could
> just make core_target::has_all_memory() return true.

You're right.  I ran into a regression while rebasing against current
sources when I started looking at this again.  I ended up writing most
of the patch that you provided to me, all except for the
has_all_memory bit.  I've now added that line from your patch.

> 
> Effectively that would mean we could eliminate that method, since it
> only exists for core files, here, in raw_memory_xfer_partial:
> 
>       /* We want to continue past core files to executables, but not
> 	 past a running target's memory.  */
>       if (ops->has_all_memory ())
> 	break;
> 
> At the very least, that comment should be updated.

I'll update the comment.  remote-sim.c's implementation of
has_all_memory can still return false.  I think there's at
least one other instance where false can be returned.

[patch snipped]

> But I'm not sure (yet anyway), whether reading that section
> as all zeroes is really the right thing to do.
> 
> Running "info proc mappings" when debugging the core shows that
> this address comes from libc.so.  It's the second libc-2.26.so
> mapping below, see "THIS ONE":
> 
> (gdb) info proc mappings 
> Mapped address spaces:
> 
>           Start Addr           End Addr       Size     Offset objfile
>             0x400000           0x401000     0x1000        0x0 /home/pedro/brno/pedro/gdb/binutils-gdb/build/gdb/testsuite/outputs/gdb.base/corefile/corefile
>             0x600000           0x601000     0x1000        0x0 /home/pedro/brno/pedro/gdb/binutils-gdb/build/gdb/testsuite/outputs/gdb.base/corefile/corefile
>             0x601000           0x602000     0x1000     0x1000 /home/pedro/brno/pedro/gdb/binutils-gdb/build/gdb/testsuite/outputs/gdb.base/corefile/corefile
>       0x7ffff76d7000     0x7ffff7884000   0x1ad000        0x0 /usr/lib64/libc-2.26.so
>       0x7ffff7884000     0x7ffff7a83000   0x1ff000   0x1ad000 /usr/lib64/libc-2.26.so   <<< THIS ONE
>       0x7ffff7a83000     0x7ffff7a87000     0x4000   0x1ac000 /usr/lib64/libc-2.26.so
>       0x7ffff7a87000     0x7ffff7a89000     0x2000   0x1b0000 /usr/lib64/libc-2.26.so
>       0x7ffff7a8d000     0x7ffff7bd7000   0x14a000        0x0 /usr/lib64/libm-2.26.so
>       0x7ffff7bd7000     0x7ffff7dd6000   0x1ff000   0x14a000 /usr/lib64/libm-2.26.so
>       0x7ffff7dd6000     0x7ffff7dd7000     0x1000   0x149000 /usr/lib64/libm-2.26.so
>       0x7ffff7dd7000     0x7ffff7dd8000     0x1000   0x14a000 /usr/lib64/libm-2.26.so
>       0x7ffff7dd8000     0x7ffff7dfd000    0x25000        0x0 /usr/lib64/ld-2.26.so
>       0x7ffff7ff5000     0x7ffff7ff7000     0x2000        0x0 /home/pedro/brno/pedro/gdb/binutils-gdb/build/gdb/coremmap.data
>       0x7ffff7ffc000     0x7ffff7ffd000     0x1000    0x24000 /usr/lib64/ld-2.26.so
> 
> 
> So, I tried comparing a live process to a core dump one.  Since we need
> to use a kernel-generated core, what I did was, load the corefile program
> under GDB, let it run till before the abort() call, and then do
> 
>  (gdb) print fork ()
> 
> This makes the program fork, and the fork child crashes and aborts.
> Now I'm still debugging the parent, and I have a kernel-generated core
> with the same same memory map as the still-running inferior.
> 
> I loaded the core dump as a second inferior under gdb.
> (add-inferior -no-connection; inferior 2; file ...; core ...)
> Remember that this now works with multi-target.
> 
>  (gdb) p fork ()
>  [Detaching after fork from child process 19488]
>  $2 = 19488
>  (gdb) add-inferior -no-connection
>  [New inferior 2]
>  Added inferior 2
>  (gdb) inferior 2
>  [Switching to inferior 2 [<null>] (<noexec>)]
>  (gdb) file /home/pedro/brno/pedro/gdb/binutils-gdb/build/gdb/testsuite/outputs/gdb.base/corefile/corefile
>  Reading symbols from /home/pedro/brno/pedro/gdb/binutils-gdb/build/gdb/testsuite/outputs/gdb.base/corefile/corefile...
>  (gdb) core core.19488 
>  [New LWP 19488]
>  Core was generated by `/home/pedro/brno/pedro/gdb/binutils-gdb/build/gdb/testsuite/outputs/gdb.base/co'.
>  Program terminated with signal SIGSEGV, Segmentation fault.
>  #0  0x00007fffffffd23f in ?? ()
> 
> Find the address in question:
> 
>  (gdb) shell objdump -h core.19488
>  ...
>  17 load6         00000000  00007ffff7884000  0000000000000000  001d3000  2**12
>                   ALLOC, READONLY
>  ...
> 
> Disassemble it in inferior 2, the core dump:
> 
>  (gdb) disassemble /r 0x00007ffff7884000,+10
>  Dump of assembler code from 0x7ffff7884000 to 0x7ffff788400a:
>     0x00007ffff7884000:  00 00   add    %al,(%rax)
>     0x00007ffff7884002:  00 00   add    %al,(%rax)
>     0x00007ffff7884004:  00 00   add    %al,(%rax)
>     0x00007ffff7884006:  00 00   add    %al,(%rax)
>     0x00007ffff7884008:  00 00   add    %al,(%rax)
>  End of assembler dump.
> 
> Now let's try disassembling the same address in the live inferior,
> inferior 1:
> 
> (gdb) inferior 1
> [Switching to inferior 1 [process 19451] (/home/pedro/brno/pedro/gdb/binutils-gdb/build/gdb/testsuite/outputs/gdb.base/corefile/corefile)]
> [Switching to thread 1.1 (process 19451)]
> #0  main (argc=1, argv=0x7fffffffd3b8) at /home/pedro/gdb/binutils-gdb/src/gdb/testsuite/gdb.base/coremaker.c:155
> 155       func1 ();
> (gdb) disassemble /r 0x00007ffff7884000,+10
> Dump of assembler code from 0x7ffff7884000 to 0x7ffff788400a:
>    0x00007ffff7884000:  20 c6   and    %al,%dh
>    0x00007ffff7884002:  07      (bad)  
>    0x00007ffff7884003:  00 00   add    %al,(%rax)
>    0x00007ffff7884005:  00 00   add    %al,(%rax)
>    0x00007ffff7884007:  00 40 b8        add    %al,-0x48(%rax)
> End of assembler dump.
> (gdb) 
> 
> They should result in the same contents, but clearly the
> core case read all zeroes, while the live one didn't.
> 
> If we unwind all the patches in this series and try pristine master,
> we hit the original:
> 
>  (gdb) disassemble /r 0x00007ffff7884000,+10
>  Dump of assembler code from 0x7ffff7884000 to 0x7ffff788400a:
>     0x00007ffff7884000:  Cannot access memory at address 0x7ffff7884000
> 
> So GDB doesn't find this section's contents in the executable or shared
> libraries, even though the file-backed mappings suggest we should be able
> to read it from libc-2.26.so.  Maybe on your system you'll have different
> results and gdb manages to find the data in the executable somehow?

I wasn't able to reproduce this on F31.  I was able to reproduce it
on F28.  It took a while to figure out what was happening.

I used the fork trick as you suggested, but I ended up debugging
with separate GDB instances so that I could place the windows side
by side to do comparisons.  However, it was cool seeing how the
add-inferior stuff worked though, so thanks for that!

Okay, so in my session(s), this is what I see:

(gdb) info proc mappings
process 23063
Mapped address spaces:

          Start Addr           End Addr       Size     Offset objfile
            0x400000           0x401000     0x1000        0x0 /mesquite2/sourceware-git/f28-bz25631/bld/gdb/testsuite/outputs/gdb.base/corefile/corefile
            0x600000           0x601000     0x1000        0x0 /mesquite2/sourceware-git/f28-bz25631/bld/gdb/testsuite/outputs/gdb.base/corefile/corefile
            0x601000           0x602000     0x1000     0x1000 /mesquite2/sourceware-git/f28-bz25631/bld/gdb/testsuite/outputs/gdb.base/corefile/corefile
            0x602000           0x623000    0x21000        0x0 [heap]
      0x7ffff7684000     0x7ffff7839000   0x1b5000        0x0 /usr/lib64/libc-2.27.so
      0x7ffff7839000     0x7ffff7a38000   0x1ff000   0x1b5000 /usr/lib64/libc-2.27.so
      0x7ffff7a38000     0x7ffff7a3c000     0x4000   0x1b4000 /usr/lib64/libc-2.27.so
      0x7ffff7a3c000     0x7ffff7a3e000     0x2000   0x1b8000 /usr/lib64/libc-2.27.so
      0x7ffff7a3e000     0x7ffff7a42000     0x4000        0x0 
      0x7ffff7a42000     0x7ffff7bd4000   0x192000        0x0 /usr/lib64/libm-2.27.so
      0x7ffff7bd4000     0x7ffff7dd4000   0x200000   0x192000 /usr/lib64/libm-2.27.so
      0x7ffff7dd4000     0x7ffff7dd5000     0x1000   0x192000 /usr/lib64/libm-2.27.so
      0x7ffff7dd5000     0x7ffff7dd6000     0x1000   0x193000 /usr/lib64/libm-2.27.so
      0x7ffff7dd6000     0x7ffff7dfd000    0x27000        0x0 /usr/lib64/ld-2.27.so
      0x7ffff7fcb000     0x7ffff7fd0000     0x5000        0x0 
      0x7ffff7ff4000     0x7ffff7ff6000     0x2000        0x0 
      0x7ffff7ff6000     0x7ffff7ff8000     0x2000        0x0 /mesquite2/sourceware-git/f28-bz25631/bld/gdb/coremmap.data
      0x7ffff7ff8000     0x7ffff7ffb000     0x3000        0x0 [vvar]
      0x7ffff7ffb000     0x7ffff7ffc000     0x1000        0x0 [vdso]
      0x7ffff7ffc000     0x7ffff7ffd000     0x1000    0x26000 /usr/lib64/ld-2.27.so
      0x7ffff7ffd000     0x7ffff7ffe000     0x1000    0x27000 /usr/lib64/ld-2.27.so
      0x7ffff7ffe000     0x7ffff7fff000     0x1000        0x0 
      0x7ffffffdd000     0x7ffffffff000    0x22000        0x0 [stack]
  0xffffffffff600000 0xffffffffff601000     0x1000        0x0 [vsyscall]

I'm going to focus on this mapping, which is in libc-2.27.so:

      0x7ffff7839000     0x7ffff7a38000   0x1ff000   0x1b5000 /usr/lib64/libc-2.27.so

It's interesting to look at the corresponding line from /proc/23063/maps too:

7ffff7839000-7ffff7a38000 ---p 001b5000 fc:03 1446829                    /usr/lib64/libc-2.27.so

Of interest here is are the permissions for this mapping; it's "---p",
which indicates that it cannot be read, written, or executed.  The "p"
flag indicates that it's private.  So from this we might infer that this
region was not really in use by the process.

Also, of very great importance (for figuring out what's happening
anyway), is the number 001b5000.  It's the offset for this region in
the shared library.

Examining memory in the live process:

(gdb) x/16x 0x7ffff7839000
0x7ffff7839000:	0x0007ddf0	0x00000000	0x0007dba0	0x00000000
0x7ffff7839010:	0x0007ec00	0x00000000	0x0007ec10	0x00000000
0x7ffff7839020:	0x0007ebe0	0x00000000	0x0007ddf0	0x00000000
0x7ffff7839030:	0x0007ebf0	0x00000000	0x0007ec20	0x00000000

And via the core file:

(gdb) x/16x 0x7ffff7839000
0x7ffff7839000:	0x00000000	0x00000000	0x00000000	0x00000000
0x7ffff7839010:	0x00000000	0x00000000	0x00000000	0x00000000
0x7ffff7839020:	0x00000000	0x00000000	0x00000000	0x00000000
0x7ffff7839030:	0x00000000	0x00000000	0x00000000	0x00000000

Okay, so they're different.  I've reproduced the problem you found.

Let's dump part of libc-2.27.so...

[kev@f28-1 gdb]$ od -A x -t x4 -j 0x001b5000 -N 176 /usr/lib64/libc-2.27.so
1b5000 0007ddf0 00000000 0007dba0 00000000
1b5010 0007ec00 00000000 0007ec10 00000000
1b5020 0007ebe0 00000000 0007ddf0 00000000
1b5030 0007ebf0 00000000 0007ec20 00000000
1b5040 0007ec30 00000000 00000000 00000000
1b5050 00000000 00000000 00000000 00000000
*
1b5070 00000000 00000000 00078ba0 00000000
1b5080 00000000 00000000 00000000 00000000
1b5090 00000000 00000000 00078ae0 00000000
1b50a0 00000000 00000000 00000000 00000000
1b50b0

Note that the initial four lines from the dump match the four lines
that I dumped via x/x for the live process.

Now, let's look at some of the objdump -h output for
/usr/lib64/libc-2.27.so:

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
...
 27 __libc_IO_vtables 00000d68  00000000003b4760  00000000003b4760  001b4760  2**5
                  CONTENTS, ALLOC, LOAD, DATA
 28 .data.rel.ro  000026a0  00000000003b54e0  00000000003b54e0  001b54e0  2**5
                  CONTENTS, ALLOC, LOAD, DATA

The offset range for __libc_IO_vtables is 0x1b4760 to 0x1b54c8.  So, I
think that what we're looking at there is some of the vtable data.

However, this not where we actually find the vtables in memory.  This
is what "info target" has to say about the matter:

	0x00007ffff7a38760 - 0x00007ffff7a394c8 is __libc_IO_vtables in /lib64/libc.so.6

Let's take the file offset corresponding to the start of the region
(start of region=0x7ffff7839000, file offset=0x1b5000) and subtract
the offset provided by objdump for __libc_IO_vtables (file offset=001b4760):

(gdb) p/x 0x1b5000-0x1b4760
$28 = 0x8a0

Now let's use this offset to try to look at actual vtables memory
corresponding to our mystery region.

(gdb) x/16x 0x00007ffff7a38760+0x8a0
0x7ffff7a39000 <_IO_strn_jumps+96>:	0xf7701df0	0x00007fff	0xf7701ba0	0x00007fff
0x7ffff7a39010 <_IO_strn_jumps+112>:	0xf7702c00	0x00007fff	0xf7702c10	0x00007fff
0x7ffff7a39020 <_IO_strn_jumps+128>:	0xf7702be0	0x00007fff	0xf7701df0	0x00007fff
0x7ffff7a39030 <_IO_strn_jumps+144>:	0xf7702bf0	0x00007fff	0xf7702c20	0x00007fff

Okay, this looks nothing at all like the memory that we dumped earlier.
But let's try something...

(gdb) p/x 0xf7701df0-0x0007ddf0
$29 = 0xf7684000
(gdb) p/x 0xf7701ba0-0x0007dba0
$30 = 0xf7684000
(gdb) p/x 0xf7702c00-0x0007ec00
$31 = 0xf7684000
(gdb) p/x 0xf7702c10-0x0007ec10
$32 = 0xf7684000

Here I took the first four non-zero values in the region starting at
0x7ffff7839000 and subtracted them from the corresponding value
starting at 0x7ffff7a39000 <_IO_strn_jumps+96>.  In each case, the
result of the subtraction is the same number, 0xf7684000, which I'm
guessing is a base address for something.  In any case, I think there's
enough of a correspondence here to suspect that one was derived from
the other.

So... my best guess is that the memory in question was scratch storage for
the libc IO vtable data.  If that's really the case (which the mapping
permissions suggest), then this memory wasn't being actively used by
the program at that point in its execution.

In any case, because the region is file backed, the kernel chose not
to dump it.  The address ranges provided by "info target" know nothing
about that address (in this case 0x7ffff7839000), so that explains why
GDB wasn't able to go to the shared library to get it (even though
it's there if it were told where to look).  If we think it important
enough, we might be able to fix this in gdb and/or bfd.

I hope that's a satisfactory enough answer to move on...

I'll post a v2 series soon; I just have a bit more polishing to do first.

Kevin



More information about the Gdb-patches mailing list