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