Logo Search packages:      
Sourcecode: linux86 version File versions  Download package

writex86.c

/* writex86.c - write binary file for linker */

/* Copyright (C) 1994 Bruce Evans */

#include "syshead.h"
#include "x86_aout.h"
#ifndef MSDOS
#include "x86_cpm86.h"
#endif
#include "const.h"
#include "obj.h"
#include "type.h"
#include "globvar.h"

#define btextoffset (text_base_value)
#define bdataoffset (data_base_value)
#define page_size() ((bin_off_t)4096)

#ifndef ELF_SYMS
#define ELF_SYMS 0
#endif

#ifdef MSDOS
#  define FILEHEADERLENGTH (headerless?0:A_MINHDR)
#else
#  define FILEHEADERLENGTH (headerless?0:(cpm86?CPM86_HEADERLEN:A_MINHDR))
#endif
                        /* part of header not counted in offsets */
#define DPSEG 2

#define CM_MASK 0xC0
#define MODIFY_MASK 0x3F
#define S_MASK 0x04
#define OF_MASK 0x03

#define CM_SPECIAL 0
#define CM_ABSOLUTE 0x40
#define CM_OFFSET_RELOC 0x80
#define CM_SYMBOL_RELOC 0xC0

#define CM_EOT 0
#define CM_BYTE_SIZE 1
#define CM_WORD_SIZE 2
#define CM_LONG_SIZE 3
#define CM_1_SKIP 17
#define CM_2_SKIP 18
#define CM_4_SKIP 19
#define CM_0_SEG 32

#define ABS_TEXT_MAX 64

#define offsetof(struc, mem) ((int) &((struc *) 0)->mem)
#define memsizeof(struc, mem) sizeof(((struc *) 0)->mem)

PRIVATE bool_t bits32;        /* nonzero for 32-bit executable */
PRIVATE bin_off_t combase[NSEG];/* bases of common parts of segments */
PRIVATE bin_off_t comsz[NSEG];      /* sizes of common parts of segments */
PRIVATE fastin_t curseg;      /* current segment, 0 to $F */
PRIVATE bin_off_t edataoffset;      /* end of data */
PRIVATE bin_off_t endoffset;  /* end of bss */
PRIVATE bin_off_t etextoffset;      /* end of text */
PRIVATE bin_off_t etextpadoff;      /* end of padded text */
PRIVATE unsigned nsym;        /* number of symbols written */
PRIVATE unsigned relocsize;   /* current relocation size 1, 2 or 4 */
PRIVATE bin_off_t segadj[NSEG];     /* adjusts (file offset - seg offset) */
                        /* depends on zero init */
PRIVATE bin_off_t segbase[NSEG];/* bases of data parts of segments */
PRIVATE char segboundary[9] = "__seg0DH";
                        /* name of seg boundary __seg0DL to __segfCH */
PRIVATE bin_off_t segpos[NSEG];     /* segment positions for current module */
PRIVATE bin_off_t segsz[NSEG];      /* sizes of data parts of segments */
                        /* depends on zero init */
PRIVATE bool_t sepid;         /* nonzero for separate I & D */
PRIVATE bool_t stripflag;     /* nonzero to strip symbols */
PRIVATE bin_off_t spos;       /* position in current seg */
PRIVATE bool_t uzp;           /* nonzero for unmapped zero page */
PRIVATE bool_t xsym;          /* extended symbol table */

FORWARD void linkmod P((struct modstruct *modptr));
FORWARD void padmod P((struct modstruct *modptr));
FORWARD void setsym P((char *name, bin_off_t value));
FORWARD void symres P((char *name));
FORWARD void setseg P((fastin_pt newseg));
FORWARD void skip P((unsigned countsize));
FORWARD void writeheader P((void));
#ifndef MSDOS
FORWARD void cpm86header P((void));
#endif
FORWARD void writenulls P((bin_off_t count));

EXTERN bool_t reloc_output;

/* write binary file */

