This is the mail archive of the
binutils@sources.redhat.com
mailing list for the binutils project.
Re: [wip] BFD from an arbitrary object; Was: provide pass-throughvalue in bfd_elf_bfd_from_remote_memory
- From: Andrew Cagney <cagney at gnu dot org>
- To: binutils at sources dot redhat dot com
- Date: Sun, 15 Feb 2004 12:42:48 -0500
- Subject: Re: [wip] BFD from an arbitrary object; Was: provide pass-throughvalue in bfd_elf_bfd_from_remote_memory
- References: <vt21xpswqmg.fsf@zenia.home> <m3r7xr7qzx.fsf@redhat.com> <402D7482.8070403@gnu.org>
Here's a revised work-in-progress. It is greatly simplified from the
previous patch vis:
- it drops the published bfd_file interface
- it makes the iovec (struct bfd_io_vec) private to bfd
- it doesn't try to fix the bfd-in-memory code (later pass)
- it leaves all the cache.c structures in the bfd (later pass?)
- it publishes an interface for creating a read-only iovec backed bfd
+/*
+FUNCTION
+ bfd_openr_iovec
+
+SYNOPSIS
+ bfd *bfd_openr_iovec (const char *filename, const char *target,
+ void *(*open) (struct bfd *nbfd,
+ void *open_closure),
+ void *open_closure,
+ file_ptr (*pread) (struct bfd *nbfd,
+ void *stream,
+ void *buf,
+ file_ptr nbytes,
+ file_ptr offset),
+ int (*close) (struct bfd *nbfd,
+ void *stream));
+
+DESCRIPTION
+
+ Create and return a BFD backed by the read-only <<stream>>
+ returned by <<open>>.
+
+ Calls <<bfd_find_target>>, so @var{target} is interpreted as by
+ that function.
+
+ Calls <<open>> (which can call <<bfd_zalloc>> and
+ <<bfd_get_filename>>) to obtain to obtain the read-only stream
+ backing the BFD.
+
+ Any request for data from <<stream>> (e.g., by <<bfd_read>>)
+ is passed through to <<pread>>.
+
+ When the BFD is later <<bfd_close>>d, <<close>> is called.
+
+ If <<NULL>> is returned then an error has occurred. Possible
errors
+ are <<bfd_error_no_memory>>, <<bfd_error_invalid_target>> or
+ <<system_call>> error.
This time everything still works. I also tested the interface by
hacking objdump to use it.
Looking better?
Andrew
Index: bfd.c
===================================================================
RCS file: /cvs/src/src/bfd/bfd.c,v
retrieving revision 1.55
diff -u -r1.55 bfd.c
--- bfd.c 24 Nov 2003 18:06:39 -0000 1.55
+++ bfd.c 15 Feb 2004 17:33:49 -0000
@@ -45,14 +45,10 @@
. {* A pointer to the target jump table. *}
. const struct bfd_target *xvec;
.
-. {* To avoid dragging too many header files into every file that
-. includes `<<bfd.h>>', IOSTREAM has been declared as a "char *",
-. and MTIME as a "long". Their correct types, to which they
-. are cast when used, are "FILE *" and "time_t". The iostream
-. is the result of an fopen on the filename. However, if the
-. BFD_IN_MEMORY flag is set, then iostream is actually a pointer
-. to a bfd_in_memory struct. *}
+. {* The IOSTREAM, and corresponding IO vector that provide access
+. to the file backing the BFD. *}
. void *iostream;
+. const struct bfd_io_vec *iovec;
.
. {* Is the file descriptor being cached? That is, can it be closed as
. needed, and re-opened when accessed later? *}
Index: bfdio.c
===================================================================
RCS file: /cvs/src/src/bfd/bfdio.c,v
retrieving revision 1.5
diff -u -r1.5 bfdio.c
--- bfdio.c 11 Feb 2004 23:23:20 -0000 1.5
+++ bfdio.c 15 Feb 2004 17:33:51 -0000
@@ -62,38 +62,33 @@
#endif
}
-/* Note that archive entries don't have streams; they share their parent's.
- This allows someone to play with the iostream behind BFD's back.
+/*
+INTERNAL_DEFINITION
+ struct bfd_io_vec
- Also, note that the origin pointer points to the beginning of a file's
- contents (0 for non-archive elements). For archive entries this is the
- first octet in the file, NOT the beginning of the archive header. */
+DESCRIPTION
+ The <<struct bfd_io_vec>> contains the internal file I/O class. Each
+ <<BFD>> has an instance of this class and all file I/O is
+ routed through it.
+
+.struct bfd_io_vec
+.{
+. {* To avoid problems with macros, use a "b" rather than "f"
+. prefix to each method name. *}
+. file_ptr (*bread) (struct bfd *abfd, void *ptr, file_ptr nbytes);
+. file_ptr (*bwrite) (struct bfd *abfd, const void *ptr,
+. file_ptr nbytes);
+. file_ptr (*btell) (struct bfd *abfd);
+. int (*bseek) (struct bfd *abfd, file_ptr offset, int whence);
+. int (*bclose) (struct bfd *abfd);
+. int (*berror) (struct bfd *abfd);
+. int (*bflush) (struct bfd *abfd);
+. int (*bstat) (struct bfd *abfd, struct stat *sb);
+.};
-static size_t
-real_read (void *where, size_t a, size_t b, FILE *file)
-{
- /* FIXME - this looks like an optimization, but it's really to cover
- up for a feature of some OSs (not solaris - sigh) that
- ld/pe-dll.c takes advantage of (apparently) when it creates BFDs
- internally and tries to link against them. BFD seems to be smart
- enough to realize there are no symbol records in the "file" that
- doesn't exist but attempts to read them anyway. On Solaris,
- attempting to read zero bytes from a NULL file results in a core
- dump, but on other platforms it just returns zero bytes read.
- This makes it to something reasonable. - DJ */
- if (a == 0 || b == 0)
- return 0;
+*/
-#if defined (__VAX) && defined (VMS)
- /* Apparently fread on Vax VMS does not keep the record length
- information. */
- return read (fileno (file), where, a * b);
-#else
- return fread (where, a, b, file);
-#endif
-}
-
/* Return value is amount read. */
bfd_size_type
@@ -121,7 +116,7 @@
return get;
}
- nread = real_read (ptr, 1, (size_t) size, bfd_cache_lookup (abfd));
+ nread = abfd->iovec->bread (abfd, ptr, size);
if (nread != (size_t) -1)
abfd->where += nread;
@@ -134,7 +129,7 @@
provide something more useful (eg. no_symbols or wrong_format). */
if (nread != size)
{
- if (ferror (bfd_cache_lookup (abfd)))
+ if (abfd->iovec->berror (abfd))
bfd_set_error (bfd_error_system_call);
else
bfd_set_error (bfd_error_file_truncated);
@@ -175,7 +170,7 @@
return size;
}
- nwrote = fwrite (ptr, 1, (size_t) size, bfd_cache_lookup (abfd));
+ nwrote = abfd->iovec->bwrite (abfd, ptr, size);
if (nwrote != (size_t) -1)
abfd->where += nwrote;
if (nwrote != size)
@@ -196,7 +191,7 @@
if ((abfd->flags & BFD_IN_MEMORY) != 0)
return abfd->where;
- ptr = real_ftell (bfd_cache_lookup (abfd));
+ ptr = abfd->iovec->btell (abfd);
if (abfd->my_archive)
ptr -= abfd->origin;
@@ -209,7 +204,7 @@
{
if ((abfd->flags & BFD_IN_MEMORY) != 0)
return 0;
- return fflush (bfd_cache_lookup(abfd));
+ return abfd->iovec->bflush (abfd);
}
/* Returns 0 for success, negative value for failure (in which case
@@ -217,19 +212,12 @@
int
bfd_stat (bfd *abfd, struct stat *statbuf)
{
- FILE *f;
int result;
if ((abfd->flags & BFD_IN_MEMORY) != 0)
abort ();
- f = bfd_cache_lookup (abfd);
- if (f == NULL)
- {
- bfd_set_error (bfd_error_system_call);
- return -1;
- }
- result = fstat (fileno (f), statbuf);
+ result = abfd->iovec->bstat (abfd, statbuf);
if (result < 0)
bfd_set_error (bfd_error_system_call);
return result;
@@ -242,7 +230,6 @@
bfd_seek (bfd *abfd, file_ptr position, int direction)
{
int result;
- FILE *f;
file_ptr file_position;
/* For the time being, a BFD may not seek to it's end. The problem
is that we don't easily have a way to recognize the end of an
@@ -266,8 +253,8 @@
if (abfd->where > bim->size)
{
- if ((abfd->direction == write_direction) ||
- (abfd->direction == both_direction))
+ if ((abfd->direction == write_direction) ||
+ (abfd->direction == both_direction))
{
bfd_size_type newsize, oldsize;
oldsize = (bim->size + 127) & ~(bfd_size_type) 127;
@@ -328,12 +315,11 @@
In the meantime, no optimization for archives. */
}
- f = bfd_cache_lookup (abfd);
file_position = position;
if (direction == SEEK_SET && abfd->my_archive != NULL)
file_position += abfd->origin;
- result = real_fseek (f, file_position, direction);
+ result = abfd->iovec->bseek (abfd, file_position, direction);
if (result != 0)
{
int hold_errno = errno;
@@ -378,14 +364,12 @@
long
bfd_get_mtime (bfd *abfd)
{
- FILE *fp;
struct stat buf;
if (abfd->mtime_set)
return abfd->mtime;
- fp = bfd_cache_lookup (abfd);
- if (0 != fstat (fileno (fp), &buf))
+ if (abfd->iovec->bstat (abfd, &buf) != 0)
return 0;
abfd->mtime = buf.st_mtime; /* Save value in case anyone wants it */
@@ -428,14 +412,12 @@
long
bfd_get_size (bfd *abfd)
{
- FILE *fp;
struct stat buf;
if ((abfd->flags & BFD_IN_MEMORY) != 0)
return ((struct bfd_in_memory *) abfd->iostream)->size;
- fp = bfd_cache_lookup (abfd);
- if (0 != fstat (fileno (fp), & buf))
+ if (abfd->iovec->bstat (abfd, &buf) != 0)
return 0;
return buf.st_size;
Index: cache.c
===================================================================
RCS file: /cvs/src/src/bfd/cache.c,v
retrieving revision 1.11
diff -u -r1.11 cache.c
--- cache.c 11 Feb 2004 23:23:20 -0000 1.11
+++ cache.c 15 Feb 2004 17:33:52 -0000
@@ -44,6 +44,85 @@
static bfd_boolean bfd_cache_delete (bfd *);
+
+static file_ptr
+cache_btell (struct bfd *abfd)
+{
+ return real_ftell (bfd_cache_lookup (abfd));
+}
+
+static int
+cache_bseek (struct bfd *abfd, file_ptr offset, int whence)
+{
+ return real_fseek (bfd_cache_lookup (abfd), offset, whence);
+}
+
+/* Note that archive entries don't have streams; they share their parent's.
+ This allows someone to play with the iostream behind BFD's back.
+
+ Also, note that the origin pointer points to the beginning of a file's
+ contents (0 for non-archive elements). For archive entries this is the
+ first octet in the file, NOT the beginning of the archive header. */
+
+static file_ptr
+cache_bread (struct bfd *abfd, void *buf, file_ptr nbytes)
+{
+ /* FIXME - this looks like an optimization, but it's really to cover
+ up for a feature of some OSs (not solaris - sigh) that
+ ld/pe-dll.c takes advantage of (apparently) when it creates BFDs
+ internally and tries to link against them. BFD seems to be smart
+ enough to realize there are no symbol records in the "file" that
+ doesn't exist but attempts to read them anyway. On Solaris,
+ attempting to read zero bytes from a NULL file results in a core
+ dump, but on other platforms it just returns zero bytes read.
+ This makes it to something reasonable. - DJ */
+ if (nbytes == 0)
+ return 0;
+
+#if defined (__VAX) && defined (VMS)
+ /* Apparently fread on Vax VMS does not keep the record length
+ information. */
+ return read (fileno (bfd_cache_lookup (abfd)), buf, nbytes);
+#else
+ return fread (buf, 1, nbytes, bfd_cache_lookup (abfd));
+#endif
+}
+
+static file_ptr
+cache_bwrite (struct bfd *abfd, const void *where, file_ptr nbytes)
+{
+ return fwrite (where, 1, nbytes, bfd_cache_lookup (abfd));
+}
+
+static int
+cache_bclose (struct bfd *abfd)
+{
+ bfd_cache_close (abfd);
+}
+
+static int
+cache_berror (struct bfd *abfd)
+{
+ return ferror (bfd_cache_lookup (abfd));
+}
+
+static int
+cache_bflush (struct bfd *abfd)
+{
+ return fflush (bfd_cache_lookup (abfd));
+}
+
+static int
+cache_bstat (struct bfd *abfd, struct stat *sb)
+{
+ return fstat (fileno (bfd_cache_lookup (abfd)), sb);
+}
+
+static const struct bfd_io_vec cache_io_vec = {
+ &cache_bread, &cache_bwrite, &cache_btell, &cache_bseek,
+ &cache_bclose, &cache_berror, &cache_bflush, &cache_bstat
+};
+
/*
INTERNAL_FUNCTION
BFD_CACHE_MAX_OPEN macro
@@ -205,6 +284,7 @@
if (! close_one ())
return FALSE;
}
+ abfd->iovec = &cache_io_vec;
insert (abfd);
++open_files;
return TRUE;
@@ -229,8 +309,7 @@
bfd_boolean
bfd_cache_close (bfd *abfd)
{
- if (abfd->iostream == NULL
- || (abfd->flags & BFD_IN_MEMORY) != 0)
+ if (abfd->iovec != &cache_io_vec)
return TRUE;
return bfd_cache_delete (abfd);
Index: opncls.c
===================================================================
RCS file: /cvs/src/src/bfd/opncls.c,v
retrieving revision 1.21
diff -u -r1.21 opncls.c
--- opncls.c 21 Jan 2004 11:17:53 -0000 1.21
+++ opncls.c 15 Feb 2004 17:33:53 -0000
@@ -103,6 +103,7 @@
if (nbfd == NULL)
return NULL;
nbfd->xvec = obfd->xvec;
+ nbfd->iovec = obfd->iovec;
nbfd->my_archive = obfd;
nbfd->direction = read_direction;
nbfd->target_defaulted = obfd->target_defaulted;
@@ -322,6 +323,181 @@
return nbfd;
}
+
+/*
+FUNCTION
+ bfd_openr_iovec
+
+SYNOPSIS
+ bfd *bfd_openr_iovec (const char *filename, const char *target,
+ void *(*open) (struct bfd *nbfd,
+ void *open_closure),
+ void *open_closure,
+ file_ptr (*pread) (struct bfd *nbfd,
+ void *stream,
+ void *buf,
+ file_ptr nbytes,
+ file_ptr offset),
+ int (*close) (struct bfd *nbfd,
+ void *stream));
+
+DESCRIPTION
+
+ Create and return a BFD backed by the read-only <<stream>>
+ returned by <<open>>.
+
+ Calls <<bfd_find_target>>, so @var{target} is interpreted as by
+ that function.
+
+ Calls <<open>> (which can call <<bfd_zalloc>> and
+ <<bfd_get_filename>>) to obtain to obtain the read-only stream
+ backing the BFD.
+
+ Any request for data from <<stream>> (e.g., by <<bfd_read>>)
+ is passed through to <<pread>>.
+
+ When the BFD is later <<bfd_close>>d, <<close>> is called.
+
+ If <<NULL>> is returned then an error has occurred. Possible errors
+ are <<bfd_error_no_memory>>, <<bfd_error_invalid_target>> or
+ <<system_call>> error.
+
+*/
+
+struct iovec
+{
+ void *stream;
+ file_ptr (*pread) (struct bfd *abfd, void *stream, void *buf,
+ file_ptr nbytes, file_ptr offset);
+ int (*close) (struct bfd *abfd, void *stream);
+ file_ptr where;
+};
+
+static file_ptr
+iovec_btell (struct bfd *abfd)
+{
+ struct iovec *vec = abfd->iostream;
+ return vec->where;
+}
+
+static int
+iovec_bseek (struct bfd *abfd, file_ptr offset, int whence)
+{
+ struct iovec *vec = abfd->iostream;
+ switch (whence)
+ {
+ case SEEK_SET: vec->where = offset; break;
+ case SEEK_CUR: vec->where += offset; break;
+ case SEEK_END: return -1;
+ }
+ return 0;
+}
+
+static file_ptr
+iovec_bread (struct bfd *abfd, void *buf, file_ptr nbytes)
+{
+ struct iovec *vec = abfd->iostream;
+ file_ptr nread = vec->pread (abfd, vec->stream, buf, nbytes, vec->where);
+ if (nread < 0)
+ return nread;
+ vec->where += nread;
+ return nread;
+}
+
+static file_ptr
+iovec_bwrite (struct bfd *abfd ATTRIBUTE_UNUSED,
+ const void *where ATTRIBUTE_UNUSED,
+ file_ptr nbytes ATTRIBUTE_UNUSED)
+{
+ return -1;
+}
+
+static int
+iovec_bclose (struct bfd *abfd)
+{
+ struct iovec *vec = abfd->iostream;
+ /* Since the VEC's memory is bound to the bfd deleting the bfd will
+ free it. */
+ int status = 0;
+ if (vec->close != NULL)
+ status = vec->close (abfd, vec->stream);
+ abfd->iostream = NULL;
+ return status;
+}
+
+static int
+iovec_berror (struct bfd *abfd ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+static int
+iovec_bflush (struct bfd *abfd ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+static int
+iovec_bstat (struct bfd *abfd ATTRIBUTE_UNUSED, struct stat *sb)
+{
+ memset (sb, 0, sizeof (*sb));
+ return 0;
+}
+
+static const struct bfd_io_vec iovec_io_vec = {
+ &iovec_bread, &iovec_bwrite, &iovec_btell, &iovec_bseek,
+ &iovec_bclose, &iovec_berror, &iovec_bflush, &iovec_bstat
+};
+
+bfd *
+bfd_openr_iovec (const char *filename, const char *target,
+ void *(*open) (struct bfd *nbfd,
+ void *open_closure),
+ void *open_closure,
+ file_ptr (*pread) (struct bfd *abfd,
+ void *stream,
+ void *buf,
+ file_ptr nbytes,
+ file_ptr offset),
+ int (*close) (struct bfd *nbfd,
+ void *stream))
+{
+ bfd *nbfd;
+ const bfd_target *target_vec;
+ struct iovec *vec;
+ void *stream;
+
+ nbfd = _bfd_new_bfd ();
+ if (nbfd == NULL)
+ return NULL;
+
+ target_vec = bfd_find_target (target, nbfd);
+ if (target_vec == NULL)
+ {
+ _bfd_delete_bfd (nbfd);
+ return NULL;
+ }
+
+ nbfd->filename = filename;
+ nbfd->direction = read_direction;
+
+ stream = open (nbfd, open_closure);
+ if (stream == NULL)
+ {
+ _bfd_delete_bfd (nbfd);
+ return NULL;
+ }
+
+ vec = bfd_zalloc (nbfd, sizeof (struct iovec));
+ vec->stream = stream;
+ vec->pread = pread;
+ vec->close = close;
+
+ nbfd->iovec = &iovec_io_vec;
+ nbfd->iostream = vec;
+
+ return nbfd;
+}
/* bfd_openw -- open for writing.
Returns a pointer to a freshly-allocated BFD on success, or NULL.
@@ -415,7 +591,12 @@
if (! BFD_SEND (abfd, _close_and_cleanup, (abfd)))
return FALSE;
- ret = bfd_cache_close (abfd);
+ /* FIXME: cagney/2004-02-15: Need to implement a BFD_IN_MEMORY io
+ vector. */
+ if (!(abfd->flags & BFD_IN_MEMORY))
+ ret = abfd->iovec->bclose (abfd);
+ else
+ ret = 0;
/* If the file was open for writing and is now executable,
make it so. */