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