Quelltext /~heha/hs/gputils64-210929.zip/gplink/gplink.cpp

/* GNU PIC Linker
   Copyright 2001-2005	Craig Franklin
   Copyright 2015-2016	Molnár Károly
*/

#include "stdhdr.h"

#include "libgputils.h"
#include "gplink.h"
#include "cod.h"
#include "scan.h"
#include "lst.h"
#include "map.h"
#include "script.h"

extern int yyparse(void);
extern bool yydebug;

#define OPTIMIZE_LEVEL_DEFAULT          1

struct gplink_state state;
static bool   processor_mismatch_warning;
static bool   enable_cinit_wanings;

static const char longopts[] =
  "ahex-format\1FMT\0"
  "boptimize-banksel\1OPT\0"
  "cobject\0"
  "Cno-cinit-warnings\0"
  "ddebug\0"
  "ffill\1VALUE\0"
  "hhelp\0"
  "Iinclude\1DIR\0"
  "jno-save-local\0"
  "lno-list\0"
  "mmap\0"
  "\2mplink-compatible\0"
  "ooutput\1FILE\0"
  "Ooptimize\1OPT\0"
  "poptimize-pagesel\1OPT\0"
  "qquiet\0"
  "ruse-shared\0"
  "sscript\1FILE\0"
  "tstack\1SIZE\0"
  "Sstrict\1[0|1|2]\0"
  "\1strict-options\0"
  "umacro\1symbol[=value]\0"
  "vversion\0"
  "wprocessor-mismatch\0";

/*------------------------------------------------------------------------------------------------*/
static const char longdesc[] =
  "Select hex file format.\0"
  "Remove unnecessary Banksel directives. [0]\0"
  "Output executable object file.\0"
  "Disable this warnings of _cinit section with -O2 option:\n"
	"\"Relocation symbol _cinit has no section.\"\0"
  "Output debug messages.\0"
  "Fill unused program memory with value.\0"
  "Show this usage message.\0"
  "Specify include directory.\0"
  "Disable the save of local registers to COD file.\0"
  "Disable list file output.\0"
  "Output a map file.\0"
  "MPLINK compatibility mode.\0"
  "Alternate name of output file.\0"
  "Optimization level. [1]\0"
  "Remove unnecessary Pagesel directives. [0]\0"
  "Quiet.\0"
  "Use shared memory if necessary.\0"
  "Linker script.\0"
  "Create a stack section.\0"
  "Set the strict level of the missing symbol.\n"
	"0: This is the default. No message.\n"
	"1: Show warning message if there is missing symbol.\n"
	"2: Show error message if there is missing symbol.\0"
  "If this is set, then an option may not be parameter"
	" of an another option. For example: -s --quiet\0"
  "Add macro value for script.\0"
  "Show version.\0"
  "Disable \"processor mismatch\" warning.\0";

static void _show_usage(const char*argv0) {
  printf("Usage: %s [options] [objects] [libraries]\n", argv0);
  gp_usage(0,longopts,longdesc,32,79,
    "Default linker script path %s\n"
    "Default library path %s\n",
    gp_lkr_path ? gp_lkr_path : "NOT SET",
    gp_lib_path ? gp_lib_path : "NOT SET");
  exit(0);
}

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

/* return the number of missing symbols */

static size_t _count_missing() {
  return gp_sym_get_symbol_count(state.symbol.missing);
}

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

static void _object_append(gp_object_t *Object) {
  /* append the entry to the list */
  if (!state.object) {
    state.object    = Object;
    /* store the processor type from the first object file */
    state.processor = Object->processor;
    state.pclass    = Object->pclass;
  }else{
    gp_object_t *list = state.object;

    while (list->next) {
      list = list->next;
    }
    list->next = Object;

    if (Object->pclass != state.pclass) {
      gp_error("Processor family mismatch in \"%s\".", Object->filename);
    }
    else if ((processor_mismatch_warning) && (Object->processor != state.processor)) {
      gp_warning("Processor mismatch in \"%s\".", Object->filename);
    }
  }

  if (state.optimize.weak_symbols) {
    gp_coffgen_check_relocations(Object, RELOC_DISABLE_WARN);
    gp_coffopt_remove_weak(Object);
  }
}

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

static void _archive_append(gp_archive_t *Archive, const char *Name) {
  /* make the pnew entry */
  archivelist_t*pnew = new archivelist_t;
  memset(pnew,0,sizeof*pnew);
  pnew->name    = GP_Strdup(Name);
  pnew->archive = Archive;

  /* append the entry to the list */
  if (!state.archives) state.archives = pnew;
  else state.archives_tail->next = pnew;
  state.archives_tail = pnew;
}

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

