
/*
 * C-startup and loader for BlackBox 
 * Implemented as the StdLoader. 
 */


#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>

/* the exact size (in bytes) of the executable part of the file. */
/* this constant needs to be updated everytime a change is made to this file */
#define exeSize 21399


/* fixup types */
#define absolute 100
#define relative 101
#define copy 102
#define table 103
#define tableend 104
#define deref 105
#define halfword 106

/* import types */
#define mConst 0x1
#define mTyp 0x2
#define mVar 0x3
#define mProc 0x4
#define mExported 4

#define any 1000000

#define init 0x10000

/* set to printf to debug and donothing to avoid debugging */ 
#define dprintf donothing

typedef void (*BodyProc)();
typedef char String[256];

typedef struct Type {
  int size;
  struct Module* mod;
  int id;
  int base[16]; /* should be ARRAY 16 OF TYPE */
  int fields; /* should be Directory* */
  int ptroffs[any];
} Type;

typedef struct Object{
  int fprint;
  int offs;
  int id;
  Type* ostruct;
} Object;

typedef struct Directory{
  int num;
  Object obj[any];
} Directory;

typedef struct Module{ /* has to be exact copy of Kernel.Module */
  struct Module *next;
  int opts;
  int refcnt;
  short compTime[6], loadTime[6];
  int ext;
  int term; /* actually a pointer to type Command */
  int nofimps, nofptrs;
  int csize, dsize, rsize;
  int code, data, refs;
  int procBase, varBase; /* meta base addresses */
  char* names;	/* names[0] = 0X */
  int* ptrs;
  struct Module* imports;
  Directory* export;
  char name[256];
} Module;

typedef struct ImpList
{
  struct ImpList* next;
  String name;
} ImpList;

typedef struct ModSpec
{
  ImpList* imp;
  String name;
  int start, hs, ms, ds, cs, vs, mad, dad;
} ModSpec;

typedef struct BootInfo
{
  Module* modList;
  int argc;
  char** argv;
} BootInfo;

const char bbfile[] = "./bb.boot";

FILE* f;
int nofMods;
String kernel, mainmod;
ModSpec mod;
Module *modlist;
BootInfo* bootInfo;
int newRecAdr, newArrAdr;
int newRecFP, newArrFP;

int donothing(char* fmt, ...)
{
}

void DumpMod()
{	
  dprintf("\n\n---- Mod info:\n");
  dprintf("        hs, ms, ds, cs, vs = %d, %d, %d, %d, %d\n",
	 mod.hs, mod.ms, mod.ds, mod.cs, mod.vs);
  dprintf("        mad, dad = %d, %d\n\n", mod.mad, mod.dad);
}


void RegisterModule()
{
  Module* m;
  m = (Module*)mod.dad;
  m->next = modlist;
  modlist = m;
  /*
  if (modlist == NULL){
    modlist = m; 
  } else {
    last->next = m;
    last = m;
  }
  last->next = NULL;
  */
  dprintf("Registred module %s\n", mod.name);
}

void PrintMods()
{
  Module* ml;
  ml = modlist;
  dprintf("Loaded Modules\n");
  while (ml != NULL){
    dprintf("mod name: %s\n", ml->name);
    ml = ml->next;
  }
  dprintf("end of list\n");
}


Module* ThisModule(char* name)
{
  Module* ml;
  ml = modlist;
  while ((ml != NULL) && (strcmp(ml->name, name) != 0)){
    ml = ml->next;
  }
  return ml;
}
		
Object* ThisObject(Module* mod, char* name)
{
  int l, r, m;
  char* p;
  l = 0; r = mod->export->num;
  while (l < r){
    m = (l + r) / 2;
    p = (char*) &(mod->names[mod->export->obj[m].id / 256]);
    if (strcmp(p, name) == 0)
      return (Object*)&(mod->export->obj[m]);
    if (strcmp(p, name) < 0)
      l = m + 1;
    else
      r = m;
  }
  return NULL;
}

Object* ThisDesc(Module* mod, int fprint)
{
  int i, n;
  i = 0; n = mod->export->num;
  while ((i < n) && (mod->export->obj[i].id / 256 == 0))
    {
      if (mod->export->obj[i].offs == fprint)
	return (Object*)&(mod->export->obj[i]);
      i++;
    }
  return NULL;
}

