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 (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 <system.h> 45 #include "../libelf/elf-knowledge.h" 46 #include "../libebl/libeblP.h" 47 48 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); 53 54 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; 58 59 /* Bug report address. */ 60 ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT; 61 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 66 67 /* Definitions of arguments for argp functions. */ 68 static const struct argp_option options[] = 69 { 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 }, 79 80 { NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 }, 81 { NULL, 0, NULL, 0, NULL, 0 } 82 }; 83 84 /* Short description of program. */ 85 static const char doc[] = N_("\ 86 Compare relevant parts of two ELF files for equality."); 87 88 /* Strings for arguments in help texts. */ 89 static const char args_doc[] = N_("FILE1 FILE2"); 90 91 /* Prototype for option handler. */ 92 static error_t parse_opt (int key, char *arg, struct argp_state *state); 93 94 /* Data structure to communicate with argp functions. */ 95 static struct argp argp = 96 { 97 options, parse_opt, args_doc, doc, NULL, NULL, NULL 98 }; 99 100 101 /* How to treat gaps in loadable segments. */ 102 static enum 103 { 104 gaps_ignore = 0, 105 gaps_match 106 } 107 gaps; 108 109 /* Structure to hold information about used regions. */ 110 struct region 111 { 112 GElf_Addr from; 113 GElf_Addr to; 114 struct region *next; 115 }; 116 117 /* Nonzero if only exit status is wanted. */ 118 static bool quiet; 119 120 /* True iff multiple differences should be output. */ 121 static bool verbose; 122 123 /* True iff SHT_HASH treatment should be generous. */ 124 static bool hash_inexact; 125 126 /* True iff build ID notes should be ignored. */ 127 static bool ignore_build_id; 128 129 static bool hash_content_equivalent (size_t entsize, Elf_Data *, Elf_Data *); 130 131 132 int 133 main (int argc, char *argv[]) 134 { 135 /* Set locale. */ 136 (void) setlocale (LC_ALL, ""); 137 138 /* Make sure the message catalog can be found. */ 139 (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR); 140 141 /* Initialize the message catalog. */ 142 (void) textdomain (PACKAGE_TARNAME); 143 144 /* Parse and process arguments. */ 145 int remaining; 146 (void) argp_parse (&argp, argc, argv, 0, &remaining, NULL); 147 148 /* We expect exactly two non-option parameters. */ 149 if (unlikely (remaining + 2 != argc)) 150 { 151 fputs (gettext ("Invalid number of parameters.\n"), stderr); 152 argp_help (&argp, stderr, ARGP_HELP_SEE, program_invocation_short_name); 153 exit (1); 154 } 155 156 if (quiet) 157 verbose = false; 158 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 162 handled special. 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. 165 */ 166 int result = 0; 167 elf_version (EV_CURRENT); 168 169 const char *const fname1 = argv[remaining]; 170 int fd1; 171 Ebl *ebl1; 172 Elf *elf1 = open_file (fname1, &fd1, &ebl1); 173 174 const char *const fname2 = argv[remaining + 1]; 175 int fd2; 176 Ebl *ebl2; 177 Elf *elf2 = open_file (fname2, &fd2, &ebl2); 178 179 GElf_Ehdr ehdr1_mem; 180 GElf_Ehdr *ehdr1 = gelf_getehdr (elf1, &ehdr1_mem); 181 if (ehdr1 == NULL) 182 error (2, 0, gettext ("cannot get ELF header of '%s': %s"), 183 fname1, elf_errmsg (-1)); 184 GElf_Ehdr ehdr2_mem; 185 GElf_Ehdr *ehdr2 = gelf_getehdr (elf2, &ehdr2_mem); 186 if (ehdr2 == NULL) 187 error (2, 0, gettext ("cannot get ELF header of '%s': %s"), 188 fname2, elf_errmsg (-1)); 189 190 #define DIFFERENCE \ 191 do \ 192 { \ 193 result = 1; \ 194 if (! verbose) \ 195 goto out; \ 196 } \ 197 while (0) 198 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)) 211 { 212 if (! quiet) 213 error (0, 0, gettext ("%s %s diff: ELF header"), fname1, fname2); 214 DIFFERENCE; 215 } 216 217 size_t shnum1; 218 size_t shnum2; 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)) 226 { 227 if (! quiet) 228 error (0, 0, gettext ("%s %s diff: section count"), fname1, fname2); 229 DIFFERENCE; 230 } 231 232 size_t phnum1; 233 size_t phnum2; 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)) 241 { 242 if (! quiet) 243 error (0, 0, gettext ("%s %s diff: program header count"), 244 fname1, fname2); 245 DIFFERENCE; 246 } 247 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; 253 size_t nregions = 0; 254 while (1) 255 { 256 GElf_Shdr shdr1_mem; 257 GElf_Shdr *shdr1; 258 const char *sname1 = NULL; 259 do 260 { 261 scn1 = elf_nextscn (elf1, scn1); 262 shdr1 = gelf_getshdr (scn1, &shdr1_mem); 263 if (shdr1 != NULL) 264 sname1 = elf_strptr (elf1, ehdr1->e_shstrndx, shdr1->sh_name); 265 } 266 while (scn1 != NULL 267 && ebl_section_strip_p (ebl1, ehdr1, shdr1, sname1, true, false)); 268 269 GElf_Shdr shdr2_mem; 270 GElf_Shdr *shdr2; 271 const char *sname2 = NULL; 272 do 273 { 274 scn2 = elf_nextscn (elf2, scn2); 275 shdr2 = gelf_getshdr (scn2, &shdr2_mem); 276 if (shdr2 != NULL) 277 sname2 = elf_strptr (elf2, ehdr2->e_shstrndx, shdr2->sh_name); 278 } 279 while (scn2 != NULL 280 && ebl_section_strip_p (ebl2, ehdr2, shdr2, sname2, true, false)); 281 282 if (scn1 == NULL || scn2 == NULL) 283 break; 284 285 if (gaps != gaps_ignore && (shdr1->sh_flags & SHF_ALLOC) != 0) 286 { 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; 291 regions = newp; 292 293 ++nregions; 294 } 295 296 /* Compare the headers. We allow the name to be at a different 297 location. */ 298 if (unlikely (strcmp (sname1, sname2) != 0)) 299 { 300 error (0, 0, gettext ("%s %s differ: section [%zu], [%zu] name"), 301 fname1, fname2, elf_ndxscn (scn1), elf_ndxscn (scn2)); 302 DIFFERENCE; 303 } 304 305 /* We ignore certain sections. */ 306 if (strcmp (sname1, ".gnu_debuglink") == 0 307 || strcmp (sname1, ".gnu.prelink_undo") == 0) 308 continue; 309 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) 322 { 323 error (0, 0, gettext ("%s %s differ: section [%zu] '%s' header"), 324 fname1, fname2, elf_ndxscn (scn1), sname1); 325 DIFFERENCE; 326 } 327 328 Elf_Data *data1 = elf_getdata (scn1, NULL); 329 if (data1 == NULL) 330 error (2, 0, 331 gettext ("cannot get content of section %zu in '%s': %s"), 332 elf_ndxscn (scn1), fname1, elf_errmsg (-1)); 333 334 Elf_Data *data2 = elf_getdata (scn2, NULL); 335 if (data2 == NULL) 336 error (2, 0, 337 gettext ("cannot get content of section %zu in '%s': %s"), 338 elf_ndxscn (scn2), fname2, elf_errmsg (-1)); 339 340 switch (shdr1->sh_type) 341 { 342 case SHT_DYNSYM: 343 case SHT_SYMTAB: 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); 347 ++ndx) 348 { 349 GElf_Sym sym1_mem; 350 GElf_Sym *sym1 = gelf_getsym (data1, ndx, &sym1_mem); 351 if (sym1 == NULL) 352 error (2, 0, 353 gettext ("cannot get symbol in '%s': %s"), 354 fname1, elf_errmsg (-1)); 355 GElf_Sym sym2_mem; 356 GElf_Sym *sym2 = gelf_getsym (data2, ndx, &sym2_mem); 357 if (sym2 == NULL) 358 error (2, 0, 359 gettext ("cannot get symbol in '%s': %s"), 360 fname2, elf_errmsg (-1)); 361 362 const char *name1 = elf_strptr (elf1, shdr1->sh_link, 363 sym1->st_name); 364 const char *name2 = elf_strptr (elf2, shdr2->sh_link, 365 sym2->st_name); 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)) 373 { 374 // XXX Do we want to allow reordered symbol tables? 375 symtab_mismatch: 376 if (! quiet) 377 { 378 if (elf_ndxscn (scn1) == elf_ndxscn (scn2)) 379 error (0, 0, 380 gettext ("%s %s differ: symbol table [%zu]"), 381 fname1, fname2, elf_ndxscn (scn1)); 382 else 383 error (0, 0, gettext ("\ 384 %s %s differ: symbol table [%zu,%zu]"), 385 fname1, fname2, elf_ndxscn (scn1), 386 elf_ndxscn (scn2)); 387 } 388 DIFFERENCE; 389 break; 390 } 391 392 if (sym1->st_shndx == SHN_UNDEF 393 && sym1->st_size != sym2->st_size) 394 { 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 402 symbol. */ 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; 406 } 407 } 408 break; 409 410 case SHT_NOTE: 411 /* Parse the note format and compare the notes themselves. */ 412 { 413 GElf_Nhdr note1; 414 GElf_Nhdr note2; 415 416 size_t off1 = 0; 417 size_t off2 = 0; 418 size_t name_offset; 419 size_t desc_offset; 420 while (off1 < data1->d_size 421 && (off1 = gelf_getnote (data1, off1, ¬e1, 422 &name_offset, &desc_offset)) > 0) 423 { 424 const char *name1 = data1->d_buf + name_offset; 425 const void *desc1 = data1->d_buf + desc_offset; 426 if (off2 >= data2->d_size) 427 { 428 if (! quiet) 429 error (0, 0, gettext ("\ 430 %s %s differ: section [%zu] '%s' number of notes"), 431 fname1, fname2, elf_ndxscn (scn1), sname1); 432 DIFFERENCE; 433 } 434 off2 = gelf_getnote (data2, off2, ¬e2, 435 &name_offset, &desc_offset); 436 if (off2 == 0) 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; 442 443 if (note1.n_namesz != note2.n_namesz 444 || memcmp (name1, name2, note1.n_namesz)) 445 { 446 if (! quiet) 447 error (0, 0, gettext ("\ 448 %s %s differ: section [%zu] '%s' note name"), 449 fname1, fname2, elf_ndxscn (scn1), sname1); 450 DIFFERENCE; 451 } 452 if (note1.n_type != note2.n_type) 453 { 454 if (! quiet) 455 error (0, 0, gettext ("\ 456 %s %s differ: section [%zu] '%s' note '%s' type"), 457 fname1, fname2, elf_ndxscn (scn1), sname1, name1); 458 DIFFERENCE; 459 } 460 if (note1.n_descsz != note2.n_descsz 461 || memcmp (desc1, desc2, note1.n_descsz)) 462 { 463 if (note1.n_type == NT_GNU_BUILD_ID 464 && note1.n_namesz == sizeof "GNU" 465 && !memcmp (name1, "GNU", sizeof "GNU")) 466 { 467 if (note1.n_descsz != note2.n_descsz) 468 { 469 if (! quiet) 470 error (0, 0, gettext ("\ 471 %s %s differ: build ID length"), 472 fname1, fname2); 473 DIFFERENCE; 474 } 475 else if (! ignore_build_id) 476 { 477 if (! quiet) 478 error (0, 0, gettext ("\ 479 %s %s differ: build ID content"), 480 fname1, fname2); 481 DIFFERENCE; 482 } 483 } 484 else 485 { 486 if (! quiet) 487 error (0, 0, gettext ("\ 488 %s %s differ: section [%zu] '%s' note '%s' content"), 489 fname1, fname2, elf_ndxscn (scn1), sname1, 490 name1); 491 DIFFERENCE; 492 } 493 } 494 } 495 if (off2 < data2->d_size) 496 { 497 if (! quiet) 498 error (0, 0, gettext ("\ 499 %s %s differ: section [%zu] '%s' number of notes"), 500 fname1, fname2, elf_ndxscn (scn1), sname1); 501 DIFFERENCE; 502 } 503 } 504 break; 505 506 default: 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)); 512 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))) 517 { 518 if (hash_inexact 519 && shdr1->sh_type == SHT_HASH 520 && data1->d_size == data2->d_size 521 && hash_content_equivalent (shdr1->sh_entsize, data1, data2)) 522 break; 523 524 if (! quiet) 525 { 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); 530 else 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); 535 } 536 DIFFERENCE; 537 } 538 break; 539 } 540 } 541 542 if (unlikely (scn1 != scn2)) 543 { 544 if (! quiet) 545 error (0, 0, 546 gettext ("%s %s differ: unequal amount of important sections"), 547 fname1, fname2); 548 DIFFERENCE; 549 } 550 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) 556 { 557 ehdr_region.from = 0; 558 ehdr_region.to = ehdr1->e_ehsize; 559 ehdr_region.next = &phdr_region; 560 561 phdr_region.from = ehdr1->e_phoff; 562 phdr_region.to = ehdr1->e_phoff + phnum1 * ehdr1->e_phentsize; 563 phdr_region.next = regions; 564 565 regions = &ehdr_region; 566 nregions += 2; 567 } 568 569 /* If we need to look at the gaps we need access to the file data. */ 570 char *raw1 = NULL; 571 size_t size1 = 0; 572 char *raw2 = NULL; 573 size_t size2 = 0; 574 struct region *regionsarr = alloca (nregions * sizeof (struct region)); 575 if (gaps != gaps_ignore) 576 { 577 raw1 = elf_rawfile (elf1, &size1); 578 if (raw1 == NULL ) 579 error (2, 0, gettext ("cannot load data of '%s': %s"), 580 fname1, elf_errmsg (-1)); 581 582 raw2 = elf_rawfile (elf2, &size2); 583 if (raw2 == NULL ) 584 error (2, 0, gettext ("cannot load data of '%s': %s"), 585 fname2, elf_errmsg (-1)); 586 587 for (size_t cnt = 0; cnt < nregions; ++cnt) 588 { 589 regionsarr[cnt] = *regions; 590 regions = regions->next; 591 } 592 593 qsort (regionsarr, nregions, sizeof (regionsarr[0]), regioncompare); 594 } 595 596 /* Compare the program header tables. */ 597 for (unsigned int ndx = 0; ndx < phnum1; ++ndx) 598 { 599 GElf_Phdr phdr1_mem; 600 GElf_Phdr *phdr1 = gelf_getphdr (elf1, ndx, &phdr1_mem); 601 if (ehdr1 == NULL) 602 error (2, 0, 603 gettext ("cannot get program header entry %d of '%s': %s"), 604 ndx, fname1, elf_errmsg (-1)); 605 GElf_Phdr phdr2_mem; 606 GElf_Phdr *phdr2 = gelf_getphdr (elf2, ndx, &phdr2_mem); 607 if (ehdr2 == NULL) 608 error (2, 0, 609 gettext ("cannot get program header entry %d of '%s': %s"), 610 ndx, fname2, elf_errmsg (-1)); 611 612 if (unlikely (memcmp (phdr1, phdr2, sizeof (GElf_Phdr)) != 0)) 613 { 614 if (! quiet) 615 error (0, 0, gettext ("%s %s differ: program header %d"), 616 fname1, fname2, ndx); 617 DIFFERENCE; 618 } 619 620 if (gaps != gaps_ignore && phdr1->p_type == PT_LOAD) 621 { 622 size_t cnt = 0; 623 while (cnt < nregions && regionsarr[cnt].to < phdr1->p_offset) 624 ++cnt; 625 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) 629 { 630 if (last < regionsarr[cnt].from) 631 { 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)) 636 { 637 gapmismatch: 638 if (!quiet) 639 error (0, 0, gettext ("%s %s differ: gap"), 640 fname1, fname2); 641 DIFFERENCE; 642 break; 643 } 644 645 } 646 last = regionsarr[cnt].to; 647 ++cnt; 648 } 649 650 if (cnt == nregions && last < end) 651 goto gapmismatch; 652 } 653 } 654 655 out: 656 elf_end (elf1); 657 elf_end (elf2); 658 close (fd1); 659 close (fd2); 660 661 return result; 662 } 663 664 665 /* Print the version information. */ 666 static void 667 print_version (FILE *stream, struct argp_state *state __attribute__ ((unused))) 668 { 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\ 674 "), "2012"); 675 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper"); 676 } 677 678 679 /* Handle program arguments. */ 680 static error_t 681 parse_opt (int key, char *arg, 682 struct argp_state *state __attribute__ ((unused))) 683 { 684 switch (key) 685 { 686 case 'q': 687 quiet = true; 688 break; 689 690 case 'l': 691 verbose = true; 692 break; 693 694 case OPT_GAPS: 695 if (strcasecmp (arg, "ignore") == 0) 696 gaps = gaps_ignore; 697 else if (likely (strcasecmp (arg, "match") == 0)) 698 gaps = gaps_match; 699 else 700 { 701 fprintf (stderr, 702 gettext ("Invalid value '%s' for --gaps parameter."), 703 arg); 704 argp_help (&argp, stderr, ARGP_HELP_SEE, 705 program_invocation_short_name); 706 exit (1); 707 } 708 break; 709 710 case OPT_HASH_INEXACT: 711 hash_inexact = true; 712 break; 713 714 case OPT_IGNORE_BUILD_ID: 715 ignore_build_id = true; 716 break; 717 718 default: 719 return ARGP_ERR_UNKNOWN; 720 } 721 return 0; 722 } 723 724 725 static Elf * 726 open_file (const char *fname, int *fdp, Ebl **eblp) 727 { 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); 732 if (elf == NULL) 733 error (2, 0, 734 gettext ("cannot create ELF descriptor for '%s': %s"), 735 fname, elf_errmsg (-1)); 736 Ebl *ebl = ebl_openbackend (elf); 737 if (ebl == NULL) 738 error (2, 0, 739 gettext ("cannot create EBL descriptor for '%s'"), fname); 740 741 *fdp = fd; 742 *eblp = ebl; 743 return elf; 744 } 745 746 747 static bool 748 search_for_copy_reloc (Ebl *ebl, size_t scnndx, int symndx) 749 { 750 Elf_Scn *scn = NULL; 751 while ((scn = elf_nextscn (ebl->elf, scn)) != NULL) 752 { 753 GElf_Shdr shdr_mem; 754 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); 755 if (shdr == NULL) 756 error (2, 0, 757 gettext ("cannot get section header of section %zu: %s"), 758 elf_ndxscn (scn), elf_errmsg (-1)); 759 760 if ((shdr->sh_type != SHT_REL && shdr->sh_type != SHT_RELA) 761 || shdr->sh_link != scnndx) 762 continue; 763 764 Elf_Data *data = elf_getdata (scn, NULL); 765 if (data == NULL) 766 error (2, 0, 767 gettext ("cannot get content of section %zu: %s"), 768 elf_ndxscn (scn), elf_errmsg (-1)); 769 770 if (shdr->sh_type == SHT_REL) 771 for (int ndx = 0; ndx < (int) (shdr->sh_size / shdr->sh_entsize); 772 ++ndx) 773 { 774 GElf_Rel rel_mem; 775 GElf_Rel *rel = gelf_getrel (data, ndx, &rel_mem); 776 if (rel == NULL) 777 error (2, 0, gettext ("cannot get relocation: %s"), 778 elf_errmsg (-1)); 779 780 if ((int) GELF_R_SYM (rel->r_info) == symndx 781 && ebl_copy_reloc_p (ebl, GELF_R_TYPE (rel->r_info))) 782 return true; 783 } 784 else 785 for (int ndx = 0; ndx < (int) (shdr->sh_size / shdr->sh_entsize); 786 ++ndx) 787 { 788 GElf_Rela rela_mem; 789 GElf_Rela *rela = gelf_getrela (data, ndx, &rela_mem); 790 if (rela == NULL) 791 error (2, 0, gettext ("cannot get relocation: %s"), 792 elf_errmsg (-1)); 793 794 if ((int) GELF_R_SYM (rela->r_info) == symndx 795 && ebl_copy_reloc_p (ebl, GELF_R_TYPE (rela->r_info))) 796 return true; 797 } 798 } 799 800 return false; 801 } 802 803 804 static int 805 regioncompare (const void *p1, const void *p2) 806 { 807 const struct region *r1 = (const struct region *) p1; 808 const struct region *r2 = (const struct region *) p2; 809 810 if (r1->from < r2->from) 811 return -1; 812 return 1; 813 } 814 815 816 static int 817 compare_Elf32_Word (const void *p1, const void *p2) 818 { 819 const Elf32_Word *w1 = p1; 820 const Elf32_Word *w2 = p2; 821 assert (sizeof (int) >= sizeof (*w1)); 822 return (int) *w1 - (int) *w2; 823 } 824 825 static int 826 compare_Elf64_Xword (const void *p1, const void *p2) 827 { 828 const Elf64_Xword *w1 = p1; 829 const Elf64_Xword *w2 = p2; 830 return *w1 < *w2 ? -1 : *w1 > *w2 ? 1 : 0; 831 } 832 833 static bool 834 hash_content_equivalent (size_t entsize, Elf_Data *data1, Elf_Data *data2) 835 { 836 #define CHECK_HASH(Hash_Word) \ 837 { \ 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) \ 844 return false; \ 845 \ 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]; \ 850 \ 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) \ 856 { \ 857 if (bucket1[i] >= nchain || bucket2[i] >= nchain) \ 858 return false; \ 859 \ 860 size_t b1 = 0; \ 861 for (size_t p = bucket1[i]; p != STN_UNDEF; p = chain1[p]) \ 862 if (p >= nchain || b1 >= nchain - 1) \ 863 return false; \ 864 else \ 865 temp1[b1++] = p; \ 866 \ 867 size_t b2 = 0; \ 868 for (size_t p = bucket2[i]; p != STN_UNDEF; p = chain2[p]) \ 869 if (p >= nchain || b2 >= nchain - 1) \ 870 return false; \ 871 else \ 872 temp2[b2++] = p; \ 873 \ 874 if (b1 != b2) \ 875 return false; \ 876 \ 877 qsort (temp1, b1, sizeof temp1[0], compare_##Hash_Word); \ 878 qsort (temp2, b2, sizeof temp2[0], compare_##Hash_Word); \ 879 \ 880 for (b1 = 0; b1 < b2; ++b1) \ 881 if (temp1[b1] != temp2[b1]) \ 882 return false; \ 883 else \ 884 chain_ok[temp1[b1]] = true; \ 885 } \ 886 \ 887 for (size_t i = 0; i < nchain; ++i) \ 888 if (!chain_ok[i] && chain1[i] != chain2[i]) \ 889 return false; \ 890 \ 891 return true; \ 892 } 893 894 switch (entsize) 895 { 896 case 4: 897 CHECK_HASH (Elf32_Word); 898 break; 899 case 8: 900 CHECK_HASH (Elf64_Xword); 901 break; 902 } 903 904 return false; 905 } 906 907 908 #include "debugpred.h" 909