diff options
Diffstat (limited to 'ld/writex86.c')
-rw-r--r-- | ld/writex86.c | 560 |
1 files changed, 560 insertions, 0 deletions
diff --git a/ld/writex86.c b/ld/writex86.c new file mode 100644 index 0000000..5dd3e8d --- /dev/null +++ b/ld/writex86.c @@ -0,0 +1,560 @@ +/* writex86.c - write binary file for linker */ + +/* Copyright (C) 1994 Bruce Evans */ + +#include "syshead.h" +#include "x86_aout.h" +#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) + +#ifdef __ELF__ +#ifndef ELF_SYMS +#define ELF_SYMS 1 +#endif +#endif + +# define FILEHEADERLENGTH (headerless?0:A_MINHDR) + /* 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 */ + +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)); +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) +char *outfilename; +bool_pt argsepid; +bool_pt argbits32; +bool_pt argstripflag; +bool_pt arguzp; +{ + 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; + 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 */ + } + curseg = 3; + symres("__edata"); + symres("__end"); + curseg = 0; /* text seg, s.b. variable */ + symres("__etext"); + if( headerless ) 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 */ + /* temp use fixed order 0D 0C 1D 1C 2D 2C ... */ + /* assume seg 0 is text and rest are data */ + 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]; + for (seg = 2; seg < NSEG; ++seg) + { + segpos[seg] = segbase[seg] = combase[seg - 1] + comsz[seg - 1]; + 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; + } + 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) + { + if (segsz[seg] != 0) + /* only count data of nonzero length */ + 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 */ + } + setsym("__etext", etextoffset); + setsym("__edata", edataoffset); + setsym("__end", endoffset = combase[NSEG - 1] + comsz[NSEG - 1]); + if( headerless ) 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); + 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, "__", 2); + strncpy((char *) extsym.n_name+2, symptr->name, + sizeof(extsym.n_name)-2); + } +#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)) + { + case 0: + extsym.n_sclass |= N_TEXT; + case A_MASK: + break; + default: + if (flags & (C_MASK | SA_MASK)) + extsym.n_sclass |= N_BSS; + else + extsym.n_sclass |= N_DATA; + break; + } + writeout((char *) &extsym, sizeof extsym); + ++nsym; + } + } + 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) + 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) + 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)); +} + +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); + 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; + + 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); +} |