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]

bfd function to read ELF file image from memory


This bfd patch adds a function that creates an in-memory BFD by reading
from process memory to read ELF headers and from that determine the size of
the whole file image to read out of the process.  I am using this in gdb to
read the Linux vsyscall DSO image from a live process; the details of what
that is all about have been discussed on the gdb@sources.redhat.com list.
I wrote this as a BFD function that takes a callback function pointer
with the same signature as gdb's `target_read_memory'.

This function is not really all that generally useful as it is.  It assumes
that the memory image simply maps the whole file image verbatim.  That
works great for the vsyscall DSO.  But for a normal DSO that has a data
segment, that is not how the memory looks.  For those, this will create a
bfd that has bogus contents for the parts of the file after the end of the
text segment; the data segment will be bogus, and so will the section
headers because they appear at the end of the file (and perhaps not at all
in the memory image).  Ultimately a more useful function would be one that
only tries to read the contents of the PT_LOAD segments and places their
contents appropriately in the bfd's in-memory file image.  As things stand
in gdb we need the section headers for the vsyscall DSO, but it so happens
that its sole PT_LOAD segment when taken with alignment covers the whole
file image.  So I may rewrite it that way.

But all of that is a bit of a digression.  I'm posting now because I don't
quite know where to put this function even it its implementation were
perfect.  It was by far easier to write it in elfcode.h than to put it
elsewhere.  It gets to use the local helper code there, and automagically
define 32 and 64 bit flavors, which keeps the code quite simple.  It would
be a lot more hair to put the code elsewhere, copy the header swapping
code, and do the 32/64 versions half as cleanly.

The proper way to put it there is to make it yet another bfd target vector
function.  I don't know what all rigamorole is involved in doing that, and
it seems like overkill for something probably only ever used by gdb and
only with ELF.

For testing purposes, I have added a gdb command that makes use of this.
It's code that doesn't need to be target-specific except for by what name
to call this function.  Right now I just have it kludged to do runtime
checks with bfd_get_flavour and bfd_get_arch_size, but that is code that
won't build properly if libbfd doesn't have some elf32 and elf64 targets
configured in.

Can anyone offer advice on where this function ought to live?  If it
doesn't live in the bfd elf backend, then I'll have to copy or
hand-integrate some sanity checking and byte-swapping code for ELF headers.


Thanks,
Roland


--- elfcode.h.~1.41.~	Sat May 10 15:09:29 2003
+++ elfcode.h	Tue May 13 17:49:15 2003
@@ -1568,6 +1568,201 @@ elf_symbol_flags (flags)
 }
 #endif
 
+/* Create a new BFD as if by bfd_openr.  Rather than opening a file, read
+   an ELF file image out of remote memory based on the ELF file header
+   found at EHDR_VMA.  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,
+      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 */
+  bfd *nbfd;
+  struct bfd_in_memory *bim;
+  int contents_size;
+  char *contents;
+  int err;
+
+  /* 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 phdrs and section headers,
+     which tells us how big the file is.  */
+
+  contents_size = ehdr.e_ehsize;
+#define BUMP(to) if (contents_size < (int) (to)) contents_size = (to)
+
+  BUMP (ehdr.e_phoff + ehdr.e_phnum * ehdr.e_phentsize);
+  if (ehdr.e_phentsize == sizeof (Elf_External_Phdr))
+    {
+      unsigned int i;
+      Elf_Internal_Phdr phdr;
+      Elf_External_Phdr *x_phdrs
+	= (Elf_External_Phdr *) bfd_malloc (ehdr.e_phnum * sizeof *x_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);
+      if (err)
+	{
+	  free (x_phdrs);
+	  bfd_set_error (bfd_error_system_call);
+	  errno = err;
+	  return NULL;
+	}
+      for (i = 0; i < ehdr.e_phnum; ++i)
+	{
+	  elf_swap_phdr_in (templ, &x_phdrs[i], &phdr);
+	  BUMP (phdr.p_offset + phdr.p_filesz);
+	}
+      free (x_phdrs);
+    }
+  else if (ehdr.e_phentsize != 0 && ehdr.e_phnum != 0)
+    {
+      bfd_set_error (bfd_error_wrong_format);
+      return NULL;
+    }
+
+  BUMP (ehdr.e_shoff + ehdr.e_shnum * ehdr.e_shentsize);
+  if (ehdr.e_shentsize == sizeof (Elf_External_Shdr))
+    {
+      unsigned int i;
+      Elf_Internal_Shdr shdr;
+      Elf_External_Shdr *x_shdrs
+	= (Elf_External_Shdr *) bfd_malloc (ehdr.e_shnum * sizeof *x_shdrs);
+      if (x_shdrs == NULL)
+	{
+	  bfd_set_error (bfd_error_no_memory);
+	  return NULL;
+	}
+      err = target_read_memory (ehdr_vma + ehdr.e_shoff,
+				(char *) x_shdrs,
+				ehdr.e_shnum * sizeof *x_shdrs);
+      if (err)
+	{
+	  free (x_shdrs);
+	  bfd_set_error (bfd_error_system_call);
+	  errno = err;
+	  return NULL;
+	}
+      for (i = 0; i < ehdr.e_shnum; ++i)
+	{
+	  elf_swap_shdr_in (templ, &x_shdrs[i], &shdr);
+	  BUMP (shdr.sh_offset
+		+ ((shdr.sh_flags & SHF_ALLOC) ? shdr.sh_size : 0));
+	}
+      free (x_shdrs);
+    }
+  else if (ehdr.e_shentsize != 0 && ehdr.e_shnum != 0)
+    {
+      bfd_set_error (bfd_error_wrong_format);
+      return NULL;
+    }
+
+#undef BUMP
+
+  /* Now we know the size of the whole image we want read in.  */
+
+  contents = (char *) bfd_malloc ((bfd_size_type) contents_size);
+  if (contents == NULL)
+    {
+      bfd_set_error (bfd_error_no_memory);
+      return NULL;
+    }
+  /* We already read the ELF header, no need to copy it in again.  */
+  memcpy (contents, &x_ehdr, sizeof x_ehdr);
+  err = target_read_memory (ehdr_vma + sizeof x_ehdr, contents + sizeof x_ehdr,
+			    contents_size - sizeof x_ehdr);
+  if (err)
+    {
+      free (contents);
+      bfd_set_error (bfd_error_system_call);
+      errno = err;
+      return NULL;
+    }
+
+  /* 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>";
+  if (templ)
+    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;
+
+  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]