public inbox for gdb-cvs@sourceware.org
help / color / mirror / Atom feed
* [binutils-gdb] gdb: include the base address in in-memory bfd filenames
@ 2022-10-10 12:10 Andrew Burgess
  0 siblings, 0 replies; only message in thread
From: Andrew Burgess @ 2022-10-10 12:10 UTC (permalink / raw)
  To: gdb-cvs

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=66984afd29ea9bad2155ed21098437a71208a106

commit 66984afd29ea9bad2155ed21098437a71208a106
Author: Andrew Burgess <aburgess@redhat.com>
Date:   Wed Oct 5 15:26:11 2022 +0100

    gdb: include the base address in in-memory bfd filenames
    
    The struct target_buffer (in gdb_bfd.c) is used to hold information
    about an in-memory BFD object created by GDB.  For now this mechanism
    is used by GDB when loading information about JIT symfiles.
    
    This commit updates target_buffer (in gdb_bfd.c) to be more C++ like,
    and, at the same time, adds the base address of the symfile into the
    BFD filename.
    
    Right now, every in-memory BFD is given the filename "<in-memory>".
    This filename is visible in things like 'maint info symtabs' and
    'maint info line-table'.  If there are multiple in-memory BFD objects
    then it can be hard to match keep track if which BFD is which.  This
    commit changes the name to be "<in-memory@ADDRESS>" where ADDRESS is
    replaced with the base address for where the in-memory symbol file was
    read from.
    
    As an example of how this is useful, here's the output of 'maint info
    jit' showing a single loaded JIT symfile:
    
      (gdb) maintenance info jit
      jit_code_entry address symfile address    symfile size
      0x00000000004056b0     0x0000000007000000 17320
    
    And here's part of the output from 'maint info symtabs':
    
      (gdb) maintenance info symtabs
      ...snip...
      { objfile <in-memory@0x7000000> ((struct objfile *) 0x5258250)
        { ((struct compunit_symtab *) 0x4f0afb0)
          debugformat DWARF 4
          producer GNU C17 9.3.1 20200408 (Red Hat 9.3.1-2) -mtune=generic -march=x86-64 -g -fno-stack-protector -fpic
          name jit-elf-solib.c
          dirname /tmp/binutils-gdb/build/gdb/testsuite
          blockvector ((struct blockvector *) 0x5477850)
          user ((struct compunit_symtab *) (null))
            { symtab /tmp/binutils-gdb/build/gdb/testsuite/../../../src/gdb/testsuite/gdb.base/jit-elf-solib.c ((struct symtab *) 0x4f0b030)
              fullname (null)
              linetable ((struct linetable *) 0x5477880)
            }
        }
      }
    
    I've added a new test that checks the new in-memory file names are
    generated correctly, and also checks that the in-memory JIT files can
    be dumped back out using 'dump binary memory'.

Diff:
---
 gdb/gdb_bfd.c                           |  56 +++++++++---
 gdb/testsuite/gdb.base/jit-bfd-name.exp | 155 ++++++++++++++++++++++++++++++++
 gdb/testsuite/lib/gdb.exp               |  22 +++++
 gdb/testsuite/lib/jit-elf-helpers.exp   |  12 ++-
 4 files changed, 229 insertions(+), 16 deletions(-)

diff --git a/gdb/gdb_bfd.c b/gdb/gdb_bfd.c
index 36ef5e1cc5a..d2e18c74168 100644
--- a/gdb/gdb_bfd.c
+++ b/gdb/gdb_bfd.c
@@ -217,12 +217,43 @@ gdb_bfd_has_target_filename (struct bfd *abfd)
   return is_target_filename (bfd_get_filename (abfd));
 }
 
