aboutsummaryrefslogtreecommitdiff
path: root/binutils/elfcomm.c
diff options
context:
space:
mode:
Diffstat (limited to 'binutils/elfcomm.c')
-rw-r--r--binutils/elfcomm.c658
1 files changed, 658 insertions, 0 deletions
diff --git a/binutils/elfcomm.c b/binutils/elfcomm.c
new file mode 100644
index 0000000..e44dee8
--- /dev/null
+++ b/binutils/elfcomm.c
@@ -0,0 +1,658 @@
+/* elfcomm.c -- common code for ELF format file.
+ Copyright 2010
+ 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. */
+
+#include "sysdep.h"
+#include "libiberty.h"
+#include "filenames.h"
+#include "bfd.h"
+#include "aout/ar.h"
+#include "bucomm.h"
+#include "elfcomm.h"
+
+void
+error (const char *message, ...)
+{
+ va_list args;
+
+ 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;
+
+ va_start (args, message);
+ fprintf (stderr, _("%s: Warning: "), program_name);
+ vfprintf (stderr, message, args);
+ va_end (args);
+}
+
+void (*byte_put) (unsigned char *, elf_vma, int);
+
+void
+byte_put_little_endian (unsigned char * field, elf_vma value, int size)
+{
+ switch (size)
+ {
+ case 8:
+ field[7] = (((value >> 24) >> 24) >> 8) & 0xff;
+ field[6] = ((value >> 24) >> 24) & 0xff;
+ field[5] = ((value >> 24) >> 16) & 0xff;
+ field[4] = ((value >> 24) >> 8) & 0xff;
+ /* Fall through. */
+ case 4:
+ field[3] = (value >> 24) & 0xff;
+ /* Fall through. */
+ case 3:
+ field[2] = (value >> 16) & 0xff;
+ /* Fall through. */
+ case 2:
+ field[1] = (value >> 8) & 0xff;
+ /* Fall through. */
+ case 1:
+ field[0] = value & 0xff;
+ break;
+
+ default:
+ error (_("Unhandled data length: %d\n"), size);
+ abort ();
+ }
+}
+
+void
+byte_put_big_endian (unsigned char * field, elf_vma value, int size)
+{
+ switch (size)
+ {
+ case 8:
+ field[7] = value & 0xff;
+ field[6] = (value >> 8) & 0xff;
+ field[5] = (value >> 16) & 0xff;
+ field[4] = (value >> 24) & 0xff;
+ value >>= 16;
+ value >>= 16;
+ /* Fall through. */
+ case 4:
+ field[3] = value & 0xff;
+ value >>= 8;
+ /* Fall through. */
+ case 3:
+ field[2] = value & 0xff;
+ value >>= 8;
+ /* Fall through. */
+ case 2:
+ field[1] = value & 0xff;
+ value >>= 8;
+ /* Fall through. */
+ case 1:
+ field[0] = value & 0xff;
+ break;
+
+ default:
+ error (_("Unhandled data length: %d\n"), size);
+ abort ();
+ }
+}
+
+elf_vma (*byte_get) (unsigned char *, int);
+
+elf_vma
+byte_get_little_endian (unsigned char *field, int size)
+{
+ switch (size)
+ {
+ case 1:
+ return *field;
+
+ case 2:
+ return ((unsigned int) (field[0]))
+ | (((unsigned int) (field[1])) << 8);
+
+ case 3:
+ return ((unsigned long) (field[0]))
+ | (((unsigned long) (field[1])) << 8)
+ | (((unsigned long) (field[2])) << 16);
+
+ case 4:
+ return ((unsigned long) (field[0]))
+ | (((unsigned long) (field[1])) << 8)
+ | (((unsigned long) (field[2])) << 16)
+ | (((unsigned long) (field[3])) << 24);
+
+ case 8:
+ if (sizeof (elf_vma) == 8)
+ return ((elf_vma) (field[0]))
+ | (((elf_vma) (field[1])) << 8)
+ | (((elf_vma) (field[2])) << 16)
+ | (((elf_vma) (field[3])) << 24)
+ | (((elf_vma) (field[4])) << 32)
+ | (((elf_vma) (field[5])) << 40)
+ | (((elf_vma) (field[6])) << 48)
+ | (((elf_vma) (field[7])) << 56);
+ else if (sizeof (elf_vma) == 4)
+ /* We want to extract data from an 8 byte wide field and
+ place it into a 4 byte wide field. Since this is a little
+ endian source we can just use the 4 byte extraction code. */
+ return ((unsigned long) (field[0]))
+ | (((unsigned long) (field[1])) << 8)
+ | (((unsigned long) (field[2])) << 16)
+ | (((unsigned long) (field[3])) << 24);
+
+ default:
+ error (_("Unhandled data length: %d\n"), size);
+ abort ();
+ }
+}
+
+elf_vma
+byte_get_big_endian (unsigned char *field, int size)
+{
+ switch (size)
+ {
+ case 1:
+ return *field;
+
+ case 2:
+ return ((unsigned int) (field[1])) | (((int) (field[0])) << 8);
+
+ case 3:
+ return ((unsigned long) (field[2]))
+ | (((unsigned long) (field[1])) << 8)
+ | (((unsigned long) (field[0])) << 16);
+
+ case 4:
+ return ((unsigned long) (field[3]))
+ | (((unsigned long) (field[2])) << 8)
+ | (((unsigned long) (field[1])) << 16)
+ | (((unsigned long) (field[0])) << 24);
+
+ case 8:
+ if (sizeof (elf_vma) == 8)
+ return ((elf_vma) (field[7]))
+ | (((elf_vma) (field[6])) << 8)
+ | (((elf_vma) (field[5])) << 16)
+ | (((elf_vma) (field[4])) << 24)
+ | (((elf_vma) (field[3])) << 32)
+ | (((elf_vma) (field[2])) << 40)
+ | (((elf_vma) (field[1])) << 48)
+ | (((elf_vma) (field[0])) << 56);
+ else if (sizeof (elf_vma) == 4)
+ {
+ /* Although we are extracing data from an 8 byte wide field,
+ we are returning only 4 bytes of data. */
+ field += 4;
+ return ((unsigned long) (field[3]))
+ | (((unsigned long) (field[2])) << 8)
+ | (((unsigned long) (field[1])) << 16)
+ | (((unsigned long) (field[0])) << 24);
+ }
+
+ default:
+ error (_("Unhandled data length: %d\n"), size);
+ abort ();
+ }
+}
+
+elf_vma
+byte_get_signed (unsigned char *field, int size)
+{
+ elf_vma x = byte_get (field, size);
+
+ switch (size)
+ {
+ case 1:
+ return (x ^ 0x80) - 0x80;
+ case 2:
+ return (x ^ 0x8000) - 0x8000;
+ case 4:
+ return (x ^ 0x80000000) - 0x80000000;
+ case 8:
+ 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,
+ int name_len)
+{
+ char * member_file_name;
+ const char * base_name = lbasename (file_name);
+
+ /* 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)
+ {
+ member_file_name = (char *) malloc (name_len + 1);
+ 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;
+ member_file_name = (char *) malloc (prefix_len + name_len + 1);
+ 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;
+}
+
+/* Read the symbol table and long-name table from an archive. */
+
+int
+setup_archive (struct archive_info *arch, const char *file_name,
+ FILE *file, bfd_boolean is_thin_archive,
+ bfd_boolean read_symbols)
+{
+ size_t got;
+ unsigned long size;
+
+ 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->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 (const_strneq (arch->arhdr.ar_name, "/ ")
+ || const_strneq (arch->arhdr.ar_name, "/SYM64/ "))
+ {
+ size = strtoul (arch->arhdr.ar_size, NULL, 10);
+ size = size + (size & 1);
+
+ arch->next_arhdr_offset += sizeof arch->arhdr + size;
+
+ if (read_symbols)
+ {
+ unsigned long i;
+ /* A buffer used to hold numbers read in from an archive index.
+ These are always 4 bytes long and stored in big-endian
+ format. */
+#define SIZEOF_AR_INDEX_NUMBERS 4
+ unsigned char integer_buffer[SIZEOF_AR_INDEX_NUMBERS];
+ unsigned char * index_buffer;
+
+ /* Check the size of the archive index. */
+ if (size < SIZEOF_AR_INDEX_NUMBERS)
+ {
+ error (_("%s: the archive index is empty\n"), file_name);
+ return 1;
+ }
+
+ /* Read the numer of entries in the archive index. */
+ got = fread (integer_buffer, 1, sizeof integer_buffer, file);
+ if (got != sizeof (integer_buffer))
+ {
+ error (_("%s: failed to read archive index\n"), file_name);
+ return 1;
+ }
+ arch->index_num = byte_get_big_endian (integer_buffer,
+ sizeof integer_buffer);
+ size -= SIZEOF_AR_INDEX_NUMBERS;
+
+ /* Read in the archive index. */
+ if (size < arch->index_num * SIZEOF_AR_INDEX_NUMBERS)
+ {
+ error (_("%s: the archive index is supposed to have %ld entries, but the size in the header is too small\n"),
+ file_name, arch->index_num);
+ return 1;
+ }
+ index_buffer = (unsigned char *)
+ malloc (arch->index_num * SIZEOF_AR_INDEX_NUMBERS);
+ if (index_buffer == NULL)
+ {
+ error (_("Out of memory whilst trying to read archive symbol index\n"));
+ return 1;
+ }
+ got = fread (index_buffer, SIZEOF_AR_INDEX_NUMBERS,
+ arch->index_num, file);
+ if (got != arch->index_num)
+ {
+ free (index_buffer);
+ error (_("%s: failed to read archive index\n"), file_name);
+ return 1;
+ }
+ size -= arch->index_num * SIZEOF_AR_INDEX_NUMBERS;
+
+ /* Convert the index numbers into the host's numeric format. */
+ arch->index_array = (long unsigned int *)
+ 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 1;
+ }
+
+ for (i = 0; i < arch->index_num; i++)
+ arch->index_array[i] = byte_get_big_endian ((unsigned char *) (index_buffer + (i * SIZEOF_AR_INDEX_NUMBERS)),
+ SIZEOF_AR_INDEX_NUMBERS);
+ 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"),
+ file_name);
+ return 1;
+ }
+ arch->sym_table = (char *) malloc (size);
+ arch->sym_size = size;
+ if (arch->sym_table == NULL)
+ {
+ error (_("Out of memory whilst trying to read archive index symbol table\n"));
+ return 1;
+ }
+ got = fread (arch->sym_table, 1, size, file);
+ if (got != size)
+ {
+ error (_("%s: failed to read archive index symbol table\n"),
+ file_name);
+ return 1;
+ }
+ }
+ else
+ {
+ if (fseek (file, size, SEEK_CUR) != 0)
+ {
+ error (_("%s: failed to skip archive symbol table\n"),
+ file_name);
+ return 1;
+ }
+ }
+
+ /* Read the next archive header. */
+ 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 following archive index\n"),
+ file_name);
+ return 1;
+ }
+ }
+ else if (read_symbols)
+ printf (_("%s has no archive index\n"), file_name);
+
+ if (const_strneq (arch->arhdr.ar_name, "// "))
+ {
+ /* This is the archive string table holding long member names. */
+ arch->longnames_size = strtoul (arch->arhdr.ar_size, NULL, 10);
+ arch->next_arhdr_offset += sizeof arch->arhdr + arch->longnames_size;
+
+ arch->longnames = (char *) malloc (arch->longnames_size);
+ 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);
+ }
+
+ 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;
+
+ /* 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);
+ release_archive (nested_arch);
+
+ member_file = fopen (member_file_name, "rb");
+ if (member_file == NULL)
+ return 1;
+ return setup_archive (nested_arch, member_file_name, member_file,
+ FALSE, FALSE);
+}
+
+/* Release the memory used for the archive information. */
+
+void
+release_archive (struct archive_info * arch)
+{
+ if (arch->file_name != NULL)
+ free (arch->file_name);
+ if (arch->index_array != NULL)
+ free (arch->index_array);
+ if (arch->sym_table != NULL)
+ free (arch->sym_table);
+ if (arch->longnames != NULL)
+ free (arch->longnames);
+}
+
+/* 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;
+
+ arch->nested_member_origin = 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);
+
+ while ((j < arch->longnames_size)
+ && (arch->longnames[j] != '\n')
+ && (arch->longnames[j] != '\0'))
+ j++;
+ if (arch->longnames[j-1] == '/')
+ j--;
+ arch->longnames[j] = '\0';
+
+ if (!arch->is_thin_archive || arch->nested_member_origin == 0)
+ return arch->longnames + k;
+
+ /* 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 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 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)
+{
+ size_t len;
+ char * name;
+
+ len = strlen (arch->file_name) + strlen (member_name) + 3;
+ if (arch->is_thin_archive && arch->nested_member_origin != 0)
+ len += strlen (nested_arch->file_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)
+ snprintf (name, len, "%s[%s(%s)]", arch->file_name,
+ nested_arch->file_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;
+}