/* $VER: vlink targets.c V0.6c (05.02.99)
 *
 * This file is part of vlink, a portable linker for multiple
 * object formats.
 * Copyright (c) 1997-99  Frank Wille
 *
 * vlink is freeware and part of the portable and retargetable ANSI C
 * compiler vbcc, copyright (c) 1995-99 by Volker Barthelmann.
 * vlink may be freely redistributed as long as no modifications are
 * made and nothing is charged for it. Non-commercial usage is allowed
 * without any restrictions.
 * EVERY PRODUCT OR PROGRAM DERIVED DIRECTLY FROM MY SOURCE MAY NOT BE
 * SOLD COMMERCIALLY WITHOUT PERMISSION FROM THE AUTHOR.
 *
 *
 * v0.6c (05.02.99) phx
 *       ctors/dtors sections always start with 0xffffffff to provide
 *       compatibility with most file formats (e.g. ELF).
 * v0.6a (19.12.98) phx
 *       Support for little endian object file formats.
 *       readsection()/writesection() obey current endianess of linking
 *       process.
 *       make_priptr_objects() updates linker symbols during add_da-
 *       ta_section().
 * v0.6  (24.10.98) phx
 *       find_lnksec() finds a LinkedSection by its name and/or type
 *       (needed for ELF small data support).
 *       New target elf32powerup, which supports the PPC coprocessor
 *       boards from Phase5.
 * v0.5e (05.10.98) phx
 *       new_priptr(), add_priptrs(), make_priptr_objects(). Support for
 *       artificial sections and objects, containing longwords sorted by
 *       priority. These sections are automatically terminated by a NULL
 *       pointer entry. Currently only used by ados-target for constructor
 *       and destructor function pointer lists (__ctors, __dtors).
 * v0.5c (08.07.98) phx
 *       art_objunit() creates an artificial ObjectUnit + LinkFile.
 * v0.5  (27.06.98) phx
 *       addlnksymbol(), findlnksymbol() for target-specific linker
 *       symbol support.
 *       smalldata_section() returns a pointer to the first section,
 *       which is referenced base relative.
 * v0.3  (17.04.98) phx
 *       addlocsymbol() allows multiple defintions of local
 *       symbols with the same name.
 *       addreloc() adds another relocation to a section.
 *       relocsize() determines size in bytes of a relocation type.
 *       addxref() requires the addend as additional argument.
 *       ar-support functions for library archives.
 * v0.1  (27.02.98) phx
 *       First version that seems to link AmigaOS ADOS and EHF
 *       objects and libraries. Many common features, like linking
 *       sections together which have relative references, are
 *       still missing. Also, PowerPC-ELF32 support is about to come.
 * v0.0  (05.08.97) phx
 *       File created.
 */


#define TARGETS_C
#include "vlink.h"


unsigned long findsecbase(struct GlobalVars *,char *);
struct Symbol *findsymbol(struct GlobalVars *,char *name);
struct Symbol *addsymbol(struct GlobalVars *,struct Section *,char *,
                         uint32,uint8,uint8,uint8,uint8,uint32);
void addlocsymbol(struct GlobalVars *,struct Section *,char *,uint32,
                  uint8,uint8,uint8,uint32);
void addglobsym(struct GlobalVars *,struct Symbol *);
struct Symbol *addlnksymbol(struct FFFuncs *,struct Section *,char *,
                            uint32,uint8,uint8,uint8,uint8,uint32);
struct Symbol *findlnksymbol(struct FFFuncs *,char *);
void addreloc(struct Section *,struct Section *,uint32,uint32,uint8,int32);
void addxref(struct GlobalVars *,struct Section *,char *,uint32,uint8,
             uint8,int32);
int32 readsection(struct GlobalVars *,uint8 *,uint8);
void writesection(struct GlobalVars *,uint8 *,uint8,uint32);
uint8 relocsize(uint8);
struct Section *create_section(struct ObjectUnit *,char *,uint8 *,
                               unsigned long);
struct Section *find_sect_type(struct ObjectUnit *,uint8,uint8);
struct Section *find_sect_id(struct ObjectUnit *,uint32);
struct LinkedSection *find_lnksec(struct GlobalVars *,char *,uint8,
                                  uint8,uint8);