PUBLIC void write_elks(outfilename, argsepid, argbits32, argstripflag, arguzp, argxsym)
char *outfilename;
bool_pt argsepid;
bool_pt argbits32;
bool_pt argstripflag;
bool_pt arguzp;
bool_pt argxsym;
{
    char buf4[4];
    char *cptr;
    struct nlist extsym;
    flags_t flags;
    struct modstruct *modptr;
    fastin_t seg;
    unsigned sizecount;
    bin_off_t tempoffset;

    if( reloc_output )
#ifndef MSDOS
       fatalerror("Output binformat not configured relocatable, use -N");
#else
       fatalerror("Cannot use -r under MSDOS, sorry");
#endif

    sepid = argsepid;
    bits32 = argbits32;
    stripflag = argstripflag;
    uzp = arguzp;
    xsym = argxsym;
    if (uzp)
    {
      if (btextoffset == 0)
          btextoffset = page_size();
      if (bdataoffset == 0 && sepid)
          bdataoffset = page_size();
    }

    /* reserve special symbols use curseg to pass parameter to symres() */
    for (curseg = 0; curseg < NSEG; ++curseg)
    {
      segboundary[5] = hexdigit[curseg];  /* to __segX?H */
      segboundary[6] = 'D';
      symres(segboundary);    /* __segXDH */
      segboundary[7] = 'L';
      symres(segboundary);    /* __segXDL */
      segboundary[6] = 'C';
      symres(segboundary);    /* __segXCL */
      segboundary[7] = 'H';
      symres(segboundary);    /* __segXCH */
#ifndef DATASEGS
        if( curseg > 3 )
      {
         segboundary[6] = 'S';
         segboundary[7] = 'O';
         symres(segboundary); /* __segXSO */
        }
#endif
    }
    curseg = 3;
    symres("__edata");
    symres("__end");
    symres("__heap_top");
    curseg = 0;               /* text seg, s.b. variable */
    symres("__etext");
    symres("__segoff");

    /* calculate segment and common sizes (sum over loaded modules) */
    /* use zero init of segsz[] */
    /* also relocate symbols relative to starts of their segments */
    for (modptr = modfirst; modptr != NUL_PTR; modptr = modptr->modnext)
      if (modptr->loadflag)
      {
          register struct symstruct **symparray;
          register struct symstruct *symptr;

          for (symparray = modptr->symparray;
             (symptr = *symparray) != NUL_PTR; ++symparray)
            if (symptr->modptr == modptr && !(symptr->flags & A_MASK))
            {
                if (!(symptr->flags & (I_MASK | SA_MASK)))
                {
                  /* relocate by offset of module in segment later */
                  /* relocate by offset of segment in memory special */
                  /* symbols get relocated improperly */
                  symptr->value += segsz[symptr->flags & SEGM_MASK];
                }
                else if (symptr->value == 0)
                {
                      undefined(symptr->name);
                }
                else
                {
                  tempoffset = ld_roundup(symptr->value, 4, bin_off_t);
                  /* temp kludge quad alignment for 386 */
                  symptr->value = comsz[seg = symptr->flags & SEGM_MASK];
                  comsz[seg] += tempoffset;
                  if (!(symptr->flags & SA_MASK))
                      symptr->flags |= C_MASK;
                }
            }
          for (seg = 0, cptr = modptr->segsize; seg < NSEG; ++seg)
          {
            segsz[seg] += cntooffset(cptr,
                    sizecount = segsizecount((unsigned) seg, modptr));

            /* adjust sizes to even to get quad boundaries */
            /* this should be specifiable dynamically */
            segsz[seg] = ld_roundup(segsz[seg], 4, bin_off_t);
            comsz[seg] = ld_roundup(comsz[seg], 4, bin_off_t);
            cptr += sizecount;
          }
      }

    /* calculate seg positions now their sizes are known */
    /*
#ifdef DATASEGS
     * Assume seg 0 is text and rest are data
#else
     * Assume seg 1..3 are data, Seg 0 is real text, seg 4+ are far text
#endif
     */
    segpos[0] = segbase[0] = spos = btextoffset;
    combase[0] = segbase[0] + segsz[0];
    segadj[1] = segadj[0] = -btextoffset;
    etextpadoff = etextoffset = combase[0] + comsz[0];
    if (sepid)
    {
      etextpadoff = ld_roundup(etextoffset, 0x10, bin_off_t);
      segadj[1] += etextpadoff - bdataoffset;
    }
    else if (bdataoffset == 0)
      bdataoffset = etextpadoff;
    segpos[1] = segbase[1] = edataoffset = bdataoffset;
    combase[1] = segbase[1] + segsz[1];
#ifndef DATASEGS
    for (seg = 4; seg < NSEG; ++seg)
    {
      segpos[seg] = segbase[seg] = 0;
      combase[seg] = segbase[seg] + segsz[seg];
      segadj[seg] = etextpadoff;

      etextpadoff += ld_roundup(segsz[seg] + comsz[seg], 0x10, bin_off_t);
      segadj[1]   += ld_roundup(segsz[seg] + comsz[seg], 0x10, bin_off_t);
    }
    for (seg = 2; seg < 4; ++seg)
#else
    for (seg = 2; seg < NSEG; ++seg)
#endif
    {
      segpos[seg] = segbase[seg] = combase[seg - 1] + comsz[seg - 1];
#ifdef MC6809
      if (seg == DPSEG)
      {
          /* temporarily have fixed DP seg */
          /* adjust if nec so it only spans 1 page */
          tempoffset = segsz[seg] + comsz[seg];
          if (tempoffset > 0x100)
            fatalerror("direct page segment too large");
          if ((((segbase[seg] + tempoffset) ^ segbase[seg])
             & ~(bin_off_t) 0xFF) != 0)
            segpos[seg] = segbase[seg] = (segbase[seg] + 0xFF)
                                   & ~(bin_off_t) 0xFF;
      }
#endif
      combase[seg] = segbase[seg] + segsz[seg];
      segadj[seg] = segadj[seg - 1];
    }

    /* relocate symbols by offsets of segments in memory */
    for (modptr = modfirst; modptr != NUL_PTR; modptr = modptr->modnext)
      if (modptr->loadflag)
      {
          register struct symstruct **symparray;
          register struct symstruct *symptr;

          for (symparray = modptr->symparray;
             (symptr = *symparray) != NUL_PTR; ++symparray)
            if (symptr->modptr == modptr && !(symptr->flags & A_MASK))
            {
                if (symptr->flags & (C_MASK | SA_MASK))
                  symptr->value += combase[symptr->flags & SEGM_MASK];
                else
                  symptr->value += segbase[symptr->flags & SEGM_MASK];
            }
      }

    /* adjust special symbols */
    for (seg = 0; seg < NSEG; ++seg)
    {
#ifdef DATASEGS
      if (segsz[seg] != 0)
          /* only count data of nonzero length */
#else
      if (segsz[seg] != 0 && seg < 4)
#endif
          edataoffset = segbase[seg] + segsz[seg];
      segboundary[5] = hexdigit[seg];           /* to __segX?H */
      segboundary[6] = 'D';
      setsym(segboundary, (tempoffset = segbase[seg]) + segsz[seg]);
                                    /* __segXDH */
      segboundary[7] = 'L';
      setsym(segboundary, tempoffset);    /* __segXDL */
      segboundary[6] = 'C';
      setsym(segboundary, tempoffset = combase[seg]);
                                    /* __segXCL */
      segboundary[7] = 'H';
      setsym(segboundary, tempoffset + comsz[seg]);
                                    /* __segXCH */
#ifndef DATASEGS
        if( seg > 3 )
      {
         segboundary[6] = 'S';
         segboundary[7] = 'O';
         setsym(segboundary, (bin_off_t)(segadj[seg]-segadj[0])/0x10);
         /* __segXSO */
        }
#endif
    }
    setsym("__etext", etextoffset);
    setsym("__edata", edataoffset);
#ifdef DATASEGS
    setsym("__end", endoffset = combase[NSEG - 1] + comsz[NSEG - 1]);
#else
    setsym("__end", endoffset = combase[3] + comsz[3]);
#endif
    setsym("__segoff", (bin_off_t)(segadj[1]-segadj[0])/0x10);
    if( !bits32 )
    {
        if( etextoffset > 65536L )
            fatalerror("text segment too large for 16bit");
        if( endoffset > 65536L )
            fatalerror("data segment too large for 16bit");
    }

    if( heap_top_value < 0x100 || endoffset > heap_top_value-0x100)
       heap_top_value = endoffset + 0x8000;
    if( heap_top_value > 0x10000 && !bits32 ) heap_top_value = 0x10000;
    setsym("__heap_top", (bin_off_t)heap_top_value);

    openout(outfilename);
#ifndef MSDOS
    if (cpm86) cpm86header();
    else
#endif
       writeheader();
    for (modptr = modfirst; modptr != NUL_PTR; modptr = modptr->modnext)
      if (modptr->loadflag)
      {
          linkmod(modptr);
          padmod(modptr);
      }

    /* dump symbol table */
    if (!stripflag)
    {
      seekout(FILEHEADERLENGTH
            + (unsigned long) (etextpadoff - btextoffset)
            + (unsigned long) (edataoffset - bdataoffset)
            );
      extsym.n_numaux = extsym.n_type = 0;
      for (modptr = modfirst; modptr != NUL_PTR; modptr = modptr->modnext)
          if (modptr->loadflag)
          {
            register struct symstruct **symparray;
            register struct symstruct *symptr;

            for (symparray = modptr->symparray;
                 (symptr = *symparray) != NUL_PTR; ++symparray)
                if (symptr->modptr == modptr)
                {
#if ELF_SYMS
                  if (symptr->name[0] == '_' && symptr->name[1] )
                    strncpy((char *) extsym.n_name, symptr->name+1,
                        sizeof extsym.n_name);
                  else
                  {
                    memcpy((char *) extsym.n_name, "$", 1);
                    strncpy((char *) extsym.n_name+1, symptr->name,
                        sizeof(extsym.n_name)-1);
                  }
#else
                  strncpy((char *) extsym.n_name, symptr->name,
                        sizeof extsym.n_name);
#endif
                  u4cn((char *) &extsym.n_value, (u4_t) symptr->value,
                       sizeof extsym.n_value);
                  if ((flags = symptr->flags) & A_MASK)
                      extsym.n_sclass = N_ABS;
                  else if (flags & (E_MASK | I_MASK))
                      extsym.n_sclass = C_EXT;
                  else
                      extsym.n_sclass = C_STAT;
                  if (!(flags & I_MASK) ||
                       flags & C_MASK)
                      switch (flags & (A_MASK | SEGM_MASK))
                      {
#ifdef DATASEGS
                      case 0:
#else
                      default:
#endif
                        extsym.n_sclass |= N_TEXT;
                      case A_MASK:
                        break;
#ifdef DATASEGS
                      default:
#else
                      case 1: case 2: case 3:
                      case A_MASK|1: case A_MASK|2: case A_MASK|3:
#endif
                        if (flags & (C_MASK | SA_MASK))
                            extsym.n_sclass |= N_BSS;
                        else
                            extsym.n_sclass |= N_DATA;
                        break;
                      }
                  writeout((char *) &extsym, sizeof extsym);
                  ++nsym;
#if !ELF_SYMS
                  if( xsym )
                  {
                     int i;
                     extsym.n_sclass = 0;
                     memset((void*)&extsym.n_value,0,
                           sizeof(extsym.n_value));

                     for(i=sizeof extsym.n_name; i<strlen(symptr->name);
                         i+=sizeof extsym.n_name)
                     {
                        strncpy((char *) extsym.n_name, symptr->name+i,
                        sizeof extsym.n_name);
                        writeout((char *) &extsym, sizeof extsym);
                        ++nsym;
                     }
                  }
#endif
                }
          }
      seekout((unsigned long) offsetof(struct exec, a_syms));
      u4cn(buf4, (u4_t) nsym * sizeof extsym,
           memsizeof(struct exec, a_syms));
      writeout(buf4, memsizeof(struct exec, a_syms));
    }
    closeout();
    executable();
}