int LoadDll (char* name)
{
  
  void *handle;
  dprintf("loading: %s\n", name);
  if ((handle = dlopen(name, RTLD_LAZY + RTLD_GLOBAL)) == NULL) {
    printf("LoadDll: failed to load lib %s\n", name); 
    printf(" - dlerror: %s\n", dlerror());
    exit(-1);
  }
  return handle != NULL;
}

	
int ThisDllObj (int mode, int fprint, char* dll, char* name)
{
  void *handle; 
  int ad = 0;
  if ((mode == mVar) || (mode == mProc)){
    if ((handle = dlopen(dll, RTLD_LAZY + RTLD_GLOBAL)) == NULL) {
      printf("ThisDllObj: lib %s not found\n", dll); 
      printf(" - dlerror: %s\n", dlerror());
      exit(-1);
    } else {
      ad = (int)dlsym((void *) handle, name);
      if (ad == 0) {
	printf("ThisDllObj: symbol %s not found\n", name); exit(-1);
      }
    }
  }
  return ad;
 }

int Read4 ()
{
  unsigned char b;
  int	w;
  b = fgetc(f); w = b % 256;
  b = fgetc(f); w = w + 0x100 * (b % 256);
  b = fgetc(f); w = w + 0x10000 * (b % 256);
  b = fgetc(f); w = w + 0x1000000 * b;
  return w;
}

int RNum()
{
  char b;
  int	s, y;
  s = 0; y = 0;
  b = fgetc(f);
  while (b < 0) 
    {
      y = y + ((b + 128) << s);
      s = s + 7;
      b = fgetc(f); 
    }
  return (((b + 64) % 128 - 64) << s) + y;
}

void ReadName (char* str)
{
  unsigned char b;
  int i;
  i = 0; b = fgetc(f);
  while (b != 0)
    {
      str[i] = b; i++; b = fgetc(f);
    }
  str[i] = 0;
}

void Fixup (int adr)
{
  int link, offset, linkadr, n, x, t;

  dprintf("fixup: %X ", adr);
  link = RNum();
  while (link != 0)
    {
      offset = RNum();
      dprintf("+%d: ", offset);
      while (link != 0)
	{
	  if (link > 0) 
	    {
	      dprintf("c");
	      linkadr = mod.mad + mod.ms + link;
	    }
	  else
	    {
	      dprintf("d");
	      link = -link;
	      if (link < mod.ms) 
		linkadr = mod.mad + link;
	      else
		linkadr = mod.dad + link - mod.ms;
	    }
	  dprintf("%X ", link);
	  
	  /*
	  t = *(char*)(linkadr + 3);
	  n = *(int*)linkadr;
	  n = n << 8; n = n >> 8;
	  */
	  x = *(int*)linkadr;
	  t = x / 0x1000000;
	  n = (x + 0x800000) % 0x1000000 - 0x800000;
	  
	  switch (t) {
	  case absolute: x = adr + offset; break;
	  case relative: x = adr + offset - linkadr -4; break;
	  case copy: x = *(int*)(adr + offset); break;
	  case table: x = adr + n; n = link + 4; break;
	  case tableend: x = adr + n; n = 0; break;
	  case deref: x = *(int*)(adr + 2); x = x + offset; break;
	  case halfword:
	    printf("fixup: halfword not implemented\n");
	    break;
	  default:
	    printf("fixup error(link=%d, offset=%d, linkadr=%d, t=%d, x=%d)\n",
		   link, offset, linkadr, t, x);
	    return;
	  }			
	  *(int*)linkadr = x;
	  link = n;
	}
      link = RNum();
    }
  dprintf("\n");
}

int ReadBootHeader()
{
  int tag, version;

  fseek(f, exeSize, SEEK_SET);
  tag = Read4();
  version = Read4();
  if ((tag != 0x3A4B5C6D) || (version != 0))
    {
      return 0;
    }
  nofMods = Read4();
  dprintf("Linked modules: %d\n", nofMods);
  ReadName(kernel);
  dprintf("kernel: %s\n", kernel);
  ReadName(mainmod);
  dprintf("main: %s\n", mainmod);
  newRecFP = Read4(); newRecAdr = 0;
  newArrFP = Read4(); newArrAdr = 0;
  mod.start = ftell(f);
  return 1;
}