struct LinkedSection *smalldata_section(struct GlobalVars *);
void add_objunit(struct GlobalVars *,struct ObjectUnit *,bool);
struct ObjectUnit *create_objunit(struct LinkFile *,char *);
struct ObjectUnit *art_objunit(char *,uint8 *,unsigned long,uint8);
void new_priptr(struct ObjectUnit *,char *,char *,int,char *,uint32);
void add_priptrs(struct GlobalVars *,struct ObjectUnit *);
void make_priptr_objects(struct GlobalVars *);
bool ar_init(struct ar_info *,char *,unsigned long,char *);
bool ar_extract(struct ar_info *);

static struct Section *getsecptr(struct list *,uint32);
static struct Section *add_data_section(struct GlobalVars *,
                                        struct ObjectUnit *,char *,uint8 *,
                                        unsigned long);


struct FFFuncs *fff[] = {
#ifdef ADOS
  &fff_amigaos,
#endif
#ifdef EHF
  &fff_ehf,
#endif
#ifdef ELF32_PPC_BE
  &fff_elf32ppcbe,
#endif
#ifdef ELF32_POWERUP
  &fff_elf32powerup,
#endif
  NULL
};

const char *sym_type[] = { "undef","abs","reloc","common" };
const char *sym_info[] = { ""," object"," function"," section"," file" };
const char *sym_bind[] = { "","local ","global ","weak " };



unsigned long findsecbase(struct GlobalVars *gv,char *name)
/* scan the secbases list for a base address definition */
{
  struct SecBase *sb = gv->secbases;

  while (sb) {
    if (!strcmp(name,sb->name))
      return (sb->base);
    sb = sb->next;
  }
}


struct Symbol *findsymbol(struct GlobalVars *gv,char *name)
/* return pointer to Symbol, if present */
{
  struct Symbol *sym = gv->symbols[elf_hash(name)%SYMHTABSIZE];

  while (sym) {
    if (!strcmp(name,sym->name))
      return (sym);  /* symbol found! */
    sym = sym->glob_chain;
  }
  return (NULL);
}


struct Symbol *addsymbol(struct GlobalVars *gv,struct Section *s,char *name,
                         uint32 val,uint8 type,uint8 flags,uint8 info,
                         uint8 bind,uint32 size)
/* Define a new symbol. If defined twice in the same object unit, then */
/* return a pointer to its first definition. Defining the symbol twice */
/* globally, is only allowed in different object units of a library. */
{
  struct Symbol *sym;
  struct ObjectUnit *ou = s->obj;
  struct Symbol **chain = &s->obj->objsyms[elf_hash(name)%OBJSYMHTABSIZE];

  while (sym = *chain) {
    if (!strcmp(name,sym->name))
      return (sym);  /* symbol already defined - don't define it twice */
      /* The target-specific routines have to handle this case. */
    chain = &sym->obj_chain;
  }

  *chain = sym = alloc(sizeof(struct Symbol));
  sym->glob_chain = sym->obj_chain = NULL;
  sym->name = name;
  sym->value = val;
  sym->relsect = s;
  sym->type = type;
  sym->flags = flags;
  sym->info = info;
  sym->bind = bind;
  sym->size = size;

  if (bind==SYMB_GLOBAL || bind==SYMB_WEAK)
    addglobsym(gv,sym);

  return (NULL);  /* ok, new symbol was created */
}


void addlocsymbol(struct GlobalVars *gv,struct Section *s,char *name,
                  uint32 val,uint8 type,uint8 flags,uint8 info,uint32 size)
/* Define a new local symbol. Local symbols are allowed to be */
/* multiply defined. */
{
  struct Symbol *sym;
  struct Symbol **chain = &s->obj->objsyms[elf_hash(name)%OBJSYMHTABSIZE];

  while (sym = *chain)
    chain = &sym->obj_chain;
  *chain = sym = alloc(sizeof(struct Symbol));
  sym->glob_chain = sym->obj_chain = NULL;
  sym->name = name;
  sym->value = val;
  sym->relsect = s;
  sym->type = type;
  sym->flags = flags;
  sym->info = info;
  sym->bind = SYMB_LOCAL;
  sym->size = size;
}


