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