Set dynamic tag VMA and size from dynamic section when possible
[external/binutils.git] / gdb / linux-tdep.c
index 982599e..b8d063f 100644 (file)
@@ -1,6 +1,6 @@
 /* Target-dependent code for GNU/Linux, architecture independent.
 
-   Copyright (C) 2009-2015 Free Software Foundation, Inc.
+   Copyright (C) 2009-2016 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
@@ -37,6 +37,7 @@
 #include "infcall.h"
 #include "gdbcmd.h"
 #include "gdb_regex.h"
+#include "common/enum-flags.h"
 
 #include <ctype.h>
 
@@ -46,7 +47,7 @@
    Documentation/filesystems/proc.txt, inside the Linux kernel
    tree.  */
 
-enum filterflags
+enum filter_flag
   {
     COREFILTER_ANON_PRIVATE = 1 << 0,
     COREFILTER_ANON_SHARED = 1 << 1,
@@ -56,6 +57,7 @@ enum filterflags
     COREFILTER_HUGETLB_PRIVATE = 1 << 5,
     COREFILTER_HUGETLB_SHARED = 1 << 6,
   };
+DEF_ENUM_FLAGS_TYPE (enum filter_flag, filter_flags);
 
 /* This struct is used to map flags found in the "VmFlags:" field (in
    the /proc/<PID>/smaps file).  */
@@ -171,7 +173,8 @@ init_linux_gdbarch_data (struct gdbarch *gdbarch)
 static struct linux_gdbarch_data *
 get_linux_gdbarch_data (struct gdbarch *gdbarch)
 {
-  return gdbarch_data (gdbarch, linux_gdbarch_data_handle);
+  return ((struct linux_gdbarch_data *)
+         gdbarch_data (gdbarch, linux_gdbarch_data_handle));
 }
 
 /* Per-inferior data key.  */
@@ -203,7 +206,7 @@ invalidate_linux_cache_inf (struct inferior *inf)
 {
   struct linux_info *info;
 
-  info = inferior_data (inf, linux_inferior_data);
+  info = (struct linux_info *) inferior_data (inf, linux_inferior_data);
   if (info != NULL)
     {
       xfree (info);
@@ -230,7 +233,7 @@ get_linux_inferior_data (void)
   struct linux_info *info;
   struct inferior *inf = current_inferior ();
 
-  info = inferior_data (inf, linux_inferior_data);
+  info = (struct linux_info *) inferior_data (inf, linux_inferior_data);
   if (info == NULL)
     {
       info = XCNEW (struct linux_info);
@@ -240,14 +243,14 @@ get_linux_inferior_data (void)
   return info;
 }
 
-/* This function is suitable for architectures that don't
-   extend/override the standard siginfo structure.  */
+/* See linux-tdep.h.  */
 
-static struct type *
-linux_get_siginfo_type (struct gdbarch *gdbarch)
+struct type *
+linux_get_siginfo_type_with_fields (struct gdbarch *gdbarch,
+                                   linux_siginfo_extra_fields extra_fields)
 {
   struct linux_gdbarch_data *linux_gdbarch_data;
-  struct type *int_type, *uint_type, *long_type, *void_ptr_type;
+  struct type *int_type, *uint_type, *long_type, *void_ptr_type, *short_type;
   struct type *uid_type, *pid_type;
   struct type *sigval_type, *clock_type;
   struct type *siginfo_type, *sifields_type;
@@ -263,6 +266,8 @@ linux_get_siginfo_type (struct gdbarch *gdbarch)
                                 1, "unsigned int");
   long_type = arch_integer_type (gdbarch, gdbarch_long_bit (gdbarch),
                                 0, "long");
+  short_type = arch_integer_type (gdbarch, gdbarch_long_bit (gdbarch),
+                                0, "short");
   void_ptr_type = lookup_pointer_type (builtin_type (gdbarch)->builtin_void);
 
   /* sival_t */
@@ -338,6 +343,18 @@ linux_get_siginfo_type (struct gdbarch *gdbarch)
   /* _sigfault */
   type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT);
   append_composite_type_field (type, "si_addr", void_ptr_type);
+
+  /* Additional bound fields for _sigfault in case they were requested.  */
+  if ((extra_fields & LINUX_SIGINFO_FIELD_ADDR_BND) != 0)
+    {
+      struct type *sigfault_bnd_fields;
+
+      append_composite_type_field (type, "_addr_lsb", short_type);
+      sigfault_bnd_fields = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT);
+      append_composite_type_field (sigfault_bnd_fields, "_lower", void_ptr_type);
+      append_composite_type_field (sigfault_bnd_fields, "_upper", void_ptr_type);
+      append_composite_type_field (type, "_addr_bnd", sigfault_bnd_fields);
+    }
   append_composite_type_field (sifields_type, "_sigfault", type);
 
   /* _sigpoll */
