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

/* Copyright (C) 1994 Bruce Evans */

#include "syshead.h"
#include "bindef.h"
#include "const.h"
#include "obj.h"
#include "type.h"
#include "globvar.h"

#ifdef AOUT_DETECTED
#define btextoffset (text_base_value)
#define bdataoffset (data_base_value)
#define page_size() 4096

#ifdef __ELF__
#ifndef ELF_SYMS
#define ELF_SYMS 1
#endif
#endif

#ifdef EDOS
# define FILEHEADERLENGTH 0
#endif
#ifdef MINIX
# ifdef BSD_A_OUT
#  ifdef STANDARD_GNU_A_OUT
#   define HEADERLEN (sizeof(struct exec))
#  else
#   define HEADERLEN (48)
#  endif
# else
#  ifdef REL_OUTPUT
#   define HEADERLEN (reloc_output?sizeof(struct exec):A_MINHDR)
				/* part of header not counted in offsets */
#  else
#   define HEADERLEN (A_MINHDR)
#  endif
# endif
# ifndef FILEHEADERLENGTH
#  define FILEHEADERLENGTH (headerless?0:HEADERLEN)
# endif
#endif
#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 */
#ifdef REL_OUTPUT
PRIVATE unsigned ndreloc;	/* number of data relocations */
#endif
PRIVATE unsigned nsym;		/* number of symbols written */
#ifdef REL_OUTPUT
PRIVATE unsigned ntreloc;	/* number of text relocations */
extern bool_t reloc_output;	/* nonzero to leave reloc info in output */
#endif
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 */

#ifdef EDOS
FORWARD unsigned binheaderlength P((char *commandname));
FORWARD char *idconvert P((struct entrylist *elptr, char *commandname));
#endif
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));
#ifdef EDOS
FORWARD void writeheader P((char *commandname));
#else
FORWARD void writeheader P((void));
#endif
FORWARD void writenulls P((bin_off_t count));

/* write binary file */
#ifndef FUNCNAME 
#define FUNCNAME writebin
#endif

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

    sepid = argsepid;
    bits32 = argbits32;
    stripflag = argstripflag;
#ifdef REL_OUTPUT
    uzp = arguzp && !reloc_output;
#else
    uzp = arguzp;
#endif
    if (uzp)
    {
	if (btextoffset == 0)
#ifdef QMAGIC
	    btextoffset = page_size()+HEADERLEN;
#else
	    btextoffset = page_size();
#endif
	if (bdataoffset == 0 && sepid)
	    bdataoffset = page_size();
    }
#ifdef EDOS
    commandname = stralloc(outfilename);
    if ((cptr = strchr(commandname, ':')) != NUL_PTR)
	commandname = cptr + 1;
    if ((cptr = strrchr(commandname, '.')) != NUL_PTR)
	*cptr = 0;
#endif

    /* 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
    }
#ifdef EDOS
    curseg = 0;			/* data seg, s.b. variable */
#else
    curseg = 3;
#endif
    symres("__edata");
    symres("__end");
    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)
		    {
#ifdef REL_OUTPUT
			if (!reloc_output)
#endif
			    undefined(symptr->name);
		    }
		    else
		    {
#ifdef REL_OUTPUT
#if 0
			if (!reloc_output)
#else	
			if (!reloc_output || !(symptr->flags & I_MASK))
#endif			
#endif
			{
			    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));
#ifndef EDOS

		/* 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);
#endif
		cptr += sizecount;
	    }
	}

    /* calculate seg positions now their sizes are known */
    /* temp use fixed order 0D 0C 1D 1C 2D 2C ... */
    /*
#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
     */
#ifdef EDOS
    if (btextoffset == 0)
	btextoffset = binheaderlength(commandname);
#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;
    }
#ifdef QMAGIC
    else if (uzp && bdataoffset == 0)
    {
	bdataoffset = ld_roundup(etextoffset, page_size(), bin_off_t);
	etextpadoff = ld_roundup(etextoffset, page_size(), bin_off_t);
	segadj[1] += etextpadoff - bdataoffset;
    }
#endif
    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

#ifdef QMAGIC
	if(seg==3 && uzp && !stripflag) /* XXX Stripped last seek needed */
	{
	   bin_off_t val;
	   val = ld_roundup(segbase[seg]+segsz[seg], page_size(), bin_off_t);
	   segsz[seg] = val - segbase[seg];
	}
#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))
		    {
#ifdef REL_OUTPUT
#if 0
			if (!reloc_output)
#else	
			if (!reloc_output || !(symptr->flags & I_MASK))
#endif			
#endif
			    symptr->value += combase[symptr->flags & SEGM_MASK];
		    }
		    else
