/* SPDX-License-Identifier: BSD-2-Clause */
/* Copyright 1996-2025 The NASM Authors - All Rights Reserved */

/*
 * outobj.c	output routines for the Netwide Assembler to produce
 *		.OBJ object files
 */

#include "compiler.h"

#include <ctype.h>              /* For toupper() */
#include "nctype.h"

#include "nasm.h"
#include "nasmlib.h"
#include "asmutil.h"
#include "error.h"
#include "stdscan.h"
#include "eval.h"
#include "ver.h"

#include "outform.h"
#include "outlib.h"

#if defined(OF_OBJ) || defined(OF_OBJ2)

/*
 * outobj.c is divided into two sections.  The first section is low level
 * routines for creating obj records;  It has nearly zero NASM specific
 * code.  The second section is high level routines for processing calls and
 * data structures from the rest of NASM into obj format.
 *
 * It should be easy (though not zero work) to lift the first section out for
 * use as an obj file writer for some other assembler or compiler.
 */

/*
 * These routines are built around the ObjRecord data structure.  An ObjRecord
 * holds an object file record that may be under construction or complete.
 *
 * A major function of these routines is to support continuation of an obj
 * record into the next record when the maximum record size is exceeded.  The
 * high level code does not need to worry about where the record breaks occur.
 * It does need to do some minor extra steps to make the automatic continuation
 * work.  Those steps may be skipped for records where the high level knows no
 * continuation could be required.
 *
 * 1) An ObjRecord is allocated and cleared by obj_new, or an existing ObjRecord
 *    is cleared by obj_clear.
 *
 * 2) The caller should fill in .type.
 *
 * 3) If the record is continuable and there is processing that must be done at
 *    the start of each record then the caller should fill in .ori with the
 *    address of the record initializer routine.
 *
 * 4) If the record is continuable and it should be saved (rather than emitted
 *    immediately) as each record is done, the caller should set .up to be a
 *    pointer to a location in which the caller keeps the master pointer to the
 *    ObjRecord.  When the record is continued, the obj_bump routine will then
 *    allocate a new ObjRecord structure and update the master pointer.
 *
 * 5) If the .ori field was used then the caller should fill in the .parm with
 *    any data required by the initializer.
 *
 * 6) The caller uses the routines: obj_byte, obj_word, obj_rword, obj_dword,
 *    obj_x, obj_index, obj_value and obj_name to fill in the various kinds of
 *    data required for this record.
 *
 * 7) If the record is continuable, the caller should call obj_commit at each
 *    point where breaking the record is permitted.
 *
 * 8) To write out the record, the caller should call obj_emit2.  If the
 *    caller has called obj_commit for all data written then he can get slightly
 *    faster code by calling obj_emit instead of obj_emit2.
 *
 * Most of these routines return an ObjRecord pointer.  This will be the input
 * pointer most of the time and will be the new location if the ObjRecord
 * moved as a result of the call.  The caller may ignore the return value in
 * three cases:  It is a "Never Reallocates" routine;  or  The caller knows
 * continuation is not possible;  or  The caller uses the master pointer for the
 * next operation.
 */

#define RECORD_MAX (1024-3)     /* maximal size of any record except type+reclen */
#define OBJ_PARMS  3            /* maximum .parm used by any .ori routine */

#define FIX_08_LOW      0x8000  /* location type for various fixup subrecords */
#define FIX_16_OFFSET   0x8400
#define FIX_16_SELECTOR 0x8800
#define FIX_32_POINTER  0x8C00
#define FIX_08_HIGH     0x9000
#define FIX_32_OFFSET   0xA400
#define FIX_48_POINTER  0xAC00

enum RecordID {                 /* record ID codes */

    THEADR = 0x80,              /* module header */
    COMENT = 0x88,              /* comment record */

    LINNUM = 0x94,              /* line number 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) */
    FIXU32 = 0x9D,              /* 32-bit fixups (relocations) */

    MODEND = 0x8A,              /* module end */
    MODE32 = 0x8B               /* module end for 32-bit objects */
};

enum ComentID {                 /* ID codes for comment records */
    dTRANSL   = 0x0000,         /* translator comment */
    dOMFEXT   = 0xC0A0,         /* "OMF extension" */
    dEXTENDED = 0xC0A1,         /* translator-specific extensions */
    dLINKPASS = 0x40A2,         /* link pass 2 marker */
    dTYPEDEF  = 0xC0E3,         /* define a type */
    dSYM      = 0xC0E6,         /* symbol debug record */
    dFILNAME  = 0xC0E8,         /* file name record */
    dDEPFILE  = 0xC0E9,         /* dependency file */
    dCOMPDEF  = 0xC0EA          /* compiler type info */
};

typedef struct ObjRecord ObjRecord;
typedef void ORI(ObjRecord * orp);

struct ObjRecord {
    ORI *ori;                   /* Initialization routine           */
    int used;                   /* Current data size                */
    int committed;              /* Data size at last boundary       */
    int x_size;                 /* (see obj_x)                      */
    unsigned int type;          /* Record type                      */
    ObjRecord *child;           /* Associated record below this one */
    ObjRecord **up;             /* Master pointer to this ObjRecord */
    ObjRecord *back;            /* Previous part of this record     */
    uint32_t parm[OBJ_PARMS];      /* Parameters for ori routine       */
    uint8_t buf[RECORD_MAX + 3];
};

static void obj_fwrite(ObjRecord * orp);
static void ori_ledata(ObjRecord * orp);
static void ori_pubdef(ObjRecord * orp);
static void ori_null(ObjRecord * orp);
static ObjRecord *obj_commit(ObjRecord * orp);

static bool obj_uppercase;       /* Flag: all names in uppercase */
static bool obj_use32;           /* Flag: at least one segment is 32-bit */
static bool obj_nodepend;        /* Flag: don't emit file dependencies */

/*
 * Clear an ObjRecord structure.  (Never reallocates).
 * To simplify reuse of ObjRecord's, .type, .ori and .parm are not cleared.
 */
static ObjRecord *obj_clear(ObjRecord * orp)
{
    orp->used = 0;
    orp->committed = 0;
    orp->x_size = 0;
    orp->child = NULL;
    orp->up = NULL;
    orp->back = NULL;
    return orp;
}

/*
 * Emit an ObjRecord structure.  (Never reallocates).
 * The record is written out proceeded (recursively) by its previous part (if
 * any) and followed (recursively) by its child (if any).
 * The previous part and the child are freed.  The main ObjRecord is cleared,
 * not freed.
 */
static ObjRecord *obj_emit(ObjRecord * orp)
{
    if (orp->back) {
        obj_emit(orp->back);
        nasm_free(orp->back);
    }

    if (orp->committed)
        obj_fwrite(orp);

    if (orp->child) {
        obj_emit(orp->child);
        nasm_free(orp->child);
    }

    return obj_clear(orp);
}

/*
 * Commit and Emit a record.  (Never reallocates).
 */
static ObjRecord *obj_emit2(ObjRecord * orp)
{
    obj_commit(orp);
    return obj_emit(orp);
}

/*
 * Allocate and clear a new ObjRecord;  Also sets .ori to ori_null
 */
static ObjRecord *obj_new(void)
{
    ObjRecord *orp;

    orp = obj_clear(nasm_malloc(sizeof(ObjRecord)));
    orp->ori = ori_null;
    return orp;
}

/*
 * Advance to the next record because the existing one is full or its x_size
 * is incompatible.
 * Any uncommitted data is moved into the next record.
 */
static ObjRecord *obj_bump(ObjRecord * orp)
{
    ObjRecord *nxt;
    int used = orp->used;
    int committed = orp->committed;

    if (orp->up) {
        *orp->up = nxt = obj_new();
        nxt->ori = orp->ori;
        nxt->type = orp->type;
        nxt->up = orp->up;
        nxt->back = orp;
        memcpy(nxt->parm, orp->parm, sizeof(orp->parm));
    } else
        nxt = obj_emit(orp);

    used -= committed;
    if (used) {
        nxt->committed = 1;
        nxt->ori(nxt);
        nxt->committed = nxt->used;
        memcpy(nxt->buf + nxt->committed, orp->buf + committed, used);
        nxt->used = nxt->committed + used;
    }

    return nxt;
}

/*
 * Advance to the next record if necessary to allow the next field to fit.
 */
static ObjRecord *obj_check(ObjRecord * orp, int size)
{
    if (orp->used + size > RECORD_MAX)
        orp = obj_bump(orp);

    if (!orp->committed) {
        orp->committed = 1;
        orp->ori(orp);
        orp->committed = orp->used;
    }

    return orp;
}

/*
 * All data written so far is committed to the current record (won't be moved to
 * the next record in case of continuation).
 */
static ObjRecord *obj_commit(ObjRecord * orp)
{
    orp->committed = orp->used;
    return orp;
}

/*
 * Write a byte
 */
static ObjRecord *obj_byte(ObjRecord * orp, uint8_t val)
{
    orp = obj_check(orp, 1);
    orp->buf[orp->used] = val;
    orp->used++;
    return orp;
}

/*
 * Write a word
 */
static ObjRecord *obj_word(ObjRecord * orp, unsigned int val)
{
    orp = obj_check(orp, 2);
    orp->buf[orp->used] = val;
    orp->buf[orp->used + 1] = val >> 8;
    orp->used += 2;
    return orp;
}

/*
 * Write a reversed word
 */
static ObjRecord *obj_rword(ObjRecord * orp, unsigned int val)
{
    orp = obj_check(orp, 2);
    orp->buf[orp->used] = val >> 8;
    orp->buf[orp->used + 1] = val;
    orp->used += 2;
    return orp;
}

/*
 * Write a dword
 */
