Home | History | Annotate | Download | only in src
      1 /* Compare relevant content of two ELF files.
      2    Copyright (C) 2005-2012 Red Hat, Inc.
      3    This file is part of Red Hat elfutils.
      4    Written by Ulrich Drepper <drepper (at) redhat.com>, 2005.
      5 
      6    Red Hat elfutils is free software; you can redistribute it and/or modify
      7    it under the terms of the GNU General Public License as published by the
      8    Free Software Foundation; version 2 of the License.
      9 
     10    Red Hat elfutils is distributed in the hope that it will be useful, but
     11    WITHOUT ANY WARRANTY; without even the implied warranty of
     12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13    General Public License for more details.
     14 
     15    You should have received a copy of the GNU General Public License along
     16    with Red Hat elfutils; if not, write to the Free Software Foundation,
     17    Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
     18 
     19    Red Hat elfutils is an included package of the Open Invention Network.
     20    An included package of the Open Invention Network is a package for which
     21    Open Invention Network licensees cross-license their patents.  No patent
     22    license is granted, either expressly or impliedly, by designation as an
     23    included package.  Should you wish to participate in the Open Invention
     24    Network licensing program, please visit www.openinventionnetwork.com
     25    <http://www.openinventionnetwork.com>.  */
     26 
     27 #ifdef HAVE_CONFIG_H
     28 # include <config.h>
     29 #endif
     30 
     31 #include <argp.h>
     32 #include <assert.h>
     33 #include <errno.h>
     34 #include <error.h>
     35 #include <fcntl.h>
     36 #include <locale.h>
     37 #include <libintl.h>
     38 #include <stdbool.h>
     39 #include <stdio.h>
     40 #include <stdlib.h>
     41 #include <string.h>
     42 #include <unistd.h>
     43 
     44 #include <system.h>
     45 #include "../libelf/elf-knowledge.h"
     46 #include "../libebl/libeblP.h"
     47 
     48 
     49 /* Prototypes of local functions.  */
     50 static Elf *open_file (const char *fname, int *fdp, Ebl **eblp);
     51 static bool search_for_copy_reloc (Ebl *ebl, size_t scnndx, int symndx);
     52 static  int regioncompare (const void *p1, const void *p2);
     53 
     54 
     55 /* Name and version of program.  */
     56 static void print_version (FILE *stream, struct argp_state *state);
     57 ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
     58 
     59 /* Bug report address.  */
     60 ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
     61 
     62 /* Values for the parameters which have no short form.  */
     63 #define OPT_GAPS		0x100
     64 #define OPT_HASH_INEXACT	0x101
     65 #define OPT_IGNORE_BUILD_ID	0x102
     66 
     67 /* Definitions of arguments for argp functions.  */
     68 static const struct argp_option options[] =
     69 {
     70   { NULL, 0, NULL, 0, N_("Control options:"), 0 },
     71   { "verbose", 'l', NULL, 0,
     72     N_("Output all differences, not just the first"), 0 },
     73   { "gaps", OPT_GAPS, "ACTION", 0, N_("Control treatment of gaps in loadable segments [ignore|match] (default: ignore)"), 0 },
     74   { "hash-inexact", OPT_HASH_INEXACT, NULL, 0,
     75     N_("Ignore permutation of buckets in SHT_HASH section"), 0 },
     76   { "ignore-build-id", OPT_IGNORE_BUILD_ID, NULL, 0,
     77     N_("Ignore differences in build ID"), 0 },
     78   { "quiet", 'q', NULL, 0, N_("Output nothing; yield exit status only"), 0 },
     79 
     80   { NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 },
     81   { NULL, 0, NULL, 0, NULL, 0 }
     82 };
     83 
     84 /* Short description of program.  */
     85 static const char doc[] = N_("\
     86 Compare relevant parts of two ELF files for equality.");
     87 
     88 /* Strings for arguments in help texts.  */
     89 static const char args_doc[] = N_("FILE1 FILE2");
     90 
     91 /* Prototype for option handler.  */
     92 static error_t parse_opt (int key, char *arg, struct argp_state *state);
     93 
     94 /* Data structure to communicate with argp functions.  */
     95 static struct argp argp =
     96 {
     97   options, parse_opt, args_doc, doc, NULL, NULL, NULL
     98 };
     99 
    100 
    101 /* How to treat gaps in loadable segments.  */
    102 static enum
    103   {
    104     gaps_ignore = 0,
    105     gaps_match
    106   }
    107   gaps;
    108 
    109 /* Structure to hold information about used regions.  */
    110 struct region
    111 {
    112   GElf_Addr from;
    113   GElf_Addr to;
    114   struct region *next;
    115 };
    116 
    117 /* Nonzero if only exit status is wanted.  */
    118 static bool quiet;
    119 
    120 /* True iff multiple differences should be output.  */
    121 static bool verbose;
    122 
    123 /* True iff SHT_HASH treatment should be generous.  */
    124 static bool hash_inexact;
    125 
    126 /* True iff build ID notes should be ignored.  */
    127 static bool ignore_build_id;
    128 
    129 static bool hash_content_equivalent (size_t entsize, Elf_Data *, Elf_Data *);
    130 
    131 
    132 int
    133 main (int argc, char *argv[])
    134 {
    135   /* Set locale.  */
    136   (void) setlocale (LC_ALL, "");
    137 
    138   /* Make sure the message catalog can be found.  */
    139   (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
    140 
    141   /* Initialize the message catalog.  */
    142   (void) textdomain (PACKAGE_TARNAME);
    143 
    144   /* Parse and process arguments.  */
    145   int remaining;
    146   (void) argp_parse (&argp, argc, argv, 0, &remaining, NULL);
    147 
    148   /* We expect exactly two non-option parameters.  */
    149   if (unlikely (remaining + 2 != argc))
    150     {
    151       fputs (gettext ("Invalid number of parameters.\n"), stderr);
    152       argp_help (&argp, stderr, ARGP_HELP_SEE, program_invocation_short_name);
    153       exit (1);
    154     }
    155 
    156   if (quiet)
    157     verbose = false;
    158 
    159   /* Comparing the files is done in two phases:
    160      1. compare all sections.  Sections which are irrelevant (i.e., if
    161 	strip would remove them) are ignored.  Some section types are
    162 	handled special.
    163      2. all parts of the loadable segments which are not parts of any
    164 	section is compared according to the rules of the --gaps option.
    165   */
    166   int result = 0;
    167   elf_version (EV_CURRENT);
    168 
    169   const char *const fname1 = argv[remaining];
    170   int fd1;
    171   Ebl *ebl1;
    172   Elf *elf1 = open_file (fname1, &fd1, &ebl1);
    173 
    174   const char *const fname2 = argv[remaining + 1];
    175   int fd2;
    176   Ebl *ebl2;
    177   Elf *elf2 = open_file (fname2, &fd2, &ebl2);
    178 
    179   GElf_Ehdr ehdr1_mem;
    180   GElf_Ehdr *ehdr1 = gelf_getehdr (elf1, &ehdr1_mem);
    181   if (ehdr1 == NULL)
    182     error (2, 0, gettext ("cannot get ELF header of '%s': %s"),
    183 	   fname1, elf_errmsg (-1));
    184   GElf_Ehdr ehdr2_mem;
    185   GElf_Ehdr *ehdr2 = gelf_getehdr (elf2, &ehdr2_mem);
    186   if (ehdr2 == NULL)
    187     error (2, 0, gettext ("cannot get ELF header of '%s': %s"),
    188 	   fname2, elf_errmsg (-1));
    189 
    190 #define DIFFERENCE							      \
    191   do									      \
    192     {									      \
    193       result = 1;							      \
    194       if (! verbose)							      \
    195 	goto out;							      \
    196     }									      \
    197   while (0)
    198 
    199   /* Compare the ELF headers.  */
    200   if (unlikely (memcmp (ehdr1->e_ident, ehdr2->e_ident, EI_NIDENT) != 0
    201 		|| ehdr1->e_type != ehdr2->e_type
    202 		|| ehdr1->e_machine != ehdr2->e_machine
    203 		|| ehdr1->e_version != ehdr2->e_version
    204 		|| ehdr1->e_entry != ehdr2->e_entry
    205 		|| ehdr1->e_phoff != ehdr2->e_phoff
    206 		|| ehdr1->e_flags != ehdr2->e_flags
    207 		|| ehdr1->e_ehsize != ehdr2->e_ehsize
    208 		|| ehdr1->e_phentsize != ehdr2->e_phentsize
    209 		|| ehdr1->e_phnum != ehdr2->e_phnum
    210 		|| ehdr1->e_shentsize != ehdr2->e_shentsize))
    211     {
    212       if (! quiet)
    213 	error (0, 0, gettext ("%s %s diff: ELF header"), fname1, fname2);
    214       DIFFERENCE;
    215     }
    216 
    217   size_t shnum1;
    218   size_t shnum2;
    219   if (unlikely (elf_getshdrnum (elf1, &shnum1) != 0))
    220     error (2, 0, gettext ("cannot get section count of '%s': %s"),
    221 	   fname1, elf_errmsg (-1));
    222   if (unlikely (elf_getshdrnum (elf2, &shnum2) != 0))
    223     error (2, 0, gettext ("cannot get section count of '%s': %s"),
    224 	   fname2, elf_errmsg (-1));
    225   if (unlikely (shnum1 != shnum2))
    226     {
    227       if (! quiet)
    228 	error (0, 0, gettext ("%s %s diff: section count"), fname1, fname2);
    229       DIFFERENCE;
    230     }
    231 
    232   size_t phnum1;
    233   size_t phnum2;
    234   if (unlikely (elf_getphdrnum (elf1, &phnum1) != 0))
    235     error (2, 0, gettext ("cannot get program header count of '%s': %s"),
    236 	   fname1, elf_errmsg (-1));
    237   if (unlikely (elf_getphdrnum (elf2, &phnum2) != 0))
    238     error (2, 0, gettext ("cannot get program header count of '%s': %s"),
    239 	   fname2, elf_errmsg (-1));
    240   if (unlikely (phnum1 != phnum2))
    241     {
    242       if (! quiet)
    243 	error (0, 0, gettext ("%s %s diff: program header count"),
    244 	       fname1, fname2);
    245       DIFFERENCE;
    246     }
    247 
    248   /* Iterate over all sections.  We expect the sections in the two
    249      files to match exactly.  */
    250   Elf_Scn *scn1 = NULL;
    251   Elf_Scn *scn2 = NULL;
    252   struct region *regions = NULL;
    253   size_t nregions = 0;
    254   while (1)
    255     {
    256       GElf_Shdr shdr1_mem;
    257       GElf_Shdr *shdr1;
    258       const char *sname1 = NULL;
    259       do
    260 	{
    261 	  scn1 = elf_nextscn (elf1, scn1);
    262 	  shdr1 = gelf_getshdr (scn1, &shdr1_mem);
    263 	  if (shdr1 != NULL)
    264 	    sname1 = elf_strptr (elf1, ehdr1->e_shstrndx, shdr1->sh_name);
    265 	}
    266       while (scn1 != NULL
    267 	     && ebl_section_strip_p (ebl1, ehdr1, shdr1, sname1, true, false));
    268 
    269       GElf_Shdr shdr2_mem;
    270       GElf_Shdr *shdr2;
    271       const char *sname2 = NULL;
    272       do
    273 	{
    274 	  scn2 = elf_nextscn (elf2, scn2);
    275 	  shdr2 = gelf_getshdr (scn2, &shdr2_mem);
    276 	  if (shdr2 != NULL)
    277 	    sname2 = elf_strptr (elf2, ehdr2->e_shstrndx, shdr2->sh_name);
    278 	}
    279       while (scn2 != NULL
    280 	     && ebl_section_strip_p (ebl2, ehdr2, shdr2, sname2, true, false));
    281 
    282       if (scn1 == NULL || scn2 == NULL)
    283 	break;
    284 
    285       if (gaps != gaps_ignore && (shdr1->sh_flags & SHF_ALLOC) != 0)
    286 	{
    287 	  struct region *newp = (struct region *) alloca (sizeof (*newp));
    288 	  newp->from = shdr1->sh_offset;
    289 	  newp->to = shdr1->sh_offset + shdr1->sh_size;
    290 	  newp->next = regions;
    291 	  regions = newp;
    292 
    293 	  ++nregions;
    294 	}
    295 
    296       /* Compare the headers.  We allow the name to be at a different
    297 	 location.  */
    298       if (unlikely (strcmp (sname1, sname2) != 0))
    299 	{
    300 	  error (0, 0, gettext ("%s %s differ: section [%zu], [%zu] name"),
    301 		 fname1, fname2, elf_ndxscn (scn1), elf_ndxscn (scn2));
    302 	  DIFFERENCE;
    303 	}
    304 
    305       /* We ignore certain sections.  */
    306       if (strcmp (sname1, ".gnu_debuglink") == 0
    307 	  || strcmp (sname1, ".gnu.prelink_undo") == 0)
    308 	continue;
    309 
    310       if (shdr1->sh_type != shdr2->sh_type
    311 	  // XXX Any flags which should be ignored?
    312 	  || shdr1->sh_flags != shdr2->sh_flags
    313 	  || shdr1->sh_addr != shdr2->sh_addr
    314 	  || (shdr1->sh_offset != shdr2->sh_offset
    315 	      && (shdr1->sh_flags & SHF_ALLOC)
    316 	      && ehdr1->e_type != ET_REL)
    317 	  || shdr1->sh_size != shdr2->sh_size
    318 	  || shdr1->sh_link != shdr2->sh_link
    319 	  || shdr1->sh_info != shdr2->sh_info
    320 	  || shdr1->sh_addralign != shdr2->sh_addralign
    321 	  || shdr1->sh_entsize != shdr2->sh_entsize)
    322 	{
    323 	  error (0, 0, gettext ("%s %s differ: section [%zu] '%s' header"),
    324 		 fname1, fname2, elf_ndxscn (scn1), sname1);
    325 	  DIFFERENCE;
    326 	}
    327 
    328       Elf_Data *data1 = elf_getdata (scn1, NULL);
    329       if (data1 == NULL)
    330 	error (2, 0,
    331 	       gettext ("cannot get content of section %zu in '%s': %s"),
    332 	       elf_ndxscn (scn1), fname1, elf_errmsg (-1));
    333 
    334       Elf_Data *data2 = elf_getdata (scn2, NULL);
    335       if (data2 == NULL)
    336 	error (2, 0,
    337 	       gettext ("cannot get content of section %zu in '%s': %s"),
    338 	       elf_ndxscn (scn2), fname2, elf_errmsg (-1));
    339 
    340       switch (shdr1->sh_type)
    341 	{
    342 	case SHT_DYNSYM:
    343 	case SHT_SYMTAB:
    344 	  /* Iterate over the symbol table.  We ignore the st_size
    345 	     value of undefined symbols.  */
    346 	  for (int ndx = 0; ndx < (int) (shdr1->sh_size / shdr1->sh_entsize);
    347 	       ++ndx)
    348 	    {
    349 	      GElf_Sym sym1_mem;
    350 	      GElf_Sym *sym1 = gelf_getsym (data1, ndx, &sym1_mem);
    351 	      if (sym1 == NULL)
    352 		error (2, 0,
    353 		       gettext ("cannot get symbol in '%s': %s"),
    354 		       fname1, elf_errmsg (-1));
    355 	      GElf_Sym sym2_mem;
    356 	      GElf_Sym *sym2 = gelf_getsym (data2, ndx, &sym2_mem);
    357 	      if (sym2 == NULL)
    358 		error (2, 0,
    359 		       gettext ("cannot get symbol in '%s': %s"),
    360 		       fname2, elf_errmsg (-1));
    361 
    362 	      const char *name1 = elf_strptr (elf1, shdr1->sh_link,
    363 					      sym1->st_name);
    364 	      const char *name2 = elf_strptr (elf2, shdr2->sh_link,
    365 					      sym2->st_name);
    366 	      if (unlikely (strcmp (name1, name2) != 0
    367 			    || sym1->st_value != sym2->st_value
    368 			    || (sym1->st_size != sym2->st_size
    369 				&& sym1->st_shndx != SHN_UNDEF)
    370 			    || sym1->st_info != sym2->st_info
    371 			    || sym1->st_other != sym2->st_other
    372 			    || sym1->st_shndx != sym1->st_shndx))
    373 		{
    374 		  // XXX Do we want to allow reordered symbol tables?
    375 		symtab_mismatch:
    376 		  if (! quiet)
    377 		    {
    378 		      if (elf_ndxscn (scn1) == elf_ndxscn (scn2))
    379 			error (0, 0,
    380 			       gettext ("%s %s differ: symbol table [%zu]"),
    381 			       fname1, fname2, elf_ndxscn (scn1));
    382 		      else
    383 			error (0, 0, gettext ("\
    384 %s %s differ: symbol table [%zu,%zu]"),
    385 			       fname1, fname2, elf_ndxscn (scn1),
    386 			       elf_ndxscn (scn2));
    387 		    }
    388 		  DIFFERENCE;
    389 		  break;
    390 		}
    391 
    392 	      if (sym1->st_shndx == SHN_UNDEF
    393 		  && sym1->st_size != sym2->st_size)
    394 		{
    395 		  /* The size of the symbol in the object defining it
    396 		     might have changed.  That is OK unless the symbol
    397 		     is used in a copy relocation.  Look over the
    398 		     sections in both files and determine which
    399 		     relocation section uses this symbol table
    400 		     section.  Then look through the relocations to
    401 		     see whether any copy relocation references this
    402 		     symbol.  */
    403 		  if (search_for_copy_reloc (ebl1, elf_ndxscn (scn1), ndx)
    404 		      || search_for_copy_reloc (ebl2, elf_ndxscn (scn2), ndx))
    405 		    goto symtab_mismatch;
    406 		}
    407 	    }
    408 	  break;
    409 
    410 	case SHT_NOTE:
    411 	  /* Parse the note format and compare the notes themselves.  */
    412 	  {
    413 	    GElf_Nhdr note1;
    414 	    GElf_Nhdr note2;
    415 
    416 	    size_t off1 = 0;
    417 	    size_t off2 = 0;
    418 	    size_t name_offset;
    419 	    size_t desc_offset;
    420 	    while (off1 < data1->d_size
    421 		   && (off1 = gelf_getnote (data1, off1, &note1,
    422 					    &name_offset, &desc_offset)) > 0)
    423 	      {
    424 		const char *name1 = data1->d_buf + name_offset;
    425 		const void *desc1 = data1->d_buf + desc_offset;
    426 		if (off2 >= data2->d_size)
    427 		  {
    428 		    if (! quiet)
    429 		      error (0, 0, gettext ("\
    430 %s %s differ: section [%zu] '%s' number of notes"),
    431 			     fname1, fname2, elf_ndxscn (scn1), sname1);
    432 		    DIFFERENCE;
    433 		  }
    434 		off2 = gelf_getnote (data2, off2, &note2,
    435 				     &name_offset, &desc_offset);
    436 		if (off2 == 0)
    437 		  error (2, 0, gettext ("\
    438 cannot read note section [%zu] '%s' in '%s': %s"),
    439 			 elf_ndxscn (scn2), sname2, fname2, elf_errmsg (-1));
    440 		const char *name2 = data2->d_buf + name_offset;
    441 		const void *desc2 = data2->d_buf + desc_offset;
    442 
    443 		if (note1.n_namesz != note2.n_namesz
    444 		    || memcmp (name1, name2, note1.n_namesz))
    445 		  {
    446 		    if (! quiet)
    447 		      error (0, 0, gettext ("\
    448 %s %s differ: section [%zu] '%s' note name"),
    449 			     fname1, fname2, elf_ndxscn (scn1), sname1);
    450 		    DIFFERENCE;
    451 		  }
    452 		if (note1.n_type != note2.n_type)
    453 		  {
    454 		    if (! quiet)
    455 		      error (0, 0, gettext ("\
    456 %s %s differ: section [%zu] '%s' note '%s' type"),
    457 			     fname1, fname2, elf_ndxscn (scn1), sname1, name1);
    458 		    DIFFERENCE;
    459 		  }
    460 		if (note1.n_descsz != note2.n_descsz
    461 		    || memcmp (desc1, desc2, note1.n_descsz))
    462 		  {
    463 		    if (note1.n_type == NT_GNU_BUILD_ID
    464 			&& note1.n_namesz == sizeof "GNU"
    465 			&& !memcmp (name1, "GNU", sizeof "GNU"))
    466 		      {
    467 			if (note1.n_descsz != note2.n_descsz)
    468 			  {
    469 			    if (! quiet)
    470 			      error (0, 0, gettext ("\
    471 %s %s differ: build ID length"),
    472 				     fname1, fname2);
    473 			    DIFFERENCE;
    474 			  }
    475 			else if (! ignore_build_id)
    476 			  {
    477 			    if (! quiet)
    478 			      error (0, 0, gettext ("\
    479 %s %s differ: build ID content"),
    480 				     fname1, fname2);
    481 			    DIFFERENCE;
    482 			  }
    483 		      }
    484 		    else
    485 		      {
    486 			if (! quiet)
    487 			  error (0, 0, gettext ("\
    488 %s %s differ: section [%zu] '%s' note '%s' content"),
    489 				 fname1, fname2, elf_ndxscn (scn1), sname1,
    490 				 name1);
    491 			DIFFERENCE;
    492 		      }
    493 		  }
    494 	      }
    495 	    if (off2 < data2->d_size)
    496 	      {
    497 		if (! quiet)
    498 		  error (0, 0, gettext ("\
    499 %s %s differ: section [%zu] '%s' number of notes"),
    500 			 fname1, fname2, elf_ndxscn (scn1), sname1);
    501 		DIFFERENCE;
    502 	      }
    503 	  }
    504 	  break;
    505 
    506 	default:
    507 	  /* Compare the section content byte for byte.  */
    508 	  assert (shdr1->sh_type == SHT_NOBITS
    509 		  || (data1->d_buf != NULL || data1->d_size == 0));
    510 	  assert (shdr2->sh_type == SHT_NOBITS
    511 		  || (data2->d_buf != NULL || data1->d_size == 0));
    512 
    513 	  if (unlikely (data1->d_size != data2->d_size
    514 			|| (shdr1->sh_type != SHT_NOBITS
    515 			    && memcmp (data1->d_buf, data2->d_buf,
    516 				       data1->d_size) != 0)))
    517 	    {
    518 	      if (hash_inexact
    519 		  && shdr1->sh_type == SHT_HASH
    520 		  && data1->d_size == data2->d_size
    521 		  && hash_content_equivalent (shdr1->sh_entsize, data1, data2))
    522 		break;
    523 
    524 	      if (! quiet)
    525 		{
    526 		  if (elf_ndxscn (scn1) == elf_ndxscn (scn2))
    527 		    error (0, 0, gettext ("\
    528 %s %s differ: section [%zu] '%s' content"),
    529 			   fname1, fname2, elf_ndxscn (scn1), sname1);
    530 		  else
    531 		    error (0, 0, gettext ("\
    532 %s %s differ: section [%zu,%zu] '%s' content"),
    533 			   fname1, fname2, elf_ndxscn (scn1),
    534 			   elf_ndxscn (scn2), sname1);
    535 		}
    536 	      DIFFERENCE;
    537 	    }
    538 	  break;
    539 	}
    540     }
    541 
    542   if (unlikely (scn1 != scn2))
    543     {
    544       if (! quiet)
    545 	error (0, 0,
    546 	       gettext ("%s %s differ: unequal amount of important sections"),
    547 	       fname1, fname2);
    548       DIFFERENCE;
    549     }
    550 
    551   /* We we look at gaps, create artificial ones for the parts of the
    552      program which we are not in sections.  */
    553   struct region ehdr_region;
    554   struct region phdr_region;
    555   if (gaps != gaps_ignore)
    556     {
    557       ehdr_region.from = 0;
    558       ehdr_region.to = ehdr1->e_ehsize;
    559       ehdr_region.next = &phdr_region;
    560 
    561       phdr_region.from = ehdr1->e_phoff;
    562       phdr_region.to = ehdr1->e_phoff + phnum1 * ehdr1->e_phentsize;
    563       phdr_region.next = regions;
    564 
    565       regions = &ehdr_region;
    566       nregions += 2;
    567     }
    568 
    569   /* If we need to look at the gaps we need access to the file data.  */
    570   char *raw1 = NULL;
    571   size_t size1 = 0;
    572   char *raw2 = NULL;
    573   size_t size2 = 0;
    574   struct region *regionsarr = alloca (nregions * sizeof (struct region));
    575   if (gaps != gaps_ignore)
    576     {
    577       raw1 = elf_rawfile (elf1, &size1);
    578       if (raw1 == NULL )
    579 	error (2, 0, gettext ("cannot load data of '%s': %s"),
    580 	       fname1, elf_errmsg (-1));
    581 
    582       raw2 = elf_rawfile (elf2, &size2);
    583       if (raw2 == NULL )
    584 	error (2, 0, gettext ("cannot load data of '%s': %s"),
    585 	       fname2, elf_errmsg (-1));
    586 
    587       for (size_t cnt = 0; cnt < nregions; ++cnt)
    588 	{
    589 	  regionsarr[cnt] = *regions;
    590 	  regions = regions->next;
    591 	}
    592 
    593       qsort (regionsarr, nregions, sizeof (regionsarr[0]), regioncompare);
    594     }
    595 
    596   /* Compare the program header tables.  */
    597   for (unsigned int ndx = 0; ndx < phnum1; ++ndx)
    598     {
    599       GElf_Phdr phdr1_mem;
    600       GElf_Phdr *phdr1 = gelf_getphdr (elf1, ndx, &phdr1_mem);
    601       if (ehdr1 == NULL)
    602 	error (2, 0,
    603 	       gettext ("cannot get program header entry %d of '%s': %s"),
    604 	       ndx, fname1, elf_errmsg (-1));
    605       GElf_Phdr phdr2_mem;
    606       GElf_Phdr *phdr2 = gelf_getphdr (elf2, ndx, &phdr2_mem);
    607       if (ehdr2 == NULL)
    608 	error (2, 0,
    609 	       gettext ("cannot get program header entry %d of '%s': %s"),
    610 	       ndx, fname2, elf_errmsg (-1));
    611 
    612       if (unlikely (memcmp (phdr1, phdr2, sizeof (GElf_Phdr)) != 0))
    613 	{
    614 	  if (! quiet)
    615 	    error (0, 0, gettext ("%s %s differ: program header %d"),
    616 		   fname1, fname2, ndx);
    617 	  DIFFERENCE;
    618 	}
    619 
    620       if (gaps != gaps_ignore && phdr1->p_type == PT_LOAD)
    621 	{
    622 	  size_t cnt = 0;
    623 	  while (cnt < nregions && regionsarr[cnt].to < phdr1->p_offset)
    624 	    ++cnt;
    625 
    626 	  GElf_Off last = phdr1->p_offset;
    627 	  GElf_Off end = phdr1->p_offset + phdr1->p_filesz;
    628 	  while (cnt < nregions && regionsarr[cnt].from < end)
    629 	    {
    630 	      if (last < regionsarr[cnt].from)
    631 		{
    632 		  /* Compare the [LAST,FROM) region.  */
    633 		  assert (gaps == gaps_match);
    634 		  if (unlikely (memcmp (raw1 + last, raw2 + last,
    635 					regionsarr[cnt].from - last) != 0))
    636 		    {
    637 		    gapmismatch:
    638 		      if (!quiet)
    639 			error (0, 0, gettext ("%s %s differ: gap"),
    640 			       fname1, fname2);
    641 		      DIFFERENCE;
    642 		      break;
    643 		    }
    644 
    645 		}
    646 	      last = regionsarr[cnt].to;
    647 	      ++cnt;
    648 	    }
    649 
    650 	  if (cnt == nregions && last < end)
    651 	    goto gapmismatch;
    652 	}
    653     }
    654 
    655  out:
    656   elf_end (elf1);
    657   elf_end (elf2);
    658   close (fd1);
    659   close (fd2);
    660 
    661   return result;
    662 }
    663 
    664 
    665 /* Print the version information.  */
    666 static void
    667 print_version (FILE *stream, struct argp_state *state __attribute__ ((unused)))
    668 {
    669   fprintf (stream, "elfcmp (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION);
    670   fprintf (stream, gettext ("\
    671 Copyright (C) %s Red Hat, Inc.\n\
    672 This is free software; see the source for copying conditions.  There is NO\n\
    673 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
    674 "), "2012");
    675   fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
    676 }
    677 
    678 
    679 /* Handle program arguments.  */
    680 static error_t
    681 parse_opt (int key, char *arg,
    682 	   struct argp_state *state __attribute__ ((unused)))
    683 {
    684   switch (key)
    685     {
    686     case 'q':
    687       quiet = true;
    688       break;
    689 
    690     case 'l':
    691       verbose = true;
    692       break;
    693 
    694     case OPT_GAPS:
    695       if (strcasecmp (arg, "ignore") == 0)
    696 	gaps = gaps_ignore;
    697       else if (likely (strcasecmp (arg, "match") == 0))
    698 	gaps = gaps_match;
    699       else
    700 	{
    701 	  fprintf (stderr,
    702 		   gettext ("Invalid value '%s' for --gaps parameter."),
    703 		   arg);
    704 	  argp_help (&argp, stderr, ARGP_HELP_SEE,
    705 		     program_invocation_short_name);
    706 	  exit (1);
    707 	}
    708       break;
    709 
    710     case OPT_HASH_INEXACT:
    711       hash_inexact = true;
    712       break;
    713 
    714     case OPT_IGNORE_BUILD_ID:
    715       ignore_build_id = true;
    716       break;
    717 
    718     default:
    719       return ARGP_ERR_UNKNOWN;
    720     }
    721   return 0;
    722 }
    723 
    724 
    725 static Elf *
    726 open_file (const char *fname, int *fdp, Ebl **eblp)
    727 {
    728   int fd = open (fname, O_RDONLY);
    729   if (unlikely (fd == -1))
    730     error (2, errno, gettext ("cannot open '%s'"), fname);
    731   Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
    732   if (elf == NULL)
    733     error (2, 0,
    734 	   gettext ("cannot create ELF descriptor for '%s': %s"),
    735 	   fname, elf_errmsg (-1));
    736   Ebl *ebl = ebl_openbackend (elf);
    737   if (ebl == NULL)
    738     error (2, 0,
    739 	   gettext ("cannot create EBL descriptor for '%s'"), fname);
    740 
    741   *fdp = fd;
    742   *eblp = ebl;
    743   return elf;
    744 }
    745 
    746 
    747 static bool
    748 search_for_copy_reloc (Ebl *ebl, size_t scnndx, int symndx)
    749 {
    750   Elf_Scn *scn = NULL;
    751   while ((scn = elf_nextscn (ebl->elf, scn)) != NULL)
    752     {
    753       GElf_Shdr shdr_mem;
    754       GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
    755       if (shdr == NULL)
    756 	error (2, 0,
    757 	       gettext ("cannot get section header of section %zu: %s"),
    758 	       elf_ndxscn (scn), elf_errmsg (-1));
    759 
    760       if ((shdr->sh_type != SHT_REL && shdr->sh_type != SHT_RELA)
    761 	  || shdr->sh_link != scnndx)
    762 	continue;
    763 
    764       Elf_Data *data = elf_getdata (scn, NULL);
    765       if (data == NULL)
    766 	error (2, 0,
    767 	       gettext ("cannot get content of section %zu: %s"),
    768 	       elf_ndxscn (scn), elf_errmsg (-1));
    769 
    770       if (shdr->sh_type == SHT_REL)
    771 	for (int ndx = 0; ndx < (int) (shdr->sh_size / shdr->sh_entsize);
    772 	     ++ndx)
    773 	  {
    774 	    GElf_Rel rel_mem;
    775 	    GElf_Rel *rel = gelf_getrel (data, ndx, &rel_mem);
    776 	    if (rel == NULL)
    777 	      error (2, 0, gettext ("cannot get relocation: %s"),
    778 		     elf_errmsg (-1));
    779 
    780 	    if ((int) GELF_R_SYM (rel->r_info) == symndx
    781 		&& ebl_copy_reloc_p (ebl, GELF_R_TYPE (rel->r_info)))
    782 	      return true;
    783 	  }
    784       else
    785 	for (int ndx = 0; ndx < (int) (shdr->sh_size / shdr->sh_entsize);
    786 	     ++ndx)
    787 	  {
    788 	    GElf_Rela rela_mem;
    789 	    GElf_Rela *rela = gelf_getrela (data, ndx, &rela_mem);
    790 	    if (rela == NULL)
    791 	      error (2, 0, gettext ("cannot get relocation: %s"),
    792 		     elf_errmsg (-1));
    793 
    794 	    if ((int) GELF_R_SYM (rela->r_info) == symndx
    795 		&& ebl_copy_reloc_p (ebl, GELF_R_TYPE (rela->r_info)))
    796 	      return true;
    797 	  }
    798     }
    799 
    800   return false;
    801 }
    802 
    803 
    804 static int
    805 regioncompare (const void *p1, const void *p2)
    806 {
    807   const struct region *r1 = (const struct region *) p1;
    808   const struct region *r2 = (const struct region *) p2;
    809 
    810   if (r1->from < r2->from)
    811     return -1;
    812   return 1;
    813 }
    814 
    815 
    816 static int
    817 compare_Elf32_Word (const void *p1, const void *p2)
    818 {
    819   const Elf32_Word *w1 = p1;
    820   const Elf32_Word *w2 = p2;
    821   assert (sizeof (int) >= sizeof (*w1));
    822   return (int) *w1 - (int) *w2;
    823 }
    824 
    825 static int
    826 compare_Elf64_Xword (const void *p1, const void *p2)
    827 {
    828   const Elf64_Xword *w1 = p1;
    829   const Elf64_Xword *w2 = p2;
    830   return *w1 < *w2 ? -1 : *w1 > *w2 ? 1 : 0;
    831 }
    832 
    833 static bool
    834 hash_content_equivalent (size_t entsize, Elf_Data *data1, Elf_Data *data2)
    835 {
    836 #define CHECK_HASH(Hash_Word)						      \
    837   {									      \
    838     const Hash_Word *const hash1 = data1->d_buf;			      \
    839     const Hash_Word *const hash2 = data2->d_buf;			      \
    840     const size_t nbucket = hash1[0];					      \
    841     const size_t nchain = hash1[1];					      \
    842     if (data1->d_size != (2 + nbucket + nchain) * sizeof hash1[0]	      \
    843 	|| hash2[0] != nbucket || hash2[1] != nchain)			      \
    844       return false;							      \
    845 									      \
    846     const Hash_Word *const bucket1 = &hash1[2];				      \
    847     const Hash_Word *const chain1 = &bucket1[nbucket];			      \
    848     const Hash_Word *const bucket2 = &hash2[2];				      \
    849     const Hash_Word *const chain2 = &bucket2[nbucket];			      \
    850 									      \
    851     bool chain_ok[nchain];						      \
    852     Hash_Word temp1[nchain - 1];					      \
    853     Hash_Word temp2[nchain - 1];					      \
    854     memset (chain_ok, 0, sizeof chain_ok);				      \
    855     for (size_t i = 0; i < nbucket; ++i)				      \
    856       {									      \
    857 	if (bucket1[i] >= nchain || bucket2[i] >= nchain)		      \
    858 	  return false;							      \
    859 									      \
    860 	size_t b1 = 0;							      \
    861 	for (size_t p = bucket1[i]; p != STN_UNDEF; p = chain1[p])	      \
    862 	  if (p >= nchain || b1 >= nchain - 1)				      \
    863 	    return false;						      \
    864 	  else								      \
    865 	    temp1[b1++] = p;						      \
    866 									      \
    867 	size_t b2 = 0;							      \
    868 	for (size_t p = bucket2[i]; p != STN_UNDEF; p = chain2[p])	      \
    869 	  if (p >= nchain || b2 >= nchain - 1)				      \
    870 	    return false;						      \
    871 	  else								      \
    872 	    temp2[b2++] = p;						      \
    873 									      \
    874 	if (b1 != b2)							      \
    875 	  return false;							      \
    876 									      \
    877 	qsort (temp1, b1, sizeof temp1[0], compare_##Hash_Word);	      \
    878 	qsort (temp2, b2, sizeof temp2[0], compare_##Hash_Word);	      \
    879 									      \
    880 	for (b1 = 0; b1 < b2; ++b1)					      \
    881 	  if (temp1[b1] != temp2[b1])					      \
    882 	    return false;						      \
    883 	  else								      \
    884 	    chain_ok[temp1[b1]] = true;					      \
    885       }									      \
    886 									      \
    887     for (size_t i = 0; i < nchain; ++i)					      \
    888       if (!chain_ok[i] && chain1[i] != chain2[i])			      \
    889 	return false;							      \
    890 									      \
    891     return true;							      \
    892   }
    893 
    894   switch (entsize)
    895     {
    896     case 4:
    897       CHECK_HASH (Elf32_Word);
    898       break;
    899     case 8:
    900       CHECK_HASH (Elf64_Xword);
    901       break;
    902     }
    903 
    904   return false;
    905 }
    906 
    907 
    908 #include "debugpred.h"
    909