1 /* Compare relevant content of two ELF files.
2 Copyright (C) 2005-2012 Red Hat, Inc.
3 This file is part of Red Hat elfutils.
4 Written by Ulrich Drepper <drepper@redhat.com>, 2005.
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.
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.
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.
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>. */
45 #include "../libelf/elf-knowledge.h"
46 #include "../libebl/libeblP.h"
49 /* Prototypes of local functions. */
50 static Elf *open_file (const char *fname, int *fdp, Ebl **eblp);
51 static bool search_for_copy_reloc (Ebl *ebl, size_t scnndx, int symndx);
52 static int regioncompare (const void *p1, const void *p2);
55 /* Name and version of program. */
56 static void print_version (FILE *stream, struct argp_state *state);
57 ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
59 /* Bug report address. */
60 ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
62 /* Values for the parameters which have no short form. */
63 #define OPT_GAPS 0x100
64 #define OPT_HASH_INEXACT 0x101
65 #define OPT_IGNORE_BUILD_ID 0x102
67 /* Definitions of arguments for argp functions. */
68 static const struct argp_option options[] =
70 { NULL, 0, NULL, 0, N_("Control options:"), 0 },
71 { "verbose", 'l', NULL, 0,
72 N_("Output all differences, not just the first"), 0 },
73 { "gaps", OPT_GAPS, "ACTION", 0, N_("Control treatment of gaps in loadable segments [ignore|match] (default: ignore)"), 0 },
74 { "hash-inexact", OPT_HASH_INEXACT, NULL, 0,
75 N_("Ignore permutation of buckets in SHT_HASH section"), 0 },
76 { "ignore-build-id", OPT_IGNORE_BUILD_ID, NULL, 0,
77 N_("Ignore differences in build ID"), 0 },
78 { "quiet", 'q', NULL, 0, N_("Output nothing; yield exit status only"), 0 },
80 { NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 },
81 { NULL, 0, NULL, 0, NULL, 0 }
84 /* Short description of program. */
85 static const char doc[] = N_("\
86 Compare relevant parts of two ELF files for equality.");
88 /* Strings for arguments in help texts. */
89 static const char args_doc[] = N_("FILE1 FILE2");
91 /* Prototype for option handler. */
92 static error_t parse_opt (int key, char *arg, struct argp_state *state);
94 /* Data structure to communicate with argp functions. */
95 static struct argp argp =
97 options, parse_opt, args_doc, doc, NULL, NULL, NULL
101 /* How to treat gaps in loadable segments. */
109 /* Structure to hold information about used regions. */
117 /* Nonzero if only exit status is wanted. */
120 /* True iff multiple differences should be output. */
123 /* True iff SHT_HASH treatment should be generous. */
124 static bool hash_inexact;
126 /* True iff build ID notes should be ignored. */
127 static bool ignore_build_id;
129 static bool hash_content_equivalent (size_t entsize, Elf_Data *, Elf_Data *);
133 main (int argc, char *argv[])
136 (void) setlocale (LC_ALL, "");
138 /* Make sure the message catalog can be found. */
139 (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
141 /* Initialize the message catalog. */
142 (void) textdomain (PACKAGE_TARNAME);
144 /* Parse and process arguments. */
146 (void) argp_parse (&argp, argc, argv, 0, &remaining, NULL);
148 /* We expect exactly two non-option parameters. */
149 if (unlikely (remaining + 2 != argc))
151 fputs (gettext ("Invalid number of parameters.\n"), stderr);
152 argp_help (&argp, stderr, ARGP_HELP_SEE, program_invocation_short_name);
159 /* Comparing the files is done in two phases:
160 1. compare all sections. Sections which are irrelevant (i.e., if
161 strip would remove them) are ignored. Some section types are
163 2. all parts of the loadable segments which are not parts of any
164 section is compared according to the rules of the --gaps option.
167 elf_version (EV_CURRENT);
169 const char *const fname1 = argv[remaining];
172 Elf *elf1 = open_file (fname1, &fd1, &ebl1);
174 const char *const fname2 = argv[remaining + 1];
177 Elf *elf2 = open_file (fname2, &fd2, &ebl2);
180 GElf_Ehdr *ehdr1 = gelf_getehdr (elf1, &ehdr1_mem);
182 error (2, 0, gettext ("cannot get ELF header of '%s': %s"),
183 fname1, elf_errmsg (-1));
185 GElf_Ehdr *ehdr2 = gelf_getehdr (elf2, &ehdr2_mem);
187 error (2, 0, gettext ("cannot get ELF header of '%s': %s"),
188 fname2, elf_errmsg (-1));
199 /* Compare the ELF headers. */
200 if (unlikely (memcmp (ehdr1->e_ident, ehdr2->e_ident, EI_NIDENT) != 0
201 || ehdr1->e_type != ehdr2->e_type
202 || ehdr1->e_machine != ehdr2->e_machine
203 || ehdr1->e_version != ehdr2->e_version
204 || ehdr1->e_entry != ehdr2->e_entry
205 || ehdr1->e_phoff != ehdr2->e_phoff
206 || ehdr1->e_flags != ehdr2->e_flags
207 || ehdr1->e_ehsize != ehdr2->e_ehsize
208 || ehdr1->e_phentsize != ehdr2->e_phentsize
209 || ehdr1->e_phnum != ehdr2->e_phnum
210 || ehdr1->e_shentsize != ehdr2->e_shentsize))
213 error (0, 0, gettext ("%s %s diff: ELF header"), fname1, fname2);
219 if (unlikely (elf_getshdrnum (elf1, &shnum1) != 0))
220 error (2, 0, gettext ("cannot get section count of '%s': %s"),
221 fname1, elf_errmsg (-1));
222 if (unlikely (elf_getshdrnum (elf2, &shnum2) != 0))
223 error (2, 0, gettext ("cannot get section count of '%s': %s"),
224 fname2, elf_errmsg (-1));
225 if (unlikely (shnum1 != shnum2))
228 error (0, 0, gettext ("%s %s diff: section count"), fname1, fname2);
234 if (unlikely (elf_getphdrnum (elf1, &phnum1) != 0))
235 error (2, 0, gettext ("cannot get program header count of '%s': %s"),
236 fname1, elf_errmsg (-1));
237 if (unlikely (elf_getphdrnum (elf2, &phnum2) != 0))
238 error (2, 0, gettext ("cannot get program header count of '%s': %s"),
239 fname2, elf_errmsg (-1));
240 if (unlikely (phnum1 != phnum2))
243 error (0, 0, gettext ("%s %s diff: program header count"),
248 /* Iterate over all sections. We expect the sections in the two
249 files to match exactly. */
250 Elf_Scn *scn1 = NULL;
251 Elf_Scn *scn2 = NULL;
252 struct region *regions = NULL;
258 const char *sname1 = NULL;
261 scn1 = elf_nextscn (elf1, scn1);
262 shdr1 = gelf_getshdr (scn1, &shdr1_mem);
264 sname1 = elf_strptr (elf1, ehdr1->e_shstrndx, shdr1->sh_name);
267 && ebl_section_strip_p (ebl1, ehdr1, shdr1, sname1, true, false));
271 const char *sname2 = NULL;
274 scn2 = elf_nextscn (elf2, scn2);
275 shdr2 = gelf_getshdr (scn2, &shdr2_mem);
277 sname2 = elf_strptr (elf2, ehdr2->e_shstrndx, shdr2->sh_name);
280 && ebl_section_strip_p (ebl2, ehdr2, shdr2, sname2, true, false));
282 if (scn1 == NULL || scn2 == NULL)
285 if (gaps != gaps_ignore && (shdr1->sh_flags & SHF_ALLOC) != 0)
287 struct region *newp = (struct region *) alloca (sizeof (*newp));
288 newp->from = shdr1->sh_offset;
289 newp->to = shdr1->sh_offset + shdr1->sh_size;
290 newp->next = regions;
296 /* Compare the headers. We allow the name to be at a different
298 if (unlikely (strcmp (sname1, sname2) != 0))
300 error (0, 0, gettext ("%s %s differ: section [%zu], [%zu] name"),
301 fname1, fname2, elf_ndxscn (scn1), elf_ndxscn (scn2));
305 /* We ignore certain sections. */
306 if (strcmp (sname1, ".gnu_debuglink") == 0
307 || strcmp (sname1, ".gnu.prelink_undo") == 0)
310 if (shdr1->sh_type != shdr2->sh_type
311 // XXX Any flags which should be ignored?
312 || shdr1->sh_flags != shdr2->sh_flags
313 || shdr1->sh_addr != shdr2->sh_addr
314 || (shdr1->sh_offset != shdr2->sh_offset
315 && (shdr1->sh_flags & SHF_ALLOC)
316 && ehdr1->e_type != ET_REL)
317 || shdr1->sh_size != shdr2->sh_size
318 || shdr1->sh_link != shdr2->sh_link
319 || shdr1->sh_info != shdr2->sh_info
320 || shdr1->sh_addralign != shdr2->sh_addralign
321 || shdr1->sh_entsize != shdr2->sh_entsize)
323 error (0, 0, gettext ("%s %s differ: section [%zu] '%s' header"),
324 fname1, fname2, elf_ndxscn (scn1), sname1);
328 Elf_Data *data1 = elf_getdata (scn1, NULL);
331 gettext ("cannot get content of section %zu in '%s': %s"),
332 elf_ndxscn (scn1), fname1, elf_errmsg (-1));
334 Elf_Data *data2 = elf_getdata (scn2, NULL);
337 gettext ("cannot get content of section %zu in '%s': %s"),
338 elf_ndxscn (scn2), fname2, elf_errmsg (-1));
340 switch (shdr1->sh_type)
344 /* Iterate over the symbol table. We ignore the st_size
345 value of undefined symbols. */
346 for (int ndx = 0; ndx < (int) (shdr1->sh_size / shdr1->sh_entsize);
350 GElf_Sym *sym1 = gelf_getsym (data1, ndx, &sym1_mem);
353 gettext ("cannot get symbol in '%s': %s"),
354 fname1, elf_errmsg (-1));
356 GElf_Sym *sym2 = gelf_getsym (data2, ndx, &sym2_mem);
359 gettext ("cannot get symbol in '%s': %s"),
360 fname2, elf_errmsg (-1));
362 const char *name1 = elf_strptr (elf1, shdr1->sh_link,
364 const char *name2 = elf_strptr (elf2, shdr2->sh_link,
366 if (unlikely (strcmp (name1, name2) != 0
367 || sym1->st_value != sym2->st_value
368 || (sym1->st_size != sym2->st_size
369 && sym1->st_shndx != SHN_UNDEF)
370 || sym1->st_info != sym2->st_info
371 || sym1->st_other != sym2->st_other
372 || sym1->st_shndx != sym1->st_shndx))
374 // XXX Do we want to allow reordered symbol tables?
378 if (elf_ndxscn (scn1) == elf_ndxscn (scn2))
380 gettext ("%s %s differ: symbol table [%zu]"),
381 fname1, fname2, elf_ndxscn (scn1));
383 error (0, 0, gettext ("\
384 %s %s differ: symbol table [%zu,%zu]"),
385 fname1, fname2, elf_ndxscn (scn1),
392 if (sym1->st_shndx == SHN_UNDEF
393 && sym1->st_size != sym2->st_size)
395 /* The size of the symbol in the object defining it
396 might have changed. That is OK unless the symbol
397 is used in a copy relocation. Look over the
398 sections in both files and determine which
399 relocation section uses this symbol table
400 section. Then look through the relocations to
401 see whether any copy relocation references this
403 if (search_for_copy_reloc (ebl1, elf_ndxscn (scn1), ndx)
404 || search_for_copy_reloc (ebl2, elf_ndxscn (scn2), ndx))
405 goto symtab_mismatch;
411 /* Parse the note format and compare the notes themselves. */
420 while (off1 < data1->d_size
421 && (off1 = gelf_getnote (data1, off1, ¬e1,
422 &name_offset, &desc_offset)) > 0)
424 const char *name1 = data1->d_buf + name_offset;
425 const void *desc1 = data1->d_buf + desc_offset;
426 if (off2 >= data2->d_size)
429 error (0, 0, gettext ("\
430 %s %s differ: section [%zu] '%s' number of notes"),
431 fname1, fname2, elf_ndxscn (scn1), sname1);
434 off2 = gelf_getnote (data2, off2, ¬e2,
435 &name_offset, &desc_offset);
437 error (2, 0, gettext ("\
438 cannot read note section [%zu] '%s' in '%s': %s"),
439 elf_ndxscn (scn2), sname2, fname2, elf_errmsg (-1));
440 const char *name2 = data2->d_buf + name_offset;
441 const void *desc2 = data2->d_buf + desc_offset;
443 if (note1.n_namesz != note2.n_namesz
444 || memcmp (name1, name2, note1.n_namesz))
447 error (0, 0, gettext ("\
448 %s %s differ: section [%zu] '%s' note name"),
449 fname1, fname2, elf_ndxscn (scn1), sname1);
452 if (note1.n_type != note2.n_type)
455 error (0, 0, gettext ("\
456 %s %s differ: section [%zu] '%s' note '%s' type"),
457 fname1, fname2, elf_ndxscn (scn1), sname1, name1);
460 if (note1.n_descsz != note2.n_descsz
461 || memcmp (desc1, desc2, note1.n_descsz))
463 if (note1.n_type == NT_GNU_BUILD_ID
464 && note1.n_namesz == sizeof "GNU"
465 && !memcmp (name1, "GNU", sizeof "GNU"))
467 if (note1.n_descsz != note2.n_descsz)
470 error (0, 0, gettext ("\
471 %s %s differ: build ID length"),
475 else if (! ignore_build_id)
478 error (0, 0, gettext ("\
479 %s %s differ: build ID content"),
487 error (0, 0, gettext ("\
488 %s %s differ: section [%zu] '%s' note '%s' content"),
489 fname1, fname2, elf_ndxscn (scn1), sname1,
495 if (off2 < data2->d_size)
498 error (0, 0, gettext ("\
499 %s %s differ: section [%zu] '%s' number of notes"),
500 fname1, fname2, elf_ndxscn (scn1), sname1);
507 /* Compare the section content byte for byte. */
508 assert (shdr1->sh_type == SHT_NOBITS
509 || (data1->d_buf != NULL || data1->d_size == 0));
510 assert (shdr2->sh_type == SHT_NOBITS
511 || (data2->d_buf != NULL || data1->d_size == 0));
513 if (unlikely (data1->d_size != data2->d_size
514 || (shdr1->sh_type != SHT_NOBITS
515 && memcmp (data1->d_buf, data2->d_buf,
516 data1->d_size) != 0)))
519 && shdr1->sh_type == SHT_HASH
520 && data1->d_size == data2->d_size
521 && hash_content_equivalent (shdr1->sh_entsize, data1, data2))
526 if (elf_ndxscn (scn1) == elf_ndxscn (scn2))
527 error (0, 0, gettext ("\
528 %s %s differ: section [%zu] '%s' content"),
529 fname1, fname2, elf_ndxscn (scn1), sname1);
531 error (0, 0, gettext ("\
532 %s %s differ: section [%zu,%zu] '%s' content"),
533 fname1, fname2, elf_ndxscn (scn1),
534 elf_ndxscn (scn2), sname1);
542 if (unlikely (scn1 != scn2))
546 gettext ("%s %s differ: unequal amount of important sections"),
551 /* We we look at gaps, create artificial ones for the parts of the
552 program which we are not in sections. */
553 struct region ehdr_region;
554 struct region phdr_region;
555 if (gaps != gaps_ignore)
557 ehdr_region.from = 0;
558 ehdr_region.to = ehdr1->e_ehsize;
559 ehdr_region.next = &phdr_region;
561 phdr_region.from = ehdr1->e_phoff;
562 phdr_region.to = ehdr1->e_phoff + phnum1 * ehdr1->e_phentsize;
563 phdr_region.next = regions;
565 regions = &ehdr_region;
569 /* If we need to look at the gaps we need access to the file data. */
574 struct region *regionsarr = alloca (nregions * sizeof (struct region));
575 if (gaps != gaps_ignore)
577 raw1 = elf_rawfile (elf1, &size1);
579 error (2, 0, gettext ("cannot load data of '%s': %s"),
580 fname1, elf_errmsg (-1));
582 raw2 = elf_rawfile (elf2, &size2);
584 error (2, 0, gettext ("cannot load data of '%s': %s"),
585 fname2, elf_errmsg (-1));
587 for (size_t cnt = 0; cnt < nregions; ++cnt)
589 regionsarr[cnt] = *regions;
590 regions = regions->next;
593 qsort (regionsarr, nregions, sizeof (regionsarr[0]), regioncompare);
596 /* Compare the program header tables. */
597 for (unsigned int ndx = 0; ndx < phnum1; ++ndx)
600 GElf_Phdr *phdr1 = gelf_getphdr (elf1, ndx, &phdr1_mem);
603 gettext ("cannot get program header entry %d of '%s': %s"),
604 ndx, fname1, elf_errmsg (-1));
606 GElf_Phdr *phdr2 = gelf_getphdr (elf2, ndx, &phdr2_mem);
609 gettext ("cannot get program header entry %d of '%s': %s"),
610 ndx, fname2, elf_errmsg (-1));
612 if (unlikely (memcmp (phdr1, phdr2, sizeof (GElf_Phdr)) != 0))
615 error (0, 0, gettext ("%s %s differ: program header %d"),
616 fname1, fname2, ndx);
620 if (gaps != gaps_ignore && phdr1->p_type == PT_LOAD)
623 while (cnt < nregions && regionsarr[cnt].to < phdr1->p_offset)
626 GElf_Off last = phdr1->p_offset;
627 GElf_Off end = phdr1->p_offset + phdr1->p_filesz;
628 while (cnt < nregions && regionsarr[cnt].from < end)
630 if (last < regionsarr[cnt].from)
632 /* Compare the [LAST,FROM) region. */
633 assert (gaps == gaps_match);
634 if (unlikely (memcmp (raw1 + last, raw2 + last,
635 regionsarr[cnt].from - last) != 0))
639 error (0, 0, gettext ("%s %s differ: gap"),
646 last = regionsarr[cnt].to;
650 if (cnt == nregions && last < end)
665 /* Print the version information. */
667 print_version (FILE *stream, struct argp_state *state __attribute__ ((unused)))
669 fprintf (stream, "elfcmp (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION);
670 fprintf (stream, gettext ("\
671 Copyright (C) %s Red Hat, Inc.\n\
672 This is free software; see the source for copying conditions. There is NO\n\
673 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
675 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
679 /* Handle program arguments. */
681 parse_opt (int key, char *arg,
682 struct argp_state *state __attribute__ ((unused)))
695 if (strcasecmp (arg, "ignore") == 0)
697 else if (likely (strcasecmp (arg, "match") == 0))
702 gettext ("Invalid value '%s' for --gaps parameter."),
704 argp_help (&argp, stderr, ARGP_HELP_SEE,
705 program_invocation_short_name);
710 case OPT_HASH_INEXACT:
714 case OPT_IGNORE_BUILD_ID:
715 ignore_build_id = true;
719 return ARGP_ERR_UNKNOWN;
726 open_file (const char *fname, int *fdp, Ebl **eblp)
728 int fd = open (fname, O_RDONLY);
729 if (unlikely (fd == -1))
730 error (2, errno, gettext ("cannot open '%s'"), fname);
731 Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
734 gettext ("cannot create ELF descriptor for '%s': %s"),
735 fname, elf_errmsg (-1));
736 Ebl *ebl = ebl_openbackend (elf);
739 gettext ("cannot create EBL descriptor for '%s'"), fname);
748 search_for_copy_reloc (Ebl *ebl, size_t scnndx, int symndx)
751 while ((scn = elf_nextscn (ebl->elf, scn)) != NULL)
754 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
757 gettext ("cannot get section header of section %zu: %s"),
758 elf_ndxscn (scn), elf_errmsg (-1));
760 if ((shdr->sh_type != SHT_REL && shdr->sh_type != SHT_RELA)
761 || shdr->sh_link != scnndx)
764 Elf_Data *data = elf_getdata (scn, NULL);
767 gettext ("cannot get content of section %zu: %s"),
768 elf_ndxscn (scn), elf_errmsg (-1));
770 if (shdr->sh_type == SHT_REL)
771 for (int ndx = 0; ndx < (int) (shdr->sh_size / shdr->sh_entsize);
775 GElf_Rel *rel = gelf_getrel (data, ndx, &rel_mem);
777 error (2, 0, gettext ("cannot get relocation: %s"),
780 if ((int) GELF_R_SYM (rel->r_info) == symndx
781 && ebl_copy_reloc_p (ebl, GELF_R_TYPE (rel->r_info)))
785 for (int ndx = 0; ndx < (int) (shdr->sh_size / shdr->sh_entsize);
789 GElf_Rela *rela = gelf_getrela (data, ndx, &rela_mem);
791 error (2, 0, gettext ("cannot get relocation: %s"),
794 if ((int) GELF_R_SYM (rela->r_info) == symndx
795 && ebl_copy_reloc_p (ebl, GELF_R_TYPE (rela->r_info)))
805 regioncompare (const void *p1, const void *p2)
807 const struct region *r1 = (const struct region *) p1;
808 const struct region *r2 = (const struct region *) p2;
810 if (r1->from < r2->from)
817 compare_Elf32_Word (const void *p1, const void *p2)
819 const Elf32_Word *w1 = p1;
820 const Elf32_Word *w2 = p2;
821 assert (sizeof (int) >= sizeof (*w1));
822 return (int) *w1 - (int) *w2;
826 compare_Elf64_Xword (const void *p1, const void *p2)
828 const Elf64_Xword *w1 = p1;
829 const Elf64_Xword *w2 = p2;
830 return *w1 < *w2 ? -1 : *w1 > *w2 ? 1 : 0;
834 hash_content_equivalent (size_t entsize, Elf_Data *data1, Elf_Data *data2)
836 #define CHECK_HASH(Hash_Word) \
838 const Hash_Word *const hash1 = data1->d_buf; \
839 const Hash_Word *const hash2 = data2->d_buf; \
840 const size_t nbucket = hash1[0]; \
841 const size_t nchain = hash1[1]; \
842 if (data1->d_size != (2 + nbucket + nchain) * sizeof hash1[0] \
843 || hash2[0] != nbucket || hash2[1] != nchain) \
846 const Hash_Word *const bucket1 = &hash1[2]; \
847 const Hash_Word *const chain1 = &bucket1[nbucket]; \
848 const Hash_Word *const bucket2 = &hash2[2]; \
849 const Hash_Word *const chain2 = &bucket2[nbucket]; \
851 bool chain_ok[nchain]; \
852 Hash_Word temp1[nchain - 1]; \
853 Hash_Word temp2[nchain - 1]; \
854 memset (chain_ok, 0, sizeof chain_ok); \
855 for (size_t i = 0; i < nbucket; ++i) \
857 if (bucket1[i] >= nchain || bucket2[i] >= nchain) \
861 for (size_t p = bucket1[i]; p != STN_UNDEF; p = chain1[p]) \
862 if (p >= nchain || b1 >= nchain - 1) \
868 for (size_t p = bucket2[i]; p != STN_UNDEF; p = chain2[p]) \
869 if (p >= nchain || b2 >= nchain - 1) \
877 qsort (temp1, b1, sizeof temp1[0], compare_##Hash_Word); \
878 qsort (temp2, b2, sizeof temp2[0], compare_##Hash_Word); \
880 for (b1 = 0; b1 < b2; ++b1) \
881 if (temp1[b1] != temp2[b1]) \
884 chain_ok[temp1[b1]] = true; \
887 for (size_t i = 0; i < nchain; ++i) \
888 if (!chain_ok[i] && chain1[i] != chain2[i]) \
897 CHECK_HASH (Elf32_Word);
900 CHECK_HASH (Elf64_Xword);
908 #include "debugpred.h"