• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.  */
50 static Elf *open_file (const char *fname, int *fdp, Ebl **eblp);
51 static bool search_for_copy_reloc (Ebl *ebl, size_t scnndx, int symndx);
52 static  int regioncompare (const void *p1, const void *p2);
53 
54 
55 /* Name and version of program.  */
56 static void print_version (FILE *stream, struct argp_state *state);
57 ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
58 
59 /* Bug report address.  */
60 ARGP_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.  */
68 static 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.  */
85 static const char doc[] = N_("\
86 Compare relevant parts of two ELF files for equality.");
87 
88 /* Strings for arguments in help texts.  */
89 static const char args_doc[] = N_("FILE1 FILE2");
90 
91 /* Prototype for option handler.  */
92 static error_t parse_opt (int key, char *arg, struct argp_state *state);
93 
94 /* Data structure to communicate with argp functions.  */
95 static 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.  */
102 static enum
103   {
104     gaps_ignore = 0,
105     gaps_match
106   }
107   gaps;
108 
109 /* Structure to hold information about used regions.  */
110 struct region
111 {
112   GElf_Addr from;
113   GElf_Addr to;
114   struct region *next;
115 };
116 
117 /* Nonzero if only exit status is wanted.  */
118 static bool quiet;
119 
120 /* True iff multiple differences should be output.  */
121 static bool verbose;
122 
123 /* True iff SHT_HASH treatment should be generous.  */
124 static bool hash_inexact;
125 
126 /* True iff build ID notes should be ignored.  */
127 static bool ignore_build_id;
128 
129 static bool hash_content_equivalent (size_t entsize, Elf_Data *, Elf_Data *);
130 
131 
132 int
main(int argc,char * argv[])133 main (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 ("\
438 cannot 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.  */
666 static void
print_version(FILE * stream,struct argp_state * state)667 print_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 ("\
671 Copyright (C) %s Red Hat, Inc.\n\
672 This is free software; see the source for copying conditions.  There is NO\n\
673 warranty; 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.  */
680 static error_t
parse_opt(int key,char * arg,struct argp_state * state)681 parse_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 
725 static Elf *
open_file(const char * fname,int * fdp,Ebl ** eblp)726 open_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 
747 static bool
search_for_copy_reloc(Ebl * ebl,size_t scnndx,int symndx)748 search_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 
804 static int
regioncompare(const void * p1,const void * p2)805 regioncompare (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 
816 static int
compare_Elf32_Word(const void * p1,const void * p2)817 compare_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 
825 static int
compare_Elf64_Xword(const void * p1,const void * p2)826 compare_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 
833 static bool
hash_content_equivalent(size_t entsize,Elf_Data * data1,Elf_Data * data2)834 hash_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