| /* elfcomm.c -- common code for ELF format file. |
| Copyright (C) 2010-2024 Free Software Foundation, Inc. |
| |
| Originally developed by Eric Youngdale <eric@andante.jic.com> |
| Modifications by Nick Clifton <nickc@redhat.com> |
| |
| This file is part of GNU Binutils. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA |
| 02110-1301, USA. */ |
| |
| /* Do not include bfd.h in this file. Functions in this file are used |
| by readelf.c and elfedit.c which define BFD64, and by objdump.c |
| which doesn't. */ |
| |
| #include "sysdep.h" |
| #include "libiberty.h" |
| #include "bfd.h" |
| #include "filenames.h" |
| #include "aout/ar.h" |
| #include "elfcomm.h" |
| #include <assert.h> |
| |
| extern char *program_name; |
| |
| void |
| error (const char *message, ...) |
| { |
| va_list args; |
| |
| /* Try to keep error messages in sync with the program's normal output. */ |
| fflush (stdout); |
| |
| va_start (args, message); |
| fprintf (stderr, _("%s: Error: "), program_name); |
| vfprintf (stderr, message, args); |
| va_end (args); |
| } |
| |
| void |
| warn (const char *message, ...) |
| { |
| va_list args; |
| |
| /* Try to keep warning messages in sync with the program's normal output. */ |
| fflush (stdout); |
| |
| va_start (args, message); |
| fprintf (stderr, _("%s: Warning: "), program_name); |
| vfprintf (stderr, message, args); |
| va_end (args); |
| } |
| |
| void |
| inform (const char *message, ...) |
| { |
| va_list args; |
| |
| /* Try to keep info messages in sync with the program's normal output. */ |
| fflush (stdout); |
| |
| va_start (args, message); |
| fprintf (stderr, _("%s: Info: "), program_name); |
| vfprintf (stderr, message, args); |
| va_end (args); |
| } |
| |
| void (*byte_put) (unsigned char *, uint64_t, unsigned int); |
| |
| void |
| byte_put_little_endian (unsigned char *field, uint64_t value, unsigned int size) |
| { |
| if (size > sizeof (uint64_t)) |
| { |
| error (_("Unhandled data length: %d\n"), size); |
| abort (); |
| } |
| while (size--) |
| { |
| *field++ = value & 0xff; |
| value >>= 8; |
| } |
| } |
| |
| void |
| byte_put_big_endian (unsigned char *field, uint64_t value, unsigned int size) |
| { |
| if (size > sizeof (uint64_t)) |
| { |
| error (_("Unhandled data length: %d\n"), size); |
| abort (); |
| } |
| while (size--) |
| { |
| field[size] = value & 0xff; |
| value >>= 8; |
| } |
| } |
| |
| uint64_t (*byte_get) (const unsigned char *, unsigned int); |
| |
| uint64_t |
| byte_get_little_endian (const unsigned char *field, unsigned int size) |
| { |
| switch (size) |
| { |
| case 1: |
| return *field; |
| |
| case 2: |
| return ((uint64_t) field[0] |
| | ((uint64_t) field[1] << 8)); |
| |
| case 3: |
| return ((uint64_t) field[0] |
| | ((uint64_t) field[1] << 8) |
| | ((uint64_t) field[2] << 16)); |
| |
| case 4: |
| return ((uint64_t) field[0] |
| | ((uint64_t) field[1] << 8) |
| | ((uint64_t) field[2] << 16) |
| | ((uint64_t) field[3] << 24)); |
| |
| case 5: |
| return ((uint64_t) field[0] |
| | ((uint64_t) field[1] << 8) |
| | ((uint64_t) field[2] << 16) |
| | ((uint64_t) field[3] << 24) |
| | ((uint64_t) field[4] << 32)); |
| |
| case 6: |
| return ((uint64_t) field[0] |
| | ((uint64_t) field[1] << 8) |
| | ((uint64_t) field[2] << 16) |
| | ((uint64_t) field[3] << 24) |
| | ((uint64_t) field[4] << 32) |
| | ((uint64_t) field[5] << 40)); |
| |
| case 7: |
| return ((uint64_t) field[0] |
| | ((uint64_t) field[1] << 8) |
| | ((uint64_t) field[2] << 16) |
| | ((uint64_t) field[3] << 24) |
| | ((uint64_t) field[4] << 32) |
| | ((uint64_t) field[5] << 40) |
| | ((uint64_t) field[6] << 48)); |
| |
| case 8: |
| return ((uint64_t) field[0] |
| | ((uint64_t) field[1] << 8) |
| | ((uint64_t) field[2] << 16) |
| | ((uint64_t) field[3] << 24) |
| | ((uint64_t) field[4] << 32) |
| | ((uint64_t) field[5] << 40) |
| | ((uint64_t) field[6] << 48) |
| | ((uint64_t) field[7] << 56)); |
| |
| default: |
| error (_("Unhandled data length: %d\n"), size); |
| abort (); |
| } |
| } |
| |
| uint64_t |
| byte_get_big_endian (const unsigned char *field, unsigned int size) |
| { |
| switch (size) |
| { |
| case 1: |
| return *field; |
| |
| case 2: |
| return ((uint64_t) field[1] |
| | ((uint64_t) field[0] << 8)); |
| |
| case 3: |
| return ((uint64_t) field[2] |
| | ((uint64_t) field[1] << 8) |
| | ((uint64_t) field[0] << 16)); |
| |
| case 4: |
| return ((uint64_t) field[3] |
| | ((uint64_t) field[2] << 8) |
| | ((uint64_t) field[1] << 16) |
| | ((uint64_t) field[0] << 24)); |
| |
| case 5: |
| return ((uint64_t) field[4] |
| | ((uint64_t) field[3] << 8) |
| | ((uint64_t) field[2] << 16) |
| | ((uint64_t) field[1] << 24) |
| | ((uint64_t) field[0] << 32)); |
| |
| case 6: |
| return ((uint64_t) field[5] |
| | ((uint64_t) field[4] << 8) |
| | ((uint64_t) field[3] << 16) |
| | ((uint64_t) field[2] << 24) |
| | ((uint64_t) field[1] << 32) |
| | ((uint64_t) field[0] << 40)); |
| |
| case 7: |
| return ((uint64_t) field[6] |
| | ((uint64_t) field[5] << 8) |
| | ((uint64_t) field[4] << 16) |
| | ((uint64_t) field[3] << 24) |
| | ((uint64_t) field[2] << 32) |
| | ((uint64_t) field[1] << 40) |
| | ((uint64_t) field[0] << 48)); |
| |
| case 8: |
| return ((uint64_t) field[7] |
| | ((uint64_t) field[6] << 8) |
| | ((uint64_t) field[5] << 16) |
| | ((uint64_t) field[4] << 24) |
| | ((uint64_t) field[3] << 32) |
| | ((uint64_t) field[2] << 40) |
| | ((uint64_t) field[1] << 48) |
| | ((uint64_t) field[0] << 56)); |
| |
| default: |
| error (_("Unhandled data length: %d\n"), size); |
| abort (); |
| } |
| } |
| |
| uint64_t |
| byte_get_signed (const unsigned char *field, unsigned int size) |
| { |
| uint64_t x = byte_get (field, size); |
| |
| switch (size) |
| { |
| case 1: |
| return (x ^ 0x80) - 0x80; |
| case 2: |
| return (x ^ 0x8000) - 0x8000; |
| case 3: |
| return (x ^ 0x800000) - 0x800000; |
| case 4: |
| return (x ^ 0x80000000) - 0x80000000; |
| case 5: |
| case 6: |
| case 7: |
| case 8: |
| /* Reads of 5-, 6-, and 7-byte numbers are the result of |
| trying to read past the end of a buffer, and will therefore |
| not have meaningful values, so we don't try to deal with |
| the sign in these cases. */ |
| return x; |
| default: |
| abort (); |
| } |
| } |
| |
| /* Return the path name for a proxy entry in a thin archive, adjusted |
| relative to the path name of the thin archive itself if necessary. |
| Always returns a pointer to malloc'ed memory. */ |
| |
| char * |
| adjust_relative_path (const char *file_name, const char *name, |
| unsigned long name_len) |
| { |
| char * member_file_name; |
| const char * base_name = lbasename (file_name); |
| size_t amt; |
| |
| /* This is a proxy entry for a thin archive member. |
| If the extended name table contains an absolute path |
| name, or if the archive is in the current directory, |
| use the path name as given. Otherwise, we need to |
| find the member relative to the directory where the |
| archive is located. */ |
| if (IS_ABSOLUTE_PATH (name) || base_name == file_name) |
| { |
| amt = name_len + 1; |
| if (amt == 0) |
| return NULL; |
| member_file_name = (char *) malloc (amt); |
| if (member_file_name == NULL) |
| { |
| error (_("Out of memory\n")); |
| return NULL; |
| } |
| memcpy (member_file_name, name, name_len); |
| member_file_name[name_len] = '\0'; |
| } |
| else |
| { |
| /* Concatenate the path components of the archive file name |
| to the relative path name from the extended name table. */ |
| size_t prefix_len = base_name - file_name; |
| |
| amt = prefix_len + name_len + 1; |
| /* PR 17531: file: 2896dc8b |
| Catch wraparound. */ |
| if (amt < prefix_len || amt < name_len) |
| { |
| error (_("Abnormal length of thin archive member name: %lx\n"), |
| name_len); |
| return NULL; |
| } |
| |
| member_file_name = (char *) malloc (amt); |
| if (member_file_name == NULL) |
| { |
| error (_("Out of memory\n")); |
| return NULL; |
| } |
| memcpy (member_file_name, file_name, prefix_len); |
| memcpy (member_file_name + prefix_len, name, name_len); |
| member_file_name[prefix_len + name_len] = '\0'; |
| } |
| return member_file_name; |
| } |
| |
| /* Processes the archive index table and symbol table in ARCH. |
| Entries in the index table are SIZEOF_AR_INDEX bytes long. |
| Fills in ARCH->next_arhdr_offset and ARCH->arhdr. |
| If READ_SYMBOLS is true then fills in ARCH->index_num, ARCH->index_array, |
| ARCH->sym_size and ARCH->sym_table. |
| It is the caller's responsibility to free ARCH->index_array and |
| ARCH->sym_table. |
| Returns 1 upon success, 0 otherwise. |
| If failure occurs an error message is printed. */ |
| |
| static int |
| process_archive_index_and_symbols (struct archive_info *arch, |
| unsigned int sizeof_ar_index, |
| int read_symbols) |
| { |
| size_t got; |
| unsigned long size; |
| char fmag_save; |
| |
| fmag_save = arch->arhdr.ar_fmag[0]; |
| arch->arhdr.ar_fmag[0] = 0; |
| size = strtoul (arch->arhdr.ar_size, NULL, 10); |
| arch->arhdr.ar_fmag[0] = fmag_save; |
| /* PR 17531: file: 912bd7de. */ |
| if ((signed long) size < 0) |
| { |
| error (_("%s: invalid archive header size: %ld\n"), |
| arch->file_name, size); |
| return 0; |
| } |
| |
| size = size + (size & 1); |
| |
| arch->next_arhdr_offset += sizeof arch->arhdr + size; |
| |
| if (! read_symbols) |
| { |
| if (fseek (arch->file, size, SEEK_CUR) != 0) |
| { |
| error (_("%s: failed to skip archive symbol table\n"), |
| arch->file_name); |
| return 0; |
| } |
| } |
| else |
| { |
| unsigned long i; |
| /* A buffer used to hold numbers read in from an archive index. |
| These are always SIZEOF_AR_INDEX bytes long and stored in |
| big-endian format. */ |
| unsigned char integer_buffer[sizeof arch->index_num]; |
| unsigned char * index_buffer; |
| |
| assert (sizeof_ar_index <= sizeof integer_buffer); |
| |
| /* Check the size of the archive index. */ |
| if (size < sizeof_ar_index) |
| { |
| error (_("%s: the archive index is empty\n"), arch->file_name); |
| return 0; |
| } |
| |
| /* Read the number of entries in the archive index. */ |
| got = fread (integer_buffer, 1, sizeof_ar_index, arch->file); |
| if (got != sizeof_ar_index) |
| { |
| error (_("%s: failed to read archive index\n"), arch->file_name); |
| return 0; |
| } |
| |
| arch->index_num = byte_get_big_endian (integer_buffer, sizeof_ar_index); |
| size -= sizeof_ar_index; |
| |
| if (size < arch->index_num * sizeof_ar_index |
| /* PR 17531: file: 585515d1. */ |
| || size < arch->index_num) |
| { |
| error (_("%s: the archive index is supposed to have 0x%lx entries of %d bytes, but the size is only 0x%lx\n"), |
| arch->file_name, (long) arch->index_num, sizeof_ar_index, size); |
| return 0; |
| } |
| |
| /* Read in the archive index. */ |
| index_buffer = (unsigned char *) |
| malloc (arch->index_num * sizeof_ar_index); |
| if (index_buffer == NULL) |
| { |
| error (_("Out of memory whilst trying to read archive symbol index\n")); |
| return 0; |
| } |
| |
| got = fread (index_buffer, sizeof_ar_index, arch->index_num, arch->file); |
| if (got != arch->index_num) |
| { |
| free (index_buffer); |
| error (_("%s: failed to read archive index\n"), arch->file_name); |
| return 0; |
| } |
| |
| size -= arch->index_num * sizeof_ar_index; |
| |
| /* Convert the index numbers into the host's numeric format. */ |
| arch->index_array = (uint64_t *) |
| malloc (arch->index_num * sizeof (*arch->index_array)); |
| if (arch->index_array == NULL) |
| { |
| free (index_buffer); |
| error (_("Out of memory whilst trying to convert the archive symbol index\n")); |
| return 0; |
| } |
| |
| for (i = 0; i < arch->index_num; i++) |
| arch->index_array[i] = |
| byte_get_big_endian ((unsigned char *) (index_buffer + (i * sizeof_ar_index)), |
| sizeof_ar_index); |
| free (index_buffer); |
| |
| /* The remaining space in the header is taken up by the symbol table. */ |
| if (size < 1) |
| { |
| error (_("%s: the archive has an index but no symbols\n"), |
| arch->file_name); |
| return 0; |
| } |
| |
| arch->sym_table = (char *) malloc (size); |
| if (arch->sym_table == NULL) |
| { |
| error (_("Out of memory whilst trying to read archive index symbol table\n")); |
| return 0; |
| } |
| |
| arch->sym_size = size; |
| got = fread (arch->sym_table, 1, size, arch->file); |
| if (got != size) |
| { |
| error (_("%s: failed to read archive index symbol table\n"), |
| arch->file_name); |
| return 0; |
| } |
| } |
| |
| /* Read the next archive header. */ |
| got = fread (&arch->arhdr, 1, sizeof arch->arhdr, arch->file); |
| if (got != sizeof arch->arhdr && got != 0) |
| { |
| error (_("%s: failed to read archive header following archive index\n"), |
| arch->file_name); |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| /* Read the symbol table and long-name table from an archive. */ |
| |
| int |
| setup_archive (struct archive_info *arch, const char *file_name, |
| FILE *file, off_t file_size, |
| int is_thin_archive, int read_symbols) |
| { |
| size_t got; |
| |
| arch->file_name = strdup (file_name); |
| arch->file = file; |
| arch->index_num = 0; |
| arch->index_array = NULL; |
| arch->sym_table = NULL; |
| arch->sym_size = 0; |
| arch->longnames = NULL; |
| arch->longnames_size = 0; |
| arch->nested_member_origin = 0; |
| arch->is_thin_archive = is_thin_archive; |
| arch->uses_64bit_indices = 0; |
| arch->next_arhdr_offset = SARMAG; |
| |
| /* Read the first archive member header. */ |
| if (fseek (file, SARMAG, SEEK_SET) != 0) |
| { |
| error (_("%s: failed to seek to first archive header\n"), file_name); |
| return 1; |
| } |
| got = fread (&arch->arhdr, 1, sizeof arch->arhdr, file); |
| if (got != sizeof arch->arhdr) |
| { |
| if (got == 0) |
| return 0; |
| |
| error (_("%s: failed to read archive header\n"), file_name); |
| return 1; |
| } |
| |
| /* See if this is the archive symbol table. */ |
| if (startswith (arch->arhdr.ar_name, "/ ")) |
| { |
| if (! process_archive_index_and_symbols (arch, 4, read_symbols)) |
| return 1; |
| } |
| else if (startswith (arch->arhdr.ar_name, "/SYM64/ ")) |
| { |
| arch->uses_64bit_indices = 1; |
| if (! process_archive_index_and_symbols (arch, 8, read_symbols)) |
| return 1; |
| } |
| else if (read_symbols) |
| printf (_("%s has no archive index\n"), file_name); |
| |
| if (startswith (arch->arhdr.ar_name, "// ")) |
| { |
| /* This is the archive string table holding long member names. */ |
| char fmag_save = arch->arhdr.ar_fmag[0]; |
| arch->arhdr.ar_fmag[0] = 0; |
| arch->longnames_size = strtoul (arch->arhdr.ar_size, NULL, 10); |
| arch->arhdr.ar_fmag[0] = fmag_save; |
| /* PR 17531: file: 01068045. */ |
| if (arch->longnames_size < 8) |
| { |
| error (_("%s: long name table is too small, (size = %" PRId64 ")\n"), |
| file_name, arch->longnames_size); |
| return 1; |
| } |
| /* PR 17531: file: 639d6a26. */ |
| if ((off_t) arch->longnames_size > file_size |
| || (signed long) arch->longnames_size < 0) |
| { |
| error (_("%s: long name table is too big, (size = %#" PRIx64 ")\n"), |
| file_name, arch->longnames_size); |
| return 1; |
| } |
| |
| arch->next_arhdr_offset += sizeof arch->arhdr + arch->longnames_size; |
| |
| /* Plus one to allow for a string terminator. */ |
| arch->longnames = (char *) malloc (arch->longnames_size + 1); |
| if (arch->longnames == NULL) |
| { |
| error (_("Out of memory reading long symbol names in archive\n")); |
| return 1; |
| } |
| |
| if (fread (arch->longnames, arch->longnames_size, 1, file) != 1) |
| { |
| free (arch->longnames); |
| arch->longnames = NULL; |
| error (_("%s: failed to read long symbol name string table\n"), |
| file_name); |
| return 1; |
| } |
| |
| if ((arch->longnames_size & 1) != 0) |
| getc (file); |
| |
| arch->longnames[arch->longnames_size] = 0; |
| } |
| |
| return 0; |
| } |
| |
| /* Open and setup a nested archive, if not already open. */ |
| |
| int |
| setup_nested_archive (struct archive_info *nested_arch, |
| const char *member_file_name) |
| { |
| FILE * member_file; |
| struct stat statbuf; |
| |
| /* Have we already setup this archive? */ |
| if (nested_arch->file_name != NULL |
| && streq (nested_arch->file_name, member_file_name)) |
| return 0; |
| |
| /* Close previous file and discard cached information. */ |
| if (nested_arch->file != NULL) |
| { |
| fclose (nested_arch->file); |
| nested_arch->file = NULL; |
| } |
| release_archive (nested_arch); |
| |
| member_file = fopen (member_file_name, "rb"); |
| if (member_file == NULL) |
| return 1; |
| if (fstat (fileno (member_file), &statbuf) < 0) |
| return 1; |
| return setup_archive (nested_arch, member_file_name, member_file, |
| statbuf.st_size, 0, 0); |
| } |
| |
| /* Release the memory used for the archive information. */ |
| |
| void |
| release_archive (struct archive_info * arch) |
| { |
| free (arch->file_name); |
| free (arch->index_array); |
| free (arch->sym_table); |
| free (arch->longnames); |
| arch->file_name = NULL; |
| arch->index_array = NULL; |
| arch->sym_table = NULL; |
| arch->longnames = NULL; |
| } |
| |
| /* Get the name of an archive member from the current archive header. |
| For simple names, this will modify the ar_name field of the current |
| archive header. For long names, it will return a pointer to the |
| longnames table. For nested archives, it will open the nested archive |
| and get the name recursively. NESTED_ARCH is a single-entry cache so |
| we don't keep rereading the same information from a nested archive. */ |
| |
| char * |
| get_archive_member_name (struct archive_info *arch, |
| struct archive_info *nested_arch) |
| { |
| unsigned long j, k; |
| |
| if (arch->arhdr.ar_name[0] == '/') |
| { |
| /* We have a long name. */ |
| char *endp; |
| char *member_file_name; |
| char *member_name; |
| char fmag_save; |
| |
| if (arch->longnames == NULL || arch->longnames_size == 0) |
| { |
| error (_("Archive member uses long names, but no longname table found\n")); |
| return NULL; |
| } |
| |
| arch->nested_member_origin = 0; |
| fmag_save = arch->arhdr.ar_fmag[0]; |
| arch->arhdr.ar_fmag[0] = 0; |
| k = j = strtoul (arch->arhdr.ar_name + 1, &endp, 10); |
| if (arch->is_thin_archive && endp != NULL && * endp == ':') |
| arch->nested_member_origin = strtoul (endp + 1, NULL, 10); |
| arch->arhdr.ar_fmag[0] = fmag_save; |
| |
| if (j > arch->longnames_size) |
| { |
| error (_("Found long name index (%ld) beyond end of long name table\n"),j); |
| return NULL; |
| } |
| while ((j < arch->longnames_size) |
| && (arch->longnames[j] != '\n') |
| && (arch->longnames[j] != '\0')) |
| j++; |
| if (j > 0 && arch->longnames[j-1] == '/') |
| j--; |
| if (j > arch->longnames_size) |
| j = arch->longnames_size; |
| arch->longnames[j] = '\0'; |
| |
| if (!arch->is_thin_archive || arch->nested_member_origin == 0) |
| return xstrdup (arch->longnames + k); |
| |
| /* PR 17531: file: 2896dc8b. */ |
| if (k >= j) |
| { |
| error (_("Invalid Thin archive member name\n")); |
| return NULL; |
| } |
| |
| /* This is a proxy for a member of a nested archive. |
| Find the name of the member in that archive. */ |
| member_file_name = adjust_relative_path (arch->file_name, |
| arch->longnames + k, j - k); |
| if (member_file_name != NULL |
| && setup_nested_archive (nested_arch, member_file_name) == 0) |
| { |
| member_name = get_archive_member_name_at (nested_arch, |
| arch->nested_member_origin, |
| NULL); |
| if (member_name != NULL) |
| { |
| free (member_file_name); |
| return member_name; |
| } |
| } |
| free (member_file_name); |
| |
| /* Last resort: just return the name of the nested archive. */ |
| return xstrdup (arch->longnames + k); |
| } |
| |
| /* We have a normal (short) name. */ |
| for (j = 0; j < sizeof (arch->arhdr.ar_name); j++) |
| if (arch->arhdr.ar_name[j] == '/') |
| { |
| arch->arhdr.ar_name[j] = '\0'; |
| return xstrdup (arch->arhdr.ar_name); |
| } |
| |
| /* The full ar_name field is used. Don't rely on ar_date starting |
| with a zero byte. */ |
| { |
| char *name = xmalloc (sizeof (arch->arhdr.ar_name) + 1); |
| memcpy (name, arch->arhdr.ar_name, sizeof (arch->arhdr.ar_name)); |
| name[sizeof (arch->arhdr.ar_name)] = '\0'; |
| return name; |
| } |
| } |
| |
| /* Get the name of an archive member at a given OFFSET within an archive |
| ARCH. */ |
| |
| char * |
| get_archive_member_name_at (struct archive_info *arch, |
| unsigned long offset, |
| struct archive_info *nested_arch) |
| { |
| size_t got; |
| |
| if (fseek (arch->file, offset, SEEK_SET) != 0) |
| { |
| error (_("%s: failed to seek to next file name\n"), arch->file_name); |
| return NULL; |
| } |
| got = fread (&arch->arhdr, 1, sizeof arch->arhdr, arch->file); |
| if (got != sizeof arch->arhdr) |
| { |
| error (_("%s: failed to read archive header\n"), arch->file_name); |
| return NULL; |
| } |
| if (memcmp (arch->arhdr.ar_fmag, ARFMAG, 2) != 0) |
| { |
| error (_("%s: did not find a valid archive header\n"), |
| arch->file_name); |
| return NULL; |
| } |
| |
| return get_archive_member_name (arch, nested_arch); |
| } |
| |
| /* Construct a string showing the name of the archive member, qualified |
| with the name of the containing archive file. For thin archives, we |
| use square brackets to denote the indirection. For nested archives, |
| we show the qualified name of the external member inside the square |
| brackets (e.g., "thin.a[normal.a(foo.o)]"). */ |
| |
| char * |
| make_qualified_name (struct archive_info * arch, |
| struct archive_info * nested_arch, |
| const char *member_name) |
| { |
| const char * error_name = _("<corrupt>"); |
| size_t len; |
| char * name; |
| |
| len = strlen (arch->file_name) + strlen (member_name) + 3; |
| if (arch->is_thin_archive |
| && arch->nested_member_origin != 0) |
| { |
| /* PR 15140: Allow for corrupt thin archives. */ |
| if (nested_arch->file_name) |
| len += strlen (nested_arch->file_name) + 2; |
| else |
| len += strlen (error_name) + 2; |
| } |
| |
| name = (char *) malloc (len); |
| if (name == NULL) |
| { |
| error (_("Out of memory\n")); |
| return NULL; |
| } |
| |
| if (arch->is_thin_archive |
| && arch->nested_member_origin != 0) |
| { |
| if (nested_arch->file_name) |
| snprintf (name, len, "%s[%s(%s)]", arch->file_name, |
| nested_arch->file_name, member_name); |
| else |
| snprintf (name, len, "%s[%s(%s)]", arch->file_name, |
| error_name, member_name); |
| } |
| else if (arch->is_thin_archive) |
| snprintf (name, len, "%s[%s]", arch->file_name, member_name); |
| else |
| snprintf (name, len, "%s(%s)", arch->file_name, member_name); |
| |
| return name; |
| } |