static ObjRecord *obj_dword(ObjRecord * orp, uint32_t val)
{
    orp = obj_check(orp, 4);
    orp->buf[orp->used] = val;
    orp->buf[orp->used + 1] = val >> 8;
    orp->buf[orp->used + 2] = val >> 16;
    orp->buf[orp->used + 3] = val >> 24;
    orp->used += 4;
    return orp;
}

/*
 * All fields of "size x" in one obj record must be the same size (either 16
 * bits or 32 bits).  There is a one bit flag in each record which specifies
 * which.
 * This routine is used to force the current record to have the desired
 * x_size.  x_size is normally automatic (using obj_x), so that this
 * routine should be used outside obj_x, only to provide compatibility with
 * linkers that have bugs in their processing of the size bit.
 */

static ObjRecord *obj_force(ObjRecord * orp, int x)
{
    if (orp->x_size == (x ^ 48))
        orp = obj_bump(orp);
    orp->x_size = x;
    return orp;
}

/*
 * This routine writes a field of size x.  The caller does not need to worry at
 * all about whether 16-bits or 32-bits are required.
 */
static ObjRecord *obj_x(ObjRecord * orp, uint32_t val)
{
    if (orp->type & 1)
        orp->x_size = 32;
    if (val > 0xFFFF)
        orp = obj_force(orp, 32);
    if (orp->x_size == 32) {
	ObjRecord *nxt = obj_dword(orp, val);
	nxt->x_size = 32;	/* x_size is cleared when a record overflows */
	return nxt;
    }
    orp->x_size = 16;
    return obj_word(orp, val);
}

/*
 * Writes an index
 */
static ObjRecord *obj_index(ObjRecord * orp, unsigned int val)
{
    if (val < 128)
        return obj_byte(orp, val);
    return obj_word(orp, (val >> 8) | (val << 8) | 0x80);
}

/*
 * Writes a variable length value
 */
static ObjRecord *obj_value(ObjRecord * orp, uint32_t val)
{
    if (val <= 128)
        return obj_byte(orp, val);
    if (val <= 0xFFFF) {
        orp = obj_byte(orp, 129);
        return obj_word(orp, val);
    }
    if (val <= 0xFFFFFF)
        return obj_dword(orp, (val << 8) + 132);
    orp = obj_byte(orp, 136);
    return obj_dword(orp, val);
}

/*
 * Writes a counted string
 */
static ObjRecord *obj_name(ObjRecord * orp, const char *name)
{
    int len = strlen(name);
    uint8_t *ptr;

    if (len > UINT8_MAX) {
        nasm_warn(WARN_OTHER, "truncating object name `%.64s...' to %u bytes",
                  name, UINT8_MAX);
        len = UINT8_MAX;
    }

    orp = obj_check(orp, len + 1);
    ptr = orp->buf + orp->used;
    *ptr++ = len;
    orp->used += len + 1;
    if (obj_uppercase)
        while (--len >= 0) {
            *ptr++ = toupper(*name);
            name++;
    } else
        memcpy(ptr, name, len);
    return orp;
}

/*
 * Initializer for an LEDATA record.
 * parm[0] = offset
 * parm[1] = segment index
 * During the use of a LEDATA ObjRecord, parm[0] is constantly updated to
 * represent the offset that would be required if the record were split at the
 * last commit point.
 * parm[2] is a copy of parm[0] as it was when the current record was initted.
 */
static void ori_ledata(ObjRecord * orp)
{
    obj_index(orp, orp->parm[1]);
    orp->parm[2] = orp->parm[0];
    obj_x(orp, orp->parm[0]);
}

/*
 * Initializer for a PUBDEF record.
 * parm[0] = group index
 * parm[1] = segment index
 * parm[2] = frame (only used when both indexes are zero)
 */
static void ori_pubdef(ObjRecord * orp)
{
    obj_index(orp, orp->parm[0]);
    obj_index(orp, orp->parm[1]);
    if (!(orp->parm[0] | orp->parm[1]))
        obj_word(orp, orp->parm[2]);
}

/*
 * Initializer for a LINNUM record.
 * parm[0] = group index
 * parm[1] = segment index
 */
static void ori_linnum(ObjRecord * orp)
{
    obj_index(orp, orp->parm[0]);
    obj_index(orp, orp->parm[1]);
}

/*
 * Initializer for a local vars record.
 */
static void ori_local(ObjRecord * orp)
{
    obj_rword(orp, dSYM);
}

/*
 * Null initializer for records that continue without any header info
 */
static void ori_null(ObjRecord * orp)
{
    (void)orp;                  /* Do nothing */
}

/*
 * This concludes the low level section of outobj.c
 */

static char obj_infile[FILENAME_MAX];

static int32_t first_seg;
static bool any_segs;
static int passtwo;
static int arrindex;

#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 */

struct Segment;                 /* need to know these structs exist */
struct Group;

struct LineNumber {
    struct LineNumber *next;
    struct Segment *segment;
    int32_t offset;
    int32_t lineno;
};

static struct FileName {
    struct FileName *next;
    char *name;
    struct LineNumber *lnhead, **lntail;
    int index;
} *fnhead, **fntail;

static struct Array {
    struct Array *next;
    unsigned size;
    int basetype;
} *arrhead, **arrtail;

#define ARRAYBOT 31             /* magic number  for first array index */

static struct Public {
    struct Public *next;
    char *name;
    int32_t offset;
    int32_t segment;               /* only if it's far-absolute */
    int type;                   /* only for local debug syms */
} *fpubhead, **fpubtail, *last_defined;

static struct External {
    struct External *next;
    char *name;
    int32_t commonsize;
    int32_t commonelem;            /* element size if FAR, else zero */
    int index;                  /* OBJ-file external index */
    enum {
        DEFWRT_NONE,            /* no unusual default-WRT */
        DEFWRT_STRING,          /* a string we don't yet understand */
        DEFWRT_SEGMENT,         /* a segment */
        DEFWRT_GROUP            /* a group */
    } defwrt_type;
    union {
        char *string;
        struct Segment *seg;
        struct Group *grp;
    } defwrt_ptr;
    struct External *next_dws;  /* next with DEFWRT_STRING */
} *exthead, **exttail, *dws;

static int externals;

static struct ExtBack {
    struct ExtBack *next;
    struct External *exts[EXT_BLKSIZ];
} *ebhead, **ebtail;

static struct Segment {
    struct Segment *next;
    char *name;
    int32_t index;                 /* the NASM segment id */
    int32_t obj_index;             /* the OBJ-file segment index */
    struct Group *grp;             /* the group it beint32_ts to */
    uint32_t currentpos;
    int32_t align;                /* can be SEG_ABS + absolute addr */
    uint64_t origalign;           /* originally requested alignment */
    int64_t pass_last_seen;
    struct Public *pubhead, **pubtail, *lochead, **loctail;
    char *segclass, *overlay;   /* `class' is a C++ keyword :-) */
    ObjRecord *orp;
    enum {
        CMB_PRIVATE = 0,
        CMB_PUBLIC = 2,
        CMB_STACK = 5,
        CMB_COMMON = 6
    } combine;
    bool use32;                 /* is this segment 32-bit? */
} *seghead, **segtail, *obj_seg_needs_update;

static struct Group {
    struct Group *next;
    char *name;
    int32_t index;                 /* NASM segment id */
    int32_t obj_index;             /* OBJ-file group index */
    int32_t nentries;              /* number of elements... */
    int32_t nindices;              /* ...and number of index elts... */
    union {
        int32_t index;
        char *name;
    } segs[GROUP_MAX];          /* ...in this */
} *grphead, **grptail, *obj_grp_needs_update;

static struct ImpDef {
    struct ImpDef *next;
    char *extname;
    char *libname;
    unsigned int impindex;
    char *impname;
} *imphead, **imptail;

static struct ExpDef {
    struct ExpDef *next;
    char *intname;
    char *extname;
    unsigned int ordinal;
    int flags;
} *exphead, **exptail;

#define EXPDEF_FLAG_ORDINAL  0x80
#define EXPDEF_FLAG_RESIDENT 0x40
#define EXPDEF_FLAG_NODATA   0x20
#define EXPDEF_MASK_PARMCNT  0x1F

struct SegmentToClass {
    const char *segment;        /* segment  */
    const char *segclass;       /* class    */
};

static int32_t obj_entry_seg, obj_entry_ofs;

const struct ofmt of_obj;
static const struct dfmt borland_debug_form;

/* The current segment */
static struct Segment *current_seg;

/* Name for segment to use if no segment directive is defined */
static char DEFAULT_SEG[] = "__NASMDEFSEG";

/* Conversion table from known segments to default classes */
static const struct SegmentToClass conv_table[] = {
    /* known segments,  default class */
    { "CODE",           "CODE"  },
    { "TEXT",           "CODE"  },
    { "CONST",          "CONST" },
    { "DATA",           "DATA"  },
    { "BSS",            "BSS"   },
    { "STACK",          "STACK" },
    { "CODE32",         "CODE"  },
    { "TEXT32",         "CODE"  },
    { "CONST32",        "CONST" },
    { "DATA32",         "DATA"  },
    { "BSS32",          "BSS"   },
    { "STACK32",        "STACK" },
    { NULL,             NULL    },
};

static int32_t obj_segment(char *, int *);
static void obj_write_file(void);
static enum directive_result obj_directive(enum directive, char *);

static const char *get_default_class(const char *segment)
{
    const struct SegmentToClass *conv;

    if (segment && segment[0]) {
        for (conv = conv_table; conv->segment; conv++) {
            if (!strcmp(segment, conv->segment))
                return conv->segclass;
        }
    }

    return NULL;
}

