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. */
40 static Elf *open_file (const char *fname, int *fdp, Ebl **eblp);
41 static bool search_for_copy_reloc (Ebl *ebl, size_t scnndx, int symndx);
42 static int regioncompare (const void *p1, const void *p2);
43
44
45 /* Name and version of program. */
46 ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
47
48 /* Bug report address. */
49 ARGP_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. */
57 static 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. */
74 static const char doc[] = N_("\
75 Compare relevant parts of two ELF files for equality.");
76
77 /* Strings for arguments in help texts. */
78 static const char args_doc[] = N_("FILE1 FILE2");
79
80 /* Prototype for option handler. */
81 static error_t parse_opt (int key, char *arg, struct argp_state *state);
82
83 /* Data structure to communicate with argp functions. */
84 static 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. */
91 static enum
92 {
93 gaps_ignore = 0,
94 gaps_match
95 }
96 gaps;
97
98 /* Structure to hold information about used regions. */
99 struct region
100 {
101 GElf_Addr from;
102 GElf_Addr to;
103 struct region *next;
104 };
105
106 /* Nonzero if only exit status is wanted. */
107 static bool quiet;
108
109 /* True iff multiple differences should be output. */
110 static bool verbose;
111
112 /* True iff SHT_HASH treatment should be generous. */
113 static bool hash_inexact;
114
115 /* True iff build ID notes should be ignored. */
116 static bool ignore_build_id;
117
118 static bool hash_content_equivalent (size_t entsize, Elf_Data *, Elf_Data *);
119
120
121 int
main(int argc,char * argv[])122 main (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, _("\
451 cannot 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. */
683 static error_t
parse_opt(int key,char * arg,struct argp_state * state)684 parse_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
728 static Elf *
open_file(const char * fname,int * fdp,Ebl ** eblp)729 open_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
750 static bool
search_for_copy_reloc(Ebl * ebl,size_t scnndx,int symndx)751 search_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
807 static int
regioncompare(const void * p1,const void * p2)808 regioncompare (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
819 static int
compare_Elf32_Word(const void * p1,const void * p2)820 compare_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
827 static int
compare_Elf64_Xword(const void * p1,const void * p2)828 compare_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
835 static bool
hash_content_equivalent(size_t entsize,Elf_Data * data1,Elf_Data * data2)836 hash_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