void addglobsym(struct GlobalVars *gv,struct Symbol *newsym)
/* insert symbol into global symbol hash table */
{
  struct Symbol **chain = &gv->symbols[elf_hash(newsym->name)%SYMHTABSIZE];
  struct Symbol *sym;
  struct ObjectUnit *newou = newsym->relsect->obj;

  while (sym = *chain) {
    if (!strcmp(newsym->name,sym->name)) {
      if (sym->bind == SYMB_GLOBAL) {
        /* symbol already defined with global binding */
        if (newsym->bind==SYMB_GLOBAL && newou->lnkfile->type<ID_LIBBASE) {
          if (newsym->type==SYM_COMMON && sym->type==SYM_COMMON) {
            if (newsym->size > sym->size) {
              /* replace by common symbol with bigger size */
              newsym->glob_chain = sym->glob_chain;
              sym->glob_chain = NULL;
              break;
            }
          }
          else
            /* Global symbol "x" is already defined in... */
            error(19,newou->lnkfile->pathname,newsym->name,newou->objname,
                  sym->relsect->obj->lnkfile->pathname,
                  sym->relsect->obj->objname);
        }
        return;  /* don't replace global symbols */
      }

      else {
        if (newsym->bind == SYMB_WEAK)
          return;  /* don't replace weak by weak */
        /* replace weak symbol */
        newsym->glob_chain = sym->glob_chain;
        sym->glob_chain = NULL;
        break;
      }
    }
    chain = &sym->glob_chain;
  }

  *chain = newsym;
  if (trace_sym_access(gv,newsym->name))
    fprintf(stderr,"Symbol %s defined in section %s in %s\n",
            newsym->name,newsym->relsect->name,getobjname(newou));
}


struct Symbol *addlnksymbol(struct FFFuncs *fffptr,struct Section *s,
                            char *name,uint32 val,uint8 type,uint8 flags,
                            uint8 info,uint8 bind,uint32 size)
/* Define a new, target-specific, linker symbol. */
{
  struct Symbol *sym;
  struct Symbol **chain;

  if (fffptr->lnksyms == NULL)
    fffptr->lnksyms = alloc_hashtable(LNKSYMHTABSIZE);
  chain = &fffptr->lnksyms[elf_hash(name)%LNKSYMHTABSIZE];

  while (sym = *chain)
    chain = &sym->obj_chain;
  *chain = sym = alloc(sizeof(struct Symbol));
  sym->glob_chain = sym->obj_chain = NULL;
  sym->name = name;
  sym->value = val;
  sym->relsect = s;
  sym->type = type;
  sym->flags = flags;
  sym->info = info;
  sym->bind = bind;
  sym->size = size;
  return (sym);
}


struct Symbol *findlnksymbol(struct FFFuncs *fffptr,char *name)
/* return pointer to Symbol, if present */
{
  struct Symbol *sym;

  if (fffptr->lnksyms) {
    sym = fffptr->lnksyms[elf_hash(name)%LNKSYMHTABSIZE];
    while (sym) {
      if (!strcmp(name,sym->name))
        return (sym);  /* symbol found! */
      sym = sym->obj_chain;
    }
  }
  return (NULL);
}


void addreloc(struct Section *cs,struct Section *rs,uint32 id,
              uint32 offset,uint8 rtype,int32 addend)
/* add a relocation to the specified section */
{
  struct Reloc *r = alloc(sizeof(struct Reloc));

  if (rs)
    r->relocsect.ptr = rs;
  else
    r->relocsect.id = id;
  r->offset = (unsigned long)offset;
  r->addend = addend;
  r->type = rtype;
  addtail(&cs->relocs,&r->n);  /* add to current section's Reloc list */
}


void addxref(struct GlobalVars *gv,struct Section *s,char *name,
             uint32 offset,uint8 rtype,uint8 size,int32 addend)
/* add external symbol reference */
{
  struct XReference *xref = alloczero(sizeof(struct XReference));

  xref->name = name;
  xref->offset = (unsigned long)offset;
  xref->type = rtype;
  xref->size = size;
  xref->addend = addend;
  addtail(&s->xrefs,&xref->n);
  if (trace_sym_access(gv,name))
    fprintf(stderr,"Symbol %s referenced in %s\n",name,getobjname(s->obj));
}


