/* opncls.c -- open and close a BFD.
- Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 2000,
- 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
- Free Software Foundation, Inc.
+ Copyright 1990-2013 Free Software Foundation, Inc.
Written by Cygnus Support.
#define S_IXOTH 0001 /* Execute by others. */
#endif
-/* Counter used to initialize the bfd identifier. */
+/* Counters used to initialize the bfd identifier. */
-static unsigned int _bfd_id_counter = 0;
+static unsigned int bfd_id_counter = 0;
+static unsigned int bfd_reserved_id_counter = 0;
+
+/*
+CODE_FRAGMENT
+.{* Set to N to open the next N BFDs using an alternate id space. *}
+.extern unsigned int bfd_use_reserved_id;
+*/
+unsigned int bfd_use_reserved_id = 0;
/* fdopen is a loser -- we should use stdio exclusively. Unfortunately
if we do that we can't use fcntl. */
if (nbfd == NULL)
return NULL;
- nbfd->id = _bfd_id_counter++;
+ if (bfd_use_reserved_id)
+ {
+ nbfd->id = --bfd_reserved_id_counter;
+ --bfd_use_reserved_id;
+ }
+ else
+ nbfd->id = bfd_id_counter++;
nbfd->memory = objalloc_create ();
if (nbfd->memory == NULL)
nbfd->arch_info = &bfd_default_arch_struct;
- nbfd->direction = no_direction;
- nbfd->iostream = NULL;
- nbfd->where = 0;
if (!bfd_hash_table_init_n (& nbfd->section_htab, bfd_section_hash_newfunc,
- sizeof (struct section_hash_entry), 251))
+ sizeof (struct section_hash_entry), 13))
{
free (nbfd);
return NULL;
}
- nbfd->sections = NULL;
- nbfd->section_last = NULL;
- nbfd->format = bfd_unknown;
- nbfd->my_archive = NULL;
- nbfd->origin = 0;
- nbfd->opened_once = FALSE;
- nbfd->output_has_begun = FALSE;
- nbfd->section_count = 0;
- nbfd->usrdata = NULL;
- nbfd->cacheable = FALSE;
- nbfd->flags = BFD_NO_FLAGS;
- nbfd->mtime_set = FALSE;
return nbfd;
}
+static const struct bfd_iovec opncls_iovec;
+
/* Allocate a new BFD as a member of archive OBFD. */
bfd *
return NULL;
nbfd->xvec = obfd->xvec;
nbfd->iovec = obfd->iovec;
+ if (obfd->iovec == &opncls_iovec)
+ nbfd->iostream = obfd->iostream;
nbfd->my_archive = obfd;
nbfd->direction = read_direction;
nbfd->target_defaulted = obfd->target_defaulted;
/* Delete a BFD. */
-void
+static void
_bfd_delete_bfd (bfd *abfd)
{
if (abfd->memory)
bfd_hash_table_free (&abfd->section_htab);
objalloc_free ((struct objalloc *) abfd->memory);
}
+
+ if (abfd->filename)
+ free ((char *) abfd->filename);
+ free (abfd->arelt_data);
free (abfd);
}
Return a pointer to the created BFD. If @var{fd} is not -1,
then <<fdopen>> is used to open the file; otherwise, <<fopen>>
is used. @var{mode} is passed directly to <<fopen>> or
- <<fdopen>>.
+ <<fdopen>>.
Calls <<bfd_find_target>>, so @var{target} is interpreted as by
that function.
If <<NULL>> is returned then an error has occured. Possible errors
are <<bfd_error_no_memory>>, <<bfd_error_invalid_target>> or
<<system_call>> error.
+
+ On error, @var{fd} is always closed.
+
+ A copy of the @var{filename} argument is stored in the newly created
+ BFD. It can be accessed via the bfd_get_filename() macro.
*/
bfd *
nbfd = _bfd_new_bfd ();
if (nbfd == NULL)
- return NULL;
+ {
+ if (fd != -1)
+ close (fd);
+ return NULL;
+ }
target_vec = bfd_find_target (target, nbfd);
if (target_vec == NULL)
{
+ if (fd != -1)
+ close (fd);
_bfd_delete_bfd (nbfd);
return NULL;
}
-
+
#ifdef HAVE_FDOPEN
if (fd != -1)
nbfd->iostream = fdopen (fd, mode);
}
/* OK, put everything where it belongs. */
- nbfd->filename = filename;
+
+ /* PR 11983: Do not cache the original filename, but
+ rather make a copy - the original might go away. */
+ nbfd->filename = xstrdup (filename);
/* Figure out whether the user is opening the file for reading,
writing, or both, by looking at the MODE argument. */
- if ((mode[0] == 'r' || mode[0] == 'w' || mode[0] == 'a')
+ if ((mode[0] == 'r' || mode[0] == 'w' || mode[0] == 'a')
&& mode[1] == '+')
nbfd->direction = both_direction;
else if (mode[0] == 'r')
return NULL;
}
nbfd->opened_once = TRUE;
+
/* If we opened the file by name, mark it cacheable; we can close it
and reopen it later. However, if a file descriptor was provided,
then it may have been opened with special flags that make it
unsafe to close and reopen the file. */
if (fd == -1)
- bfd_set_cacheable (nbfd, TRUE);
+ (void) bfd_set_cacheable (nbfd, TRUE);
return nbfd;
}
If <<NULL>> is returned then an error has occured. Possible errors
are <<bfd_error_no_memory>>, <<bfd_error_invalid_target>> or
<<system_call>> error.
+
+ A copy of the @var{filename} argument is stored in the newly created
+ BFD. It can be accessed via the bfd_get_filename() macro.
*/
bfd *
Possible errors are <<bfd_error_no_memory>>,
<<bfd_error_invalid_target>> and <<bfd_error_system_call>>.
+
+ On error, @var{fd} is closed.
+
+ A copy of the @var{filename} argument is stored in the newly created
+ BFD. It can be accessed via the bfd_get_filename() macro.
*/
bfd *
fdflags = fcntl (fd, F_GETFL, NULL);
if (fdflags == -1)
{
+ int save = errno;
+
+ close (fd);
+ errno = save;
bfd_set_error (bfd_error_system_call);
return NULL;
}
bfd_openstreamr
SYNOPSIS
- bfd *bfd_openstreamr (const char *, const char *, void *);
+ bfd *bfd_openstreamr (const char * filename, const char * target, void * stream);
DESCRIPTION
Open a BFD for read access on an existing stdio stream. When
the BFD is passed to <<bfd_close>>, the stream will be closed.
+
+ A copy of the @var{filename} argument is stored in the newly created
+ BFD. It can be accessed via the bfd_get_filename() macro.
*/
bfd *
}
nbfd->iostream = stream;
- nbfd->filename = filename;
+ /* PR 11983: Do not cache the original filename, but
+ rather make a copy - the original might go away. */
+ nbfd->filename = xstrdup (filename);
nbfd->direction = read_direction;
if (! bfd_cache_init (nbfd))
occurred. Possible errors are <<bfd_error_no_memory>>,
<<bfd_error_invalid_target>> and <<bfd_error_system_call>>.
+ A copy of the @var{filename} argument is stored in the newly created
+ BFD. It can be accessed via the bfd_get_filename() macro.
*/
struct opncls
bfd_size_type len ATTRIBUTE_UNUSED,
int prot ATTRIBUTE_UNUSED,
int flags ATTRIBUTE_UNUSED,
- file_ptr offset ATTRIBUTE_UNUSED)
+ file_ptr offset ATTRIBUTE_UNUSED,
+ void **map_addr ATTRIBUTE_UNUSED,
+ bfd_size_type *map_len ATTRIBUTE_UNUSED)
{
return (void *) -1;
}
return NULL;
}
- nbfd->filename = filename;
+ /* PR 11983: Do not cache the original filename, but
+ rather make a copy - the original might go away. */
+ nbfd->filename = xstrdup (filename);
nbfd->direction = read_direction;
/* `open_p (...)' would get expanded by an the open(2) syscall macro. */
Possible errors are <<bfd_error_system_call>>, <<bfd_error_no_memory>>,
<<bfd_error_invalid_target>>.
+
+ A copy of the @var{filename} argument is stored in the newly created
+ BFD. It can be accessed via the bfd_get_filename() macro.
*/
bfd *
return NULL;
}
- nbfd->filename = filename;
+ /* PR 11983: Do not cache the original filename, but
+ rather make a copy - the original might go away. */
+ nbfd->filename = xstrdup (filename);
nbfd->direction = write_direction;
if (bfd_open_file (nbfd) == NULL)
bfd_close (bfd *abfd)
{
bfd_boolean ret;
- bfd *nbfd;
- bfd *next;
if (bfd_write_p (abfd))
{
return FALSE;
}
- /* Close nested archives (if this bfd is a thin archive). */
- for (nbfd = abfd->nested_archives; nbfd; nbfd = next)
- {
- next = nbfd->archive_next;
- bfd_close (nbfd);
- }
-
if (! BFD_SEND (abfd, _close_and_cleanup, (abfd)))
return FALSE;
- ret = abfd->iovec->bclose (abfd);
+ ret = abfd->iovec->bclose (abfd) == 0;
if (ret)
_maybe_make_executable (abfd);
DESCRIPTION
Create a new BFD in the manner of <<bfd_openw>>, but without
opening a file. The new BFD takes the target from the target
- used by @var{template}. The format is always set to <<bfd_object>>.
+ used by @var{templ}. The format is always set to <<bfd_object>>.
+
+ A copy of the @var{filename} argument is stored in the newly created
+ BFD. It can be accessed via the bfd_get_filename() macro.
*/
bfd *
nbfd = _bfd_new_bfd ();
if (nbfd == NULL)
return NULL;
- nbfd->filename = filename;
+ /* PR 11983: Do not cache the original filename, but
+ rather make a copy - the original might go away. */
+ nbfd->filename = xstrdup (filename);
if (templ)
nbfd->xvec = templ->xvec;
nbfd->direction = no_direction;
abfd->section_count = 0;
abfd->usrdata = NULL;
abfd->cacheable = FALSE;
- abfd->flags = BFD_IN_MEMORY;
+ abfd->flags |= BFD_IN_MEMORY;
abfd->mtime_set = FALSE;
abfd->target_defaulted = TRUE;
}
/*
-INTERNAL_FUNCTION
+FUNCTION
bfd_alloc
SYNOPSIS
}
/*
-INTERNAL_FUNCTION
+FUNCTION
bfd_zalloc
SYNOPSIS
This facilitates "optional" provision of debugging information, without
having to provide two complete copies of every binary object (with and
- without debug symbols).
-*/
+ without debug symbols). */
+
+#define GNU_DEBUGLINK ".gnu_debuglink"
+#define GNU_DEBUGALTLINK ".gnu_debugaltlink"
-#define GNU_DEBUGLINK ".gnu_debuglink"
/*
FUNCTION
bfd_calc_gnu_debuglink_crc32
crc = ~crc & 0xffffffff;
for (end = buf + len; buf < end; ++ buf)
crc = crc32_table[(crc ^ *buf) & 0xff] ^ (crc >> 8);
- return ~crc & 0xffffffff;;
+ return ~crc & 0xffffffff;
}
/*
-INTERNAL_FUNCTION
- get_debug_link_info
+FUNCTION
+ bfd_get_debug_link_info
SYNOPSIS
- char *get_debug_link_info (bfd *abfd, unsigned long *crc32_out);
+ char *bfd_get_debug_link_info (bfd *abfd, unsigned long *crc32_out);
DESCRIPTION
- fetch the filename and CRC32 value for any separate debuginfo
- associated with @var{abfd}. Return NULL if no such info found,
- otherwise return filename and update @var{crc32_out}.
+ Fetch the filename and CRC32 value for any separate debuginfo
+ associated with @var{abfd}. Return NULL if no such info found,
+ otherwise return filename and update @var{crc32_out}. The
+ returned filename is allocated with @code{malloc}; freeing it
+ is the responsibility of the caller.
*/
-static char *
-get_debug_link_info (bfd *abfd, unsigned long *crc32_out)
+char *
+bfd_get_debug_link_info (bfd *abfd, unsigned long *crc32_out)
{
asection *sect;
unsigned long crc32;
return name;
}
+/*
+FUNCTION
+ bfd_get_alt_debug_link_info
+
+SYNOPSIS
+ char *bfd_get_alt_debug_link_info (bfd * abfd,
+ bfd_size_type *buildid_len,
+ bfd_byte **buildid_out);
+
+DESCRIPTION
+ Fetch the filename and BuildID value for any alternate debuginfo
+ associated with @var{abfd}. Return NULL if no such info found,
+ otherwise return filename and update @var{buildid_len} and
+ @var{buildid_out}. The returned filename and build_id are
+ allocated with @code{malloc}; freeing them is the
+ responsibility of the caller.
+*/
+
+char *
+bfd_get_alt_debug_link_info (bfd * abfd, bfd_size_type *buildid_len,
+ bfd_byte **buildid_out)
+{
+ asection *sect;
+ bfd_byte *contents;
+ int buildid_offset;
+ char *name;
+
+ BFD_ASSERT (abfd);
+ BFD_ASSERT (buildid_len);
+ BFD_ASSERT (buildid_out);
+
+ sect = bfd_get_section_by_name (abfd, GNU_DEBUGALTLINK);
+
+ if (sect == NULL)
+ return NULL;
+
+ if (!bfd_malloc_and_get_section (abfd, sect, & contents))
+ {
+ if (contents != NULL)
+ free (contents);
+ return NULL;
+ }
+
+ /* BuildID value is stored after the filename. */
+ name = (char *) contents;
+ buildid_offset = strlen (name) + 1;
+
+ *buildid_len = bfd_get_section_size (sect) - buildid_offset;
+ *buildid_out = bfd_malloc (*buildid_len);
+ memcpy (*buildid_out, contents + buildid_offset, *buildid_len);
+
+ return name;
+}
+
/*
INTERNAL_FUNCTION
separate_debug_file_exists
return crc == file_crc;
}
+/*
+INTERNAL_FUNCTION
+ separate_alt_debug_file_exists
+
+SYNOPSIS
+ bfd_boolean separate_alt_debug_file_exists
+ (char *name, unsigned long crc32);
+
+DESCRIPTION
+ Checks to see if @var{name} is a file and if its BuildID
+ matches @var{buildid}.
+*/
+
+static bfd_boolean
+separate_alt_debug_file_exists (const char *name,
+ const unsigned long buildid ATTRIBUTE_UNUSED)
+{
+ FILE *f;
+
+ BFD_ASSERT (name);
+
+ f = real_fopen (name, FOPEN_RB);
+ if (f == NULL)
+ return FALSE;
+
+ /* FIXME: Add code to check buildid. */
+
+ fclose (f);
+
+ return TRUE;
+}
/*
INTERNAL_FUNCTION
char *find_separate_debug_file (bfd *abfd);
DESCRIPTION
- Searches @var{abfd} for a reference to separate debugging
- information, scans various locations in the filesystem, including
- the file tree rooted at @var{debug_file_directory}, and returns a
- filename of such debugging information if the file is found and has
- matching CRC32. Returns NULL if no reference to debugging file
- exists, or file cannot be found.
+ Searches @var{abfd} for a section called @var{section_name} which
+ is expected to contain a reference to a file containing separate
+ debugging information. The function scans various locations in
+ the filesystem, including the file tree rooted at
+ @var{debug_file_directory}, and returns the first matching
+ filename that it finds. If @var{check_crc} is TRUE then the
+ contents of the file must also match the CRC value contained in
+ @var{section_name}. Returns NULL if no valid file could be found.
*/
+typedef char * (* get_func_type) (bfd *, unsigned long *);
+typedef bfd_boolean (* check_func_type) (const char *, const unsigned long);
+
static char *
-find_separate_debug_file (bfd *abfd, const char *debug_file_directory)
+find_separate_debug_file (bfd * abfd,
+ const char * debug_file_directory,
+ get_func_type get_func,
+ check_func_type check_func)
{
char *base;
char *dir;
return NULL;
}
- base = get_debug_link_info (abfd, & crc32);
+ base = get_func (abfd, & crc32);
+
if (base == NULL)
return NULL;
+ strlen (base)
+ 1);
if (debugfile == NULL)
- {
- free (base);
- free (dir);
- free (canon_dir);
- return NULL;
- }
+ goto found; /* Actually this returns NULL. */
/* First try in the same directory as the original file: */
strcpy (debugfile, dir);
strcat (debugfile, base);
- if (separate_debug_file_exists (debugfile, crc32))
- {
- free (base);
- free (dir);
- free (canon_dir);
- return debugfile;
- }
+ if (check_func (debugfile, crc32))
+ goto found;
/* Then try in a subdirectory called .debug. */
strcpy (debugfile, dir);
strcat (debugfile, ".debug/");
strcat (debugfile, base);
- if (separate_debug_file_exists (debugfile, crc32))
- {
- free (base);
- free (dir);
- free (canon_dir);
- return debugfile;
- }
+ if (check_func (debugfile, crc32))
+ goto found;
/* Then try in the global debugfile directory. */
strcpy (debugfile, debug_file_directory);
strcat (debugfile, canon_dir);
strcat (debugfile, base);
- if (separate_debug_file_exists (debugfile, crc32))
- {
- free (base);
- free (dir);
- free (canon_dir);
- return debugfile;
- }
+ if (check_func (debugfile, crc32))
+ goto found;
+ /* Failed to find the file. */
free (debugfile);
+ debugfile = NULL;
+
+ found:
free (base);
free (dir);
free (canon_dir);
- return NULL;
+ return debugfile;
}
char *
bfd_follow_gnu_debuglink (bfd *abfd, const char *dir)
{
- return find_separate_debug_file (abfd, dir);
+ return find_separate_debug_file (abfd, dir,
+ bfd_get_debug_link_info,
+ separate_debug_file_exists);
+}
+
+/* Helper for bfd_follow_gnu_debugaltlink. It just pretends to return
+ a CRC. .gnu_debugaltlink supplies a build-id, which is different,
+ but this is ok because separate_alt_debug_file_exists ignores the
+ CRC anyway. */
+
+static char *
+get_alt_debug_link_info_shim (bfd * abfd, unsigned long *crc32_out)
+{
+ bfd_size_type len;
+ bfd_byte *buildid = NULL;
+ char *result = bfd_get_alt_debug_link_info (abfd, &len, &buildid);
+
+ *crc32_out = 0;
+ free (buildid);
+
+ return result;
+}
+
+/*
+FUNCTION
+ bfd_follow_gnu_debugaltlink
+
+SYNOPSIS
+ char *bfd_follow_gnu_debugaltlink (bfd *abfd, const char *dir);
+
+DESCRIPTION
+
+ Takes a BFD and searches it for a .gnu_debugaltlink section. If this
+ section is found, it examines the section for the name of a file
+ containing auxiliary debugging information. It then searches the
+ filesystem for this file in a set of standard locations, including
+ the directory tree rooted at @var{dir}, and if found returns the
+ full filename.
+
+ If @var{dir} is NULL, it will search a default path configured into
+ libbfd at build time. [FIXME: 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.
+*/
+
+char *
+bfd_follow_gnu_debugaltlink (bfd *abfd, const char *dir)
+{
+ return find_separate_debug_file (abfd, dir,
+ get_alt_debug_link_info_shim,
+ separate_alt_debug_file_exists);
}
/*