/* $VER: vlink t_elf32.c V0.6d (13.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.6d (13.02.98) phx
 *       The PowerUp ELF loader has its own constructor/destructor
 *       functionality and wildly mixes all words in .ctors and .dtors.
 *       Have to rename the sections to .ctor_/.dtor_ for elf32powerup
 *       executable generation.
 * v0.6c (05.02.98) phx
 *       Constructor/destructor support for vbcc's _INIT/_EXIT
 *       function. The function pointers will be placed into the
 *       the .ctors and .dtors sections.
 * v0.6a (19.12.98) phx
 *       Support for little endian object file formats.
 * v0.6  (24.10.98) phx
 *       Standard ELF32 linker symbols _SDA_BASE_ and _SDA2_BASE_
 *       are supported. _SDA_BASE_ points to .sdata+0x7ff0 (or .sbss, if
 *       .sdata is missing) and _SDA2_BASE_ (EABI only) to .sdata2+0x7ff0.
 *       Take base register section offset from FFFuncs.baseoff. Each
 *       target defines their own offsets.
 *       .sdata/.sbss and .sdata2/.sbss2 will always be combined.
 *       Linking with shared objects is possible. Creating them is
 *       a another story...
 *       New target elf32powerup. It is a elf32ppcbe-type target, which
 *       uses relocatable objects as final executables. It is used for
 *       the Amiga PowerUp turbo boards from Phase5, which have a special
 *       ELF object loader. This loader will do some kind of dynamic
 *       linking with functions from the PowerUp kernel.
 *       _LinkerDB is no longer known to elf32ppcbe, but only to
 *       elf32powerup.
 * v0.5d (22.08.98) phx
 *       Faster memory allocation can be activated by #define FASTALLOC.
 * v0.5b (05.07.98) phx
 *       Linker symbol _LinkerDB for elf32ppcbe. _LinkerDB is a reloc sym-
 *       bol, which can be used to initialize a small data base register.
 * v0.5  (27.06.98) phx
 *       Target-specific linker symbol support: elf32_lnksym(),
 *       elf32_setlnksym().
 * v0.4  (05.06.98) phx
 *       New FFF targetlink(). Currently no meaning for ELF.
 * v0.3b (25.04.98) phx
 *       Renamed from t_elf.c into t_elf32.c.
 * v0.3  (17.04.98) phx
 *       First version, which links and writes ELF PowerPC 32Bit big
 *       endian objects (elf32ppcbe) reliable.
 * v0.0  (21.03.98) phx
 *       File created.
 */


#if defined(ELF32_PPC_BE) || defined(ELF32_PPC_LE) || defined(ELF32_POWERUP)
#define T_ELF32_C
#include "vlink.h"
#include "elf32.h"
#include "rel_elfppc.h"

#define ELF_VER 1


#if defined(ELF32_PPC_BE) || defined(ELF32_POWERUP)
static int ppc32be_identify(char*,uint8 *,unsigned long);
static void ppc32be_readconv(struct GlobalVars *,struct LinkFile *);
static void ppc32be_readELF(struct GlobalVars *,struct LinkFile *,uint8 *);
static unsigned long ppc32be_secbase(struct GlobalVars *,char *);
static uint8 ppc32be_cmpsecflags(uint8,uint8);
static struct Section *ppc32be_bssdefault(struct ObjectUnit *);
static int ppc32be_targetlink(struct GlobalVars *,struct LinkedSection *,
                              struct Section *);
static struct Symbol *ppc32be_lnksym(struct GlobalVars *,struct Section *,
                                     struct XReference *);
static void ppc32be_setlnksym(struct GlobalVars *,struct Symbol *,
                              struct XReference *);
static void ppc32be_relocs(struct GlobalVars *,uint8 *,struct ObjectUnit *,
                           struct Elf32_Shdr *);
#endif
#ifdef ELF32_PPC_BE
static void ppc32be_writeobject(struct GlobalVars *,FILE *);
static void ppc32be_writeshared(struct GlobalVars *,FILE *);
static void ppc32be_writeexec(struct GlobalVars *,FILE *);
static uint8 ppc32be_getrel(uint8,char *,uint32);

