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