Source file: /~heha/hs/gputils64-210929.zip/libgputils/gpcoffgen.cpp

/* GNU PIC general coff functions
   Copyright 2001, 2002, 2003, 2004, 2005
   Craig Franklin

    Copyright 2016 Molnár Károly
*/

#include "stdhdr.h"
#include "libgputils.h"

#define CHECK_RELOCATIONS_PASS_MAX      20
#define CHECK_RELOCATIONS_DEPTH_MAX     100

static unsigned object_serial_id  = 0;
static unsigned section_serial_id = 0;

/*------------------------------------------------------------------------------------------------*/

FUNC(gp_object_t*) gp_coffgen_new_object(const char *File_name) {
  /* allocate memory for the object file */
  gp_object_t *object = new gp_object_t;
  memset(object,0,sizeof*object);

  /* initialize the object */
  object->filename  = GP_Strdup(File_name);
  object->processor = gp_find_processor("generic");
  object->pclass     = PROC_CLASS_GENERIC;
  object->time      = (uint32_t)time(NULL);
  object->serial_id = object_serial_id++;

  gp_list_set_delete_node_func(&object->section_list, (gp_node_del_t)gp_coffgen_free_section);
  gp_list_set_delete_node_func(&object->dead_section_list, (gp_node_del_t)gp_coffgen_free_section);

  gp_list_set_delete_node_func(&object->symbol_list, (gp_node_del_t)gp_coffgen_free_symbol);
  gp_list_set_delete_node_func(&object->dead_symbol_list, (gp_node_del_t)gp_coffgen_free_symbol);

  return object;
}

/*------------------------------------------------------------------------------------------------*/

/* Move linked lists between two object. */

FUNC(void) gp_coffgen_transfer_object_data(gp_object_t *Receiver, gp_object_t *Sender) {
  if ((Receiver == NULL) || (Sender == NULL)) {
    return;
  }

  gp_list_move(&Receiver->section_list, &Sender->section_list);
  gp_list_move(&Receiver->dead_section_list, &Sender->dead_section_list);
  gp_list_move(&Receiver->symbol_list, &Sender->symbol_list);
  gp_list_move(&Receiver->dead_symbol_list, &Sender->dead_symbol_list);

  Receiver->num_symbols += Sender->num_symbols;
  Sender->num_symbols    = 0;
}

/*------------------------------------------------------------------------------------------------*/

/* Update the object identifier in the all internal linked lists. */

FUNC(bool) gp_coffgen_update_all_object_id(gp_object_t *Object) {
  gp_section_t *section;
//  gp_symbol_t  *symbol;
  unsigned  id;

  if (Object == NULL) {
    return false;
  }

  id      = Object->serial_id;
  section = Object->section_list.first;
  while (section) {
    section->object_id = id;
    section            = section->next;
  }

  section = Object->dead_section_list.first;
  while (section) {
    section->object_id = id;
    section            = section->next;
  }

  gp_symbol_list_t::iterator sit;
  for (sit = Object->symbol_list.begin(); sit!=Object->symbol_list.end(); sit++) {
//  symbol = Object->symbol_list.first;
//  while (symbol) {
//    symbol->object_id = id;
    sit->object_id = id;
//    symbol            = symbol->next;
  }

//  symbol = Object->dead_symbol_list.first;
  for (sit = Object->dead_symbol_list.begin(); sit!=Object->dead_symbol_list.end(); sit++) {
//  while (symbol) {
//    symbol->object_id = id;
    sit->object_id = id;
//    symbol            = symbol->next;
  }

  return true;
}

/*------------------------------------------------------------------------------------------------*/

/* Find a "Name" section from the given starting section. */

FUNC(gp_section_t*) gp_coffgen_find_section(gp_object_t *Object, gp_section_t *Start, const char *Name) {
  gp_section_t *section;

  if ((Object == NULL) || (Start == NULL)) {
    return NULL;
  }

  section = Start;
  while (section) {
    if ((section->name) && (strcmp(section->name, Name) == 0)) {
      return section;
    }
    section = section->next;
  }

  return NULL;
}

/*------------------------------------------------------------------------------------------------*/

/* Create a pnew section. */

FUNC(gp_section_t*) gp_coffgen_new_section(const char *Name, MemBlock_t *Data) {
  gp_section_t *pnew;

  /* allocate memory for the section */
  pnew = (gp_section_t *)gp_list_node_new(sizeof(gp_section_t));

  /* initialize section */
  pnew->name      = GP_Strdup(Name);
  pnew->data      = (Data) ? Data : gp_mem_i_create();
  pnew->serial_id = section_serial_id++;

  return pnew;
}

/*------------------------------------------------------------------------------------------------*/

/* Allocate a block of section. -- gpreadobj.c */

FUNC(gp_section_t*) gp_coffgen_make_block_section(gp_object_t *Object, unsigned Num_sections) {
  gp_section_t **ptr_array;
  unsigned   id;
  unsigned   i;

  if (Object->section_list.first) {
    gp_error("'%s': The list of sections already exists, can not be created again.", Object->filename);
    assert(0);
  }

  if (Num_sections == 0) {
    return NULL;
  }

  ptr_array = (gp_section_t **)gp_list_make_block(&Object->section_list, Num_sections, sizeof(gp_section_t));

  id = Object->serial_id;
  for (i = 0; i < Num_sections; i++) {
    ptr_array[i]->object_id = id;
    ptr_array[i]->serial_id = section_serial_id++;
  }

  /* The first->prev and last->next values is already NULL. */

  Object->section_ptr_array = ptr_array;

  return Object->section_list.first;
}

/*------------------------------------------------------------------------------------------------*/

/* Add to object an exists section. */

