/* $VER: vlink t_amigaos.c V0.6c (05.02.99)
 *
 * This file is part of vlink, a portable linker for multiple
 * object formats.
 * Copyright (c) 1997-99  Frank Wille
 *
 * vlink is freeware and part of the portable and retargetable ANSI C
 * compiler vbcc, copyright (c) 1995-99 by Volker Barthelmann.
 * vlink may be freely redistributed as long as no modifications are
 * made and nothing is charged for it. Non-commercial usage is allowed
 * without any restrictions.
 * EVERY PRODUCT OR PROGRAM DERIVED DIRECTLY FROM MY SOURCE MAY NOT BE
 * SOLD COMMERCIALLY WITHOUT PERMISSION FROM THE AUTHOR.
 *
 *
 * v0.6c (05.02.98) phx
 *       __ctors/__dtors for executables only.
 * v0.6a (19.12.98) phx
 *       Support for little endian object file formats.
 * v0.6  (24.10.98) phx
 *       Take base register section offset from FFFuncs.baseoff.
 * v0.5f (08.10.98) phx
 *       The automatic constructor/destructor functions have to begin
 *       with __INIT or __EXIT to avoid conflicts with ANSI-C identifiers.
 * v0.5e (05.10.98) phx
 *       Global symbols beginning with _INIT or _EXIT will create an
 *       entry into the constructor/destructor function pointer table.
 *       These tables can be addressed by using the symbols __ctors
 *       and __dtors (e.g. in the startup/cleanup code of a program).
 *       The priority of these functions may be defined by specifying
 *       a number behind the INIT/EXIT string. Example: _INIT_9_OpenLibs.
 *       Otherwise their priority defaults to 0 and they will be
 *       positioned in order of occurence.
 * v0.5d (22.08.98) phx
 *       Faster memory allocation can be activated by #define FASTALLOC.
 * v0.5c (08.07.98) phx
 *       ehf_lnksym():
 *       Target amigaehf supports the automatic generation of "@__name"
 *       pointer variables in ".tocd". @__name will not be created, if
 *       _name is undefined too. Then it's assumed to be a real unknown
 *       symbol reference.
 * v0.5  (27.06.98) phx
 *       Target-specific linker symbol support: ados_lnksym(), ehf_lnksym(),
 *       ados_setlnksym(), ehf_setlnksym().
 *       Linker symbols currently supported by ADOS and EHF:
 *       _DATA_BAS_,_DATA_LEN_,_BSS_LEN_,_LinkerDB,__BSSBAS,__BSSLEN,
 *       __ctors,__dtors,__DATA_BAS,__DATA_LEN,__BSS_LEN,__RESIDENT.
 * v0.4  (05.06.98) phx
 *       Support for Code-Bss and Data-Bss (small data) sections.
 *       New FFF targetlink(). Checks for __MERGED, _NOMERGE and
 *       unnamed sections.
 * v0.3b (02.05.98) phx
 *       Creates a dummy code section, if an output file has no
 *       section at all.
 * v0.3  (12.04.98) phx
 *       fwrite32() is now called as fwrite32be().
 *       Uses new addreloc() from targets.c.
 *       addxref() requires addend.
 *       Ignore absolute local symbols for object files and ignore
 *       absolute local and global symbols for executable files.
 * v0.2  (07.03.98) phx
 *       Unnamed sections of an executable will always get different
 *       names, to avoid that they are linked together.
 *       HUNK_DREL32 must be treated as HUNK_RELOC32SHORT, if the
 *       input file is executable.
 *       Number of hunks in HUNK_HEADER was wrong, if resident library
 *       name is missing (which is always the case).
 * v0.1  (27.02.98) phx
 *       First version that seems to link AmigaOS ADOS and EHF
 *       objects and libraries. Many common features, like linking
 *       sections together which have relative references, are
 *       still missing. Also, PowerPC-ELF32 support is about to come.
 * v0.0  (02.12.97) phx
 *       File created.
 */


#if defined(ADOS) || defined(EHF)
#define T_AMIGAOS_C
#include "vlink.h"
#include "amigahunks.h"

#define BSSDEFAULTNAME "BSS"
#define EXT_IGNORE 0x100

struct HunkInfo {
  uint8 *hunkbase;  /* base address of amigaos/ehf file */
  uint8 *hunkptr;   /* current hunk data pointer */
  long hunkcnt;     /* remaining bytes in this file */
  char *filename;
  bool exec;        /* executable file? */
};

struct XRefNode {
  struct node n;
  char *sym_name;
  uint8 ref_type;
  int noffsets;
  struct list xreflist;
};


static int ados_identify(char*,uint8 *,unsigned long);
static int ehf_identify(char *,uint8 *,unsigned long);
static int identify(char *,uint8 *,unsigned long);
static void readconv(struct GlobalVars *,struct LinkFile *);
static unsigned long secbase(struct GlobalVars *,char *);
static uint8 cmpsecflags(uint8,uint8);
static struct Section *bssdefault(struct ObjectUnit *);
static int targetlink(struct GlobalVars *,struct LinkedSection *,
                      struct Section *);
static struct Symbol *ados_lnksym(struct GlobalVars *,struct Section *,
                                  struct XReference *);
static void ados_setlnksym(struct GlobalVars *,struct Symbol *,
                           struct XReference *);
static struct Symbol *ehf_lnksym(struct GlobalVars *,struct Section *,
                                  struct XReference *);
static void ehf_setlnksym(struct GlobalVars *,struct Symbol *,
                           struct XReference *);
static int get_xtors_pri(char *);
static void create_xdef(struct GlobalVars *,struct Section *,char *,
                        uint32,uint8,uint8);
static void create_xrefs(struct GlobalVars *,struct HunkInfo *,
                         struct Section *,char *,uint8,uint8);
static char *gethunkname(struct HunkInfo *);
static bool addlongrelocs(struct GlobalVars *,struct HunkInfo *,
                          struct Section *,uint8);
static bool addshortrelocs(struct GlobalVars *,struct HunkInfo *,
                           struct Section *,uint8);
static void init_hunkinfo(struct HunkInfo *,char *,uint8 *,unsigned long);
static uint32 skiphunk(struct HunkInfo *);
static void movehunkptr32(struct HunkInfo *,uint32);
static void movehunkptr16(struct HunkInfo *,uint32);
static void alignhunkptr(struct HunkInfo *);
static uint32 testword32(struct HunkInfo *);
static uint32 nextword32(struct HunkInfo *);
static uint16 nextword16(struct HunkInfo *);
static void writeobject(struct GlobalVars *,FILE *);
static void writeshared(struct GlobalVars *,FILE *);
static void writeexec(struct GlobalVars *,FILE *);
static void hunk_name_len(FILE *,char *);
static void hunk_name(FILE *,char *);
static int strlen32(char *);
static void reloc_hunk(FILE *,struct LinkedSection *,struct GlobalVars *,
                       uint8,uint32);