int32 readsection(struct GlobalVars *gv,uint8 *p,uint8 rtype)
/* Read 8-, 16- or 32-bit word from a section. The data-width is */
/* determined from the relocation type rtype. */
{
  int32 v;
  char be = gv->big_endian;

  switch (rtype) {

    case R_REL8:
    case R_BASEREL8:
      v = (int32)*p;
      break;

    case R_ADDR14:  /* PowerPC */
    case R_ADDR14_BRTAKEN:
    case R_ADDR14_BRNTAKEN:
    case R_REL14:
    case R_REL14_BRTAKEN:
    case R_REL14_BRNTAKEN:
      v = (int32)read16(be,p) & 0xfffffffc;
      break;

    case R_ADDR16:
    case R_ADDR16_LO:
    case R_ADDR16_HI:
    case R_ADDR16_HA:
    case R_REL16:
    case R_BASEREL16:
      v = (int32)read16(be,p);
      break;

    case R_ADDR26:  /* PowerPC */
    case R_REL26:
    case R_BASEREL26:
      v = (int32)(read32(be,p) & 0x3fffffc);
      if (v >= 0x2000000)
        v = -v;
      break;

    default:
      v = (int32)read32(be,p);
      break;
  }
  return (v);
}


void writesection(struct GlobalVars *gv,uint8 *p,uint8 rtype,uint32 x)
/* Write a 8-, 16- or 32-bit word into a section. The data-width is */
/* determined from the relocation type rtype. */
{
  char be = gv->big_endian;

  switch (rtype) {

    case R_REL8:
    case R_BASEREL8:
      *p = (uint8)x;
      break;

    case R_ADDR14:  /* PowerPC */
    case R_ADDR14_BRTAKEN:
    case R_ADDR14_BRNTAKEN:
    case R_REL14:
    case R_REL14_BRTAKEN:
    case R_REL14_BRNTAKEN:
      write16(be,p,(read16(be,p) & 3) | (uint16)(x & 0xfffc));
      break;

    case R_ADDR16:
    case R_ADDR16_LO:
    case R_ADDR16_HI:
    case R_ADDR16_HA:
    case R_REL16:
    case R_BASEREL16:
      write16(be,p,(uint16)(x & 0xffff));
      break;

    case R_ADDR26:  /* PowerPC */
    case R_REL26:
    case R_BASEREL26:
      write32(be,p,(read32(be,p) & 0xfc000000) | (x & 0x3ffffff));
      break;

    default:
      write32(be,p,x);
      break;
  }
}


uint8 relocsize(uint8 rtype)
/* determine size of relocation in bytes for the specified reloc type */
{
  switch (rtype) {

    case R_REL8:
    case R_BASEREL8:
      return (1);

    case R_ADDR14:
    case R_ADDR14_BRTAKEN:
    case R_ADDR14_BRNTAKEN:
    case R_REL14:
    case R_REL14_BRTAKEN:
    case R_REL14_BRNTAKEN:
    case R_ADDR16:
    case R_ADDR16_LO:
    case R_ADDR16_HI:
    case R_ADDR16_HA:
    case R_REL16:
    case R_BASEREL16:
      return (2);

    case R_ADDR26:
    case R_REL26:
    case R_BASEREL26:
      return (3);
  }
  return (4);
}


struct Section *create_section(struct ObjectUnit *ou,char *name,
                               uint8 *data,unsigned long size)
/* creates and initializes a Section node */
{
  static char *noname = "";
  static uint32 idcnt = 0;
  struct Section *s = alloczero(sizeof(struct Section));

  if (name)
    s->name = name;
  else
    s->name = noname;
  s->data = data;
  s->size = size;
  s->obj = ou;
  s->id = idcnt++;
  initlist(&s->relocs);  /* empty relocation list */
  initlist(&s->xrefs);   /* empty external references list */
  return (s);
}


struct Section *find_sect_type(struct ObjectUnit *ou,uint8 type,uint8 prot)
/* find a section in current object unit with approp. type and protection */
{
  struct Section *sec = (struct Section *)ou->sections.first;
  struct Section *nextsec;

  while (nextsec = (struct Section *)sec->n.next) {
    if (sec->type==type && (sec->protection&prot)==prot)
      return (sec);
    sec = nextsec;
  }
  return (NULL);
}


struct Section *find_sect_id(struct ObjectUnit *ou,uint32 id)
/* find a section by its identification value */
{
  struct Section *sec = (struct Section *)ou->sections.first;
  struct Section *nextsec;

  while (nextsec = (struct Section *)sec->n.next) {
    if (sec->id == id)
      return (sec);
    sec = nextsec;
  }
  return (NULL);
}


struct LinkedSection *find_lnksec(struct GlobalVars *gv,char *name,
                                  uint8 type,uint8 flags,uint8 fmask)