PRIVATE void linkmod(modptr)
struct modstruct *modptr;
{
    char buf[ABS_TEXT_MAX];
    int command;
    unsigned char modify;
    bin_off_t offset;
    int symbolnum;
    struct symstruct **symparray;
    struct symstruct *symptr;

    setseg(0);
    relocsize = 2;
    symparray = modptr->symparray;
    openin(modptr->filename); /* does nothing if already open */
    seekin(modptr->textoffset);
    while (TRUE)
    {
      if ((command = readchar()) < 0)
          prematureeof();
      modify = command & MODIFY_MASK;
      switch (command & CM_MASK)
      {
      case CM_SPECIAL:
          switch (modify)
          {
          case CM_EOT:
            segpos[curseg] = spos;
            return;
          case CM_BYTE_SIZE:
            relocsize = 1;
            break;
          case CM_WORD_SIZE:
            relocsize = 2;
            break;
          case CM_LONG_SIZE:
#ifdef LONG_OFFSETS
            relocsize = 4;
            break;
#else
            fatalerror("relocation by long offsets not implemented");
#endif
          case CM_1_SKIP:
            skip(1);
            break;
          case CM_2_SKIP:
            skip(2);
            break;
          case CM_4_SKIP:
            skip(4);
            break;
          default:
            if ((modify -= CM_0_SEG) >= NSEG)
                inputerror("bad data in");
            setseg(modify);
            break;
          }
          break;
      case CM_ABSOLUTE:
          if (modify == 0)
            modify = ABS_TEXT_MAX;
          readin(buf, (unsigned) modify);
          writeout(buf, (unsigned) modify);
          spos += (int) modify;
          break;
      case CM_OFFSET_RELOC:
          offset = readsize(relocsize);
          if (modify & R_MASK)
          {
#ifndef DATASEGS
                int m = (modify & SEGM_MASK);
              if( curseg != m && m != SEGM_MASK )
                 interseg(modptr->filename, modptr->archentry, (char*)0);
#endif
            offset -= (spos + relocsize);
            }
          offtocn(buf, segbase[modify & SEGM_MASK] + offset, relocsize);
          writeout(buf, relocsize);
          spos += relocsize;
          break;
      case CM_SYMBOL_RELOC:
          symptr = symparray[symbolnum = readconvsize((unsigned)
                                  (modify & S_MASK ? 2 : 1))];
          offset = readconvsize((unsigned) modify & OF_MASK);
          if (modify & R_MASK)
          {
#ifndef DATASEGS
                int m = (symptr->flags & SEGM_MASK);
              if( curseg != m && m != SEGM_MASK )
                 interseg(modptr->filename, modptr->archentry, symptr->name);
#endif
            offset -= (spos + relocsize);
          }
          offset += symptr->value;      
          offtocn(buf, offset, relocsize);
          writeout(buf, relocsize);
          spos += relocsize;
      }
    }
}