static void unsupp_relocs(struct LinkedSection *);
static void ext_refs(FILE *,struct LinkedSection *);
static void ext_defs(FILE *,struct LinkedSection *,uint8,uint8,uint32);
static void unsupp_symbols(struct LinkedSection *);


struct FFFuncs fff_amigaos = {
  "amigaos",
  ados_identify,
  readconv,
  secbase,
  cmpsecflags,
  bssdefault,
  targetlink,
  ados_lnksym,
  ados_setlnksym,
  writeobject,
  writeshared,
  writeexec,
  0x7ffe,
  NULL,
  1 /* big endian */
};

struct FFFuncs fff_ehf = {
  "amigaehf",
  ehf_identify,
  readconv,
  secbase,
  cmpsecflags,
  bssdefault,
  targetlink,
  ehf_lnksym,
  ehf_setlnksym,
  writeobject,
  writeshared,
  writeexec,
  0x7ffe,
  NULL,
  1 /* big endian */
};

/* Automagically create symbols in .tocd, which start with the */
/* following letters: */
static char ehf_addrsym[] = "@_";

static char *ados_symnames[] = {
  /* PhxAss */     "_DATA_BAS_","_DATA_LEN_","_BSS_LEN_",
  /* SAS/StormC */ "_LinkerDB","__BSSBAS","__BSSLEN","__ctors","__dtors",
  /* DICE-C */     "__DATA_BAS","__DATA_LEN","__BSS_LEN","__RESIDENT"
};
#define PHXDB 0
#define PHXDL 1
#define PHXBL 2
#define SASPT 3
#define SASBB 4
#define SASBL 5
#define SASCT 6
#define SASDT 7
#define DICDB 8
#define DICDL 9
#define DICBL 10
#define DICRS 11
#define LAST_LNKSYM DICRS

/* automatic constructors and destructors */
static char vbcc_INIT[] = "__INIT";
static char vbcc_EXIT[] = "__EXIT";
static char xtors_objname[] = "INITEXIT"; /* unit name for con/destructors */

static bool exthunk,symhunk;



/*****************************************************************/
/*                      Read ADOS / EHF                          */
/*****************************************************************/


static int ados_identify(char *name,uint8 *p,unsigned long plen)
{
  int ff;

  if ((ff = identify(name,p,plen)) >= 0)
    return (ff);
  return (ID_UNKNOWN);  /* ehf */
}


static int ehf_identify(char *name,uint8 *p,unsigned long plen)
{
  int ff;

  if ((ff = identify(name,p,plen)) < 0)
    return (-ff);
  return (ff);  /* ados is a subset of ehf */
}


static int identify(char *name,uint8 *p,unsigned long plen)
/* Identify AmigaDOS or extended hunk format */
{
  uint32 w = read32be(p);
  struct HunkInfo hi;
  int type;

  if (w == HUNK_HEADER) {
    error(12,name);  /* file is already an executable */
    return (ID_EXECUTABLE);  /* EHF-executables doesn't exist! */
  }
  else if (w!=HUNK_UNIT && w!=HUNK_LIB)
    return (ID_UNKNOWN);

  /* @@@ EHF will never be in HUNK_LIB format, because HUNK_PPC_CODE */
  /* (0x4e9) conflicts with HUNK_CODE|(MEMB_CHIP<<14) = 0x7e9. @@@   */
  if (w == HUNK_LIB)
    return (ID_LIBARCH);  /* SAS/C-style library */

  /* Check, by comparing the suffix with ".LIB", if the units */
  /* of this object file have to be handled as part of an object */
  /* or library archive.  Yes, this is the only way... */
  type = ID_OBJECT;
  if ((w = strlen(name)-4) > 0) {
    if (name[w]=='.' &&
        toupper((unsigned char)name[w+1])=='L' &&
        toupper((unsigned char)name[w+2])=='I' &&
        toupper((unsigned char)name[w+3])=='B')
      type = ID_LIBARCH;
  }

  /* Now check if any unit contains EHF hunks */
  init_hunkinfo(&hi,name,p,plen);
  while (w = skiphunk(&hi)) {
    if (w == HUNK_PPC_CODE)
      return (-type);  /* it's an EHF object/library, containing PPC code */
  }
  return (type);
}


