/* $VER: vlink t_elf64.c V0.6a (19.12.98)
 *
 * 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.6a (19.12.98) phx
 *       Support for little endian object file formats.
 *       ** t_elf64.c still doesn't work - don't activate it! **
 * v0.6  (24.10.98) phx
 *       .sdata/.sbss and .sdata2/.sbss2 will always be combined.
 *       Each target defines their own base register section offsets.
 *       Linking with shared objects is possible. Creating them is
 *       a another story...
 *       ** t_elf64.c still doesn't work - don't activate it! **
 * v0.5d (22.08.98) phx
 *       Faster memory allocation can be activated by #define FASTALLOC.
 *       ** t_elf64.c still doesn't work - don't activate it! **
 * v0.5  (27.06.98) phx
 *       Target-specific linker symbol support: elf64_lnksym(),
 *       elf64_setlnksym().
 *       ** t_elf64.c still doesn't work - don't activate it! **
 * v0.4  (05.06.98) phx
 *       New FFF targetlink(). Currently no meaning for ELF.
 *       ** t_elf64.c still doesn't work - don't activate it! **
 * v0.0  (25.04.98) phx
 *       File created.
 */


#if defined(ELF64_ALPHA)
#define T_ELF64_C
#include "vlink.h"
#include "elf64.h"
#if defined(ELF64_ALPHA)
#include "rel_elfalpha.h"
#endif

#define ELF_VER 1


#ifdef ELF64_ALPHA
static int alpha64_identify(char*,uint8 *,unsigned long);
static void alpha64_readconv(struct GlobalVars *,struct LinkFile *);
static void alpha64_readELF(struct GlobalVars *,struct LinkFile *,uint8 *);
static unsigned long alpha64_secbase(struct GlobalVars *,char *);
static uint8 alpha64_cmpsecflags(uint8,uint8);
static struct Section *alpha64_bssdefault(struct ObjectUnit *);
static int alpha64_targetlink(struct GlobalVars *,struct LinkedSection *,
                              struct Section *);
static struct Symbol *alpha64_lnksym(struct GlobalVars *,struct Section *,
                                     struct XReference *);
static void alpha64_setlnksym(struct GlobalVars *,struct Symbol *,
                              struct XReference *);
static void alpha64_relocs(struct GlobalVars *,uint8 *,struct ObjectUnit *,
                           struct Elf64_Shdr *);
static void alpha64_writeobject(struct GlobalVars *,FILE *);
static void alpha64_writeshared(struct GlobalVars *,FILE *);
static void alpha64_writeexec(struct GlobalVars *,FILE *);
static uint8 alpha64_getrel(uint8,char *,uint32);

struct FFFuncs fff_elf64alpha = {
  "elf64alpha",
  alpha64_identify,
  alpha64_readconv,
  alpha64_secbase,
  alpha64_cmpsecflags,
  alpha64_bssdefault,
  alpha64_targetlink,
  alpha64_lnksym,
  alpha64_setlnksym,
  alpha64_writeobject,
  alpha64_writeshared,
  alpha64_writeexec,
  0x7ff0,  /*@@@ I have no information */
  NULL,
  0 /* little endian */
};
#endif

/* small data sections */
static char *sdata = ".sdata";
static char *sbss = ".sbss";
static char *sdata2 = ".sdata2";
static char *sbss2 = ".sbss2";


static int elf64le_identify(struct FFFuncs *,char *,struct Elf64_Ehdr *,
                            unsigned long,unsigned char,unsigned char,
                            uint16,uint32);
static void elf64le_check_ar_type(struct FFFuncs *,char *,
                                  struct Elf64_Ehdr *,unsigned char,
                                  unsigned char,uint32,uint16,uint16,uint16);
static char *elf64le_shstrtab(struct LinkFile *,struct Elf64_Ehdr *);
static char *elf64le_strtab(struct LinkFile *,struct Elf64_Ehdr *,int);
static struct Elf64_Sym *elf64le_symtab(struct LinkFile *,
                                        struct Elf64_Ehdr *,int);
static struct Elf64_Shdr *elf64le_shdr(struct LinkFile *lf,
                                       struct Elf64_Ehdr *,uint16);
static void elf64le_section(uint8 *,struct ObjectUnit *,
                            struct Elf64_Shdr *,int);
static void elf64le_symbols(struct GlobalVars *,uint8 *,struct ObjectUnit *,
                            struct Elf64_Shdr *);
static void elf64le_reloc(struct GlobalVars *,struct Elf64_Ehdr *,
                          struct Section *,uint32,struct Elf64_Rela *,
                          bool,uint8);
static struct Section *elf64le_bssdefault(struct ObjectUnit *);
static int elf64_targetlink(struct GlobalVars *,struct LinkedSection *,
                            struct Section *);
static struct Symbol *elf64_lnksym(struct GlobalVars *,struct Section *,
                                   struct XReference *);
static void elf64_setlnksym(struct GlobalVars *,struct Symbol *,
                            struct XReference *);
static void elf64le_header(FILE *,uint16,uint16,uint32,uint32,uint32,
                           uint32,uint16,uint16,uint16);
static void elf64le_writeshdrs(FILE *,uint32,uint32);
static void elf64le_stdsymtab(struct GlobalVars *,uint8,uint8);
static void elf64le_addsymlist(struct GlobalVars *,struct SymTabList *,
                               uint8,uint8);
static void elf64le_makeshdrs(struct GlobalVars *);
static void elf64le_addrelocs(struct GlobalVars *,
                              uint8 (*)(uint8,char *,uint32));
static void elf64le_makeshstrtab(void);
static void elf64le_makestrtab(void);
static void elf64le_makesymtab(uint32);
static struct ShdrNode *elf64le_addshdr(uint32,uint32,uint32,uint32,uint32,
                                        uint32,uint32,uint32,uint32,uint32);
static uint32 elf64le_addsym(struct SymTabList *,char *,uint32,uint32,
                             uint8,uint8,uint16);
static uint32 elf64le_findsym(struct SymTabList *,char *,uint16);
static void elf64le_addrela(uint32,int32,uint32,uint8);

static void elf64_initlists(void);
static struct ShdrNode *elf64_newshdr(void);
static uint32 elf64_addshdrstr(char *);
static uint32 elf64_addstr(char *);
static uint32 elf64_addstrlist(struct StrTabList *,char *);
static uint8 elf64_getinfo(struct Symbol *);
static uint8 elf64_getbind(struct Symbol *);
static uint16 elf64_getshndx(struct Symbol *,uint8);
static void elf64_writesections(struct GlobalVars *,FILE *);
static void elf64_writestrtab(FILE *,struct StrTabList *);
static void elf64_writesymtab(FILE *,struct SymTabList *);
static void elf64_writerelocs(struct GlobalVars *,FILE *);