/* Return pointer to the first section which fits the passed */
/* name-, type- and flags-conditions. If a condition is 0, then */
/* it is ignored. If no appropriate section was found, return NULL. */
{
  struct LinkedSection *ls = (struct LinkedSection *)gv->lnksec.first;
  struct LinkedSection *nextls;

  while (nextls = (struct LinkedSection *)ls->n.next) {
    bool found = TRUE;

    if (name)
      if (strcmp(ls->name,name))
        found = FALSE;
    if (type)
      if (ls->type != type)
        found = FALSE;
    if (fmask)
      if ((ls->flags&fmask) != (flags&fmask))
        found = FALSE;
    if (found)
      return (ls);
    ls = nextls;
  }
  return (NULL);
}


struct LinkedSection *smalldata_section(struct GlobalVars *gv)
/* Return pointer to first small data LinkedSection. If not existing, */
/* return first data/bss section or the first section. */
{
  struct LinkedSection *ls = (struct LinkedSection *)gv->lnksec.first;
  struct LinkedSection *ldls=NULL,*nextls;

  while (nextls = (struct LinkedSection *)ls->n.next) {
    if (ls->type==ST_DATA || ls->type==ST_UDATA && ldls==NULL)
      ldls = ls;
    if (ls->flags & SF_SMALLDATA)  /* first SD section found! */
      return (ls);
    ls = nextls;
  }
  return (ldls?ldls:((struct LinkedSection *)gv->lnksec.first));
}


void add_objunit(struct GlobalVars *gv,struct ObjectUnit *ou,bool fixrelocs)
/* adds an ObjectUnit to the approriate list */
{
  if (ou) {
    switch (ou->lnkfile->type) {
      case ID_OBJECT:
      case ID_EXECUTABLE:
        addtail(&gv->selobjects,&ou->n);
        add_priptrs(gv,ou);
        break;
      case ID_LIBARCH:
        addtail(&gv->libobjects,&ou->n);
        break;
      case ID_SHAREDOBJ:
        addtail(&gv->sharedobjects,&ou->n);
        break;
      default:
        ierror("add_objunit(): Link File type = %d",
               (int)ou->lnkfile->type);
    }

    if (fixrelocs) {  /* convert section index into address */
      struct Section *sec = (struct Section *)ou->sections.first;
      struct Section *nextsec;

      while (nextsec = (struct Section *)sec->n.next) {
        struct Reloc *r = (struct Reloc *)sec->relocs.first;
        struct Reloc *nextr;

        while (nextr = (struct Reloc *)r->n.next) {
          r->relocsect.ptr = getsecptr(&ou->sections,r->relocsect.id);
          r = nextr;
        }
        sec = nextsec;
      }
    }
  }
}


static struct Section *getsecptr(struct list *seclist,uint32 index)
/* return pointer to i. section in list */
{
  int i = (int)index;
  struct Section *s = (struct Section *)seclist->first;
  struct Section *nexts;

  while (i--) {
    if (s->n.next == NULL)
      ierror("getsecptr(): Not enough sections in list for index=%u",index);
    else
      s = (struct Section *)s->n.next;
  }
  return (s);
}
 

struct ObjectUnit *create_objunit(struct LinkFile *lf,char *objname)
/* creates and initializes an ObjectUnit node */
{
  struct ObjectUnit *ou = alloc(sizeof(struct ObjectUnit));
  static char *noname="";

  ou->lnkfile = lf;
  if (objname)
    ou->objname = objname;
  else
    ou->objname = noname;
  initlist(&ou->sections);  /* empty section list */
  ou->objsyms = alloc_hashtable(OBJSYMHTABSIZE);
  ou->flags = 0;
  initlist(&ou->pripointers);  /* empty PriPointer list */
  return (ou);
}


struct ObjectUnit *art_objunit(char *n,uint8 *d,unsigned long len,uint8 fmt)
/* creates and initializes an artificial linker-object */
{
  struct LinkFile *lf = alloczero(sizeof(struct LinkFile));

  lf->pathname = lf->filename = lf->objname = n;
  lf->data = d;
  lf->length = len;
  lf->format = fmt;
  lf->type = ID_ARTIFICIAL;
  return (create_objunit(lf,n));
}


void new_priptr(struct ObjectUnit *ou,char *objname,char *sec,int pri,
                char *xref,uint32 addend)
