value_maybe_namespace_elt: Remove unnecessary test of result != NULL.
[deliverable/binutils-gdb.git] / gdb / linux-tdep.c
index 5bca07666b7cf35b0e4b2c4624f496ed4e6f9f91..485f5ca0033bbbd48c3ba3fb257b2a8c0552d7c1 100644 (file)
@@ -1,6 +1,6 @@
 /* Target-dependent code for GNU/Linux, architecture independent.
 
-   Copyright (C) 2009-2013 Free Software Foundation, Inc.
+   Copyright (C) 2009-2014 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
@@ -32,7 +32,9 @@
 #include "cli/cli-utils.h"
 #include "arch-utils.h"
 #include "gdb_obstack.h"
-#include "cli/cli-utils.h"
+#include "observer.h"
+#include "objfiles.h"
+#include "infcall.h"
 
 #include <ctype.h>
 
@@ -120,6 +122,72 @@ get_linux_gdbarch_data (struct gdbarch *gdbarch)
   return gdbarch_data (gdbarch, linux_gdbarch_data_handle);
 }
 
+/* Per-inferior data key.  */
+static const struct inferior_data *linux_inferior_data;
+
+/* Linux-specific cached data.  This is used by GDB for caching
+   purposes for each inferior.  This helps reduce the overhead of
+   transfering data from a remote target to the local host.  */
+struct linux_info
+{
+  /* Cache of the inferior's vsyscall/vDSO mapping range.  Only valid
+     if VSYSCALL_RANGE_P is positive.  This is cached because getting
+     at this info requires an auxv lookup (which is itself cached),
+     and looking through the inferior's mappings (which change
+     throughout execution and therefore cannot be cached).  */
+  struct mem_range vsyscall_range;
+
+  /* Zero if we haven't tried looking up the vsyscall's range before
+     yet.  Positive if we tried looking it up, and found it.  Negative
+     if we tried looking it up but failed.  */
+  int vsyscall_range_p;
+};
+
+/* Frees whatever allocated space there is to be freed and sets INF's
+   linux cache data pointer to NULL.  */
+
+static void
+invalidate_linux_cache_inf (struct inferior *inf)
+{
+  struct linux_info *info;
+
+  info = inferior_data (inf, linux_inferior_data);
+  if (info != NULL)
+    {
+      xfree (info);
+      set_inferior_data (inf, linux_inferior_data, NULL);
+    }
+}
+
+/* Handles the cleanup of the linux cache for inferior INF.  ARG is
+   ignored.  Callback for the inferior_appeared and inferior_exit
+   events.  */
+
+static void
+linux_inferior_data_cleanup (struct inferior *inf, void *arg)
+{
+  invalidate_linux_cache_inf (inf);
+}
+
+/* Fetch the linux cache info for INF.  This function always returns a
+   valid INFO pointer.  */
+
+static struct linux_info *
+get_linux_inferior_data (void)
+{
+  struct linux_info *info;
+  struct inferior *inf = current_inferior ();
+
+  info = inferior_data (inf, linux_inferior_data);
+  if (info == NULL)
+    {
+      info = XCNEW (struct linux_info);
+      set_inferior_data (inf, linux_inferior_data, info);
+    }
+
+  return info;
+}
+
 /* This function is suitable for architectures that don't
    extend/override the standard siginfo structure.  */
 
