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