static char ELFid[4] = {   /* identification for all ELF files */
  0x7f,'E','L','F'
};

static struct ar_info ai;  /* for scanning library archives */
static char *shstrtab;     /* section header string table */
static char *nullstr = "";

/* static data required for output file generation */
static struct list shdrlist;
static struct list phdrlist;
static struct list relalist;
static struct SymTabList symlist;
static struct StrTabList shstrlist;
static struct StrTabList stringlist;
static uint32 shdrindex;
static uint32 symtabidx,shstrtabidx,strtabidx; /* indexes of Shdr names */
static uint32 elfoffset;    /* current ELF file offset */
static int secsyms;         /* offset to find section symbols by shndx */



/*****************************************************************/
/*                          Read ELF                             */
/*****************************************************************/


#ifdef ELF64_ALPHA

static int alpha64_identify(char *name,uint8 *p,unsigned long plen)
/* identify ELF-Alpha-64Bit-LittleEndian */
{
  return (elf64le_identify(&fff_elf64alpha,name,(struct Elf64_Ehdr *)p,plen,
                           ELFCLASS64,ELFDATA2LSB,EM_ALPHA,ELF_VER));
}


static void alpha64_readconv(struct GlobalVars *gv,struct LinkFile *lf)
/* Read elf64alpha executable / object / shared obj. */
{
  if (lf->type == ID_LIBARCH) {
    if (ar_init(&ai,(char *)lf->data,lf->length,lf->filename)) {
      while (ar_extract(&ai)) {
        lf->objname = allocstring(ai.name);
        alpha64_readELF(gv,lf,ai.data);
      }
    }
    else
      ierror("alpha64_readconv(): archive %s corrupted since last access",
             lf->pathname);
  }
  else {
    lf->objname = lf->filename;
    alpha64_readELF(gv,lf,lf->data);
  }
}


static void alpha64_readELF(struct GlobalVars *gv,struct LinkFile *lf,
                            uint8 *elf)
{
  struct ObjectUnit *u;
  struct Elf64_Ehdr *ehdr = (struct Elf64_Ehdr *)elf;
  struct Elf64_Shdr *shdr;
  uint16 i,num_shdr;

  if (lf->type == ID_LIBARCH)  /* check ar-member for correct format */
    elf64le_check_ar_type(&fff_elf64alpha,lf->pathname,ehdr,ELFCLASS64,
                          ELFDATA2LSB,ELF_VER,EM_ALPHA,EM_NONE,EM_NONE);

  shstrtab = elf64le_shstrtab(lf,ehdr);
  u = create_objunit(lf,lf->objname);

  switch (read16le(ehdr->e_type)) {

    case ET_REL:  /* relocatable object file */
      if (read16le(ehdr->e_phnum) > 0)
        error(47,lf->pathname,lf->objname);  /* ignoring program hdr. tab */
      num_shdr = read16le(ehdr->e_shnum);

      /* create vlink sections */
      for (i=1; i<num_shdr; i++) {
        shdr = elf64le_shdr(lf,ehdr,i);
        switch (read32le(shdr->sh_type)) {
          case SHT_PROGBITS:
          case SHT_NOBITS:
            elf64le_section(elf,u,shdr,i);  /* create a new section */
          default:
            break;
        }
      }

      /* parse the other section headers */
      for (i=1; i<num_shdr; i++) {
        shdr = elf64le_shdr(lf,ehdr,i);
        switch (read32le(shdr->sh_type)) {
          case SHT_NULL:
          case SHT_STRTAB:
          case SHT_PROGBITS:
          case SHT_NOBITS:
            break;
          case SHT_SYMTAB:
            elf64le_symbols(gv,elf,u,shdr);  /* symbol definitions */
            break;
          case SHT_REL:
          case SHT_RELA:
            alpha64_relocs(gv,elf,u,shdr);  /* relocation information */
            break;
          case SHT_NOTE:
            ierror("alpha64_readELF(): %s: Section header type %d is "
                   "currently not supported",lf->pathname,
                   (int)read32le(shdr->sh_type));
            break;
          default:
            /* section header type not needed in relocatable objects */
            error(48,lf->pathname,read32le(shdr->sh_type),lf->objname);
            break;
        }
      }
      break;

    case ET_DYN:  /* shared object file */
      num_shdr = read16le(ehdr->e_shnum);

      /* create vlink sections */
      for (i=1; i<num_shdr; i++) {
        shdr = elf64le_shdr(lf,ehdr,i);
        switch (read32le(shdr->sh_type)) {
          case SHT_PROGBITS:
          case SHT_NOBITS:
            elf64le_section(elf,u,shdr,i);  /* create a new section */
          default:
            break;
        }
      }

      /* parse the other section headers */
      for (i=1; i<num_shdr; i++) {
        shdr = elf64le_shdr(lf,ehdr,i);
        switch (read32le(shdr->sh_type)) {
          case SHT_NULL:
          case SHT_STRTAB:
          case SHT_PROGBITS:
          case SHT_NOBITS:
          case SHT_HASH:
          case SHT_DYNAMIC:
          case SHT_DYNSYM:
            break;
          case SHT_SYMTAB:
            elf64le_symbols(gv,elf,u,shdr);  /* symbol definitions */
            break;
          case SHT_NOTE:
            ierror("alpha64_readELF(): %s: Section header type %d is "
                   "currently not supported",lf->pathname,
                   (int)read32le(shdr->sh_type));
            break;
          default:
            /* section header type not needed in shared objects */
            error(60,lf->pathname,read32le(shdr->sh_type),lf->objname);
            break;
        }
      }
      break;

    case ET_EXEC: /* executable file */
      /* @@@ */
      ierror("alpha64_readELF(): %s: Executables are currently "
             "not supported",lf->pathname);
      break;

    default:
      error(41,lf->pathname,lf->objname);  /* illegal fmt./file corrupted */
      break;
  }

  /* add new object unit to the appropriate list */
  add_objunit(gv,u,FALSE);
}


static unsigned long alpha64_secbase(struct GlobalVars *gv,char *name)
/* return default base address for a section with this name */
{
  return (0);  /* @@@ has to be fixed... */
}


static uint8 alpha64_cmpsecflags(uint8 oldflags,uint8 newflags)
/* compare and verify target-specific section flags, */
/* return 0xff if sections are incompatible, otherwise return new flags */
{
  return (oldflags|newflags);
}


static struct Section *alpha64_bssdefault(struct ObjectUnit *ou)
/* Create a default BSS section for elfppc64be */
{
  return (elf64le_bssdefault(ou));
}


static int alpha64_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. */
{
  return (elf64_targetlink(gv,ls,s));
}