static void obj_init(void)
{
    strlcpy(obj_infile, inname, sizeof(obj_infile));
    first_seg = seg_alloc();
    any_segs = false;
    fpubhead = NULL;
    fpubtail = &fpubhead;
    exthead = NULL;
    exttail = &exthead;
    imphead = NULL;
    imptail = &imphead;
    exphead = NULL;
    exptail = &exphead;
    dws = NULL;
    externals = 0;
    ebhead = NULL;
    ebtail = &ebhead;
    seghead = obj_seg_needs_update = NULL;
    segtail = &seghead;
    grphead = obj_grp_needs_update = NULL;
    grptail = &grphead;
    obj_entry_seg = NO_SEG;
    obj_uppercase = false;
    obj_use32 = false;
    passtwo = 0;
    current_seg = NULL;

    /*
     * Convert known Unix sections to OMF segments via macros.
     */
    if (ofmt == &of_obj2) {
        char section_text[]   = ".text=TEXT32";
        char section_rodata[] = ".rodata=CONST32";
        char section_data[]   = ".data=DATA32";
        char section_bss[]    = ".bss=BSS32";

        pp_pre_define(section_text);
        pp_pre_define(section_rodata);
        pp_pre_define(section_data);
        pp_pre_define(section_bss);
    }
}

static void obj_cleanup(void)
{
    obj_write_file();
    dfmt->cleanup();
    while (seghead) {
        struct Segment *segtmp = seghead;
        seghead = seghead->next;
        while (segtmp->pubhead) {
            struct Public *pubtmp = segtmp->pubhead;
            segtmp->pubhead = pubtmp->next;
            nasm_free(pubtmp->name);
            nasm_free(pubtmp);
        }
        nasm_free(segtmp->segclass);
        nasm_free(segtmp->overlay);
        nasm_free(segtmp);
    }
    while (fpubhead) {
        struct Public *pubtmp = fpubhead;
        fpubhead = fpubhead->next;
        nasm_free(pubtmp->name);
        nasm_free(pubtmp);
    }
    while (exthead) {
        struct External *exttmp = exthead;
        exthead = exthead->next;
        nasm_free(exttmp);
    }
    while (imphead) {
        struct ImpDef *imptmp = imphead;
        imphead = imphead->next;
        nasm_free(imptmp->extname);
        nasm_free(imptmp->libname);
        nasm_free(imptmp->impname);     /* nasm_free won't mind if it's NULL */
        nasm_free(imptmp);
    }
    while (exphead) {
        struct ExpDef *exptmp = exphead;
        exphead = exphead->next;
        nasm_free(exptmp->extname);
        nasm_free(exptmp->intname);
        nasm_free(exptmp);
    }
    while (ebhead) {
        struct ExtBack *ebtmp = ebhead;
        ebhead = ebhead->next;
        nasm_free(ebtmp);
    }
    while (grphead) {
        struct Group *grptmp = grphead;
        grphead = grphead->next;
        nasm_free(grptmp);
    }
}

static void obj_ext_set_defwrt(struct External *ext, char *id)
{
    struct Segment *seg;
    struct Group *grp;

    for (seg = seghead; seg; seg = seg->next)
        if (!strcmp(seg->name, id)) {
            ext->defwrt_type = DEFWRT_SEGMENT;
            ext->defwrt_ptr.seg = seg;
            nasm_free(id);
            return;
        }

    for (grp = grphead; grp; grp = grp->next)
        if (!strcmp(grp->name, id)) {
            ext->defwrt_type = DEFWRT_GROUP;
            ext->defwrt_ptr.grp = grp;
            nasm_free(id);
            return;
        }

    ext->defwrt_type = DEFWRT_STRING;
    ext->defwrt_ptr.string = id;
    ext->next_dws = dws;
    dws = ext;
}

static void obj_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 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;
    bool used_special = false;   /* have we used the special text? */

    nasm_debug(2, " obj_deflabel: %s, seg=%"PRIx32", off=%"PRIx64", is_global=%d, %s\n",
               name, segment, offset, is_global, 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] != '@') {
        if (!strcmp(name, "..start")) {
            obj_entry_seg = segment;
            obj_entry_ofs = offset;
            return;
        }
        nasm_nonfatal("unrecognised special symbol `%s'", name);
    }

    /*
     * 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 || segment == NO_SEG) {
        /*
         * 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 = nasm_strdup(name);
            pub->offset = offset;
            pub->segment = (segment == NO_SEG ? 0 : segment & ~SEG_ABS);
        }
        if (special)
            nasm_nonfatal("OBJ supports no special symbol features"
                          " for this symbol type");
        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'.
     */
    if (!any_segs && segment == first_seg) {
        int tempint = 0;
        if (segment != obj_segment(DEFAULT_SEG, &tempint))
            nasm_panic("strange segment conditions in OBJ driver");
    }

    for (seg = seghead; seg && is_global; seg = seg->next)
        if (seg->index == segment) {
            struct Public *loc = nasm_malloc(sizeof(*loc));
            /*
             * Case (ii). Maybe MODPUB someday?
             */
            *seg->pubtail = loc;
            seg->pubtail = &loc->next;
            loc->next = NULL;
            loc->name = nasm_strdup(name);
            loc->offset = offset;

            if (special)
                nasm_nonfatal("OBJ supports no special symbol features"
                              " for this symbol type");
            return;
        }

    /*
     * Case (iii).
     */
    if (is_global) {
        ext = *exttail = nasm_malloc(sizeof(*ext));
        ext->next = NULL;
        exttail = &ext->next;
        ext->name = name;
        /* Place by default all externs into the current segment */
        ext->defwrt_type = DEFWRT_NONE;

/* 28-Apr-2002 - John Coffman
  The following code was introduced on 12-Aug-2000, and breaks fixups
  on code passed thru the MSC 5.1 linker (3.66) and MSC 6.00A linker
  (5.10).  It was introduced after FIXUP32 was added, and may be needed
  for 32-bit segments.  The following will get 16-bit segments working
  again, and maybe someone can correct the 'if' condition which is
  actually needed.
*/
#if 0
        if (current_seg) {
#else
        if (current_seg && current_seg->use32) {
            if (current_seg->grp) {
                ext->defwrt_type = DEFWRT_GROUP;
                ext->defwrt_ptr.grp = current_seg->grp;
            } else {
                ext->defwrt_type = DEFWRT_SEGMENT;
                ext->defwrt_ptr.seg = current_seg;
            }
        }
#endif

        if (is_global == 2) {
            ext->commonsize = offset;
            ext->commonelem = 1;        /* default FAR */
        } else
            ext->commonsize = 0;
    } else
        return;

    /*
     * Now process the special text, if any, to find default-WRT
     * specifications and common-variable element-size and near/far
     * specifications.
     */
    while (special && *special) {
        used_special = true;

        /*
         * We might have a default-WRT specification.
         */
        if (!nasm_strnicmp(special, "wrt", 3)) {
            char *p;
            int len;
            special += 3;
            special += strspn(special, " \t");
            p = nasm_strndup(special, len = strcspn(special, ":"));
            obj_ext_set_defwrt(ext, p);
            special += len;
            if (*special && *special != ':')
                nasm_nonfatal("`:' expected in special symbol"
                              " text for `%s'", ext->name);
            else if (*special == ':')
                special++;
        }

        /*
         * The NEAR or FAR keywords specify nearness or
         * farness. FAR gives default element size 1.
         */
        if (!nasm_strnicmp(special, "far", 3)) {
            if (ext->commonsize)
                ext->commonelem = 1;
            else
                nasm_nonfatal("`%s': `far' keyword may only be applied"
                              " to common variables\n", ext->name);
            special += 3;
            special += strspn(special, " \t");
        } else if (!nasm_strnicmp(special, "near", 4)) {
            if (ext->commonsize)
                ext->commonelem = 0;
            else
                nasm_nonfatal("`%s': `far' keyword may only be applied"
                              " to common variables\n", ext->name);
            special += 4;
            special += strspn(special, " \t");
        }

        /*
         * If it's a common, and anything else remains on the line
         * before a further colon, evaluate it as an expression and
         * use that as the element size. Forward references aren't
         * allowed.
         */
        if (*special == ':')
            special++;
        else if (*special) {
            if (ext->commonsize) {
                expr *e;
                struct tokenval tokval;

                stdscan_reset(special);
                tokval.t_type = TOKEN_INVALID;
                e = evaluate(stdscan, NULL, &tokval, NULL, 1, NULL);
                if (e) {
                    if (!is_simple(e))
                        nasm_nonfatal("cannot use relocatable"
                                      " expression as common-variable element size");
                    else
                        ext->commonelem = reloc_value(e);
                }
                special = stdscan_tell();
            } else {
                nasm_nonfatal("`%s': element-size specifications only"
                              " apply to common variables", ext->name);
                while (*special && *special != ':')
                    special++;
                if (*special == ':')
                    special++;
            }
        }
    }

    i = segment / 2;
    eb = ebhead;
    if (!eb) {
        eb = *ebtail = nasm_zalloc(sizeof(*eb));
        eb->next = NULL;
        ebtail = &eb->next;
    }
    while (i >= EXT_BLKSIZ) {
        if (eb && eb->next)
            eb = eb->next;
        else {
            eb = *ebtail = nasm_zalloc(sizeof(*eb));
            eb->next = NULL;
            ebtail = &eb->next;
        }
        i -= EXT_BLKSIZ;
    }
    eb->exts[i] = ext;
    ext->index = ++externals;

    if (special && !used_special)
        nasm_nonfatal("OBJ supports no special symbol features"
                      " for this symbol type");
}

