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