FUNC(gp_section_t*) gp_coffgen_add_exists_section(gp_object_t *Object, gp_section_t *Section) {
  if (!Object) return NULL;

  gp_list_node_append(&Object->section_list, Section);
  Section->object_id = Object->serial_id;

  return Section;
}

/*------------------------------------------------------------------------------------------------*/

/* Behind a section inserts another section. */

FUNC(gp_section_t*) gp_coffgen_insert_after_section(gp_object_t *Object, gp_section_t *Ancestor, gp_section_t *Following) {
  if (!Object) return NULL;

  if (Ancestor->object_id != Object->serial_id) {
    gp_error("The '%s'{%u} section does not belong to this object: '%s'{%u}",
             Ancestor->name, Ancestor->object_id, Object->filename, Object->serial_id);
    assert(0);
  }

  gp_list_node_insert_after(&Object->section_list, Ancestor, Following);
  Following->object_id = Object->serial_id;

  return Following;
}

/*------------------------------------------------------------------------------------------------*/

/* Add a pnew section to the object. */

FUNC(gp_section_t*) gp_coffgen_add_section(gp_object_t *Object, const char *Name, MemBlock_t *Data) {
  if (!Object) return NULL;
  return gp_coffgen_add_exists_section(Object, gp_coffgen_new_section(Name, Data));
}

/*------------------------------------------------------------------------------------------------*/

/* Move linked lists between two section. */

FUNC(void) gp_coffgen_transfer_section_data(gp_section_t *Receiver, gp_section_t *Sender)
{
  if ((Receiver == NULL) || (Sender == NULL)) {
    return;
  }

  gp_list_move(&Receiver->relocation_list, &Sender->relocation_list);
  gp_list_move(&Receiver->line_number_list, &Sender->line_number_list);
}

/*------------------------------------------------------------------------------------------------*/

/* Update the section identifier in the all internal linked lists. */

FUNC(bool) gp_coffgen_update_all_section_id(gp_section_t *Section) {
  gp_reloc_t   *relocation;
  gp_linenum_t *line_number;
  unsigned  id;

  if (Section == NULL) {
    return false;
  }

  id = Section->serial_id;

  relocation = Section->relocation_list.first;
  while (relocation) {
    relocation->section_id = id;
    relocation             = relocation->next;
  }

  line_number = Section->line_number_list.first;
  while (line_number) {
    line_number->section_id = id;
    line_number             = line_number->next;
  }

  return true;
}

/*------------------------------------------------------------------------------------------------*/

/* Move all symbols of section to dead list. */

FUNC(void) gp_coffgen_move_reserve_section_symbols(gp_object_t *Object, gp_section_t *Section) {
  if (!Object) return;
  /* Move all symbols for the section into dead list. */
  FOR (gp_symbol_list_t,it,Object->symbol_list) {
//  gp_symbol_t *list = Object->symbol_list.first;
//  while (list) {
//    gp_symbol_t *symbol = list;
//    list   = list->next;

//    if (symbol->section == Section) gp_coffgen_move_reserve_symbol(Object, symbol);
    if (it->section == Section) gp_coffgen_move_reserve_symbol(Object, &*it);
  }
}

/*------------------------------------------------------------------------------------------------*/

/* Delete all symbols of section. */

FUNC(void) gp_coffgen_del_section_symbols(gp_object_t *Object, gp_section_t *Section) {
  if (!Object) return;
  /* Remove all symbols for the section. */
  FOR (gp_symbol_list_t,it,Object->symbol_list) {
    if (it->section == Section) {
      Object->gp_coffgen_del_symbol(it, true);
    }
  }
}

/*------------------------------------------------------------------------------------------------*/

static void _decrease_relocation_counts(gp_section_t *Section) {
  gp_reloc_t   *relocation;
  gp_symbol_t  *symbol;
  gp_section_t *sym_sect;

  relocation = Section->relocation_list.first;
  while (relocation) {
    symbol   = relocation->symbol;
    sym_sect = symbol->section;

    if (symbol->reloc_count_all_section > 0) {
      (symbol->reloc_count_all_section)--;

      if (sym_sect == Section) {
        /*  Relocation reference from own section. */
        if (symbol->reloc_count_own_section > 0) {
          (symbol->reloc_count_own_section)--;
        }
        else {
          gp_warning("Number of relocation references of symbol from own section is zero: '%s'", symbol->name);
        }
      }
      else {
        /*  Relocation reference from another section. */
        if (symbol->reloc_count_other_section > 0) {
          (symbol->reloc_count_other_section)--;

          if (sym_sect->reloc_count > 0) {
            (sym_sect->reloc_count)--;
          }
          else {
            gp_warning("Number of relocation references of section from another section is zero: '%s'", sym_sect->name);
          }
        }
        else {
          gp_warning("Number of relocation references of symbol from another section is zero: '%s'", symbol->name);
        }
      }
    }

    relocation = relocation->next;
  }
}

/*------------------------------------------------------------------------------------------------*/

/* Move a section to dead list. */

FUNC(gp_section_t*) gp_coffgen_move_reserve_section(gp_object_t *Object, gp_section_t *Section) {
  if (Object->section_list.first == NULL) {
    return NULL;
  }

  if (Section->object_id != Object->serial_id) {
    gp_error("The '%s'{%u} section does not belong to this object: '%s'{%u}",
             Section->name, Section->object_id, Object->filename, Object->serial_id);
    assert(0);
  }

  gp_list_node_move(&Object->dead_section_list, &Object->section_list, Section);
  _decrease_relocation_counts(Section);
  return Section;
}

/*------------------------------------------------------------------------------------------------*/

/* Delete a section from object. */

