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@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.  */
49static Elf *open_file (const char *fname, int *fdp, Ebl **eblp);
50static bool search_for_copy_reloc (Ebl *ebl, size_t scnndx, int symndx);
51static  int regioncompare (const void *p1, const void *p2);
52
53
54/* Name and version of program.  */
55static void print_version (FILE *stream, struct argp_state *state);
56void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
57
58/* Bug report address.  */
59const 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.  */
66static 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.  */
79static const char doc[] = N_("\
80Compare relevant parts of two ELF files for equality.");
81
82/* Strings for arguments in help texts.  */
83static const char args_doc[] = N_("FILE1 FILE2");
84
85/* Prototype for option handler.  */
86static error_t parse_opt (int key, char *arg, struct argp_state *state);
87
88/* Data structure to communicate with argp functions.  */
89static 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.  */
96static enum
97  {
98    gaps_ignore = 0,
99    gaps_match
100  }
101  gaps;
102
103/* Structure to hold information about used regions.  */
104struct region
105{
106  GElf_Addr from;
107  GElf_Addr to;
108  struct region *next;
109};
110
111/* Nonzero if only exit status is wanted.  */
112static bool quiet;
113
114/* True iff SHT_HASH treatment should be generous.  */
115static bool hash_inexact;
116
117static bool hash_content_equivalent (size_t entsize, Elf_Data *, Elf_Data *);
118
119
120int
121main (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.  */
517static void
518print_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 ("\
522Copyright (C) %s Red Hat, Inc.\n\
523This is free software; see the source for copying conditions.  There is NO\n\
524warranty; 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.  */
531static error_t
532parse_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
568static Elf *
569open_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
590static bool
591search_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
647static int
648regioncompare (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
659static int
660compare_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
668static int
669compare_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
676static bool
677hash_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