static void readconv(struct GlobalVars *gv,struct LinkFile *lf)
/* Read AmigaDOS/EHF hunks and symbols */
{
  struct HunkInfo hi;
  uint32 w;
  struct ObjectUnit *u=NULL;
  struct Section *s=NULL;
  char *secname=NULL;
  uint8 *headerattr=NULL;

  init_hunkinfo(&hi,lf->filename,lf->data,lf->length);
  if (testword32(&hi) == HUNK_LIB) {
    ierror("readconv(): HUNK_LIB not yet supported");
  }

  /* HUNK_UNIT or HUNK_HEADER */
  else {
    while (w = testword32(&hi)) {
      switch (w & 0x3fffffff) {

        case HUNK_HEADER:
          /* header must be at the beginning of the file */
          if (hi.hunkptr == hi.hunkbase) {
            if (!s) {
              uint32 n;
              add_objunit(gv,u,TRUE); /* add last one to glob. ObjUnit list */
              u = create_objunit(lf,lf->filename);
              nextword32(&hi);
              n = nextword32(&hi);
              movehunkptr32(&hi,n?n+2:1);
              w = nextword32(&hi);
              n = nextword32(&hi) - w + 1;  /* number of size specifiers */
              headerattr = hi.hunkptr;  /* remember section attributes */
              while (n--) {
                if ((nextword32(&hi) & 0xc0000000) == 0xc0000000)
                  nextword32(&hi);
              }
            }
            else
              /* Unexpected end of section */
              error(15,lf->pathname,s->name,u->objname);
          }
          else
            /* header appeared twice */
            error(16,lf->pathname,"HUNK_HEADER",lf->filename);
          break;

        case HUNK_UNIT:  /* a new ObjectUnit */
          if (!s) {
            char *uname;
            nextword32(&hi);
            add_objunit(gv,u,TRUE); /* add last one to glob. ObjUnit list */
            uname = gethunkname(&hi);
            u = create_objunit(lf,uname);
          }
          else
            /* Unexpected end of section */
            error(15,lf->pathname,s->name,u->objname);
          break;

        case HUNK_NAME:  /* name of a section */
          if (u && !s) {
            nextword32(&hi);
            secname = gethunkname(&hi);
          }
          else
            /* misplaced hunk */
            error(17,lf->pathname,"HUNK_NAME",u?u->objname:lf->filename);
          break;

        case HUNK_CODE:
        case HUNK_PPC_CODE:
        case HUNK_DATA:
        case HUNK_BSS:
          if (u && !s) {  /* a new Section */
            uint8 s_attr;
            unsigned long s_size;
            nextword32(&hi);

            /* determine section attributes */
            if (headerattr) {
              if ((s_attr = (read32be(headerattr)>>24) & 0xc0) == 0xc0) {
                s_attr = 0;  /* ext. mem. type identifier is not supported */
                headerattr += 8;
              }
              else
                headerattr += 4;
            }
            else
              s_attr = (w>>24) & 0xc0;  /* SF_FAST | SF_CHIP */

            /* Sections of an executable should never be joined, or */
            /* dangerous things will happen. */
            if (!secname && hi.exec) {
              secname = alloc(10);
              sprintf(secname,"S%08lx",(unsigned long)hi.hunkptr);
            }

            /* create new section node */
            s_size = (unsigned long)nextword32(&hi) & 0x3fffffff;
            s = create_section(u,secname,hi.hunkptr,s_size<<2);
            s->flags = s_attr;
            s->alignment = 2;  /* amigaos/ehf always 32-bit aligned */
            switch (w & 0xffff) {
              case HUNK_PPC_CODE:
                s->flags |= SF_EHFPPC;  /* section contains PowerPC code */
              case HUNK_CODE:
                s->type = ST_CODE;
                /* @@@@ amigaos/ehf code is always writeable ??? */
                s->protection = SP_READ | /*SP_WRITE |*/ SP_EXEC;
                break;
              case HUNK_DATA:
                s->type = ST_DATA;
                s->protection = SP_READ | SP_WRITE;
                break;
              case HUNK_BSS:
                s->flags |= SF_UNINITIALIZED;
                s->data = NULL;
                s->type = ST_UDATA;
                s->protection = SP_READ | SP_WRITE;
                break;
            }
            if (!(s->flags & SF_UNINITIALIZED))
              movehunkptr32(&hi,s_size);  /* skip section's contents */
          }
          else
            /* Section appeared twice */
            error(16,lf->pathname,"Section",u?u->objname:lf->filename);
          break;

        case HUNK_ABSRELOC32:
          if (!addlongrelocs(gv,&hi,s,R_ADDR32))
            error(17,lf->pathname,"HUNK_ABSRELOC32",u?u->objname:lf->filename);
          break;

        case HUNK_RELOC32SHORT:
          if (!addshortrelocs(gv,&hi,s,R_ADDR32))
            error(17,lf->pathname,"HUNK_RELOC32SHORT",u?u->objname:lf->filename);
          break;

        case HUNK_RELRELOC16:
          if (!addlongrelocs(gv,&hi,s,R_REL16))
            error(17,lf->pathname,"HUNK_RELRELOC16",u?u->objname:lf->filename);
          break;

        case HUNK_RELRELOC8:
          if (!addlongrelocs(gv,&hi,s,R_REL8))
            error(17,lf->pathname,"HUNK_RELRELOC8",u?u->objname:lf->filename);
          break;

        case HUNK_RELRELOC32:
          if (!addlongrelocs(gv,&hi,s,R_REL32))
            error(17,lf->pathname,"HUNK_RELRELOC32",u?u->objname:lf->filename);
          break;

        case HUNK_ABSRELOC16:
          if (!addlongrelocs(gv,&hi,s,R_ADDR16))
            error(17,lf->pathname,"HUNK_ABSRELOC16",u?u->objname:lf->filename);
          break;

        case HUNK_DREL32:
          /* This hunk block has a double meaning. Before Amiga OS 3.0 */
          /* it was often used as RELOC32SHORT, but will appear in */
          /* executables only. */
          if (hi.exec) {
            if (!addshortrelocs(gv,&hi,s,R_ADDR32))
              error(17,lf->pathname,"HUNK_RELOC32SHORT",
                    u?u->objname:lf->filename);
          }
          else {
            if (!addlongrelocs(gv,&hi,s,R_BASEREL32))
              error(17,lf->pathname,"HUNK_DREL32",u?u->objname:lf->filename);
          }
          break;

        case HUNK_DREL16:
          if (!addlongrelocs(gv,&hi,s,R_BASEREL16))
            error(17,lf->pathname,"HUNK_DREL16",u?u->objname:lf->filename);
          break;

        case HUNK_DREL8:
          if (!addlongrelocs(gv,&hi,s,R_BASEREL8))
            error(17,lf->pathname,"HUNK_DREL8",u?u->objname:lf->filename);
          break;

        case HUNK_RELRELOC26: /* EHF */
          if (!addlongrelocs(gv,&hi,s,R_REL26))
            error(17,lf->pathname,"HUNK_RELRELOC26",u?u->objname:lf->filename);
          break;

        case HUNK_EXT:  /* external definitions and references */
        case HUNK_SYMBOL:
          if (s) {
            uint32 xtype,n;
            char *xname;

            nextword32(&hi);
            while (xtype = testword32(&hi)) {
              xname = gethunkname(&hi);
              switch (xtype >>= 24) {

                /* Symbol Definitions */
                case EXT_SYMB:  /* local symbol definition (for debugging) */
                  create_xdef(gv,s,xname,nextword32(&hi),
                              SYM_RELOC,SYMB_LOCAL);
                  break;
                case EXT_DEF:  /* global addr. symbol def. (requires reloc) */
                  create_xdef(gv,s,xname,nextword32(&hi),
                              SYM_RELOC,SYMB_GLOBAL);
                  break;
                case EXT_ABS:  /* global def. of absolute symbol */
                  create_xdef(gv,s,xname,nextword32(&hi),
                              SYM_ABS,SYMB_GLOBAL);
                  break;
                case EXT_RES:  /* unsupported type, invalid since OS2.0 */
                  error(18,lf->pathname,xname,u->objname,EXT_RES);
                  break;

                /* Unresolved Symbol References */
                case EXT_ABSREF32:
                  create_xrefs(gv,&hi,s,xname,R_ADDR32,4);
                  break;
                case EXT_ABSREF16:
                  create_xrefs(gv,&hi,s,xname,R_ADDR16,2);
                  break;
                case EXT_ABSREF8:
                  create_xrefs(gv,&hi,s,xname,R_ADDR8,1);
                  break;
                case EXT_RELREF32:
                  create_xrefs(gv,&hi,s,xname,R_REL32,4);
                  break;
                case EXT_RELREF26: /* EHF */
                  create_xrefs(gv,&hi,s,xname,R_REL26,4);
                  break;
                case EXT_RELREF16:
                  create_xrefs(gv,&hi,s,xname,R_REL16,2);
                  break;
                case EXT_RELREF8:
                  create_xrefs(gv,&hi,s,xname,R_REL8,1);
                  break;
                case EXT_DEXT32:
                  create_xrefs(gv,&hi,s,xname,R_BASEREL32,4);
                  break;
                case EXT_DEXT16:
                  create_xrefs(gv,&hi,s,xname,R_BASEREL16,2);
                  break;
                case EXT_DEXT8:
                  create_xrefs(gv,&hi,s,xname,R_BASEREL8,1);
                  break;
                case EXT_ABSCOMMON:
                  /* ABSCOMMON was never used and only supported */
                  /* by the VERY old ALink linker on AmigaOS. */
                  /* But I like it... :) */
                  create_xdef(gv,s,xname,nextword32(&hi),
                              SYM_COMMON,SYMB_GLOBAL);
                  create_xrefs(gv,&hi,s,xname,R_ADDR32,4);
                  break;
                case EXT_RELCOMMON:
                  /* RELCOMMON was introduced in OS3.1 and is */
                  /* neither used nor supported by any known software. */
                  /* It makes no sense for me too... */
                  error(20,lf->pathname,xname,u->objname,xtype>>24);
                  break;

                default:  /* unsupported HUNK_EXT sub type */
                  if (xtype & 0x80000000)
                    error(20,lf->pathname,xname,u->objname,xtype>>24);
                  else
                    error(18,lf->pathname,xname,u->objname,xtype>>24);
              }
            }
            nextword32(&hi);
          }
          else
            error(17,lf->pathname,(w==HUNK_EXT) ? "HUNK_EXT" : "HUNK_SYMBOL",
                  u ? u->objname : lf->filename);
          break;

        case HUNK_DEBUG:
          /* @@@@ currently ignored - will be implemented later */
          skiphunk(&hi);
          break;

        case HUNK_OVERLAY:
        case HUNK_BREAK:
          ierror("readconv(): HUNK_OVERLAY/BREAK not yet supported");
          break;

        case HUNK_END:
          nextword32(&hi);
          if (s) {
            addtail(&u->sections,&s->n);  /* add Section to ObjectUnit */
            s = NULL;
            secname = NULL;
          }
          else
            error(17,lf->pathname,"HUNK_END",u ? u->objname : lf->filename);
          break;

        default:
          error(13,lf->pathname);  /* File format corrupted */
          break;
      }
    }
    add_objunit(gv,u,TRUE);  /* add last one to glob. ObjUnit list */
    u = NULL;
  }
}