#ifdef REL_OUTPUT
		    if (!reloc_output || !(symptr->flags & I_MASK))
#endif
			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");
    }

    openout(outfilename);
#ifdef REL_OUTPUT
    if (reloc_output)
	seektrel(FILEHEADERLENGTH
		 + (unsigned long) (etextpadoff - btextoffset)
		 + (unsigned long) (edataoffset - bdataoffset));
#endif
#ifdef EDOS
    writeheader(commandname);
#else
    writeheader();
#endif
    for (modptr = modfirst; modptr != NUL_PTR; modptr = modptr->modnext)
	if (modptr->loadflag)
	{
	    linkmod(modptr);
	    padmod(modptr);
	}

    /* dump symbol table */
#ifdef MINIX
    if (!stripflag)
    {
#ifdef BSD_A_OUT
	unsigned stringoff;
#endif

	seekout(FILEHEADERLENGTH
		+ (unsigned long) (etextpadoff - btextoffset)
		+ (unsigned long) (edataoffset - bdataoffset)
#ifdef REL_OUTPUT
		+ ((unsigned long) ndreloc + ntreloc) * RELOC_INFO_SIZE
#endif
		);
	extsym.n_was_numaux = extsym.n_was_type = 0;
#ifdef BSD_A_OUT
	stringoff = 4;
#endif
	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)
		    {
#ifdef BSD_A_OUT
			offtocn((char *) &extsym.n_was_strx,
				(bin_off_t) stringoff, 4);
#else
#if ELF_SYMS
			if( symptr->name[0] == '_' && symptr->name[1] )
			   strncpy((char *) extsym.n_was_name, symptr->name+1,
				   sizeof extsym.n_was_name);
			else
			{
			   memcpy((char *) extsym.n_was_name, "__", 2);
			   strncpy((char *) extsym.n_was_name+2, symptr->name,
				   sizeof(extsym.n_was_name)-2);
			}
#else
			strncpy((char *) extsym.n_was_name, symptr->name,
				sizeof extsym.n_was_name);
#endif
#endif
			u4cn((char *) &extsym.n_value, (u4_t) symptr->value,
			     sizeof extsym.n_value);
			if ((flags = symptr->flags) & A_MASK)
			    extsym.n_was_sclass = N_ABS;
			else if (flags & (E_MASK | I_MASK))
			    extsym.n_was_sclass = C_EXT;
			else
			    extsym.n_was_sclass = C_STAT;
			if (!(flags & I_MASK) || (
#ifdef REL_OUTPUT
			     !reloc_output &&
#endif
			     (flags & C_MASK)))
			    switch (flags & (A_MASK | SEGM_MASK))
			    {
#ifdef DATASEGS
 			    case 0:
#else
			    default:
#endif
				extsym.n_was_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_was_sclass |= N_BSS;
				else
				    extsym.n_was_sclass |= N_DATA;
				break;
			    }
			writeout((char *) &extsym, sizeof extsym);
			++nsym;
#ifdef BSD_A_OUT
#if ELF_SYMS
			stringoff += strlen(symptr->name);
			if( symptr->name[0] != '_' || symptr->name[1] == '\0' )
			   stringoff += 3;
#else
			stringoff += strlen(symptr->name) + 1;
#endif
#endif
		    }
	    }
#ifdef BSD_A_OUT
	offtocn((char *) &extsym.n_was_strx, (bin_off_t) stringoff, 4);
	writeout((char *) &extsym.n_was_strx, 4);
	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] )
			  writeout(symptr->name + 1, strlen(symptr->name));
		       else
		       {
		          writeout("__", 2);
			  writeout(symptr->name, strlen(symptr->name) + 1);
		       }
		    }
#else
			writeout(symptr->name, strlen(symptr->name) + 1);
#endif
	    }
#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));
#ifdef REL_OUTPUT
	if( FILEHEADERLENGTH >= offsetof(struct exec, a_trsize)+8)
	{
		seekout((unsigned long) offsetof(struct exec, a_trsize));
		u4cn(buf4, (u4_t) ntreloc * RELOC_INFO_SIZE,
	     	memsizeof(struct exec, a_trsize));
		writeout(buf4, memsizeof(struct exec, a_trsize));
		seekout((unsigned long) offsetof(struct exec, a_drsize));
		u4cn(buf4, (u4_t) ndreloc * RELOC_INFO_SIZE,
	     	memsizeof(struct exec, a_drsize));
		writeout(buf4, memsizeof(struct exec, a_drsize));
	}
#endif
    }
#endif /* MINIX */
    closeout();
#ifdef REL_OUTPUT
    if (!reloc_output)
#endif
	executable();
}

