• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.  */
40 static Elf *open_file (const char *fname, int *fdp, Ebl **eblp);
41 static bool search_for_copy_reloc (Ebl *ebl, size_t scnndx, int symndx);
42 static  int regioncompare (const void *p1, const void *p2);
43 
44 
45 /* Name and version of program.  */
46 ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
47 
48 /* Bug report address.  */
49 ARGP_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.  */
57 static 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.  */
74 static const char doc[] = N_("\
75 Compare relevant parts of two ELF files for equality.");
76 
77 /* Strings for arguments in help texts.  */
78 static const char args_doc[] = N_("FILE1 FILE2");
79 
80 /* Prototype for option handler.  */
81 static error_t parse_opt (int key, char *arg, struct argp_state *state);
82 
83 /* Data structure to communicate with argp functions.  */
84 static 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.  */
91 static enum
92   {
93     gaps_ignore = 0,
94     gaps_match
95   }
96   gaps;
97 
98 /* Structure to hold information about used regions.  */
99 struct region
100 {
101   GElf_Addr from;
102   GElf_Addr to;
103   struct region *next;
104 };
105 
106 /* Nonzero if only exit status is wanted.  */
107 static bool quiet;
108 
109 /* True iff multiple differences should be output.  */
110 static bool verbose;
111 
112 /* True iff SHT_HASH treatment should be generous.  */
113 static bool hash_inexact;
114 
115 /* True iff build ID notes should be ignored.  */
116 static bool ignore_build_id;
117 
118 static bool hash_content_equivalent (size_t entsize, Elf_Data *, Elf_Data *);
119 
120 
121 int
main(int argc,char * argv[])122 main (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, _("\
451 cannot 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.  */
683 static error_t
parse_opt(int key,char * arg,struct argp_state * state)684 parse_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 
728 static Elf *
open_file(const char * fname,int * fdp,Ebl ** eblp)729 open_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 
750 static bool
search_for_copy_reloc(Ebl * ebl,size_t scnndx,int symndx)751 search_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 
807 static int
regioncompare(const void * p1,const void * p2)808 regioncompare (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 
819 static int
compare_Elf32_Word(const void * p1,const void * p2)820 compare_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 
827 static int
compare_Elf64_Xword(const void * p1,const void * p2)828 compare_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 
835 static bool
hash_content_equivalent(size_t entsize,Elf_Data * data1,Elf_Data * data2)836 hash_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