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