PRIVATE void padmod(modptr)
struct modstruct *modptr;
{
    bin_off_t count;
    fastin_t seg;
    bin_off_t size;
    unsigned sizecount;
    char *sizeptr;

    for (seg = 0, sizeptr = modptr->segsize; seg < NSEG; ++seg)
    {
      size = cntooffset(sizeptr,
                    sizecount = segsizecount((unsigned) seg, modptr));
      sizeptr += sizecount;
      if ((count = segpos[seg] - segbase[seg]) != size)
          size_error(seg, count, size);

      /* pad to quad boundary */
      /* not padding in-between common areas which sometimes get into file */
      if ((size = ld_roundup(segpos[seg], 4, bin_off_t) - segpos[seg]) != 0)
      {
          setseg(seg);
          writenulls(size);
          segpos[seg] = spos;
      }
      segbase[seg] = segpos[seg];
    }
}

PRIVATE void setsym(name, value)
char *name;
bin_off_t value;
{
    struct symstruct *symptr;

    if ((symptr = findsym(name)) != NUL_PTR)
      symptr->value = value;
}

PRIVATE void symres(name)
register char *name;
{
    register struct symstruct *symptr;

    if ((symptr = findsym(name)) != NUL_PTR)
    {
      if ((symptr->flags & SEGM_MASK) == SEGM_MASK)
          symptr->flags &= ~SEGM_MASK | curseg;
      if (symptr->flags != (I_MASK | curseg) || symptr->value != 0)
          reserved(name);
      symptr->flags = E_MASK | curseg;    /* show defined, not common */
    }
}