FUNC(bool) gp_coffgen_del_section(gp_object_t *Object, gp_section_t *Section) {
  if (Object->section_list.first == NULL) {
    return false;
  }

  if (Section->object_id != Object->serial_id) {
    gp_error("The '%s'{%u} section does not belong to this object: '%s'{%u}",
             Section->name, Section->object_id, Object->filename, Object->serial_id);
    assert(0);
  }

  gp_list_node_remove(&Object->section_list, Section);
  gp_coffgen_free_section(Section);
  return true;
}

/*------------------------------------------------------------------------------------------------*/

static int _cdecl _section_cmp(const void *P0, const void *P1) {
  const gp_section_t *s0 = *(const gp_section_t **)P0;
  const gp_section_t *s1 = *(const gp_section_t **)P1;
  uint32_t            a0 = s0->address;
  uint32_t            a1 = s1->address;

  if (a0 < a1) {
    return -1;
  }

  if (a0 > a1) {
    return 1;
  }

  return 0;
}

/*------------------------------------------------------------------------------------------------*/

/* Put in an array that sections which are located the same page. */

FUNC(gp_section_t**) gp_coffgen_make_section_array(gp_object_t *Object, unsigned *Num_sections,
	unsigned Page_addr, uint32_t Flags)
{
  proc_class_t   pclass;
  gp_section_t  *section;
  gp_section_t **array;
  unsigned   i;
  unsigned   page;
  unsigned   n_sections;

  if ((Object == NULL) || (Num_sections == NULL)) {
    return NULL;
  }

  pclass = Object->pclass;

  /* Examines the sections. */
  n_sections = 0;
  section    = Object->section_list.first;
  while (section) {
    FlagClr(section->opt_flags, OPT_FLAGS_GPCOFFGEN_MODULE);
    page = gp_processor_page_addr(pclass, gp_processor_insn_from_byte_c(pclass, section->address));

    if ((page == Page_addr) && (FlagsIsNotAllClr(section->flags, Flags))) {
      /* Located on same page and there is at least one valid flag. */
      ++n_sections;
      FlagSet(section->opt_flags, OPT_FLAGS_GPCOFFGEN_MODULE);
    }
    section = section->next;
  }

  if (n_sections == 0) {
    return NULL;
  }

  array = new gp_section_t*[n_sections];

  /* Populate the array. */
  i       = 0;
  section = Object->section_list.first;
  while (section) {
    if (FlagIsSet(section->opt_flags, OPT_FLAGS_GPCOFFGEN_MODULE)) {
      array[i] = section;
      ++i;
    }
    section = section->next;
  }

  if (n_sections > 1) {
    qsort(array, n_sections, sizeof(gp_section_t *), _section_cmp);
  }

  *Num_sections = n_sections;
  return array;
}

/*------------------------------------------------------------------------------------------------*/

/* Find by name a symbol that not section symbol. */
gp_symbol_list_t::iterator gp_object_t::gp_coffgen_find_symbol(const char *Name) {
  FOR (gp_symbol_list_t,it,symbol_list) {
    if (it->pclass != C_SECTION && it->name && !strcmp(it->name, Name)) {
      return it;
    }
  }
  return symbol_list.end();
}

/*------------------------------------------------------------------------------------------------*/

/* Find by name a symbol that a section symbol. */
gp_symbol_list_t::iterator gp_object_t::gp_coffgen_find_section_symbol(const char *Name) {
  FOR (gp_symbol_list_t,it,symbol_list) {
    if (it->pclass == C_SECTION && it->name && !strcmp(it->name, Name)) {
      return it;
    }
  }
  return symbol_list.end();
}

/*------------------------------------------------------------------------------------------------*/

/* Search for a symbol, based on the value and name of host section.
   The function is slow, but only need the error messages. */

FUNC(gp_symbol_t*) gp_coffgen_find_symbol_section_value(gp_object_t *Object, const char *Section_name, long Value) {
  if (!Object || !Section_name) return 0;
  FOR (gp_symbol_list_t,it,Object->symbol_list) {
    if (it->pclass != C_SECTION
     && it->pclass != C_FILE
     && it->section_name
     && !strcmp(it->section_name, Section_name)
     && it->value == Value) return &*it;
  }
  return 0;
}

/*------------------------------------------------------------------------------------------------*/

/* Allocate a block of symbols. -- gpreadobj.c */

gp_symbol_list_t::iterator gp_object_t::gp_coffgen_make_block_symbol() {
  unsigned   n_symbols = num_symbols;
  unsigned   id;
  unsigned   i;

  if (!n_symbols) return symbol_list.end();

  if (symbol_list.size()) {
    gp_error("'%s': The list of symbols already exists, can not be created again.", filename);
    assert(0);
  }

  gp_symbol_t**ptr_array = (gp_symbol_t **)gp_list_make_block(&symbol_list, n_symbols, sizeof(gp_symbol_t));

  id = serial_id;
  for (i = 0; i < n_symbols; i++) {
    ptr_array[i]->object_id = id;
  }

  /* The first->prev and last->next values is already NULL. */

  symbol_ptr_array = ptr_array;

  return symbol_list.begin();
}

/*------------------------------------------------------------------------------------------------*/

/* Add a pnew symbol to the object. */

gp_symbol_list_t::iterator gp_object_t::gp_coffgen_add_symbol(const char *Name, int Section_number) {
  /* allocate memory for the symbol */
  gp_symbol_t sym;
//  gp_symbol_t *pnew = (gp_symbol_t *)gp_list_node_append(&Object->symbol_list, gp_list_node_new(sizeof(gp_symbol_t)));
  sym.name           = GP_Strdup(Name);
  sym.number         = num_symbols;
  sym.section_number = Section_number;
  sym.object_id      = serial_id;
  num_symbols++;
  return symbol_list.insert(symbol_list.end(),sym);
}

