From 30fd33bb933183e837b6709e189f1122333b95e2 Mon Sep 17 00:00:00 2001 From: "H.J. Lu" Date: Wed, 6 Jan 2010 16:52:15 +0000 Subject: [PATCH] Add elfedit binutils/ 2010-01-06 H.J. Lu PR binutils/11131 * Makefile.am (ELFEDIT_PROG): New. (bin_PROGRAMS): Add $(ELFEDIT_PROG). (CFILES): Add elfedit.c. (elfedit_DEPENDENCIES): New. (elfedit_SOURCES): Likewise. (elfedit_LDADD): Likewise. * Makefile.in: Regenerated. * elfedit.c: New. * doc/binutils.texi: Document elfedit. * doc/Makefile.am (man_MANS): Add elfedit.1. (elfedit.1): New. * doc/Makefile.in: Regenerated. binutils/testsuite/ 2010-01-06 H.J. Lu PR binutils/11131 * binutils-all/elfedit-1.d: New. * binutils-all/elfedit.exp: Likewise. * config/default.exp (ELFEDIT): New. Set if it doesn't exist. (ELFEDITFLAGS): Likewise. * lib/utils-lib.exp (run_dump_test): Support elfedit. --- binutils/ChangeLog | 19 + binutils/Makefile.am | 10 +- binutils/Makefile.in | 27 +- binutils/doc/Makefile.am | 8 + binutils/doc/Makefile.in | 8 + binutils/doc/binutils.texi | 71 +- binutils/elfedit.c | 1110 +++++++++++++++++++ binutils/testsuite/ChangeLog | 11 + binutils/testsuite/binutils-all/elfedit-1.d | 16 + binutils/testsuite/binutils-all/elfedit.exp | 31 + binutils/testsuite/config/default.exp | 6 + binutils/testsuite/lib/utils-lib.exp | 3 + 12 files changed, 1309 insertions(+), 11 deletions(-) create mode 100644 binutils/elfedit.c create mode 100644 binutils/testsuite/binutils-all/elfedit-1.d create mode 100644 binutils/testsuite/binutils-all/elfedit.exp diff --git a/binutils/ChangeLog b/binutils/ChangeLog index ad9bdbd4eeb..d2c4b1eb911 100644 --- a/binutils/ChangeLog +++ b/binutils/ChangeLog @@ -1,3 +1,22 @@ +2010-01-06 H.J. Lu + + PR binutils/11131 + * Makefile.am (ELFEDIT_PROG): New. + (bin_PROGRAMS): Add $(ELFEDIT_PROG). + (CFILES): Add elfedit.c. + (elfedit_DEPENDENCIES): New. + (elfedit_SOURCES): Likewise. + (elfedit_LDADD): Likewise. + * Makefile.in: Regenerated. + + * elfedit.c: New. + + * doc/binutils.texi: Document elfedit. + + * doc/Makefile.am (man_MANS): Add elfedit.1. + (elfedit.1): New. + * doc/Makefile.in: Regenerated. + 2010-01-06 Tristan Gingold * nm.c (usage): Remove extra blanks. diff --git a/binutils/Makefile.am b/binutils/Makefile.am index db7a77352a2..873e5684340 100644 --- a/binutils/Makefile.am +++ b/binutils/Makefile.am @@ -39,6 +39,8 @@ STRINGS_PROG=strings READELF_PROG=readelf +ELFEDIT_PROG=elfedit + # These should all be the same program too. SIZE_PROG=size NM_PROG=nm-new @@ -58,7 +60,7 @@ DLLWRAP_PROG=dllwrap SRCONV_PROG=srconv$(EXEEXT) sysdump$(EXEEXT) coffdump$(EXEEXT) -bin_PROGRAMS = $(SIZE_PROG) $(OBJDUMP_PROG) $(AR_PROG) $(STRINGS_PROG) $(RANLIB_PROG) $(OBJCOPY_PROG) @BUILD_NLMCONV@ @BUILD_SRCONV@ @BUILD_DLLTOOL@ @BUILD_WINDRES@ @BUILD_WINDMC@ $(ADDR2LINE_PROG) $(READELF_PROG) @BUILD_DLLWRAP@ @BUILD_INSTALL_MISC@ +bin_PROGRAMS = $(SIZE_PROG) $(OBJDUMP_PROG) $(AR_PROG) $(STRINGS_PROG) $(RANLIB_PROG) $(OBJCOPY_PROG) @BUILD_NLMCONV@ @BUILD_SRCONV@ @BUILD_DLLTOOL@ @BUILD_WINDRES@ @BUILD_WINDMC@ $(ADDR2LINE_PROG) $(READELF_PROG) $(ELFEDIT_PROG) @BUILD_DLLWRAP@ @BUILD_INSTALL_MISC@ ## We need a special rule to install the programs which are built with ## -new, and to rename cxxfilt to c++filt. @@ -100,7 +102,7 @@ CFILES = \ rclex.c rdcoff.c rddbg.c readelf.c rename.c \ resbin.c rescoff.c resrc.c resres.c \ size.c srconv.c stabs.c strings.c sysdump.c \ - unwind-ia64.c version.c \ + unwind-ia64.c elfedit.c version.c \ windres.c winduni.c wrstabs.c \ windmc.c mclex.c @@ -179,6 +181,7 @@ windres_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB) windmc_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB) addr2line_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB) readelf_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) +elfedit_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) dllwrap_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) LDADD = $(BFDLIB) $(LIBIBERTY) $(LIBINTL) @@ -192,6 +195,9 @@ strings_SOURCES = strings.c $(BULIBS) readelf_SOURCES = readelf.c version.c unwind-ia64.c dwarf.c readelf_LDADD = $(LIBINTL) $(LIBIBERTY) +elfedit_SOURCES = elfedit.c version.c +elfedit_LDADD = $(LIBINTL) $(LIBIBERTY) + strip_new_SOURCES = objcopy.c is-strip.c rename.c $(WRITE_DEBUG_SRCS) $(BULIBS) nm_new_SOURCES = nm.c $(BULIBS) diff --git a/binutils/Makefile.in b/binutils/Makefile.in index 5b29a026f8e..8cc17460f63 100644 --- a/binutils/Makefile.in +++ b/binutils/Makefile.in @@ -39,8 +39,8 @@ bin_PROGRAMS = $(am__EXEEXT_6) $(am__EXEEXT_7) $(am__EXEEXT_8) \ $(am__EXEEXT_9) $(am__EXEEXT_10) $(am__EXEEXT_11) \ @BUILD_NLMCONV@ @BUILD_SRCONV@ @BUILD_DLLTOOL@ @BUILD_WINDRES@ \ @BUILD_WINDMC@ $(am__EXEEXT_12) $(am__EXEEXT_13) \ - @BUILD_DLLWRAP@ @BUILD_INSTALL_MISC@ $(am__empty) -noinst_PROGRAMS = $(am__EXEEXT_17) @BUILD_MISC@ + $(am__EXEEXT_14) @BUILD_DLLWRAP@ @BUILD_INSTALL_MISC@ +noinst_PROGRAMS = $(am__EXEEXT_18) @BUILD_MISC@ EXTRA_PROGRAMS = $(am__EXEEXT_1) srconv$(EXEEXT) sysdump$(EXEEXT) \ coffdump$(EXEEXT) $(am__EXEEXT_2) $(am__EXEEXT_3) \ $(am__EXEEXT_4) $(am__EXEEXT_5) @@ -93,11 +93,12 @@ am__EXEEXT_10 = ranlib$(EXEEXT) am__EXEEXT_11 = objcopy$(EXEEXT) am__EXEEXT_12 = addr2line$(EXEEXT) am__EXEEXT_13 = readelf$(EXEEXT) +am__EXEEXT_14 = elfedit$(EXEEXT) am__installdirs = "$(DESTDIR)$(bindir)" -am__EXEEXT_14 = nm-new$(EXEEXT) -am__EXEEXT_15 = strip-new$(EXEEXT) -am__EXEEXT_16 = cxxfilt$(EXEEXT) -am__EXEEXT_17 = $(am__EXEEXT_14) $(am__EXEEXT_15) $(am__EXEEXT_16) +am__EXEEXT_15 = nm-new$(EXEEXT) +am__EXEEXT_16 = strip-new$(EXEEXT) +am__EXEEXT_17 = cxxfilt$(EXEEXT) +am__EXEEXT_18 = $(am__EXEEXT_15) $(am__EXEEXT_16) $(am__EXEEXT_17) PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS) am__objects_1 = bucomm.$(OBJEXT) version.$(OBJEXT) filemode.$(OBJEXT) am_addr2line_OBJECTS = addr2line.$(OBJEXT) $(am__objects_1) @@ -161,6 +162,8 @@ strip_new_LDADD = $(LDADD) am_sysdump_OBJECTS = sysdump.$(OBJEXT) $(am__objects_1) sysdump_OBJECTS = $(am_sysdump_OBJECTS) sysdump_LDADD = $(LDADD) +am_elfedit_OBJECTS = elfedit.$(OBJEXT) version.$(OBJEXT) +elfedit_OBJECTS = $(am_elfedit_OBJECTS) am_windmc_OBJECTS = windmc.$(OBJEXT) mcparse.$(OBJEXT) mclex.$(OBJEXT) \ winduni.$(OBJEXT) $(am__objects_1) windmc_OBJECTS = $(am_windmc_OBJECTS) @@ -197,7 +200,7 @@ SOURCES = $(addr2line_SOURCES) $(ar_SOURCES) $(EXTRA_ar_SOURCES) \ $(objcopy_SOURCES) $(objdump_SOURCES) $(ranlib_SOURCES) \ $(readelf_SOURCES) $(size_SOURCES) $(srconv_SOURCES) \ $(strings_SOURCES) $(strip_new_SOURCES) $(sysdump_SOURCES) \ - $(windmc_SOURCES) $(windres_SOURCES) + $(elfedit_SOURCES) $(windmc_SOURCES) $(windres_SOURCES) RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ html-recursive info-recursive install-data-recursive \ install-dvi-recursive install-exec-recursive \ @@ -392,6 +395,7 @@ OBJCOPY_PROG = objcopy STRIP_PROG = strip-new STRINGS_PROG = strings READELF_PROG = readelf +ELFEDIT_PROG = elfedit # These should all be the same program too. SIZE_PROG = size @@ -440,7 +444,7 @@ CFILES = \ rclex.c rdcoff.c rddbg.c readelf.c rename.c \ resbin.c rescoff.c resrc.c resres.c \ size.c srconv.c stabs.c strings.c sysdump.c \ - unwind-ia64.c version.c \ + unwind-ia64.c elfedit.c version.c \ windres.c winduni.c wrstabs.c \ windmc.c mclex.c @@ -496,6 +500,7 @@ windres_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB) windmc_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB) addr2line_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) $(BFDLIB) readelf_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) +elfedit_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) dllwrap_DEPENDENCIES = $(LIBINTL_DEP) $(LIBIBERTY) LDADD = $(BFDLIB) $(LIBIBERTY) $(LIBINTL) size_SOURCES = size.c $(BULIBS) @@ -503,6 +508,8 @@ objcopy_SOURCES = objcopy.c not-strip.c rename.c $(WRITE_DEBUG_SRCS) $(BULIBS) strings_SOURCES = strings.c $(BULIBS) readelf_SOURCES = readelf.c version.c unwind-ia64.c dwarf.c readelf_LDADD = $(LIBINTL) $(LIBIBERTY) +elfedit_SOURCES = elfedit.c version.c +elfedit_LDADD = $(LIBINTL) $(LIBIBERTY) strip_new_SOURCES = objcopy.c is-strip.c rename.c $(WRITE_DEBUG_SRCS) $(BULIBS) nm_new_SOURCES = nm.c $(BULIBS) objdump_SOURCES = objdump.c dwarf.c prdbg.c $(DEBUG_SRCS) $(BULIBS) @@ -718,6 +725,9 @@ strip-new$(EXEEXT): $(strip_new_OBJECTS) $(strip_new_DEPENDENCIES) sysdump$(EXEEXT): $(sysdump_OBJECTS) $(sysdump_DEPENDENCIES) @rm -f sysdump$(EXEEXT) $(LINK) $(sysdump_OBJECTS) $(sysdump_LDADD) $(LIBS) +elfedit$(EXEEXT): $(elfedit_OBJECTS) $(elfedit_DEPENDENCIES) + @rm -f elfedit$(EXEEXT) + $(LINK) $(elfedit_OBJECTS) $(elfedit_LDADD) $(LIBS) mcparse.h: mcparse.c @if test ! -f $@; then \ rm -f mcparse.c; \ @@ -793,6 +803,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strings.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sysdump.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unwind-ia64.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/elfedit.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/version.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/windmc.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/windres.Po@am__quote@ diff --git a/binutils/doc/Makefile.am b/binutils/doc/Makefile.am index bdd470d2811..a2295ca2707 100644 --- a/binutils/doc/Makefile.am +++ b/binutils/doc/Makefile.am @@ -27,6 +27,7 @@ man_MANS = \ size.1 \ strings.1 \ strip.1 \ + elfedit.1 \ windres.1 \ windmc.1 \ $(DEMANGLER_NAME).1 @@ -124,6 +125,13 @@ strip.1: $(binutils_TEXI) $(binutils_TEXINFOS) mv -f $@.T$$$$ $@) || (rm -f $@.T$$$$ && exit 1) rm -f strip.pod +elfedit.1: $(binutils_TEXI) $(binutils_TEXINFOS) + touch $@ + -$(TEXI2POD) $(MANCONF) -Delfedit < $(binutils_TEXI) > elfedit.pod + -($(POD2MAN) elfedit.pod | sed -e '/^.if n .na/d' > $@.T$$$$ && \ + mv -f $@.T$$$$ $@) || (rm -f $@.T$$$$ && exit 1) + rm -f elfedit.pod + windres.1: $(binutils_TEXI) $(binutils_TEXINFOS) touch $@ -$(TEXI2POD) $(MANCONF) -Dwindres < $(binutils_TEXI) > windres.pod diff --git a/binutils/doc/Makefile.in b/binutils/doc/Makefile.in index 897e5d8bee5..31f4dd3d12f 100644 --- a/binutils/doc/Makefile.in +++ b/binutils/doc/Makefile.in @@ -286,6 +286,7 @@ man_MANS = \ size.1 \ strings.1 \ strip.1 \ + elfedit.1 \ windres.1 \ windmc.1 \ $(DEMANGLER_NAME).1 @@ -816,6 +817,13 @@ strip.1: $(binutils_TEXI) $(binutils_TEXINFOS) mv -f $@.T$$$$ $@) || (rm -f $@.T$$$$ && exit 1) rm -f strip.pod +elfedit.1: $(binutils_TEXI) $(binutils_TEXINFOS) + touch $@ + -$(TEXI2POD) $(MANCONF) -Delfedit < $(binutils_TEXI) > elfedit.pod + -($(POD2MAN) elfedit.pod | sed -e '/^.if n .na/d' > $@.T$$$$ && \ + mv -f $@.T$$$$ $@) || (rm -f $@.T$$$$ && exit 1) + rm -f elfedit.pod + windres.1: $(binutils_TEXI) $(binutils_TEXINFOS) touch $@ -$(TEXI2POD) $(MANCONF) -Dwindres < $(binutils_TEXI) > windres.pod diff --git a/binutils/doc/binutils.texi b/binutils/doc/binutils.texi index fa4fa26436e..60ac2bda456 100644 --- a/binutils/doc/binutils.texi +++ b/binutils/doc/binutils.texi @@ -44,6 +44,7 @@ section entitled ``GNU Free Documentation License''. * size: (binutils)size. List section sizes and total size. * strings: (binutils)strings. List printable strings from files. * strip: (binutils)strip. Discard symbols. +* elfedit: (binutils)elfedit. Update the ELF header of ELF files. * windmc: (binutils)windmc. Generator for Windows message resources. * windres: (binutils)windres. Manipulate Windows resources. @end direntry @@ -111,6 +112,9 @@ List printable strings from files @item strip Discard symbols +@item elfedit +Update the ELF header of ELF files. + @item c++filt Demangle encoded C++ symbols (on MS-DOS, this program is named @code{cxxfilt}) @@ -146,6 +150,7 @@ in the section entitled ``GNU Free Documentation License''. * size:: List section sizes and total size * strings:: List printable strings from files * strip:: Discard symbols +* elfedit:: Update the ELF header of ELF files * c++filt:: Filter to demangle encoded C++ symbols * cxxfilt: c++filt. MS-DOS name for c++filt * addr2line:: Convert addresses to file and line @@ -2717,7 +2722,7 @@ the Info entries for @file{binutils}. @c man end @end ignore -@node c++filt, addr2line, strip, Top +@node c++filt, addr2line, elfedit, Top @chapter c++filt @kindex c++filt @@ -4090,6 +4095,70 @@ objdump(1), and the Info entries for @file{binutils}. @c man end @end ignore +@node elfedit +@chapter elfedit + +@cindex Update ELF header +@kindex elfedit + +@c man title elfedit Update the ELF header of ELF files. + +@smallexample +@c man begin SYNOPSIS elfedit +elfedit [@option{--input-mach=}@var{machine}] + @option{--output-machine=}@var{machine} + [@option{-v}|@option{--version}] + [@option{-h}|@option{--help}] + @var{elffile}@dots{} +@c man end +@end smallexample + +@c man begin DESCRIPTION elfedit + +@command{elfedit} updates the ELF header of ELF files. The +options control how and which fields in the ELF header should be +updated. + +@var{elffile}@dots{} are the ELF files to be updated. 32-bit and +64-bit ELF files are supported, as are archives containing ELF files. +@c man end + +@c man begin OPTIONS elfedit + +The long and short forms of options, shown here as alternatives, are +equivalent. The @option{--output-mach} option must be given. + +@table @env + +@itemx --input-mach=@var{machine} +Only the input files with ELF machine type, @var{machine}, will be +updated. If @option{--input-mach} isn't specified, any ELF file +will be updated. + +The supported ELF machine types are, @var{L1OM} and @var{x86-64}. + +@itemx --output-mach=@var{machine} +Change the ELF machine type in the ELF header to @var{machine}. The +supported ELF machine types are the same as @option{--input-mach}. + +@item -v +@itemx --version +Display the version number of @command{elfedit}. + +@item -h +@itemx --help +Display the command line options understood by @command{elfedit}. + +@end table + +@c man end + +@ignore +@c man begin SEEALSO elfedit +readelf(1), and the Info entries for @file{binutils}. +@c man end +@end ignore + @node Common Options @chapter Common Options diff --git a/binutils/elfedit.c b/binutils/elfedit.c new file mode 100644 index 00000000000..28e40be3215 --- /dev/null +++ b/binutils/elfedit.c @@ -0,0 +1,1110 @@ +/* elfedit.c -- Update the ELF header of an ELF format file + Copyright 2010 + 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 "config.h" +#include "sysdep.h" +#include +#include + +#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 "bucomm.h" + +#include "elf/common.h" +#include "elf/external.h" +#include "elf/internal.h" + + +#include "aout/ar.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_class = -1; + +#define streq(a,b) (strcmp ((a), (b)) == 0) +#define strneq(a,b,n) (strncmp ((a), (b), (n)) == 0) +#define const_strneq(a,b) (strncmp ((a), (b), sizeof (b) - 1) == 0) + +void +non_fatal (const char *message, ...) +{ + va_list args; + + va_start (args, message); + fprintf (stderr, _("%s: Error: "), program_name); + vfprintf (stderr, message, args); + va_end (args); +} + +#define BYTE_GET(field) byte_get (field, sizeof (field)) +#define BYTE_PUT(field, val) byte_put (field, val, sizeof (field)) + +static bfd_vma (*byte_get) (unsigned char *, int); +static void (*byte_put) (unsigned char *, bfd_vma, int); + +static bfd_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 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 (bfd_vma) == 8) + return ((bfd_vma) (field[0])) + | (((bfd_vma) (field[1])) << 8) + | (((bfd_vma) (field[2])) << 16) + | (((bfd_vma) (field[3])) << 24) + | (((bfd_vma) (field[4])) << 32) + | (((bfd_vma) (field[5])) << 40) + | (((bfd_vma) (field[6])) << 48) + | (((bfd_vma) (field[7])) << 56); + else if (sizeof (bfd_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: + non_fatal (_("Unhandled data length: %d\n"), size); + abort (); + } +} + +static bfd_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 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 (bfd_vma) == 8) + return ((bfd_vma) (field[7])) + | (((bfd_vma) (field[6])) << 8) + | (((bfd_vma) (field[5])) << 16) + | (((bfd_vma) (field[4])) << 24) + | (((bfd_vma) (field[3])) << 32) + | (((bfd_vma) (field[2])) << 40) + | (((bfd_vma) (field[1])) << 48) + | (((bfd_vma) (field[0])) << 56); + else if (sizeof (bfd_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: + non_fatal (_("Unhandled data length: %d\n"), size); + abort (); + } +} + +static void +byte_put_little_endian (unsigned char * field, bfd_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; + field[2] = (value >> 16) & 0xff; + /* Fall through. */ + case 2: + field[1] = (value >> 8) & 0xff; + /* Fall through. */ + case 1: + field[0] = value & 0xff; + break; + + default: + non_fatal (_("Unhandled data length: %d\n"), size); + abort (); + } +} + +static void +byte_put_big_endian (unsigned char * field, bfd_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; + field[2] = (value >> 8) & 0xff; + value >>= 16; + /* Fall through. */ + case 2: + field[1] = value & 0xff; + value >>= 8; + /* Fall through. */ + case 1: + field[0] = value & 0xff; + break; + + default: + non_fatal (_("Unhandled data length: %d\n"), size); + abort (); + } +} + +static int +update_elf_header (const char *file_name, FILE *file) +{ + int status; + + 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) + { + non_fatal + (_("%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) + { + non_fatal + (_("%s: Unsupported EI_VERSION: %d is not %d\n"), + file_name, elf_header.e_ident[EI_VERSION], + EV_CURRENT); + return 0; + } + + /* Skip if class doesn't match. */ + if (input_elf_class == -1) + input_elf_class = elf_header.e_ident[EI_CLASS]; + else + if (elf_header.e_ident[EI_CLASS] != input_elf_class) + { + non_fatal + (_("%s: Unmatched EI_CLASS: %d is not %d\n"), + file_name, elf_header.e_ident[EI_CLASS], + input_elf_class); + return 0; + } + + /* Return if e_machine is the same as output_elf_machine. */ + if (output_elf_machine == elf_header.e_machine) + return 1; + + /* Skip if e_machine doesn't match. */ + if (input_elf_machine == -1) + input_elf_machine = elf_header.e_machine; + else if (elf_header.e_machine != input_elf_machine) + { + non_fatal + (_("%s: Unmatched e_machine: %d is not %d\n"), + file_name, elf_header.e_machine, input_elf_machine); + return 0; + } + + /* Update e_machine. */ + switch (input_elf_class) + { + default: + /* We should never get here. */ + abort (); + break; + case ELFCLASS32: + BYTE_PUT (ehdr32.e_machine, output_elf_machine); + status = fwrite (&ehdr32, sizeof (ehdr32), 1, file) == 1; + break; + case ELFCLASS64: + BYTE_PUT (ehdr64.e_machine, output_elf_machine); + status = fwrite (&ehdr64, sizeof (ehdr64), 1, file) == 1; + break; + } + + if (status != 1) + non_fatal (_("%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: + non_fatal (_("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) + { + non_fatal (_("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)) + { + non_fatal (_("%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) + { + non_fatal (_("%s: Failed to seek to ELF header\n"), file_name); + } + + if (! update_elf_header (file_name, file)) + return 1; + + return 0; +} + +/* 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. */ + +static char * +adjust_relative_path (const char *file_name, 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 = malloc (name_len + 1); + if (member_file_name == NULL) + { + non_fatal (_("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 = malloc (prefix_len + name_len + 1); + if (member_file_name == NULL) + { + non_fatal (_("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; +} + +/* Structure to hold information about an archive file. */ + +struct archive_info +{ + char * file_name; /* Archive file name. */ + FILE * file; /* Open file descriptor. */ + unsigned long index_num; /* Number of symbols in table. */ + unsigned long * index_array; /* The array of member offsets. */ + char * sym_table; /* The symbol table. */ + unsigned long sym_size; /* Size of the symbol table. */ + char * longnames; /* The long file names table. */ + unsigned long longnames_size; /* Size of the long file names table. */ + unsigned long nested_member_origin; /* Origin in the nested archive of the current member. */ + unsigned long next_arhdr_offset; /* Offset of the next archive header. */ + bfd_boolean is_thin_archive; /* TRUE if this is a thin archive. */ + struct ar_hdr arhdr; /* Current archive header. */ +}; + +/* Read the symbol table and long-name table from an archive. */ + +static int +setup_archive (struct archive_info * arch, const char * file_name, + FILE * file, bfd_boolean is_thin_archive) +{ + 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) + { + non_fatal (_("%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; + + non_fatal (_("%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 (fseek (file, size, SEEK_CUR) != 0) + { + non_fatal (_("%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; + non_fatal (_("%s: failed to read archive header following archive index\n"), + file_name); + return 1; + } + } + + 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 = malloc (arch->longnames_size); + if (arch->longnames == NULL) + { + non_fatal (_("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; + non_fatal (_("%s: failed to read long symbol name string table\n") + , file_name); + return 1; + } + + if ((arch->longnames_size & 1) != 0) + getc (file); + } + + return 0; +} + +/* Release the memory used for the archive information. */ + +static 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); +} + +/* Open and setup a nested archive, if not already open. */ + +static int +setup_nested_archive (struct archive_info * nested_arch, 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, "r+b"); + if (member_file == NULL) + return 1; + return setup_archive (nested_arch, member_file_name, member_file, + FALSE); +} + +static char * +get_archive_member_name_at (struct archive_info * arch, + unsigned long offset, + struct archive_info * nested_arch); + +/* 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. */ + +static 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)) != 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. */ + j = 0; + while ((arch->arhdr.ar_name[j] != '/') && (j < 16)) + j++; + arch->arhdr.ar_name[j] = '\0'; + return arch->arhdr.ar_name; +} + +/* Get the name of an archive member at a given OFFSET within an + archive ARCH. */ + +static 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) + { + non_fatal (_("%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) + { + non_fatal (_("%s: failed to read archive header\n"), + arch->file_name); + return NULL; + } + if (memcmp (arch->arhdr.ar_fmag, ARFMAG, 2) != 0) + { + non_fatal (_("%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)]"). */ + +static char * +make_qualified_name (struct archive_info * arch, + struct archive_info * nested_arch, + 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 = malloc (len); + if (name == NULL) + { + non_fatal (_("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; +} + +/* 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; + size_t file_name_size; + 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) != 0) + { + ret = 1; + goto out; + } + + file_name_size = strlen (file_name); + 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) + { + non_fatal (_("%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; + non_fatal (_("%s: failed to read archive header\n"), + file_name); + ret = 1; + break; + } + if (memcmp (arch.arhdr.ar_fmag, ARFMAG, 2) != 0) + { + non_fatal (_("%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) + { + non_fatal (_("%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) + { + non_fatal (_("%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) + { + non_fatal (_("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) + { + non_fatal (_("%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) + non_fatal (_("'%s': No such file\n"), file_name); + else + non_fatal (_("Could not locate '%s'. System error message: %s\n"), + file_name, strerror (errno)); + return 1; + } + + if (! S_ISREG (statbuf_p->st_mode)) + { + non_fatal (_("'%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) + { + non_fatal (_("Input file '%s' is not readable\n"), file_name); + return 1; + } + + if (fread (armag, SARMAG, 1, file) != 1) + { + non_fatal (_("%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; +} + +/* 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, "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; + + non_fatal (_("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_X86_64: + return ELFCLASS64; + case EM_NONE: + return ELFCLASSNONE; + default: + non_fatal (_("Unknown machine type: %d\n"), mach); + return -1; + } +} + +enum command_line_switch + { + OPTION_INPUT_MACH = 150, + OPTION_OUTPUT_MACH + }; + +static struct option options[] = +{ + {"input-mach", required_argument, 0, OPTION_INPUT_MACH}, + {"output-mach", required_argument, 0, OPTION_OUTPUT_MACH}, + {"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)] --output-mach 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 Set input machine type to \n\ + --output-mach Set output machine type to \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 'h': + usage (stdout, 0); + + case 'v': + print_version (program_name); + break; + + default: + usage (stderr, 1); + } + } + + if (optind == argc || output_elf_machine == -1) + usage (stderr, 1); + + status = 0; + while (optind < argc) + status |= process_file (argv[optind++]); + + return status; +} diff --git a/binutils/testsuite/ChangeLog b/binutils/testsuite/ChangeLog index 3001daf8d16..bd066e1f10b 100644 --- a/binutils/testsuite/ChangeLog +++ b/binutils/testsuite/ChangeLog @@ -1,3 +1,14 @@ +2010-01-06 H.J. Lu + + PR binutils/11131 + * binutils-all/elfedit-1.d: New. + * binutils-all/elfedit.exp: Likewise. + + * config/default.exp (ELFEDIT): New. Set if it doesn't exist. + (ELFEDITFLAGS): Likewise. + + * lib/utils-lib.exp (run_dump_test): Support elfedit. + 2009-10-28 Kai Tietz * binutils-all/dlltool.exp: Add tests for --no-leading-underscore diff --git a/binutils/testsuite/binutils-all/elfedit-1.d b/binutils/testsuite/binutils-all/elfedit-1.d new file mode 100644 index 00000000000..3459315a6df --- /dev/null +++ b/binutils/testsuite/binutils-all/elfedit-1.d @@ -0,0 +1,16 @@ +#PROG: elfedit +#elfedit: --output-mach l1om +#source: empty.s +#readelf: -h +#name: Update ELF header 1 +#target: x86_64-*-* + +#... +ELF Header: + Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 + Class: ELF64 + Data: 2's complement, little endian + Version: 1 \(current\) +#... + Machine: Intel L1OM +#... diff --git a/binutils/testsuite/binutils-all/elfedit.exp b/binutils/testsuite/binutils-all/elfedit.exp new file mode 100644 index 00000000000..d43c31a476f --- /dev/null +++ b/binutils/testsuite/binutils-all/elfedit.exp @@ -0,0 +1,31 @@ +# Copyright 2010 Free Software Foundation, Inc. + +# 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. + +# Exclude non-ELF targets. +if ![is_elf_format] { + verbose "$ELFEDIT is only intended for ELF targets" 2 + return +} + +if ![is_remote host] { + set tempfile tmpdir/bintest.o + set copyfile tmpdir/bintest +} else { + set tempfile [remote_download host tmpdir/bintest.o] + set copyfile bintest +} + +run_dump_test "elfedit-1" diff --git a/binutils/testsuite/config/default.exp b/binutils/testsuite/config/default.exp index f3fd6244e91..01fd8ccc306 100644 --- a/binutils/testsuite/config/default.exp +++ b/binutils/testsuite/config/default.exp @@ -61,6 +61,12 @@ if ![info exists READELF] then { if ![info exists READELFFLAGS] then { set READELFFLAGS "" } +if ![info exists ELFEDIT] then { + set ELFEDIT [findfile $base_dir/elfedit] +} +if ![info exists ELFEDITFLAGS] then { + set ELFEDITFLAGS "" +} if ![info exists WINDRES] then { set WINDRES [findfile $base_dir/windres] } diff --git a/binutils/testsuite/lib/utils-lib.exp b/binutils/testsuite/lib/utils-lib.exp index 9881f0133f4..393aae89cb8 100644 --- a/binutils/testsuite/lib/utils-lib.exp +++ b/binutils/testsuite/lib/utils-lib.exp @@ -281,6 +281,7 @@ proc run_dump_test { name {extra_options {}} } { global subdir srcdir global OBJDUMP NM OBJCOPY READELF STRIP global OBJDUMPFLAGS NMFLAGS OBJCOPYFLAGS READELFFLAGS STRIPFLAGS + global ELFEDIT ELFEDITFLAGS global host_triplet global env global copyfile @@ -309,6 +310,7 @@ proc run_dump_test { name {extra_options {}} } { set opts(size) {} set opts(strings) {} set opts(name) {} + set opts(elfedit) {} set opts(PROG) {} set opts(DUMPPROG) {} set opts(source) {} @@ -372,6 +374,7 @@ proc run_dump_test { name {extra_options {}} } { set destopt "-o" } strings { set program strings } + elfedit { set program elfedit } default { perror "unrecognized program option $opts(PROG) in $file.d" unresolved $testname -- 2.30.2