nasm/output/outieee.c
Victor van den Elzen f568613c2a Fix BR #916647
nasm.c should respect the default debug format of the output format,
instead of replacing it with the first format in the list.
This is cleaner and allows the list to be sorted normally.

This commit rewrites commit 116994111b which was very fragile.
2008-11-05 14:19:15 +01:00

1490 lines
42 KiB
C

/* outieee.c output routines for the Netwide Assembler to produce
* IEEE-std object files
*
* The Netwide Assembler is copyright (C) 1996 Simon Tatham and
* Julian Hall. All rights reserved. The software is
* redistributable under the license given in the file "LICENSE"
* distributed in the NASM archive.
*/
/* notes: I have tried to make this correspond to the IEEE version
* of the standard, specifically the primary ASCII version. It should
* be trivial to create the binary version given this source (which is
* one of MANY things that have to be done to make this correspond to
* the hp-microtek version of the standard).
*
* 16-bit support is assumed to use 24-bit addresses
* The linker can sort out segmentation-specific stuff
* if it keeps track of externals
* in terms of being relative to section bases
*
* A non-standard variable type, the 'Yn' variable, has been introduced.
* Basically it is a reference to extern 'n'- denoting the low limit
* (L-variable) of the section that extern 'n' is defined in. Like the
* x variable, there may be no explicit assignment to it, it is derived
* from the public definition corresponding to the extern name. This
* is required because the one thing the mufom guys forgot to do well was
* take into account segmented architectures.
*
* I use comment classes for various things and these are undefined by
* the standard.
*
* Debug info should be considered totally non-standard (local labels are
* standard but linenum records are not covered by the standard.
* Type defs have the standard format but absolute meanings for ordinal
* types are not covered by the standard.)
*
* David Lindauer, LADsoft
*/
#include "compiler.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <stdarg.h> /* Note: we need the ANSI version of stdarg.h */
#include <ctype.h>
#include <inttypes.h>
#include "nasm.h"
#include "nasmlib.h"
#include "outform.h"
#ifdef OF_IEEE
#define ARRAY_BOT 0x1
static char ieee_infile[FILENAME_MAX];
static int ieee_uppercase;
static efunc error;
static ldfunc deflabel;
static FILE *ofp;
static bool any_segs;
static int arrindex;
#define HUNKSIZE 1024 /* Size of the data hunk */
#define EXT_BLKSIZ 512
#define LDPERLINE 32 /* bytes per line in output */
struct ieeeSection;
struct LineNumber {
struct LineNumber *next;
struct ieeeSection *segment;
int32_t offset;
int32_t lineno;
};
static struct FileName {
struct FileName *next;
char *name;
int32_t index;
} *fnhead, **fntail;
static struct Array {
struct Array *next;
unsigned size;
int basetype;
} *arrhead, **arrtail;
static struct ieeePublic {
struct ieeePublic *next;
char *name;
int32_t offset;
int32_t segment; /* only if it's far-absolute */
int32_t index;
int type; /* for debug purposes */
} *fpubhead, **fpubtail, *last_defined;
static struct ieeeExternal {
struct ieeeExternal *next;
char *name;
int32_t commonsize;
} *exthead, **exttail;
static int externals;
static struct ExtBack {
struct ExtBack *next;
int index[EXT_BLKSIZ];
} *ebhead, **ebtail;
/* NOTE: the first segment MUST be the lineno segment */
static struct ieeeSection {
struct ieeeObjData *data, *datacurr;
struct ieeeSection *next;
struct ieeeFixupp *fptr, *flptr;
int32_t index; /* the NASM segment id */
int32_t ieee_index; /* the OBJ-file segment index */
int32_t currentpos;
int32_t align; /* can be SEG_ABS + absolute addr */
int32_t startpos;
enum {
CMB_PRIVATE = 0,
CMB_PUBLIC = 2,
CMB_COMMON = 6
} combine;
int32_t use32; /* is this segment 32-bit? */
struct ieeePublic *pubhead, **pubtail, *lochead, **loctail;
char *name;
} *seghead, **segtail, *ieee_seg_needs_update;
struct ieeeObjData {
struct ieeeObjData *next;
uint8_t data[HUNKSIZE];
};
struct ieeeFixupp {
struct ieeeFixupp *next;
enum {
FT_SEG = 0,
FT_REL = 1,
FT_OFS = 2,
FT_EXT = 3,
FT_WRT = 4,
FT_EXTREL = 5,
FT_EXTWRT = 6,
FT_EXTSEG = 7
} ftype;
int16_t size;
int32_t id1;
int32_t id2;
int32_t offset;
int32_t addend;
};
static int32_t ieee_entry_seg, ieee_entry_ofs;
static int checksum;
extern struct ofmt of_ieee;
static void ieee_data_new(struct ieeeSection *);
static void ieee_write_fixup(int32_t, int32_t, struct ieeeSection *,
int, uint64_t, int32_t);
static void ieee_install_fixup(struct ieeeSection *, struct ieeeFixupp *);
static int32_t ieee_segment(char *, int, int *);
static void ieee_write_file(int debuginfo);
static void ieee_write_byte(struct ieeeSection *, int);
static void ieee_write_word(struct ieeeSection *, int);
static void ieee_write_dword(struct ieeeSection *, int32_t);
static void ieee_putascii(char *, ...);
static void ieee_putcs(int);
static int32_t ieee_putld(int32_t, int32_t, uint8_t *);
static int32_t ieee_putlr(struct ieeeFixupp *);
static void ieee_unqualified_name(char *, char *);
/*
* pup init
*/
static void ieee_init(FILE * fp, efunc errfunc, ldfunc ldef, evalfunc eval)
{
(void)eval;
ofp = fp;
error = errfunc;
deflabel = ldef;
any_segs = false;
fpubhead = NULL;
fpubtail = &fpubhead;
exthead = NULL;
exttail = &exthead;
externals = 1;
ebhead = NULL;
ebtail = &ebhead;
seghead = ieee_seg_needs_update = NULL;
segtail = &seghead;
ieee_entry_seg = NO_SEG;
ieee_uppercase = false;
checksum = 0;
of_ieee.current_dfmt->init(&of_ieee, NULL, fp, errfunc);
}
static int ieee_set_info(enum geninfo type, char **val)
{
(void)type;
(void)val;
return 0;
}
/*
* Rundown
*/
static void ieee_cleanup(int debuginfo)
{
ieee_write_file(debuginfo);
of_ieee.current_dfmt->cleanup();
fclose(ofp);
while (seghead) {
struct ieeeSection *segtmp = seghead;
seghead = seghead->next;
while (segtmp->pubhead) {
struct ieeePublic *pubtmp = segtmp->pubhead;
segtmp->pubhead = pubtmp->next;
nasm_free(pubtmp);
}
while (segtmp->fptr) {
struct ieeeFixupp *fixtmp = segtmp->fptr;
segtmp->fptr = fixtmp->next;
nasm_free(fixtmp);
}
while (segtmp->data) {
struct ieeeObjData *dattmp = segtmp->data;
segtmp->data = dattmp->next;
nasm_free(dattmp);
}
nasm_free(segtmp);
}
while (fpubhead) {
struct ieeePublic *pubtmp = fpubhead;
fpubhead = fpubhead->next;
nasm_free(pubtmp);
}
while (exthead) {
struct ieeeExternal *exttmp = exthead;
exthead = exthead->next;
nasm_free(exttmp);
}
while (ebhead) {
struct ExtBack *ebtmp = ebhead;
ebhead = ebhead->next;
nasm_free(ebtmp);
}
}
/*
* callback for labels
*/
static void ieee_deflabel(char *name, int32_t segment,
int64_t offset, int is_global, char *special)
{
/*
* We have three cases:
*
* (i) `segment' is a segment-base. If so, set the name field
* for the segment 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.
*
*
* (iii) `segment' is not one of our segments. Save the label
* position for later output of an EXTDEF.
*/
struct ieeeExternal *ext;
struct ExtBack *eb;
struct ieeeSection *seg;
int i;
if (special) {
error(ERR_NONFATAL, "unrecognised symbol type `%s'", special);
}
/*
* First check for the double-period, signifying something
* unusual.
*/
if (name[0] == '.' && name[1] == '.') {
if (!strcmp(name, "..start")) {
ieee_entry_seg = segment;
ieee_entry_ofs = offset;
}
return;
}
/*
* Case (i):
*/
if (ieee_seg_needs_update) {
ieee_seg_needs_update->name = name;
return;
}
if (segment < SEG_ABS && segment != NO_SEG && segment % 2)
return;
/*
* case (ii)
*/
if (segment >= SEG_ABS) {
/*
* SEG_ABS subcase of (ii).
*/
if (is_global) {
struct ieeePublic *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 && is_global; seg = seg->next)
if (seg->index == segment) {
struct ieeePublic *pub;
last_defined = pub = *seg->pubtail = nasm_malloc(sizeof(*pub));
seg->pubtail = &pub->next;
pub->next = NULL;
pub->name = name;
pub->offset = offset;
pub->index = seg->ieee_index;
pub->segment = -1;
return;
}
/*
* Case (iii).
*/
if (is_global) {
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++;
}
}
/*
* Put data out
*/
static void ieee_out(int32_t segto, const void *data,
enum out_type type, uint64_t size,
int32_t segment, int32_t wrt)
{
const uint8_t *ucdata;
int32_t ldata;
struct ieeeSection *seg;
/*
* handle absolute-assembly (structure definitions)
*/
if (segto == NO_SEG) {
if (type != 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 != ieee_segment("__NASMDEFSEG", 2, &tempint))
error(ERR_PANIC, "strange segment conditions in IEEE 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?");
if (type == OUT_RAWDATA) {
ucdata = data;
while (size--)
ieee_write_byte(seg, *ucdata++);
} else if (type == OUT_ADDRESS || type == OUT_REL2ADR ||
type == OUT_REL4ADR) {
if (segment == NO_SEG && type != OUT_ADDRESS)
error(ERR_NONFATAL, "relative call to absolute address not"
" supported by IEEE format");
ldata = *(int64_t *)data;
if (type == OUT_REL2ADR)
ldata += (size - 2);
if (type == OUT_REL4ADR)
ldata += (size - 4);
ieee_write_fixup(segment, wrt, seg, size, type, ldata);
if (size == 2)
ieee_write_word(seg, ldata);
else
ieee_write_dword(seg, ldata);
} else if (type == OUT_RESERVE) {
while (size--)
ieee_write_byte(seg, 0);
}
}
static void ieee_data_new(struct ieeeSection *segto)
{
if (!segto->data)
segto->data = segto->datacurr =
nasm_malloc(sizeof(*(segto->datacurr)));
else
segto->datacurr = segto->datacurr->next =
nasm_malloc(sizeof(*(segto->datacurr)));
segto->datacurr->next = NULL;
}
/*
* this routine is unalduterated bloatware. I usually don't do this
* but I might as well see what it is like on a harmless program.
* If anyone wants to optimize this is a good canditate!
*/
static void ieee_write_fixup(int32_t segment, int32_t wrt,
struct ieeeSection *segto, int size,
uint64_t realtype, int32_t offset)
{
struct ieeeSection *target;
struct ieeeFixupp s;
/* Don't put a fixup for things NASM can calculate */
if (wrt == NO_SEG && segment == NO_SEG)
return;
s.ftype = -1;
/* if it is a WRT offset */
if (wrt != NO_SEG) {
s.ftype = FT_WRT;
s.addend = offset;
if (wrt >= SEG_ABS)
s.id1 = -(wrt - SEG_ABS);
else {
if (wrt % 2 && realtype != OUT_REL2ADR
&& realtype != OUT_REL4ADR) {
wrt--;
for (target = seghead; target; target = target->next)
if (target->index == wrt)
break;
if (target) {
s.id1 = target->ieee_index;
for (target = seghead; target; target = target->next)
if (target->index == segment)
break;
if (target)
s.id2 = target->ieee_index;
else {
/*
* Now we assume the segment field is being used
* to hold an extern index
*/
int32_t i = segment / 2;
struct ExtBack *eb = ebhead;
while (i > EXT_BLKSIZ) {
if (eb)
eb = eb->next;
else
break;
i -= EXT_BLKSIZ;
}
/* if we have an extern decide the type and make a record
*/
if (eb) {
s.ftype = FT_EXTWRT;
s.addend = 0;
s.id2 = eb->index[i];
} else
error(ERR_NONFATAL,
"Source of WRT must be an offset");
}
} else
error(ERR_PANIC,
"unrecognised WRT value in ieee_write_fixup");
} else
error(ERR_NONFATAL, "target of WRT must be a section ");
}
s.size = size;
ieee_install_fixup(segto, &s);
return;
}
/* Pure segment fixup ? */
if (segment != NO_SEG) {
s.ftype = FT_SEG;
s.id1 = 0;
if (segment >= SEG_ABS) {
/* absolute far segment fixup */
s.id1 = -(segment - ~SEG_ABS);
} else if (segment % 2) {
/* fixup to named segment */
/* look it up */
for (target = seghead; target; target = target->next)
if (target->index == segment - 1)
break;
if (target)
s.id1 = target->ieee_index;
else {
/*
* Now we assume the segment field is being used
* to hold an extern index
*/
int32_t i = segment / 2;
struct ExtBack *eb = ebhead;
while (i > EXT_BLKSIZ) {
if (eb)
eb = eb->next;
else
break;
i -= EXT_BLKSIZ;
}
/* if we have an extern decide the type and make a record
*/
if (eb) {
if (realtype == OUT_REL2ADR || realtype == OUT_REL4ADR) {
error(ERR_PANIC,
"Segment of a rel not supported in ieee_write_fixup");
} else {
/* If we want the segment */
s.ftype = FT_EXTSEG;
s.addend = 0;
s.id1 = eb->index[i];
}
} else
/* If we get here the seg value doesn't make sense */
error(ERR_PANIC,
"unrecognised segment value in ieee_write_fixup");
}
} else {
/* Assume we are offsetting directly from a section
* So look up the target segment
*/
for (target = seghead; target; target = target->next)
if (target->index == segment)
break;
if (target) {
if (realtype == OUT_REL2ADR || realtype == OUT_REL4ADR) {
/* PC rel to a known offset */
s.id1 = target->ieee_index;
s.ftype = FT_REL;
s.size = size;
s.addend = offset;
} else {
/* We were offsetting from a seg */
s.id1 = target->ieee_index;
s.ftype = FT_OFS;
s.size = size;
s.addend = offset;
}
} else {
/*
* Now we assume the segment field is being used
* to hold an extern index
*/
int32_t i = segment / 2;
struct ExtBack *eb = ebhead;
while (i > EXT_BLKSIZ) {
if (eb)
eb = eb->next;
else
break;
i -= EXT_BLKSIZ;
}
/* if we have an extern decide the type and make a record
*/
if (eb) {
if (realtype == OUT_REL2ADR || realtype == OUT_REL4ADR) {
s.ftype = FT_EXTREL;
s.addend = 0;
s.id1 = eb->index[i];
} else {
/* else we want the external offset */
s.ftype = FT_EXT;
s.addend = 0;
s.id1 = eb->index[i];
}
} else
/* If we get here the seg value doesn't make sense */
error(ERR_PANIC,
"unrecognised segment value in ieee_write_fixup");
}
}
if (size != 2 && s.ftype == FT_SEG)
error(ERR_NONFATAL, "IEEE format can only handle 2-byte"
" segment base references");
s.size = size;
ieee_install_fixup(segto, &s);
return;
}
/* should never get here */
}
static void ieee_install_fixup(struct ieeeSection *seg,
struct ieeeFixupp *fix)
{
struct ieeeFixupp *f;
f = nasm_malloc(sizeof(struct ieeeFixupp));
memcpy(f, fix, sizeof(struct ieeeFixupp));
f->offset = seg->currentpos;
seg->currentpos += fix->size;
f->next = NULL;
if (seg->fptr)
seg->flptr = seg->flptr->next = f;
else
seg->fptr = seg->flptr = f;
}
/*
* segment registry
*/
static int32_t ieee_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;
if (!any_segs)
return 0;
return seghead->index;
} else {
struct ieeeSection *seg;
int ieee_idx, attrs;
bool rn_error;
char *p;
/*
* Look for segment attributes.
*/
attrs = 0;
while (*name == '.')
name++; /* hack, but a documented one */
p = name;
while (*p && !nasm_isspace(*p))
p++;
if (*p) {
*p++ = '\0';
while (*p && nasm_isspace(*p))
*p++ = '\0';
}
while (*p) {
while (*p && !nasm_isspace(*p))
p++;
if (*p) {
*p++ = '\0';
while (*p && nasm_isspace(*p))
*p++ = '\0';
}
attrs++;
}
ieee_idx = 1;
for (seg = seghead; seg; seg = seg->next) {
ieee_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 = seg_alloc();
seg->ieee_index = ieee_idx;
any_segs = true;
seg->name = NULL;
seg->currentpos = 0;
seg->align = 1; /* default */
seg->use32 = *bits == 32; /* default to user spec */
seg->combine = CMB_PUBLIC; /* default */
seg->pubhead = NULL;
seg->pubtail = &seg->pubhead;
seg->data = NULL;
seg->fptr = NULL;
seg->lochead = NULL;
seg->loctail = &seg->lochead;
/*
* 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, "use16"))
seg->use32 = false;
else if (!nasm_stricmp(p, "use32"))
seg->use32 = true;
else if (!nasm_strnicmp(p, "align=", 6)) {
seg->align = readnum(p + 6, &rn_error);
if (seg->align == 0)
seg->align = 1;
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 */
case 8:
case 32:
case 64:
case 128:
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");
}
}
ieee_seg_needs_update = seg;
if (seg->align >= SEG_ABS)
deflabel(name, NO_SEG, seg->align - SEG_ABS,
NULL, false, false, &of_ieee, error);
else
deflabel(name, seg->index + 1, 0L,
NULL, false, false, &of_ieee, error);
ieee_seg_needs_update = NULL;
if (seg->use32)
*bits = 32;
else
*bits = 16;
return seg->index;
}
}
/*
* directives supported
*/
static int ieee_directive(char *directive, char *value, int pass)
{
(void)value;
(void)pass;
if (!strcmp(directive, "uppercase")) {
ieee_uppercase = true;
return 1;
}
return 0;
}
/*
* Return segment data
*/
static int32_t ieee_segbase(int32_t segment)
{
struct ieeeSection *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 */
return segment; /* no special treatment */
}
/*
* filename
*/
static void ieee_filename(char *inname, char *outname, efunc error)
{
strcpy(ieee_infile, inname);
standard_extension(inname, outname, ".o", error);
}
static void ieee_write_file(int debuginfo)
{
struct tm *thetime;
time_t reltime;
struct FileName *fn;
struct ieeeSection *seg;
struct ieeePublic *pub, *loc;
struct ieeeExternal *ext;
struct ieeeObjData *data;
struct ieeeFixupp *fix;
struct Array *arr;
int i;
/*
* Write the module header
*/
ieee_putascii("MBFNASM,%02X%s.\r\n", strlen(ieee_infile), ieee_infile);
/*
* Write the NASM boast comment.
*/
ieee_putascii("CO0,%02X%s.\r\n", strlen(nasm_comment), nasm_comment);
/*
* write processor-specific information
*/
ieee_putascii("AD8,4,L.\r\n");
/*
* date and time
*/
time(&reltime);
thetime = localtime(&reltime);
ieee_putascii("DT%04d%02d%02d%02d%02d%02d.\r\n",
1900 + thetime->tm_year, thetime->tm_mon + 1,
thetime->tm_mday, thetime->tm_hour, thetime->tm_min,
thetime->tm_sec);
/*
* if debugging, dump file names
*/
for (fn = fnhead; fn && debuginfo; fn = fn->next) {
ieee_putascii("C0105,%02X%s.\r\n", strlen(fn->name), fn->name);
}
ieee_putascii("CO101,07ENDHEAD.\r\n");
/*
* the standard doesn't specify when to put checksums,
* we'll just do it periodically.
*/
ieee_putcs(false);
/*
* Write the section headers
*/
seg = seghead;
if (!debuginfo && !strcmp(seg->name, "??LINE"))
seg = seg->next;
while (seg) {
char buf[256];
char attrib;
switch (seg->combine) {
case CMB_PUBLIC:
default:
attrib = 'C';
break;
case CMB_PRIVATE:
attrib = 'S';
break;
case CMB_COMMON:
attrib = 'M';
break;
}
ieee_unqualified_name(buf, seg->name);
if (seg->align >= SEG_ABS) {
ieee_putascii("ST%X,A,%02X%s.\r\n", seg->ieee_index,
strlen(buf), buf);
ieee_putascii("ASL%X,%lX.\r\n", seg->ieee_index,
(seg->align - SEG_ABS) * 16);
} else {
ieee_putascii("ST%X,%c,%02X%s.\r\n", seg->ieee_index, attrib,
strlen(buf), buf);
ieee_putascii("SA%X,%lX.\r\n", seg->ieee_index, seg->align);
ieee_putascii("ASS%X,%X.\r\n", seg->ieee_index,
seg->currentpos);
}
seg = seg->next;
}
/*
* write the start address if there is one
*/
if (ieee_entry_seg) {
for (seg = seghead; seg; seg = seg->next)
if (seg->index == ieee_entry_seg)
break;
if (!seg)
error(ERR_PANIC, "Start address records are incorrect");
else
ieee_putascii("ASG,R%X,%lX,+.\r\n", seg->ieee_index,
ieee_entry_ofs);
}
ieee_putcs(false);
/*
* Write the publics
*/
i = 1;
for (seg = seghead; seg; seg = seg->next) {
for (pub = seg->pubhead; pub; pub = pub->next) {
char buf[256];
ieee_unqualified_name(buf, pub->name);
ieee_putascii("NI%X,%02X%s.\r\n", i, strlen(buf), buf);
if (pub->segment == -1)
ieee_putascii("ASI%X,R%X,%lX,+.\r\n", i, pub->index,
pub->offset);
else
ieee_putascii("ASI%X,%lX,%lX,+.\r\n", i, pub->segment * 16,
pub->offset);
if (debuginfo) {
if (pub->type >= 0x100)
ieee_putascii("ATI%X,T%X.\r\n", i, pub->type - 0x100);
else
ieee_putascii("ATI%X,%X.\r\n", i, pub->type);
}
i++;
}
}
pub = fpubhead;
i = 1;
while (pub) {
char buf[256];
ieee_unqualified_name(buf, pub->name);
ieee_putascii("NI%X,%02X%s.\r\n", i, strlen(buf), buf);
if (pub->segment == -1)
ieee_putascii("ASI%X,R%X,%lX,+.\r\n", i, pub->index,
pub->offset);
else
ieee_putascii("ASI%X,%lX,%lX,+.\r\n", i, pub->segment * 16,
pub->offset);
if (debuginfo) {
if (pub->type >= 0x100)
ieee_putascii("ATI%X,T%X.\r\n", i, pub->type - 0x100);
else
ieee_putascii("ATI%X,%X.\r\n", i, pub->type);
}
i++;
pub = pub->next;
}
/*
* Write the externals
*/
ext = exthead;
i = 1;
while (ext) {
char buf[256];
ieee_unqualified_name(buf, ext->name);
ieee_putascii("NX%X,%02X%s.\r\n", i++, strlen(buf), buf);
ext = ext->next;
}
ieee_putcs(false);
/*
* IEEE doesn't have a standard pass break record
* so use the ladsoft variant
*/
ieee_putascii("CO100,06ENDSYM.\r\n");
/*
* now put types
*/
i = ARRAY_BOT;
for (arr = arrhead; arr && debuginfo; arr = arr->next) {
ieee_putascii("TY%X,20,%X,%lX.\r\n", i++, arr->basetype,
arr->size);
}
/*
* now put locals
*/
i = 1;
for (seg = seghead; seg && debuginfo; seg = seg->next) {
for (loc = seg->lochead; loc; loc = loc->next) {
char buf[256];
ieee_unqualified_name(buf, loc->name);
ieee_putascii("NN%X,%02X%s.\r\n", i, strlen(buf), buf);
if (loc->segment == -1)
ieee_putascii("ASN%X,R%X,%lX,+.\r\n", i, loc->index,
loc->offset);
else
ieee_putascii("ASN%X,%lX,%lX,+.\r\n", i, loc->segment * 16,
loc->offset);
if (debuginfo) {
if (loc->type >= 0x100)
ieee_putascii("ATN%X,T%X.\r\n", i, loc->type - 0x100);
else
ieee_putascii("ATN%X,%X.\r\n", i, loc->type);
}
i++;
}
}
/*
* put out section data;
*/
seg = seghead;
if (!debuginfo && !strcmp(seg->name, "??LINE"))
seg = seg->next;
while (seg) {
if (seg->currentpos) {
int32_t size, org = 0;
data = seg->data;
ieee_putascii("SB%X.\r\n", seg->ieee_index);
fix = seg->fptr;
while (fix) {
size = HUNKSIZE - (org % HUNKSIZE);
size =
size + org >
seg->currentpos ? seg->currentpos - org : size;
size = fix->offset - org > size ? size : fix->offset - org;
org = ieee_putld(org, org + size, data->data);
if (org % HUNKSIZE == 0)
data = data->next;
if (org == fix->offset) {
org += ieee_putlr(fix);
fix = fix->next;
}
}
while (org < seg->currentpos && data) {
size =
seg->currentpos - org >
HUNKSIZE ? HUNKSIZE : seg->currentpos - org;
org = ieee_putld(org, org + size, data->data);
data = data->next;
}
ieee_putcs(false);
}
seg = seg->next;
}
/*
* module end record
*/
ieee_putascii("ME.\r\n");
}
static void ieee_write_byte(struct ieeeSection *seg, int data)
{
int temp;
if (!(temp = seg->currentpos++ % HUNKSIZE))
ieee_data_new(seg);
seg->datacurr->data[temp] = data;
}
static void ieee_write_word(struct ieeeSection *seg, int data)
{
ieee_write_byte(seg, data & 0xFF);
ieee_write_byte(seg, (data >> 8) & 0xFF);
}
static void ieee_write_dword(struct ieeeSection *seg, int32_t data)
{
ieee_write_byte(seg, data & 0xFF);
ieee_write_byte(seg, (data >> 8) & 0xFF);
ieee_write_byte(seg, (data >> 16) & 0xFF);
ieee_write_byte(seg, (data >> 24) & 0xFF);
}
static void ieee_putascii(char *format, ...)
{
char buffer[256];
int i, l;
va_list ap;
va_start(ap, format);
vsnprintf(buffer, sizeof(buffer), format, ap);
l = strlen(buffer);
for (i = 0; i < l; i++)
if ((buffer[i] & 0xff) > 31)
checksum += buffer[i];
va_end(ap);
fprintf(ofp, buffer);
}
/*
* put out a checksum record */
static void ieee_putcs(int toclear)
{
if (toclear) {
ieee_putascii("CS.\r\n");
} else {
checksum += 'C';
checksum += 'S';
ieee_putascii("CS%02X.\r\n", checksum & 127);
}
checksum = 0;
}
static int32_t ieee_putld(int32_t start, int32_t end, uint8_t *buf)
{
int32_t val;
if (start == end)
return (start);
val = start % HUNKSIZE;
/* fill up multiple lines */
while (end - start >= LDPERLINE) {
int i;
ieee_putascii("LD");
for (i = 0; i < LDPERLINE; i++) {
ieee_putascii("%02X", buf[val++]);
start++;
}
ieee_putascii(".\r\n");
}
/* if no partial lines */
if (start == end)
return (start);
/* make a partial line */
ieee_putascii("LD");
while (start < end) {
ieee_putascii("%02X", buf[val++]);
start++;
}
ieee_putascii(".\r\n");
return (start);
}
static int32_t ieee_putlr(struct ieeeFixupp *p)
{
/*
* To deal with the vagaries of segmentation the LADsoft linker
* defines two types of segments: absolute and virtual. Note that
* 'absolute' in this context is a different thing from the IEEE
* definition of an absolute segment type, which is also supported. If a
* sement is linked in virtual mode the low limit (L-var) is
* subtracted from each R,X, and P variable which appears in an
* expression, so that we can have relative offsets. Meanwhile
* in the ABSOLUTE mode this subtraction is not done and
* so we can use absolute offsets from 0. In the LADsoft linker
* this configuration is not done in the assemblker source but in
* a source the linker reads. Generally this type of thing only
* becomes an issue if real mode code is used. A pure 32-bit linker could
* get away without defining the virtual mode...
*/
char buf[40];
int32_t size = p->size;
switch (p->ftype) {
case FT_SEG:
if (p->id1 < 0)
sprintf(buf, "%"PRIX32"", -p->id1);
else
sprintf(buf, "L%"PRIX32",10,/", p->id1);
break;
case FT_OFS:
sprintf(buf, "R%"PRIX32",%"PRIX32",+", p->id1, p->addend);
break;
case FT_REL:
sprintf(buf, "R%"PRIX32",%"PRIX32",+,P,-,%X,-", p->id1, p->addend, p->size);
break;
case FT_WRT:
if (p->id2 < 0)
sprintf(buf, "R%"PRIX32",%"PRIX32",+,L%"PRIX32",+,%"PRIX32",-", p->id2, p->addend,
p->id2, -p->id1 * 16);
else
sprintf(buf, "R%"PRIX32",%"PRIX32",+,L%"PRIX32",+,L%"PRIX32",-", p->id2, p->addend,
p->id2, p->id1);
break;
case FT_EXT:
sprintf(buf, "X%"PRIX32"", p->id1);
break;
case FT_EXTREL:
sprintf(buf, "X%"PRIX32",P,-,%"PRIX32",-", p->id1, size);
break;
case FT_EXTSEG:
/* We needed a non-ieee hack here.
* We introduce the Y variable, which is the low
* limit of the native segment the extern resides in
*/
sprintf(buf, "Y%"PRIX32",10,/", p->id1);
break;
case FT_EXTWRT:
if (p->id2 < 0)
sprintf(buf, "X%"PRIX32",Y%"PRIX32",+,%"PRIX32",-", p->id2, p->id2,
-p->id1 * 16);
else
sprintf(buf, "X%"PRIX32",Y%"PRIX32",+,L%"PRIX32",-", p->id2, p->id2, p->id1);
break;
}
ieee_putascii("LR(%s,%"PRIX32").\r\n", buf, size);
return (size);
}
/* Dump all segment data (text and fixups )*/
static void ieee_unqualified_name(char *dest, char *source)
{
if (ieee_uppercase) {
while (*source)
*dest++ = toupper(*source++);
*dest = 0;
} else
strcpy(dest, source);
}
void dbgls_init(struct ofmt *of, void *id, FILE * fp, efunc error)
{
int tempint;
(void)of;
(void)id;
(void)fp;
(void)error;
fnhead = NULL;
fntail = &fnhead;
arrindex = ARRAY_BOT;
arrhead = NULL;
arrtail = &arrhead;
ieee_segment("??LINE", 2, &tempint);
any_segs = false;
}
static void dbgls_cleanup(void)
{
struct ieeeSection *segtmp;
while (fnhead) {
struct FileName *fntemp = fnhead;
fnhead = fnhead->next;
nasm_free(fntemp->name);
nasm_free(fntemp);
}
for (segtmp = seghead; segtmp; segtmp = segtmp->next) {
while (segtmp->lochead) {
struct ieeePublic *loctmp = segtmp->lochead;
segtmp->lochead = loctmp->next;
nasm_free(loctmp->name);
nasm_free(loctmp);
}
}
while (arrhead) {
struct Array *arrtmp = arrhead;
arrhead = arrhead->next;
nasm_free(arrtmp);
}
}
/*
* because this routine is not bracketed in
* the main program, this routine will be called even if there
* is no request for debug info
* so, we have to make sure the ??LINE segment is avaialbe
* as the first segment when this debug format is selected
*/
static void dbgls_linnum(const char *lnfname, int32_t lineno, int32_t segto)
{
struct FileName *fn;
struct ieeeSection *seg;
int i = 0;
if (segto == NO_SEG)
return;
/*
* If `any_segs' is still false, we must define a default
* segment.
*/
if (!any_segs) {
int tempint; /* ignored */
if (segto != ieee_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, "lineno directed to nonexistent segment?");
for (fn = fnhead; fn; fn = fn->next) {
if (!nasm_stricmp(lnfname, fn->name))
break;
i++;
}
if (!fn) {
fn = nasm_malloc(sizeof(*fn));
fn->name = nasm_malloc(strlen(lnfname) + 1);
fn->index = i;
strcpy(fn->name, lnfname);
fn->next = NULL;
*fntail = fn;
fntail = &fn->next;
}
ieee_write_byte(seghead, fn->index);
ieee_write_word(seghead, lineno);
ieee_write_fixup(segto, NO_SEG, seghead, 4, OUT_ADDRESS,
seg->currentpos);
}
static void dbgls_deflabel(char *name, int32_t segment,
int64_t offset, int is_global, char *special)
{
struct ieeeSection *seg;
/* Keep compiler from warning about special */
(void)special;
/*
* If it's a special-retry from pass two, discard it.
*/
if (is_global == 3)
return;
/*
* First check for the double-period, signifying something
* unusual.
*/
if (name[0] == '.' && name[1] == '.' && name[2] != '@') {
return;
}
/*
* Case (i):
*/
if (ieee_seg_needs_update)
return;
if (segment < SEG_ABS && segment != NO_SEG && segment % 2)
return;
if (segment >= SEG_ABS || segment == NO_SEG) {
return;
}
/*
* If `any_segs' is still false, we might need to define a
* default segment, if they're trying to declare a label in
* `first_seg'. But the label should exist due to a prior
* call to ieee_deflabel so we can skip that.
*/
for (seg = seghead; seg; seg = seg->next)
if (seg->index == segment) {
struct ieeePublic *loc;
/*
* Case (ii). Maybe MODPUB someday?
*/
if (!is_global) {
last_defined = loc = nasm_malloc(sizeof(*loc));
*seg->loctail = loc;
seg->loctail = &loc->next;
loc->next = NULL;
loc->name = nasm_strdup(name);
loc->offset = offset;
loc->segment = -1;
loc->index = seg->ieee_index;
}
}
}
static void dbgls_typevalue(int32_t type)
{
int elem = TYM_ELEMENTS(type);
type = TYM_TYPE(type);
if (!last_defined)
return;
switch (type) {
case TY_BYTE:
last_defined->type = 1; /* uint8_t */
break;
case TY_WORD:
last_defined->type = 3; /* unsigned word */
break;
case TY_DWORD:
last_defined->type = 5; /* unsigned dword */
break;
case TY_FLOAT:
last_defined->type = 9; /* float */
break;
case TY_QWORD:
last_defined->type = 10; /* qword */
break;
case TY_TBYTE:
last_defined->type = 11; /* TBYTE */
break;
default:
last_defined->type = 0x10; /* near label */
break;
}
if (elem > 1) {
struct Array *arrtmp = nasm_malloc(sizeof(*arrtmp));
int vtype = last_defined->type;
arrtmp->size = elem;
arrtmp->basetype = vtype;
arrtmp->next = NULL;
last_defined->type = arrindex++ + 0x100;
*arrtail = arrtmp;
arrtail = &(arrtmp->next);
}
last_defined = NULL;
}
static void dbgls_output(int output_type, void *param)
{
(void)output_type;
(void)param;
}
static struct dfmt ladsoft_debug_form = {
"LADsoft Debug Records",
"ladsoft",
dbgls_init,
dbgls_linnum,
dbgls_deflabel,
null_debug_routine,
dbgls_typevalue,
dbgls_output,
dbgls_cleanup,
};
static struct dfmt *ladsoft_debug_arr[3] = {
&ladsoft_debug_form,
&null_debug_form,
NULL
};
struct ofmt of_ieee = {
"IEEE-695 (LADsoft variant) object file format",
"ieee",
NULL,
ladsoft_debug_arr,
&ladsoft_debug_form,
NULL,
ieee_init,
ieee_set_info,
ieee_out,
ieee_deflabel,
ieee_segment,
ieee_segbase,
ieee_directive,
ieee_filename,
ieee_cleanup
};
#endif /* OF_IEEE */