/* Windows Put Interface */
/* Copyright (C) 2001 Analog Devices Inc., All Rights Reserved.
** This contains Analog Devices Background IP and Development IP as
** defined in the ADI/Intel Collaboration Agreement.
** ADI/Intel Confidential
*/


/* This module provides functions for generating a Windows assembler or object
 * file from the e-machine.  For assembler files, the code within functions
 * is output from within the e-machine but all surrounding assembler
 * directives are dealt with by this module.  For object files this is
 * similar except that the e-machine fills up this module's code area
 * with bytes representing the machine code produced, and more work has to
 * be done to deal with relocations and symbol tables.
 *
 * This put interface is derived from previous put interfaces written for
 * different object file formats.  It has been written from scratch,
 * removing a lot of redundant code from the compiler, and now adds support
 * for generating assembler files.  It is intended that this file will not
 * have to rely on include files specific to a particular file format and
 * so all such definitions are declared locally in this file.  It is also
 * intended that the format of output file produced will be switchable at
 * run-time, so no conditional compilation should exist.
 *
 * The put interface functions expect sizes and alignments to be passed as
 * bytes, whether they be for a byte-addressed target architecture or a
 * word-addressed target architecture.  The sizes and alignments will be
 * converted accordingly when the output file is written.
 *
 * Graeme Roy (3rd September, 2001)
 */


#define __EXTENSIONS__


#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <fcntl.h>
#include <limits.h>
#ifdef _WIN32
#include <io.h>
#else
#include <unistd.h>
#endif
#include "put.h"


#define SECTION_STACK_SIZE  64     /* maximum items on section stack */
#define SCOPE_STACK_SIZE    64     /* maximum items on scope stack */

#define BIG_ENDIAN          0      /* architecture is big-endian */
#define LITTLE_ENDIAN       1      /* architecture is little-endian */

#define DEBUG_COFF          2      /* debugging format is COFF */
#define DEBUG_CODEVIEW      3      /* debugging format is CodeView */

#define SECT_READONLY       1      /* section contents are read-only */
#define SECT_EXECUTABLE     2      /* section contents may be executed */
#define SECT_DEBUG          4      /* section contents contain debugging info */

#define DIRECT_ANY          0      /* any directive */
#define DIRECT_EXPORT       1      /* export directive */
#define DIRECT_LIBRARY      2      /* library directive */

#define TARGET_386          25     /* target processor is i386 */

#define COFF_ARCH_386       0x014C /* i386-specific object file */

#define COFF_SECT_CODE      0x0020 /* section contents contain code */
#define COFF_SECT_DATA      0x0040 /* section contents contain data */
#define COFF_SECT_BSS       0x0080 /* section contents occupy no file space */
#define COFF_SECT_INFO      0x0200 /* section contents contain comments */
#define COFF_SECT_REMOVE    0x0800 /* section contents should be removed */
#define COFF_SECT_DISCARD   0x02000000
                                   /* section contents can be discarded */
#define COFF_SECT_EXEC      0x20000000
                                   /* section contents may be executed */
#define COFF_SECT_READ      0x40000000
                                   /* section contents are readable */
#define COFF_SECT_WRITE     0x80000000
                                   /* section contents are writable */

#define COFF_SYMBOL_ABS     0xFFFF /* symbol is not affected by relocation */
#define COFF_SYMBOL_DEBUG   0xFFFE /* symbol is a debug symbol */

#define COFF_SYMBOL_OBJECT  0x00   /* symbol represents an object */
#define COFF_SYMBOL_FUNC    0x20   /* symbol represents a function */

#define COFF_SYMBOL_GLOBAL  2      /* symbol has global visibility */
#define COFF_SYMBOL_LOCAL   3      /* symbol has local visibility */
#define COFF_SYMBOL_FUNCAUX 101    /* symbol is function auxilliary */
#define COFF_SYMBOL_FILE    103    /* symbol has file visibility */
#define COFF_SYMBOL_WEAK    105    /* symbol has weak visibility */

#define COFF_REL_386_DIR32  6      /* i386-specific relocation type */
#define COFF_REL_386_SEC    10     /* i386-specific relocation type */
#define COFF_REL_386_SECREL 11     /* i386-specific relocation type */
#define COFF_REL_386_REL32  20     /* i386-specific relocation type */


/* available sections */

typedef enum section_type
{
    sec_null,
    sec_text,
    sec_init,
    sec_fini,
    sec_data,
    sec_tls,
    sec_bss,
    sec_rodata,
    sec_gdt,
    sec_frt,
    sec_edt,
    sec_cht,
    sec_directive,
    sec_comment,
    sec_debsym,
    sec_debtyp,
    sec_num
}
section_type;


/* structure containing information about each section */

typedef struct section_info
{
    char *name;
    size_t index;
    size_t size;
    size_t targets;
    size_t relocs;
    size_t lines;
    size_t align;
    size_t default_align;
    unsigned long flags;
}
section_info;


/* table containing information about each section */

static section_info section_info_table[sec_num] =
{
    {"",         0xFFFFFFFF, 0, 0, 0, 0, 0, 0,
                 0},
    {".text",    0xFFFFFFFF, 0, 0, 0, 0, 0, 4,
                 COFF_SECT_CODE | COFF_SECT_EXEC | COFF_SECT_READ},
    {".init",    0xFFFFFFFF, 0, 0, 0, 0, 0, 4,
                 COFF_SECT_CODE | COFF_SECT_EXEC | COFF_SECT_READ},
    {".fini",    0xFFFFFFFF, 0, 0, 0, 0, 0, 4,
                 COFF_SECT_CODE | COFF_SECT_EXEC | COFF_SECT_READ},
    {".data",    0xFFFFFFFF, 0, 0, 0, 0, 0, 4,
                 COFF_SECT_DATA | COFF_SECT_READ | COFF_SECT_WRITE},
    {".tls",     0xFFFFFFFF, 0, 0, 0, 0, 0, 4,
                 COFF_SECT_DATA | COFF_SECT_READ | COFF_SECT_WRITE},
    {".bss",     0xFFFFFFFF, 0, 0, 0, 0, 0, 4,
                 COFF_SECT_BSS | COFF_SECT_READ | COFF_SECT_WRITE},
    {".rdata",   0xFFFFFFFF, 0, 0, 0, 0, 0, 4,
                 COFF_SECT_DATA | COFF_SECT_READ},
    {".ptabs",   0xFFFFFFFF, 0, 0, 0, 0, 0, 4,
                 COFF_SECT_DATA | COFF_SECT_READ | COFF_SECT_WRITE},
    {".xtabs$1", 0xFFFFFFFF, 0, 0, 0, 0, 0, 4,
                 COFF_SECT_DATA | COFF_SECT_READ},
    {".xtabs$2", 0xFFFFFFFF, 0, 0, 0, 0, 0, 4,
                 COFF_SECT_DATA | COFF_SECT_READ},
    {".xtabs$3", 0xFFFFFFFF, 0, 0, 0, 0, 0, 4,
                 COFF_SECT_DATA | COFF_SECT_READ},
    {".drectve", 0xFFFFFFFF, 0, 0, 0, 0, 0, 1,
                 COFF_SECT_INFO | COFF_SECT_REMOVE},
    {".comment", 0xFFFFFFFF, 0, 0, 0, 0, 0, 1,
                 COFF_SECT_INFO | COFF_SECT_REMOVE},
    {".debug$S", 0xFFFFFFFF, 0, 0, 0, 0, 0, 1,
                 COFF_SECT_DATA | COFF_SECT_DISCARD | COFF_SECT_READ},
    {".debug$T", 0xFFFFFFFF, 0, 0, 0, 0, 0, 1,
                 COFF_SECT_DATA | COFF_SECT_DISCARD | COFF_SECT_READ},
};


/* available areas */

typedef enum area_type
{
    area_stack,
    area_code,
    area_gla,
    area_plt,
    area_sst,
    area_gst,
    area_diags,
    area_params,
    area_iotab,
    area_bss,
    area_tls,
    area_init,
    area_fini,
    area_auxbss,
    area_directive,
    area_debugtabs,
    area_debugtabt,
    area_linetab,
    area_datagptab,
    area_bssgptab,
    area_reginfo,
    area_comment,
    area_gdt,
    area_frt,
    area_edt,
    area_cht,
    area_section,
    area_symbol,
    area_string,
    area_secstring,
    area_num
}
area_type;


/* structure containing information about each area */

typedef struct area_info
{
    char *area;
    char *name;
    section_type section;
}
area_info;


/* table containing information about each area */

static area_info area_info_table[area_num] =
{
    {"LOCALS",    NULL,         sec_null},
    {"CODE",      ".epctext",   sec_text},
    {"GLA",       ".epcdata",   sec_data},
    {"PLT",       NULL,         sec_null},
    {"SST",       ".epcrodata", sec_rodata},
    {"GST",       NULL,         sec_null},
    {"DIAGS",     NULL,         sec_null},
    {"PARAMS",    NULL,         sec_null},
    {"IOTAB",     NULL,         sec_null},
    {"BSS",       ".epcbss",    sec_bss},
    {"TLS",       ".epctls",    sec_tls},
    {"INIT",      ".epcinit",   sec_init},
    {"FINI",      ".epcfini",   sec_fini},
    {"AUXBSS",    NULL,         sec_null},
    {"DIRECTIVE", ".epcdirect", sec_directive},
    {"DEBUGTABS", ".epcdebsym", sec_debsym},
    {"DEBUGTABT", ".epcdebtyp", sec_debtyp},
    {"LINETAB",   NULL,         sec_null},
    {"DATAGPTAB", NULL,         sec_null},
    {"BSSGPTAB",  NULL,         sec_null},
    {"REGINFO",   NULL,         sec_null},
    {"COMMENT",   ".epccmt",    sec_comment},
    {"GDT",       ".epcgdt",    sec_gdt},
    {"FRT",       ".epcfrt",    sec_frt},
    {"EDT",       ".epcedt",    sec_edt},
    {"CHT",       ".epccht",    sec_cht},
    {"SECTION",   NULL,         sec_null},
    {"SYMBOL",    NULL,         sec_null},
    {"STRING",    NULL,         sec_null},
    {"SECSTRING", NULL,         sec_null}
};


/* structure containing information about the assembler */

typedef struct assembler_specification
{
    char *section;
    char *cursect;
    char *endsect;
    char *proc;
    char *label;
    char *endproc;
    char *local;
    char *global;
    char *weak;
    char *external;
    char *common;
    char *align;
    char *byte;
    char *word;
    char *zero;
    char *end;
    char *comment;
}
assembler_specification;


/* specification for an Intel i386 COFF assembler */

static assembler_specification coff_i386_assembler =
{
    "SEGMENT",
    "@CurSeg",
    "ENDS",
    "PROC",
    "LABEL",
    "ENDP",
    "PRIVATE",
    "PUBLIC",
    "WEAK",
    "EXTERN",
    "COMM",
    "ALIGN",
    "BYTE",
    "DWORD",
    "DUP",
    "END",
    ";"
};


/* structure representing a COFF header */

typedef struct coff_header
{
    unsigned short magic;
    unsigned short nscns;
    unsigned long timdat;
    unsigned long symptr;
    unsigned long nsyms;
    unsigned short opthdr;
    unsigned short flags;
}
coff_header;


/* structure representing a COFF section */

typedef struct coff_section
{
    char name[8];
    unsigned long paddr;
    unsigned long vaddr;
    unsigned long size;
    unsigned long scnptr;
    unsigned long relptr;
    unsigned long lnnoptr;
    unsigned short nreloc;
    unsigned short nlnno;
    unsigned long flags;
}
coff_section;


/* structure representing a COFF symbol */

typedef struct coff_symbol
{
    union
    {
        char name[8];
        struct
        {
            unsigned long zeroes;
            unsigned long offset;
        }
        y;
    }
    x;
    unsigned long value;
    unsigned short scnum;
    unsigned short type;
    unsigned char sclass;
    unsigned char numaux;
}
coff_symbol;


/* structure representing a COFF relocation */

typedef struct coff_relocation
{
    unsigned long vaddr;
    unsigned long symndx;
    unsigned short type;
}
coff_relocation;


/* structure representing a COFF line number */

typedef struct coff_lineno
{
    unsigned long addr;
    unsigned short line;
}
coff_lineno;


/* table containing information about the available types */
/* of COFF relocations for the Intel i386 architecture */

static unsigned char coff_i386_relocation_table[14] =
{
    COFF_REL_386_DIR32,
    COFF_REL_386_DIR32,
    COFF_REL_386_DIR32,
    COFF_REL_386_REL32,
    0,
    COFF_REL_386_REL32,
    0,
    0,
    0,
    0,
    COFF_REL_386_SEC,
    COFF_REL_386_SECREL,
    0,
    0
};


/* table containing bit-masks for the available types */
/* of COFF relocations for the Intel i386 architecture */

static unsigned long coff_i386_rel_mask_table[20] =
{
    0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
    0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
    0xFFFFFFFF, 0x0000FFFF, 0xFFFFFFFF, 0xFFFFFFFF,
    0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
    0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
};


/* available types of linkage */

typedef enum linkage
{
    link_none,
    link_file,
    link_section,
    link_local,
    link_global,
    link_weak,
    link_local_common,
    link_global_common,
    link_weak_common,
    link_global_external,
    link_weak_external,
    link_local_function,
    link_global_function,
    link_weak_function,
    link_global_external_function,
    link_weak_external_function
}
linkage;


/* structure containing information about the current function */

typedef struct function_info
{
    size_t area;
    size_t address;
}
function_info;


/* structure containing symbol aliasing information */

typedef struct alias
{
    struct alias *next;
    char *name;
    linkage link;
}
alias;


/* structure containing line number information */

typedef struct lineno
{
    struct lineno *next;
    char *file;
    unsigned long line;
    union
    {
        char *label;
        size_t offset;
    }
    addr;
}
lineno;


/* member list node */

typedef struct member_node
{
    struct member_node *next;
    struct member_node *prev;
    size_t offset;
    unsigned char type;
    char *name;
    union
    {
        struct
        {
            linkage link;
            size_t size;
            lineno *lines;
            size_t symidx;
        }
        entry;
        struct
        {
            size_t area;
            long offset;
            unsigned long size;
        }
        target;
    }
    data;
}
member_node;


/* member list header */

typedef struct member_list
{
    struct member_node *head;
    struct member_node *tail;
    struct member_node *tlpr;
}
member_list;


/* structure containing information about each declared area */

typedef struct area_header
{
    char *name;
    char *contents;
    member_list members;
    alias *aliases;
    lineno *lines;
    linkage link;
    unsigned char fill;
    unsigned long flags;
    size_t parent;
    size_t offset;
    size_t align;
    size_t size;
    size_t max;
}
area_header;


/* The following version strings should be defined elsewhere and are declared
 * tentatively in C in order to make them optional.  In C++ tentative
 * declarations do not exist and so we must force their definition elsewhere.
 */

#ifdef __cplusplus

extern char *epc_version_string;
extern char *epc_front_end_version;
extern char *epc_back_end_version;

#else

char *epc_version_string;
char *epc_front_end_version;
char *epc_back_end_version;

#endif


/* indicates if the put interface has been initialised */
/* and also whether or not it has not been terminated */

static int put_initialised, put_unterminated;


/* the old and new output files */

static int old_outfile, new_outfile;


/* the source and destination filenames */

static char *source_file, *dest_file;


/* the current target assembler */

static assembler_specification *assembler;


/* the current target relocation table */

static unsigned char *relocation_table;


/* the current target relocation bit-mask table */

static unsigned long *rel_mask_table;


/* table containing information about each area */

static area_header *area_table;


/* the current size of the area table and the highest area in use */

static size_t area_table_size, highest_area;


/* the maximum stack size for the current function */

static size_t max_stack_size;


/* stack containing information about the section nesting level */

static long section_stack[SECTION_STACK_SIZE];


/* the current section nesting level */

static unsigned char section_level;


/* the current section when generating assembler */

static long current_section;


/* stack containing information about the function nesting level */

static function_info function_stack[SCOPE_STACK_SIZE];


/* the current function nesting level */

static unsigned char scope_level;


/* the area number of the first defined function */

static size_t first_function;


/* the file offset of the symbol table */

static size_t symbol_table_offset;


/* the current filename for line number tables */

static char *current_filename;


/* the default byte used to fill the uninitialised contents of areas */

static unsigned char area_filler;


/* indicates the processor to generate code for */

static unsigned char target_processor;


/* indicates the endianness of the host and target architectures */

static unsigned char host_endian, target_endian;


/* indicates the debugging format being generated */

static int debug_format;


/* indicates if an object file is to be produced instead of an assembler file */

static int object_generating;


/* indicates if tracing is to be displayed for the put interface */
/* or for the put memory allocation interface */

static int put_trace, put_memory_trace;


/* specifies an optional prefix (and its length in characters) that is */
/* to be prepended to all globally-visible symbol names */

static char *external_prefix;
static size_t external_prefix_size;


/* indicates if all non-local symbol names should be prepended with an */
/* optional prefix */

static int prepend_prefix;


/* indicates if all common symbols should be converted to */
/* defined symbols in the BSS area */

static int no_commons;


/* indicates if annotations should appear in the assembler output */

static int annotate_assembler;


/* indicates if globally visible functions should be made weak */
/* and automatically given a global alias */

static int auto_weak_functions;


/* indicates if the put interface should suppress code generation */

static int suppress_code_gen;


/* definition of assert which indicates that an internal error has occurred */

#ifdef NDEBUG
#define assert(test) (void) 0
#else
#define assert(test) (void) ((test) || (put_assert(#test, __LINE__), 0))
#endif


