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