/*------------------------------------------------------------------------------------------------*/

/* Add a pnew auxiliary symbol to the symbol from the object. */

FUNC(gp_aux_t*) gp_coffgen_add_aux(gp_object_t *Object, gp_symbol_t *Symbol) {
  gp_aux_t *pnew;

  pnew = (gp_aux_t *)gp_list_node_append(&Symbol->aux_list, gp_list_node_new(sizeof(gp_aux_t)));

  (Object->num_symbols)++;

  return pnew;
}

/*------------------------------------------------------------------------------------------------*/

/* Allocate a block of auxiliary symbols. -- gpreadobj.c */

FUNC(gp_aux_t*) gp_coffgen_make_block_aux(gp_symbol_t *Symbol, unsigned Num_auxsyms) {
  gp_aux_t     **ptr_array;
#if (AUX_NONE != 0)
  unsigned   i;
#endif

  if (Symbol->aux_list.first) {
    gp_error("'%s': The list of aux symbols already exists, can not be created again.", Symbol->name);
    assert(0);
  }

  if (Num_auxsyms == 0) {
    return NULL;
  }

  ptr_array = (gp_aux_t **)gp_list_make_block(&Symbol->aux_list, Num_auxsyms, sizeof(gp_aux_t));
#if (AUX_NONE != 0)
  for (i = 0; i < Num_auxsyms; i++) {
    ptr_array[i]->type = AUX_NONE;
  }
#endif
  free(ptr_array);

  return Symbol->aux_list.first;
}

/*------------------------------------------------------------------------------------------------*/

/* Move a symbol to dead list. */

FUNC(gp_symbol_t*) gp_coffgen_move_reserve_symbol(gp_object_t *Object, gp_symbol_t *Symbol) {
  if (!Object->symbol_list.size()) return NULL;

  if (Symbol->object_id != Object->serial_id) {
    gp_error("The '%s'{%u} symbol does not belong to this object: '%s'{%u}",
             Symbol->name, Symbol->object_id, Object->filename, Object->serial_id);
    assert(0);
  }

  gp_list_node_move(&Object->dead_symbol_list, &Object->symbol_list, Symbol);
  Object->num_symbols -= 1 + Symbol->aux_list.num_nodes;

  return Symbol;
}

/*------------------------------------------------------------------------------------------------*/

/* Delete the symbol from the object. */

gp_symbol_list_t::iterator gp_object_t::gp_coffgen_del_symbol(gp_symbol_list_t::iterator it, bool Touch_number) {
  unsigned n_deleted;

  if (!symbol_list.size()) return symbol_list.end();

  if (it->object_id != serial_id) {
    gp_error("The '%s'{%u} symbol does not belong to this object: '%s'{%u}",
             it->name, it->object_id, filename, serial_id);
    assert(0);
  }

  gp_symbol_list_t::iterator ret = symbol_list.erase(it);

  n_deleted = 1 + gp_coffgen_free_symbol(&*it);

  if (Touch_number) num_symbols -= n_deleted;

  return ret;
}

/*------------------------------------------------------------------------------------------------*/

/* Make an array from symbol list of object. */

FUNC(gp_symbol_t**) gp_coffgen_make_symbol_array(const gp_object_t *Object, int (_cdecl*Cmp)(const void *, const void *)) {
  if (!Object || !Object->symbol_list.size()) return 0;

  gp_symbol_t**array = new gp_symbol_t*[Object->symbol_list.size()];
  unsigned i = 0;
  FOR (gp_symbol_list_t,it,*const_cast<gp_symbol_list_t*>(&Object->symbol_list)) {
    array[i++] = &*it;
  }
  assert(Object->symbol_list.size() == i);

  if (Cmp && i>1) qsort(array, i, sizeof(gp_symbol_t*), Cmp);
  return array;
}

/*------------------------------------------------------------------------------------------------*/

/* Convert to string the symbol type. */

FUNC(const char*) gp_coffgen_symbol_type_to_str(uint8_t Type) {
  static const char * const type_str[] = {
    "T_NULL",
    "T_VOID",
    "T_CHAR",
    "T_SHORT",
    "T_INT",
    "T_LONG",
    "T_FLOAT",
    "T_DOUBLE",
    "T_STRUCT",
    "T_UNION",
    "T_ENUM",
    "T_MOE",
    "T_UCHAR",
    "T_USHORT",
    "T_UINT",
    "T_ULONG",
    "T_LNGDBL",
    "T_SLONG",
    "T_USLONG"
  };

  if (Type >= ARRAY_SIZE(type_str)) {
    return NULL;
  }

  return type_str[Type];
}

/*------------------------------------------------------------------------------------------------*/

/* Convert to string the symbol derived type. */

FUNC(const char*) gp_coffgen_symbol_derived_type_to_str(uint32_t Type) {
  static const char * const type_str[] = {
    "DT_NON",
    "DT_PTR",
    "DT_FUNCTION",
    "DT_ARRAY",
    "DT_ROMPTR",
    "DT_FARROMPTR"
  };

  if (Type >= ARRAY_SIZE(type_str)) {
    return NULL;
  }

  return type_str[Type];
}

/*------------------------------------------------------------------------------------------------*/

/* Convert to string the symbol pclass. */