struct FFFuncs fff_elf32ppcbe = {
  "elf32ppcbe",
  ppc32be_identify,
  ppc32be_readconv,
  ppc32be_secbase,
  ppc32be_cmpsecflags,
  ppc32be_bssdefault,
  ppc32be_targetlink,
  ppc32be_lnksym,
  ppc32be_setlnksym,
  ppc32be_writeobject,
  ppc32be_writeshared,
  ppc32be_writeexec,
  0x7ff0,
  NULL,
  1 /* big endian */
};
#endif


#ifdef ELF32_POWERUP
static struct Symbol *powerup_lnksym(struct GlobalVars *,struct Section *,
                                     struct XReference *);
static void powerup_setlnksym(struct GlobalVars *,struct Symbol *,
                              struct XReference *);
static void powerup_writeobject(struct GlobalVars *,FILE *);
static void powerup_writeshared(struct GlobalVars *,FILE *);
static void powerup_writeexec(struct GlobalVars *,FILE *);
static uint8 powerup_getrel(uint8,char *,uint32);

struct FFFuncs fff_elf32powerup = {
  "elf32powerup",
  ppc32be_identify,
  ppc32be_readconv,
  ppc32be_secbase,
  ppc32be_cmpsecflags,
  ppc32be_bssdefault,
  ppc32be_targetlink,
  powerup_lnksym,
  powerup_setlnksym,
  powerup_writeobject,
  powerup_writeshared,
  powerup_writeexec,
  0x7ff0,
  NULL,
  1 /* big endian */
};


/* elf32powerup linker symbols */
static char *linkerdb = "_LinkerDB";
#define LINKERDB        0   /* _LinkerDB, powerup */
#endif


/* elf32 common linker symbols */
static char *elf32_symnames[] = {
  "_SDA_BASE_","_SDA2_BASE_",".ctors",".dtors"
};
/* number of elf32 linker symbols */
#define ELF32_LNKSYMS 4

/* Linker symbol IDs */
#define SDABASE         0   /* _SDA_BASE_, elf32 */
#define SDA2BASE        1   /* _SDA2_BASE_, elf32 */
#define CTORS           2   /* .ctors */
#define DTORS           3   /* .dtors */

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

/* automatic constructors and destructors (vbcc) */
static char vbcc_INIT[] = "_INIT";
static char vbcc_EXIT[] = "_EXIT";
static char xtors_objname[] = "INITEXIT"; /* unit name for con/destructors */


static int elf32be_identify(struct FFFuncs *,char *,struct Elf32_Ehdr *,
                            unsigned long,unsigned char,unsigned char,
                            uint16,uint32);
static void elf32be_check_ar_type(struct FFFuncs *,char *,
                                  struct Elf32_Ehdr *,unsigned char,
                                  unsigned char,uint32,uint16,uint16,uint16);
static char *elf32be_shstrtab(struct LinkFile *,struct Elf32_Ehdr *);
static char *elf32be_strtab(struct LinkFile *,struct Elf32_Ehdr *,int);
static struct Elf32_Sym *elf32be_symtab(struct LinkFile *,
                                        struct Elf32_Ehdr *,int);
static struct Elf32_Shdr *elf32be_shdr(struct LinkFile *lf,
                                       struct Elf32_Ehdr *,uint16);
static void elf32be_section(uint8 *,struct ObjectUnit *,
                            struct Elf32_Shdr *,int);
static int get_xtors_pri(char *);
static void elf32be_symbols(struct GlobalVars *,uint8 *,struct ObjectUnit *,
                            struct Elf32_Shdr *);
static void elf32be_reloc(struct GlobalVars *,struct Elf32_Ehdr *,
                          struct Section *,uint32,struct Elf32_Rela *,
                          bool,uint8);
static struct Section *elf32be_bssdefault(struct ObjectUnit *);
static int elf32_targetlink(struct GlobalVars *,struct LinkedSection *,
                            struct Section *);
static struct Symbol *elf32_lnksym(struct GlobalVars *,struct Section *,
                                   struct XReference *,struct FFFuncs *);
static void elf32_setlnksym(struct GlobalVars *,struct Symbol *,
                            struct XReference *,struct FFFuncs *);
static void elf32be_header(FILE *,uint16,uint16,uint32,uint32,uint32,
                           uint32,uint16,uint16,uint16);
static void elf32be_writeshdrs(FILE *,uint32,uint32);
static void elf32be_stdsymtab(struct GlobalVars *,uint8,uint8);
static void elf32be_addsymlist(struct GlobalVars *,struct SymTabList *,
                               uint8,uint8);
