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