static unsigned long secbase(struct GlobalVars *gv,char *name)
/* return default base address for a section with this name */
{
  return (0);  /* base address has no meaning for ados/ehf */
}


static uint8 cmpsecflags(uint8 oldflags,uint8 newflags)
/* compare and verify target-specific section flags, */
/* return 0xff if sections are incompatible, otherwise return new flags */
{
  if (oldflags&SF_EHFPPC != newflags&SF_EHFPPC)
    return (0xff);
  if ((oldflags&(SF_CHIP|SF_FAST))==0 ||
      (newflags&(SF_CHIP|SF_FAST))==0 ||
      (oldflags&(SF_CHIP|SF_FAST)) == (newflags&(SF_CHIP|SF_FAST)))
    return (newflags | (oldflags&(SF_CHIP|SF_FAST)));
  return (0xff);
}


static struct Section *bssdefault(struct ObjectUnit *ou)
/* Create a default BSS section for ADOS/EHF */
{
  static char *bssname = BSSDEFAULTNAME;
  struct Section *s = create_section(ou,bssname,NULL,0);

  s->flags |= SF_UNINITIALIZED;
  s->type = ST_UDATA;
  s->protection = SP_READ | SP_WRITE;
  return (s);
}


static int targetlink(struct GlobalVars *gv,struct LinkedSection *ls,
                      struct Section *s)
/* returns 1, if target requires the combination of the two sections, */
/* returns -1, if target don't want to combine them, */
/* returns 0, if target doesn't care - standard linking rules are used. */
{
  static char *merged = "__MERGED";

  /* AmigaDOS doesn't merge unnamed sections, unless the Small- */
  /* Data/Code option was set */
  if (!(gv->small_code && s->type==ST_CODE) &&
      !(gv->small_data && (s->type==ST_DATA || s->type==ST_UDATA)))
    if (*(ls->name)==0 && *(s->name)==0)
      return (-1);

  /* sections with name "_NOMERGE" are never combined */
  if (!strcmp(s->name,"_NOMERGE"))
    return (-1);

  /* Executable only: */
  if (!gv->dest_object) {
    if (!strcmp(ls->name,merged) && !strcmp(s->name,merged)) {
      /* data and bss section with name __MERGED are always combined */
      if (s->type == ST_CODE)
        error(57,getobjname(s->obj)); /* Merging code section "__MERGED" */
      return (1);
    }
  }

  return (0);
}


static struct Symbol *ados_lnksym(struct GlobalVars *gv,struct Section *sec,
                                  struct XReference *xref)
/* Checks, if undefined symbol is an ADOS-linker symbol */
{
  struct Symbol *sym;
  int i;

  if (sym = findlnksymbol(&fff_amigaos,xref->name))
    return (sym);

  if (!gv->dest_object) {
    for (i=0; i<=LAST_LNKSYM; i++) {
      if (!strcmp(ados_symnames[i],xref->name)) {
        sym = addlnksymbol(&fff_amigaos,NULL,ados_symnames[i],0,SYM_ABS,
                           SYMF_LNKSYM,SYMI_OBJECT,SYMB_GLOBAL,0);
        sym->extra = i;  /* for easy identification in ados_setlnksym */
        if (i==PHXDB || i==SASPT || i==SASBB || i==DICDB)
          sym->type = SYM_RELOC;
        return (sym);  /* new linker symbol created */
      }
    }
  }
  return (NULL);
}


static void ados_setlnksym(struct GlobalVars *gv,struct Symbol *xdef,
                           struct XReference *xref)
/* Initialize ADOS linker symbol structure during resolve_xref() */
{
  if (xdef->flags & SYMF_LNKSYM) {
    struct LinkedSection *sdsec = smalldata_section(gv);
    struct Symbol *s;

    xdef->relsect = (struct Section *)sdsec->sections.first;
    switch (xdef->extra) {
      case PHXDL:
      case SASBB:
      case DICDL:
        xdef->value = sdsec->filesize;
        break;
      case PHXBL:
      case SASBL:
      case DICBL:
        xdef->value = sdsec->size - sdsec->filesize;
        break;
      case SASPT:
        xdef->value = fff_amigaos.baseoff;
        break;
      case SASCT:
      case SASDT:
        if (s = findsymbol(gv,ados_symnames[xdef->extra])) {
          /* a __ctors or __dtors is really defined: */
          xdef->value = s->value;
          xdef->relsect = s->relsect;
          xdef->type = s->type;
          xdef->flags = s->flags;
          xdef->info = s->info;
          xdef->bind = s->bind;
          xdef->size = s->size;
        }
        break;
    }
    xdef->flags &= ~SYMF_LNKSYM;  /* do not init again */
  }
}


