Add ability for readelf to parse archives

This commit is contained in:
Nick Clifton 2003-08-11 09:15:55 +00:00
parent e561512e36
commit fb52b2f459
6 changed files with 272 additions and 42 deletions

View File

@ -1,3 +1,25 @@
2003-08-11 Ian Lance Taylor <ian@airs.com>
* 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 <nickc@redhat.com>
* po/fr.po: Updated French translation.

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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