static struct Symbol *alpha64_lnksym(struct GlobalVars *gv,
                                     struct Section *sec,
                                     struct XReference *xref)
{
  return (elf64_lnksym(gv,sec,xref));
}


static void alpha64_setlnksym(struct GlobalVars *gv,struct Symbol *xdef,
                              struct XReference *xref)
{
  elf64_setlnksym(gv,xdef,xref);
}


/* @@@@ hier gehts weiter @@@@ */
static void alpha64_relocs(struct GlobalVars *gv,uint8 *elf,
                           struct ObjectUnit *ou,struct Elf64_Shdr *shdr)
{
  char *sec_name = shstrtab + read32le(shdr->sh_name);
  struct LinkFile *lf = ou->lnkfile;
  bool rela = read32le(shdr->sh_type) == SHT_RELA;
  uint8 rtype,*data = elf + read32le(shdr->sh_offset);
  unsigned long entsize = read32le(shdr->sh_entsize);
  int nrelocs = (int)(read32le(shdr->sh_size) / (uint32)entsize);
  uint32 symndx = read32le(shdr->sh_link);

  struct Section *s;

  if ((data < lf->data) || 
      (data + read32le(shdr->sh_size) > lf->data + lf->length))
    error(51,lf->pathname,"ppc64be reloc",lf->objname);  /* illegal offset */
  if (!(s = find_sect_id(ou,read32le(shdr->sh_info))))
    /* a section with this index doesn't exist! */
    error(52,lf->pathname,sec_name,lf->objname,(int)read32le(shdr->sh_info));

  while (nrelocs--) {
    rtype = R_NONE;
    switch (ELF64_R_TYPE_LE(((struct Elf64_Rela *)data)->r_info)) {
      case R_NONE:
        break;  /* ignore */
      case R_PPC_ADDR32:
        rtype = R_ADDR32;
        break;
      case R_PPC_ADDR24:
        rtype = R_ADDR26;
        break;
      case R_PPC_ADDR16:
        rtype = R_ADDR16;
        break;
      case R_PPC_ADDR16_LO:
        rtype = R_ADDR16_LO;
        break;
      case R_PPC_ADDR16_HI:
        rtype = R_ADDR16_HI;
        break;
      case R_PPC_ADDR16_HA:
        rtype = R_ADDR16_HA;
        break;
      case R_PPC_ADDR14:
        rtype = R_ADDR14;
        break;
      case R_PPC_ADDR14_BRTAKEN:
        rtype = R_ADDR14_BRTAKEN;
        break;
      case R_PPC_ADDR14_BRNTAKEN:
        rtype = R_ADDR14_BRNTAKEN;
        break;
      case R_PPC_REL24:
        rtype = R_REL26;
        break;
      case R_PPC_REL14:
        rtype = R_REL14;
        break;
      case R_PPC_REL14_BRTAKEN:
        rtype = R_REL14_BRTAKEN;
        break;
      case R_PPC_REL14_BRNTAKEN:
        rtype = R_REL14_BRNTAKEN;
        break;
      case R_PPC_REL32:
        rtype = R_REL32;
        break;
      case R_PPC_SDAREL16:
        rtype = R_BASEREL16;
        break;
      case R_PPC_PASM_TOC16:
        rtype = R_BASEREL16;
        break;
      default:
        ierror("alpha64_relocs(): %s (%s): Reloc type %d in %s is currently "
               "not supported",lf->pathname,lf->objname,
               ELF64_R_TYPE_LE(((struct Elf64_Rela *)data)->r_info),
               sec_name);
        break;
    }

    if (rtype > R_NONE)
      elf64le_reloc(gv,(struct Elf64_Ehdr *)elf,s,symndx,
                    (struct Elf64_Rela *)data,rela,rtype);
    data += entsize;  /* next reloc */
  }

}

#endif /* ELF64_ALPHA */


static int elf64le_identify(struct FFFuncs *ff,char *name,
                            struct Elf64_Ehdr *p,unsigned long plen,
                            unsigned char class,unsigned char endian,
                            uint16 machine,uint32 ver)
/* check a possible ELF file against the requirements, then */
/* return its type (object, library, shared object) */
{
  bool arflag = FALSE;

  if (plen < sizeof(struct Elf64_Ehdr))
    return (ID_UNKNOWN);

  if (ar_init(&ai,(char *)p,plen,name)) {
    /* library archive detected, extract 1st archive member */
    arflag = TRUE;
    if (!(ar_extract(&ai))) {
      error(38,name);  /* Empty archive ignored */
      return (ID_LIBARCH);
    }
    p = (struct Elf64_Ehdr *)ai.data;
  }

  if (!strncmp(p->e_ident,ELFid,4)) {
    /* ELF identification found */
    if (p->e_ident[EI_CLASS]==class && p->e_ident[EI_DATA]==endian &&
        p->e_ident[EI_VERSION]==(unsigned char)ver &&
        read32le(p->e_version)==ver && read16le(p->e_machine)==machine) {
      switch (read16le(p->e_type)) {
        case ET_REL:
          return (arflag ? ID_LIBARCH : ID_OBJECT);
        case ET_EXEC:
          if (arflag) /* no executables in library archives */
            error(40,name,ff->tname);
          return (ID_EXECUTABLE);
        case ET_DYN:
          if (arflag) /* no shared objects in library archives */
            error(39,name,ff->tname);
          return (ID_SHAREDOBJ);
        default:
          error(41,name,ff->tname);  /* illegal fmt. / file corrupted */
          break;
      }
    }
  }

  return (ID_UNKNOWN);
}


static void elf64le_check_ar_type(struct FFFuncs *ff,char *name,
                                  struct Elf64_Ehdr *ehdr,unsigned char class,
                                  unsigned char endian,uint32 ver,
                                  uint16 mach1,uint16 mach2,uint16 mach3)
/* check all library archive members before conversion */
{
  uint16 m = read16le(ehdr->e_machine);

  if (!strncmp(ehdr->e_ident,ELFid,4)) {
    /* ELF identification found */
    if (ehdr->e_ident[EI_CLASS]==class && ehdr->e_ident[EI_DATA]==endian &&
        ehdr->e_ident[EI_VERSION]==(unsigned char)ver &&
        read32le(ehdr->e_version)==ver && (m==mach1 || m==mach2 || m==mach3)) {
      switch (read16le(ehdr->e_type)) {
        case ET_REL:
          return;
        case ET_EXEC:  /* no executables in library archives */
          error(40,name,ff->tname);
          break;
        case ET_DYN:  /* no shared objects in library archives */
          error(39,name,ff->tname);
          break;
        default:  /* illegal fmt. / file corrupted */
          break;
      }
    }
  }
  error(41,name,ff->tname);
}


