/* 

RUN COFF FILE...

Author: Matthew W. Coan
Date: Fri Sep 20 14:28:09 EDT 2013

*/

#include "coff.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#include <vector>
#include <fstream>
#include <iostream>
#include <cctype>
//#include "bfsream.h"

using namespace std;

#define  YESNO(x) (x?"yes":"no")

void usage(void);

typedef int (*pf_t)();

class Symbol {
public:
   string name;
   size_t address;
   pf_t function;
};

class Reloc {
public:
  size_t address;
  size_t index;
  Reloc() {
    address = 0;
    index = 0;
  }
  Reloc(size_t addr, size_t ind) {
    address = addr;
    index = ind;
  }
};

class Section {
public:
   string name;
   char * buffer;
   size_t size;
   vector< Reloc* > reloc_vec;
   Section() {
     buffer = 0;
     size = 0;
   }
   void add_relocation(Reloc * r) {
     reloc_vec.push_back(r);
   }
};

class Linker {
public:
   typedef vector< Section* > section_vector_type;
   typedef vector< Symbol* > symbol_vector_type;
  
private:
   char * sym_tbl;
   int str_length;
   long section_seek;
   long symptr;
   long nsyms;
   FILE * coffFile;
   unsigned long nrSect;
   unsigned long flags;
   Section * p_section;
   section_vector_type section_vec;
   symbol_vector_type symbol_vec;
   char * entry_point;
   char * buffer;
   char * code;

public:
   Linker() {
      sym_tbl = 0;
      str_length = 0;
      section_seek = 0;
      symptr = 0;
      nsyms = 0;
      coffFile = 0;
      nrSect = 0;
      flags = 0;
      p_section = 0;
      entry_point = 0;
      buffer = 0;
      code = 0;
   }

   ~Linker() {
      if(sym_tbl) {
         delete [] sym_tbl;
      }
      size_t i;
      
      for(i = 0; i < section_vec.size(); i++) {
         for(size_t j = 0; j < section_vec[i]->reloc_vec.size(); j++) {
           Reloc  * p_reloc = section_vec[i]->reloc_vec[j];
           delete p_reloc;
         }
         if(section_vec[i]->buffer != 0) {
            delete [] section_vec[i]->buffer;
         }
         delete section_vec[i];
      }
      section_vec.clear();
      
      /*
      for(i = 0; symbol_vec.size(); i++) {
        delete symbol_vec[i];
      }
      symbol_vec.clear();
      */
   }

   const char * get_symbol_name(const unsigned short t);
   const char * get_storage_class(const unsigned short t);
   const char * get_type(unsigned int code);
   void read_file_header();
   void read_strings();
   void read_symbols();
   void read_aout(FILHDR * section);
   void read_header(SCNHDR * section);
   void read_bss(SCNHDR * section);
   void read_data(SCNHDR * section);
   void read_text(SCNHDR * section);
   void read_lineno(SCNHDR * section);
   void read_sections();
   void link();
   void run();
   int run(int argc, char ** argv);
};

const char * hello;
 

void Linker::link() {
  Reloc * p_reloc = 0;
  hello = "HELLO WORLD\n";
  for(size_t i = 0; i < section_vec.size(); i++) {
    for(size_t j = 0; j < section_vec[i]->reloc_vec.size(); j++) {
       p_reloc = section_vec[i]->reloc_vec[j];
       if(j == 0) {
          (*((size_t*)(code + p_reloc->address))) = (size_t)hello;
       }
       else {
          for(int k = 0; k < symbol_vec.size(); k++) {
            cout << "symbol_vec[" << k << "]=" << symbol_vec[k]->name.c_str() << endl;
          }
          cout << "RELOC=" << p_reloc->index << endl;
          cout << "SIZE=" << symbol_vec.size() << endl;

          if(p_reloc->index < symbol_vec.size()) {
              Symbol * p_symbol = symbol_vec[p_reloc->index];
              (*((size_t*)(code + p_reloc->address))) = (size_t)p_symbol->function;
          }
       }
    }
  }
}