int ReadHeader ()
{
  int ofTag, i, nofImps, processor;
  char str[80];
  ImpList *imp, *last;
  char* n;

  ofTag = Read4();
  if (ofTag != 0x6F4F4346) 
    {
      printf("wrong object file version\n");
      return 0;
    }
  processor = Read4(); 
  mod.hs = Read4();
  mod.ms = Read4();
  mod.ds = Read4();
  mod.cs = Read4();
  mod.vs = Read4();
  dprintf("File tag: %d ", ofTag); dprintf("Processor: %d\n", processor);
  dprintf("Header size: %d ", mod.hs);
  dprintf("Meta size: %d ", mod.ms);
  dprintf("Desc size: %d ", mod.ds );
  dprintf("Code size: %d ", mod.cs);
  dprintf("Data size: %d\n", mod.vs);
  nofImps = RNum(); dprintf("Nof imports: %d\n", nofImps);
  ReadName(mod.name); dprintf("Module name: %s\n", mod.name);
  mod.imp = NULL;
  for (i = 0; i < nofImps; i++)
    {
      imp = (ImpList*)calloc(1, sizeof(ImpList));
      ReadName(imp->name);
      if (mod.imp == NULL)
	mod.imp = imp;
      else
	last->next = imp;
      last = imp;
      last->next = NULL;
      dprintf("Import %d: %s\n", i, imp->name);
      if ((imp->name[0] == '$') && (imp->name[1] == '$'))
	strcpy(imp->name, "Kernel");
      if (imp->name[0] == '$'){
	n = imp->name;
	n++;
	if (!LoadDll(n)){
	  printf("Could not load lib: %s\n", imp->name[1]);
	  return 0;
	}
      }
    }
  dprintf("Pos: %d\n", ftell(f));
  return 1;
}