FUNC(const char*) gp_coffgen_symbol_class_to_str(uint8_t Class) {
  switch (Class) {
    case C_NULL:    return "C_NULL";
    case C_AUTO:    return "C_AUTO";      /* automatic variable */
    case C_EXT:     return "C_EXT";       /* external symbol */
    case C_STAT:    return "C_STAT";      /* static */
    case C_REG:     return "C_REG";       /* register variable */
    case C_EXTDEF:  return "C_EXTDEF";    /* external definition */
    case C_LABEL:   return "C_LABEL";     /* label */
    case C_ULABEL:  return "C_ULABEL";    /* undefined label */
    case C_MOS:     return "C_MOS";       /* member of structure */
    case C_ARG:     return "C_ARG";       /* function argument */
    case C_STRTAG:  return "C_STRTAG";    /* structure tag */
    case C_MOU:     return "C_MOU";       /* member of union */
    case C_UNTAG:   return "C_UNTAG";     /* union tag */
    case C_TPDEF:   return "C_TPDEF";     /* type definition */
    case C_USTATIC: return "C_USTATIC";   /* undefined static */
    case C_ENTAG:   return "C_ENTAG";     /* enumeration tag */
    case C_MOE:     return "C_MOE";       /* member of enumeration */
    case C_REGPARM: return "C_REGPARM";   /* register parameter */
    case C_FIELD:   return "C_FIELD";     /* bit field */
    case C_AUTOARG: return "C_AUTOARG";   /* auto argument */
    case C_LASTENT: return "C_LASTENT";   /* dummy entry (end of block) */
    case C_BLOCK:   return "C_BLOCK";     /* ".bb" or ".eb" */
    case C_FCN:     return "C_FCN";       /* ".bf" or ".ef" */
    case C_EOS:     return "C_EOS";       /* end of structure */
    case C_FILE:    return "C_FILE";      /* file name */
    case C_LINE:    return "C_LINE";      /* line number reformatted as symbol table entry */
    case C_ALIAS:   return "C_ALIAS";     /* duplicate tag */
    case C_HIDDEN:  return "C_HIDDEN";    /* ext symbol in dmert public lib */
    case C_EOF:     return "C_EOF";       /* end of file */
    case C_LIST:    return "C_LIST";      /* absoulte listing on or off */
    case C_SECTION: return "C_SECTION";   /* section */
    case C_EFCN:    return "C_EFCN";      /* physical end of function */
    default:        return NULL;
  }
}

/*------------------------------------------------------------------------------------------------*/

/* Allocate a block of relocations. -- gpreadobj.c */

FUNC(gp_reloc_t*) gp_coffgen_make_block_reloc(gp_section_t *Section, unsigned Num_relocations) {
  gp_reloc_t   **ptr_array;
  unsigned   id;
  unsigned   i;

  if (Section->relocation_list.first) {
    gp_error("'%s': The list of relocations already exists, can not be created again.", Section->name);
    assert(0);
  }

  if (Num_relocations == 0) {
    return NULL;
  }

  ptr_array = (gp_reloc_t **)gp_list_make_block(&Section->relocation_list, Num_relocations, sizeof(gp_reloc_t));

  id = Section->serial_id;
  for (i = 0; i < Num_relocations; i++) {
    ptr_array[i]->section_id = id;
  }

  free(ptr_array);

  return Section->relocation_list.first;
}

/*------------------------------------------------------------------------------------------------*/

/* Add a pnew relocation to the section. */

FUNC(gp_reloc_t*) gp_coffgen_add_reloc(gp_section_t *Section) {
  gp_reloc_t *pnew;

  /* allocate memory for the relocation */
  pnew = (gp_reloc_t *)gp_list_node_append(&Section->relocation_list, gp_list_node_new(sizeof(gp_reloc_t)));
  pnew->section_id = Section->serial_id;

  return pnew;
}

/*------------------------------------------------------------------------------------------------*/

/* Clears all relocation counters. */

static void _clear_relocation_counts(const gp_object_t *Object) {
  gp_section_t *section;

  section = Object->section_list.first;
  while (section) {
    section->reloc_count = 0;

    if (FlagsIsNotAllClr(section->flags, STYP_ABS | STYP_DATA)) {
      /* This section get protection immediately. The additional sections' protection
         is based on this. */
      FlagSet(section->opt_flags, OPT_FLAGS_PROTECTED_SECTION);
    }
    else {
      FlagClr(section->opt_flags, OPT_FLAGS_PROTECTED_SECTION);
    }

    section = section->next;
  }
  FOR(gp_symbol_list_t,it,*const_cast<gp_symbol_list_t*>(&Object->symbol_list)) {
    it->reloc_count_all_section   = 0;
    it->reloc_count_own_section   = 0;
    it->reloc_count_other_section = 0;
  }
}

/*------------------------------------------------------------------------------------------------*/

static bool _check_section_relocations(proc_class_t Class, gp_section_t *Section, unsigned Pass,
	unsigned Behavior, unsigned Level)
{
  gp_reloc_t   *relocation;
  gp_symbol_t  *symbol;
  gp_section_t *sym_sect;
  bool    use_counters;
  bool    change;

  if (Level >= CHECK_RELOCATIONS_DEPTH_MAX) {
    gp_warning("Depth of relocations check reached the limit: %u", CHECK_RELOCATIONS_DEPTH_MAX);
    return false;
  }

  use_counters = (Pass == 0) ? true : false;
  change       = false;
  relocation   = Section->relocation_list.first;
  while (relocation) {
    symbol = relocation->symbol;
    if (use_counters) {
      (symbol->reloc_count_all_section)++;
    }

    sym_sect = symbol->section;
    if (sym_sect == NULL) {
      /* This is an orphan symbol. */
      if (FlagIsClr(Behavior, RELOC_DISABLE_WARN)) {
        if (FlagIsSet(Behavior, RELOC_ENABLE_CINIT_WARN) || (strcmp(symbol->name, "_cinit") != 0)) {
          gp_warning("Relocation symbol \"%s\" [0x%0*X] has no section. (pass %u)",
                     symbol->name, Class->addr_digits, relocation->address, Pass);
        }
      }
    }
    else {
      if (sym_sect == Section) {
        /* In this case not change the level of protection. */
        if (use_counters) {
          (symbol->reloc_count_own_section)++;
        }
      }
      else {
        /* The symbol is located another section. */
        if (use_counters) {
          (symbol->reloc_count_other_section)++;
          (sym_sect->reloc_count)++;
        }

        if (FlagIsSet(Section->opt_flags, OPT_FLAGS_PROTECTED_SECTION)) {
          /* If the section of reference is s_protected, then the section of symbol also will
             be s_protected. */
          if (FlagIsClr(sym_sect->opt_flags, OPT_FLAGS_PROTECTED_SECTION)) {
            /* Only then modify and check if have not been under protection. */
            FlagSet(sym_sect->opt_flags, OPT_FLAGS_PROTECTED_SECTION);
            change = _check_section_relocations(Class, sym_sect, Pass, Behavior, Level + 1);
          }
        }
      }
    }
    relocation = relocation->next;
  }

  return change;
}

