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

/* GNU PIC coff linker functions
   Copyright 2001-2005	Craig Franklin
   Copyright 2016	Molnár Károly
*/

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

bool gp_relocate_to_shared = false;

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

/* Two symbol tables are constructed. The first contains the definitions of all
   external symbols in all the object files.  This symbol table is used for
   relocation and linking.  The second table contains all external symbols
   that do not yet have a definition.  This table is used to determine which
   objects in a library are to be linked against.  This table should be empty
   at the begining of the relocation process. */

FUNC(void) gp_cofflink_add_symbol(symbol_table_t *Table, gp_symbol_t *Symbol, gp_object_t *File) {
  /* Search the for the symbol. If not found, then add it to the global symbol table. */
  symbol_t*sym = gp_sym_get_symbol(Table, Symbol->name);
  if (sym) return;
  sym = gp_sym_add_symbol(Table, Symbol->name);
  gp_coffsymbol_t* var = new gp_coffsymbol_t;
  var->symbol = Symbol;
  var->file   = File;
  gp_sym_annotate_symbol(sym, var);
}

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

FUNC(void) gp_cofflink_remove_symbol(symbol_table_t *Table, const char *Name) {
  symbol_t* sym = gp_sym_get_symbol(Table, Name);
  if (!sym) return;
  gp_coffsymbol_t*var = (gp_coffsymbol_t*)gp_sym_get_symbol_annotation(sym);
  delete var;
  gp_sym_remove_symbol(Table, Name);
}

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

/* Add the external symbols from an object file to the appropriate symbol
   tables. NOTE: The missing symbol table is optional. This feature is
   not used for generating symbol indexes for archives. */

FUNC(bool) gp_cofflink_add_symbols(symbol_table_t *Definition, symbol_table_t *Missing, gp_object_t *Object) {
  if (!Definition || !Object) return false;

  /* The gp_convert_file() function has read it the elements of the "object". */

  FOR(gp_symbol_list_t,it,Object->symbol_list) {
    /* process all external symbols that are not directives */
    if (it->pclass == C_EXT && it->name[0] != '.') {
      const symbol_t*sym = gp_sym_get_symbol(Definition, it->name);
      if (it->section_number == N_UNDEF) {
        /* This symbol is defined elsewhere. Check for it in the symbol
           definitions. If it doesn't exist there, add it to the missing
           symbol table, if not already entered. */

        if (!sym && Missing) {
          gp_cofflink_add_symbol(Missing, &*it, Object);
        }
      }else{
        /* External symbol definition. See if it is already defined, it so
           it is an error. Add the symbol to the symbol definitions and remove
           it from the missing symbol table if it exists there. */
        if (sym) {
          /* duplicate symbol */
          const gp_coffsymbol_t*var = (const gp_coffsymbol_t *)gp_sym_get_symbol_annotation(sym);
          gp_error("Duplicate symbol \"%s\" defined in \"%s\" and \"%s\".",
                   it->name, var->file->filename, Object->filename);
        }else{
          gp_cofflink_add_symbol(Definition, &*it, Object);
        }

        if (Missing) gp_cofflink_remove_symbol(Missing, it->name);
      }
    }
  }

  return true;
}

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

/* Combine all sections and symbols from all objects into one object file. */

FUNC(void) gp_cofflink_combine_objects(gp_object_t *Object) {
  gp_object_t *object_list;

  /* assign the time the operation occured */
  Object->time = (uint32_t)time(NULL);

  /* append the sections onto the list */
  object_list = Object->next;
  while (object_list) {
    gp_coffgen_transfer_object_data(Object, object_list);
    object_list = object_list->next;
  }

  gp_coffgen_update_all_object_id(Object);

  /* FIXME: breaking the chain isn't good */
  Object->next = NULL;
}

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

/* Cleanup the symbol table after combining objects. */

FUNC(void) gp_cofflink_clean_table(gp_object_t *Object, symbol_table_t *Symbols) {
  gp_section_t          *section;
  gp_reloc_t            *relocation;
  gp_symbol_t           *symbol;
  const gp_coffsymbol_t *var;
  const symbol_t        *sym;
//  gp_symbol_t           *next;
  int                    num_clean_errors;

  gp_debug("Cleaning symbol table.");

  num_clean_errors = gp_real_num_errors();

  /* point all relocations to the symbol definitions */
  section = Object->section_list.first;
  while (section) {
    relocation = section->relocation_list.first;
    while (relocation) {
      symbol = relocation->symbol;

      if (symbol->gp_coffgen_is_external_symbol()) {
        /* This is an external symbol defined elsewhere. */
        sym = gp_sym_get_symbol(Symbols, symbol->name);
        if (sym == NULL) {
          gp_error("Non-existent external symbol - \"%s\" - used in \"%s\" section.", symbol->name, section->name);
        }
        else {
          var    = (const gp_coffsymbol_t *)gp_sym_get_symbol_annotation(sym);
          assert(!(var == NULL));
          symbol = var->symbol;
          assert(!(symbol == NULL));
          relocation->symbol = symbol;
        }
      }

      relocation = relocation->next;
    }

    section = section->next;
  }

  if (gp_real_num_errors() > num_clean_errors) {
    exit(1);
  }
  gp_symbol_list_t::iterator it = Object->symbol_list.begin();
  while (it!=Object->symbol_list.end()) {
    if (it->gp_coffgen_is_external_symbol()) {
      gp_debug("  removed symbol \"%s\"", it->name);
      it = Object->gp_coffgen_del_symbol(it, true);
    }else it++;
  }
}

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

/* Update the line number offsets. */

static void
_update_line_numbers(gp_linenum_t *Line_number, unsigned Offset)
{
  while (Line_number) {
    Line_number->address += Offset;
    Line_number = Line_number->next;
  }
}

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

/* Combine overlay sections in an object file. */

FUNC(void) gp_cofflink_combine_overlay(gp_object_t *Object, bool Remove_symbol) {
  int           addr_digits;
  gp_section_t *first;
  gp_section_t *second;
//  gp_symbol_t  *symbol;

  addr_digits = Object->pclass->addr_digits;
  first       = Object->section_list.first;
  while (first) {
    if (FlagIsSet(first->flags, STYP_OVERLAY)) {
      second = gp_coffgen_find_section(Object, first->next, first->name);

      if (second) {
        /* The sections must have the same properties or they can't be combined. */
        if (first->flags != second->flags) {
          gp_error("Section types for \"%s\" do not match.", first->name);
          continue;
        }
        else if (FlagIsSet(first->flags, STYP_ABS) && (first->address != second->address)) {
          gp_error("Different addresses for absolute overlay sections \"%s\" (0x%0*X != 0x%0*X).",
                   first->name, addr_digits, first->address, addr_digits, second->address);
          continue;
        }

        /* Set the size of the first section to the larger of the two. */
        if (second->size > first->size) {
          first->size = second->size;
          first->symbol->aux_list.first->_aux_symbol._aux_scn.length = second->size;
        }

        /* Remove the section symbol. */
        if (Remove_symbol) {
          Object->gp_coffgen_del_symbol(second->symbol, true);
        }

        /* Update the symbol table */
	FOR(gp_symbol_list_t,symbol,Object->symbol_list) {
          if (symbol->section == second) {
            symbol->section = first;
          }
        }

        /* Remove the second section. */
        gp_coffgen_del_section(Object, second);

        /* Take another pass. */
        gp_cofflink_combine_overlay(Object, Remove_symbol);
        return;
      }
    }
    first = first->next;
  }
}

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

