[patch] Implement qXfer:libraries for Linux/gdbserver

Jan Kratochvil jan.kratochvil@redhat.com
Tue Aug 9 09:07:00 GMT 2011


On Tue, 09 Aug 2011 01:49:49 +0200, Paul Pluzhnikov wrote:
> --- gdbserver/linux-low.c	21 Jul 2011 23:46:12 -0000	1.173
> +++ gdbserver/linux-low.c	8 Aug 2011 23:34:10 -0000
> @@ -4755,6 +4755,289 @@ linux_emit_ops (void)
[...]
> +static CORE_ADDR
> +get_dynamic (const int pid, const int is_elf64)
> +{
> +  CORE_ADDR phdr_memaddr;
> +  int num_phdr, i;
> +  unsigned char *phdr_buf;
> +  const int phdr_size = is_elf64 ? sizeof (Elf64_Phdr) : sizeof (Elf32_Phdr);
> +
> +

Two empty lines, should be one.

> +  if (get_phdr_phnum_from_proc_auxv (pid, is_elf64, &phdr_memaddr, &num_phdr))
> +    return 0;
> +
> +  gdb_assert (num_phdr < 100);  /* Basic sanity check.  */
> +  phdr_buf = alloca (num_phdr * phdr_size);
> +
> +  if (linux_read_memory (phdr_memaddr, phdr_buf, num_phdr * phdr_size))
> +    return 0;
> +
> +  for (i = 0; i < num_phdr; i++)
> +    {
> +      if (is_elf64)
> +	{
> +	  Elf64_Phdr *const p = (Elf64_Phdr *) (phdr_buf + i * phdr_size);
> +
> +	  if (p->p_type == PT_DYNAMIC)
> +	    return p->p_vaddr;

This does not work for -fPIE -pie executables such as those on Chromebook.
One needs to relocate A_VAL as it is the in-file (0-based) address while
in-memory address is shifted by X.  X is calculated by solib-svr4.c
svr4_exec_displacement.

One can find here X probably most easily by subtracting PT_PHDR->P_VADDR from
PHDR_MEMADDR.

There is also a bug in it in solib-svr4.c scan_dyntag_auxv
	[rfc] Use auxillary vector to retrieve .dynamic/.interp sections
	http://sourceware.org/ml/gdb-patches/2008-08/msg00360.html
but one would need to patch the code around to get the fix applicable first.


> +	}
> +      else
> +	{
> +	  Elf32_Phdr *const p = (Elf32_Phdr *) (phdr_buf + i * phdr_size);
> +
> +	  if (p->p_type == PT_DYNAMIC)
> +	    return p->p_vaddr;

Here too.


> +	}
> +    }
> +
> +  return 0;
> +}
> +
> +/* Return &_r_debug in the inferior, or -1 if not present.  Return value
> +   can be 0 if the inferior does not yet have the library list initialized.  */
> +
> +static CORE_ADDR
> +get_r_debug (const int pid, const int is_elf64)
> +{
> +  CORE_ADDR dynamic_memaddr;
> +  const int dyn_size = is_elf64 ? sizeof (Elf64_Dyn) : sizeof (Elf32_Dyn);
> +  unsigned char buf[sizeof (Elf64_Dyn)];  /* The larger of the two.  */
> +
> +  dynamic_memaddr = get_dynamic (pid, is_elf64);
> +  if (dynamic_memaddr == 0)
> +    return (CORE_ADDR) -1;
> +
> +  while (linux_read_memory (dynamic_memaddr, buf, dyn_size) == 0)
> +    {
> +      if (is_elf64)
> +	{
> +	  Elf64_Dyn *const dyn = (Elf64_Dyn *) buf;
> +
> +	  if (dyn->d_tag == DT_DEBUG)
> +	    return dyn->d_un.d_val;
> +
> +	  if (dyn->d_tag == DT_NULL)
> +	    break;
> +	}
> +      else
> +	{
> +	  Elf32_Dyn *const dyn = (Elf32_Dyn *) buf;
> +
> +	  if (dyn->d_tag == DT_DEBUG)
> +	    return dyn->d_un.d_val;
> +
> +	  if (dyn->d_tag == DT_NULL)
> +	    break;
> +	}
> +
> +      dynamic_memaddr += dyn_size;
> +    }
> +
> +  return (CORE_ADDR) -1;
> +}
> +
> +/* Read one pointer from MEMADDR in the inferior.  */
> +
> +static int
> +read_one_ptr (CORE_ADDR memaddr, CORE_ADDR *ptr, int ptr_size)
> +{
> +  *ptr = 0;
> +  return linux_read_memory (memaddr, (unsigned char *) ptr, ptr_size);
> +}
> +
> +

Two empty lines, should be one.