const char *
Linker::get_symbol_name(const unsigned short t)
{
  const char * ret = "UNKNOWN";
  switch(t) {
      case DT_NON:
      ret = "NON";
      break;

     case DT_PTR:
      ret = "PTR";
      break;
      case DT_FCN:
      ret = "FCN";
      break;
      case DT_ARY:
      ret = "ARY";
      break;
      default:
      ret = "UNKNOWN";
  }
  return ret;
}

const char * 
Linker::get_storage_class(const unsigned short t)
{
   const char * ret = "";
   switch(t) {
      case C_EFCN:
      ret = "EFCN";
      break;
      case C_NULL:
      ret = "NULL";
      break;
      case C_AUTO:
      ret = "AUTO";
      break;
      case C_EXT:
      ret = "EXT";
      break;
      case C_STAT:
      ret = "STAT";
      break;
      case C_REG:
      ret = "REG";
      break;
      case C_EXTDEF:
      ret = "EXTDEF";
      break;
      case C_LABEL:
      ret = "LABEL";
      break;
      case C_ULABEL:
      ret = "ULABEL";
      break;
      case C_MOS:
      ret = "MOS";
      break;
      case C_ARG:
      ret = "ARG";
      break;
      case C_STRTAG:
      ret = "STRING";
      break;
      case C_MOU:
      ret = "MOU";
      break;
      case C_UNTAG:
      ret = "UNTAG";
      break;
      case C_TPDEF:
      ret = "TPDEF";
      break;
      case C_USTATIC:
      ret = "USTATIC";
      break;
      case C_ENTAG:
      ret = "ENTAG";
      break;
      case C_MOE: 
      ret = "MOE";
      break;
      case C_REGPARM:
      ret = "REGPARM";
      break;
      case C_FIELD:
      ret = "FIELD";
      break;
      case C_AUTOARG:
      ret = "AUTOARG";
      break;
      case C_LASTENT:
      ret = "LASTENT";
      break;
      case C_BLOCK:
      ret = "BLOCK";
      break;
      case C_FCN:
      ret = "FCN";
      break;
      case C_EOS:
      ret = "EOS";
      break;
      case C_FILE:
      ret = "FILE";
      break;
      case C_LINE:
      ret = "LINE";
      break;
      case C_ALIAS:
      ret = "ALIAS";
      break;
      case C_HIDDEN:
      ret = "HIDDEN";
      break;
      default:
      ret = "UNKNWN";
      break;
   }
   return ret;
}

const char * Linker::get_type(unsigned int code)
{
  const char * ret = "UNKNOWN";
  switch(code) {
      case T_NULL:
      ret = "NULL";
      break;
      case T_VOID:
      ret = "VOID";
      break;
      case T_CHAR:
      ret = "CHAR";
      break;
      case T_SHORT:
      ret = "SHORT";
      break;
      case T_INT:
      ret = "INT";
      break;
      case T_LONG:
      ret = "LONG";
      break;
      case T_FLOAT:
      ret = "FLOAT";
      break;
      case T_DOUBLE:
      ret = "DOUBLE";
      break;
      case T_STRUCT:
      ret = "STRUCT";
      break;
      case T_UNION:
      ret = "UNION";
      break;
      case T_ENUM:
      ret = "ENUM";
      break;
      case T_MOE:
      ret = "MOE";
      break;
      case T_UCHAR:
      ret = "UCHAR";
      break;
      case T_USHORT:
      ret = "USHORT";
      break;
      case T_LNGDBL:
      ret = "LNGDBL";
      break;
      case T_UINT:
      ret = "UINT";
      break;
      case T_ULONG:
      ret = "ULONG";
      break;
  }
  return ret;
}