/* Scan the archive for missing symbol definitions.  This has to be done
   recursively.  The order of the archive members is unknown and there
   might be inter member dependancies.  Scan the archive muliple times.
   Stop whenever a complete pass through the archive happens and no
   objects are added. */

static bool _scan_index(symbol_table_t *Table) {
  int num_added = 1; /* initalize to 1 so while loop can be entered */
  bool modified  = false;
  while (num_added) {
    num_added = 0;
    for (size_t i = 0; i < gp_sym_get_symbol_count(state.symbol.missing); ++i) {
      const symbol_t *sym_miss = gp_sym_get_symbol_with_index(state.symbol.missing, i);
      const char *name = gp_sym_get_symbol_name(sym_miss);
      assert(name);
      /* Search for missing symbol name in archive symbol table. */
      const symbol_t *sym_arch = gp_sym_get_symbol(Table, name);

      if (sym_arch) {
        /* Fetch the archive member, convert its binary data to an object
           file, and add the object to the object list. */
        gp_archive_t *member = (gp_archive_t*)gp_sym_get_symbol_annotation(sym_arch);
        char*object_name = gp_archive_member_name(member);
        gp_object_t*object = gp_convert_file(object_name, &member->data);
        _object_append(object);
        gp_cofflink_add_symbols(state.symbol.extern_global, state.symbol.missing, object);
        /* The symbol tables have been modified. Need to take another
           pass to make sure we get everything. */
        num_added++;
        modified = true;
        free(object_name);
        /* This branch of the table has been modified. Go to the next one. */
        break;
      }
    }
  }

  return modified;
}

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

static bool _scan_archive(gp_archive_t *Archive, const char *Name) {
  bool      modified;
  symbol_table_t *archive_tbl;

  state.symbol.archive = gp_sym_push_table(NULL, false);

  /* If necessary, build a symbol index for the archive. */
  if (gp_archive_have_index(Archive) == 0) {
    archive_tbl = gp_sym_push_table(NULL, true);
    gp_archive_make_index(Archive, archive_tbl);
    Archive = gp_archive_add_index(archive_tbl, Archive);
    gp_warning("\"%s\" is missing symbol index.", Name);
    archive_tbl = gp_sym_pop_table(archive_tbl);
  }

  /* Read the symbol index. */
  gp_archive_read_index(state.symbol.archive, Archive);

  /* Scan the symbol index for symbols in the missing symbol table.
     If found, add the object to state.objects. */
  modified = _scan_index(state.symbol.archive);

  state.symbol.archive = gp_sym_pop_table(state.symbol.archive);

  return modified;
}

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

/* Remove a symbol the linker created from the missing table. */

static void _remove_linker_symbol(const char *Name) {
  const symbol_t*sym = gp_sym_get_symbol(state.symbol.missing, Name);
  if (sym) gp_cofflink_remove_symbol(state.symbol.missing, Name);
}

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

/* Add a symbol the linker created to the symbol table. */

static void _add_linker_symbol(const char *Name) {
  gp_symbol_list_t::iterator found = state.object->symbol_list.end();
  FOR(gp_symbol_list_t,current,state.object->symbol_list) 
   if (current->name && !strcmp(current->name, Name) && current->section_number > N_UNDEF) {
    found = current;
    break;
  }
  assert(found!=state.object->symbol_list.end());
  gp_cofflink_add_symbol(state.symbol.extern_global, &*found, NULL);
}

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

static void _add_local_ram_symbols() {
  FOR(gp_symbol_list_t,symbol,state.object->symbol_list)
   if (symbol->section
    && FlagIsSet(symbol->section->flags, STYP_RAM_AREA)
    && symbol->pclass == C_STAT) {
//          gp_cofflink_add_symbol(state.symbol.local, symbol, NULL);
    gp_cofflink_add_symbol(state.symbol.local, &*symbol, state.object);
  }
}

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

/* Search the object list for an idata section. */

static void _search_idata() {
  gp_object_t  *object;
  

  object = state.object;
  while (object) {
    gp_section_t*section=object->section_list.first;
    while (section) {
      if (section->flags & STYP_DATA) {
        state.has_idata = true;
        return;
      }
      section = section->next;
    }
    object = object->next;
  }
}

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