/*------------------------------------------------------------------------------------------------*/

static bool _check_object_relocations(const gp_object_t *Object, unsigned Pass, unsigned Behavior) {
  proc_class_t  pclass;
  gp_section_t *section;
  bool    change;
  unsigned  level;

  level   = 0;
  change  = false;
  pclass   = Object->pclass;
  section = Object->section_list.first;
  while (section) {
    change |= _check_section_relocations(pclass, section, Pass, Behavior, level);
    section = section->next;
  }

  return change;
}

/*------------------------------------------------------------------------------------------------*/

/* Handle the relocation counters of sections and symbols. */

FUNC(void) gp_coffgen_check_relocations(const gp_object_t *Object, bool Enable_cinit_warns) {
  unsigned pass;

  _clear_relocation_counts(Object);
  pass = 0;
  while (true) {
    /* Need for further rounds because may occur back and forth references. */
    if (!_check_object_relocations(Object, pass, Enable_cinit_warns)) {
      break;
    }

    ++pass;

    if (pass > CHECK_RELOCATIONS_PASS_MAX) {
      gp_warning("Number of relocations check reached the limit: %u", CHECK_RELOCATIONS_PASS_MAX);
      break;
    }
  }
}

/*------------------------------------------------------------------------------------------------*/

/* Delete the relocation from the section. */

FUNC(bool) gp_coffgen_del_reloc(gp_section_t *Section, gp_reloc_t *Relocation) {
  gp_section_t *section;
  gp_symbol_t  *symbol;

  if (Section->relocation_list.first == NULL) {
    return false;
  }

  if (Relocation->section_id != Section->serial_id) {
    gp_error("The relocation{%u} does not belong to this section: '%s'{%u}",
             Relocation->section_id, Section->name, Section->serial_id);
    assert(0);
  }

  gp_list_node_remove(&Section->relocation_list, Relocation);

  symbol  = Relocation->symbol;
  assert(symbol);
  section = symbol->section;
  assert(section);

  if (symbol->reloc_count_all_section > 0) {
    (symbol->reloc_count_all_section)--;

    if (symbol->section == Section) {
      /*  Relocation reference from own section. */
      if (symbol->reloc_count_own_section > 0) {
        (symbol->reloc_count_own_section)--;
      }
      else {
        gp_warning("Number of relocation references of symbol from own section is zero: '%s'", symbol->name);
      }
    }
    else {
      /*  Relocation reference from another section. */
      if (symbol->reloc_count_other_section > 0) {
        (symbol->reloc_count_other_section)--;

        if (section->reloc_count > 0) {
          (section->reloc_count)--;
        }
        else {
          gp_warning("Number of relocation references of section from another section is zero: '%s'", section->name);
        }
      }
      else {
        gp_warning("Number of relocation references of symbol from another section is zero: '%s'", symbol->name);
      }
    }
  }
  else {
    gp_warning("Number of relocation references of symbol from all section is zero: '%s'", symbol->name);
  }

  gp_list_node_free(&Section->relocation_list, Relocation);
  return true;
}

/*------------------------------------------------------------------------------------------------*/

/* Convert to string the relocation type. */

FUNC(const char*) gp_coffgen_reloc_type_to_str(uint16_t Type) {
  static const char * const type_str[] = {
    "",
    "RELOC_CALL",
    "RELOC_GOTO",
    "RELOC_HIGH",
    "RELOC_LOW",
    "RELOC_P",
    "RELOC_BANKSEL",
    "RELOC_PAGESEL",
    "RELOC_ALL",
    "RELOC_IBANKSEL",
    "RELOC_F",
    "RELOC_TRIS",
    "RELOC_MOVLR",
    "RELOC_MOVLB",
    "RELOC_GOTO2/CALL2",
    "RELOC_FF1",
    "RELOC_FF2",
    "RELOC_LFSR1",
    "RELOC_LFSR2",
    "RELOC_BRA/RCALL",
    "RELOC_CONDBRA",
    "RELOC_UPPER",
    "RELOC_ACCESS",
    "RELOC_PAGESEL_WREG",
    "RELOC_PAGESEL_BITS",
    "RELOC_SCNSZ_LOW",
    "RELOC_SCNSZ_HIGH",
    "RELOC_SCNSZ_UPPER",
    "RELOC_SCNEND_LOW",
    "RELOC_SCNEND_HIGH",
    "RELOC_SCNEND_UPPER",
    "RELOC_SCNEND_LFSR1",
    "RELOC_SCNEND_LFSR2",
    "RELOC_TRIS_3BIT",
    "RELOC_PAGESEL_MOVLP"
  };

  if (Type >= ARRAY_SIZE(type_str)) {
    Type = 0;
  }

  return type_str[Type];
}

/*------------------------------------------------------------------------------------------------*/

