This is the mail archive of the gdb-patches@sources.redhat.com mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: bfd function to read ELF file image from memory


> Well since your function is creating a new bfd, one obvious place for
> it would be in opncls.c.  Have you considered using an interface like
> the one provided by BFD_IN_MEMORY ?  You could make the in-memory
> interface generic and provide different memory accessing functions
> (for local memory, remote memory, mmap'ed files, etc).
>
> I do not think that treating this in-memory bfd as a new target is
> appropriate, since it is not really a new file format or instruction
> set architecture, but a new method of loading (part of) a binary file
> into a normal bfd structure.

I think you have misunderstood what the code does, or else I am just very
confused about what your comments mean.  The terminology in my remarks
about the code was surely sloppy, since I was mostly expecting folks to
look at the code itself.

This is a new function that bfd_malloc's a buffer, fills in its contents,
and makes that into a bfd with BFD_IN_MEMORY set.  It has nothing to do
with adding a new bfd backend.  The function's implementation is
format-specific, but its calling interface is generic.  So it seems like it
ought to be accessed through a new function pointer in struct bfd_target,
which we would at least so far only have implemented in the ELF backend.
But, as I said, I don't really know what all the pieces are that one ought
to write to properly add another dispatched function to bfd.

> I am all for keeping things simple, so if it is easier to put it in
> elfcode.h then that is probably where it should live.  Given the
> probably limited utility of this function it probably ought to be only
> conditionally defined, based on the particular configured target or
> maybe a configure time switch.

I am not aware of functions in libbfd that are conditionalized this way.
Can you point me to an example that would be a model of what you are
suggesting?  I guess I can figure out what compile-time check to use in gdb
for an ELF target and call bfd_elf_bfd_from_memory from libbfd under that
#ifdef, but it doesn't seem very BFDish.  Still, I would just to have one
calling interface that handles 32-bit and 64-bit ELF.  To implement that
front-end function I need a compile-time check I can use in elfcode.h that
tells me whether 32-bit and/or 64-bit targets are being built.  What works?

It so happens that I rewrote the function in the fashion I was describing.
So here's the new version.  This one should be robustly generally useful to
extract the runtime-loaded portions of any ELF DSO from inferior memory.
Please take a look at the code.

For any DSO that isn't very very tiny, if it's not stripped (and perhaps
even if it is) then its section headers won't be in the memory image and so
what the extraction gives you appears to be an ELF ET_DYN file that never
had section headers at all.  Right now the ELF backend doesn't really cope
with such objects, though it could certainly be made to (grokking the phdrs
and synthesizing fake sections similar to how ELF core files are handled).
But this function produces a valid ELF file image and feeds it to the rest
of BFD to choke on.  (The Linux vsyscall DSO is in fact very very tiny and
so you get the whole thing with section headers and the ELF backend is
perfectly happy.)

This version also figures out the offset between actual VMAs read from and
those in the ELF headers for you.  With that, the new gdb command I've
added using this function works in the general case.


Thanks,
Roland



--- elfcode.h.~1.41.~	Sat May 10 15:09:29 2003
+++ elfcode.h	Thu May 15 21:44:46 2003
@@ -1568,6 +1568,221 @@ elf_symbol_flags (flags)
 }
 #endif
 