/* Build the symbol tables. Determine which objects from the archives are required for linking. */

static void _build_tables() {
  gp_object_t           *object;
  archivelist_t         *arlist;
  bool             modified;
  size_t                 i;
  const symbol_t        *sym;
  const char            *name;
  const gp_coffsymbol_t *var;
  bool             need_exit;

  /* Create the object file symbol tables. */
  object = state.object;
  while (object) {
    gp_cofflink_add_symbols(state.symbol.extern_global, state.symbol.missing, object);
    object = object->next;
  }

  /* All of the objects have been scanned. If there are remaining references
     to symbols, then the archives must contain the missing references. */
  if ((_count_missing() > 0) && (state.archives)) {
    modified = false;
    arlist   = state.archives;
    while (true) {
      if (_scan_archive(arlist->archive, arlist->name)) {
        modified = true;
      }
      if (_count_missing() == 0) {
        /* No more missing references, no need to continue. */
        break;
      }
      else if (arlist->next == NULL) {
        if (modified) {
          /* At least one object was loaded from an archive and there are
             still missing symbols. Scan all the archives again. */
          modified = false;
          arlist   = state.archives;
        }
        else {
          /* Quit */
          break;
        }
      }
      else {
        arlist = arlist->next;
      }
    }
  }

  _search_idata();

  if (state.has_idata) {
    _remove_linker_symbol("_cinit");
  }

  if (state.has_stack) {
    _remove_linker_symbol("_stack");
    _remove_linker_symbol("_stack_end");
  }

  /* All of the archives have been scanned. If there are still missing
     references, it is an error. */
  if (_count_missing() > 0) {
    need_exit = false;
    for (i = 0; i < gp_sym_get_symbol_count(state.symbol.missing); ++i) {
      sym  = gp_sym_get_symbol_with_index(state.symbol.missing, i);
      name = gp_sym_get_symbol_name(sym);
      assert(name);
      var = (const gp_coffsymbol_t *)gp_sym_get_symbol_annotation(sym);
      assert(var);

      switch (state.strict_level) {
        case 1:
          gp_warning("Missing definition for symbol \"%s\", required by \"%s\".", name, var->file->filename);
          break;

        case 2:
          gp_error("Missing definition for symbol \"%s\", required by \"%s\".", name, var->file->filename);
          need_exit = true;
          break;
      }
    }

    if (need_exit) {
      exit(1);
    }
  }
}

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

/* Read a coff object or archive. gplink doesn't care about file extensions.
   This allows alternate extensions such as .a archives and .obj coff objects. */

void gplink_open_coff(const char *Name) {
  gp_object_t  *object;
  gp_archive_t *archive;
  FILE         *coff;
  char         *full_name;
  int           i;

  full_name = GP_Strdup(Name);

  coff = fopen(full_name, "rb");
  if (!coff && !strchr(full_name, PATH_SEPARATOR_CHAR)) {
    /* If no PATH_SEPARATOR_CHAR in name, try searching include pathes. */
    for (i = 0; i < state.num_paths; i++) {
      size_t len = strlen(state.paths[i])
          + strlen(PATH_SEPARATOR_STR)
	  + strlen(Name) + 1;
      delete[] full_name;
      full_name = new char[len];
      snprintf(full_name, len, "%s" PATH_SEPARATOR_STR "%s", state.paths[i], Name);

      coff = fopen(full_name, "rb");
      if (coff) break;
    }
  }

  if (!coff) {
    perror(Name);
    exit(1);
  }

  /* FIXME: Three files are opened, surely one is sufficent. */

  switch (gp_identify_coff_file(full_name)) {
    case GP_COFF_OBJECT_V2:
    case GP_COFF_OBJECT:
      /* read the object */
      object = gp_read_coff(full_name);
      /*object_append(object, full_name);*/
      _object_append(object);
      break;

    case GP_COFF_ARCHIVE:
      /* read the archive */
      archive = gp_archive_read(full_name);
      _archive_append(archive, full_name);
      break;

    case GP_COFF_SYS_ERR:
      gp_error("Can't open file \"%s\".", full_name);
      break;

    case GP_COFF_UNKNOWN:
      gp_error("\"%s\" is not a valid coff object or archive.", full_name);
      break;

    default:
      assert(0);
  }

  free(full_name);
}

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

