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