+/* Create a new BFD as if by bfd_openr.  Rather than opening a file,
+   reconstruct an ELF file by reading the segments out of remote memory
+   based on the ELF file header at EHDR_VMA and the ELF program headers it
+   points to.  The function TARGET_READ_MEMORY is called to read remote
+   memory regions by VMA.  TEMPL must be a BFD for a target with the word
+   size and byte order found in the remote memory.  */
+
+bfd *
+NAME(bfd_elf,bfd_from_memory)
+     (bfd *templ, bfd_vma ehdr_vma, bfd_vma *loadbasep,
+      int (*target_read_memory) (bfd_vma vma, char *myaddr, int len))
+{
+  Elf_External_Ehdr x_ehdr;	/* Elf file header, external form */
+  Elf_Internal_Ehdr ehdr;	/* Elf file header, internal form */
+  Elf_External_Phdr *x_phdrs;
+  Elf_Internal_Phdr *i_phdrs;
+  bfd *nbfd;
+  struct bfd_in_memory *bim;
+  int contents_size;
+  char *contents;
+  int err;
+  unsigned int i;
+  bfd_vma loadbase;
+
+  /* Read in the ELF header in external format.  */
+  err = target_read_memory (ehdr_vma, (char *) &x_ehdr, sizeof x_ehdr);
+  if (err)
+    {
+      bfd_set_error (bfd_error_system_call);
+      errno = err;
+      return NULL;
+    }
+
+  /* Now check to see if we have a valid ELF file, and one that BFD can
+     make use of.  The magic number must match, the address size ('class')
+     and byte-swapping must match our XVEC entry.  */
+
+  if (! elf_file_p (&x_ehdr)
+      || x_ehdr.e_ident[EI_VERSION] != EV_CURRENT
+      || x_ehdr.e_ident[EI_CLASS] != ELFCLASS)
+    {
+      bfd_set_error (bfd_error_wrong_format);
+      return NULL;
+    }
+
+  /* Check that file's byte order matches xvec's */
+  switch (x_ehdr.e_ident[EI_DATA])
+    {
+    case ELFDATA2MSB:		/* Big-endian */
+      if (! bfd_header_big_endian (templ))
+	{
+	  bfd_set_error (bfd_error_wrong_format);
+	  return NULL;
+	}
+      break;
+    case ELFDATA2LSB:		/* Little-endian */
+      if (! bfd_header_little_endian (templ))
+	{
+	  bfd_set_error (bfd_error_wrong_format);
+	  return NULL;
+	}
+      break;
+    case ELFDATANONE:		/* No data encoding specified */
+    default:			/* Unknown data encoding specified */
+      bfd_set_error (bfd_error_wrong_format);
+      return NULL;
+    }
+
+  elf_swap_ehdr_in (templ, &x_ehdr, &ehdr);
+
+  /* The file header tells where to find the program headers.
+     These are what we use to actually choose what to read.  */
+
+  if (ehdr.e_phentsize != sizeof (Elf_External_Phdr) || ehdr.e_phnum == 0)
+    {
+      bfd_set_error (bfd_error_wrong_format);
+      return NULL;
+    }
+
+  x_phdrs = (Elf_External_Phdr *)
+    bfd_malloc (ehdr.e_phnum * (sizeof *x_phdrs + sizeof *i_phdrs));
+  if (x_phdrs == NULL)
+    {
+      bfd_set_error (bfd_error_no_memory);
+      return NULL;
+    }
+  err = target_read_memory (ehdr_vma + ehdr.e_phoff, (char *) x_phdrs,
+			    ehdr.e_phnum * sizeof x_phdrs[0]);
+  if (err)
+    {
+      free (x_phdrs);
+      bfd_set_error (bfd_error_system_call);
+      errno = err;
+      return NULL;
+    }
+  i_phdrs = (Elf_Internal_Phdr *) &x_phdrs[ehdr.e_phnum];
+
+  contents_size = ehdr.e_phoff + ehdr.e_phnum;
+  if ((bfd_vma) contents_size < ehdr.e_ehsize)
+    {
+      bfd_set_error (bfd_error_wrong_format);
+      return NULL;
+    }
+
+  loadbase = ehdr_vma;
+  for (i = 0; i < ehdr.e_phnum; ++i)
+    {
+      elf_swap_phdr_in (templ, &x_phdrs[i], &i_phdrs[i]);
+      if (i_phdrs[i].p_type == PT_LOAD)
+	{
+	  bfd_vma segment_end;
+	  segment_end = (i_phdrs[i].p_offset + i_phdrs[i].p_filesz
+			 + i_phdrs[i].p_align - 1) & -i_phdrs[i].p_align;
+	  if (segment_end > (bfd_vma) contents_size)
+	    contents_size = segment_end;
+
+	  if ((i_phdrs[i].p_offset & -i_phdrs[i].p_align) == 0)
+	    loadbase = ehdr_vma - (i_phdrs[i].p_vaddr & -i_phdrs[i].p_align);
+	}
+    }
+
+  /* Back up to the last PT_LOAD header.  */
+  do
+    --i;
+  while (i > 0 && i_phdrs[i].p_type != PT_LOAD);
+
+  /* Trim the last segment so we don't bother with zeros in the last page
+     that are off the end of the file.  However, if the extra bit in that
+     page includes the section headers, keep them.  */
+  if ((bfd_vma) contents_size > i_phdrs[i].p_offset + i_phdrs[i].p_filesz
+      && (bfd_vma) contents_size >= (ehdr.e_shoff
+				     + ehdr.e_shnum * ehdr.e_shentsize))
+    {
+      contents_size = i_phdrs[i].p_offset + i_phdrs[i].p_filesz;
+      if ((bfd_vma) contents_size < (ehdr.e_shoff
+				     + ehdr.e_shnum * ehdr.e_shentsize))
+	contents_size = ehdr.e_shoff + ehdr.e_shnum * ehdr.e_shentsize;
+    }
+  else
+    contents_size = i_phdrs[i].p_offset + i_phdrs[i].p_filesz;
+
+  /* Now we know the size of the whole image we want read in.  */
+  contents = (char *) bfd_zmalloc ((bfd_size_type) contents_size);
+  if (contents == NULL)
+    {
+      free (x_phdrs);
+      bfd_set_error (bfd_error_no_memory);
+      return NULL;
+    }
+
+  for (i = 0; i < ehdr.e_phnum; ++i)
+    if (i_phdrs[i].p_type == PT_LOAD)
+      {
+	bfd_vma start = i_phdrs[i].p_offset & -i_phdrs[i].p_align;
+	bfd_vma end = (i_phdrs[i].p_offset + i_phdrs[i].p_filesz
+		       + i_phdrs[i].p_align - 1) & -i_phdrs[i].p_align;
+	if (end > (bfd_vma) contents_size)
+	  end = contents_size;
+	err = target_read_memory ((loadbase + i_phdrs[i].p_vaddr)
+				  & -i_phdrs[i].p_align,
+				  contents + start, end - start);
+	if (err)
+	  {
+	    free (x_phdrs);
+	    free (contents);
+	    bfd_set_error (bfd_error_system_call);
+	    errno = err;
+	    return NULL;
+	  }
+      }
+  free (x_phdrs);
+
+  /* If the segments visible in memory didn't include the section headers,
+     then clear them from the file header.  */
+  if ((bfd_vma) contents_size < ehdr.e_shoff + ehdr.e_shnum * ehdr.e_shentsize)
+    {
+      memset (&x_ehdr.e_shoff, 0, sizeof x_ehdr.e_shoff);
+      memset (&x_ehdr.e_shnum, 0, sizeof x_ehdr.e_shnum);
+    }
+
+  /* This will normally have been in the first PT_LOAD segment.  But it
+     conceivably could be missing, and we might have just changed it.  */
+  memcpy (contents, &x_ehdr, sizeof x_ehdr);
+
+  /* Now we have a memory image of the ELF file contents.  Make a BFD.  */
+  bim = ((struct bfd_in_memory *)
+	 bfd_malloc ((bfd_size_type) sizeof (struct bfd_in_memory)));
+  if (bim == NULL)
+    {
+      free (contents);
+      bfd_set_error (bfd_error_no_memory);
+      return NULL;
+    }
+  nbfd = _bfd_new_bfd ();
+  if (nbfd == NULL)
+    {
+      free (bim);
+      free (contents);
+      bfd_set_error (bfd_error_no_memory);
+      return NULL;
+    }
+  nbfd->filename = "<in-memory>";
+  nbfd->xvec = templ->xvec;
+  bim->size = contents_size;
+  bim->buffer = contents;
+  nbfd->iostream = (PTR) bim;
+  nbfd->flags = BFD_IN_MEMORY;
+  nbfd->direction = read_direction;
+  nbfd->mtime = time (NULL);
+  nbfd->mtime_set = TRUE;
+
+  *loadbasep = loadbase;
+  return nbfd;
+}
+
 #include "elfcore.h"
 #include "elflink.h"
 


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]