void
Linker::read_file_header()
{
  FILHDR buf;
  int rc=fread((char*)&buf,1,FILHSZ,coffFile);
  if(rc<FILHSZ)
  {
    cerr << "Error while reading the File(5)\n";
    exit(0);
  }
  if(I386BADMAG(buf))
  {
    cerr << "Error: no coff File\n";
    exit(0);
  }

  nrSect = buf.f_nscns;
  flags = buf.f_flags;
  symptr = buf.f_symptr;
  nsyms = buf.f_nsyms;

  cout << "\n\nFile Header:\n";
  cout << "***********************************************\n";
  cout << "Number of Sections:          " << nrSect << endl;
  cout << "Timestamp:                   " << ctime((time_t*)&buf.f_timdat) << endl;
  cout << "Number of Symtab entries:    " << buf.f_nsyms << endl;
  cout << "Size of optional Header:     " << buf.f_opthdr << endl;

  cout << "\nFlags:\n";
  cout << "-Relocations in File:        " << YESNO(flags&F_RELFLG) << endl;
  cout << "-Executeble File:            " << YESNO(flags&F_EXEC) << endl;
  cout << "-No Line Number Information: " << YESNO(flags&F_LNNO) << endl;
  cout << "-Local Symbols removed:      " << YESNO(flags&F_LSYMS) << endl;
  cout << "-32 Bit Little Endian File:  " << YESNO(flags&0x0100) << endl;

  read_aout(&buf);

  section_seek = FILHSZ + buf.f_opthdr;
}

void
Linker::read_strings()
{
  int strings;

  strings = symptr+sizeof(SYMENT)*nsyms;

  fseek(coffFile, strings, SEEK_SET);

  fread((char*)&str_length,sizeof(long),1,coffFile);

  cout << "str_length=" << str_length << endl;
 
  if(str_length) { 
    str_length -= 4;

    sym_tbl = new char [str_length];

    memset(sym_tbl, 0, str_length);

    fseek(coffFile, strings+4, SEEK_SET);

    fread(sym_tbl, str_length, 1, coffFile);

    char * str_ptr = sym_tbl;
    do {
      cout << str_ptr << endl;
      while(*str_ptr++ != '\0') {
        ;
      }
    }
    while(str_ptr < (sym_tbl+str_length));
  }
}

void
Linker::read_symbols()
{
  SYMENT sym;
  AUXENT aux;
  int i,j,k;
  int count = 1;

  fseek(coffFile, symptr, SEEK_SET);

  for(j = 0; j < nsyms;) {
      memset(&sym,0,sizeof(sym));
      if(j != 0) {
        //fseek(coffFile, -4, SEEK_CUR);
      }
      fread((char*)&sym,sizeof(sym)-2,1,coffFile);
      long off  = ftell(coffFile);
      cout << "sym=" << sym.e_name << endl;
      cout << "val=" << sym.e_value << endl;
      Symbol * p_symbol = new Symbol();
      p_symbol->name = sym.e_name;

      if(strcmp(p_symbol->name.c_str(), "printf") == 0) {
        p_symbol->function = (pf_t)printf;
      }
      symbol_vec.push_back(p_symbol);
      //if(sym.e.e.e_zeroes || sym.e.e.e_offset) {
          cout << "-----------------\n";
          cout << "[";
          for(i = 0; i < NAME_LENGTH; i++) {
            //if(sym.e.e_name[i] > 0x1f && sym.e.e_name[i] < 0x7f) {
              cout << (char)sym.e_name[i];
            //}
          }
          cout << "]" << endl;
          cout << "scnum=" << sym.e_scnum << endl;
          cout << "value=" << sym.e_value << endl;
          cout << "sclass=" << get_storage_class(sym.e_sclass) << endl;
          cout << "type=" << get_type(sym.e_type) << endl;
          cout << "numaux=" << (int)sym.e_numaux << endl;
      //}
      j++;

      //fseek(coffFile, sizeof(aux) * sym.e_numaux, SEEK_CUR);

      cout << "-----------------" << endl;
      cout << "Aux = " << (int)sym.e_numaux << endl;
      cout << "j=" << j << endl;
      if(sym.e_numaux > 0) {
        for(k = 0; k < sym.e_numaux; k++) {
            memset(&aux,0,sizeof(aux));
            fread((char*)&aux,sizeof(aux)-2,1,coffFile);
            cout << "SCLASS=" << (size_t)sym.e_sclass << endl;
            if(sym.e_sclass == C_FILE) {
               cout << "FILE=" << aux.x_file.x_fname << endl;
            }
            else if(sym.e_sclass == C_STAT) {
               cout << "STATIC\n" << endl; 
            }
            //fseek(coffFile, -2, SEEK_CUR);
            j++;
            p_symbol = new Symbol();
            symbol_vec.push_back(p_symbol);
            cout << "j=" << j << endl;
        }
        cout << endl;
        //fseek(coffFile, -2, SEEK_CUR);
      }
      else {
        //fseek(coffFile, -2, SEEK_CUR);
      }
      //fseek(coffFile, off + sizeof(aux) * sym.e_numaux, SEEK_SET);
  }
}