static void _set_optimize_level() {
  /* default */
  state.optimize.pagesel       = false;
  state.optimize.dead_sections = false;
  state.optimize.weak_symbols  = false;

  switch(state.optimize.level) {
    case 3:     /* fall through */
    case 2: state.optimize.dead_sections = true;	/* fall through */
    case 1: state.optimize.weak_symbols  = true;	/* fall through */
    case 0: break;
    default: gp_error("Invalid optimization level: %i", state.optimize.level);
  }
}

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

static void _init() {
  gp_init();
  memset(&state, 0, sizeof(state));
  /* initialize */
  gp_date_string(state.start_date, sizeof(state.start_date));
  state.hex_format           = INHX32;
  state.optimize.level       = OPTIMIZE_LEVEL_DEFAULT;
  state.cod_file             = OUT_NORMAL;
  state.hex_file             = OUT_NORMAL;
  state.lst_file             = OUT_NORMAL;
  state.map_file             = OUT_SUPPRESS;
  state.obj_file             = OUT_SUPPRESS;
  state.strict_level         = 0;

  /* set default output filename to be a.o, a.hex, a.cod, a.map */
  strncpy(state.base_file_name, "a", sizeof(state.base_file_name));

  state.script_symbols       = gp_sym_push_table(NULL, false);

  /* The symbols are case sensitive. */
  state.symbol.extern_global = gp_sym_push_table(NULL, false);
  state.symbol.local         = gp_sym_push_table(NULL, false);
  state.symbol.missing       = gp_sym_push_table(NULL, false);
  state.section.definition   = gp_sym_push_table(NULL, false);
  state.section.logical      = gp_sym_push_table(NULL, false);
}

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

void
gplink_add_path(const char *Path)
{
  if (state.num_paths < MAX_PATHS) {
    state.paths[state.num_paths++] = GP_Strdup(Path);
  }
  else {
    gp_error("Too many -I paths.");
  }
}

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

/* I have this take the func in anticipation of the option
  printf("  -z, --defsym symbol=value      Add symbol value to symbol table.\n");
*/

static void _parse_define(const char *Optarg, void (*Func)(const char *, long))
{
  long  value = 0;
  char *pc = strchr(Optarg, '=');

  if (pc) {
    *pc++ = '\0';
    value = strtol(pc, &pc, 10);
  }

  Func(Optarg, value);
}

/*------------------------------------------------------------------------------------------------*/
static bool strict_options, usage;

static void _stdcall onOption(void*,char c, const char*arg) {
  if (strict_options) gp_checkarg(arg,longopts);
  switch (c) {
    case 'O': state.optimize.level = atoi(arg);
              _set_optimize_level();			break;
    case 'a': {
      if (!strcasecmp(arg, "inhx8m")) state.hex_format = INHX8M;
      else if (!strcasecmp(arg, "inhx16")) state.hex_format = INHX16;
      else if (!strcasecmp(arg, "inhx32")) state.hex_format = INHX32;
      else gp_error("Invalid hex format \"%s\", expected inhx8m, inhx16, or inhx32.", arg);
    }break;
    case 'b': {
      char*pc;
      state.optimize.banksel = (unsigned)strtol(arg, &pc, 10);
      if (*pc) gp_error("Invalid character %#x in number constant.", *pc);
    }break;
    case 'c': state.obj_file = OUT_NORMAL;		break;
    case 'C': enable_cinit_wanings = false;		break;
    case 'd': gp_debug_disable = false; yydebug = true;	break;
    case 'f': {
      char*pc;
      state.fill_value = strtol(arg, &pc, 16);
      if (*pc) gp_error("Invalid character %#x in number constant.", *pc);
      else if (state.fill_value > 0xffff) gp_error("Fill value exceeds 0xffff: %#x", *pc);
      else state.fill_enable = true;
    }break;
    case '?':
    case 'h': usage = true;				break;
    case 'I': gplink_add_path(arg);			break;
    case 'j': state.cod.no_save_local = true;		break;
    case 'l': state.lst_file = OUT_SUPPRESS;		break;
    case 'm': state.map_file = OUT_NORMAL;		break;
    case 'o': {
      strncpy(state.base_file_name, arg, sizeof state.base_file_name);
      char*pc = strrchr(state.base_file_name,'.');
      if (pc) *pc = 0;
    }break;
    case 'p': {
      char*pc;
      state.optimize.pagesel = (unsigned)strtol(arg, &pc, 10);
      if (*pc) gp_error("Invalid character %#x in number constant.", *pc);
    }break;
    case 'q': gp_quiet = true;				break;
    case 'r': gp_relocate_to_shared = true;		break;
    case 's': state.src_file_names.push_back(arg);	break;
    case 'S': {
      state.strict_level = atoi(arg);
      if (state.strict_level < 0 || state.strict_level > 2)
       gp_error("Invalid strict level: %d (Must be: 0, 1, 2)", state.strict_level);
    }break;
    case 't': {
      char*pc;
      state.stack_size = strtol(arg, &pc, 10);
      if (*pc) gp_error("Invalid character %#x in number constant.", *pc);
      else state.has_stack = true;
    }break;
    case 'u': _parse_define(arg, script_add_symbol_value); break;
    case 'v':
      fprintf(stderr, "%s\n", GPLINK_VERSION_STRING);
      exit(0);
    case 'w': processor_mismatch_warning = false;	break;
    case 2: state.mplink_compatible = true;		break;
    case 1: strict_options = true;			break;
    case 0: {
  /* check if the first file is the linker script */
      const char*pc = strrchr(arg,'.');
      if (pc && !strcasecmp(pc+1,"lkr")) {
        state.src_file_names.push_back(arg);
      }else gplink_open_coff(arg);
    }break;
  }
}