/* Allocate memory for a stack. */

FUNC(void) gp_cofflink_make_stack(gp_object_t *Object, unsigned Num_bytes) {
  gp_section_t *pnew;
  unsigned           i;

  pnew = gp_coffgen_add_section(Object, ".stack", NULL);
  pnew->flags = STYP_BSS;
  pnew->size  = Num_bytes;

  gp_debug("Allocating stack memory of size %#x (%i).", Num_bytes, Num_bytes);

  /* mark the memory locations as used */
  for (i = 0; i < Num_bytes; i++) {
    gp_mem_b_put(pnew->data, i, 0, ".stack", NULL);
  }

  /* create the symbol for the start address of the stack */
  gp_symbol_list_t::iterator symbol = Object->gp_coffgen_find_symbol("_stack");
  if (&*symbol && symbol->section_number > N_UNDEF) {
    gp_error("The \"_stack\" symbol already exists.");
  }else{
    symbol = Object->gp_coffgen_add_symbol("_stack", N_SCNUM);
    symbol->section = pnew;
    symbol->pclass   = C_EXT;
  }

  /* create the symbol for the end of the stack */
  symbol = Object->gp_coffgen_find_symbol("_stack_end");
  if (&*symbol && symbol->section_number > N_UNDEF) {
    gp_error("The \"_stack_end\" symbol already exists.");
  }else{
    symbol = Object->gp_coffgen_add_symbol("_stack_end", N_SCNUM);
    symbol->value   = Num_bytes - 1;
    symbol->section = pnew;
    symbol->pclass   = C_EXT;
  }
}

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

/* Merge all sections in one object file with the same name. The overlayed
   sections must have been combined first.  */

FUNC(void) gp_cofflink_merge_sections(gp_object_t *Object) {
  int           addr_digits;
  gp_section_t *first;
  gp_section_t *second;
//  gp_symbol_t  *symbol;
  gp_reloc_t   *relocation;
  unsigned  section_org;
  uint8_t       data;
  const char   *section_name;
  const char   *symbol_name;
  unsigned  last;
  unsigned  offset;
  unsigned  byte_addr;

  addr_digits = Object->pclass->addr_digits;
  first       = Object->section_list.first;
  while (first) {
    second = gp_coffgen_find_section(Object, first->next, first->name);

    if (second) {
      /* The sections must have the same properties or they can't be combined. */
      if (FlagIsSet(first->flags, STYP_ABS) ||
          FlagIsSet(second->flags, STYP_ABS) ||
          (strcmp(first->name, ".config") == 0) ||
          (strcmp(first->name, ".idlocs") == 0)) {
        gp_error("File \"%s\", section \"%s\" (0x%0*X) is absolute but occurs in more than one file.",
                 Object->filename, first->name, addr_digits, first->address);
        exit(1);
      }

      gp_debug("  merging section \"%s\" with section \"%s\"", first->name, second->name);

      /* Update the addresses in the relocation table. */
      relocation = second->relocation_list.first;
      while (relocation) {
        relocation->address += first->size;
        relocation = relocation->next;
      }

      /* Copy the section data. */
      if (gp_coffgen_section_has_data(second)) {
        last   = second->size;
        offset = first->size;

        if (!gp_coffgen_section_has_data(first)) {
          /* TODO optimization: adopt data from second by moving second->size bytes from org to org + offset */
          first->data = gp_mem_i_create();
        }

        for (byte_addr = 0; byte_addr < last; byte_addr++) {
          if (gp_mem_b_get(second->data, byte_addr, &data, &section_name, &symbol_name)) {
            gp_mem_b_put(first->data, byte_addr + offset, data, section_name, symbol_name);
          }
          else {
            assert(0);
          }
        }
      }

      /* Update the line number offsets. */
      _update_line_numbers(second->line_number_list.first, first->size);

      if (FlagIsSet(first->flags, STYP_ROM_AREA)) {
        section_org = gp_processor_insn_from_byte_c(Object->pclass, first->size);
      }
      else {
        section_org = first->size;
      }

      /* Update the symbol table. */
      FOR(gp_symbol_list_t,symbol,Object->symbol_list)
       if (symbol->section_number > N_UNDEF && symbol->section == second) {
        symbol->section  = first;
        symbol->value   += section_org;
      }

      /* Add section sizes. */
      first->size += second->size;

      /* Append the relocations from the second section to the first. */
      /* Append the line numbers from the second section to the first. */
      gp_coffgen_transfer_section_data(first, second);
      gp_coffgen_update_all_section_id(first);

      /* Remove the second section. */
      gp_coffgen_del_section(Object, second);

      /* Take another pass. */
      gp_cofflink_merge_sections(Object);
      return;
    }
    first = first->next;
  }
}

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

/* copy data from idata section to the ROM section */

static void
_copy_rom_section(const gp_object_t *Object, const gp_section_t *Idata, gp_section_t *Rom)
{
  proc_class_t  pclass;
  int           from;
  int           stop;
  int           to;
  const char   *section_name;
  const char   *symbol_name;
  uint8_t       data;
  uint16_t      insn_retlw;

  pclass = Object->pclass;
  from  = Idata->address;
  stop  = from + Idata->size;
  to    = Rom->address;

  if (pclass->rom_width == 8) {
    /* PIC16E */
    for ( ; from < stop; ++from, ++to) {
      if (gp_mem_b_get(Idata->data, from, &data, &section_name, &symbol_name)) {
        gp_mem_b_put(Rom->data, to, data, section_name, symbol_name);
      }
    }
  }
  else {
    /* PIC12, PIC12E, PIC12I, SX, PIC14, PIC14E, PIC14EX, PIC16 */
    /* select "retlw" instruction */
    insn_retlw = gp_processor_retlw(pclass);

    for ( ; from < stop; ++from, to += 2) {
      if (gp_mem_b_get(Idata->data, from, &data, &section_name, &symbol_name)) {
        pclass->i_memory_put(Rom->data, to, insn_retlw | data, section_name, symbol_name);
      }
    }
  }
}

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

/* create the pnew section name */

static char* _create_i_section_name(const char *Name) {
  size_t len = strlen(Name);
  char*name_i = new char[len + 3];

  memcpy(name_i, Name, len);
  memcpy(name_i + len, "_i", 3);
  return name_i;
}

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

/* create a program memory section to hold the data */

