summaryrefslogtreecommitdiffstats
path: root/binutils-2.25/binutils/elfedit.c
diff options
context:
space:
mode:
Diffstat (limited to 'binutils-2.25/binutils/elfedit.c')
-rw-r--r--binutils-2.25/binutils/elfedit.c738
1 files changed, 738 insertions, 0 deletions
diff --git a/binutils-2.25/binutils/elfedit.c b/binutils-2.25/binutils/elfedit.c
new file mode 100644
index 00000000..d9e23a12
--- /dev/null
+++ b/binutils-2.25/binutils/elfedit.c
@@ -0,0 +1,738 @@
+/* elfedit.c -- Update the ELF header of an ELF format file
+ Copyright 2010, 2011, 2012
+ Free Software Foundation, Inc.
+
+ 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 <assert.h>
+
+#if __GNUC__ >= 2
+/* Define BFD64 here, even if our default architecture is 32 bit ELF
+ as this will allow us to read in and parse 64bit and 32bit ELF files.
+ Only do this if we believe that the compiler can support a 64 bit
+ data type. For now we only rely on GCC being able to do this. */
+#define BFD64
+#endif
+
+#include "bfd.h"
+#include "elfcomm.h"
+#include "bucomm.h"
+
+#include "elf/common.h"
+#include "elf/external.h"
+#include "elf/internal.h"
+
+#include "getopt.h"
+#include "libiberty.h"
+#include "safe-ctype.h"
+#include "filenames.h"
+
+char * program_name = "elfedit";
+static long archive_file_offset;
+static unsigned long archive_file_size;
+static Elf_Internal_Ehdr elf_header;
+static Elf32_External_Ehdr ehdr32;
+static Elf64_External_Ehdr ehdr64;
+static int input_elf_machine = -1;
+static int output_elf_machine = -1;
+static int input_elf_type = -1;
+static int output_elf_type = -1;
+static int input_elf_osabi = -1;
+static int output_elf_osabi = -1;
+static int input_elf_class = -1;
+
+static int
+update_elf_header (const char *file_name, FILE *file)
+{
+ int class, machine, type, status, osabi;
+
+ if (elf_header.e_ident[EI_MAG0] != ELFMAG0
+ || elf_header.e_ident[EI_MAG1] != ELFMAG1
+ || elf_header.e_ident[EI_MAG2] != ELFMAG2
+ || elf_header.e_ident[EI_MAG3] != ELFMAG3)
+ {
+ error
+ (_("%s: Not an ELF file - wrong magic bytes at the start\n"),
+ file_name);
+ return 0;
+ }
+
+ if (elf_header.e_ident[EI_VERSION] != EV_CURRENT)
+ {
+ error
+ (_("%s: Unsupported EI_VERSION: %d is not %d\n"),
+ file_name, elf_header.e_ident[EI_VERSION],
+ EV_CURRENT);
+ return 0;
+ }
+
+ /* Return if e_machine is the same as output_elf_machine. */
+ if (output_elf_machine == elf_header.e_machine)
+ return 1;
+
+ class = elf_header.e_ident[EI_CLASS];
+
+ /* Skip if class doesn't match. */
+ if (input_elf_class != -1 && class != input_elf_class)
+ {
+ error
+ (_("%s: Unmatched EI_CLASS: %d is not %d\n"),
+ file_name, class, input_elf_class);
+ return 0;
+ }
+
+ machine = elf_header.e_machine;
+
+ /* Skip if e_machine doesn't match. */
+ if (input_elf_machine != -1 && machine != input_elf_machine)
+ {
+ error
+ (_("%s: Unmatched e_machine: %d is not %d\n"),
+ file_name, machine, input_elf_machine);
+ return 0;
+ }
+
+ type = elf_header.e_type;
+
+ /* Skip if e_type doesn't match. */
+ if (input_elf_type != -1 && type != input_elf_type)
+ {
+ error
+ (_("%s: Unmatched e_type: %d is not %d\n"),
+ file_name, type, input_elf_type);
+ return 0;
+ }
+
+ osabi = elf_header.e_ident[EI_OSABI];
+
+ /* Skip if OSABI doesn't match. */
+ if (input_elf_osabi != -1 && osabi != input_elf_osabi)
+ {
+ error
+ (_("%s: Unmatched EI_OSABI: %d is not %d\n"),
+ file_name, osabi, input_elf_osabi);
+ return 0;
+ }
+
+ /* Update e_machine, e_type and EI_OSABI. */
+ switch (class)
+ {
+ default:
+ /* We should never get here. */
+ abort ();
+ break;
+ case ELFCLASS32:
+ if (output_elf_machine != -1)
+ BYTE_PUT (ehdr32.e_machine, output_elf_machine);
+ if (output_elf_type != -1)
+ BYTE_PUT (ehdr32.e_type, output_elf_type);
+ if (output_elf_osabi != -1)
+ ehdr32.e_ident[EI_OSABI] = output_elf_osabi;
+ status = fwrite (&ehdr32, sizeof (ehdr32), 1, file) == 1;
+ break;
+ case ELFCLASS64:
+ if (output_elf_machine != -1)
+ BYTE_PUT (ehdr64.e_machine, output_elf_machine);
+ if (output_elf_type != -1)
+ BYTE_PUT (ehdr64.e_type, output_elf_type);
+ if (output_elf_osabi != -1)
+ ehdr64.e_ident[EI_OSABI] = output_elf_osabi;
+ status = fwrite (&ehdr64, sizeof (ehdr64), 1, file) == 1;
+ break;
+ }
+
+ if (status != 1)
+ error (_("%s: Failed to update ELF header: %s\n"),
+ file_name, strerror (errno));
+
+ return status;
+}
+
+static int
+get_file_header (FILE * file)
+{
+ /* Read in the identity array. */
+ if (fread (elf_header.e_ident, EI_NIDENT, 1, file) != 1)
+ return 0;
+
+ /* Determine how to read the rest of the header. */
+ switch (elf_header.e_ident[EI_DATA])
+ {
+ default: /* fall through */
+ case ELFDATANONE: /* fall through */
+ case ELFDATA2LSB:
+ byte_get = byte_get_little_endian;
+ byte_put = byte_put_little_endian;
+ break;
+ case ELFDATA2MSB:
+ byte_get = byte_get_big_endian;
+ byte_put = byte_put_big_endian;
+ break;
+ }
+
+ /* Read in the rest of the header. For now we only support 32 bit
+ and 64 bit ELF files. */
+ switch (elf_header.e_ident[EI_CLASS])
+ {
+ default:
+ error (_("Unsupported EI_CLASS: %d\n"),
+ elf_header.e_ident[EI_CLASS]);
+ return 0;
+
+ case ELFCLASS32:
+ if (fread (ehdr32.e_type, sizeof (ehdr32) - EI_NIDENT,
+ 1, file) != 1)
+ return 0;
+
+ elf_header.e_type = BYTE_GET (ehdr32.e_type);
+ elf_header.e_machine = BYTE_GET (ehdr32.e_machine);
+ elf_header.e_version = BYTE_GET (ehdr32.e_version);
+ elf_header.e_entry = BYTE_GET (ehdr32.e_entry);
+ elf_header.e_phoff = BYTE_GET (ehdr32.e_phoff);
+ elf_header.e_shoff = BYTE_GET (ehdr32.e_shoff);
+ elf_header.e_flags = BYTE_GET (ehdr32.e_flags);
+ elf_header.e_ehsize = BYTE_GET (ehdr32.e_ehsize);
+ elf_header.e_phentsize = BYTE_GET (ehdr32.e_phentsize);
+ elf_header.e_phnum = BYTE_GET (ehdr32.e_phnum);
+ elf_header.e_shentsize = BYTE_GET (ehdr32.e_shentsize);
+ elf_header.e_shnum = BYTE_GET (ehdr32.e_shnum);
+ elf_header.e_shstrndx = BYTE_GET (ehdr32.e_shstrndx);
+
+ memcpy (&ehdr32, &elf_header, EI_NIDENT);
+ break;
+
+ case ELFCLASS64:
+ /* If we have been compiled with sizeof (bfd_vma) == 4, then
+ we will not be able to cope with the 64bit data found in
+ 64 ELF files. Detect this now and abort before we start
+ overwriting things. */
+ if (sizeof (bfd_vma) < 8)
+ {
+ error (_("This executable has been built without support for a\n\
+64 bit data type and so it cannot process 64 bit ELF files.\n"));
+ return 0;
+ }
+
+ if (fread (ehdr64.e_type, sizeof (ehdr64) - EI_NIDENT,
+ 1, file) != 1)
+ return 0;
+
+ elf_header.e_type = BYTE_GET (ehdr64.e_type);
+ elf_header.e_machine = BYTE_GET (ehdr64.e_machine);
+ elf_header.e_version = BYTE_GET (ehdr64.e_version);
+ elf_header.e_entry = BYTE_GET (ehdr64.e_entry);
+ elf_header.e_phoff = BYTE_GET (ehdr64.e_phoff);
+ elf_header.e_shoff = BYTE_GET (ehdr64.e_shoff);
+ elf_header.e_flags = BYTE_GET (ehdr64.e_flags);
+ elf_header.e_ehsize = BYTE_GET (ehdr64.e_ehsize);
+ elf_header.e_phentsize = BYTE_GET (ehdr64.e_phentsize);
+ elf_header.e_phnum = BYTE_GET (ehdr64.e_phnum);
+ elf_header.e_shentsize = BYTE_GET (ehdr64.e_shentsize);
+ elf_header.e_shnum = BYTE_GET (ehdr64.e_shnum);
+ elf_header.e_shstrndx = BYTE_GET (ehdr64.e_shstrndx);
+
+ memcpy (&ehdr64, &elf_header, EI_NIDENT);
+ break;
+ }
+ return 1;
+}
+
+/* Process one ELF object file according to the command line options.
+ This file may actually be stored in an archive. The file is
+ positioned at the start of the ELF object. */
+
+static int
+process_object (const char *file_name, FILE *file)
+{
+ /* Rememeber where we are. */
+ long offset = ftell (file);
+
+ if (! get_file_header (file))
+ {
+ error (_("%s: Failed to read ELF header\n"), file_name);
+ return 1;
+ }
+
+ /* Go to the position of the ELF header. */
+ if (fseek (file, offset, SEEK_SET) != 0)
+ {
+ error (_("%s: Failed to seek to ELF header\n"), file_name);
+ }
+
+ if (! update_elf_header (file_name, file))
+ return 1;
+
+ return 0;
+}
+
+/* Process an ELF archive.
+ On entry the file is positioned just after the ARMAG string. */
+
+static int
+process_archive (const char * file_name, FILE * file,
+ bfd_boolean is_thin_archive)
+{
+ struct archive_info arch;
+ struct archive_info nested_arch;
+ size_t got;
+ int ret;
+
+ /* The ARCH structure is used to hold information about this archive. */
+ arch.file_name = NULL;
+ arch.file = NULL;
+ arch.index_array = NULL;
+ arch.sym_table = NULL;
+ arch.longnames = NULL;
+
+ /* The NESTED_ARCH structure is used as a single-item cache of information
+ about a nested archive (when members of a thin archive reside within
+ another regular archive file). */
+ nested_arch.file_name = NULL;
+ nested_arch.file = NULL;
+ nested_arch.index_array = NULL;
+ nested_arch.sym_table = NULL;
+ nested_arch.longnames = NULL;
+
+ if (setup_archive (&arch, file_name, file, is_thin_archive, FALSE) != 0)
+ {
+ ret = 1;
+ goto out;
+ }
+
+ ret = 0;
+
+ while (1)
+ {
+ char * name;
+ size_t namelen;
+ char * qualified_name;
+
+ /* Read the next archive header. */
+ if (fseek (file, arch.next_arhdr_offset, SEEK_SET) != 0)
+ {
+ error (_("%s: failed to seek to next archive header\n"),
+ file_name);
+ return 1;
+ }
+ got = fread (&arch.arhdr, 1, sizeof arch.arhdr, file);
+ if (got != sizeof arch.arhdr)
+ {
+ if (got == 0)
+ break;
+ error (_("%s: failed to read archive header\n"),
+ file_name);
+ ret = 1;
+ break;
+ }
+ if (memcmp (arch.arhdr.ar_fmag, ARFMAG, 2) != 0)
+ {
+ error (_("%s: did not find a valid archive header\n"),
+ arch.file_name);
+ ret = 1;
+ break;
+ }
+
+ arch.next_arhdr_offset += sizeof arch.arhdr;
+
+ archive_file_size = strtoul (arch.arhdr.ar_size, NULL, 10);
+ if (archive_file_size & 01)
+ ++archive_file_size;
+
+ name = get_archive_member_name (&arch, &nested_arch);
+ if (name == NULL)
+ {
+ error (_("%s: bad archive file name\n"), file_name);
+ ret = 1;
+ break;
+ }
+ namelen = strlen (name);
+
+ qualified_name = make_qualified_name (&arch, &nested_arch, name);
+ if (qualified_name == NULL)
+ {
+ error (_("%s: bad archive file name\n"), file_name);
+ ret = 1;
+ break;
+ }
+
+ if (is_thin_archive && arch.nested_member_origin == 0)
+ {
+ /* This is a proxy for an external member of a thin archive. */
+ FILE *member_file;
+ char *member_file_name = adjust_relative_path (file_name,
+ name, namelen);
+ if (member_file_name == NULL)
+ {
+ ret = 1;
+ break;
+ }
+
+ member_file = fopen (member_file_name, "r+b");
+ if (member_file == NULL)
+ {
+ error (_("Input file '%s' is not readable\n"),
+ member_file_name);
+ free (member_file_name);
+ ret = 1;
+ break;
+ }
+
+ archive_file_offset = arch.nested_member_origin;
+
+ ret |= process_object (qualified_name, member_file);
+
+ fclose (member_file);
+ free (member_file_name);
+ }
+ else if (is_thin_archive)
+ {
+ /* This is a proxy for a member of a nested archive. */
+ archive_file_offset = arch.nested_member_origin + sizeof arch.arhdr;
+
+ /* The nested archive file will have been opened and setup by
+ get_archive_member_name. */
+ if (fseek (nested_arch.file, archive_file_offset,
+ SEEK_SET) != 0)
+ {
+ error (_("%s: failed to seek to archive member\n"),
+ nested_arch.file_name);
+ ret = 1;
+ break;
+ }
+
+ ret |= process_object (qualified_name, nested_arch.file);
+ }
+ else
+ {
+ archive_file_offset = arch.next_arhdr_offset;
+ arch.next_arhdr_offset += archive_file_size;
+
+ ret |= process_object (qualified_name, file);
+ }
+
+ free (qualified_name);
+ }
+
+ out:
+ if (nested_arch.file != NULL)
+ fclose (nested_arch.file);
+ release_archive (&nested_arch);
+ release_archive (&arch);
+
+ return ret;
+}
+
+static int
+check_file (const char *file_name, struct stat *statbuf_p)
+{
+ struct stat statbuf;
+
+ if (statbuf_p == NULL)
+ statbuf_p = &statbuf;
+
+ if (stat (file_name, statbuf_p) < 0)
+ {
+ if (errno == ENOENT)
+ error (_("'%s': No such file\n"), file_name);
+ else
+ error (_("Could not locate '%s'. System error message: %s\n"),
+ file_name, strerror (errno));
+ return 1;
+ }
+
+ if (! S_ISREG (statbuf_p->st_mode))
+ {
+ error (_("'%s' is not an ordinary file\n"), file_name);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+process_file (const char *file_name)
+{
+ FILE * file;
+ char armag[SARMAG];
+ int ret;
+
+ if (check_file (file_name, NULL))
+ return 1;
+
+ file = fopen (file_name, "r+b");
+ if (file == NULL)
+ {
+ error (_("Input file '%s' is not readable\n"), file_name);
+ return 1;
+ }
+
+ if (fread (armag, SARMAG, 1, file) != 1)
+ {
+ error (_("%s: Failed to read file's magic number\n"),
+ file_name);
+ fclose (file);
+ return 1;
+ }
+
+ if (memcmp (armag, ARMAG, SARMAG) == 0)
+ ret = process_archive (file_name, file, FALSE);
+ else if (memcmp (armag, ARMAGT, SARMAG) == 0)
+ ret = process_archive (file_name, file, TRUE);
+ else
+ {
+ rewind (file);
+ archive_file_size = archive_file_offset = 0;
+ ret = process_object (file_name, file);
+ }
+
+ fclose (file);
+
+ return ret;
+}
+
+static const struct
+{
+ int osabi;
+ const char *name;
+}
+osabis[] =
+{
+ { ELFOSABI_NONE, "none" },
+ { ELFOSABI_HPUX, "HPUX" },
+ { ELFOSABI_NETBSD, "NetBSD" },
+ { ELFOSABI_GNU, "GNU" },
+ { ELFOSABI_GNU, "Linux" },
+ { ELFOSABI_SOLARIS, "Solaris" },
+ { ELFOSABI_AIX, "AIX" },
+ { ELFOSABI_IRIX, "Irix" },
+ { ELFOSABI_FREEBSD, "FreeBSD" },
+ { ELFOSABI_TRU64, "TRU64" },
+ { ELFOSABI_MODESTO, "Modesto" },
+ { ELFOSABI_OPENBSD, "OpenBSD" },
+ { ELFOSABI_OPENVMS, "OpenVMS" },
+ { ELFOSABI_NSK, "NSK" },
+ { ELFOSABI_AROS, "AROS" },
+ { ELFOSABI_FENIXOS, "FenixOS" }
+};
+
+/* Return ELFOSABI_XXX for an OSABI string, OSABI. */
+
+static int
+elf_osabi (const char *osabi)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE (osabis); i++)
+ if (strcasecmp (osabi, osabis[i].name) == 0)
+ return osabis[i].osabi;
+
+ error (_("Unknown OSABI: %s\n"), osabi);
+
+ return -1;
+}
+
+/* Return EM_XXX for a machine string, MACH. */
+
+static int
+elf_machine (const char *mach)
+{
+ if (strcasecmp (mach, "l1om") == 0)
+ return EM_L1OM;
+ if (strcasecmp (mach, "k1om") == 0)
+ return EM_K1OM;
+ if (strcasecmp (mach, "x86_64") == 0)
+ return EM_X86_64;
+ if (strcasecmp (mach, "x86-64") == 0)
+ return EM_X86_64;
+ if (strcasecmp (mach, "none") == 0)
+ return EM_NONE;
+
+ error (_("Unknown machine type: %s\n"), mach);
+
+ return -1;
+}
+
+/* Return ELF class for a machine type, MACH. */
+
+static int
+elf_class (int mach)
+{
+ switch (mach)
+ {
+ case EM_L1OM:
+ case EM_K1OM:
+ case EM_X86_64:
+ return ELFCLASS64;
+ case EM_NONE:
+ return ELFCLASSNONE;
+ default:
+ error (_("Unknown machine type: %d\n"), mach);
+ return -1;
+ }
+}
+
+/* Return ET_XXX for a type string, TYPE. */
+
+static int
+elf_type (const char *type)
+{
+ if (strcasecmp (type, "rel") == 0)
+ return ET_REL;
+ if (strcasecmp (type, "exec") == 0)
+ return ET_EXEC;
+ if (strcasecmp (type, "dyn") == 0)
+ return ET_DYN;
+ if (strcasecmp (type, "none") == 0)
+ return ET_NONE;
+
+ error (_("Unknown type: %s\n"), type);
+
+ return -1;
+}
+
+enum command_line_switch
+ {
+ OPTION_INPUT_MACH = 150,
+ OPTION_OUTPUT_MACH,
+ OPTION_INPUT_TYPE,
+ OPTION_OUTPUT_TYPE,
+ OPTION_INPUT_OSABI,
+ OPTION_OUTPUT_OSABI
+ };
+
+static struct option options[] =
+{
+ {"input-mach", required_argument, 0, OPTION_INPUT_MACH},
+ {"output-mach", required_argument, 0, OPTION_OUTPUT_MACH},
+ {"input-type", required_argument, 0, OPTION_INPUT_TYPE},
+ {"output-type", required_argument, 0, OPTION_OUTPUT_TYPE},
+ {"input-osabi", required_argument, 0, OPTION_INPUT_OSABI},
+ {"output-osabi", required_argument, 0, OPTION_OUTPUT_OSABI},
+ {"version", no_argument, 0, 'v'},
+ {"help", no_argument, 0, 'h'},
+ {0, no_argument, 0, 0}
+};
+
+static void
+usage (FILE *stream, int exit_status)
+{
+ fprintf (stream, _("Usage: %s <option(s)> elffile(s)\n"),
+ program_name);
+ fprintf (stream, _(" Update the ELF header of ELF files\n"));
+ fprintf (stream, _(" The options are:\n"));
+ fprintf (stream, _("\
+ --input-mach <machine> Set input machine type to <machine>\n\
+ --output-mach <machine> Set output machine type to <machine>\n\
+ --input-type <type> Set input file type to <type>\n\
+ --output-type <type> Set output file type to <type>\n\
+ --input-osabi <osabi> Set input OSABI to <osabi>\n\
+ --output-osabi <osabi> Set output OSABI to <osabi>\n\
+ -h --help Display this information\n\
+ -v --version Display the version number of %s\n\
+"),
+ program_name);
+ if (REPORT_BUGS_TO[0] && exit_status == 0)
+ fprintf (stream, _("Report bugs to %s\n"), REPORT_BUGS_TO);
+ exit (exit_status);
+}
+
+int
+main (int argc, char ** argv)
+{
+ int c, status;
+
+#if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES)
+ setlocale (LC_MESSAGES, "");
+#endif
+#if defined (HAVE_SETLOCALE)
+ setlocale (LC_CTYPE, "");
+#endif
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+
+ expandargv (&argc, &argv);
+
+ while ((c = getopt_long (argc, argv, "hv",
+ options, (int *) 0)) != EOF)
+ {
+ switch (c)
+ {
+ case OPTION_INPUT_MACH:
+ input_elf_machine = elf_machine (optarg);
+ if (input_elf_machine < 0)
+ return 1;
+ input_elf_class = elf_class (input_elf_machine);
+ if (input_elf_class < 0)
+ return 1;
+ break;
+
+ case OPTION_OUTPUT_MACH:
+ output_elf_machine = elf_machine (optarg);
+ if (output_elf_machine < 0)
+ return 1;
+ break;
+
+ case OPTION_INPUT_TYPE:
+ input_elf_type = elf_type (optarg);
+ if (input_elf_type < 0)
+ return 1;
+ break;
+
+ case OPTION_OUTPUT_TYPE:
+ output_elf_type = elf_type (optarg);
+ if (output_elf_type < 0)
+ return 1;
+ break;
+
+ case OPTION_INPUT_OSABI:
+ input_elf_osabi = elf_osabi (optarg);
+ if (input_elf_osabi < 0)
+ return 1;
+ break;
+
+ case OPTION_OUTPUT_OSABI:
+ output_elf_osabi = elf_osabi (optarg);
+ if (output_elf_osabi < 0)
+ return 1;
+ break;
+
+ case 'h':
+ usage (stdout, 0);
+
+ case 'v':
+ print_version (program_name);
+ break;
+
+ default:
+ usage (stderr, 1);
+ }
+ }
+
+ if (optind == argc
+ || (output_elf_machine == -1
+ && output_elf_type == -1
+ && output_elf_osabi == -1))
+ usage (stderr, 1);
+
+ status = 0;
+ while (optind < argc)
+ status |= process_file (argv[optind++]);
+
+ return status;
+}