/*
 * binding.c
 *
 * Create DLL header and things
 *
 *  1994-1998 Straylight
 */

/*----- Licensing note ----------------------------------------------------*
 *
 * This file is part of Straylight's Dynamic Linking System (SDLS)
 *
 * SDLS is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * SDLS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with SDLS.  If not, write to the Free Software Foundation,
 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>

#include "binding.h"
#include "readdef.h"
#include "aof/aof.h"
#include "aof/chunk.h"
#include "hashtable.h"
#include "error.h"

typedef struct
{
  /* --- Basic parts of the header --- */

  unsigned magic;
  unsigned bversion;
  unsigned name;
  unsigned copyright;
  unsigned version;
  unsigned relocate;
  unsigned stubs;
  unsigned entries;
  unsigned enames;
  unsigned eveneer;
  unsigned dllBase;
  unsigned dllLimit;
  unsigned instBase;
  unsigned instLimit;

  /* --- New bits added for v. 1.01 --- */

  unsigned zinitBase;
  unsigned zinitLimit;
  unsigned appStub;
  unsigned appNames;
}
bind__header;

typedef struct
{
  aof_file f;
  bind__header *h;
  hashtable osym;
}
bind__thing;

#define bind__MAGIC 0x004c4c44
#define bind__VERSION 101

static unsigned int nextOrd;

static void bind__enames(char *p,unsigned ord,void *handle)
{
  bind__thing *t=handle;
  int o; /* dummy for the aof... macros */

  if (ord!=0xFFFFFFFF)
  {
    while (ord>nextOrd)
    {
      aof_byte(1,t->f.area);
      nextOrd++;
    }
  }
  if (hash_find(t->osym,p))
  {
    aof_string(p,t->f.area);
    t->h->entries++;
    if (ord!=0xFFFFFFFF)
      nextOrd=ord+1;
  }
}

static void bind__count(char *p,unsigned ord,void *handle)
{
  bind__thing *t=handle;
  if (hash_find(t->osym,p))
    t->h->entries++;
}

static void bind__entries(char *p,unsigned ord,void *handle)
{
  bind__thing *t=handle;
  static int entry[4]={0xE92D0007,		/* STMFD   sp!,{a1-a3}	*/
                       0xE59FC000};		/* LDR     ip,=address	*/
  char buf[256];
  if (ord!=0xFFFFFFFF)
  {
    static int duff[4];
    while (ord>nextOrd)
    {
      aof_add(duff,t->f.area);
      nextOrd++;
    }
  }
  if (hash_find(t->osym,p))
  {
    entry[2]=aof_branch(t->f.area->next+8,sizeof(bind__header));
    aof_reloc(p,t->f.area->next+12,0,&t->f);
    sprintf(buf,"_dllEntry_%s",p);
    aof_addsym(buf,t->f.area->next,0,4,&t->f);
    aof_add(entry,t->f.area);
    if (ord!=0xFFFFFFFF)
      nextOrd=ord+1;
  }
}

static void bind__appEntries(char *p,unsigned ord,void *handle)
{
  bind__thing *t=handle;
  int zero=0;
  if (ord!=0xFFFFFFFF)
  {
    while (ord>nextOrd)
    {
      aof_add(zero,t->f.area);
      nextOrd++;
    }
  }
  if (hash_find(t->osym,p))
  {
    aof_reloc(p,aof_add(zero,t->f.area),0,&t->f);
    if (ord!=0xFFFFFFFF)
      nextOrd=ord+1;
  }
}