static void _create_rom_section(gp_object_t *Object, gp_section_t *Section) {
  /* create the pnew section */
  char*name = _create_i_section_name(Section->name);
  gp_section_t*pnew  = gp_coffgen_new_section(name, NULL);
  delete[] name;

  if (Object->pclass->rom_width == 8) {
    /* PIC16E */
    pnew->size = Section->size;
    /* Force the section size to be an even number of bytes. */
    if (Section->size & 1) {
      gp_mem_b_put(pnew->data, Section->size, 0, Object->filename, "adjust");
      (pnew->size)++;
    }
  }else{
    /* PIC12, PIC12E, PIC12I, SX, PIC14, PIC14E, PIC14EX, PIC16 */
    pnew->size = Section->size << 1;
  }
  pnew->flags = STYP_DATA_ROM;

  /* Copy the data to get the MEM_USED_MASK correct. It is copied again later
     to ensure that any patched data is updated in the ROM section. */
  _copy_rom_section(Object, Section, pnew);

  /* Insert the pnew ROM section after the idata section. */
  gp_coffgen_insert_after_section(Object, Section, pnew);
}

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

/* write a word (16 bit) into four bytes of memory (non PIC16E) */

static void _write_table_u16(proc_class_t Class, const gp_section_t *Section,
	unsigned Byte_address,
	unsigned Insn,
	unsigned Data,
	const char *Symbol_name) {
  Class->i_memory_put(Section->data, Byte_address,     Insn | (Data & 0xff), Section->name, Symbol_name);
  Class->i_memory_put(Section->data, Byte_address + 2, Insn | (Data >> 8),   Section->name, Symbol_name);
}

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

/* write a long (32 bit) into four bytes of memory (PIC16E) */

static void _write_table_u32(const proc_class_t Class, const gp_section_t *Section,
	unsigned Byte_address,
	unsigned Data,
	const char *Symbol_name) {
  Class->i_memory_put(Section->data, Byte_address,     Data & 0xffff, Section->name, Symbol_name);
  Class->i_memory_put(Section->data, Byte_address + 2, Data >> 16,    Section->name, Symbol_name);
}

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

/* read a word from four bytes of memory (non PIC16E) */

static uint16_t
_read_table_u16(proc_class_t Class, const gp_section_t *Section, unsigned Byte_address)
{
  uint16_t data[2];

  Class->i_memory_get(Section->data, Byte_address,     data,     NULL, NULL);
  Class->i_memory_get(Section->data, Byte_address + 2, data + 1, NULL, NULL);

  return ((data[0] & 0xff) | ((data[1] & 0xff) << 8));
}

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

/* create the symbol for the start address of the table */

FUNC(void) gp_cofflink_make_cinit(gp_object_t *Object) {
  /* create the symbol for the start address of the table */
  /* TODO MPLINK 4.34 does not create this. We must implement the
     section address relocations RELOC_SCN*. */
  gp_symbol_list_t::iterator symbol = Object->gp_coffgen_find_symbol("_cinit");

  if (&*symbol && symbol->section_number > N_UNDEF) {
    gp_error("_cinit symbol already exists.");
  }else{
    symbol = Object->gp_coffgen_add_symbol("_cinit", N_SCNUM);
    symbol->pclass = C_EXT;
  }
}

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

/* create ROM data for initialized data sections */

FUNC(void) gp_cofflink_make_idata(gp_object_t *Object, bool Force_cinit) {
  gp_section_t *section;
  proc_class_t  pclass;
  gp_section_t *pnew;
  int           count_sections;
  int           byte_count;
  int           i;
  int           insn_retlw;
  gp_symbol_t  *symbol;

  count_sections = 0;
  section        = Object->section_list.first;
  while (section) {
    if (FlagIsSet(section->flags, STYP_DATA)) {
      _create_rom_section(Object, section);
      count_sections++;
    }
    section = section->next;
  }

  if ((count_sections > 0) || Force_cinit) {
    pclass = Object->pclass;
    pnew   = gp_coffgen_add_section(Object, ".cinit", NULL);
    pnew->flags = STYP_DATA_ROM;

    byte_count = 2 + count_sections * 12;
    if (pclass->rom_width != 8) {
      /* PIC12, PIC12E, PIC12I, SX, PIC14, PIC14E, PIC14EX, PIC16 */
      /* retlw is used so 16-bit count is stored in 4 bytes not 2 */
      byte_count += 2;
    }

    pnew->size = byte_count;

    /* load the table with data */
    for (i = 0; i < byte_count; i++) {
      gp_mem_b_put(pnew->data, i, 0, ".cinit", "table");
    }

    if (pclass->rom_width == 8) {
      /* PIC16E */
      pclass->i_memory_put(pnew->data, 0, count_sections, ".cinit", "table_size");
    }
    else {
      /* PIC12, PIC12E, PIC12I, SX, PIC14, PIC14E, PIC14EX, PIC16 */
      insn_retlw = gp_processor_retlw(pclass);
      _write_table_u16(pclass, pnew, 0, insn_retlw, count_sections, "table_size");
    }

    /* update the section pointer in _cinit */
    symbol = &*Object->gp_coffgen_find_symbol("_cinit");

    if (!Force_cinit) {
      assert(symbol);
    }

    if (symbol) {
      symbol->section = pnew;
    }
  }
}

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

/* load the relocated sections addresses in the table */

FUNC(void) gp_cofflink_add_cinit_section(gp_object_t *Object) {
  gp_section_t       *section;
  proc_class_t        pclass;
  const gp_section_t *pnew;
  const gp_section_t *prog_section;
  int                 insn_retlw;
  int                 count_sections;
  int                 base_address;
  char               *prog_name;
  uint16_t            number;

  pnew = gp_coffgen_find_section(Object, Object->section_list.first, ".cinit");

  if (pnew) {
    /* scan through the sections to determine the addresses */
    count_sections = 0;
    base_address   = pnew->address;
    pclass          = Object->pclass;

    if (pclass->rom_width == 8) {
      /* PIC16E */
      base_address += 2;
      insn_retlw = 0;
    }
    else {
      /* PIC12, PIC12E, PIC12I, SX, PIC14, PIC14E, PIC14EX, PIC16 */
      base_address += 4;
      insn_retlw = gp_processor_retlw(pclass);
    }

    section = Object->section_list.first;
    while (section) {
      if (FlagIsSet(section->flags, STYP_DATA)) {
        /* locate the rom table */
        prog_name    = _create_i_section_name(section->name);
        prog_section = gp_coffgen_find_section(Object, Object->section_list.first, prog_name);
        free(prog_name);

        if (pclass->rom_width == 8) {
          /* PIC16E */
          /* Write program memory address (from: source address of data in CODE space). */
          _write_table_u32(pclass, pnew, base_address,
                           gp_processor_insn_from_byte_c(pclass, prog_section->address), "prog_mem_addr");

          /* Write data memory address (to: destination address of values in DATA space). */
          _write_table_u32(pclass, pnew, base_address + 4, section->address, "data_mem_addr");

          /* Write the table size (size: number of bytes to copy from CODE to DATA). */
          _write_table_u32(pclass, pnew, base_address + 8, section->size, "table_size");
        }
        else {
          /* PIC12, PIC12E, PIC12I, SX, PIC14, PIC14E, PIC14EX, PIC16 */
          /* Write program memory address (from: source address of data in CODE space). */
          _write_table_u16(pclass, pnew, base_address, insn_retlw,
                           gp_processor_insn_from_byte_c(pclass, prog_section->address), "prog_mem_addr");

          /* Write data memory address (to: destination address of values in DATA space). */
          _write_table_u16(pclass, pnew, base_address + 4, insn_retlw, section->address, "data_mem_addr");

          /* Write the table size (size: number of bytes to copy from CODE to DATA). */
          _write_table_u16(pclass, pnew, base_address + 8, insn_retlw, section->size, "table_size");
        }

        count_sections++;
        base_address += 12;
      } /* if (FlagIsSet(section->flags, STYP_DATA)) */

      section = section->next;
    } /* while (section) */

    /* make sure the section count matches */
    if (pclass->rom_width == 8) {
      /* PIC16E */
      pclass->i_memory_get(pnew->data, pnew->address, &number, NULL, NULL);
    }
    else {
      /* PIC12, PIC12E, PIC12I, SX, PIC14, PIC14E, PIC14EX, PIC16 */
      number = _read_table_u16(pclass, pnew, pnew->address);
    }
    assert(number == count_sections);
  }
}

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

