Blame src/elfclassify.c

Packit 032894
/* Classification of ELF files.
Packit 032894
   Copyright (C) 2019 Red Hat, Inc.
Packit 032894
   This file is part of elfutils.
Packit 032894
Packit 032894
   This file is free software; you can redistribute it and/or modify
Packit 032894
   it under the terms of the GNU General Public License as published by
Packit 032894
   the Free Software Foundation; either version 3 of the License, or
Packit 032894
   (at your option) any later version.
Packit 032894
Packit 032894
   elfutils is distributed in the hope that it will be useful, but
Packit 032894
   WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 032894
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 032894
   GNU General Public License for more details.
Packit 032894
Packit 032894
   You should have received a copy of the GNU General Public License
Packit 032894
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit 032894
Packit 032894
#include <config.h>
Packit 032894
Packit 032894
#include <argp.h>
Packit 032894
#include <error.h>
Packit 032894
#include <fcntl.h>
Packit 032894
#include <gelf.h>
Packit 032894
#include <stdbool.h>
Packit 032894
#include <stddef.h>
Packit 032894
#include <stdio.h>
Packit 032894
#include <stdlib.h>
Packit 032894
#include <string.h>
Packit 032894
#include <sys/stat.h>
Packit 032894
#include <unistd.h>
Packit 032894
Packit 032894
#include ELFUTILS_HEADER(elf)
Packit 032894
#include ELFUTILS_HEADER(dwelf)
Packit 032894
#include "printversion.h"
Packit 032894
Packit 032894
/* Name and version of program.  */
Packit 032894
ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
Packit 032894
Packit 032894
/* Bug report address.  */
Packit 032894
ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
Packit 032894
Packit 032894
/* Set by parse_opt.  */
Packit 032894
static int verbose;
Packit 032894
Packit 032894
/* Set by the main function.  */
Packit 032894
static const char *current_path;
Packit 032894
Packit 032894
/* Set by open_file.  */
Packit 032894
static int file_fd = -1;
Packit 032894
Packit 032894
/* Set by issue or elf_issue.  */
Packit 032894
static bool issue_found;
Packit 032894
Packit 032894
/* Non-fatal issue occured while processing the current_path.  */
Packit 032894
static void
Packit 032894
issue (int e, const char *msg)
Packit 032894
{
Packit 032894
  if (verbose >= 0)
Packit 032894
    {
Packit 032894
      if (current_path == NULL)
Packit 032894
	error (0, e, "%s", msg);
Packit 032894
      else
Packit 032894
	error (0, e, "%s '%s'", msg, current_path);
Packit 032894
    }
Packit 032894
  issue_found = true;
Packit 032894
}
Packit 032894
Packit 032894
/* Non-fatal issue occured while processing the current ELF.  */
Packit 032894
static void
Packit 032894
elf_issue (const char *msg)
Packit 032894
{
Packit 032894
  if (verbose >= 0)
Packit 032894
    error (0, 0, "%s: %s: '%s'", msg, elf_errmsg (-1), current_path);
Packit 032894
  issue_found = true;
Packit 032894
}
Packit 032894
Packit 032894
/* Set by parse_opt.  */
Packit 032894
static bool flag_only_regular_files;
Packit 032894
Packit 032894
static bool
Packit 032894
open_file (void)
Packit 032894
{
Packit 032894
  if (verbose > 1)
Packit 032894
    fprintf (stderr, "debug: processing file: %s\n", current_path);
Packit 032894
Packit 032894
  file_fd = open (current_path, O_RDONLY | (flag_only_regular_files
Packit 032894
					    ? O_NOFOLLOW : 0));
Packit 032894
  if (file_fd < 0)
Packit 032894
    {
Packit 032894
      if (!flag_only_regular_files || errno != ELOOP)
Packit 032894
	issue (errno, N_("opening"));
Packit 032894
      return false;
Packit 032894
    }
Packit 032894
Packit 032894
  struct stat st;
Packit 032894
  if (fstat (file_fd, &st) != 0)
Packit 032894
    {
Packit 032894
      issue (errno, N_("reading"));
Packit 032894
      return false;
Packit 032894
    }
Packit 032894
Packit 032894
  /* Don't even bother with directories.  */
Packit 032894
  if (S_ISDIR (st.st_mode)
Packit 032894
      || (flag_only_regular_files && !S_ISREG (st.st_mode)))
Packit 032894
    return false;
Packit 032894
Packit 032894
  return true;
Packit 032894
}
Packit 032894
Packit 032894
static void
Packit 032894
close_file (void)
Packit 032894
{
Packit 032894
  if (file_fd >= 0)
Packit 032894
    {
Packit 032894
      close (file_fd);
Packit 032894
      file_fd = -1;
Packit 032894
    }
Packit 032894
}
Packit 032894
Packit 032894
/* Set by open_elf.  */
Packit 032894
static Elf *elf;
Packit 032894
Packit 032894
/* Set by parse_opt.  */
Packit 032894
static bool flag_compressed;
Packit 032894
Packit 032894
static bool
Packit 032894
open_elf (void)
Packit 032894
{
Packit 032894
  if (!open_file ())
Packit 032894
    {
Packit 032894
      /* Make sure the file descriptor is gone.  */
Packit 032894
      close_file ();
Packit 032894
      return false;
Packit 032894
    }
Packit 032894
Packit 032894
  if (flag_compressed)
Packit 032894
    elf = dwelf_elf_begin (file_fd);
Packit 032894
  else
Packit 032894
    elf = elf_begin (file_fd, ELF_C_READ, NULL);
Packit 032894
Packit 032894
  if (elf == NULL)
Packit 032894
    {
Packit 032894
      elf_issue ("opening ELF file");
Packit 032894
      close_file ();
Packit 032894
      return false;
Packit 032894
    }
Packit 032894
Packit 032894
  return true;
Packit 032894
}
Packit 032894
Packit 032894
static void
Packit 032894
close_elf (void)
Packit 032894
{
Packit 032894
  if (elf != NULL)
Packit 032894
    {
Packit 032894
      elf_end (elf);
Packit 032894
      elf = NULL;
Packit 032894
    }
Packit 032894
Packit 032894
  close_file ();
Packit 032894
}
Packit 032894
Packit 032894
static const char *
Packit 032894
elf_kind_string (int kind)
Packit 032894
{
Packit 032894
  switch (kind)
Packit 032894
    {
Packit 032894
    case ELF_K_NONE:
Packit 032894
      return "ELF_K_NONE";
Packit 032894
    case ELF_K_AR:
Packit 032894
      return "ELF_K_AR";
Packit 032894
    case ELF_K_COFF:
Packit 032894
      return "ELF_K_COFF"; /* libelf doesn't really support this.  */
Packit 032894
    case ELF_K_ELF:
Packit 032894
      return "ELF_K_ELF";
Packit 032894
    default:
Packit 032894
      return "<unknown>";
Packit 032894
    }
Packit 032894
}
Packit 032894
Packit 032894
static const char *
Packit 032894
elf_type_string (int type)
Packit 032894
{
Packit 032894
  switch (type)
Packit 032894
    {
Packit 032894
    case ET_NONE:
Packit 032894
      return "ET_NONE";
Packit 032894
    case ET_REL:
Packit 032894
      return "ET_REL";
Packit 032894
    case ET_EXEC:
Packit 032894
      return "ET_EXEC";
Packit 032894
    case ET_DYN:
Packit 032894
      return "ET_DYN";
Packit 032894
    case ET_CORE:
Packit 032894
      return "ET_CORE";
Packit 032894
    default:
Packit 032894
      return "<unknown>";
Packit 032894
    }
Packit 032894
}
Packit 032894
Packit 032894
static int elf_type;
Packit 032894
static bool has_program_load;
Packit 032894
static bool has_sections;
Packit 032894
static bool has_bits_alloc;
Packit 032894
static bool has_program_interpreter;
Packit 032894
static bool has_dynamic;
Packit 032894
static bool has_soname;
Packit 032894
static bool has_pie_flag;
Packit 032894
static bool has_dt_debug;
Packit 032894
static bool has_symtab;
Packit 032894
static bool has_debug_sections;
Packit 032894
static bool has_modinfo;
Packit 032894
static bool has_gnu_linkonce_this_module;
Packit 032894
Packit 032894
static bool
Packit 032894
run_classify (void)
Packit 032894
{
Packit 032894
  /* Reset to unanalyzed default.  */
Packit 032894
  elf_type = 0;
Packit 032894
  has_program_load = false;
Packit 032894
  has_sections = false;
Packit 032894
  has_bits_alloc = false;
Packit 032894
  has_program_interpreter = false;
Packit 032894
  has_dynamic = false;
Packit 032894
  has_soname = false;
Packit 032894
  has_pie_flag = false;
Packit 032894
  has_dt_debug = false;
Packit 032894
  has_symtab = false;
Packit 032894
  has_debug_sections = false;
Packit 032894
  has_modinfo = false;
Packit 032894
  has_gnu_linkonce_this_module = false;
Packit 032894
Packit 032894
  int kind = elf_kind (elf);
Packit 032894
  if (verbose > 0)
Packit 032894
    fprintf (stderr, "info: %s: ELF kind: %s (0x%x)\n", current_path,
Packit 032894
	     elf_kind_string (kind), kind);
Packit 032894
  if (kind != ELF_K_ELF)
Packit 032894
    return true;
Packit 032894
Packit 032894
  GElf_Ehdr ehdr_storage;
Packit 032894
  GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_storage);