/* set new segment */

PRIVATE void setseg(newseg)
fastin_pt newseg;
{
    if (newseg != curseg)
    {
      segpos[curseg] = spos;
      spos = segpos[curseg = newseg];
      seekout(FILEHEADERLENGTH + (unsigned long) spos
            + (unsigned long) segadj[curseg]);
    }
}

PRIVATE void skip(countsize)
unsigned countsize;
{
    writenulls((bin_off_t) readsize(countsize));
}

#ifndef MSDOS
PRIVATE void cpm86header()
{
    struct cpm86_exec header;
    memset(&header, 0, sizeof header);

    if (sepid)
    {
      header.ce_group[0].cg_type = CG_CODE;
      u2c2(header.ce_group[0].cg_len, (15 + etextpadoff) / 16);
      u2c2(header.ce_group[0].cg_min, (15 + etextpadoff) / 16);
      header.ce_group[1].cg_type = CG_DATA;
      u2c2(header.ce_group[1].cg_len, (15 + edataoffset) / 16);
      u2c2(header.ce_group[1].cg_min, (15 + endoffset  ) / 16);
      u2c2(header.ce_group[1].cg_max, 0x1000);
    }
    else
    {
      header.ce_group[0].cg_type = CG_CODE;
      u2c2(header.ce_group[0].cg_len, (15 + edataoffset) / 16);
      u2c2(header.ce_group[0].cg_min, (15 + endoffset  ) / 16);
    }
    if( FILEHEADERLENGTH )
       writeout((char *) &header, FILEHEADERLENGTH);
}
#endif