#ifdef EDOS

PRIVATE unsigned binheaderlength(commandname)
char *commandname;
{
    unsigned count;
    char *name;
    struct entrylist *elptr;
    struct symstruct *startptr;

    count = 2 + 2 + 1;		/* len len nul */
    startptr = findsym("start");
    for (elptr = entryfirst; elptr != NUL_PTR; elptr = elptr->elnext)
    {
	name = idconvert(elptr, commandname);
	count += strlen(name) + 1 + 2 + 1;	/* nul off flg */
	ourfree(name);
	if (startptr != NUL_PTR)
	    count += 6;		/* LBSR $xxxx and LBRA $xxxx */
    }
    return count;
}

/* convert name of symbol (entry) list element to a Basic identifier */
/* new name is built in storage obtained from stralloc() */
/* the special name  _main  is converted to the command name first */
/* copy upper case and numerals, convert lower case to upper, ignore rest */

PRIVATE char *idconvert(elptr, commandname)
struct entrylist *elptr;
char *commandname;
{
    char *name;
    char *newname;

    if (strcmp(name = elptr->elsymptr->name, "_main") == 0)
	name = commandname;
    newname = stralloc(name);
    {
	register char *t;
	register char *s;

	t = newname;
	s = name;
	do
	{
	    if (*s >= '0' && *s <= '9' || *s >= 'A' && *s <= 'Z')
		*t++ = *s;
	    if (*s >= 'a' && *s <= 'z')
		*t++ = *s + ('A' - 'a');
	}
	while (*s++);
	*t = 0;
    }
    if (*newname < 'A')		/* numeral or null */
	fatalerror("bad entry name");
    return newname;
}

#endif /* EDOS */

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);
#ifdef REL_OUTPUT
	    if (reloc_output)
	    {
		u4_t bitfield;

		if (curseg == 0)
		{
		    ++ntreloc;
		    offtocn(buf, spos, 4);
		    writetrel(buf, 4);
		}
		else
		{
		    ++ndreloc;
		    offtocn(buf, spos - segbase[1], 4);
		    writedrel(buf, 4);
		}
		if ((modify & SEGM_MASK) == 0)
		    bitfield = N_TEXT;
		else
		    bitfield = N_DATA;
		if (modify & R_MASK)
		    bitfield |= 1L << 24;
		if (relocsize == 2)
		    bitfield |= 1L << 25;
		else if (relocsize == 4)
		    bitfield |= 1L << 26;
		u4cn(buf, bitfield, 4);
		if (curseg == 0)
		    writetrel(buf, 4);
		else
		    writedrel(buf, 4);
	    }
#endif /* REL_OUTPUT */
	    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);
	    }
#ifdef REL_OUTPUT
	    if (!reloc_output || !(symptr->flags & I_MASK))
#endif
		offset += symptr->value;	    
	    offtocn(buf, offset, relocsize);
	    writeout(buf, relocsize);
#ifdef REL_OUTPUT
	    if (reloc_output)
	    {
		u4_t bitfield;

		if (curseg == 0)
		{
		    ++ntreloc;
		    offtocn(buf, spos, 4);
		    writetrel(buf, 4);
		}
		else
		{
		    ++ndreloc;
		    offtocn(buf, spos - segbase[1], 4);
		    writedrel(buf, 4);
		}
		if (symptr->flags & I_MASK)
		    bitfield = (1L << 27) | symbolnum;
		else if ((symptr->flags & SEGM_MASK) == 0)
		    bitfield = N_TEXT;
		else if (symptr->flags & (C_MASK | SA_MASK))
		    bitfield = N_BSS;
		else
		    bitfield = N_DATA;
		if (modify & R_MASK)
		    bitfield |= 1L << 24;
		if (relocsize == 2)
		    bitfield |= 1L << 25;
		else if (relocsize == 4)
		    bitfield |= 1L << 26;
		u4cn(buf, bitfield, 4);
		if (curseg == 0)
		    writetrel(buf, 4);
		else
		    writedrel(buf, 4);
	    }
#endif /* REL_OUTPUT */
	    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;

#ifdef REL_OUTPUT
    if (!reloc_output)
#endif
	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);
#ifdef REL_OUTPUT
	if (!reloc_output)
#endif
	    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));
}

#ifdef EDOS