static void elf32be_makeshdrs(struct GlobalVars *);
static void elf32be_addrelocs(struct GlobalVars *,
                              uint8 (*)(uint8,char *,uint32));
static void elf32be_makeshstrtab(void);
static void elf32be_makestrtab(void);
static void elf32be_makesymtab(uint32);
static struct ShdrNode *elf32be_addshdr(uint32,uint32,uint32,uint32,uint32,
                                        uint32,uint32,uint32,uint32,uint32);
static uint32 elf32be_addsym(struct SymTabList *,char *,uint32,uint32,
                             uint8,uint8,uint16);
static uint32 elf32be_findsym(struct SymTabList *,char *,uint16);
static void elf32be_addrela(uint32,int32,uint32,uint8);

static void elf32_initlists(void);
static struct ShdrNode *elf32_newshdr(void);
static uint32 elf32_addshdrstr(char *);
static uint32 elf32_addstr(char *);
static uint32 elf32_addstrlist(struct StrTabList *,char *);
static uint8 elf32_getinfo(struct Symbol *);
static uint8 elf32_getbind(struct Symbol *);
static uint16 elf32_getshndx(struct Symbol *,uint8);
static void elf32_writesections(struct GlobalVars *,FILE *);
static void elf32_writestrtab(FILE *,struct StrTabList *);
static void elf32_writesymtab(FILE *,struct SymTabList *);
static void elf32_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                             */
/*****************************************************************/


#if defined(ELF32_PPC_BE) || defined(ELF32_POWERUP)

static int ppc32be_identify(char *name,uint8 *p,unsigned long plen)
/* identify ELF-PPC-32Bit-BigEndian */
{
  int id;

  /* first check for ancient PowerPC machine types */
  if ((id = elf32be_identify(&fff_elf32ppcbe,name,(struct Elf32_Ehdr *)p,
                             plen,ELFCLASS32,ELFDATA2MSB,EM_CYGNUS_POWERPC,
                             ELF_VER)) != ID_UNKNOWN)
    return (id);

  /* EM_PPC_OLD is used in Motorola's libmoto.a, for example... */
  if ((id = elf32be_identify(&fff_elf32ppcbe,name,(struct Elf32_Ehdr *)p,
                             plen,ELFCLASS32,ELFDATA2MSB,EM_PPC_OLD,
                             ELF_VER)) != ID_UNKNOWN)
    return (id);

  /* the normal case: EM_PPC */
  return (elf32be_identify(&fff_elf32ppcbe,name,(struct Elf32_Ehdr *)p,plen,
                           ELFCLASS32,ELFDATA2MSB,EM_PPC,ELF_VER));
}


