LCOV - code coverage report
Current view: top level - src - elfcmp.c (source / functions) Hit Total Coverage
Test: lcov.out Lines: 144 334 43.1 %
Date: 2017-08-02 15:24:56 Functions: 5 8 62.5 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.12