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 <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
main(int argc,char * argv[])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
parse_opt(int key,char * arg,struct argp_state * state)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 *
open_file(const char * fname,int * fdp,Ebl ** eblp)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
search_for_copy_reloc(Ebl * ebl,size_t scnndx,int symndx)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
regioncompare(const void * p1,const void * p2)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
compare_Elf32_Word(const void * p1,const void * p2)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
compare_Elf64_Xword(const void * p1,const void * p2)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
hash_content_equivalent(size_t entsize,Elf_Data * data1,Elf_Data * data2)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