/* BFD back-end for archive files (libraries).
- Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
- 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011,
- 2012 Free Software Foundation, Inc.
+ Copyright (C) 1990-2016 Free Software Foundation, Inc.
Written by Cygnus Support. Mostly Gumby Henkel-Wallace's fault.
This file is part of BFD, the Binary File Descriptor library.
have to read the entire archive if you don't want
to! Read it until you find what you want.
+ A BFD returned by <<bfd_openr_next_archived_file>> can be
+ closed manually with <<bfd_close>>. If you do not close it,
+ then a second iteration through the members of an archive may
+ return the same BFD. If you close the archive BFD, then all
+ the member BFDs will automatically be closed as well.
+
Archive contents of output BFDs are chained through the
- <<next>> pointer in a BFD. The first one is findable through
- the <<archive_head>> slot of the archive. Set it with
- <<bfd_set_archive_head>> (q.v.). A given BFD may be in only one
- open output archive at a time.
+ <<archive_next>> pointer in a BFD. The first one is findable
+ through the <<archive_head>> slot of the archive. Set it with
+ <<bfd_set_archive_head>> (q.v.). A given BFD may be in only
+ one open output archive at a time.
As expected, the BFD archive code is more general than the
archive code of any given environment. BFD archives may
#include "safe-ctype.h"
#include "hashtab.h"
#include "filenames.h"
+#include "bfdlink.h"
#ifndef errno
extern int errno;
return TRUE;
}
-/* Free the archive hash table, if it exists. */
-
-void
-_bfd_delete_archive_data (bfd *abfd)
-{
- struct artdata *ardata = bfd_ardata (abfd);
-
- BFD_ASSERT (abfd->format == bfd_archive);
-
- if (ardata && ardata->cache)
- htab_delete (ardata->cache);
-}
-
bfd *
_bfd_look_for_bfd_in_cache (bfd *arch_bfd, file_ptr filepos)
{
struct ar_cache *entry = (struct ar_cache *) htab_find (hash_table, &m);
if (!entry)
return NULL;
- else
- return entry->arbfd;
+
+ /* Unfortunately this flag is set after checking that we have
+ an archive, and checking for an archive means one element has
+ sneaked into the cache. */
+ entry->arbfd->no_export = arch_bfd->no_export;
+ return entry->arbfd;
}
else
return NULL;
cache->arbfd = new_elt;
*htab_find_slot (hash_table, (const void *) cache, INSERT) = cache;
+ /* Provide a means of accessing this from child. */
+ arch_eltdata (new_elt)->parent_cache = hash_table;
+ arch_eltdata (new_elt)->key = filepos;
+
return TRUE;
}
\f
static bfd *
-_bfd_find_nested_archive (bfd *arch_bfd, const char *filename)
+open_nested_file (const char *filename, bfd *archive)
{
- bfd *abfd;
const char *target;
+ bfd *n_bfd;
+
+ target = NULL;
+ if (!archive->target_defaulted)
+ target = archive->xvec->name;
+ n_bfd = bfd_openr (filename, target);
+ if (n_bfd != NULL)
+ {
+ n_bfd->lto_output = archive->lto_output;
+ n_bfd->no_export = archive->no_export;
+ n_bfd->my_archive = archive;
+ }
+ return n_bfd;
+}
+
+static bfd *
+find_nested_archive (const char *filename, bfd *arch_bfd)
+{
+ bfd *abfd;
+
+ /* PR 15140: Don't allow a nested archive pointing to itself. */
+ if (filename_cmp (filename, arch_bfd->filename) == 0)
+ {
+ bfd_set_error (bfd_error_malformed_archive);
+ return NULL;
+ }
for (abfd = arch_bfd->nested_archives;
abfd != NULL;
if (filename_cmp (filename, abfd->filename) == 0)
return abfd;
}
- target = NULL;
- if (!arch_bfd->target_defaulted)
- target = arch_bfd->xvec->name;
- abfd = bfd_openr (filename, target);
+ abfd = open_nested_file (filename, arch_bfd);
if (abfd)
{
abfd->archive_next = arch_bfd->nested_archives;
parsed_size -= namelen;
extra_size = namelen;
- allocptr = (char *) bfd_zalloc (abfd, allocsize);
+ allocptr = (char *) bfd_zmalloc (allocsize);
if (allocptr == NULL)
return NULL;
filename = (allocptr
+ sizeof (struct ar_hdr));
if (bfd_bread (filename, namelen, abfd) != namelen)
{
+ free (allocptr);
if (bfd_get_error () != bfd_error_system_call)
bfd_set_error (bfd_error_no_more_archived_files);
return NULL;
if (!allocptr)
{
- allocptr = (char *) bfd_zalloc (abfd, allocsize);
+ allocptr = (char *) bfd_zmalloc (allocsize);
if (allocptr == NULL)
return NULL;
}
_bfd_get_elt_at_filepos (bfd *archive, file_ptr filepos)
{
struct areltdata *new_areldata;
- bfd *n_nfd;
+ bfd *n_bfd;
char *filename;
- n_nfd = _bfd_look_for_bfd_in_cache (archive, filepos);
- if (n_nfd)
- return n_nfd;
+ n_bfd = _bfd_look_for_bfd_in_cache (archive, filepos);
+ if (n_bfd)
+ return n_bfd;
if (0 > bfd_seek (archive, filepos, SEEK_SET))
return NULL;
if (bfd_is_thin_archive (archive))
{
- const char *target;
-
/* This is a proxy entry for an external file. */
if (! IS_ABSOLUTE_PATH (filename))
{
filename = _bfd_append_relative_path (archive, filename);
if (filename == NULL)
- return NULL;
+ {
+ free (new_areldata);
+ return NULL;
+ }
}
if (new_areldata->origin > 0)
{
/* This proxy entry refers to an element of a nested archive.
Locate the member of that archive and return a bfd for it. */
- bfd *ext_arch = _bfd_find_nested_archive (archive, filename);
+ bfd *ext_arch = find_nested_archive (filename, archive);
if (ext_arch == NULL
|| ! bfd_check_format (ext_arch, bfd_archive))
{
- bfd_release (archive, new_areldata);
+ free (new_areldata);
return NULL;
}
- n_nfd = _bfd_get_elt_at_filepos (ext_arch, new_areldata->origin);
- if (n_nfd == NULL)
+ n_bfd = _bfd_get_elt_at_filepos (ext_arch, new_areldata->origin);
+ if (n_bfd == NULL)
{
- bfd_release (archive, new_areldata);
+ free (new_areldata);
return NULL;
}
- n_nfd->proxy_origin = bfd_tell (archive);
- return n_nfd;
+ n_bfd->proxy_origin = bfd_tell (archive);
+ return n_bfd;
}
+
/* It's not an element of a nested archive;
open the external file as a bfd. */
- target = NULL;
- if (!archive->target_defaulted)
- target = archive->xvec->name;
- n_nfd = bfd_openr (filename, target);
- if (n_nfd == NULL)
+ n_bfd = open_nested_file (filename, archive);
+ if (n_bfd == NULL)
bfd_set_error (bfd_error_malformed_archive);
}
else
{
- n_nfd = _bfd_create_empty_archive_element_shell (archive);
+ n_bfd = _bfd_create_empty_archive_element_shell (archive);
}
- if (n_nfd == NULL)
+ if (n_bfd == NULL)
{
- bfd_release (archive, new_areldata);
+ free (new_areldata);
return NULL;
}
- n_nfd->proxy_origin = bfd_tell (archive);
+ n_bfd->proxy_origin = bfd_tell (archive);
if (bfd_is_thin_archive (archive))
{
- n_nfd->origin = 0;
+ n_bfd->origin = 0;
}
else
{
- n_nfd->origin = n_nfd->proxy_origin;
- n_nfd->filename = filename;
+ n_bfd->origin = n_bfd->proxy_origin;
+ n_bfd->filename = xstrdup (filename);
}
- n_nfd->arelt_data = new_areldata;
+ n_bfd->arelt_data = new_areldata;
+
+ /* Copy BFD_COMPRESS, BFD_DECOMPRESS and BFD_COMPRESS_GABI flags. */
+ n_bfd->flags |= archive->flags & (BFD_COMPRESS
+ | BFD_DECOMPRESS
+ | BFD_COMPRESS_GABI);
- /* Copy BFD_COMPRESS and BFD_DECOMPRESS flags. */
- n_nfd->flags |= archive->flags & (BFD_COMPRESS | BFD_DECOMPRESS);
+ /* Copy is_linker_input. */
+ n_bfd->is_linker_input = archive->is_linker_input;
- if (_bfd_add_bfd_to_archive_cache (archive, filepos, n_nfd))
- return n_nfd;
+ if (_bfd_add_bfd_to_archive_cache (archive, filepos, n_bfd))
+ return n_bfd;
- bfd_release (archive, new_areldata);
+ free (new_areldata);
+ n_bfd->arelt_data = NULL;
return NULL;
}
bfd *
bfd_generic_openr_next_archived_file (bfd *archive, bfd *last_file)
{
- file_ptr filestart;
+ ufile_ptr filestart;
if (!last_file)
filestart = bfd_ardata (archive)->first_file_filepos;
else
{
- bfd_size_type size = arelt_size (last_file);
-
filestart = last_file->proxy_origin;
if (! bfd_is_thin_archive (archive))
- filestart += size;
- /* Pad to an even boundary...
- Note that last_file->origin can be odd in the case of
- BSD-4.4-style element with a long odd size. */
- filestart += filestart % 2;
+ {
+ bfd_size_type size = arelt_size (last_file);
+
+ filestart += size;
+ /* Pad to an even boundary...
+ Note that last_file->origin can be odd in the case of
+ BSD-4.4-style element with a long odd size. */
+ filestart += filestart % 2;
+ if (filestart < last_file->proxy_origin)
+ {
+ /* Prevent looping. See PR19256. */
+ bfd_set_error (bfd_error_malformed_archive);
+ return NULL;
+ }
+ }
}
return _bfd_get_elt_at_filepos (archive, filestart);
first->target_defaulted = FALSE;
if (bfd_check_format (first, bfd_object)
&& first->xvec != abfd->xvec)
- {
- bfd_set_error (bfd_error_wrong_object_format);
- bfd_ardata (abfd) = tdata_hold;
- return NULL;
- }
+ bfd_set_error (bfd_error_wrong_object_format);
/* And we ought to close `first' here too. */
}
}
if (mapdata == NULL)
return FALSE;
parsed_size = mapdata->parsed_size;
- bfd_release (abfd, mapdata); /* Don't need it any more. */
+ free (mapdata);
+ /* PR 17512: file: 883ff754. */
+ /* PR 17512: file: 0458885f. */
+ if (parsed_size < 4)
+ return FALSE;
raw_armap = (bfd_byte *) bfd_zalloc (abfd, parsed_size);
if (raw_armap == NULL)
}
ardata->symdef_count = H_GET_32 (abfd, raw_armap) / BSD_SYMDEF_SIZE;
-
if (ardata->symdef_count * BSD_SYMDEF_SIZE >
parsed_size - BSD_SYMDEF_COUNT_SIZE)
{
if (mapdata == NULL)
return FALSE;
parsed_size = mapdata->parsed_size;
- bfd_release (abfd, mapdata); /* Don't need it any more. */
+ free (mapdata);
if (bfd_bread (int_buf, 4, abfd) != 4)
{
}
/* OK, build the carsyms. */
- for (i = 0; i < nsymz; i++)
+ for (i = 0; i < nsymz && stringsize > 0; i++)
{
+ bfd_size_type len;
+
rawptr = raw_armap + i;
carsyms->file_offset = swap ((bfd_byte *) rawptr);
carsyms->name = stringbase;
- stringbase += strlen (stringbase) + 1;
+ /* PR 17512: file: 4a1d50c1. */
+ len = strnlen (stringbase, stringsize);
+ if (len < stringsize)
+ len ++;
+ stringbase += len;
+ stringsize -= len;
carsyms++;
}
*stringbase = 0;
ardata->first_file_filepos +=
(tmp->parsed_size + sizeof (struct ar_hdr) + 1) & ~(unsigned) 1;
}
- bfd_release (abfd, tmp);
+ free (tmp);
}
}
return do_slurp_coff_armap (abfd);
else if (CONST_STRNEQ (nextname, "/SYM64/ "))
{
- /* 64bit ELF (Irix 6) archive. */
+ /* 64bit (Irix 6) archive. */
#ifdef BFD64
- extern bfd_boolean bfd_elf64_archive_slurp_armap (bfd *);
- return bfd_elf64_archive_slurp_armap (abfd);
+ return _bfd_archive_64_bit_slurp_armap (abfd);
#else
bfd_set_error (bfd_error_wrong_format);
return FALSE;
return FALSE;
if (bfd_seek (abfd, -(file_ptr) (sizeof (hdr) + 20), SEEK_CUR) != 0)
return FALSE;
+ extname[20] = 0;
if (CONST_STRNEQ (extname, "__.SYMDEF SORTED")
|| CONST_STRNEQ (extname, "__.SYMDEF"))
return do_slurp_bsd_armap (abfd);
if (mapdata->parsed_size < HPUX_SYMDEF_COUNT_SIZE + BSD_STRING_COUNT_SIZE)
{
+ free (mapdata);
wrong_format:
bfd_set_error (bfd_error_wrong_format);
byebye:
- bfd_release (abfd, mapdata);
return FALSE;
}
left = mapdata->parsed_size - HPUX_SYMDEF_COUNT_SIZE - BSD_STRING_COUNT_SIZE;
amt = mapdata->parsed_size;
+ free (mapdata);
+
raw_armap = (bfd_byte *) bfd_zalloc (abfd, amt);
if (raw_armap == NULL)
goto byebye;
if (bfd_ardata (abfd)->extended_names == NULL)
{
byebye:
- bfd_release (abfd, namedata);
+ free (namedata);
+ bfd_ardata (abfd)->extended_names = NULL;
+ bfd_ardata (abfd)->extended_names_size = 0;
return FALSE;
}
text, the entries in the list are newline-padded, not null
padded. In SVR4-style archives, the names also have a
trailing '/'. DOS/NT created archive often have \ in them
- We'll fix all problems here.. */
+ We'll fix all problems here. */
{
char *ext_names = bfd_ardata (abfd)->extended_names;
char *temp = ext_names;
char *limit = temp + namedata->parsed_size;
+
for (; temp < limit; ++temp)
{
if (*temp == ARFMAG[1])
bfd_ardata (abfd)->first_file_filepos +=
(bfd_ardata (abfd)->first_file_filepos) % 2;
- /* FIXME, we can't release namedata here because it was allocated
- below extended_names on the objalloc... */
+ free (namedata);
}
return TRUE;
}
}
amt = sizeof (struct ar_hdr) + sizeof (struct areltdata);
- ared = (struct areltdata *) bfd_zalloc (abfd, amt);
+ ared = (struct areltdata *) bfd_zmalloc (amt);
if (ared == NULL)
return NULL;
hdr = (struct ar_hdr *) (((char *) ared) + sizeof (struct areltdata));
}
hdr = arch_hdr (abfd);
-
+ /* PR 17512: file: 3d9e9fe9. */
+ if (hdr == NULL)
+ return -1;
#define foo(arelt, stelt, size) \
buf->stelt = strtol (hdr->arelt, &aloser, size); \
if (aloser == hdr->arelt) \
{
if (bfd_update_armap_timestamp (arch))
break;
- (*_bfd_error_handler)
+ _bfd_error_handler
(_("Warning: writing archive was slow: rewriting timestamp\n"));
}
while (++tries < 6);
map = new_map;
}
+ if (strcmp (syms[src_count]->name, "__gnu_lto_slim") == 0)
+ _bfd_error_handler
+ (_("%s: plugin needed to handle lto object"),
+ bfd_get_filename (current));
namelen = strlen (syms[src_count]->name);
amt = sizeof (char *);
map[orl_count].name = (char **) bfd_alloc (arch, amt);
unsigned int stringsize = stridx + padit;
/* Include 8 bytes to store ranlibsize and stringsize in output. */
unsigned int mapsize = ranlibsize + stringsize + 8;
- file_ptr firstreal;
- bfd *current = arch->archive_head;
- bfd *last_elt = current; /* Last element arch seen. */
+ file_ptr firstreal, first;
+ bfd *current;
+ bfd *last_elt;
bfd_byte temp[4];
unsigned int count;
struct ar_hdr hdr;
long uid, gid;
- file_ptr max_first_real = 1;
- max_first_real <<= 31;
+ first = mapsize + elength + sizeof (struct ar_hdr) + SARMAG;
+
+#ifdef BFD64
+ firstreal = first;
+ current = arch->archive_head;
+ last_elt = current; /* Last element arch seen. */
+ for (count = 0; count < orl_count; count++)
+ {
+ unsigned int offset;
+
+ if (map[count].u.abfd != last_elt)
+ {
+ do
+ {
+ struct areltdata *ared = arch_eltdata (current);
+
+ firstreal += (ared->parsed_size + ared->extra_size
+ + sizeof (struct ar_hdr));
+ firstreal += firstreal % 2;
+ current = current->archive_next;
+ }
+ while (current != map[count].u.abfd);
+ }
+
+ /* The archive file format only has 4 bytes to store the offset
+ of the member. Generate 64-bit archive if an archive is past
+ its 4Gb limit. */
+ offset = (unsigned int) firstreal;
+ if (firstreal != (file_ptr) offset)
+ return _bfd_archive_64_bit_write_armap (arch, elength, map,
+ orl_count, stridx);
- firstreal = mapsize + elength + sizeof (struct ar_hdr) + SARMAG;
+ last_elt = current;
+ }
+#endif
/* If deterministic, we use 0 as the timestamp in the map.
Some linkers may require that the archive filesystem modification
if (bfd_bwrite (temp, sizeof (temp), arch) != sizeof (temp))
return FALSE;
+ firstreal = first;
+ current = arch->archive_head;
+ last_elt = current; /* Last element arch seen. */
for (count = 0; count < orl_count; count++)
{
+ unsigned int offset;
bfd_byte buf[BSD_SYMDEF_SIZE];
if (map[count].u.abfd != last_elt)
/* The archive file format only has 4 bytes to store the offset
of the member. Check to make sure that firstreal has not grown
too big. */
- if (firstreal >= max_first_real)
+ offset = (unsigned int) firstreal;
+ if (firstreal != (file_ptr) offset)
{
bfd_set_error (bfd_error_file_truncated);
return FALSE;
}
-
+
last_elt = current;
H_PUT_32 (arch, map[count].namidx, buf);
H_PUT_32 (arch, firstreal, buf + BSD_SYMDEF_OFFSET_SIZE);
unsigned int stringsize = stridx;
unsigned int mapsize = stringsize + ranlibsize;
file_ptr archive_member_file_ptr;
+ file_ptr first_archive_member_file_ptr;
bfd *current = arch->archive_head;
unsigned int count;
struct ar_hdr hdr;
mapsize++;
/* Work out where the first object file will go in the archive. */
- archive_member_file_ptr = (mapsize
- + elength
- + sizeof (struct ar_hdr)
- + SARMAG);
+ first_archive_member_file_ptr = (mapsize
+ + elength
+ + sizeof (struct ar_hdr)
+ + SARMAG);
+
+#ifdef BFD64
+ current = arch->archive_head;
+ count = 0;
+ archive_member_file_ptr = first_archive_member_file_ptr;
+ while (current != NULL && count < symbol_count)
+ {
+ /* For each symbol which is used defined in this object, write
+ out the object file's address in the archive. */
+
+ while (count < symbol_count && map[count].u.abfd == current)
+ {
+ unsigned int offset = (unsigned int) archive_member_file_ptr;
+
+ /* Generate 64-bit archive if an archive is past its 4Gb
+ limit. */
+ if (archive_member_file_ptr != (file_ptr) offset)
+ return _bfd_archive_64_bit_write_armap (arch, elength, map,
+ symbol_count, stridx);
+ count++;
+ }
+ archive_member_file_ptr += sizeof (struct ar_hdr);
+ if (! bfd_is_thin_archive (arch))
+ {
+ /* Add size of this archive entry. */
+ archive_member_file_ptr += arelt_size (current);
+ /* Remember about the even alignment. */
+ archive_member_file_ptr += archive_member_file_ptr % 2;
+ }
+ current = current->archive_next;
+ }
+#endif
memset (&hdr, ' ', sizeof (struct ar_hdr));
hdr.ar_name[0] = '/';
current = arch->archive_head;
count = 0;
+ archive_member_file_ptr = first_archive_member_file_ptr;
while (current != NULL && count < symbol_count)
{
/* For each symbol which is used defined in this object, write
return TRUE;
}
+
+static int
+archive_close_worker (void **slot, void *inf ATTRIBUTE_UNUSED)
+{
+ struct ar_cache *ent = (struct ar_cache *) *slot;
+
+ bfd_close_all_done (ent->arbfd);
+ return 1;
+}
+
+bfd_boolean
+_bfd_archive_close_and_cleanup (bfd *abfd)
+{
+ if (bfd_read_p (abfd) && abfd->format == bfd_archive)
+ {
+ bfd *nbfd;
+ bfd *next;
+ htab_t htab;
+
+ /* 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);
+ }
+
+ htab = bfd_ardata (abfd)->cache;
+ if (htab)
+ {
+ htab_traverse_noresize (htab, archive_close_worker, NULL);
+ htab_delete (htab);
+ bfd_ardata (abfd)->cache = NULL;
+ }
+ }
+ if (arch_eltdata (abfd) != NULL)
+ {
+ struct areltdata *ared = arch_eltdata (abfd);
+ htab_t htab = (htab_t) ared->parent_cache;
+
+ if (htab)
+ {
+ struct ar_cache ent;
+ void **slot;
+
+ ent.ptr = ared->key;
+ slot = htab_find_slot (htab, &ent, NO_INSERT);
+ if (slot != NULL)
+ {
+ BFD_ASSERT (((struct ar_cache *) *slot)->arbfd == abfd);
+ htab_clear_slot (htab, slot);
+ }
+ }
+ }
+ if (abfd->is_linker_output)
+ (*abfd->link.hash->hash_table_free) (abfd);
+
+ return TRUE;
+}