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