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