Blame src/elfcmp.c

Packit 032894
/* Compare relevant content of two ELF files.
Packit 032894
   Copyright (C) 2005-2012, 2014, 2015 Red Hat, Inc.
Packit 032894
   This file is part of elfutils.
Packit 032894
   Written by Ulrich Drepper <drepper@redhat.com>, 2005.
Packit 032894
Packit 032894
   This file is free software; you can redistribute it and/or modify
Packit 032894
   it under the terms of the GNU General Public License as published by
Packit 032894
   the Free Software Foundation; either version 3 of the License, or
Packit 032894
   (at your option) any later version.
Packit 032894
Packit 032894
   elfutils is distributed in the hope that it will be useful, but
Packit 032894
   WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 032894
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 032894
   GNU General Public License for more details.
Packit 032894
Packit 032894
   You should have received a copy of the GNU General Public License
Packit 032894
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit 032894
Packit 032894
#ifdef HAVE_CONFIG_H
Packit 032894
# include <config.h>
Packit 032894
#endif
Packit 032894
Packit 032894
#include <argp.h>
Packit 032894
#include <assert.h>
Packit 032894
#include <errno.h>
Packit 032894
#include <fcntl.h>
Packit 032894
#include <locale.h>
Packit 032894
#include <libintl.h>
Packit 032894
#include <stdbool.h>
Packit 032894
#include <stdio.h>
Packit 032894
#include <stdlib.h>
Packit 032894
#include <string.h>
Packit 032894
#include <unistd.h>
Packit 032894
Packit 032894
#include <printversion.h>
Packit 032894
#include "../libelf/elf-knowledge.h"
Packit 032894
#include "../libebl/libeblP.h"
Packit 032894
#include "system.h"
Packit 032894
Packit 032894
/* Prototypes of local functions.  */
Packit 032894
static Elf *open_file (const char *fname, int *fdp, Ebl **eblp);
Packit 032894
static bool search_for_copy_reloc (Ebl *ebl, size_t scnndx, int symndx);
Packit 032894
static  int regioncompare (const void *p1, const void *p2);
Packit 032894
Packit 032894
Packit 032894
/* Name and version of program.  */
Packit 032894
ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
Packit 032894
Packit 032894
/* Bug report address.  */
Packit 032894
ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
Packit 032894
Packit 032894
/* Values for the parameters which have no short form.  */
Packit 032894
#define OPT_GAPS		0x100
Packit 032894
#define OPT_HASH_INEXACT	0x101
Packit 032894
#define OPT_IGNORE_BUILD_ID	0x102
Packit 032894
Packit 032894
/* Definitions of arguments for argp functions.  */
Packit 032894
static const struct argp_option options[] =
Packit 032894
{
Packit 032894
  { NULL, 0, NULL, 0, N_("Control options:"), 0 },
Packit 032894
  { "verbose", 'l', NULL, 0,
Packit 032894
    N_("Output all differences, not just the first"), 0 },
Packit 032894
  { "gaps", OPT_GAPS, "ACTION", 0, N_("Control treatment of gaps in loadable segments [ignore|match] (default: ignore)"), 0 },
Packit 032894
  { "hash-inexact", OPT_HASH_INEXACT, NULL, 0,
Packit 032894
    N_("Ignore permutation of buckets in SHT_HASH section"), 0 },
Packit 032894
  { "ignore-build-id", OPT_IGNORE_BUILD_ID, NULL, 0,
Packit 032894
    N_("Ignore differences in build ID"), 0 },
Packit 032894
  { "quiet", 'q', NULL, 0, N_("Output nothing; yield exit status only"), 0 },
Packit 032894
Packit 032894
  { NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 },
Packit 032894
  { NULL, 0, NULL, 0, NULL, 0 }
Packit 032894
};
Packit 032894
Packit 032894
/* Short description of program.  */
Packit 032894
static const char doc[] = N_("\
Packit 032894
Compare relevant parts of two ELF files for equality.");
Packit 032894
Packit 032894
/* Strings for arguments in help texts.  */
Packit 032894
static const char args_doc[] = N_("FILE1 FILE2");
Packit 032894
Packit 032894
/* Prototype for option handler.  */
Packit 032894
static error_t parse_opt (int key, char *arg, struct argp_state *state);
Packit 032894
Packit 032894
/* Data structure to communicate with argp functions.  */
Packit 032894
static struct argp argp =
Packit 032894
{
Packit 032894
  options, parse_opt, args_doc, doc, NULL, NULL, NULL
Packit 032894
};
Packit 032894
Packit 032894
Packit 032894
/* How to treat gaps in loadable segments.  */
Packit 032894
static enum
Packit 032894
  {
Packit 032894
    gaps_ignore = 0,
Packit 032894
    gaps_match
Packit 032894
  }
Packit 032894
  gaps;
Packit 032894
Packit 032894
/* Structure to hold information about used regions.  */
Packit 032894
struct region
Packit 032894
{
Packit 032894
  GElf_Addr from;
Packit 032894
  GElf_Addr to;
Packit 032894
  struct region *next;
Packit 032894
};
Packit 032894
Packit 032894
/* Nonzero if only exit status is wanted.  */
Packit 032894
static bool quiet;
Packit 032894
Packit 032894
/* True iff multiple differences should be output.  */
Packit 032894
static bool verbose;
Packit 032894
Packit 032894
/* True iff SHT_HASH treatment should be generous.  */
Packit 032894
static bool hash_inexact;
Packit 032894
Packit 032894
/* True iff build ID notes should be ignored.  */
Packit 032894
static bool ignore_build_id;
Packit 032894
Packit 032894
static bool hash_content_equivalent (size_t entsize, Elf_Data *, Elf_Data *);
Packit 032894
Packit 032894
Packit 032894
int
Packit 032894
main (int argc, char *argv[])
Packit 032894
{
Packit 032894
  /* Set locale.  */
Packit 032894
  (void) setlocale (LC_ALL, "");
Packit 032894
Packit 032894
  /* Make sure the message catalog can be found.  */
Packit 032894
  (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
Packit 032894
Packit 032894
  /* Initialize the message catalog.  */
Packit 032894
  (void) textdomain (PACKAGE_TARNAME);
Packit 032894
Packit 032894
  /* Parse and process arguments.  */
Packit 032894
  int remaining;
Packit 032894
  (void) argp_parse (&argp, argc, argv, 0, &remaining, NULL);
Packit 032894
Packit 032894
  /* We expect exactly two non-option parameters.  */
Packit 032894
  if (unlikely (remaining + 2 != argc))
Packit 032894
    {
Packit 032894
      fputs (gettext ("Invalid number of parameters.\n"), stderr);
Packit 032894
      argp_help (&argp, stderr, ARGP_HELP_SEE, program_invocation_short_name);
Packit 032894
      exit (1);
Packit 032894
    }
Packit 032894
Packit 032894
  if (quiet)
Packit 032894
    verbose = false;
Packit 032894
Packit 032894
  /* Comparing the files is done in two phases:
Packit 032894
     1. compare all sections.  Sections which are irrelevant (i.e., if
Packit 032894
	strip would remove them) are ignored.  Some section types are
Packit 032894
	handled special.
Packit 032894
     2. all parts of the loadable segments which are not parts of any
Packit 032894
	section is compared according to the rules of the --gaps option.
Packit 032894
  */
Packit 032894
  int result = 0;
Packit 032894
  elf_version (EV_CURRENT);
Packit 032894
Packit 032894
  const char *const fname1 = argv[remaining];
Packit 032894
  int fd1;
Packit 032894
  Ebl *ebl1;
Packit 032894
  Elf *elf1 = open_file (fname1, &fd1, &ebl1);
Packit 032894
Packit 032894
  const char *const fname2 = argv[remaining + 1];
Packit 032894
  int fd2;
Packit 032894
  Ebl *ebl2;
Packit 032894
  Elf *elf2 = open_file (fname2, &fd2, &ebl2);
Packit 032894
Packit 032894
  GElf_Ehdr ehdr1_mem;
Packit 032894
  GElf_Ehdr *ehdr1 = gelf_getehdr (elf1, &ehdr1_mem);
Packit 032894
  if (ehdr1 == NULL)
Packit 032894
    error (2, 0, gettext ("cannot get ELF header of '%s': %s"),
Packit 032894
	   fname1, elf_errmsg (-1));
Packit 032894
  GElf_Ehdr ehdr2_mem;
Packit 032894
  GElf_Ehdr *ehdr2 = gelf_getehdr (elf2, &ehdr2_mem);
Packit 032894
  if (ehdr2 == NULL)
Packit 032894
    error (2, 0, gettext ("cannot get ELF header of '%s': %s"),
Packit 032894
	   fname2, elf_errmsg (-1));
Packit 032894
Packit 032894
#define DIFFERENCE							      \
Packit 032894
  do									      \
Packit 032894
    {									      \
Packit 032894
      result = 1;							      \
Packit 032894
      if (! verbose)							      \
Packit 032894
	goto out;							      \
Packit 032894
    }									      \
Packit 032894
  while (0)
Packit 032894
Packit 032894
  /* Compare the ELF headers.  */
Packit 032894
  if (unlikely (memcmp (ehdr1->e_ident, ehdr2->e_ident, EI_NIDENT) != 0
Packit 032894
		|| ehdr1->e_type != ehdr2->e_type
Packit 032894
		|| ehdr1->e_machine != ehdr2->e_machine
Packit 032894
		|| ehdr1->e_version != ehdr2->e_version
Packit 032894
		|| ehdr1->e_entry != ehdr2->e_entry
Packit 032894
		|| ehdr1->e_phoff != ehdr2->e_phoff
Packit 032894
		|| ehdr1->e_flags != ehdr2->e_flags
Packit 032894
		|| ehdr1->e_ehsize != ehdr2->e_ehsize
Packit 032894
		|| ehdr1->e_phentsize != ehdr2->e_phentsize
Packit 032894
		|| ehdr1->e_phnum != ehdr2->e_phnum
Packit 032894
		|| ehdr1->e_shentsize != ehdr2->e_shentsize))
Packit 032894
    {
Packit 032894
      if (! quiet)
Packit 032894
	error (0, 0, gettext ("%s %s diff: ELF header"), fname1, fname2);
Packit 032894
      DIFFERENCE;
Packit 032894
    }
Packit 032894
Packit 032894
  size_t shnum1;
Packit 032894
  size_t shnum2;
Packit 032894
  if (unlikely (elf_getshdrnum (elf1, &shnum1) != 0))
Packit 032894
    error (2, 0, gettext ("cannot get section count of '%s': %s"),
Packit 032894
	   fname1, elf_errmsg (-1));
Packit 032894
  if (unlikely (elf_getshdrnum (elf2, &shnum2) != 0))
Packit 032894
    error (2, 0, gettext ("cannot get section count of '%s': %s"),
Packit 032894
	   fname2, elf_errmsg (-1));
Packit 032894
  if (unlikely (shnum1 != shnum2))
Packit 032894
    {
Packit 032894
      if (! quiet)
Packit 032894
	error (0, 0, gettext ("%s %s diff: section count"), fname1, fname2);
Packit 032894
      DIFFERENCE;
Packit 032894
    }
Packit 032894
Packit 032894
  size_t phnum1;
Packit 032894
  size_t phnum2;
Packit 032894
  if (unlikely (elf_getphdrnum (elf1, &phnum1) != 0))
Packit 032894
    error (2, 0, gettext ("cannot get program header count of '%s': %s"),
Packit 032894
	   fname1, elf_errmsg (-1));
Packit 032894
  if (unlikely (elf_getphdrnum (elf2, &phnum2) != 0))
Packit 032894
    error (2, 0, gettext ("cannot get program header count of '%s': %s"),
Packit 032894
	   fname2, elf_errmsg (-1));
Packit 032894
  if (unlikely (phnum1 != phnum2))
Packit 032894
    {
Packit 032894
      if (! quiet)
Packit 032894
	error (0, 0, gettext ("%s %s diff: program header count"),
Packit 032894
	       fname1, fname2);
Packit 032894
      DIFFERENCE;
Packit 032894
    }
Packit 032894
Packit 032894
  size_t shstrndx1;
Packit 032894
  size_t shstrndx2;
Packit 032894
  if (elf_getshdrstrndx (elf1, &shstrndx1) != 0)
Packit 032894
    error (2, 0, gettext ("cannot get hdrstrndx of '%s': %s"),
Packit 032894
	   fname1, elf_errmsg (-1));
Packit 032894
  if (elf_getshdrstrndx (elf2, &shstrndx2) != 0)
Packit 032894
    error (2, 0, gettext ("cannot get hdrstrndx of '%s': %s"),
Packit 032894
	   fname2, elf_errmsg (-1));
Packit 032894
  if (shstrndx1 != shstrndx2)
Packit 032894
    {
Packit 032894
      if (! quiet)
Packit 032894
	error (0, 0, gettext ("%s %s diff: shdr string index"),
Packit 032894
	       fname1, fname2);
Packit 032894
      DIFFERENCE;
Packit 032894
    }
Packit 032894
Packit 032894
  /* Iterate over all sections.  We expect the sections in the two
Packit 032894
     files to match exactly.  */
Packit 032894
  Elf_Scn *scn1 = NULL;
Packit 032894
  Elf_Scn *scn2 = NULL;
Packit 032894
  struct region *regions = NULL;
Packit 032894
  size_t nregions = 0;
Packit 032894
  while (1)
Packit 032894
    {
Packit 032894
      GElf_Shdr shdr1_mem;
Packit 032894
      GElf_Shdr *shdr1;
Packit 032894
      const char *sname1 = NULL;
Packit 032894
      do
Packit 032894
	{
Packit 032894
	  scn1 = elf_nextscn (elf1, scn1);
Packit 032894
	  shdr1 = gelf_getshdr (scn1, &shdr1_mem);
Packit 032894
	  if (shdr1 != NULL)
Packit 032894
	    sname1 = elf_strptr (elf1, shstrndx1, shdr1->sh_name);
Packit 032894
	}
Packit 032894
      while (scn1 != NULL && shdr1 != NULL
Packit 032894
	     && ebl_section_strip_p (ebl1, shdr1, sname1, true, false));
Packit 032894
Packit 032894
      GElf_Shdr shdr2_mem;
Packit 032894
      GElf_Shdr *shdr2;
Packit 032894
      const char *sname2 = NULL;
Packit 032894
      do
Packit 032894
	{
Packit 032894
	  scn2 = elf_nextscn (elf2, scn2);
Packit 032894
	  shdr2 = gelf_getshdr (scn2, &shdr2_mem);
Packit 032894
	  if (shdr2 != NULL)
Packit 032894
	    sname2 = elf_strptr (elf2, shstrndx2, shdr2->sh_name);
Packit 032894
	}
Packit 032894
      while (scn2 != NULL && shdr2 != NULL
Packit 032894
	     && ebl_section_strip_p (ebl2, shdr2, sname2, true, false));
Packit 032894
Packit 032894
      if (scn1 == NULL || scn2 == NULL || shdr1 == NULL || shdr2 == NULL)
Packit 032894
	break;
Packit 032894
Packit 032894
      if (gaps != gaps_ignore && (shdr1->sh_flags & SHF_ALLOC) != 0)
Packit 032894
	{
Packit 032894
	  struct region *newp = (struct region *) alloca (sizeof (*newp));
Packit 032894
	  newp->from = shdr1->sh_offset;
Packit 032894
	  newp->to = shdr1->sh_offset + shdr1->sh_size;
Packit 032894
	  newp->next = regions;
Packit 032894
	  regions = newp;
Packit 032894
Packit 032894
	  ++nregions;
Packit 032894
	}
Packit 032894
Packit 032894
      /* Compare the headers.  We allow the name to be at a different
Packit 032894
	 location.  */
Packit 032894
      if (unlikely (sname1 == NULL || sname2 == NULL
Packit 032894
		    || strcmp (sname1, sname2) != 0))
Packit 032894
	{
Packit 032894
	  error (0, 0, gettext ("%s %s differ: section [%zu], [%zu] name"),
Packit 032894
		 fname1, fname2, elf_ndxscn (scn1), elf_ndxscn (scn2));
Packit 032894
	  DIFFERENCE;
Packit 032894
	}
Packit 032894
Packit 032894
      /* We ignore certain sections.  */
Packit 032894
      if ((sname1 != NULL && strcmp (sname1, ".gnu_debuglink") == 0)
Packit 032894
	  || (sname1 != NULL && strcmp (sname1, ".gnu.prelink_undo") == 0))
Packit 032894
	continue;
Packit 032894
Packit 032894
      if (shdr1->sh_type != shdr2->sh_type
Packit 032894
	  // XXX Any flags which should be ignored?
Packit 032894
	  || shdr1->sh_flags != shdr2->sh_flags
Packit 032894
	  || shdr1->sh_addr != shdr2->sh_addr
Packit 032894
	  || (shdr1->sh_offset != shdr2->sh_offset
Packit 032894
	      && (shdr1->sh_flags & SHF_ALLOC)
Packit 032894
	      && ehdr1->e_type != ET_REL)
Packit 032894
	  || shdr1->sh_size != shdr2->sh_size
Packit 032894
	  || shdr1->sh_link != shdr2->sh_link
Packit 032894
	  || shdr1->sh_info != shdr2->sh_info
Packit 032894
	  || shdr1->sh_addralign != shdr2->sh_addralign
Packit 032894
	  || shdr1->sh_entsize != shdr2->sh_entsize)
Packit 032894
	{
Packit 032894
	  error (0, 0, gettext ("%s %s differ: section [%zu] '%s' header"),
Packit 032894
		 fname1, fname2, elf_ndxscn (scn1), sname1);
Packit 032894
	  DIFFERENCE;
Packit 032894
	}
Packit 032894
Packit 032894
      Elf_Data *data1 = elf_getdata (scn1, NULL);
Packit 032894
      if (data1 == NULL)
Packit 032894
	error (2, 0,
Packit 032894
	       gettext ("cannot get content of section %zu in '%s': %s"),
Packit 032894
	       elf_ndxscn (scn1), fname1, elf_errmsg (-1));
Packit 032894
Packit 032894
      Elf_Data *data2 = elf_getdata (scn2, NULL);
Packit 032894
      if (data2 == NULL)
Packit 032894
	error (2, 0,
Packit 032894
	       gettext ("cannot get content of section %zu in '%s': %s"),
Packit 032894
	       elf_ndxscn (scn2), fname2, elf_errmsg (-1));
Packit 032894
Packit 032894
      switch (shdr1->sh_type)
Packit 032894
	{
Packit 032894
	case SHT_DYNSYM:
Packit 032894
	case SHT_SYMTAB:
Packit 032894
	  if (shdr1->sh_entsize == 0)
Packit 032894
	    error (2, 0,
Packit 032894
		   gettext ("symbol table [%zu] in '%s' has zero sh_entsize"),
Packit 032894
		   elf_ndxscn (scn1), fname1);
Packit 032894
Packit 032894
	  /* Iterate over the symbol table.  We ignore the st_size
Packit 032894
	     value of undefined symbols.  */
Packit 032894
	  for (int ndx = 0; ndx < (int) (shdr1->sh_size / shdr1->sh_entsize);
Packit 032894
	       ++ndx)
Packit 032894
	    {
Packit 032894
	      GElf_Sym sym1_mem;
Packit 032894
	      GElf_Sym *sym1 = gelf_getsym (data1, ndx, &sym1_mem);
Packit 032894
	      if (sym1 == NULL)
Packit 032894
		error (2, 0,
Packit 032894
		       gettext ("cannot get symbol in '%s': %s"),
Packit 032894
		       fname1, elf_errmsg (-1));
Packit 032894
	      GElf_Sym sym2_mem;
Packit 032894
	      GElf_Sym *sym2 = gelf_getsym (data2, ndx, &sym2_mem);
Packit 032894
	      if (sym2 == NULL)
Packit 032894
		error (2, 0,
Packit 032894
		       gettext ("cannot get symbol in '%s': %s"),
Packit 032894
		       fname2, elf_errmsg (-1));
Packit 032894
Packit 032894
	      const char *name1 = elf_strptr (elf1, shdr1->sh_link,
Packit 032894
					      sym1->st_name);
Packit 032894
	      const char *name2 = elf_strptr (elf2, shdr2->sh_link,
Packit 032894
					      sym2->st_name);
Packit 032894
	      if (unlikely (name1 == NULL || name2 == NULL
Packit 032894
			    || strcmp (name1, name2) != 0
Packit 032894
			    || sym1->st_value != sym2->st_value
Packit 032894
			    || (sym1->st_size != sym2->st_size
Packit 032894
				&& sym1->st_shndx != SHN_UNDEF)
Packit 032894
			    || sym1->st_info != sym2->st_info
Packit 032894
			    || sym1->st_other != sym2->st_other
Packit 032894
			    || sym1->st_shndx != sym2->st_shndx))
Packit 032894
		{
Packit 032894
		  // XXX Do we want to allow reordered symbol tables?
Packit 032894
		symtab_mismatch:
Packit 032894
		  if (! quiet)
Packit 032894
		    {
Packit 032894
		      if (elf_ndxscn (scn1) == elf_ndxscn (scn2))
Packit 032894
			error (0, 0,
Packit 032894
			       gettext ("%s %s differ: symbol table [%zu]"),
Packit 032894
			       fname1, fname2, elf_ndxscn (scn1));
Packit 032894
		      else
Packit 032894
			error (0, 0, gettext ("\
Packit 032894
%s %s differ: symbol table [%zu,%zu]"),
Packit 032894
			       fname1, fname2, elf_ndxscn (scn1),
Packit 032894
			       elf_ndxscn (scn2));
Packit 032894
		    }
Packit 032894
		  DIFFERENCE;
Packit 032894
		  break;
Packit 032894
		}
Packit 032894
Packit 032894
	      if (sym1->st_shndx == SHN_UNDEF
Packit 032894
		  && sym1->st_size != sym2->st_size)
Packit 032894
		{
Packit 032894
		  /* The size of the symbol in the object defining it
Packit 032894
		     might have changed.  That is OK unless the symbol
Packit 032894
		     is used in a copy relocation.  Look over the
Packit 032894
		     sections in both files and determine which
Packit 032894
		     relocation section uses this symbol table
Packit 032894
		     section.  Then look through the relocations to
Packit 032894
		     see whether any copy relocation references this
Packit 032894
		     symbol.  */
Packit 032894
		  if (search_for_copy_reloc (ebl1, elf_ndxscn (scn1), ndx)
Packit 032894
		      || search_for_copy_reloc (ebl2, elf_ndxscn (scn2), ndx))
Packit 032894
		    goto symtab_mismatch;
Packit 032894
		}
Packit 032894
	    }
Packit 032894
	  break;
Packit 032894
Packit 032894
	case SHT_NOTE:
Packit 032894
	  /* Parse the note format and compare the notes themselves.  */
Packit 032894
	  {
Packit 032894
	    GElf_Nhdr note1;
Packit 032894
	    GElf_Nhdr note2;
Packit 032894
Packit 032894
	    size_t off1 = 0;
Packit 032894
	    size_t off2 = 0;
Packit 032894
	    size_t name_offset;
Packit 032894
	    size_t desc_offset;
Packit 032894
	    while (off1 < data1->d_size
Packit 032894
		   && (off1 = gelf_getnote (data1, off1, &note1,
Packit 032894
					    &name_offset, &desc_offset)) > 0)
Packit 032894
	      {
Packit 032894
		const char *name1 = (note1.n_namesz == 0
Packit 032894
				     ? "" : data1->d_buf + name_offset);
Packit 032894
		const void *desc1 = data1->d_buf + desc_offset;
Packit 032894
		if (off2 >= data2->d_size)
Packit 032894
		  {
Packit 032894
		    if (! quiet)
Packit 032894
		      error (0, 0, gettext ("\
Packit 032894
%s %s differ: section [%zu] '%s' number of notes"),
Packit 032894
			     fname1, fname2, elf_ndxscn (scn1), sname1);
Packit 032894
		    DIFFERENCE;
Packit 032894
		  }
Packit 032894
		off2 = gelf_getnote (data2, off2, &note2,
Packit 032894
				     &name_offset, &desc_offset);
Packit 032894
		if (off2 == 0)
Packit 032894
		  error (2, 0, gettext ("\
Packit 032894
cannot read note section [%zu] '%s' in '%s': %s"),
Packit 032894
			 elf_ndxscn (scn2), sname2, fname2, elf_errmsg (-1));
Packit 032894
		const char *name2 = (note2.n_namesz == 0
Packit 032894
				     ? "" : data2->d_buf + name_offset);
Packit 032894
		const void *desc2 = data2->d_buf + desc_offset;
Packit 032894
Packit 032894
		if (note1.n_namesz != note2.n_namesz
Packit 032894
		    || memcmp (name1, name2, note1.n_namesz))
Packit 032894
		  {
Packit 032894
		    if (! quiet)
Packit 032894
		      error (0, 0, gettext ("\
Packit 032894
%s %s differ: section [%zu] '%s' note name"),
Packit 032894
			     fname1, fname2, elf_ndxscn (scn1), sname1);
Packit 032894
		    DIFFERENCE;
Packit 032894
		  }
Packit 032894
		if (note1.n_type != note2.n_type)
Packit 032894
		  {
Packit 032894
		    if (! quiet)
Packit 032894
		      error (0, 0, gettext ("\
Packit 032894
%s %s differ: section [%zu] '%s' note '%s' type"),
Packit 032894
			     fname1, fname2, elf_ndxscn (scn1), sname1, name1);
Packit 032894
		    DIFFERENCE;
Packit 032894
		  }
Packit 032894
		if (note1.n_descsz != note2.n_descsz
Packit 032894
		    || memcmp (desc1, desc2, note1.n_descsz))
Packit 032894
		  {
Packit 032894
		    if (note1.n_type == NT_GNU_BUILD_ID
Packit 032894
			&& note1.n_namesz == sizeof "GNU"
Packit 032894
			&& !memcmp (name1, "GNU", sizeof "GNU"))
Packit 032894
		      {
Packit 032894
			if (note1.n_descsz != note2.n_descsz)
Packit 032894
			  {
Packit 032894
			    if (! quiet)
Packit 032894
			      error (0, 0, gettext ("\
Packit 032894
%s %s differ: build ID length"),
Packit 032894
				     fname1, fname2);
Packit 032894
			    DIFFERENCE;
Packit 032894
			  }
Packit 032894
			else if (! ignore_build_id)
Packit 032894
			  {
Packit 032894
			    if (! quiet)
Packit 032894
			      error (0, 0, gettext ("\
Packit 032894
%s %s differ: build ID content"),
Packit 032894
				     fname1, fname2);
Packit 032894
			    DIFFERENCE;
Packit 032894
			  }
Packit 032894
		      }
Packit 032894
		    else
Packit 032894
		      {
Packit 032894
			if (! quiet)
Packit 032894
			  error (0, 0, gettext ("\
Packit 032894
%s %s differ: section [%zu] '%s' note '%s' content"),
Packit 032894
				 fname1, fname2, elf_ndxscn (scn1), sname1,
Packit 032894
				 name1);
Packit 032894
			DIFFERENCE;
Packit 032894
		      }
Packit 032894
		  }
Packit 032894
	      }
Packit 032894
	    if (off2 < data2->d_size)
Packit 032894
	      {
Packit 032894
		if (! quiet)
Packit 032894
		  error (0, 0, gettext ("\
Packit 032894
%s %s differ: section [%zu] '%s' number of notes"),
Packit 032894
			 fname1, fname2, elf_ndxscn (scn1), sname1);
Packit 032894
		DIFFERENCE;
Packit 032894
	      }
Packit 032894
	  }
Packit 032894
	  break;
Packit 032894
Packit 032894
	default:
Packit 032894
	  /* Compare the section content byte for byte.  */
Packit 032894
	  assert (shdr1->sh_type == SHT_NOBITS
Packit 032894
		  || (data1->d_buf != NULL || data1->d_size == 0));
Packit 032894
	  assert (shdr2->sh_type == SHT_NOBITS
Packit 032894
		  || (data2->d_buf != NULL || data1->d_size == 0));
Packit 032894
Packit 032894
	  if (unlikely (data1->d_size != data2->d_size
Packit 032894
			|| (shdr1->sh_type != SHT_NOBITS
Packit 032894
			    && data1->d_size != 0
Packit 032894
			    && memcmp (data1->d_buf, data2->d_buf,
Packit 032894
				       data1->d_size) != 0)))
Packit 032894
	    {
Packit 032894
	      if (hash_inexact
Packit 032894
		  && shdr1->sh_type == SHT_HASH
Packit 032894
		  && data1->d_size == data2->d_size
Packit 032894
		  && hash_content_equivalent (shdr1->sh_entsize, data1, data2))
Packit 032894
		break;
Packit 032894
Packit 032894
	      if (! quiet)
Packit 032894
		{
Packit 032894
		  if (elf_ndxscn (scn1) == elf_ndxscn (scn2))
Packit 032894
		    error (0, 0, gettext ("\
Packit 032894
%s %s differ: section [%zu] '%s' content"),
Packit 032894
			   fname1, fname2, elf_ndxscn (scn1), sname1);
Packit 032894
		  else
Packit 032894
		    error (0, 0, gettext ("\
Packit 032894
%s %s differ: section [%zu,%zu] '%s' content"),
Packit 032894
			   fname1, fname2, elf_ndxscn (scn1),
Packit 032894
			   elf_ndxscn (scn2), sname1);
Packit 032894
		}
Packit 032894
	      DIFFERENCE;
Packit 032894
	    }
Packit 032894
	  break;
Packit 032894
	}
Packit 032894
    }
Packit 032894
Packit 032894
  if (unlikely (scn1 != scn2))
Packit 032894
    {
Packit 032894
      if (! quiet)
Packit 032894
	error (0, 0,
Packit 032894
	       gettext ("%s %s differ: unequal amount of important sections"),
Packit 032894
	       fname1, fname2);
Packit 032894
      DIFFERENCE;
Packit 032894
    }
Packit 032894
Packit 032894
  /* We we look at gaps, create artificial ones for the parts of the
Packit 032894
     program which we are not in sections.  */
Packit 032894
  struct region ehdr_region;
Packit 032894
  struct region phdr_region;
Packit 032894
  if (gaps != gaps_ignore)
Packit 032894
    {
Packit 032894
      ehdr_region.from = 0;
Packit 032894
      ehdr_region.to = ehdr1->e_ehsize;
Packit 032894
      ehdr_region.next = &phdr_region;
Packit 032894
Packit 032894
      phdr_region.from = ehdr1->e_phoff;
Packit 032894
      phdr_region.to = ehdr1->e_phoff + phnum1 * ehdr1->e_phentsize;
Packit 032894
      phdr_region.next = regions;
Packit 032894
Packit 032894
      regions = &ehdr_region;
Packit 032894
      nregions += 2;
Packit 032894
    }
Packit 032894
Packit 032894
  /* If we need to look at the gaps we need access to the file data.  */
Packit 032894
  char *raw1 = NULL;
Packit 032894
  size_t size1 = 0;
Packit 032894
  char *raw2 = NULL;
Packit 032894
  size_t size2 = 0;
Packit 032894
  struct region *regionsarr = alloca (nregions * sizeof (struct region));
Packit 032894
  if (gaps != gaps_ignore)
Packit 032894
    {
Packit 032894
      raw1 = elf_rawfile (elf1, &size1);
Packit 032894
      if (raw1 == NULL )
Packit 032894
	error (2, 0, gettext ("cannot load data of '%s': %s"),
Packit 032894
	       fname1, elf_errmsg (-1));
Packit 032894
Packit 032894
      raw2 = elf_rawfile (elf2, &size2);
Packit 032894
      if (raw2 == NULL )
Packit 032894
	error (2, 0, gettext ("cannot load data of '%s': %s"),
Packit 032894
	       fname2, elf_errmsg (-1));
Packit 032894
Packit 032894
      for (size_t cnt = 0; cnt < nregions; ++cnt)
Packit 032894
	{
Packit 032894
	  regionsarr[cnt] = *regions;
Packit 032894
	  regions = regions->next;
Packit 032894
	}
Packit 032894
Packit 032894
      qsort (regionsarr, nregions, sizeof (regionsarr[0]), regioncompare);
Packit 032894
    }
Packit 032894
Packit 032894
  /* Compare the program header tables.  */
Packit 032894
  for (unsigned int ndx = 0; ndx < phnum1; ++ndx)
Packit 032894
    {
Packit 032894
      GElf_Phdr phdr1_mem;
Packit 032894
      GElf_Phdr *phdr1 = gelf_getphdr (elf1, ndx, &phdr1_mem);
Packit 032894
      if (phdr1 == NULL)
Packit 032894
	error (2, 0,
Packit 032894
	       gettext ("cannot get program header entry %d of '%s': %s"),
Packit 032894
	       ndx, fname1, elf_errmsg (-1));
Packit 032894
      GElf_Phdr phdr2_mem;
Packit 032894
      GElf_Phdr *phdr2 = gelf_getphdr (elf2, ndx, &phdr2_mem);
Packit 032894
      if (phdr2 == NULL)
Packit 032894
	error (2, 0,
Packit 032894
	       gettext ("cannot get program header entry %d of '%s': %s"),
Packit 032894
	       ndx, fname2, elf_errmsg (-1));
Packit 032894
Packit 032894
      if (unlikely (memcmp (phdr1, phdr2, sizeof (GElf_Phdr)) != 0))
Packit 032894
	{
Packit 032894
	  if (! quiet)
Packit 032894
	    error (0, 0, gettext ("%s %s differ: program header %d"),
Packit 032894
		   fname1, fname2, ndx);
Packit 032894
	  DIFFERENCE;
Packit 032894
	}
Packit 032894
Packit 032894
      if (gaps != gaps_ignore && phdr1->p_type == PT_LOAD)
Packit 032894
	{
Packit 032894
	  size_t cnt = 0;
Packit 032894
	  while (cnt < nregions && regionsarr[cnt].to < phdr1->p_offset)
Packit 032894
	    ++cnt;
Packit 032894
Packit 032894
	  GElf_Off last = phdr1->p_offset;
Packit 032894
	  GElf_Off end = phdr1->p_offset + phdr1->p_filesz;
Packit 032894
	  while (cnt < nregions && regionsarr[cnt].from < end)
Packit 032894
	    {
Packit 032894
	      if (last < regionsarr[cnt].from)
Packit 032894
		{
Packit 032894
		  /* Compare the [LAST,FROM) region.  */
Packit 032894
		  assert (gaps == gaps_match);
Packit 032894
		  if (unlikely (memcmp (raw1 + last, raw2 + last,
Packit 032894
					regionsarr[cnt].from - last) != 0))
Packit 032894
		    {
Packit 032894
		    gapmismatch:
Packit 032894
		      if (!quiet)
Packit 032894
			error (0, 0, gettext ("%s %s differ: gap"),
Packit 032894
			       fname1, fname2);
Packit 032894
		      DIFFERENCE;
Packit 032894
		      break;
Packit 032894
		    }
Packit 032894
Packit 032894
		}
Packit 032894
	      last = regionsarr[cnt].to;
Packit 032894
	      ++cnt;
Packit 032894
	    }
Packit 032894
Packit 032894
	  if (cnt == nregions && last < end)
Packit 032894
	    goto gapmismatch;
Packit 032894
	}
Packit 032894
    }
Packit 032894
Packit 032894
 out:
Packit 032894
  elf_end (elf1);
Packit 032894
  elf_end (elf2);
Packit 032894
  ebl_closebackend (ebl1);
Packit 032894
  ebl_closebackend (ebl2);
Packit 032894
  close (fd1);
Packit 032894
  close (fd2);
Packit 032894
Packit 032894
  return result;
Packit 032894
}
Packit 032894
Packit 032894
Packit 032894
/* Handle program arguments.  */
Packit 032894
static error_t
Packit 032894
parse_opt (int key, char *arg,
Packit 032894
	   struct argp_state *state __attribute__ ((unused)))
Packit 032894
{
Packit 032894
  switch (key)
Packit 032894
    {
Packit 032894
    case 'q':
Packit 032894
      quiet = true;
Packit 032894
      break;
Packit 032894
Packit 032894
    case 'l':
Packit 032894
      verbose = true;
Packit 032894
      break;
Packit 032894
Packit 032894
    case OPT_GAPS:
Packit 032894
      if (strcasecmp (arg, "ignore") == 0)
Packit 032894
	gaps = gaps_ignore;
Packit 032894
      else if (likely (strcasecmp (arg, "match") == 0))
Packit 032894
	gaps = gaps_match;
Packit 032894
      else
Packit 032894
	{
Packit 032894
	  fprintf (stderr,
Packit 032894
		   gettext ("Invalid value '%s' for --gaps parameter."),
Packit 032894
		   arg);
Packit 032894
	  argp_help (&argp, stderr, ARGP_HELP_SEE,
Packit 032894
		     program_invocation_short_name);
Packit 032894
	  exit (1);
Packit 032894
	}
Packit 032894
      break;
Packit 032894
Packit 032894
    case OPT_HASH_INEXACT:
Packit 032894
      hash_inexact = true;
Packit 032894
      break;
Packit 032894
Packit 032894
    case OPT_IGNORE_BUILD_ID:
Packit 032894
      ignore_build_id = true;
Packit 032894
      break;
Packit 032894
Packit 032894
    default:
Packit 032894
      return ARGP_ERR_UNKNOWN;
Packit 032894
    }
Packit 032894
  return 0;
Packit 032894
}
Packit 032894
Packit 032894
Packit 032894
static Elf *
Packit 032894
open_file (const char *fname, int *fdp, Ebl **eblp)
Packit 032894
{
Packit 032894
  int fd = open (fname, O_RDONLY);
Packit 032894
  if (unlikely (fd == -1))
Packit 032894
    error (2, errno, gettext ("cannot open '%s'"), fname);
Packit 032894
  Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
Packit 032894
  if (elf == NULL)
Packit 032894
    error (2, 0,
Packit 032894
	   gettext ("cannot create ELF descriptor for '%s': %s"),
Packit 032894
	   fname, elf_errmsg (-1));
Packit 032894
  Ebl *ebl = ebl_openbackend (elf);
Packit 032894
  if (ebl == NULL)
Packit 032894
    error (2, 0,
Packit 032894
	   gettext ("cannot create EBL descriptor for '%s'"), fname);
Packit 032894
Packit 032894
  *fdp = fd;
Packit 032894
  *eblp = ebl;
Packit 032894
  return elf;
Packit 032894
}
Packit 032894
Packit 032894
Packit 032894
static bool
Packit 032894
search_for_copy_reloc (Ebl *ebl, size_t scnndx, int symndx)
Packit 032894
{
Packit 032894
  Elf_Scn *scn = NULL;
Packit 032894
  while ((scn = elf_nextscn (ebl->elf, scn)) != NULL)
Packit 032894
    {
Packit 032894
      GElf_Shdr shdr_mem;
Packit 032894
      GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
Packit 032894
      if (shdr == NULL)
Packit 032894
	error (2, 0,
Packit 032894
	       gettext ("cannot get section header of section %zu: %s"),
Packit 032894
	       elf_ndxscn (scn), elf_errmsg (-1));
Packit 032894
Packit 032894
      if ((shdr->sh_type != SHT_REL && shdr->sh_type != SHT_RELA)
Packit 032894
	  || shdr->sh_link != scnndx)
Packit 032894
	continue;
Packit 032894
Packit 032894
      Elf_Data *data = elf_getdata (scn, NULL);
Packit 032894
      if (data == NULL)
Packit 032894
	error (2, 0,
Packit 032894
	       gettext ("cannot get content of section %zu: %s"),
Packit 032894
	       elf_ndxscn (scn), elf_errmsg (-1));
Packit 032894
Packit 032894
      if (shdr->sh_type == SHT_REL && shdr->sh_entsize != 0)
Packit 032894
	for (int ndx = 0; ndx < (int) (shdr->sh_size / shdr->sh_entsize);
Packit 032894
	     ++ndx)
Packit 032894
	  {
Packit 032894
	    GElf_Rel rel_mem;
Packit 032894
	    GElf_Rel *rel = gelf_getrel (data, ndx, &rel_mem);
Packit 032894
	    if (rel == NULL)
Packit 032894
	      error (2, 0, gettext ("cannot get relocation: %s"),
Packit 032894
		     elf_errmsg (-1));
Packit 032894
Packit 032894
	    if ((int) GELF_R_SYM (rel->r_info) == symndx
Packit 032894
		&& ebl_copy_reloc_p (ebl, GELF_R_TYPE (rel->r_info)))
Packit 032894
	      return true;
Packit 032894
	  }
Packit 032894
      else if (shdr->sh_entsize != 0)
Packit 032894
	for (int ndx = 0; ndx < (int) (shdr->sh_size / shdr->sh_entsize);
Packit 032894
	     ++ndx)
Packit 032894
	  {
Packit 032894
	    GElf_Rela rela_mem;
Packit 032894
	    GElf_Rela *rela = gelf_getrela (data, ndx, &rela_mem);
Packit 032894
	    if (rela == NULL)
Packit 032894
	      error (2, 0, gettext ("cannot get relocation: %s"),
Packit 032894
		     elf_errmsg (-1));
Packit 032894
Packit 032894
	    if ((int) GELF_R_SYM (rela->r_info) == symndx
Packit 032894
		&& ebl_copy_reloc_p (ebl, GELF_R_TYPE (rela->r_info)))
Packit 032894
	      return true;
Packit 032894
	  }
Packit 032894
    }
Packit 032894
Packit 032894
  return false;
Packit 032894
}
Packit 032894
Packit 032894
Packit 032894
static int
Packit 032894
regioncompare (const void *p1, const void *p2)
Packit 032894
{
Packit 032894
  const struct region *r1 = (const struct region *) p1;
Packit 032894
  const struct region *r2 = (const struct region *) p2;
Packit 032894
Packit 032894
  if (r1->from < r2->from)
Packit 032894
    return -1;
Packit 032894
  return 1;
Packit 032894
}
Packit 032894
Packit 032894
Packit 032894
static int
Packit 032894
compare_Elf32_Word (const void *p1, const void *p2)
Packit 032894
{
Packit 032894
  const Elf32_Word *w1 = p1;
Packit 032894
  const Elf32_Word *w2 = p2;
Packit 032894
  return *w1 < *w2 ? -1 : *w1 > *w2 ? 1 : 0;
Packit 032894
}
Packit 032894
Packit 032894
static int
Packit 032894
compare_Elf64_Xword (const void *p1, const void *p2)
Packit 032894
{
Packit 032894
  const Elf64_Xword *w1 = p1;
Packit 032894
  const Elf64_Xword *w2 = p2;
Packit 032894
  return *w1 < *w2 ? -1 : *w1 > *w2 ? 1 : 0;
Packit 032894
}
Packit 032894
Packit 032894
static bool
Packit 032894
hash_content_equivalent (size_t entsize, Elf_Data *data1, Elf_Data *data2)
Packit 032894
{
Packit 032894
#define CHECK_HASH(Hash_Word)						      \
Packit 032894
  {									      \
Packit 032894
    const Hash_Word *const hash1 = data1->d_buf;			      \
Packit 032894
    const Hash_Word *const hash2 = data2->d_buf;			      \
Packit 032894
    const size_t nbucket = hash1[0];					      \
Packit 032894
    const size_t nchain = hash1[1];					      \
Packit 032894
    if (data1->d_size != (2 + nbucket + nchain) * sizeof hash1[0]	      \
Packit 032894
	|| hash2[0] != nbucket || hash2[1] != nchain)			      \
Packit 032894
      return false;							      \
Packit 032894
									      \
Packit 032894
    const Hash_Word *const bucket1 = &hash1[2];				      \
Packit 032894
    const Hash_Word *const chain1 = &bucket1[nbucket];			      \
Packit 032894
    const Hash_Word *const bucket2 = &hash2[2];				      \
Packit 032894
    const Hash_Word *const chain2 = &bucket2[nbucket];			      \
Packit 032894
									      \
Packit 032894
    bool chain_ok[nchain];						      \
Packit 032894
    Hash_Word temp1[nchain - 1];					      \
Packit 032894
    Hash_Word temp2[nchain - 1];					      \
Packit 032894
    memset (chain_ok, 0, sizeof chain_ok);				      \
Packit 032894
    for (size_t i = 0; i < nbucket; ++i)				      \
Packit 032894
      {									      \
Packit 032894
	if (bucket1[i] >= nchain || bucket2[i] >= nchain)		      \
Packit 032894
	  return false;							      \
Packit 032894
									      \
Packit 032894
	size_t b1 = 0;							      \
Packit 032894
	for (size_t p = bucket1[i]; p != STN_UNDEF; p = chain1[p])	      \
Packit 032894
	  if (p >= nchain || b1 >= nchain - 1)				      \
Packit 032894
	    return false;						      \
Packit 032894
	  else								      \
Packit 032894
	    temp1[b1++] = p;						      \
Packit 032894
									      \
Packit 032894
	size_t b2 = 0;							      \
Packit 032894
	for (size_t p = bucket2[i]; p != STN_UNDEF; p = chain2[p])	      \
Packit 032894
	  if (p >= nchain || b2 >= nchain - 1)				      \
Packit 032894
	    return false;						      \
Packit 032894
	  else								      \
Packit 032894
	    temp2[b2++] = p;						      \
Packit 032894
									      \
Packit 032894
	if (b1 != b2)							      \
Packit 032894
	  return false;							      \
Packit 032894
									      \
Packit 032894
	qsort (temp1, b1, sizeof temp1[0], compare_##Hash_Word);	      \
Packit 032894
	qsort (temp2, b2, sizeof temp2[0], compare_##Hash_Word);	      \
Packit 032894
									      \
Packit 032894
	for (b1 = 0; b1 < b2; ++b1)					      \
Packit 032894
	  if (temp1[b1] != temp2[b1])					      \
Packit 032894
	    return false;						      \
Packit 032894
	  else								      \
Packit 032894
	    chain_ok[temp1[b1]] = true;					      \
Packit 032894
      }									      \
Packit 032894
									      \
Packit 032894
    for (size_t i = 0; i < nchain; ++i)					      \
Packit 032894
      if (!chain_ok[i] && chain1[i] != chain2[i])			      \
Packit 032894
	return false;							      \
Packit 032894
									      \
Packit 032894
    return true;							      \
Packit 032894
  }
Packit 032894
Packit 032894
  switch (entsize)
Packit 032894
    {
Packit 032894
    case 4:
Packit 032894
      CHECK_HASH (Elf32_Word);
Packit 032894
      break;
Packit 032894
    case 8:
Packit 032894
      CHECK_HASH (Elf64_Xword);
Packit 032894
      break;
Packit 032894
    }
Packit 032894
Packit 032894
  return false;
Packit 032894
}
Packit 032894
Packit 032894
Packit 032894
#include "debugpred.h"