/*
 * Copyright 2003 Ned Ludd <solar@gentoo.org>
 * Distributed under the terms of the GNU General Public License v2
 * $Header: $
 *
 * On Gentoo Linux we need a simple way to detect if an ELF ehdr is of
 * type ET_DYN, we have a PT_INTERP phdr and also contains a symbol for main()
 * When these three conditions are true as we should be strip safe.
 *
 * Date:
 *	20030908
 * Compile:
 *	gcc -o is_elf is_elf.c
 * Note:
 *	This program has no visible output.
 *	It's intended use is from shell scripts via return values 
 * Function listing:
 *	readelf(), check_elf_header(), main()
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <dlfcn.h>

#ifdef __linux__
#include <elf.h>
#include <asm/elf.h>
#else
#include <sys/elf_common.h>
#endif


#ifndef ELF_CLASS
#error "UNABLE TO DETECT ELF_CLASS"
#endif

#if (ELF_CLASS == ELFCLASS32)
#define Elf_Ehdr        Elf32_Ehdr
#define Elf_Phdr        Elf32_Phdr
#define Elf_Shdr	Elf32_Shdr
#define Elf_Dyn		Elf32_Dyn
#endif

#if (ELF_CLASS == ELFCLASS64)
#define Elf_Ehdr        Elf64_Ehdr
#define Elf_Phdr        Elf64_Phdr
#define Elf_Shdr	Elf64_Shdr
#define Elf_Dyn		Elf64_Dyn
#endif

struct Elf_File {
   Elf_Ehdr *ehdr;
   Elf_Phdr *phdr;
   Elf_Shdr *shdr;
   Elf_Dyn *dyn;
   char *data;
   int len;
};

typedef struct Elf_File elfobj;

#define IS_ETDYN(elf) ( \
	(elf->ehdr->e_type == ET_DYN) && \
		(elf->ehdr->e_ident[EI_CLASS] == ELFCLASS32 || \
			elf->ehdr->e_ident[EI_CLASS] == ELFCLASS64) \
	)

/* Read an elf file into memory and map headers */
elfobj *readelf(char *filename)
{
   struct stat st;
   elfobj *elf;
   int fd;

   if (stat(filename, &st) == -1)
      return NULL;

   if ((fd = open(filename, O_RDONLY)) == -1)
      return NULL;

   if (st.st_size <= 0)
      return NULL;

   elf = NULL;
   elf = (void *) malloc(sizeof(elfobj));
   if (elf == NULL)
      return NULL;
   elf->len = st.st_size;
   elf->data =
       (char *) mmap(0, elf->len, PROT_READ | PROT_WRITE,
		     MAP_PRIVATE | MAP_DENYWRITE, fd, 0);

   if (elf->data == (char *) MAP_FAILED) {
      free(elf);
      return NULL;
   }

   elf->ehdr = (void *) elf->data;
   elf->phdr = (void *) (elf->data + elf->ehdr->e_phoff);
   elf->shdr = (void *) (elf->data + elf->ehdr->e_shoff);

   /* elf->fd = fd; */
   /* do we want to keep the fd open? */
   close(fd);
   return elf;
}

/* check the elf header */
int check_elf_header(Elf_Ehdr const *const ehdr)
{
   if (!ehdr || strncmp((void *) ehdr, ELFMAG, SELFMAG) != 0 ||
       (ehdr->e_ident[EI_CLASS] != ELFCLASS32
	&& ehdr->e_ident[EI_CLASS] != ELFCLASS64)
       || ehdr->e_ident[EI_VERSION] != EV_CURRENT) {
      return 1;
   }
   return 0;
}

int main(int argc, char **argv)
{
   int i = 0;
   elfobj *elf = NULL;
   int exit_val = 1;
   static char *fname;
   int elf_type = -1;

   if (argc < 2)
      return exit_val;

   fname = argv[1];

   if (argc > 2) {
      elf_type = atoi(argv[1]);
      fname = argv[2];
   }

   if ((elf = readelf(fname)) == NULL)
      return exit_val;

   if (!check_elf_header(elf->ehdr)) {
      if (elf_type != (-1)) {
	 if (elf_type == elf->ehdr->e_type)
	    puts(fname);
      } else {
	 puts(fname);
      }
      exit_val = 0;
   }
   munmap(elf->data, elf->len);
   free(elf);
   return exit_val;
}