/* Inserts a new longword into the object's PriPointers list. */
{
  struct PriPointer *newpp = alloc(sizeof(struct PriPointer));

  newpp->objname = objname;
  newpp->secname = sec;
  newpp->priority = pri;
  newpp->xrefname = xref;
  newpp->addend = addend;
  addtail(&ou->pripointers,&newpp->n);
}


void add_priptrs(struct GlobalVars *gv,struct ObjectUnit *ou)
/* Inserts all PriPointer nodes of an object into the global list. */
/* The node's position depends on 1. object name, 2. section name, */
/* 3. priority. */
{
  struct PriPointer *newpp,*nextpp,*pp;

  while (newpp = (struct PriPointer *)remhead(&ou->pripointers)) {
    pp = (struct PriPointer *)gv->pripointers.first;

    while (nextpp = (struct PriPointer *)pp->n.next) {
      int c;

      if ((c = strcmp(newpp->objname,pp->objname)) > 0)
        break;
      if (!c) {
        if ((c = strcmp(newpp->secname,pp->secname)) > 0)
          break;
        if (!c && newpp->priority < pp->priority)
          break;
      }
      pp = nextpp;
    }

    if (pp->n.pred)
      insertbefore(&newpp->n,&pp->n);
    else
      addhead(&gv->pripointers,&newpp->n);  /* first node in list */
  }
}


void make_priptr_objects(struct GlobalVars *gv)
/* Make artificial objects from PriPointer list entries. */
/* This function usually has to be called *before* linker_resolve(). */
{
  struct PriPointer *pp = (struct PriPointer *)gv->pripointers.first;
  struct PriPointer *nextpp,*firstpp = pp;
  unsigned long len = 0;
  uint32 *data;
  struct ObjectUnit *ou = NULL;
  struct Section *s = NULL;
  char be = gv->big_endian;

  while (nextpp = (struct PriPointer *)pp->n.next) {

    if (!ou) {
      int scmp = strcmp(pp->objname,firstpp->objname);

      if (!scmp) {
        len += sizeof(uint32);
        if (nextpp->n.next) {
          if (strcmp(pp->secname,nextpp->secname))
            /* include -1 word and terminating NULL pointer */
            len += 2*sizeof(uint32);
        }
        else {
          /* include -1 word and terminating NULL pointer */
          len += 2*sizeof(uint32);
          scmp = 1;
          pp = nextpp;
        }
      }
      if (scmp) {
        data = alloczero(len);
        ou = art_objunit(firstpp->objname,(uint8 *)data,len,gv->dest_format);
        nextpp = firstpp;  /* begin with first node of same object again */
        firstpp = pp;
      }
    }

    else {
      if (pp != firstpp) {
        /* The standard in most file formats is to start with the */
        /* number of function pointers, or with a -1 */
        if (!s) {
          s = add_data_section(gv,ou,pp->secname,(uint8 *)data,
                               2*sizeof(uint32));
          *data++ = 0xffffffff;
        }
        else if (strcmp(pp->secname,s->name)) {
          s = add_data_section(gv,ou,pp->secname,(uint8 *)(++data),
                               2*sizeof(uint32));
          *data++ = 0xffffffff;
        }
        write32(be,(uint8 *)data,pp->addend);
        ++data;
        if (pp->xrefname)
          addxref(gv,s,pp->xrefname,s->size-sizeof(uint32),R_ADDR32,
                  sizeof(uint32),pp->addend);
        s->size += sizeof(uint32);
      }
      if (pp == firstpp || !nextpp->n.next) {
        ou->lnkfile->type = ID_OBJECT;
        add_objunit(gv,ou,FALSE);
        ou = NULL;
        s = NULL;
        len = 0;
        nextpp = firstpp;  /* loop for next object unit */
      }
    }

    pp = nextpp;
  }
}