static char *elf64le_shstrtab(struct LinkFile *lf,struct Elf64_Ehdr *ehdr)
/* returns a pointer to the section header string table, if present, */
/* or NULL, otherwise */
{
  uint16 i;
  struct Elf64_Shdr *shdr;
  char *stab;

  if (i = read16le(ehdr->e_shstrndx)) {
    shdr = elf64le_shdr(lf,ehdr,i);
    if (read32le(shdr->sh_type) != SHT_STRTAB)
      error(45,lf->pathname,lf->objname);  /* illegal type */
    stab = ((char *)ehdr) + read32le(shdr->sh_offset);
    if (((uint8 *)stab < lf->data) || 
        ((uint8 *)stab + read32le(shdr->sh_size) > lf->data + lf->length))
      error(46,lf->pathname,lf->objname);  /* illegal offset */
    else
      return (stab);
  }
  return (NULL);
}


static char *elf64le_strtab(struct LinkFile *lf,
                            struct Elf64_Ehdr *ehdr,int idx)
/* returns a pointer to the string table */
{
  static char *tabname = "string";
  struct Elf64_Shdr *shdr;
  char *stab;

  shdr = elf64le_shdr(lf,ehdr,idx);
  if (read32le(shdr->sh_type) != SHT_STRTAB)
     error(50,lf->pathname,tabname,lf->objname);  /* illegal type */
  stab = ((char *)ehdr) + read32le(shdr->sh_offset);
  if (((uint8 *)stab < lf->data) || 
      ((uint8 *)stab + read32le(shdr->sh_size) > lf->data + lf->length))
    error(51,lf->pathname,tabname,lf->objname);  /* illegal offset */
  return (stab);
}


static struct Elf64_Sym *elf64le_symtab(struct LinkFile *lf,
                                        struct Elf64_Ehdr *ehdr,int idx)
/* returns a pointer to the symbol table */
{
  static char *tabname = "symbol";
  struct Elf64_Shdr *shdr;
  struct Elf64_Sym *symtab;

  shdr = elf64le_shdr(lf,ehdr,idx);
  if (read32le(shdr->sh_type) != SHT_SYMTAB)
     error(50,lf->pathname,tabname,lf->objname);  /* illegal type */
  symtab = (struct Elf64_Sym *)((uint8 *)ehdr + read32le(shdr->sh_offset));
  if (((uint8 *)symtab < lf->data) || 
      ((uint8 *)symtab + read32le(shdr->sh_size) > lf->data + lf->length))
    error(51,lf->pathname,tabname,lf->objname);  /* illegal offset */
  return (symtab);
}


static struct Elf64_Shdr *elf64le_shdr(struct LinkFile *lf,
                                       struct Elf64_Ehdr *ehdr,uint16 idx)
/* return pointer to section header #idx */
{
  struct Elf64_Shdr *shdr;

  if (idx < read16le(ehdr->e_shnum)) {
    shdr = (struct Elf64_Shdr *)(((char *)ehdr) + ((read32le(ehdr->e_shoff) +
           (uint32)read16le(ehdr->e_shentsize) * (uint32)idx)));
    if (((uint8 *)shdr < lf->data) ||
        (((uint8 *)shdr)+read16le(ehdr->e_shentsize) > lf->data+lf->length))
      /* section header #x has illegal offset */
      error(44,lf->pathname,(int)idx,lf->objname);
    return (shdr);
  }
  else  /* Invalid ELF section header index */
    error(43,lf->pathname,(int)idx,lf->objname);
  return (NULL);  /* not reached, for compiler's sake */
}


static void elf64le_section(uint8 *elf,struct ObjectUnit *ou,
                            struct Elf64_Shdr *shdr,int shndx)
/* create a new section */
{
  char *sec_name = shstrtab + read32le(shdr->sh_name);
  uint8 *data = elf + read32le(shdr->sh_offset);
  unsigned long size = (unsigned long)read32le(shdr->sh_size);
  uint32 f = read32le(shdr->sh_flags);
  struct LinkFile *lf = ou->lnkfile;
  uint8 type=ST_DATA,flags=0;
  struct Section *s;

  if (read32le(shdr->sh_type) == SHT_NOBITS) {
    data = NULL;
    type = ST_UDATA;
    flags |= SF_UNINITIALIZED;
  }
  else {
    if (data+size > lf->data+lf->length)  /* illegal section offset */
      error(49,lf->pathname,sec_name,lf->objname);
  }
  s = create_section(ou,sec_name,data,size);

  s->id = shndx;  /* use section header index for identification */
  s->protection = SP_READ;
  if ((f & SHF_EXECINSTR) && data) {
    type = ST_CODE;
    s->protection |= SP_EXEC;
  }
  if (f & SHF_WRITE)
    s->protection |= SP_WRITE;
  if (!(f & SHF_ALLOC))
    flags |= SF_DISCARD;

  s->type = type;
  s->flags = flags;
  s->alignment = (uint8)shiftval(read32le(shdr->sh_addralign));
  addtail(&ou->sections,&s->n);  /* add Section to ObjectUnit */
}


static void elf64le_symbols(struct GlobalVars *gv,uint8 *elf,
                            struct ObjectUnit *ou,struct Elf64_Shdr *shdr)