> +struct link_map_offsets
> +  {
> +    /* Offset and size of r_debug.r_version.  */
> +    int r_version_offset;
> +
> +    /* Offset and size of r_debug.r_map.  */
> +    int r_map_offset;
> +
> +    /* Offset to l_addr field in struct link_map.  */
> +    int l_addr_offset;
> +
> +    /* Offset to l_name field in struct link_map.  */
> +    int l_name_offset;
> +
> +    /* Offset to l_next field in struct link_map.  */
> +    int l_next_offset;
> +
> +    /* Offset to l_prev field in struct link_map.  */
> +    int l_prev_offset;
> +

Excessive empty line.

> +  };
> +
> +/* Clear and refresh ALL_DLLS list.  */
> +
> +static void
> +linux_refresh_libraries (void)
> +{
> +  struct process_info_private *const priv = current_process ()->private;
> +  char filename[PATH_MAX];
> +  int pid, is_elf64, ptr_size, r_version;
> +  CORE_ADDR lm_addr, lm_prev, l_name, l_addr, l_next, l_prev;
> +
> +  static const struct link_map_offsets lmo_32bit_offsets =
> +    {
> +      0,     /* r_version offset. */
> +      4,     /* r_debug.r_map offset.  */
> +      0,     /* l_addr offset in link_map.  */
> +      4,     /* l_name offset in link_map.  */
> +      12,    /* l_next offset in link_map.  */
> +      16     /* l_prev offset in link_map.  */
> +    };
> +
> +  static const struct link_map_offsets lmo_64bit_offsets =
> +    {
> +      0,     /* r_version offset. */
> +      8,     /* r_debug.r_map offset.  */
> +      0,     /* l_addr offset in link_map.  */
> +      8,     /* l_name offset in link_map.  */
> +      24,    /* l_next offset in link_map.  */
> +      32     /* l_prev offset in link_map.  */
> +    };
> +  const struct link_map_offsets *lmo;
> +
> +  pid = lwpid_of (get_thread_lwp (current_inferior));
> +  xsnprintf (filename, sizeof filename, "/proc/%d/exe", pid);
> +  is_elf64 = elf_64_file_p (filename);
> +  lmo = is_elf64 ? &lmo_64bit_offsets : &lmo_32bit_offsets;
> +  ptr_size = is_elf64 ? 8 : 4;
> +
> +  if (priv->r_debug == 0)
> +    priv->r_debug = get_r_debug (pid, is_elf64);
> +
> +  if (priv->r_debug == (CORE_ADDR) -1 || priv->r_debug == 0)
> +    return;
> +
> +  r_version = 0;
> +  if (linux_read_memory (priv->r_debug + lmo->r_version_offset,
> +			 (unsigned char *) &r_version,
> +			 sizeof (r_version)) != 0
> +      || r_version != 1)
> +    {
> +      warning ("unexpected r_debug version %d", r_version);
> +      return;
> +    }
> +
> +  if (read_one_ptr (priv->r_debug + lmo->r_map_offset,
> +		    &lm_addr, ptr_size) != 0)
> +    {
> +      warning ("unable to read r_map from 0x%lx",
> +	       (long) priv->r_debug + lmo->r_map_offset);
> +      return;
> +    }
> +
> +  clear_all_dlls ();
> +
> +  lm_prev = 0;
> +  while (read_one_ptr (lm_addr + lmo->l_name_offset,
> +		       &l_name, ptr_size) == 0
> +	 && read_one_ptr (lm_addr + lmo->l_addr_offset,
> +			  &l_addr, ptr_size) == 0
> +	 && read_one_ptr (lm_addr + lmo->l_prev_offset,
> +			  &l_prev, ptr_size) == 0
> +	 && read_one_ptr (lm_addr + lmo->l_next_offset,
> +			  &l_next, ptr_size) == 0)
> +    {
> +      unsigned char libname[PATH_MAX];
> +
> +      if (lm_prev != l_prev)
> +	{
> +	  warning ("corrupt solib chain: 0x%lx != 0x%lx",
> +		   (long) lm_prev, (long) l_prev);
> +	  break;
> +	}
> +
> +      /* Not checking for error because reading may stop before
> +	 we've got PATH_MAX worth of characters.  */
> +      libname[0] = '\0';
> +      linux_read_memory (l_name, libname, sizeof (libname));
> +      if (libname[0] != '\0')
> +	loaded_dll ((const char *) libname, l_addr);

Unprotected against inferior string longer than PATH_MAX, this is a regression
against solib-svr4.c.


> +
> +      if (l_next == 0)
> +	break;
> +
> +      lm_prev = lm_addr;
> +      lm_addr = l_next;
> +    }
> +
> +  /* The library notification response is not expected for GNU/Linux.  */
> +  dlls_changed = 0;
> +}
> +
> +
>  static struct target_ops linux_target_ops = {
>    linux_create_inferior,
>    linux_attach,
> @@ -4813,7 +5096,8 @@ static struct target_ops linux_target_op
>    linux_cancel_breakpoints,
>    linux_stabilize_threads,
>    linux_install_fast_tracepoint_jump_pad,
> -  linux_emit_ops
> +  linux_emit_ops,
> +  linux_refresh_libraries
>  };
>  
>  static void
> Index: gdbserver/linux-low.h
> ===================================================================
> RCS file: /cvs/src/src/gdb/gdbserver/linux-low.h,v
> retrieving revision 1.47
> diff -u -p -r1.47 linux-low.h
> --- gdbserver/linux-low.h	1 Jan 2011 15:33:24 -0000	1.47
> +++ gdbserver/linux-low.h	8 Aug 2011 23:34:10 -0000
> @@ -56,6 +56,9 @@ struct process_info_private
>    /* libthread_db-specific additions.  Not NULL if this process has loaded
>       thread_db, and it is active.  */
>    struct thread_db *thread_db;
> +
> +  /* &_r_debug.  -1 if not yet determined.  0 if no PT_DYNAMIC in Phdrs.  */

  +  /* &_r_debug.  0 if not yet determined.  -1 if no PT_DYNAMIC in Phdrs.  */

My mistake.


> +  CORE_ADDR r_debug;
>  };
>  
>  struct lwp_info;


But I still believe the GDB part should be more unified, I will try it.


Thanks,
Jan



More information about the Gdb-patches mailing list