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 <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 (sname1 == NULL || sname2 == NULL
291 || strcmp (sname1, sname2) != 0))
292 {
293 error (0, 0, gettext ("%s %s differ: section [%zu], [%zu] name"),
294 fname1, fname2, elf_ndxscn (scn1), elf_ndxscn (scn2));
295 DIFFERENCE;
296 }
297
298 /* We ignore certain sections. */
299 if ((sname1 != NULL && strcmp (sname1, ".gnu_debuglink") == 0)
300 || (sname1 != NULL && strcmp (sname1, ".gnu.prelink_undo") == 0))
301 continue;
302
303 if (shdr1->sh_type != shdr2->sh_type
304 // XXX Any flags which should be ignored?
305 || shdr1->sh_flags != shdr2->sh_flags
306 || shdr1->sh_addr != shdr2->sh_addr
307 || (shdr1->sh_offset != shdr2->sh_offset
308 && (shdr1->sh_flags & SHF_ALLOC)
309 && ehdr1->e_type != ET_REL)
310 || shdr1->sh_size != shdr2->sh_size
311 || shdr1->sh_link != shdr2->sh_link
312 || shdr1->sh_info != shdr2->sh_info
313 || shdr1->sh_addralign != shdr2->sh_addralign
314 || shdr1->sh_entsize != shdr2->sh_entsize)
315 {
316 error (0, 0, gettext ("%s %s differ: section [%zu] '%s' header"),
317 fname1, fname2, elf_ndxscn (scn1), sname1);
318 DIFFERENCE;
319 }
320
321 Elf_Data *data1 = elf_getdata (scn1, NULL);
322 if (data1 == NULL)
323 error (2, 0,
324 gettext ("cannot get content of section %zu in '%s': %s"),
325 elf_ndxscn (scn1), fname1, elf_errmsg (-1));
326
327 Elf_Data *data2 = elf_getdata (scn2, NULL);
328 if (data2 == NULL)
329 error (2, 0,
330 gettext ("cannot get content of section %zu in '%s': %s"),
331 elf_ndxscn (scn2), fname2, elf_errmsg (-1));
332
333 switch (shdr1->sh_type)
334 {
335 case SHT_DYNSYM:
336 case SHT_SYMTAB:
337 if (shdr1->sh_entsize == 0)
338 error (2, 0,
339 gettext ("symbol table [%zu] in '%s' has zero sh_entsize"),
340 elf_ndxscn (scn1), fname1);
341
342 /* Iterate over the symbol table. We ignore the st_size
343 value of undefined symbols. */
344 for (int ndx = 0; ndx < (int) (shdr1->sh_size / shdr1->sh_entsize);
345 ++ndx)
346 {
347 GElf_Sym sym1_mem;
348 GElf_Sym *sym1 = gelf_getsym (data1, ndx, &sym1_mem);
349 if (sym1 == NULL)
350 error (2, 0,
351 gettext ("cannot get symbol in '%s': %s"),
352 fname1, elf_errmsg (-1));
353 GElf_Sym sym2_mem;
354 GElf_Sym *sym2 = gelf_getsym (data2, ndx, &sym2_mem);
355 if (sym2 == NULL)
356 error (2, 0,
357 gettext ("cannot get symbol in '%s': %s"),
358 fname2, elf_errmsg (-1));
359
360 const char *name1 = elf_strptr (elf1, shdr1->sh_link,
361 sym1->st_name);
362 const char *name2 = elf_strptr (elf2, shdr2->sh_link,
363 sym2->st_name);
364 if (unlikely (name1 == NULL || name2 == NULL
365 || strcmp (name1, name2) != 0
366 || sym1->st_value != sym2->st_value
367 || (sym1->st_size != sym2->st_size
368 && sym1->st_shndx != SHN_UNDEF)
369 || sym1->st_info != sym2->st_info
370 || sym1->st_other != sym2->st_other
371 || sym1->st_shndx != sym1->st_shndx))
372 {
373 // XXX Do we want to allow reordered symbol tables?
374 symtab_mismatch:
375 if (! quiet)
376 {
377 if (elf_ndxscn (scn1) == elf_ndxscn (scn2))
378 error (0, 0,
379 gettext ("%s %s differ: symbol table [%zu]"),
380 fname1, fname2, elf_ndxscn (scn1));
381 else
382 error (0, 0, gettext ("\
383 %s %s differ: symbol table [%zu,%zu]"),
384 fname1, fname2, elf_ndxscn (scn1),
385 elf_ndxscn (scn2));
386 }
387 DIFFERENCE;
388 break;
389 }
390
391 if (sym1->st_shndx == SHN_UNDEF
392 && sym1->st_size != sym2->st_size)
393 {
394 /* The size of the symbol in the object defining it
395 might have changed. That is OK unless the symbol
396 is used in a copy relocation. Look over the
397 sections in both files and determine which
398 relocation section uses this symbol table
399 section. Then look through the relocations to
400 see whether any copy relocation references this
401 symbol. */
402 if (search_for_copy_reloc (ebl1, elf_ndxscn (scn1), ndx)
403 || search_for_copy_reloc (ebl2, elf_ndxscn (scn2), ndx))
404 goto symtab_mismatch;
405 }
406 }
407 break;
408
409 case SHT_NOTE:
410 /* Parse the note format and compare the notes themselves. */
411 {
412 GElf_Nhdr note1;
413 GElf_Nhdr note2;
414
415 size_t off1 = 0;
416 size_t off2 = 0;
417 size_t name_offset;
418 size_t desc_offset;
419 while (off1 < data1->d_size
420 && (off1 = gelf_getnote (data1, off1, ¬e1,
421 &name_offset, &desc_offset)) > 0)
422 {
423 const char *name1 = 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, ¬e2,
434 &name_offset, &desc_offset);
435 if (off2 == 0)
436 error (2, 0, gettext ("\
437 cannot read note section [%zu] '%s' in '%s': %s"),
438 elf_ndxscn (scn2), sname2, fname2, elf_errmsg (-1));
439 const char *name2 = data2->d_buf + name_offset;
440 const void *desc2 = data2->d_buf + desc_offset;
441
442 if (note1.n_namesz != note2.n_namesz
443 || memcmp (name1, name2, note1.n_namesz))
444 {
445 if (! quiet)
446 error (0, 0, gettext ("\
447 %s %s differ: section [%zu] '%s' note name"),
448 fname1, fname2, elf_ndxscn (scn1), sname1);
449 DIFFERENCE;
450 }
451 if (note1.n_type != note2.n_type)
452 {
453 if (! quiet)
454 error (0, 0, gettext ("\
455 %s %s differ: section [%zu] '%s' note '%s' type"),
456 fname1, fname2, elf_ndxscn (scn1), sname1, name1);
457 DIFFERENCE;
458 }
459 if (note1.n_descsz != note2.n_descsz
460 || memcmp (desc1, desc2, note1.n_descsz))
461 {
462 if (note1.n_type == NT_GNU_BUILD_ID
463 && note1.n_namesz == sizeof "GNU"
464 && !memcmp (name1, "GNU", sizeof "GNU"))
465 {
466 if (note1.n_descsz != note2.n_descsz)
467 {
468 if (! quiet)
469 error (0, 0, gettext ("\
470 %s %s differ: build ID length"),
471 fname1, fname2);
472 DIFFERENCE;
473 }
474 else if (! ignore_build_id)
475 {
476 if (! quiet)
477 error (0, 0, gettext ("\
478 %s %s differ: build ID content"),
479 fname1, fname2);
480 DIFFERENCE;
481 }
482 }
483 else
484 {
485 if (! quiet)
486 error (0, 0, gettext ("\
487 %s %s differ: section [%zu] '%s' note '%s' content"),
488 fname1, fname2, elf_ndxscn (scn1), sname1,
489 name1);
490 DIFFERENCE;
491 }
492 }
493 }
494 if (off2 < data2->d_size)
495 {
496 if (! quiet)
497 error (0, 0, gettext ("\
498 %s %s differ: section [%zu] '%s' number of notes"),
499 fname1, fname2, elf_ndxscn (scn1), sname1);
500 DIFFERENCE;
501 }
502 }
503 break;
504
505 default:
506 /* Compare the section content byte for byte. */
507 assert (shdr1->sh_type == SHT_NOBITS
508 || (data1->d_buf != NULL || data1->d_size == 0));
509 assert (shdr2->sh_type == SHT_NOBITS
510 || (data2->d_buf != NULL || data1->d_size == 0));
511
512 if (unlikely (data1->d_size != data2->d_size
513 || (shdr1->sh_type != SHT_NOBITS
514 && data1->d_size != 0
515 && memcmp (data1->d_buf, data2->d_buf,
516 data1->d_size) != 0)))
517 {
518 if (hash_inexact
519 && shdr1->sh_type == SHT_HASH
520 && data1->d_size == data2->d_size
521 && hash_content_equivalent (shdr1->sh_entsize, data1, data2))
522 break;
523
524 if (! quiet)
525 {
526 if (elf_ndxscn (scn1) == elf_ndxscn (scn2))
527 error (0, 0, gettext ("\
528 %s %s differ: section [%zu] '%s' content"),
529 fname1, fname2, elf_ndxscn (scn1), sname1);
530 else
531 error (0, 0, gettext ("\
532 %s %s differ: section [%zu,%zu] '%s' content"),
533 fname1, fname2, elf_ndxscn (scn1),
534 elf_ndxscn (scn2), sname1);
535 }
536 DIFFERENCE;
537 }
538 break;
539 }
540 }
541
542 if (unlikely (scn1 != scn2))
543 {
544 if (! quiet)
545 error (0, 0,
546 gettext ("%s %s differ: unequal amount of important sections"),
547 fname1, fname2);
548 DIFFERENCE;
549 }
550
551 /* We we look at gaps, create artificial ones for the parts of the
552 program which we are not in sections. */
553 struct region ehdr_region;
554 struct region phdr_region;
555 if (gaps != gaps_ignore)
556 {
557 ehdr_region.from = 0;
558 ehdr_region.to = ehdr1->e_ehsize;
559 ehdr_region.next = &phdr_region;
560
561 phdr_region.from = ehdr1->e_phoff;
562 phdr_region.to = ehdr1->e_phoff + phnum1 * ehdr1->e_phentsize;
563 phdr_region.next = regions;
564
565 regions = &ehdr_region;
566 nregions += 2;
567 }
568
569 /* If we need to look at the gaps we need access to the file data. */
570 char *raw1 = NULL;
571 size_t size1 = 0;
572 char *raw2 = NULL;
573 size_t size2 = 0;
574 struct region *regionsarr = alloca (nregions * sizeof (struct region));
575 if (gaps != gaps_ignore)
576 {
577 raw1 = elf_rawfile (elf1, &size1);
578 if (raw1 == NULL )
579 error (2, 0, gettext ("cannot load data of '%s': %s"),
580 fname1, elf_errmsg (-1));
581
582 raw2 = elf_rawfile (elf2, &size2);
583 if (raw2 == NULL )
584 error (2, 0, gettext ("cannot load data of '%s': %s"),
585 fname2, elf_errmsg (-1));
586
587 for (size_t cnt = 0; cnt < nregions; ++cnt)
588 {
589 regionsarr[cnt] = *regions;
590 regions = regions->next;
591 }
592
593 qsort (regionsarr, nregions, sizeof (regionsarr[0]), regioncompare);
594 }
595
596 /* Compare the program header tables. */
597 for (unsigned int ndx = 0; ndx < phnum1; ++ndx)
598 {
599 GElf_Phdr phdr1_mem;
600 GElf_Phdr *phdr1 = gelf_getphdr (elf1, ndx, &phdr1_mem);
601 if (phdr1 == NULL)
602 error (2, 0,
603 gettext ("cannot get program header entry %d of '%s': %s"),
604 ndx, fname1, elf_errmsg (-1));
605 GElf_Phdr phdr2_mem;
606 GElf_Phdr *phdr2 = gelf_getphdr (elf2, ndx, &phdr2_mem);
607 if (phdr2 == NULL)
608 error (2, 0,
609 gettext ("cannot get program header entry %d of '%s': %s"),
610 ndx, fname2, elf_errmsg (-1));
611
612 if (unlikely (memcmp (phdr1, phdr2, sizeof (GElf_Phdr)) != 0))
613 {
614 if (! quiet)
615 error (0, 0, gettext ("%s %s differ: program header %d"),
616 fname1, fname2, ndx);
617 DIFFERENCE;
618 }
619
620 if (gaps != gaps_ignore && phdr1->p_type == PT_LOAD)
621 {
622 size_t cnt = 0;
623 while (cnt < nregions && regionsarr[cnt].to < phdr1->p_offset)
624 ++cnt;
625
626 GElf_Off last = phdr1->p_offset;
627 GElf_Off end = phdr1->p_offset + phdr1->p_filesz;
628 while (cnt < nregions && regionsarr[cnt].from < end)
629 {
630 if (last < regionsarr[cnt].from)
631 {
632 /* Compare the [LAST,FROM) region. */
633 assert (gaps == gaps_match);
634 if (unlikely (memcmp (raw1 + last, raw2 + last,
635 regionsarr[cnt].from - last) != 0))
636 {
637 gapmismatch:
638 if (!quiet)
639 error (0, 0, gettext ("%s %s differ: gap"),
640 fname1, fname2);
641 DIFFERENCE;
642 break;
643 }
644
645 }
646 last = regionsarr[cnt].to;
647 ++cnt;
648 }
649
650 if (cnt == nregions && last < end)
651 goto gapmismatch;
652 }
653 }
654
655 out:
656 elf_end (elf1);
657 elf_end (elf2);
658 ebl_closebackend (ebl1);
659 ebl_closebackend (ebl2);
660 close (fd1);
661 close (fd2);
662
663 return result;
664 }
665
666
667 /* Print the version information. */
668 static void
print_version(FILE * stream,struct argp_state * state)669 print_version (FILE *stream, struct argp_state *state __attribute__ ((unused)))
670 {
671 fprintf (stream, "elfcmp (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION);
672 fprintf (stream, gettext ("\
673 Copyright (C) %s Red Hat, Inc.\n\
674 This is free software; see the source for copying conditions. There is NO\n\
675 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
676 "), "2012");
677 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
678 }
679
680
681 /* Handle program arguments. */
682 static error_t
parse_opt(int key,char * arg,struct argp_state * state)683 parse_opt (int key, char *arg,
684 struct argp_state *state __attribute__ ((unused)))
685 {
686 switch (key)
687 {
688 case 'q':
689 quiet = true;
690 break;
691
692 case 'l':
693 verbose = true;
694 break;
695
696 case OPT_GAPS:
697 if (strcasecmp (arg, "ignore") == 0)
698 gaps = gaps_ignore;
699 else if (likely (strcasecmp (arg, "match") == 0))
700 gaps = gaps_match;
701 else
702 {
703 fprintf (stderr,
704 gettext ("Invalid value '%s' for --gaps parameter."),
705 arg);
706 argp_help (&argp, stderr, ARGP_HELP_SEE,
707 program_invocation_short_name);
708 exit (1);
709 }
710 break;
711
712 case OPT_HASH_INEXACT:
713 hash_inexact = true;
714 break;
715
716 case OPT_IGNORE_BUILD_ID:
717 ignore_build_id = true;
718 break;
719
720 default:
721 return ARGP_ERR_UNKNOWN;
722 }
723 return 0;
724 }
725
726
727 static Elf *
open_file(const char * fname,int * fdp,Ebl ** eblp)728 open_file (const char *fname, int *fdp, Ebl **eblp)
729 {
730 int fd = open (fname, O_RDONLY);
731 if (unlikely (fd == -1))
732 error (2, errno, gettext ("cannot open '%s'"), fname);
733 Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
734 if (elf == NULL)
735 error (2, 0,
736 gettext ("cannot create ELF descriptor for '%s': %s"),
737 fname, elf_errmsg (-1));
738 Ebl *ebl = ebl_openbackend (elf);
739 if (ebl == NULL)
740 error (2, 0,
741 gettext ("cannot create EBL descriptor for '%s'"), fname);
742
743 *fdp = fd;
744 *eblp = ebl;
745 return elf;
746 }
747
748
749 static bool
search_for_copy_reloc(Ebl * ebl,size_t scnndx,int symndx)750 search_for_copy_reloc (Ebl *ebl, size_t scnndx, int symndx)
751 {
752 Elf_Scn *scn = NULL;
753 while ((scn = elf_nextscn (ebl->elf, scn)) != NULL)
754 {
755 GElf_Shdr shdr_mem;
756 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
757 if (shdr == NULL)
758 error (2, 0,
759 gettext ("cannot get section header of section %zu: %s"),
760 elf_ndxscn (scn), elf_errmsg (-1));
761
762 if ((shdr->sh_type != SHT_REL && shdr->sh_type != SHT_RELA)
763 || shdr->sh_link != scnndx)
764 continue;
765
766 Elf_Data *data = elf_getdata (scn, NULL);
767 if (data == NULL)
768 error (2, 0,
769 gettext ("cannot get content of section %zu: %s"),
770 elf_ndxscn (scn), elf_errmsg (-1));
771
772 if (shdr->sh_type == SHT_REL && shdr->sh_entsize != 0)
773 for (int ndx = 0; ndx < (int) (shdr->sh_size / shdr->sh_entsize);
774 ++ndx)
775 {
776 GElf_Rel rel_mem;
777 GElf_Rel *rel = gelf_getrel (data, ndx, &rel_mem);
778 if (rel == NULL)
779 error (2, 0, gettext ("cannot get relocation: %s"),
780 elf_errmsg (-1));
781
782 if ((int) GELF_R_SYM (rel->r_info) == symndx
783 && ebl_copy_reloc_p (ebl, GELF_R_TYPE (rel->r_info)))
784 return true;
785 }
786 else if (shdr->sh_entsize != 0)
787 for (int ndx = 0; ndx < (int) (shdr->sh_size / shdr->sh_entsize);
788 ++ndx)
789 {
790 GElf_Rela rela_mem;
791 GElf_Rela *rela = gelf_getrela (data, ndx, &rela_mem);
792 if (rela == NULL)
793 error (2, 0, gettext ("cannot get relocation: %s"),
794 elf_errmsg (-1));
795
796 if ((int) GELF_R_SYM (rela->r_info) == symndx
797 && ebl_copy_reloc_p (ebl, GELF_R_TYPE (rela->r_info)))
798 return true;
799 }
800 }
801
802 return false;
803 }
804
805
806 static int
regioncompare(const void * p1,const void * p2)807 regioncompare (const void *p1, const void *p2)
808 {
809 const struct region *r1 = (const struct region *) p1;
810 const struct region *r2 = (const struct region *) p2;
811
812 if (r1->from < r2->from)
813 return -1;
814 return 1;
815 }
816
817
818 static int
compare_Elf32_Word(const void * p1,const void * p2)819 compare_Elf32_Word (const void *p1, const void *p2)
820 {
821 const Elf32_Word *w1 = p1;
822 const Elf32_Word *w2 = p2;
823 return *w1 < *w2 ? -1 : *w1 > *w2 ? 1 : 0;
824 }
825
826 static int
compare_Elf64_Xword(const void * p1,const void * p2)827 compare_Elf64_Xword (const void *p1, const void *p2)
828 {
829 const Elf64_Xword *w1 = p1;
830 const Elf64_Xword *w2 = p2;
831 return *w1 < *w2 ? -1 : *w1 > *w2 ? 1 : 0;
832 }
833
834 static bool
hash_content_equivalent(size_t entsize,Elf_Data * data1,Elf_Data * data2)835 hash_content_equivalent (size_t entsize, Elf_Data *data1, Elf_Data *data2)
836 {
837 #define CHECK_HASH(Hash_Word) \
838 { \
839 const Hash_Word *const hash1 = data1->d_buf; \
840 const Hash_Word *const hash2 = data2->d_buf; \
841 const size_t nbucket = hash1[0]; \
842 const size_t nchain = hash1[1]; \
843 if (data1->d_size != (2 + nbucket + nchain) * sizeof hash1[0] \
844 || hash2[0] != nbucket || hash2[1] != nchain) \
845 return false; \
846 \
847 const Hash_Word *const bucket1 = &hash1[2]; \
848 const Hash_Word *const chain1 = &bucket1[nbucket]; \
849 const Hash_Word *const bucket2 = &hash2[2]; \
850 const Hash_Word *const chain2 = &bucket2[nbucket]; \
851 \
852 bool chain_ok[nchain]; \
853 Hash_Word temp1[nchain - 1]; \
854 Hash_Word temp2[nchain - 1]; \
855 memset (chain_ok, 0, sizeof chain_ok); \
856 for (size_t i = 0; i < nbucket; ++i) \
857 { \
858 if (bucket1[i] >= nchain || bucket2[i] >= nchain) \
859 return false; \
860 \
861 size_t b1 = 0; \
862 for (size_t p = bucket1[i]; p != STN_UNDEF; p = chain1[p]) \
863 if (p >= nchain || b1 >= nchain - 1) \
864 return false; \
865 else \
866 temp1[b1++] = p; \
867 \
868 size_t b2 = 0; \
869 for (size_t p = bucket2[i]; p != STN_UNDEF; p = chain2[p]) \
870 if (p >= nchain || b2 >= nchain - 1) \
871 return false; \
872 else \
873 temp2[b2++] = p; \
874 \
875 if (b1 != b2) \
876 return false; \
877 \
878 qsort (temp1, b1, sizeof temp1[0], compare_##Hash_Word); \
879 qsort (temp2, b2, sizeof temp2[0], compare_##Hash_Word); \
880 \
881 for (b1 = 0; b1 < b2; ++b1) \
882 if (temp1[b1] != temp2[b1]) \
883 return false; \
884 else \
885 chain_ok[temp1[b1]] = true; \
886 } \
887 \
888 for (size_t i = 0; i < nchain; ++i) \
889 if (!chain_ok[i] && chain1[i] != chain2[i]) \
890 return false; \
891 \
892 return true; \
893 }
894
895 switch (entsize)
896 {
897 case 4:
898 CHECK_HASH (Elf32_Word);
899 break;
900 case 8:
901 CHECK_HASH (Elf64_Xword);
902 break;
903 }
904
905 return false;
906 }
907
908
909 #include "debugpred.h"
910