[v3,12/14] add linux_infcall_mmap

Message ID 20141101214719.13230.96367.stgit@host1.jankratochvil.net
State New, archived
Headers

Commit Message

Jan Kratochvil Nov. 1, 2014, 9:47 p.m. UTC
  This implements the new gdbarch "infcall_mmap" method for Linux.

2014-10-07  Jan Kratochvil  <jan.kratochvil@redhat.com>

	* linux-tdep.c (linux_infcall_mmap): New function.
	(linux_init_abi): Add it to gdbarch.
---
 gdb/ChangeLog    |    5 +++++
 gdb/linux-tdep.c |   46 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 51 insertions(+)
  

Comments

Pedro Alves Nov. 11, 2014, 4:43 p.m. UTC | #1
On 11/01/2014 09:47 PM, Jan Kratochvil wrote:
> +/* See gdbarch.sh 'infcall_mmap'.  */
> +
> +static CORE_ADDR
> +linux_infcall_mmap (CORE_ADDR size, unsigned prot)
> +{
> +  struct objfile *objf;
> +  /* Do there still exist any Linux systems without "mmap64"?
> +     "mmap" uses 64-bit off_t on x86_64 and 32-bit off_t on i386 and x32.  */
> +  struct value *mmap_val = find_function_in_inferior ("mmap64", &objf);
> +  struct value *addr_val;
> +  struct gdbarch *gdbarch = get_objfile_arch (objf);
> +  CORE_ADDR retval;
> +  enum
> +    {
> +      ARG_ADDR, ARG_LENGTH, ARG_PROT, ARG_FLAGS, ARG_FD, ARG_OFFSET, ARG_MAX
> +    };
> +  struct value *arg[ARG_MAX];
> +
> +  arg[ARG_ADDR] = value_from_pointer (builtin_type (gdbarch)->builtin_data_ptr,
> +				      0);
> +  /* Assuming sizeof (unsigned long) == sizeof (size_t).  */
> +  arg[ARG_LENGTH] = value_from_ulongest
> +		    (builtin_type (gdbarch)->builtin_unsigned_long, size);
> +  gdb_assert ((prot & ~7) == 0);
> +  arg[ARG_PROT] = value_from_longest (builtin_type (gdbarch)->builtin_int,
> +				      0
> +				      | ((prot & 4) != 0 ? PROT_READ : 0)
> +				      | ((prot & 2) != 0 ? PROT_WRITE : 0)
> +				      | ((prot & 1) != 0 ? PROT_EXEC : 0));
> +  arg[ARG_FLAGS] = value_from_longest (builtin_type (gdbarch)->builtin_int,
> +				       MAP_PRIVATE | MAP_ANONYMOUS);

PROT_READ, PROT_WRITE, PROT_EXEC, MAP_PRIVATE, MAP_ANONYMOUS
are host values/macros.

This needs to be made host independent, otherwise cross debugging to
Linux passes the wrong values to mmap.

It also likely breaks --enable-targets=all builds on hosts
that don't have mmap at all.  E.g., this comes out empty on F20:

 $ grep -rn PROT_READ /usr/i686-w64-mingw32/sys-root/mingw/include/

OTOH, the 4, 2 and 1 bits checked against the 'prot' argument
are magical constants; they'd best be put behind macros, like e.g.,
GDB_MMAP_PROT_READ, etc., here and at the callers
of gdbarch_infcall_mmap.

Thanks,
Pedro Alves
  
Jan Kratochvil Nov. 23, 2014, 7:11 p.m. UTC | #2
On Tue, 11 Nov 2014 17:43:19 +0100, Pedro Alves wrote:
> On 11/01/2014 09:47 PM, Jan Kratochvil wrote:
> > +/* See gdbarch.sh 'infcall_mmap'.  */
> > +
> > +static CORE_ADDR
> > +linux_infcall_mmap (CORE_ADDR size, unsigned prot)
> > +{
> > +  struct objfile *objf;
> > +  /* Do there still exist any Linux systems without "mmap64"?
> > +     "mmap" uses 64-bit off_t on x86_64 and 32-bit off_t on i386 and x32.  */
> > +  struct value *mmap_val = find_function_in_inferior ("mmap64", &objf);
> > +  struct value *addr_val;
> > +  struct gdbarch *gdbarch = get_objfile_arch (objf);
> > +  CORE_ADDR retval;
> > +  enum
> > +    {
> > +      ARG_ADDR, ARG_LENGTH, ARG_PROT, ARG_FLAGS, ARG_FD, ARG_OFFSET, ARG_MAX
> > +    };
> > +  struct value *arg[ARG_MAX];
> > +
> > +  arg[ARG_ADDR] = value_from_pointer (builtin_type (gdbarch)->builtin_data_ptr,
> > +				      0);
> > +  /* Assuming sizeof (unsigned long) == sizeof (size_t).  */
> > +  arg[ARG_LENGTH] = value_from_ulongest
> > +		    (builtin_type (gdbarch)->builtin_unsigned_long, size);
> > +  gdb_assert ((prot & ~7) == 0);
> > +  arg[ARG_PROT] = value_from_longest (builtin_type (gdbarch)->builtin_int,
> > +				      0
> > +				      | ((prot & 4) != 0 ? PROT_READ : 0)
> > +				      | ((prot & 2) != 0 ? PROT_WRITE : 0)
> > +				      | ((prot & 1) != 0 ? PROT_EXEC : 0));
> > +  arg[ARG_FLAGS] = value_from_longest (builtin_type (gdbarch)->builtin_int,
> > +				       MAP_PRIVATE | MAP_ANONYMOUS);
> 
> PROT_READ, PROT_WRITE, PROT_EXEC, MAP_PRIVATE, MAP_ANONYMOUS
> are host values/macros.

Added to arch-utils.h:
+/* Symbols for gdbarch_infcall_mmap; their Linux PROT_* system
+   definitions would be dependent on compilation host.  */
+#define GDB_MMAP_PROT_READ     0x1     /* Page can be read.  */
+#define GDB_MMAP_PROT_WRITE    0x2     /* Page can be written.  */
+#define GDB_MMAP_PROT_EXEC     0x4     /* Page can be executed.  */

Added to linux-tdep.c:
+/* Symbols for linux_infcall_mmap's ARG_FLAGS; their Linux MAP_* system
+   definitions would be dependent on compilation host.  */
+#define GDB_MMAP_MAP_PRIVATE   0x02            /* Changes are private.  */
+#define GDB_MMAP_MAP_ANONYMOUS 0x20            /* Don't use a file.  */
and
-  gdb_assert ((prot & ~7) == 0);
+  gdb_assert ((prot & ~(GDB_MMAP_PROT_READ | GDB_MMAP_PROT_WRITE
+                       | GDB_MMAP_PROT_EXEC))
+             == 0);

Changed gdbarch.sh comment:
-# PROT has rwx bitmask format - bit 2 (value 4) is for readable memory, bit 1
-# (value 2) is for writable memory and bit 0 (value 1) is for executable memory.
+# PROT has GDB_MMAP_PROT_* bitmask format.

Change changed the magic numbers to GDB_MMAP_PROT_* accordingly:
       // Make the memory always readable.
-      prot = 4;
+      prot = GDB_MMAP_PROT_READ;
       if ((bfd_get_section_flags (abfd, sect) & SEC_READONLY) == 0)
-       prot |= 2;
+       prot |= GDB_MMAP_PROT_WRITE;
       if ((bfd_get_section_flags (abfd, sect) & SEC_CODE) != 0)
-       prot |= 1;
+       prot |= GDB_MMAP_PROT_EXEC;
[...]
-                                       TYPE_LENGTH (regs_type), 4);
+                                       TYPE_LENGTH (regs_type),
+                                       GDB_MMAP_PROT_READ);