static void ppc32be_readconv(struct GlobalVars *gv,struct LinkFile *lf)
/* Read elfppc32be 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);
        ppc32be_readELF(gv,lf,ai.data);
      }
    }
    else
      ierror("ppc32be_readconv(): archive %s corrupted since last access",
             lf->pathname);
  }
  else {
    lf->objname = lf->filename;
    ppc32be_readELF(gv,lf,lf->data);
  }
}


static void ppc32be_readELF(struct GlobalVars *gv,struct LinkFile *lf,
                            uint8 *elf)
{
  struct ObjectUnit *u;
  struct Elf32_Ehdr *ehdr = (struct Elf32_Ehdr *)elf;
  struct Elf32_Shdr *shdr;
  uint16 i,num_shdr;

  if (lf->type == ID_LIBARCH)  /* check ar-member for correct format */
    elf32be_check_ar_type(&fff_elf32ppcbe,lf->pathname,ehdr,
                          ELFCLASS32,ELFDATA2MSB,ELF_VER,
                          EM_PPC,EM_PPC_OLD,EM_CYGNUS_POWERPC);

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

  switch (read16be(ehdr->e_type)) {

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

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

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


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

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

      /* parse the other section headers */
      for (i=1; i<num_shdr; i++) {
        shdr = elf32be_shdr(lf,ehdr,i);
        switch (read32be(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:
            elf32be_symbols(gv,elf,u,shdr);  /* symbol definitions */
            break;
          case SHT_NOTE:
            ierror("ppc32be_readELF(): %s: Section header type %d is "
                   "currently not supported",lf->pathname,
                   (int)read32be(shdr->sh_type));
            break;
          default:
            /* section header type not needed in shared objects */
            error(60,lf->pathname,read32be(shdr->sh_type),lf->objname);
            break;
        }
      }
      break;


    case ET_EXEC: /* executable file */
      /* @@@ */
      ierror("ppc32be_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 ppc32be_secbase(struct GlobalVars *gv,char *name)
/* return default base address for a section with this name */
{
  return (0);  /* @@@ has to be fixed... */
}


static uint8 ppc32be_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 *ppc32be_bssdefault(struct ObjectUnit *ou)
/* Create a default BSS section for elfppc32be */
{
  return (elf32be_bssdefault(ou));
}


static int ppc32be_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 (elf32_targetlink(gv,ls,s));
}


static struct Symbol *ppc32be_lnksym(struct GlobalVars *gv,
                                     struct Section *sec,
                                     struct XReference *xref)
{
  return (elf32_lnksym(gv,sec,xref,&fff_elf32ppcbe));
}


static void ppc32be_setlnksym(struct GlobalVars *gv,struct Symbol *xdef,
                              struct XReference *xref)
{
  elf32_setlnksym(gv,xdef,xref,&fff_elf32ppcbe);
}


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

  struct Section *s;

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

  while (nrelocs--) {
    rtype = R_NONE;
    switch (ELF32_R_TYPE(read32be(((struct Elf32_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("ppc32be_relocs(): %s (%s): Reloc type %d in %s is currently "
               "not supported",lf->pathname,lf->objname,
               ELF32_R_TYPE(read32be(((struct Elf32_Rela *)data)->r_info)),
               sec_name);
        break;
    }

    if (rtype > R_NONE)
      elf32be_reloc(gv,(struct Elf32_Ehdr *)elf,s,symndx,
                    (struct Elf32_Rela *)data,rela,rtype);
    data += entsize;  /* next reloc */
  }

}

#endif /* ELF32_PPC_BE || ELF32_POWERUP */


#ifdef ELF32_POWERUP

static struct Symbol *powerup_lnksym(struct GlobalVars *gv,
                                     struct Section *sec,
                                     struct XReference *xref)
{
  struct Symbol *sym;

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

  if (!gv->dest_object) {
    if (!strcmp(linkerdb,xref->name)) {  /* _LinkerDB */
      sym = addlnksymbol(&fff_elf32powerup,NULL,linkerdb,
                         fff_elf32powerup.baseoff,SYM_RELOC,
                         SYMF_LNKSYM,SYMI_OBJECT,SYMB_GLOBAL,0);
      sym->extra = SYMX_SPECIAL|LINKERDB;
      return (sym);  /* new linker symbol created */
    }
  }

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


static void powerup_setlnksym(struct GlobalVars *gv,struct Symbol *xdef,
                              struct XReference *xref)
{
  if ((xdef->flags & SYMF_LNKSYM) && (xdef->extra & SYMX_SPECIAL)) {
    struct LinkedSection *sdsec = smalldata_section(gv);

    /* currently, there is only _LinkerDB... set small data section */
    xdef->relsect = (struct Section *)sdsec->sections.first;

    xdef->flags &= ~SYMF_LNKSYM;  /* do not init again */
  }
  else
    ppc32be_setlnksym(gv,xdef,xref);
}

#endif /* ELF32_POWERUP */


static int elf32be_identify(struct FFFuncs *ff,char *name,
                            struct Elf32_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 Elf32_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 Elf32_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 &&
        read32be(p->e_version)==ver && read16be(p->e_machine)==machine) {
      switch (read16be(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 elf32be_check_ar_type(struct FFFuncs *ff,char *name,
                                  struct Elf32_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 = read16be(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 &&
        read32be(ehdr->e_version)==ver && (m==mach1 || m==mach2 || m==mach3)) {
      switch (read16be(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 *elf32be_shstrtab(struct LinkFile *lf,struct Elf32_Ehdr *ehdr)
/* returns a pointer to the section header string table, if present, */
/* or NULL, otherwise */
{
  uint16 i;
  struct Elf32_Shdr *shdr;
  char *stab;

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


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

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


static struct Elf32_Sym *elf32be_symtab(struct LinkFile *lf,
                                        struct Elf32_Ehdr *ehdr,int idx)
/* returns a pointer to the symbol table */
{
  static char *tabname = "symbol";
  struct Elf32_Shdr *shdr;
  struct Elf32_Sym *symtab;

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


static struct Elf32_Shdr *elf32be_shdr(struct LinkFile *lf,
                                       struct Elf32_Ehdr *ehdr,uint16 idx)
/* return pointer to section header #idx */
{
  struct Elf32_Shdr *shdr;

  if (idx < read16be(ehdr->e_shnum)) {
    shdr = (struct Elf32_Shdr *)(((char *)ehdr) + ((read32be(ehdr->e_shoff) +
           (uint32)read16be(ehdr->e_shentsize) * (uint32)idx)));
    if (((uint8 *)shdr < lf->data) ||
        (((uint8 *)shdr)+read16be(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 elf32be_section(uint8 *elf,struct ObjectUnit *ou,
                            struct Elf32_Shdr *shdr,int shndx)
/* create a new section */
{
  char *sec_name = shstrtab + read32be(shdr->sh_name);
  uint8 *data = elf + read32be(shdr->sh_offset);
  unsigned long size = (unsigned long)read32be(shdr->sh_size);
  uint32 f = read32be(shdr->sh_flags);
  struct LinkFile *lf = ou->lnkfile;
  uint8 type=ST_DATA,flags=0;
  struct Section *s;

  if (read32be(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(read32be(shdr->sh_addralign));
  addtail(&ou->sections,&s->n);  /* add Section to ObjectUnit */
}


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 elf32be_symbols(struct GlobalVars *gv,uint8 *elf,
                            struct ObjectUnit *ou,struct Elf32_Shdr *shdr)
/* convert ELF symbol definitions into internal format */
{
  struct LinkFile *lf = ou->lnkfile;
  uint8 *data = elf + read32be(shdr->sh_offset);
  unsigned long entsize = read32be(shdr->sh_entsize);
  int nsyms = (int)(read32be(shdr->sh_size) / (uint32)entsize);
  char *strtab = elf32be_strtab(lf,(struct Elf32_Ehdr *)elf,
                                read32be(shdr->sh_link));
  struct Section *sec,*firstsec;
  struct Elf32_Sym *elfsym;

  if ((data < lf->data) || 
      (data + read32be(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 = elf32be_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 Elf32_Sym *)(data += entsize);
    symname = strtab + read32be(elfsym->st_name);
    switch (shndx = (int)read16be(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 (ELF32_ST_TYPE(*elfsym->st_info) != STT_SECTION)
            error(53,lf->pathname,symname,lf->objname,shndx);
        }
        type = SYM_RELOC;
        break;
    }

    if ((objinfo = ELF32_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 (ELF32_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,ELF32_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,read32be(elfsym->st_value),type,0,
                     objinfo,read32be(elfsym->st_size));
      }
      else {
        if (sym = addsymbol(gv,sec,symname,read32be(elfsym->st_value),type,
                            0,objinfo,objbind,read32be(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);
        }
      }
      if (objbind == SYMB_GLOBAL && !gv->dest_object) {
      /* symbol describes an automatic vbcc con-/destructor function ? */
        if (!strncmp(vbcc_INIT,symname,sizeof(vbcc_INIT)-1))
          new_priptr(sec->obj,xtors_objname,elf32_symnames[CTORS],
                     get_xtors_pri(symname+(sizeof(vbcc_INIT)-1)),symname,0);
        else if (!strncmp(vbcc_EXIT,symname,sizeof(vbcc_EXIT)-1))
          new_priptr(sec->obj,xtors_objname,elf32_symnames[DTORS],
                     get_xtors_pri(symname+(sizeof(vbcc_EXIT)-1)),symname,0);
      }
    }
  }
}


static void elf32be_reloc(struct GlobalVars *gv,struct Elf32_Ehdr *elf,
                          struct Section *s,uint32 symndx,
                          struct Elf32_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 Elf32_Shdr *symhdr = elf32be_shdr(lf,elf,symndx);
  struct Elf32_Sym *sym = elf32be_symtab(lf,elf,symndx) +
                          ELF32_R_SYM(read32be(rel->r_info));
  uint32 offs = read32be(rel->r_offset);
  uint32 shndx = (uint32)read16be(sym->st_shndx);
  int32 a;

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

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

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

  else if (ELF32_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 (ELF32_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)read32be(sym->st_value) + a);
  }

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


static struct Section *elf32be_bssdefault(struct ObjectUnit *ou)
/* Create a default BSS section for ELF32 BigEndian */
{
  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 elf32_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 *elf32_lnksym(struct GlobalVars *gv,
                                   struct Section *sec,
                                   struct XReference *xref,
                                   struct FFFuncs *tf)
/* Check for common ELF32 linker symbols. */
{
  struct Symbol *sym;
  int i;

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

  if (!gv->dest_object) {
    for (i=0; i<ELF32_LNKSYMS; i++) {
      if (!strcmp(elf32_symnames[i],xref->name)) {
        sym = addlnksymbol(tf,NULL,elf32_symnames[i],0,SYM_ABS,
                           SYMF_LNKSYM,SYMI_OBJECT,SYMB_GLOBAL,0);
        sym->extra = i;             /* for easy ident. in elf32_setlnksym */
        switch (i) {
          case SDABASE:
          case SDA2BASE:
            sym->type = SYM_RELOC;
            sym->value = (uint32)tf->baseoff;
            break;
        }
        return (sym);  /* new linker symbol created */
      }
    }
  }
  return (NULL);
}


static void elf32_setlnksym(struct GlobalVars *gv,struct Symbol *xdef,
                            struct XReference *xref,struct FFFuncs *tf)
/* Initialize common ELF32 linker symbol structure during resolve_xref() */
{
  if (xdef->flags & SYMF_LNKSYM) {
    struct LinkedSection *ls;

    switch (xdef->extra) {
      case SDABASE:
        if (!(ls = find_lnksec(gv,sdata,ST_DATA,0,0)))
          if (!(ls = find_lnksec(gv,sbss,ST_UDATA,0,0)))
            ls = smalldata_section(gv);
        xdef->relsect = (struct Section *)ls->sections.first;
        break;
      case SDA2BASE:
        if (!(ls = find_lnksec(gv,sdata2,ST_DATA,0,0)))
          if (!(ls = find_lnksec(gv,sbss2,ST_UDATA,0,0)))
            ls = smalldata_section(gv);
        xdef->relsect = (struct Section *)ls->sections.first;
        break;
      case CTORS:
        if (ls = find_lnksec(gv,elf32_symnames[CTORS],0,0,0)) {
          xdef->type = SYM_RELOC;
          xdef->relsect = (struct Section *)ls->sections.first;
        }
        break;
      case DTORS:
        if (ls = find_lnksec(gv,elf32_symnames[DTORS],0,0,0)) {
          xdef->type = SYM_RELOC;
          xdef->relsect = (struct Section *)ls->sections.first;
        }
        break;
    }
    xdef->flags &= ~SYMF_LNKSYM;  /* do not init again */
  }
}



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


#ifdef ELF32_PPC_BE

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


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

  elf32_initlists();
  elf32be_addsym(&symlist,nullstr,0,0,0,0,SHN_UNDEF);
  if (gv->discard_local < DISLOC_ALL)
    elf32be_stdsymtab(gv,STB_LOCAL,STT_FILE);

  elf32be_makeshdrs(gv);
  if (gv->discard_local < DISLOC_ALL)
    elf32be_stdsymtab(gv,STB_LOCAL,0);
  symlist.global_index = symlist.nextindex;
  if (gv->strip_symbols < STRIP_ALL) { /* although not recomm. for objects */
    elf32be_stdsymtab(gv,STB_WEAK,0);
    elf32be_stdsymtab(gv,STB_GLOBAL,0);
  }
  elf32be_addrelocs(gv,ppc32be_getrel);

  shstrndx = shdrindex;
  elf32be_makeshstrtab();
  sh_off = elfoffset;
  stabndx = shdrindex;
  elfoffset += (shdrindex+2) * sizeof(struct Elf32_Shdr);
  elf32be_makesymtab(shdrindex+1);
  elf32be_makestrtab();

  elf32be_header(f,ET_REL,EM_PPC,0,0,sh_off,0,0,shdrindex,shstrndx);
  elf32_writesections(gv,f);
  elf32_writestrtab(f,&shstrlist);
  fwrite_align(f,2,ftell(f));
  elf32be_writeshdrs(f,elfoffset,stabndx);
  elf32_writesymtab(f,&symlist);
  elf32_writestrtab(f,&stringlist);
  fwrite_align(f,2,ftell(f));
  elf32_writerelocs(gv,f);
}


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


static uint8 ppc32be_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 /* ELF32_PPC_BE */


#ifdef ELF32_POWERUP

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


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

  elf32_initlists();
  elf32be_addsym(&symlist,nullstr,0,0,0,0,SHN_UNDEF);
  if (gv->discard_local < DISLOC_ALL)
    elf32be_stdsymtab(gv,STB_LOCAL,STT_FILE);

  elf32be_makeshdrs(gv);
  if (gv->discard_local < DISLOC_ALL)
    elf32be_stdsymtab(gv,STB_LOCAL,0);
  symlist.global_index = symlist.nextindex;
#if 0 /*@@@ strip all for object?*/
  if (gv->strip_symbols < STRIP_ALL) {
    elf32be_stdsymtab(gv,STB_WEAK,0);
    elf32be_stdsymtab(gv,STB_GLOBAL,0);
  }
#endif
  elf32be_addrelocs(gv,powerup_getrel);

  shstrndx = shdrindex;
  elf32be_makeshstrtab();
  sh_off = elfoffset;
  stabndx = shdrindex;
  elfoffset += (shdrindex+2) * sizeof(struct Elf32_Shdr);
  elf32be_makesymtab(shdrindex+1);
  elf32be_makestrtab();

  elf32be_header(f,ET_REL,EM_PPC,0,0,sh_off,0,0,shdrindex,shstrndx);
  elf32_writesections(gv,f);
  elf32_writestrtab(f,&shstrlist);
  fwrite_align(f,2,ftell(f));
  elf32be_writeshdrs(f,elfoffset,stabndx);
  elf32_writesymtab(f,&symlist);
  elf32_writestrtab(f,&stringlist);
  fwrite_align(f,2,ftell(f));
  elf32_writerelocs(gv,f);
}


static void powerup_writeexec(struct GlobalVars *gv,FILE *f)
/* creates a target-elf32powerup executable file, which in reality */
/* is a relocatable object too, but all symbol references have */
/* been resolved */
{
  uint32 sh_off,shstrndx,stabndx;
  struct LinkedSection *ls;

  /* rename .ctors into .ctor_ and .dtors into .dtor_, otherwise */
  /* the PowerUp ELF loader will rearrange the pointers */
  if (ls = find_lnksec(gv,elf32_symnames[CTORS],0,0,0))
    ls->name[5] = '_';
  if (ls = find_lnksec(gv,elf32_symnames[DTORS],0,0,0))
    ls->name[5] = '_';

  elf32_initlists();
  elf32be_addsym(&symlist,nullstr,0,0,0,0,SHN_UNDEF);
  if (gv->discard_local < DISLOC_ALL)
    elf32be_stdsymtab(gv,STB_LOCAL,STT_FILE);

  elf32be_makeshdrs(gv);
  if (gv->discard_local < DISLOC_ALL)
    elf32be_stdsymtab(gv,STB_LOCAL,0);
  symlist.global_index = symlist.nextindex;
  if (gv->strip_symbols < STRIP_ALL) {
    elf32be_stdsymtab(gv,STB_WEAK,0);
    elf32be_stdsymtab(gv,STB_GLOBAL,0);
  }
  elf32be_addrelocs(gv,powerup_getrel);

  shstrndx = shdrindex;
  elf32be_makeshstrtab();
  sh_off = elfoffset;
  stabndx = shdrindex;
  elfoffset += (shdrindex+2) * sizeof(struct Elf32_Shdr);
  elf32be_makesymtab(shdrindex+1);
  elf32be_makestrtab();

  elf32be_header(f,ET_REL,EM_PPC,0,0,sh_off,0,0,shdrindex,shstrndx);
  elf32_writesections(gv,f);
  elf32_writestrtab(f,&shstrlist);
  fwrite_align(f,2,ftell(f));
  elf32be_writeshdrs(f,elfoffset,stabndx);
  elf32_writesymtab(f,&symlist);
  elf32_writestrtab(f,&stringlist);
  fwrite_align(f,2,ftell(f));
  elf32_writerelocs(gv,f);
}


static uint8 powerup_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_elf32powerup.tname,reloc_name[rtype],sname,offs);
      break;
  }
  return (R_NONE);
}
#endif /* ELF32_POWERUP */


static void elf32be_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 Elf32_Ehdr eh;

  memset(&eh,0,sizeof(struct Elf32_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;
  write16be(eh.e_type,type);
  write16be(eh.e_machine,mach);
  write32be(eh.e_version,ELF_VER);
  write32be(eh.e_entry,entry);
  write32be(eh.e_phoff,phoff);
  write32be(eh.e_shoff,shoff);
  write32be(eh.e_flags,flags);
  write16be(eh.e_ehsize,sizeof(struct Elf32_Ehdr));
  write16be(eh.e_phentsize,phnum ? sizeof(struct Elf32_Phdr):0);
  write16be(eh.e_phnum,phnum);
  write16be(eh.e_shentsize,shnum ? sizeof(struct Elf32_Shdr):0);
  write16be(eh.e_shnum,shnum);
  write16be(eh.e_shstrndx,shstrndx);
  fwritex(f,&eh,sizeof(struct Elf32_Ehdr));
}


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

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


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


static void elf32be_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 = elf32_getinfo(sym);
      uint8 symbind = elf32_getbind(sym);

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


static void elf32be_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;
    elf32be_addshdr(elf32_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) */
    elf32be_addsym(&symlist,nullstr,0,0,STB_LOCAL,STT_SECTION,
                   (uint16)ls->index);
    ls = nextls;  /* next section */
  }
}


static void elf32be_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) {
      elf32be_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 Elf32_Rel) : sizeof(struct Elf32_Rela);
      rel = nextrel;
    }

    /* external references */
    xref = (struct XReference *)ls->xrefs.first;
    while (nextxref = (struct XReference *)xref->n.next) {
      /* undefined symbol */
      if (!(symidx = elf32be_findsym(&symlist,xref->name,SHN_UNDEF)))
        symidx = elf32be_addsym(&symlist,xref->name,0,0,STB_GLOBAL,
                                STT_NOTYPE,SHN_UNDEF);
      /* symbol's relocation */
      elf32be_addrela((uint32)xref->offset,xref->addend,symidx,
                      getrel(xref->type,ls->name,(uint32)xref->offset));
      roffs += gv->short_rel ?
               sizeof(struct Elf32_Rel) : sizeof(struct Elf32_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);
      elf32be_addshdr(elf32_addshdrstr(relname),
                      gv->short_rel ? SHT_REL:SHT_RELA,
                      0,0,sroffs,roffs-sroffs,0,(uint32)ls->index,4,
                      gv->short_rel ? sizeof(struct Elf32_Rel) : 
                                      sizeof(struct Elf32_Rela));
      /* sroffs is relative and will be corrected later */
      /* sh_link will be initialized, when .symtab exists */
    }
    ls = nextls;  /* next section */
  }
}


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


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


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


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

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


static uint32 elf32be_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);
  write32be(sym->s.st_name,elf32_addstr(name));
  write32be(sym->s.st_value,value);
  write32be(sym->s.st_size,size);
  *sym->s.st_info = ELF32_ST_INFO(bind,type);
  write16be(sym->s.st_shndx,shndx);
  return (sym->index);
}


static uint32 elf32be_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 (read16be(sym->s.st_shndx) == shndx)
        return (sym->index);
    chain = &sym->hashchain;
  }
  return (0);
}


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

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


static void elf32_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 Elf32_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 *));

  elf32_addshdrstr(nullstr);  /* first string is always "" */
  symtabidx = elf32_addshdrstr(".symtab");
  strtabidx = elf32_addshdrstr(".strtab");
  shstrtabidx = elf32_addshdrstr(".shstrtab");
  elf32_addstr(nullstr);
  elf32_newshdr();          /* first Shdr is always zero */
}


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

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


static uint32 elf32_addshdrstr(char *s)
{
  return (elf32_addstrlist(&shstrlist,s));
}


static uint32 elf32_addstr(char *s)
{
  return (elf32_addstrlist(&stringlist,s));
}


static uint32 elf32_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 elf32_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("elf32be_addsymlist(): STT_SECTION symbol detected");
      type = STT_SECTION;
      break;
    case SYMI_FILE:
      type = STT_FILE;
      break;
    default:
      ierror("elf32_getinfo(): Illegal symbol info: %d",(int)sym->info);
      break;
  }
  return (type);
}


static uint8 elf32_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("elf32_getbind(): Illegal symbol binding: %d",(int)sym->bind);
      break;
  }
  return (bind);
}


static uint16 elf32_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("elf32_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("elf32_getshndx(): Illegal symbol type: %d",(int)sym->type);
      break;
  }
  return (shndx);
}


static void elf32_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 elf32_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 elf32_writesymtab(FILE *f,struct SymTabList *sl)
{
  struct SymbolNode *sym;

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


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

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


#endif
