* fixed GCC 4.4.0 mingw compilation:
[fw/sdcc] / as / xa51 / xa_link.c
index 7c376f156bd35e9e3b64abaadf6e7984e88bce2c..a6823d246765f8272f394fb5266747832df3bc99 100644 (file)
@@ -1,20 +1,27 @@
+/* xa_link.c
+
+This program is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 3, or (at your option) any
+later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
 /* WORK IN PROGRESS: do not watch this if you don't have the legal
    age in your country to watch this.
 */
 
-/* This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
-
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
 /* This is a cheap hack. The xa51 has a couple of ways to scramble
    relocation info into it's opcode that the standard linker can't
-   handle. 
+   handle, not to mention word allignment. 
+
+   No hash or qsort yet.
 
    The relocatable format looks like the known one, BUT ISN'T.
 
 
    "SDCCXA rel, version %f" must be the first line, sort of MAGIC word
    "H %d areas %d global symbols" defines the # of areas and globals
-   "S <symbol> [Ref0000 | DefXXXX]" Def's are supposed to be defined in
-     their own area/segment
+   "S <symbol> [Ref0000 | DefXXXX | AbsXXXX]" Def's are supposed to be
+     defined in their own area/segment
    "A <seg> size %d flags %d" switch to another segment. this can happen
      multiple times and should be equal. flags is ignored for now
-   "T xx xx bb bb ..." where xx xx is the address within the current segment
-     and bb are the bytes
-   "R xx <how> <symbol>" the relocation info. xx is the offset within the
-     previous "T .." line. <how> could be something like REL_FF, REL_FFFF, 
-     ABS_F0FF. symbol is the (previous) defined symbol it refers to
+   "T xxxx <how> <symbol> 0"
+   "R xxxx <how> <symbol> <pc+>" the relocation info. xxxx is the address
+     within relative code space. How is something like REL_FF, REL_FFFF, 
+     ABS_70FF. Symbol is the referenced symbol and pc+ is the program 
+     counter that will be used to calculate the relative address (that is
+     the address of the following instruction).
 
    So, this is not a standalone linker. It will only link files generated
-   by xa_asm, which will only process files generated by the xa51 sdcc
+   by xa_rasm, which will only process files generated by the xa51 sdcc
    port.
 */
 
@@ -49,21 +57,56 @@ enum {
   GSINIT=1,
   CSEG,
   XINIT,
-  //GSFINAL, // do we need this?
+
+  // here goes the final output and should be used by the assembler
+  GSFINAL,
 
   // these are only for storage
   BSEG,
   DSEG,
   XSEG,
   XISEG,
+
+  // that's all
   MAX_SEGMENTS
 };
 
+enum {
+  REL_FF=1,
+  REL_FFFF,
+  BIT_03FF,
+  DIR_07FF,
+  DIR_70FF,
+  DIR_0700FF,
+  ABS_0F,
+  ABS_FF,
+  ABS_FFFF,
+  ABS_PC,
+  MAX_REFS
+};
+
+char *refModes[]={
+  "???",
+  "REL_FF",
+  "REL_FFFF",
+  "BIT_03FF",
+  "DIR_07FF",
+  "DIR_70FF",
+  "DIR_0700FF",
+  "ABS_0F",
+  "ABS_FF",
+  "ABS_FFFF",
+  "ABS_PC"
+};
+
 #define CODESIZE 0x10000
 