#ifdef __cplusplus
extern "C"
{
#endif


/* local function prototypes */

static void write_code(char *, ...);
static void put_message(char *, ...);
static void put_error(char *, ...);
static void put_assert(char *, size_t);
static void hex_dump(unsigned char *, size_t);
static void string_dump(unsigned char *, size_t);
static void byte_dump(size_t, size_t, unsigned char *, size_t);
static void zero_dump(size_t, size_t, size_t);
static void byte_swap(char *, size_t);
static unsigned long log_base_2(unsigned long);
static int is_power_of_two(unsigned long);
static unsigned long round_up(unsigned long, unsigned long);
static size_t base_area(size_t);
static int is_function(linkage);
static void new_member_list(member_list *);
static member_node *add_member(size_t, size_t, unsigned char, char *,
                               unsigned long, unsigned long, unsigned long,
                               int);
static void move_members(size_t, size_t, size_t);
static void free_members(size_t);
static alias *add_alias(size_t, char *, linkage);
static void move_aliases(size_t, size_t, size_t);
static void free_aliases(size_t);
static lineno *add_lineno(size_t, char *, unsigned long, void *);
static void free_linenos(lineno *);
static void expand_contents(size_t);
static void expand_area_table(void);
static size_t add_string(area_type, char *);
static void add_directive(int, char *);
static void set_area_name(size_t, char *, int);
static void init_area_table(void);
static void free_area_table(void);
static char *label_name(char *, size_t, long, int);
static void enter_section(long);
static size_t add_section(section_type);
static size_t add_symbol(char *, linkage, size_t, unsigned long, section_type);
static void add_function_aux(size_t, size_t, size_t);
static void add_beginend_aux(size_t);
static void add_weak_aux(size_t);
static void add_file_aux(char *, size_t);
static void add_section_aux(size_t, size_t, size_t);
static void alter_relocations(size_t, size_t, size_t, size_t);
static void optimise_relocations(void);
static void count_relocations(void);
static void count_lines(void);
static void write_addends(void);
static void write_relocations(member_list *, char *);
static void write_lines(size_t, char *);
static void write_header(void);
static void write_contents(size_t);
static void write_areas(void);
static void write_commons(void);
static void write_external_objects(void);
static void write_external_functions(void);
static void write_function_aliases(void);
static void move_area(size_t, size_t, linkage);
static void move_data_areas(void);
static void move_commons(int);
static void move_functions(void);
static void merge_areas(void);
static void patch_symbol_table(void);
static void swap_symbol_table(void);
static void swap_section_table(void);
static void build_section_table(void);
static size_t add_line_symbols(lineno **, char **, section_type);
static void build_symbol_table(void);
static void write_object(void);
static char *linkage_name(linkage);
static void print_lines(lineno *);
static void print_members(size_t);
static void print_areas(void);


/* writes an assembler statement to the output file */

static void write_code(char *code, ...)
{
    va_list list;

    va_start(list, code);
    vfprintf(stdout, code, list);
    va_end(list);
}


/* displays a message */

static void put_message(char *message, ...)
{
    va_list list;

    va_start(list, message);
    vfprintf(stderr, message, list);
    va_end(list);
}


/* displays an error message and then terminates */

static void put_error(char *message, ...)
{
    va_list list;

    fputs("put error: ", stderr);
    va_start(list, message);
    vfprintf(stderr, message, list);
    va_end(list);
    fputc('\n', stderr);
    if (dest_file)
        premoveobject();
    exit(2);
}


/* displays an assertion failure and then terminates */

static void put_assert(char *assertion, size_t line)
{
    put_message("assertion failed (line %lu): %s\n", line, assertion);
    put_error("internal error");
}


/* displays a hexadecimal dump for the contents of a memory location */

static void hex_dump(unsigned char *address, size_t length)
{
    size_t index;

    for (index = 0; index < length; index++)
    {
        if (index % 16 == 0)
            put_message("\t0x%08lX: ", address + index);
        put_message("%02X", address[index]);
        if ((index % 16 == 15) || (index == length - 1))
            put_message("\n");
        else
            put_message(" ");
    }
}


/* dumps the contents of a memory location as a string */

static void string_dump(unsigned char *address, size_t length)
{
    size_t index;
    unsigned char temp;

    for (index = 0; index < length; index++)
    {
        temp = address[index];
        switch (temp)
        {
          case '\0':
            if (index == length - 1)
                write_code("\\0");
            else
                write_code("\\000");
            break;
          case '\b':
            write_code("\\b");
            break;
          case '\f':
            write_code("\\f");
            break;
          case '\n':
            write_code("\\n");
            break;
          case '\r':
            write_code("\\r");
            break;
          case '\t':
            write_code("\\t");
            break;
          case '\v':
            write_code("\\v");
            break;
          case '"':
          case '\\':
            write_code("\\%c", temp);
            break;
          default:
            if (isprint(temp))
                write_code("%c", temp);
            else
                write_code("\\%03o", temp);
            break;
        }
    }
}


/* dumps the contents of a memory location as a sequence of bytes */

static void byte_dump(size_t area, size_t offset, unsigned char *address,
                      size_t length)
{
    char *name;
    size_t index;

    if (area_table[area].link == link_section)
        name = label_name(NULL, area, 0, 0);
    else
        name = area_table[area].name;
    for (index = 0; index < length; index++)
    {
        if (index % 12 == 0)
        {
            if (annotate_assembler)
            {
                write_code("\t%s [%s", assembler->comment, name);
                if (offset + index > 0)
                    write_code("+%lu", offset + index);
                write_code("] \"");
                if (length - index >= 12)
                    string_dump(address + index, 12);
                else
                    string_dump(address + index, length - index);
                write_code("\"\n");
            }
            write_code("\t%s ", assembler->byte);
        }
        write_code("%02XH", address[index]);
        if ((index % 12 == 11) || (index == length - 1))
            write_code("\n");
        else
            write_code(",");
    }
}


/* dumps a specified number of zero bytes */

static void zero_dump(size_t area, size_t offset, size_t length)
{
    if (area == area_bss)
        write_code("\t%s %lu %s (?)\n", assembler->byte, length,
                   assembler->zero);
    else
        write_code("\t%s %lu %s (0)\n", assembler->byte, length,
                   assembler->zero);
}


/* performs byte-swapping in a buffer of a specified length */

static void byte_swap(char *buffer, size_t length)
{
    size_t index;
    char temp;

    for (index = 0; index < length >> 1; index++)
    {
        temp = buffer[index];
        buffer[index] = buffer[length - index - 1];
        buffer[length - index - 1] = temp;
    }
}


/* computes the rounded-down base-2 logarithm of a number */

static unsigned long log_base_2(unsigned long number)
{
    unsigned long value;

    for (value = 0, number >>= 1; number > 0; value++, number >>= 1);
    return value;
}


/* determines if a number is a power of 2 */

static int is_power_of_two(unsigned long number)
{
    return ((number != 0) && ((number & (number - 1)) == 0));
}


/* rounds a value up to a specified power-of-2 alignment */

static unsigned long round_up(unsigned long value, unsigned long align)
{
    return ((value - 1) & ~(align - 1)) + align;
}


/* determines the base area for a data area */

static size_t base_area(size_t area)
{
    while (area_table[area].parent != 0)
        area = area_table[area].parent;
    return area;
}


/* determines if a linkage specifies a function */

static int is_function(linkage link)
{
    if ((link == link_local_function) || (link == link_global_function) ||
        (link == link_weak_function) ||
        (link == link_global_external_function) ||
        (link == link_weak_external_function))
        return 1;
    return 0;
}


/* allocates a block of bytes for the put interface */

void *getspace(int size)
{
    void *pointer;

    if (put_memory_trace)
        put_message("getspace(size = %d)\n", size);
    assert(size > 0);
    if ((pointer = (void *) calloc(size, 1)) == NULL)
        put_error("out of memory");
    if (put_memory_trace)
        put_message("getspace() returns (pointer = 0x%08lX)\n", pointer);
    return pointer;
}


/* frees a previously allocated block of bytes */

void freespace(void *pointer)
{
    if (put_memory_trace)
        put_message("freespace(pointer = 0x%08lX)\n", pointer);
    assert(pointer != NULL);
    free(pointer);
}


/* resizes the number of bytes in a previously allocated block */

void *reallocspace(void *pointer, int size)
{
    if (put_memory_trace)
        put_message("reallocspace(pointer = 0x%08lX, size = %d)\n", pointer,
                    size);
    if (pointer == NULL)
        pointer = getspace(size);
    else if (size == 0)
    {
        freespace(pointer);
        pointer = NULL;
    }
    else
    {
        assert(size > 0);
        if ((pointer = (void *) realloc(pointer, size)) == NULL)
            put_error("out of memory");
    }
    if (put_memory_trace)
        put_message("reallocspace() returns (pointer = 0x%08lX)\n", pointer);
    return pointer;
}


/* initializes a member list to be an empty list */

static void new_member_list(member_list *list)
{
    list->head = (member_node *) &list->tail;
    list->tail = NULL;
    list->tlpr = (member_node *) &list->head;
}


/* adds a member to the member list of an area */

static member_node *add_member(size_t area, size_t offset, unsigned char type,
                               char *name, unsigned long data1,
                               unsigned long data2, unsigned long data3,
                               int no_prepend)
{
    member_node *next, *node, *previous;

    for (previous = area_table[area].members.tlpr; previous->prev != NULL;
         previous = previous->prev)
        if ((offset > previous->offset) || ((offset == previous->offset) &&
             ((type != 0) || (previous->type == 0))))
            break;
    node = (member_node *) getspace(sizeof(member_node));
    next = previous->next;
    previous->next = node;
    node->prev = previous;
    node->next = next;
    next->prev = node;
    node->offset = offset;
    node->type = type;
    /* Entry names may be local, global or weak, but may also begin with
     * a dot, which indicates that the name should not normally be placed
     * in the symbol table of the object file.  Names beginning with a dot
     * or that are locally visible will therefore not be preceded by an
     * optional prefix.
     */
    if (name != NULL)
        if (!no_prepend && prepend_prefix && (*name != '.') && (type == 0) &&
            ((linkage) data1 != link_local) &&
            ((linkage) data1 != link_local_common) &&
            ((linkage) data1 != link_local_function))
        {
            node->name = (char *) getspace(strlen(name) + external_prefix_size +
                                           1);
            strcpy(node->name, external_prefix);
            strcat(node->name, name);
        }
        else
        {
            node->name = (char *) getspace(strlen(name) + 1);
            strcpy(node->name, name);
        }
    else
        node->name = NULL;
    if (type == 0)
    {
        /* Create a data entry with a given linkage and size.
         */
        node->data.entry.link = (linkage) data1;
        node->data.entry.size = (size_t) data2;
        node->data.entry.lines = (lineno *) data3;
        node->data.entry.symidx = 0;
    }
    else
    {
        /* Create a relocation of a specified size pointing to a given area
         * and offset.
         */
        node->data.target.area = (size_t) data1;
        node->data.target.offset = (long) data2;
        node->data.target.size = data3;
    }
    return node;
}


/* moves all of the members from the member list of one area to another */

static void move_members(size_t area, size_t new_area, size_t new_offset)
{
    member_node *node;

    for (node = area_table[area].members.head; node->next != NULL;
         node = node->next)
        if (node->type == 0)
        {
            add_member(new_area, new_offset + node->offset, 0, node->name,
                       node->data.entry.link, node->data.entry.size,
                       (unsigned long) node->data.entry.lines, 1);
            node->data.entry.lines = NULL;
        }
        else
            add_member(new_area, new_offset + node->offset, node->type,
                       node->name, node->data.target.area,
                       node->data.target.offset, node->data.target.size, 1);
    free_members(area);
}


/* frees all of the members from the member list of an area */

static void free_members(size_t area)
{
    member_node *next, *node;

    for (node = area_table[area].members.head; next = node->next; node = next)
    {
        if (node->name != NULL)
            freespace(node->name);
        if (node->type == 0)
            free_linenos(node->data.entry.lines);
        freespace(node);
    }
    new_member_list(&area_table[area].members);
}


/* adds an alias to the alias list of an area */

static alias *add_alias(size_t area, char *name, linkage link)
{
    alias *anode, *previous;

    anode = (alias *) getspace(sizeof(alias));
    if ((previous = area_table[area].aliases) == NULL)
        area_table[area].aliases = anode;
    else
    {
        while (previous->next != NULL)
            previous = previous->next;
        previous->next = anode;
    }
    anode->next = NULL;
    /* Alias names may be local, global or weak, but may also begin with
     * a dot, which indicates that the name should not normally be placed
     * in the symbol table of the object file.  Names beginning with a dot
     * or that are locally visible will therefore not be preceded by an
     * optional prefix.
     */
    if (prepend_prefix && (*name != '.') && (link != link_local) &&
        (link != link_local_common) && (link != link_local_function))
    {
        anode->name = (char *) getspace(strlen(name) + external_prefix_size +
                                        1);
        strcpy(anode->name, external_prefix);
        strcat(anode->name, name);
    }
    else
    {
        anode->name = (char *) getspace(strlen(name) + 1);
        strcpy(anode->name, name);
    }
    anode->link = link;
    return anode;
}


/* moves all of the aliases from the alias list of one area to */
/* the member list of another area */

static void move_aliases(size_t area, size_t new_area, size_t new_offset)
{
    alias *anode;

    for (anode = area_table[area].aliases; anode != NULL; anode = anode->next)
        add_member(new_area, new_offset, 0, anode->name, anode->link,
                   area_table[area].size, 0, 1);
    free_aliases(area);
}


/* frees all of the aliases from the alias list of an area */

static void free_aliases(size_t area)
{
    alias *anode, *next;

    for (anode = area_table[area].aliases; anode != NULL; anode = next)
    {
        next = anode->next;
        if (anode->name != NULL)
            freespace(anode->name);
        freespace(anode);
    }
    area_table[area].aliases = NULL;
}


/* adds a line number to the line number list of a function */

static lineno *add_lineno(size_t area, char *file, unsigned long line,
                          void *addr)
{
    lineno *lnode, *previous;

    lnode = (lineno *) getspace(sizeof(lineno));
    if ((previous = area_table[area].lines) == NULL)
        area_table[area].lines = lnode;
    else
    {
        while (previous->next != NULL)
            previous = previous->next;
        previous->next = lnode;
    }
    lnode->next = NULL;
    lnode->file = (char *) getspace(strlen((char *) file) + 1);
    strcpy(lnode->file, file);
    lnode->line = line;
    if (object_generating)
        lnode->addr.offset = (size_t) addr;
    else
    {
        lnode->addr.label = (char *) getspace(strlen((char *) addr) + 1);
        strcpy(lnode->addr.label, (char *) addr);
    }
    return lnode;
}


/* frees all of the line numbers from a line number list */

static void free_linenos(lineno *lnode)
{
    lineno *next;

    while (lnode != NULL)
    {
        next = lnode->next;
        freespace(lnode->file);
        if (!object_generating)
            freespace(lnode->addr.label);
        freespace(lnode);
        lnode = next;
    }
}


/* increases the size of the contents of an area */

static void expand_contents(size_t area)
{
    area_header *table;
    size_t old_size;

    table = &area_table[area];
    old_size = table->max;
    if (table->contents == NULL)
    {
        if (area < area_num)
            table->max = 2048;
        else
            table->max = 256;
        table->contents = (char *) getspace(table->max);
    }
    else
    {
        if (area < area_num)
            table->max *= 2;
        else
            table->max *= 2;
        table->contents = (char *) reallocspace(table->contents, table->max);
    }
    memset(table->contents + old_size, table->fill, table->max - old_size);
}


/* increases the size of the area table to allow for more areas */

static void expand_area_table(void)
{
    area_header *new_area_table, *new_table, *table;
    size_t index, old_size;

    old_size = area_table_size;
    if (area_table == NULL)
        area_table_size = 32;
    else
        area_table_size += 128;
    new_area_table = (area_header *) getspace(area_table_size *
                      sizeof(area_header));
    for (index = 0; index < old_size; index++)
    {
        table = &area_table[index];
        new_table = &new_area_table[index];
        new_table->name = table->name;
        new_table->contents = table->contents;
        /* If the old member list is empty then we can simply create
         * a new empty member list.  However, if the old member list
         * contains members then we must fix the previous pointer of
         * the head and the next pointer of the tail otherwise there
         * will be dangling references to freed memory.
         */
        if (table->members.head->next == NULL)
            new_member_list(&new_table->members);
        else
        {
            new_table->members.head = table->members.head;
            new_table->members.tail = NULL;
            new_table->members.tlpr = table->members.tlpr;
            new_table->members.head->prev = (member_node *)
                                            &new_table->members.head;
            new_table->members.tlpr->next = (member_node *)
                                            &new_table->members.tail;
        }
        new_table->aliases = table->aliases;
        new_table->lines = table->lines;
        new_table->link = table->link;
        new_table->fill = table->fill;
        new_table->flags = table->flags;
        new_table->parent = table->parent;
        new_table->offset = table->offset;
        new_table->align = table->align;
        new_table->size = table->size;
        new_table->max = table->max;
    }
    if (area_table != NULL)
        freespace(area_table);
    area_table = new_area_table;
    for (index = old_size; index < area_table_size; index++)
    {
        table = &area_table[index];
        table->name = NULL;
        table->contents = NULL;
        new_member_list(&table->members);
        table->aliases = NULL;
        table->lines = NULL;
        table->link = link_local;
        if ((index == area_gla) || (index == area_sst) ||
            (index >= area_num))
            table->fill = area_filler;
        else
            table->fill = 0;
        table->flags = 0;
        table->parent = 0;
        table->offset = 0;
        table->align = 0;
        table->size = 0;
        table->max = 0;
    }
}


/* adds a string to a string table */

static size_t add_string(area_type area, char *name)
{
    area_header *table;
    size_t offset, size;

    table = &area_table[area];
    offset = table->size;
    size = offset + strlen(name) + 1;
    while (size > table->max)
        expand_contents(area);
    strcpy(table->contents + offset, name);
    table->size = size;
    return offset;
}


/* adds a directive to the directive table */

static void add_directive(int type, char *dir)
{
    area_header *table;

    table = &area_table[area_directive];
    if (type == DIRECT_EXPORT)
    {
        add_string(area_directive, "-export");
        table->contents[table->size - 1] = ':';
    }
    else if (type == DIRECT_LIBRARY)
    {
        add_string(area_directive, "-defaultlib");
        table->contents[table->size - 1] = ':';
    }
    add_string(area_directive, dir);
    table->contents[table->size - 1] = ' ';
}


/* sets the name of a specific area */

static void set_area_name(size_t area, char *name, int local)
{
    area_header *table;

    table = &area_table[area];
    /* Area names may be local, global or weak, but may also begin with
     * a dot, which indicates that the name should not normally be placed
     * in the symbol table of the object file.  Names beginning with a dot
     * or that are locally visible will therefore not be preceded by an
     * optional prefix.
     */
    if (table->name != NULL)
        free(table->name);
    if (prepend_prefix && (*name != '.') && !local)
    {
        table->name = (char *) getspace(strlen(name) + external_prefix_size +
                                        1);
        strcpy(table->name, external_prefix);
        strcat(table->name, name);
    }
    else
    {
        table->name = (char *) getspace(strlen(name) + 1);
        strcpy(table->name, name);
    }
}


/* initialises the area table */

static void init_area_table(void)
{
    size_t area;

    for (area = 0; area < area_num; area++)
    {
        highest_area = area + 1;
        while (area >= area_table_size)
            expand_area_table();
        if (area_info_table[area].name != NULL)
            set_area_name(area, area_info_table[area].name, 1);
    }
}


/* deletes the entire area table */

static void free_area_table(void)
{
    area_header *table;
    size_t index;

    for (index = 0; index < highest_area; index++)
    {
        table = &area_table[index];
        if (table->name != NULL)
            freespace(table->name);
        if (table->contents != NULL)
            freespace(table->contents);
        free_members(index);
        free_aliases(index);
        free_linenos(table->lines);
    }
    if (area_table != NULL)
        freespace(area_table);
    area_table = NULL;
    area_table_size = 0;
    highest_area = 0;
}


/* returns the label name for a given area and offset */

static char *label_name(char *name, size_t area, long offset, int end)
{
    static char label[1024];
    area_header *table;
    member_node *node;

    table = &area_table[area];
    /* We first look for an entry within the area.
     */
    for (node = table->members.head; (name == NULL) && (node->next != NULL) &&
         (node->offset <= offset); node = node->next)
        if ((node->type == 0) &&
            (node->offset + node->data.entry.size > offset))
        {
            name = node->name;
            offset -= node->offset;
        }
    /* If that failed and we are in a user-defined section then we should
     * try to use the first entry within the area and adjust the offset
     * accordingly.
     */
    node = table->members.head;
    if ((name == NULL) && (node->next != NULL) && (table->link == link_section))
    {
        name = node->name;
        offset -= node->offset;
    }
    /* If that failed then we should just use the area name.
     */
    if (name == NULL)
        name = table->name;
    /* If that failed then we use the diagnostic name of the area.  This
     * will occur for unused areas that are used elsewhere in the backend
     * and will most likely produce an undefined symbol, but it is expected
     * that such results are for tracing purposes only.
     */
    if ((name == NULL) && (area < area_num))
        name = area_info_table[area].area;
    if (end != 0)
    {
        if (*name == '.')
            end = 0;
        else
            label[0] = '.';
        if (offset != 0)
            sprintf(label + end, "%s%+ld", name, offset);
        else
            strcpy(label + end, name);
        strcat(label, ".end");
    }
    else if (offset != 0)
        sprintf(label, "%s%+ld", name, offset);
    else
        return name;
    return label;
}


/* switches the current assembler section */

static void enter_section(long section)
{
    area_header *user_table;
    section_info *table;
    char *name;

    if (section != sec_null)
    {
        if (section != current_section)
        {
            if (section < sec_null)
            {
                name = area_table[-section].name;
                /* The following code represents the culmination of a series of
                 * hacks in order to allow data entries to be placed in the text
                 * section.  The TEXTDATA section is a user-defined section
                 * which contains all of the data entries for the text section.
                 */
                if (strcmp(name, "TEXTDATA") == 0)
                    section = sec_text;
            }
            if (section > sec_null)
            {
                if (section == sec_text)
                    name = ".CODE";
                else if (section == sec_data)
                    name = ".DATA";
                else if (section == sec_bss)
                    name = ".DATA?";
                else if (section == sec_rodata)
                    name = ".CONST";
                else
                    name = section_info_table[section].name;
            }
            if (current_section != sec_null)
                write_code("%s\t%s\n\n", assembler->cursect,
                           assembler->endsect);
            if ((section == sec_text) || (section == sec_data) ||
                (section == sec_bss) || (section == sec_rodata))
                write_code("\t%s\n", name);
            else
            {
                write_code("\t%s\t%s", name, assembler->section);
                if (section > sec_null)
                {
                    table = &section_info_table[section];
                    if ((table->flags & COFF_SECT_INFO) ||
                        (table->flags & COFF_SECT_DISCARD))
                        write_code(" %s", assembler->byte);
                    else
                        write_code(" %s", assembler->word);
                    if (!(table->flags & COFF_SECT_WRITE))
                        write_code(" READONLY");
                    write_code(" %s", assembler->global);
                    if (section == sec_debsym)
                        write_code(" 'DEBSYM'");
                    else if (section == sec_debtyp)
                        write_code(" 'DEBTYP'");
                    else if (table->flags & COFF_SECT_CODE)
                        write_code(" 'CODE'");
                    else
                        write_code(" 'DATA'");
                }
                else
                {
                    user_table = &area_table[-section];
                    if (user_table->flags & SECT_DEBUG)
                        write_code(" %s", assembler->byte);
                    else
                        write_code(" %s", assembler->word);
                    if (user_table->flags & SECT_READONLY)
                        write_code(" READONLY");
                    write_code(" %s", assembler->global);
                    if (user_table->flags & SECT_DEBUG)
                        write_code(" 'DEBSYM'");
                    else if (user_table->flags & SECT_EXECUTABLE)
                        write_code(" 'CODE'");
                    else
                        write_code(" 'DATA'");
                }
                write_code("\n");
            }
            write_code("\n");
            current_section = section;
        }
        section_stack[section_level] = section;
    }
}


/* returns the name of a section */

char *psectionname(int section)
{
    char *name;

    if (suppress_code_gen)
        return NULL;
    if (put_trace)
        put_message("psectionname(section = %d)\n", section);
    assert(put_initialised);
    assert(put_unterminated);
    assert(((section > sec_null) && (section < sec_num)) ||
           ((section > -highest_area) && (section <= -area_num)));
    if (section > sec_null)
        name = section_info_table[section].name;
    else
    {
        name = area_table[-section].name;
        /* The following code represents the culmination of a series of
         * hacks in order to allow data entries to be placed in the text
         * section.  The TEXTDATA section is a user-defined section
         * which contains all of the data entries for the text section.
         */
        if (strcmp(name, "TEXTDATA") == 0)
            name = section_info_table[sec_text].name;
    }
    if (put_trace)
        put_message("psectionname() returns (name = \"%s\")\n",
                    name ? name : "NULL");
    return name;
}


/* returns the section id for a section name */

int pfindsection(char *name)
{
    area_header *table;
    size_t index;
    long section;

    if (suppress_code_gen)
        return sec_null;
    if (put_trace)
        put_message("pfindsection(name = \"%s\")\n", name);
    assert(put_initialised);
    assert(put_unterminated);
    assert((name != NULL) && (*name != '\0'));
    /* We initially search in the predefined section table for a section that
     * matches, and if that fails then we look for the first user-defined
     * section that matches.  This ordering is useful for checking that a
     * user-defined section does not clash with a predefined section.
     */
    section = sec_null;
    for (index = 1; index < sec_num; index++)
        if (strcmp(section_info_table[index].name, name) == 0)
        {
            section = index;
            break;
        }
    if (section == sec_null)
        for (index = area_num; index < highest_area; index++)
        {
            table = &area_table[index];
            if ((table->link == link_section) &&
                (strcmp(table->name, name) == 0))
            {
                section = -index;
                break;
            }
        }
    if (put_trace)
        put_message("pfindsection() returns (section = %d)\n", section);
    return section;
}


/* returns the section associated with an area */

int pareasection(int area)
{
    area_header *table;
    long section;

    if (suppress_code_gen)
        return sec_null;
    if (put_trace)
        put_message("pareasection(area = %d)\n", area);
    assert(put_initialised);
    assert(put_unterminated);
    assert((area >= 0) && (area < highest_area));
    /* This function provides the only safe and reliable way to obtain
     * the section id that a given area maps onto.  The resulting section
     * id can then only be used with the ppushsection() and ppopsection()
     * functions.
     */
    table = &area_table[area];
    if (area < area_num)
        section = area_info_table[area].section;
    else if (table->link == link_section)
        section = -area;
    else if (is_function(table->link))
        section = area_info_table[area_code].section;
    else if ((table->link == link_local) || (table->link == link_global) ||
             (table->link == link_weak))
        section = area_info_table[area_gla].section;
    else if ((table->link == link_local_common) ||
             (table->link == link_global_common) ||
             (table->link == link_weak_common))
        section = area_info_table[area_bss].section;
    else
        section = sec_null;
    if (put_trace)
        put_message("pareasection() returns (section = %d)\n", section);
    return section;
}


/* returns the area associated with a section */

int psectionarea(int section)
{
    size_t area, index;

    if (suppress_code_gen)
        return 0;
    if (put_trace)
        put_message("psectionarea(section = %d)\n", section);
    assert(put_initialised);
    assert(put_unterminated);
    assert(((section > sec_null) && (section < sec_num)) ||
           ((section > -highest_area) && (section <= -area_num)));
    area = 0;
    if ((section > sec_null) && (section < sec_num))
        /* If the section is a predefined section then there should be a
         * predefined area that uses it.  However, there may be several areas
         * that use the same section, but we can only return the first one we
         * come across without any further information.
         */
        for (index = 1; index < area_num; index++)
        {
            if (area_info_table[index].section == section)
            {
                area = index;
                break;
            }
        }
    else
        area = -section;
    if (put_trace)
        put_message("psectionarea() returns (area = %d)\n", area);
    return area;
}


/* pushes a new section onto the section stack and enters it */

void ppushsection(int section)
{
    if (suppress_code_gen)
        return;
    if (put_trace)
        put_message("ppushsection(section = %d)\n", section);
    assert(put_initialised);
    assert(put_unterminated);
    assert(!object_generating);
    assert(((section > sec_null) && (section < sec_num)) ||
           ((section > -highest_area) && (section <= -area_num)));
    /* Avoid calling this function for data sections as there are now
     * functions available for placing data at any position in a data
     * area, which will then be dumped at the end of the assembler file.
     * This function should really only be used for switching between
     * different text sections.
     */
    if (++section_level == SECTION_STACK_SIZE)
        put_error("section nesting level too deep");
    enter_section(section);
}


/* pops the current section from the section stack and */
/* enters the previous section before returning it */

int ppopsection(void)
{
    long section;

    if (suppress_code_gen)
        return sec_null;
    if (put_trace)
        put_message("ppopsection()\n");
    assert(put_initialised);
    assert(put_unterminated);
    assert(!object_generating);
    assert(section_level > 0);
    /* Avoid calling this function for data sections as there are now
     * functions available for placing data at any position in a data
     * area, which will then be dumped at the end of the assembler file.
     * This function should really only be used for switching between
     * different text sections.
     */
    section = section_stack[--section_level];
    enter_section(section);
    if (put_trace)
        put_message("ppopsection() returns (section = %d)\n", section);
    return section;
}


/* returns the initialisation data for a block of bytes */
/* located within an area at a specific offset */

char *pgetbytes(int area, int offset, int length)
{
    area_header *table;
    char *buffer;

    if (suppress_code_gen)
        return NULL;
    if (put_trace)
        put_message("pgetbytes(area = %d, offset = %d, length = %d)\n", area,
                    offset, length);
    assert(put_initialised);
    assert(put_unterminated);
    assert((area >= 0) && (area < highest_area));
    assert(!is_function(area_table[area].link));
    assert((offset >= 0) && (length > 0));
    table = &area_table[area];
    /* We must return a valid pointer only if the area contains data and
     * is large enough to hold data of a given length at offset into the
     * area.  All non-data areas will return NULL.
     */
    buffer = NULL;
    if ((table->link != link_local_common) &&
        (table->link != link_global_common) &&
        (table->link != link_weak_common) &&
        (offset + length <= table->size))
        buffer = table->contents + offset;
    if (put_trace)
    {
        put_message("pgetbytes() returns (buffer = 0x%08lX)\n", buffer);
        if (buffer != NULL)
            hex_dump((unsigned char *) buffer, length);
    }
    return buffer;
}


/* allocates a given amount of space at the end of a specified area */

int preservespace(int area, int length, int alignment, int uninitialised)
{
    area_header *table;
    long offset;

    if (suppress_code_gen)
        return 0;
    if (put_trace)
        put_message("preservespace(area = %d, length = %d, alignment = %d, "
                    "uninitialised = %d)\n", area, length, alignment,
                    uninitialised);
    assert(put_initialised);
    assert(put_unterminated);
    assert((area >= 0) && (area < highest_area));
    assert(!is_function(area_table[area].link));
    assert((alignment > 0) && is_power_of_two(alignment));
    table = &area_table[area];
    if (area == area_stack)
    {
        table->size = round_up(table->size + length, alignment);
        offset = -table->size;
        if (table->size > max_stack_size)
            max_stack_size = table->size;
    }
    else
    {
        table->size = round_up(table->size, alignment);
        offset = table->size;
        table->size += length;
    }
    if ((area != area_stack) && (area != area_params) &&
        ((area == area_bss) || !uninitialised))
        while (table->size > table->max)
            expand_contents(area);
    if (put_trace)
        put_message("preservespace() returns (offset = %ld)\n", offset);
    return offset;
}


/* allocates temporary memory from the end of the stack */

int etempspace(int length, int alignment)
{
    long offset;

    if (suppress_code_gen)
        return 0;
    if (put_trace)
        put_message("etempspace(length = %d, alignment = %d)\n", length,
                    alignment);
    assert(put_initialised);
    assert(put_unterminated);
    offset = preservespace(area_stack, length, alignment, 0);
    if (put_trace)
        put_message("etempspace() returns (offset = %ld)\n", offset);
    return offset;
}


/* allocates permanent memory from the end of the GLA */

int epermspace(int length, int alignment)
{
    long offset;

    if (suppress_code_gen)
        return 0;
    if (put_trace)
        put_message("epermspace(length = %d, alignment = %d)\n", length,
                    alignment);
    assert(put_initialised);
    assert(put_unterminated);
    offset = preservespace(area_gla, length, alignment, 0);
    if (put_trace)
        put_message("epermspace() returns (offset = %ld)\n", offset);
    return offset;
}


/* allocates working memory from the end of a suitable area */

int eworkspace(int length, int alignment, int type, int *area)
{
    long offset;

    if (suppress_code_gen)
        return 0;
    if (put_trace)
        put_message("eworkspace(length = %d, alignment = %d, type = %d, "
                    "area = 0x%08lX)\n", length, alignment, type, area);
    assert(put_initialised);
    assert(put_unterminated);
    assert(area != NULL);
    if (type == 0)
        *area = area_stack;
    else if (type == 2)
        *area = area_sst;
    else
        *area = area_gla;
    offset = preservespace(*area, length, alignment, 0);
    if (put_trace)
        put_message("eworkspace() returns (area = %d, offset = %ld)\n", *area,
                    offset);
    return offset;
}


/* initialises a block of bytes located within an area at a specific offset */

void pdinit(int area, int offset, int length)
{
    area_header *table;
    size_t total;

    if (suppress_code_gen)
        return;
    if (put_trace)
        put_message("pdinit(area = %d, offset = %d, length = %d)\n", area,
                    offset, length);
    assert(put_initialised);
    assert(put_unterminated);
    assert((area >= 0) && (area < highest_area));
    assert(!is_function(area_table[area].link));
    assert((offset >= 0) && (length > 0));
    table = &area_table[area];
    /* If the area being initialised is common or external data then
     * we can upgrade the area to become an initialised data area.
     * However, we must first check that any common areas have been
     * finalised.
     */
    if ((table->link == link_local_common) ||
        (table->link == link_global_common) ||
        (table->link == link_weak_common))
        assert(table->size > 0);
    if (table->link == link_local_common)
        table->link = link_local;
    else if ((table->link == link_global_common) ||
             (table->link == link_global_external))
        table->link = link_global;
    else if ((table->link == link_weak_common) ||
             (table->link == link_weak_external))
        table->link = link_weak;
    total = offset + length;
    while (total > table->max)
        expand_contents(area);
    if (total > table->size)
        table->size = total;
}


/* initialises a block of bytes located within the code area */
/* at a specific offset to the contents of a buffer */

void pcbytes(int offset, int length, char *buffer)
{
    if (suppress_code_gen)
        return;
    if (put_trace)
    {
        put_message("pcbytes(offset = %d, length = %d)\n", offset, length);
        hex_dump((unsigned char *) buffer, length);
    }
    assert(put_initialised);
    assert(put_unterminated);
    assert(object_generating);
    assert(buffer != NULL);
    pdinit(area_code, offset, length);
    memcpy(area_table[area_code].contents + offset, buffer, length);
}


/* initialises a block of bytes located within an area */
/* at a specific offset to the contents of a buffer */

void pdbytes(int area, int offset, int length, char *buffer)
{
    if (suppress_code_gen)
        return;
    if (put_trace)
    {
        put_message("pdbytes(area = %d, offset = %d, length = %d)\n", area,
                    offset, length);
        hex_dump((unsigned char *) buffer, length);
    }
    assert(put_initialised);
    assert(put_unterminated);
    assert((area != area_code) && (area != area_bss));
    assert(buffer != NULL);
    pdinit(area, offset, length);
    memcpy(area_table[area].contents + offset, buffer, length);
}


/* initialises multiple consecutive blocks of bytes */
/* located within an area at a specific offset */

void pdpattern(int area, int offset, int repeat, int length, char *buffer)
{
    size_t index;

    if (suppress_code_gen)
        return;
    if (put_trace)
    {
        put_message("pdpattern(area = %d, offset = %d, repeat = %d, "
                    "length = %d)\n", area, offset, repeat, length);
        hex_dump((unsigned char *) buffer, length);
    }
    assert(put_initialised);
    assert(put_unterminated);
    assert(repeat > 0);
    for (index = 0; index < repeat; index++, offset += length)
        pdbytes(area, offset, length, buffer);
}


/* initialises a byte within an area at a specific offset */

void pd(int area, int offset, int value)
{
    char *buffer;

    if (suppress_code_gen)
        return;
    if (put_trace)
        put_message("pd(area = %d, offset = %d, value = %d)\n", area, offset,
                    value);
    assert(put_initialised);
    assert(put_unterminated);
    buffer = (char *) &value;
    if (host_endian == BIG_ENDIAN)
        buffer += 3;
    pdbytes(area, offset, 1, buffer);
}


/* initialises a 2-byte word within an area at a specific offset */

void pd2(int area, int offset, int value)
{
    char *buffer;

    if (suppress_code_gen)
        return;
    if (put_trace)
        put_message("pd2(area = %d, offset = %d, value = %d)\n", area, offset,
                    value);
    assert(put_initialised);
    assert(put_unterminated);
    buffer = (char *) &value;
    if (host_endian == BIG_ENDIAN)
        buffer += 2;
    pdbytes(area, offset, 2, buffer);
}


/* initialises a 4-byte word within an area at a specific offset */

void pd4(int area, int offset, int value)
{
    char *buffer;

    if (suppress_code_gen)
        return;
    if (put_trace)
        put_message("pd4(area = %d, offset = %d, value = %d)\n", area, offset,
                    value);
    assert(put_initialised);
    assert(put_unterminated);
    buffer = (char *) &value;
    pdbytes(area, offset, 4, buffer);
}


/* initialises a 4-byte word within the code area at a specific offset */

void pcword(int offset, int value)
{
    if (suppress_code_gen)
        return;
    if (put_trace)
        put_message("pcword(offset = %d, value = %d)\n", offset, value);
    assert(put_initialised);
    assert(put_unterminated);
    assert(object_generating);
    pcbytes(offset, 4, (char *) &value);
}


/* inserts a string into the assembler file */

void pasmstring(char *string)
{
    if (suppress_code_gen)
        return;
    if (put_trace)
        put_message("pasmstring(string = \"%s\")\n", string);
    assert(put_initialised);
    assert(put_unterminated);
    assert(!object_generating);
    assert((string != NULL) && (*string != '\0'));
    write_code("%s", string);
}


/* inserts a comment into the assembler file */

void pcomment(char *string)
{
    if (suppress_code_gen)
        return;
    if (put_trace)
        put_message("pcomment(string = \"%s\")\n", string);
    assert(put_initialised);
    assert(put_unterminated);
    assert(!object_generating);
    assert((string != NULL) && (*string != '\0'));
    write_code("\t%s %s\n", assembler->comment, string);
}


/* adds a string to the comment section of an object file */

void pident(char *string)
{
    if (suppress_code_gen)
        return;
    if (put_trace)
        put_message("pident(string = \"%s\")\n", string);
    assert(put_initialised);
    assert(put_unterminated);
    assert((string != NULL) && (*string != '\0'));
    add_string(area_comment, string);
}


/* notes the registers used by the compiler */

void preginfo(int int_registers, int float_registers)
{
}


/* sets the current filename for line number tables */

void pfile(char *file)
{
    if (suppress_code_gen)
        return;
    if (put_trace)
        put_message("pfile(\"%s\")\n", file);
    assert(put_initialised);
    assert(put_unterminated);
    assert(file != NULL);
    current_filename = file;
}


/* adds a line number to a given function */

void plinestart(int area, int line, int column, int offset)
{
    if (suppress_code_gen)
        return;
    if (put_trace)
    {
        put_message("plinestart(area = %d, line = %d, column = %d, ", area,
                    line, column);
        if (object_generating)
            put_message("offset = %d)\n", offset);
        else
            put_message("label = \"%s\")\n", (char *) offset);
    }
    assert(put_initialised);
    assert(put_unterminated);
    assert((area >= 0) && (area < highest_area));
    assert(is_function(area_table[area].link));
    assert((line > 0) && (column >= 0));
    if (object_generating)
        assert(offset >= 0);
    else
        assert((char *) offset != NULL);
    assert(current_filename != NULL);
    if ((debug_format == DEBUG_COFF) || (debug_format == DEBUG_CODEVIEW))
        add_lineno(area, current_filename, (unsigned short) line,
                   (void *) offset);
}


/* returns the label name for a given area and offset */

char *pgetname(int area, int offset)
{
    char *name;

    if (put_trace)
        put_message("pgetname(area = %d, offset = %d)\n", area, offset);
    assert(put_initialised);
    assert(put_unterminated);
    assert((area >= 0) && (area < highest_area));
    name = label_name(NULL, area, offset, 0);
    if (put_trace)
        put_message("pgetname() returns (name = \"%s\")\n", name);
    return name;
}


/* returns the name of a given area */

char *pgivename(int area)
{
    char *name;

    if (put_trace)
        put_message("pgivename(area = %d)\n", area);
    assert(put_initialised);
    assert(put_unterminated);
    assert((area >= 0) && (area < highest_area));
    name = area_table[area].name;
    /* If that failed then we use the diagnostic name of the area.  This
     * will occur for unused areas that are used elsewhere in the backend
     * and will most likely produce an undefined symbol, but it is expected
     * that such results are for tracing purposes only.
     */
    if ((area < area_num) && (name == NULL))
        name = area_info_table[area].area;
    if (put_trace)
        put_message("pgivename() returns (name = \"%s\")\n", name);
    return name;
}


/* returns the current size of a given area */

int pgivecurrentsize(int area)
{
    int size;

    if (put_trace)
        put_message("pgivecurrentsize(area = %d)\n", area);
    assert(put_initialised);
    assert(put_unterminated);
    assert((area >= 0) && (area < highest_area));
    size = area_table[area].size;
    if (put_trace)
        put_message("pgivecurrentsize() returns (size = %d)\n", size);
    return size;
}


/* returns the maximum stack size for the current function */

int pstackmax(void)
{
    if (put_trace)
        put_message("pstackmax()\n");
    assert(put_initialised);
    assert(put_unterminated);
    if (put_trace)
        put_message("pstackmax() returns (max = %d)\n", max_stack_size);
    return max_stack_size;
}


/* sets the current stack offset and returns the previous offset */

int pstackoffset(int offset)
{
    long old_offset;

    if (put_trace)
        put_message("pstackoffset(offset = %d)\n", offset);
    assert(put_initialised);
    assert(put_unterminated);
    assert(offset <= 0);
    old_offset = -area_table[area_stack].size;
    area_table[area_stack].size = -offset;
    if (-offset > max_stack_size)
        max_stack_size = -offset;
    if (put_trace)
        put_message("pstackoffset() returns (old_offset = %d)\n", old_offset);
    return old_offset;
}


/* sets the alignment for a specific area and returns the previous alignment */

int pareaalign(int area, int alignment)
{
    section_type section;
    size_t old_align;

    if (put_trace)
        put_message("pareaalign(area = %d, alignment = %d)\n", area, alignment);
    assert(put_initialised);
    assert(put_unterminated);
    assert((area >= 0) && (area < highest_area));
    assert(!is_function(area_table[area].link));
    assert((alignment == 0) || is_power_of_two(alignment));
    old_align = area_table[area].align;
    if (alignment > 0)
    {
        area_table[area].align = alignment;
        if (area < area_num)
        {
            section = area_info_table[area].section;
            if ((section != sec_null) &&
                (section_info_table[section].align < alignment))
                section_info_table[section].align = alignment;
        }
    }
    if (put_trace)
        put_message("pareaalign() returns (old_alignment = %d)\n", old_align);
    return old_align;
}


/* sets the alignment for a common area */

void paligncommon(int area, int alignment)
{
    linkage link;

    if (put_trace)
        put_message("paligncommon(area = %d, alignment = %d)\n", area,
                    alignment);
    assert(put_initialised);
    assert(put_unterminated);
    assert((area >= area_num) && (area < highest_area));
    link = area_table[area].link;
    assert((link == link_local_common) || (link == link_global_common) ||
           (link == link_weak_common));
    pareaalign(area, alignment);
}


/* sets the filler byte for a specific area */

void pfiller(int area, int filler)
{
    if (put_trace)
        put_message("pfiller(area = %d, filler = 0x%08lX)\n", area, filler);
    assert(put_initialised);
    assert(put_unterminated);
    assert((area >= 0) && (area < highest_area));
    assert((area == area_gla) || (area == area_sst) || (area >= area_num));
    assert(!is_function(area_table[area].link));
    assert((filler >= 0) && (filler < 256));
    area_table[area].fill = filler;
}


/* adds a relocation request of a specified size from a host area to an */
/* assembler label of a specified type */

void pfixlabel3(int area, int offset, int target_area, char *label,
                int label_offset, int size, int type)
{
    unsigned char fix;

    if (suppress_code_gen)
        return;
    if (put_trace)
        put_message("pfixlabel3(area = %d, offset = %d, target_area = %d, "
                    "label = \"%s\", label_offset = %d, size = %d, "
                    "type = %d)\n", area, offset, target_area, label,
                    label_offset, size, type);
    assert(put_initialised);
    assert(put_unterminated);
    assert(!object_generating);
    assert((area != area_code) && (area != area_bss));
    assert((target_area >= 0) && (target_area < highest_area));
    assert((label != NULL) && (*label != '\0'));
    /* When the relocation type is positive we cross-reference the
     * relocation information table to obtain the architecture-dependent
     * relocation type.  If the relocation type is negative then its
     * positive value will be used without referencing the relocation
     * information table.
     */
    if (type >= 0)
    {
        assert(type < 14);
        fix = relocation_table[type];
        assert(fix > 0);
    }
    else
        fix = -type;
    if (fix == COFF_REL_386_SEC)
        if (size == 0)
            size = 2;
        else
            assert(size == 2);
    else
        if (size == 0)
            size = 4;
        else
            assert(size == 4);
    /* This relocation type is for assembler-generating compilers only.
     * It saves having to create a new area for a label by storing the
     * label name with the relocation.
     */
    if (size == 2)
        pd2(area, offset, 0);
    else
        pd4(area, offset, 0);
    add_member(area, offset, fix, label, 0, label_offset, size, 0);
    /* Ensure that the section of the target area will be given an entry
     * in the symbol table of the generated object file.
     */
    area_table[target_area].offset++;
}


/* adds a relocation request of a specified size from a host area to an */
/* assembler label */

void pfixlabel2(int area, int offset, int target_area, char *label,
                int label_offset, int size)
{
    if (suppress_code_gen)
        return;
    if (put_trace)
        put_message("pfixlabel2(area = %d, offset = %d, target_area = %d, "
                    "label = \"%s\", label_offset = %d, size = %d)\n", area,
                    offset, target_area, label, label_offset, size);
    assert(put_initialised);
    assert(put_unterminated);
    pfixlabel3(area, offset, target_area, label, label_offset, size, 0);
}


/* adds a relocation request from a host area to an assembler label */

void pfixlabel(int area, int offset, int target_area, char *label,
               int label_offset)
{
    if (suppress_code_gen)
        return;
    if (put_trace)
        put_message("pfixlabel(area = %d, offset = %d, target_area = %d, "
                    "label = \"%s\", label_offset = %d)\n", area, offset,
                    target_area, label, label_offset);
    assert(put_initialised);
    assert(put_unterminated);
    pfixlabel2(area, offset, target_area, label, label_offset, 4);
}


/* adds a relocation request from a host area to a target area */
/* of a specified type */

void pfix2(int area, int offset, int target_area, int target_offset, int type)
{
    size_t size;
    unsigned char fix;

    if (suppress_code_gen)
        return;
    if (put_trace)
        put_message("pfix2(area = %d, offset = %d, target_area = %d, "
                    "target_offset = %d, type = %d)\n", area, offset,
                    target_area, target_offset, type);
    assert(put_initialised);
    assert(put_unterminated);
    assert(area != area_bss);
    assert((target_area >= 0) && (target_area < highest_area));
    /* When the relocation type is positive we cross-reference the
     * relocation information table to obtain the architecture-dependent
     * relocation type.  If the relocation type is negative then its
     * positive value will be used without referencing the relocation
     * information table.
     */
    if (type >= 0)
    {
        assert(type < 14);
        fix = relocation_table[type];
        assert(fix > 0);
    }
    else
        fix = -type;
    if (fix == COFF_REL_386_SEC)
        size = 2;
    else
        size = 4;
    if (area == area_code)
        pdinit(area_code, offset, size);
    else if (size == 2)
        pd2(area, offset, 0);
    else
        pd4(area, offset, 0);
    add_member(area, offset, fix, NULL, target_area, target_offset, size, 0);
    /* When generating assembler, ensure that the section of the target area
     * will be given an entry in the symbol table of the generated object file.
     */
    if (!object_generating)
        area_table[target_area].offset++;
}


/* adds a relocation request from a host area to a target area */

void pfix(int area, int offset, int target_area, int target_offset)
{
    if (suppress_code_gen)
        return;
    if (put_trace)
        put_message("pfix(area = %d, offset = %d, target_area = %d, "
                    "target_offset = %d)\n", area, offset, target_area,
                    target_offset);
    assert(put_initialised);
    assert(put_unterminated);
    pfix2(area, offset, target_area, target_offset, 0);
}


/* adds a relocation request from a host area to the start of a target area */

void pdxref(int area, int offset, int target_area)
{
    if (suppress_code_gen)
        return;
    if (put_trace)
        put_message("pdxref(area = %d, offset = %d, target_area = %d)\n", area,
                    offset, target_area);
    assert(put_initialised);
    assert(put_unterminated);
    pfix(area, offset, target_area, 0);
}


/* defines a data symbol at a given area and offset */
/* and with a specified visibility */

void pdataentry2(char *name, int visibility, int area, int length, int offset)
{
    member_node *node;
    linkage link;

    if (suppress_code_gen)
        return;
    if (put_trace)
        put_message("pdataentry2(name = \"%s\", visibility = %d, area = %d, "
                    "length = %d, offset = %d)\n", name, visibility, area,
                    length, offset);
    assert(put_initialised);
    assert(put_unterminated);
    assert((name != NULL) && (*name != '\0'));
    assert(area != area_code);
    if (visibility > 0)
        link = link_local;
    else if (visibility < 0)
        link = link_weak;
    else
        link = link_global;
    pdinit(area, offset, length);
    node = add_member(area, offset, 0, name, link, length, 0, 0);
    if (visibility & 0x80000000)
        add_directive(DIRECT_EXPORT, node->name);
}


/* defines a global data symbol at a given area and offset */

void pdataentry(char *name, int area, int length, int offset)
{
    if (suppress_code_gen)
        return;
    if (put_trace)
        put_message("pdataentry(name = \"%s\", area = %d, length = %d, "
                    "offset = %d)\n", name, area, length, offset);
    assert(put_initialised);
    assert(put_unterminated);
    pdataentry2(name, 0, area, length, offset);
}


/* reserves a new area */

int pnextsymbol(void)
{
    int area;

    if (suppress_code_gen)
        return 0;
    if (put_trace)
        put_message("pnextsymbol()\n");
    assert(put_initialised);
    assert(put_unterminated);
    /* This function traditionally allocated a new area for an unnamed
     * function symbol.  However, as we always know the name of a function
     * symbol, even for a function prototype, we can use pxname() instead
     * and so this function now allocates an empty local data area.
     */
    area = highest_area++;
    while (area >= area_table_size)
        expand_area_table();
    if (put_trace)
        put_message("pnextsymbol() returns (area = %d)\n", area);
    return area;
}


/* defines a new section with a specified alignment */

int psection(char *name, int alignment, int flags, char *label)
{
    area_header *table;
    int area;

    if (suppress_code_gen)
        return 0;
    if (put_trace)
        put_message("psection(name = \"%s\", alignment = %d, "
                    "flags = 0x%08lX, label = \"%s\")\n", name, alignment,
                    flags, label);
    assert(put_initialised);
    assert(put_unterminated);
    assert((name != NULL) && (*name != '\0'));
    assert((alignment > 0) && is_power_of_two(alignment));
    area = pnextsymbol();
    set_area_name(area, name, 1);
    table = &area_table[area];
    table->link = link_section;
    table->flags = flags;
    table->align = alignment;
    add_member(area, 0, 0, label, link_local, 0, 0, 1);
    if (put_trace)
        put_message("psection() returns (area = %d)\n", area);
    return area;
}


/* defines a data area with a specified visibility and parent data area */

int pcdataarea2(int visibility, int length, int alignment, int parent,
                char *name)
{
    area_header *table;
    int area;

    if (suppress_code_gen)
        return 0;
    if (put_trace)
        put_message("pcdataarea2(visibility = %d, length = %d, alignment = %d, "
                    "parent = %d, name = \"%s\")\n", visibility, length,
                    alignment, parent, name);
    assert(put_initialised);
    assert(put_unterminated);
    assert((alignment > 0) && is_power_of_two(alignment));
    assert((parent != area_code) && (parent != area_init) &&
           (parent != area_fini) && !is_function((linkage) parent));
    assert((name != NULL) && (*name != '\0'));
    area = pnextsymbol();
    set_area_name(area, name, (visibility > 0));
    table = &area_table[area];
    while (length > table->max)
        expand_contents(area);
    if (visibility > 0)
        table->link = link_local;
    else if (visibility < 0)
        table->link = link_weak;
    else
        table->link = link_global;
    if (visibility & 0x80000000)
        add_directive(DIRECT_EXPORT, table->name);
    table->parent = parent;
    table->size = length;
    table->align = alignment;
    if (put_trace)
        put_message("pcdataarea2() returns (area = %d)\n", area);
    return area;
}


/* defines a data area with a specified visibility */

int pcdataarea(int visibility, int length, int alignment, char *name)
{
    int area;

    if (suppress_code_gen)
        return 0;
    if (put_trace)
        put_message("pcdataarea(visibility = %d, length = %d, alignment = %d, "
                    "name = \"%s\")\n", visibility, length, alignment, name);
    assert(put_initialised);
    assert(put_unterminated);
    area = pcdataarea2(visibility, length, alignment, area_gla, name);
    if (put_trace)
        put_message("pcdataarea() returns (area = %d)\n", area);
    return area;
}


/* defines a common area with a specified visibility */

int pcommon2(int visibility, char *name)
{
    int area;

    if (suppress_code_gen)
        return 0;
    if (put_trace)
        put_message("pcommon2(visibility = %d, name = \"%s\")\n", visibility,
                    name);
    assert(put_initialised);
    assert(put_unterminated);
    assert((name != NULL) && (*name != '\0'));
    area = pnextsymbol();
    set_area_name(area, name, (visibility > 0));
    /* A common area is unfinalised until pendcommon() or pendcommon2() are
     * called.  This is indicated by the area having a size of zero.
     */
    if (visibility > 0)
        area_table[area].link = link_local_common;
    else if (visibility < 0)
        area_table[area].link = link_weak_common;
    else
        area_table[area].link = link_global_common;
    if (visibility & 0x80000000)
        add_directive(DIRECT_EXPORT, area_table[area].name);
    if (put_trace)
        put_message("pcommon2() returns (area = %d)\n", area);
    return area;
}


/* defines a common area with global visibility */

int pcommon(char *name)
{
    int area;

    if (suppress_code_gen)
        return 0;
    if (put_trace)
        put_message("pcommon(name = \"%s\")\n", name);
    assert(put_initialised);
    assert(put_unterminated);
    area = pcommon2(0, name);
    if (put_trace)
        put_message("pcommon() returns (area = %d)\n", area);
    return area;
}


/* finalises a common area with a specified alignment */

void pendcommon2(int area, int length, int flags, int alignment)
{
    if (suppress_code_gen)
        return;
    if (put_trace)
        put_message("pendcommon2(area = %d, length = %d, flags = 0x%08lX, "
                    "alignment = %d)\n", area, length, flags, alignment);
    assert(put_initialised);
    assert(put_unterminated);
    assert((area >= area_num) && (area < highest_area));
    assert((area_table[area].link == link_local_common) ||
           (area_table[area].link == link_global_common) ||
           (area_table[area].link == link_weak_common));
    assert(area_table[area].size == 0);
    assert(length > 0);
    assert((alignment > 0) && is_power_of_two(alignment));
    area_table[area].size = length;
    area_table[area].align = alignment;
}


/* finalises a common area with a default alignment */

void pendcommon(int area, int length)
{
    if (suppress_code_gen)
        return;
    if (put_trace)
        put_message("pendcommon(area = %d, length = %d)\n", area, length);
    assert(put_initialised);
    assert(put_unterminated);
    pendcommon2(area, length, 0, 4);
}


/* declares an external data symbol */

int pdxname(int visibility, char *name)
{
    int area;

    if (suppress_code_gen)
        return 0;
    if (put_trace)
        put_message("pdxname(visibility = %d, name = \"%s\")\n", visibility,
                    name);
    assert(put_initialised);
    assert(put_unterminated);
    assert((name != NULL) && (*name != '\0'));
    area = pnextsymbol();
    set_area_name(area, name, 0);
    if (visibility < 0)
        area_table[area].link = link_weak_external;
    else
        area_table[area].link = link_global_external;
    area_table[area].align = 4;
    if (put_trace)
        put_message("pdxname() returns (area = %d)\n", area);
    return area;
}


/* declares an external function symbol */

int pxname(int visibility, char *name)
{
    int area;

    if (suppress_code_gen)
        return 0;
    if (put_trace)
        put_message("pxname(visibility = %d, name = \"%s\")\n", visibility,
                    name);
    assert(put_initialised);
    assert(put_unterminated);
    assert((name != NULL) && (*name != '\0'));
    area = pnextsymbol();
    set_area_name(area, name, 0);
    if (visibility < 0)
        area_table[area].link = link_weak_external_function;
    else
        area_table[area].link = link_global_external_function;
    area_table[area].fill = 0;
    if (put_trace)
        put_message("pxname() returns (area = %d)\n", area);
    return area;
}


/* declares a new name for an existing function symbol */

void pprocname(int area, char *name)
{
    if (suppress_code_gen)
        return;
    if (put_trace)
        put_message("pprocname(area = %d, name = \"%s\")\n", area, name);
    assert(put_initialised);
    assert(put_unterminated);
    assert((area >= 0) && (area < highest_area));
    assert(is_function(area_table[area].link));
    assert((name != NULL) && (*name != '\0'));
    set_area_name(area, name, 0);
}


/* returns the low PC and high PC of a given function */

void pprocbounds(int area, void **start, void **end)
{
    area_header *table;

    if (put_trace)
        put_message("pprocbounds(area = %d)\n", area);
    assert(put_initialised);
    assert(put_unterminated);
    if (area != area_code)
    {
        assert((area >= area_num) && (area < highest_area));
        assert((area_table[area].link == link_local_function) ||
               (area_table[area].link == link_global_function) ||
               (area_table[area].link == link_weak_function));
    }
    table = &area_table[area];
    if (object_generating)
    {
        if (area == area_code)
        {
            *start = (void *) 0;
            *end = (void *) table->size;
        }
        else
        {
            assert(table->size > 0);
            *start = (void *) table->offset;
            *end = (void *) (table->offset + table->size);
        }
        if (put_trace)
            put_message("pprocbounds() returns (start = %d, end = %d)\n",
                        (int) *start, (int) *end);
    }
    else
    {
        if (area == area_code)
            if (first_function == 0)
            {
                *start = NULL;
                *end = NULL;
            }
            else
            {
                *start = (void *) table->name;
                *end = (void *) label_name(table->name, area_code, 0, 1);
            }
        else
        {
            *start = (void *) table->name;
            *end = (void *) label_name(table->name, area, 0, 1);
        }
        if (put_trace)
            put_message("pprocbounds() returns (start = %s, end = %s)\n",
                        (char *) *start, (char *) *end);
    }
}


/* returns the next function that has been defined */

int pprocnext(int area)
{
    area_header *table;

    assert(put_initialised);
    assert(put_unterminated);
    if (area < area_num - 1)
        area = area_num - 1;
    while (++area < highest_area)
    {
        table = &area_table[area];
        if ((table->link == link_local_function) ||
            (table->link == link_global_function) ||
            (table->link == link_weak_function))
            return area;
    }
    return 0;
}


/* defines a function with a specified visibility */

void pproc(char *name, int visibility, int offset, int *area)
{
    if (suppress_code_gen)
        return;
    if (put_trace)
        put_message("pproc(name = \"%s\", visibility = %d, offset = %d, "
                    "area = %d)\n", name, visibility, offset, *area);
    assert(put_initialised);
    assert(put_unterminated);
    assert((name != NULL) && (*name != '\0'));
    assert(offset >= 0);
    assert(area != NULL);
    /* If the supplied area identifier is -1 then we are creating a new
     * area to represent a function.  Otherwise, the area identifier
     * represents an existing area allocated by pxname() which we are
     * going to upgrade to a function definition.
     */
    if (*area == -1)
    {
        *area = pnextsymbol();
        set_area_name(*area, name, (visibility > 0));
        area_table[*area].fill = 0;
    }
    else
    {
        /* An area identifier has already been allocated for this function.
         * We must ensure that the identifier refers to this function and
         * the easiest way to do this is to compare the names.
         */
        assert((*area >= area_num) && (*area < highest_area));
        assert((area_table[*area].link == link_global_external_function) ||
               (area_table[*area].link == link_weak_external_function));
    }
    if (scope_level == SCOPE_STACK_SIZE)
        put_error("function nesting level too deep");
    function_stack[scope_level].area = *area;
    if (object_generating)
        function_stack[scope_level].address = offset;
    scope_level++;
    if (visibility > 0)
        area_table[*area].link = link_local_function;
    else if (visibility < 0)
        area_table[*area].link = link_weak_function;
    else
        area_table[*area].link = link_global_function;
    if (visibility & 0x80000000)
        add_directive(DIRECT_EXPORT, area_table[*area].name);
    if (!object_generating)
    {
        if (first_function == 0)
        {
            if (annotate_assembler)
                write_code("%s functions:\n\n", assembler->comment);
            first_function = *area;
        }
        if (annotate_assembler)
            write_code("%s start of %s {\n", assembler->comment,
                       area_table[*area].name);
        write_code("%s\t%s", area_table[*area].name, assembler->proc);
        /* If the function name begins with a dot then we do not wish
         * to place its symbol in the symbol table of the object file.
         */
        if (*area_table[*area].name == '.')
            write_code(" SYSCALL %s\n", assembler->local);
        else if (visibility > 0)
            write_code(" %s\n", assembler->local);
        else if (visibility < 0)
            write_code(" %s\n", assembler->weak);
        else
            write_code(" %s\n", assembler->global);
    }
}


/* finalises the definition of a function */

void pprocend(int offset, int frame)
{
    area_header *table;
    char *name;
    size_t area;

    if (suppress_code_gen)
        return;
    if (put_trace)
        put_message("pprocend(offset = %d, frame = %d)\n", offset, frame);
    assert(put_initialised);
    assert(put_unterminated);
    assert(scope_level > 0);
    scope_level--;
    area = function_stack[scope_level].area;
    assert((area >= area_num) && (area < highest_area));
    assert((area_table[area].link == link_local_function) ||
           (area_table[area].link == link_global_function) ||
           (area_table[area].link == link_weak_function));
    assert((offset >= 0) && (frame >= 0));
    max_stack_size = 0;
    area_table[area_stack].size = 0;
    area_table[area_params].size = 0;
    table = &area_table[area];
    if (auto_weak_functions && (*table->name != '.') &&
        (table->link == link_global_function))
    {
        /* Change the linkage of the function to be weak and then define
         * it to have a globally visible alias of the same name except
         * beginning with an underscore.
         */
        table->link = link_weak_function;
        name = (char *) getspace(strlen(table->name) + 2);
        strcpy(name, "_");
        strcat(name, table->name);
        add_alias(area, name, link_global_function);
        freespace(name);
    }
    if (object_generating)
    {
        table->offset = function_stack[scope_level].address;
        assert(offset > table->offset);
        table->size = offset - table->offset;
    }
    else
    {
        write_code("%s\t%s %s\n", label_name(table->name, area, 0, 1),
                   assembler->label, assembler->byte);
        write_code("%s\t%s\n", table->name, assembler->endproc);
        if (annotate_assembler)
            write_code("%s } end of %s\n", assembler->comment, table->name);
        write_code("\n");
    }
}


/* defines a side entry with a specified visibility */

int pentry2(int visibility, int offset, int area, char *name)
{
    area_header *table;

    if (suppress_code_gen)
        return 0;
    if (put_trace)
        put_message("pentry2(visibility = %d, offset = %d, area = %d, "
                    "name = \"%s\")\n", visibility, offset, area, name);
    assert(put_initialised);
    assert(put_unterminated);
    assert(offset >= 0);
    assert((name != NULL) && (*name != '\0'));
    /* If the supplied area identifier is -1 then we are creating a new
     * area to represent a side entry.  Otherwise, the area identifier
     * represents an existing area allocated by pxname() which we are
     * going to upgrade to a function definition.
     */
    if (area == -1)
    {
        area = pnextsymbol();
        set_area_name(area, name, (visibility > 0));
        area_table[area].fill = 0;
    }
    else
    {
        assert((area >= area_num) && (area < highest_area));
        assert((area_table[area].link == link_global_external_function) ||
               (area_table[area].link == link_weak_external_function));
    }
    table = &area_table[area];
    if (visibility > 0)
        table->link = link_local_function;
    else if (visibility < 0)
        table->link = link_weak_function;
    else
        table->link = link_global_function;
    if (visibility & 0x80000000)
        add_directive(DIRECT_EXPORT, table->name);
    if (auto_weak_functions && (*table->name != '.') &&
        (table->link == link_global_function))
    {
        /* Change the linkage of the side entry to be weak and then define
         * it to have a globally visible alias of the same name except
         * beginning with an underscore.
         */
        table->link = link_weak_function;
        name = (char *) getspace(strlen(table->name) + 2);
        strcpy(name, "_");
        strcat(name, table->name);
        add_alias(area, name, link_global_function);
        freespace(name);
    }
    if (object_generating)
        table->offset = offset;
    else
    {
        write_code("%s\t%s %s\n", table->name, assembler->label,
                   assembler->byte);
        /* If the side entry name begins with a dot then we do not wish
         * to place its symbol in the symbol table of the object file.
         */
        if (*table->name != '.')
            if (table->link == link_global_function)
                write_code("\t%s %s\n", assembler->global, table->name);
            else if (table->link == link_weak_function)
                write_code("\t%s %s\n", assembler->weak, table->name);
    }
    if (put_trace)
        put_message("pentry2() returns (area = %d)\n", area);
    return area;
}


/* defines a new side entry with a specified visibility */

int pentry(int visibility, int offset, char *name)
{
    int area;

    if (suppress_code_gen)
        return 0;
    if (put_trace)
        put_message("pentry(visibility = %d, offset = %d, name = \"%s\")\n",
                    visibility, offset, name);
    assert(put_initialised);
    assert(put_unterminated);
    area = pentry2(visibility, offset, -1, name);
    if (put_trace)
        put_message("pentry() returns (area = %d)\n", area);
    return area;
}


/* defines an alias to another symbol at a given area */

void palias(int area, char *name, int visibility)
{
    area_header *table;
    alias *anode;
    linkage link;

    if (suppress_code_gen)
        return;
    if (put_trace)
        put_message("palias(area = %d, name = \"%s\", visibility = %d)\n", area,
                    name, visibility);
    assert(put_initialised);
    assert(put_unterminated);
    assert((area >= 0) && (area < highest_area));
    assert((name != NULL) && (*name != '\0'));
    table = &area_table[area];
    /* Aliases may be used to associate more than one name with an entry in
     * the symbol table and each name can also have different visibilities in
     * order to provide a different functionality, such as the use of weak
     * aliases to global symbols.  The type of linkage that the alias should
     * be given depends on the type of symbol that is being referred to.  No
     * aliases may be given to common areas.
     */
    if ((table->link == link_local) || (table->link == link_global) ||
        (table->link == link_weak))
        if (visibility > 0)
            link = link_local;
        else if (visibility < 0)
            link = link_weak;
        else
            link = link_global;
    else if ((table->link == link_global_external) ||
             (table->link == link_weak_external))
        if (visibility < 0)
            link = link_weak_external;
        else
            link = link_global_external;
    else if ((table->link == link_local_function) ||
             (table->link == link_global_function) ||
             (table->link == link_weak_function))
        if (visibility > 0)
            link = link_local_function;
        else if (visibility < 0)
            link = link_weak_function;
        else
            link = link_global_function;
    else if ((table->link == link_global_external_function) ||
             (table->link == link_weak_external_function))
        if (visibility < 0)
            link = link_weak_external_function;
        else
            link = link_global_external_function;
    else
        put_error("attempted to alias the wrong type of symbol");
    /* Add the name to the list of aliases for this area.  Note that this
     * cannot be done for data entries, but can be achieved by adding another
     * data entry at the same area and offset.
     */
    anode = add_alias(area, name, link);
    if (visibility & 0x80000000)
        add_directive(DIRECT_EXPORT, anode->name);
}


/* adds a section to the section table */
/* note that some of the fields may have to be filled in later */

static size_t add_section(section_type index)
{
    area_header *table;
    section_info *section_table;
    coff_section *section;
    size_t align, length;

    table = &area_table[area_section];
    section_table = &section_info_table[index];
    length = table->size + sizeof(coff_section);
    while (length > table->max)
        expand_contents(area_section);
    section = (coff_section *) (table->contents + table->size);
    table->size = length;
    memset(section->name, 0, 8);
    if (strlen(section_table->name) <= 8)
        strncpy(section->name, section_table->name, 8);
    else
        sprintf(section->name, "/%s",
                add_string(area_string, section_table->name) + 4);
    section->paddr = 0;
    section->vaddr = 0;
    section->size = section_table->size;
    section->scnptr = 0;
    section->relptr = 0;
    section->lnnoptr = 0;
    if (section_table->relocs > USHRT_MAX)
        put_error("too many relocations");
    section->nreloc = section_table->relocs;
    if (section_table->lines > USHRT_MAX)
        put_error("too many line numbers");
    section->nlnno = section_table->lines;
    if (section_table->align > 0)
        align = section_table->align;
    else
        align = section_table->default_align;
    section->flags = section_table->flags | ((log_base_2(align) + 1) << 20);
    return ((char *) section - table->contents) / sizeof(coff_section);
}


/* adds a symbol to the symbol table */

static size_t add_symbol(char *name, linkage link, size_t numaux,
                         unsigned long value, section_type section)
{
    area_header *table;
    coff_symbol symbol;
    size_t length;

    if (strlen(name) <= 8)
    {
        memset(symbol.x.name, 0, 8);
        strncpy(symbol.x.name, name, 8);
    }
    else
    {
        symbol.x.y.zeroes = 0;
        symbol.x.y.offset = add_string(area_string, name) + 4;
    }
    symbol.value = value;
    if ((link == link_local_common) || (link == link_global_common) ||
        (link == link_weak_common) || (link == link_global_external) ||
        (link == link_weak_external) ||
        (link == link_global_external_function) ||
        (link == link_weak_external_function))
        symbol.scnum = sec_null;
    else if (link == link_file)
        symbol.scnum = COFF_SYMBOL_DEBUG;
    else
        symbol.scnum = section + 1;
    if (is_function(link))
        symbol.type = COFF_SYMBOL_FUNC;
    else
        symbol.type = COFF_SYMBOL_OBJECT;
    if (link == link_none)
        symbol.sclass = COFF_SYMBOL_FUNCAUX;
    else if (link == link_file)
        symbol.sclass = COFF_SYMBOL_FILE;
    else if ((link == link_section) || (link == link_local) ||
             (link == link_local_common) || (link == link_local_function))
        symbol.sclass = COFF_SYMBOL_LOCAL;
    else if ((link == link_global) || (link == link_global_common) ||
             (link == link_global_external) || (link == link_global_function) ||
             (link == link_global_external_function))
        symbol.sclass = COFF_SYMBOL_GLOBAL;
    else
        symbol.sclass = COFF_SYMBOL_WEAK;
    symbol.numaux = numaux;
    table = &area_table[area_symbol];
    length = table->size + 18;
    while (length > table->max)
        expand_contents(area_symbol);
    memcpy(table->contents + table->size, &symbol, 18);
    table->size = length;
    return (length / 18) - 1;
}


/* adds an auxilliary function symbol to the symbol table */

static void add_function_aux(size_t index, size_t size, size_t lines)
{
    area_header *table;
    coff_symbol symbol;
    size_t length;

    memset(&symbol, 0, 18);
    *((unsigned long *) &symbol) = index;
    *((unsigned long *) ((char *) &symbol + 4)) = size;
    *((unsigned long *) ((char *) &symbol + 8)) = lines;
    table = &area_table[area_symbol];
    length = table->size + 18;
    while (length > table->max)
        expand_contents(area_symbol);
    memcpy(table->contents + table->size, &symbol, 18);
    table->size = length;
}


/* adds an auxilliary .bf or .ef symbol to the symbol table */

static void add_beginend_aux(size_t line)
{
    area_header *table;
    coff_symbol symbol;
    size_t length;

    memset(&symbol, 0, 18);
    *((unsigned short *) ((char *) &symbol + 4)) = line;
    table = &area_table[area_symbol];
    length = table->size + 18;
    while (length > table->max)
        expand_contents(area_symbol);
    memcpy(table->contents + table->size, &symbol, 18);
    table->size = length;
}


/* adds an auxilliary weak symbol to the symbol table */

static void add_weak_aux(size_t index)
{
    area_header *table;
    coff_symbol symbol;
    size_t length;

    memset(&symbol, 0, 18);
    *((unsigned long *) &symbol) = index;
    *((unsigned short *) ((char *) &symbol + 4)) = 3;
    table = &area_table[area_symbol];
    length = table->size + 18;
    while (length > table->max)
        expand_contents(area_symbol);
    memcpy(table->contents + table->size, &symbol, 18);
    table->size = length;
}


/* adds an auxilliary file symbol to the symbol table */

static void add_file_aux(char *name, size_t count)
{
    area_header *table;
    char *symbol;
    size_t length;

    symbol = (char *) getspace(count * 18);
    memset(symbol, 0, count * 18);
    strncpy(symbol, name, count * 18);
    table = &area_table[area_symbol];
    length = table->size + (count * 18);
    while (length > table->max)
        expand_contents(area_symbol);
    memcpy(table->contents + table->size, symbol, count * 18);
    table->size = length;
    freespace(symbol);
}


/* adds an auxilliary section symbol to the symbol table */

static void add_section_aux(size_t size, size_t relocs, size_t lines)
{
    area_header *table;
    coff_symbol symbol;
    size_t length;

    memset(&symbol, 0, 18);
    *((unsigned long *) &symbol) = size;
    *((unsigned short *) ((char *) &symbol + 4)) = relocs;
    *((unsigned short *) ((char *) &symbol + 6)) = lines;
    table = &area_table[area_symbol];
    length = table->size + 18;
    while (length > table->max)
        expand_contents(area_symbol);
    memcpy(table->contents + table->size, &symbol, 18);
    table->size = length;
}


/* alters all relocations targetting a particular area */

static void alter_relocations(size_t area, size_t offset, size_t new_area,
                              size_t new_offset)
{
    member_node *node;
    size_t index;

    for (index = 0; index < highest_area; index++)
        for (node = area_table[index].members.head; node->next != NULL;
             node = node->next)
            if ((node->type > 0) && (node->data.target.area == area))
            {
                node->data.target.area = new_area;
                node->data.target.offset += new_offset - offset;
            }
}


/* removes any relocations resulting from relative calls to local functions */

static void optimise_relocations(void)
{
    member_node *next, *node;
    unsigned long call;
    long offset;

    for (node = area_table[area_code].members.head; next = node->next;
         node = next)
        if ((node->type == relocation_table[3]) &&
            (area_table[node->data.target.area].link == link_local_function))
        {
            offset = area_table[node->data.target.area].offset +
                     node->data.target.offset - node->offset - 4;
            memcpy(&call, area_table[area_code].contents + node->offset, 4);
            if (host_endian != target_endian)
                byte_swap((char *) &call, 4);
            call = (call & 0x80000000) | offset;
            if (host_endian != target_endian)
                byte_swap((char *) &call, 4);
            memcpy(area_table[area_code].contents + node->offset, &call, 4);
            if (put_trace)
                put_message("internal call to \"%s\" from code offset %lu "
                            "optimised\n",
                            area_table[node->data.target.area].name,
                            node->offset);
            node->prev->next = next;
            next->prev = node->prev;
            freespace(node);
        }
}


/* counts the number of relocations in each section */

static void count_relocations(void)
{
    member_node *node;
    section_info *section;
    size_t index, target;

    for (index = 0; index < area_num; index++)
    {
        section = &section_info_table[area_info_table[index].section];
        for (node = area_table[index].members.head; node->next != NULL;
             node = node->next)
            if (node->type > 0)
            {
                target = node->data.target.area;
                if (target < area_num)
                    section_info_table[area_info_table[target].section].targets++;
                section->relocs++;
            }
    }
}


/* counts the number of line numbers in each section */

static void count_lines(void)
{
    area_header *table;
    member_node *node;
    lineno *lnode;
    section_info *section;
    size_t index;

    for (index = 0; index < area_num; index++)
    {
        table = &area_table[index];
        section = &section_info_table[area_info_table[index].section];
        for (lnode = table->lines; lnode != NULL; lnode = lnode->next)
            section->lines++;
        for (node = table->members.head; node->next != NULL; node = node->next)
            if (node->type == 0)
                for (lnode = node->data.entry.lines; lnode != NULL;
                     lnode = lnode->next)
                    section->lines++;
    }
}


/* fills in an addend for every relocation */

static void write_addends(void)
{
    area_header *table;
    member_node *node;
    size_t index;
    unsigned long contents4;
    long offset;
    unsigned short contents2;

    for (index = 0; index < area_num; index++)
    {
        table = &area_table[index];
        for (node = table->members.head; node->next != NULL; node = node->next)
            if ((node->type > 0) && (node->type != COFF_REL_386_SEC))
            {
                offset = node->data.target.offset;
                if (node->data.target.size == 2)
                {
                    memcpy(&contents2, table->contents + node->offset, 2);
                    if (host_endian != target_endian)
                        byte_swap((char *) &contents2, 2);
                    contents2 = (contents2 & ~rel_mask_table[node->type - 1]) |
                                (offset & rel_mask_table[node->type - 1]);
                    if (host_endian != target_endian)
                        byte_swap((char *) &contents2, 2);
                    memcpy(table->contents + node->offset, &contents2, 2);
                }
                else
                {
                    memcpy(&contents4, table->contents + node->offset, 4);
                    if (host_endian != target_endian)
                        byte_swap((char *) &contents4, 4);
                    contents4 = (contents4 & ~rel_mask_table[node->type - 1]) |
                                (offset & rel_mask_table[node->type - 1]);
                    if (host_endian != target_endian)
                        byte_swap((char *) &contents4, 4);
                    memcpy(table->contents + node->offset, &contents4, 4);
                }
            }
    }
}


/* fills in the entries for a relocation table */

static void write_relocations(member_list *list, char *buffer)
{
    member_node *node;
    coff_relocation reloc;
    size_t symbol;

    for (node = list->head; node->next != NULL; node = node->next)
        if (node->type > 0)
        {
            symbol = area_table[node->data.target.area].offset;
            reloc.vaddr = node->offset;
            reloc.symndx = symbol;
            reloc.type = node->type;
            if (host_endian != target_endian)
            {
                byte_swap((char *) &reloc.vaddr, 4);
                byte_swap((char *) &reloc.symndx, 4);
                byte_swap((char *) &reloc.type, 2);
            }
            memcpy(buffer, &reloc, 10);
            buffer += 10;
        }
}


/* fills in the entries for a line number table */

static void write_lines(size_t area, char *buffer)
{
    area_header *table;
    member_node *node;
    lineno *lnode;
    coff_lineno line;
    unsigned long current_line;

    table = &area_table[area];
    for (lnode = table->lines; lnode != NULL; lnode = lnode->next)
    {
        if (lnode == table->lines)
        {
            line.addr = table->offset;
            line.line = 0;
            current_line = lnode->line;
        }
        else
        {
            line.addr = lnode->addr.offset;
            line.line = (unsigned short)
                        ((lnode->line - current_line) & 0xFFFF);
            /* If, through some amazing feat of optimisation, we end
             * up back at the first line of the function again, then
             * we need to add a hack to prevent the linker interpreting
             * this entry as the start of a new function.  The easiest
             * way to do this is to increment the line number by one.
             */
            if (line.line == 0)
                line.line++;
        }
        if (host_endian != target_endian)
        {
            byte_swap((char *) &line.addr, 4);
            byte_swap((char *) &line.line, 2);
        }
        memcpy(buffer, &line, 6);
        buffer += 6;
    }
    for (node = table->members.head; node->next != NULL; node = node->next)
        if (node->type == 0)
            for (lnode = node->data.entry.lines; lnode != NULL;
                 lnode = lnode->next)
            {
                if (lnode == node->data.entry.lines)
                {
                    line.addr = node->data.entry.symidx;
                    line.line = 0;
                    current_line = lnode->line;
                }
                else
                {
                    line.addr = lnode->addr.offset;
                    line.line = (unsigned short)
                                ((lnode->line - current_line) & 0xFFFF);
                    /* If, through some amazing feat of optimisation, we end
                     * up back at the first line of the function again, then
                     * we need to add a hack to prevent the linker interpreting
                     * this entry as the start of a new function.  The easiest
                     * way to do this is to increment the line number by one.
                     */
                    if (line.line == 0)
                        line.line++;
                }
                if (host_endian != target_endian)
                {
                    byte_swap((char *) &line.addr, 4);
                    byte_swap((char *) &line.line, 2);
                }
                memcpy(buffer, &line, 6);
                buffer += 6;
            }
}


/* writes out the header information */

static void write_header(void)
{
    write_code("\tTITLE %s\n", source_file);
    write_code("\t.386\n");
    write_code("\t.MODEL FLAT, C\n");
    write_code("\tOPTION DOTNAME\n");
    write_code("\tOPTION NOSCOPED\n\n");
}


/* writes out the contents of a data area */

static void write_contents(size_t area)
{
    area_header *table;
    member_node *node;
    size_t offset, size;

    table = &area_table[area];
    node = table->members.head;
    if (node->next == NULL)
        size = table->size;
    else
        size = node->offset;
    if (size > 0)
        if (area == area_bss)
            zero_dump(area, 0, size);
        else
            byte_dump(area, 0, (unsigned char *) table->contents, size);
    for (offset = size; node->next != NULL; offset += size)
    {
        if (node->type == 0)
        {
            size = node->data.entry.size;
            if (annotate_assembler && (size > 0))
                write_code("\t%s [%s]\n", assembler->comment,
                           label_name(table->name, area, offset, 0));
            write_code("%s\t%s %s\n", node->name, assembler->label,
                       assembler->byte);
            /* If the entry name begins with a dot then we do not wish
             * to place its symbol in the symbol table of the object file.
             */
            if (*node->name != '.')
            {
                if (node->data.entry.link == link_global)
                    write_code("\t%s %s\n", assembler->global, node->name);
                else if (node->data.entry.link == link_weak)
                    write_code("\t%s %s\n", assembler->weak, node->name);
            }
            /* A data entry can contain fixups as well as data and so we
             * must allow for the case when the offset of the next member
             * overlaps the current data entry.  The following check ensures
             * that the size is correctly modified when this occurs.
             */
            if ((node->next->next != NULL) &&
                (size > node->next->offset - offset))
                size = node->next->offset - offset;
            if (size > 0)
                if (area == area_bss)
                    zero_dump(area, offset, size);
                else
                    byte_dump(area, offset,
                              (unsigned char *) table->contents + offset, size);
        }
        else
        {
            /* Generate a fixup to an area and offset.
             */
            size = node->data.target.size;
            if ((node->next->next != NULL) &&
                (node->next->offset < offset + size))
                put_error("relocation overwritten in `%s' at offset %lu",
                          table->name, offset);
            if (annotate_assembler)
                write_code("\t%s [%s]\n", assembler->comment,
                           label_name(table->name, area, offset, 0));
            if (node->type == COFF_REL_386_SEC)
                write_code("\t%s SECTION:%s\n", assembler->word,
                           label_name(node->name, node->data.target.area,
                           node->data.target.offset, 0));
            else if (node->type == COFF_REL_386_SECREL)
                write_code("\t%s OFFSET:%s\n", assembler->word,
                           label_name(node->name, node->data.target.area,
                           node->data.target.offset, 0));
            else
                write_code("\t%s %s\n", assembler->word,
                           label_name(node->name, node->data.target.area,
                           node->data.target.offset, 0));
        }
        offset += size;
        node = node->next;
        if (node->next == NULL)
            size = table->size - offset;
        else
            size = node->offset - offset;
        if (size > 0)
            if (area == area_bss)
                zero_dump(area, offset, size);
            else
                byte_dump(area, offset,
                          (unsigned char *) table->contents + offset, size);
    }
}


/* writes out the details of any data areas */

static void write_areas(void)
{
    area_header *table;
    alias *anode;
    size_t align, base, index;
    long section;

    enter_section(sec_data);
    if (annotate_assembler)
        write_code("%s data:\n\n", assembler->comment);
    for (index = 0; index < highest_area; index++)
    {
        table = &area_table[index];
        if ((index != area_code) && (table->name != NULL) &&
            ((table->size > 0) || (table->offset > 0)) &&
            ((table->link == link_section) || (table->link == link_local) ||
             (table->link == link_global) || (table->link == link_weak)))
        {
            base = base_area(index);
            if (base < area_num)
                section = area_info_table[base].section;
            else if (area_table[base].link == link_section)
                section = -base;
            else
                section = sec_data;
            enter_section(section);
            if (table->align > 0)
                align = table->align;
            else
                align = section_info_table[section].default_align;
            write_code("\t%s %lu\n", assembler->align, align);
            if (table->link != link_section)
                write_code("%s\t%s %s\n", table->name, assembler->label,
                           assembler->byte);
            /* If the area name begins with a dot then we do not wish
             * to place its symbol in the symbol table of the object file.
             */
            if ((table->link != link_section) && (*table->name != '.'))
            {
                if (table->link == link_global)
                    write_code("\t%s %s\n", assembler->global, table->name);
                else if (table->link == link_weak)
                    write_code("\t%s %s\n", assembler->weak, table->name);
            }
            /* We now write out any symbols that are aliased to this area.
             */
            for (anode = table->aliases; anode != NULL; anode = anode->next)
            {
                write_code("%s\t%s %s\n", anode->name, assembler->label,
                           assembler->byte);
                if (anode->link == link_global)
                    write_code("\t%s %s\n", assembler->global, anode->name);
                else if (anode->link == link_weak)
                    write_code("\t%s %s\n", assembler->weak, anode->name);
            }
            if (table->size > 0)
                write_contents(index);
        }
    }
    enter_section(sec_data);
}


/* writes out the details of any common symbols */

static void write_commons(void)
{
    area_header *table;
    size_t index;
    int found;

    found = 0;
    for (index = area_num; index < highest_area; index++)
    {
        table = &area_table[index];
        if ((table->link == link_local_common) ||
            (table->link == link_global_common) ||
            (table->link == link_weak_common))
        {
            assert(table->aliases == NULL);
            assert(table->size > 0);
            if (!found)
            {
                write_code("\n");
                if (annotate_assembler)
                    write_code("%s common symbols:\n\n", assembler->comment);
                found = 1;
            }
            write_code("\t%s %s:%s:%lu\n", assembler->common, table->name,
                       assembler->byte, table->size);
            if (table->link == link_local)
                write_code("\t%s %s\n", assembler->local, table->name);
            else if (table->link == link_weak)
                write_code("\t%s %s\n", assembler->weak, table->name);
        }
    }
}


/* writes out the details of any external data symbols */

static void write_external_objects(void)
{
    area_header *table;
    alias *anode;
    size_t index;
    int found;

    /* Most assemblers will not complain about any undefined symbols
     * at the end of assembly.  However, they will simply describe the
     * symbol in the symbol table of the object file as having no type.
     * We can be more specific by explicitly specifying the type at
     * this point.
     */
    found = 0;
    for (index = area_num; index < highest_area; index++)
    {
        table = &area_table[index];
        if ((table->link == link_global_external) ||
            (table->link == link_weak_external))
        {
            if (!found)
            {
                write_code("\n");
                if (annotate_assembler)
                    write_code("%s external data symbols:\n\n",
                               assembler->comment);
                found = 1;
            }
            write_code("\t%s %s:%s\n", assembler->external, table->name,
                       assembler->byte);
            if (table->link == link_weak_external)
                write_code("\t%s %s\n", assembler->weak, table->name);
            /* We now write out any symbols that are aliased to this area.
             */
            for (anode = table->aliases; anode != NULL; anode = anode->next)
            {
                write_code("\t%s %s:%s\n", assembler->external, anode->name,
                           assembler->byte);
                if (anode->link == link_weak_external)
                    write_code("\t%s %s\n", assembler->weak, anode->name);
            }
        }
    }
}


/* writes out the details of any external function symbols */

static void write_external_functions(void)
{
    area_header *table;
    alias *anode;
    size_t index;
    int found;

    /* Most assemblers will not complain about any undefined symbols
     * at the end of assembly.  However, they will simply describe the
     * symbol in the symbol table of the object file as having no type.
     * We can be more specific by explicitly specifying the type at
     * this point.
     */
    found = 0;
    for (index = area_num; index < highest_area; index++)
    {
        table = &area_table[index];
        if ((table->link == link_global_external_function) ||
            (table->link == link_weak_external_function))
        {
            if (!found)
            {
                write_code("\n");
                if (annotate_assembler)
                    write_code("%s external function symbols:\n\n",
                               assembler->comment);
                found = 1;
            }
            write_code("\t%s %s:%s\n", assembler->external, table->name,
                       assembler->byte);
            if (table->link == link_weak_external_function)
                write_code("\t%s %s\n", assembler->weak, table->name);
            /* We now write out any symbols that are aliased to this area.
             */
            for (anode = table->aliases; anode != NULL; anode = anode->next)
            {
                write_code("\t%s %s:%s\n", assembler->external, anode->name,
                           assembler->byte);
                if (anode->link == link_weak_external_function)
                    write_code("\t%s %s\n", assembler->weak, anode->name);
            }
        }
    }
}


/* writes out the details of any aliases for defined functions */

static void write_function_aliases(void)
{
    area_header *table;
    alias *anode;
    size_t index;
    int found;

    /* Because function aliases may have been declared after the functions
     * that they referred to were defined we must write out the list of
     * function aliases to the assembler file at this point.
     */
    found = 0;
    for (index = area_num; index < highest_area; index++)
    {
        table = &area_table[index];
        if ((table->link == link_local_function) ||
            (table->link == link_global_function) ||
            (table->link == link_weak_function))
            for (anode = table->aliases; anode != NULL; anode = anode->next)
            {
                if (!found)
                {
                    write_code("\n");
                    if (annotate_assembler)
                        write_code("%s function aliases:\n\n",
                                   assembler->comment);
                    found = 1;
                }
                write_code("\t%s=%s\n", anode->name, table->name);
                if (anode->link == link_global_function)
                    write_code("\t%s %s\n", assembler->global, anode->name);
                else if (anode->link == link_weak_function)
                    write_code("\t%s %s\n", assembler->weak, anode->name);
            }
    }
}


/* moves one area to the end of another area */

static void move_area(size_t area, size_t new_area, linkage link)
{
    area_header *new_table, *table;
    section_info *section;
    size_t align, index, offset, size;

    table = &area_table[area];
    new_table = &area_table[new_area];
    align = table->align;
    if (new_table->align == 0)
    {
        /* The area being targetted has no specified alignment but we should
         * use the alignment of its section just to be safe.
         */
        section = &section_info_table[area_info_table[new_area].section];
        if (section->align > 0)
            new_table->align = section->align;
        else
            new_table->align = section->default_align;
    }
    if (align == 0)
    {
        /* The area being moved has no specified alignment but we should
         * use the alignment of its section just to be safe.
         */
        section = &section_info_table[area_info_table[area].section];
        if (section->align > 0)
            align = section->align;
        else
            align = section->default_align;
    }
    /* If the area being moved has a specified alignment then we must
     * ensure that the destination area and destination section have at
     * least that alignment as well.
     */
    if ((new_table->align > 0) && (new_table->align < align))
        new_table->align = align;
    if (new_area < area_num)
    {
        section = &section_info_table[area_info_table[new_area].section];
        if (section->align < align)
            section->align = align;
    }
    offset = round_up(new_table->size, align);
    size = table->size + offset;
    while (size > new_table->max)
        expand_contents(new_area);
    if (table->contents != NULL)
        memcpy(new_table->contents + offset, table->contents, table->size);
    new_table->size = size;
    if (link != link_none)
        add_member(new_area, offset, 0, table->name, link, table->size,
                   (unsigned long) table->lines, 1);
    move_members(area, new_area, offset);
    move_aliases(area, new_area, offset);
    alter_relocations(area, 0, new_area, offset);
    for (index = 0; index < highest_area; index++)
        if (area_table[index].parent == area)
            area_table[index].parent = new_area;
    if (!object_generating)
        new_table->offset += table->offset;
    freespace(table->name);
    table->name = NULL;
    if (table->contents != NULL)
    {
        freespace(table->contents);
        table->contents = NULL;
    }
    table->link = link_local;
    table->lines = NULL;
    table->fill = 0;
    table->flags = 0;
    table->parent = 0;
    table->offset = 0;
    table->align = 0;
    table->size = 0;
    table->max = 0;
}


/* moves any data areas that were defined with the pcdataarea function */

static void move_data_areas(void)
{
    area_header *table;
    size_t index, parent;

    /* Any data areas defined with the pcdataarea function must be merged
     * into their parent areas before generating an object file.  This step
     * does not need to be performed when generating assembler code as the
     * assembler will automatically deal with them.
     */
    for (index = area_num; index < highest_area; index++)
    {
        table = &area_table[index];
        if ((table->name != NULL) && ((table->link == link_local) ||
             (table->link == link_global) || (table->link == link_weak)))
        {
            if (table->parent != 0)
                parent = table->parent;
            else
                parent = area_gla;
            move_area(index, parent, table->link);
        }
    }
}


/* moves common symbols to the BSS area */

static void move_commons(int local_only)
{
    area_header *table;
    linkage link;
    size_t index;

    /* Local common symbols cause problems for linkers on some platforms
     * so we try to remove them here by converting them to local data symbols
     * in the BSS area.  Note that if local_only is not set then all common
     * symbols will be converted to symbols in the BSS area.
     */
    for (index = area_num; index < highest_area; index++)
    {
        table = &area_table[index];
        if ((table->link == link_local_common) || (!local_only &&
             ((table->link == link_global_common) ||
              (table->link == link_weak_common))))
        {
            assert(table->size > 0);
            if (table->link == link_local_common)
                link = link_local;
            else if (table->link == link_global_common)
                link = link_global;
            else
                link = link_weak;
            move_area(index, area_bss, link);
        }
    }
}


/* moves function symbols to the code area */

static void move_functions(void)
{
    area_header *table;
    size_t index;

    /* When generating object files, all defined functions logically belong
     * in the code area and not in their own area so we perform this
     * translation here.  This also means that any relocations to an area
     * representing a function can also be converted to a code offset and will
     * also result in all functions being sorted in the order of their offset
     * into the code area.  None of this requires to be done when generating
     * assembler code.
     */
    for (index = area_num; index < highest_area; index++)
    {
        table = &area_table[index];
        if ((table->link == link_local_function) ||
            (table->link == link_global_function) ||
            (table->link == link_weak_function))
        {
            add_member(area_code, table->offset, 0, table->name, table->link,
                       table->size, (unsigned long) table->lines, 1);
            move_aliases(index, area_code, table->offset);
            alter_relocations(index, 0, area_code, table->offset);
            freespace(table->name);
            table->name = NULL;
            table->link = link_local;
            table->lines = NULL;
            table->offset = 0;
            table->size = 0;
        }
    }
}


/* merges any predefined areas that belong to the same section */

static void merge_areas(void)
{
    area_header *table;
    section_info *section;
    size_t index, new_index;

    for (index = 0; index < area_num; index++)
    {
        table = &area_table[index];
        if (table->size > 0)
        {
            section = &section_info_table[area_info_table[index].section];
            if (section->size > 0)
            {
                for (new_index = 0; new_index < index; new_index++)
                    if (area_info_table[new_index].section ==
                        area_info_table[index].section)
                        break;
                assert(new_index != index);
                move_area(index, new_index, link_local);
                section->size = area_table[new_index].size;
            }
            else
                section->size = table->size;
        }
    }
}


/* patches the symbol table */

static void patch_symbol_table(void)
{
    coff_symbol *begin_symbol, *file_symbol, *func_symbol, *symbol;
    coff_symbol new_symbol;
    size_t count, index;

    begin_symbol = file_symbol = func_symbol = NULL;
    count = area_table[area_symbol].size / 18;
    symbol = (coff_symbol *) area_table[area_symbol].contents;
    for (index = 0; index < count; index += new_symbol.numaux + 1)
    {
        memcpy(&new_symbol, symbol, 18);
        if (new_symbol.sclass == COFF_SYMBOL_FILE)
        {
            if (file_symbol != NULL)
                memcpy((char *) &file_symbol->value, &index, 4);
            file_symbol = symbol;
        }
        symbol = (coff_symbol *) ((char *) symbol + 18);
        if (new_symbol.numaux != 0)
        {
            if ((new_symbol.type == COFF_SYMBOL_FUNC) &&
                (new_symbol.scnum != sec_null))
            {
                if (func_symbol != NULL)
                    memcpy((char *) func_symbol + 12, &index, 4);
                func_symbol = symbol;
            }
            else if ((new_symbol.sclass == COFF_SYMBOL_FUNCAUX) &&
                     (strcmp(new_symbol.x.name, ".bf") == 0))
            {
                if (begin_symbol != NULL)
                    memcpy((char *) begin_symbol + 12, &index, 4);
                begin_symbol = symbol;
            }
            symbol = (coff_symbol *) ((char *) symbol +
                                      (new_symbol.numaux * 18));
        }
    }
}


/* byte-swaps the symbol table */

static void swap_symbol_table(void)
{
    coff_symbol *symbol;
    coff_symbol new_symbol;
    size_t count, index;

    count = area_table[area_symbol].size / 18;
    symbol = (coff_symbol *) area_table[area_symbol].contents;
    for (index = 0; index < count; index += new_symbol.numaux + 1)
    {
        memcpy(&new_symbol, symbol, 18);
        if (new_symbol.x.y.zeroes == 0)
            byte_swap((char *) &symbol->x.y.offset, 4);
        byte_swap((char *) &symbol->value, 4);
        byte_swap((char *) &symbol->scnum, 2);
        byte_swap((char *) &symbol->type, 2);
        symbol = (coff_symbol *) ((char *) symbol + 18);
        if (new_symbol.numaux != 0)
        {
            if ((new_symbol.type == COFF_SYMBOL_FUNC) &&
                (new_symbol.scnum != sec_null))
            {
                /* function auxilliary */
                assert(new_symbol.numaux == 1);
                byte_swap((char *) symbol, 4);
                byte_swap((char *) symbol + 4, 4);
                byte_swap((char *) symbol + 8, 4);
                byte_swap((char *) symbol + 12, 4);
            }
            else if ((new_symbol.sclass == COFF_SYMBOL_FUNCAUX) &&
                     ((strcmp(new_symbol.x.name, ".bf") == 0) ||
                      (strcmp(new_symbol.x.name, ".ef") == 0)))
            {
                /* .bf or .ef auxilliary */
                assert(new_symbol.numaux == 1);
                byte_swap((char *) symbol + 4, 2);
                byte_swap((char *) symbol + 12, 4);
            }
            else if ((new_symbol.sclass == COFF_SYMBOL_GLOBAL) &&
                     (new_symbol.scnum == sec_null))
            {
                /* weak auxilliary */
                assert(new_symbol.numaux == 1);
                byte_swap((char *) symbol, 4);
                byte_swap((char *) symbol + 4, 4);
            }
            else if ((new_symbol.type == COFF_SYMBOL_OBJECT) &&
                     (new_symbol.sclass == COFF_SYMBOL_LOCAL) &&
                     (new_symbol.scnum != sec_null))
            {
                /* section auxilliary */
                assert(new_symbol.numaux == 1);
                byte_swap((char *) symbol, 4);
                byte_swap((char *) symbol + 4, 2);
                byte_swap((char *) symbol + 6, 2);
            }
            symbol = (coff_symbol *) ((char *) symbol +
                                      (new_symbol.numaux * 18));
        }
    }
}


/* byte-swaps the section header table */

static void swap_section_table(void)
{
    coff_section *section;
    size_t count, index;

    count = area_table[area_section].size / sizeof(coff_section);
    section = (coff_section *) area_table[area_section].contents;
    for (index = 0; index < count; index++)
    {
        byte_swap((char *) &section->paddr, 4);
        byte_swap((char *) &section->vaddr, 4);
        byte_swap((char *) &section->size, 4);
        byte_swap((char *) &section->scnptr, 4);
        byte_swap((char *) &section->relptr, 4);
        byte_swap((char *) &section->lnnoptr, 4);
        byte_swap((char *) &section->nreloc, 2);
        byte_swap((char *) &section->nlnno, 2);
        byte_swap((char *) &section->flags, 4);
        section = (coff_section *) ((char *) section + sizeof(coff_section));
    }
}


/* adds most of the sections to the object file section header table */

static void build_section_table(void)
{
    section_info *section_table;
    coff_section *section;
    size_t count, index, offset;

    /* This will add any required sections to the section header table.
     */
    for (index = 1; index < sec_num; index++)
    {
        section_table = &section_info_table[index];
        if ((section_table->size > 0) || (section_table->targets > 0))
            section_table->index = add_section((section_type) index);
    }
    /* We now work out the file offsets of every section.
     */
    section = (coff_section *) area_table[area_section].contents;
    count = area_table[area_section].size / sizeof(coff_section);
    offset = sizeof(coff_header) + area_table[area_section].size;
    for (index = 0; index < count; index++)
    {
        /* BSS sections occupy no space in the object file and their
         * file offset should always be zero.
         */
        if (!(section->flags & COFF_SECT_BSS) && (section->size > 0))
        {
            /* It is recommended that the file offsets for section contents
             * are a multiple of four.
             */
            section->scnptr = round_up(offset, 4);
            offset = section->scnptr + section->size;
            /* It is recommended that the file offsets for section relocations
             * are a multiple of four.
             */
            if (section->nreloc != 0)
            {
                section->relptr = round_up(offset, 4);
                offset = section->relptr + (section->nreloc * 10);
            }
            /* It is recommended that the file offsets for section line numbers
             * are a multiple of four.
             */
            if (section->nlnno != 0)
            {
                section->lnnoptr = round_up(offset, 4);
                offset = section->lnnoptr + (section->nlnno * 6);
            }
        }
        section = (coff_section *) ((char *) section + sizeof(coff_section));
    }
    symbol_table_offset = round_up(offset, 4);
}


/* adds any line number symbols to the object file symbol table */

static size_t add_line_symbols(lineno **lnode, char **file,
                               section_type section)
{
    lineno *node;
    size_t count, lcount, scount;

    lcount = scount = 0;
    node = *lnode;
    do
    {
        if (strcmp(*file, node->file) != 0)
        {
            add_symbol(".lf", link_none, 0, scount, section);
            scount = 0;
            count = (strlen(node->file) + 17) / 18;
            add_symbol(".file", link_file, count, 0, sec_null);
            add_file_aux(node->file, count);
            *file = node->file;
        }
        *lnode = node;
        lcount++;
        scount++;
    }
    while (node = node->next);
    add_symbol(".lf", link_none, 0, scount, section);
    return lcount;
}


/* adds all of the symbols to the object file symbol table */

static void build_symbol_table(void)
{
    area_header *table;
    member_node *node;
    alias *anode;
    lineno *lnode;
    section_info *section;
    coff_section *sect;
    char *file;
    size_t count, debugging, index, lines, new_index;

    /* This function adds all relevant symbols to the symbol table of the
     * object file in sorted order.  The symbols are sorted by section index
     * and by their offset into that section, with function symbols coming
     * first, followed by object symbols, common symbols and finally external
     * symbols.
     */
    file = source_file;
    if ((debug_format == DEBUG_COFF) || (debug_format == DEBUG_CODEVIEW))
    {
        count = (strlen(source_file) + 17) / 18;
        add_symbol(".file", link_file, count, 0, sec_null);
        add_file_aux(source_file, count);
        debugging = 1;
    }
    else
        debugging = 0;
    for (index = 1; index < sec_num; index++)
    {
        section = &section_info_table[index];
        if ((section->size > 0) || (section->targets > 0))
        {
            for (new_index = 0; new_index < area_num; new_index++)
                if (area_info_table[new_index].section == index)
                    break;
            assert(new_index != area_num);
            table = &area_table[new_index];
            table->offset = add_symbol(section->name, link_section, 1, 0,
                                       (section_type) section->index);
            add_section_aux(section->size, section->relocs, section->lines);
        }
    }
    for (index = 0; index < area_num; index++)
    {
        section = &section_info_table[area_info_table[index].section];
        if (debugging && (section->index != 0xFFFFFFFF))
        {
            sect = ((coff_section *) area_table[area_section].contents) +
                   section->index;
            lines = sect->lnnoptr;
        }
        else
        {
            sect = NULL;
            lines = 0;
        }
        for (anode = area_table[index].aliases; anode != NULL;
             anode = anode->next)
            if ((*anode->name != '.') &&
                ((anode->link == link_local_function) ||
                 (anode->link == link_global_function) ||
                 (anode->link == link_weak_function)))
            {
                if (debugging)
                {
                    if (sect != NULL)
                        lines = sect->lnnoptr;
                    else
                        lines = 0;
                    lnode = area_table[index].lines;
                }
                else
                    lnode = NULL;
                if ((lnode != NULL) && (strcmp(file, lnode->file) != 0))
                {
                    count = (strlen(lnode->file) + 17) / 18;
                    add_symbol(".file", link_file, count, 0, sec_null);
                    add_file_aux(lnode->file, count);
                    file = lnode->file;
                }
                if (anode->link == link_weak_function)
                    new_index = add_symbol(anode->name, link_local_function,
                                           debugging, 0,
                                           (section_type) section->index);
                else
                    new_index = add_symbol(anode->name, anode->link, debugging,
                                           0, (section_type) section->index);
                if (debugging)
                {
                    add_function_aux(new_index + 2, area_table[index].size,
                                     lines);
                    add_symbol(".bf", link_none, 1, 0,
                               (section_type) section->index);
                    if (lnode != NULL)
                    {
                        add_beginend_aux(lnode->line);
                        lines += add_line_symbols(&lnode, &file, (section_type)
                                                  section->index) * 6;
                    }
                    else
                    {
                        add_beginend_aux(0);
                        add_symbol(".lf", link_none, 0, 0,
                                   (section_type) section->index);
                    }
                    add_symbol(".ef", link_none, 1, area_table[index].size,
                               (section_type) section->index);
                    if (lnode != NULL)
                        add_beginend_aux(lnode->line);
                    else
                        add_beginend_aux(0);
                }
                if (anode->link == link_weak_function)
                {
                    add_symbol(anode->name, link_global_external_function, 1, 0,
                               sec_null);
                    add_weak_aux(new_index);
                }
            }
        for (node = area_table[index].members.head; node->next != NULL;
             node = node->next)
            if ((node->type == 0) && (*node->name != '.') &&
                ((node->data.entry.link == link_local_function) ||
                 (node->data.entry.link == link_global_function) ||
                 (node->data.entry.link == link_weak_function)))
            {
                if (debugging)
                    lnode = node->data.entry.lines;
                else
                    lnode = NULL;
                if ((lnode != NULL) && (strcmp(file, lnode->file) != 0))
                {
                    count = (strlen(lnode->file) + 17) / 18;
                    add_symbol(".file", link_file, count, 0, sec_null);
                    add_file_aux(lnode->file, count);
                    file = lnode->file;
                }
                if (node->data.entry.link == link_weak_function)
                    new_index = add_symbol(node->name, link_local_function,
                                           debugging, node->offset,
                                           (section_type) section->index);
                else
                    new_index = add_symbol(node->name, node->data.entry.link,
                                           debugging, node->offset,
                                           (section_type) section->index);
                node->data.entry.symidx = new_index;
                if (debugging)
                {
                    add_function_aux(new_index + 2, node->data.entry.size,
                                     lines);
                    add_symbol(".bf", link_none, 1, node->offset,
                               (section_type) section->index);
                    if (lnode != NULL)
                    {
                        add_beginend_aux(lnode->line);
                        lines += add_line_symbols(&lnode, &file, (section_type)
                                                  section->index) * 6;
                    }
                    else
                    {
                        add_beginend_aux(0);
                        add_symbol(".lf", link_none, 0, 0,
                                   (section_type) section->index);
                    }
                    add_symbol(".ef", link_none, 1,
                               node->offset + node->data.entry.size,
                               (section_type) section->index);
                    if (lnode != NULL)
                        add_beginend_aux(lnode->line);
                    else
                        add_beginend_aux(0);
                }
                if (node->data.entry.link == link_weak_function)
                {
                    add_symbol(node->name, link_global_external_function, 1, 0,
                               sec_null);
                    add_weak_aux(new_index);
                }
            }
    }
    for (index = 0; index < area_num; index++)
    {
        section = &section_info_table[area_info_table[index].section];
        for (anode = area_table[index].aliases; anode != NULL;
             anode = anode->next)
            if ((*anode->name != '.') && ((anode->link == link_local) ||
                 (anode->link == link_global) || (anode->link == link_weak)))
                if (anode->link == link_weak)
                {
                    new_index = add_symbol(anode->name, link_local, 0, 0,
                                           (section_type) section->index);
                    add_symbol(anode->name, link_global_external, 1, 0,
                               sec_null);
                    add_weak_aux(new_index);
                }
                else
                    add_symbol(anode->name, anode->link, 0, 0,
                               (section_type) section->index);
        for (node = area_table[index].members.head; node->next != NULL;
             node = node->next)
            if ((node->type == 0) && (*node->name != '.') &&
                ((node->data.entry.link == link_local) ||
                 (node->data.entry.link == link_global) ||
                 (node->data.entry.link == link_weak)))
            {
                if (node->data.entry.link == link_weak)
                {
                    new_index = add_symbol(node->name, link_local, 0,
                                           node->offset,
                                           (section_type) section->index);
                    add_symbol(node->name, link_global_external, 1, 0,
                               sec_null);
                    add_weak_aux(new_index);
                }
                else
                    new_index = add_symbol(node->name, node->data.entry.link, 0,
                                           node->offset,
                                           (section_type) section->index);
                node->data.entry.symidx = new_index;
            }
    }
    for (index = area_num; index < highest_area; index++)
    {
        table = &area_table[index];
        if ((table->name != NULL) && ((table->link == link_local_common) ||
             (table->link == link_global_common) ||
             (table->link == link_weak_common)))
            if (table->link == link_weak_common)
            {
                table->offset = add_symbol(table->name, link_local_common, 0,
                                           table->size, sec_null);
                add_symbol(table->name, link_global_external, 1, 0, sec_null);
                add_weak_aux(table->offset);
            }
            else
                table->offset = add_symbol(table->name, table->link, 0,
                                           table->size, sec_null);
    }
    for (index = area_num; index < highest_area; index++)
    {
        table = &area_table[index];
        if ((table->name != NULL) &&
            ((table->link == link_global_external_function) ||
             (table->link == link_weak_external_function)))
        {
            table->offset = add_symbol(table->name, table->link, 0, 0,
                                       sec_null);
            for (anode = table->aliases; anode != NULL; anode = anode->next)
                add_symbol(anode->name, anode->link, 0, 0, sec_null);
        }
    }
    for (index = area_num; index < highest_area; index++)
    {
        table = &area_table[index];
        if ((table->name != NULL) &&
            ((table->link == link_global_external) ||
             (table->link == link_weak_external)))
        {
            table->offset = add_symbol(table->name, table->link, 0, 0,
                                       sec_null);
            for (anode = table->aliases; anode != NULL; anode = anode->next)
                add_symbol(anode->name, anode->link, 0, 0, sec_null);
        }
    }
    patch_symbol_table();
    if (host_endian != target_endian)
        swap_symbol_table();
}


/* writes out the entire object file */

static void write_object(void)
{
    coff_section *section;
    char *buffer;
    coff_header header;
    size_t area_index, count, dummy, index, offset;

    dummy = 0;
    count = area_table[area_section].size / sizeof(coff_section);
    header.magic = COFF_ARCH_386;
    header.nscns = count;
    header.timdat = (unsigned long) time(NULL);
    header.symptr = symbol_table_offset;
    header.nsyms = area_table[area_symbol].size / 18;
    header.opthdr = 0;
    header.flags = 0;
    if (host_endian != target_endian)
    {
        byte_swap((char *) &header.magic, 2);
        byte_swap((char *) &header.nscns, 2);
        byte_swap((char *) &header.timdat, 4);
        byte_swap((char *) &header.symptr, 4);
        byte_swap((char *) &header.nsyms, 4);
        byte_swap((char *) &header.opthdr, 2);
        byte_swap((char *) &header.flags, 2);
    }
    write(new_outfile, &header, sizeof(coff_header));
    /* We now put out the section table, but that may need to be byte-swapped
     * before it is written.  However, we'll be using the section table again
     * so we may need to un-swap it after writing it to the object file.
     */
    if (host_endian != target_endian)
        swap_section_table();
    write(new_outfile, area_table[area_section].contents,
          area_table[area_section].size);
    if (host_endian != target_endian)
        swap_section_table();
    section = (coff_section *) area_table[area_section].contents;
    offset = sizeof(coff_header) + area_table[area_section].size;
    for (index = 0; index < count; index++)
    {
        if (!(section->flags & COFF_SECT_BSS) && (section->size > 0))
        {
            for (area_index = 1; area_index < area_num; area_index++)
                if (section_info_table[area_info_table[area_index].section].index ==
                    index)
                    break;
            assert(area_index != area_num);
            /* It is recommended that the file offsets for section contents
             * are a multiple of four.
             */
            if (offset % 4)
                write(new_outfile, &dummy, 4 - (offset % 4));
            offset = round_up(offset, 4) + section->size;
            assert(area_table[area_index].contents != NULL);
            write(new_outfile, area_table[area_index].contents, section->size);
            /* It is recommended that the file offsets for section relocations
             * are a multiple of four.
             */
            if (section->nreloc != 0)
            {
                if (offset % 4)
                    write(new_outfile, &dummy, 4 - (offset % 4));
                offset = round_up(offset, 4) + (section->nreloc * 10);
                buffer = (char *) getspace(section->nreloc * 10);
                write_relocations(&area_table[area_index].members, buffer);
                write(new_outfile, buffer, section->nreloc * 10);
                freespace(buffer);
            }
            /* It is recommended that the file offsets for section line numbers
             * are a multiple of four.
             */
            if (section->nlnno != 0)
            {
                if (offset % 4)
                    write(new_outfile, &dummy, 4 - (offset % 4));
                offset = round_up(offset, 4) + (section->nlnno * 6);
                buffer = (char *) getspace(section->nlnno * 6);
                write_lines(area_index, buffer);
                write(new_outfile, buffer, section->nlnno * 6);
                freespace(buffer);
            }
        }
        section = (coff_section *) ((char *) section + sizeof(coff_section));
    }
    /* Write out the symbol table.
     */
    if (offset % 4)
        write(new_outfile, &dummy, 4 - (offset % 4));
    write(new_outfile, area_table[area_symbol].contents,
          area_table[area_symbol].size);
    /* Write out the string table plus four bytes for the length.
     */
    dummy = area_table[area_string].size + 4;
    if (host_endian != target_endian)
        byte_swap((char *) &dummy, 4);
    write(new_outfile, &dummy, 4);
    write(new_outfile, area_table[area_string].contents,
          area_table[area_string].size);
}


/* returns the name associated with a specific type of linkage */

static char *linkage_name(linkage link)
{
    static char *type;

    switch (link)
    {
      case link_none:
        type = "none";
        break;
      case link_file:
        type = "file";
        break;
      case link_section:
        type = "section";
        break;
      case link_global:
        type = "global data";
        break;
      case link_weak:
        type = "weak data";
        break;
      case link_local_common:
        type = "local common";
        break;
      case link_global_common:
        type = "global common";
        break;
      case link_weak_common:
        type = "weak common";
        break;
      case link_global_external:
        type = "global ext data";
        break;
      case link_weak_external:
        type = "weak ext data";
        break;
      case link_local_function:
        type = "local func";
        break;
      case link_global_function:
        type = "global func";
        break;
      case link_weak_function:
        type = "weak func";
        break;
      case link_global_external_function:
        type = "global ext func";
        break;
      case link_weak_external_function:
        type = "weak ext func";
        break;
      default:
        type = "local data";
        break;
    }
    return type;
}


/* displays a summary of all the line numbers in a list */

static void print_lines(lineno *node)
{
    char *file;

    file = "";
    while (node != NULL)
    {
        if (strcmp(file, node->file) != 0)
        {
            put_message("    %s:\n", node->file);
            file = node->file;
        }
        if (object_generating)
            put_message("    %6lu  ", node->addr.offset);
        else
            put_message("      %s: ", node->addr.label);
        put_message("line %lu\n", node->line);
        node = node->next;
    }
}


/* displays a summary of all the members in an area */

static void print_members(size_t area)
{
    member_node *node;

    for (node = area_table[area].members.head; node->next != NULL;
         node = node->next)
    {
        put_message("    %6lu  ", node->offset);
        if (node->type == 0)
        {
            put_message("entry  (\"%s\", %s, %lu)\n", node->name,
                        linkage_name(node->data.entry.link),
                        node->data.entry.size);
            print_lines(node->data.entry.lines);
        }
        else
        {
            put_message("fixup  (");
            if (node->name != NULL)
                put_message("%s", node->name);
            else
                put_message("%s", area_table[node->data.target.area].name);
            if (node->data.target.offset)
            {
                if (node->data.target.offset > 0)
                    put_message("+");
                put_message("%ld", node->data.target.offset);
            }
            put_message(", %u)\n", node->type);
        }
    }
}


/* displays a summary of all allocated areas */

static void print_areas(void)
{
    area_header *table;
    alias *anode;
    size_t index;

    put_message("area  disp  size align  fill  type             name\n");
    for (index = 0; index < highest_area; index++)
    {
        table = &area_table[index];
        if (table->name != NULL)
        {
            put_message("%4lu  %4lu  %4lu  %4lu  0x%02X  %-15s  %s\n", index,
                        table->offset, table->size, table->align, table->fill,
                        linkage_name(table->link), table->name);
            for (anode = table->aliases; anode != NULL; anode = anode->next)
                put_message("                       (alias %-15s  %s)\n",
                            linkage_name(anode->link), anode->name);
            print_members(index);
            print_lines(table->lines);
        }
    }
}


/* sets up the source and destination filenames for the put interface */
/* and then returns the output file handle */

int psetfiles2(char *source_name, char *dest_name, int no_file)
{
    int oflags;

    if (put_trace)
        put_message("psetfiles2(source_name = \"%s\", dest_name = \"%s\", "
                    "no_file = %d)\n", source_name, dest_name, no_file);
    assert(!put_unterminated);
    assert((source_file == NULL) && (dest_file == NULL));
    assert((source_name != NULL) && (*source_name != '\0'));
    assert((dest_name != NULL) && (*dest_name != '\0'));
    source_file = source_name;
    dest_file = dest_name;
    oflags = O_WRONLY | O_CREAT | O_TRUNC;
#ifdef _WIN32
    oflags |= O_BINARY;
#endif /* _WIN32 */
    fflush(stdout);
    if (no_file)
        new_outfile = fileno(stdout);
    else if ((new_outfile = open(dest_name, oflags, 0664)) == -1)
        put_error("cannot create output file `%s'", dest_name);
    if (!object_generating)
    {
        /* Because the backend writes all of the generated assembler to the
         * standard output, we need to redirect the standard output to a
         * file in order to pass it to the assembler.  This can be prevented
         * by setting no_file to a non-zero value, in which case the generated
         * assembler will be sent to the standard output.
         */
        if ((old_outfile = dup(fileno(stdout))) == -1)
            put_error("cannot duplicate output file handle");
        if (dup2(new_outfile, fileno(stdout)) == -1)
            put_error("cannot reassign output file handle");
    }
    if (put_trace)
        put_message("psetfiles2() returns (new_outfile = %d)\n", new_outfile);
    return new_outfile;
}


/* sets up the source and destination filenames for the put interface */

void psetfiles(char *source_name, char *dest_name, int no_file)
{
    if (put_trace)
        put_message("psetfiles(source_name = \"%s\", dest_name = \"%s\", "
                    "no_file = %d)\n", source_name, dest_name, no_file);
    assert(!put_unterminated);
    psetfiles2(source_name, dest_name, no_file);
}


/* initialises the put interface */

void pinitialise(int version, int release, int language)
{
    char buffer[256];
    unsigned long dummy;
    int ver;

    if (put_trace)
        put_message("pinitialise(version = %d, release = %d, "
                    "language = 0x%08lX)\n", version, release, language);
    assert(!put_initialised);
    assert(!put_unterminated);
    assert((source_file != NULL) && (dest_file != NULL));
    /* At this point we can perform a once-only initialisation of
     * target-specific features as these will not change on subsequent
     * calls to preinitialise.  This is also done to prevent conditional
     * compilation and so facilitate cross-compilation.  Note that this
     * module should have no dependencies on other modules and should
     * provide only a functional interface without sharing data.
     */
    target_processor = (language & 0xFF00) >> 8;
    if (target_processor == TARGET_386)
    {
        assembler = &coff_i386_assembler;
        relocation_table = coff_i386_relocation_table;
        rel_mask_table = coff_i386_rel_mask_table;
    }
    else
        put_error("unsupported target processor `%u'", target_processor);
    dummy = 1;
    if (*((unsigned char *) &dummy) == 1)
        host_endian = LITTLE_ENDIAN;
    else
        host_endian = BIG_ENDIAN;
    target_endian = LITTLE_ENDIAN;
    /* Set the default external prefix to be an underscore.  However, this will
     * not be enabled unless the prepend_prefix global variable is set.
     */
    if (external_prefix == NULL)
        pexternalprefix("_");
    init_area_table();
    if (!object_generating && !suppress_code_gen)
    {
        write_header();
        enter_section(sec_text);
    }
    put_initialised = 1;
    put_unterminated = 1;
    /* If any of the following version strings exist then place them
     * in the ident section of the object file.  They serve as a
     * way to better identify the compiler which produced the output.
     */
    ver = 0;
    if (epc_version_string)
    {
        sscanf(epc_version_string, "%[^\n]", buffer);
        pident(buffer);
        ver = 1;
    }
    if (epc_front_end_version)
    {
        sscanf(epc_front_end_version, "%[^\n]", buffer);
        pident(buffer);
        ver = 1;
    }
    if (epc_back_end_version)
    {
        sscanf(epc_back_end_version, "%[^\n]", buffer);
        pident(buffer);
        ver = 1;
    }
    if (ver && !object_generating)
        write_code("\n");
}


/* reinitialises the put interface */

void preinitialise(void)
{
    char buffer[256];
    int ver;

    if (put_trace)
        put_message("preinitialise()\n");
    assert(put_initialised);
    assert(!put_unterminated);
    assert((source_file != NULL) && (dest_file != NULL));
    init_area_table();
    if (!object_generating && !suppress_code_gen)
    {
        write_header();
        enter_section(sec_text);
    }
    put_unterminated = 1;
    /* If any of the following version strings exist then place them
     * in the ident section of the object file.  They serve as a
     * way to better identify the compiler which produced the output.
     */
    ver = 0;
    if (epc_version_string)
    {
        sscanf(epc_version_string, "%[^\n]", buffer);
        pident(buffer);
        ver = 1;
    }
    if (epc_front_end_version)
    {
        sscanf(epc_front_end_version, "%[^\n]", buffer);
        pident(buffer);
        ver = 1;
    }
    if (epc_back_end_version)
    {
        sscanf(epc_back_end_version, "%[^\n]", buffer);
        pident(buffer);
        ver = 1;
    }
    if (ver && !object_generating)
        write_code("\n");
}


/* terminates the put interface */

void pterminate(int *sizes)
{
    size_t area, index;

    if (put_trace)
        put_message("pterminate(sizes = 0x%08lX)\n", sizes);
    assert(put_initialised);
    assert(put_unterminated);
    assert((source_file != NULL) && (dest_file != NULL));
    assert((section_level == 0) && (scope_level == 0));
    assert(sizes != NULL);
    assert(max_stack_size == 0);
    assert((area_table[area_stack].size == 0) &&
           (area_table[area_params].size == 0));
    /* Display and check the area sizes passed in by the backend.  The
     * code area is dealt with by the backend and so its size is ignored.
     * The final size of the BSS area is set from here since we would not
     * normally initialise any space in this area.
     */
    if (!suppress_code_gen)
    {
        if (put_trace)
            put_message("\n");
        for (index = 1; index <= 10; index++, sizes++)
        {
            if (put_trace)
                put_message("%3lu %5lu %s\n", index, *sizes,
                            area_info_table[index].area);
            if (index == area_bss)
            {
                if (area_table[area_bss].size >= *sizes)
                    area_table[area_bss].size = *sizes;
                else
                    pdinit(area_bss, 0, *sizes);
            }
            else if ((index != area_code) && (*sizes != area_table[index].size))
                put_error("mismatching sizes for the length of the %s area",
                          area_info_table[index].area);
        }
        if (put_trace)
            put_message("\n");
    }
    /* Any required library directives get specified here.
     */
    add_directive(DIRECT_LIBRARY, "libc");
    /* The following code is used to plant a dummy local common symbol into
     * the symbol table of the object file to indicate that it was compiled
     * using an EPC compiler.
     */
    area = pcommon2(1, "epc.compiled");
    pendcommon2(area, 1, 0, 1);
    /* These are hacks to work with the Microsoft C library and are not
     * required for other libraries.
     */
    pxname(0, "__fltused");
    if (area_table[area_tls].size != 0)
        pxname(0, "__tls_used");
    put_unterminated = 0;
}


/* finishes writing the destination output file */

void pgenerateobject(char *dest_name)
{
    size_t index;

    if (put_trace)
        put_message("pgenerateobject(dest_name = \"%s\")\n", dest_name);
    assert(put_initialised);
    assert(!put_unterminated);
    assert((source_file != NULL) && (dest_file != NULL));
    assert((dest_name != NULL) && (*dest_name != '\0'));
    assert(strcmp(dest_file, dest_name) == 0);
    if (!suppress_code_gen)
    {
        if (put_trace)
        {
            put_message("\narea table before processing:\n\n");
            print_areas();
        }
        /* We must first move all user-defined data areas into their
         * corresponding parent areas.
         */
        move_data_areas();
        /* We may now have to move certain common areas into the BSS area if
         * required, depending on the no_commons flag and the format of file
         * being generated.
         */
        if (no_commons)
            move_commons(0);
        else
            move_commons(1);
        /* We now try to remove any unnecessary relocations to local functions
         * and convert all functions to entries in the code area if we are
         * generating an object file.
         */
        if (object_generating)
        {
            optimise_relocations();
            move_functions();
        }
        merge_areas();
        if (put_trace)
        {
            put_message("\narea table after processing:\n\n");
            print_areas();
        }
        /* Apart from the section headers and function code when generating
         * assembler, all of the contents of the output file is written here.
         */
        if (object_generating)
        {
            count_relocations();
            count_lines();
            build_section_table();
            build_symbol_table();
            write_addends();
            write_object();
        }
        else
        {
            /* If there have been references to the .epctext label but there
             * were no functions in the text section then we must output the
             * .epctext label anyway and generate an empty text section.
             */
            if ((first_function == 0) && (area_table[area_code].offset > 0))
                write_code("%s\t%s %s\n", area_table[area_code].name,
                           assembler->label, assembler->byte);
            else if (first_function != 0)
                write_code("%s\t%s %s\n", label_name(area_table[area_code].name,
                           area_code, 0, 1), assembler->label, assembler->byte);
            write_function_aliases();
            write_external_functions();
            write_areas();
            write_commons();
            write_external_objects();
            write_code("\t%s\n", assembler->end);
        }
    }
    /* At this point we can reset all of the global variables, free up the
     * entire area table and reset some of the fields in the section table,
     * just in case we need to generate another output file in the same
     * compilation.
     */
    section_stack[0] = sec_null;
    current_section = sec_null;
    symbol_table_offset = 0;
    current_filename = NULL;
    first_function = 0;
    free_area_table();
    for (index = sec_null; index < sec_num; index++)
    {
        section_info_table[index].index = 0xFFFFFFFF;
        section_info_table[index].size = 0;
        section_info_table[index].targets = 0;
        section_info_table[index].relocs = 0;
        section_info_table[index].lines = 0;
        section_info_table[index].align = 0;
    }
    fflush(stdout);
    if (!object_generating)
    {
        /* We now need to restore the standard output file stream to its
         * previous value as we may wish to write another assembler file
         * or display a message on the original standard output.
         */
        if (dup2(old_outfile, fileno(stdout)) == -1)
            put_error("cannot reassign output file handle");
        close(old_outfile);
    }
    if (new_outfile != fileno(stdout))
        close(new_outfile);
    if (suppress_code_gen)
        premoveobject();
    source_file = NULL;
    dest_file = NULL;
}


/* removes the resulting output file */

void premoveobject(void)
{
    if (put_trace)
        put_message("premoveobject()\n");
    assert(dest_file != NULL);
    remove(dest_file);
}


/* controls the put interface options and indicates if */
/* the output should be an assembler or object file and if */
/* position-independent code should be produced */

void psetoptions2(int options, int object, int debug, int pic)
{
    if (put_trace)
        put_message("psetoptions2(options = 0x%08lX, object = %d, debug = %d, "
                    "pic = %d)\n", options, object, debug, pic);
    assert(!put_initialised);
    assert(!put_unterminated);
    if (options & 0x10)
        annotate_assembler = 1;
    if (options & 0x20)
        no_commons = 1;
    if (options & 0x40)
        prepend_prefix = 1;
    if (options & 0x80)
        put_memory_trace = 1;
    if (options & 0x1000)
        auto_weak_functions = 1;
    object_generating = object;
    if ((debug == DEBUG_COFF) || (debug == DEBUG_CODEVIEW))
        debug_format = debug;
    else
        put_error("unsupported debugging format `%u'", debug);
}


/* controls the put interface options */

void psetoptions(int options)
{
    if (put_trace)
        put_message("psetoptions(options = 0x%08lX)\n", options);
    assert(!put_initialised);
    assert(!put_unterminated);
    psetoptions2(options, 1, 0, 0);
}


/* returns 1 if data initialiser constants need to be byte-swapped before */
/* being passed to pd2(), pd4() and pdbytes(), else returns 0 */

int pdswapneeded(void)
{
    int r;

    if (put_trace)
        put_message("pdswapneeded()\n");
    assert(put_initialised);
    assert(put_unterminated);
    r = 0;
    if (put_trace)
        put_message("pdswapneeded() returns %d\n", r);
    return r;
}


/* sets the prefix to be prepended to externally-visible symbol names */

void pexternalprefix(char *prefix)
{
    if (put_trace)
        put_message("pexternalprefix(prefix = \"%s\")\n", prefix);
    assert(prefix != NULL);
    external_prefix = prefix;
    external_prefix_size = strlen(prefix);
}


/* sets the default filler byte for all areas */

void pdefaultfiller(int filler)
{
    if (put_trace)
        put_message("pdefaultfiller(filler = 0x%08lX)\n", filler);
    assert((filler >= 0) && (filler < 256));
    area_filler = filler;
}


/* enforces the suppression of code generation */

void pfaulty(void)
{
    if (put_trace)
        put_message("pfaulty()\n");
    suppress_code_gen = 1;
}


/* turns on put tracing */

void pmonon(void)
{
    put_trace = 1;
}


/* turns off put tracing */

void pmonoff(void)
{
    put_trace = 0;
}


#ifdef __cplusplus
}
#endif