/* forward declaration */
static void obj_write_fixup(ObjRecord * orp, int bytes,
                            int segrel, int32_t seg, int32_t wrt,
                            struct Segment *segto);

static void obj_out(const struct out_data *out)
{
    OUT_LEGACY(out,segto,data,type,size,segment,wrt);
    const uint8_t *ucdata;
    int32_t ldata;
    struct Segment *seg;
    ObjRecord *orp;

    /*
     * If `any_segs' is still false, we must define a default
     * segment.
     */
    if (!any_segs) {
        int tempint = 0;
        if (segto != obj_segment(DEFAULT_SEG, &tempint))
            nasm_panic("strange segment conditions in OBJ driver");
    }

    /*
     * Find the segment we are targeting.
     */
    for (seg = seghead; seg; seg = seg->next)
        if (seg->index == segto)
            break;
    if (!seg)
        nasm_panic("code directed to nonexistent segment?");

    orp = seg->orp;
    orp->parm[0] = seg->currentpos;

    switch (type) {
    case OUT_RAWDATA:
        ucdata = data;
        while (size > 0) {
            unsigned int len;
            orp = obj_check(seg->orp, 1);
            len = RECORD_MAX - orp->used;
            if (len > size)
                len = size;
            memcpy(orp->buf + orp->used, ucdata, len);
            orp->committed = orp->used += len;
            orp->parm[0] = seg->currentpos += len;
            ucdata += len;
            size -= len;
        }
	break;

    case OUT_ADDRESS:
    case OUT_REL1ADR:
    case OUT_REL2ADR:
    case OUT_REL4ADR:
    case OUT_REL8ADR:
    {
        int rsize;

        if (type == OUT_ADDRESS)
            size = abs((int)size);

        if (segment == NO_SEG && type != OUT_ADDRESS)
            nasm_nonfatal("relative call to absolute address not"
                          " supported by OBJ format");
        if (segment >= SEG_ABS)
            nasm_nonfatal("far-absolute relocations not supported"
                          " by OBJ format");

        ldata = *(int64_t *)data;
        if (type != OUT_ADDRESS) {
	    /*
	     * For 16-bit and 32-bit x86 code, the size and realsize() always
	     * matches as only jumps, calls and loops uses PC relative
	     * addressing and the address isn't followed by any other opcode
	     * bytes.  In 64-bit mode there is RIP relative addressing which
	     * means the fixup location can be followed by an immediate value,
	     * meaning that size > realsize().
	     *
	     * When the CPU is calculating the effective address, it takes the
	     * RIP at the end of the instruction and adds the fixed up relative
	     * address value to it.
	     *
	     * The linker's point of reference is the end of the fixup location
	     * (which is the end of the instruction for Jcc, CALL, LOOP[cc]).
	     * It is calculating distance between the target symbol and the end
	     * of the fixup location, and add this to the displacement value we
	     * are calculating here and storing at the fixup location.
	     *
	     * To get the right effect, we need to _reduce_ the displacement
	     * value by the number of bytes following the fixup.
	     *
	     * Example:
	     *  data at address 0x100; REL4ADR at 0x050, 4 byte immediate,
	     *  end of fixup at 0x054, end of instruction at 0x058.
	     *  => size = 8.
	     *  => realsize() -> 4
	     *  => CPU needs a value of:   0x100 - 0x058 = 0x0a8
	     *  => linker/loader will add: 0x100 - 0x054 = 0x0ac
	     *  => We must add an addend of -4.
	     *  => realsize() - size = -4.
	     *
	     * The code used to do size - realsize() at least since v0.90,
	     * probably because it wasn't needed...
	     */
	    ldata -= size;
	    size = realsize(type, size);
	    ldata += size;
        }

	switch (size) {
	default:
            nasm_nonfatal("OBJ format can only handle 16- or "
                          "32-byte relocations");
	    segment = NO_SEG;	/* Don't actually generate a relocation */
	    break;
	case 2:
            orp = obj_word(orp, ldata);
	    break;
	case 4:
            orp = obj_dword(orp, ldata);
	    break;
	}

        rsize = size;
        if (segment < SEG_ABS && (segment != NO_SEG && segment % 2) &&
            size == 4) {
            /*
             * This is a 4-byte segment-base relocation such as
             * `MOV EAX,SEG foo'. OBJ format can't actually handle
             * these, but if the constant term has the 16 low bits
             * zero, we can just apply a 2-byte segment-base
             * relocation to the low word instead.
             */
            rsize = 2;
            if (ldata & 0xFFFF)
                nasm_nonfatal("OBJ format cannot handle complex"
                              " dword-size segment base references");
        }
        if (segment != NO_SEG)
            obj_write_fixup(orp, rsize,
                            (type == OUT_ADDRESS ? 0x4000 : 0),
                            segment, wrt, seg);
        seg->currentpos += size;
	break;
    }

    default:
        nasm_nonfatal("Relocation type not supported by output format");
	/* fall through */

    case OUT_RESERVE:
        if (orp->committed)
            orp = obj_bump(orp);
        seg->currentpos += size;
	break;
    }
    obj_commit(orp);
}

static void obj_write_fixup(ObjRecord * orp, int bytes,
                            int segrel, int32_t seg, int32_t wrt,
                            struct Segment *segto)
{
    unsigned locat;
    int method;
    int base;
    int32_t tidx, fidx;
    struct Segment *s = NULL;
    struct Group *g = NULL;
    struct External *e = NULL;
    ObjRecord *forp;

    if (bytes != 2 && bytes != 4) {
        nasm_nonfatal("`obj' output driver does not support"
                      " %d-bit relocations", bytes << 3);
        return;
    }

    forp = orp->child;
    if (forp == NULL) {
        orp->child = forp = obj_new();
        forp->up = &(orp->child);
        /* We should choose between FIXUPP and FIXU32 record type */
        /* If we're targeting a 32-bit segment, use a FIXU32 record */
        if (segto->use32)
            forp->type = FIXU32;
        else
            forp->type = FIXUPP;
    }

    if (seg % 2) {
        base = true;
        locat = FIX_16_SELECTOR;
        seg--;
        if (bytes != 2)
            nasm_panic("OBJ: 4-byte segment base fixup got"
                  " through sanity check");
    } else {
        base = false;
        locat = (bytes == 2) ? FIX_16_OFFSET : FIX_32_OFFSET;
        if (!segrel)
            /*
             * There is a bug in tlink that makes it process self relative
             * fixups incorrectly if the x_size doesn't match the location
             * size.
             */
            forp = obj_force(forp, bytes << 3);
    }

    forp = obj_rword(forp, locat | segrel | (orp->parm[0] - orp->parm[2]));

    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 {
            int32_t 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, e = eb->exts[i], tidx = e->index;
            else
                nasm_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).
     *
     * - we are doing an OFFSET fixup for an external with a
     *   default WRT, in which case we must honour the default WRT.
     */
    if (wrt == NO_SEG) {
        if (!base && s && s->grp)
            method |= 0x10, fidx = s->grp->obj_index;
        else if (!base && e && e->defwrt_type != DEFWRT_NONE) {
            if (e->defwrt_type == DEFWRT_SEGMENT)
                method |= 0x00, fidx = e->defwrt_ptr.seg->obj_index;
            else if (e->defwrt_type == DEFWRT_GROUP)
                method |= 0x10, fidx = e->defwrt_ptr.grp->obj_index;
            else {
                nasm_nonfatal("default WRT specification for"
                              " external `%s' unresolved", e->name);
                method |= 0x50, fidx = -1;      /* got to do _something_ */
            }
        } 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 {
                int32_t 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->exts[i]->index;
                else
                    nasm_panic("unrecognised WRT value in obj_write_fixup");
            }
        }
    }

    forp = obj_byte(forp, method);
    if (fidx != -1)
        forp = obj_index(forp, fidx);
    forp = obj_index(forp, tidx);
    obj_commit(forp);
}

static uint32_t check_segment_alignment(const uint64_t origalign)
{
    /* Supported alignment values */
    const uint32_t alignments = 1 | 2 | 4 | 16 | 256 | 4096;
    uint32_t align = origalign;

    if (origalign &&
        (!is_power2(origalign) || origalign > alignments)) {
        nasm_nonfatal("invalid alignment value %"PRIu64, origalign);
        return 1;
    }

    if (align == 0)
        align = 1;

    if (align & alignments)
        return align;           /* All good! */

    while (!(align & alignments))
        align <<= 1;

    nasm_warn(WARN_SECTION_ALIGNMENT_ROUNDED,
              "alignment of %"PRIu64" not supported, using %"PRIu32,
              origalign, align);

    return align;
}

