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