/* Allocate a block of line numbers. -- gpreadobj.c */

FUNC(gp_linenum_t*) gp_coffgen_make_block_linenum(gp_section_t *Section, unsigned Num_linenums) {
  gp_linenum_t **ptr_array;
  unsigned   id;
  unsigned   i;

  if (Section->line_number_list.first) {
    gp_error("'%s': The list of line numbers already exists, can not be created again.", Section->name);
    assert(0);
  }

  if (Num_linenums == 0) {
    return NULL;
  }

  ptr_array = (gp_linenum_t **)gp_list_make_block(&Section->line_number_list, Num_linenums, sizeof(gp_linenum_t));

  id = Section->serial_id;
  for (i = 0; i < Num_linenums; i++) {
    ptr_array[i]->section_id = id;
  }

  free(ptr_array);

  return Section->line_number_list.first;
}

/*------------------------------------------------------------------------------------------------*/

/* Add pnew line number to the section. */

FUNC(gp_linenum_t*) gp_coffgen_add_linenum(gp_section_t *Section) {
  gp_linenum_t *pnew;

  /* allocate memory for the line number */
  pnew = (gp_linenum_t *)gp_list_node_append(&Section->line_number_list, gp_list_node_new(sizeof(gp_linenum_t)));
  pnew->section_id = Section->serial_id;

  return pnew;
}

/*------------------------------------------------------------------------------------------------*/

/* Delete the line number from the section. */

FUNC(bool) gp_coffgen_del_linenum(gp_section_t *Section, gp_linenum_t *Linenum) {
  if (Section->line_number_list.first == NULL) {
    return false;
  }

  if (Linenum->section_id != Section->serial_id) {
    gp_error("The line number{%u} does not belong to this section: '%s'{%u}",
             Linenum->section_id, Section->name, Section->serial_id);
    assert(0);
  }

  gp_list_node_delete(&Section->line_number_list, Linenum);
  return true;
}

/*------------------------------------------------------------------------------------------------*/

/* Find line number by address in the section. */

FUNC(gp_linenum_t*) gp_coffgen_find_linenum_by_address(gp_section_t *Section, unsigned Address) {
  gp_linenum_t *linenum;

  if (Section->line_number_list.first == NULL) {
    return NULL;
  }

  linenum = Section->line_number_list.first;
  while (linenum) {
    if (linenum->address == Address) {
      return linenum;
    }
    linenum = linenum->next;
  }

  return NULL;
}

/*------------------------------------------------------------------------------------------------*/

/* Delete a line number by address from the section. */

FUNC(bool) gp_coffgen_del_linenum_by_address(gp_section_t *Section, unsigned Address) {
  gp_linenum_t *linenum;

  linenum = gp_coffgen_find_linenum_by_address(Section, Address);

  if (linenum == NULL) {
    return false;
  }

  return gp_coffgen_del_linenum(Section, linenum);
}

/*------------------------------------------------------------------------------------------------*/

/* Delete line numbers by address range from the section. */

FUNC(unsigned) gp_coffgen_del_linenum_by_address_area(gp_section_t *Section, unsigned Address_start,
	unsigned Address_end)
{
  gp_linenum_t *linenum;
  gp_linenum_t *next;
  unsigned  num;

  if (Section->line_number_list.first == NULL) {
    return false;
  }

  num     = 0;
  linenum = Section->line_number_list.first;
  while ((linenum) && (linenum->address <= Address_end)) {
    next = linenum->next;

    if (linenum->address >= Address_start) {
      num += (gp_coffgen_del_linenum(Section, linenum) != false) ? 1 : 0;
    }

    linenum = next;
  }

  return num;
}

/*------------------------------------------------------------------------------------------------*/

static int _cdecl _linenum_cmp(const void *P0, const void *P1) {
  const gp_linenum_t *l0   = *(const gp_linenum_t **)P0;
  const gp_linenum_t *l1   = *(const gp_linenum_t **)P1;
  const gp_symbol_t  *sym0 = l0->symbol;
  const gp_symbol_t  *sym1 = l1->symbol;
  unsigned        num0 = l0->line_number;
  unsigned        num1 = l1->line_number;

  if (sym0 < sym1) {
    return -1;
  }

  if (sym0 > sym1) {
    return 1;
  }

  if (num0 < num1) {
    return -1;
  }

  if (num0 > num1) {
    return 1;
  }

  return 0;
}

/*------------------------------------------------------------------------------------------------*/

/* Create line number array. Use gplink/gplink.c */

FUNC(void) gp_coffgen_make_linenum_array(gp_object_t *Object) {
  gp_section_t  *section;
  gp_linenum_t  *linenum;
  gp_linenum_t **array;
  unsigned   n_linenums;
  unsigned   prev_num;
  unsigned   i;

  section = Object->section_list.first;
  while (section) {
    n_linenums = section->line_number_list.num_nodes;
    array      = new gp_linenum_t*[n_linenums];
    i          = 0;
    prev_num   = (unsigned)(-1);
    linenum    = section->line_number_list.first;
    while (linenum) {
      /* From among identical line numbers only places the first in the array. */
      if (prev_num != linenum->line_number) {
        array[i] = linenum;
        ++i;
        prev_num = linenum->line_number;
      }

      linenum = linenum->next;
    }

    /* Reduces the required memory size. */
    array = (gp_linenum_t **)GP_Realloc(array, i * sizeof(gp_linenum_t *));
    section->line_numbers_array        = array;
    section->line_numbers_array_length = i;

    qsort(array, i, sizeof(gp_linenum_t *), _linenum_cmp);
    section = section->next;
  }
}

/*------------------------------------------------------------------------------------------------*/

/* Find a line number in array by symbol and number. Use gplink/lst.c */