-/* For `gdb_bfd_open_from_target_memory`.  */
+/* For `gdb_bfd_open_from_target_memory`.  An object that manages the
+   details of a BFD in target memory.  */
 
 struct target_buffer
 {
-  CORE_ADDR base;
-  ULONGEST size;
+  /* Constructor.  BASE and SIZE define where the BFD can be found in
+     target memory.  */
+  target_buffer (CORE_ADDR base, ULONGEST size)
+    : m_base (base),
+      m_size (size)
+  {
+    m_filename
+      = xstrprintf ("<in-memory@%s>", core_addr_to_string_nz (m_base));
+  }
+
+  /* Return the size of the in-memory BFD file.  */
+  ULONGEST size () const
+  { return m_size; }
+
+  /* Return the base address of the in-memory BFD file.  */
+  CORE_ADDR base () const
+  { return m_base; }
+
+  /* Return a generated filename for the in-memory BFD file.  The generated
+     name will include the M_BASE value.  */
+  const char *filename () const
+  { return m_filename.get (); }
+
+private:
+  /* The base address of the in-memory BFD file.  */
+  CORE_ADDR m_base;
+
+  /* The size (in-bytes) of the in-memory BFD file.  */
+  ULONGEST m_size;
+
+  /* Holds the generated name of the in-memory BFD file.  */
+  gdb::unique_xmalloc_ptr<char> m_filename;
 };
 
 /* For `gdb_bfd_open_from_target_memory`.  Opening the file is a no-op.  */