PRIVATE void writeheader(commandname)
char *commandname;
{
    char buf[MAX_OFFSET_SIZE];
    bin_off_t offset;
    unsigned headlength;
    char *name;
    struct entrylist *elptr;
    struct symstruct *startptr;

    headlength = binheaderlength(commandname);
    for (elptr = entryfirst; elptr != NUL_PTR; elptr = elptr->elnext)
	headlength -= 6;
    offset = headlength;
    startptr = findsym("start");
    offtocn(buf, edataoffset, 2);
    writeout(buf, 2);
    writechar(0xFF);		/* dummy data length 0xFFFF takes everything */
    writechar(0xFF);
    for (elptr = entryfirst; elptr != NUL_PTR; elptr = elptr->elnext)
    {
	name = idconvert(elptr, commandname);
	writeout(name, (unsigned) strlen(name) + 1);
	ourfree(name);
	offtocn(buf, startptr == NUL_PTR ? elptr->elsymptr->value : offset, 2);
	writeout(buf, 2);
	writechar(0x82);	/* 8 = set flags from here, 2 = cmd line */
	offset += 6;		/* LBSR $xxxx and LBRA $xxxx */
    }
    writechar(0);
    if (startptr != NUL_PTR)
    {
	offset = headlength + 3;	/* over 1st LBSR */
	for (elptr = entryfirst; elptr != NUL_PTR; elptr = elptr->elnext)
	{
	    writechar(0x17);	/* LBSR */
	    offtocn(buf, startptr->value - offset, 2);
	    writeout(buf, 2);
	    writechar(0x16);	/* LBRA */
	    offtocn(buf, elptr->elsymptr->value - offset - 3, 2);
	    writeout(buf, 2);
	    offset += 6;
	}
    }
}

#endif /* EDOS */

#ifdef MINIX

PRIVATE void writeheader()
{
    struct exec header;

    memset(&header, 0, sizeof header);
#ifdef STANDARD_GNU_A_OUT
#ifdef N_SET_MAGIC
#ifdef QMAGIC
    if(uzp)
       N_SET_MAGIC(header, QMAGIC);
    else
#endif
    N_SET_MAGIC(header, OMAGIC);
#else
    *(unsigned short *) &header.a_magic = OMAGIC;  /* XXX - works for 386BSD */
#endif
#else
    header.a_magic[0] = A_MAGIC0;
    header.a_magic[1] = A_MAGIC1;
#endif
#ifdef REL_OUTPUT
    if (!reloc_output)
#endif
    {
#ifdef STANDARD_GNU_A_OUT
#ifdef N_SET_FLAGS
	N_SET_FLAGS(header, 0);
#else
	/* XXX - works for 386BSD */
#endif
#else
	header.a_flags = sepid ? A_SEP : A_EXEC;
	if (uzp)
	    header.a_flags |= A_UZP;
#endif
    }
#ifdef BSD_A_OUT
#ifdef STANDARD_GNU_A_OUT
#ifdef N_SET_FLAGS
    N_SET_MACHTYPE(header, M_386);
#else
	/* XXX - works for 386BSD which doesn't define its own machtype :-( */
#endif
#else
    header.a_cpu = (bits32 || reloc_output) ? A_I80386 : A_I8086;
#endif
#else
    header.a_cpu = bits32 ? A_I80386 : A_I8086;
#endif
#ifndef STANDARD_GNU_A_OUT
    header.a_hdrlen = FILEHEADERLENGTH;
#endif
#ifdef QMAGIC
    if (uzp)
       offtocn((char *) &header.a_text, etextpadoff - btextoffset+HEADERLEN,
	       sizeof header.a_text);
    else
#endif
    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);

#ifdef REL_OUTPUT
    if (!reloc_output)
#endif
    {
	offtocn((char *) &header.a_entry, btextoffset,
		    sizeof header.a_entry);
#ifndef STANDARD_GNU_A_OUT
	offtocn((char *) &header.a_total, (bin_off_t)
    	    (endoffset < 0x00010000L ? 0x00010000L : endoffset + 0x0008000L),
		sizeof header.a_total);
#endif
    }
    if( FILEHEADERLENGTH )
       writeout((char *) &header, FILEHEADERLENGTH);
}

#endif /* MINIX */

PRIVATE void writenulls(count)
bin_off_t count;
{
    long lcount = count;
    if( lcount < 0 )
    	fatalerror("org command requires reverse seek");
    spos += count;
    while (count-- > 0)
	writechar(0);
}
#else

#ifndef FUNCNAME
#define FUNCNAME writebin
#endif

PUBLIC void FUNCNAME(outfilename, argsepid, argbits32, argstripflag, arguzp)
char *outfilename;
bool_pt argsepid;
bool_pt argbits32;
bool_pt argstripflag;
bool_pt arguzp;
{
    char * s  = "WARNING: Native a.out generation not included, sorry\n";
    write(2, s, strlen(s));
    write_elks(outfilename, argsepid, argbits32, argstripflag, arguzp, 0);
}
#endif