static struct Symbol *ehf_lnksym(struct GlobalVars *gv,struct Section *sec,
                                 struct XReference *xref)
/* Checks, if undefined symbol is an EHF-linker symbol */
{
  struct Symbol *sym;

#if 0
/*@@@ findlnksymbol is not used, because @__xxx are real symbols */
  if (sym = findlnksymbol(&fff_ehf,xref->name))
    return (sym);
#endif

  if (!gv->dest_object) {

    if (!strncmp(ehf_addrsym,xref->name,sizeof(ehf_addrsym)-1)) {
      char *symname = xref->name + (sizeof(ehf_addrsym) - 1);

      if (findsymbol(gv,symname)) {
        /* Reference to an unknown symbol, which starts with "@_". */
        /* The symbol without "@_" has to exist. */
        char *objname = alloc(strlen(symname)+3);
        uint8 *dat = alloczero(sizeof(uint32));  /* space for a 32-bit ptr */
        struct ObjectUnit *ou;
        struct Section *s;
        struct Symbol *sym;

        /* create an artificial object with a ".tocd" data section */
        sprintf(objname,"%s.o",symname);
        ou = art_objunit(objname,dat,sizeof(uint32),gv->dest_format);
        s = create_section(ou,".tocd",dat,sizeof(uint32));
        s->type = ST_DATA;
        s->protection = SP_READ | SP_WRITE;
        addtail(&ou->sections,&s->n);

        /* "@__name" contains a 32-bit reloc pointer, which has an */
        /* external reference to the symbol "_name". */
        addxref(gv,s,symname,0,R_ADDR32,sizeof(uint32),0);

        /* make "@__name" visible for the further linking process: */
        if (addsymbol(gv,s,xref->name,0,SYM_RELOC,0,SYMI_OBJECT,
                      SYMB_GLOBAL,sizeof(uint32)))
          ierror("ehf_lnksym(): %s was assumed to be undefined, but "
                 "in reality it *is* defined",xref->name);

        if (!(sym = findsymbol(gv,xref->name)))
          ierror("ehf_lnksym(): The just defined symbol %s has "
                 "disappeared",xref->name);
        return (sym);
      }
    }
  }

  return (ados_lnksym(gv,sec,xref));
}


static void ehf_setlnksym(struct GlobalVars *gv,struct Symbol *xdef,
                          struct XReference *xref)
/* Initialize EHF linker symbol structure during resolve_xref() */
{
  ados_setlnksym(gv,xdef,xref);
}


static int get_xtors_pri(char *s)
/* Return priority of a constructor/destructor function name. */
/* Its priority may be specified by a number behind the 2nd underscore. */
/* Example: __INIT_9_OpenLibs (constructor with priority 9) */
{
  if (*s++ == '_')
    if (isdigit((unsigned)*s))
      return (atoi(s));
  return (0);
}


static void create_xdef(struct GlobalVars *gv,struct Section *s,
                        char *name,uint32 val,uint8 type,uint8 bind)
/* add new symbol to the symbols list of the current object unit */
{
  struct Symbol *sym;
  uint32 size = 0;

#ifdef DELUNDERSCORE
  if (*name == '_')
    ++name;  /* internal representation is without leading '_' */
#endif

  if (type == SYM_COMMON) {
    /* Oh, a common symbol? A rare guest. :) */
    size = val;  /* its size was stored in val */
    val = 2;     /* how about a 32-bit alignment? */
  }

  if (sym = addsymbol(gv,s,name,val,type,0,SYMI_NOTYPE,bind,size)) {
    /* Symbol already defined. If defined globally, ignore it. */
    /* Otherwise, change into a global definition. */
    if (sym->bind != SYMB_GLOBAL) {
      if (bind == SYMB_GLOBAL) {
        sym->value = val;  /* redefine as global (xdef) symbol */
        sym->relsect = s;
        sym->type = type;
        sym->flags = 0;
        sym->info = SYMI_NOTYPE;
        sym->bind = SYMB_GLOBAL;
        sym->size = size;
        addglobsym(gv,sym);
      }
    }
  }

  if (bind == SYMB_GLOBAL && !gv->dest_object) {
    /* symbol describes an automatic constructor/destructor function ? */
    if (!strncmp(vbcc_INIT,name,sizeof(vbcc_INIT)-1))
      new_priptr(s->obj,xtors_objname,ados_symnames[SASCT],
                 get_xtors_pri(name+(sizeof(vbcc_INIT)-1)),name,0);
    else if (!strncmp(vbcc_EXIT,name,sizeof(vbcc_EXIT)-1))
      new_priptr(s->obj,xtors_objname,ados_symnames[SASDT],
                 get_xtors_pri(name+(sizeof(vbcc_EXIT)-1)),name,0);
  }
}


static void create_xrefs(struct GlobalVars *gv,struct HunkInfo *hi,
                         struct Section *s,char *name,uint8 rtype,uint8 size)
/* add new unresolved symbol reference to the current section */
{
  uint32 n = nextword32(hi);  /* number of references */
  uint32 offs;

#ifdef DELUNDERSCORE
  if (*name == '_')
    ++name;  /* internal representation is without leading '_' */
#endif
  while (n--) {
    offs = nextword32(hi);
    addxref(gv,s,name,offs,rtype,size,readsection(gv,s->data+offs,rtype));
  }
}


static char *gethunkname(struct HunkInfo *hi)
/* read 32-bit aligned name, the most significant byte of the */
/* length specifier is ignored (for HUNK_EXT names, for example) */
{
  uint32 n;
  char *s=NULL;

  if (n = nextword32(hi) & 0xffffff) {  /* name given? */
    s = alloczero((n+1)<<2);
    strncpy(s,(char *)hi->hunkptr,n<<2);
    movehunkptr32(hi,n);
  }
  return (s);
}


static bool addlongrelocs(struct GlobalVars *gv,struct HunkInfo *hi,
                          struct Section *s,uint8 type)
/* convert an amigaos/ehf relocation hunk into an internal Reloc node */
{
  struct Reloc *r;
  uint32 n,id,offs;

  if (s) {
    nextword32(hi);
    while (n = nextword32(hi)) {
      id = nextword32(hi);  /* add base addr. of section with this index */
      while (n--) {
        offs = nextword32(hi);
        addreloc(s,NULL,id,offs,type,readsection(gv,s->data+offs,type));
      }
    }
    return (TRUE);
  }
  return (FALSE);
}


