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