> It also likely breaks --enable-targets=all builds on hosts
> that don't have mmap at all.  E.g., this comes out empty on F20:
> 
>  $ grep -rn PROT_READ /usr/i686-w64-mingw32/sys-root/mingw/include/

This could be caught automatically by Gerrit+Jenkins like other projects do
instead of wasting engineering time to do by hand all the parts of reviews
which machines can do.  Moreover such regressions would be caught before the
commit, compared to the GDB regressions happening post-commit as there is no
automatic pre-commit checking.  My Gerrit proposal was denied and replaced by
Patchwork instead.  This also makes regression results comparison difficult to
do as the regressions stack on top of each others.


Thanks,
Jan
  
Pedro Alves Dec. 12, 2014, 2:37 p.m. UTC | #3
On 11/23/2014 07:11 PM, Jan Kratochvil wrote:
> On Tue, 11 Nov 2014 17:43:19 +0100, Pedro Alves wrote:
>> On 11/01/2014 09:47 PM, Jan Kratochvil wrote:
>>> +/* See gdbarch.sh 'infcall_mmap'.  */
>>> +
>>> +static CORE_ADDR
>>> +linux_infcall_mmap (CORE_ADDR size, unsigned prot)
>>> +{
>>> +  struct objfile *objf;
>>> +  /* Do there still exist any Linux systems without "mmap64"?
>>> +     "mmap" uses 64-bit off_t on x86_64 and 32-bit off_t on i386 and x32.  */
>>> +  struct value *mmap_val = find_function_in_inferior ("mmap64", &objf);
>>> +  struct value *addr_val;
>>> +  struct gdbarch *gdbarch = get_objfile_arch (objf);
>>> +  CORE_ADDR retval;
>>> +  enum
>>> +    {
>>> +      ARG_ADDR, ARG_LENGTH, ARG_PROT, ARG_FLAGS, ARG_FD, ARG_OFFSET, ARG_MAX
>>> +    };
>>> +  struct value *arg[ARG_MAX];
>>> +
>>> +  arg[ARG_ADDR] = value_from_pointer (builtin_type (gdbarch)->builtin_data_ptr,
>>> +				      0);
>>> +  /* Assuming sizeof (unsigned long) == sizeof (size_t).  */
>>> +  arg[ARG_LENGTH] = value_from_ulongest
>>> +		    (builtin_type (gdbarch)->builtin_unsigned_long, size);
>>> +  gdb_assert ((prot & ~7) == 0);
>>> +  arg[ARG_PROT] = value_from_longest (builtin_type (gdbarch)->builtin_int,
>>> +				      0
>>> +				      | ((prot & 4) != 0 ? PROT_READ : 0)
>>> +				      | ((prot & 2) != 0 ? PROT_WRITE : 0)
>>> +				      | ((prot & 1) != 0 ? PROT_EXEC : 0));
>>> +  arg[ARG_FLAGS] = value_from_longest (builtin_type (gdbarch)->builtin_int,
>>> +				       MAP_PRIVATE | MAP_ANONYMOUS);
>>
>> PROT_READ, PROT_WRITE, PROT_EXEC, MAP_PRIVATE, MAP_ANONYMOUS
>> are host values/macros.
> 
> Added to arch-utils.h:
> +/* Symbols for gdbarch_infcall_mmap; their Linux PROT_* system
> +   definitions would be dependent on compilation host.  */
> +#define GDB_MMAP_PROT_READ     0x1     /* Page can be read.  */
> +#define GDB_MMAP_PROT_WRITE    0x2     /* Page can be written.  */
> +#define GDB_MMAP_PROT_EXEC     0x4     /* Page can be executed.  */
> 
> Added to linux-tdep.c:
> +/* Symbols for linux_infcall_mmap's ARG_FLAGS; their Linux MAP_* system
> +   definitions would be dependent on compilation host.  */
> +#define GDB_MMAP_MAP_PRIVATE   0x02            /* Changes are private.  */
> +#define GDB_MMAP_MAP_ANONYMOUS 0x20            /* Don't use a file.  */
> and
> -  gdb_assert ((prot & ~7) == 0);
> +  gdb_assert ((prot & ~(GDB_MMAP_PROT_READ | GDB_MMAP_PROT_WRITE
> +                       | GDB_MMAP_PROT_EXEC))
> +             == 0);
> 
> Changed gdbarch.sh comment:
> -# PROT has rwx bitmask format - bit 2 (value 4) is for readable memory, bit 1
> -# (value 2) is for writable memory and bit 0 (value 1) is for executable memory.
> +# PROT has GDB_MMAP_PROT_* bitmask format.
> 
> Change changed the magic numbers to GDB_MMAP_PROT_* accordingly:
>        // Make the memory always readable.
> -      prot = 4;
> +      prot = GDB_MMAP_PROT_READ;
>        if ((bfd_get_section_flags (abfd, sect) & SEC_READONLY) == 0)
> -       prot |= 2;
> +       prot |= GDB_MMAP_PROT_WRITE;
>        if ((bfd_get_section_flags (abfd, sect) & SEC_CODE) != 0)
> -       prot |= 1;
> +       prot |= GDB_MMAP_PROT_EXEC;
> [...]
> -                                       TYPE_LENGTH (regs_type), 4);
> +                                       TYPE_LENGTH (regs_type),
> +                                       GDB_MMAP_PROT_READ);
> 