void binding(char *name,readdef_info *inf,hashtable osym)
{
  FILE *fp;

  if (!inf->apptbl)
  {
    /* --- Don't check this lot if you can help it --- */

    if (!inf->name[0])
    {
      if (!(inf->errored & rd_name))
        error(NONAME);
      inf->errored|=rd_name;
    }
    if (inf->version==-1)
    {
      if (!(inf->errored & rd_version))
        error(NOVERSION);
      inf->errored|=rd_version;
      inf->version=100;
    }
    if (!inf->copyright[0])
    {
      if (!(inf->errored & rd_cright))
        error(NOCRIGHT);
      inf->errored|=rd_cright;
    }
    if (inf->errored & rd_name)
      return;
  }

  fp=fopen(name,"wb");			/* Open the output file		*/
  if (!fp)				/* If the file wasn't opened	*/
    error(NOOPENOUT,name);		/* Better give an error		*/
  else
  {
    char _buf[sizeof(chunk_fixedHeader)+5*sizeof(chunk_tableEntry)];
    chunk_header *h=(chunk_header *)&_buf; /* Allocate memory easily :-) */

    aof_chunkinfo obj_idfn={0};
    aof_chunkinfo obj_head={0};
    aof_chunkinfo obj_area={0};
    aof_chunkinfo obj_symt={0};
    aof_chunkinfo obj_strt={0};
    aof_chunkinfo reloc={0};
    bind__thing t;

    bind__header hdr;
    int o;

    static int prologue[]={0xE1A0000E,	/* MOV     a1,lr	*/
                           0xE1A0100A,	/* MOV     a2,sl	*/
                           0xE24F2058,	/* ADR     a3,header	*/
                           0xEF04A30A,	/* SWI     DLL_Prologue	*/
                           0xE1A0E000,	/* MOV     lr,a1	*/
                           0xE8BD0007,	/* LDMFD   sp!,{a1-a3}	*/
                           0xE1A0F00C};	/* MOV     pc,ip	*/

    h->hdr.id=chunk_MAGIC;		/* Fill in magic thingy		*/
    h->hdr.maxChunks=h->hdr.numChunks=5; /* And number of chunks	*/

    if (!inf->apptbl)
    {
      /* --- Don't set up the header if we won't need it --- */

      hdr.magic=bind__MAGIC;
      hdr.bversion=bind__VERSION;
      hdr.version=inf->version;
      hdr.relocate=0xea000000;
      hdr.stubs=0;
      hdr.entries=0;
      hdr.dllBase=0;
      hdr.dllLimit=0;
      hdr.instBase=0;
      hdr.instLimit=0;
      hdr.zinitBase=0;
      hdr.zinitLimit=0;
      hdr.appStub=0;
      hdr.appNames=0;
    }

    t.f.area=&obj_area;
    t.h=&hdr;
    t.osym=osym;
    t.f.symt=&obj_symt;
    t.f.strt=&obj_strt;
    t.f.reloc=&reloc;

    aof_string("Straylight Dynamic Link Library building system 1.01",
                 &obj_idfn);
    aof_align(obj_idfn);

    if (!inf->apptbl)
    {
      aof_add(hdr,&obj_area);
      if (~inf->flags & rdFlag_shortEntry)
        aof_add(prologue,&obj_area);
      aof_int(0,&obj_strt);
      aof_string("!!!DLL$$Header",&obj_strt);

      hdr.name=aof_string(inf->name,&obj_area);
      aof_roff(offsetof(bind__header,name),&t.f);
      hdr.copyright=aof_string(inf->copyright,&obj_area);
      aof_roff(offsetof(bind__header,copyright),&t.f);
      hdr.enames=obj_area.next;
      aof_roff(offsetof(bind__header,enames),&t.f);
      nextOrd=0;
      if (~inf->flags & rdFlag_noNames)
        hash_enumOrdered(inf->sym,bind__enames,&t);
      else
        hash_enumOrdered(inf->sym,bind__count,&t);
      if (inf->flags & rdFlag_shortEntry)
        hdr.entries|=0x80000000u;
      if (inf->flags & rdFlag_noNames)
        hdr.entries|=0x40000000u;
      aof_align(obj_area);
      hdr.eveneer=obj_area.next;
      aof_roff(offsetof(bind__header,eveneer),&t.f);
      nextOrd=0;
      if (~inf->flags & rdFlag_shortEntry)
      {
        hash_enumOrdered(inf->sym,bind__entries,&t);
        hash_enumOrdered(inf->vsym,bind__entries,&t);
      }
      else
      {
        hash_enumOrdered(inf->sym,bind__appEntries,&t);
        hash_enumOrdered(inf->vsym,bind__appEntries,&t);
      }

      aof_reloc_b("__RelocCode",offsetof(bind__header,relocate),&t.f);
      if (~inf->flags & rdFlag_shortEntry)
        aof_reloc("_kernel_init",offsetof(bind__header,stubs),1,&t.f);
      aof_reloc("DLL$$ExternalTable$$Base",
                offsetof(bind__header,dllBase),
                1,
                &t.f);
      aof_reloc("DLL$$ExternalTable$$Limit",
                offsetof(bind__header,dllLimit),
                1,
                &t.f);
      aof_reloc("Image$$RW$$Base",offsetof(bind__header,instBase),0,&t.f);
      aof_reloc("Image$$RW$$Limit",offsetof(bind__header,instLimit),0,&t.f);
      aof_reloc("Image$$ZI$$Base",offsetof(bind__header,zinitBase),0,&t.f);
      aof_reloc("Image$$ZI$$Limit",offsetof(bind__header,zinitLimit),0,&t.f);
      aof_reloc("DLL$$AppEntryStubs",offsetof(bind__header,appStub),1,&t.f);
      aof_reloc("DLL$$AppEntryNames",offsetof(bind__header,appNames),1,&t.f);

      aof_fill(hdr,0,&obj_area);

      {
        aof_symbol sym={0};
        sym.name=aof_string("_dll_findall",&obj_strt);
        sym.defined=1;
        sym.export=1;
        sym.absolute=1;
        sym.value=0;
        aof_add(sym,&obj_symt);
      }
    }
    else
    {
      aof_int(0,&obj_strt);
      aof_string("DLL$$AppEntryDefinition",&obj_strt);

      aof_addsym("_dll_appEntryStubs",
                 obj_area.next,
                 0,
                 4,
                 &t.f);
      nextOrd=0;
      if (~inf->flags & rdFlag_noNames)
        hash_enumOrdered(inf->sym,bind__appEntries,&t);

      aof_addsym("_dll_appEntryNames",
                 obj_area.next,
                 0,
                 4,
                 &t.f);
      nextOrd=0;
      hash_enumOrdered(inf->sym,bind__enames,&t);
      { char c=0; aof_add(c,&obj_area); }
      aof_align(obj_area);

      /* --- Hack for link 5.00 --- *
       *
       * We IMPORT _dll_regAppEntry here so that link will grab the correct
       * bits of DLLLib
       */

      {
        aof_symbol sym={0};
        sym.name=aof_string("_dll_regAppEntry",&obj_strt);
        sym.defined=0;
        sym.export=1;
        aof_add(sym,&obj_symt);
      }

    }

    aof_int((int)aof_RELOC,&obj_head);
    aof_int(150,&obj_head);
    aof_int(1,&obj_head);
    aof_int(obj_symt.next/sizeof(aof_symbol),&obj_head);
    aof_int(0,&obj_head);
    aof_int(0,&obj_head);

    aof_int(4,&obj_head);
    aof_int(0x00002202,&obj_head);
    aof_int(obj_area.next,&obj_head);
    aof_int(reloc.next/8,&obj_head);
    aof_int(0,&obj_head);

    aof_addBlock(reloc.p,reloc.next,&obj_area);

    aof_fill(obj_strt.next,0,&obj_strt);
    aof_align(obj_strt);

    memcpy(h->table[0].chunkName,"OBJ_IDFN",8);
    memcpy(h->table[1].chunkName,"OBJ_HEAD",8);
    memcpy(h->table[2].chunkName,"OBJ_AREA",8);
    memcpy(h->table[3].chunkName,"OBJ_SYMT",8);
    memcpy(h->table[4].chunkName,"OBJ_STRT",8);

    o=sizeof(chunk_fixedHeader)+5*sizeof(chunk_tableEntry);

    h->table[0].offset=o;
    h->table[0].size=obj_idfn.next;
    o+=obj_idfn.next;

    h->table[1].offset=o;
    h->table[1].size=obj_head.next;
    o+=obj_head.next;

    h->table[2].offset=o;
    h->table[2].size=obj_area.next;
    o+=obj_area.next;

    h->table[3].offset=o;
    h->table[3].size=obj_symt.next;
    o+=obj_symt.next;

    h->table[4].offset=o;
    h->table[4].size=obj_strt.next;
    o+=obj_strt.next;

    fwrite(h,sizeof(chunk_fixedHeader)+5*sizeof(chunk_tableEntry),1,fp);
    fwrite(obj_idfn.p,obj_idfn.next,1,fp);
    fwrite(obj_head.p,obj_head.next,1,fp);
    fwrite(obj_area.p,obj_area.next,1,fp);
    fwrite(obj_symt.p,obj_symt.next,1,fp);
    fwrite(obj_strt.p,obj_strt.next,1,fp);
    fclose(fp);

    free(obj_idfn.p);
    free(obj_area.p);
    free(obj_strt.p);
    free(obj_head.p);
    free(obj_symt.p);
  }
}
