2008-03-19 Cary Coutant Add support for thin archives. * bfd/archive.c (_bfd_find_nested_archive): New function. (get_extended_arelt_filename): Add origin parameter. (_bfd_generic_read_ar_hdr_mag): Deal with extended name combined with a file offset. (append_relative_path): New function. (_bfd_get_elt_at_filepos): Deal with external members and nested archives. (bfd_generic_openr_next_archived_file): Thin archives. (bfd_generic_archive_p): Recognize new magic string. (adjust_relative_path): New function. (_bfd_construct_extended_name_table): Construct extended names for thin archive members. (_bfd_write_archive_contents): Emit new magic string, skip copying files for thin archives. * bfd/bfd-in.h (bfd_is_thin_archive): New macro. * bfd/bfd.c (struct bfd): New fields for thin archives. * bfd/libbfd-in.h (struct areltdata): New field for thin archives. * bfd/opncls.c (bfd_close): Delete BFDs for nested archives. * binutils/ar.c (make_thin_archive): New global flag. (map_over_members): Deal with full pathnames in thin archives. (usage, main): Add 'T' option for building thin archives. (replace_members): Pass thin archive flag to ar_emul_append. * binutils/arsup.c (ar_open): Initialize new flag. * binutils/binemul.c (ar_emul_append): Add new parameter for flattening nested archives. (do_ar_emul_default_append): New function. (ar_emul_default_append): Factored out recursive code. * binutils/binemul.h (ar_emul_default_append): Add new parameter. (struct bin_emulation_xfer_struct): New parameter for ar_append. * binutils/dlltool.c (gen_lib_file): Initialize thin archive flag. * binutils/emul_aix.c (ar_emul_aix_internal): Add new flatten parameter, currently unimplemented. All callers changed. * binutils/objcopy.c (copy_archive): Preserve thin archive flag. * binutils/doc/binutils.texi: Update ar documentation. * binutils/testsuite/binutils-all/ar.exp: Add thin archive tests. * include/aout/ar.h (ARMAGT): New magic string for thin archives. Index: bfd/archive.c =================================================================== RCS file: /cvs/src/src/bfd/archive.c,v retrieving revision 1.51 diff -u -p -r1.51 archive.c --- bfd/archive.c 27 Jul 2007 00:49:06 -0000 1.51 +++ bfd/archive.c 20 Mar 2008 18:20:24 -0000 @@ -137,6 +137,7 @@ SUBSECTION #include "aout/ranlib.h" #include "safe-ctype.h" #include "hashtab.h" +#include "filenames.h" #ifndef errno extern int errno; @@ -326,24 +327,59 @@ _bfd_add_bfd_to_archive_cache (bfd *arch return TRUE; } +static bfd * +_bfd_find_nested_archive (bfd *arch_bfd, const char *filename) +{ + bfd *abfd; + for (abfd = arch_bfd->nested_archives; + abfd != NULL; + abfd = abfd->archive_next) + { + if (strcmp (filename, abfd->filename) == 0) + return abfd; + } + abfd = bfd_openr (filename, NULL); + if (abfd) + { + abfd->archive_next = arch_bfd->nested_archives; + arch_bfd->nested_archives = abfd; + } + return abfd; +} + /* The name begins with space. Hence the rest of the name is an index into the string table. */ static char * -get_extended_arelt_filename (bfd *arch, const char *name) +get_extended_arelt_filename (bfd *arch, const char *name, file_ptr *originp) { unsigned long index = 0; + const char *endp; /* Should extract string so that I can guarantee not to overflow into the next region, but I'm too lazy. */ errno = 0; /* Skip first char, which is '/' in SVR4 or ' ' in some other variants. */ - index = strtol (name + 1, NULL, 10); + index = strtol (name + 1, (char **)&endp, 10); if (errno != 0 || index >= bfd_ardata (arch)->extended_names_size) { bfd_set_error (bfd_error_malformed_archive); return NULL; } + /* In a thin archive, a member of an archive-within-an-archive + will have the offset in the inner archive encoded here. */ + if (bfd_is_thin_archive (arch) && endp != NULL && *endp == ':') + { + file_ptr origin = strtol (endp + 1, NULL, 10); + if (errno != 0 || index >= bfd_ardata (arch)->extended_names_size) + { + bfd_set_error (bfd_error_malformed_archive); + return NULL; + } + *originp = origin; + } + else + *originp = 0; return bfd_ardata (arch)->extended_names + index; } @@ -376,6 +412,7 @@ _bfd_generic_read_ar_hdr_mag (bfd *abfd, bfd_size_type namelen = 0; bfd_size_type allocsize = sizeof (struct areltdata) + sizeof (struct ar_hdr); char *allocptr = 0; + file_ptr origin = 0; if (bfd_bread (hdrp, sizeof (struct ar_hdr), abfd) != sizeof (struct ar_hdr)) { @@ -407,7 +444,7 @@ _bfd_generic_read_ar_hdr_mag (bfd *abfd, && memchr (hdr.ar_name, '/', ar_maxnamelen (abfd)) == NULL)) && bfd_ardata (abfd)->extended_names != NULL) { - filename = get_extended_arelt_filename (abfd, hdr.ar_name); + filename = get_extended_arelt_filename (abfd, hdr.ar_name, &origin); if (filename == NULL) return NULL; } @@ -476,6 +513,7 @@ _bfd_generic_read_ar_hdr_mag (bfd *abfd, ared->arch_header = allocptr + sizeof (struct areltdata); memcpy (ared->arch_header, &hdr, sizeof (struct ar_hdr)); ared->parsed_size = parsed_size; + ared->origin = origin; if (filename != NULL) ared->filename = filename; @@ -491,6 +529,25 @@ _bfd_generic_read_ar_hdr_mag (bfd *abfd, return ared; } +/* Append the relative pathname for a member of the thin archive + to the pathname of the directory containing the archive. */ + +static char * +append_relative_path (bfd *arch, char *elt_name) +{ + const char *arch_name = arch->filename; + const char *base_name = lbasename (arch_name); + if (base_name == arch_name) + return elt_name; + size_t prefix_len = base_name - arch_name; + char *filename = bfd_alloc (arch, prefix_len + strlen(elt_name) + 1); + if (filename == NULL) + return NULL; + strncpy (filename, arch_name, prefix_len); + strcpy (filename + prefix_len, elt_name); + return filename; +} + /* This is an internal function; it's mainly used when indexing through the archive symbol table, but also used to get the next element, since it handles the bookkeeping so nicely for us. */ @@ -500,6 +557,7 @@ _bfd_get_elt_at_filepos (bfd *archive, f { struct areltdata *new_areldata; bfd *n_nfd; + char *filename; if (archive->my_archive) { @@ -517,21 +575,72 @@ _bfd_get_elt_at_filepos (bfd *archive, f if ((new_areldata = _bfd_read_ar_hdr (archive)) == NULL) return NULL; - n_nfd = _bfd_create_empty_archive_element_shell (archive); + filename = new_areldata->filename; + + if (bfd_is_thin_archive (archive)) + { + /* This is a proxy entry for an external file. */ + if (! IS_ABSOLUTE_PATH (filename)) + { + filename = append_relative_path (archive, filename); + if (filename == NULL) + 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); + if (ext_arch == NULL + || ! bfd_check_format (ext_arch, bfd_archive)) + { + bfd_release (archive, new_areldata); + return NULL; + } + n_nfd = _bfd_get_elt_at_filepos (ext_arch, new_areldata->origin); + if (n_nfd == NULL) + { + bfd_release (archive, new_areldata); + return NULL; + } + n_nfd->proxy_origin = bfd_tell (archive); + return n_nfd; + } + /* It's not an element of a nested archive; + open the external file as a bfd. */ + n_nfd = bfd_openr (filename, NULL); + } + else + { + 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->proxy_origin = bfd_tell (archive); + + if (bfd_is_thin_archive (archive)) + { + n_nfd->origin = 0; + } + else + { + n_nfd->origin = n_nfd->proxy_origin; + n_nfd->filename = filename; + } + n_nfd->arelt_data = new_areldata; - n_nfd->filename = new_areldata->filename; if (_bfd_add_bfd_to_archive_cache (archive, filepos, n_nfd)) return n_nfd; /* Huh? */ + /* FIXME: n_nfd isn't allocated in the archive's memory pool. + If we reach this point, I think bfd_release will abort. */ bfd_release (archive, n_nfd); bfd_release (archive, new_areldata); return NULL; @@ -589,7 +698,9 @@ bfd_generic_openr_next_archived_file (bf else { unsigned int size = arelt_size (last_file); - filestart = last_file->origin + size; + filestart = last_file->proxy_origin; + if (! bfd_is_thin_archive (archive)) + filestart += size; if (archive->my_archive) filestart -= archive->origin; /* Pad to an even boundary... @@ -615,8 +726,11 @@ bfd_generic_archive_p (bfd *abfd) return NULL; } + bfd_is_thin_archive (abfd) = (strncmp (armag, ARMAGT, SARMAG) == 0); + if (strncmp (armag, ARMAG, SARMAG) != 0 && - strncmp (armag, ARMAGB, SARMAG) != 0) + strncmp (armag, ARMAGB, SARMAG) != 0 && + ! bfd_is_thin_archive (abfd)) return 0; tdata_hold = bfd_ardata (abfd); @@ -1190,6 +1304,59 @@ normalize (bfd *abfd ATTRIBUTE_UNUSED, c } #endif +/* Adjust a relative path name based on the reference path. */ + +static const char * +adjust_relative_path (const char *path, const char *ref_path) +{ + static char *pathbuf = NULL; + static int pathbuf_len = 0; + const char *pathp = path; + const char *refp = ref_path; + int element_count = 0; + + /* Remove common leading path elements. */ + for (;;) + { + const char *e1 = pathp; + const char *e2 = refp; + while (*e1 && ! IS_DIR_SEPARATOR (*e1)) + ++e1; + while (*e2 && ! IS_DIR_SEPARATOR (*e2)) + ++e2; + if (*e1 == '\0' || *e2 == '\0' || e1 - pathp != e2 - refp || + strncmp (pathp, refp, e1 - pathp) != 0) + break; + pathp = e1 + 1; + refp = e2 + 1; + } + + /* For each leading path element in the reference path, + insert "../" into the path. */ + for (; *refp; ++refp) + if (IS_DIR_SEPARATOR (*refp)) + ++element_count; + int len = 3 * element_count + strlen (path) + 1; + if (len > pathbuf_len) + { + if (pathbuf != NULL) + free (pathbuf); + pathbuf_len = 0; + pathbuf = bfd_malloc (len); + if (pathbuf == NULL) + return path; + pathbuf_len = len; + } + char *newp = pathbuf; + while (element_count-- > 0) + { + strcpy (newp, "../"); + newp += 3; + } + strcpy (newp, pathp); + return pathbuf; +} + /* Build a BFD style extended name table. */ bfd_boolean @@ -1232,8 +1399,11 @@ _bfd_construct_extended_name_table (bfd bfd_size_type total_namelen = 0; bfd *current; char *strptr; + const char *last_filename; + long last_stroff; *tablen = 0; + last_filename = NULL; /* Figure out how long the table should be. */ for (current = abfd->archive_head; @@ -1243,6 +1413,38 @@ _bfd_construct_extended_name_table (bfd const char *normal; unsigned int thislen; + if (bfd_is_thin_archive (abfd)) + { + const char *filename = current->filename; + /* If the element being added is a member of another archive + (i.e., we are flattening), use the containing archive's name. */ + if (current->my_archive + && ! bfd_is_thin_archive (current->my_archive)) + filename = current->my_archive->filename; + /* If the path is the same as the previous path seen, + reuse it. This can happen when flattening a thin + archive that contains other archives. */ + if (last_filename && strcmp (last_filename, filename) == 0) + continue; + last_filename = filename; + /* If the path is relative, adjust it relative to + the containing archive. */ + if (! IS_ABSOLUTE_PATH (filename) + && ! IS_ABSOLUTE_PATH (abfd->filename)) + normal = adjust_relative_path (filename, abfd->filename); + else + normal = filename; + /* In a thin archive, always store the full pathname + in the extended name table. */ + total_namelen += strlen (normal) + 1; + if (trailing_slash) + { + /* Leave room for trailing slash. */ + ++total_namelen; + } + continue; + } + normal = normalize (current, current->filename); if (normal == NULL) return FALSE; @@ -1290,38 +1492,85 @@ _bfd_construct_extended_name_table (bfd *tablen = total_namelen; strptr = *tabloc; + last_filename = NULL; + last_stroff = 0; + for (current = abfd->archive_head; current != NULL; current = current->archive_next) { const char *normal; unsigned int thislen; + long stroff; + const char *filename = current->filename; - normal = normalize (current, current->filename); - if (normal == NULL) - return FALSE; + if (bfd_is_thin_archive (abfd)) + { + /* If the element being added is a member of another archive + (i.e., we are flattening), use the containing archive's name. */ + if (current->my_archive + && ! bfd_is_thin_archive (current->my_archive)) + filename = current->my_archive->filename; + /* If the path is the same as the previous path seen, + reuse it. This can happen when flattening a thin + archive that contains other archives. + If the path is relative, adjust it relative to + the containing archive. */ + if (last_filename && strcmp (last_filename, filename) == 0) + normal = last_filename; + else if (! IS_ABSOLUTE_PATH (filename) + && ! IS_ABSOLUTE_PATH (abfd->filename)) + normal = adjust_relative_path (filename, abfd->filename); + else + normal = filename; + } + else + { + normal = normalize (current, filename); + if (normal == NULL) + return FALSE; + } thislen = strlen (normal); - if (thislen > maxname) + if (thislen > maxname || bfd_is_thin_archive (abfd)) { /* Works for now; may need to be re-engineered if we encounter an oddball archive format and want to generalise this hack. */ struct ar_hdr *hdr = arch_hdr (current); - strcpy (strptr, normal); - if (! trailing_slash) - strptr[thislen] = '\012'; - else - { - strptr[thislen] = '/'; - strptr[thislen + 1] = '\012'; + if (normal == last_filename) + stroff = last_stroff; + else + { + strcpy (strptr, normal); + if (! trailing_slash) + strptr[thislen] = '\012'; + else + { + strptr[thislen] = '/'; + strptr[thislen + 1] = '\012'; + } + stroff = strptr - *tabloc; + last_stroff = stroff; } 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; + if (bfd_is_thin_archive (abfd) && current->origin > 0) + { + int len = snprintf (hdr->ar_name + 1, maxname - 1, "%-ld:", + stroff); + _bfd_ar_spacepad (hdr->ar_name + 1 + len, maxname - 1 - len, + "%-ld", + current->origin - sizeof (struct ar_hdr)); + } + else + _bfd_ar_spacepad (hdr->ar_name + 1, maxname - 1, "%-ld", stroff); + if (normal != last_filename) + { + strptr += thislen + 1; + if (trailing_slash) + ++strptr; + last_filename = filename; + } } } @@ -1681,7 +1930,10 @@ _bfd_write_archive_contents (bfd *arch) if (bfd_seek (arch, (file_ptr) 0, SEEK_SET) != 0) return FALSE; - wrote = bfd_bwrite (ARMAG, SARMAG, arch); + char *armag = ARMAG; + if (bfd_is_thin_archive (arch)) + armag = ARMAGT; + wrote = bfd_bwrite (armag, SARMAG, arch); if (wrote != SARMAG) return FALSE; @@ -1724,6 +1976,8 @@ _bfd_write_archive_contents (bfd *arch) if (bfd_bwrite (hdr, sizeof (*hdr), arch) != sizeof (*hdr)) return FALSE; + if (bfd_is_thin_archive (arch)) + continue; if (bfd_seek (current, (file_ptr) 0, SEEK_SET) != 0) goto input_err; while (remaining) @@ -2139,10 +2393,14 @@ coff_write_armap (bfd *arch, return FALSE; count++; } - /* Add size of this archive entry. */ - archive_member_file_ptr += arelt_size (current) + sizeof (struct ar_hdr); - /* Remember aboout the even alignment. */ - archive_member_file_ptr += archive_member_file_ptr % 2; + 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; } Index: bfd/bfd-in.h =================================================================== RCS file: /cvs/src/src/bfd/bfd-in.h,v retrieving revision 1.133 diff -u -p -r1.133 bfd-in.h --- bfd/bfd-in.h 16 Mar 2008 06:53:48 -0000 1.133 +++ bfd/bfd-in.h 20 Mar 2008 18:20:24 -0000 @@ -499,6 +499,7 @@ extern void warn_deprecated (const char #define bfd_applicable_section_flags(abfd) ((abfd)->xvec->section_flags) #define bfd_my_archive(abfd) ((abfd)->my_archive) #define bfd_has_map(abfd) ((abfd)->has_armap) +#define bfd_is_thin_archive(abfd) ((abfd)->is_thin_archive) #define bfd_valid_reloc_types(abfd) ((abfd)->xvec->valid_reloc_types) #define bfd_usrdata(abfd) ((abfd)->usrdata) Index: bfd/bfd.c =================================================================== RCS file: /cvs/src/src/bfd/bfd.c,v retrieving revision 1.101 diff -u -p -r1.101 bfd.c --- bfd/bfd.c 16 Mar 2008 06:53:48 -0000 1.101 +++ bfd/bfd.c 20 Mar 2008 18:20:24 -0000 @@ -150,6 +150,13 @@ CODE_FRAGMENT . origin, with origin set to 0 for non archive files. *} . ufile_ptr origin; . +. {* The origin in the archive of the proxy entry. This will +. normally be the same as origin, except for thin archives, +. when it will contain the current offset of the proxy in the +. thin archive rather than the offset of the bfd in its actual +. container. *} +. ufile_ptr proxy_origin; +. . {* A hash table for section names. *} . struct bfd_hash_table section_htab; . @@ -183,6 +190,8 @@ CODE_FRAGMENT . struct bfd *my_archive; {* The containing archive BFD. *} . struct bfd *archive_next; {* The next BFD in the archive. *} . struct bfd *archive_head; {* The first BFD in the archive. *} +. struct bfd *nested_archives; {* List of nested archive in a flattened +. thin archive. *} . . {* A chain of BFD structures involved in a link. *} . struct bfd *link_next; @@ -265,6 +274,9 @@ CODE_FRAGMENT . . {* Have archive map. *} . unsigned int has_armap : 1; +. +. {* Set if this is a thin archive. *} +. unsigned int is_thin_archive : 1; .}; . */ Index: bfd/libbfd-in.h =================================================================== RCS file: /cvs/src/src/bfd/libbfd-in.h,v retrieving revision 1.73 diff -u -p -r1.73 libbfd-in.h --- bfd/libbfd-in.h 20 Feb 2008 17:42:36 -0000 1.73 +++ bfd/libbfd-in.h 20 Mar 2008 18:20:24 -0000 @@ -91,6 +91,7 @@ struct areltdata { char * arch_header; /* it's actually a string */ unsigned int parsed_size; /* octets of filesize not including ar_hdr */ char *filename; /* null-terminated */ + file_ptr origin; /* for element of a thin archive */ }; #define arelt_size(bfd) (((struct areltdata *)((bfd)->arelt_data))->parsed_size) Index: bfd/opncls.c =================================================================== RCS file: /cvs/src/src/bfd/opncls.c,v retrieving revision 1.51 diff -u -p -r1.51 opncls.c --- bfd/opncls.c 15 Nov 2007 05:20:30 -0000 1.51 +++ bfd/opncls.c 20 Mar 2008 18:20:24 -0000 @@ -647,6 +647,8 @@ bfd_boolean bfd_close (bfd *abfd) { bfd_boolean ret; + bfd *nbfd; + bfd *next; if (bfd_write_p (abfd)) { @@ -654,6 +656,13 @@ bfd_close (bfd *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; Index: binutils/ar.c =================================================================== RCS file: /cvs/src/src/binutils/ar.c,v retrieving revision 1.54 diff -u -p -r1.54 ar.c --- binutils/ar.c 27 Feb 2008 10:56:20 -0000 1.54 +++ binutils/ar.c 20 Mar 2008 18:20:24 -0000 @@ -134,6 +134,9 @@ static bfd_boolean ar_truncate = FALSE; program. */ static bfd_boolean full_pathname = FALSE; +/* Whether to create a "thin" archive (symbol index only -- no files). */ +static bfd_boolean make_thin_archive = FALSE; + int interactive = 0; static void @@ -177,15 +180,21 @@ map_over_members (bfd *arch, void (*func for (head = arch->archive_next; head; head = head->archive_next) { PROGRESS (1); - if (head->filename == NULL) + const char *filename = head->filename; + if (filename == NULL) { /* Some archive formats don't get the filenames filled in until the elements are opened. */ struct stat buf; bfd_stat_arch_elt (head, &buf); } - if ((head->filename != NULL) && - (!FILENAME_CMP (normalize (*files, arch), head->filename))) + else if (bfd_is_thin_archive (arch)) + { + /* Thin archives store full pathnames. Need to normalize. */ + filename = normalize (filename, arch); + } + if ((filename != NULL) && + (!FILENAME_CMP (normalize (*files, arch), filename))) { ++match_count; if (counted_name_mode @@ -242,6 +251,7 @@ usage (int help) fprintf (s, _(" [c] - do not warn if the library had to be created\n")); fprintf (s, _(" [s] - create an archive index (cf. ranlib)\n")); fprintf (s, _(" [S] - do not build a symbol table\n")); + fprintf (s, _(" [T] - make a thin archive\n")); fprintf (s, _(" [v] - be verbose\n")); fprintf (s, _(" [V] - display the version number\n")); fprintf (s, _(" @ - read options from \n")); @@ -559,6 +569,9 @@ main (int argc, char **argv) case 'P': full_pathname = TRUE; break; + case 'T': + make_thin_archive = TRUE; + break; default: /* xgettext:c-format */ non_fatal (_("illegal option -- %c"), c); @@ -629,6 +642,9 @@ main (int argc, char **argv) arch = open_inarch (inarch_filename, files == NULL ? (char *) NULL : files[0]); + if (operation == extract && bfd_is_thin_archive (arch)) + fatal (_("`x' cannot be used on thin archives.")); + switch (operation) { case print_table: @@ -956,6 +972,9 @@ write_archive (bfd *iarch) obfd->flags |= BFD_TRADITIONAL_FORMAT; } + if (make_thin_archive || bfd_is_thin_archive (iarch)) + bfd_is_thin_archive (obfd) = 1; + if (!bfd_set_archive_head (obfd, contents_head)) bfd_fatal (old_name); @@ -1189,7 +1208,8 @@ replace_members (bfd *arch, char **files /* Add to the end of the archive. */ after_bfd = get_pos_bfd (&arch->archive_next, pos_end, NULL); - if (ar_emul_append (after_bfd, *files_to_move, verbose)) + if (ar_emul_append (after_bfd, *files_to_move, verbose, + make_thin_archive)) changed = TRUE; next_file:; Index: binutils/arsup.c =================================================================== RCS file: /cvs/src/src/binutils/arsup.c,v retrieving revision 1.19 diff -u -p -r1.19 arsup.c --- binutils/arsup.c 5 Jul 2007 16:54:45 -0000 1.19 +++ binutils/arsup.c 20 Mar 2008 18:20:24 -0000 @@ -207,6 +207,7 @@ ar_open (char *name, int t) bfd_set_format (obfd, bfd_archive); obfd->has_armap = 1; + obfd->is_thin_archive = 0; } } Index: binutils/binemul.c =================================================================== RCS file: /cvs/src/src/binutils/binemul.c,v retrieving revision 1.10 diff -u -p -r1.10 binemul.c --- binutils/binemul.c 5 Jul 2007 16:54:45 -0000 1.10 +++ binutils/binemul.c 20 Mar 2008 18:20:24 -0000 @@ -39,32 +39,61 @@ ar_emul_default_usage (FILE *fp) } bfd_boolean -ar_emul_append (bfd **after_bfd, char *file_name, bfd_boolean verbose) +ar_emul_append (bfd **after_bfd, char *file_name, bfd_boolean verbose, + bfd_boolean flatten) { if (bin_dummy_emulation.ar_append) - return bin_dummy_emulation.ar_append (after_bfd, file_name, verbose); + return bin_dummy_emulation.ar_append (after_bfd, file_name, verbose, + flatten); return FALSE; } -bfd_boolean -ar_emul_default_append (bfd **after_bfd, char *file_name, - bfd_boolean verbose) -{ - bfd *temp; +static bfd_boolean +do_ar_emul_default_append (bfd **after_bfd, bfd *new_bfd, + bfd_boolean verbose, bfd_boolean flatten) + { + /* When flattening, add the members of an archive instead of the + archive itself. */ + if (flatten && bfd_check_format (new_bfd, bfd_archive)) + { + bfd *elt; + bfd_boolean added = FALSE; + + for (elt = bfd_openr_next_archived_file (new_bfd, NULL); + elt; + elt = bfd_openr_next_archived_file (new_bfd, elt)) + { + if (do_ar_emul_default_append (after_bfd, elt, verbose, TRUE)) + { + added = TRUE; + after_bfd = &((*after_bfd)->archive_next); + } + } - temp = *after_bfd; - *after_bfd = bfd_openr (file_name, NULL); + return added; + } - AR_EMUL_ELEMENT_CHECK (*after_bfd, file_name); - AR_EMUL_APPEND_PRINT_VERBOSE (verbose, file_name); + AR_EMUL_APPEND_PRINT_VERBOSE (verbose, new_bfd->filename); - (*after_bfd)->archive_next = temp; + new_bfd->archive_next = *after_bfd; + *after_bfd = new_bfd; return TRUE; } bfd_boolean +ar_emul_default_append (bfd **after_bfd, char *file_name, + bfd_boolean verbose, bfd_boolean flatten) +{ + bfd *new_bfd; + + new_bfd = bfd_openr (file_name, NULL); + AR_EMUL_ELEMENT_CHECK (new_bfd, file_name); + return do_ar_emul_default_append (after_bfd, new_bfd, verbose, flatten); +} + +bfd_boolean ar_emul_replace (bfd **after_bfd, char *file_name, bfd_boolean verbose) { if (bin_dummy_emulation.ar_replace) Index: binutils/binemul.h =================================================================== RCS file: /cvs/src/src/binutils/binemul.h,v retrieving revision 1.9 diff -u -p -r1.9 binemul.h --- binutils/binemul.h 5 Jul 2007 16:54:45 -0000 1.9 +++ binutils/binemul.h 20 Mar 2008 18:20:24 -0000 @@ -28,8 +28,9 @@ extern void ar_emul_usage (FILE *); extern void ar_emul_default_usage (FILE *); -extern bfd_boolean ar_emul_append (bfd **, char *, bfd_boolean); -extern bfd_boolean ar_emul_default_append (bfd **, char *, bfd_boolean); +extern bfd_boolean ar_emul_append (bfd **, char *, bfd_boolean, bfd_boolean); +extern bfd_boolean ar_emul_default_append (bfd **, char *, bfd_boolean, + bfd_boolean); extern bfd_boolean ar_emul_replace (bfd **, char *, bfd_boolean); extern bfd_boolean ar_emul_default_replace (bfd **, char *, bfd_boolean); extern bfd_boolean ar_emul_parse_arg (char *); @@ -54,7 +55,7 @@ typedef struct bin_emulation_xfer_struct { /* Print out the extra options. */ void (* ar_usage) (FILE *fp); - bfd_boolean (* ar_append) (bfd **, char *, bfd_boolean); + bfd_boolean (* ar_append) (bfd **, char *, bfd_boolean, bfd_boolean); bfd_boolean (* ar_replace) (bfd **, char *, bfd_boolean); bfd_boolean (* ar_parse_arg) (char *); } Index: binutils/dlltool.c =================================================================== RCS file: /cvs/src/src/binutils/dlltool.c,v retrieving revision 1.80 diff -u -p -r1.80 dlltool.c --- binutils/dlltool.c 12 Feb 2008 12:33:51 -0000 1.80 +++ binutils/dlltool.c 20 Mar 2008 18:20:25 -0000 @@ -2813,6 +2813,7 @@ gen_lib_file (void) bfd_set_format (outarch, bfd_archive); outarch->has_armap = 1; + outarch->is_thin_archive = 0; /* Work out a reasonable size of things to put onto one line. */ ar_head = make_head (); Index: binutils/emul_aix.c =================================================================== RCS file: /cvs/src/src/binutils/emul_aix.c,v retrieving revision 1.11 diff -u -p -r1.11 emul_aix.c --- binutils/emul_aix.c 5 Jul 2007 16:54:45 -0000 1.11 +++ binutils/emul_aix.c 20 Mar 2008 18:20:25 -0000 @@ -36,8 +36,10 @@ static bfd_boolean X32 = TRUE; static bfd_boolean X64 = FALSE; static void ar_emul_aix_usage (FILE *); -static bfd_boolean ar_emul_aix_append (bfd **, char *, bfd_boolean); -static bfd_boolean ar_emul_aix5_append (bfd **, char *, bfd_boolean); +static bfd_boolean ar_emul_aix_append (bfd **, char *, bfd_boolean, + bfd_boolean); +static bfd_boolean ar_emul_aix5_append (bfd **, char *, bfd_boolean, + bfd_boolean); static bfd_boolean ar_emul_aix_replace (bfd **, char *, bfd_boolean); static bfd_boolean ar_emul_aix5_replace (bfd **, char *, bfd_boolean); static bfd_boolean ar_emul_aix_parse_arg (char *); @@ -57,7 +59,8 @@ ar_emul_aix_usage (FILE *fp) static bfd_boolean ar_emul_aix_internal (bfd **after_bfd, char *file_name, bfd_boolean verbose, - const char * target_name, bfd_boolean is_append) + const char * target_name, bfd_boolean is_append, + bfd_boolean flatten) { bfd *temp; bfd *try_bfd; @@ -97,31 +100,33 @@ ar_emul_aix_internal (bfd **after_bfd, c static bfd_boolean -ar_emul_aix_append (bfd **after_bfd, char *file_name, bfd_boolean verbose) +ar_emul_aix_append (bfd **after_bfd, char *file_name, bfd_boolean verbose, + bfd_boolean flatten) { return ar_emul_aix_internal (after_bfd, file_name, verbose, - "aixcoff64-rs6000", TRUE); + "aixcoff64-rs6000", TRUE, flatten); } static bfd_boolean -ar_emul_aix5_append (bfd **after_bfd, char *file_name, bfd_boolean verbose) +ar_emul_aix5_append (bfd **after_bfd, char *file_name, bfd_boolean verbose + bfd_boolean flatten) { return ar_emul_aix_internal (after_bfd, file_name, verbose, - "aix5coff64-rs6000", TRUE); + "aix5coff64-rs6000", TRUE, flatten); } static bfd_boolean ar_emul_aix_replace (bfd **after_bfd, char *file_name, bfd_boolean verbose) { return ar_emul_aix_internal (after_bfd, file_name, verbose, - "aixcoff64-rs6000", FALSE); + "aixcoff64-rs6000", FALSE, FALSE); } static bfd_boolean ar_emul_aix5_replace (bfd **after_bfd, char *file_name, bfd_boolean verbose) { return ar_emul_aix_internal (after_bfd, file_name, verbose, - "aix5coff64-rs6000", FALSE); + "aix5coff64-rs6000", FALSE, FALSE); } static bfd_boolean Index: binutils/objcopy.c =================================================================== RCS file: /cvs/src/src/binutils/objcopy.c,v retrieving revision 1.119 diff -u -p -r1.119 objcopy.c --- binutils/objcopy.c 30 Aug 2007 10:19:03 -0000 1.119 +++ binutils/objcopy.c 20 Mar 2008 18:20:25 -0000 @@ -1830,6 +1830,7 @@ copy_archive (bfd *ibfd, bfd *obfd, cons strerror (errno)); obfd->has_armap = ibfd->has_armap; + obfd->is_thin_archive = ibfd->is_thin_archive; list = NULL; Index: binutils/doc/binutils.texi =================================================================== RCS file: /cvs/src/src/binutils/doc/binutils.texi,v retrieving revision 1.125 diff -u -p -r1.125 binutils.texi --- binutils/doc/binutils.texi 27 Feb 2008 11:11:06 -0000 1.125 +++ binutils/doc/binutils.texi 20 Mar 2008 18:20:25 -0000 @@ -211,6 +211,18 @@ You may use @samp{nm -s} or @samp{nm --p table. If an archive lacks the table, another form of @command{ar} called @command{ranlib} can be used to add just the table. +@cindex thin archives +@sc{gnu} @command{ar} can optionally create a @emph{thin} archive, +which contains a symbol index and references to the original copies +of the member files of the archives. Such an archive is useful +for building libraries for use within a local build, where the +relocatable objects are expected to remain available, and copying the +contents of each object would only waste time and space. Thin archives +are also @emph{flattened}, so that adding one or more archives to a +thin archive will add the elements of the nested archive individually. +The paths to the elements of the archive are stored relative to the +archive itself. + @cindex compatibility, @command{ar} @cindex @command{ar} compatibility @sc{gnu} @command{ar} is designed to be compatible with two different @@ -356,6 +368,8 @@ use the @samp{v} modifier with this oper If you do not specify a @var{member}, all files in the archive are extracted. +Files cannot be extracted from a thin archive. + @end table A number of modifiers (@var{mod}) may immediately follow the @var{p} @@ -434,6 +448,12 @@ with the linker. In order to build a sy @samp{S} modifier on the last execution of @samp{ar}, or you must run @samp{ranlib} on the archive. +@item T +@cindex creating thin archive +Make the specified @var{archive} a @emph{thin} archive. If it already +exists and is a regular archive, the existing members must be present +in the same directory as @var{archive}. + @item u @cindex updating an archive Normally, @samp{ar r}@dots{} inserts all files Index: binutils/testsuite/binutils-all/ar.exp =================================================================== RCS file: /cvs/src/src/binutils/testsuite/binutils-all/ar.exp,v retrieving revision 1.10 diff -u -p -r1.10 ar.exp --- binutils/testsuite/binutils-all/ar.exp 28 Aug 2007 13:21:53 -0000 1.10 +++ binutils/testsuite/binutils-all/ar.exp 20 Mar 2008 18:20:25 -0000 @@ -215,6 +215,110 @@ proc symbol_table { } { pass $testname } +# Test building a thin archive. + +proc thin_archive { } { + global AR + global AS + global NM + global srcdir + global subdir + + set testname "ar thin archive" + + if ![binutils_assemble $srcdir/$subdir/bintest.s tmpdir/bintest.o] { + unresolved $testname + return + } + + if [is_remote host] { + set archive artest.a + set objfile [remote_download host tmpdir/bintest.o] + remote_file host delete $archive + } else { + set archive tmpdir/artest.a + set objfile tmpdir/bintest.o + } + + remote_file build delete tmpdir/artest.a + + set got [binutils_run $AR "rcT $archive ${objfile}"] + if ![string match "" $got] { + fail $testname + return + } + + set got [binutils_run $NM "--print-armap $archive"] + if { ![string match "*text_symbol in *bintest.o*" $got] \ + || ![string match "*data_symbol in *bintest.o*" $got] \ + || ![string match "*common_symbol in *bintest.o*" $got] \ + || [string match "*static_text_symbol in *bintest.o*" $got] \ + || [string match "*static_data_symbol in *bintest.o*" $got] \ + || [string match "*external_symbol in *bintest.o*" $got] } { + fail $testname + return + } + + pass $testname +} + +# Test building a thin archive with a nested archive. + +proc thin_archive_with_nested { } { + global AR + global AS + global NM + global srcdir + global subdir + + set testname "ar thin archive with nested archive" + + if ![binutils_assemble $srcdir/$subdir/bintest.s tmpdir/bintest.o] { + unresolved $testname + return + } + + if [is_remote host] { + set archive artest.a + set archive2 artest2.a + set objfile [remote_download host tmpdir/bintest.o] + remote_file host delete $archive + } else { + set archive tmpdir/artest.a + set archive2 tmpdir/artest2.a + set objfile tmpdir/bintest.o + } + + remote_file build delete tmpdir/artest.a + + set got [binutils_run $AR "rc $archive ${objfile}"] + if ![string match "" $got] { + fail $testname + return + } + + remote_file build delete tmpdir/artest2.a + + set got [binutils_run $AR "rcT $archive2 ${archive}"] + if ![string match "" $got] { + fail $testname + return + } + + set got [binutils_run $NM "--print-armap $archive"] + if { ![string match "*text_symbol in *bintest.o*" $got] \ + || ![string match "*data_symbol in *bintest.o*" $got] \ + || ![string match "*common_symbol in *bintest.o*" $got] \ + || [string match "*static_text_symbol in *bintest.o*" $got] \ + || [string match "*static_data_symbol in *bintest.o*" $got] \ + || [string match "*external_symbol in *bintest.o*" $got] } { + fail $testname + return + } + + pass $testname +} + # Test POSIX-compatible argument parsing. proc argument_parsing { } { @@ -254,4 +358,6 @@ proc argument_parsing { } { long_filenames symbol_table +thin_archive +thin_archive_with_nested argument_parsing Index: include/aout/ar.h =================================================================== RCS file: /cvs/src/src/include/aout/ar.h,v retrieving revision 1.3 diff -u -p -r1.3 ar.h --- include/aout/ar.h 10 May 2005 10:21:09 -0000 1.3 +++ include/aout/ar.h 20 Mar 2008 18:20:25 -0000 @@ -28,6 +28,7 @@ #define ARMAG "!\012" /* For COFF and a.out archives */ #define ARMAGB "!\012" /* For b.out archives */ +#define ARMAGT "!\012" /* For thin archives */ #define SARMAG 8 #define ARFMAG "`\012"