/* opncls.c -- open and close a BFD.
Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 2000,
- 2001, 2002, 2003
+ 2001, 2002, 2003, 2004, 2005
Free Software Foundation, Inc.
Written by Cygnus Support.
/* Return a new BFD. All BFD's are allocated through this routine. */
bfd *
-_bfd_new_bfd ()
+_bfd_new_bfd (void)
{
bfd *nbfd;
- nbfd = (bfd *) bfd_zmalloc ((bfd_size_type) sizeof (bfd));
+ nbfd = bfd_zmalloc (sizeof (bfd));
if (nbfd == NULL)
return NULL;
nbfd->id = _bfd_id_counter++;
- nbfd->memory = (PTR) objalloc_create ();
+ nbfd->memory = objalloc_create ();
if (nbfd->memory == NULL)
{
bfd_set_error (bfd_error_no_memory);
nbfd->direction = no_direction;
nbfd->iostream = NULL;
nbfd->where = 0;
- if (!bfd_hash_table_init_n (&nbfd->section_htab,
- bfd_section_hash_newfunc,
+ if (!bfd_hash_table_init_n (& nbfd->section_htab, bfd_section_hash_newfunc,
251))
{
free (nbfd);
return NULL;
}
- nbfd->sections = (asection *) NULL;
+ nbfd->sections = NULL;
nbfd->section_tail = &nbfd->sections;
nbfd->format = bfd_unknown;
- nbfd->my_archive = (bfd *) NULL;
+ nbfd->my_archive = NULL;
nbfd->origin = 0;
nbfd->opened_once = FALSE;
nbfd->output_has_begun = FALSE;
nbfd->section_count = 0;
- nbfd->usrdata = (PTR) NULL;
+ nbfd->usrdata = NULL;
nbfd->cacheable = FALSE;
nbfd->flags = BFD_NO_FLAGS;
nbfd->mtime_set = FALSE;
/* Allocate a new BFD as a member of archive OBFD. */
bfd *
-_bfd_new_bfd_contained_in (obfd)
- bfd *obfd;
+_bfd_new_bfd_contained_in (bfd *obfd)
{
bfd *nbfd;
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;
/* Delete a BFD. */
void
-_bfd_delete_bfd (abfd)
- bfd *abfd;
+_bfd_delete_bfd (bfd *abfd)
{
bfd_hash_table_free (&abfd->section_htab);
objalloc_free ((struct objalloc *) abfd->memory);
bfd_openr
SYNOPSIS
- bfd *bfd_openr(const char *filename, const char *target);
+ bfd *bfd_openr (const char *filename, const char *target);
DESCRIPTION
Open the file @var{filename} (using <<fopen>>) with the target
*/
bfd *
-bfd_openr (filename, target)
- const char *filename;
- const char *target;
+bfd_openr (const char *filename, const char *target)
{
bfd *nbfd;
const bfd_target *target_vec;
bfd_fdopenr
SYNOPSIS
- bfd *bfd_fdopenr(const char *filename, const char *target, int fd);
+ bfd *bfd_fdopenr (const char *filename, const char *target, int fd);
DESCRIPTION
<<bfd_fdopenr>> is to <<bfd_fopenr>> much like <<fdopen>> is to
descriptors for other opens), with the supplied @var{fd} used as
an initial file descriptor (but subject to closure at any time),
call bfd_set_cacheable(bfd, 1) on the returned BFD. The default
- is to assume no cacheing; the file descriptor will remain open
+ is to assume no caching; the file descriptor will remain open
until <<bfd_close>>, and will not be affected by BFD operations
on other files.
*/
bfd *
-bfd_fdopenr (filename, target, fd)
- const char *filename;
- const char *target;
- int fd;
+bfd_fdopenr (const char *filename, const char *target, int fd)
{
bfd *nbfd;
const bfd_target *target_vec;
}
#ifndef HAVE_FDOPEN
- nbfd->iostream = (PTR) fopen (filename, FOPEN_RB);
+ nbfd->iostream = fopen (filename, FOPEN_RB);
#else
/* (O_ACCMODE) parens are to avoid Ultrix header file bug. */
switch (fdflags & (O_ACCMODE))
{
- case O_RDONLY: nbfd->iostream = (PTR) fdopen (fd, FOPEN_RB); break;
- case O_WRONLY: nbfd->iostream = (PTR) fdopen (fd, FOPEN_RUB); break;
- case O_RDWR: nbfd->iostream = (PTR) fdopen (fd, FOPEN_RUB); break;
+ case O_RDONLY: nbfd->iostream = fdopen (fd, FOPEN_RB); break;
+ case O_WRONLY: nbfd->iostream = fdopen (fd, FOPEN_RUB); break;
+ case O_RDWR: nbfd->iostream = fdopen (fd, FOPEN_RUB); break;
default: abort ();
}
#endif
bfd_openstreamr
SYNOPSIS
- bfd *bfd_openstreamr(const char *, const char *, PTR);
+ bfd *bfd_openstreamr (const char *, const char *, void *);
DESCRIPTION
*/
bfd *
-bfd_openstreamr (filename, target, streamarg)
- const char *filename;
- const char *target;
- PTR streamarg;
+bfd_openstreamr (const char *filename, const char *target, void *streamarg)
{
- FILE *stream = (FILE *) streamarg;
+ FILE *stream = streamarg;
bfd *nbfd;
const bfd_target *target_vec;
return NULL;
}
- nbfd->iostream = (PTR) stream;
+ nbfd->iostream = stream;
nbfd->filename = filename;
nbfd->direction = read_direction;
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 a read-only @var{stream}.
+ The @var{stream} is created using @var{open}, accessed using
+ @var{pread} and destroyed using @var{close}.
+
+ Calls <<bfd_find_target>>, so @var{target} is interpreted as by
+ that function.
+
+ Calls @var{open} (which can call <<bfd_zalloc>> and
+ <<bfd_get_filename>>) to obtain the read-only stream backing
+ the BFD. @var{open} either succeeds returning the
+ non-<<NULL>> @var{stream}, or fails returning <<NULL>>
+ (setting <<bfd_error>>).
+
+ Calls @var{pread} to request @var{nbytes} of data from
+ @var{stream} starting at @var{offset} (e.g., via a call to
+ <<bfd_read>>). @var{pread} either succeeds returning the
+ number of bytes read (which can be less than @var{nbytes} when
+ end-of-file), or fails returning -1 (setting <<bfd_error>>).
+
+ Calls @var{close} when the BFD is later closed using
+ <<bfd_close>>. @var{close} either succeeds returning 0, or
+ fails returning -1 (setting <<bfd_error>>).
+
+ If <<bfd_openr_iovec>> returns <<NULL>> then an error has
+ occurred. Possible errors are <<bfd_error_no_memory>>,
+ <<bfd_error_invalid_target>> and <<bfd_error_system_call>>.
+
+*/
+
+struct opncls
+{
+ 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
+opncls_btell (struct bfd *abfd)
+{
+ struct opncls *vec = abfd->iostream;
+ return vec->where;
+}
+
+static int
+opncls_bseek (struct bfd *abfd, file_ptr offset, int whence)
+{
+ struct opncls *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
+opncls_bread (struct bfd *abfd, void *buf, file_ptr nbytes)
+{
+ struct opncls *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
+opncls_bwrite (struct bfd *abfd ATTRIBUTE_UNUSED,
+ const void *where ATTRIBUTE_UNUSED,
+ file_ptr nbytes ATTRIBUTE_UNUSED)
+{
+ return -1;
+}
+
+static int
+opncls_bclose (struct bfd *abfd)
+{
+ struct opncls *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
+opncls_bflush (struct bfd *abfd ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+static int
+opncls_bstat (struct bfd *abfd ATTRIBUTE_UNUSED, struct stat *sb)
+{
+ memset (sb, 0, sizeof (*sb));
+ return 0;
+}
+
+static const struct bfd_iovec opncls_iovec = {
+ &opncls_bread, &opncls_bwrite, &opncls_btell, &opncls_bseek,
+ &opncls_bclose, &opncls_bflush, &opncls_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 opncls *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 opncls));
+ vec->stream = stream;
+ vec->pread = pread;
+ vec->close = close;
+
+ nbfd->iovec = &opncls_iovec;
+ nbfd->iostream = vec;
+
+ return nbfd;
+}
\f
/* bfd_openw -- open for writing.
Returns a pointer to a freshly-allocated BFD on success, or NULL.
bfd_openw
SYNOPSIS
- bfd *bfd_openw(const char *filename, const char *target);
+ bfd *bfd_openw (const char *filename, const char *target);
DESCRIPTION
Create a BFD, associated with file @var{filename}, using the
*/
bfd *
-bfd_openw (filename, target)
- const char *filename;
- const char *target;
+bfd_openw (const char *filename, const char *target)
{
bfd *nbfd;
const bfd_target *target_vec;
bfd_boolean
-bfd_close (abfd)
- bfd *abfd;
+bfd_close (bfd *abfd)
{
bfd_boolean ret;
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 = TRUE;
/* If the file was open for writing and is now executable,
make it so. */
*/
bfd_boolean
-bfd_close_all_done (abfd)
- bfd *abfd;
+bfd_close_all_done (bfd *abfd)
{
bfd_boolean ret;
bfd_create
SYNOPSIS
- bfd *bfd_create(const char *filename, bfd *templ);
+ bfd *bfd_create (const char *filename, bfd *templ);
DESCRIPTION
Create a new BFD in the manner of <<bfd_openw>>, but without
*/
bfd *
-bfd_create (filename, templ)
- const char *filename;
- bfd *templ;
+bfd_create (const char *filename, bfd *templ)
{
bfd *nbfd;
*/
bfd_boolean
-bfd_make_writable(abfd)
- bfd *abfd;
+bfd_make_writable (bfd *abfd)
{
struct bfd_in_memory *bim;
return FALSE;
}
- bim = ((struct bfd_in_memory *)
- bfd_malloc ((bfd_size_type) sizeof (struct bfd_in_memory)));
- abfd->iostream = (PTR) bim;
+ bim = bfd_malloc (sizeof (struct bfd_in_memory));
+ abfd->iostream = bim;
/* bfd_bwrite will grow these as needed. */
bim->size = 0;
bim->buffer = 0;
<<TRUE>> is returned if all is ok, otherwise <<FALSE>>. */
bfd_boolean
-bfd_make_readable(abfd)
- bfd *abfd;
+bfd_make_readable (bfd *abfd)
{
if (abfd->direction != write_direction || !(abfd->flags & BFD_IN_MEMORY))
{
abfd->where = 0;
abfd->format = bfd_unknown;
- abfd->my_archive = (bfd *) NULL;
+ abfd->my_archive = NULL;
abfd->origin = 0;
abfd->opened_once = FALSE;
abfd->output_has_begun = FALSE;
abfd->section_count = 0;
- abfd->usrdata = (PTR) NULL;
+ abfd->usrdata = NULL;
abfd->cacheable = FALSE;
abfd->flags = BFD_IN_MEMORY;
abfd->mtime_set = FALSE;
bfd_alloc
SYNOPSIS
- PTR bfd_alloc (bfd *abfd, size_t wanted);
+ void *bfd_alloc (bfd *abfd, bfd_size_type wanted);
DESCRIPTION
Allocate a block of @var{wanted} bytes of memory attached to
<<abfd>> and return a pointer to it.
*/
-
-PTR
-bfd_alloc (abfd, size)
- bfd *abfd;
- bfd_size_type size;
+void *
+bfd_alloc (bfd *abfd, bfd_size_type size)
{
- PTR ret;
+ void *ret;
if (size != (unsigned long) size)
{
return ret;
}
-PTR
-bfd_zalloc (abfd, size)
- bfd *abfd;
- bfd_size_type size;
+/*
+INTERNAL_FUNCTION
+ bfd_zalloc
+
+SYNOPSIS
+ void *bfd_zalloc (bfd *abfd, bfd_size_type wanted);
+
+DESCRIPTION
+ Allocate a block of @var{wanted} bytes of zeroed memory
+ attached to <<abfd>> and return a pointer to it.
+*/
+
+void *
+bfd_zalloc (bfd *abfd, bfd_size_type size)
{
- PTR res;
+ void *res;
res = bfd_alloc (abfd, size);
if (res)
Note: Also frees all more recently allocated blocks! */
void
-bfd_release (abfd, block)
- bfd *abfd;
- PTR block;
+bfd_release (bfd *abfd, void *block)
{
objalloc_free_block ((struct objalloc *) abfd->memory, block);
}
-/*
- GNU Extension: separate debug-info files
-
+/*
+ GNU Extension: separate debug-info files
+
The idea here is that a special section called .gnu_debuglink might be
embedded in a binary file, which indicates that some *other* file
contains the real debugging information. This special section contains a
without debug symbols).
*/
-static unsigned long calc_crc32 PARAMS ((unsigned long, const unsigned char *, size_t));
-static char * get_debug_link_info PARAMS ((bfd *, unsigned long *));
-static bfd_boolean separate_debug_file_exists PARAMS ((const char *, const unsigned long));
-static char * find_separate_debug_file PARAMS ((bfd *, const char *));
-
+#define GNU_DEBUGLINK ".gnu_debuglink"
/*
-INTERNAL_FUNCTION
- calc_crc32
+FUNCTION
+ bfd_calc_gnu_debuglink_crc32
SYNOPSIS
- unsigned long calc_crc32 (unsigned long crc, const unsigned char *buf, size_t len);
+ unsigned long bfd_calc_gnu_debuglink_crc32
+ (unsigned long crc, const unsigned char *buf, bfd_size_type len);
DESCRIPTION
- Advance the CRC32 given by @var{crc} through @var{len}
- bytes of @var{buf}. Return the updated CRC32 value.
-*/
-
-static unsigned long
-calc_crc32 (crc, buf, len)
- unsigned long crc;
- const unsigned char *buf;
- size_t len;
+ Computes a CRC value as used in the .gnu_debuglink section.
+ Advances the previously computed @var{crc} value by computing
+ and adding in the crc32 for @var{len} bytes of @var{buf}.
+
+RETURNS
+ Return the updated CRC32 value.
+*/
+
+unsigned long
+bfd_calc_gnu_debuglink_crc32 (unsigned long crc,
+ const unsigned char *buf,
+ bfd_size_type len)
{
static const unsigned long crc32_table[256] =
{
get_debug_link_info
SYNOPSIS
- char *get_debug_link_info (bfd *abfd, unsigned long *crc32_out)
+ char *get_debug_link_info (bfd *abfd, unsigned long *crc32_out);
DESCRIPTION
fetch the filename and CRC32 value for any separate debuginfo
*/
static char *
-get_debug_link_info (abfd, crc32_out)
- bfd *abfd;
- unsigned long *crc32_out;
+get_debug_link_info (bfd *abfd, unsigned long *crc32_out)
{
- asection * sect;
- bfd_size_type debuglink_size;
+ asection *sect;
unsigned long crc32;
- char * contents;
+ bfd_byte *contents;
int crc_offset;
- bfd_boolean ret;
+ char *name;
BFD_ASSERT (abfd);
BFD_ASSERT (crc32_out);
- sect = bfd_get_section_by_name (abfd, ".gnu_debuglink");
+ sect = bfd_get_section_by_name (abfd, GNU_DEBUGLINK);
if (sect == NULL)
return NULL;
- debuglink_size = bfd_section_size (abfd, sect);
-
- contents = xmalloc (debuglink_size);
- ret = bfd_get_section_contents (abfd, sect, contents,
- (file_ptr)0, debuglink_size);
- if (! ret)
+ if (!bfd_malloc_and_get_section (abfd, sect, &contents))
{
- free (contents);
+ if (contents != NULL)
+ free (contents);
return NULL;
}
/* Crc value is stored after the filename, aligned up to 4 bytes. */
- crc_offset = strlen (contents) + 1;
+ name = (char *) contents;
+ crc_offset = strlen (name) + 1;
crc_offset = (crc_offset + 3) & ~3;
- crc32 = bfd_get_32 (abfd, (bfd_byte *) (contents + crc_offset));
+ crc32 = bfd_get_32 (abfd, contents + crc_offset);
*crc32_out = crc32;
- return contents;
+ return name;
}
/*
separate_debug_file_exists
SYNOPSIS
- bfd_boolean separate_debug_file_exists (char * name, unsigned long crc32)
+ bfd_boolean separate_debug_file_exists
+ (char *name, unsigned long crc32);
DESCRIPTION
Checks to see if @var{name} is a file and if its contents
*/
static bfd_boolean
-separate_debug_file_exists (name, crc)
- const char *name;
- const unsigned long crc;
+separate_debug_file_exists (const char *name, const unsigned long crc)
{
- static char buffer [8 * 1024];
+ static unsigned char buffer [8 * 1024];
unsigned long file_crc = 0;
int fd;
- int count;
+ bfd_size_type count;
BFD_ASSERT (name);
return FALSE;
while ((count = read (fd, buffer, sizeof (buffer))) > 0)
- file_crc = calc_crc32 (file_crc, buffer, count);
+ file_crc = bfd_calc_gnu_debuglink_crc32 (file_crc, buffer, count);
close (fd);
find_separate_debug_file
SYNOPSIS
- char * find_separate_debug_file (bfd *abfd)
+ char *find_separate_debug_file (bfd *abfd);
DESCRIPTION
Searches @var{abfd} for a reference to separate debugging
*/
static char *
-find_separate_debug_file (abfd, debug_file_directory)
- bfd *abfd;
- const char *debug_file_directory;
+find_separate_debug_file (bfd *abfd, const char *debug_file_directory)
{
char *basename;
char *dir;
return NULL;
basename = get_debug_link_info (abfd, & crc32);
-
if (basename == NULL)
return NULL;
+
if (strlen (basename) < 1)
{
free (basename);
return NULL;
}
- dir = xstrdup (abfd->filename);
+ dir = strdup (abfd->filename);
+ if (dir == NULL)
+ {
+ free (basename);
+ return NULL;
+ }
BFD_ASSERT (strlen (dir) != 0);
-
+
/* Strip off filename part. */
for (i = strlen (dir) - 1; i >= 0; i--)
if (IS_DIR_SEPARATOR (dir[i]))
break;
-
- dir[i + 1] = '\0';
- BFD_ASSERT (dir[i] == '/' || dir[0] == '\0')
- debugfile = xmalloc (strlen (debug_file_directory) + 1
- + strlen (dir)
- + strlen (".debug/")
- + strlen (basename)
- + 1);
+ dir[i + 1] = '\0';
+ BFD_ASSERT (dir[i] == '/' || dir[0] == '\0');
+
+ debugfile = malloc (strlen (debug_file_directory) + 1
+ + strlen (dir)
+ + strlen (".debug/")
+ + strlen (basename)
+ + 1);
+ if (debugfile == NULL)
+ {
+ free (basename);
+ free (dir);
+ return NULL;
+ }
/* First try in the same directory as the original file: */
strcpy (debugfile, dir);
bfd_follow_gnu_debuglink
SYNOPSIS
- char * bfd_follow_gnu_debuglink(bfd *abfd, const char *dir);
+ char *bfd_follow_gnu_debuglink (bfd *abfd, const char *dir);
DESCRIPTION
Takes a BFD and searches it for a .gnu_debuglink section. If this
- section is found, examines the section for the name and checksum of
- a '.debug' file containing auxiliary debugging
- information. Searches filesystem for .debug file in some standard
+ section is found, it examines the section for the name and checksum
+ of a '.debug' file containing auxiliary debugging information. It
+ then searches the filesystem for this .debug file in some standard
locations, including the directory tree rooted at @var{dir}, and if
- found returns the full filename. If @var{dir} is NULL, will search
- default path configured into libbfd at build time.
+ found returns the full filename.
+
+ If @var{dir} is NULL, it will search a default path configured into
+ libbfd at build time. [XXX this feature is not currently
+ implemented].
RETURNS
<<NULL>> on any errors or failure to locate the .debug file,
otherwise a pointer to a heap-allocated string containing the
- filename. The caller is responsible for freeing this string.
+ filename. The caller is responsible for freeing this string.
*/
char *
-bfd_follow_gnu_debuglink (abfd, dir)
- bfd *abfd;
- const char * dir;
+bfd_follow_gnu_debuglink (bfd *abfd, const char *dir)
{
-#if 0 /* Disabled until DEBUGDIR can be defined by configure.in */
- if (dir == NULL)
- dir = DEBUGDIR;
-#endif
return find_separate_debug_file (abfd, dir);
}
+
+/*
+FUNCTION
+ bfd_create_gnu_debuglink_section
+
+SYNOPSIS
+ struct bfd_section *bfd_create_gnu_debuglink_section
+ (bfd *abfd, const char *filename);
+
+DESCRIPTION
+
+ Takes a @var{BFD} and adds a .gnu_debuglink section to it. The section is sized
+ to be big enough to contain a link to the specified @var{filename}.
+
+RETURNS
+ A pointer to the new section is returned if all is ok. Otherwise <<NULL>> is
+ returned and bfd_error is set.
+*/
+
+asection *
+bfd_create_gnu_debuglink_section (bfd *abfd, const char *filename)
+{
+ asection *sect;
+ bfd_size_type debuglink_size;
+
+ if (abfd == NULL || filename == NULL)
+ {
+ bfd_set_error (bfd_error_invalid_operation);
+ return NULL;
+ }
+
+ /* Strip off any path components in filename. */
+ filename = lbasename (filename);
+
+ sect = bfd_get_section_by_name (abfd, GNU_DEBUGLINK);
+ if (sect)
+ {
+ /* Section already exists. */
+ bfd_set_error (bfd_error_invalid_operation);
+ return NULL;
+ }
+
+ sect = bfd_make_section (abfd, GNU_DEBUGLINK);
+ if (sect == NULL)
+ return NULL;
+
+ if (! bfd_set_section_flags (abfd, sect,
+ SEC_HAS_CONTENTS | SEC_READONLY | SEC_DEBUGGING))
+ /* XXX Should we delete the section from the bfd ? */
+ return NULL;
+
+
+ debuglink_size = strlen (filename) + 1;
+ debuglink_size += 3;
+ debuglink_size &= ~3;
+ debuglink_size += 4;
+
+ if (! bfd_set_section_size (abfd, sect, debuglink_size))
+ /* XXX Should we delete the section from the bfd ? */
+ return NULL;
+
+ return sect;
+}
+
+
+/*
+FUNCTION
+ bfd_fill_in_gnu_debuglink_section
+
+SYNOPSIS
+ bfd_boolean bfd_fill_in_gnu_debuglink_section
+ (bfd *abfd, struct bfd_section *sect, const char *filename);
+
+DESCRIPTION
+
+ Takes a @var{BFD} and containing a .gnu_debuglink section @var{SECT}
+ and fills in the contents of the section to contain a link to the
+ specified @var{filename}. The filename should be relative to the
+ current directory.
+
+RETURNS
+ <<TRUE>> is returned if all is ok. Otherwise <<FALSE>> is returned
+ and bfd_error is set.
+*/
+
+bfd_boolean
+bfd_fill_in_gnu_debuglink_section (bfd *abfd,
+ struct bfd_section *sect,
+ const char *filename)
+{
+ bfd_size_type debuglink_size;
+ unsigned long crc32;
+ char * contents;
+ bfd_size_type crc_offset;
+ FILE * handle;
+ static unsigned char buffer[8 * 1024];
+ size_t count;
+
+ if (abfd == NULL || sect == NULL || filename == NULL)
+ {
+ bfd_set_error (bfd_error_invalid_operation);
+ return FALSE;
+ }
+
+ /* Make sure that we can read the file.
+ XXX - Should we attempt to locate the debug info file using the same
+ algorithm as gdb ? At the moment, since we are creating the
+ .gnu_debuglink section, we insist upon the user providing us with a
+ correct-for-section-creation-time path, but this need not conform to
+ the gdb location algorithm. */
+ handle = fopen (filename, FOPEN_RB);
+ if (handle == NULL)
+ {
+ bfd_set_error (bfd_error_system_call);
+ return FALSE;
+ }
+
+ crc32 = 0;
+ while ((count = fread (buffer, 1, sizeof buffer, handle)) > 0)
+ crc32 = bfd_calc_gnu_debuglink_crc32 (crc32, buffer, count);
+ fclose (handle);
+
+ /* Strip off any path components in filename,
+ now that we no longer need them. */
+ filename = lbasename (filename);
+
+ debuglink_size = strlen (filename) + 1;
+ debuglink_size += 3;
+ debuglink_size &= ~3;
+ debuglink_size += 4;
+
+ contents = malloc (debuglink_size);
+ if (contents == NULL)
+ {
+ /* XXX Should we delete the section from the bfd ? */
+ bfd_set_error (bfd_error_no_memory);
+ return FALSE;
+ }
+
+ strcpy (contents, filename);
+ crc_offset = debuglink_size - 4;
+
+ bfd_put_32 (abfd, crc32, contents + crc_offset);
+
+ if (! bfd_set_section_contents (abfd, sect, contents, 0, debuglink_size))
+ {
+ /* XXX Should we delete the section from the bfd ? */
+ free (contents);
+ return FALSE;
+ }
+
+ return TRUE;
+}