static int32_t obj_segment(char *name, 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.
     */
    nasm_debug(3, " obj_segment: < %s >, *bits=%d\n", name, *bits);

    if (!name) {
        *bits = 16;
        current_seg = NULL;
        return first_seg;
    } else {
        struct Segment *seg;
        struct Group *grp;
        struct External **extp;
        int obj_idx, i;
	bool rn_error;
        char *p;

        /*
         * Look for segment attributes.
         */
        while (*name == '.')
            name++;             /* hack, but a documented one */
        p = nasm_skip_word(name);
        if (*p) {
            *p++ = '\0';
            p = nasm_skip_spaces(p);
        }

        for (seg = seghead, obj_idx = 1; ; seg = seg->next, obj_idx++) {
            if (!seg)
                break;

            if (!strcmp(seg->name, name)) {
                if (seg->pass_last_seen == pass_count()) {
                    if (*p)
                        nasm_warn(WARN_OTHER, "segment attributes specified on"
                                  " redeclaration of segment: ignoring");
                } else {
                    /* Reissue alignment warning on this pass if necessary */
                    if (seg->align < SEG_ABS)
                        check_segment_alignment(seg->origalign);
                }

                if (seg->use32)
                    *bits = 32;
                else
                    *bits = 16;
                current_seg = seg;
                seg->pass_last_seen = pass_count();
                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 = nasm_strdup(name);
        seg->currentpos = 0;
        if (ofmt == &of_obj) {
            seg->align = 1;         /* default for obj */
            seg->origalign = 1;     /* default for obj */
            seg->use32 = false;     /* default for obj */
        } else {
            seg->align = 16;        /* default for obj2 */
            seg->origalign = 16;    /* default for obj2 */
            seg->use32 = true;      /* default for obj2 */
        }
        seg->combine = CMB_PUBLIC;      /* default */
        seg->segclass = seg->overlay = NULL;
        seg->pubhead = NULL;
        seg->pubtail = &seg->pubhead;
        seg->lochead = NULL;
        seg->loctail = &seg->lochead;
        seg->orp = obj_new();
        seg->orp->up = &(seg->orp);
        seg->orp->ori = ori_ledata;
        seg->orp->type = LEDATA;
        seg->orp->parm[1] = obj_idx;

        /*
         * Process the segment attributes.
         */
        while (*p) {
            const char *q = p;
            p = nasm_skip_word(p);
            if (*p) {
                *p++ = '\0';
                p = nasm_skip_spaces(p);
            }

            /*
             * `p' contains a segment attribute.
             */
            if (!nasm_stricmp(q, "private"))
                seg->combine = CMB_PRIVATE;
            else if (!nasm_stricmp(q, "public"))
                seg->combine = CMB_PUBLIC;
            else if (!nasm_stricmp(q, "common"))
                seg->combine = CMB_COMMON;
            else if (!nasm_stricmp(q, "stack"))
                seg->combine = CMB_STACK;
            else if (!nasm_stricmp(q, "use16"))
                seg->use32 = false;
            else if (!nasm_stricmp(q, "use32"))
                seg->use32 = true;
            else if (!nasm_stricmp(q, "flat")) {
                /*
                 * This segment is an OS/2 FLAT segment. That means
                 * that its default group is group FLAT, even if
                 * the group FLAT does not explicitly _contain_ the
                 * segment.
                 *
                 * When we see this, we must create the group
                 * `FLAT', containing no segments, if it does not
                 * already exist; then we must set the default
                 * group of this segment to be the FLAT group.
                 */
                struct Group *grp;
                for (grp = grphead; grp; grp = grp->next)
                    if (!strcmp(grp->name, "FLAT"))
                        break;
                if (!grp) {
                    obj_directive(D_GROUP, "FLAT");
                    for (grp = grphead; grp; grp = grp->next)
                        if (!strcmp(grp->name, "FLAT"))
                            break;
                    if (!grp)
                        nasm_panic("failure to define FLAT?!");
                }
                seg->grp = grp;
            } else if (!nasm_strnicmp(q, "class=", 6))
                seg->segclass = nasm_strdup(q + 6);
            else if (!nasm_strnicmp(q, "overlay=", 8))
                seg->overlay = nasm_strdup(q + 8);
            else if (!nasm_strnicmp(q, "align=", 6)) {
                uint64_t n = readnum(q + 6, &rn_error);
                if (rn_error) {
                    nasm_nonfatal("segment alignment should be numeric");
                    n = 1;
                }
                seg->origalign = n;
                seg->align = check_segment_alignment(n);
            } else if (!nasm_strnicmp(q, "absolute=", 9)) {
                uint64_t n = readnum(q + 9, &rn_error);
                if (rn_error) {
                    nasm_nonfatal("argument to `absolute' segment"
                                  " attribute should be numeric");
                    n = 0;
                } else if (n >= SEG_ABS) {
                    /* Probably could even be max 64K? */
                    nasm_nonfatal("unsupported `absolute' segment number %#"PRIx64, n);
                    n = 0;
                }
                seg->align = SEG_ABS + n;
            }
        }

        if (!seg->use32 && seg->grp && !strcmp(seg->grp->name, "FLAT"))
           nasm_panic("wrong combination of USE16(16-bit segment) and FLAT");

        if (ofmt == &of_obj2) {
            if (seg->use32 && !seg->grp) {
                struct Group *grp;
                for (grp = grphead; grp; grp = grp->next)
                    if (!strcmp(grp->name, "FLAT"))
                        break;
                if (!grp) {
                    obj_directive(D_GROUP, "FLAT");
                    for (grp = grphead; grp; grp = grp->next)
                        if (!strcmp(grp->name, "FLAT"))
                            break;
                    if (!grp)
                        nasm_panic("failure to define FLAT?!");
                }
                seg->grp = grp;
            }

            if (!seg->segclass) {
                const char *segclass = get_default_class(seg->name);

                if (segclass)
                    seg->segclass = nasm_strdup(segclass);
            }
        }

        /* We need to know whenever we have at least one 32-bit segment */
        obj_use32 |= seg->use32;

        /*
         * Trying to create a symbol on the last pass will end in tears.
         * This can happen if there is no SEGMENT directive, and there
         * are no labels.
         */
        if (!pass_final()) {
            obj_seg_needs_update = seg;
            if (seg->align >= SEG_ABS)
                define_label(name, NO_SEG, seg->align - SEG_ABS, false);
            else
                define_label(name, seg->index + 1, 0L, false);
            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;
                    /*
                     * The group FLAT is a pseudo group. Therefore, it is
                     * allowed to redefine a segment in the group FLAT as
                     * other group.
                     */
                    if (seg->grp && strcmp(seg->grp->name, "FLAT"))
                        nasm_warn(WARN_OTHER, "segment `%s' is already part of"
                                  " a group: first one takes precedence",
                                  seg->name);
                    else
                        seg->grp = grp;
                }
            }
        }

        /*
         * Walk through the list of externals with unresolved
         * default-WRT clauses, and resolve any that point at this
         * segment.
         */
        extp = &dws;
        while (*extp) {
            if ((*extp)->defwrt_type == DEFWRT_STRING &&
                !strcmp((*extp)->defwrt_ptr.string, seg->name)) {
                nasm_free((*extp)->defwrt_ptr.string);
                (*extp)->defwrt_type = DEFWRT_SEGMENT;
                (*extp)->defwrt_ptr.seg = seg;
                *extp = (*extp)->next_dws;
            } else
                extp = &(*extp)->next_dws;
        }

        if (seg->use32)
            *bits = 32;
        else
            *bits = 16;
        current_seg = seg;
        return seg->index;
    }
}

