LCOV - code coverage report
Current view: top level - src - elfclassify.c (source / functions) Hit Total Coverage
Test: elfutils-0.178 Lines: 218 404 54.0 %
Date: 2019-11-26 23:55:16 Functions: 15 18 83.3 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13