/* Set the memory used flags in a block of words. */

static void
_set_used(const gp_object_t *Object, MemBlock_t *M, unsigned Org_to_byte_shift,
          unsigned Byte_address, unsigned Size, const char *Type, const char *Section_name,
          bool P16e_align_needed)
{
  uint8_t            data;
  const char        *owner_section_name;
  int                addr_digits;

  if (P16e_align_needed && (Size & 1)) {
    /* code_pack --> STYP_BPACK */
    gp_debug("    align to even size: %u ==> %u", Size, Size + 1);
    ++Size;
  }

  addr_digits = Object->pclass->addr_digits;
  gp_debug("      marking %#x (%u) words from 0x%0*X to 0x%0*X as used", Size, Size,
           addr_digits, gp_insn_from_byte(Org_to_byte_shift, Byte_address),
           addr_digits, gp_insn_from_byte(Org_to_byte_shift, Byte_address + Size - 1));

  for ( ; Size > 0; Byte_address++, Size--) {
    if (gp_mem_b_get(M, Byte_address, &data, &owner_section_name, NULL)) {
      if ((owner_section_name) && (Section_name)) {
        gp_error("More %s sections use same address: 0x%0*X -- \"%s\", \"%s\"", Type,
                 addr_digits, gp_insn_from_byte(Org_to_byte_shift, Byte_address),
                 owner_section_name, Section_name);
      }
      else {
        gp_error("More %s sections use same address: 0x%0*X", Type,
                 addr_digits, gp_insn_from_byte(Org_to_byte_shift, Byte_address));
      }
      return;
    }
    else {
      gp_mem_b_put(M, Byte_address, 0, Section_name, NULL);
    }
  }
}

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

/* allocate space for the absolute sections */

FUNC(void) gp_cofflink_reloc_abs(gp_object_t *Object, MemBlock_t *M,
 unsigned Org_to_byte_shift, uint32_t Flags) {
  gp_section_t *section;
  unsigned  org;
  bool    p16e_align_needed;

  section = Object->section_list.first;
  while (section) {
    if (FlagIsSet(section->flags, STYP_ABS) && FlagIsSet(section->flags, Flags)) {
      /* Workaround for the "odd size memory problem" in the PIC16E pclass.
         code_pack --> STYP_BPACK */
      p16e_align_needed = false;

      if ((Object->pclass == PROC_CLASS_PIC16E) && FlagIsSet(section->flags, STYP_ROM_AREA) &&
          (section->size & 1)) {
        org = gp_processor_insn_from_byte_p(Object->processor, section->address);

        if ((gp_processor_is_idlocs_org(Object->processor, org) < 0) &&
            (gp_processor_is_config_org(Object->processor, org) < 0) &&
            (gp_processor_is_eeprom_org(Object->processor, org) < 0)) {
          p16e_align_needed = true;
        }
      }

      _set_used(Object, M, Org_to_byte_shift, section->address, section->size, "absolute",
                section->name, p16e_align_needed);

      /* Set the relocated flag. */
      FlagSet(section->flags, STYP_RELOC);
    }
    section = section->next;
  }
}

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

/* Search through all the sections in the object list. Locate the biggest
   assigned section that has not been relocated. */

static gp_section_t *
_find_big_assigned(gp_section_t *Section, uint32_t Flags, symbol_table_t *Logical_sections)
{
  gp_section_t   *biggest;
  const symbol_t *sym;

  biggest = NULL;
  while (Section) {
    sym = gp_sym_get_symbol(Logical_sections, Section->name);

    if ((sym) && FlagIsSet(Section->flags, Flags) && FlagIsClr(Section->flags, STYP_RELOC)) {
      /* This section has not been relocated. */
      if ((biggest == NULL) || (biggest->size < Section->size)) {
        biggest = Section;
      }
    }
    Section = Section->next;
  }

  return biggest;
}

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

/* Search through all the sections in the object list.  Locate the biggest
   section that has not been relocated. */

static gp_section_t *
_find_big_section(gp_section_t *Section, uint32_t Flags)
{
  gp_section_t *biggest;

  biggest = NULL;
  while (Section) {
    if (FlagIsSet(Section->flags, Flags) && FlagIsClr(Section->flags, STYP_RELOC)) {
      /* This section has not been relocated. */
      if ((biggest == NULL) || (biggest->size < Section->size)) {
        biggest = Section;
      }
    }
    Section = Section->next;
  }

  if (biggest) {
    gp_debug("  biggest section = %s, section flags = %#x, Flags = %#x",
             biggest->name, biggest->flags, Flags);
  }

  return biggest;
}

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

/* Search through the target memory. Locate the smallest block of memory
   that is larger than the requested size. Return the address of that
   block. */

static bool
_search_memory(const MemBlock_t *M, unsigned Org_to_byte_shift, unsigned Start,
               unsigned Stop, unsigned Size, unsigned *Block_address,
               unsigned *Block_size, bool Stop_at_first)
{
  unsigned address;
  unsigned current_address = 0;
  unsigned current_size = 0;
  bool   mem_used;
  bool   in_block = false;
  bool   end_block = false;
  bool   success = false;
  uint8_t      byte;

  /* set the size to max value */
  *Block_size = (unsigned)(-1);

  for (address = Start; address <= Stop; address++) {
    mem_used = gp_mem_b_get(M, address, &byte, NULL, NULL);

    if (address == Stop) {
      if (in_block) {
        /* end of the section definition */
        end_block = true;
        /* increment for last address */
        current_size++;
      }
      else if (Start == Stop) {
        /* special case, one word section */
        if (! mem_used) {
          end_block = true;
          current_address = Start;
          current_size    = 1;
        }
      }
      in_block = false;
    }
    else if (mem_used) {
      if (in_block) {
        /* end of an unused block of memory */
        end_block = true;
      }
      in_block = false;
    }
    else {
      if (! in_block) {
        /* start of an unused block of memory */
        gp_debug("    start unused block at %#x", gp_insn_from_byte(Org_to_byte_shift, address));
        current_address = address;
        current_size    = 1;
      }
      else {
        /* continuation of an unused block of memory */
        current_size++;
      }
      in_block = true;
    }

    if (end_block) {
      gp_debug("    end unused block at %#x with size %#x",
               gp_insn_from_byte(Org_to_byte_shift, address),
               gp_insn_from_byte(Org_to_byte_shift, current_size));
      if (current_size >= Size) {
        if (Stop_at_first) {
          *Block_size    = current_size;
          *Block_address = current_address;
          success = true;
          break;
        }
        else if (current_size < *Block_size) {
          *Block_size    = current_size;
          *Block_address = current_address;
          success = true;
        }
      }
      end_block = false;
    }
  }

  return success;
}

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