static bool addshortrelocs(struct GlobalVars *gv,struct HunkInfo *hi,
                           struct Section *s,uint8 type)
/* convert an amigaos/ehf relocation hunk into an internal Reloc node */
{
  struct Reloc *r;
  uint32 n,id,offs;

  if (s) {
    nextword32(hi);
    while (n = (uint32)nextword16(hi)) {
      id = (uint32)nextword16(hi); /* add base addr. of sec. with this idx. */
      while (n--) {
        offs = (uint32)nextword16(hi);
        addreloc(s,NULL,id,offs,type,readsection(gv,s->data+offs,type));
      }
    }
    alignhunkptr(hi);
    return (TRUE);
  }
  return (FALSE);
}


static void init_hunkinfo(struct HunkInfo *hi,char *name,uint8 *p,
                          unsigned long plen)
{
  hi->hunkptr = hi->hunkbase = p;
  hi->hunkcnt = (long)plen;
  hi->filename = name;
  hi->exec = read32be(p) == HUNK_HEADER;  /* executable file? */
}


static uint32 skiphunk(struct HunkInfo *hi)
/* Skip the hunk where hunkptr points to. */
/* Return first word of next hunk. */
{
  uint32 type,n;

  switch (type = read32be(hi->hunkptr) & 0x3fffffff) {
    case HUNK_UNIT:
    case HUNK_NAME:
    case HUNK_CODE:
    case HUNK_DATA:
    case HUNK_DEBUG:
    case HUNK_LIB:
    case HUNK_INDEX:
    case HUNK_PPC_CODE: /* EHF */
      movehunkptr32(hi,read32be(hi->hunkptr+4)+2);
      break;

    case HUNK_BSS:
      movehunkptr32(hi,2);  /* skip size specifier */
      break;

    case HUNK_ABSRELOC32:
    case HUNK_RELRELOC16:
    case HUNK_RELRELOC8:
    case HUNK_RELRELOC32:
    case HUNK_ABSRELOC16:
    case HUNK_DREL32:
    case HUNK_DREL16:
    case HUNK_DREL8:
    case HUNK_RELRELOC26: /* EHF */
      if (!(type==HUNK_DREL32 && hi->exec)) {
        nextword32(hi);
        while (n = nextword32(hi))
          movehunkptr32(hi,n+1);
        break;
      }
      /* else, fall through to HUNK_RELOC32SHORT */

    case HUNK_RELOC32SHORT:
      nextword32(hi);
      while (n = (uint32)nextword16(hi))
        movehunkptr16(hi,n+1);
      alignhunkptr(hi);
      break;

    case HUNK_EXT:
    case HUNK_SYMBOL:
      nextword32(hi);
      while (n = nextword32(hi)) {
        if (n & 0x80000000) {  /* external reference */
          movehunkptr32(hi,n & 0xffffff);
          if ((n>>24)==EXT_ABSCOMMON || (n>>24)==EXT_RELCOMMON)
            nextword32(hi);  /* size of common area */
          movehunkptr32(hi,nextword32(hi));  /* skip reference offsets */
        }
        else
          movehunkptr32(hi,(n&0xffffff)+1);  /* skip xdef-value */
      }
      break;

    case HUNK_HEADER:
      nextword32(hi);
      movehunkptr32(hi,nextword32(hi)+2);
      n = nextword32(hi);  /* first root hunk index */
      n = nextword32(hi) - n + 1;  /* number of size specifiers */
      while (n--) {
        if ((nextword32(hi) & 0xc0000000) == 0xc0000000)
        nextword32(hi);
      }
      break;

    case HUNK_OVERLAY:
      movehunkptr32(hi,read32be(hi->hunkptr+4)+3); /* @@@ I'm not sure...*/
      break;

    case HUNK_END:
    case HUNK_BREAK:
      nextword32(hi);
      break;

    default:
      error(13,hi->filename);  /* File format corrupted */
      break;
  }

  /* return next 32-bit word */
  return (testword32(hi));
}


static void movehunkptr32(struct HunkInfo *hi,uint32 n)
/* skip n 32-bit words */
{
  long offset = (long)n << 2;

  if ((hi->hunkcnt -= offset) < 0)
    error(13,hi->filename);  /* File format corrupted */
  hi->hunkptr += offset;
}


static void movehunkptr16(struct HunkInfo *hi,uint32 n)
/* skip n 16-bit words */
{
  long offset = (long)n << 1;

  if ((hi->hunkcnt -= offset) < 0)
    error(13,hi->filename);  /* File format corrupted */
  hi->hunkptr += offset;
}


static void alignhunkptr(struct HunkInfo *hi)
/* make hunk pointer 32-bit aligned */
{
  if ((hi->hunkptr - hi->hunkbase) & 2)
    movehunkptr16(hi,1);
}


static uint32 testword32(struct HunkInfo *hi)
/* try to read next 32-bit word - return 0, if end of file reached */
{
  if (hi->hunkcnt >= sizeof(uint32))
    return (read32be(hi->hunkptr));
  else
    return (0);
}


static uint32 nextword32(struct HunkInfo *hi)
/* read next 32-bit word */
{
  uint32 w;

  if ((hi->hunkcnt -= sizeof(uint32)) < 0)
    error(13,hi->filename);  /* File format corrupted */
  w = read32be(hi->hunkptr);
  hi->hunkptr += sizeof(uint32);
  return (w);
}


static uint16 nextword16(struct HunkInfo *hi)
/* read next 16-bit word */
{
  uint16 w;

  if ((hi->hunkcnt -= sizeof(uint16)) < 0)
    error(13,hi->filename);  /* File format corrupted */
  w = read16be(hi->hunkptr);
  hi->hunkptr += sizeof(uint16);
  return (w);
}


/*****************************************************************/
/*                     Write ADOS / EHF                          */
/*****************************************************************/


static void writeshared(struct GlobalVars *gv,FILE *f)
{
  error(30);  /* Target file format doesn't support shared objects */
}