Packit 032894
  if (ehdr == NULL)
Packit 032894
    {
Packit 032894
      elf_issue (N_("ELF header"));
Packit 032894
      return false;
Packit 032894
    }
Packit 032894
  elf_type = ehdr->e_type;
Packit 032894
Packit 032894
  /* Examine program headers.  */
Packit 032894
  GElf_Phdr dyn_seg = { .p_type = 0 };
Packit 032894
  {
Packit 032894
    size_t nphdrs;
Packit 032894
    if (elf_getphdrnum (elf, &nphdrs) != 0)
Packit 032894
      {
Packit 032894
	elf_issue (N_("program headers"));
Packit 032894
	return false;
Packit 032894
      }
Packit 032894
    for (size_t phdr_idx = 0; phdr_idx < nphdrs; ++phdr_idx)
Packit 032894
      {
Packit 032894
	GElf_Phdr phdr_storage;
Packit 032894
	GElf_Phdr *phdr = gelf_getphdr (elf, phdr_idx, &phdr_storage);
Packit 032894
	if (phdr == NULL)
Packit 032894
	  {
Packit 032894
	    elf_issue (N_("program header"));
Packit 032894
	    return false;
Packit 032894
	  }
Packit 032894
	if (phdr->p_type == PT_DYNAMIC)
Packit 032894
	  {
Packit 032894
	    dyn_seg = *phdr;
Packit 032894
	    has_dynamic = true;
Packit 032894
	  }
Packit 032894
	if (phdr->p_type == PT_INTERP)
Packit 032894
	  has_program_interpreter = true;
Packit 032894
	if (phdr->p_type == PT_LOAD)
Packit 032894
	  has_program_load = true;
Packit 032894
      }
Packit 032894
  }
Packit 032894
Packit 032894
  /* Do we have sections?  */
Packit 032894
  {
Packit 032894
    size_t nshdrs;
Packit 032894
    if (elf_getshdrnum (elf, &nshdrs) != 0)
Packit 032894
      {
Packit 032894
	elf_issue (N_("section headers"));
Packit 032894
	return false;
Packit 032894
      }
Packit 032894
    if (nshdrs > 0)
Packit 032894
      has_sections = true;
Packit 032894
  }