/* Move data in i_memory. This function assumes the move will be towards
   a higher address. */

static void
_move_data(MemBlock_t *M, unsigned Byte_address, unsigned Size, unsigned New_address)
{
  int         org;
  uint8_t     data;
  const char *section_name;
  const char *symbol_name;

  if (Byte_address == New_address) {
    return;
  }

  gp_debug("    moving %#x (%u) bytes from %#x to %#x", Size, Size, Byte_address, New_address);

  for (org = Byte_address + Size - 1; org >= 0; org--) {
    gp_mem_b_assert_get(M, org, &data, &section_name, &symbol_name);
    gp_debug("      moving byte %#x from %#x to %#x", data, org, New_address + org);
    gp_mem_b_put(M, New_address + org, data, section_name, symbol_name);
    gp_mem_b_clear(M, org);
  }
}

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

/* map real addres to shadow address */

static unsigned
_map_to_shadow_address(const linker_section_t *Section_def, unsigned Address)
{
  unsigned new_address;

  if (Section_def->shadow_sym) {
    new_address = Address + Section_def->shadow_val - Section_def->start;
    gp_debug("      mapping shadow address %#x => %#x", Address, new_address);
    return new_address;
  }
  else {
    return Address;
  }
}

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

/* unmap real addres from shadow address */

static unsigned
_unmap_from_shadow_address(const linker_section_t *Section_def, unsigned Address)
{
  unsigned new_address;

  if (Section_def->shadow_sym) {
    new_address = Address + Section_def->start - Section_def->shadow_val;
    gp_debug("      unmapping shadow address %#x => %#x", Address, new_address);
    return new_address;
  }
  else {
    return Address;
  }
}

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

/* Compare function for gp_sym_clone_symbol_array(). */

static int _cdecl _sect_addr_cmp(const void *P0, const void *P1)
{
  const symbol_t *sym0 = *(const symbol_t **)P0;
  const symbol_t *sym1 = *(const symbol_t **)P1;

  const linker_section_t *sect0 = (const linker_section_t*)gp_sym_get_symbol_annotation(sym0);
  const linker_section_t *sect1 = (const linker_section_t*)gp_sym_get_symbol_annotation(sym1);

  if (sect0->start < sect1->start) return -1;

  if (sect0->start > sect1->start) return 1;

  return 0;
}

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

/* allocate memory for relocatable assigned sections */

FUNC(void) gp_cofflink_reloc_assigned(gp_object_t *Object, MemBlock_t *M, unsigned Org_to_byte_shift,
                           uint32_t Flags, symbol_table_t *Sections, symbol_table_t *Logical_sections) {
  gp_section_t     *section;
  gp_section_t     *current;
  const symbol_t   *sym;
  char             *section_name;
  linker_section_t *section_def;
  unsigned      current_shadow_address;
  unsigned      current_size;
  unsigned      org;
  bool        p16e_align_needed;

  section = Object->section_list.first;
  while (true) {
    current = _find_big_assigned(section, Flags, Logical_sections);

    if (current == NULL) {
      break;
    }

    /* Fetch the logical section. */
    sym = gp_sym_get_symbol(Logical_sections, current->name);
    assert(sym);

    /* Fetch the section definition. */
    section_name = (char *)gp_sym_get_symbol_annotation(sym);
    sym          = gp_sym_get_symbol(Sections, section_name);
    assert(sym);

    section_def = (linker_section_t *)gp_sym_get_symbol_annotation(sym);
    assert(section_def);

    p16e_align_needed = false;

    /* Workaround for the "odd size memory problem" in the PIC16E pclass.
       code_pack --> STYP_BPACK */
    if ((Object->pclass == PROC_CLASS_PIC16E) &&
        FlagIsSet(current->flags, Flags) &&
        FlagIsSet(current->flags, STYP_ABS) &&
        FlagIsSet(current->flags, STYP_ROM_AREA) &&
        (current->size & 1)) {
      org = gp_processor_insn_from_byte_p(Object->processor, current->address);

      if ((gp_processor_is_idlocs_org(Object->processor, org) < 0) &&
          (gp_processor_is_config_org(Object->processor, org) < 0) &&
          (gp_processor_is_eeprom_org(Object->processor, org) < 0)) {
        p16e_align_needed = true;
      }
    }

    /* assign the address to this section */
    if (_search_memory(M, Org_to_byte_shift,
                       _map_to_shadow_address(section_def, section_def->start),
                       _map_to_shadow_address(section_def, section_def->end),
                       (p16e_align_needed) ? (current->size + 1) : current->size,
                       &current_shadow_address, &current_size, false) == 1) {
      gp_debug("    logical section: '%s'", current->name);
      gp_debug("    section name   : '%s'", section_name);
      gp_debug("    successful relocation to %#x", gp_insn_from_byte(Org_to_byte_shift, current_shadow_address));

      if (gp_coffgen_section_has_data(current)) {
        _move_data(current->data, current->shadow_address, current->size, current_shadow_address);
      }

      current->shadow_address = current_shadow_address;
      current->address        = _unmap_from_shadow_address(section_def, current_shadow_address);
      _set_used(Object, M, 0, current_shadow_address, current->size, "relocatable assigned",
                section_name, p16e_align_needed);

      /* Update the line number offsets. */
      _update_line_numbers(current->line_number_list.first, current->address);

      /* Set the relocated flag. */
      FlagSet(current->flags, STYP_RELOC);
    }
    else {
      gp_error("No target memory available for section \"%s\".", current->name);
      return;
    }
  }
}

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

/* allocate memory for cinit section */