@@ -239,7 +270,8 @@ mem_bfd_iovec_open (struct bfd *abfd, void *open_closure)
 static int
 mem_bfd_iovec_close (struct bfd *abfd, void *stream)
 {
-  xfree (stream);
+  struct target_buffer *buffer = (target_buffer *) stream;
+  delete buffer;
 
   /* Zero means success.  */
   return 0;
@@ -253,18 +285,18 @@ static file_ptr
 mem_bfd_iovec_pread (struct bfd *abfd, void *stream, void *buf,
 		     file_ptr nbytes, file_ptr offset)
 {
-  int err;
   struct target_buffer *buffer = (struct target_buffer *) stream;
 
   /* If this read will read all of the file, limit it to just the rest.  */
-  if (offset + nbytes > buffer->size)
-    nbytes = buffer->size - offset;
+  if (offset + nbytes > buffer->size ())
+    nbytes = buffer->size () - offset;
 
   /* If there are no more bytes left, we've reached EOF.  */
   if (nbytes == 0)
     return 0;
 
-  err = target_read_memory (buffer->base + offset, (gdb_byte *) buf, nbytes);
+  int err
+    = target_read_memory (buffer->base () + offset, (gdb_byte *) buf, nbytes);
   if (err)
     return -1;
 
@@ -280,7 +312,7 @@ mem_bfd_iovec_stat (struct bfd *abfd, void *stream, struct stat *sb)
   struct target_buffer *buffer = (struct target_buffer*) stream;
 
   memset (sb, 0, sizeof (struct stat));
-  sb->st_size = buffer->size;
+  sb->st_size = buffer->size ();
   return 0;
 }
 
@@ -290,11 +322,9 @@ gdb_bfd_ref_ptr
 gdb_bfd_open_from_target_memory (CORE_ADDR addr, ULONGEST size,
 				 const char *target)
 {
-  struct target_buffer *buffer = XNEW (struct target_buffer);
+  struct target_buffer *buffer = new target_buffer (addr, size);
 
-  buffer->base = addr;
-  buffer->size = size;
-  return gdb_bfd_openr_iovec ("<in-memory>", target,
+  return gdb_bfd_openr_iovec (buffer->filename (), target,
 			      mem_bfd_iovec_open,
 			      buffer,
 			      mem_bfd_iovec_pread,
diff --git a/gdb/testsuite/gdb.base/jit-bfd-name.exp b/gdb/testsuite/gdb.base/jit-bfd-name.exp
new file mode 100644
index 00000000000..7c8ad50d072
--- /dev/null
+++ b/gdb/testsuite/gdb.base/jit-bfd-name.exp
@@ -0,0 +1,155 @@
+# Copyright 2022 Free Software Foundation, Inc.
+
+# This program 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.
+#
+# This program 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/>.
+
+# Check the BFD filename (as used in the symfile name) that is
+# automatically generated for in-memory BFD files, as used by the JIT
+# system within GDB.
+#
+# Additionally, check that GDB cau use 'dump binary memory' to write
+# out the in-memory JIT files.
+
+if {[skip_shlib_tests]} {
+    untested "skipping shared library tests"
+    return -1
+}
+
+load_lib jit-elf-helpers.exp
+
+# The main code that loads and registers JIT objects.
+set main_basename "jit-elf-main"
+set main_srcfile ${srcdir}/${subdir}/${main_basename}.c
+set main_binfile [standard_output_file ${main_basename}]
+
+# The shared library that gets loaded as JIT objects.
+set jit_solib_basename jit-elf-solib
+set jit_solib_srcfile ${srcdir}/${subdir}/${jit_solib_basename}.c
+
+# Compile two shared libraries to use as JIT objects.
+set jit_solibs_target [compile_and_download_n_jit_so \
+			   $jit_solib_basename $jit_solib_srcfile 2 \
+			   {debug}]
+if { $jit_solibs_target == -1 } {
+    return
+}
+
+# Compile the main code (which loads the JIT objects).
+if { [compile_jit_main ${main_srcfile} ${main_binfile} {}] != 0 } {
+    return
+}
+
+clean_restart $::main_binfile
+if { ![runto_main] } {
+    return
+}
+
+# Poke desired values directly into inferior instead of using "set
+# args" because "set args" does not work under gdbserver.
+set count [expr [llength $jit_solibs_target] + 1]
+gdb_test_no_output "set var argc=$count" "forging argc"
+gdb_test_no_output "set var argv=fake_argv" "forging argv"
+for {set i 1} {$i < $count} {incr i} {
+    set jit_solib_target [lindex $jit_solibs_target [expr $i-1]]
+    gdb_test_no_output "set var argv\[$i\]=\"${jit_solib_target}\"" \
+	"forging argv\[$i\]"
+}
+
+# Run until the JIT libraries are loaded.
+gdb_breakpoint [gdb_get_line_number "break here 1" $::main_srcfile]
+gdb_continue_to_breakpoint "break here 1"
+
+# Confirm that the two expected functions are available.
+gdb_test "info function ^jit_function" \
+    [multi_line \
+	 "File \[^\r\n\]+jit-elf-solib.c:" \
+	 "${decimal}:\\s+int jit_function_0001\\(\\);" \
+	 "${decimal}:\\s+int jit_function_0002\\(\\);"]
+
+# Capture the addresses of each JIT symfile.
+set symfile_addrs {}
+set symfile_lengths {}
+gdb_test_multiple "maint info jit" "" {
+    -re "^maint info jit\r\n" {
+	exp_continue
+    }
+    -re "^jit_code_entry address\\s+symfile address\\s+symfile size\\s*\r\n" {
+	exp_continue
+    }
+    -re "^${hex}\\s+(${hex})\\s+(${decimal})\\s*\r\n" {
+	lappend symfile_addrs $expect_out(1,string)
+	lappend symfile_lengths $expect_out(2,string)
+	exp_continue
+    }
+    -re "^$gdb_prompt $" {
+    }
+}
+
+# Now check the 'maint info symtabs' output to ensure that each
+# symfile is mentioned, and that the names are as expected.
+set bfd_name_addrs {}
+gdb_test_multiple "maint info symtabs" "" {
+    -re "^maint info symtabs\r\n" {
+	exp_continue
+    }
+    -re "^\\\}\\s*\r\n" {
+	exp_continue
+    }
+    -re "^\\\{ objfile <in-memory@($hex)>\\s+\[^\r\n\]+\r\n" {
+	lappend bfd_name_addrs $expect_out(1,string)
+	exp_continue
+    }
+    -re "^\\\{ objfile (\\S+)\\s+\[^\r\n\]+\r\n" {
+	exp_continue
+    }
+    -re "^\\s+\[^\r\n\]+\r\n" {
+	exp_continue
+    }
+    -re "^$gdb_prompt $" {
+    }
+}
+
+# Now dump each JIT solib using the 'dump binary memory' command.
+set count 0
+foreach addr $symfile_addrs len $symfile_lengths {
+    incr count
+    set output [standard_output_file "dump-elf-solib.${count}.so"]
+    set end [expr $addr + $len]
+    gdb_test_no_output "dump binary memory $output $addr $end" \
+	"dump jit solib $count"
+
+    gdb_assert { [cmp_binary_files $output [standard_output_file "jit-elf-solib.${count}.so"]] == 0} \
+	"check dump of jit solib $count is as expected"
+}
+
+
+# Check that each of the expected jit symfile addresses was mentioned
+# in an in-memory BFD filename.
+set count 1
+foreach addr $symfile_addrs {
+    # Drop any loading zeros from the symfile address.
+    set addr [format 0x%x $addr]
+
+    # Check there was a BFD with the expected address in its name.
+    gdb_assert { [expr [lsearch -exact $bfd_name_addrs $addr] != -1] } \
+	"check for in-memory bfd $count"
+    incr count
+}
+
+# Continue until the JIT libraries are unloaded.
+gdb_breakpoint [gdb_get_line_number "break here 2" $::main_srcfile]
+gdb_continue_to_breakpoint "break here 2"
+
+# All jit librares must have been unregistered.
+gdb_test "info function jit_function" \
+    "All functions matching regular expression \"jit_function\":"
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 44cc28b3005..ae3a46cd4ce 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -8164,6 +8164,28 @@ proc cmp_file_string { file str msg } {
     }
 }
 
+# Compare FILE1 and FILE2 as binary files.  Return 0 if the files are
+# equal, otherwise, return non-zero.
+
+proc cmp_binary_files { file1 file2 } {
+    set fd1 [open $file1]
+    fconfigure $fd1 -translation binary
+    set fd2 [open $file2]
+    fconfigure $fd2 -translation binary
+
+    set blk_size 1024
+    while {true} {
+	set blk1 [read $fd1 $blk_size]
+	set blk2 [read $fd2 $blk_size]
+	set diff [string compare $blk1 $blk2]
+	if {$diff != 0 || [eof $fd1] || [eof $fd2]} {
+	    close $fd1
+	    close $fd2
+	    return $diff
+	}
+    }
+}
+
 # Does the compiler support CTF debug output using '-gctf' compiler
 # flag?  If not then we should skip these tests.  We should also
 # skip them if libctf was explicitly disabled.
diff --git a/gdb/testsuite/lib/jit-elf-helpers.exp b/gdb/testsuite/lib/jit-elf-helpers.exp
index b699917f209..80ba769434c 100644
--- a/gdb/testsuite/lib/jit-elf-helpers.exp
+++ b/gdb/testsuite/lib/jit-elf-helpers.exp
@@ -74,9 +74,13 @@ proc compile_jit_elf_main_as_so {main_solib_srcfile main_solib_binfile options}
 # Compile jit-elf-solib.c as a shared library in multiple copies and
 # upload them to the target.
 #
+# OPTIONS_LIST is a list of additional options to pass through to
+# gdb_compile_shlib.
+#
 # On success, return a list of target path to the shared libraries.
 # On failure, return -1.
-proc compile_and_download_n_jit_so {jit_solib_basename jit_solib_srcfile count} {
+proc compile_and_download_n_jit_so {jit_solib_basename jit_solib_srcfile \
+					count {options_list {}}} {
     global jit_load_address jit_load_increment
     set binfiles_target {}
 
@@ -93,9 +97,11 @@ proc compile_and_download_n_jit_so {jit_solib_basename jit_solib_srcfile count}
 	# compiled shared library against a fixed base address.  Combined
 	# with mapping the resulting binary to the same fixed base it allows
 	# to dynamically execute functions from it without any further adjustments.
+	set fname [format "jit_function_%04d" $i]
 	set options [list \
-	    additional_flags=-DFUNCTION_NAME=[format "jit_function_%04d" $i] \
-	    text_segment=$addr]
+			 ${options_list} \
+			 additional_flags=-DFUNCTION_NAME=$fname \
+			 text_segment=$addr]
 	if { [gdb_compile_shlib ${jit_solib_srcfile} ${binfile} \
 		  $options] != "" } {
 	    set f [file tail $binfile]

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2022-10-10 12:10 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-10-10 12:10 [binutils-gdb] gdb: include the base address in in-memory bfd filenames Andrew Burgess

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).