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