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