#include "bfd.h"
#include "sysdep.h"
#include "libiberty.h"
#include "libbfd.h"
#include "aout/ar.h"
#include "aout/ranlib.h"
#include "safe-ctype.h"
#include "hashtab.h"
#ifndef errno
extern int errno;
#endif
struct ar_cache {
file_ptr ptr;
bfd *arbfd;
};
#define ar_padchar(abfd) ((abfd)->xvec->ar_pad_char)
#define ar_maxnamelen(abfd) ((abfd)->xvec->ar_max_namelen)
#define arch_eltdata(bfd) ((struct areltdata *) ((bfd)->arelt_data))
#define arch_hdr(bfd) ((struct ar_hdr *) arch_eltdata(bfd)->arch_header)
void
_bfd_ar_spacepad (char *p, size_t n, const char *fmt, long val)
{
static char buf[20];
size_t len;
snprintf (buf, sizeof (buf), fmt, val);
len = strlen (buf);
if (len < n)
{
memcpy (p, buf, len);
memset (p + len, ' ', n - len);
}
else
memcpy (p, buf, n);
}
bfd_boolean
_bfd_generic_mkarchive (bfd *abfd)
{
bfd_size_type amt = sizeof (struct artdata);
abfd->tdata.aout_ar_data = bfd_zalloc (abfd, amt);
if (bfd_ardata (abfd) == NULL)
return FALSE;
return TRUE;
}
symindex
bfd_get_next_mapent (bfd *abfd, symindex prev, carsym **entry)
{
if (!bfd_has_map (abfd))
{
bfd_set_error (bfd_error_invalid_operation);
return BFD_NO_MORE_SYMBOLS;
}
if (prev == BFD_NO_MORE_SYMBOLS)
prev = 0;
else
++prev;
if (prev >= bfd_ardata (abfd)->symdef_count)
return BFD_NO_MORE_SYMBOLS;
*entry = (bfd_ardata (abfd)->symdefs + prev);
return prev;
}
bfd *
_bfd_create_empty_archive_element_shell (bfd *obfd)
{
return _bfd_new_bfd_contained_in (obfd);
}
bfd_boolean
bfd_set_archive_head (bfd *output_archive, bfd *new_head)
{
output_archive->archive_head = new_head;
return TRUE;
}
bfd *
_bfd_look_for_bfd_in_cache (bfd *arch_bfd, file_ptr filepos)
{
htab_t hash_table = bfd_ardata (arch_bfd)->cache;
struct ar_cache m;
m.ptr = filepos;
if (hash_table)
{
struct ar_cache *entry = (struct ar_cache *) htab_find (hash_table, &m);
if (!entry)
return NULL;
else
return entry->arbfd;
}
else
return NULL;
}
static int
_bfd_clear_archive_entry (void **slot, void *htab)
{
struct ar_cache *entry = (struct ar_cache *) *slot;
if (entry != NULL && entry->arbfd != NULL)
{
bfd_close (entry->arbfd);
htab_clear_slot (htab, slot);
}
return 1;
}
void
bfd_archive_free_cached_info (bfd *archive)
{
htab_t hash_table = bfd_ardata (archive)->cache;
htab_traverse_noresize (hash_table, _bfd_clear_archive_entry, hash_table);
}
static hashval_t
hash_file_ptr (const PTR p)
{
return (hashval_t) (((struct ar_cache *) p)->ptr);
}
static int
eq_file_ptr (const PTR p1, const PTR p2)
{
struct ar_cache *arc1 = (struct ar_cache *) p1;
struct ar_cache *arc2 = (struct ar_cache *) p2;
return arc1->ptr == arc2->ptr;
}
bfd_boolean
_bfd_add_bfd_to_archive_cache (bfd *arch_bfd, file_ptr filepos, bfd *new_elt)
{
struct ar_cache *cache;
htab_t hash_table = bfd_ardata (arch_bfd)->cache;
if (hash_table == NULL)
{
hash_table = htab_create_alloc (16, hash_file_ptr, eq_file_ptr,
NULL, calloc, free);
if (hash_table == NULL)
return FALSE;
bfd_ardata (arch_bfd)->cache = hash_table;
}
cache = bfd_zalloc (arch_bfd, sizeof (struct ar_cache));
cache->ptr = filepos;
cache->arbfd = new_elt;
*htab_find_slot (hash_table, (const void *) cache, INSERT) = cache;
return TRUE;
}
static char *
get_extended_arelt_filename (bfd *arch, const char *name)
{
unsigned long index = 0;
errno = 0;
index = strtol (name + 1, NULL, 10);
if (errno != 0 || index >= bfd_ardata (arch)->extended_names_size)
{
bfd_set_error (bfd_error_malformed_archive);
return NULL;
}
return bfd_ardata (arch)->extended_names + index;
}
void *
_bfd_generic_read_ar_hdr (bfd *abfd)
{
return _bfd_generic_read_ar_hdr_mag (abfd, NULL);
}
void *
_bfd_generic_read_ar_hdr_mag (bfd *abfd, const char *mag)
{
struct ar_hdr hdr;
char *hdrp = (char *) &hdr;
size_t parsed_size;
struct areltdata *ared;
char *filename = NULL;
bfd_size_type namelen = 0;
bfd_size_type allocsize = sizeof (struct areltdata) + sizeof (struct ar_hdr);
char *allocptr = 0;
if (bfd_bread (hdrp, sizeof (struct ar_hdr), abfd) != sizeof (struct ar_hdr))
{
if (bfd_get_error () != bfd_error_system_call)
bfd_set_error (bfd_error_no_more_archived_files);
return NULL;
}
if (strncmp (hdr.ar_fmag, ARFMAG, 2) != 0
&& (mag == NULL
|| strncmp (hdr.ar_fmag, mag, 2) != 0))
{
bfd_set_error (bfd_error_malformed_archive);
return NULL;
}
errno = 0;
parsed_size = strtol (hdr.ar_size, NULL, 10);
if (errno != 0)
{
bfd_set_error (bfd_error_malformed_archive);
return NULL;
}
if ((hdr.ar_name[0] == '/'
|| (hdr.ar_name[0] == ' '
&& memchr (hdr.ar_name, '/', ar_maxnamelen (abfd)) == NULL))
&& bfd_ardata (abfd)->extended_names != NULL)
{
filename = get_extended_arelt_filename (abfd, hdr.ar_name);
if (filename == NULL)
return NULL;
}
else if (hdr.ar_name[0] == '#'
&& hdr.ar_name[1] == '1'
&& hdr.ar_name[2] == '/'
&& ISDIGIT (hdr.ar_name[3]))
{
namelen = atoi (&hdr.ar_name[3]);
allocsize += namelen + 1;
parsed_size -= namelen;
allocptr = bfd_zalloc (abfd, allocsize);
if (allocptr == NULL)
return NULL;
filename = (allocptr
+ sizeof (struct areltdata)
+ sizeof (struct ar_hdr));
if (bfd_bread (filename, namelen, abfd) != namelen)
{
if (bfd_get_error () != bfd_error_system_call)
bfd_set_error (bfd_error_no_more_archived_files);
return NULL;
}
filename[namelen] = '\0';
}
else
{
char *e;
e = memchr (hdr.ar_name, '\0', ar_maxnamelen (abfd));
if (e == NULL)
{
e = memchr (hdr.ar_name, '/', ar_maxnamelen (abfd));
if (e == NULL)
e = memchr (hdr.ar_name, ' ', ar_maxnamelen (abfd));
}
if (e != NULL)
namelen = e - hdr.ar_name;
else
{
namelen = ar_maxnamelen (abfd);
}
allocsize += namelen + 1;
}
if (!allocptr)
{
allocptr = bfd_zalloc (abfd, allocsize);
if (allocptr == NULL)
return NULL;
}
ared = (struct areltdata *) allocptr;
ared->arch_header = allocptr + sizeof (struct areltdata);
memcpy (ared->arch_header, &hdr, sizeof (struct ar_hdr));
ared->parsed_size = parsed_size;
if (filename != NULL)
ared->filename = filename;
else
{
ared->filename = allocptr + (sizeof (struct areltdata) +
sizeof (struct ar_hdr));
if (namelen)
memcpy (ared->filename, hdr.ar_name, namelen);
ared->filename[namelen] = '\0';
}
return ared;
}
bfd *
_bfd_get_elt_at_filepos (bfd *archive, file_ptr filepos)
{
struct areltdata *new_areldata;
bfd *n_nfd;
#if 0
if (archive->my_archive)
{
filepos += archive->origin;
archive = archive->my_archive;
}
#endif
n_nfd = _bfd_look_for_bfd_in_cache (archive, filepos);
if (n_nfd)
{
#ifdef BFD_TRACK_OPEN_CLOSE
printf ("Opening 0x%lx from cache of archive 0x%lx: \"%s\"\n",
(unsigned long) n_nfd,
(unsigned long) archive,
n_nfd->filename);
#endif
return n_nfd;
}
if (0 > bfd_seek (archive, filepos, SEEK_SET))
return NULL;
if ((new_areldata = _bfd_read_ar_hdr (archive)) == NULL)
return NULL;
n_nfd = _bfd_create_empty_archive_element_shell (archive);
if (n_nfd == NULL)
{
bfd_release (archive, new_areldata);
return NULL;
}
n_nfd->origin = bfd_tell (archive);
n_nfd->arelt_data = new_areldata;
n_nfd->filename = new_areldata->filename;
if (_bfd_add_bfd_to_archive_cache (archive, filepos, n_nfd))
{
#ifdef BFD_TRACK_OPEN_CLOSE
printf ("Opening 0x%lx from archive 0x%lx: \"%s\"\n",
(unsigned long) n_nfd,
(unsigned long) archive,
n_nfd->filename);
#endif
return n_nfd;
}
bfd_release (archive, n_nfd);
bfd_release (archive, new_areldata);
return NULL;
}
bfd *
_bfd_generic_get_elt_at_index (bfd *abfd, symindex index)
{
carsym *entry;
entry = bfd_ardata (abfd)->symdefs + index;
return _bfd_get_elt_at_filepos (abfd, entry->file_offset);
}
bfd *
bfd_openr_next_archived_file (bfd *archive, bfd *last_file)
{
if ((bfd_get_format (archive) != bfd_archive) ||
(archive->direction == write_direction))
{
bfd_set_error (bfd_error_invalid_operation);
return NULL;
}
return BFD_SEND (archive,
openr_next_archived_file, (archive, last_file));
}
bfd *
bfd_generic_openr_next_archived_file (bfd *archive, bfd *last_file)
{
file_ptr filestart;
if (!last_file)
filestart = bfd_ardata (archive)->first_file_filepos;
else
{
unsigned int size = arelt_size (last_file);
filestart = last_file->origin + size;
#if 0
if (archive->my_archive)
filestart -= archive->origin;
#endif
filestart += filestart % 2;
}
return _bfd_get_elt_at_filepos (archive, filestart);
}
const bfd_target *
bfd_generic_archive_p (bfd *abfd)
{
struct artdata *tdata_hold;
char armag[SARMAG + 1];
bfd_size_type amt;
if (bfd_bread (armag, SARMAG, abfd) != SARMAG)
{
if (bfd_get_error () != bfd_error_system_call)
bfd_set_error (bfd_error_wrong_format);
return NULL;
}
if (strncmp (armag, ARMAG, SARMAG) != 0 &&
strncmp (armag, ARMAGB, SARMAG) != 0)
return 0;
tdata_hold = bfd_ardata (abfd);
amt = sizeof (struct artdata);
bfd_ardata (abfd) = bfd_zalloc (abfd, amt);
if (bfd_ardata (abfd) == NULL)
{
bfd_ardata (abfd) = tdata_hold;
return NULL;
}
bfd_ardata (abfd)->first_file_filepos = SARMAG;
if (!BFD_SEND (abfd, _bfd_slurp_armap, (abfd))
|| !BFD_SEND (abfd, _bfd_slurp_extended_name_table, (abfd)))
{
if (bfd_get_error () != bfd_error_system_call)
bfd_set_error (bfd_error_wrong_format);
bfd_release (abfd, bfd_ardata (abfd));
bfd_ardata (abfd) = tdata_hold;
return NULL;
}
if (bfd_has_map (abfd))
{
bfd *first;
first = bfd_openr_next_archived_file (abfd, NULL);
if (first != NULL)
{
bfd_boolean fail;
first->target_defaulted = FALSE;
fail = 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;
}
}
}
return abfd->xvec;
}
#define BSD_SYMDEF_SIZE 8
#define BSD_SYMDEF_OFFSET_SIZE 4
#define BSD_SYMDEF_COUNT_SIZE 4
#define BSD_STRING_COUNT_SIZE 4
static bfd_boolean
do_slurp_bsd_armap (bfd *abfd)
{
struct areltdata *mapdata;
unsigned int counter;
bfd_byte *raw_armap, *rbase;
struct artdata *ardata = bfd_ardata (abfd);
char *stringbase;
bfd_size_type parsed_size, amt;
carsym *set;
mapdata = _bfd_read_ar_hdr (abfd);
if (mapdata == NULL)
return FALSE;
parsed_size = mapdata->parsed_size;
bfd_release (abfd, mapdata);
raw_armap = bfd_zalloc (abfd, parsed_size);
if (raw_armap == NULL)
return FALSE;
if (bfd_bread (raw_armap, parsed_size, abfd) != parsed_size)
{
if (bfd_get_error () != bfd_error_system_call)
bfd_set_error (bfd_error_malformed_archive);
byebye:
bfd_release (abfd, raw_armap);
return FALSE;
}
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)
{
bfd_set_error (bfd_error_wrong_format);
goto byebye;
}
ardata->cache = 0;
rbase = raw_armap + BSD_SYMDEF_COUNT_SIZE;
stringbase = ((char *) rbase
+ ardata->symdef_count * BSD_SYMDEF_SIZE
+ BSD_STRING_COUNT_SIZE);
amt = ardata->symdef_count * sizeof (carsym);
ardata->symdefs = bfd_alloc (abfd, amt);
if (!ardata->symdefs)
return FALSE;
for (counter = 0, set = ardata->symdefs;
counter < ardata->symdef_count;
counter++, set++, rbase += BSD_SYMDEF_SIZE)
{
set->name = H_GET_32 (abfd, rbase) + stringbase;
set->file_offset = H_GET_32 (abfd, rbase + BSD_SYMDEF_OFFSET_SIZE);
}
ardata->first_file_filepos = bfd_tell (abfd);
ardata->first_file_filepos += (ardata->first_file_filepos) % 2;
bfd_has_map (abfd) = TRUE;
return TRUE;
}
static bfd_boolean
do_slurp_coff_armap (bfd *abfd)
{
struct areltdata *mapdata;
int *raw_armap, *rawptr;
struct artdata *ardata = bfd_ardata (abfd);
char *stringbase;
bfd_size_type stringsize;
unsigned int parsed_size;
carsym *carsyms;
bfd_size_type nsymz;
bfd_vma (*swap) (const void *);
char int_buf[sizeof (long)];
bfd_size_type carsym_size, ptrsize;
unsigned int i;
mapdata = _bfd_read_ar_hdr (abfd);
if (mapdata == NULL)
return FALSE;
parsed_size = mapdata->parsed_size;
bfd_release (abfd, mapdata);
if (bfd_bread (int_buf, 4, abfd) != 4)
{
if (bfd_get_error () != bfd_error_system_call)
bfd_set_error (bfd_error_malformed_archive);
return FALSE;
}
swap = bfd_getb32;
nsymz = bfd_getb32 (int_buf);
stringsize = parsed_size - (4 * nsymz) - 4;
if (stringsize > 0xfffff
&& bfd_get_arch (abfd) == bfd_arch_i960
&& bfd_get_flavour (abfd) == bfd_target_coff_flavour)
{
nsymz = bfd_getl32 (int_buf);
stringsize = parsed_size - (4 * nsymz) - 4;
swap = bfd_getl32;
}
if (nsymz > ~ (bfd_size_type) 0 / sizeof (carsym))
return FALSE;
carsym_size = (nsymz * sizeof (carsym));
ptrsize = (4 * nsymz);
if (carsym_size + stringsize + 1 <= carsym_size)
return FALSE;
ardata->symdefs = bfd_zalloc (abfd, carsym_size + stringsize + 1);
if (ardata->symdefs == NULL)
return FALSE;
carsyms = ardata->symdefs;
stringbase = ((char *) ardata->symdefs) + carsym_size;
raw_armap = bfd_alloc (abfd, ptrsize);
if (raw_armap == NULL)
goto release_symdefs;
if (bfd_bread (raw_armap, ptrsize, abfd) != ptrsize
|| (bfd_bread (stringbase, stringsize, abfd) != stringsize))
{
if (bfd_get_error () != bfd_error_system_call)
bfd_set_error (bfd_error_malformed_archive);
goto release_raw_armap;
}
for (i = 0; i < nsymz; i++)
{
rawptr = raw_armap + i;
carsyms->file_offset = swap ((bfd_byte *) rawptr);
carsyms->name = stringbase;
stringbase += strlen (stringbase) + 1;
carsyms++;
}
*stringbase = 0;
ardata->symdef_count = nsymz;
ardata->first_file_filepos = bfd_tell (abfd);
ardata->first_file_filepos += (ardata->first_file_filepos) % 2;
bfd_has_map (abfd) = TRUE;
bfd_release (abfd, raw_armap);
{
struct areltdata *tmp;
bfd_seek (abfd, ardata->first_file_filepos, SEEK_SET);
tmp = _bfd_read_ar_hdr (abfd);
if (tmp != NULL)
{
if (tmp->arch_header[0] == '/'
&& tmp->arch_header[1] == ' ')
{
ardata->first_file_filepos +=
(tmp->parsed_size + sizeof (struct ar_hdr) + 1) & ~(unsigned) 1;
}
bfd_release (abfd, tmp);
}
}
return TRUE;
release_raw_armap:
bfd_release (abfd, raw_armap);
release_symdefs:
bfd_release (abfd, (ardata)->symdefs);
return FALSE;
}
bfd_boolean
bfd_slurp_armap (bfd *abfd)
{
char nextname[17];
int i = bfd_bread (nextname, 16, abfd);
if (i == 0)
return TRUE;
if (i != 16)
return FALSE;
if (bfd_seek (abfd, (file_ptr) -16, SEEK_CUR) != 0)
return FALSE;
if (!strncmp (nextname, "__.SYMDEF ", 16)
|| !strncmp (nextname, "__.SYMDEF/ ", 16))
return do_slurp_bsd_armap (abfd);
else if (!strncmp (nextname, "/ ", 16))
return do_slurp_coff_armap (abfd);
else if (!strncmp (nextname, "/SYM64/ ", 16))
{
#ifdef BFD64
extern bfd_boolean bfd_elf64_archive_slurp_armap (bfd *);
return bfd_elf64_archive_slurp_armap (abfd);
#else
bfd_set_error (bfd_error_wrong_format);
return FALSE;
#endif
}
bfd_has_map (abfd) = FALSE;
return TRUE;
}
#define HPUX_SYMDEF_COUNT_SIZE 2
bfd_boolean
bfd_slurp_bsd_armap_f2 (bfd *abfd)
{
struct areltdata *mapdata;
char nextname[17];
unsigned int counter;
bfd_byte *raw_armap, *rbase;
struct artdata *ardata = bfd_ardata (abfd);
char *stringbase;
unsigned int stringsize;
bfd_size_type amt;
carsym *set;
int i = bfd_bread (nextname, 16, abfd);
if (i == 0)
return TRUE;
if (i != 16)
return FALSE;
if (bfd_seek (abfd, (file_ptr) -16, SEEK_CUR) != 0)
return FALSE;
if (!strncmp (nextname, "__.SYMDEF ", 16)
|| !strncmp (nextname, "__.SYMDEF/ ", 16))
return do_slurp_bsd_armap (abfd);
if (strncmp (nextname, "/ ", 16))
{
bfd_has_map (abfd) = FALSE;
return TRUE;
}
mapdata = _bfd_read_ar_hdr (abfd);
if (mapdata == NULL)
return FALSE;
amt = mapdata->parsed_size;
raw_armap = bfd_zalloc (abfd, amt);
if (raw_armap == NULL)
{
byebye:
bfd_release (abfd, mapdata);
return FALSE;
}
if (bfd_bread (raw_armap, amt, abfd) != amt)
{
if (bfd_get_error () != bfd_error_system_call)
bfd_set_error (bfd_error_malformed_archive);
byebyebye:
bfd_release (abfd, raw_armap);
goto byebye;
}
ardata->symdef_count = H_GET_16 (abfd, raw_armap);
if (ardata->symdef_count * BSD_SYMDEF_SIZE
> mapdata->parsed_size - HPUX_SYMDEF_COUNT_SIZE)
{
bfd_set_error (bfd_error_wrong_format);
goto byebyebye;
}
ardata->cache = 0;
stringsize = H_GET_32 (abfd, raw_armap + HPUX_SYMDEF_COUNT_SIZE);
stringbase = ((char *) raw_armap
+ HPUX_SYMDEF_COUNT_SIZE
+ BSD_STRING_COUNT_SIZE);
rbase = (bfd_byte *) stringbase + stringsize;
amt = ardata->symdef_count * BSD_SYMDEF_SIZE;
ardata->symdefs = bfd_alloc (abfd, amt);
if (!ardata->symdefs)
return FALSE;
for (counter = 0, set = ardata->symdefs;
counter < ardata->symdef_count;
counter++, set++, rbase += BSD_SYMDEF_SIZE)
{
set->name = H_GET_32 (abfd, rbase) + stringbase;
set->file_offset = H_GET_32 (abfd, rbase + BSD_SYMDEF_OFFSET_SIZE);
}
ardata->first_file_filepos = bfd_tell (abfd);
ardata->first_file_filepos += (ardata->first_file_filepos) % 2;
bfd_has_map (abfd) = TRUE;
return TRUE;
}
bfd_boolean
_bfd_slurp_extended_name_table (bfd *abfd)
{
char nextname[17];
struct areltdata *namedata;
bfd_size_type amt;
bfd_seek (abfd, bfd_ardata (abfd)->first_file_filepos, SEEK_SET);
if (bfd_bread (nextname, 16, abfd) == 16)
{
if (bfd_seek (abfd, (file_ptr) -16, SEEK_CUR) != 0)
return FALSE;
if (strncmp (nextname, "ARFILENAMES/ ", 16) != 0 &&
strncmp (nextname, "// ", 16) != 0)
{
bfd_ardata (abfd)->extended_names = NULL;
bfd_ardata (abfd)->extended_names_size = 0;
return TRUE;
}
namedata = _bfd_read_ar_hdr (abfd);
if (namedata == NULL)
return FALSE;
amt = namedata->parsed_size;
if (amt + 1 == 0)
goto byebye;
bfd_ardata (abfd)->extended_names_size = amt;
bfd_ardata (abfd)->extended_names = bfd_zalloc (abfd, amt + 1);
if (bfd_ardata (abfd)->extended_names == NULL)
{
byebye:
bfd_release (abfd, namedata);
return FALSE;
}
if (bfd_bread (bfd_ardata (abfd)->extended_names, amt, abfd) != amt)
{
if (bfd_get_error () != bfd_error_system_call)
bfd_set_error (bfd_error_malformed_archive);
bfd_release (abfd, (bfd_ardata (abfd)->extended_names));
bfd_ardata (abfd)->extended_names = NULL;
goto byebye;
}
{
char *ext_names = bfd_ardata (abfd)->extended_names;
char *temp = ext_names;
char *limit = temp + namedata->parsed_size;
for (; temp < limit; ++temp)
{
if (*temp == '\012')
temp[temp > ext_names && temp[-1] == '/' ? -1 : 0] = '\0';
if (*temp == '\\')
*temp = '/';
}
*limit = '\0';
}
bfd_ardata (abfd)->first_file_filepos = bfd_tell (abfd);
bfd_ardata (abfd)->first_file_filepos +=
(bfd_ardata (abfd)->first_file_filepos) % 2;
}
return TRUE;
}
#ifdef VMS
static const char *
normalize (bfd *abfd, const char *file)
{
const char *first;
const char *last;
char *copy;
first = file + strlen (file) - 1;
last = first + 1;
while (first != file)
{
if (*first == ';')
last = first;
if (*first == ':' || *first == ']' || *first == '>')
{
first++;
break;
}
first--;
}
copy = bfd_alloc (abfd, last - first + 1);
if (copy == NULL)
return NULL;
memcpy (copy, first, last - first);
copy[last - first] = 0;
return copy;
}
#else
static const char *
normalize (bfd *abfd ATTRIBUTE_UNUSED, const char *file)
{
const char *filename = strrchr (file, '/');
#ifdef HAVE_DOS_BASED_FILE_SYSTEM
{
char *bslash = strrchr (file, '\\');
if (filename == NULL || (bslash != NULL && bslash > filename))
filename = bslash;
if (filename == NULL && file[0] != '\0' && file[1] == ':')
filename = file + 1;
}
#endif
if (filename != NULL)
filename++;
else
filename = file;
return filename;
}
#endif
bfd_boolean
_bfd_archive_bsd_construct_extended_name_table (bfd *abfd,
char **tabloc,
bfd_size_type *tablen,
const char **name)
{
*name = "ARFILENAMES/";
return _bfd_construct_extended_name_table (abfd, FALSE, tabloc, tablen);
}
bfd_boolean
_bfd_archive_coff_construct_extended_name_table (bfd *abfd,
char **tabloc,
bfd_size_type *tablen,
const char **name)
{
*name = "//";
return _bfd_construct_extended_name_table (abfd, TRUE, tabloc, tablen);
}
bfd_boolean
_bfd_construct_extended_name_table (bfd *abfd,
bfd_boolean trailing_slash,
char **tabloc,
bfd_size_type *tablen)
{
unsigned int maxname = abfd->xvec->ar_max_namelen;
bfd_size_type total_namelen = 0;
bfd *current;
char *strptr;
*tablen = 0;
for (current = abfd->archive_head; current != NULL; current = current->next)
{
const char *normal;
unsigned int thislen;
normal = normalize (current, current->filename);
if (normal == NULL)
return FALSE;
thislen = strlen (normal);
if (thislen > maxname
&& (bfd_get_file_flags (abfd) & BFD_TRADITIONAL_FORMAT) != 0)
thislen = maxname;
if (thislen > maxname)
{
total_namelen += thislen + 1;
if (trailing_slash)
{
++total_namelen;
}
}
else
{
struct ar_hdr *hdr = arch_hdr (current);
if (strncmp (normal, hdr->ar_name, thislen) != 0
|| (thislen < sizeof hdr->ar_name
&& hdr->ar_name[thislen] != ar_padchar (current)))
{
memcpy (hdr->ar_name, normal, thislen);
if (thislen < maxname
|| (thislen == maxname && thislen < sizeof hdr->ar_name))
hdr->ar_name[thislen] = ar_padchar (current);
}
}
}
if (total_namelen == 0)
return TRUE;
*tabloc = bfd_zalloc (abfd, total_namelen);
if (*tabloc == NULL)
return FALSE;
*tablen = total_namelen;
strptr = *tabloc;
for (current = abfd->archive_head; current != NULL; current =
current->next)
{
const char *normal;
unsigned int thislen;
normal = normalize (current, current->filename);
if (normal == NULL)
return FALSE;
thislen = strlen (normal);
if (thislen > maxname)
{
struct ar_hdr *hdr = arch_hdr (current);
strcpy (strptr, normal);
if (! trailing_slash)
strptr[thislen] = '\012';
else
{
strptr[thislen] = '/';
strptr[thislen + 1] = '\012';
}
hdr->ar_name[0] = ar_padchar (current);
_bfd_ar_spacepad (hdr->ar_name + 1, maxname - 1, "%-ld",
strptr - *tabloc);
strptr += thislen + 1;
if (trailing_slash)
++strptr;
}
}
return TRUE;
}
#ifdef HPUX_LARGE_AR_IDS
static void
hpux_uid_gid_encode (char str[6], long int id)
{
int cnt;
str[5] = '@' + (id & 3);
id >>= 2;
for (cnt = 4; cnt >= 0; ++cnt, id >>= 6)
str[cnt] = ' ' + (id & 0x3f);
}
#endif
#ifndef HAVE_GETUID
#define getuid() 0
#endif
#ifndef HAVE_GETGID
#define getgid() 0
#endif
static struct areltdata *
bfd_ar_hdr_from_filesystem (bfd *abfd, const char *filename, bfd *member)
{
struct stat status;
struct areltdata *ared;
struct ar_hdr *hdr;
bfd_size_type amt;
if (member && (member->flags & BFD_IN_MEMORY) != 0)
{
struct bfd_in_memory *bim = member->iostream;
time (&status.st_mtime);
status.st_uid = getuid ();
status.st_gid = getgid ();
status.st_mode = 0644;
status.st_size = bim->size;
}
else if (stat (filename, &status) != 0)
{
bfd_set_error (bfd_error_system_call);
return NULL;
}
amt = sizeof (struct ar_hdr) + sizeof (struct areltdata);
ared = bfd_zalloc (abfd, amt);
if (ared == NULL)
return NULL;
hdr = (struct ar_hdr *) (((char *) ared) + sizeof (struct areltdata));
memset (hdr, ' ', sizeof (struct ar_hdr));
_bfd_ar_spacepad (hdr->ar_date, sizeof (hdr->ar_date), "%-12ld",
status.st_mtime);
#ifdef HPUX_LARGE_AR_IDS
if (status.st_uid > 99999)
hpux_uid_gid_encode (hdr->ar_uid, (long) status.st_uid);
else
#endif
_bfd_ar_spacepad (hdr->ar_uid, sizeof (hdr->ar_uid), "%ld",
status.st_uid);
#ifdef HPUX_LARGE_AR_IDS
if (status.st_gid > 99999)
hpux_uid_gid_encode (hdr->ar_gid, (long) status.st_gid);
else
#endif
_bfd_ar_spacepad (hdr->ar_gid, sizeof (hdr->ar_gid), "%ld",
status.st_gid);
_bfd_ar_spacepad (hdr->ar_mode, sizeof (hdr->ar_mode), "%-8lo",
status.st_mode);
_bfd_ar_spacepad (hdr->ar_size, sizeof (hdr->ar_size), "%-10ld",
status.st_size);
memcpy (hdr->ar_fmag, ARFMAG, 2);
ared->parsed_size = status.st_size;
ared->arch_header = (char *) hdr;
return ared;
}
struct ar_hdr *bfd_special_undocumented_glue (bfd *, const char *);
struct ar_hdr *
bfd_special_undocumented_glue (bfd *abfd, const char *filename)
{
struct areltdata *ar_elt = bfd_ar_hdr_from_filesystem (abfd, filename, 0);
if (ar_elt == NULL)
return NULL;
return (struct ar_hdr *) ar_elt->arch_header;
}
int
bfd_generic_stat_arch_elt (bfd *abfd, struct stat *buf)
{
struct ar_hdr *hdr;
char *aloser;
if (abfd->arelt_data == NULL)
{
bfd_set_error (bfd_error_invalid_operation);
return -1;
}
hdr = arch_hdr (abfd);
#define foo(arelt, stelt, size) \
buf->stelt = strtol (hdr->arelt, &aloser, size); \
if (aloser == hdr->arelt) \
return -1;
#ifdef HPUX_LARGE_AR_IDS
# define foo2(arelt, stelt, size) \
if (hdr->arelt[5] == ' ') \
{ \
foo (arelt, stelt, size); \
} \
else \
{ \
int cnt; \
for (buf->stelt = cnt = 0; cnt < 5; ++cnt) \
{ \
if (hdr->arelt[cnt] < ' ' || hdr->arelt[cnt] > ' ' + 0x3f) \
return -1; \
buf->stelt <<= 6; \
buf->stelt += hdr->arelt[cnt] - ' '; \
} \
if (hdr->arelt[5] < '@' || hdr->arelt[5] > '@' + 3) \
return -1; \
buf->stelt <<= 2; \
buf->stelt += hdr->arelt[5] - '@'; \
}
#else
# define foo2(arelt, stelt, size) foo (arelt, stelt, size)
#endif
foo (ar_date, st_mtime, 10);
foo2 (ar_uid, st_uid, 10);
foo2 (ar_gid, st_gid, 10);
foo (ar_mode, st_mode, 8);
buf->st_size = arch_eltdata (abfd)->parsed_size;
return 0;
}
void
bfd_dont_truncate_arname (bfd *abfd, const char *pathname, char *arhdr)
{
struct ar_hdr *hdr = (struct ar_hdr *) arhdr;
size_t length;
const char *filename;
size_t maxlen = ar_maxnamelen (abfd);
if ((bfd_get_file_flags (abfd) & BFD_TRADITIONAL_FORMAT) != 0)
{
bfd_bsd_truncate_arname (abfd, pathname, arhdr);
return;
}
filename = normalize (abfd, pathname);
if (filename == NULL)
{
abort ();
}
length = strlen (filename);
if (length <= maxlen)
memcpy (hdr->ar_name, filename, length);
if (length < maxlen
|| (length == maxlen && length < sizeof hdr->ar_name))
(hdr->ar_name)[length] = ar_padchar (abfd);
}
void
bfd_bsd_truncate_arname (bfd *abfd, const char *pathname, char *arhdr)
{
struct ar_hdr *hdr = (struct ar_hdr *) arhdr;
size_t length;
const char *filename = strrchr (pathname, '/');
size_t maxlen = ar_maxnamelen (abfd);
#ifdef HAVE_DOS_BASED_FILE_SYSTEM
{
char *bslash = strrchr (pathname, '\\');
if (filename == NULL || (bslash != NULL && bslash > filename))
filename = bslash;
if (filename == NULL && pathname[0] != '\0' && pathname[1] == ':')
filename = pathname + 1;
}
#endif
if (filename == NULL)
filename = pathname;
else
++filename;
length = strlen (filename);
if (length <= maxlen)
memcpy (hdr->ar_name, filename, length);
else
{
memcpy (hdr->ar_name, filename, maxlen);
length = maxlen;
}
if (length < maxlen)
(hdr->ar_name)[length] = ar_padchar (abfd);
}
void
bfd_gnu_truncate_arname (bfd *abfd, const char *pathname, char *arhdr)
{
struct ar_hdr *hdr = (struct ar_hdr *) arhdr;
size_t length;
const char *filename = strrchr (pathname, '/');
size_t maxlen = ar_maxnamelen (abfd);
#ifdef HAVE_DOS_BASED_FILE_SYSTEM
{
char *bslash = strrchr (pathname, '\\');
if (filename == NULL || (bslash != NULL && bslash > filename))
filename = bslash;
if (filename == NULL && pathname[0] != '\0' && pathname[1] == ':')
filename = pathname + 1;
}
#endif
if (filename == NULL)
filename = pathname;
else
++filename;
length = strlen (filename);
if (length <= maxlen)
memcpy (hdr->ar_name, filename, length);
else
{
memcpy (hdr->ar_name, filename, maxlen);
if ((filename[length - 2] == '.') && (filename[length - 1] == 'o'))
{
hdr->ar_name[maxlen - 2] = '.';
hdr->ar_name[maxlen - 1] = 'o';
}
length = maxlen;
}
if (length < 16)
(hdr->ar_name)[length] = ar_padchar (abfd);
}
bfd_boolean
_bfd_write_archive_contents (bfd *arch)
{
bfd *current;
char *etable = NULL;
bfd_size_type elength = 0;
const char *ename = NULL;
bfd_boolean makemap = bfd_has_map (arch);
bfd_boolean hasobjects = FALSE;
bfd_size_type wrote;
int tries;
for (current = arch->archive_head; current; current = current->next)
{
if (bfd_write_p (current))
{
bfd_set_error (bfd_error_invalid_operation);
return FALSE;
}
if (!current->arelt_data)
{
current->arelt_data =
bfd_ar_hdr_from_filesystem (arch, current->filename, current);
if (!current->arelt_data)
return FALSE;
BFD_SEND (arch, _bfd_truncate_arname,
(arch, current->filename, (char *) arch_hdr (current)));
}
if (makemap && ! hasobjects)
{
if ((bfd_check_format (current, bfd_object)))
hasobjects = TRUE;
}
}
if (!BFD_SEND (arch, _bfd_construct_extended_name_table,
(arch, &etable, &elength, &ename)))
return FALSE;
if (bfd_seek (arch, (file_ptr) 0, SEEK_SET) != 0)
return FALSE;
wrote = bfd_bwrite (ARMAG, SARMAG, arch);
if (wrote != SARMAG)
return FALSE;
if (makemap && hasobjects)
{
if (! _bfd_compute_and_write_armap (arch, (unsigned int) elength))
return FALSE;
}
if (elength != 0)
{
struct ar_hdr hdr;
memset (&hdr, ' ', sizeof (struct ar_hdr));
memcpy (hdr.ar_name, ename, strlen (ename));
_bfd_ar_spacepad (hdr.ar_size, sizeof (hdr.ar_size), "%-10ld",
(elength + 1) & ~(bfd_size_type) 1);
memcpy (hdr.ar_fmag, ARFMAG, 2);
if ((bfd_bwrite (&hdr, sizeof (struct ar_hdr), arch)
!= sizeof (struct ar_hdr))
|| bfd_bwrite (etable, elength, arch) != elength)
return FALSE;
if ((elength % 2) == 1)
{
if (bfd_bwrite ("\012", 1, arch) != 1)
return FALSE;
}
}
for (current = arch->archive_head; current; current = current->next)
{
char buffer[DEFAULT_BUFFERSIZE];
unsigned int remaining = arelt_size (current);
struct ar_hdr *hdr = arch_hdr (current);
if (bfd_bwrite (hdr, sizeof (*hdr), arch)
!= sizeof (*hdr))
return FALSE;
if (bfd_seek (current, (file_ptr) 0, SEEK_SET) != 0)
return FALSE;
while (remaining)
{
unsigned int amt = DEFAULT_BUFFERSIZE;
if (amt > remaining)
amt = remaining;
errno = 0;
if (bfd_bread (buffer, amt, current) != amt)
{
if (bfd_get_error () != bfd_error_system_call)
bfd_set_error (bfd_error_malformed_archive);
return FALSE;
}
if (bfd_bwrite (buffer, amt, arch) != amt)
return FALSE;
remaining -= amt;
}
if ((arelt_size (current) % 2) == 1)
{
if (bfd_bwrite ("\012", 1, arch) != 1)
return FALSE;
}
}
if (makemap && hasobjects)
{
tries = 1;
do
{
if (bfd_update_armap_timestamp (arch))
break;
(*_bfd_error_handler)
(_("Warning: writing archive was slow: rewriting timestamp\n"));
}
while (++tries < 6);
}
return TRUE;
}
bfd_boolean
_bfd_compute_and_write_armap (bfd *arch, unsigned int elength)
{
char *first_name = NULL;
bfd *current;
file_ptr elt_no = 0;
struct orl *map = NULL;
unsigned int orl_max = 1024;
unsigned int orl_count = 0;
int stridx = 0;
asymbol **syms = NULL;
long syms_max = 0;
bfd_boolean ret;
bfd_size_type amt;
if (elength != 0)
elength += sizeof (struct ar_hdr);
elength += elength % 2;
amt = orl_max * sizeof (struct orl);
map = bfd_malloc (amt);
if (map == NULL)
goto error_return;
first_name = bfd_alloc (arch, 1);
if (first_name == NULL)
goto error_return;
while (arch->archive_head &&
strcmp (arch->archive_head->filename, "__.SYMDEF") == 0)
arch->archive_head = arch->archive_head->next;
for (current = arch->archive_head;
current != NULL;
current = current->next, elt_no++)
{
if (bfd_check_format (current, bfd_object)
&& (bfd_get_file_flags (current) & HAS_SYMS) != 0)
{
long storage;
long symcount;
long src_count;
storage = bfd_get_symtab_upper_bound (current);
if (storage < 0)
goto error_return;
if (storage != 0)
{
if (storage > syms_max)
{
if (syms_max > 0)
free (syms);
syms_max = storage;
syms = bfd_malloc (syms_max);
if (syms == NULL)
goto error_return;
}
symcount = bfd_canonicalize_symtab (current, syms);
if (symcount < 0)
goto error_return;
for (src_count = 0; src_count < symcount; src_count++)
{
flagword flags = (syms[src_count])->flags;
asection *sec = syms[src_count]->section;
if ((flags & BSF_GLOBAL ||
flags & BSF_WEAK ||
flags & BSF_INDIRECT ||
bfd_is_com_section (sec))
&& ! bfd_is_und_section (sec))
{
bfd_size_type namelen;
struct orl *new_map;
if (orl_count == orl_max)
{
orl_max *= 2;
amt = orl_max * sizeof (struct orl);
new_map = bfd_realloc (map, amt);
if (new_map == NULL)
goto error_return;
map = new_map;
}
namelen = strlen (syms[src_count]->name);
amt = sizeof (char *);
map[orl_count].name = bfd_alloc (arch, amt);
if (map[orl_count].name == NULL)
goto error_return;
*(map[orl_count].name) = bfd_alloc (arch, namelen + 1);
if (*(map[orl_count].name) == NULL)
goto error_return;
strcpy (*(map[orl_count].name), syms[src_count]->name);
map[orl_count].u.abfd = current;
map[orl_count].namidx = stridx;
stridx += namelen + 1;
++orl_count;
}
}
}
if (! bfd_free_cached_info (current))
goto error_return;
}
}
ret = BFD_SEND (arch, write_armap,
(arch, elength, map, orl_count, stridx));
if (syms_max > 0)
free (syms);
if (map != NULL)
free (map);
if (first_name != NULL)
bfd_release (arch, first_name);
return ret;
error_return:
if (syms_max > 0)
free (syms);
if (map != NULL)
free (map);
if (first_name != NULL)
bfd_release (arch, first_name);
return FALSE;
}
bfd_boolean
bsd_write_armap (bfd *arch,
unsigned int elength,
struct orl *map,
unsigned int orl_count,
int stridx)
{
int padit = stridx & 1;
unsigned int ranlibsize = orl_count * BSD_SYMDEF_SIZE;
unsigned int stringsize = stridx + padit;
unsigned int mapsize = ranlibsize + stringsize + 8;
file_ptr firstreal;
bfd *current = arch->archive_head;
bfd *last_elt = current;
bfd_byte temp[4];
unsigned int count;
struct ar_hdr hdr;
struct stat statbuf;
firstreal = mapsize + elength + sizeof (struct ar_hdr) + SARMAG;
stat (arch->filename, &statbuf);
memset (&hdr, ' ', sizeof (struct ar_hdr));
memcpy (hdr.ar_name, RANLIBMAG, strlen (RANLIBMAG));
bfd_ardata (arch)->armap_timestamp = statbuf.st_mtime + ARMAP_TIME_OFFSET;
bfd_ardata (arch)->armap_datepos = (SARMAG
+ offsetof (struct ar_hdr, ar_date[0]));
_bfd_ar_spacepad (hdr.ar_date, sizeof (hdr.ar_date), "%ld",
bfd_ardata (arch)->armap_timestamp);
_bfd_ar_spacepad (hdr.ar_uid, sizeof (hdr.ar_uid), "%ld", getuid ());
_bfd_ar_spacepad (hdr.ar_gid, sizeof (hdr.ar_gid), "%ld", getgid ());
_bfd_ar_spacepad (hdr.ar_size, sizeof (hdr.ar_size), "%-10ld", mapsize);
memcpy (hdr.ar_fmag, ARFMAG, 2);
if (bfd_bwrite (&hdr, sizeof (struct ar_hdr), arch)
!= sizeof (struct ar_hdr))
return FALSE;
H_PUT_32 (arch, ranlibsize, temp);
if (bfd_bwrite (temp, sizeof (temp), arch) != sizeof (temp))
return FALSE;
for (count = 0; count < orl_count; count++)
{
bfd_byte buf[BSD_SYMDEF_SIZE];
if (map[count].u.abfd != last_elt)
{
do
{
firstreal += arelt_size (current) + sizeof (struct ar_hdr);
firstreal += firstreal % 2;
current = current->next;
}
while (current != map[count].u.abfd);
}
last_elt = current;
H_PUT_32 (arch, map[count].namidx, buf);
H_PUT_32 (arch, firstreal, buf + BSD_SYMDEF_OFFSET_SIZE);
if (bfd_bwrite (buf, BSD_SYMDEF_SIZE, arch)
!= BSD_SYMDEF_SIZE)
return FALSE;
}
H_PUT_32 (arch, stringsize, temp);
if (bfd_bwrite (temp, sizeof (temp), arch) != sizeof (temp))
return FALSE;
for (count = 0; count < orl_count; count++)
{
size_t len = strlen (*map[count].name) + 1;
if (bfd_bwrite (*map[count].name, len, arch) != len)
return FALSE;
}
if (padit)
{
if (bfd_bwrite ("", 1, arch) != 1)
return FALSE;
}
return TRUE;
}
bfd_boolean
_bfd_archive_bsd_update_armap_timestamp (bfd *arch)
{
struct stat archstat;
struct ar_hdr hdr;
bfd_flush (arch);
if (bfd_stat (arch, &archstat) == -1)
{
bfd_perror (_("Reading archive file mod timestamp"));
return TRUE;
}
if (archstat.st_mtime <= bfd_ardata (arch)->armap_timestamp)
return TRUE;
bfd_ardata (arch)->armap_timestamp = archstat.st_mtime + ARMAP_TIME_OFFSET;
memset (hdr.ar_date, ' ', sizeof (hdr.ar_date));
_bfd_ar_spacepad (hdr.ar_date, sizeof (hdr.ar_date), "%ld",
bfd_ardata (arch)->armap_timestamp);
bfd_ardata (arch)->armap_datepos = (SARMAG
+ offsetof (struct ar_hdr, ar_date[0]));
if (bfd_seek (arch, bfd_ardata (arch)->armap_datepos, SEEK_SET) != 0
|| (bfd_bwrite (hdr.ar_date, sizeof (hdr.ar_date), arch)
!= sizeof (hdr.ar_date)))
{
bfd_perror (_("Writing updated armap timestamp"));
return TRUE;
}
return FALSE;
}
bfd_boolean
coff_write_armap (bfd *arch,
unsigned int elength,
struct orl *map,
unsigned int symbol_count,
int stridx)
{
unsigned int ranlibsize = (symbol_count * 4) + 4;
unsigned int stringsize = stridx;
unsigned int mapsize = stringsize + ranlibsize;
unsigned int archive_member_file_ptr;
bfd *current = arch->archive_head;
unsigned int count;
struct ar_hdr hdr;
int padit = mapsize & 1;
if (padit)
mapsize++;
archive_member_file_ptr = (mapsize
+ elength
+ sizeof (struct ar_hdr)
+ SARMAG);
memset (&hdr, ' ', sizeof (struct ar_hdr));
hdr.ar_name[0] = '/';
_bfd_ar_spacepad (hdr.ar_size, sizeof (hdr.ar_size), "%-10ld",
mapsize);
_bfd_ar_spacepad (hdr.ar_date, sizeof (hdr.ar_date), "%ld",
time (NULL));
_bfd_ar_spacepad (hdr.ar_uid, sizeof (hdr.ar_uid), "%ld", 0);
_bfd_ar_spacepad (hdr.ar_gid, sizeof (hdr.ar_gid), "%ld", 0);
_bfd_ar_spacepad (hdr.ar_mode, sizeof (hdr.ar_mode), "%-7lo", 0);
memcpy (hdr.ar_fmag, ARFMAG, 2);
if (bfd_bwrite (&hdr, sizeof (struct ar_hdr), arch)
!= sizeof (struct ar_hdr))
return FALSE;
if (!bfd_write_bigendian_4byte_int (arch, symbol_count))
return FALSE;
current = arch->archive_head;
count = 0;
while (current != NULL && count < symbol_count)
{
while (count < symbol_count && map[count].u.abfd == current)
{
if (!bfd_write_bigendian_4byte_int (arch, archive_member_file_ptr))
return FALSE;
count++;
}
archive_member_file_ptr += arelt_size (current) + sizeof (struct ar_hdr);
archive_member_file_ptr += archive_member_file_ptr % 2;
current = current->next;
}
for (count = 0; count < symbol_count; count++)
{
size_t len = strlen (*map[count].name) + 1;
if (bfd_bwrite (*map[count].name, len, arch) != len)
return FALSE;
}
if (padit)
{
if (bfd_bwrite ("", 1, arch) != 1)
return FALSE;
}
return TRUE;
}