Branch data Line data Source code
1 : : /* Classification of ELF files.
2 : : Copyright (C) 2019 Red Hat, Inc.
3 : : This file is part of elfutils.
4 : :
5 : : This file is free software; you can redistribute it and/or modify
6 : : it under the terms of the GNU General Public License as published by
7 : : the Free Software Foundation; either version 3 of the License, or
8 : : (at your option) any later version.
9 : :
10 : : elfutils is distributed in the hope that it will be useful, but
11 : : WITHOUT ANY WARRANTY; without even the implied warranty of
12 : : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : : GNU General Public License for more details.
14 : :
15 : : You should have received a copy of the GNU General Public License
16 : : along with this program. If not, see <http://www.gnu.org/licenses/>. */
17 : :
18 : : #include <config.h>
19 : : #include <system.h>
20 : :
21 : : #include <argp.h>
22 : : #include <error.h>
23 : : #include <fcntl.h>
24 : : #include <gelf.h>
25 : : #include <stdbool.h>
26 : : #include <stddef.h>
27 : : #include <stdio.h>
28 : : #include <stdlib.h>
29 : : #include <string.h>
30 : : #include <sys/stat.h>
31 : : #include <unistd.h>
32 : :
33 : : #include ELFUTILS_HEADER(elf)
34 : : #include ELFUTILS_HEADER(dwelf)
35 : : #include "printversion.h"
36 : :
37 : : /* Name and version of program. */
38 : : ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
39 : :
40 : : /* Bug report address. */
41 : : ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
42 : :
43 : : /* Set by parse_opt. */
44 : : static int verbose;
45 : :
46 : : /* Set by the main function. */
47 : : static const char *current_path;
48 : :
49 : : /* Set by open_file. */
50 : : static int file_fd = -1;
51 : :
52 : : /* Set by issue or elf_issue. */
53 : : static bool issue_found;
54 : :
55 : : /* Non-fatal issue occurred while processing the current_path. */
56 : : static void
57 : 0 : issue (int e, const char *msg)
58 : : {
59 [ # # ]: 0 : if (verbose >= 0)
60 : : {
61 [ # # ]: 0 : if (current_path == NULL)
62 : 0 : error (0, e, "%s", msg);
63 : : else
64 : 0 : error (0, e, "%s '%s'", msg, current_path);
65 : : }
66 : 0 : issue_found = true;
67 : 0 : }
68 : :
69 : : /* Non-fatal issue occurred while processing the current ELF. */
70 : : static void
71 : 0 : elf_issue (const char *msg)
72 : : {
73 [ # # ]: 0 : if (verbose >= 0)
74 : 0 : error (0, 0, "%s: %s: '%s'", msg, elf_errmsg (-1), current_path);
75 : 0 : issue_found = true;
76 : 0 : }
77 : :
78 : : /* Set by parse_opt. */
79 : : static bool flag_only_regular_files;
80 : :
81 : : static bool
82 : 600 : open_file (void)
83 : : {
84 [ - + ]: 600 : if (verbose > 1)
85 : 0 : fprintf (stderr, "debug: processing file: %s\n", current_path);
86 : :
87 : 1200 : file_fd = open (current_path, O_RDONLY | (flag_only_regular_files
88 [ + - ]: 600 : ? O_NOFOLLOW : 0));
89 [ - + ]: 600 : if (file_fd < 0)
90 : : {
91 [ # # # # ]: 0 : if (!flag_only_regular_files || errno != ELOOP)
92 : 0 : issue (errno, N_("opening"));
93 : 0 : return false;
94 : : }
95 : :
96 : 600 : struct stat st;
97 [ - + ]: 600 : if (fstat (file_fd, &st) != 0)
98 : : {
99 : 0 : issue (errno, N_("reading"));
100 : 0 : return false;
101 : : }
102 : :
103 : : /* Don't even bother with directories. */
104 [ + - ]: 600 : if (S_ISDIR (st.st_mode)
105 [ - + - - ]: 600 : || (flag_only_regular_files && !S_ISREG (st.st_mode)))
106 : 0 : return false;
107 : :
108 : : return true;
109 : : }
110 : :
111 : : static void
112 : 600 : close_file (void)
113 : : {
114 [ + - ]: 600 : if (file_fd >= 0)
115 : : {
116 : 600 : close (file_fd);
117 : 600 : file_fd = -1;
118 : : }
119 : 600 : }
120 : :
121 : : /* Set by open_elf. */
122 : : static Elf *elf;
123 : :
124 : : /* Set by parse_opt. */
125 : : static bool flag_compressed;
126 : :
127 : : static bool
128 : 600 : open_elf (void)
129 : : {
130 [ - + ]: 600 : if (!open_file ())
131 : : {
132 : : /* Make sure the file descriptor is gone. */
133 : 0 : close_file ();
134 : 0 : return false;
135 : : }
136 : :
137 [ - + ]: 600 : if (flag_compressed)
138 : 0 : elf = dwelf_elf_begin (file_fd);
139 : : else
140 : 600 : elf = elf_begin (file_fd, ELF_C_READ, NULL);
141 : :
142 [ - + ]: 600 : if (elf == NULL)
143 : : {
144 : 0 : elf_issue ("opening ELF file");
145 : 0 : close_file ();
146 : 0 : return false;
147 : : }
148 : :
149 : : return true;
150 : : }
151 : :
152 : : static void
153 : 600 : close_elf (void)
154 : : {
155 [ + - ]: 600 : if (elf != NULL)
156 : : {
157 : 600 : elf_end (elf);
158 : 600 : elf = NULL;
159 : : }
160 : :
161 : 600 : close_file ();
162 : 600 : }
163 : :
164 : : static const char *
165 : 0 : elf_kind_string (int kind)
166 : : {
167 [ # # # # : 0 : switch (kind)
# ]
168 : : {
169 : : case ELF_K_NONE:
170 : : return "ELF_K_NONE";
171 : 0 : case ELF_K_AR:
172 : 0 : return "ELF_K_AR";
173 : 0 : case ELF_K_COFF:
174 : 0 : return "ELF_K_COFF"; /* libelf doesn't really support this. */
175 : 0 : case ELF_K_ELF:
176 : 0 : return "ELF_K_ELF";
177 : 0 : default:
178 : 0 : return "<unknown>";
179 : : }
180 : : }
181 : :
182 : : static const char *
183 : 0 : elf_type_string (int type)
184 : : {
185 [ # # # # : 0 : switch (type)
# # ]
186 : : {
187 : : case ET_NONE:
188 : : return "ET_NONE";
189 : 0 : case ET_REL:
190 : 0 : return "ET_REL";
191 : 0 : case ET_EXEC:
192 : 0 : return "ET_EXEC";
193 : 0 : case ET_DYN:
194 : 0 : return "ET_DYN";
195 : 0 : case ET_CORE:
196 : 0 : return "ET_CORE";
197 : 0 : default:
198 : 0 : return "<unknown>";
199 : : }
200 : : }
201 : :
202 : : static int elf_type;
203 : : static bool has_program_load;
204 : : static bool has_sections;
205 : : static bool has_bits_alloc;
206 : : static bool has_program_interpreter;
207 : : static bool has_dynamic;
208 : : static bool has_soname;
209 : : static bool has_pie_flag;
210 : : static bool has_dt_debug;
211 : : static bool has_symtab;
212 : : static bool has_debug_sections;
213 : : static bool has_modinfo;
214 : : static bool has_gnu_linkonce_this_module;
215 : :
216 : : static bool
217 : 600 : run_classify (void)
218 : : {
219 : : /* Reset to unanalyzed default. */
220 : 600 : elf_type = 0;
221 : 600 : has_program_load = false;
222 : 600 : has_sections = false;
223 : 600 : has_bits_alloc = false;
224 : 600 : has_program_interpreter = false;
225 : 600 : has_dynamic = false;
226 : 600 : has_soname = false;
227 : 600 : has_pie_flag = false;
228 : 600 : has_dt_debug = false;
229 : 600 : has_symtab = false;
230 : 600 : has_debug_sections = false;
231 : 600 : has_modinfo = false;
232 : 600 : has_gnu_linkonce_this_module = false;
233 : :
234 : 600 : int kind = elf_kind (elf);
235 [ - + ]: 600 : if (verbose > 0)
236 : 0 : fprintf (stderr, "info: %s: ELF kind: %s (0x%x)\n", current_path,
237 : : elf_kind_string (kind), kind);
238 [ + + ]: 600 : if (kind != ELF_K_ELF)
239 : : return true;
240 : :
241 : 590 : GElf_Ehdr ehdr_storage;
242 : 590 : GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_storage);
243 [ - + ]: 590 : if (ehdr == NULL)
244 : : {
245 : 0 : elf_issue (N_("ELF header"));
246 : 0 : return false;
247 : : }
248 : 590 : elf_type = ehdr->e_type;
249 : :
250 : : /* Examine program headers. */
251 : 590 : GElf_Phdr dyn_seg = { .p_type = 0 };
252 : : {
253 : 590 : size_t nphdrs;
254 [ - + ]: 590 : if (elf_getphdrnum (elf, &nphdrs) != 0)
255 : : {
256 : 0 : elf_issue (N_("program headers"));
257 : 0 : return false;
258 : : }
259 [ + + ]: 4178 : for (size_t phdr_idx = 0; phdr_idx < nphdrs; ++phdr_idx)
260 : : {
261 : 3588 : GElf_Phdr phdr_storage;
262 : 3588 : GElf_Phdr *phdr = gelf_getphdr (elf, phdr_idx, &phdr_storage);
263 [ - + ]: 3588 : if (phdr == NULL)
264 : : {
265 : 0 : elf_issue (N_("program header"));
266 : 0 : return false;
267 : : }
268 [ + + ]: 3588 : if (phdr->p_type == PT_DYNAMIC)
269 : : {
270 : 252 : dyn_seg = *phdr;
271 : 252 : has_dynamic = true;
272 : : }
273 [ + + ]: 3588 : if (phdr->p_type == PT_INTERP)
274 : 132 : has_program_interpreter = true;
275 [ + + ]: 3588 : if (phdr->p_type == PT_LOAD)
276 : 2002 : has_program_load = true;
277 : : }
278 : : }
279 : :
280 : : /* Do we have sections? */
281 : : {
282 : 590 : size_t nshdrs;
283 [ - + ]: 590 : if (elf_getshdrnum (elf, &nshdrs) != 0)
284 : : {
285 : 0 : elf_issue (N_("section headers"));
286 : 0 : return false;
287 : : }
288 [ + + ]: 590 : if (nshdrs > 0)
289 : 470 : has_sections = true;
290 : : }
291 : :
292 : : {
293 : 590 : size_t shstrndx;
294 [ - + ]: 590 : if (unlikely (elf_getshdrstrndx (elf, &shstrndx) < 0))
295 : : {
296 : 0 : elf_issue (N_("section header string table index"));
297 : 0 : return false;
298 : : }
299 : :
300 : : Elf_Scn *scn = NULL;
301 : 26822 : while (true)
302 : 13116 : {
303 : 13706 : scn = elf_nextscn (elf, scn);
304 [ + + ]: 13706 : if (scn == NULL)
305 : : break;
306 : 13116 : GElf_Shdr shdr_storage;
307 : 13116 : GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_storage);
308 [ - + ]: 13116 : if (shdr == NULL)
309 : : {
310 : 0 : elf_issue (N_("could not obtain section header"));
311 : 0 : return false;
312 : : }
313 : 13116 : const char *section_name = elf_strptr (elf, shstrndx, shdr->sh_name);
314 [ - + ]: 13116 : if (section_name == NULL)
315 : : {
316 : 0 : elf_issue(N_("could not obtain section name"));
317 : 0 : return false;
318 : : }
319 [ - + ]: 13116 : if (verbose > 2)
320 : 0 : fprintf (stderr, "debug: section header %s (type %d) found\n",
321 : : section_name, shdr->sh_type);
322 [ + + ]: 13116 : if (shdr->sh_type == SHT_SYMTAB)
323 : : {
324 [ - + ]: 430 : if (verbose > 1)
325 : 0 : fputs ("debug: symtab section found\n", stderr);
326 : 430 : has_symtab = true;
327 : : }
328 : : /* NOBITS and NOTE sections can be in any file. We want to be
329 : : sure there is at least one other allocated section. */
330 : 13116 : if (shdr->sh_type != SHT_NOBITS
331 [ + + ]: 13116 : && shdr->sh_type != SHT_NOTE
332 [ + + ]: 10040 : && (shdr->sh_flags & SHF_ALLOC) != 0)
333 : : {
334 [ - + - - ]: 4706 : if (verbose > 1 && !has_bits_alloc)
335 : 0 : fputs ("debug: allocated (non-nobits/note) section found\n",
336 : : stderr);
337 : 4706 : has_bits_alloc = true;
338 : : }
339 [ + + ]: 13116 : if (startswith (section_name, ".debug_")
340 [ + + ]: 11248 : || startswith (section_name, ".zdebug_"))
341 : : {
342 [ - + - - ]: 2274 : if (verbose > 1 && !has_debug_sections)
343 : 0 : fputs ("debug: .debug_* section found\n", stderr);
344 : 2274 : has_debug_sections = true;
345 : : }
346 [ + + ]: 13116 : if (strcmp (section_name, ".modinfo") == 0)
347 : : {
348 [ - + ]: 80 : if (verbose > 1)
349 : 0 : fputs ("debug: .modinfo section found\n", stderr);
350 : 80 : has_modinfo = true;
351 : : }
352 [ + + ]: 13116 : if (strcmp (section_name, ".gnu.linkonce.this_module") == 0)
353 : : {
354 [ - + ]: 80 : if (verbose > 1)
355 : 0 : fputs ("debug: .gnu.linkonce.this_module section found\n",
356 : : stderr);
357 : 80 : has_gnu_linkonce_this_module = true;
358 : : }
359 : : }
360 : : }
361 : :
362 : : /* Examine the dynamic section. */
363 [ + + ]: 590 : if (has_dynamic)
364 : : {
365 : 252 : Elf_Data *data = elf_getdata_rawchunk (elf, dyn_seg.p_offset,
366 : : dyn_seg.p_filesz,
367 : : ELF_T_DYN);
368 [ + + ]: 252 : if (data != NULL)
369 : 5270 : for (int dyn_idx = 0; ; ++dyn_idx)
370 : 5270 : {
371 : 5510 : GElf_Dyn dyn_storage;
372 : 5510 : GElf_Dyn *dyn = gelf_getdyn (data, dyn_idx, &dyn_storage);
373 [ + + ]: 5510 : if (dyn == NULL)
374 : : break;
375 [ - + ]: 5458 : if (verbose > 2)
376 : 5458 : fprintf (stderr, "debug: dynamic entry %d"
377 : : " with tag %llu found\n",
378 : 0 : dyn_idx, (unsigned long long int) dyn->d_tag);
379 [ + + ]: 5458 : if (dyn->d_tag == DT_SONAME)
380 : 16 : has_soname = true;
381 [ + + + - ]: 5458 : if (dyn->d_tag == DT_FLAGS_1 && (dyn->d_un.d_val & DF_1_PIE))
382 : 32 : has_pie_flag = true;
383 [ + + ]: 5458 : if (dyn->d_tag == DT_DEBUG)
384 : 72 : has_dt_debug = true;
385 [ + + ]: 5458 : if (dyn->d_tag == DT_NULL)
386 : : break;
387 : : }
388 : : }
389 : :
390 [ - + ]: 590 : if (verbose > 0)
391 : : {
392 : 0 : fprintf (stderr, "info: %s: ELF type: %s (0x%x)\n", current_path,
393 : : elf_type_string (elf_type), elf_type);
394 [ # # ]: 0 : if (has_program_load)
395 : 0 : fprintf (stderr, "info: %s: PT_LOAD found\n", current_path);
396 [ # # ]: 0 : if (has_sections)
397 : 0 : fprintf (stderr, "info: %s: has sections\n", current_path);
398 [ # # ]: 0 : if (has_bits_alloc)
399 : 0 : fprintf (stderr, "info: %s: allocated (real) section found\n",
400 : : current_path);
401 [ # # ]: 0 : if (has_program_interpreter)
402 : 0 : fprintf (stderr, "info: %s: program interpreter found\n",
403 : : current_path);
404 [ # # ]: 0 : if (has_dynamic)
405 : 0 : fprintf (stderr, "info: %s: dynamic segment found\n", current_path);
406 [ # # ]: 0 : if (has_soname)
407 : 0 : fprintf (stderr, "info: %s: soname found\n", current_path);
408 [ # # ]: 0 : if (has_pie_flag)
409 : 0 : fprintf (stderr, "info: %s: DF_1_PIE flag found\n", current_path);
410 [ # # ]: 0 : if (has_dt_debug)
411 : 0 : fprintf (stderr, "info: %s: DT_DEBUG found\n", current_path);
412 [ # # ]: 0 : if (has_symtab)
413 : 0 : fprintf (stderr, "info: %s: symbol table found\n", current_path);
414 [ # # ]: 0 : if (has_debug_sections)
415 : 0 : fprintf (stderr, "info: %s: .debug_* section found\n", current_path);
416 [ # # ]: 0 : if (has_modinfo)
417 : 0 : fprintf (stderr, "info: %s: .modinfo section found\n", current_path);
418 [ # # ]: 0 : if (has_gnu_linkonce_this_module)
419 : 0 : fprintf (stderr,
420 : : "info: %s: .gnu.linkonce.this_module section found\n",
421 : : current_path);
422 : : }
423 : :
424 : : return true;
425 : : }
426 : :
427 : : static bool
428 : 600 : is_elf (void)
429 : : {
430 : 600 : return elf_kind (elf) != ELF_K_NONE;
431 : : }
432 : :
433 : : static bool
434 : 600 : is_elf_file (void)
435 : : {
436 : 600 : return elf_kind (elf) == ELF_K_ELF;
437 : : }
438 : :
439 : : static bool
440 : 600 : is_elf_archive (void)
441 : : {
442 : 600 : return elf_kind (elf) == ELF_K_AR;
443 : : }
444 : :
445 : : static bool
446 : 600 : is_core (void)
447 : : {
448 [ + + + + ]: 600 : return elf_kind (elf) == ELF_K_ELF && elf_type == ET_CORE;
449 : : }
450 : :
451 : : /* Return true if the file is a loadable object, which basically means
452 : : it is an ELF file, but not a relocatable object or a core dump
453 : : file. (The kernel and various userspace components can load ET_REL
454 : : files, but we disregard that for our classification purposes.) */
455 : : static bool
456 : 2814 : is_loadable (void)
457 : : {
458 : 2814 : return elf_kind (elf) == ELF_K_ELF
459 [ + + ]: 2774 : && (elf_type == ET_EXEC || elf_type == ET_DYN)
460 [ + - ]: 1582 : && has_program_load
461 [ + + + + : 4396 : && (!has_sections || has_bits_alloc); /* It isn't debug-only. */
+ + ]
462 : : }
463 : :
464 : : /* Return true if the file is an ELF file which has a symbol table or
465 : : .debug_* sections (and thus can be stripped further). */
466 : : static bool
467 : 600 : is_unstripped (void)
468 : : {
469 : 600 : return elf_kind (elf) != ELF_K_NONE
470 [ + + ]: 600 : && (elf_type == ET_REL || elf_type == ET_EXEC || elf_type == ET_DYN)
471 [ + - + + : 1090 : && (has_symtab || has_debug_sections);
+ - ]
472 : : }
473 : :
474 : : /* Return true if the file contains only debuginfo, but no loadable
475 : : program bits. Then it is most likely a separate .debug file, a dwz
476 : : multi-file or a .dwo file. Note that it can still be loadable,
477 : : but in that case the phdrs shouldn't be trusted. */
478 : : static bool
479 : 600 : is_debug_only (void)
480 : : {
481 : 600 : return elf_kind (elf) != ELF_K_NONE
482 [ + + ]: 600 : && (elf_type == ET_REL || elf_type == ET_EXEC || elf_type == ET_DYN)
483 [ + + + + ]: 490 : && (has_debug_sections || has_symtab)
484 [ + - + + ]: 1030 : && !has_bits_alloc;
485 : : }
486 : :
487 : : static bool
488 : 808 : is_shared (void)
489 : : {
490 [ + + ]: 808 : if (!is_loadable ())
491 : : return false;
492 : :
493 : : /* The ELF type is very clear: this is an executable. */
494 [ + + ]: 416 : if (elf_type == ET_EXEC)
495 : : return false;
496 : :
497 : : /* If there is no dynamic section, the file cannot be loaded as a
498 : : shared object. */
499 [ + - ]: 316 : if (!has_dynamic)
500 : : return false;
501 : :
502 : : /* If the object is marked as PIE, it is definitely an executable,
503 : : and not a loadlable shared object. */
504 [ + + ]: 316 : if (has_pie_flag)
505 : : return false;
506 : :
507 : : /* Treat a DT_SONAME tag as a strong indicator that this is a shared
508 : : object. */
509 [ + + ]: 252 : if (has_soname)
510 : : return true;
511 : :
512 : : /* This is probably a PIE program: there is no soname, but a program
513 : : interpreter. In theory, this file could be also a DSO with a
514 : : soname implied by its file name that can be run as a program.
515 : : This situation is impossible to resolve in the general case. */
516 [ + + ]: 220 : if (has_program_interpreter)
517 : : return false;
518 : :
519 : : /* Roland McGrath mentions in
520 : : <https://www.sourceware.org/ml/libc-alpha/2015-03/msg00605.html>,
521 : : that “we defined a PIE as an ET_DYN with a DT_DEBUG”. This
522 : : matches current binutils behavior (version 2.32). DT_DEBUG is
523 : : added if bfd_link_executable returns true or if bfd_link_pic
524 : : returns false, depending on the architectures. However, DT_DEBUG
525 : : is not documented as being specific to executables, therefore use
526 : : it only as a low-priority discriminator. */
527 [ - + ]: 160 : if (has_dt_debug)
528 : 0 : return false;
529 : :
530 : : return true;
531 : : }
532 : :
533 : : static bool
534 : 600 : is_executable (void)
535 : : {
536 [ + + ]: 600 : if (!is_loadable ())
537 : : return false;
538 : :
539 : : /* A loadable object which is not a shared object is treated as an
540 : : executable. */
541 : 208 : return !is_shared ();
542 : : }
543 : :
544 : : /* Like is_executable, but the object can also be a shared library at
545 : : the same time. */
546 : : static bool
547 : 600 : is_program (void)
548 : : {
549 [ + + ]: 600 : if (!is_loadable ())
550 : : return false;
551 : :
552 : : /* The ELF type is very clear: this is an executable. */
553 [ + + ]: 208 : if (elf_type == ET_EXEC)
554 : : return true;
555 : :
556 : : /* If the object is marked as PIE, it is definitely an executable,
557 : : and not a loadlable shared object. */
558 [ + + ]: 158 : if (has_pie_flag)
559 : : return true;
560 : :
561 : : /* This is probably a PIE program. It isn't ET_EXEC, but has a
562 : : program interpreter. In theory, this file could be also a DSO
563 : : with a soname. This situation is impossible to resolve in the
564 : : general case. See is_shared. This is different from
565 : : is_executable. */
566 [ + + ]: 126 : if (has_program_interpreter)
567 : : return true;
568 : :
569 : : /* Roland McGrath mentions in
570 : : <https://www.sourceware.org/ml/libc-alpha/2015-03/msg00605.html>,
571 : : that “we defined a PIE as an ET_DYN with a DT_DEBUG”. This
572 : : matches current binutils behavior (version 2.32). DT_DEBUG is
573 : : added if bfd_link_executable returns true or if bfd_link_pic
574 : : returns false, depending on the architectures. However, DT_DEBUG
575 : : is not documented as being specific to executables, therefore use
576 : : it only as a low-priority discriminator. */
577 [ - + ]: 96 : if (has_dt_debug)
578 : 0 : return true;
579 : :
580 : : return false;
581 : : }
582 : :
583 : : /* Like is_shared but the library could also be an executable. */
584 : : static bool
585 : 600 : is_library (void)
586 : : {
587 : : /* Only ET_DYN can be shared libraries. */
588 [ + + ]: 600 : if (elf_type != ET_DYN)
589 : : return false;
590 : :
591 [ + + ]: 206 : if (!is_loadable ())
592 : : return false;
593 : :
594 : : /* Without a PT_DYNAMIC segment the library cannot be loaded. */
595 [ + - ]: 158 : if (!has_dynamic)
596 : : return false;
597 : :
598 : : /* This really is a (PIE) executable. See is_shared. */
599 [ + + + + ]: 158 : if (has_pie_flag || has_dt_debug)
600 : 62 : return false;
601 : :
602 : : /* It could still (also) be a (PIE) executable, but most likely you
603 : : can dlopen it just fine. */
604 : : return true;
605 : : }
606 : :
607 : : /* Returns true if the file is a linux kernel module (is ET_REL and
608 : : has the two magic sections .modinfo and .gnu.linkonce.this_module). */
609 : : static bool
610 : 600 : is_linux_kernel_module (void)
611 : : {
612 : 600 : return (elf_kind (elf) == ELF_K_ELF
613 [ + + ]: 590 : && elf_type == ET_REL
614 [ + + ]: 198 : && has_modinfo
615 [ + + - + ]: 680 : && has_gnu_linkonce_this_module);
616 : : }
617 : :
618 : : enum classify_requirement { do_not_care, required, forbidden };
619 : :
620 : : enum classify_check
621 : : {
622 : : classify_elf,
623 : : classify_elf_file,
624 : : classify_elf_archive,
625 : : classify_core,
626 : : classify_unstripped,
627 : : classify_executable,
628 : : classify_program,
629 : : classify_shared,
630 : : classify_library,
631 : : classify_linux_kernel_module,
632 : : classify_debug_only,
633 : : classify_loadable,
634 : :
635 : : classify_check_last = classify_loadable
636 : : };
637 : :
638 : : enum
639 : : {
640 : : classify_check_offset = 1000,
641 : : classify_check_not_offset = 2000,
642 : :
643 : : classify_flag_stdin = 3000,
644 : : classify_flag_stdin0,
645 : : classify_flag_no_stdin,
646 : : classify_flag_print,
647 : : classify_flag_print0,
648 : : classify_flag_no_print,
649 : : classify_flag_matching,
650 : : classify_flag_not_matching,
651 : : };
652 : :
653 : : static bool
654 : 842 : classify_check_positive (int key)
655 : : {
656 : 842 : return key >= classify_check_offset
657 : 842 : && key <= classify_check_offset + classify_check_last;
658 : : }
659 : :
660 : : static bool
661 : 782 : classify_check_negative (int key)
662 : : {
663 : 782 : return key >= classify_check_not_offset
664 : 782 : && key <= classify_check_not_offset + classify_check_last;
665 : : }
666 : :
667 : : /* Set by parse_opt. */
668 : : static enum classify_requirement requirements[classify_check_last + 1];
669 : : static enum { no_stdin, do_stdin, do_stdin0 } flag_stdin;
670 : : static enum { no_print, do_print, do_print0 } flag_print;
671 : : static bool flag_print_matching = true;
672 : :
673 : : static error_t
674 : 842 : parse_opt (int key, char *arg __attribute__ ((unused)),
675 : : struct argp_state *state __attribute__ ((unused)))
676 : : {
677 [ + + ]: 842 : if (classify_check_positive (key))
678 : 60 : requirements[key - classify_check_offset] = required;
679 [ + + ]: 782 : else if (classify_check_negative (key))
680 : 74 : requirements[key - classify_check_not_offset] = forbidden;
681 : : else
682 [ - - - - : 708 : switch (key)
- - - + -
- - - + ]
683 : : {
684 : 0 : case 'v':
685 : 0 : ++verbose;
686 : 0 : break;
687 : :
688 : 0 : case 'q':
689 : 0 : --verbose;
690 : 0 : break;
691 : :
692 : 0 : case 'z':
693 : 0 : flag_compressed = true;
694 : 0 : break;
695 : :
696 : 0 : case 'f':
697 : 0 : flag_only_regular_files = true;
698 : 0 : break;
699 : :
700 : 0 : case classify_flag_stdin:
701 : 0 : flag_stdin = do_stdin;
702 : 0 : break;
703 : :
704 : 0 : case classify_flag_stdin0:
705 : 0 : flag_stdin = do_stdin0;
706 : 0 : break;
707 : :
708 : 0 : case classify_flag_no_stdin:
709 : 0 : flag_stdin = no_stdin;
710 : 0 : break;
711 : :
712 : 38 : case classify_flag_print:
713 : 38 : flag_print = do_print;
714 : 38 : break;
715 : :
716 : 0 : case classify_flag_print0:
717 : 0 : flag_print = do_print0;
718 : 0 : break;
719 : :
720 : 0 : case classify_flag_no_print:
721 : 0 : flag_print = no_print;
722 : 0 : break;
723 : :
724 : 0 : case classify_flag_matching:
725 : 0 : flag_print_matching = true;
726 : 0 : break;
727 : :
728 : 0 : case classify_flag_not_matching:
729 : 0 : flag_print_matching = false;
730 : 0 : break;
731 : :
732 : : default:
733 : : return ARGP_ERR_UNKNOWN;
734 : : }
735 : :
736 : : return 0;
737 : : }
738 : :
739 : : /* Perform requested checks against the file at current_path. If
740 : : necessary, sets *STATUS to 1 if checks failed. */
741 : : static void
742 : 600 : process_current_path (int *status)
743 : : {
744 : 600 : bool checks_passed = true;
745 : :
746 [ + - + - ]: 600 : if (open_elf () && run_classify ())
747 : 600 : {
748 : 600 : bool checks[] =
749 : : {
750 : : [classify_elf] = is_elf (),
751 : : [classify_elf_file] = is_elf_file (),
752 : : [classify_elf_archive] = is_elf_archive (),
753 : 600 : [classify_core] = is_core (),
754 : 600 : [classify_unstripped] = is_unstripped (),
755 : 600 : [classify_executable] = is_executable (),
756 : 600 : [classify_program] = is_program (),
757 : 600 : [classify_shared] = is_shared (),
758 : 600 : [classify_library] = is_library (),
759 : 600 : [classify_linux_kernel_module] = is_linux_kernel_module (),
760 : 600 : [classify_debug_only] = is_debug_only (),
761 : 600 : [classify_loadable] = is_loadable (),
762 : : };
763 : :
764 [ - + ]: 600 : if (verbose > 1)
765 : : {
766 [ # # ]: 0 : if (checks[classify_elf])
767 : 0 : fprintf (stderr, "debug: %s: elf\n", current_path);
768 [ # # ]: 0 : if (checks[classify_elf_file])
769 : 0 : fprintf (stderr, "debug: %s: elf_file\n", current_path);
770 [ # # ]: 0 : if (checks[classify_elf_archive])
771 : 0 : fprintf (stderr, "debug: %s: elf_archive\n", current_path);
772 [ # # ]: 0 : if (checks[classify_core])
773 : 0 : fprintf (stderr, "debug: %s: core\n", current_path);
774 [ # # ]: 0 : if (checks[classify_unstripped])
775 : 0 : fprintf (stderr, "debug: %s: unstripped\n", current_path);
776 [ # # ]: 0 : if (checks[classify_executable])
777 : 0 : fprintf (stderr, "debug: %s: executable\n", current_path);
778 [ # # ]: 0 : if (checks[classify_program])
779 : 0 : fprintf (stderr, "debug: %s: program\n", current_path);
780 [ # # ]: 0 : if (checks[classify_shared])
781 : 0 : fprintf (stderr, "debug: %s: shared\n", current_path);
782 [ # # ]: 0 : if (checks[classify_library])
783 : 0 : fprintf (stderr, "debug: %s: library\n", current_path);
784 [ # # ]: 0 : if (checks[classify_linux_kernel_module])
785 : 0 : fprintf (stderr, "debug: %s: linux kernel module\n", current_path);
786 [ # # ]: 0 : if (checks[classify_debug_only])
787 : 0 : fprintf (stderr, "debug: %s: debug-only\n", current_path);
788 [ # # ]: 0 : if (checks[classify_loadable])
789 : 0 : fprintf (stderr, "debug: %s: loadable\n", current_path);
790 : : }
791 : :
792 : 600 : for (enum classify_check check = 0;
793 [ + + ]: 7800 : check <= classify_check_last; ++check)
794 [ + + + ]: 7200 : switch (requirements[check])
795 : : {
796 : 856 : case required:
797 [ - + ]: 856 : if (!checks[check])
798 : 0 : checks_passed = false;
799 : : break;
800 : 344 : case forbidden:
801 [ - + ]: 344 : if (checks[check])
802 : 0 : checks_passed = false;
803 : : break;
804 : : case do_not_care:
805 : : break;
806 : : }
807 : 7200 : }
808 [ # # ]: 0 : else if (file_fd == -1)
809 : : checks_passed = false; /* There is nothing to check, bad file. */
810 : : else
811 : : {
812 : 0 : for (enum classify_check check = 0;
813 [ # # ]: 0 : check <= classify_check_last; ++check)
814 [ # # ]: 0 : if (requirements[check] == required)
815 : 0 : checks_passed = false;
816 : : }
817 : :
818 : 600 : close_elf ();
819 : :
820 [ + - + - ]: 600 : switch (flag_print)
821 : : {
822 : 270 : case do_print:
823 [ + - ]: 270 : if (checks_passed == flag_print_matching)
824 : 270 : puts (current_path);
825 : : break;
826 : 0 : case do_print0:
827 [ # # ]: 0 : if (checks_passed == flag_print_matching)
828 [ # # ]: 0 : if (fwrite (current_path, strlen (current_path) + 1, 1, stdout) < 1)
829 : 0 : issue (errno, N_("writing to standard output"));
830 : : break;
831 : 330 : case no_print:
832 [ - + ]: 330 : if (!checks_passed)
833 : 0 : *status = 1;
834 : : break;
835 : : }
836 : 600 : }
837 : :
838 : : /* Called to process standard input if flag_stdin is not no_stdin. */
839 : : static void
840 : 0 : process_stdin (int *status)
841 : : {
842 : 0 : char delim;
843 [ # # ]: 0 : if (flag_stdin == do_stdin0)
844 : : delim = '\0';
845 : : else
846 : 0 : delim = '\n';
847 : :
848 : 0 : char *buffer = NULL;
849 : 0 : size_t buffer_size = 0;
850 : 0 : while (true)
851 : 0 : {
852 : 0 : ssize_t ret = getdelim (&buffer, &buffer_size, delim, stdin);
853 [ # # ]: 0 : if (ferror (stdin))
854 : : {
855 : 0 : current_path = NULL;
856 : 0 : issue (errno, N_("reading from standard input"));
857 : 0 : break;
858 : : }
859 [ # # ]: 0 : if (feof (stdin))
860 : : break;
861 [ # # ]: 0 : if (ret < 0)
862 : 0 : abort (); /* Cannot happen due to error checks above. */
863 [ # # # # ]: 0 : if (delim != '\0' && ret > 0 && buffer[ret - 1] == '\n')
864 : 0 : buffer[ret - 1] = '\0';
865 : 0 : current_path = buffer;
866 : 0 : process_current_path (status);
867 : : }
868 : :
869 : 0 : free (buffer);
870 : 0 : }
871 : :
872 : : int
873 : 134 : main (int argc, char **argv)
874 : : {
875 : 134 : const struct argp_option options[] =
876 : : {
877 : : { NULL, 0, NULL, OPTION_DOC, N_("Classification options"), 1 },
878 : : { "elf", classify_check_offset + classify_elf, NULL, 0,
879 : : N_("File looks like an ELF object or archive/static library (default)")
880 : : , 1 },
881 : : { "elf-file", classify_check_offset + classify_elf_file, NULL, 0,
882 : : N_("File is an regular ELF object (not an archive/static library)")
883 : : , 1 },
884 : : { "elf-archive", classify_check_offset + classify_elf_archive, NULL, 0,
885 : : N_("File is an ELF archive or static library")
886 : : , 1 },
887 : : { "core", classify_check_offset + classify_core, NULL, 0,
888 : : N_("File is an ELF core dump file")
889 : : , 1 },
890 : : { "unstripped", classify_check_offset + classify_unstripped, NULL, 0,
891 : : N_("File is an ELF file with symbol table or .debug_* sections \
892 : : and can be stripped further"), 1 },
893 : : { "executable", classify_check_offset + classify_executable, NULL, 0,
894 : : N_("File is (primarily) an ELF program executable \
895 : : (not primarily a DSO)"), 1 },
896 : : { "program", classify_check_offset + classify_program, NULL, 0,
897 : : N_("File is an ELF program executable \
898 : : (might also be a DSO)"), 1 },
899 : : { "shared", classify_check_offset + classify_shared, NULL, 0,
900 : : N_("File is (primarily) an ELF shared object (DSO) \
901 : : (not primarily an executable)"), 1 },
902 : : { "library", classify_check_offset + classify_library, NULL, 0,
903 : : N_("File is an ELF shared object (DSO) \
904 : : (might also be an executable)"), 1 },
905 : : { "linux-kernel-module", (classify_check_offset
906 : : + classify_linux_kernel_module), NULL, 0,
907 : : N_("File is a linux kernel module"), 1 },
908 : : { "debug-only", (classify_check_offset + classify_debug_only), NULL, 0,
909 : : N_("File is a debug only ELF file \
910 : : (separate .debug, .dwo or dwz multi-file)"), 1 },
911 : : { "loadable", classify_check_offset + classify_loadable, NULL, 0,
912 : : N_("File is a loadable ELF object (program or shared object)"), 1 },
913 : :
914 : : /* Negated versions of the above. */
915 : : { "not-elf", classify_check_not_offset + classify_elf,
916 : : NULL, OPTION_HIDDEN, NULL, 1 },
917 : : { "not-elf-file", classify_check_not_offset + classify_elf_file,
918 : : NULL, OPTION_HIDDEN, NULL, 1 },
919 : : { "not-elf-archive", classify_check_not_offset + classify_elf_archive,
920 : : NULL, OPTION_HIDDEN, NULL, 1 },
921 : : { "not-core", classify_check_not_offset + classify_core,
922 : : NULL, OPTION_HIDDEN, NULL, 1 },
923 : : { "not-unstripped", classify_check_not_offset + classify_unstripped,
924 : : NULL, OPTION_HIDDEN, NULL, 1 },
925 : : { "not-executable", classify_check_not_offset + classify_executable,
926 : : NULL, OPTION_HIDDEN, NULL, 1 },
927 : : { "not-program", classify_check_not_offset + classify_program,
928 : : NULL, OPTION_HIDDEN, NULL, 1 },
929 : : { "not-shared", classify_check_not_offset + classify_shared,
930 : : NULL, OPTION_HIDDEN, NULL, 1 },
931 : : { "not-library", classify_check_not_offset + classify_library,
932 : : NULL, OPTION_HIDDEN, NULL, 1 },
933 : : { "not-linux-kernel-module", (classify_check_not_offset
934 : : + classify_linux_kernel_module),
935 : : NULL, OPTION_HIDDEN, NULL, 1 },
936 : : { "not-debug-only", (classify_check_not_offset + classify_debug_only),
937 : : NULL, OPTION_HIDDEN, NULL, 1 },
938 : : { "not-loadable", classify_check_not_offset + classify_loadable,
939 : : NULL, OPTION_HIDDEN, NULL, 1 },
940 : :
941 : : { NULL, 0, NULL, OPTION_DOC, N_("Input flags"), 2 },
942 : : { "file", 'f', NULL, 0,
943 : : N_("Only classify regular (not symlink nor special device) files"), 2 },
944 : : { "stdin", classify_flag_stdin, NULL, 0,
945 : : N_("Also read file names to process from standard input, \
946 : : separated by newlines"), 2 },
947 : : { "stdin0", classify_flag_stdin0, NULL, 0,
948 : : N_("Also read file names to process from standard input, \
949 : : separated by ASCII NUL bytes"), 2 },
950 : : { "no-stdin", classify_flag_stdin, NULL, 0,
951 : : N_("Do not read files from standard input (default)"), 2 },
952 : : { "compressed", 'z', NULL, 0,
953 : : N_("Try to open compressed files or embedded (kernel) ELF images"),
954 : : 2 },
955 : :
956 : : { NULL, 0, NULL, OPTION_DOC, N_("Output flags"), 3 },
957 : : { "print", classify_flag_print, NULL, 0,
958 : : N_("Output names of files, separated by newline"), 3 },
959 : : { "print0", classify_flag_print0, NULL, 0,
960 : : N_("Output names of files, separated by ASCII NUL"), 3 },
961 : : { "no-print", classify_flag_no_print, NULL, 0,
962 : : N_("Do not output file names"), 3 },
963 : : { "matching", classify_flag_matching, NULL, 0,
964 : : N_("If printing file names, print matching files (default)"), 3 },
965 : : { "not-matching", classify_flag_not_matching, NULL, 0,
966 : : N_("If printing file names, print files that do not match"), 3 },
967 : :
968 : : { NULL, 0, NULL, OPTION_DOC, N_("Additional flags"), 4 },
969 : : { "verbose", 'v', NULL, 0,
970 : : N_("Output additional information (can be specified multiple times)"), 4 },
971 : : { "quiet", 'q', NULL, 0,
972 : : N_("Suppress some error output (counterpart to --verbose)"), 4 },
973 : : { NULL, 0, NULL, 0, NULL, 0 }
974 : : };
975 : :
976 : 134 : const struct argp argp =
977 : : {
978 : : .options = options,
979 : : .parser = parse_opt,
980 : : .args_doc = N_("FILE..."),
981 : : .doc = N_("\
982 : : Determine the type of an ELF file.\
983 : : \n\n\
984 : : All of the classification options must apply at the same time to a \
985 : : particular file. Classification options can be negated using a \
986 : : \"--not-\" prefix.\
987 : : \n\n\
988 : : Since modern ELF does not clearly distinguish between programs and \
989 : : dynamic shared objects, you should normally use either --executable or \
990 : : --shared to identify the primary purpose of a file. \
991 : : Only one of the --shared and --executable checks can pass for a file.\
992 : : \n\n\
993 : : If you want to know whether an ELF object might a program or a \
994 : : shared library (but could be both), then use --program or --library. \
995 : : Some ELF files will classify as both a program and a library.\
996 : : \n\n\
997 : : If you just want to know whether an ELF file is loadable (as program \
998 : : or library) use --loadable. Note that files that only contain \
999 : : (separate) debug information (--debug-only) are never --loadable (even \
1000 : : though they might contain program headers). Linux kernel modules are \
1001 : : also not --loadable (in the normal sense).\
1002 : : \n\n\
1003 : : Without any of the --print options, the program exits with status 0 \
1004 : : if the requested checks pass for all input files, with 1 if a check \
1005 : : fails for any file, and 2 if there is an environmental issue (such \
1006 : : as a file read error or a memory allocation error).\
1007 : : \n\n\
1008 : : When printing file names, the program exits with status 0 even if \
1009 : : no file names are printed, and exits with status 2 if there is an \
1010 : : environmental issue.\
1011 : : \n\n\
1012 : : On usage error (e.g. a bad option was given), the program exits with \
1013 : : a status code larger than 2.\
1014 : : \n\n\
1015 : : The --quiet or -q option suppresses some error warning output, but \
1016 : : doesn't change the exit status.\
1017 : : ")
1018 : : };
1019 : :
1020 : : /* Require that the file is an ELF file by default. User can
1021 : : disable with --not-elf. */
1022 : 134 : requirements[classify_elf] = required;
1023 : :
1024 : 134 : int remaining;
1025 [ + - ]: 134 : if (argp_parse (&argp, argc, argv, 0, &remaining, NULL) != 0)
1026 : : return 2;
1027 : :
1028 : 134 : elf_version (EV_CURRENT);
1029 : :
1030 : 134 : int status = 0;
1031 : :
1032 [ + + ]: 734 : for (int i = remaining; i < argc; ++i)
1033 : : {
1034 : 600 : current_path = argv[i];
1035 : 600 : process_current_path (&status);
1036 : : }
1037 : :
1038 [ - + ]: 134 : if (flag_stdin != no_stdin)
1039 : 0 : process_stdin (&status);
1040 : :
1041 [ + - ]: 134 : if (issue_found)
1042 : : return 2;
1043 : :
1044 : 134 : return status;
1045 : : }
|