static void writeobject(struct GlobalVars *gv,FILE *f)
/* creates a target-amigaos relocatable object file */
{
  struct LinkedSection *ls = (struct LinkedSection *)gv->lnksec.first;
  struct LinkedSection *nextls;

  fwrite32be(f,HUNK_UNIT);
  hunk_name_len(f,gv->dest_name);  /* unit name is output file name */

  if (ls->n.next == NULL) {
    /* special case: no sections, create dummy section */
    fwrite32be(f,HUNK_CODE);
    fwrite32be(f,0);
    fwrite32be(f,HUNK_END);
    return;
  }

  /* section loop */
  while (nextls = (struct LinkedSection *)ls->n.next) {
    uint32 memflags = (ls->flags&(SF_FAST|SF_CHIP))<<24;

    exthunk = symhunk = FALSE;
    fwrite32be(f,HUNK_NAME);
    hunk_name_len(f,ls->name);  /* section name */

    switch (ls->type) {  /* section type */
      case ST_CODE:
        if (ls->flags & SF_EHFPPC)
          fwrite32be(f,HUNK_PPC_CODE|memflags);
        else
          fwrite32be(f,HUNK_CODE|memflags);
        break;
      case ST_DATA:
        fwrite32be(f,HUNK_DATA|memflags);
        break;
      case ST_UDATA:
        fwrite32be(f,HUNK_BSS|memflags);
        break;
      default:
        ierror("writeobject(): Illegal section type %d",ls->type);
        break;
    }

    fwrite32be(f,(ls->size+3)>>2);  /* section size */
    if (!(ls->flags & SF_UNINITIALIZED)) {
      fwritex(f,ls->data,ls->size);  /* write section contents */
      fwrite_align(f,2,ls->size);
    }

    /* relocation hunks */
    reloc_hunk(f,ls,gv,R_ADDR32,HUNK_ABSRELOC32);
    if (ls->flags & SF_EHFPPC)
      reloc_hunk(f,ls,gv,R_REL26,HUNK_RELRELOC26);
    reloc_hunk(f,ls,gv,R_REL14,HUNK_RELRELOC16);
    reloc_hunk(f,ls,gv,R_REL14_BRTAKEN,HUNK_RELRELOC16);
    reloc_hunk(f,ls,gv,R_REL14_BRNTAKEN,HUNK_RELRELOC16);
    reloc_hunk(f,ls,gv,R_REL32,HUNK_RELRELOC32);
    reloc_hunk(f,ls,gv,R_BASEREL16,HUNK_DREL16);
    unsupp_relocs(ls);  /* print unsupported relocations */

    /* external references and global definitions */
    ext_refs(f,ls);
    ext_defs(f,ls,SYMB_GLOBAL,SYM_RELOC,EXT_DEF);
    ext_defs(f,ls,SYMB_GLOBAL,SYM_ABS,EXT_ABS);
    if (exthunk)
      fwrite32be(f,0);  /* close HUNK_EXT block, if required */

    if (!gv->strip_symbols) {
      /* symbol table */
      ext_defs(f,ls,SYMB_LOCAL,SYM_RELOC,EXT_SYMB);
      ext_defs(f,ls,SYMB_LOCAL,SYM_ABS,EXT_IGNORE);
      if (symhunk)
        fwrite32be(f,0);  /* close HUNK_SYMBOL block, if required */
      unsupp_symbols(ls);  /* print unsupported symbol definitions */
    }

    fwrite32be(f,HUNK_END);  /* end of this section */
    ls = nextls;
  }
}


static void writeexec(struct GlobalVars *gv,FILE *f)
/* creates a target-amigaos executable file (which is relocatable too) */
{
  struct LinkedSection *ls = (struct LinkedSection *)gv->lnksec.first;
  struct LinkedSection *nextls;
  int i=0;

  fwrite32be(f,HUNK_HEADER);
  fwrite32be(f,0);  /* resident libraries no longer supp. since OS2.0 */

  if (ls->n.next == NULL) {
    /* special case: no sections, create dummy section */
    fwrite32be(f,1);
    fwrite32be(f,0);
    fwrite32be(f,0);
    fwrite32be(f,0);
    fwrite32be(f,HUNK_CODE);
    fwrite32be(f,0);
    fwrite32be(f,HUNK_END);
    return;
  }

  fwrite32be(f,gv->nsecs);  /* number of sections - no overlay support! @@@ */
  fwrite32be(f,0);
  fwrite32be(f,gv->nsecs-1);

  /* write section size specifiers */
  while (nextls = (struct LinkedSection *)ls->n.next) {
    fwrite32be(f,((ls->flags&(SF_FAST|SF_CHIP))<<24) | ((ls->size+3)>>2));
    ls = nextls;
    i++;
  }
  if (i != gv->nsecs)
    ierror("writeexec(): %d sections in list, but it should be %d",
           i,gv->nsecs);

  /* section loop */
  ls = (struct LinkedSection *)gv->lnksec.first;
  while (nextls = (struct LinkedSection *)ls->n.next) {
    exthunk = symhunk = FALSE;

    switch (ls->type) {  /* section type */
      case ST_CODE:
        fwrite32be(f,HUNK_CODE);
        break;
      case ST_DATA:
        fwrite32be(f,HUNK_DATA);
        break;
      case ST_UDATA:
        fwrite32be(f,HUNK_BSS);
        break;
      default:
        ierror("writeexec(): Illegal section type %d",ls->type);
        break;
    }

    if (ls->flags & SF_UNINITIALIZED) {
      fwrite32be(f,(ls->size+3)>>2);  /* bss - size only */
    }
    else {
      fwrite32be(f,(ls->filesize+3)>>2);  /* initialized section size */
      fwritex(f,ls->data,ls->filesize);   /* write section contents */
      fwrite_align(f,2,ls->filesize);
    }

    /* relocation hunks */
    reloc_hunk(f,ls,gv,R_ADDR32,HUNK_ABSRELOC32);
    unsupp_relocs(ls);  /* print unsupported relocations */

    if (!gv->strip_symbols) {
      /* symbol table */
      ext_defs(f,ls,SYMB_GLOBAL,SYM_RELOC,EXT_SYMB);
      ext_defs(f,ls,SYMB_GLOBAL,SYM_ABS,EXT_IGNORE);
      ext_defs(f,ls,SYMB_LOCAL,SYM_RELOC,EXT_SYMB);
      ext_defs(f,ls,SYMB_LOCAL,SYM_ABS,EXT_IGNORE);
      if (symhunk)
        fwrite32be(f,0);  /* close HUNK_SYMBOL block, if required */
      unsupp_symbols(ls);  /* print unsupported symbol definitions */
    }

    fwrite32be(f,HUNK_END);  /* end of this section */
    ls = nextls;
  }
}


static void hunk_name_len(FILE *f,char *name)
/* writes a string in hunk-format style, i.e. first longword contains */
/* strlen in longwords and then follows the string itself, long-aligned */
{
  size_t l=strlen(name);

  fwrite32be(f,l?((l+3)>>2):0);
  fwritex(f,name,l);
  fwrite_align(f,2,l);
}


static void hunk_name(FILE *f,char *name)
/* writes a longword-aligned string, like hunk_name_len(), but */
/* without writing the length */
{
  size_t l=strlen(name);

  fwritex(f,name,l);
  fwrite_align(f,2,l);
}


static int strlen32(char *s)
/* strlen in 32-bit words */
{
  int l=strlen(s);

  return (l?((l+3)>>2):0);
}