@@ -316,7 +384,7 @@ read_mapping (const char *line,
 /* Implement the "info proc" command.  */
 
 static void
-linux_info_proc (struct gdbarch *gdbarch, char *args,
+linux_info_proc (struct gdbarch *gdbarch, const char *args,
                 enum info_proc_what what)
 {
   /* A long is used for pid instead of an int to avoid a loss of precision
@@ -333,7 +401,12 @@ linux_info_proc (struct gdbarch *gdbarch, char *args,
   int target_errno;
 
   if (args && isdigit (args[0]))
-    pid = strtoul (args, &args, 10);
+    {
+      char *tem;
+
+      pid = strtoul (args, &tem, 10);
+      args = tem;
+    }
   else
     {
       if (!target_has_execution)
@@ -344,7 +417,7 @@ linux_info_proc (struct gdbarch *gdbarch, char *args,
       pid = current_inferior ()->pid;
     }
 
-  args = skip_spaces (args);
+  args = skip_spaces_const (args);
   if (args && args[0])
     error (_("Too many parameters: %s"), args);
 
@@ -477,7 +550,9 @@ linux_info_proc (struct gdbarch *gdbarch, char *args,
          p = skip_spaces_const (p);
          if (*p == '(')
            {
-             const char *ep = strchr (p, ')');
+             /* ps command also relies on no trailing fields
+                ever contain ')'.  */
+             const char *ep = strrchr (p, ')');
              if (ep != NULL)
                {
                  printf_filtered ("Exec file: %.*s\n",
@@ -602,7 +677,7 @@ linux_info_proc (struct gdbarch *gdbarch, char *args,
 /* Implement "info proc mappings" for a corefile.  */
 
 static void
-linux_core_info_proc_mappings (struct gdbarch *gdbarch, char *args)
+linux_core_info_proc_mappings (struct gdbarch *gdbarch, const char *args)
 {
   asection *section;
   ULONGEST count, page_size;
@@ -705,7 +780,7 @@ linux_core_info_proc_mappings (struct gdbarch *gdbarch, char *args)
 /* Implement "info proc" for a corefile.  */
 
 static void
-linux_core_info_proc (struct gdbarch *gdbarch, char *args,
+linux_core_info_proc (struct gdbarch *gdbarch, const char *args,
                      enum info_proc_what what)
 {
   int exe_f = (what == IP_MINIMAL || what == IP_EXE || what == IP_ALL);
@@ -1078,6 +1153,56 @@ linux_make_mappings_corefile_notes (struct gdbarch *gdbarch, bfd *obfd,
   return note_data;
 }
 
+/* Structure for passing information from
+   linux_collect_thread_registers via an iterator to
+   linux_collect_regset_section_cb. */
+
+struct linux_collect_regset_section_cb_data
+{
+  struct gdbarch *gdbarch;
+  const struct regcache *regcache;
+  bfd *obfd;
+  char *note_data;
+  int *note_size;
+  unsigned long lwp;
+  enum gdb_signal stop_signal;
+  int abort_iteration;
+};
+
+/* Callback for iterate_over_regset_sections that records a single
+   regset in the corefile note section.  */
+
+static void
+linux_collect_regset_section_cb (const char *sect_name, int size,
+                                const struct regset *regset,
+                                const char *human_name, void *cb_data)
+{
+  char *buf;
+  struct linux_collect_regset_section_cb_data *data = cb_data;
+
+  if (data->abort_iteration)
+    return;
+
+  gdb_assert (regset && regset->collect_regset);
+
+  buf = xmalloc (size);
+  regset->collect_regset (regset, data->regcache, -1, buf, size);
+
+  /* PRSTATUS still needs to be treated specially.  */
+  if (strcmp (sect_name, ".reg") == 0)
+    data->note_data = (char *) elfcore_write_prstatus
+      (data->obfd, data->note_data, data->note_size, data->lwp,
+       gdb_signal_to_host (data->stop_signal), buf);
+  else
+    data->note_data = (char *) elfcore_write_register_note
+      (data->obfd, data->note_data, data->note_size,
+       sect_name, buf, size);
+  xfree (buf);
+
+  if (data->note_data == NULL)
+    data->abort_iteration = 1;
+}
+
 /* Records the thread's register state for the corefile note
    section.  */
 
@@ -1088,47 +1213,25 @@ linux_collect_thread_registers (const struct regcache *regcache,
                                enum gdb_signal stop_signal)
 {
   struct gdbarch *gdbarch = get_regcache_arch (regcache);
-  struct core_regset_section *sect_list;
-  unsigned long lwp;
+  struct linux_collect_regset_section_cb_data data;
 
-  sect_list = gdbarch_core_regset_sections (gdbarch);
-  gdb_assert (sect_list);
+  data.gdbarch = gdbarch;
+  data.regcache = regcache;
+  data.obfd = obfd;
+  data.note_data = note_data;
+  data.note_size = note_size;
+  data.stop_signal = stop_signal;
+  data.abort_iteration = 0;
 
   /* For remote targets the LWP may not be available, so use the TID.  */
-  lwp = ptid_get_lwp (ptid);
-  if (!lwp)
-    lwp = ptid_get_tid (ptid);
-
-  while (sect_list->sect_name != NULL)
-    {
-      const struct regset *regset;
-      char *buf;
-
-      regset = gdbarch_regset_from_core_section (gdbarch,
-                                                sect_list->sect_name,
-                                                sect_list->size);
-      gdb_assert (regset && regset->collect_regset);
-
-      buf = xmalloc (sect_list->size);
-      regset->collect_regset (regset, regcache, -1, buf, sect_list->size);
-
-      /* PRSTATUS still needs to be treated specially.  */
-      if (strcmp (sect_list->sect_name, ".reg") == 0)
-       note_data = (char *) elfcore_write_prstatus
-                              (obfd, note_data, note_size, lwp,
-                               gdb_signal_to_host (stop_signal), buf);
-      else
-       note_data = (char *) elfcore_write_register_note
-                              (obfd, note_data, note_size,
-                               sect_list->sect_name, buf, sect_list->size);
-      xfree (buf);
-      sect_list++;
-
-      if (!note_data)
-       return NULL;
-    }
-
-  return note_data;
+  data.lwp = ptid_get_lwp (ptid);
+  if (!data.lwp)
+    data.lwp = ptid_get_tid (ptid);
+
+  gdbarch_iterate_over_regset_sections (gdbarch,
+                                       linux_collect_regset_section_cb,
+                                       &data, regcache);
+  return data.note_data;
 }
 
 /* Fetch the siginfo data for the current thread, if it exists.  If
@@ -1177,7 +1280,6 @@ struct linux_corefile_thread_data
   char *note_data;
   int *note_size;
   enum gdb_signal stop_signal;
-  linux_collect_thread_registers_ftype collect;
 };
 
 /* Called by gdbthread.c once per thread.  Records the thread's
@@ -1188,12 +1290,17 @@ linux_corefile_thread_callback (struct thread_info *info, void *data)
 {
   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;
+      LONGEST siginfo_size = 0;
 
       regcache = get_thread_arch_regcache (info->ptid, args->gdbarch);
 
@@ -1205,18 +1312,19 @@ linux_corefile_thread_callback (struct thread_info *info, void *data)
 
       old_chain = make_cleanup (xfree, siginfo_data);
 
-      args->note_data = args->collect (regcache, info->ptid, args->obfd,
-                                      args->note_data, args->note_size,
-                                      args->stop_signal);
+      args->note_data = linux_collect_thread_registers
+       (regcache, info->ptid, args->obfd, args->note_data,
+        args->note_size, args->stop_signal);
 
-      if (siginfo_data != NULL)
-       {
+      /* 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);
     }
@@ -1331,12 +1439,14 @@ linux_fill_prpsinfo (struct elf_internal_linux_prpsinfo *p)
 
   proc_stat = skip_spaces (proc_stat);
 
-  /* Getting rid of the executable name, since we already have it.  We
-     know that this name will be in parentheses, so we can safely look
-     for the close-paren.  */
-  while (*proc_stat != ')')
-    ++proc_stat;
-  ++proc_stat;
+  /* ps command also relies on no trailing fields ever contain ')'.  */
+  proc_stat = strrchr (proc_stat, ')');
+  if (proc_stat == NULL)
+    {
+      do_cleanups (c);
+      return 1;
+    }
+  proc_stat++;
 
   proc_stat = skip_spaces (proc_stat);
 
@@ -1424,18 +1534,21 @@ linux_fill_prpsinfo (struct elf_internal_linux_prpsinfo *p)
   return 1;
 }
 
-/* Fills the "to_make_corefile_note" target vector.  Builds the note
-   section for a corefile, and returns it in a malloc buffer.  */
+/* Build the note section for a corefile, and return it in a malloc
+   buffer.  */
 
-char *
-linux_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd, int *note_size,
-                          linux_collect_thread_registers_ftype collect)
+static char *
+linux_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd, int *note_size)
 {
   struct linux_corefile_thread_data thread_args;
   struct elf_internal_linux_prpsinfo prpsinfo;
   char *note_data = NULL;
   gdb_byte *auxv;
   int auxv_len;
+  volatile struct gdb_exception e;
+
+  if (! gdbarch_iterate_over_regset_sections_p (gdbarch))
+    return NULL;
 
   if (linux_fill_prpsinfo (&prpsinfo))
     {
@@ -1459,13 +1572,18 @@ linux_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd, int *note_size,
     }
 
   /* Thread register information.  */
+  TRY_CATCH (e, RETURN_MASK_ERROR)
+    {
+      update_thread_list ();
+    }
+  if (e.reason < 0)
+    exception_print (gdb_stderr, e);
   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 ();
-  thread_args.collect = collect;
   iterate_over_threads (linux_corefile_thread_callback, &thread_args);
   note_data = thread_args.note_data;
   if (!note_data)
@@ -1493,24 +1611,9 @@ linux_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd, int *note_size,
   note_data = linux_make_mappings_corefile_notes (gdbarch, obfd,
                                                  note_data, note_size);
 
-  make_cleanup (xfree, note_data);
   return note_data;
 }
 
-static char *
-linux_make_corefile_notes_1 (struct gdbarch *gdbarch, bfd *obfd, int *note_size)
-{
-  /* FIXME: uweigand/2011-10-06: Once all GNU/Linux architectures have been
-     converted to gdbarch_core_regset_sections, we no longer need to fall back
-     to the target method at this point.  */
-
-  if (!gdbarch_core_regset_sections (gdbarch))
-    return target_make_corefile_notes (obfd, note_size);
-  else
-    return linux_make_corefile_notes (gdbarch, obfd, note_size,
-                                     linux_collect_thread_registers);
-}
-
 /* Implementation of `gdbarch_gdb_signal_from_target', as defined in
    gdbarch.h.  This function is not static because it is exported to
    other -tdep files.  */
@@ -1762,6 +1865,110 @@ linux_gdb_signal_to_target (struct gdbarch *gdbarch,
   return -1;
 }
 
+/* Rummage through mappings to find a mapping's size.  */
+
+static int
+find_mapping_size (CORE_ADDR vaddr, unsigned long size,
+                  int read, int write, int exec, int modified,
+                  void *data)
+{
+  struct mem_range *range = data;
+
+  if (vaddr == range->start)
+    {
+      range->length = size;
+      return 1;
+    }
+  return 0;
+}
+
+/* Helper for linux_vsyscall_range that does the real work of finding
+   the vsyscall's address range.  */
+
+static int
+linux_vsyscall_range_raw (struct gdbarch *gdbarch, struct mem_range *range)
+{
+  if (target_auxv_search (&current_target, AT_SYSINFO_EHDR, &range->start) <= 0)
+    return 0;
+
+  /* This is installed by linux_init_abi below, so should always be
+     available.  */
+  gdb_assert (gdbarch_find_memory_regions_p (target_gdbarch ()));
+
+  range->length = 0;
+  gdbarch_find_memory_regions (gdbarch, find_mapping_size, range);
+  return 1;
+}
+
+/* Implementation of the "vsyscall_range" gdbarch hook.  Handles
+   caching, and defers the real work to linux_vsyscall_range_raw.  */
+
+static int
+linux_vsyscall_range (struct gdbarch *gdbarch, struct mem_range *range)
+{
+  struct linux_info *info = get_linux_inferior_data ();
+
+  if (info->vsyscall_range_p == 0)
+    {
+      if (linux_vsyscall_range_raw (gdbarch, &info->vsyscall_range))
+       info->vsyscall_range_p = 1;
+      else
+       info->vsyscall_range_p = -1;
+    }
+
+  if (info->vsyscall_range_p < 0)
+    return 0;
+
+  *range = info->vsyscall_range;
+  return 1;
+}
+
+/* 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.  */
+
+/* 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 & ~(GDB_MMAP_PROT_READ | GDB_MMAP_PROT_WRITE
+                       | GDB_MMAP_PROT_EXEC))
+             == 0);
+  arg[ARG_PROT] = value_from_longest (builtin_type (gdbarch)->builtin_int, prot);
+  arg[ARG_FLAGS] = value_from_longest (builtin_type (gdbarch)->builtin_int,
+                                      GDB_MMAP_MAP_PRIVATE
+                                      | GDB_MMAP_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.  */
 
@@ -1772,13 +1979,15 @@ linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
   set_gdbarch_info_proc (gdbarch, linux_info_proc);
   set_gdbarch_core_info_proc (gdbarch, linux_core_info_proc);
   set_gdbarch_find_memory_regions (gdbarch, linux_find_memory_regions);
-  set_gdbarch_make_corefile_notes (gdbarch, linux_make_corefile_notes_1);
+  set_gdbarch_make_corefile_notes (gdbarch, linux_make_corefile_notes);
   set_gdbarch_has_shared_address_space (gdbarch,
                                        linux_has_shared_address_space);
   set_gdbarch_gdb_signal_from_target (gdbarch,
                                      linux_gdb_signal_from_target);
   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.  */
@@ -1789,4 +1998,11 @@ _initialize_linux_tdep (void)
 {
   linux_gdbarch_data_handle =
     gdbarch_data_register_post_init (init_linux_gdbarch_data);
+
+  /* Set a cache per-inferior.  */
+  linux_inferior_data
+    = register_inferior_data_with_cleanup (NULL, linux_inferior_data_cleanup);
+  /* Observers used to invalidate the cache when needed.  */
+  observer_attach_inferior_exit (invalidate_linux_cache_inf);
+  observer_attach_inferior_appeared (invalidate_linux_cache_inf);
 }
This page took 0.030087 seconds and 4 git commands to generate.