Packit 032894
Packit 032894
  {
Packit 032894
    size_t shstrndx;
Packit 032894
    if (unlikely (elf_getshdrstrndx (elf, &shstrndx) < 0))
Packit 032894
      {
Packit 032894
	elf_issue (N_("section header string table index"));
Packit 032894
	return false;
Packit 032894
      }
Packit 032894
Packit 032894
    Elf_Scn *scn = NULL;
Packit 032894
    while (true)
Packit 032894
      {
Packit 032894
        scn = elf_nextscn (elf, scn);
Packit 032894
        if (scn == NULL)
Packit 032894
          break;
Packit 032894
        GElf_Shdr shdr_storage;
Packit 032894
        GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_storage);
Packit 032894
        if (shdr == NULL)
Packit 032894
	  {
Packit 032894
            elf_issue (N_("could not obtain section header"));
Packit 032894
	    return false;
Packit 032894
	  }
Packit 032894
        const char *section_name = elf_strptr (elf, shstrndx, shdr->sh_name);
Packit 032894
        if (section_name == NULL)
Packit 032894
	  {
Packit 032894
            elf_issue(N_("could not obtain section name"));
Packit 032894
	    return false;
Packit 032894
	  }
Packit 032894
        if (verbose > 2)
Packit 032894
          fprintf (stderr, "debug: section header %s (type %d) found\n",
Packit 032894
                   section_name, shdr->sh_type);
Packit 032894
        if (shdr->sh_type == SHT_SYMTAB)
Packit 032894
          {
Packit 032894
            if (verbose > 1)
Packit 032894
              fputs ("debug: symtab section found\n", stderr);
Packit 032894
            has_symtab = true;
Packit 032894
          }
Packit 032894
	/* NOBITS and NOTE sections can be in any file.  We want to be
Packit 032894
	   sure there is at least one other allocated section.  */
Packit 032894
	if (shdr->sh_type != SHT_NOBITS
Packit 032894
	    && shdr->sh_type != SHT_NOTE
Packit 032894
	    && (shdr->sh_flags & SHF_ALLOC) != 0)
Packit 032894
	  {
Packit 032894
	    if (verbose > 1 && !has_bits_alloc)
Packit 032894
	      fputs ("debug: allocated (non-nobits/note) section found\n",
Packit 032894
		     stderr);
Packit 032894
	    has_bits_alloc = true;
Packit 032894
	  }
Packit 032894
        const char *debug_prefix = ".debug_";
Packit 032894
        const char *zdebug_prefix = ".zdebug_";
Packit 032894
        if (strncmp (section_name, debug_prefix, strlen (debug_prefix)) == 0
Packit 032894
	    || strncmp (section_name, zdebug_prefix,
Packit 032894
			strlen (zdebug_prefix)) == 0)
Packit 032894
          {
Packit 032894
            if (verbose > 1 && !has_debug_sections)
Packit 032894
              fputs ("debug: .debug_* section found\n", stderr);
Packit 032894
            has_debug_sections = true;
Packit 032894
          }
Packit 032894
	if (strcmp (section_name, ".modinfo") == 0)
Packit 032894
	  {
Packit 032894
	    if (verbose > 1)
Packit 032894
	      fputs ("debug: .modinfo section found\n", stderr);
Packit 032894
	    has_modinfo = true;
Packit 032894
	  }
Packit 032894
	if (strcmp (section_name, ".gnu.linkonce.this_module") == 0)
Packit 032894
	  {
Packit 032894
	    if (verbose > 1)
Packit 032894
	      fputs ("debug: .gnu.linkonce.this_module section found\n",
Packit 032894
		     stderr);
Packit 032894
	    has_gnu_linkonce_this_module = true;
Packit 032894
	  }
Packit 032894
      }
Packit 032894
  }
Packit 032894
Packit 032894
  /* Examine the dynamic section.  */
Packit 032894
  if (has_dynamic)
Packit 032894
    {
Packit 032894
      Elf_Data *data = elf_getdata_rawchunk (elf, dyn_seg.p_offset,
Packit 032894
					     dyn_seg.p_filesz,
Packit 032894
					     ELF_T_DYN);
Packit 032894
      if (data != NULL)
Packit 032894
	for (int dyn_idx = 0; ; ++dyn_idx)
Packit 032894
	  {
Packit 032894
	    GElf_Dyn dyn_storage;
Packit 032894
	    GElf_Dyn *dyn = gelf_getdyn (data, dyn_idx, &dyn_storage);
Packit 032894
	    if (dyn == NULL)
Packit 032894
	      break;
Packit 032894
	    if (verbose > 2)
Packit 032894
	      fprintf (stderr, "debug: dynamic entry %d"
Packit 032894
		       " with tag %llu found\n",
Packit 032894
		       dyn_idx, (unsigned long long int) dyn->d_tag);
Packit 032894
	    if (dyn->d_tag == DT_SONAME)
Packit 032894
	      has_soname = true;
Packit 032894
	    if (dyn->d_tag == DT_FLAGS_1 && (dyn->d_un.d_val & DF_1_PIE))
Packit 032894
	      has_pie_flag = true;
Packit 032894
	    if (dyn->d_tag == DT_DEBUG)
Packit 032894
	      has_dt_debug = true;
Packit 032894
	    if (dyn->d_tag == DT_NULL)
Packit 032894
	      break;
Packit 032894
	  }
Packit 032894
    }
Packit 032894
Packit 032894
  if (verbose > 0)
Packit 032894
    {
Packit 032894
      fprintf (stderr, "info: %s: ELF type: %s (0x%x)\n", current_path,
Packit 032894
	       elf_type_string (elf_type), elf_type);
Packit 032894
      if (has_program_load)
Packit 032894
        fprintf (stderr, "info: %s: PT_LOAD found\n", current_path);
Packit 032894
      if (has_sections)
Packit 032894
	fprintf (stderr, "info: %s: has sections\n", current_path);
Packit 032894
      if (has_bits_alloc)
Packit 032894
	fprintf (stderr, "info: %s: allocated (real) section found\n",
Packit 032894
		 current_path);
Packit 032894
      if (has_program_interpreter)
Packit 032894
        fprintf (stderr, "info: %s: program interpreter found\n",
Packit 032894
                 current_path);
Packit 032894
      if (has_dynamic)
Packit 032894
        fprintf (stderr, "info: %s: dynamic segment found\n", current_path);
Packit 032894
      if (has_soname)
Packit 032894
        fprintf (stderr, "info: %s: soname found\n", current_path);
Packit 032894
      if (has_pie_flag)
Packit 032894
        fprintf (stderr, "info: %s: DF_1_PIE flag found\n", current_path);
Packit 032894
      if (has_dt_debug)
Packit 032894
        fprintf (stderr, "info: %s: DT_DEBUG found\n", current_path);
Packit 032894
      if (has_symtab)
Packit 032894
        fprintf (stderr, "info: %s: symbol table found\n", current_path);
Packit 032894
      if (has_debug_sections)
Packit 032894
        fprintf (stderr, "info: %s: .debug_* section found\n", current_path);
Packit 032894
      if (has_modinfo)
Packit 032894
        fprintf (stderr, "info: %s: .modinfo section found\n", current_path);
Packit 032894
      if (has_gnu_linkonce_this_module)
Packit 032894
        fprintf (stderr,
Packit 032894
		 "info: %s: .gnu.linkonce.this_module section found\n",
Packit 032894
		 current_path);
Packit 032894
    }