static enum directive_result
obj_directive(enum directive directive, char *value)
{
    switch (directive) {
    case D_GROUP:
        /* Is pass_first() really correct? */
        if (value && pass_first()) {
            char *p, *q, *v;
            struct Group *grp;
            struct Segment *seg;
            struct External **extp;
            int obj_idx;
            const char *segname;
            int i;

            q = value;
            while (*q == '.')
                q++;            /* hack, but a documented one */
            v = q;
            while (*q && !nasm_isspace(*q))
                q++;
            if (nasm_isspace(*q)) {
                *q++ = '\0';
                while (*q && nasm_isspace(*q))
                    q++;
            }
            /*
             * Here we used to sanity-check the group directive to
             * ensure nobody tried to declare a group containing no
             * segments. However, OS/2 does this as standard
             * practice, so the sanity check has been removed.
             *
             * if (!*q) {
             *     nasm_error(ERR_NONFATAL,"GROUP directive contains no segments");
             *     return DIRR_ERROR;
             * }
             */

            obj_idx = 1;
            for (grp = grphead; grp; grp = grp->next) {
                obj_idx++;
                if (!strcmp(grp->name, v)) {
                    break;
                }
            }

            if (!grp) {
                *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;
                backend_label(v, grp->index + 1, 0L);
                obj_grp_needs_update = NULL;
            }

            while (*q) {
                p = q;
                while (*q && !nasm_isspace(*q))
                    q++;
                if (nasm_isspace(*q)) {
                    *q++ = '\0';
                    while (*q && nasm_isspace(*q))
                        q++;
                }
                /*
                 * Now p contains a segment name. Find it.
                 */
                for (i = 0; i < grp->nentries; i++) {
                    if (i < grp->nindices) {
                        segname = NULL;     /* make compiler happy */
                        for (seg = seghead; seg; seg = seg->next) {
                            if (grp->segs[i].index == seg->obj_index) {
                                segname = seg->name;
                                break;
                            }
                        }
                    }
                    else
                        segname = grp->segs[i].name;
                    /*
                     * See if this segment is defined in this group.
                     */
                    if (!strcmp(segname, p))
                        break;
                }
                if (i < grp->nentries) {
                    /*
                     * We have already this segment. Skip.
                     */
                    continue;
                }
                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;
                    /*
                     * The group FLAT is a pseudo group. Therefore, it is
                     * allowed to redefine a segment in the group FLAT as
                     * other group.
                     */
                    if (seg->grp && strcmp(seg->grp->name, "FLAT"))
                        nasm_warn(WARN_OTHER, "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);
                }
            }

            /*
             * Walk through the list of externals with unresolved
             * default-WRT clauses, and resolve any that point at
             * this group.
             */
            extp = &dws;
            while (*extp) {
                if ((*extp)->defwrt_type == DEFWRT_STRING &&
                    !strcmp((*extp)->defwrt_ptr.string, grp->name)) {
                    nasm_free((*extp)->defwrt_ptr.string);
                    (*extp)->defwrt_type = DEFWRT_GROUP;
                    (*extp)->defwrt_ptr.grp = grp;
                    *extp = (*extp)->next_dws;
                } else
                    extp = &(*extp)->next_dws;
            }
        }
        return DIRR_OK;

    case D_UPPERCASE:
        if (value)
            get_boolean_option(value, &obj_uppercase);
        return DIRR_OK;

    case D_IMPORT:
        /* Is pass_first() really correct? */
        if (value && pass_first()) {
            char *q, *extname, *libname, *impname;

            extname = q = value;
            while (*q && !nasm_isspace(*q))
                q++;
            if (nasm_isspace(*q)) {
                *q++ = '\0';
                while (*q && nasm_isspace(*q))
                    q++;
            }

            libname = q;
            while (*q && !nasm_isspace(*q))
                q++;
            if (nasm_isspace(*q)) {
                *q++ = '\0';
                while (*q && nasm_isspace(*q))
                    q++;
            }

            impname = q;

            if (!*extname || !*libname)
                nasm_nonfatal("`import' directive requires symbol name"
                              " and library name");
            else {
                struct ImpDef *imp;
                bool err = false;

                imp = *imptail = nasm_malloc(sizeof(struct ImpDef));
                imptail = &imp->next;
                imp->next = NULL;
                imp->extname = nasm_strdup(extname);
                imp->libname = nasm_strdup(libname);
                imp->impindex = readnum(impname, &err);
                if (!*impname || err)
                    imp->impname = nasm_strdup(impname);
                else
                    imp->impname = NULL;
            }
        }
        return DIRR_OK;

    case D_EXPORT:
        /* Is pass_first() really correct? */
        if (value && pass_first()) {
            char *q, *extname, *intname, *v;
            struct ExpDef *export;
            int flags = 0;
            unsigned int ordinal = 0;

            intname = q = value;
            while (*q && !nasm_isspace(*q))
                q++;
            if (nasm_isspace(*q)) {
                *q++ = '\0';
                while (*q && nasm_isspace(*q))
                    q++;
            }

            extname = q;
            while (*q && !nasm_isspace(*q))
                q++;
            if (nasm_isspace(*q)) {
                *q++ = '\0';
                while (*q && nasm_isspace(*q))
                    q++;
            }

            if (!*intname) {
                nasm_nonfatal("`export' directive requires export name");
                return DIRR_OK;
            }
            if (!*extname) {
                extname = intname;
                intname = "";
            }
            while (*q) {
                v = q;
                while (*q && !nasm_isspace(*q))
                    q++;
                if (nasm_isspace(*q)) {
                    *q++ = '\0';
                    while (*q && nasm_isspace(*q))
                        q++;
                }
                if (!nasm_stricmp(v, "resident"))
                    flags |= EXPDEF_FLAG_RESIDENT;
                else if (!nasm_stricmp(v, "nodata"))
                    flags |= EXPDEF_FLAG_NODATA;
                else if (!nasm_strnicmp(v, "parm=", 5)) {
                    bool err = false;
                    flags |= EXPDEF_MASK_PARMCNT & readnum(v + 5, &err);
                    if (err) {
                        nasm_nonfatal("value `%s' for `parm' is non-numeric", v + 5);
                        return DIRR_ERROR;
                    }
                } else {
                    bool err = false;
                    ordinal = readnum(v, &err);
                    if (err) {
                        nasm_nonfatal("unrecognised export qualifier `%s'", v);
                        return DIRR_ERROR;
                    }
                    flags |= EXPDEF_FLAG_ORDINAL;
                }
            }

            export = *exptail = nasm_malloc(sizeof(struct ExpDef));
            exptail = &export->next;
            export->next = NULL;
            export->extname = nasm_strdup(extname);
            export->intname = nasm_strdup(intname);
            export->ordinal = ordinal;
            export->flags = flags;
        }
        return DIRR_OK;

    default:
	return DIRR_UNKNOWN;
    }
}

static void obj_sectalign(int32_t seg, unsigned int value)
{
    struct Segment *s;

    list_for_each(s, seghead) {
        if (s->index == seg)
            break;
    }

    /*
     * it should not be too big value
     * and applied on non-absolute sections
     */
    if (!s || !is_power2(value) ||
        value > 4096 || s->align >= SEG_ABS)
        return;

    /*
     * FIXME: No code duplication please
     * consider making helper for this
     * mapping since section handler has
     * to do the same
     */
    switch (value) {
    case 8:
        value = 16;
        break;
    case 32:
    case 64:
    case 128:
        value = 256;
        break;
    case 512:
    case 1024:
    case 2048:
        value = 4096;
        break;
    }

    if (s->align < (int)value)
        s->align = value;
}

static int32_t obj_segbase(int32_t 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) {
        /*
         * Might be an external with a default WRT.
         */
        int32_t i = segment / 2;
        struct ExtBack *eb = ebhead;
        struct External *e;

        while (i >= EXT_BLKSIZ) {
            if (eb)
                eb = eb->next;
            else
                break;
            i -= EXT_BLKSIZ;
        }
        if (eb) {
            e = eb->exts[i];
	    if (!e) {
                /* Not available yet, probably a forward reference */
		nasm_assert(!pass_final());
		return NO_SEG;
	    }

	    switch (e->defwrt_type) {
	    case DEFWRT_NONE:
                return segment; /* fine */
	    case DEFWRT_SEGMENT:
                return e->defwrt_ptr.seg->index + 1;
	    case DEFWRT_GROUP:
                return e->defwrt_ptr.grp->index + 1;
	    default:
                return NO_SEG;  /* can't tell what it is */
	    }
        }

        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 */
}

/* Get a file timestamp in MS-DOS format */
static uint32_t obj_file_timestamp(const char *pathname)
{
    time_t t;
    const struct tm *lt;

    if (!nasm_file_time(&t, pathname))
        return 0;

    lt = localtime(&t);
    if (!lt)
        return 0;

    if (lt->tm_year < 80 || lt->tm_year > 207)
        return 0;               /* Only years 1980-2107 representable */

    return
        ((uint32_t)lt->tm_sec >> 1) +
        ((uint32_t)lt->tm_min << 5) +
        ((uint32_t)lt->tm_hour << 11) +
        ((uint32_t)lt->tm_mday << 16) +
        (((uint32_t)lt->tm_mon + 1) << 21) +
        (((uint32_t)lt->tm_year - 80) << 25);
}