PRIVATE void writeheader()
{
    struct exec header;

    memset(&header, 0, sizeof header);
    header.a_magic[0] = A_MAGIC0;
    header.a_magic[1] = A_MAGIC1;
    header.a_flags = sepid ? A_SEP : A_EXEC;
    if (uzp)
      header.a_flags |= A_UZP;
    header.a_cpu = bits32 ? A_I80386 : A_I8086;
    header.a_hdrlen = FILEHEADERLENGTH;
    offtocn((char *) &header.a_text, etextpadoff - btextoffset,
          sizeof header.a_text);
    offtocn((char *) &header.a_data, edataoffset - bdataoffset,
          sizeof header.a_data);
    offtocn((char *) &header.a_bss, endoffset - edataoffset,
          sizeof header.a_bss);
    if (uzp)
      offtocn((char *) &header.a_entry, page_size(),
            sizeof header.a_entry);

    offtocn((char *) &header.a_total, (bin_off_t) heap_top_value,
          sizeof header.a_total);
    if( FILEHEADERLENGTH )
       writeout((char *) &header, FILEHEADERLENGTH);
}

PRIVATE void writenulls(count)
bin_off_t count;
{
    long lcount = count;
    spos += count;
#if 0
    /* This will only work if we record the highest spos found an seek there
     * at the end of the generation
     */

    seekout(FILEHEADERLENGTH + (unsigned long) spos
       + (unsigned long) segadj[curseg]);
    return;

#endif
    if( lcount < 0 )
      fatalerror("org command requires reverse seek");
    while (count-- > 0)
      writechar(0);
}

Generated by  Doxygen 1.6.0   Back to index