Thanks Jan.  This sounds good.

Thanks,
Pedro Alves
  

Patch

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 329e42b..52b048e 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,10 @@ 
 2014-10-07  Jan Kratochvil  <jan.kratochvil@redhat.com>
 
+	* linux-tdep.c (linux_infcall_mmap): New function.
+	(linux_init_abi): Add it to gdbarch.
+
+2014-10-07  Jan Kratochvil  <jan.kratochvil@redhat.com>
+
 	* dwarf2loc.h (dwarf2_reg_to_regnum_or_error): Declare.
 	* dwarf2loc.c (dwarf2_reg_to_regnum_or_error): Rename from
 	translate_register.  Now public.
diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
index ffc3e87..e93ba61 100644
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -33,6 +33,9 @@ 
 #include "arch-utils.h"
 #include "gdb_obstack.h"
 #include "observer.h"
+#include "objfiles.h"
+#include "infcall.h"
+#include <sys/mman.h>
 
 #include <ctype.h>
 
@@ -1921,6 +1924,48 @@  linux_vsyscall_range (struct gdbarch *gdbarch, struct mem_range *range)
   return 1;
 }
 
+/* See gdbarch.sh 'infcall_mmap'.  */
+
+static CORE_ADDR
+linux_infcall_mmap (CORE_ADDR size, unsigned prot)
+{
+  struct objfile *objf;
+  /* Do there still exist any Linux systems without "mmap64"?
+     "mmap" uses 64-bit off_t on x86_64 and 32-bit off_t on i386 and x32.  */
+  struct value *mmap_val = find_function_in_inferior ("mmap64", &objf);
+  struct value *addr_val;
+  struct gdbarch *gdbarch = get_objfile_arch (objf);
+  CORE_ADDR retval;
+  enum
+    {
+      ARG_ADDR, ARG_LENGTH, ARG_PROT, ARG_FLAGS, ARG_FD, ARG_OFFSET, ARG_MAX
+    };
+  struct value *arg[ARG_MAX];
+
+  arg[ARG_ADDR] = value_from_pointer (builtin_type (gdbarch)->builtin_data_ptr,
+				      0);
+  /* Assuming sizeof (unsigned long) == sizeof (size_t).  */
+  arg[ARG_LENGTH] = value_from_ulongest
+		    (builtin_type (gdbarch)->builtin_unsigned_long, size);
+  gdb_assert ((prot & ~7) == 0);
+  arg[ARG_PROT] = value_from_longest (builtin_type (gdbarch)->builtin_int,
+				      0
+				      | ((prot & 4) != 0 ? PROT_READ : 0)
+				      | ((prot & 2) != 0 ? PROT_WRITE : 0)
+				      | ((prot & 1) != 0 ? PROT_EXEC : 0));
+  arg[ARG_FLAGS] = value_from_longest (builtin_type (gdbarch)->builtin_int,
+				       MAP_PRIVATE | MAP_ANONYMOUS);
+  arg[ARG_FD] = value_from_longest (builtin_type (gdbarch)->builtin_int, -1);
+  arg[ARG_OFFSET] = value_from_longest (builtin_type (gdbarch)->builtin_int64,
+					0);
+  addr_val = call_function_by_hand (mmap_val, ARG_MAX, arg);
+  retval = value_as_address (addr_val);
+  if (retval == (CORE_ADDR) -1)
+    error (_("Failed inferior mmap call for %s bytes, errno is changed."),
+	   pulongest (size));
+  return retval;
+}
+
 /* To be called from the various GDB_OSABI_LINUX handlers for the
    various GNU/Linux architectures and machine types.  */
 
@@ -1939,6 +1984,7 @@  linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
   set_gdbarch_gdb_signal_to_target (gdbarch,
 				    linux_gdb_signal_to_target);
   set_gdbarch_vsyscall_range (gdbarch, linux_vsyscall_range);
+  set_gdbarch_infcall_mmap (gdbarch, linux_infcall_mmap);
 }
 
 /* Provide a prototype to silence -Wmissing-prototypes.  */