static void obj_write_file(void)
{
    struct Segment *seg, *entry_seg_ptr = 0;
    struct FileName *fn;
    struct LineNumber *ln;
    struct Group *grp;
    struct Public *pub, *loc;
    struct External *ext;
    struct ImpDef *imp;
    struct ExpDef *export;
    int lname_idx;
    ObjRecord *orp;
    const struct strlist_entry *depfile;
    const bool debuginfo = (dfmt == &borland_debug_form);

    /*
     * Write the THEADR module header.
     */
    orp = obj_new();
    orp->type = THEADR;
    obj_name(orp, obj_infile);
    obj_emit2(orp);

    /*
     * Write the NASM boast comment.
     */
    orp->type = COMENT;
    obj_rword(orp, dTRANSL);
    obj_name(orp, nasm_comment());
    obj_emit2(orp);

    /*
     * Output file dependency information
     */
    if (!obj_nodepend && depend_list) {
        strlist_for_each(depfile, depend_list) {
            uint32_t ts;

            ts = obj_file_timestamp(depfile->str);
            if (ts) {
                orp->type = COMENT;
                obj_rword(orp, dDEPFILE);
                obj_dword(orp, ts);
                obj_name(orp, depfile->str);
                obj_emit2(orp);
            }
        }
    }

    orp->type = COMENT;
    /*
     * Write the IMPDEF records, if any.
     */
    for (imp = imphead; imp; imp = imp->next) {
        obj_rword(orp, dOMFEXT);
        obj_byte(orp, 1);       /* subfunction 1: IMPDEF */
        if (imp->impname)
            obj_byte(orp, 0);   /* import by name */
        else
            obj_byte(orp, 1);   /* import by ordinal */
        obj_name(orp, imp->extname);
        obj_name(orp, imp->libname);
        if (imp->impname)
            obj_name(orp, imp->impname);
        else
            obj_word(orp, imp->impindex);
        obj_emit2(orp);
    }

    /*
     * Write the EXPDEF records, if any.
     */
    for (export = exphead; export; export = export->next) {
        obj_rword(orp, dOMFEXT);
        obj_byte(orp, 2);       /* subfunction 2: EXPDEF */
        obj_byte(orp, export->flags);
        obj_name(orp, export->extname);
        obj_name(orp, export->intname);
        if (export->flags & EXPDEF_FLAG_ORDINAL)
            obj_word(orp, export->ordinal);
        obj_emit2(orp);
    }

    /* we're using extended OMF if we put in debug info */
    if (debuginfo) {
        orp->type = COMENT;
        obj_rword(orp, dEXTENDED);
        obj_emit2(orp);
    }

    /*
     * Write the first LNAMES record, containing LNAME one, which
     * is null. Also initialize the LNAME counter.
     */
    orp->type = LNAMES;
    obj_byte(orp, 0);
    lname_idx = 1;
    /*
     * Write some LNAMES for the segment names
     */
    for (seg = seghead; seg; seg = seg->next) {
        orp = obj_name(orp, seg->name);
        if (seg->segclass)
            orp = obj_name(orp, seg->segclass);
        if (seg->overlay)
            orp = obj_name(orp, seg->overlay);
        obj_commit(orp);
    }
    /*
     * Write some LNAMES for the group names
     */
    for (grp = grphead; grp; grp = grp->next) {
        orp = obj_name(orp, grp->name);
        obj_commit(orp);
    }
    obj_emit(orp);

    /*
     * Write the SEGDEF records.
     */
    orp->type = SEGDEF;
    for (seg = seghead; seg; seg = seg->next) {
        int acbp;
        uint32_t seglen = seg->currentpos;

        acbp = (seg->combine << 2);     /* C field */

        if (seg->use32)
            acbp |= 0x01;       /* P bit is Use32 flag */
        else if (seglen == 0x10000L) {
            seglen = 0;         /* This special case may be needed for old linkers */
            acbp |= 0x02;       /* B bit */
        }

        /* A field */
        if (seg->align >= SEG_ABS)
            /* acbp |= 0x00 */ ;
        else if (seg->align >= 4096) {
            if (seg->align > 4096)
                nasm_nonfatal("segment `%s' requires more alignment"
                              " than OBJ format supports", seg->name);
            acbp |= 0xC0;       /* PharLap extension */
        } else if (seg->align >= 256) {
            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;

        obj_byte(orp, acbp);
        if (seg->align & SEG_ABS) {
            obj_x(orp, seg->align - SEG_ABS);   /* Frame */
            obj_byte(orp, 0);   /* Offset */
        }
        obj_x(orp, seglen);
        obj_index(orp, ++lname_idx);
        obj_index(orp, seg->segclass ? ++lname_idx : 1);
        obj_index(orp, seg->overlay ? ++lname_idx : 1);
        obj_emit2(orp);
    }

    /*
     * Write the GRPDEF records.
     */
    orp->type = GRPDEF;
    for (grp = grphead; grp; grp = grp->next) {
        int i;

        if (grp->nindices != grp->nentries) {
            for (i = grp->nindices; i < grp->nentries; i++) {
                nasm_nonfatal("group `%s' contains undefined segment"
                              " `%s'", grp->name, grp->segs[i].name);
                nasm_free(grp->segs[i].name);
                grp->segs[i].name = NULL;
            }
        }
        obj_index(orp, ++lname_idx);
        for (i = 0; i < grp->nindices; i++) {
            obj_byte(orp, 0xFF);
            obj_index(orp, grp->segs[i].index);
        }
        obj_emit2(orp);
    }

    /*
     * Write the PUBDEF records: first the ones in the segments,
     * then the far-absolutes.
     */
    orp->type = PUBDEF;
    orp->ori = ori_pubdef;
    for (seg = seghead; seg; seg = seg->next) {
        orp->parm[0] = seg->grp ? seg->grp->obj_index : 0;
        orp->parm[1] = seg->obj_index;
        for (pub = seg->pubhead; pub; pub = pub->next) {
            orp = obj_name(orp, pub->name);
            orp = obj_x(orp, pub->offset);
            orp = obj_byte(orp, 0);     /* type index */
            obj_commit(orp);
        }
        obj_emit(orp);
    }
    orp->parm[0] = 0;
    orp->parm[1] = 0;
    for (pub = fpubhead; pub; pub = pub->next) {        /* pub-crawl :-) */
        if (orp->parm[2] != (uint32_t)pub->segment) {
            obj_emit(orp);
            orp->parm[2] = pub->segment;
        }
        orp = obj_name(orp, pub->name);
        orp = obj_x(orp, pub->offset);
        orp = obj_byte(orp, 0); /* type index */
        obj_commit(orp);
    }
    obj_emit(orp);

    /*
     * Write the EXTDEF and COMDEF records, in order.
     */
    orp->ori = ori_null;
    for (ext = exthead; ext; ext = ext->next) {
        if (ext->commonsize == 0) {
            if (orp->type != EXTDEF) {
                obj_emit(orp);
                orp->type = EXTDEF;
            }
            orp = obj_name(orp, ext->name);
            orp = obj_index(orp, 0);
        } else {
            if (orp->type != COMDEF) {
                obj_emit(orp);
                orp->type = COMDEF;
            }
            orp = obj_name(orp, ext->name);
            orp = obj_index(orp, 0);
            if (ext->commonelem) {
                orp = obj_byte(orp, 0x61);      /* far communal */
                orp = obj_value(orp, (ext->commonsize / ext->commonelem));
                orp = obj_value(orp, ext->commonelem);
            } else {
                orp = obj_byte(orp, 0x62);      /* near communal */
                orp = obj_value(orp, ext->commonsize);
            }
        }
        obj_commit(orp);
    }
    obj_emit(orp);

    /*
     * Write a COMENT record stating that the linker's first pass
     * may stop processing at this point. Exception is if our
     * MODEND record specifies a start point, in which case,
     * according to some variants of the documentation, this COMENT
     * should be omitted. So we'll omit it just in case.
     * But, TASM puts it in all the time so if we are using
     * TASM debug stuff we are putting it in
     */
    if (debuginfo || obj_entry_seg == NO_SEG) {
        orp->type = COMENT;
        obj_rword(orp, dLINKPASS);
        obj_byte(orp, 1);
        obj_emit2(orp);
    }

    /*
     * 1) put out the compiler type
     * 2) Put out the type info.  The only type we are using is near label #19
     */
    if (debuginfo) {
        int i;
        struct Array *arrtmp = arrhead;
        orp->type = COMENT;
        obj_rword(orp, dCOMPDEF);
        obj_byte(orp, 4);
        obj_byte(orp, 0);
        obj_emit2(orp);

        obj_rword(orp, dTYPEDEF);
        obj_word(orp, 0x18);    /* type # for linking */
        obj_word(orp, 6);       /* size of type */
        obj_byte(orp, 0x2a);    /* absolute type for debugging */
        obj_emit2(orp);
        obj_rword(orp, dTYPEDEF);
        obj_word(orp, 0x19);    /* type # for linking */
        obj_word(orp, 0);       /* size of type */
        obj_byte(orp, 0x24);    /* absolute type for debugging */
        obj_byte(orp, 0);       /* near/far specifier */
        obj_emit2(orp);
        obj_rword(orp, dTYPEDEF);
        obj_word(orp, 0x1A);    /* type # for linking */
        obj_word(orp, 0);       /* size of type */
        obj_byte(orp, 0x24);    /* absolute type for debugging */
        obj_byte(orp, 1);       /* near/far specifier */
        obj_emit2(orp);
        obj_rword(orp, dTYPEDEF);
        obj_word(orp, 0x1b);    /* type # for linking */
        obj_word(orp, 0);       /* size of type */
        obj_byte(orp, 0x23);    /* absolute type for debugging */
        obj_byte(orp, 0);
        obj_byte(orp, 0);
        obj_byte(orp, 0);
        obj_emit2(orp);
        obj_rword(orp, dTYPEDEF);
        obj_word(orp, 0x1c);    /* type # for linking */
        obj_word(orp, 0);       /* size of type */
        obj_byte(orp, 0x23);    /* absolute type for debugging */
        obj_byte(orp, 0);
        obj_byte(orp, 4);
        obj_byte(orp, 0);
        obj_emit2(orp);
        obj_rword(orp, dTYPEDEF);
        obj_word(orp, 0x1d);    /* type # for linking */
        obj_word(orp, 0);       /* size of type */
        obj_byte(orp, 0x23);    /* absolute type for debugging */
        obj_byte(orp, 0);
        obj_byte(orp, 1);
        obj_byte(orp, 0);
        obj_emit2(orp);
        obj_rword(orp, dTYPEDEF);
        obj_word(orp, 0x1e);    /* type # for linking */
        obj_word(orp, 0);       /* size of type */
        obj_byte(orp, 0x23);    /* absolute type for debugging */
        obj_byte(orp, 0);
        obj_byte(orp, 5);
        obj_byte(orp, 0);
        obj_emit2(orp);

        /* put out the array types */
        for (i = ARRAYBOT; i < arrindex; i++) {
            obj_rword(orp, dTYPEDEF);
            obj_word(orp, i);   /* type # for linking */
            obj_word(orp, arrtmp->size);        /* size of type */
            obj_byte(orp, 0x1A);        /* absolute type for debugging (array) */
            obj_byte(orp, arrtmp->basetype);    /* base type */
            obj_emit2(orp);
            arrtmp = arrtmp->next;
        }
    }
    /*
     * write out line number info with a LINNUM record
     * switch records when we switch segments, and output the
     * file in a pseudo-TASM fashion.  The record switch is naive; that
     * is that one file may have many records for the same segment
     * if there are lots of segment switches
     */
    if (fnhead && debuginfo) {
        seg = fnhead->lnhead->segment;

        for (fn = fnhead; fn; fn = fn->next) {
            /* write out current file name */
            orp->type = COMENT;
            orp->ori = ori_null;
            obj_rword(orp, dFILNAME);
            obj_byte(orp, 0);
            obj_name(orp, fn->name);
            obj_dword(orp, 0);
            obj_emit2(orp);

            /* write out line numbers this file */

            orp->type = LINNUM;
            orp->ori = ori_linnum;
            for (ln = fn->lnhead; ln; ln = ln->next) {
                if (seg != ln->segment) {
                    /* if we get here have to flush the buffer and start
                     * a new record for a new segment
                     */
                    seg = ln->segment;
                    obj_emit(orp);
                }
                orp->parm[0] = seg->grp ? seg->grp->obj_index : 0;
                orp->parm[1] = seg->obj_index;
                orp = obj_word(orp, ln->lineno);
                orp = obj_x(orp, ln->offset);
                obj_commit(orp);
            }
            obj_emit(orp);
        }
    }
    /*
     * we are going to locate the entry point segment now
     * rather than wait until the MODEND record, because,
     * then we can output a special symbol to tell where the
     * entry point is.
     *
     */
    if (obj_entry_seg != NO_SEG) {
        for (seg = seghead; seg; seg = seg->next) {
            if (seg->index == obj_entry_seg) {
                entry_seg_ptr = seg;
                break;
            }
        }
        if (!seg)
            nasm_nonfatal("entry point is not in this module");
    }

    /*
     * get ready to put out symbol records
     */
    orp->type = COMENT;
    orp->ori = ori_local;

    /*
     * put out a symbol for the entry point
     * no dots in this symbol, because, borland does
     * not (officially) support dots in label names
     * and I don't know what various versions of TLINK will do
     */
    if (debuginfo && obj_entry_seg != NO_SEG) {
        orp = obj_name(orp, "start_of_program");
        orp = obj_word(orp, 0x19);      /* type: near label */
        orp = obj_index(orp, seg->grp ? seg->grp->obj_index : 0);
        orp = obj_index(orp, seg->obj_index);
        orp = obj_x(orp, obj_entry_ofs);
        obj_commit(orp);
    }

    /*
     * put out the local labels
     */
    for (seg = seghead; seg && debuginfo; seg = seg->next) {
        /* labels this seg */
        for (loc = seg->lochead; loc; loc = loc->next) {
            orp = obj_name(orp, loc->name);
            orp = obj_word(orp, loc->type);
            orp = obj_index(orp, seg->grp ? seg->grp->obj_index : 0);
            orp = obj_index(orp, seg->obj_index);
            orp = obj_x(orp, loc->offset);
            obj_commit(orp);
        }
    }
    if (orp->used)
        obj_emit(orp);

    /*
     * Write the LEDATA/FIXUPP pairs.
     */
    for (seg = seghead; seg; seg = seg->next) {
        obj_emit(seg->orp);
        nasm_free(seg->orp);
    }

    /*
     * Write the MODEND module end marker.
     */
    orp->type = obj_use32 ? MODE32 : MODEND;
    orp->ori = ori_null;
    if (entry_seg_ptr) {
        orp->type = entry_seg_ptr->use32 ? MODE32 : MODEND;
        obj_byte(orp, 0xC1);
        seg = entry_seg_ptr;
        if (seg->grp) {
            obj_byte(orp, 0x10);
            obj_index(orp, seg->grp->obj_index);
        } else {
            /*
             * the below changed to prevent TLINK crashing.
             * Previous more efficient version read:
             *
             *  obj_byte (orp, 0x50);
             */
            obj_byte(orp, 0x00);
            obj_index(orp, seg->obj_index);
        }
        obj_index(orp, seg->obj_index);
        obj_x(orp, obj_entry_ofs);
    } else
        obj_byte(orp, 0);
    obj_emit2(orp);
    nasm_free(orp);
}

static void obj_fwrite(ObjRecord * orp)
{
    unsigned int cksum, len;
    uint8_t *ptr;

    cksum = orp->type;
    if (orp->x_size == 32)
        cksum |= 1;
    fputc(cksum, ofile);
    len = orp->committed + 1;
    cksum += (len & 0xFF) + ((len >> 8) & 0xFF);
    fwriteint16_t(len, ofile);
    nasm_write(orp->buf, len-1, ofile);
    for (ptr = orp->buf; --len; ptr++)
        cksum += *ptr;
    fputc((-cksum) & 0xFF, ofile);
}

static enum directive_result
obj_pragma(const struct pragma *pragma)
{
    switch (pragma->opcode) {
    case D_NODEPEND:
        obj_nodepend = true;
        break;

    default:
        break;
    }

    return DIRR_OK;
}

extern macros_t obj_stdmac[];

static void dbgbi_init(void)
{
    fnhead = NULL;
    fntail = &fnhead;
    arrindex = ARRAYBOT;
    arrhead = NULL;
    arrtail = &arrhead;
}
static void dbgbi_cleanup(void)
{
    struct Segment *segtmp;
    while (fnhead) {
        struct FileName *fntemp = fnhead;
        while (fnhead->lnhead) {
            struct LineNumber *lntemp = fnhead->lnhead;
            fnhead->lnhead = lntemp->next;
            nasm_free(lntemp);
        }
        fnhead = fnhead->next;
        nasm_free(fntemp->name);
        nasm_free(fntemp);
    }
    for (segtmp = seghead; segtmp; segtmp = segtmp->next) {
        while (segtmp->lochead) {
            struct Public *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);
    }
}

static void dbgbi_linnum(const char *lnfname, int32_t lineno, int32_t segto)
{
    struct FileName *fn;
    struct LineNumber *ln;
    struct Segment *seg;

    if (segto == NO_SEG)
        return;

    /*
     * If `any_segs' is still false, we must define a default
     * segment.
     */
    if (!any_segs) {
        int tempint = 0;
        if (segto != obj_segment(DEFAULT_SEG, &tempint))
            nasm_panic("strange segment conditions in OBJ driver");
    }

    /*
     * Find the segment we are targeting.
     */
    for (seg = seghead; seg; seg = seg->next)
        if (seg->index == segto)
            break;
    if (!seg)
        nasm_panic("lineno directed to nonexistent segment?");

/*    for (fn = fnhead; fn; fn = fnhead->next) */
    for (fn = fnhead; fn; fn = fn->next)        /* fbk - Austin Lunnen - John Fine */
        if (!nasm_stricmp(lnfname, fn->name))
            break;
    if (!fn) {
        fn = nasm_malloc(sizeof(*fn));
        fn->name = nasm_malloc(strlen(lnfname) + 1);
        strcpy(fn->name, lnfname);
        fn->lnhead = NULL;
        fn->lntail = &fn->lnhead;
        fn->next = NULL;
        *fntail = fn;
        fntail = &fn->next;
    }
    ln = nasm_malloc(sizeof(*ln));
    ln->segment = seg;
    ln->offset = seg->currentpos;
    ln->lineno = lineno;
    ln->next = NULL;
    *fn->lntail = ln;
    fn->lntail = &ln->next;

}
static void dbgbi_deflabel(char *name, int32_t segment,
                           int64_t offset, int is_global, char *special)
{
    struct Segment *seg;

    (void)special;

    /*
     * Note: ..[^@] special symbols are filtered in labels.c
     */

    /*
     * If it's a special-retry from pass two, discard it.
     */
    if (is_global == 3)
        return;

    /*
     * Case (i):
     */
    if (obj_seg_needs_update) {
        return;
    } else if (obj_grp_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 obj_deflabel so we can skip that.
     */

    for (seg = seghead; seg; seg = seg->next)
        if (seg->index == segment) {
            struct Public *loc = nasm_malloc(sizeof(*loc));
            /*
             * Case (ii). Maybe MODPUB someday?
             */
            last_defined = *seg->loctail = loc;
            seg->loctail = &loc->next;
            loc->next = NULL;
            loc->name = nasm_strdup(name);
            loc->offset = offset;
        }
}
static void dbgbi_typevalue(int32_t type)
{
    int vsize;
    int elem = TYM_ELEMENTS(type);
    type = TYM_TYPE(type);

    if (!last_defined)
        return;

    switch (type) {
    case TY_BYTE:
        last_defined->type = 8; /* uint8_t */
        vsize = 1;
        break;
    case TY_WORD:
        last_defined->type = 10;        /* unsigned word */
        vsize = 2;
        break;
    case TY_DWORD:
        last_defined->type = 12;        /* unsigned dword */
        vsize = 4;
        break;
    case TY_FLOAT:
        last_defined->type = 14;        /* float */
        vsize = 4;
        break;
    case TY_QWORD:
        last_defined->type = 15;        /* qword */
        vsize = 8;
        break;
    case TY_TBYTE:
        last_defined->type = 16;        /* TBYTE */
        vsize = 10;
        break;
    default:
        last_defined->type = 0x19;      /* label */
        vsize = 0;
        break;
    }

    if (elem > 1) {
        struct Array *arrtmp = nasm_malloc(sizeof(*arrtmp));
        int vtype = last_defined->type;
        arrtmp->size = vsize * elem;
        arrtmp->basetype = vtype;
        arrtmp->next = NULL;
        last_defined->type = arrindex++;
        *arrtail = arrtmp;
        arrtail = &(arrtmp->next);
    }
    last_defined = NULL;
}
static void dbgbi_output(int output_type, void *param)
{
    (void)output_type;
    (void)param;
}
static const struct dfmt borland_debug_form = {
    "Borland Debug Records",
    "borland",
    dbgbi_init,
    dbgbi_linnum,
    dbgbi_deflabel,
    NULL,                       /* .debug_smacros */
    NULL,                       /* .debug_include */
    NULL,                       /* .debug_mmacros */
    null_debug_directive,
    dbgbi_typevalue,
    dbgbi_output,
    dbgbi_cleanup,
    NULL                        /* pragma list */
};

static const struct dfmt * const borland_debug_arr[3] = {
    &borland_debug_form,
    &null_debug_form,
    NULL
};

static const struct pragma_facility obj_pragma_list[] = {
    { NULL, obj_pragma }
};

const struct ofmt of_obj = {
    "Intel/Microsoft OMF (MS-DOS, OS/2, Win16)",
    "obj",
    ".obj",
    0,
    32,
    borland_debug_arr,
    &borland_debug_form,
    obj_stdmac,
    obj_init,
    null_reset,
    obj_out,
    obj_deflabel,
    obj_segment,
    NULL,
    obj_sectalign,
    obj_segbase,
    obj_directive,
    obj_cleanup,
    obj_pragma_list
};

const struct ofmt of_obj2 = {
    "Intel/Microsoft OMF (i386) (OS/2)",
    "obj2",
    ".obj",
    0,
    32,
    borland_debug_arr,
    &borland_debug_form,
    obj_stdmac,
    obj_init,
    null_reset,
    obj_out,
    obj_deflabel,
    obj_segment,
    NULL,
    obj_sectalign,
    obj_segbase,
    obj_directive,
    obj_cleanup,
    obj_pragma_list
};
#endif                          /* OF_OBJ || OF_OBJ2 */