int ReadModule ()
{
  char *dp, *mp; 
  unsigned int cnt;
  ImpList* imp;
  int x, fp, opt, link, ofp, imptab, a;
  Module *desc, *k;
  String name;
  Object* obj;
  int isLib;
  char* im;

  mod.dad = (int) calloc(1, mod.ds);
  mod.mad = (int) calloc(1, mod.ms + mod.cs + mod.vs);
  if ((mod.dad == 0) || (mod.mad == 0)) 
    {
      printf("BootLoader: Couldn't initalize heap\n");
      free((void*)mod.dad);
      free((void*)mod.mad);
      return 0;
    }
  dp = (char*) mod.dad;
  mp = (char*) mod.mad;
  fseek(f, mod.start + mod.hs, SEEK_SET);
  dprintf("ReadModule after fseek pos: %d\n", ftell(f));
  cnt = fread(mp, 1, mod.ms, f);
  dprintf("Read meta bulk (%d bytes. New pos: %d)\n", cnt, ftell(f));
  cnt = fread(dp, 1, mod.ds, f);
  dprintf("Read desc bulk (%d bytes. New pos: %d)\n", cnt, ftell(f));
  mp = (char*)(mod.mad + mod.ms);
  cnt = fread(mp, 1, mod.cs, f); 
  dprintf("Read code bulk (%d bytes. New pos: %d)\n", cnt, ftell(f));

  DumpMod();

  dprintf("before fixup: pos = %d\n", ftell(f));
  
  if ((!newRecAdr) || (!newArrAdr)){
    k = ThisModule(kernel);
    if (k != NULL){
      /*      obj = ThisDesc(k, newRecFP);*/
      obj = ThisObject(k, "NewRec");
      if (obj != NULL)
	newRecAdr = k->procBase + obj->offs;
      /*      obj = ThisDesc(k, newArrFP);*/
      obj = ThisObject(k, "NewArr");
      if (obj != NULL)
	newArrAdr = k->procBase + obj->offs;
      dprintf("newRecFP: %X  newArrFP: %X\n", newRecFP, newArrFP);
      dprintf("newRecAdr: %X  newArrAdr: %X\n", newRecAdr, newArrAdr);
    } else {
      dprintf("no kernel before %s.\n", mod.name);
    }
  }
  Fixup(newRecAdr);
  Fixup(newArrAdr);
  Fixup(mod.mad);
  Fixup(mod.dad);
  Fixup(mod.mad + mod.ms);
  Fixup(mod.mad + mod.ms + mod.cs);
  dprintf("after fixup: pos = %d\n", ftell(f));
  imp = mod.imp; imptab = (int)((Module*)(mod.dad))->imports;
  while (imp != NULL){
    x = RNum();
    if ((imp->name[0] == '$') && (imp->name[1] == '$'))
      printf("should be Kerneln");
    if (imp->name[0] == '$')
      isLib = 1;
    else{
      isLib = 0;
      desc = ThisModule(imp->name);
      if (desc == NULL){
	printf("invalid import list\n");
	return 0;
      }
    }
    while (x != 0) {
      ReadName(name); fp = RNum(); opt = 0;
      if (!isLib) {
	if (name[0] == 0)
	  obj = ThisDesc(desc, fp);
	else
	  obj = ThisObject(desc, name);
	if ((obj != NULL) && (obj->id % 16 == x)){
	  ofp = obj->fprint;
	  switch (x){
	  case mTyp:
	    opt = RNum();
	    if (opt % 2 == 1) ofp = obj->offs;
	    if ((opt > 1) && ((obj->id / 16) % 16 != mExported)){
	      printf("object not found (%s)\n", imp->name);
	      return 0;
		}
	    Fixup((int)obj->ostruct);
	    break;
	  case mVar:
	    Fixup(desc->varBase + obj->offs);
	    break;
	  case mProc:
	    Fixup(desc->procBase + obj->offs);
	  }
	  if (ofp != fp){
	    printf("illigal foot print (%s)\n", imp->name);
	    return 0;
	  }
	} else {
	  if (obj == NULL) printf("obj == NULL\n");
	  printf("descriptor not found (%s, x: %d, id: %d)\n", name, x, obj->id);
	  return 0;
	}
      }else{
	if ((x == mVar)  || (x == mProc)){	
	  im = imp->name;
	  im++;
	  a = ThisDllObj(x, fp, im, name);
	  if (a != 0)
	    Fixup(a);
	  else{
	    printf("ReadModule: Object not found: %s\n", name);
	    return 0;
	  }
	} else {
	  if (x == mTyp) {
	    opt = RNum();
	    x = RNum();
	    if (x != 0) {
	      printf("ReadModule: Object not found: %s\n", name);
	      return 0;
	    }
	  }
	}
      }
      x = RNum();
    }
    *(int*)imptab = (int)desc; imptab += 4;
    imp = imp->next;
  }

  mod.start = ftell(f);
  return 1;
}

int main (int argc, char *argv[])
{
  int i, ok;
  BodyProc body;
  int callBackAdr;
  Module *k, *m;

  modlist = NULL;
  dprintf("initializing BlackBox for Linux...\n");
  /*f = fopen(bbfile, "rb");*/
  f = fopen(argv[0], "r");
  if (f != NULL) 
    {
      if (ReadBootHeader())
	{
	  i = 0; ok = 1;
	  while ((i < nofMods) && (ok)){
	    ok = ReadHeader();
	    if (ok) {
	      ok = ReadModule();
	      if (ok)
		RegisterModule();
	      else
		printf("Incorrect module: %s\n", mod.name);
	    } else
	      printf("Incorrect header: %s\n", mod.name);
	    i++;
	  }
	  fclose(f);
	  if (ok) {
	    k = ThisModule(kernel);
	    m = ThisModule(mainmod);
	    if (k == NULL)
	      printf("no kernel\n");
	    else
	      {
		if (m == NULL) 
		  printf("no main module");
		else
		  {
		    /* assign the boot info to first variable in Kernel */
		    bootInfo = calloc(1, sizeof(BootInfo));
		    bootInfo->modList = modlist;
		    bootInfo->argc = argc;
		    bootInfo->argv = argv;
		    *((int*)(k->varBase)) = (int)bootInfo;
		    dprintf("before body\n");
		    body = (BodyProc)(m->code);
		    k->opts = k->opts | init; /* include init in opts */
		    body();
		    dprintf("after body\n");
		  }
	      }
	    PrintMods();
	  }
	} else
	  printf("Invalid BlackBox executable, make sure that the constant exeSize is correctly set\n");

    } else 
      printf("Couldn't find file: %s\n", bbfile);
}
