/*
 * 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
 *	20031007
 * Compile:
 *	gcc -o isetdyn isetdyn.c -ldl
 * 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()
 */

/* 
 * <pappy-> # readelf -a filename | grep "program interpreter"
 * <pappy-> # readelf -a filename | grep "__libc_start_main"
 * <pappy-> # readelf -a filename | grep "Type:
 */

#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;
   void *handle = NULL;
   int exit_val = 1;

   if (argc < 2)
      return exit_val;

   if ((elf = readelf(argv[1])) == NULL)
      return exit_val;

   if (!check_elf_header(elf->ehdr))
      if (IS_ETDYN(elf))
	 for (i = 0; i < elf->ehdr->e_phnum; i++)
	    if (elf->phdr[i].p_type == PT_INTERP)
	       if ((handle = dlopen(argv[1], RTLD_NOW)) != NULL)
		  if ((dlsym(handle, "main")) != 0x0) {
		     puts(argv[1]);
		     exit_val = 0;
		  }

   if (handle != NULL)
      dlclose(handle);

   munmap(elf->data, elf->len);
   free(elf);
   return exit_val;
}