FUNC(gp_linenum_t*) gp_coffgen_find_linenum(const gp_section_t *Section, const gp_symbol_t *Symbol, unsigned Line_number) {
  gp_linenum_t   linenum;
  gp_linenum_t  *ptr;
  gp_linenum_t **ret;

  if ((Section == NULL) || (Section->line_numbers_array == NULL)) {
    return NULL;
  }

  linenum.symbol      = Symbol;
  linenum.line_number = Line_number;
  ptr = &linenum;
  ret = (gp_linenum_t **)bsearch(&ptr, Section->line_numbers_array, Section->line_numbers_array_length,
                                 sizeof(gp_linenum_t *), _linenum_cmp);
  return ((ret) ? *ret : NULL);
}

/*------------------------------------------------------------------------------------------------*/

/* check if the object is absolute: all sections are absolute and there
   are no relocations (undefined symbols) */

FUNC(bool) gp_coffgen_is_absolute_object(const gp_object_t *Object) {
  const gp_section_t *section;

  section = Object->section_list.first;
  while (section) {
    if ((section->relocation_list.num_nodes > 0) || FlagIsClr(section->flags, STYP_ABS)) {
      return false;
    }

    section = section->next;
  }

  return true;
}

/*------------------------------------------------------------------------------------------------*/

FUNC(bool) gp_coffgen_section_has_data(const gp_section_t *Section) {
  if (Section->size == 0) {
    return false;
  }

  return FlagIsSet(Section->flags, (STYP_TEXT | STYP_DATA | STYP_DATA_ROM));
}

/*------------------------------------------------------------------------------------------------*/

/* Determine if any relocation uses the symbol. */

bool gp_symbol_t::gp_coffgen_symbol_has_reloc(int Type) const{
  switch (Type) {
    case COFF_SYM_RELOC_OWN:
      return reloc_count_own_section > 0;

    case COFF_SYM_RELOC_OTHER:
      return reloc_count_other_section > 0;

    default:
      return reloc_count_all_section > 0;
  }
}

/*------------------------------------------------------------------------------------------------*/

/* Determine if the symbol is global. */

bool gp_symbol_t::gp_coffgen_is_global_symbol() const{
  return pclass == C_EXT && section_number == N_SCNUM;
}

/*------------------------------------------------------------------------------------------------*/

/* Determine if the symbol is external. */

bool gp_symbol_t::gp_coffgen_is_external_symbol() const{
  return pclass == C_EXT && section_number == N_UNDEF;
}

/*------------------------------------------------------------------------------------------------*/

/* Determine if the symbol is debug. */

bool gp_symbol_t::gp_coffgen_is_debug_symbol() const{
  return pclass == C_NULL && section_number == N_DEBUG;
}

/*------------------------------------------------------------------------------------------------*/

/* Determine if the symbol is absolute. */

bool gp_symbol_t::gp_coffgen_is_absolute_symbol() const{
  return pclass == C_NULL && section_number == N_ABS;
}

/*------------------------------------------------------------------------------------------------*/

/* Free the section. */

FUNC(void) gp_coffgen_free_section(gp_section_t *Section) {
  if (Section->data) {
    gp_mem_i_free(Section->data);
  }

  gp_list_delete(&Section->relocation_list);
  gp_list_delete(&Section->line_number_list);

  if (Section->line_numbers_array) {
    free(Section->line_numbers_array);
  }

  free(Section->name);
  free(Section);
}

/*------------------------------------------------------------------------------------------------*/

/* Free the symbol. */

FUNC(unsigned) gp_coffgen_free_symbol(gp_symbol_t *Symbol) {
  unsigned num_auxsym;

  if (!Symbol) return 0;

  /* free the auxilary symbols */
  num_auxsym = Symbol->aux_list.num_nodes;
  if (num_auxsym > 0) {
    gp_list_delete(&Symbol->aux_list);
  }

  if (Symbol->name) {
    free(Symbol->name);
  }

  free(Symbol);

  return num_auxsym;
}

/*------------------------------------------------------------------------------------------------*/

/* Free the object. */

FUNC(bool) gp_coffgen_free_object(gp_object_t *Object) {
  if (!Object) return false;

  gp_list_delete(&Object->section_list);
  gp_list_delete(&Object->dead_section_list);

  if (Object->section_ptr_array) delete[] Object->section_ptr_array;

  gp_list_delete(&Object->symbol_list);
  gp_list_delete(&Object->dead_symbol_list);

  if (Object->symbol_ptr_array) delete[] Object->symbol_ptr_array;
  if (Object->symbol_hashtable) delete[] Object->symbol_hashtable;
  if (Object->filename) delete[] const_cast<char*>(Object->filename);

  free(Object);

  return true;
}

/*------------------------------------------------------------------------------------------------*/

FUNC(unsigned) gp_coffgen_determine_aux_symbol(const gp_symbol_t *Symbol) {
  unsigned aux_type;

  if (strcasecmp(".direct", Symbol->name) == 0) {
    return AUX_DIRECT;
  }

  if (strcasecmp(".ident", Symbol->name) == 0) {
    return AUX_IDENT;
  }

  if ((Symbol->derived_type & 7) == DT_FUNCTION) {
    return AUX_FUNCTION;
  }

  switch (Symbol->pclass) {
    case C_FILE:
      aux_type = AUX_FILE;
      break;

    case C_SECTION:
      aux_type = AUX_SECTION;
      break;

    case C_BLOCK:
    case C_FCN:
      aux_type = (Symbol->name[1] == 'b') ? AUX_BOBF : AUX_EOBF;
      break;

    default:
      aux_type = AUX_NONE;
  }

  return aux_type;
}
Detected encoding: UTF-80