FUNC(void) gp_cofflink_reloc_cinit(gp_object_t *Object, MemBlock_t *M, unsigned Org_to_byte_shift, gp_section_t *Cinit_section, const symbol_table_t *Sections) {
  unsigned       size;
  bool         success;
  bool         type_avail;
  unsigned       current_shadow_address;
  unsigned       current_size;
  unsigned       smallest_shadow_address;
  unsigned       smallest_address;
  size_t             i;
  const symbol_t   **sym_list;
  const symbol_t    *sym;
  size_t             sym_count;
  linker_section_t  *section_def;

  if ((Cinit_section == NULL) || FlagIsSet(Cinit_section->flags, STYP_RELOC)) {
    return;
  }

  size = Cinit_section->size;

  gp_debug("  Relocating cinit code.");

  success                 = false;
  type_avail              = false;
  smallest_shadow_address = (unsigned)(-1);
  smallest_address        = (unsigned)(-1);
  sym_list                = gp_sym_clone_symbol_array(Sections, _sect_addr_cmp);

  /* Search the section definitions for the smallest block of memory that the section will fit in. */

  sym_count = gp_sym_get_symbol_count(Sections);
  for (i = 0; i < sym_count; ++i) {
    sym         = sym_list[i];
    section_def = (linker_section_t *)gp_sym_get_symbol_annotation(sym);

    if ((section_def->type == SECT_CODEPAGE) && !section_def->s_protected) {
      gp_debug("  section     = '%s'",     Cinit_section->name);
      gp_debug("    name      = '%s'",     gp_sym_get_symbol_name(sym));
      gp_debug("    size      = %#x (%u)", Cinit_section->size, Cinit_section->size);
      gp_debug("    def start = %#x",      section_def->start);
      gp_debug("    def end   = %#x",      section_def->end);

      if (section_def->shadow_sym) {
        gp_debug("    def shadow_sym = %s",  section_def->shadow_sym);
        gp_debug("    def shadow_val = %#x", section_def->shadow_val);
      }

      type_avail = true;

      if (_search_memory(M, Org_to_byte_shift,
                         _map_to_shadow_address(section_def, section_def->start),
                         _map_to_shadow_address(section_def, section_def->end),
                         size, &current_shadow_address, &current_size, true) == 1) {
        success = true;

        if (smallest_shadow_address > current_shadow_address) {
          smallest_shadow_address = current_shadow_address;
          smallest_address        = _unmap_from_shadow_address(section_def, smallest_shadow_address);
        }
      }
    }
  }

  free((void*)sym_list);

  /* set the memory used flag for all words in the block */
  if (success) {
    gp_debug("    successful relocation to %#x", gp_insn_from_byte(Org_to_byte_shift, smallest_shadow_address));

    if (gp_coffgen_section_has_data(Cinit_section)) {
      _move_data(Cinit_section->data, Cinit_section->shadow_address, size, smallest_shadow_address);
    }
    Cinit_section->shadow_address = smallest_shadow_address;
    Cinit_section->address        = smallest_address;
    _set_used(Object, M, 0, smallest_shadow_address, size, "cinit", Cinit_section->name, false);

    /* Update the line number offsets. */
    _update_line_numbers(Cinit_section->line_number_list.first, Cinit_section->address);

    /* Set the relocated flag. */
    FlagSet(Cinit_section->flags, STYP_RELOC);
  }
  else if (!type_avail) {
    gp_error("Linker script has no definition that matches the type of section \"%s\".",
             Cinit_section->name);
  }
  else {
    gp_error("No target memory available for section \"%s\".", Cinit_section->name);
  }
}

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

/* allocate memory for relocatable unassigned sections */

FUNC(void) gp_cofflink_reloc_unassigned(
 gp_object_t *Object, MemBlock_t *M, unsigned Org_to_byte_shift,
 uint32_t Flags, const symbol_table_t *Sections) {

  gp_section_t*section = Object->section_list.first;
  while (true) {
    gp_section_t*current = _find_big_section(section, Flags);

    if (!current) break;

    unsigned size = current->size;
    bool p16e_align_needed = false;

    /* determine what type of sections are being relocated */
    enum section_type type;
    const char*msg;
    if (FlagIsSet(current->flags, STYP_ROM_AREA)) {
      type = SECT_CODEPAGE;
      msg  = "relocating codepage";
      gp_debug("  relocating code");
    }else if (FlagIsSet(current->flags, STYP_ACCESS)) {
      type = SECT_ACCESSBANK;
      msg  = "relocating accessbank";
      gp_debug("  relocating accessbank");
    }else if (FlagIsSet(current->flags, STYP_SHARED)) {
      type = SECT_SHAREBANK;
      msg  = "relocating sharebank";
      gp_debug("  relocating sharebank");
    }else {
      type = SECT_DATABANK;
      msg  = "relocating databank";
      gp_debug("  relocating data");
    }

    bool first_time = true;

    /* Workaround for the "odd size memory problem" in the PIC16E pclass. */
    if ((Object->pclass == PROC_CLASS_PIC16E) && (type == SECT_CODEPAGE) && (size & 1)) {
      unsigned org = gp_processor_insn_from_byte_p(Object->processor, current->address);

      if ((gp_processor_is_idlocs_org(Object->processor, org) < 0) &&
          (gp_processor_is_config_org(Object->processor, org) < 0) &&
          (gp_processor_is_eeprom_org(Object->processor, org) < 0)) {
        p16e_align_needed = true;
      }
    }


next_pass:
    bool success = false;
    bool type_avail = false;
    unsigned smallest_shadow_address = 0;
    unsigned smallest_address = 0;
    unsigned smallest_size = (unsigned)(-1);
    const symbol_t**sym_list = gp_sym_clone_symbol_array(Sections, _sect_addr_cmp);

    /* search the section definitions for the smallest block of memory that
       the section will fit in */
    size_t sym_count = gp_sym_get_symbol_count(Sections);
    for (size_t i = 0; i < sym_count; ++i) {
      const symbol_t*sym = sym_list[i];
      linker_section_t*section_def = (linker_section_t*)gp_sym_get_symbol_annotation(sym);

      if ((section_def->type == type) && (!section_def->s_protected)) {
        gp_debug("  section     = '%s'",     current->name);
        gp_debug("    name      = %s",       gp_sym_get_symbol_name(sym));
        gp_debug("    size      = %#x (%u)", size, size);
        gp_debug("    def start = %#x",      section_def->start);
        gp_debug("    def end   = %#x",      section_def->end);

        if (section_def->shadow_sym) {
          gp_debug("    def shadow_sym = %s",  section_def->shadow_sym);
          gp_debug("    def shadow_val = %#x", section_def->shadow_val);
        }

        type_avail = true;

        unsigned current_shadow_address, current_size;
        if (_search_memory(M, Org_to_byte_shift,
                           _map_to_shadow_address(section_def, section_def->start),
                           _map_to_shadow_address(section_def, section_def->end),
                           (p16e_align_needed) ? (size + 1) : size,
                           &current_shadow_address, &current_size, false) == 1) {
          success = true;

          if (smallest_size > current_size) {
            smallest_size           = current_size;
            smallest_shadow_address = current_shadow_address;
            smallest_address        = _unmap_from_shadow_address(section_def, smallest_shadow_address);
          }
        }
      }
    }

    free((void*)sym_list);

    /* set the memory used flag for all words in the block */
    if (success) {
      gp_debug("    successful relocation to %#x", gp_insn_from_byte(Org_to_byte_shift, smallest_shadow_address));

      if (gp_coffgen_section_has_data(current)) {
        _move_data(current->data, current->shadow_address, size, smallest_shadow_address);
      }

      current->shadow_address = smallest_shadow_address;
      current->address        = smallest_address;
      _set_used(Object, M, 0, smallest_shadow_address, size, msg, current->name, p16e_align_needed);

      /* Update the line number offsets */
      _update_line_numbers(current->line_number_list.first, current->address);

      /* Set the relocated flag */
      FlagSet(current->flags, STYP_RELOC);
    }
    else if (gp_relocate_to_shared && first_time && (type == SECT_DATABANK)) {
      first_time = false;
      type       = SECT_SHAREBANK;
      gp_warning("Relocation of section \"%s\" failed, relocating to a shared memory location.", current->name);
      goto next_pass;
    }
    else if (!type_avail) {
      gp_error("Linker script has no definition that matches the type of section \"%s\".", current->name);
      return;
    }
    else {
      gp_error("No target memory available for section \"%s\".", current->name);
      return;
    }
  }
}

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

