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