static void reloc_hunk(FILE *f,struct LinkedSection *sec,
                       struct GlobalVars *gv,uint8 r,uint32 relhunk)
/* generate an EHF relocation hunk for a specific reloc type */
{
  struct Reloc *nextrel,*rel=(struct Reloc *)sec->relocs.first;
  struct list **rlist=alloc(gv->nsecs*sizeof(struct list *));
  int *rcnt=alloczero(gv->nsecs*sizeof(int)); /* reloc cnt for all sect. */
  bool hunk_required=FALSE;
  int i;

  for (i=0; i<gv->nsecs; i++) {  /* empty reloc lists for each section */
    rlist[i] = alloc(sizeof(struct list));
    initlist(rlist[i]);
  }

  while (nextrel = (struct Reloc *)rel->n.next) {
    if (rel->type == r) {
      /* move reloc node of correct type into relocssect's rlist */
      remnode(&rel->n);
      addtail(rlist[rel->relocsect.lnk->index],&rel->n);
      rcnt[rel->relocsect.lnk->index]++;
      hunk_required = TRUE;
    }
    rel = nextrel;
  }

  if (hunk_required) {  /* there's at least one relocation */
    fwrite32be(f,relhunk);  /* reloc hunk id */
    for (i=0; i<gv->nsecs; i++) {
      if (rcnt[i]) {
        fwrite32be(f,(uint32)rcnt[i]);  /* number of relocations */
        fwrite32be(f,(uint32)i);  /* section index */

        /* store relocation offsets */
        while(rel = (struct Reloc *)remhead(rlist[i])) {
          fwrite32be(f,(uint32)rel->offset);
#ifndef FASTALLOC
          free(rel);
#endif
        }
      }
    }
    fwrite32be(f,0);  /* no more relocation entries */
  }

#ifndef FASTALLOC
  /* free dynamically allocated rlists and rcnt array */
  for (i=0; i<gv->nsecs; free(rlist[i++]));
  free(rcnt);
#endif
}


static void unsupp_relocs(struct LinkedSection *sec)
{
  struct Reloc *rel;

  while (rel = (struct Reloc *)remhead(&sec->relocs)) {
    error(32,fff_amigaos.tname,reloc_name[rel->type],sec->name,
          rel->offset);
#ifndef FASTALLOC
    free(rel);
#endif
  }
}


static void ext_refs(FILE *f,struct LinkedSection *sec)
{
  struct list xnodelist;  /* xrefs with same ref. type and symbol name */
  struct XRefNode *xn,*nextxn;
  struct XReference *xref;

  initlist(&xnodelist);
  while (xref = (struct XReference *)remhead(&sec->xrefs)) {
    char *name = xref->name;  /* name of xref'ed symbol */
    uint8 rtype = xref->type;

    /* reference type mapping */
    switch (rtype) {
      case R_ADDR32:
        rtype = EXT_ABSREF32;
        break;
      case R_REL14:
      case R_REL14_BRTAKEN:
      case R_REL14_BRNTAKEN:
      case R_ADDR16:
        rtype = EXT_RELREF16;
        break;
      case R_BASEREL16:
        rtype = EXT_DEXT16;  /* small data */
        break;
      case R_REL26:
        rtype = EXT_RELREF26;
        break;
      default:
        error(32,fff_amigaos.tname,reloc_name[xref->type],sec->name,
              xref->offset);
        rtype = EXT_RELREF8;  /* @@@ to keep the loop running */
        break;
    }

    /* search appropriate XRefNode for referenced symbol and type */
    xn = (struct XRefNode *)xnodelist.first;
    while (nextxn = (struct XRefNode *)xn->n.next) {
      if (!strcmp(name,xn->sym_name) && rtype==xn->ref_type)
        break;
      xn = nextxn;
    }

    if (nextxn==NULL) {  /* we have to create a new XRefNode? */
      xn = alloc(sizeof(struct XRefNode));
      xn->sym_name = name;
      xn->ref_type = rtype;
      xn->noffsets = 0;
      initlist(&xn->xreflist);
      addtail(&xnodelist,&xn->n);
    }

    /* add new offset to xreflist for same ref. type and symbol name */
    addtail(&xn->xreflist,&xref->n);
    xn->noffsets++;
  }

  if (xnodelist.first->next) {  /* at least one reference in this section? */
    if (!exthunk) {
      exthunk = TRUE;
      fwrite32be(f,HUNK_EXT);
    }
    while (xn = (struct XRefNode *)remhead(&xnodelist)) {
      fwrite32be(f,((uint32)xn->ref_type << 24) | strlen32(xn->sym_name));
      hunk_name(f,xn->sym_name);  /* symbol's name */
      fwrite32be(f,(uint32)xn->noffsets);  /* number of references */
      while (xref = (struct XReference *)remhead(&xn->xreflist)) {
        fwrite32be(f,(uint32)xref->offset);  /* offset */
#ifndef FASTALLOC
        free(xref);
#endif
      }
#ifndef FASTALLOC
      free(xn);
#endif
    }
  }
}


static void ext_defs(FILE *f,struct LinkedSection *sec,uint8 bind,
                     uint8 stype,uint32 xdeftype)
{
  struct Symbol *nextsym,*sym=(struct Symbol *)sec->symbols.first;
  bool xdefs = FALSE;
  int i;

  while (nextsym = (struct Symbol *)sym->n.next) {
    if (sym->type==stype && sym->bind==bind && sym->info<=SYMI_FUNC) {
      remnode(&sym->n);

      if (xdeftype != EXT_IGNORE) {
        if (!xdefs) {
          xdefs = TRUE;
          if (xdeftype == EXT_SYMB) {
            if (!symhunk) {
              symhunk = TRUE;
              fwrite32be(f,HUNK_SYMBOL);
            }
          }
          else {
            if (!exthunk) {
              exthunk = TRUE;
              fwrite32be(f,HUNK_EXT);
            }
          }
        }

        /* generate xdef or symbol table entry */
        fwrite32be(f,(xdeftype << 24) | strlen32(sym->name));
        hunk_name(f,sym->name);  /* symbol's name */
        fwrite32be(f,sym->value);  /* ... and its value */
#ifndef FASTALLOC
        free(sym);
#endif
      }
    }
    sym = nextsym;
  }
}


static void unsupp_symbols(struct LinkedSection *sec)
{
  struct Symbol *sym;

  while (sym = (struct Symbol *)remhead(&sec->symbols)) {
    error(33,fff_amigaos.tname,sym->name,sym_bind[sym->bind],
          sym_type[sym->type],sym_info[sym->info]);
#ifndef FASTALLOC
    free(sym);
#endif
  }
}


#endif