/* convert ELF symbol definitions into internal format */
{
  struct LinkFile *lf = ou->lnkfile;
  uint8 *data = elf + read32le(shdr->sh_offset);
  unsigned long entsize = read32le(shdr->sh_entsize);
  int nsyms = (int)(read32le(shdr->sh_size) / (uint32)entsize);
  char *strtab = elf64le_strtab(lf,(struct Elf64_Ehdr *)elf,
                                read32le(shdr->sh_link));
  struct Section *sec,*firstsec;
  struct Elf64_Sym *elfsym;

  if ((data < lf->data) || 
      (data + read32le(shdr->sh_size) > lf->data + lf->length))
    error(51,lf->pathname,"symbol",lf->objname);  /* illegal offset */

  if (listempty(&ou->sections))  /* not a single section? */
    firstsec = elf64le_bssdefault(ou);
  else
    firstsec = (struct Section *)ou->sections.first;

  /* read ELF xdef symbols and convert to internal format */
  while (--nsyms > 0) {
    int shndx;
    char *symname;
    uint8 type,objinfo,objbind;

    elfsym = (struct Elf64_Sym *)(data += entsize);
    symname = strtab + read32le(elfsym->st_name);
    switch (shndx = (int)read16le(elfsym->st_shndx)) {
      case SHN_UNDEF:
        sec = NULL;  /* ignore xrefs for now */
        break;
      /* assign a section for ABS and COMMON symbols, to prevent */
      /* accidental NULL-pointer references */
      case SHN_ABS:
        sec = firstsec;
        type = SYM_ABS;
        break;
      case SHN_COMMON:
        sec = firstsec;
        type = SYM_COMMON;
        break;
      /* reloc symbols have a definite section to which they belong */
      default:
        if (!(sec = find_sect_id(ou,shndx))) {
          /* a section with this index doesn't exist! */
          if (ELF64_ST_TYPE(*elfsym->st_info) != STT_SECTION)
            error(53,lf->pathname,symname,lf->objname,shndx);
        }
        type = SYM_RELOC;
        break;
    }

    if ((objinfo = ELF64_ST_TYPE(*elfsym->st_info)) == STT_SECTION)
      sec = NULL;  /* ignore section defines - will be reproduced */

    if (sec) {
      struct Symbol *sym;

      if (objinfo > STT_FILE) {
        /* illegal symbol type */
        error(54,lf->pathname,(int)objinfo,symname,lf->objname);
        objinfo = STT_NOTYPE;
      }
      switch (ELF64_ST_BIND(*elfsym->st_info)) {
        case STB_LOCAL:
          objbind = SYMB_LOCAL;
          break;
        case STB_GLOBAL:
          objbind = SYMB_GLOBAL;
          break;
        case STB_WEAK:
          objbind = SYMB_WEAK;
          break;
        default:  /* illegal binding type */
          error(55,lf->pathname,symname,ELF64_ST_BIND(*elfsym->st_info),
                lf->objname);
          objbind = SYMB_LOCAL;
          break;
      }

      /* add a new symbol definition */
      if (objbind == SYMB_LOCAL) {
        /* always define local symbols - multiple defintions allowed */
        addlocsymbol(gv,sec,symname,read32le(elfsym->st_value),type,0,
                     objinfo,read32le(elfsym->st_size));
      }
      else {
        if (sym = addsymbol(gv,sec,symname,read32le(elfsym->st_value),type,
                            0,objinfo,objbind,read32le(elfsym->st_size))) {
          /* symbol is multiply defined! */
          struct LinkFile *lf2 = sym->relsect->obj->lnkfile;
          char buf[256];  /* @@@ dangerous... */

          if (lf2->type == ID_LIBARCH)
            sprintf(buf,"%s (%s)",lf2->filename,lf2->objname);
          else
            strcpy(buf,lf2->objname);
          error(56,lf->pathname,symname,lf->objname,buf);
        }
      }
    }
  }
}


static void elf64le_reloc(struct GlobalVars *gv,struct Elf64_Ehdr *elf,
                          struct Section *s,uint32 symndx,
                          struct Elf64_Rela *rel,bool rela,uint8 rtype)
/* Insert relocations, which are relative to a defined symbol, into */
/* the section's reloc-list. If the symbol is undefined, create an */
/* external reference on it, with the supplied relocation type. */
{
  struct LinkFile *lf = s->obj->lnkfile;
  struct Elf64_Shdr *symhdr = elf64le_shdr(lf,elf,symndx);
  struct Elf64_Sym *sym = elf64le_symtab(lf,elf,symndx) +
                          ELF64_R_SYM_LE(rel->r_info);
  uint32 offs = read32le(rel->r_offset);
  uint32 shndx = (uint32)read16le(sym->st_shndx);
  int32 a;

  /* if addend isn't defined in Reloc, read it from the section data */
  if (rela)
    a = (int32)read32le(rel->r_addend);
  else
    a = readsection(s->data+offs,rtype);

  if (shndx == SHN_UNDEF) {
    /* undefined symbol - create external reference */
    char *xrefname = elf64le_strtab(lf,elf,read32le(symhdr->sh_link)) +
                                    read32le(sym->st_name);

    addxref(gv,s,xrefname,offs,rtype,relocsize(rtype),a);
  }

  else if (ELF64_ST_TYPE(*sym->st_info) == STT_SECTION) {
    /* a normal relocation, with an offset relative to a section base */

    addreloc(s,find_sect_id(s->obj,shndx),0,offs,rtype,a);
  }

  else if (ELF64_ST_TYPE(*sym->st_info)<STT_SECTION && shndx<SHN_ABS) {
    /* relocations, which are relative to a known symbol */

    addreloc(s,find_sect_id(s->obj,shndx),0,offs,rtype,
             (int32)read32le(sym->st_value) + a);
  }

  else
    ierror("elf64le_reloc(): %s (%s): Only relocations which are relative "
           "to a section, function or object are supported",
           lf->pathname,lf->objname);
}


static struct Section *elf64le_bssdefault(struct ObjectUnit *ou)
/* Create a default BSS section for ELF64 LittleEndian */
{
  static char *bssname = ".bss";
  struct Section *s = create_section(ou,bssname,NULL,0);

  s->flags |= SF_UNINITIALIZED;
  s->type = ST_UDATA;
  s->protection = SP_READ | SP_WRITE;
  s->alignment = 4;  /* @@@ */
  return (s);
}


static int elf64_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. */
{
  if (!gv->dest_object) {
    if ((!strncmp(ls->name,sdata,6) && !strncmp(s->name,sbss,5)
         && *(ls->name+6) == *(s->name+5)) ||
        (!strncmp(ls->name,sbss,5) && !strncmp(s->name,sdata,6)
         && *(ls->name+5) == *(s->name+6)))
      /* .sdata/.sbss, .sdata2/.sbss2, etc. are always combined */
      return (1);
  }
  return (0);
}


static struct Symbol *elf64_lnksym(struct GlobalVars *gv,
                                     struct Section *sec,
                                     struct XReference *xref)
{
  return (NULL);
}


static void elf64_setlnksym(struct GlobalVars *gv,struct Symbol *xdef,
                              struct XReference *xref)
{
}



/*****************************************************************/
/*                          Write ELF                            */
/*****************************************************************/


#ifdef ELF64_ALPHA

static void alpha64_writeshared(struct GlobalVars *gv,FILE *f)
/* creates a target-elfppc64be shared object (which is pos. independant) */
{
  ierror("alpha64_writeshared(): Shared object generation has not "
         "yet been implemented");
}


