/*

    DLLEXP.C - exports dll entries in a list

    Copyright (C) 1995-1996
	Rainer Schnitker, Heeper Str. 283, 33607 Bielefeld
	email: rainer@mathematik.uni-bielefeld.de

    All rights reserved

*/

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "ntbind.h"
#include "makelib.h"
#include "port.h"

DWORD text_begin, text_end;
DWORD rdata_begin, rdata_end;
DWORD base;

static void put_list(char *name, int val, DWORD address)
{
    if (!n_export) {
        export_list = (EXPORT_LIST *) malloc(sizeof(EXPORT_LIST));
    }

    export_list = realloc(export_list, sizeof(EXPORT_LIST) * ++n_export);
    if (export_list == NULL) {
        static char bad[] = "Memory out for export list";
        write (1, bad, sizeof(bad));
        exit(1);
    }

    export_list[n_export - 1].ord = val;
    export_list[n_export - 1].name = name;
    export_list[n_export - 1].address = address;
    export_list[n_export - 1].flags =
        (address == 0
         || (address >= text_begin && address <= text_end)
         || (address >= rdata_begin && address <= rdata_end)
        ) ? 0 : 1;
}


static void *xmalloc (size_t n)
{
    long *q;
    q = malloc (n);
    if (q == NULL) {
        write(1 , "Out of memory!!", 15);
        exit(1);
    }
    return q;
}

static void error(char *err)
{
    puts(err);
    exit(1);
}

static void build_entries(int pehandle, DWORD headoff)
{
    static FILEHDR         file_hdr;
    static NTOPTHDR        ntopt_hdr;
    static SCNHDR          scnd_hdr;
    char *export_data;
    EXP_DESC *exportDir;
    int i;
    int delta;
    char *filename;
    DWORD *functions;
    WORD *ordinals;
    char **name;
    DWORD pos;

    lseek(pehandle, headoff, SEEK_SET);
    read(pehandle, &file_hdr, sizeof(FILEHDR));
    read(pehandle, &ntopt_hdr, sizeof(NTOPTHDR));
    pos = tell(pehandle);

    text_begin = 0xffffffff;
    text_end = 0;

    for (i = 0; i < file_hdr.f_nscns; i++) {
        read(pehandle, &scnd_hdr, sizeof(SCNHDR));

        if (scnd_hdr.s_flags & 0x20000000)  /* execute section */
        {
            if (scnd_hdr.s_vaddr < text_begin)
                text_begin = scnd_hdr.s_vaddr;

            if (scnd_hdr.s_vaddr + scnd_hdr.s_size > text_end)
                text_end = scnd_hdr.s_vaddr + scnd_hdr.s_size;
        }
        else if (strcmp(scnd_hdr.s_name, ".rdata") == 0) {
            /* MSVC: some exported functions are here */

            rdata_begin = scnd_hdr.s_vaddr;
            rdata_end = scnd_hdr.s_vaddr + scnd_hdr.s_size;
        }
    }

    lseek(pehandle, pos, SEEK_SET);

    for (i = 0; i < file_hdr.f_nscns; i++) {
        read(pehandle, &scnd_hdr, sizeof(SCNHDR));

        if (strcmp(scnd_hdr.s_name, ".edata") == 0)
            break;
    }

    if (i >= file_hdr.f_nscns) {
        DWORD offset, find;

        if (!ntopt_hdr.DataDirectory[DIR_EXPORT].Size)
            error ("No Export section");

        /* export section is in the .text/.rdata segment (msvc4) */

        lseek(pehandle, pos, SEEK_SET);

        find = ntopt_hdr.DataDirectory[DIR_EXPORT].VirtualAddress;

        for (i = 0; i < file_hdr.f_nscns; i++) {
            read(pehandle, &scnd_hdr, sizeof(SCNHDR));
            if (scnd_hdr.s_vaddr <= find && scnd_hdr.s_vaddr + scnd_hdr.s_size >= find)
                break;
        }

        if (i >= file_hdr.f_nscns) 
            error ("No Export and Text section");

        offset = ntopt_hdr.DataDirectory[DIR_EXPORT].VirtualAddress
                    - scnd_hdr.s_vaddr;

        scnd_hdr.s_size = ntopt_hdr.DataDirectory[DIR_EXPORT].Size;
        scnd_hdr.s_vaddr = ntopt_hdr.DataDirectory[DIR_EXPORT].VirtualAddress;
        scnd_hdr.s_scnptr += offset;
    }

    export_data = xmalloc(scnd_hdr.s_size);
    lseek(pehandle, scnd_hdr.s_scnptr, SEEK_SET);
    read(pehandle, export_data, scnd_hdr.s_size);
    exportDir = (EXP_DESC *) export_data;

    base = ntopt_hdr.ImageBase;
    delta = (int)scnd_hdr.s_vaddr - (int)export_data;
    filename = (char *) ((DWORD)exportDir->Name - delta);
    functions = (DWORD *)((DWORD)exportDir->AddressOfFunctions - delta);
    ordinals = (WORD *)((DWORD)exportDir->AddressOfNameOrdinals - delta);
    name = (char **)((DWORD)exportDir->AddressOfNames - delta);

    i = strlen(filename) - 1;
    while (i >= 0) {
        if (filename[i] == '.') {
            filename[i] = '_';
            break;
        }
        --i;
    }

    for (i=0;i<exportDir->NumberOfNames;i++)
    {
        put_list(*name - delta, (int)(*ordinals + exportDir->Base),
                   functions[*ordinals]);
        name++;
        ordinals++;
    }
}

static int skip_exe_hdr(int filehandle, DWORD * headoff)
{
    struct exe_hdr exehdr;

    read(filehandle, &exehdr, sizeof(struct exe_hdr));

    if (exehdr.signatur == 0x5a4d) {	/* falls exe-kopf */
        DWORD new_off;

        lseek(filehandle, 0x3C, SEEK_SET);
        read(filehandle, &new_off, sizeof(long));

        if (new_off) {
            DWORD pe_magic;
            lseek(filehandle, new_off, SEEK_SET);
            read(filehandle, &pe_magic, sizeof(pe_magic));
            if (pe_magic == 0x4550) {
                *headoff = new_off + 4;
                return 0;
            }
        }
    }   /* exe files */
    *headoff = 0;
    return -1;
}

int get_export_list(char *filename)
{
    int fhandle;
    DWORD headoff;

    if ((fhandle = open(filename, O_RDONLY | O_BINARY)) == -1) {
	perror("open");
        exit(1);
    }

    if (skip_exe_hdr(fhandle, &headoff) < 0)
        error("no a valid Win32 file");

    build_entries(fhandle, headoff);

    close(fhandle);

    return 0;
}
