This is the mail archive of the
binutils@sources.redhat.com
mailing list for the binutils project.
Re: bfd function to read ELF file image from memory
- From: Roland McGrath <roland at redhat dot com>
- To: binutils at sources dot redhat dot com
- Date: Mon, 19 May 2003 14:37:35 -0700
- Subject: Re: bfd function to read ELF file image from memory
Here is a complete patch adding a public function
bfd_elf_bfd_from_remote_memory that works with any ELF bfd.
If people are interested in lazy reading, I think that should be considered
an optimization of this function and it can be done under the covers later
if there is demand for it.
As is, this works nicely for the immediate needs of gdb. Approved to commit?
Thanks,
Roland
2003-05-19 Roland McGrath <roland@redhat.com>
* elf.c (bfd_elf_bfd_from_remote_memory): New function.
* bfd-in.h: Declare it.
* elfcode.h (NAME(_bfd_elf,bfd_from_remote_memory)): New function.
* elf-bfd.h (struct elf_backend_data): New function pointer member
elf_backend_bfd_from_remote_memory.
(_bfd_elf32_bfd_from_remote_memory, _bfd_elf64_bfd_from_remote_memory):
Declare them.
* elfxx-target.h (elf_backend_bfd_from_remote_memory): New macro.
(elfNN_bed): Add that to the initializer.
Index: bfd-in.h
===================================================================
RCS file: /cvs/src/src/bfd/bfd-in.h,v
retrieving revision 1.59
diff -B -p -u -r1.59 bfd-in.h
--- bfd-in.h 9 May 2003 02:26:59 -0000 1.59
+++ bfd-in.h 19 May 2003 21:31:14 -0000
@@ -694,6 +694,22 @@ extern long bfd_get_elf_phdr_upper_bound
extern int bfd_get_elf_phdrs
PARAMS ((bfd *abfd, void *phdrs));
+/* 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. If not null, *LOADBASEP is filled in with the difference
+ between the VMAs from which the segments were read, and the VMAs the
+ file headers (and hence BFD's idea of each section's VMA) put them at.
+
+ The function TARGET_READ_MEMORY is called to copy LEN bytes from the
+ remote memory at target address VMA into the local buffer at MYADDR; it
+ should return zero on success or an `errno' code on failure. TEMPL must
+ be a BFD for an ELF target with the word size and byte order found in
+ the remote memory. */
+extern bfd *bfd_elf_bfd_from_remote_memory
+ PARAMS ((bfd *templ, bfd_vma ehdr_vma, bfd_vma *loadbasep,
+ int (*target_read_memory) (bfd_vma vma, char *myaddr, int len)));
+
/* Return the arch_size field of an elf bfd, or -1 if not elf. */
extern int bfd_get_arch_size
PARAMS ((bfd *));
Index: elf-bfd.h
===================================================================
RCS file: /cvs/src/src/bfd/elf-bfd.h,v
retrieving revision 1.96
diff -B -p -u -r1.96 elf-bfd.h
--- elf-bfd.h 13 May 2003 14:09:50 -0000 1.96
+++ elf-bfd.h 19 May 2003 21:31:14 -0000
@@ -875,6 +875,12 @@ struct elf_backend_data
Used for the MIPS ELF .mdebug section. */
const struct ecoff_debug_swap *elf_backend_ecoff_debug_swap;
+ /* This function implements `bfd_elf_bfd_from_remote_memory';
+ see elf.c, elfcode.h. */
+ bfd *(*elf_backend_bfd_from_remote_memory)
+ PARAMS ((bfd *templ, bfd_vma ehdr_vma, bfd_vma *loadbasep,
+ int (*target_read_memory) (bfd_vma vma, char *myaddr, int len)));
+
/* Alternate EM_xxxx machine codes for this backend. */
int elf_machine_alt1;
int elf_machine_alt2;
@@ -1749,6 +1755,13 @@ extern char *elfcore_write_prxfpreg
PARAMS ((bfd *, char *, int *, const PTR, int));
extern char *elfcore_write_lwpstatus
PARAMS ((bfd *, char *, int *, long, int, const PTR));
+
+extern bfd *_bfd_elf32_bfd_from_remote_memory
+ PARAMS ((bfd *templ, bfd_vma ehdr_vma, bfd_vma *loadbasep,
+ int (*target_read_memory) (bfd_vma, char *, int)));
+extern bfd *_bfd_elf64_bfd_from_remote_memory
+ PARAMS ((bfd *templ, bfd_vma ehdr_vma, bfd_vma *loadbasep,
+ int (*target_read_memory) (bfd_vma, char *, int)));
/* SH ELF specific routine. */
Index: elf.c
===================================================================
RCS file: /cvs/src/src/bfd/elf.c,v
retrieving revision 1.185
diff -B -p -u -r1.185 elf.c
--- elf.c 13 May 2003 15:32:19 -0000 1.185
+++ elf.c 19 May 2003 21:31:18 -0000
@@ -7534,4 +7548,28 @@ _bfd_elf_section_offset (abfd, info, sec
default:
return offset;
}
+}
+
+/* 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. If not null, *LOADBASEP is filled in with the difference
+ between the VMAs from which the segments were read, and the VMAs the
+ file headers (and hence BFD's idea of each section's VMA) put them at.
+
+ The function TARGET_READ_MEMORY is called to copy LEN bytes from the
+ remote memory at target address VMA into the local buffer at MYADDR; it
+ should return zero on success or an `errno' code on failure. TEMPL must
+ be a BFD for an ELF target with the word size and byte order found in
+ the remote memory. */
+
+bfd *
+bfd_elf_bfd_from_remote_memory (templ, ehdr_vma, loadbasep, target_read_memory)
+ bfd *templ;
+ bfd_vma ehdr_vma;
+ bfd_vma *loadbasep;
+ int (*target_read_memory) PARAMS ((bfd_vma vma, char *myaddr, int len));
+{
+ return (*get_elf_backend_data (templ)->elf_backend_bfd_from_remote_memory)
+ (templ, ehdr_vma, loadbasep, target_read_memory);
}
Index: elfcode.h
===================================================================
RCS file: /cvs/src/src/bfd/elfcode.h,v
retrieving revision 1.41
diff -B -p -u -r1.41 elfcode.h
--- elfcode.h 9 May 2003 02:27:00 -0000 1.41
+++ elfcode.h 19 May 2003 21:31:19 -0000
@@ -1568,6 +1568,232 @@ 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. If not null, *LOADBASEP is filled in with the difference
+ between the VMAs from which the segments were read, and the VMAs the
+ file headers (and hence BFD's idea of each section's VMA) put them at.
+
+ The function TARGET_READ_MEMORY is called to copy LEN bytes from the
+ remote memory at target address VMA into the local buffer at MYADDR; it
+ should return zero on success or an `errno' code on failure. 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_remote_memory) (templ, ehdr_vma, loadbasep,
+ target_read_memory)
+ bfd *templ;
+ bfd_vma ehdr_vma;
+ bfd_vma *loadbasep;
+ int (*target_read_memory) PARAMS ((bfd_vma vma, char *myaddr, int len));
+{
+ Elf_External_Ehdr x_ehdr; /* Elf file header, external form */
+ Elf_Internal_Ehdr i_ehdr; /* Elf file header, internal form */
+ Elf_External_Phdr *x_phdrs;
+ Elf_Internal_Phdr *i_phdrs, *last_phdr;
+ 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, &i_ehdr);
+
+ /* The file header tells where to find the program headers.
+ These are what we use to actually choose what to read. */
+
+ if (i_ehdr.e_phentsize != sizeof (Elf_External_Phdr) || i_ehdr.e_phnum == 0)
+ {
+ bfd_set_error (bfd_error_wrong_format);
+ return NULL;
+ }
+
+ x_phdrs = (Elf_External_Phdr *)
+ bfd_malloc (i_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 + i_ehdr.e_phoff, (char *) x_phdrs,
+ i_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[i_ehdr.e_phnum];
+
+ contents_size = 0;
+ last_phdr = NULL;
+ loadbase = ehdr_vma;
+ for (i = 0; i < 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);
+
+ last_phdr = &i_phdrs[i];
+ }
+ }
+ if (last_phdr == NULL)
+ {
+ /* There were no PT_LOAD segments, so we don't have anything to read. */
+ free (x_phdrs);
+ bfd_set_error (bfd_error_wrong_format);
+ return NULL;
+ }
+
+ /* 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 > last_phdr->p_offset + last_phdr->p_filesz
+ && (bfd_vma) contents_size >= (i_ehdr.e_shoff
+ + i_ehdr.e_shnum * i_ehdr.e_shentsize))
+ {
+ contents_size = last_phdr->p_offset + last_phdr->p_filesz;
+ if ((bfd_vma) contents_size < (i_ehdr.e_shoff
+ + i_ehdr.e_shnum * i_ehdr.e_shentsize))
+ contents_size = i_ehdr.e_shoff + i_ehdr.e_shnum * i_ehdr.e_shentsize;
+ }
+ else
+ contents_size = last_phdr->p_offset + last_phdr->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 < 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 < (i_ehdr.e_shoff
+ + i_ehdr.e_shnum * i_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);
+ memset (&x_ehdr.e_shstrndx, 0, sizeof x_ehdr.e_shstrndx);
+ }
+
+ /* 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;
+
+ if (loadbasep)
+ *loadbasep = loadbase;
+ return nbfd;
+}
+
#include "elfcore.h"
#include "elflink.h"
Index: elfxx-target.h
===================================================================
RCS file: /cvs/src/src/bfd/elfxx-target.h,v
retrieving revision 1.50
diff -B -p -u -r1.50 elfxx-target.h
--- elfxx-target.h 12 Mar 2003 22:52:12 -0000 1.50
+++ elfxx-target.h 19 May 2003 21:31:19 -0000
@@ -338,6 +338,9 @@
#ifndef elf_backend_ecoff_debug_swap
#define elf_backend_ecoff_debug_swap 0
#endif
+#ifndef elf_backend_bfd_from_remote_memory
+#define elf_backend_bfd_from_remote_memory _bfd_elfNN_bfd_from_remote_memory
+#endif
#ifndef elf_backend_got_header_size
#define elf_backend_got_header_size 0
#endif
@@ -490,6 +493,7 @@ static const struct elf_backend_data elf
elf_backend_mips_irix_compat,
elf_backend_mips_rtype_to_howto,
elf_backend_ecoff_debug_swap,
+ elf_backend_bfd_from_remote_memory,
ELF_MACHINE_ALT1,
ELF_MACHINE_ALT2,
&elf_backend_size_info,