-char codeImage[CODESIZE];
-char gsinitImage[CODESIZE];
-char xinitImage[CODESIZE];
+int fatalErrors=0;
+
+unsigned char gsinitImage[CODESIZE];
+unsigned char csegImage[CODESIZE];
+unsigned char xinitImage[CODESIZE];
+unsigned char gsfinalImage[CODESIZE];
 
 struct SEGMENT {
   short id;
@@ -74,12 +117,12 @@ struct SEGMENT {
   int current;
   unsigned char *image;
 } segments[MAX_SEGMENTS]={
-  {0,       "????",    0, 0, 0, 0, NULL},
+  {0,       "???",    0, 0, 0, 0, NULL},
 
   {GSINIT,  "GSINIT",  0, 0, 0, 0, gsinitImage},
-  {CSEG,    "CSEG",    0, 0, 0, 0, codeImage},
+  {CSEG,    "CSEG",    0, 0, 0, 0, csegImage},
   {XINIT,   "XINIT",   0, 0, 0, 0, xinitImage},
-  //{GSFINAL, "GSFINAL", 0, 0, 0, 0, NULL},
+  {GSFINAL, "GSFINAL", 0, 0, 0, 0, gsfinalImage},
 
   {BSEG,    "BSEG",    0, 0, 0, 0, NULL},
   {DSEG,    "DSEG",    0, 0, 0, 0, NULL},
@@ -91,6 +134,7 @@ struct MODULE {
   char *name;
   int offset[MAX_SEGMENTS];
   int size[MAX_SEGMENTS];
+  int isLib;
   struct MODULE *next;
   struct MODULE *last;
 } *modules=NULL;
@@ -99,65 +143,165 @@ struct MODULE {
 struct SYMBOL {
   char *name;
   struct MODULE *module;
+  int lineno;
   struct SEGMENT *segment;
+  char absolute;
   int address;
   struct SYMBOL *next;
   struct SYMBOL *last;
 } *symbols=NULL;
 
-char *libPaths[128];
+struct REFERENCE {
+  char *name;
+  struct MODULE *module;
+  struct SEGMENT *segment;
+  int lineno;
+  unsigned address, pc;
+  short how;
+  short resolved;
+  struct REFERENCE *next;
+  struct REFERENCE *last;
+} *references=NULL;
+
+char *libraryPaths[128];
 int nlibPaths=0;
-char *libFiles[128];
+char *libraryFiles[128];
 int nlibFiles=0;
 
-static char outFileName[PATH_MAX];
+static char outFileName[PATH_MAX]={'\0'};
+static char mapFileName[PATH_MAX]={'\0'};
+FILE *mapOut;
 
 struct SEGMENT *currentSegment;
 struct MODULE *currentModule;
+int currentLine;
 
-struct SEGMENT *findSegment(char *segment) {
-  int i;
-  for (i=1; i<MAX_SEGMENTS; i++) {
-    if (strcmp(segments[i].name, segment)==0) {
-      return &segments[i];
+int howToReference(char *how) {
+  int r;
+  for (r=1; r<MAX_REFS; r++) {
+    if (strcmp(refModes[r], how)==0) {
+      return r;
     }
   }
   return 0;
 }
 
-void addToModules (char *name) {
-  struct MODULE *module;
+struct SEGMENT *findSegmentByName(char *segment) {
   int s;
+  for (s=0; s<MAX_SEGMENTS; s++) {
+    if (strcmp(segments[s].name, segment)==0) {
+      return &segments[s];
+    }
+  }
+  return 0;
+}
+
+struct SYMBOL *findSymbolByName(char *symName) {
+  struct SYMBOL *symbol;
+  for (symbol=symbols; symbol; symbol=symbol->next) {
+    if (strcmp(symbol->name, symName)==0) {
+      return symbol;
+    }
+  }
+  return 0;
+}
+
+struct MODULE *findModuleByName(char *modName) {
+  struct MODULE *module;
+  for (module=modules; module; module=module->next) {
+    if (strcmp(module->name, modName)==0) {
+      return module;
+    }
+  }
+  return NULL;
+}
 
-  //fprintf (stderr, "addToModules: %s\n", name);
+void addToModules (char *name, int isLib) {
+  struct MODULE *module;
+  int s;
 
   module=calloc(1, sizeof(struct MODULE));
   module->name=strdup(name);
   for (s=0; s<MAX_SEGMENTS; s++) {
-    module->offset[s]=segments[s].current;
+    module->offset[s]=(segments[s]._size+1)&0xfffffe;
   }
+  module->isLib=isLib;
   if (!modules) {
     modules=module;
-  } else {
+   } else {
     modules->last->next=module;
   }
   currentModule=modules->last=module;
 }
 
-void addToRefs(char *ref) {
-  //fprintf (stderr, "addToRefs: %s\n", ref);
+void addToRefs(char *ref, int address, char *how, int pc) {
+  struct REFERENCE *reference;
+
+  reference=calloc(1, sizeof(struct REFERENCE));
+  reference->name=strdup(ref);
+  reference->module=currentModule;
+  reference->segment=currentSegment;
+  reference->lineno=currentLine;
+  reference->address=address;
+  reference->how=howToReference(how);
+  if (reference->how==ABS_PC) {
+    reference->resolved=1;
+  }
+  reference->pc=pc;
+  if (!references) {
+    references=reference;
+  } else {
+    references->last->next=reference;
+  }
+  references->last=reference;
+}
+
+void resolve() {
+  struct REFERENCE *reference;
+  for (reference=references; reference; reference=reference->next) {
+    if ((reference->how==ABS_PC) || findSymbolByName(reference->name)) {
+      reference->resolved=1;
+    }
+  }
 }
 
-void addToDefs(char *def, int address) {
+int isUnresolved(char *ref, int resolved) {
+  struct REFERENCE *reference;
+
+  for (reference=references; reference; reference=reference->next) {
+    if (strcmp(reference->name, ref)==0) {
+      // found 
+      if (reference->resolved) {
+       // already resolved
+       return 0;
+      }
+      if (resolved) {
+       reference->resolved=1;
+       return 1;
+      }
+    }
+  }
+  return 0;
+}
+
+void addToDefs(char *def, int address, char absolute) {
   struct SYMBOL *symbol;
-  /* fprintf (stderr, "addToDefs: %s %s 0x%04x + 0x%04x\n", 
-     currentSegment->name, def, 
-     currentModule->offset[currentSegment->id],
-     address); */
+
+  // no duplicates allowed
+  if ((symbol=findSymbolByName(def))) {
+    fprintf (stderr, "*** %s:%d duplicate symbol %s first defined in "
+            "module %s:%d\n",
+            currentModule->name, currentLine, def, 
+            symbol->module->name, symbol->lineno);
+    fatalErrors++;
+  }
+
   symbol=calloc(1, sizeof(struct SYMBOL));
   symbol->name=strdup(def);
   symbol->module=currentModule;
+  symbol->lineno=currentLine;
   symbol->segment=currentSegment;
+  symbol->absolute=absolute;
   symbol->address=currentModule->offset[currentSegment->id]+address;
   if (!symbols) {
     symbols=symbol;
@@ -169,60 +313,33 @@ void addToDefs(char *def, int address) {
 }
 
 void syntaxError (char *err) {
-  fprintf (stderr, "error while parsing '%s'\n", err);
-  exit(1);
+  fprintf (stderr, "*** %s:%d error while parsing '%s'\n", 
+          currentModule->name, currentLine, err);
+  fatalErrors++;
 }
 
-void baseName(char *name, char*base) {
-  int i, first, last;
-
-  // find the last path seperator in name
-  for (first=strlen(name)-1; 
-       (name[first]!='/' && name[first]!='\\') && first;
-       first--);
-  if (name[first]=='/' || name[first]=='\\') {
-    first++;
-  }
-
-  // find the last ext seperator in name
-  for (last=strlen(name)-1; 
-       (name[last]!='.' && last);
-       last--);
-  if (!last) {
-    last=strlen(name);
-  }
-
-  fprintf (stderr, "baseName: %s %d %d\n", name, first, last);
-  // fill the base with the baseName
-  for (i=first; i<last; i++) {
-    base[i-first]=name[i];
-  }
-  base[i]='\0';
-}
-  
-void readModule(char *module) {
+void readModule(char *module, int isLib) {
   double hisVersion;
   char line[132];
   FILE *relModule;
   char moduleName[PATH_MAX];
   int segments, globals;
-  int currentLine=1;
+
+  currentLine=1;
 
   if ((relModule=fopen(module, "r"))==NULL) {
     perror (module);
     exit (1);
   }
 
-  //fprintf (stderr, "ReadModule: %s\n", module);
-
   // first we need to check if this is a valid file
   if (sscanf(fgets(line, 132, relModule), 
             "SDCCXA rel, version %lf", &hisVersion)!=1) {
-    fprintf (stderr, "%s is not a valid input file\n", module);
+    fprintf (stderr, "*** %s is not a valid input file\n", module);
     exit (1);
   }
   if (hisVersion!=version) {
-    fprintf (stderr, "WARNING: version conflict; "
+    fprintf (stderr, "*** WARNING: version conflict; "
             "we(%1.1f) != %s(%1.1f)\n", 
             version, module, hisVersion);
   }
@@ -243,10 +360,8 @@ void readModule(char *module) {
   }
 
   // add this to the known modules with current offsets
-  addToModules(module);
+  addToModules(module, isLib);
 
-  fprintf (stderr, "module %s has %d segment%s and %d globals\n",
-          moduleName, segments, segments==1?"":"s", globals);
   currentLine++;
 
   // now for the ASTR tags
@@ -258,30 +373,29 @@ void readModule(char *module) {
        int size, flags;
        if (sscanf(line, "A %[^ ] size %d flags %d",
                   segment, &size, &flags)!=3) {
-         fprintf (stderr, "%s:%d error in A record line\n", 
-                  module, currentLine);
-         exit (1);
+         syntaxError(line);
        }
        // do we know this segment?
-       if (!(currentSegment=findSegment(segment))) {
-         fprintf (stderr, "%s:%d unknown area: %s\n", module,
+       if (!(currentSegment=findSegmentByName(segment))) {
+         fprintf (stderr, "*** %s:%d unknown area: %s\n", module,
                   currentLine, segment);
          exit (1);
        }
+       // double check repeated 'A' records
        if (currentModule->size[currentSegment->id]) {
+         // pleased to meet you again, I hope ...
          if (currentModule->size[currentSegment->id] != size) {
-           fprintf (stderr, "%s:%d error %s %d %d\n",
+           fprintf (stderr, "*** %s:%d error %s size %d != %d\n",
                     module, currentLine,
                     currentSegment->name,
                     currentModule->size[currentSegment->id], 
                     size);
+           fatalErrors++;
          }
        } else {
-         currentModule->size[currentSegment->id]=size;
-         currentModule->offset[currentSegment->id]+=currentSegment->_size;
          currentSegment->_size += size;
+         currentModule->size[currentSegment->id] = size;
        }
-       //fprintf (stderr, "Area: %s size: %d\n", segment, size);
        // never mind about the flags for now
        break;
       }
@@ -290,15 +404,18 @@ void readModule(char *module) {
        char refdef[132];
        unsigned int address;
        if (sscanf(line, "S %[^ ] %s", symbol, refdef)!=2) {
-         fprintf (stderr, "%s:%d syntax error near \"%s\"\n",
+         fprintf (stderr, "*** %s:%d syntax error near \"%s\"\n",
                   module, currentLine, line);
          exit (1);
        }
        if (strncmp(refdef, "Ref", 3)==0) {
-         addToRefs(symbol);
+         // we don't need them
        } else if (strncmp(refdef, "Def", 3)==0) {
          sscanf (refdef, "Def%04x", &address);
-         addToDefs(symbol, address);
+         addToDefs(symbol, address, 0);
+       } else if (strncmp(refdef, "Abs", 3)==0) {
+         sscanf (refdef, "Abs%04x", &address);
+         addToDefs(symbol, address, 1);
        } else {
          fprintf (stderr, "%s:%d found invalid symbol definition \"%s\"\n", 
                   module, currentLine, line);
@@ -312,7 +429,6 @@ void readModule(char *module) {
        char *tline=NULL;
        if (currentSegment->id!=CSEG && 
            currentSegment->id!=GSINIT &&
-           //currentSegment->id!=GSFINAL &&
            currentSegment->id!=XINIT) {
          fprintf (stderr, "%s:%d cannot emit bytes in %s\n",
                   module, currentLine, currentSegment->name);
@@ -320,62 +436,228 @@ void readModule(char *module) {
        }
        if (sscanf(strtok(&line[2], " "), "%04x", &address)!=1) {
          fprintf (stderr, "%s:%d error in T record\n", module, currentLine);
-         exit (1);
+         fatalErrors++;
        }
-       //fprintf (stderr, "%04x:", address);
-       address+=currentSegment->current;
+
+       address+=currentModule->offset[currentSegment->id];
+       //address+=currentSegment->current;
        for ( ;
              (tline=strtok(NULL, " \t\n")) && 
                (sscanf(tline, "%02x", &byte)==1);
              ) {
-         //fprintf (stderr, " %02x", byte);
          currentSegment->image[address++]=byte;
          currentSegment->current++;
        }
-       //fprintf (stderr, "\n");
        break;
       }
-      case 'R':
-       //fprintf (stderr, "%s", line);
+      case 'R': {
+       unsigned address, pc;
+       char symbol[132];
+       char how[32];
+       sscanf (line, "R %x %[^ ] %[^ ] %x", &address, how, symbol, &pc);
+       addToRefs (symbol, address, how, pc);
        break;
+      }
       default:
-       /* fprintf (stderr, "%s:%d unknown record \"%s\"\n",
-          module, currentLine, line); */
+       fprintf (stderr, "%s:%d unknown record \"%s\"\n",
+                module, currentLine, line);
+       fatalErrors++;
        break;
       }
     currentLine++;
   }
-  // that's all for now, thanks for watching */
   fclose (relModule);
 }
 
-void writeModule() {
-  fprintf (stderr, "WriteModule: %s\n", outFileName);
-  // oops, forgot something :) */
+void writeModule(char *outFileName) {
+  FILE *fOut;
+  unsigned int address=segments[GSFINAL].start;
+  unsigned int size=segments[GSFINAL]._size;
+  unsigned int len;
+  unsigned int checksum;
+
+  if ((fOut=fopen(outFileName, "w"))==NULL) {
+    perror (outFileName);
+  }
+
+  while (size) {
+    len = size>16 ? 16 : size;
+    size-=len;
+    fprintf (fOut, ":%02X%04X%02X", len, address, 0);
+    checksum = len + (address>>8) + (address&0xff);
+    while (len--) {
+      checksum += gsfinalImage[address];
+      fprintf (fOut, "%02X", gsfinalImage[address++]);
+    }
+    checksum &= 0xff;
+    if (checksum) {
+      checksum = 0x100 - checksum;
+    }
+    fprintf (fOut, "%02X\n", checksum);
+  }
+  fprintf (fOut, ":00000001FF\n");
+
+  fclose (fOut);
 }
 
-void relocate() {
+int relocate() {
   struct SYMBOL *symbol;
-  int length=segments[GSINIT].current +
-    segments[CSEG].current +
-    segments[XINIT].current;
+  struct REFERENCE *reference;
+  char *from, *to;
+  int length=segments[GSINIT]._size +
+    segments[CSEG]._size +
+    segments[XINIT]._size;
+  int unresolved=0;
 
   // first check if it will fit
   if (length > 0xffff) {
     fprintf (stderr, "error: code segment exceeds 0xffff\n");
-    exit(1);
+    fatalErrors++;
   }
-  fprintf (stderr, "relocate: total code size: 0x%04x\n", length);
 
-  // GSINIT gets the --code-loc
-  segments[GSINIT].start=segments[CSEG].start;
-  segments[CSEG].start=segments[GSINIT].start+segments[GSINIT]._size;
-  segments[XINIT].start=segments[CSEG].start+segments[CSEG]._size;
-  segments[XISEG].start=segments[XSEG].start+segments[XINIT]._size;  
+  // resolve reverences
+  for (reference=references; reference; reference=reference->next) {
+    if (!reference->resolved && !findSymbolByName(reference->name)) {
+      unresolved++;
+    }
+  }
+  if (unresolved) {
+    // first scan the libraries
+    return unresolved;
+  }
+
+  // GSFINAL starts at --code-loc ( -b CSEG = 0x1234 )
+  if (segments[CSEG].start & 1) {
+    fprintf (stderr, "*** error: code doesn't start at "
+            "an even address: %04x\n", segments[CSEG].start);
+    exit (1);
+  }
+  segments[GSFINAL].start=segments[CSEG].start;
+  memset(gsfinalImage, 0xff, CODESIZE);
+
+  // copy gsinit to gsfinal
+  from = gsinitImage;
+  to = gsfinalImage + segments[GSFINAL].start + segments[GSFINAL]._size;
+  memcpy(to, from, segments[GSINIT]._size);
+  segments[GSINIT].start=segments[GSFINAL].start;
+  segments[GSFINAL]._size += segments[GSINIT]._size;
+  if (segments[GSFINAL]._size & 1) {
+    segments[GSFINAL]._size++;
+  }
+    
+  // append cseg to gsfinal
+  from=csegImage;
+  to = gsfinalImage + segments[GSFINAL].start + segments[GSFINAL]._size;
+  memcpy(to, from, segments[CSEG]._size);
+  segments[CSEG].start=segments[GSFINAL].start+segments[GSFINAL]._size;
+  segments[GSFINAL]._size += segments[CSEG]._size;
+  if (segments[GSFINAL]._size & 1) {
+    segments[GSFINAL]._size++;
+  }
+
+  // append xinit to gsfinal
+  from=xinitImage;
+  to = gsfinalImage + segments[GSFINAL].start + segments[GSFINAL]._size;
+  memcpy(to, from, segments[XINIT]._size);
+  segments[XINIT].start=segments[GSFINAL].start+segments[GSFINAL]._size;
+  segments[GSFINAL]._size += segments[XINIT]._size;
+  if (segments[GSFINAL]._size & 1) {
+    segments[GSFINAL]._size++;
+  }
+
+  // XISEG is located after XSEG
+  if (segments[XSEG].start & 1) {
+    fprintf (stderr, "*** warning: xdata doesn't start at "
+            "an even address: %04x\n", segments[XSEG].start);
+  }
+  if (segments[XSEG]._size & 1) {
+    segments[XSEG]._size++;
+  }
+  
+  segments[XISEG].start=segments[XSEG].start + 
+    segments[XSEG]._size;  
+
   // now relocate the defined symbols
   for (symbol=symbols; symbol; symbol=symbol->next) {
-    symbol->address += symbol->segment->start;
+    if (!symbol->absolute) {
+      symbol->address += symbol->segment->start;
+    }
+  }
+  // and the references
+  for (reference=references; reference; reference=reference->next) {
+    symbol=findSymbolByName(reference->name);
+    if (!reference->resolved && !symbol && reference->how!=ABS_PC) {
+      // this reference isn't resolved after all
+      fprintf (stderr, "*** %s:%d undefined symbol %s\n",
+              reference->module->name, reference->lineno,
+              reference->name);
+      fatalErrors++;
+    } else {
+      reference->address += 
+       reference->module->offset[reference->segment->id]+
+       reference->segment->start;
+      reference->pc += 
+       reference->module->offset[reference->segment->id]+
+       reference->segment->start;
+      switch (reference->how) 
+       {
+       case REL_FF: {
+         int rel8 = symbol->address-(reference->pc & ~1);
+         if (rel8<-256 || rel8>256) {
+           fprintf (stderr,
+                    "rel8 target for %s is out of range in module %s:%d\n",
+                    reference->name, reference->module->name, 
+                    reference->lineno);
+           fatalErrors++;
+         }
+         gsfinalImage[reference->address]=rel8/2;
+         break;
+       }
+       case REL_FFFF: {
+         int rel16 = symbol->address-(reference->pc & ~1);
+         if (rel16<-65536 || rel16>65534) {
+           fprintf (stderr, 
+                    "rel16 target for %s is out of range in module %s:%d\n",
+                    reference->name, reference->module->name,
+                    reference->lineno);
+           fatalErrors++;
+         }
+         gsfinalImage[reference->address]=(rel16/2)>>8;
+         gsfinalImage[reference->address+1]=rel16/2;
+         break;
+       }
+       case DIR_70FF:
+         gsfinalImage[reference->address] = 
+           (gsfinalImage[reference->address]&~0x70) + 
+           ((symbol->address>>4)&0x70);
+         gsfinalImage[reference->address+1] = symbol->address;
+         break;
+       case ABS_FFFF:
+         gsfinalImage[reference->address] = symbol->address>>8;
+         gsfinalImage[reference->address+1] = symbol->address;
+         break;
+       case ABS_FF:
+         gsfinalImage[reference->address] = symbol->address;
+         break;
+       case ABS_PC: 
+         {
+           unsigned int address=
+             (gsfinalImage[reference->address]<<8) +
+             gsfinalImage[reference->address+1];
+           address += reference->module->offset[reference->segment->id];
+           address += segments[reference->segment->id].start;
+           gsfinalImage[reference->address] = address>>8;
+           gsfinalImage[reference->address+1] = address;
+         };
+         break;
+       default:
+         fprintf (stderr, "unsupported reference mode %d.\n",
+                  reference->how);
+         fatalErrors++;
+       }
+    }
   }
+  return 0;
 }
 
 void usage (char * progName, int errNo) {
@@ -385,6 +667,48 @@ void usage (char * progName, int errNo) {
   }
 }
 
+int scanLibraries(int unresolved) {
+  int resolved=0;
+  int nlp, nlf;
+  char libFiles[PATH_MAX];
+  char libFile[PATH_MAX];
+  char line[132];
+  char symName[132];
+  FILE *lf, *lfs;
+  
+  for (nlp=0; nlp<nlibPaths; nlp++) {
+    for (nlf=0; nlf<nlibFiles; nlf++) {
+      sprintf (libFiles, "%s/%s.lib", libraryPaths[nlp], libraryFiles[nlf]);
+      if ((lfs=fopen(libFiles,"r"))==NULL) {
+       continue;
+      }
+      while (fgets(line, 132, lfs)) {
+       // remove trailing \n
+       line[strlen(line)-1]='\0';
+       sprintf (libFile, "%s/%s", libraryPaths[nlp], line);
+       if ((lf=fopen(libFile,"r"))==NULL) {
+         continue;
+       }
+       while (fgets(line, 132, lf)) {
+         int dummy; // we need this to get the right count of the next sscanf
+         if (sscanf(line, "S %[^ ] Def%04x", symName, &dummy)==2) {
+           if (isUnresolved(symName, 1)) {
+             readModule(libFile,1);
+             if (resolved++ == unresolved) {
+               // we are done
+               return resolved;
+             }
+             // skip to next lib module
+             break;
+           }
+         }
+       }
+      }
+    }
+  }
+  return resolved;
+}
+
 int main(int argc, char **argv) {
   FILE *linkCommandsFile;
   char linkCommandsPath[PATH_MAX];
@@ -392,13 +716,12 @@ int main(int argc, char **argv) {
   struct MODULE *module;
   struct SYMBOL *symbol;
   int s;
+  int unresolved;
 
   if (argc!=2) {
     usage(argv[0], 1);
   }
 
-  memset(codeImage, 0xff, CODESIZE);
-
   // read in the commands
   sprintf (linkCommandsPath, "%s.lnk", argv[1]);
   if (!(linkCommandsFile=fopen(linkCommandsPath, "r"))) {
@@ -435,8 +758,6 @@ int main(int argc, char **argv) {
                           &segments[s].start)!=1) {
                  syntaxError(linkCommand);
                }
-               /* fprintf (stderr, "%s starts at 0x%04x\n", segments[s].name,
-                  segments[s].start); */
                break;
              }
            }
@@ -447,52 +768,97 @@ int main(int argc, char **argv) {
          break;
        case 'k':
          // a lib path like: "-k /usr/local/share/sdcc/lib/xa51"; one/line
-         libPaths[nlibPaths++]=strdup(&linkCommand[3]);
+         libraryPaths[nlibPaths++]=strdup(&linkCommand[3]);
          break;
        case 'l':
          // a lib file like: "-l libsdcc"; one/line
-         libFiles[nlibFiles++]=strdup(&linkCommand[3]);
+         libraryFiles[nlibFiles++]=strdup(&linkCommand[3]);
          break;
        default:
          syntaxError(linkCommand);
        }
     } else {
       // not a switch, must be an inputfile; one/line
-      readModule(linkCommand);
+      readModule(linkCommand, 0);
+      // the first one defines the output name
+      if (!outFileName[0]) {
+       strncpy(outFileName, linkCommand,
+               strlen(linkCommand)-4);
+       sprintf(mapFileName, "%s.map", outFileName);
+       strcat(outFileName, ".hex");
+       if ((mapOut=fopen(mapFileName, "w"))==NULL) {
+         perror(mapFileName);
+       }
+      }
     }
   }
 
-  relocate();
+  // add the segment symbols
+  currentSegment=findSegmentByName("XINIT");
+  addToDefs("s_XINIT", 0, 0);
+  addToDefs("l_XINIT", segments[XINIT]._size, 1);
+  currentSegment=findSegmentByName("XISEG");
+  addToDefs("s_XISEG", 0, 0);
+  addToDefs("l_XISEG", segments[XISEG]._size, 1);
+
+  // mark the resolved references
+  resolve();
+
+  // now do something EXTREMELY SLOW AND INEFFICIENT :)
+  while ((unresolved=relocate())) {
+    if (!scanLibraries(unresolved)) {
+      struct REFERENCE *reference;
+      resolve();
+      for (reference=references; reference; reference=reference->next) {
+       if (!reference->resolved) {
+         fprintf (stderr, "*** unresolved symbol %s in %s:%d\n",
+                  reference->name, reference->module->name,
+                  reference->lineno);
+         fatalErrors++;
+       }
+      }
+      break;
+    }
+  }
+
+  if (unresolved==0) {
+    writeModule(outFileName);
+  }
 
   // the modules
+  fprintf (mapOut, "Modules:\n");
   for (module=modules; module; module=module->next) {
-    fprintf (stderr, "%s: ", module->name);
+    fprintf (mapOut, "\t%s\n", module->name);
     for (s=0; s<MAX_SEGMENTS; s++) {
       if (module->size[s]) {
-       fprintf (stderr, "%s:0x%04x-0x%04x ", segments[s].name,
-                module->offset[s], module->offset[s]+module->size[s]);
+       fprintf (mapOut, "\t\t%s:0x%04x-0x%04x\n", segments[s].name,
+                module->offset[s]+segments[s].start,
+                module->offset[s]+segments[s].start+module->size[s]);
       }
     }
-    fprintf (stderr, "\n");
-  }
-
-  // the symbols
-  for (symbol=symbols; symbol; symbol=symbol->next) {
-    fprintf (stderr, "%s %s 0x%04x %s\n", symbol->name, symbol->segment->name,
-            symbol->address, symbol->module->name);
   }
 
   // the segments
+  fprintf (mapOut, "\nSegments:\n");
   for (s=1; s<MAX_SEGMENTS; s++) {
     if (segments[s]._size) {
-      fprintf (stderr, "%s start 0x%04x size 0x%04x %d symbols\n",
+      fprintf (mapOut, "\t%s start 0x%04x size 0x%04x %d symbols\n",
               segments[s].name, segments[s].start,
               segments[s]._size, 
               segments[s].hasSymbols);
     }
   }
 
-  writeModule();
-  return 0;
+  // the symbols
+  fprintf (mapOut, "\nSymbols:\n");
+  for (symbol=symbols; symbol; symbol=symbol->next) {
+    fprintf (mapOut, "%s\t%s %s0x%04x %s\n", symbol->name, 
+            symbol->segment->name,
+            symbol->absolute ? "= " : "",
+            symbol->address, symbol->module->name);
+  }
+
+  fclose(mapOut);
+  return fatalErrors? 1 : 0;
 }