/*
 * ldd - print shared library dependencies
 *
 * usage: ldd [-vVdr] prog ...
 *        -v: print ldd version
 *        -V: print ld.so version
 *	  -d: Perform relocations and report any missing functions. (ELF only).
 *	  -r: Perform relocations for both data objects and functions, and
 *	      report any missing objects (ELF only).
 *        prog ...: programs to check
 *
 * Copyright 1993, David Engel
 *
 * This program may be used for any purpose as long as this
 * copyright notice is kept.
 */

#include <stdio.h>
#include <stdlib.h>
#include <asm/string.h>
#include <getopt.h>
#include <unistd.h>
#include <a.out.h>
#include <errno.h>
#include <sys/wait.h>
#include <linux/elf.h>
#include "../config.h"

/* see if prog is a binary file */
int is_bin(char *argv0, char *prog)
{
    int res = 0;
    FILE *file;
    struct exec exec;

    /* must be able to open it for reading */
    if ((file = fopen(prog, "rb")) == NULL)
	fprintf(stderr, "%s: can't open %s (%s)\n", argv0, prog,
		strerror(errno));
    else
    {
	/* then see if it's Z, Q or OMAGIC */
	if (fread(&exec, sizeof exec, 1, file) < 1)
	    fprintf(stderr, "%s: can't read header from %s\n", argv0, prog);
	else if (N_MAGIC(exec) != ZMAGIC && N_MAGIC(exec) != QMAGIC &&
                 N_MAGIC(exec) != OMAGIC)
	{
	    struct elfhdr elf_hdr;
	    
 	    rewind(file);
	    fread(&elf_hdr, sizeof elf_hdr, 1, file);
	    if (elf_hdr.e_ident[0] != 0x7f ||
		strncmp(elf_hdr.e_ident+1, "ELF", 3) != 0)
		fprintf(stderr, "%s: %s is not a.out or ELF\n", argv0, prog);
	    else
	    {
		struct elf_phdr phdr;
		int i;

		/* Check its an ELF exectuable and not an object. We assume it
		   is static currently until we locate a .dynamic section */
		res = elf_hdr.e_type == ET_EXEC ? 3 : 0;

		/* determine if it is dynamic ELF */
		for (i=0; i<elf_hdr.e_phnum; i++)
		{
		    fread(&phdr, sizeof phdr, 1, file);
		    if (phdr.p_type == PT_DYNAMIC)
		    {
			res = 2;
			break;
		    }
		}
	    }
	}
	else
	    res = 1; /* looks good */

	fclose(file);
    }
    return res;
}

int main(int argc, char **argv, char **envp)
{
    int i;
    int vprinted = 0;
    int resolve = 0;
    int bind = 0;
    int bintype;

    /* this must be volatile to work with -O, GCC bug? */
    volatile loadptr loader = (loadptr)LDSO_ADDR;
  
    while ((i = getopt(argc, argv, "drvV")) != EOF)
	switch (i)
	{
	case 'v':
	    /* print our version number */
	    printf("%s: version %s\n", argv[0], VERSION);
	    vprinted = 1;
	    break;
	case 'd':
	    bind = 1;
	    break;
	case 'r':
	    resolve = 1;
	    break;
	case 'V':
	    /* print the version number of ld.so */
	    if (uselib(LDSO_IMAGE))
	    {
		fprintf(stderr, "%s: can't load dynamic linker %s (%s)\n",
			argv[0], LDSO_IMAGE, strerror(errno));
		exit(1);
	    }
	    loader(FUNC_VERS, NULL);
	    vprinted = 1;
	    break;
	}

    /* must specify programs if -v or -V not used */
    if (optind >= argc && !vprinted)
    {
	printf("usage: %s [-vVdr] prog ...\n", argv[0]);
	exit(0);
    }

    /* print the dependencies for each program */
    for (i = optind; i < argc; i++)
    {
	pid_t pid;
	char buff[1024];

	/* make sure it's a binary file */
	if (!(bintype = is_bin(argv[0], argv[i])))
	    continue;

	/* print the program name if doing more than one */
	if (optind < argc-1)
	{
	    printf("%s:\n", argv[i]);
	    fflush(stdout);
	}

	if (bintype == 3)
	{
	    printf("\tstatically linked (ELF)\n");
	    continue;
	}

	if (bintype == 2)
	{	    
	    /* For ELF binaries we do this instead */
	    putenv("LD_TRACE_LOADED_OBJECTS=1");
	    if (resolve || bind)
	        putenv("LD_BIND_NOW=1");
	    if (resolve)
	        putenv("LD_WARN=1");
	}

	/* save the name in the enviroment, ld.so may need it */
	sprintf(buff, "%s=%s", LDD_ARGV0, argv[i]);
	putenv(buff);

	/* now fork and exec prog with argc = 0 */
	if ((pid = fork()) < 0)
	{
	    fprintf(stderr, "%s: can't fork (%s)\n", argv[0], strerror(errno));
	    exit(1);
	}
	else if (pid == 0)
	{
	    if (bintype == 2)
		execl(argv[i], argv[i], NULL);
	    else
		execl(argv[i], NULL);
	    fprintf(stderr, "%s: can't execute %s (%s)\n", argv[0], argv[i],
		    strerror(errno));
	    exit(1);
	}
	else
	{
	    /* then wait for it to complete */
	    if (waitpid(pid, NULL, 0) != pid)
	    {
	        fprintf(stderr, "%s: error waiting for %s (%s)\n", argv[0], 
			argv[i], strerror(errno));
	    }
	}
    }

    exit(0);
}