static void alpha64_writeobject(struct GlobalVars *gv,FILE *f)
/* creates a target-elfppc64be relocatable object file */
{
  uint32 sh_off,shstrndx,stabndx;

  elf64_initlists();
  elf64le_addsym(&symlist,nullstr,0,0,0,0,SHN_UNDEF);
  if (gv->discard_local < DISLOC_ALL)
    elf64le_stdsymtab(gv,STB_LOCAL,STT_FILE);

  elf64le_makeshdrs(gv);
  if (gv->discard_local < DISLOC_ALL)
    elf64le_stdsymtab(gv,STB_LOCAL,0);
  symlist.global_index = symlist.nextindex;
  if (gv->strip_symbols < STRIP_ALL) { /* although not recomm. for objects */
    elf64le_stdsymtab(gv,STB_WEAK,0);
    elf64le_stdsymtab(gv,STB_GLOBAL,0);
  }
  elf64le_addrelocs(gv,alpha64_getrel);

  shstrndx = shdrindex;
  elf64le_makeshstrtab();
  sh_off = elfoffset;
  stabndx = shdrindex;
  elfoffset += (shdrindex+2) * sizeof(struct Elf64_Shdr);
  elf64le_makesymtab(shdrindex+1);
  elf64le_makestrtab();

  elf64le_header(f,ET_REL,EM_PPC,0,0,sh_off,0,0,shdrindex,shstrndx);
  elf64_writesections(gv,f);
  elf64_writestrtab(f,&shstrlist);
  fwrite_align(f,2,ftell(f));
  elf64le_writeshdrs(f,elfoffset,stabndx);
  elf64_writesymtab(f,&symlist);
  elf64_writestrtab(f,&stringlist);
  fwrite_align(f,2,ftell(f));
  elf64_writerelocs(gv,f);
}


static void alpha64_writeexec(struct GlobalVars *gv,FILE *f)
/* creates a target-elfppc64be executable file (with absolute addresses) */
{
  ierror("alpha64_writeexec(): Executable file generation has not "
         "yet been implemented");
}


static uint8 alpha64_getrel(uint8 rtype,char *sname,uint32 offs)
{
  switch (rtype) {
    case R_NONE:
      break;
    case R_ADDR32:
      return (R_PPC_ADDR32);
    case R_ADDR26:
      return (R_PPC_ADDR24);
    case R_ADDR16:
      return (R_PPC_ADDR16);
    case R_ADDR16_LO:
      return (R_PPC_ADDR16_LO);
    case R_ADDR16_HI:
      return (R_PPC_ADDR16_HI);
    case R_ADDR16_HA:
      return (R_PPC_ADDR16_HA);
    case R_ADDR14:
      return (R_PPC_ADDR14);
    case R_ADDR14_BRTAKEN:
      return (R_PPC_ADDR14_BRTAKEN);
    case R_ADDR14_BRNTAKEN:
      return (R_PPC_ADDR14_BRNTAKEN);
    case R_REL26:
      return (R_PPC_REL24);
    case R_REL14:
      return (R_PPC_REL14);
    case R_REL14_BRTAKEN:
      return (R_PPC_REL14_BRTAKEN);
    case R_REL14_BRNTAKEN:
      return (R_PPC_REL14_BRNTAKEN);
    case R_BASEREL16:
      return (R_PPC_SDAREL16);
    default:
      /* unsupported relocation type */
      error(32,fff_elf32ppcbe.tname,reloc_name[rtype],sname,offs);
      break;
  }
  return (R_NONE);
}

#endif /* ELF64_ALPHA */


static void elf64le_header(FILE *f,uint16 type,uint16 mach,uint32 entry,
                           uint32 phoff,uint32 shoff,uint32 flags,
                           uint16 phnum,uint16 shnum,uint16 shstrndx)
/* write ELF header for 32-bit big endian */
{
  struct Elf64_Ehdr eh;

  memset(&eh,0,sizeof(struct Elf64_Ehdr));
  strncpy((char *)eh.e_ident,ELFid,4);
  eh.e_ident[EI_CLASS] = ELFCLASS32;
  eh.e_ident[EI_DATA] = ELFDATA2MSB;
  eh.e_ident[EI_VERSION] = ELF_VER;
  write16le(eh.e_type,type);
  write16le(eh.e_machine,mach);
  write32le(eh.e_version,ELF_VER);
  write32le(eh.e_entry,entry);
  write32le(eh.e_phoff,phoff);
  write32le(eh.e_shoff,shoff);
  write32le(eh.e_flags,flags);
  write16le(eh.e_ehsize,sizeof(struct Elf64_Ehdr));
  write16le(eh.e_phentsize,phnum ? sizeof(struct Elf64_Phdr):0);
  write16le(eh.e_phnum,phnum);
  write16le(eh.e_shentsize,shnum ? sizeof(struct Elf64_Shdr):0);
  write16le(eh.e_shnum,shnum);
  write16le(eh.e_shstrndx,shstrndx);
  fwritex(f,&eh,sizeof(struct Elf64_Ehdr));
}


static void elf64le_writeshdrs(FILE *f,uint32 reloffset,uint32 stabndx)
/* write all section headers */
{
  struct ShdrNode *shn;
  uint32 type;

  while (shn = (struct ShdrNode *)remhead(&shdrlist)) {
    type = read32le(shn->s.sh_type);
    if (type == SHT_RELA || type == SHT_REL) {
      /* patch correct sh_offset and sh_link for reloc header */
      write32le(shn->s.sh_offset,read32le(shn->s.sh_offset)+reloffset);
      write32le(shn->s.sh_link,stabndx);
    }
    fwritex(f,&shn->s,sizeof(struct Elf64_Shdr));
  }
}


static void elf64le_stdsymtab(struct GlobalVars *gv,uint8 bind,uint8 type)
{
  elf64le_addsymlist(gv,&symlist,bind,type);
}


static void elf64le_addsymlist(struct GlobalVars *gv,struct SymTabList *sl,
                               uint8 bind,uint8 type)
/* add all symbols with specified bind and type to the ELF symbol list */
{
  struct LinkedSection *ls = (struct LinkedSection *)gv->lnksec.first;
  struct LinkedSection *nextls;
  struct Symbol *nextsym,*sym;

  while (nextls = (struct LinkedSection *)ls->n.next) {
    sym = (struct Symbol *)ls->symbols.first;

    while (nextsym = (struct Symbol *)sym->n.next) {
      uint8 symtype = elf64_getinfo(sym);
      uint8 symbind = elf64_getbind(sym);

      if (symbind == bind && (!type || (symtype == type))) {
        remnode(&sym->n);
        elf64le_addsym(sl,sym->name,sym->value,sym->size,
                       symbind,symtype,elf64_getshndx(sym,symtype));
#ifndef FASTALLOC
        free(sym);
#endif
      }
      sym = nextsym;
    }
    ls = nextls;
  }
}