static void _process_args(int Argc, char *Argv[]) {
  enable_cinit_wanings       = true;
  processor_mismatch_warning = true;
  _set_optimize_level();

  gp_getopt(Argv,longopts,onOption,0);

  if (state.src_file_names.empty() || usage) _show_usage(*Argv);

  /* Add the library path to the include paths list last, so that the user
     specified directories are searched first. */
  if (gp_lib_path) gplink_add_path(gp_lib_path);
  if (gp_lkr_path) gplink_add_path(gp_lkr_path);
}

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

static bool _linker() {
  MemBlock_t *data;
  MemBlock_t *program;
//  srcfns_t   *p;

  /* setup output filenames */
  snprintf(state.hex_file_name, sizeof(state.hex_file_name), "%s.hex", state.base_file_name);
  snprintf(state.map_file_name, sizeof(state.map_file_name), "%s.map", state.base_file_name);
  snprintf(state.obj_file_name, sizeof(state.obj_file_name), "%s.cof", state.base_file_name);

  /* Read the script. */
  if (!state.src_file_names.empty()) {
    for (std::list<const char*>::iterator it=state.src_file_names.begin();
	it!=state.src_file_names.end();++it) {
      open_src(*it, false);
      yyparse();
    }
#ifdef USE_DEFAULT_PATHS
  }
  else if ((state.object) && (gp_lkr_path)) {
    /* The processor is known because an object was on the command line. So
       use one of the default scripts that are distributed with gputils. */
    const char *script_name;
    char        file_name[BUFSIZ];

    assert(state.processor);
    script_name = gp_processor_script(state.processor);

    if (script_name == NULL) {
      gp_error("Linker script not specified and can't determine default script.");
      return EXIT_FAILURE;
    }

    snprintf(file_name, sizeof(file_name), "%s" PATH_SEPARATOR_STR "%s", gp_lkr_path, script_name);
    gp_message("Using default linker script \"%s\".", file_name);
    open_src(file_name, false);
    yyparse();
#endif
  }
  else {
    /* The user must supply the linker script name.  The processor isn't
       commanded so the linker has no way to pick. */
    gp_error("Linker script not specified.");
    return EXIT_FAILURE;
  }

  if (state.object == NULL) {
    gp_error("Missing input object file.");
    return EXIT_FAILURE;
  }

  /* An error occured while reading the input files, no need to continue. */
  if (gp_num.errors) return EXIT_FAILURE;

  /* Construct the symbol tables. Determine which archive members are
     required to resolve external references. */
  _build_tables();

  /* combine all object files into one object */
  gp_cofflink_combine_objects(state.object);

  /* add the stack section */
  if (state.has_stack) {
    gp_cofflink_make_stack(state.object, state.stack_size);
    _add_linker_symbol("_stack");
    _add_linker_symbol("_stack_end");
  }

  if (state.has_idata) {
    gp_cofflink_make_cinit(state.object);
    _add_linker_symbol("_cinit");
  }

  /* clean up symbol table */
  gp_cofflink_clean_table(state.object, state.symbol.extern_global);
  gp_coffgen_check_relocations(state.object, enable_cinit_wanings);

  if (state.optimize.dead_sections) {
    gp_coffopt_remove_dead_sections(state.object, 0, enable_cinit_wanings);
  }

  /* combine overlay sections */
  gp_cofflink_combine_overlay(state.object, 0);

  /* combine all sections with the same name */
  gp_cofflink_merge_sections(state.object);
  gp_symbol_make_hash_table(state.object);

  /* create ROM data for initialized data sections */
  gp_cofflink_make_idata(state.object, state.mplink_compatible);

  /* create memory representing target memory */
  data    = gp_mem_i_create();
  program = gp_mem_i_create();

  /* allocate memory for absolute sections */
  gp_debug("Verifying absolute sections.");
  gp_cofflink_reloc_abs(state.object, program, state.pclass->org_to_byte_shift,
                        STYP_ROM_AREA);

  gp_cofflink_reloc_abs(state.object, data, 0,
                        STYP_RAM_AREA | STYP_SHARED | STYP_OVERLAY | STYP_ACCESS);

  if (state.mplink_compatible) {
    /* allocate cinit section to the lowest possible address */
    gp_section_t *cinit_section;

    cinit_section = gp_coffgen_find_section(state.object, state.object->section_list.first, ".cinit");

    if (cinit_section) {
      gp_cofflink_reloc_cinit(state.object, program, state.pclass->org_to_byte_shift,
                              cinit_section, state.section.definition);
    }
  }

  /* FIXME: allocate assigned stacks */

  /* allocate memory for relocatable assigned sections */
  gp_debug("Relocating assigned sections.");
  gp_cofflink_reloc_assigned(state.object, program, state.pclass->org_to_byte_shift,
                             STYP_ROM_AREA,
                             state.section.definition, state.section.logical);

  gp_cofflink_reloc_assigned(state.object, data, 0,
                             STYP_RAM_AREA | STYP_SHARED | STYP_OVERLAY | STYP_ACCESS,
                             state.section.definition, state.section.logical);

  /* FIXME: allocate unassigned stacks */

  /* allocate memory for relocatable unassigned sections */
  gp_debug("Relocating unassigned sections.");
  gp_cofflink_reloc_unassigned(state.object, program, state.pclass->org_to_byte_shift,
                               STYP_ROM_AREA, state.section.definition);

  gp_cofflink_reloc_unassigned(state.object, data, 0,
                               STYP_RAM_AREA | STYP_SHARED | STYP_OVERLAY | STYP_ACCESS,
                               state.section.definition);

  /* load the table with the relocated addresses */
  gp_cofflink_add_cinit_section(state.object);

  gp_cofflink_update_table(state.object, state.pclass->org_to_byte_shift);

  if (state.optimize.pagesel > 0) {
    gp_coffopt_remove_unnecessary_pagesel(state.object);
  }

  if (state.optimize.banksel > 0) {
    gp_coffopt_remove_unnecessary_banksel(state.object);
  }

  gp_coffgen_make_linenum_array(state.object);

  gp_cofflink_fill_pages(state.object, program, state.section.definition);

  gp_mem_i_free(data);
  gp_mem_i_free(program);

  /* patch raw data with the relocated symbol values */
  gp_cofflink_patch(state.object, state.mplink_compatible);

  /* Modify the executable object name. */
  if (state.object->filename) delete[] const_cast<char*>(state.object->filename);

  state.object->filename  = GP_Strdup(state.obj_file_name);
  state.object->flags    |= F_EXEC;

  if (state.obj_file == OUT_NORMAL) {
    /* write the executable object in memory */
    if (!gp_writeobj_write_coff(state.object, gp_num.errors)) {
      gp_error("Error while writing object file.");
      exit(1);
    }
  }
  else {
    unlink(state.object->filename);
  }

  /* convert the executable object into a hex file */
  state.i_memory = gp_cofflink_make_memory(state.object);

  /* write hex file */
  if (!gp_writehex(state.base_file_name, state.i_memory, state.hex_format, gp_num.errors,
                   0, state.pclass->core_mask)) {
    gp_error("Error while writing hex file.");
    exit(1);
  }

  /* convert the executable object into a cod file and list file */
  cod_init();
  lst_write();

  if (!state.cod.no_save_local) {
    _add_local_ram_symbols();
  }

  cod_close_file();

  /* write map file */
  make_map();

  gp_mem_i_free(state.i_memory);
  gp_coffgen_free_object(state.object);

  return !gp_num.errors;
}

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

int main(int argc, char *argv[]) {
  _init();
  _process_args(argc, argv);
  return _linker() ? EXIT_SUCCESS : EXIT_FAILURE;
}
Vorgefundene Kodierung: UTF-80