static struct Section *add_data_section(struct GlobalVars *gv,
                                        struct ObjectUnit *ou,char *name,
                                        uint8 *data,unsigned long size)
{
  struct Section *s = create_section(ou,name,data,size);

  s->type = ST_DATA;
  s->protection = SP_READ | SP_WRITE;
  s->alignment = 2;
  addtail(&ou->sections,&s->n);

  /* If the section name starts with '_', define it as a global */
  /* symbol which defines the section's base address. */
  if (*name == '_') {
    struct Symbol *realsym,*lnksym;

    if (addsymbol(gv,s,name,0,SYM_RELOC,0,SYMI_OBJECT,SYMB_GLOBAL,0)) {
      error(59,name);  /* Can't define symbol as section label */
    }
    else if (lnksym = findlnksymbol(fff[gv->dest_format],name)) {
      if (realsym = findsymbol(gv,name)) {
        /* update a linker symbol, which has the same name */
        lnksym->value = realsym->value;
        lnksym->relsect = realsym->relsect;
        lnksym->type = realsym->type;
        lnksym->flags = realsym->flags | SYMF_LNKSYM;
        lnksym->info = realsym->info;
        lnksym->bind = realsym->bind;
        lnksym->size = realsym->size;
      }
    }
  }
  return (s);
}


bool ar_init(struct ar_info *ai,char *p,unsigned long plen,char *name)
/* check for valid archive header and initialize ar_info, if successful */
{
  /* check for "!<arch>\n" id */
  if (plen<sizeof(struct ar_hdr) || strncmp(p,ARMAG,SARMAG))
    return (FALSE);

  memset(ai,0,sizeof(struct ar_hdr));
  ai->arname = name;
  ai->next = (struct ar_hdr *)(p+SARMAG);
  ai->arlen = plen - SARMAG;
  return (TRUE);
}


bool ar_extract(struct ar_info *ai)
/* fill ar_info structure with informations about the next */
/* archive member */
{
  struct ar_hdr *ah;
  uint8 *p;
  bool cont;
  unsigned long size;

  do {
    cont = FALSE;
    if (ai->next==NULL || ai->arlen<sizeof(struct ar_hdr))
      return (FALSE);  /* archive ends here */

    ah = ai->next;
    p = ((uint8 *)ah) + sizeof(struct ar_hdr);
    ai->arlen -= sizeof(struct ar_hdr);
    ai->name[0] = 0;
    sscanf(ah->ar_size,"%lu",&ai->size);  /* file size */

    if (!strncmp(ah->ar_name,"/ ",2) ||             /* GNU symbol table */
        !strncmp(ah->ar_name,"__.SYMDEF ",10))      /* BSD symbol table */
      cont = TRUE;
    if (!strncmp(ah->ar_name,"// ",3)) {            /* GNU long names */
      ai->long_names = p;
      cont = TRUE;
    }

    if (ai->long_names && ah->ar_name[0]=='/' &&    /* long name (GNU) */
        (ah->ar_name[1]>='0' && ah->ar_name[1]<='9')) {
      int i,offset;
      char c,*s,*d=ai->name;

      sscanf(&ah->ar_name[1],"%d",&offset);  /* name offset */
      s = ai->long_names + offset;
      for (i=0; i<MAXARNAME; i++) {
        c = *s++;
        if (c==0 || c=='/' || c=='\n')
          break;
        *d++ = c;
      }
      *d = 0;
    }
    else if (!strncmp(ah->ar_name,"#1/",3)) {  /* ext. name fmt. #1 (BSD) */
      int d,len;

      sscanf(&ah->ar_name[3],"%d",&d);  /* ext.fmt. name length */
      if (d > ai->arlen)
        error(37,ai->arname,ai->name);  /* Malformatted archive member */
      len = (d>MAXARNAME) ? MAXARNAME : d;
      memcpy(ai->name,p,len);
      ai->name[len] = 0;
      if (d & 1)
        ++d;   /* align to even offset */
      p += d;  /* set real beginning of file and size */
      ai->size -= d;
      ai->arlen -= d;
    }
    else {  /* normal file name <= 16 chars */
      char *n = &ah->ar_name[16];
      int len;

      for (len=16; len>0; len--)
        if (*(--n) != ' ')
          break;
      if (len && *n=='/')  /* GNU */
        --len;
      memcpy(ai->name,ah->ar_name,len);
      ai->name[len] = 0;
    }

    if (strncmp(ah->ar_fmag,ARFMAG,2))
      error(42,ai->arname,ai->name);  /* consistency check failed */
    ai->header = ah;
    ai->data = p;
    size = (ai->size&1) ? (ai->size+1) : ai->size;
    ai->next = (struct ar_hdr *)(p + size);
    if ((long)(ai->arlen -= size) < 0)
      error(37,ai->arname,ai->name);  /* Malformatted archive member */
  }
  while (cont);
  return (TRUE);
}