void Linker::read_aout(FILHDR * buf)
{
  int rc;
  GNU_AOUT gnu_aout;
  AOUTHDR aout;
  if(buf->f_opthdr>0)
  {
    if(buf->f_opthdr==sizeof(GNU_AOUT))
    {
      rc=fread((char*)&gnu_aout,1,sizeof(GNU_AOUT),coffFile);
      if(rc<sizeof(GNU_AOUT))
      {
        cerr << "Error while reading the File (1)\n";
        exit(0);
      }
      cerr << "\nOptional GNU-aout Header:\n"
          << "-Entry Point: "
          << ((GNU_AOUT*)buf)->entry << "\n";
    }
    else if(buf->f_opthdr==AOUTSZ)
    {
      rc=fread((char*)&aout,1,AOUTSZ,coffFile);
      if(rc<AOUTSZ)

      {
        cerr << "Error while reading the File(2)\n";
        exit(0);
      }
      cerr << "\nOptional aout Header:\n"
           << "-Entry Point: " << aout.entry << "\n";
    }
    else
    {
      cerr << "\nUnknown optional Header..\n";
    }
  }
}

void Linker::read_header(SCNHDR * buf)
{
    int rc;
    rc=fread((char*)buf,1,SCNHSZ,coffFile);
    if(rc<SCNHSZ)
    {
      cerr << "Error while reading the File(3)\n";
      exit(0);
    }
    flags=buf->s_flags;

    cout << "\n\nSection %s:\n";
    cout << "***********************************************\n";
    cout << "Physical Adress:                 " << buf->s_name << "\n";
    cout << "Virtual Adress:                  " << buf->s_vaddr << "\n";
    cout << "Section Size:                    " << buf->s_size << "\n";
    cout << "Relocation entries:              " << buf->s_nreloc << "\n";
    cout << "Line number entries:             " << buf->s_nlnno << "\n";
    cout << "Relocation Data Address:         " << buf->s_relptr << "\n";

    if(buf->s_relptr) {
      fseek(coffFile, buf->s_relptr - buf->s_size, SEEK_SET);
    }

    cout << "\nFlags:\n";
    cout << "-contains only executable code:  " << YESNO(flags&STYP_TEXT) << endl;
    cout << "-contains only initialized data: " << YESNO(flags&STYP_DATA) << endl;
    cout << "-defines uninitialized data,\n";
    cout << " and has no data stored in the\n";
    cout << " coff file for it:" << YESNO(flags&STYP_BSS) << endl
         << endl;

    if(flags&STYP_TEXT) {
       cout << "CODE*********\n";
    }
    if(flags&STYP_DATA) {
       cout << "DATA*********\n";
    }
    if(flags&STYP_BSS) {
       cout << "BSS**********\n";
    }
}

void Linker::read_bss(SCNHDR * buf)
{
    if(flags&STYP_BSS && buf->s_size != 0) {
       buffer = new char[buf->s_size];
       memset(buffer, 0, buf->s_size);
       fread(buffer,buf->s_size,1,coffFile);
       cout << "********\n";
       
       p_section->name = "bss";
       p_section->buffer = buffer;
       p_section->size = buf->s_size;
       
       //delete [] buffer;
    }
}

