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