diff options
Diffstat (limited to 'outobj.c')
-rw-r--r-- | outobj.c | 1229 |
1 files changed, 1229 insertions, 0 deletions
diff --git a/outobj.c b/outobj.c new file mode 100644 index 00000000..b33b72de --- /dev/null +++ b/outobj.c @@ -0,0 +1,1229 @@ +/* outobj.c output routines for the Netwide Assembler to produce + * Microsoft 16-bit .OBJ object files + * + * The Netwide Assembler is copyright (C) 1996 Simon Tatham and + * Julian Hall. All rights reserved. The software is + * redistributable under the licence given in the file "Licence" + * distributed in the NASM archive. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#include "nasm.h" +#include "nasmlib.h" +#include "outform.h" + +#ifdef OF_OBJ + +static char obj_infile[FILENAME_MAX]; +static int obj_uppercase; + +static efunc error; +static ldfunc deflabel; +static FILE *ofp; +static long first_seg; +static int any_segs; + +#define LEDATA_MAX 1024 /* maximum size of LEDATA record */ +#define RECORD_MAX 1024 /* maximum size of _any_ record */ +#define GROUP_MAX 256 /* we won't _realistically_ have more + * than this many segs in a group */ +#define EXT_BLKSIZ 256 /* block size for externals list */ + +static unsigned char record[RECORD_MAX], *recptr; + +static struct Public { + struct Public *next; + char *name; + long offset; + long segment; /* only if it's far-absolute */ +} *fpubhead, **fpubtail; + +static struct External { + struct External *next; + char *name; + long commonsize; +} *exthead, **exttail; + +static int externals; + +static struct ExtBack { + struct ExtBack *next; + int index[EXT_BLKSIZ]; +} *ebhead, **ebtail; + +static struct Segment { + struct Segment *next; + long index; /* the NASM segment id */ + long obj_index; /* the OBJ-file segment index */ + struct Group *grp; /* the group it belongs to */ + long currentpos; + long align; /* can be SEG_ABS + absolute addr */ + enum { + CMB_PRIVATE = 0, + CMB_PUBLIC = 2, + CMB_STACK = 5, + CMB_COMMON = 6 + } combine; + long use32; /* is this segment 32-bit? */ + struct Public *pubhead, **pubtail; + char *name; + char *segclass, *overlay; /* `class' is a C++ keyword :-) */ +} *seghead, **segtail, *obj_seg_needs_update; + +static struct Group { + struct Group *next; + char *name; + long index; /* NASM segment id */ + long obj_index; /* OBJ-file group index */ + long nentries; /* number of elements... */ + long nindices; /* ...and number of index elts... */ + union { + long index; + char *name; + } segs[GROUP_MAX]; /* ...in this */ +} *grphead, **grptail, *obj_grp_needs_update; + +static struct ObjData { + struct ObjData *next; + int nonempty; + struct Segment *seg; + long startpos; + int letype, ftype; + unsigned char ledata[LEDATA_MAX], *lptr; + unsigned char fixupp[RECORD_MAX], *fptr; +} *datahead, *datacurr, **datatail; + +static long obj_entry_seg, obj_entry_ofs; + +enum RecordID { /* record ID codes */ + + THEADR = 0x80, /* module header */ + COMENT = 0x88, /* comment record */ + + LNAMES = 0x96, /* list of names */ + + SEGDEF = 0x98, /* segment definition */ + GRPDEF = 0x9A, /* group definition */ + EXTDEF = 0x8C, /* external definition */ + PUBDEF = 0x90, /* public definition */ + COMDEF = 0xB0, /* common definition */ + + LEDATA = 0xA0, /* logical enumerated data */ + FIXUPP = 0x9C, /* fixups (relocations) */ + + MODEND = 0x8A /* module end */ +}; + +extern struct ofmt of_obj; + +static long obj_ledata_space(struct Segment *); +static int obj_fixup_free(struct Segment *); +static void obj_ledata_new(struct Segment *); +static void obj_ledata_commit(void); +static void obj_write_fixup (struct ObjData *, int, int, long, long, long); +static long obj_segment (char *, int, int *); +static void obj_write_file(void); +static unsigned char *obj_write_data(unsigned char *, unsigned char *, int); +static unsigned char *obj_write_byte(unsigned char *, int); +static unsigned char *obj_write_word(unsigned char *, int); +static unsigned char *obj_write_dword(unsigned char *, long); +static unsigned char *obj_write_rword(unsigned char *, int); +static unsigned char *obj_write_name(unsigned char *, char *); +static unsigned char *obj_write_index(unsigned char *, int); +static unsigned char *obj_write_value(unsigned char *, unsigned long); +static void obj_record(int, unsigned char *, unsigned char *); + +static void obj_init (FILE *fp, efunc errfunc, ldfunc ldef) { + ofp = fp; + error = errfunc; + deflabel = ldef; + first_seg = seg_alloc(); + any_segs = FALSE; + fpubhead = NULL; + fpubtail = &fpubhead; + exthead = NULL; + exttail = &exthead; + externals = 0; + ebhead = NULL; + ebtail = &ebhead; + seghead = obj_seg_needs_update = NULL; + segtail = &seghead; + grphead = obj_grp_needs_update = NULL; + grptail = &grphead; + datahead = datacurr = NULL; + datatail = &datahead; + obj_entry_seg = NO_SEG; + obj_uppercase = FALSE; +} + +static void obj_cleanup (void) { + obj_write_file(); + fclose (ofp); + while (seghead) { + struct Segment *segtmp = seghead; + seghead = seghead->next; + while (segtmp->pubhead) { + struct Public *pubtmp = segtmp->pubhead; + segtmp->pubhead = pubtmp->next; + nasm_free (pubtmp); + } + nasm_free (segtmp); + } + while (fpubhead) { + struct Public *pubtmp = fpubhead; + fpubhead = fpubhead->next; + nasm_free (pubtmp); + } + while (exthead) { + struct External *exttmp = exthead; + exthead = exthead->next; + nasm_free (exttmp); + } + while (ebhead) { + struct ExtBack *ebtmp = ebhead; + ebhead = ebhead->next; + nasm_free (ebtmp); + } + while (grphead) { + struct Group *grptmp = grphead; + grphead = grphead->next; + nasm_free (grptmp); + } + while (datahead) { + struct ObjData *datatmp = datahead; + datahead = datahead->next; + nasm_free (datatmp); + } +} + +static void obj_deflabel (char *name, long segment, + long offset, int is_global) { + /* + * We have three cases: + * + * (i) `segment' is a segment-base. If so, set the name field + * for the segment or group structure it refers to, and then + * return. + * + * (ii) `segment' is one of our segments, or a SEG_ABS segment. + * Save the label position for later output of a PUBDEF record. + * (Or a MODPUB, if we work out how.) + * + * (iii) `segment' is not one of our segments. Save the label + * position for later output of an EXTDEF, and also store a + * back-reference so that we can map later references to this + * segment number to the external index. + */ + struct External *ext; + struct ExtBack *eb; + struct Segment *seg; + int i; + + /* + * First check for the double-period, signifying something + * unusual. + */ + if (name[0] == '.' && name[1] == '.') { + if (!strcmp(name, "..start")) { + obj_entry_seg = segment; + obj_entry_ofs = offset; + } + return; + } + + /* + * Case (i): + */ + if (obj_seg_needs_update) { + obj_seg_needs_update->name = name; + return; + } else if (obj_grp_needs_update) { + obj_grp_needs_update->name = name; + return; + } + if (segment < SEG_ABS && segment != NO_SEG && segment % 2) + return; + + if (segment >= SEG_ABS) { + /* + * SEG_ABS subcase of (ii). + */ + if (is_global) { + struct Public *pub; + + pub = *fpubtail = nasm_malloc(sizeof(*pub)); + fpubtail = &pub->next; + pub->next = NULL; + pub->name = name; + pub->offset = offset; + pub->segment = segment & ~SEG_ABS; + } + return; + } + + for (seg = seghead; seg; seg = seg->next) + if (seg->index == segment) { + /* + * Case (ii). Maybe MODPUB someday? + */ + if (is_global) { + struct Public *pub; + + pub = *seg->pubtail = nasm_malloc(sizeof(*pub)); + seg->pubtail = &pub->next; + pub->next = NULL; + pub->name = name; + pub->offset = offset; + } + return; + } + + /* + * Case (iii). + */ + ext = *exttail = nasm_malloc(sizeof(*ext)); + ext->next = NULL; + exttail = &ext->next; + ext->name = name; + if (is_global == 2) + ext->commonsize = offset; + else + ext->commonsize = 0; + + i = segment/2; + eb = ebhead; + if (!eb) { + eb = *ebtail = nasm_malloc(sizeof(*eb)); + eb->next = NULL; + ebtail = &eb->next; + } + while (i > EXT_BLKSIZ) { + if (eb && eb->next) + eb = eb->next; + else { + eb = *ebtail = nasm_malloc(sizeof(*eb)); + eb->next = NULL; + ebtail = &eb->next; + } + i -= EXT_BLKSIZ; + } + eb->index[i] = ++externals; +} + +static void obj_out (long segto, void *data, unsigned long type, + long segment, long wrt) { + long size, realtype; + unsigned char *ucdata; + long ldata; + struct Segment *seg; + + /* + * handle absolute-assembly (structure definitions) + */ + if (segto == NO_SEG) { + if ((type & OUT_TYPMASK) != OUT_RESERVE) + error (ERR_NONFATAL, "attempt to assemble code in [ABSOLUTE]" + " space"); + return; + } + + /* + * If `any_segs' is still FALSE, we must define a default + * segment. + */ + if (!any_segs) { + int tempint; /* ignored */ + if (segto != obj_segment("__NASMDEFSEG", 2, &tempint)) + error (ERR_PANIC, "strange segment conditions in OBJ driver"); + } + + /* + * Find the segment we are targetting. + */ + for (seg = seghead; seg; seg = seg->next) + if (seg->index == segto) + break; + if (!seg) + error (ERR_PANIC, "code directed to nonexistent segment?"); + + size = type & OUT_SIZMASK; + realtype = type & OUT_TYPMASK; + if (realtype == OUT_RAWDATA) { + ucdata = data; + while (size > 0) { + long len = obj_ledata_space(seg); + if (len == 0) { + obj_ledata_new(seg); + len = obj_ledata_space(seg); + } + if (len > size) + len = size; + datacurr->lptr = obj_write_data (datacurr->lptr, ucdata, len); + datacurr->nonempty = TRUE; + ucdata += len; + size -= len; + seg->currentpos += len; + } + } else if (realtype == OUT_ADDRESS || realtype == OUT_REL2ADR || + realtype == OUT_REL4ADR) { + if (segment == NO_SEG && realtype != OUT_ADDRESS) + error(ERR_NONFATAL, "relative call to absolute address not" + " supported by OBJ format"); + if (segment >= SEG_ABS) + error(ERR_NONFATAL, "far-absolute relocations not supported" + " by OBJ format"); + ldata = *(long *)data; + if (realtype == OUT_REL2ADR) + ldata += (size-2); + if (realtype == OUT_REL4ADR) + ldata += (size-4); + if (obj_ledata_space(seg) < 4 || !obj_fixup_free(seg)) + obj_ledata_new(seg); + if (size == 2) + datacurr->lptr = obj_write_word (datacurr->lptr, ldata); + else + datacurr->lptr = obj_write_dword (datacurr->lptr, ldata); + datacurr->nonempty = TRUE; + if (segment != NO_SEG) + obj_write_fixup (datacurr, size, + (realtype == OUT_REL2ADR ? 0 : 0x4000), + segment, wrt, + (seg->currentpos - datacurr->startpos)); + seg->currentpos += size; + } else if (realtype == OUT_RESERVE) { + obj_ledata_commit(); + seg->currentpos += size; + } +} + +static long obj_ledata_space(struct Segment *segto) { + if (datacurr && datacurr->seg == segto) + return datacurr->ledata + LEDATA_MAX - datacurr->lptr; + else + return 0; +} + +static int obj_fixup_free(struct Segment *segto) { + if (datacurr && datacurr->seg == segto) + return (datacurr->fixupp + RECORD_MAX - datacurr->fptr) > 8; + else + return 0; +} + +static void obj_ledata_new(struct Segment *segto) { + datacurr = *datatail = nasm_malloc(sizeof(*datacurr)); + datacurr->next = NULL; + datatail = &datacurr->next; + datacurr->nonempty = FALSE; + datacurr->lptr = datacurr->ledata; + datacurr->fptr = datacurr->fixupp; + datacurr->seg = segto; + if (segto->use32) + datacurr->letype = LEDATA+1; + else + datacurr->letype = LEDATA; + datacurr->startpos = segto->currentpos; + datacurr->ftype = FIXUPP; + + datacurr->lptr = obj_write_index (datacurr->lptr, segto->obj_index); + if (datacurr->letype == LEDATA) + datacurr->lptr = obj_write_word (datacurr->lptr, segto->currentpos); + else + datacurr->lptr = obj_write_dword (datacurr->lptr, segto->currentpos); +} + +static void obj_ledata_commit(void) { + datacurr = NULL; +} + +static void obj_write_fixup (struct ObjData *data, int bytes, + int segrel, long seg, long wrt, + long offset) { + int locat, method; + int base; + long tidx, fidx; + struct Segment *s = NULL; + struct Group *g = NULL; + + locat = 0x8000 | segrel | offset; + if (seg % 2) { + base = TRUE; + locat |= 0x800; + seg--; + if (bytes != 2) + error(ERR_NONFATAL, "OBJ format can only handle 2-byte" + " segment base references"); + } else { + base = FALSE; + if (bytes == 2) + locat |= 0x400; + else { + locat |= 0x2400; + data->ftype = FIXUPP+1; /* need new-style FIXUPP record */ + } + } + data->fptr = obj_write_rword (data->fptr, locat); + + tidx = fidx = -1, method = 0; /* placate optimisers */ + + /* + * See if we can find the segment ID in our segment list. If + * so, we have a T4 (LSEG) target. + */ + for (s = seghead; s; s = s->next) + if (s->index == seg) + break; + if (s) + method = 4, tidx = s->obj_index; + else { + for (g = grphead; g; g = g->next) + if (g->index == seg) + break; + if (g) + method = 5, tidx = g->obj_index; + else { + long i = seg/2; + struct ExtBack *eb = ebhead; + while (i > EXT_BLKSIZ) { + if (eb) + eb = eb->next; + else + break; + i -= EXT_BLKSIZ; + } + if (eb) + method = 6, tidx = eb->index[i]; + else + error(ERR_PANIC, + "unrecognised segment value in obj_write_fixup"); + } + } + + /* + * If no WRT given, assume the natural default, which is method + * F5 unless we are doing an OFFSET fixup for a grouped + * segment, in which case we require F1 (group). + */ + if (wrt == NO_SEG) { + if (!base && s && s->grp) + method |= 0x10, fidx = s->grp->obj_index; + else + method |= 0x50, fidx = -1; + } else { + /* + * See if we can find the WRT-segment ID in our segment + * list. If so, we have a F0 (LSEG) frame. + */ + for (s = seghead; s; s = s->next) + if (s->index == wrt-1) + break; + if (s) + method |= 0x00, fidx = s->obj_index; + else { + for (g = grphead; g; g = g->next) + if (g->index == wrt-1) + break; + if (g) + method |= 0x10, fidx = g->obj_index; + else { + long i = wrt/2; + struct ExtBack *eb = ebhead; + while (i > EXT_BLKSIZ) { + if (eb) + eb = eb->next; + else + break; + i -= EXT_BLKSIZ; + } + if (eb) + method |= 0x20, fidx = eb->index[i]; + else + error(ERR_PANIC, + "unrecognised WRT value in obj_write_fixup"); + } + } + } + + data->fptr = obj_write_byte (data->fptr, method); + if (fidx != -1) + data->fptr = obj_write_index (data->fptr, fidx); + data->fptr = obj_write_index (data->fptr, tidx); +} + +static long obj_segment (char *name, int pass, int *bits) { + /* + * We call the label manager here to define a name for the new + * segment, and when our _own_ label-definition stub gets + * called in return, it should register the new segment name + * using the pointer it gets passed. That way we save memory, + * by sponging off the label manager. + */ + if (!name) { + *bits = 16; + return first_seg; + } else { + struct Segment *seg; + struct Group *grp; + int obj_idx, i, attrs, rn_error; + char *p; + + /* + * Look for segment attributes. + */ + attrs = 0; + p = name; + while (*p && !isspace(*p)) + p++; + if (*p) { + *p++ = '\0'; + while (*p && isspace(*p)) + *p++ = '\0'; + } + while (*p) { + while (*p && !isspace(*p)) + p++; + if (*p) { + *p++ = '\0'; + while (*p && isspace(*p)) + *p++ = '\0'; + } + + attrs++; + } + + obj_idx = 1; + for (seg = seghead; seg; seg = seg->next) { + obj_idx++; + if (!strcmp(seg->name, name)) { + if (attrs > 0 && pass == 1) + error(ERR_WARNING, "segment attributes specified on" + " redeclaration of segment: ignoring"); + if (seg->use32) + *bits = 32; + else + *bits = 16; + return seg->index; + } + } + + *segtail = seg = nasm_malloc(sizeof(*seg)); + seg->next = NULL; + segtail = &seg->next; + seg->index = (any_segs ? seg_alloc() : first_seg); + seg->obj_index = obj_idx; + seg->grp = NULL; + any_segs = TRUE; + seg->name = NULL; + seg->currentpos = 0; + seg->align = 1; /* default */ + seg->use32 = FALSE; /* default */ + seg->combine = CMB_PUBLIC; /* default */ + seg->segclass = seg->overlay = NULL; + seg->pubhead = NULL; + seg->pubtail = &seg->pubhead; + + /* + * Process the segment attributes. + */ + p = name; + while (attrs--) { + p += strlen(p); + while (!*p) p++; + + /* + * `p' contains a segment attribute. + */ + if (!nasm_stricmp(p, "private")) + seg->combine = CMB_PRIVATE; + else if (!nasm_stricmp(p, "public")) + seg->combine = CMB_PUBLIC; + else if (!nasm_stricmp(p, "common")) + seg->combine = CMB_COMMON; + else if (!nasm_stricmp(p, "stack")) + seg->combine = CMB_STACK; + else if (!nasm_stricmp(p, "use16")) + seg->use32 = FALSE; + else if (!nasm_stricmp(p, "use32")) + seg->use32 = TRUE; + else if (!nasm_strnicmp(p, "class=", 6)) + seg->segclass = nasm_strdup(p+6); + else if (!nasm_strnicmp(p, "overlay=", 8)) + seg->overlay = nasm_strdup(p+8); + else if (!nasm_strnicmp(p, "align=", 6)) { + seg->align = readnum(p+6, &rn_error); + if (rn_error) { + seg->align = 1; + error (ERR_NONFATAL, "segment alignment should be" + " numeric"); + } + switch ((int) seg->align) { + case 1: /* BYTE */ + case 2: /* WORD */ + case 4: /* DWORD */ + case 16: /* PARA */ + case 256: /* PAGE */ + break; + case 8: + error(ERR_WARNING, "OBJ format does not support alignment" + " of 8: rounding up to 16"); + seg->align = 16; + break; + case 32: + case 64: + case 128: + error(ERR_WARNING, "OBJ format does not support alignment" + " of %d: rounding up to 256", seg->align); + seg->align = 256; + break; + default: + error(ERR_NONFATAL, "invalid alignment value %d", + seg->align); + seg->align = 1; + break; + } + } else if (!nasm_strnicmp(p, "absolute=", 9)) { + seg->align = SEG_ABS + readnum(p+9, &rn_error); + if (rn_error) + error (ERR_NONFATAL, "argument to `absolute' segment" + " attribute should be numeric"); + } + } + + obj_seg_needs_update = seg; + if (seg->align >= SEG_ABS) + deflabel (name, NO_SEG, seg->align - SEG_ABS, &of_obj, error); + else + deflabel (name, seg->index+1, 0L, &of_obj, error); + obj_seg_needs_update = NULL; + + /* + * See if this segment is defined in any groups. + */ + for (grp = grphead; grp; grp = grp->next) { + for (i = grp->nindices; i < grp->nentries; i++) { + if (!strcmp(grp->segs[i].name, seg->name)) { + nasm_free (grp->segs[i].name); + grp->segs[i] = grp->segs[grp->nindices]; + grp->segs[grp->nindices++].index = seg->obj_index; + if (seg->grp) + error(ERR_WARNING, "segment `%s' is already part of" + " a group: first one takes precedence", + seg->name); + else + seg->grp = grp; + } + } + } + + if (seg->use32) + *bits = 32; + else + *bits = 16; + return seg->index; + } +} + +static int obj_directive (char *directive, char *value, int pass) { + if (!strcmp(directive, "group")) { + char *p, *q; + if (pass == 1) { + struct Group *grp; + struct Segment *seg; + int obj_idx; + + q = value; + while (*q && !isspace(*q)) + q++; + if (isspace(*q)) { + *q++ = '\0'; + while (*q && isspace(*q)) + q++; + } + if (!*q) { + error(ERR_NONFATAL, "GROUP directive contains no segments"); + return 1; + } + + obj_idx = 1; + for (grp = grphead; grp; grp = grp->next) { + obj_idx++; + if (!strcmp(grp->name, value)) { + error(ERR_NONFATAL, "group `%s' defined twice", value); + return 1; + } + } + + *grptail = grp = nasm_malloc(sizeof(*grp)); + grp->next = NULL; + grptail = &grp->next; + grp->index = seg_alloc(); + grp->obj_index = obj_idx; + grp->nindices = grp->nentries = 0; + grp->name = NULL; + + obj_grp_needs_update = grp; + deflabel (value, grp->index+1, 0L, &of_obj, error); + obj_grp_needs_update = NULL; + + while (*q) { + p = q; + while (*q && !isspace(*q)) + q++; + if (isspace(*q)) { + *q++ = '\0'; + while (*q && isspace(*q)) + q++; + } + /* + * Now p contains a segment name. Find it. + */ + for (seg = seghead; seg; seg = seg->next) + if (!strcmp(seg->name, p)) + break; + if (seg) { + /* + * We have a segment index. Shift a name entry + * to the end of the array to make room. + */ + grp->segs[grp->nentries++] = grp->segs[grp->nindices]; + grp->segs[grp->nindices++].index = seg->obj_index; + if (seg->grp) + error(ERR_WARNING, "segment `%s' is already part of" + " a group: first one takes precedence", + seg->name); + else + seg->grp = grp; + } else { + /* + * We have an as-yet undefined segment. + * Remember its name, for later. + */ + grp->segs[grp->nentries++].name = nasm_strdup(p); + } + } + } + return 1; + } + if (!strcmp(directive, "uppercase")) { + obj_uppercase = TRUE; + return 1; + } + return 0; +} + +static long obj_segbase (long segment) { + struct Segment *seg; + + /* + * Find the segment in our list. + */ + for (seg = seghead; seg; seg = seg->next) + if (seg->index == segment-1) + break; + + if (!seg) + return segment; /* not one of ours - leave it alone */ + + if (seg->align >= SEG_ABS) + return seg->align; /* absolute segment */ + if (seg->grp) + return seg->grp->index+1; /* grouped segment */ + + return segment; /* no special treatment */ +} + +static void obj_filename (char *inname, char *outname, efunc error) { + strcpy(obj_infile, inname); + standard_extension (inname, outname, ".obj", error); +} + +static void obj_write_file (void) { + struct Segment *seg; + struct Group *grp; + struct Public *pub; + struct External *ext; + struct ObjData *data; + static unsigned char boast[] = "The Netwide Assembler " NASM_VER; + int lname_idx, rectype; + + /* + * Write the THEADR module header. + */ + recptr = record; + recptr = obj_write_name (recptr, obj_infile); + obj_record (THEADR, record, recptr); + + /* + * Write the NASM boast comment. + */ + recptr = record; + recptr = obj_write_rword (recptr, 0); /* comment type zero */ + recptr = obj_write_data (recptr, boast, sizeof(boast)-1); + obj_record (COMENT, record, recptr); + + /* + * Write the first LNAMES record, containing LNAME one, which + * is null. Also initialise the LNAME counter. + */ + recptr = record; + recptr = obj_write_name (recptr, ""); + obj_record (LNAMES, record, recptr); + lname_idx = 2; + + /* + * Write the SEGDEF records. Each has an associated LNAMES + * record. + */ + for (seg = seghead; seg; seg = seg->next) { + int new_segdef; /* do we use the newer record type? */ + int acbp; + int sn, cn, on; /* seg, class, overlay LNAME idx */ + + if (seg->use32 || seg->currentpos >= 0x10000) + new_segdef = TRUE; + else + new_segdef = FALSE; + + recptr = record; + recptr = obj_write_name (recptr, seg->name); + sn = lname_idx++; + if (seg->segclass) { + recptr = obj_write_name (recptr, seg->segclass); + cn = lname_idx++; + } else + cn = 1; + if (seg->overlay) { + recptr = obj_write_name (recptr, seg->overlay); + on = lname_idx++; + } else + on = 1; + obj_record (LNAMES, record, recptr); + + acbp = (seg->combine << 2); /* C field */ + + if (seg->currentpos >= 0x10000 && !new_segdef) + acbp |= 0x02; /* B bit */ + + if (seg->use32) + acbp |= 0x01; /* P bit is Use32 flag */ + + /* A field */ + if (seg->align >= SEG_ABS) + acbp |= 0x00; + else if (seg->align >= 256) { + if (seg->align > 256) + error(ERR_NONFATAL, "segment `%s' requires more alignment" + " than OBJ format supports", seg->name); + acbp |= 0x80; + } else if (seg->align >= 16) { + acbp |= 0x60; + } else if (seg->align >= 4) { + acbp |= 0xA0; + } else if (seg->align >= 2) { + acbp |= 0x40; + } else + acbp |= 0x20; + + recptr = record; + recptr = obj_write_byte (recptr, acbp); + if (seg->align & SEG_ABS) { + recptr = obj_write_word (recptr, seg->align - SEG_ABS); + recptr = obj_write_byte (recptr, 0); + } + if (new_segdef) + recptr = obj_write_dword (recptr, seg->currentpos); + else + recptr = obj_write_word (recptr, seg->currentpos & 0xFFFF); + recptr = obj_write_index (recptr, sn); + recptr = obj_write_index (recptr, cn); + recptr = obj_write_index (recptr, on); + if (new_segdef) + obj_record (SEGDEF+1, record, recptr); + else + obj_record (SEGDEF, record, recptr); + } + + /* + * Write some LNAMES for the group names. lname_idx is left + * alone here - it will catch up when we write the GRPDEFs. + */ + recptr = record; + for (grp = grphead; grp; grp = grp->next) { + recptr = obj_write_name (recptr, grp->name); + if (recptr - record > 1024) { + obj_record (LNAMES, record, recptr); + recptr = record; + } + } + if (recptr > record) + obj_record (LNAMES, record, recptr); + + /* + * Write the GRPDEF records. + */ + for (grp = grphead; grp; grp = grp->next) { + int i; + + if (grp->nindices != grp->nentries) { + for (i = grp->nindices; i < grp->nentries; i++) { + error(ERR_NONFATAL, "group `%s' contains undefined segment" + " `%s'", grp->name, grp->segs[i].name); + nasm_free (grp->segs[i].name); + grp->segs[i].name = NULL; + } + } + recptr = record; + recptr = obj_write_index (recptr, lname_idx++); + for (i = 0; i < grp->nindices; i++) { + recptr = obj_write_byte (recptr, 0xFF); + recptr = obj_write_index (recptr, grp->segs[i].index); + } + obj_record (GRPDEF, record, recptr); + } + + /* + * Write the PUBDEF records: first the ones in the segments, + * then the far-absolutes. + */ + for (seg = seghead; seg; seg = seg->next) { + int any; + + recptr = record; + recptr = obj_write_index (recptr, seg->grp ? seg->grp->obj_index : 0); + recptr = obj_write_index (recptr, seg->obj_index); + any = FALSE; + if (seg->use32) + rectype = PUBDEF+1; + else + rectype = PUBDEF; + for (pub = seg->pubhead; pub; pub = pub->next) { + if (recptr - record + strlen(pub->name) > 1024) { + if (any) + obj_record (rectype, record, recptr); + recptr = record; + recptr = obj_write_index (recptr, 0); + recptr = obj_write_index (recptr, seg->obj_index); + } + recptr = obj_write_name (recptr, pub->name); + if (seg->use32) + recptr = obj_write_dword (recptr, pub->offset); + else + recptr = obj_write_word (recptr, pub->offset); + recptr = obj_write_index (recptr, 0); + any = TRUE; + } + if (any) + obj_record (rectype, record, recptr); + } + for (pub = fpubhead; pub; pub = pub->next) { /* pub-crawl :-) */ + recptr = record; + recptr = obj_write_index (recptr, 0); /* no group */ + recptr = obj_write_index (recptr, 0); /* no segment either */ + recptr = obj_write_word (recptr, pub->segment); + recptr = obj_write_name (recptr, pub->name); + recptr = obj_write_word (recptr, pub->offset); + recptr = obj_write_index (recptr, 0); + obj_record (PUBDEF, record, recptr); + } + + /* + * Write the EXTDEF and COMDEF records, in order. + */ + recptr = record; + for (ext = exthead; ext; ext = ext->next) { + if (ext->commonsize == 0) { + recptr = obj_write_name (recptr, ext->name); + recptr = obj_write_index (recptr, 0); + if (recptr - record > 1024) { + obj_record (EXTDEF, record, recptr); + recptr = record; + } + } else { + if (recptr > record) + obj_record (EXTDEF, record, recptr); + recptr = record; + if (ext->commonsize > 0) { + recptr = obj_write_name (recptr, ext->name); + recptr = obj_write_index (recptr, 0); + recptr = obj_write_byte (recptr, 0x61);/* far communal */ + recptr = obj_write_value (recptr, 1L); + recptr = obj_write_value (recptr, ext->commonsize); + obj_record (COMDEF, record, recptr); + } else if (ext->commonsize < 0) { + recptr = obj_write_name (recptr, ext->name); + recptr = obj_write_index (recptr, 0); + recptr = obj_write_byte (recptr, 0x62);/* near communal */ + recptr = obj_write_value (recptr, ext->commonsize); + obj_record (COMDEF, record, recptr); + } + recptr = record; + } + } + if (recptr > record) + obj_record (EXTDEF, record, recptr); + + /* + * Write a COMENT record stating that the linker's first pass + * may stop processing at this point. + */ + recptr = record; + recptr = obj_write_rword (recptr, 0x40A2); + recptr = obj_write_byte (recptr, 1); + obj_record (COMENT, record, recptr); + + /* + * Write the LEDATA/FIXUPP pairs. + */ + for (data = datahead; data; data = data->next) { + if (data->nonempty) { + obj_record (data->letype, data->ledata, data->lptr); + if (data->fptr != data->fixupp) + obj_record (FIXUPP, data->fixupp, data->fptr); + } + } + + /* + * Write the MODEND module end marker. + */ + recptr = record; + rectype = MODEND; + if (obj_entry_seg != NO_SEG) { + recptr = obj_write_byte (recptr, 0xC1); + /* + * Find the segment in the segment list. + */ + for (seg = seghead; seg; seg = seg->next) { + if (seg->index == obj_entry_seg) { + if (seg->grp) { + recptr = obj_write_byte (recptr, 0x10); + recptr = obj_write_index (recptr, seg->grp->obj_index); + } else { + recptr = obj_write_byte (recptr, 0x50); + } + recptr = obj_write_index (recptr, seg->obj_index); + if (seg->use32) { + rectype = MODEND+1; + recptr = obj_write_dword (recptr, obj_entry_ofs); + } else + recptr = obj_write_word (recptr, obj_entry_ofs); + break; + } + } + if (!seg) + error(ERR_NONFATAL, "entry point is not in this module"); + } else + recptr = obj_write_byte (recptr, 0); + obj_record (rectype, record, recptr); +} + +static unsigned char *obj_write_data(unsigned char *ptr, + unsigned char *data, int len) { + while (len--) + *ptr++ = *data++; + return ptr; +} + +static unsigned char *obj_write_byte(unsigned char *ptr, int data) { + *ptr++ = data; + return ptr; +} + +static unsigned char *obj_write_word(unsigned char *ptr, int data) { + *ptr++ = data & 0xFF; + *ptr++ = (data >> 8) & 0xFF; + return ptr; +} + +static unsigned char *obj_write_dword(unsigned char *ptr, long data) { + *ptr++ = data & 0xFF; + *ptr++ = (data >> 8) & 0xFF; + *ptr++ = (data >> 16) & 0xFF; + *ptr++ = (data >> 24) & 0xFF; + return ptr; +} + +static unsigned char *obj_write_rword(unsigned char *ptr, int data) { + *ptr++ = (data >> 8) & 0xFF; + *ptr++ = data & 0xFF; + return ptr; +} + +static unsigned char *obj_write_name(unsigned char *ptr, char *data) { + *ptr++ = strlen(data); + if (obj_uppercase) { + while (*data) { + *ptr++ = (unsigned char) toupper(*data); + data++; + } + } else { + while (*data) + *ptr++ = (unsigned char) *data++; + } + return ptr; +} + +static unsigned char *obj_write_index(unsigned char *ptr, int data) { + if (data < 128) + *ptr++ = data; + else { + *ptr++ = 0x80 | ((data >> 8) & 0x7F); + *ptr++ = data & 0xFF; + } + return ptr; +} + +static unsigned char *obj_write_value(unsigned char *ptr, + unsigned long data) { + if (data <= 128) + *ptr++ = data; + else if (data <= 0xFFFF) { + *ptr++ = 129; + *ptr++ = data & 0xFF; + *ptr++ = (data >> 8) & 0xFF; + } else if (data <= 0xFFFFFF) { + *ptr++ = 132; + *ptr++ = data & 0xFF; + *ptr++ = (data >> 8) & 0xFF; + *ptr++ = (data >> 16) & 0xFF; + } else { + *ptr++ = 136; + *ptr++ = data & 0xFF; + *ptr++ = (data >> 8) & 0xFF; + *ptr++ = (data >> 16) & 0xFF; + *ptr++ = (data >> 24) & 0xFF; + } + return ptr; +} + +static void obj_record(int type, unsigned char *start, unsigned char *end) { + unsigned long cksum, len; + + cksum = type; + fputc (type, ofp); + len = end-start+1; + cksum += (len & 0xFF) + ((len>>8) & 0xFF); + fwriteshort (len, ofp); + fwrite (start, 1, end-start, ofp); + while (start < end) + cksum += *start++; + fputc ( (-cksum) & 0xFF, ofp); +} + +struct ofmt of_obj = { + "Microsoft MS-DOS 16-bit object files", + "obj", + obj_init, + obj_out, + obj_deflabel, + obj_segment, + obj_segbase, + obj_directive, + obj_filename, + obj_cleanup +}; + +#endif /* OF_OBJ */ |