void Linker::read_data(SCNHDR * buf)
{
    int j;
    if(flags&STYP_DATA && buf->s_size != 0) {
       buffer = new char[buf->s_size];
       memset(buffer, 0, buf->s_size);
       fread(buffer,buf->s_size,1,coffFile);
       for(j = 0; j < buf->s_size; j++) {
          if(buffer[j] != '\0') {
             cout << buffer[j];
          }
       }  
       cout << "********\n";
       
       p_section->name = "data";
       p_section->buffer = buffer;
       p_section->size = buf->s_size;
       
       //delete [] buffer;
    }
}

void Linker::read_text(SCNHDR * buf)
{
    int j;
    RELOC * relocation;
    if(flags&STYP_TEXT && buf->s_size != 0) {
       buffer = new char[buf->s_size];
       code = buffer;
       p_section->name = "text";
       p_section->buffer = buffer;
       p_section->size = buf->s_size;
       memset(buffer, 0, buf->s_size);
       fread(buffer,buf->s_size,1,coffFile);
       for(j = 1; j <= buf->s_size; j++) {
          cout << (int)buffer[j-1] << ",";
          if((j % 5) == 0) {
             cout << "\n";
          }
       }  
       cout << "\n";
       const char * hello = "Hello, world!\n";
       fseek(coffFile, buf->s_relptr, SEEK_SET);
       cout << "********\n";
       cout << "N-relocations = " << buf->s_nreloc << endl;
       for(j = 0; j < buf->s_nreloc; j++) {
          cout << "********\n";
          RELOC relocation;
          fread((char*)&relocation, sizeof(RELOC), 1, coffFile);
          //p_section->reloc_vec.push_back(new Reloc(relocation.r_vaddr,relocation.r_symndx));
          cout << "Relocation address: " << relocation.r_vaddr << endl;
          cout << "Symbol index: " << relocation.r_symndx << endl;
          cout << "Symbol type: " << get_storage_class(relocation.r_type) << endl;
          //cout << "Symbol temp1: " << relocation.temp1 << endl;
          //cout << "Symbol temp2: " << relocation.temp2 << endl;
          cout << "sizeof = " << sizeof(RELOC) << endl;
          Reloc * p_reloc = new Reloc(); 
          p_reloc->address = relocation.r_vaddr;
          p_reloc->index = relocation.r_symndx; 
          p_section->add_relocation(p_reloc);
       }
       cout << "********\n";
       //delete [] buffer;
    }
}

void Linker::run() {
    pf_t pf = (pf_t)code;
    int rc = pf();
    cout << "result=" << rc << endl;
}

void Linker::read_lineno(SCNHDR * section)
{
  if(section->s_nlnno) {
    cout << "**************LINE NO*****************" << endl;
  }
}

void Linker::read_sections() 
{
  int currSect;
  SCNHDR section;

  for(currSect = 0; currSect < nrSect; currSect++) {
    fseek(coffFile, section_seek, SEEK_SET);

    read_header(&section);

    section_seek += sizeof(SCNHDR);

    p_section = new Section();

    section_vec.push_back(p_section);

    read_bss(&section);

    read_data(&section);

    read_text(&section);

    read_lineno(&section);

    p_section = 0;
  }
}

int Linker::run(int argc, char ** argv) {
  if(!(coffFile=fopen(argv[1],"rb"))) {
     throw "unable to open COFF file for reading...";
  }

  read_file_header();

  read_sections();

  read_strings();

  read_symbols();

  fclose(coffFile);

  link();

  run();

  return 0;
}

int main(int argc, char** argv)
{
  int rc = 0;

  if(argc < 2) {
    usage();
  }

  cout << "syment=" << sizeof(external_syment) << endl;
  cout << "auxent=" << sizeof(external_auxent) << endl;
  cout << "filehdr=" << sizeof(external_filehdr) << endl;
  cout << "scnhdr=" << sizeof(external_scnhdr) << endl;
  cout << "RELOC=" << sizeof(RELOC) << endl;

  try {
     Linker linker;
     rc = linker.run(argc,argv);
  }
  catch(const char * message) {
     cerr << message << endl;
     rc = 1;
  }
  catch(...) {
     cerr << "exception..." << endl;
     rc = 1;   
  }

  exit(rc);

  return rc;
}

void usage()
{
  cout << "Usage: runcoff <coff-file>\n";
  exit(0);
}