static void elf64le_makeshdrs(struct GlobalVars *gv)
/* generate all ELF section headers */
{
  struct LinkedSection *ls = (struct LinkedSection *)gv->lnksec.first;
  struct LinkedSection *nextls;
  struct ShdrNode *shn;
  struct SymbolNode *sym;
  uint32 f;
  bool bss;

  /* offset, to find section-symbols by section header index */
  secsyms = (int)symlist.nextindex - (int)shdrindex;

  while (nextls = (struct LinkedSection *)ls->n.next) {
    ls->index = (int)shdrindex;
    bss = ls->type==ST_UDATA || (ls->flags&SF_UNINITIALIZED);
    f = (ls->flags & SF_DISCARD) ? 0 : SHF_ALLOC;
    if (ls->protection & SP_WRITE)
      f |= SHF_WRITE;
    if (ls->protection & SP_EXEC)
      f |= SHF_EXECINSTR;
    elf64le_addshdr(elf64_addshdrstr(ls->name),bss?SHT_NOBITS:SHT_PROGBITS,
                    f,0,elfoffset,ls->size,0,0,1<<(uint32)ls->alignment,0);
    if (!bss)
      elfoffset += ls->size;

    /* add section symbol (without name) */
    elf64le_addsym(&symlist,nullstr,0,0,STB_LOCAL,STT_SECTION,
                   (uint16)ls->index);
    ls = nextls;  /* next section */
  }
}


static void elf64le_addrelocs(struct GlobalVars *gv,
                              uint8 (*getrel)(uint8,char *,uint32))
/* creates relocations for all sections */
{
  struct LinkedSection *ls = (struct LinkedSection *)gv->lnksec.first;
  struct LinkedSection *nextls;
  struct Reloc *rel,*nextrel;
  struct XReference *xref,*nextxref;
  uint32 symidx,sroffs=0,roffs=0;

  while (nextls = (struct LinkedSection *)ls->n.next) {
    sroffs = roffs;

    /* relocations */
    rel = (struct Reloc *)ls->relocs.first;
    while (nextrel = (struct Reloc *)rel->n.next) {
      elf64le_addrela((uint32)rel->offset,rel->addend,
                      (uint32)(rel->relocsect.lnk->index + secsyms),
                      getrel(rel->type,ls->name,(uint32)rel->offset));
      roffs += gv->short_rel ?
               sizeof(struct Elf64_Rel) : sizeof(struct Elf64_Rela);
      rel = nextrel;
    }

    /* external references */
    xref = (struct XReference *)ls->xrefs.first;
    while (nextxref = (struct XReference *)xref->n.next) {
      /* undefined symbol */
      if (!(symidx = elf64le_findsym(&symlist,xref->name,SHN_UNDEF)))
        symidx = elf64le_addsym(&symlist,xref->name,0,0,STB_GLOBAL,
                                STT_NOTYPE,SHN_UNDEF);
      /* symbol's relocation */
      elf64le_addrela((uint32)xref->offset,xref->addend,symidx,
                      getrel(xref->type,ls->name,(uint32)xref->offset));
      roffs += gv->short_rel ?
               sizeof(struct Elf64_Rel) : sizeof(struct Elf64_Rela);
      xref = nextxref;
    }

    if (roffs != sroffs) {
      /* create ".rel(a).name" header */
      char *relname = (char *)alloc(strlen(ls->name)+6);

      sprintf(relname,".%s%s",gv->short_rel ? "rel":"rela",ls->name);
      elf64le_addshdr(elf64_addshdrstr(relname),
                      gv->short_rel ? SHT_REL:SHT_RELA,
                      0,0,sroffs,roffs-sroffs,0,(uint32)ls->index,4,
                      gv->short_rel ? sizeof(struct Elf64_Rel) : 
                                      sizeof(struct Elf64_Rela));
      /* sroffs is relative and will be corrected later */
      /* sh_link will be initialized, when .symtab exists */
    }
    ls = nextls;  /* next section */
  }
}


static void elf64le_makeshstrtab()
/* creates .shstrtab */
{
  elf64le_addshdr(shstrtabidx,SHT_STRTAB,0,0,elfoffset,
                  shstrlist.nextindex,0,0,1,0);
  elfoffset += shstrlist.nextindex;
  elfoffset += align(elfoffset,2);
}


static void elf64le_makestrtab()
/* creates .strtab */
{
  elf64le_addshdr(strtabidx,SHT_STRTAB,0,0,elfoffset,
                  stringlist.nextindex,0,0,1,0);
  elfoffset += stringlist.nextindex;
  elfoffset += align(elfoffset,2);
}


static void elf64le_makesymtab(uint32 strtabindex)
/* creates .symtab */
{
  elf64le_addshdr(symtabidx,SHT_SYMTAB,0,0,elfoffset,
                  symlist.nextindex * sizeof(struct Elf64_Sym),
                  strtabindex,symlist.global_index,4,
                  sizeof(struct Elf64_Sym));
  elfoffset += symlist.nextindex * sizeof(struct Elf64_Sym);
}


static struct ShdrNode *elf64le_addshdr(uint32 name,uint32 type,uint32 flags,
                                        uint32 addr,uint32 offset,
                                        uint32 size,uint32 link,uint32 info,
                                        uint32 align,uint32 entsize)
{
  struct ShdrNode *shn = elf64_newshdr();

  write32le(shn->s.sh_name,name);
  write32le(shn->s.sh_type,type);
  write32le(shn->s.sh_flags,flags);
  write32le(shn->s.sh_addr,addr);
  write32le(shn->s.sh_offset,offset);
  write32le(shn->s.sh_size,size);
  write32le(shn->s.sh_link,link);
  write32le(shn->s.sh_info,info);
  write32le(shn->s.sh_addralign,align);
  write32le(shn->s.sh_entsize,entsize);
  return (shn);
}


static uint32 elf64le_addsym(struct SymTabList *sl,char *name,uint32 value,
                             uint32 size,uint8 bind,uint8 type,uint16 shndx)
/* add a new ELF symbol, return its symbol table index */
{
  struct SymbolNode **chain = &sl->hashtab[elf_hash(name) % SYMHTABSIZE];
  struct SymbolNode *sym;

  while (sym = *chain)
    chain = &sym->hashchain;
  /* new symbol table entry */
  *chain = sym = alloczero(sizeof(struct SymbolNode));
  sym->name = name;
  sym->index = sl->nextindex++;
  addtail(&sl->l,&sym->n);
  write32le(sym->s.st_name,elf64_addstr(name));
  write32le(sym->s.st_value,value);
  write32le(sym->s.st_size,size);
  *sym->s.st_info = ELF64_ST_INFO(bind,type);
  write16le(sym->s.st_shndx,shndx);
  return (sym->index);
}


static uint32 elf64le_findsym(struct SymTabList *sl,char *name,uint16 shndx)
/* find an ELF symbol by its name and shndx */
/* return its symbol table index, index=0 means 'not found' */
{
  struct SymbolNode **chain = &sl->hashtab[elf_hash(name) % SYMHTABSIZE];
  struct SymbolNode *sym;

  while (sym = *chain) {
    if (!strcmp(name,sym->name))
      if (read16le(sym->s.st_shndx) == shndx)
        return (sym->index);
    chain = &sym->hashchain;
  }
  return (0);
}