/* Update all symbols with their pnew relocated values. */

FUNC(void) gp_cofflink_update_table(gp_object_t *Object, unsigned Org_to_byte_shift) {
  gp_section_t *section;
  gp_section_t *sym_sect;
  unsigned  offset;

  gp_debug("Updating symbols with their pnew relocated values.");

  FOR(gp_symbol_list_t,symbol,Object->symbol_list) {
    if (symbol->section_number > N_UNDEF) {
      sym_sect = symbol->section;
      assert(sym_sect);

      if (FlagIsClr(sym_sect->flags, STYP_ABS)) {
        offset = sym_sect->address;

        if (FlagIsSet(sym_sect->flags, STYP_ROM_AREA)) {
          offset = gp_insn_from_byte(Org_to_byte_shift, offset);
        }

        symbol->value += offset;
      }
    }
  }

  gp_debug("Stripping section relocated flag.");

  section = Object->section_list.first;
  while (section) {
    FlagClr(section->flags, STYP_RELOC);
    section = section->next;
  }
}

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

/* Create sections to fill unused memory in the pages with constant data. */

FUNC(void) gp_cofflink_fill_pages(gp_object_t *Object, MemBlock_t *M, const symbol_table_t *Sections) {
  linker_section_t  *section_def;
  size_t             i;
  const symbol_t   **sym_list;
  const symbol_t    *sym;
  size_t             sym_count;
  bool         found;
  char               fill_name[BUFSIZ];
  unsigned       fill_number;
  gp_section_t      *section;
  unsigned       current_shadow_address;
  unsigned       current_size;
  unsigned       org;
  unsigned       end;

  gp_debug("Adding fill sections.");
  sym_list = gp_sym_clone_symbol_array(Sections, _sect_addr_cmp);

  /* search for any section definitions that have a fill */
  sym_count   = gp_sym_get_symbol_count(Sections);
  fill_number = 1;
  for (i = 0; i < sym_count; ++i) {
    sym         = sym_list[i];
    section_def = (linker_section_t *)gp_sym_get_symbol_annotation(sym);

    if ((section_def->type == SECT_CODEPAGE) && (section_def->use_fill)) {
      while (true) {
        found = _search_memory(M, Object->pclass->org_to_byte_shift,
                               _map_to_shadow_address(section_def, section_def->start),
                               _map_to_shadow_address(section_def, section_def->end),
                               1, &current_shadow_address, &current_size, false);
        if (found) {
          snprintf(fill_name, sizeof(fill_name), ".fill_%u", fill_number++);
          gp_debug("  pnew section \"%s\" at %#x with size %#x and data %#x",
                   fill_name, current_shadow_address, current_size, section_def->fill);
          section = gp_coffgen_find_section(Object, Object->section_list.first, fill_name);

          if (section) {
            gp_error("Fill section \"%s\" already exists.", fill_name);
            free((void*)sym_list);
            return;
          }
          else {
            /* create a pnew section for the fill data */
            section                 = gp_coffgen_add_section(Object, fill_name, NULL);
            section->address        = _unmap_from_shadow_address(section_def, current_shadow_address);
            section->shadow_address = current_shadow_address;
            section->size           = current_size;
            section->flags          = STYP_TEXT;
            /* FIXME: do we really need a section symbol? */

            /* mark the memory as used */
            _set_used(Object, M, Object->pclass->org_to_byte_shift,
                      current_shadow_address, current_size, "fill", section->name, false);

            /* fill the section memory */
            org = current_shadow_address;
            end = org + current_size;
            for ( ; org < end; org += 2) {
              Object->pclass->i_memory_put(section->data, org, section_def->fill, section->name, NULL);
            }
          }
        }
        else {
          break;
        }
      }
    }
  }

  free((void*)sym_list);
}

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

static void
_check_relative(const gp_section_t *Section, unsigned Org, int Argument, int Range)
{
  /* If the branch is too far then issue a warning */
  if ((Argument > Range) || (Argument < -(Range + 1))) {
    gp_warning("Relative branch out of range in at %#x of section \"%s\".", Org, Section->name);
  }
}

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

/* patch one word with the relocated address */