Packit 032894
Packit 032894
  return true;
Packit 032894
}
Packit 032894
Packit 032894
static bool
Packit 032894
is_elf (void)
Packit 032894
{
Packit 032894
  return elf_kind (elf) != ELF_K_NONE;
Packit 032894
}
Packit 032894
Packit 032894
static bool
Packit 032894
is_elf_file (void)
Packit 032894
{
Packit 032894
  return elf_kind (elf) == ELF_K_ELF;
Packit 032894
}
Packit 032894
Packit 032894
static bool
Packit 032894
is_elf_archive (void)
Packit 032894
{
Packit 032894
  return elf_kind (elf) == ELF_K_AR;
Packit 032894
}
Packit 032894
Packit 032894
static bool
Packit 032894
is_core (void)
Packit 032894
{
Packit 032894
  return elf_kind (elf) == ELF_K_ELF && elf_type == ET_CORE;
Packit 032894
}
Packit 032894
Packit 032894
/* Return true if the file is a loadable object, which basically means
Packit 032894
   it is an ELF file, but not a relocatable object or a core dump
Packit 032894
   file.  (The kernel and various userspace components can load ET_REL
Packit 032894
   files, but we disregard that for our classification purposes.)  */
Packit 032894
static bool
Packit 032894
is_loadable (void)
Packit 032894
{
Packit 032894
  return elf_kind (elf) == ELF_K_ELF
Packit 032894
    && (elf_type == ET_EXEC || elf_type == ET_DYN)
Packit 032894
    && has_program_load
Packit 032894
    && (!has_sections || has_bits_alloc); /* It isn't debug-only.  */
Packit 032894
}
Packit 032894
Packit 032894
/* Return true if the file is an ELF file which has a symbol table or
Packit 032894
   .debug_* sections (and thus can be stripped futher).  */
Packit 032894
static bool
Packit 032894
is_unstripped (void)
Packit 032894
{
Packit 032894
  return elf_kind (elf) != ELF_K_NONE
Packit 032894
    && (elf_type == ET_REL || elf_type == ET_EXEC || elf_type == ET_DYN)
Packit 032894
    && (has_symtab || has_debug_sections);
Packit 032894
}
Packit 032894
Packit 032894
/* Return true if the file contains only debuginfo, but no loadable
Packit 032894
   program bits.  Then it is most likely a separate .debug file, a dwz
Packit 032894
   multi-file or a .dwo file.  Note that it can still be loadable,
Packit 032894
   but in that case the phdrs shouldn't be trusted.  */
Packit 032894
static bool
Packit 032894
is_debug_only (void)
Packit 032894
{
Packit 032894
  return elf_kind (elf) != ELF_K_NONE
Packit 032894
    && (elf_type == ET_REL || elf_type == ET_EXEC || elf_type == ET_DYN)
Packit 032894
    && (has_debug_sections || has_symtab)
Packit 032894
    && !has_bits_alloc;
Packit 032894
}
Packit 032894
Packit 032894
static bool
Packit 032894
is_shared (void)
Packit 032894
{
Packit 032894
  if (!is_loadable ())
Packit 032894
    return false;
Packit 032894
Packit 032894
  /* The ELF type is very clear: this is an executable.  */
Packit 032894
  if (elf_type == ET_EXEC)
Packit 032894
    return false;
Packit 032894
Packit 032894
  /* If there is no dynamic section, the file cannot be loaded as a
Packit 032894
     shared object.  */
Packit 032894
  if (!has_dynamic)
Packit 032894
    return false;
Packit 032894
Packit 032894
  /* If the object is marked as PIE, it is definitely an executable,
Packit 032894
     and not a loadlable shared object.  */
Packit 032894
  if (has_pie_flag)
Packit 032894
    return false;
Packit 032894
Packit 032894
  /* Treat a DT_SONAME tag as a strong indicator that this is a shared
Packit 032894
     object.  */
Packit 032894
  if (has_soname)
Packit 032894
    return true;
Packit 032894
Packit 032894
  /* This is probably a PIE program: there is no soname, but a program
Packit 032894
     interpreter.  In theory, this file could be also a DSO with a
Packit 032894
     soname implied by its file name that can be run as a program.
Packit 032894
     This situation is impossible to resolve in the general case. */
Packit 032894
  if (has_program_interpreter)
Packit 032894
    return false;
Packit 032894
Packit 032894
  /* Roland McGrath mentions in
Packit 032894
     <https://www.sourceware.org/ml/libc-alpha/2015-03/msg00605.html>,
Packit 032894
     that “we defined a PIE as an ET_DYN with a DT_DEBUG”.  This
Packit 032894
     matches current binutils behavior (version 2.32).  DT_DEBUG is
Packit 032894
     added if bfd_link_executable returns true or if bfd_link_pic
Packit 032894
     returns false, depending on the architectures.  However, DT_DEBUG
Packit 032894
     is not documented as being specific to executables, therefore use
Packit 032894
     it only as a low-priority discriminator.  */
Packit 032894
  if (has_dt_debug)
Packit 032894
    return false;
Packit 032894
Packit 032894
  return true;
Packit 032894
}
Packit 032894
Packit 032894
static bool
Packit 032894
is_executable (void)
Packit 032894
{
Packit 032894
  if (!is_loadable ())
Packit 032894
    return false;
Packit 032894
Packit 032894
  /* A loadable object which is not a shared object is treated as an
Packit 032894
     executable.  */
Packit 032894
  return !is_shared ();
Packit 032894
}
Packit 032894
Packit 032894
/* Like is_executable, but the object can also be a shared library at
Packit 032894
   the same time.  */
