Home | History | Annotate | Download | only in src
      1 /* Compare relevant content of two ELF files.
      2    Copyright (C) 2005, 2006, 2007, 2008 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 "../libelf/elf-knowledge.h"
     45 #include "../libebl/libeblP.h"
     46 
     47 
     48 /* Prototypes of local functions.  */
     49 static Elf *open_file (const char *fname, int *fdp, Ebl **eblp);
     50 static bool search_for_copy_reloc (Ebl *ebl, size_t scnndx, int symndx);
     51 static  int regioncompare (const void *p1, const void *p2);
     52 
     53 
     54 /* Name and version of program.  */
     55 static void print_version (FILE *stream, struct argp_state *state);
     56 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
     57 
     58 /* Bug report address.  */
     59 const char *argp_program_bug_address = PACKAGE_BUGREPORT;
     60 
     61 /* Values for the parameters which have no short form.  */
     62 #define OPT_GAPS		0x100
     63 #define OPT_HASH_INEXACT	0x101
     64 
     65 /* Definitions of arguments for argp functions.  */
     66 static const struct argp_option options[] =
     67 {
     68   { NULL, 0, NULL, 0, N_("Control options:"), 0 },
     69   { "gaps", OPT_GAPS, "ACTION", 0, N_("Control treatment of gaps in loadable segments [ignore|match] (default: ignore)"), 0 },
     70   { "hash-inexact", OPT_HASH_INEXACT, NULL, 0,
     71     N_("Ignore permutation of buckets in SHT_HASH section"), 0 },
     72   { "quiet", 'q', NULL, 0, N_("Output nothing; yield exit status only"), 0 },
     73 
     74   { NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 },
     75   { NULL, 0, NULL, 0, NULL, 0 }
     76 };
     77 
     78 /* Short description of program.  */
     79 static const char doc[] = N_("\
     80 Compare relevant parts of two ELF files for equality.");
     81 
     82 /* Strings for arguments in help texts.  */
     83 static const char args_doc[] = N_("FILE1 FILE2");
     84 
     85 /* Prototype for option handler.  */
     86 static error_t parse_opt (int key, char *arg, struct argp_state *state);
     87 
     88 /* Data structure to communicate with argp functions.  */
     89 static struct argp argp =
     90 {
     91   options, parse_opt, args_doc, doc, NULL, NULL, NULL
     92 };
     93 
     94 
     95 /* How to treat gaps in loadable segments.  */
     96 static enum
     97   {
     98     gaps_ignore = 0,
     99     gaps_match
    100   }
    101   gaps;
    102 
    103 /* Structure to hold information about used regions.  */
    104 struct region
    105 {
    106   GElf_Addr from;
    107   GElf_Addr to;
    108   struct region *next;
    109 };
    110 
    111 /* Nonzero if only exit status is wanted.  */
    112 static bool quiet;
    113 
    114 /* True iff SHT_HASH treatment should be generous.  */
    115 static bool hash_inexact;
    116 
    117 static bool hash_content_equivalent (size_t entsize, Elf_Data *, Elf_Data *);
    118 
    119 
    120 int
    121 main (int argc, char *argv[])
    122 {
    123   /* Set locale.  */
    124   (void) setlocale (LC_ALL, "");
    125 
    126   /* Make sure the message catalog can be found.  */
    127   (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
    128 
    129   /* Initialize the message catalog.  */
    130   (void) textdomain (PACKAGE_TARNAME);
    131 
    132   /* Parse and process arguments.  */
    133   int remaining;
    134   (void) argp_parse (&argp, argc, argv, 0, &remaining, NULL);
    135 
    136   /* We expect exactly two non-option parameters.  */
    137   if (unlikely (remaining + 2 != argc))
    138     {
    139       fputs (gettext ("Invalid number of parameters.\n"), stderr);
    140       argp_help (&argp, stderr, ARGP_HELP_SEE, program_invocation_short_name);
    141       exit (1);
    142     }
    143 
    144   /* Comparing the files is done in two phases:
    145      1. compare all sections.  Sections which are irrelevant (i.e., if
    146         strip would remove them) are ignored.  Some section types are
    147 	handled special.
    148      2. all parts of the loadable segments which are not parts of any
    149         section is compared according to the rules of the --gaps option.
    150   */
    151   int result = 0;
    152   elf_version (EV_CURRENT);
    153 
    154   const char *const fname1 = argv[remaining];
    155   int fd1;
    156   Ebl *ebl1;
    157   Elf *elf1 = open_file (fname1, &fd1, &ebl1);
    158 
    159   const char *const fname2 = argv[remaining + 1];
    160   int fd2;
    161   Ebl *ebl2;
    162   Elf *elf2 = open_file (fname2, &fd2, &ebl2);
    163 
    164   GElf_Ehdr ehdr1_mem;
    165   GElf_Ehdr *ehdr1 = gelf_getehdr (elf1, &ehdr1_mem);
    166   if (ehdr1 == NULL)
    167     error (EXIT_FAILURE, 0, gettext ("cannot get ELF header of '%s': %s"),
    168 	   fname1, elf_errmsg (-1));
    169   GElf_Ehdr ehdr2_mem;
    170   GElf_Ehdr *ehdr2 = gelf_getehdr (elf2, &ehdr2_mem);
    171   if (ehdr2 == NULL)
    172     error (EXIT_FAILURE, 0, gettext ("cannot get ELF header of '%s': %s"),
    173 	   fname2, elf_errmsg (-1));
    174 
    175   /* Compare the ELF headers.  */
    176   if (unlikely (memcmp (ehdr1->e_ident, ehdr2->e_ident, EI_NIDENT) != 0
    177 		|| ehdr1->e_type != ehdr2->e_type
    178 		|| ehdr1->e_machine != ehdr2->e_machine
    179 		|| ehdr1->e_version != ehdr2->e_version
    180 		|| ehdr1->e_entry != ehdr2->e_entry
    181 		|| ehdr1->e_phoff != ehdr2->e_phoff
    182 		|| ehdr1->e_flags != ehdr2->e_flags
    183 		|| ehdr1->e_ehsize != ehdr2->e_ehsize
    184 		|| ehdr1->e_phentsize != ehdr2->e_phentsize
    185 		|| ehdr1->e_phnum != ehdr2->e_phnum
    186 		|| ehdr1->e_shentsize != ehdr2->e_shentsize))
    187     {
    188       if (! quiet)
    189 	error (0, 0, gettext ("%s %s diff: ELF header"), fname1, fname2);
    190       result = 1;
    191       goto out;
    192     }
    193 
    194   /* Iterate over all sections.  We expect the sections in the two
    195      files to match exactly.  */
    196   Elf_Scn *scn1 = NULL;
    197   Elf_Scn *scn2 = NULL;
    198   struct region *regions = NULL;
    199   size_t nregions = 0;
    200   while (1)
    201     {
    202       GElf_Shdr shdr1_mem;
    203       GElf_Shdr *shdr1;
    204       const char *sname1 = NULL;
    205       do
    206 	{
    207 	  scn1 = elf_nextscn (elf1, scn1);
    208 	  shdr1 = gelf_getshdr (scn1, &shdr1_mem);
    209 	  if (shdr1 != NULL)
    210 	    sname1 = elf_strptr (elf1, ehdr1->e_shstrndx, shdr1->sh_name);
    211 	}
    212       while (scn1 != NULL
    213 	     && ebl_section_strip_p (ebl1, ehdr1, shdr1, sname1, true, false));
    214 
    215       GElf_Shdr shdr2_mem;
    216       GElf_Shdr *shdr2;
    217       const char *sname2 = NULL;
    218       do
    219 	{
    220 	  scn2 = elf_nextscn (elf2, scn2);
    221 	  shdr2 = gelf_getshdr (scn2, &shdr2_mem);
    222 	  if (shdr2 != NULL)
    223 	    sname2 = elf_strptr (elf2, ehdr2->e_shstrndx, shdr2->sh_name);
    224 	}
    225       while (scn2 != NULL
    226 	     && ebl_section_strip_p (ebl2, ehdr2, shdr2, sname2, true, false));
    227 
    228       if (scn1 == NULL || scn2 == NULL)
    229 	break;
    230 
    231       if (gaps != gaps_ignore && (shdr1->sh_flags & SHF_ALLOC) != 0)
    232 	{
    233 	  struct region *newp = (struct region *) alloca (sizeof (*newp));
    234 	  newp->from = shdr1->sh_offset;
    235 	  newp->to = shdr1->sh_offset + shdr1->sh_size;
    236 	  newp->next = regions;
    237 	  regions = newp;
    238 
    239 	  ++nregions;
    240 	}
    241 
    242       /* Compare the headers.  We allow the name to be at a different
    243 	 location.  */
    244       if (unlikely (strcmp (sname1, sname2) != 0))
    245 	{
    246 	header_mismatch:
    247 	  error (0, 0, gettext ("%s %s differ: section header"),
    248 		 fname1, fname2);
    249 	  result = 1;
    250 	  goto out;
    251 	}
    252 
    253       /* We ignore certain sections.  */
    254       if (strcmp (sname1, ".gnu_debuglink") == 0
    255 	  || strcmp (sname1, ".gnu.prelink_undo") == 0)
    256 	continue;
    257 
    258       if (shdr1->sh_type != shdr2->sh_type
    259 	  // XXX Any flags which should be ignored?
    260 	  || shdr1->sh_flags != shdr2->sh_flags
    261 	  || shdr1->sh_addr != shdr2->sh_addr
    262 	  || (shdr1->sh_offset != shdr2->sh_offset
    263 	      && (shdr1->sh_flags & SHF_ALLOC)
    264 	      && ehdr1->e_type != ET_REL)
    265 	  || shdr1->sh_size != shdr2->sh_size
    266 	  || shdr1->sh_link != shdr2->sh_link
    267 	  || shdr1->sh_info != shdr2->sh_info
    268 	  || shdr1->sh_addralign != shdr2->sh_addralign
    269 	  || shdr1->sh_entsize != shdr2->sh_entsize)
    270 	goto header_mismatch;
    271 
    272       Elf_Data *data1 = elf_getdata (scn1, NULL);
    273       if (data1 == NULL)
    274 	error (EXIT_FAILURE, 0,
    275 	       gettext ("cannot get content of section %zu in '%s': %s"),
    276 	       elf_ndxscn (scn1), fname1, elf_errmsg (-1));
    277 
    278       Elf_Data *data2 = elf_getdata (scn2, NULL);
    279       if (data2 == NULL)
    280 	error (EXIT_FAILURE, 0,
    281 	       gettext ("cannot get content of section %zu in '%s': %s"),
    282 	       elf_ndxscn (scn2), fname2, elf_errmsg (-1));
    283 
    284       switch (shdr1->sh_type)
    285 	{
    286 	case SHT_DYNSYM:
    287 	case SHT_SYMTAB:
    288 	  /* Iterate over the symbol table.  We ignore the st_size
    289 	     value of undefined symbols.  */
    290 	  for (int ndx = 0; ndx < (int) (shdr1->sh_size / shdr1->sh_entsize);
    291 	       ++ndx)
    292 	    {
    293 	      GElf_Sym sym1_mem;
    294 	      GElf_Sym *sym1 = gelf_getsym (data1, ndx, &sym1_mem);
    295 	      if (sym1 == NULL)
    296 		error (EXIT_FAILURE, 0,
    297 		       gettext ("cannot get symbol in '%s': %s"),
    298 		       fname1, elf_errmsg (-1));
    299 	      GElf_Sym sym2_mem;
    300 	      GElf_Sym *sym2 = gelf_getsym (data2, ndx, &sym2_mem);
    301 	      if (sym2 == NULL)
    302 		error (EXIT_FAILURE, 0,
    303 		       gettext ("cannot get symbol in '%s': %s"),
    304 		       fname2, elf_errmsg (-1));
    305 
    306 	      const char *name1 = elf_strptr (elf1, shdr1->sh_link,
    307 					      sym1->st_name);
    308 	      const char *name2 = elf_strptr (elf2, shdr2->sh_link,
    309 					      sym2->st_name);
    310 	      if (unlikely (strcmp (name1, name2) != 0
    311 			    || sym1->st_value != sym2->st_value
    312 			    || (sym1->st_size != sym2->st_size
    313 				&& sym1->st_shndx != SHN_UNDEF)
    314 			    || sym1->st_info != sym2->st_info
    315 			    || sym1->st_other != sym2->st_other
    316 			    || sym1->st_shndx != sym1->st_shndx))
    317 		{
    318 		  // XXX Do we want to allow reordered symbol tables?
    319 		symtab_mismatch:
    320 		  if (! quiet)
    321 		    {
    322 		      if (elf_ndxscn (scn1) == elf_ndxscn (scn2))
    323 			error (0, 0,
    324 			       gettext ("%s %s differ: symbol table [%zu]"),
    325 			       fname1, fname2, elf_ndxscn (scn1));
    326 		      else
    327 			error (0, 0, gettext ("\
    328 %s %s differ: symbol table [%zu,%zu]"),
    329 			       fname1, fname2, elf_ndxscn (scn1),
    330 			       elf_ndxscn (scn2));
    331 		    }
    332 		  result = 1;
    333 		  goto out;
    334 		}
    335 
    336 	      if (sym1->st_shndx == SHN_UNDEF
    337 		  && sym1->st_size != sym2->st_size)
    338 		{
    339 		  /* The size of the symbol in the object defining it
    340 		     might have changed.  That is OK unless the symbol
    341 		     is used in a copy relocation.  Look over the
    342 		     sections in both files and determine which
    343 		     relocation section uses this symbol table
    344 		     section.  Then look through the relocations to
    345 		     see whether any copy relocation references this
    346 		     symbol.  */
    347 		  if (search_for_copy_reloc (ebl1, elf_ndxscn (scn1), ndx)
    348 		      || search_for_copy_reloc (ebl2, elf_ndxscn (scn2), ndx))
    349 		    goto symtab_mismatch;
    350 		}
    351 	    }
    352 	  break;
    353 
    354 	default:
    355 	  /* Compare the section content byte for byte.  */
    356 	  assert (shdr1->sh_type == SHT_NOBITS
    357 		  || (data1->d_buf != NULL || data1->d_size == 0));
    358 	  assert (shdr2->sh_type == SHT_NOBITS
    359 		  || (data2->d_buf != NULL || data1->d_size == 0));
    360 
    361 	  if (unlikely (data1->d_size != data2->d_size
    362 			|| (shdr1->sh_type != SHT_NOBITS
    363 			    && memcmp (data1->d_buf, data2->d_buf,
    364 				       data1->d_size) != 0)))
    365 	    {
    366 	      if (hash_inexact
    367 		  && shdr1->sh_type == SHT_HASH
    368 		  && data1->d_size == data2->d_size
    369 		  && hash_content_equivalent (shdr1->sh_entsize, data1, data2))
    370 		break;
    371 
    372 	      if (! quiet)
    373 		{
    374 		  if (elf_ndxscn (scn1) == elf_ndxscn (scn2))
    375 		    error (0, 0, gettext ("\
    376 %s %s differ: section [%zu] '%s' content"),
    377 			   fname1, fname2, elf_ndxscn (scn1), sname1);
    378 		  else
    379 		    error (0, 0, gettext ("\
    380 %s %s differ: section [%zu,%zu] '%s' content"),
    381 			   fname1, fname2, elf_ndxscn (scn1),
    382 			   elf_ndxscn (scn2), sname1);
    383 		}
    384 	      result = 1;
    385 	      goto out;
    386 	    }
    387 	  break;
    388 	}
    389     }
    390 
    391   if (unlikely (scn1 != scn2))
    392     {
    393       if (! quiet)
    394 	error (0, 0,
    395 	       gettext ("%s %s differ: unequal amount of important sections"),
    396 	       fname1, fname2);
    397       result = 1;
    398       goto out;
    399     }
    400 
    401   /* We we look at gaps, create artificial ones for the parts of the
    402      program which we are not in sections.  */
    403   struct region ehdr_region;
    404   struct region phdr_region;
    405   if (gaps != gaps_ignore)
    406     {
    407       ehdr_region.from = 0;
    408       ehdr_region.to = ehdr1->e_ehsize;
    409       ehdr_region.next = &phdr_region;
    410 
    411       phdr_region.from = ehdr1->e_phoff;
    412       phdr_region.to = ehdr1->e_phoff + ehdr1->e_phnum * ehdr1->e_phentsize;
    413       phdr_region.next = regions;
    414 
    415       regions = &ehdr_region;
    416       nregions += 2;
    417     }
    418 
    419   /* If we need to look at the gaps we need access to the file data.  */
    420   char *raw1 = NULL;
    421   size_t size1 = 0;
    422   char *raw2 = NULL;
    423   size_t size2 = 0;
    424   struct region *regionsarr = alloca (nregions * sizeof (struct region));
    425   if (gaps != gaps_ignore)
    426     {
    427       raw1 = elf_rawfile (elf1, &size1);
    428       if (raw1 == NULL )
    429 	error (EXIT_FAILURE, 0, gettext ("cannot load data of '%s': %s"),
    430 	       fname1, elf_errmsg (-1));
    431 
    432       raw2 = elf_rawfile (elf2, &size2);
    433       if (raw2 == NULL )
    434 	error (EXIT_FAILURE, 0, gettext ("cannot load data of '%s': %s"),
    435 	       fname2, elf_errmsg (-1));
    436 
    437       for (size_t cnt = 0; cnt < nregions; ++cnt)
    438 	{
    439 	  regionsarr[cnt] = *regions;
    440 	  regions = regions->next;
    441 	}
    442 
    443       qsort (regionsarr, nregions, sizeof (regionsarr[0]), regioncompare);
    444     }
    445 
    446   /* Compare the program header tables.  */
    447   for (int ndx = 0; ndx < ehdr1->e_phnum; ++ndx)
    448     {
    449       GElf_Phdr phdr1_mem;
    450       GElf_Phdr *phdr1 = gelf_getphdr (elf1, ndx, &phdr1_mem);
    451       if (ehdr1 == NULL)
    452 	error (EXIT_FAILURE, 0,
    453 	       gettext ("cannot get program header entry %d of '%s': %s"),
    454 	       ndx, fname1, elf_errmsg (-1));
    455       GElf_Phdr phdr2_mem;
    456       GElf_Phdr *phdr2 = gelf_getphdr (elf2, ndx, &phdr2_mem);
    457       if (ehdr2 == NULL)
    458 	error (EXIT_FAILURE, 0,
    459 	       gettext ("cannot get program header entry %d of '%s': %s"),
    460 	       ndx, fname2, elf_errmsg (-1));
    461 
    462       if (unlikely (memcmp (phdr1, phdr2, sizeof (GElf_Phdr)) != 0))
    463 	{
    464 	  if (! quiet)
    465 	    error (0, 0, gettext ("%s %s differ: program header %d"),
    466 		   fname1, fname2, ndx);
    467 	  result = 1;
    468 	  goto out;
    469 	}
    470 
    471       if (gaps != gaps_ignore && phdr1->p_type == PT_LOAD)
    472 	{
    473 	  size_t cnt = 0;
    474 	  while (cnt < nregions && regionsarr[cnt].to < phdr1->p_offset)
    475 	    ++cnt;
    476 
    477 	  GElf_Off last = phdr1->p_offset;
    478 	  GElf_Off end = phdr1->p_offset + phdr1->p_filesz;
    479 	  while (cnt < nregions && regionsarr[cnt].from < end)
    480 	    {
    481 	      if (last < regionsarr[cnt].from)
    482 		{
    483 		  /* Compare the [LAST,FROM) region.  */
    484 		  assert (gaps == gaps_match);
    485 		  if (unlikely (memcmp (raw1 + last, raw2 + last,
    486 					regionsarr[cnt].from - last) != 0))
    487 		    {
    488 		    gapmismatch:
    489 		      if (!quiet)
    490 			error (0, 0, gettext ("%s %s differ: gap"),
    491 			       fname1, fname2);
    492 		      result = 1;
    493 		      goto out;
    494 		    }
    495 
    496 		}
    497 	      last = regionsarr[cnt].to;
    498 	      ++cnt;
    499 	    }
    500 
    501 	  if (cnt == nregions && last < end)
    502 	    goto gapmismatch;
    503 	}
    504     }
    505 
    506  out:
    507   elf_end (elf1);
    508   elf_end (elf2);
    509   close (fd1);
    510   close (fd2);
    511 
    512   return result;
    513 }
    514 
    515 
    516 /* Print the version information.  */
    517 static void
    518 print_version (FILE *stream, struct argp_state *state __attribute__ ((unused)))
    519 {
    520   fprintf (stream, "elfcmp (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION);
    521   fprintf (stream, gettext ("\
    522 Copyright (C) %s Red Hat, Inc.\n\
    523 This is free software; see the source for copying conditions.  There is NO\n\
    524 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
    525 "), "2008");
    526   fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
    527 }
    528 
    529 
    530 /* Handle program arguments.  */
    531 static error_t
    532 parse_opt (int key, char *arg,
    533 	   struct argp_state *state __attribute__ ((unused)))
    534 {
    535   switch (key)
    536     {
    537     case 'q':
    538       quiet = true;
    539       break;
    540 
    541     case OPT_GAPS:
    542       if (strcasecmp (arg, "ignore") == 0)
    543 	gaps = gaps_ignore;
    544       else if (likely (strcasecmp (arg, "match") == 0))
    545 	gaps = gaps_match;
    546       else
    547 	{
    548 	  fprintf (stderr,
    549 		   gettext ("Invalid value '%s' for --gaps parameter."),
    550 		   arg);
    551 	  argp_help (&argp, stderr, ARGP_HELP_SEE,
    552 		     program_invocation_short_name);
    553 	  exit (1);
    554 	}
    555       break;
    556 
    557     case OPT_HASH_INEXACT:
    558       hash_inexact = true;
    559       break;
    560 
    561     default:
    562       return ARGP_ERR_UNKNOWN;
    563     }
    564   return 0;
    565 }
    566 
    567 
    568 static Elf *
    569 open_file (const char *fname, int *fdp, Ebl **eblp)
    570 {
    571   int fd = open (fname, O_RDONLY);
    572   if (unlikely (fd == -1))
    573     error (EXIT_FAILURE, errno, gettext ("cannot open '%s'"), fname);
    574   Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
    575   if (elf == NULL)
    576     error (EXIT_FAILURE, 0,
    577 	   gettext ("cannot create ELF descriptor for '%s': %s"),
    578 	   fname, elf_errmsg (-1));
    579   Ebl *ebl = ebl_openbackend (elf);
    580   if (ebl == NULL)
    581     error (EXIT_FAILURE, 0,
    582 	   gettext ("cannot create EBL descriptor for '%s'"), fname);
    583 
    584   *fdp = fd;
    585   *eblp = ebl;
    586   return elf;
    587 }
    588 
    589 
    590 static bool
    591 search_for_copy_reloc (Ebl *ebl, size_t scnndx, int symndx)
    592 {
    593   Elf_Scn *scn = NULL;
    594   while ((scn = elf_nextscn (ebl->elf, scn)) != NULL)
    595     {
    596       GElf_Shdr shdr_mem;
    597       GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
    598       if (shdr == NULL)
    599 	error (EXIT_FAILURE, 0,
    600 	       gettext ("cannot get section header of section %zu: %s"),
    601 	       elf_ndxscn (scn), elf_errmsg (-1));
    602 
    603       if ((shdr->sh_type != SHT_REL && shdr->sh_type != SHT_RELA)
    604 	  || shdr->sh_link != scnndx)
    605 	continue;
    606 
    607       Elf_Data *data = elf_getdata (scn, NULL);
    608       if (data == NULL)
    609 	error (EXIT_FAILURE, 0,
    610 	       gettext ("cannot get content of section %zu: %s"),
    611 	       elf_ndxscn (scn), elf_errmsg (-1));
    612 
    613       if (shdr->sh_type == SHT_REL)
    614 	for (int ndx = 0; ndx < (int) (shdr->sh_size / shdr->sh_entsize);
    615 	     ++ndx)
    616 	  {
    617 	    GElf_Rel rel_mem;
    618 	    GElf_Rel *rel = gelf_getrel (data, ndx, &rel_mem);
    619 	    if (rel == NULL)
    620 	      error (EXIT_FAILURE, 0, gettext ("cannot get relocation: %s"),
    621 		     elf_errmsg (-1));
    622 
    623 	    if ((int) GELF_R_SYM (rel->r_info) == symndx
    624 		&& ebl_copy_reloc_p (ebl, GELF_R_TYPE (rel->r_info)))
    625 	      return true;
    626 	  }
    627       else
    628 	for (int ndx = 0; ndx < (int) (shdr->sh_size / shdr->sh_entsize);
    629 	     ++ndx)
    630 	  {
    631 	    GElf_Rela rela_mem;
    632 	    GElf_Rela *rela = gelf_getrela (data, ndx, &rela_mem);
    633 	    if (rela == NULL)
    634 	      error (EXIT_FAILURE, 0, gettext ("cannot get relocation: %s"),
    635 		     elf_errmsg (-1));
    636 
    637 	    if ((int) GELF_R_SYM (rela->r_info) == symndx
    638 		&& ebl_copy_reloc_p (ebl, GELF_R_TYPE (rela->r_info)))
    639 	      return true;
    640 	  }
    641     }
    642 
    643   return false;
    644 }
    645 
    646 
    647 static int
    648 regioncompare (const void *p1, const void *p2)
    649 {
    650   const struct region *r1 = (const struct region *) p1;
    651   const struct region *r2 = (const struct region *) p2;
    652 
    653   if (r1->from < r2->from)
    654     return -1;
    655   return 1;
    656 }
    657 
    658 
    659 static int
    660 compare_Elf32_Word (const void *p1, const void *p2)
    661 {
    662   const Elf32_Word *w1 = p1;
    663   const Elf32_Word *w2 = p2;
    664   assert (sizeof (int) >= sizeof (*w1));
    665   return (int) *w1 - (int) *w2;
    666 }
    667 
    668 static int
    669 compare_Elf64_Xword (const void *p1, const void *p2)
    670 {
    671   const Elf64_Xword *w1 = p1;
    672   const Elf64_Xword *w2 = p2;
    673   return *w1 < *w2 ? -1 : *w1 > *w2 ? 1 : 0;
    674 }
    675 
    676 static bool
    677 hash_content_equivalent (size_t entsize, Elf_Data *data1, Elf_Data *data2)
    678 {
    679 #define CHECK_HASH(Hash_Word)						      \
    680   {									      \
    681     const Hash_Word *const hash1 = data1->d_buf;			      \
    682     const Hash_Word *const hash2 = data2->d_buf;			      \
    683     const size_t nbucket = hash1[0];					      \
    684     const size_t nchain = hash1[1];					      \
    685     if (data1->d_size != (2 + nbucket + nchain) * sizeof hash1[0]	      \
    686 	|| hash2[0] != nbucket || hash2[1] != nchain)			      \
    687       return false;							      \
    688 									      \
    689     const Hash_Word *const bucket1 = &hash1[2];				      \
    690     const Hash_Word *const chain1 = &bucket1[nbucket];			      \
    691     const Hash_Word *const bucket2 = &hash2[2];				      \
    692     const Hash_Word *const chain2 = &bucket2[nbucket];			      \
    693 									      \
    694     bool chain_ok[nchain];						      \
    695     Hash_Word temp1[nchain - 1];					      \
    696     Hash_Word temp2[nchain - 1];					      \
    697     memset (chain_ok, 0, sizeof chain_ok);				      \
    698     for (size_t i = 0; i < nbucket; ++i)				      \
    699       {									      \
    700 	if (bucket1[i] >= nchain || bucket2[i] >= nchain)		      \
    701 	  return false;							      \
    702 									      \
    703 	size_t b1 = 0;							      \
    704 	for (size_t p = bucket1[i]; p != STN_UNDEF; p = chain1[p])	      \
    705 	  if (p >= nchain || b1 >= nchain - 1)				      \
    706 	    return false;						      \
    707 	  else								      \
    708 	    temp1[b1++] = p;						      \
    709 									      \
    710 	size_t b2 = 0;							      \
    711 	for (size_t p = bucket2[i]; p != STN_UNDEF; p = chain2[p])	      \
    712 	  if (p >= nchain || b2 >= nchain - 1)				      \
    713 	    return false;						      \
    714 	  else								      \
    715 	    temp2[b2++] = p;						      \
    716 									      \
    717 	if (b1 != b2)							      \
    718 	  return false;							      \
    719 									      \
    720 	qsort (temp1, b1, sizeof temp1[0], compare_##Hash_Word);	      \
    721 	qsort (temp2, b2, sizeof temp2[0], compare_##Hash_Word);	      \
    722 									      \
    723 	for (b1 = 0; b1 < b2; ++b1)					      \
    724 	  if (temp1[b1] != temp2[b1])					      \
    725 	    return false;						      \
    726 	  else								      \
    727 	    chain_ok[temp1[b1]] = true;					      \
    728       }									      \
    729 									      \
    730     for (size_t i = 0; i < nchain; ++i)					      \
    731       if (!chain_ok[i] && chain1[i] != chain2[i])			      \
    732 	return false;							      \
    733 									      \
    734     return true;							      \
    735   }
    736 
    737   switch (entsize)
    738     {
    739     case 4:
    740       CHECK_HASH (Elf32_Word);
    741       break;
    742     case 8:
    743       CHECK_HASH (Elf64_Xword);
    744       break;
    745     }
    746 
    747   return false;
    748 }
    749 
    750 
    751 #include "debugpred.h"
    752