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