Packit 032894
static bool
Packit 032894
is_program (void)
Packit 032894
{
Packit 032894
  if (!is_loadable ())
Packit 032894
    return false;
Packit 032894
Packit 032894
  /* The ELF type is very clear: this is an executable.  */
Packit 032894
  if (elf_type == ET_EXEC)
Packit 032894
    return true;
Packit 032894
Packit 032894
  /* If the object is marked as PIE, it is definitely an executable,
Packit 032894
     and not a loadlable shared object.  */
Packit 032894
  if (has_pie_flag)
Packit 032894
    return true;
Packit 032894
Packit 032894
  /* This is probably a PIE program. It isn't ET_EXEC, but has a
Packit 032894
     program interpreter. In theory, this file could be also a DSO
Packit 032894
     with a soname. This situation is impossible to resolve in the
Packit 032894
     general case. See is_shared. This is different from
Packit 032894
     is_executable.  */
Packit 032894
  if (has_program_interpreter)
Packit 032894
    return true;
Packit 032894
Packit 032894
  /* Roland McGrath mentions in
Packit 032894
     <https://www.sourceware.org/ml/libc-alpha/2015-03/msg00605.html>,
Packit 032894
     that “we defined a PIE as an ET_DYN with a DT_DEBUG”.  This
Packit 032894
     matches current binutils behavior (version 2.32).  DT_DEBUG is
Packit 032894
     added if bfd_link_executable returns true or if bfd_link_pic
Packit 032894
     returns false, depending on the architectures.  However, DT_DEBUG
Packit 032894
     is not documented as being specific to executables, therefore use
Packit 032894
     it only as a low-priority discriminator.  */
Packit 032894
  if (has_dt_debug)
Packit 032894
    return true;
Packit 032894
Packit 032894
  return false;
Packit 032894
}
Packit 032894
Packit 032894
/* Like is_shared but the library could also be an executable.  */
Packit 032894
static bool
Packit 032894
is_library  (void)
Packit 032894
{
Packit 032894
  /* Only ET_DYN can be shared libraries.  */
Packit 032894
  if (elf_type != ET_DYN)
Packit 032894
    return false;
Packit 032894
Packit 032894
  if (!is_loadable ())
Packit 032894
    return false;
Packit 032894
Packit 032894
  /* Without a PT_DYNAMIC segment the library cannot be loaded.  */
Packit 032894
  if (!has_dynamic)
Packit 032894
    return false;
Packit 032894
Packit 032894
  /* This really is a (PIE) executable.  See is_shared.  */
Packit 032894
  if (has_pie_flag || has_dt_debug)
Packit 032894
    return false;
Packit 032894
Packit 032894
  /* It could still (also) be a (PIE) executable, but most likely you
Packit 032894
     can dlopen it just fine.  */
Packit 032894
  return true;
Packit 032894
}
Packit 032894
Packit 032894
/* Returns true if the file is a linux kernel module (is ET_REL and
Packit 032894
   has the two magic sections .modinfo and .gnu.linkonce.this_module).  */
Packit 032894
static bool
Packit 032894
is_linux_kernel_module (void)
Packit 032894
{
Packit 032894
  return (elf_kind (elf) == ELF_K_ELF
Packit 032894
	  && elf_type == ET_REL
Packit 032894
	  && has_modinfo
Packit 032894
	  && has_gnu_linkonce_this_module);
Packit 032894
}
Packit 032894
Packit 032894
enum classify_requirement { do_not_care, required, forbidden };
Packit 032894
Packit 032894
enum classify_check
Packit 032894
{
Packit 032894
  classify_elf,
Packit 032894
  classify_elf_file,
Packit 032894
  classify_elf_archive,
Packit 032894
  classify_core,
Packit 032894
  classify_unstripped,
Packit 032894
  classify_executable,
Packit 032894
  classify_program,
Packit 032894
  classify_shared,
Packit 032894
  classify_library,
Packit 032894
  classify_linux_kernel_module,
Packit 032894
  classify_debug_only,
Packit 032894
  classify_loadable,
Packit 032894
Packit 032894
  classify_check_last = classify_loadable
Packit 032894
};
Packit 032894
Packit 032894
enum
Packit 032894
{
Packit 032894
  classify_check_offset = 1000,
Packit 032894
  classify_check_not_offset = 2000,
Packit 032894
Packit 032894
  classify_flag_stdin = 3000,
Packit 032894
  classify_flag_stdin0,
Packit 032894
  classify_flag_no_stdin,
Packit 032894
  classify_flag_print,
Packit 032894
  classify_flag_print0,
Packit 032894
  classify_flag_no_print,
Packit 032894
  classify_flag_matching,
Packit 032894
  classify_flag_not_matching,
Packit 032894
};
Packit 032894
Packit 032894
static bool
Packit 032894
classify_check_positive (int key)
Packit 032894
{
Packit 032894
  return key >= classify_check_offset
Packit 032894
    && key <= classify_check_offset + classify_check_last;
Packit 032894
}
Packit 032894
Packit 032894
static bool
Packit 032894
classify_check_negative (int key)
Packit 032894
{
Packit 032894
  return key >= classify_check_not_offset
Packit 032894
    && key <= classify_check_not_offset + classify_check_last;
Packit 032894
}
Packit 032894
Packit 032894
/* Set by parse_opt.  */
Packit 032894
static enum classify_requirement requirements[classify_check_last + 1];
Packit 032894
static enum { no_stdin, do_stdin, do_stdin0 } flag_stdin;
Packit 032894
static enum { no_print, do_print, do_print0 } flag_print;
Packit 032894
static bool flag_print_matching = true;
Packit 032894
Packit 032894
static error_t
Packit 032894
parse_opt (int key, char *arg __attribute__ ((unused)),
Packit 032894
           struct argp_state *state __attribute__ ((unused)))