@@ -361,6 +378,15 @@ linux_get_siginfo_type (struct gdbarch *gdbarch)
   return siginfo_type;
 }
 
+/* This function is suitable for architectures that don't
+   extend/override the standard siginfo structure.  */
+
+static struct type *
+linux_get_siginfo_type (struct gdbarch *gdbarch)
+{
+  return linux_get_siginfo_type_with_fields (gdbarch, 0);
+}
+
 /* Return true if the target is running on uClinux instead of normal
    Linux kernel.  */
 
@@ -598,7 +624,7 @@ mapping_is_anonymous_p (const char *filename)
      This should work OK enough, however.  */
 
 static int
-dump_mapping_p (enum filterflags filterflags, const struct smaps_vmflags *v,
+dump_mapping_p (filter_flags filterflags, const struct smaps_vmflags *v,
                int maybe_private_p, int mapping_anon_p, int mapping_file_p,
                const char *filename)
 {
@@ -997,7 +1023,7 @@ linux_core_info_proc_mappings (struct gdbarch *gdbarch, const char *args)
   if (note_size < 2 * addr_size)
     error (_("malformed core note - too short for header"));
 
-  contents = xmalloc (note_size);
+  contents = (unsigned char *) xmalloc (note_size);
   cleanup = make_cleanup (xfree, contents);
   if (!bfd_get_section_contents (core_bfd, section, contents, 0, note_size))
     error (_("could not get core note contents"));
@@ -1098,10 +1124,6 @@ linux_core_info_proc (struct gdbarch *gdbarch, const char *args,
     error (_("unable to handle request"));
 }
 
-/* Callback function for linux_find_memory_regions_full.  If it returns
-   non-zero linux_find_memory_regions_full returns immediately with that
-   value.  */
-
 typedef int linux_find_memory_region_ftype (ULONGEST vaddr, ULONGEST size,
                                            ULONGEST offset, ULONGEST inode,
                                            int read, int write,
@@ -1109,22 +1131,46 @@ typedef int linux_find_memory_region_ftype (ULONGEST vaddr, ULONGEST size,
                                            const char *filename,
                                            void *data);
 
-/* List memory regions in the inferior PID matched to FILTERFLAGS for
-   a corefile.  Call FUNC with FUNC_DATA for each such region.  Return
-   immediately with the value returned by FUNC if it is non-zero.
-   *MEMORY_TO_FREE_PTR should be registered to be freed automatically if
-   called FUNC throws an exception.  MEMORY_TO_FREE_PTR can be also
-   passed as NULL if it is not used.  Return -1 if error occurs, 0 if
-   all memory regions have been processed or return the value from FUNC
-   if FUNC returns non-zero.  */
+/* List memory regions in the inferior for a corefile.  */
 
 static int
-linux_find_memory_regions_full (pid_t pid, enum filterflags filterflags,
+linux_find_memory_regions_full (struct gdbarch *gdbarch,
                                linux_find_memory_region_ftype *func,
-                               void *func_data)
+                               void *obfd)
 {
   char mapsfilename[100];
-  char *data;
+  char coredumpfilter_name[100];
+  char *data, *coredumpfilterdata;
+  pid_t pid;
+  /* Default dump behavior of coredump_filter (0x33), according to
+     Documentation/filesystems/proc.txt from the Linux kernel
+     tree.  */
+  filter_flags filterflags = (COREFILTER_ANON_PRIVATE
+                             | COREFILTER_ANON_SHARED
+                             | COREFILTER_ELF_HEADERS
+                             | COREFILTER_HUGETLB_PRIVATE);
+
+  /* We need to know the real target PID to access /proc.  */
+  if (current_inferior ()->fake_pid_p)
+    return 1;
+
+  pid = current_inferior ()->pid;
+
+  if (use_coredump_filter)
+    {
+      xsnprintf (coredumpfilter_name, sizeof (coredumpfilter_name),
+                "/proc/%d/coredump_filter", pid);
+      coredumpfilterdata = target_fileio_read_stralloc (NULL,
+                                                       coredumpfilter_name);
+      if (coredumpfilterdata != NULL)
+       {
+         unsigned int flags;
+
+         sscanf (coredumpfilterdata, "%x", &flags);
+         filterflags = (enum filter_flag) flags;
+         xfree (coredumpfilterdata);
+       }
+    }
 
   xsnprintf (mapsfilename, sizeof mapsfilename, "/proc/%d/smaps", pid);
   data = target_fileio_read_stralloc (NULL, mapsfilename);
@@ -1139,7 +1185,6 @@ linux_find_memory_regions_full (pid_t pid, enum filterflags filterflags,
     {
       struct cleanup *cleanup = make_cleanup (xfree, data);
       char *line, *t;
-      int retval = 0;
 
       line = strtok_r (data, "\n", &t);
       while (line != NULL)
@@ -1254,20 +1299,17 @@ linux_find_memory_regions_full (pid_t pid, enum filterflags filterflags,
 
          /* Invoke the callback function to create the corefile segment.  */
          if (should_dump_p)
-           retval = func (addr, endaddr - addr, offset, inode,
-                          read, write, exec,
-                          1, /* MODIFIED is true because we want to dump the
-                                mapping.  */
-                          filename, func_data);
-         if (retval != 0)
-           break;
+           func (addr, endaddr - addr, offset, inode,
+                 read, write, exec, 1, /* MODIFIED is true because we
+                                          want to dump the mapping.  */
+                 filename, obfd);
        }
 
       do_cleanups (cleanup);
-      return retval;
+      return 0;
     }
 
-  return -1;
+  return 1;
 }
 
 /* A structure for passing information through
@@ -1281,7 +1323,7 @@ struct linux_find_memory_regions_data
 
   /* The original datum.  */
 
-  void *data;
+  void *obfd;
 };
 
 /* A callback for linux_find_memory_regions that converts between the
@@ -1293,50 +1335,10 @@ linux_find_memory_regions_thunk (ULONGEST vaddr, ULONGEST size,
                                 int read, int write, int exec, int modified,
                                 const char *filename, void *arg)
 {
-  struct linux_find_memory_regions_data *data = arg;
-
-  return data->func (vaddr, size, read, write, exec, modified, data->data);
-}
-
-/* Wrapper of linux_find_memory_regions_full handling FAKE_PID_P in GDB.  */
-
-static int
-linux_find_memory_regions_gdb (struct gdbarch *gdbarch,
-                              linux_find_memory_region_ftype *func,
-                              void *func_data)
-{
-  pid_t pid;
-  /* Default dump behavior of coredump_filter (0x33), according to
-     Documentation/filesystems/proc.txt from the Linux kernel
-     tree.  */
-  enum filterflags filterflags = (COREFILTER_ANON_PRIVATE
-                                 | COREFILTER_ANON_SHARED
-                                 | COREFILTER_ELF_HEADERS
-                                 | COREFILTER_HUGETLB_PRIVATE);
-
-  /* We need to know the real target PID so
-     linux_find_memory_regions_full can access /proc.  */
-  if (current_inferior ()->fake_pid_p)
-    return -1;
-
-  pid = current_inferior ()->pid;
-
-  if (use_coredump_filter)
-    {
-      char coredumpfilter_name[100], *coredumpfilterdata;
-
-      xsnprintf (coredumpfilter_name, sizeof (coredumpfilter_name),
-                "/proc/%d/coredump_filter", pid);
-      coredumpfilterdata = target_fileio_read_stralloc (NULL,
-                                                       coredumpfilter_name);
-      if (coredumpfilterdata != NULL)
-       {
-         sscanf (coredumpfilterdata, "%x", &filterflags);
-         xfree (coredumpfilterdata);
-       }
-    }
+  struct linux_find_memory_regions_data *data
+    = (struct linux_find_memory_regions_data *) arg;
 
-  return linux_find_memory_regions_full (pid, filterflags, func, func_data);
+  return data->func (vaddr, size, read, write, exec, modified, data->obfd);
 }
 
 /* A variant of linux_find_memory_regions_full that is suitable as the
@@ -1344,15 +1346,16 @@ linux_find_memory_regions_gdb (struct gdbarch *gdbarch,
 
 static int
 linux_find_memory_regions (struct gdbarch *gdbarch,
-                          find_memory_region_ftype func, void *func_data)
+                          find_memory_region_ftype func, void *obfd)
 {
   struct linux_find_memory_regions_data data;
 
   data.func = func;
-  data.data = func_data;
+  data.obfd = obfd;
 
-  return linux_find_memory_regions_gdb (gdbarch,
-                                       linux_find_memory_regions_thunk, &data);
+  return linux_find_memory_regions_full (gdbarch,
+                                        linux_find_memory_regions_thunk,
+                                        &data);
 }
 
 /* Determine which signal stopped execution.  */
@@ -1367,18 +1370,6 @@ find_signalled_thread (struct thread_info *info, void *data)
   return 0;
 }
 
-static enum gdb_signal
-find_stop_signal (void)
-{
-  struct thread_info *info =
-    iterate_over_threads (find_signalled_thread, NULL);
-
-  if (info)
-    return info->suspend.stop_signal;
-  else
-    return GDB_SIGNAL_0;
-}
-
 /* Generate corefile notes for SPU contexts.  */
 
 static char *
@@ -1481,7 +1472,8 @@ linux_make_mappings_callback (ULONGEST vaddr, ULONGEST size,
                              int read, int write, int exec, int modified,
                              const char *filename, void *data)
 {
-  struct linux_make_mappings_data *map_data = data;
+  struct linux_make_mappings_data *map_data
+    = (struct linux_make_mappings_data *) data;
   gdb_byte buf[sizeof (ULONGEST)];
 
   if (*filename == '\0' || inode == 0)
@@ -1534,8 +1526,8 @@ linux_make_mappings_corefile_notes (struct gdbarch *gdbarch, bfd *obfd,
   pack_long (buf, long_type, 1);
   obstack_grow (&data_obstack, buf, TYPE_LENGTH (long_type));
 
-  linux_find_memory_regions_gdb (gdbarch, linux_make_mappings_callback,
-                                &mapping_data);
+  linux_find_memory_regions_full (gdbarch, linux_make_mappings_callback,
+                                 &mapping_data);
 
   if (mapping_data.file_count != 0)
     {
@@ -1582,14 +1574,15 @@ linux_collect_regset_section_cb (const char *sect_name, int size,
                                 const char *human_name, void *cb_data)
 {
   char *buf;
-  struct linux_collect_regset_section_cb_data *data = cb_data;
+  struct linux_collect_regset_section_cb_data *data
+    = (struct linux_collect_regset_section_cb_data *) cb_data;
 
   if (data->abort_iteration)
     return;
 
   gdb_assert (regset && regset->collect_regset);
 
-  buf = xmalloc (size);
+  buf = (char *) xmalloc (size);
   regset->collect_regset (regset, data->regcache, -1, buf, size);
 
   /* PRSTATUS still needs to be treated specially.  */
@@ -1657,7 +1650,7 @@ linux_get_siginfo_data (struct gdbarch *gdbarch, LONGEST *size)
   
   siginfo_type = gdbarch_get_siginfo_type (gdbarch);
 
-  buf = xmalloc (TYPE_LENGTH (siginfo_type));
+  buf = (gdb_byte *) xmalloc (TYPE_LENGTH (siginfo_type));
   cleanups = make_cleanup (xfree, buf);
 
   bytes_read = target_read (&current_target, TARGET_OBJECT_SIGNAL_INFO, NULL,
@@ -1679,61 +1672,49 @@ linux_get_siginfo_data (struct gdbarch *gdbarch, LONGEST *size)
 struct linux_corefile_thread_data
 {
   struct gdbarch *gdbarch;
-  int pid;
   bfd *obfd;
   char *note_data;
   int *note_size;
   enum gdb_signal stop_signal;
 };
 
-/* Called by gdbthread.c once per thread.  Records the thread's
-   register state for the corefile note section.  */
+/* Records the thread's register state for the corefile note
+   section.  */
 
-static int
-linux_corefile_thread_callback (struct thread_info *info, void *data)
+static void
+linux_corefile_thread (struct thread_info *info,
+                      struct linux_corefile_thread_data *args)
 {
-  struct linux_corefile_thread_data *args = data;
-
-  /* It can be current thread
-     which cannot be removed by update_thread_list.  */
-  if (info->state == THREAD_EXITED)
-    return 0;
-
-  if (ptid_get_pid (info->ptid) == args->pid)
-    {
-      struct cleanup *old_chain;
-      struct regcache *regcache;
-      gdb_byte *siginfo_data;
-      LONGEST siginfo_size = 0;
-
-      regcache = get_thread_arch_regcache (info->ptid, args->gdbarch);
-
-      old_chain = save_inferior_ptid ();
-      inferior_ptid = info->ptid;
-      target_fetch_registers (regcache, -1);
-      siginfo_data = linux_get_siginfo_data (args->gdbarch, &siginfo_size);
-      do_cleanups (old_chain);
-
-      old_chain = make_cleanup (xfree, siginfo_data);
-
-      args->note_data = linux_collect_thread_registers
-       (regcache, info->ptid, args->obfd, args->note_data,
-        args->note_size, args->stop_signal);
-
-      /* Don't return anything if we got no register information above,
-         such a core file is useless.  */
-      if (args->note_data != NULL)
-       if (siginfo_data != NULL)
-         args->note_data = elfcore_write_note (args->obfd,
-                                               args->note_data,
-                                               args->note_size,
-                                               "CORE", NT_SIGINFO,
-                                               siginfo_data, siginfo_size);
-
-      do_cleanups (old_chain);
-    }
-
-  return !args->note_data;
+  struct cleanup *old_chain;
+  struct regcache *regcache;
+  gdb_byte *siginfo_data;
+  LONGEST siginfo_size = 0;
+
+  regcache = get_thread_arch_regcache (info->ptid, args->gdbarch);
+
+  old_chain = save_inferior_ptid ();
+  inferior_ptid = info->ptid;
+  target_fetch_registers (regcache, -1);
+  siginfo_data = linux_get_siginfo_data (args->gdbarch, &siginfo_size);
+  do_cleanups (old_chain);
+
+  old_chain = make_cleanup (xfree, siginfo_data);
+
+  args->note_data = linux_collect_thread_registers
+    (regcache, info->ptid, args->obfd, args->note_data,
+     args->note_size, args->stop_signal);
+
+  /* Don't return anything if we got no register information above,
+     such a core file is useless.  */
+  if (args->note_data != NULL)
+    if (siginfo_data != NULL)
+      args->note_data = elfcore_write_note (args->obfd,
+                                           args->note_data,
+                                           args->note_size,
+                                           "CORE", NT_SIGINFO,
+                                           siginfo_data, siginfo_size);
+
+  do_cleanups (old_chain);
 }
 
 /* Fill the PRPSINFO structure with information about the process being
@@ -1776,7 +1757,6 @@ linux_fill_prpsinfo (struct elf_internal_linux_prpsinfo *p)
   int n_fields = 0;
   /* Cleanups.  */
   struct cleanup *c;
-  int i;
 
   gdb_assert (p != NULL);
 
@@ -1808,7 +1788,7 @@ linux_fill_prpsinfo (struct elf_internal_linux_prpsinfo *p)
 
   psargs = xstrdup (fname);
   if (infargs != NULL)
-    psargs = reconcat (psargs, psargs, " ", infargs, NULL);
+    psargs = reconcat (psargs, psargs, " ", infargs, (char *) NULL);
 
   make_cleanup (xfree, psargs);
 
@@ -1949,6 +1929,7 @@ linux_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd, int *note_size)
   char *note_data = NULL;
   gdb_byte *auxv;
   int auxv_len;
+  struct thread_info *curr_thr, *signalled_thr, *thr;
 
   if (! gdbarch_iterate_over_regset_sections_p (gdbarch))
     return NULL;
@@ -1985,13 +1966,37 @@ linux_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd, int *note_size)
     }
   END_CATCH
 
+  /* Like the kernel, prefer dumping the signalled thread first.
+     "First thread" is what tools use to infer the signalled thread.
+     In case there's more than one signalled thread, prefer the
+     current thread, if it is signalled.  */
+  curr_thr = inferior_thread ();
+  if (curr_thr->suspend.stop_signal != GDB_SIGNAL_0)
+    signalled_thr = curr_thr;
+  else
+    {
+      signalled_thr = iterate_over_threads (find_signalled_thread, NULL);
+      if (signalled_thr == NULL)
+       signalled_thr = curr_thr;
+    }
+
   thread_args.gdbarch = gdbarch;
-  thread_args.pid = ptid_get_pid (inferior_ptid);
   thread_args.obfd = obfd;
   thread_args.note_data = note_data;
   thread_args.note_size = note_size;
-  thread_args.stop_signal = find_stop_signal ();
-  iterate_over_threads (linux_corefile_thread_callback, &thread_args);
+  thread_args.stop_signal = signalled_thr->suspend.stop_signal;
+
+  linux_corefile_thread (signalled_thr, &thread_args);
+  ALL_NON_EXITED_THREADS (thr)
+    {
+      if (thr == signalled_thr)
+       continue;
+      if (ptid_get_pid (thr->ptid) != ptid_get_pid (inferior_ptid))
+       continue;
+
+      linux_corefile_thread (thr, &thread_args);
+    }
+
   note_data = thread_args.note_data;
   if (!note_data)
     return NULL;
@@ -2279,7 +2284,7 @@ find_mapping_size (CORE_ADDR vaddr, unsigned long size,
                   int read, int write, int exec, int modified,
                   void *data)
 {
-  struct mem_range *range = data;
+  struct mem_range *range = (struct mem_range *) data;
 
   if (vaddr == range->start)
     {
@@ -2420,7 +2425,8 @@ linux_displaced_step_location (struct gdbarch *gdbarch)
      location.  The auxiliary vector gets us the PowerPC-side entry
      point address instead.  */
   if (target_auxv_search (&current_target, AT_ENTRY, &addr) <= 0)
-    error (_("Cannot find AT_ENTRY auxiliary vector entry."));
+    throw_error (NOT_SUPPORTED_ERROR,
+                _("Cannot find AT_ENTRY auxiliary vector entry."));
 
   /* Make certain that the address points at real code, and not a
      function descriptor.  */