From fb52b2f459b98125ab0cff0e16b421418541a8e1 Mon Sep 17 00:00:00 2001 From: Nick Clifton Date: Mon, 11 Aug 2003 09:15:55 +0000 Subject: Add ability for readelf to parse archives --- binutils/ChangeLog | 22 ++++ binutils/Makefile.am | 2 +- binutils/Makefile.in | 8 +- binutils/NEWS | 2 + binutils/doc/binutils.texi | 5 +- binutils/readelf.c | 275 +++++++++++++++++++++++++++++++++++++++------ 6 files changed, 272 insertions(+), 42 deletions(-) diff --git a/binutils/ChangeLog b/binutils/ChangeLog index 7b8db96..e0e1d13 100644 --- a/binutils/ChangeLog +++ b/binutils/ChangeLog @@ -1,3 +1,25 @@ +2003-08-11 Ian Lance Taylor + + * readelf.c: Add ability to read archives. + (archive_file_offset): New variable. + (archive_file_size): New variable. + (get_data): Include archive_file_offset in file offset + calculation when fseeking. + (process_program_headers): Likewise. + (process_symbol_table): Likewise. + (process_dynamic_segment): Handle computation of end of file + position when the file is in an archive. + (process_object): New function. Contains the body of + process_file(). + (process_archive): New function. Call process_object on each + member of an archive. + (process_file): Detect archives and handle appropriately. + * Makefile.am: Add dependency on aout/ar.h for readelf.c + * Makefile.in: Regenerate. + * NEWS: Document readelf's new ability. + * doc/binutils: Alter text to say that readelf supports archives + and 64-bit ELF files. + 2003-08-08 Nick Clifton * po/fr.po: Updated French translation. diff --git a/binutils/Makefile.am b/binutils/Makefile.am index 1d52738..63174e4 100644 --- a/binutils/Makefile.am +++ b/binutils/Makefile.am @@ -477,7 +477,7 @@ readelf.o: readelf.c ../bfd/bfd.h $(INCDIR)/ansidecl.h \ $(INCDIR)/elf/sparc.h $(INCDIR)/elf/v850.h $(INCDIR)/elf/vax.h \ $(INCDIR)/elf/x86-64.h $(INCDIR)/elf/xstormy16.h $(INCDIR)/elf/iq2000.h \ bucomm.h config.h $(INCDIR)/bin-bugs.h $(INCDIR)/fopen-same.h \ - unwind-ia64.h + unwind-ia64.h $(INCDIR)/aout/ar.h rename.o: rename.c ../bfd/bfd.h $(INCDIR)/ansidecl.h \ $(INCDIR)/symcat.h bucomm.h config.h $(INCDIR)/bin-bugs.h \ $(INCDIR)/fopen-same.h diff --git a/binutils/Makefile.in b/binutils/Makefile.in index 7005f08..96c9176 100644 --- a/binutils/Makefile.in +++ b/binutils/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated automatically by automake 1.4-p6 from Makefile.am +# Makefile.in generated automatically by automake 1.4-p5 from Makefile.am # Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation @@ -442,7 +442,7 @@ configure.in deflex.c defparse.c nlmheader.c rclex.c rcparse.c DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) -TAR = tar +TAR = gtar GZIP_ENV = --best SOURCES = $(nlmconv_SOURCES) $(srconv_SOURCES) $(sysdump_SOURCES) $(coffdump_SOURCES) $(dlltool_SOURCES) $(windres_SOURCES) $(dllwrap_SOURCES) $(size_SOURCES) $(objdump_SOURCES) $(ar_SOURCES) $(strings_SOURCES) $(ranlib_SOURCES) $(objcopy_SOURCES) $(addr2line_SOURCES) $(readelf_SOURCES) $(nm_new_SOURCES) $(strip_new_SOURCES) $(cxxfilt_SOURCES) OBJECTS = $(nlmconv_OBJECTS) $(srconv_OBJECTS) $(sysdump_OBJECTS) $(coffdump_OBJECTS) $(dlltool_OBJECTS) $(windres_OBJECTS) $(dllwrap_OBJECTS) $(size_OBJECTS) $(objdump_OBJECTS) $(ar_OBJECTS) $(strings_OBJECTS) $(ranlib_OBJECTS) $(objcopy_OBJECTS) $(addr2line_OBJECTS) $(readelf_OBJECTS) $(nm_new_OBJECTS) $(strip_new_OBJECTS) $(cxxfilt_OBJECTS) @@ -870,7 +870,7 @@ distclean-generic: -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) maintainer-clean-generic: - -test -z "arlex.cdeflex.crclex.carparse.harparse.cdefparse.hdefparse.cnlmheader.hnlmheader.crcparse.hrcparse.c" || rm -f arlex.c deflex.c rclex.c arparse.h arparse.c defparse.h defparse.c nlmheader.h nlmheader.c rcparse.h rcparse.c + -test -z "arlexldeflexlrclexlarparseharparsecdefparsehdefparsecnlmheaderhnlmheadercrcparsehrcparsec" || rm -f arlexl deflexl rclexl arparseh arparsec defparseh defparsec nlmheaderh nlmheaderc rcparseh rcparsec mostlyclean-am: mostlyclean-hdr mostlyclean-binPROGRAMS \ mostlyclean-noinstPROGRAMS mostlyclean-compile \ mostlyclean-libtool mostlyclean-tags \ @@ -1205,7 +1205,7 @@ readelf.o: readelf.c ../bfd/bfd.h $(INCDIR)/ansidecl.h \ $(INCDIR)/elf/sparc.h $(INCDIR)/elf/v850.h $(INCDIR)/elf/vax.h \ $(INCDIR)/elf/x86-64.h $(INCDIR)/elf/xstormy16.h $(INCDIR)/elf/iq2000.h \ bucomm.h config.h $(INCDIR)/bin-bugs.h $(INCDIR)/fopen-same.h \ - unwind-ia64.h + unwind-ia64.h $(INCDIR)/aout/ar.h rename.o: rename.c ../bfd/bfd.h $(INCDIR)/ansidecl.h \ $(INCDIR)/symcat.h bucomm.h config.h $(INCDIR)/bin-bugs.h \ $(INCDIR)/fopen-same.h diff --git a/binutils/NEWS b/binutils/NEWS index 0a8c0cf..3fa22e8 100644 --- a/binutils/NEWS +++ b/binutils/NEWS @@ -1,5 +1,7 @@ -*- text -*- +* readelf can now parse archives. + * objdump now accepts --debugging-tags to print the debug information in a format compatible with ctags tool. diff --git a/binutils/doc/binutils.texi b/binutils/doc/binutils.texi index f86169d..ce9dcf0 100644 --- a/binutils/doc/binutils.texi +++ b/binutils/doc/binutils.texi @@ -3078,9 +3078,8 @@ readelf [@option{-a}|@option{--all}] @command{readelf} displays information about one or more ELF format object files. The options control what particular information to display. -@var{elffile}@dots{} are the object files to be examined. At the -moment, @command{readelf} does not support examining archives, nor does it -support examining 64 bit ELF files. +@var{elffile}@dots{} are the object files to be examined. 32-bit and +64-bit ELF files are supported, as are archives containing ELF files. @c man end diff --git a/binutils/readelf.c b/binutils/readelf.c index b2366a6..25f5de6 100644 --- a/binutils/readelf.c +++ b/binutils/readelf.c @@ -90,11 +90,15 @@ #include "elf/iq2000.h" #include "elf/xtensa.h" +#include "aout/ar.h" + #include "bucomm.h" #include "getopt.h" #include "libiberty.h" char *program_name = "readelf"; +long archive_file_offset; +unsigned long archive_file_size; unsigned long dynamic_addr; bfd_size_type dynamic_size; char *dynamic_strings; @@ -244,9 +248,10 @@ get_data (void *var, FILE *file, long offset, size_t size, const char *reason) if (size == 0) return NULL; - if (fseek (file, offset, SEEK_SET)) + if (fseek (file, archive_file_offset + offset, SEEK_SET)) { - error (_("Unable to seek to 0x%x for %s\n"), offset, reason); + error (_("Unable to seek to 0x%x for %s\n"), + archive_file_offset + offset, reason); return NULL; } @@ -3064,7 +3069,8 @@ process_program_headers (FILE *file) break; case PT_INTERP: - if (fseek (file, (long) segment->p_offset, SEEK_SET)) + if (fseek (file, archive_file_offset + (long) segment->p_offset, + SEEK_SET)) error (_("Unable to find program interpreter name\n")); else { @@ -4503,10 +4509,16 @@ process_dynamic_segment (FILE *file) should work. */ section.sh_offset = offset_from_vma (file, entry->d_un.d_val, 0); - if (fseek (file, 0, SEEK_END)) - error (_("Unable to seek to end of file!")); + if (archive_file_offset != 0) + section.sh_size = archive_file_size - section.sh_offset; + else + { + if (fseek (file, 0, SEEK_END)) + error (_("Unable to seek to end of file!")); + + section.sh_size = ftell (file) - section.sh_offset; + } - section.sh_size = ftell (file) - section.sh_offset; if (is_32bit_elf) section.sh_entsize = sizeof (Elf32_External_Sym); else @@ -4544,9 +4556,15 @@ process_dynamic_segment (FILE *file) should work. */ offset = offset_from_vma (file, entry->d_un.d_val, 0); - if (fseek (file, 0, SEEK_END)) - error (_("Unable to seek to end of file\n")); - str_tab_len = ftell (file) - offset; + + if (archive_file_offset != 0) + str_tab_len = archive_file_size - offset; + else + { + if (fseek (file, 0, SEEK_END)) + error (_("Unable to seek to end of file\n")); + str_tab_len = ftell (file) - offset; + } if (str_tab_len < 1) { @@ -5598,8 +5616,10 @@ process_symbol_table (FILE *file) if (dynamic_info[DT_HASH] && ((do_using_dynamic && dynamic_strings != NULL) || do_histogram)) { - if (fseek (file, offset_from_vma (file, dynamic_info[DT_HASH], - sizeof nb + sizeof nc), + if (fseek (file, + (archive_file_offset + + offset_from_vma (file, dynamic_info[DT_HASH], + sizeof nb + sizeof nc)), SEEK_SET)) { error (_("Unable to seek to start of dynamic information")); @@ -10142,30 +10162,18 @@ get_file_header (FILE *file) 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_file (char *file_name) +process_object (char *file_name, FILE *file) { - FILE *file; - struct stat statbuf; unsigned int i; - if (stat (file_name, & statbuf) < 0) - { - error (_("Cannot stat input file %s.\n"), file_name); - return 1; - } - - file = fopen (file_name, "rb"); - if (file == NULL) - { - error (_("Input file %s not found.\n"), file_name); - return 1; - } - if (! get_file_header (file)) { error (_("%s: Failed to read file header\n"), file_name); - fclose (file); return 1; } @@ -10181,10 +10189,7 @@ process_file (char *file_name) printf (_("\nFile: %s\n"), file_name); if (! process_file_header ()) - { - fclose (file); - return 1; - } + return 1; if (! process_section_headers (file)) { @@ -10217,8 +10222,6 @@ process_file (char *file_name) process_arch_specific (file); - fclose (file); - if (program_headers) { free (program_headers); @@ -10260,6 +10263,210 @@ process_file (char *file_name) return 0; } +/* Process an ELF archive. The file is positioned just after the + ARMAG string. */ + +static int +process_archive (char *file_name, FILE *file) +{ + struct ar_hdr arhdr; + size_t got; + unsigned long size; + char *longnames = NULL; + unsigned long longnames_size = 0; + size_t file_name_size; + + show_name = 1; + + got = fread (&arhdr, 1, sizeof arhdr, file); + if (got != sizeof arhdr) + { + if (got == 0) + return 0; + + error (_("%s: failed to read archive header\n"), file_name); + return 1; + } + + if (memcmp (arhdr.ar_name, "/ ", 16) == 0) + { + /* This is the archive symbol table. Skip it. + FIXME: We should have an option to dump it. */ + size = strtoul (arhdr.ar_size, NULL, 10); + if (fseek (file, size + (size & 1), SEEK_CUR) != 0) + { + error (_("%s: failed to skip archive symbol table\n"), file_name); + return 1; + } + + got = fread (&arhdr, 1, sizeof arhdr, file); + if (got != sizeof arhdr) + { + if (got == 0) + return 0; + + error (_("%s: failed to read archive header\n"), file_name); + return 1; + } + } + + if (memcmp (arhdr.ar_name, "// ", 16) == 0) + { + /* This is the archive string table holding long member + names. */ + + longnames_size = strtoul (arhdr.ar_size, NULL, 10); + + longnames = malloc (longnames_size); + if (longnames == NULL) + { + error (_("Out of memory\n")); + return 1; + } + + if (fread (longnames, longnames_size, 1, file) != 1) + { + error(_("%s: failed to read string table\n"), file_name); + return 1; + } + + if ((longnames_size & 1) != 0) + getc (file); + + got = fread (&arhdr, 1, sizeof arhdr, file); + if (got != sizeof arhdr) + { + if (got == 0) + return 0; + + error (_("%s: failed to read archive header\n"), file_name); + return 1; + } + } + + file_name_size = strlen (file_name); + + while (1) + { + char *name; + char *nameend; + char *namealc; + + if (arhdr.ar_name[0] == '/') + { + unsigned long off; + + off = strtoul (arhdr.ar_name + 1, NULL, 10); + if (off >= longnames_size) + { + error (_("%s: invalid archive string table offset %lu\n"), off); + return 1; + } + + name = longnames + off; + nameend = memchr (name, '/', longnames_size - off); + } + else + { + name = arhdr.ar_name; + nameend = memchr (name, '/', 16); + } + + if (nameend == NULL) + { + error (_("%s: bad archive file name\n")); + return 1; + } + + namealc = malloc (file_name_size + (nameend - name) + 3); + if (namealc == NULL) + { + error (_("Out of memory\n")); + return 1; + } + + memcpy (namealc, file_name, file_name_size); + namealc[file_name_size] = '('; + memcpy (namealc + file_name_size + 1, name, nameend - name); + namealc[file_name_size + 1 + (nameend - name)] = ')'; + namealc[file_name_size + 2 + (nameend - name)] = '\0'; + + archive_file_offset = ftell (file); + archive_file_size = strtoul (arhdr.ar_size, NULL, 10); + + process_object (namealc, file); + + free (namealc); + + if (fseek (file, + (archive_file_offset + + archive_file_size + + (archive_file_size & 1)), + SEEK_SET) != 0) + { + error (_("%s: failed to seek to next archive header\n"), file_name); + return 1; + } + + got = fread (&arhdr, 1, sizeof arhdr, file); + if (got != sizeof arhdr) + { + if (got == 0) + return 0; + + error (_("%s: failed to read archive header\n"), file_name); + return 1; + } + } + + if (longnames != 0) + free (longnames); + + return 0; +} + +static int +process_file (char *file_name) +{ + FILE *file; + struct stat statbuf; + char armag[SARMAG]; + int ret; + + if (stat (file_name, &statbuf) < 0) + { + error (_("Cannot stat input file %s.\n"), file_name); + return 1; + } + + file = fopen (file_name, "rb"); + if (file == NULL) + { + error (_("Input file %s not found.\n"), file_name); + return 1; + } + + if (fread (armag, SARMAG, 1, file) != 1) + { + error (_("%s: Failed to read file header\n"), file_name); + fclose (file); + return 1; + } + + if (memcmp (armag, ARMAG, SARMAG) == 0) + ret = process_archive (file_name, file); + else + { + rewind (file); + archive_file_size = archive_file_offset = 0; + ret = process_object (file_name, file); + } + + fclose (file); + + return ret; +} + #ifdef SUPPORT_DISASSEMBLY /* Needed by the i386 disassembler. For extra credit, someone could fix this so that we insert symbolic addresses here, esp for GOT/PLT -- cgit v1.1