Packit 032894
{
Packit 032894
  if (classify_check_positive (key))
Packit 032894
    requirements[key - classify_check_offset] = required;
Packit 032894
  else if (classify_check_negative (key))
Packit 032894
    requirements[key - classify_check_not_offset] = forbidden;
Packit 032894
  else
Packit 032894
    switch (key)
Packit 032894
      {
Packit 032894
      case 'v':
Packit 032894
        ++verbose;
Packit 032894
        break;
Packit 032894
Packit 032894
      case 'q':
Packit 032894
	--verbose;
Packit 032894
	break;
Packit 032894
Packit 032894
      case 'z':
Packit 032894
	flag_compressed = true;
Packit 032894
	break;
Packit 032894
Packit 032894
      case 'f':
Packit 032894
	flag_only_regular_files = true;
Packit 032894
	break;
Packit 032894
Packit 032894
      case classify_flag_stdin:
Packit 032894
        flag_stdin = do_stdin;
Packit 032894
        break;
Packit 032894
Packit 032894
      case classify_flag_stdin0:
Packit 032894
        flag_stdin = do_stdin0;
Packit 032894
        break;
Packit 032894
Packit 032894
      case classify_flag_no_stdin:
Packit 032894
        flag_stdin = no_stdin;
Packit 032894
        break;
Packit 032894
Packit 032894
      case classify_flag_print:
Packit 032894
        flag_print = do_print;
Packit 032894
        break;
Packit 032894
Packit 032894
      case classify_flag_print0:
Packit 032894
        flag_print = do_print0;
Packit 032894
        break;
Packit 032894
Packit 032894
      case classify_flag_no_print:
Packit 032894
        flag_print = no_print;
Packit 032894
        break;
Packit 032894
Packit 032894
      case classify_flag_matching:
Packit 032894
        flag_print_matching = true;
Packit 032894
        break;
Packit 032894
Packit 032894
      case classify_flag_not_matching:
Packit 032894
        flag_print_matching = false;
Packit 032894
        break;
Packit 032894
Packit 032894
      default:
Packit 032894
        return ARGP_ERR_UNKNOWN;
Packit 032894
      }
Packit 032894
Packit 032894
  return 0;
Packit 032894
}
Packit 032894
Packit 032894
/* Perform requested checks against the file at current_path.  If
Packit 032894
   necessary, sets *STATUS to 1 if checks failed.  */