static void
_patch_addr(gp_object_t *Object, gp_section_t *Section, const gp_reloc_t *Relocation,
            bool Mplink_compatible)
{
  proc_class_t       pclass;
  int                num_pages;
  int                num_banks;
  const gp_symbol_t *symbol;
  int                byte_addr;
  int                value;
  uint16_t           current_value;
  int                data;
  int                offset;
  int                bank;
  int                page;
  bool         write_data;
  const insn_t      *instruction;

  pclass     = Object->pclass;
  num_pages = gp_processor_num_pages(Object->processor);
  num_banks = gp_processor_num_banks(Object->processor);

  symbol    = Relocation->symbol;
  byte_addr = Relocation->address + Section->address;
  value     = symbol->value       + Relocation->offset;

  gp_debug("  patching at %#x from %s/%s with %#x", byte_addr, Section->name, symbol->name, value);

  /* fetch the current contents of the memory */
  pclass->i_memory_get(Section->data, byte_addr, &current_value, NULL, NULL);

  /* FIXME: Not sure if warnings should be generated for out of range arguments.
            The linker should make sure values are within ranges in the linker scripts. */

  data       = 0;
  write_data = true;
  switch (Relocation->type) {
    case RELOC_ALL:
      data = value & 0xffff;
      break;

    case RELOC_CALL:
      data = pclass->reloc_call(value);
      break;

    case RELOC_GOTO:
      data = pclass->reloc_goto(value);
      break;

    case RELOC_LOW: {
      instruction = pclass->find_insn(pclass, current_value);

      if (instruction == NULL)  {
        gp_error("No instruction for %#x at %#x(%s/%s)", current_value,
                 byte_addr, Section->name, symbol->name);
        return;
      }

      if (instruction->pclass == INSN_CLASS_LIT8) {
        data = value & 0xff;
      }
      else {
        data = pclass->reloc_f(value);
      }
      break;
    }

    case RELOC_HIGH:
      data = pclass->reloc_high((bool)FlagIsSet(symbol->section->flags, STYP_ROM_AREA), value);
      break;

    case RELOC_UPPER:
      data = (value >> 16) & 0xff;
      break;

    case RELOC_P:
      data = (value << 8) & PIC16_BMSK_MOVFP;
      break;

    case RELOC_BANKSEL:
      bank = gp_processor_bank_from_addr(pclass, value);
      gp_processor_set_bank(pclass, num_banks, bank, Section->data, byte_addr, Mplink_compatible);
      write_data = false;
      break;

    case RELOC_IBANKSEL:
      bank = gp_processor_check_ibank(pclass, value);
      gp_processor_set_ibank(pclass, num_banks, bank, Section->data, byte_addr);
      write_data = false;
      break;

    case RELOC_F:
      data = pclass->reloc_f(value);
      break;

    case RELOC_TRIS:
    case RELOC_TRIS_3BIT:
      data = pclass->reloc_tris(value);
      break;

    case RELOC_MOVLR:
      data = (value << 4) & PIC16_BMSK_MOVLR;
      break;

    case RELOC_MOVLB:
      data = pclass->reloc_movlb(value);
      break;

    case RELOC_GOTO2:
      /* This is only used for PIC16E (pic18). */
      data = (value >> 9) & PIC16E_BMSK_BRANCH_HIGHER;
      break;

    case RELOC_FF1:
    case RELOC_FF2:
      data = value & PIC16E_BMSK_MOVFF1;
      break;

    case RELOC_LFSR1:
      data = (value >> 8) & PIC16E_BMSK_LFSR1;
      break;

    case RELOC_LFSR2:
      data = value & PIC16E_BMSK_LFSR2;
      break;

    case RELOC_BRA:
      data = pclass->reloc_bra(Section, value, byte_addr);
      break;

    case RELOC_CONDBRA: {
      /* This is only used for PIC16E (pic18). */
      offset = value - byte_addr - 2;

      if (offset & 1) {
        if (symbol->name) {
          gp_warning("Destination address must be word aligned at %#x of section \"%s\" at symbol: \"%s\".",
                     byte_addr, Section->name, symbol->name);
        }
        else {
          gp_warning("Destination address must be word aligned at %#x of section \"%s\".",
                     byte_addr, Section->name);
        }
      }

      offset >>= 1;
      _check_relative(Section, byte_addr, offset, 0x7f);
      data = offset & 0xff;
      break;
    }

    case RELOC_ACCESS:
      data = (gp_processor_is_p16e_access(Object->processor, value, false)) ? 0 : 0x100;
      break;

    case RELOC_PAGESEL_WREG:
      page = gp_processor_check_page(pclass, value);
      gp_processor_set_page(pclass, num_pages, page, Section->data, byte_addr, true);
      write_data = false;
      break;

    case RELOC_PAGESEL_BITS:
    case RELOC_PAGESEL_MOVLP:
      page = gp_processor_check_page(pclass, value);
      gp_processor_set_page(pclass, num_pages, page, Section->data, byte_addr, false);
      write_data = false;
      break;

    /* unimplemented relocations */
    case RELOC_PAGESEL:
    case RELOC_SCNSZ_LOW:
    case RELOC_SCNSZ_HIGH:
    case RELOC_SCNSZ_UPPER:
    case RELOC_SCNEND_LOW:
    case RELOC_SCNEND_HIGH:
    case RELOC_SCNEND_UPPER:
    case RELOC_SCNEND_LFSR1:
    case RELOC_SCNEND_LFSR2:
    default: {
      if (symbol->name) {
        gp_error("Unimplemented relocation = %s (%u) in section \"%s\" at symbol \"%s\".",
                 gp_coffgen_reloc_type_to_str(Relocation->type),
                 Relocation->type, Section->name, symbol->name);
      }
      else {
        gp_error("Unimplemented relocation = %s (%u) in section \"%s\".",
                 gp_coffgen_reloc_type_to_str(Relocation->type),
                 Relocation->type, Section->name);
      }
      assert(0);
    }
  }

  /* update memory with the pnew value */
  if (write_data) {
    pclass->i_memory_put(Section->data, byte_addr, current_value | data, Section->name, symbol->name);
  }
}

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

/* Patch all addresses with the relocated symbols. The relocations are
   stripped from the sections. */

FUNC(void) gp_cofflink_patch(gp_object_t *Object, bool Mplink_compatible) {
  gp_section_t     *section;
  const gp_reloc_t *relocation;

  gp_debug("Patching data with relocated symbols.");

  section = Object->section_list.first;
  while (section) {
    if (gp_coffgen_section_has_data(section)) {
      /* patch raw data with relocation entries */
      relocation = section->relocation_list.first;
      while (relocation) {
        _patch_addr(Object, section, relocation, Mplink_compatible);
        relocation = relocation->next;
      }

      /* update the rom with the patched idata sections */
      if (FlagIsSet(section->flags, STYP_DATA) && (section->relocation_list.first)) {
        assert(FlagIsSet(section->next->flags, STYP_DATA_ROM));
        _copy_rom_section(Object, section, section->next);
      }

      /* strip the relocations from the section */
      gp_list_delete(&section->relocation_list);
    }

    section = section->next;
  }
}

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

/* copy all executable data to pnew memory */

FUNC(MemBlock_t*) gp_cofflink_make_memory(gp_object_t *Object){
  MemBlock_t         *m;
  const gp_section_t *section;
  proc_class_t        pclass;
  pic_processor_t     processor;
  int                 addr_digits;
  unsigned        addr;
  unsigned        org;
  unsigned        stop;
  const char         *section_name;
  const char         *symbol_name;
  uint8_t             byte;
  bool          not_8_bit;

  m           = gp_mem_i_create();
  section     = Object->section_list.first;
  pclass       = Object->pclass;
  processor   = Object->processor;
  addr_digits = pclass->addr_digits;

  if ((pclass == PROC_CLASS_PIC12)  || (pclass == PROC_CLASS_PIC12E) ||
      (pclass == PROC_CLASS_PIC12I) || (pclass == PROC_CLASS_SX)     ||
      (pclass == PROC_CLASS_PIC14)  || (pclass == PROC_CLASS_PIC14E) ||
      (pclass == PROC_CLASS_PIC14EX)) {
    not_8_bit = true;
  }
  else {
    not_8_bit = false;
  }

  while (section) {
    if (FlagIsSet(section->flags, STYP_ROM_AREA)) {
      addr = section->address;
      stop = addr + section->size;
      gp_debug("   make memory: section \"%s\" (0x%0*X - 0x%0*X)", section->name,
               addr_digits, addr, addr_digits, stop - 1);

      for ( ; addr < stop; addr++) {
        /* fetch the current contents of the memory */
        gp_mem_b_assert_get(section->data, addr, &byte, &section_name, &symbol_name);

        if (not_8_bit) {
          org = gp_processor_insn_from_byte_c(pclass, addr);

          if (gp_processor_is_idlocs_org(processor, org) >= 0) {
            if (addr & 1) {
              /* This is higher byte. */
              byte |= ((Object->processor->idlocs_mask) >> 8) & 0xFF;
            }
            else {
              /* This is lower byte. */
              byte |= Object->processor->idlocs_mask & 0xFF;
            }
          }
        }

        /* write data to pnew memory */
        gp_mem_b_put(m, addr, byte, section_name, symbol_name);
      }
    }
    section = section->next;
  }

 return m;
}
Detected encoding: UTF-80