1 /* Compare relevant content of two ELF files.
2 Copyright (C) 2005-2012 Red Hat, Inc.
3 This file is part of Red Hat elfutils.
4 Written by Ulrich Drepper <drepper@redhat.com>, 2005.
5
6 Red Hat elfutils is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by the
8 Free Software Foundation; version 2 of the License.
9
10 Red Hat elfutils is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with Red Hat elfutils; if not, write to the Free Software Foundation,
17 Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
18
19 Red Hat elfutils is an included package of the Open Invention Network.
20 An included package of the Open Invention Network is a package for which
21 Open Invention Network licensees cross-license their patents. No patent
22 license is granted, either expressly or impliedly, by designation as an
23 included package. Should you wish to participate in the Open Invention
24 Network licensing program, please visit www.openinventionnetwork.com
25 <http://www.openinventionnetwork.com>. */
26
27 #ifdef HAVE_CONFIG_H
28 # include <config.h>
29 #endif
30
31 #include <argp.h>
32 #include <assert.h>
33 #include <errno.h>
34 #include <error.h>
35 #include <fcntl.h>
36 #include <locale.h>
37 #include <libintl.h>
38 #include <stdbool.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43
44 #include <system.h>
45 #include "../libelf/elf-knowledge.h"
46 #include "../libebl/libeblP.h"
47
48
49 /* Prototypes of local functions. */
50 static Elf *open_file (const char *fname, int *fdp, Ebl **eblp);
51 static bool search_for_copy_reloc (Ebl *ebl, size_t scnndx, int symndx);
52 static int regioncompare (const void *p1, const void *p2);
53
54
55 /* Name and version of program. */
56 static void print_version (FILE *stream, struct argp_state *state);
57 ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
58
59 /* Bug report address. */
60 ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
61
62 /* Values for the parameters which have no short form. */
63 #define OPT_GAPS 0x100
64 #define OPT_HASH_INEXACT 0x101
65 #define OPT_IGNORE_BUILD_ID 0x102
66
67 /* Definitions of arguments for argp functions. */
68 static const struct argp_option options[] =
69 {
70 { NULL, 0, NULL, 0, N_("Control options:"), 0 },
71 { "verbose", 'l', NULL, 0,
72 N_("Output all differences, not just the first"), 0 },
73 { "gaps", OPT_GAPS, "ACTION", 0, N_("Control treatment of gaps in loadable segments [ignore|match] (default: ignore)"), 0 },
74 { "hash-inexact", OPT_HASH_INEXACT, NULL, 0,
75 N_("Ignore permutation of buckets in SHT_HASH section"), 0 },
76 { "ignore-build-id", OPT_IGNORE_BUILD_ID, NULL, 0,
77 N_("Ignore differences in build ID"), 0 },
78 { "quiet", 'q', NULL, 0, N_("Output nothing; yield exit status only"), 0 },
79
80 { NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 },
81 { NULL, 0, NULL, 0, NULL, 0 }
82 };
83
84 /* Short description of program. */
85 static const char doc[] = N_("\
86 Compare relevant parts of two ELF files for equality.");
87
88 /* Strings for arguments in help texts. */
89 static const char args_doc[] = N_("FILE1 FILE2");
90
91 /* Prototype for option handler. */
92 static error_t parse_opt (int key, char *arg, struct argp_state *state);
93
94 /* Data structure to communicate with argp functions. */
95 static struct argp argp =
96 {
97 options, parse_opt, args_doc, doc, NULL, NULL, NULL
98 };
99
100
101 /* How to treat gaps in loadable segments. */
102 static enum
103 {
104 gaps_ignore = 0,
105 gaps_match
106 }
107 gaps;
108
109 /* Structure to hold information about used regions. */
110 struct region
111 {
112 GElf_Addr from;
113 GElf_Addr to;
114 struct region *next;
115 };
116
117 /* Nonzero if only exit status is wanted. */
118 static bool quiet;
119
120 /* True iff multiple differences should be output. */
121 static bool verbose;
122
123 /* True iff SHT_HASH treatment should be generous. */
124 static bool hash_inexact;
125
126 /* True iff build ID notes should be ignored. */
127 static bool ignore_build_id;
128
129 static bool hash_content_equivalent (size_t entsize, Elf_Data *, Elf_Data *);
130
131
132 int
main(int argc,char * argv[])133 main (int argc, char *argv[])
134 {
135 /* Set locale. */
136 (void) setlocale (LC_ALL, "");
137
138 /* Make sure the message catalog can be found. */
139 (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
140
141 /* Initialize the message catalog. */
142 (void) textdomain (PACKAGE_TARNAME);
143
144 /* Parse and process arguments. */
145 int remaining;
146 (void) argp_parse (&argp, argc, argv, 0, &remaining, NULL);
147
148 /* We expect exactly two non-option parameters. */
149 if (unlikely (remaining + 2 != argc))
150 {
151 fputs (gettext ("Invalid number of parameters.\n"), stderr);
152 argp_help (&argp, stderr, ARGP_HELP_SEE, program_invocation_short_name);
153 exit (1);
154 }
155
156 if (quiet)
157 verbose = false;
158
159 /* Comparing the files is done in two phases:
160 1. compare all sections. Sections which are irrelevant (i.e., if
161 strip would remove them) are ignored. Some section types are
162 handled special.
163 2. all parts of the loadable segments which are not parts of any
164 section is compared according to the rules of the --gaps option.
165 */
166 int result = 0;
167 elf_version (EV_CURRENT);
168
169 const char *const fname1 = argv[remaining];
170 int fd1;
171 Ebl *ebl1;
172 Elf *elf1 = open_file (fname1, &fd1, &ebl1);
173
174 const char *const fname2 = argv[remaining + 1];
175 int fd2;
176 Ebl *ebl2;
177 Elf *elf2 = open_file (fname2, &fd2, &ebl2);
178
179 GElf_Ehdr ehdr1_mem;
180 GElf_Ehdr *ehdr1 = gelf_getehdr (elf1, &ehdr1_mem);
181 if (ehdr1 == NULL)
182 error (2, 0, gettext ("cannot get ELF header of '%s': %s"),
183 fname1, elf_errmsg (-1));
184 GElf_Ehdr ehdr2_mem;
185 GElf_Ehdr *ehdr2 = gelf_getehdr (elf2, &ehdr2_mem);
186 if (ehdr2 == NULL)
187 error (2, 0, gettext ("cannot get ELF header of '%s': %s"),
188 fname2, elf_errmsg (-1));
189
190 #define DIFFERENCE \
191 do \
192 { \
193 result = 1; \
194 if (! verbose) \
195 goto out; \
196 } \
197 while (0)
198
199 /* Compare the ELF headers. */
200 if (unlikely (memcmp (ehdr1->e_ident, ehdr2->e_ident, EI_NIDENT) != 0
201 || ehdr1->e_type != ehdr2->e_type
202 || ehdr1->e_machine != ehdr2->e_machine
203 || ehdr1->e_version != ehdr2->e_version
204 || ehdr1->e_entry != ehdr2->e_entry
205 || ehdr1->e_phoff != ehdr2->e_phoff
206 || ehdr1->e_flags != ehdr2->e_flags
207 || ehdr1->e_ehsize != ehdr2->e_ehsize
208 || ehdr1->e_phentsize != ehdr2->e_phentsize
209 || ehdr1->e_phnum != ehdr2->e_phnum
210 || ehdr1->e_shentsize != ehdr2->e_shentsize))
211 {
212 if (! quiet)
213 error (0, 0, gettext ("%s %s diff: ELF header"), fname1, fname2);
214 DIFFERENCE;
215 }
216
217 size_t shnum1;
218 size_t shnum2;
219 if (unlikely (elf_getshdrnum (elf1, &shnum1) != 0))
220 error (2, 0, gettext ("cannot get section count of '%s': %s"),
221 fname1, elf_errmsg (-1));
222 if (unlikely (elf_getshdrnum (elf2, &shnum2) != 0))
223 error (2, 0, gettext ("cannot get section count of '%s': %s"),
224 fname2, elf_errmsg (-1));
225 if (unlikely (shnum1 != shnum2))
226 {
227 if (! quiet)
228 error (0, 0, gettext ("%s %s diff: section count"), fname1, fname2);
229 DIFFERENCE;
230 }
231
232 size_t phnum1;
233 size_t phnum2;
234 if (unlikely (elf_getphdrnum (elf1, &phnum1) != 0))
235 error (2, 0, gettext ("cannot get program header count of '%s': %s"),
236 fname1, elf_errmsg (-1));
237 if (unlikely (elf_getphdrnum (elf2, &phnum2) != 0))
238 error (2, 0, gettext ("cannot get program header count of '%s': %s"),
239 fname2, elf_errmsg (-1));
240 if (unlikely (phnum1 != phnum2))
241 {
242 if (! quiet)
243 error (0, 0, gettext ("%s %s diff: program header count"),
244 fname1, fname2);
245 DIFFERENCE;
246 }
247
248 /* Iterate over all sections. We expect the sections in the two
249 files to match exactly. */
250 Elf_Scn *scn1 = NULL;
251 Elf_Scn *scn2 = NULL;
252 struct region *regions = NULL;
253 size_t nregions = 0;
254 while (1)
255 {
256 GElf_Shdr shdr1_mem;
257 GElf_Shdr *shdr1;
258 const char *sname1 = NULL;
259 do
260 {
261 scn1 = elf_nextscn (elf1, scn1);
262 shdr1 = gelf_getshdr (scn1, &shdr1_mem);
263 if (shdr1 != NULL)
264 sname1 = elf_strptr (elf1, ehdr1->e_shstrndx, shdr1->sh_name);
265 }
266 while (scn1 != NULL
267 && ebl_section_strip_p (ebl1, ehdr1, shdr1, sname1, true, false));
268
269 GElf_Shdr shdr2_mem;
270 GElf_Shdr *shdr2;
271 const char *sname2 = NULL;
272 do
273 {
274 scn2 = elf_nextscn (elf2, scn2);
275 shdr2 = gelf_getshdr (scn2, &shdr2_mem);
276 if (shdr2 != NULL)
277 sname2 = elf_strptr (elf2, ehdr2->e_shstrndx, shdr2->sh_name);
278 }
279 while (scn2 != NULL
280 && ebl_section_strip_p (ebl2, ehdr2, shdr2, sname2, true, false));
281
282 if (scn1 == NULL || scn2 == NULL)
283 break;
284
285 if (gaps != gaps_ignore && (shdr1->sh_flags & SHF_ALLOC) != 0)
286 {
287 struct region *newp = (struct region *) alloca (sizeof (*newp));
288 newp->from = shdr1->sh_offset;
289 newp->to = shdr1->sh_offset + shdr1->sh_size;
290 newp->next = regions;
291 regions = newp;
292
293 ++nregions;
294 }
295
296 /* Compare the headers. We allow the name to be at a different
297 location. */
298 if (unlikely (strcmp (sname1, sname2) != 0))
299 {
300 error (0, 0, gettext ("%s %s differ: section [%zu], [%zu] name"),
301 fname1, fname2, elf_ndxscn (scn1), elf_ndxscn (scn2));
302 DIFFERENCE;
303 }
304
305 /* We ignore certain sections. */
306 if (strcmp (sname1, ".gnu_debuglink") == 0
307 || strcmp (sname1, ".gnu.prelink_undo") == 0)
308 continue;
309
310 if (shdr1->sh_type != shdr2->sh_type
311 // XXX Any flags which should be ignored?
312 || shdr1->sh_flags != shdr2->sh_flags
313 || shdr1->sh_addr != shdr2->sh_addr
314 || (shdr1->sh_offset != shdr2->sh_offset
315 && (shdr1->sh_flags & SHF_ALLOC)
316 && ehdr1->e_type != ET_REL)
317 || shdr1->sh_size != shdr2->sh_size
318 || shdr1->sh_link != shdr2->sh_link
319 || shdr1->sh_info != shdr2->sh_info
320 || shdr1->sh_addralign != shdr2->sh_addralign
321 || shdr1->sh_entsize != shdr2->sh_entsize)
322 {
323 error (0, 0, gettext ("%s %s differ: section [%zu] '%s' header"),
324 fname1, fname2, elf_ndxscn (scn1), sname1);
325 DIFFERENCE;
326 }
327
328 Elf_Data *data1 = elf_getdata (scn1, NULL);
329 if (data1 == NULL)
330 error (2, 0,
331 gettext ("cannot get content of section %zu in '%s': %s"),
332 elf_ndxscn (scn1), fname1, elf_errmsg (-1));
333
334 Elf_Data *data2 = elf_getdata (scn2, NULL);
335 if (data2 == NULL)
336 error (2, 0,
337 gettext ("cannot get content of section %zu in '%s': %s"),
338 elf_ndxscn (scn2), fname2, elf_errmsg (-1));
339
340 switch (shdr1->sh_type)
341 {
342 case SHT_DYNSYM:
343 case SHT_SYMTAB:
344 /* Iterate over the symbol table. We ignore the st_size
345 value of undefined symbols. */
346 for (int ndx = 0; ndx < (int) (shdr1->sh_size / shdr1->sh_entsize);
347 ++ndx)
348 {
349 GElf_Sym sym1_mem;
350 GElf_Sym *sym1 = gelf_getsym (data1, ndx, &sym1_mem);
351 if (sym1 == NULL)
352 error (2, 0,
353 gettext ("cannot get symbol in '%s': %s"),
354 fname1, elf_errmsg (-1));
355 GElf_Sym sym2_mem;
356 GElf_Sym *sym2 = gelf_getsym (data2, ndx, &sym2_mem);
357 if (sym2 == NULL)
358 error (2, 0,
359 gettext ("cannot get symbol in '%s': %s"),
360 fname2, elf_errmsg (-1));
361
362 const char *name1 = elf_strptr (elf1, shdr1->sh_link,
363 sym1->st_name);
364 const char *name2 = elf_strptr (elf2, shdr2->sh_link,
365 sym2->st_name);
366 if (unlikely (strcmp (name1, name2) != 0
367 || sym1->st_value != sym2->st_value
368 || (sym1->st_size != sym2->st_size
369 && sym1->st_shndx != SHN_UNDEF)
370 || sym1->st_info != sym2->st_info
371 || sym1->st_other != sym2->st_other
372 || sym1->st_shndx != sym1->st_shndx))
373 {
374 // XXX Do we want to allow reordered symbol tables?
375 symtab_mismatch:
376 if (! quiet)
377 {
378 if (elf_ndxscn (scn1) == elf_ndxscn (scn2))
379 error (0, 0,
380 gettext ("%s %s differ: symbol table [%zu]"),
381 fname1, fname2, elf_ndxscn (scn1));
382 else
383 error (0, 0, gettext ("\
384 %s %s differ: symbol table [%zu,%zu]"),
385 fname1, fname2, elf_ndxscn (scn1),
386 elf_ndxscn (scn2));
387 }
388 DIFFERENCE;
389 break;
390 }
391
392 if (sym1->st_shndx == SHN_UNDEF
393 && sym1->st_size != sym2->st_size)
394 {
395 /* The size of the symbol in the object defining it
396 might have changed. That is OK unless the symbol
397 is used in a copy relocation. Look over the
398 sections in both files and determine which
399 relocation section uses this symbol table
400 section. Then look through the relocations to
401 see whether any copy relocation references this
402 symbol. */
403 if (search_for_copy_reloc (ebl1, elf_ndxscn (scn1), ndx)
404 || search_for_copy_reloc (ebl2, elf_ndxscn (scn2), ndx))
405 goto symtab_mismatch;
406 }
407 }
408 break;
409
410 case SHT_NOTE:
411 /* Parse the note format and compare the notes themselves. */
412 {
413 GElf_Nhdr note1;
414 GElf_Nhdr note2;
415
416 size_t off1 = 0;
417 size_t off2 = 0;
418 size_t name_offset;
419 size_t desc_offset;
420 while (off1 < data1->d_size
421 && (off1 = gelf_getnote (data1, off1, ¬e1,
422 &name_offset, &desc_offset)) > 0)
423 {
424 const char *name1 = data1->d_buf + name_offset;
425 const void *desc1 = data1->d_buf + desc_offset;
426 if (off2 >= data2->d_size)
427 {
428 if (! quiet)
429 error (0, 0, gettext ("\
430 %s %s differ: section [%zu] '%s' number of notes"),
431 fname1, fname2, elf_ndxscn (scn1), sname1);
432 DIFFERENCE;
433 }
434 off2 = gelf_getnote (data2, off2, ¬e2,
435 &name_offset, &desc_offset);
436 if (off2 == 0)
437 error (2, 0, gettext ("\
438 cannot read note section [%zu] '%s' in '%s': %s"),
439 elf_ndxscn (scn2), sname2, fname2, elf_errmsg (-1));
440 const char *name2 = 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 && 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 (ehdr1 == 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 (ehdr2 == 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 close (fd1);
659 close (fd2);
660
661 return result;
662 }
663
664
665 /* Print the version information. */
666 static void
print_version(FILE * stream,struct argp_state * state)667 print_version (FILE *stream, struct argp_state *state __attribute__ ((unused)))
668 {
669 fprintf (stream, "elfcmp (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION);
670 fprintf (stream, gettext ("\
671 Copyright (C) %s Red Hat, Inc.\n\
672 This is free software; see the source for copying conditions. There is NO\n\
673 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
674 "), "2012");
675 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
676 }
677
678
679 /* Handle program arguments. */
680 static error_t
parse_opt(int key,char * arg,struct argp_state * state)681 parse_opt (int key, char *arg,
682 struct argp_state *state __attribute__ ((unused)))
683 {
684 switch (key)
685 {
686 case 'q':
687 quiet = true;
688 break;
689
690 case 'l':
691 verbose = true;
692 break;
693
694 case OPT_GAPS:
695 if (strcasecmp (arg, "ignore") == 0)
696 gaps = gaps_ignore;
697 else if (likely (strcasecmp (arg, "match") == 0))
698 gaps = gaps_match;
699 else
700 {
701 fprintf (stderr,
702 gettext ("Invalid value '%s' for --gaps parameter."),
703 arg);
704 argp_help (&argp, stderr, ARGP_HELP_SEE,
705 program_invocation_short_name);
706 exit (1);
707 }
708 break;
709
710 case OPT_HASH_INEXACT:
711 hash_inexact = true;
712 break;
713
714 case OPT_IGNORE_BUILD_ID:
715 ignore_build_id = true;
716 break;
717
718 default:
719 return ARGP_ERR_UNKNOWN;
720 }
721 return 0;
722 }
723
724
725 static Elf *
open_file(const char * fname,int * fdp,Ebl ** eblp)726 open_file (const char *fname, int *fdp, Ebl **eblp)
727 {
728 int fd = open (fname, O_RDONLY);
729 if (unlikely (fd == -1))
730 error (2, errno, gettext ("cannot open '%s'"), fname);
731 Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
732 if (elf == NULL)
733 error (2, 0,
734 gettext ("cannot create ELF descriptor for '%s': %s"),
735 fname, elf_errmsg (-1));
736 Ebl *ebl = ebl_openbackend (elf);
737 if (ebl == NULL)
738 error (2, 0,
739 gettext ("cannot create EBL descriptor for '%s'"), fname);
740
741 *fdp = fd;
742 *eblp = ebl;
743 return elf;
744 }
745
746
747 static bool
search_for_copy_reloc(Ebl * ebl,size_t scnndx,int symndx)748 search_for_copy_reloc (Ebl *ebl, size_t scnndx, int symndx)
749 {
750 Elf_Scn *scn = NULL;
751 while ((scn = elf_nextscn (ebl->elf, scn)) != NULL)
752 {
753 GElf_Shdr shdr_mem;
754 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
755 if (shdr == NULL)
756 error (2, 0,
757 gettext ("cannot get section header of section %zu: %s"),
758 elf_ndxscn (scn), elf_errmsg (-1));
759
760 if ((shdr->sh_type != SHT_REL && shdr->sh_type != SHT_RELA)
761 || shdr->sh_link != scnndx)
762 continue;
763
764 Elf_Data *data = elf_getdata (scn, NULL);
765 if (data == NULL)
766 error (2, 0,
767 gettext ("cannot get content of section %zu: %s"),
768 elf_ndxscn (scn), elf_errmsg (-1));
769
770 if (shdr->sh_type == SHT_REL)
771 for (int ndx = 0; ndx < (int) (shdr->sh_size / shdr->sh_entsize);
772 ++ndx)
773 {
774 GElf_Rel rel_mem;
775 GElf_Rel *rel = gelf_getrel (data, ndx, &rel_mem);
776 if (rel == NULL)
777 error (2, 0, gettext ("cannot get relocation: %s"),
778 elf_errmsg (-1));
779
780 if ((int) GELF_R_SYM (rel->r_info) == symndx
781 && ebl_copy_reloc_p (ebl, GELF_R_TYPE (rel->r_info)))
782 return true;
783 }
784 else
785 for (int ndx = 0; ndx < (int) (shdr->sh_size / shdr->sh_entsize);
786 ++ndx)
787 {
788 GElf_Rela rela_mem;
789 GElf_Rela *rela = gelf_getrela (data, ndx, &rela_mem);
790 if (rela == NULL)
791 error (2, 0, gettext ("cannot get relocation: %s"),
792 elf_errmsg (-1));
793
794 if ((int) GELF_R_SYM (rela->r_info) == symndx
795 && ebl_copy_reloc_p (ebl, GELF_R_TYPE (rela->r_info)))
796 return true;
797 }
798 }
799
800 return false;
801 }
802
803
804 static int
regioncompare(const void * p1,const void * p2)805 regioncompare (const void *p1, const void *p2)
806 {
807 const struct region *r1 = (const struct region *) p1;
808 const struct region *r2 = (const struct region *) p2;
809
810 if (r1->from < r2->from)
811 return -1;
812 return 1;
813 }
814
815
816 static int
compare_Elf32_Word(const void * p1,const void * p2)817 compare_Elf32_Word (const void *p1, const void *p2)
818 {
819 const Elf32_Word *w1 = p1;
820 const Elf32_Word *w2 = p2;
821 assert (sizeof (int) >= sizeof (*w1));
822 return (int) *w1 - (int) *w2;
823 }
824
825 static int
compare_Elf64_Xword(const void * p1,const void * p2)826 compare_Elf64_Xword (const void *p1, const void *p2)
827 {
828 const Elf64_Xword *w1 = p1;
829 const Elf64_Xword *w2 = p2;
830 return *w1 < *w2 ? -1 : *w1 > *w2 ? 1 : 0;
831 }
832
833 static bool
hash_content_equivalent(size_t entsize,Elf_Data * data1,Elf_Data * data2)834 hash_content_equivalent (size_t entsize, Elf_Data *data1, Elf_Data *data2)
835 {
836 #define CHECK_HASH(Hash_Word) \
837 { \
838 const Hash_Word *const hash1 = data1->d_buf; \
839 const Hash_Word *const hash2 = data2->d_buf; \
840 const size_t nbucket = hash1[0]; \
841 const size_t nchain = hash1[1]; \
842 if (data1->d_size != (2 + nbucket + nchain) * sizeof hash1[0] \
843 || hash2[0] != nbucket || hash2[1] != nchain) \
844 return false; \
845 \
846 const Hash_Word *const bucket1 = &hash1[2]; \
847 const Hash_Word *const chain1 = &bucket1[nbucket]; \
848 const Hash_Word *const bucket2 = &hash2[2]; \
849 const Hash_Word *const chain2 = &bucket2[nbucket]; \
850 \
851 bool chain_ok[nchain]; \
852 Hash_Word temp1[nchain - 1]; \
853 Hash_Word temp2[nchain - 1]; \
854 memset (chain_ok, 0, sizeof chain_ok); \
855 for (size_t i = 0; i < nbucket; ++i) \
856 { \
857 if (bucket1[i] >= nchain || bucket2[i] >= nchain) \
858 return false; \
859 \
860 size_t b1 = 0; \
861 for (size_t p = bucket1[i]; p != STN_UNDEF; p = chain1[p]) \
862 if (p >= nchain || b1 >= nchain - 1) \
863 return false; \
864 else \
865 temp1[b1++] = p; \
866 \
867 size_t b2 = 0; \
868 for (size_t p = bucket2[i]; p != STN_UNDEF; p = chain2[p]) \
869 if (p >= nchain || b2 >= nchain - 1) \
870 return false; \
871 else \
872 temp2[b2++] = p; \
873 \
874 if (b1 != b2) \
875 return false; \
876 \
877 qsort (temp1, b1, sizeof temp1[0], compare_##Hash_Word); \
878 qsort (temp2, b2, sizeof temp2[0], compare_##Hash_Word); \
879 \
880 for (b1 = 0; b1 < b2; ++b1) \
881 if (temp1[b1] != temp2[b1]) \
882 return false; \
883 else \
884 chain_ok[temp1[b1]] = true; \
885 } \
886 \
887 for (size_t i = 0; i < nchain; ++i) \
888 if (!chain_ok[i] && chain1[i] != chain2[i]) \
889 return false; \
890 \
891 return true; \
892 }
893
894 switch (entsize)
895 {
896 case 4:
897 CHECK_HASH (Elf32_Word);
898 break;
899 case 8:
900 CHECK_HASH (Elf64_Xword);
901 break;
902 }
903
904 return false;
905 }
906
907
908 #include "debugpred.h"
909