static void elf64le_addrela(uint32 offs,int32 addend,uint32 sym,uint8 type)
{
  struct RelaNode *rn = alloc(sizeof(struct RelaNode));

  write32le(rn->r.r_offset,offs);
  write32le(rn->r.r_addend,addend);
  ELF64_R_INFO_LE(rn->r.r_info,sym,(uint32)type);
  addtail(&relalist,&rn->n);
}


static void elf64_initlists()
/* initialize section header, program header, relocation, symbol, */
/* string and section header string lists */
{
  static struct StrTabNode *str_hashtab[STRHTABSIZE];
  static struct StrTabNode *shstr_hashtab[SHSTRHTABSIZE];
  static struct SymbolNode *sym_hashtab[SYMHTABSIZE];

  elfoffset = sizeof(struct Elf64_Ehdr);
  initlist(&shdrlist);
  initlist(&phdrlist);
  initlist(&relalist);
  shdrindex = 0;

  initlist(&symlist.l);
  symlist.hashtab = sym_hashtab;
  memset(sym_hashtab,0,SYMHTABSIZE*sizeof(struct SymbolNode *));
  symlist.nextindex = symlist.global_index = 0;

  initlist(&shstrlist.l);
  initlist(&stringlist.l);
  shstrlist.hashtab = shstr_hashtab;
  stringlist.hashtab = str_hashtab;
  shstrlist.nextindex = stringlist.nextindex = 0;
  stringlist.htabsize = STRHTABSIZE;
  shstrlist.htabsize = SHSTRHTABSIZE;
  memset(shstr_hashtab,0,SHSTRHTABSIZE*sizeof(struct StrTabNode *));
  memset(str_hashtab,0,STRHTABSIZE*sizeof(struct StrTabNode *));

  elf64_addshdrstr(nullstr);  /* first string is always "" */
  symtabidx = elf64_addshdrstr(".symtab");
  strtabidx = elf64_addshdrstr(".strtab");
  shstrtabidx = elf64_addshdrstr(".shstrtab");
  elf64_addstr(nullstr);
  elf64_newshdr();          /* first Shdr is always zero */
}


static struct ShdrNode *elf64_newshdr()
{
  struct ShdrNode *s = alloczero(sizeof(struct ShdrNode));

  addtail(&shdrlist,&s->n);
  ++shdrindex;
  return (s);
}


static uint32 elf64_addshdrstr(char *s)
{
  return (elf64_addstrlist(&shstrlist,s));
}


static uint32 elf64_addstr(char *s)
{
  return (elf64_addstrlist(&stringlist,s));
}


static uint32 elf64_addstrlist(struct StrTabList *sl,char *s)
/* add a new string to an ELF string table and return its index */
{
  struct StrTabNode **chain = &sl->hashtab[elf_hash(s) % sl->htabsize];
  struct StrTabNode *sn;

  /* search string in hash table */
  while (sn = *chain) {
    if (!strcmp(s,sn->str))
      return (sn->index);  /* it's already in, return index */
    chain = &sn->hashchain;
  }

  /* new string table entry */
  *chain = sn = alloc(sizeof(struct StrTabNode));
  sn->hashchain = NULL;
  sn->str = s;
  sn->index = sl->nextindex;
  addtail(&sl->l,&sn->n);
  sl->nextindex += (uint32)strlen(s) + 1;
  return (sn->index);
}


static uint8 elf64_getinfo(struct Symbol *sym)
{
  uint8 type;

  switch (sym->info) {
    case SYMI_NOTYPE:
      type = STT_NOTYPE;  /* @@@ Is this allowed? */
      break;
    case SYMI_OBJECT:
      type = STT_OBJECT;
      break;
    case SYMI_FUNC:
      type = STT_FUNC;
      break;
    case SYMI_SECTION:
      ierror("elf64le_addsymlist(): STT_SECTION symbol detected");
      type = STT_SECTION;
      break;
    case SYMI_FILE:
      type = STT_FILE;
      break;
    default:
      ierror("elf64_getinfo(): Illegal symbol info: %d",(int)sym->info);
      break;
  }
  return (type);
}


static uint8 elf64_getbind(struct Symbol *sym)
{
  uint8 bind;

  switch (sym->bind) {
    case SYMB_LOCAL:
      bind = STB_LOCAL;
      break;
    case SYMB_GLOBAL:
      bind = STB_GLOBAL;
      break;
    case SYMB_WEAK:
      bind = STB_WEAK;
      break;
    default:
      ierror("elf64_getbind(): Illegal symbol binding: %d",(int)sym->bind);
      break;
  }
  return (bind);
}


static uint16 elf64_getshndx(struct Symbol *sym,uint8 symtype)
{
  uint16 shndx;

  switch (sym->type) {
    case SYM_UNDEF:
      shndx = SHN_UNDEF;
      break;
    case SYM_ABS:
      shndx = SHN_ABS;
      break;
    case SYM_RELOC:
      if (symtype > STT_FUNC)
        ierror("elf64_getshndx(): %s is relocatable, but not a "
               "function or object (type %d)",sym->name,(int)symtype);
      shndx = (uint16)sym->relsect->lnksec->index;
      break;
    case SYM_COMMON:
      shndx = SHN_COMMON;
      break;
    default:
      ierror("elf64_getshndx(): Illegal symbol type: %d",(int)sym->type);
      break;
  }
  return (shndx);
}


static void elf64_writesections(struct GlobalVars *gv,FILE *f)
/* write all linked sections */
{
  struct LinkedSection *ls = (struct LinkedSection *)gv->lnksec.first;
  struct LinkedSection *nextls;

  while (nextls = (struct LinkedSection *)ls->n.next) {
    if (!(ls->flags & SF_UNINITIALIZED))
      fwritex(f,ls->data,ls->size);
    ls = nextls;
  }
}


static void elf64_writestrtab(FILE *f,struct StrTabList *sl)
{
  struct StrTabNode *stn;

  while (stn = (struct StrTabNode *)remhead(&sl->l))
    fwritex(f,stn->str,strlen(stn->str)+1);
}


static void elf64_writesymtab(FILE *f,struct SymTabList *sl)
{
  struct SymbolNode *sym;

  while (sym = (struct SymbolNode *)remhead(&sl->l))
    fwritex(f,&sym->s,sizeof(struct Elf64_Sym));
}


static void elf64_writerelocs(struct GlobalVars *gv,FILE *f)
{
  struct RelaNode *rn;

  while (rn = (struct RelaNode *)remhead(&relalist))
    fwritex(f,&rn->r,gv->short_rel ? sizeof(struct Elf64_Rel) : 
                                     sizeof(struct Elf64_Rela));
}


#endif