Packit 032894
static void
Packit 032894
process_current_path (int *status)
Packit 032894
{
Packit 032894
  bool checks_passed = true;
Packit 032894
Packit 032894
  if (open_elf () && run_classify ())
Packit 032894
    {
Packit 032894
      bool checks[] =
Packit 032894
        {
Packit 032894
	 [classify_elf] = is_elf (),
Packit 032894
	 [classify_elf_file] = is_elf_file (),
Packit 032894
	 [classify_elf_archive] = is_elf_archive (),
Packit 032894
	 [classify_core] = is_core (),
Packit 032894
	 [classify_unstripped] = is_unstripped (),
Packit 032894
	 [classify_executable] = is_executable (),
Packit 032894
	 [classify_program] = is_program (),
Packit 032894
	 [classify_shared] = is_shared (),
Packit 032894
	 [classify_library] = is_library (),
Packit 032894
	 [classify_linux_kernel_module] = is_linux_kernel_module (),
Packit 032894
	 [classify_debug_only] = is_debug_only (),
Packit 032894
	 [classify_loadable] = is_loadable (),
Packit 032894
	};
Packit 032894
Packit 032894
      if (verbose > 1)
Packit 032894
        {
Packit 032894
	  if (checks[classify_elf])
Packit 032894
	    fprintf (stderr, "debug: %s: elf\n", current_path);
Packit 032894
	  if (checks[classify_elf_file])
Packit 032894
	    fprintf (stderr, "debug: %s: elf_file\n", current_path);
Packit 032894
	  if (checks[classify_elf_archive])
Packit 032894
	    fprintf (stderr, "debug: %s: elf_archive\n", current_path);
Packit 032894
	  if (checks[classify_core])
Packit 032894
	    fprintf (stderr, "debug: %s: core\n", current_path);
Packit 032894
          if (checks[classify_unstripped])
Packit 032894
            fprintf (stderr, "debug: %s: unstripped\n", current_path);
Packit 032894
          if (checks[classify_executable])
Packit 032894
            fprintf (stderr, "debug: %s: executable\n", current_path);
Packit 032894
          if (checks[classify_program])
Packit 032894
            fprintf (stderr, "debug: %s: program\n", current_path);
Packit 032894
          if (checks[classify_shared])
Packit 032894
            fprintf (stderr, "debug: %s: shared\n", current_path);
Packit 032894
          if (checks[classify_library])
Packit 032894
            fprintf (stderr, "debug: %s: library\n", current_path);
Packit 032894
	  if (checks[classify_linux_kernel_module])
Packit 032894
	    fprintf (stderr, "debug: %s: linux kernel module\n", current_path);
Packit 032894
	  if (checks[classify_debug_only])
Packit 032894
	    fprintf (stderr, "debug: %s: debug-only\n", current_path);
Packit 032894
          if (checks[classify_loadable])
Packit 032894
            fprintf (stderr, "debug: %s: loadable\n", current_path);
Packit 032894
        }
Packit 032894
Packit 032894
      for (enum classify_check check = 0;
Packit 032894
           check <= classify_check_last; ++check)
Packit 032894
        switch (requirements[check])
Packit 032894
          {
Packit 032894
          case required:
Packit 032894
            if (!checks[check])
Packit 032894
              checks_passed = false;
Packit 032894
            break;
Packit 032894
          case forbidden:
Packit 032894
            if (checks[check])
Packit 032894
              checks_passed = false;
Packit 032894
            break;
Packit 032894
          case do_not_care:
Packit 032894
            break;
Packit 032894
          }
Packit 032894
    }
Packit 032894
  else if (file_fd == -1)
Packit 032894
    checks_passed = false; /* There is nothing to check, bad file.  */
Packit 032894
  else
Packit 032894
    {
Packit 032894
      for (enum classify_check check = 0;
Packit 032894
           check <= classify_check_last; ++check)
Packit 032894
        if (requirements[check] == required)
Packit 032894
          checks_passed = false;
Packit 032894
    }
Packit 032894
Packit 032894
  close_elf ();
Packit 032894
Packit 032894
  switch (flag_print)
Packit 032894
    {
Packit 032894
    case do_print:
Packit 032894
      if (checks_passed == flag_print_matching)
Packit 032894
        puts (current_path);
Packit 032894
      break;
Packit 032894
    case do_print0:
Packit 032894
      if (checks_passed == flag_print_matching)
Packit 032894
        fwrite (current_path, strlen (current_path) + 1, 1, stdout);
Packit 032894
      break;
Packit 032894
    case no_print:
Packit 032894
      if (!checks_passed)
Packit 032894
        *status = 1;
Packit 032894
      break;
Packit 032894
    }
Packit 032894
}
Packit 032894
Packit 032894
/* Called to process standard input if flag_stdin is not no_stdin.  */
Packit 032894
static void
Packit 032894
process_stdin (int *status)
Packit 032894
{
Packit 032894
  char delim;
Packit 032894
  if (flag_stdin == do_stdin0)
Packit 032894
    delim = '\0';
Packit 032894
  else
Packit 032894
    delim = '\n';
Packit 032894
Packit 032894
  char *buffer = NULL;
Packit 032894
  size_t buffer_size = 0;
Packit 032894
  while (true)
Packit 032894
    {
Packit 032894
      ssize_t ret = getdelim (&buffer, &buffer_size, delim, stdin);
Packit 032894
      if (ferror (stdin))
Packit 032894
	{
Packit 032894
	  current_path = NULL;
Packit 032894
	  issue (errno, N_("reading from standard input"));
Packit 032894
	  break;
Packit 032894
	}
Packit 032894
      if (feof (stdin))
Packit 032894
        break;
Packit 032894
      if (ret < 0)
Packit 032894
        abort ();           /* Cannot happen due to error checks above.  */
Packit 032894
      if (delim != '\0' && ret > 0 && buffer[ret - 1] == '\n')
Packit 032894
        buffer[ret - 1] = '\0';
Packit 032894
      current_path = buffer;
Packit 032894
      process_current_path (status);
Packit 032894
    }
Packit 032894
Packit 032894
  free (buffer);
Packit 032894
}
Packit 032894
Packit 032894
int
Packit 032894
main (int argc, char **argv)
Packit 032894
{
Packit 032894
  const struct argp_option options[] =
Packit 032894
    {
Packit 032894
      { NULL, 0, NULL, OPTION_DOC, N_("Classification options"), 1 },
Packit 032894
      { "elf", classify_check_offset + classify_elf, NULL, 0,
Packit 032894
        N_("File looks like an ELF object or archive/static library (default)")
Packit 032894
	, 1 },
Packit 032894
      { "elf-file", classify_check_offset + classify_elf_file, NULL, 0,
Packit 032894
        N_("File is an regular ELF object (not an archive/static library)")
Packit 032894
	, 1 },
Packit 032894
      { "elf-archive", classify_check_offset + classify_elf_archive, NULL, 0,
Packit 032894
        N_("File is an ELF archive or static library")
Packit 032894
	, 1 },
Packit 032894
      { "core", classify_check_offset + classify_core, NULL, 0,
Packit 032894
        N_("File is an ELF core dump file")
Packit 032894
	, 1 },
Packit 032894
      { "unstripped", classify_check_offset + classify_unstripped, NULL, 0,
Packit 032894
        N_("File is an ELF file with symbol table or .debug_* sections \
Packit 032894
and can be stripped further"), 1 },
Packit 032894
      { "executable", classify_check_offset + classify_executable, NULL, 0,
Packit 032894
        N_("File is (primarily) an ELF program executable \
Packit 032894
(not primarily a DSO)"), 1 },
Packit 032894
      { "program", classify_check_offset + classify_program, NULL, 0,
Packit 032894
        N_("File is an ELF program executable \
Packit 032894
(might also be a DSO)"), 1 },
Packit 032894
      { "shared", classify_check_offset + classify_shared, NULL, 0,
Packit 032894
        N_("File is (primarily) an ELF shared object (DSO) \
Packit 032894
(not primarily an executable)"), 1 },
Packit 032894
      { "library", classify_check_offset + classify_library, NULL, 0,
Packit 032894
        N_("File is an ELF shared object (DSO) \
Packit 032894
(might also be an executable)"), 1 },
Packit 032894
      { "linux-kernel-module", (classify_check_offset
Packit 032894
				+ classify_linux_kernel_module), NULL, 0,
Packit 032894
        N_("File is a linux kernel module"), 1 },
Packit 032894
      { "debug-only", (classify_check_offset + classify_debug_only), NULL, 0,
Packit 032894
        N_("File is a debug only ELF file \
Packit 032894
(separate .debug, .dwo or dwz multi-file)"), 1 },
Packit 032894
      { "loadable", classify_check_offset + classify_loadable, NULL, 0,
Packit 032894
        N_("File is a loadable ELF object (program or shared object)"), 1 },
Packit 032894
Packit 032894
      /* Negated versions of the above.  */
Packit 032894
      { "not-elf", classify_check_not_offset + classify_elf,
Packit 032894
        NULL, OPTION_HIDDEN, NULL, 1 },
Packit 032894
      { "not-elf-file", classify_check_not_offset + classify_elf_file,
Packit 032894
        NULL, OPTION_HIDDEN, NULL, 1 },
Packit 032894
      { "not-elf-archive", classify_check_not_offset + classify_elf_archive,
Packit 032894
        NULL, OPTION_HIDDEN, NULL, 1 },
Packit 032894
      { "not-core", classify_check_not_offset + classify_core,
Packit 032894
        NULL, OPTION_HIDDEN, NULL, 1 },
Packit 032894
      { "not-unstripped", classify_check_not_offset + classify_unstripped,
Packit 032894
        NULL, OPTION_HIDDEN, NULL, 1 },
Packit 032894
      { "not-executable", classify_check_not_offset + classify_executable,
Packit 032894
        NULL, OPTION_HIDDEN, NULL, 1 },
Packit 032894
      { "not-program", classify_check_not_offset + classify_program,
Packit 032894
        NULL, OPTION_HIDDEN, NULL, 1 },
Packit 032894
      { "not-shared", classify_check_not_offset + classify_shared,
Packit 032894
        NULL, OPTION_HIDDEN, NULL, 1 },
Packit 032894
      { "not-library", classify_check_not_offset + classify_library,
Packit 032894
        NULL, OPTION_HIDDEN, NULL, 1 },
Packit 032894
      { "not-linux-kernel-module", (classify_check_not_offset
Packit 032894
				    + classify_linux_kernel_module),
Packit 032894
        NULL, OPTION_HIDDEN, NULL, 1 },
Packit 032894
      { "not-debug-only", (classify_check_not_offset + classify_debug_only),
Packit 032894
        NULL, OPTION_HIDDEN, NULL, 1 },
Packit 032894
      { "not-loadable", classify_check_not_offset + classify_loadable,
Packit 032894
        NULL, OPTION_HIDDEN, NULL, 1 },
Packit 032894
Packit 032894
      { NULL, 0, NULL, OPTION_DOC, N_("Input flags"), 2 },
Packit 032894
      { "file", 'f', NULL, 0,
Packit 032894
        N_("Only classify regular (not symlink nor special device) files"), 2 },
Packit 032894
      { "stdin", classify_flag_stdin, NULL, 0,
Packit 032894
        N_("Also read file names to process from standard input, \
Packit 032894
separated by newlines"), 2 },
Packit 032894
      { "stdin0", classify_flag_stdin0, NULL, 0,
Packit 032894
        N_("Also read file names to process from standard input, \
Packit 032894
separated by ASCII NUL bytes"), 2 },
Packit 032894
      { "no-stdin", classify_flag_stdin, NULL, 0,
Packit 032894
        N_("Do not read files from standard input (default)"), 2 },
Packit 032894
      { "compressed", 'z', NULL, 0,
Packit 032894
	N_("Try to open compressed files or embedded (kernel) ELF images"),
Packit 032894
	2 },
Packit 032894
Packit 032894
      { NULL, 0, NULL, OPTION_DOC, N_("Output flags"), 3 },
Packit 032894
      { "print", classify_flag_print, NULL, 0,
Packit 032894
        N_("Output names of files, separated by newline"), 3 },
Packit 032894
      { "print0", classify_flag_print0, NULL, 0,
Packit 032894
        N_("Output names of files, separated by ASCII NUL"), 3 },
Packit 032894
      { "no-print", classify_flag_no_print, NULL, 0,
Packit 032894
        N_("Do not output file names"), 3 },
Packit 032894
      { "matching", classify_flag_matching, NULL, 0,
Packit 032894
        N_("If printing file names, print matching files (default)"), 3 },
Packit 032894
      { "not-matching", classify_flag_not_matching, NULL, 0,
Packit 032894
        N_("If printing file names, print files that do not match"), 3 },
Packit 032894
Packit 032894
      { NULL, 0, NULL, OPTION_DOC, N_("Additional flags"), 4 },
Packit 032894
      { "verbose", 'v', NULL, 0,
Packit 032894
        N_("Output additional information (can be specified multiple times)"), 4 },
Packit 032894
      { "quiet", 'q', NULL, 0,
Packit 032894
        N_("Suppress some error output (counterpart to --verbose)"), 4 },
Packit 032894
      { NULL, 0, NULL, 0, NULL, 0 }
Packit 032894
    };
Packit 032894
Packit 032894
  const struct argp argp =
Packit 032894
    {
Packit 032894
      .options = options,
Packit 032894
      .parser = parse_opt,
Packit 032894
      .args_doc = N_("FILE..."),
Packit 032894
      .doc = N_("\
Packit 032894
Determine the type of an ELF file.\
Packit 032894
\n\n\
Packit 032894
All of the classification options must apply at the same time to a \
Packit 032894
particular file.  Classification options can be negated using a \
Packit 032894
\"--not-\" prefix.\
Packit 032894
\n\n\
Packit 032894
Since modern ELF does not clearly distinguish between programs and \
Packit 032894
dynamic shared objects, you should normally use either --executable or \
Packit 032894
--shared to identify the primary purpose of a file.  \
Packit 032894
Only one of the --shared and --executable checks can pass for a file.\
Packit 032894
\n\n\
Packit 032894
If you want to know whether an ELF object might a program or a \
Packit 032894
shared library (but could be both), then use --program or --library. \
Packit 032894
Some ELF files will classify as both a program and a library.\
Packit 032894
\n\n\
Packit 032894
If you just want to know whether an ELF file is loadable (as program \
Packit 032894
or library) use --loadable.  Note that files that only contain \
Packit 032894
(separate) debug information (--debug-only) are never --loadable (even \
Packit 032894
though they might contain program headers).  Linux kernel modules are \
Packit 032894
also not --loadable (in the normal sense).\
Packit 032894
\n\n\
Packit 032894
Without any of the --print options, the program exits with status 0 \
Packit 032894
if the requested checks pass for all input files, with 1 if a check \
Packit 032894
fails for any file, and 2 if there is an environmental issue (such \
Packit 032894
as a file read error or a memory allocation error).\
Packit 032894
\n\n\
Packit 032894
When printing file names, the program exits with status 0 even if \
Packit 032894
no file names are printed, and exits with status 2 if there is an \
Packit 032894
environmental issue.\
Packit 032894
\n\n\
Packit 032894
On usage error (e.g. a bad option was given), the program exits with \
Packit 032894
a status code larger than 2.\
Packit 032894
\n\n\
Packit 032894
The --quiet or -q option suppresses some error warning output, but \
Packit 032894
doesn't change the exit status.\
Packit 032894
")
Packit 032894
    };
Packit 032894
Packit 032894
  /* Require that the file is an ELF file by default.  User can
Packit 032894
     disable with --not-elf.  */
Packit 032894
  requirements[classify_elf] = required;
Packit 032894
Packit 032894
  int remaining;
Packit 032894
  if (argp_parse (&argp, argc, argv, 0, &remaining, NULL) != 0)
Packit 032894
    return 2;
Packit 032894
Packit 032894
  elf_version (EV_CURRENT);
Packit 032894
Packit 032894
  int status = 0;
Packit 032894
Packit 032894
  for (int i = remaining; i < argc; ++i)
Packit 032894
    {
Packit 032894
      current_path = argv[i];
Packit 032894
      process_current_path (&status);
Packit 032894
    }
Packit 032894
Packit 032894
  if (flag_stdin != no_stdin)
Packit 032894
    process_stdin (&status);
Packit 032894
Packit 032894
  if (issue_found)
Packit 032894
    return 2;
Packit 032894
Packit 032894
  return status;
Packit 032894
}