xa51, work in progress
[fw/sdcc] / as / xa51 / xa_link.c
1 /* WORK IN PROGRESS: do not watch this if you don't have the legal
2    age in your country to watch this.
3 */
4
5 /* This program is distributed in the hope that it will be useful,
6  * but WITHOUT ANY WARRANTY; without even the implied warranty of
7  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
8  * GNU General Public License for more details.
9
10  * You should have received a copy of the GNU General Public License
11  * along with this program; if not, write to the Free Software
12  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
13  */
14
15 /* This is a cheap hack. The xa51 has a couple of ways to scramble
16    relocation info into it's opcode that the standard linker can't
17    handle. No hash or qsort yet.
18
19    The relocatable format looks like the known one, BUT ISN'T.
20
21    The only things that are handled now are:
22
23    "SDCCXA rel, version %f" must be the first line, sort of MAGIC word
24    "H %d areas %d global symbols" defines the # of areas and globals
25    "S <symbol> [Ref0000 | DefXXXX | AbsXXXX]" Def's are supposed to be
26      defined in their own area/segment
27    "A <seg> size %d flags %d" switch to another segment. this can happen
28      multiple times and should be equal. flags is ignored for now
29    "T xxxx <how> <symbol> 0"
30    "R xxxx <how> <symbol> <pc+>" the relocation info. xxxx is the address
31      within relative code space. How is something like REL_FF, REL_FFFF, 
32      ABS_F0FF. Symbol is the referenced symbol and pc+ is the program 
33      counter that will be used to calculate the relative address (that is
34      the address of the following instruction).
35
36    So, this is not a standalone linker. It will only link files generated
37    by xa_rasm, which will only process files generated by the xa51 sdcc
38    port.
39 */
40
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <limits.h>
44 #include <string.h>
45
46 #include "xa_version.h"
47
48 enum {
49   // these are all concatenated into the code image
50   GSINIT=1,
51   CSEG,
52   XINIT,
53   //GSFINAL, // do we need this?
54
55   // these are only for storage
56   BSEG,
57   DSEG,
58   XSEG,
59   XISEG,
60   MAX_SEGMENTS
61 };
62
63 enum {
64   REL_FF=1,
65   REL_FFFF,
66   ABS_0F,
67   ABS_FF,
68   ABS_03FF,
69   ABS_07FF,
70   ABS_F0FF,
71   ABS_FFFF,
72   ABS_0F00FF,
73   MAX_REFS
74 };
75
76 char *refModes[]={
77   "???",
78   "REL_FF",
79   "REL_FFFF",
80   "ABS_0F",
81   "ABS_FF",
82   "ABS_03FF",
83   "ABS_07FF",
84   "ABS_F0FF",
85   "ABS_FFFF",
86   "ABS_0F00FF",
87 };
88
89 #define CODESIZE 0x10000
90
91 int fatalErrors=0;
92
93 char gsinitImage[CODESIZE];
94 char csegImage[CODESIZE];
95 char xinitImage[CODESIZE];
96 //char gsfinalImage[CODESIZE];
97
98 struct SEGMENT {
99   short id;
100   char *name;
101   int hasSymbols;
102   int _size;
103   int start;
104   int current;
105   unsigned char *image;
106 } segments[MAX_SEGMENTS]={
107   {0,       "???",    0, 0, 0, 0, NULL},
108
109   {GSINIT,  "GSINIT",  0, 0, 0, 0, gsinitImage},
110   {CSEG,    "CSEG",    0, 0, 0, 0, csegImage},
111   {XINIT,   "XINIT",   0, 0, 0, 0, xinitImage},
112   //{GSFINAL, "GSFINAL", 0, 0, 0, 0, NULL},
113
114   {BSEG,    "BSEG",    0, 0, 0, 0, NULL},
115   {DSEG,    "DSEG",    0, 0, 0, 0, NULL},
116   {XSEG,    "XSEG",    0, 0, 0, 0, NULL},
117   {XISEG,   "XISEG",   0, 0, 0, 0, NULL},
118 };
119
120 struct MODULE {
121   char *name;
122   int offset[MAX_SEGMENTS];
123   int size[MAX_SEGMENTS];
124   int isLib;
125   struct MODULE *next;
126   struct MODULE *last;
127 } *modules=NULL;
128
129
130 struct SYMBOL {
131   char *name;
132   struct MODULE *module;
133   int lineno;
134   struct SEGMENT *segment;
135   char absolute;
136   int address;
137   struct SYMBOL *next;
138   struct SYMBOL *last;
139 } *symbols=NULL;
140
141 struct REFERENCE {
142   char *name;
143   struct MODULE *module;
144   int lineno;
145   unsigned address, pc;
146   short how;
147   short resolved;
148   struct REFERENCE *next;
149   struct REFERENCE *last;
150 } *references=NULL;
151
152 char *libraryPaths[128];
153 int nlibPaths=0;
154 char *libraryFiles[128];
155 int nlibFiles=0;
156
157 static char outFileName[PATH_MAX]={'\0'};
158 static char mapFileName[PATH_MAX]={'\0'};
159 FILE *mapOut;
160
161 struct SEGMENT *currentSegment;
162 struct MODULE *currentModule;
163 int currentLine;
164
165 int howToReference(char *how) {
166   int r;
167   for (r=1; r<MAX_REFS; r++) {
168     if (strcmp(refModes[r], how)==0) {
169       return r;
170     }
171   }
172   return 0;
173 }
174
175 struct SEGMENT *findSegmentByName(char *segment) {
176   int s;
177   for (s=0; s<MAX_SEGMENTS; s++) {
178     if (strcmp(segments[s].name, segment)==0) {
179       return &segments[s];
180     }
181   }
182   return 0;
183 }
184
185 struct SYMBOL *findSymbolByName(char *symName) {
186   struct SYMBOL *symbol;
187   for (symbol=symbols; symbol; symbol=symbol->next) {
188     if (strcmp(symbol->name, symName)==0) {
189       return symbol;
190     }
191   }
192   return 0;
193 }
194
195 void addToModules (char *name, int isLib) {
196   struct MODULE *module;
197   int s;
198
199   module=calloc(1, sizeof(struct MODULE));
200   module->name=strdup(name);
201   for (s=0; s<MAX_SEGMENTS; s++) {
202     module->offset[s]=segments[s].current;
203   }
204   module->isLib=isLib;
205   if (!modules) {
206     modules=module;
207    } else {
208     modules->last->next=module;
209   }
210   currentModule=modules->last=module;
211 }
212
213 void addToRefs(char *ref, int address, char *how, int pc) {
214   struct REFERENCE *reference;
215
216   reference=calloc(1, sizeof(struct REFERENCE));
217   reference->name=strdup(ref);
218   reference->module=currentModule;
219   reference->lineno=currentLine;
220   reference->address=address;
221   reference->how=howToReference(how);
222   reference->pc=pc;
223   if (!references) {
224     references=reference;
225   } else {
226     references->last->next=reference;
227   }
228   references->last=reference;
229 }
230
231 void resolve() {
232   struct REFERENCE *reference;
233   for (reference=references; reference; reference=reference->next) {
234     if (findSymbolByName(reference->name)) {
235       reference->resolved=1;
236     }
237   }
238 }
239
240 int isUnresolved(char *ref, int resolved) {
241   struct REFERENCE *reference;
242
243   for (reference=references; reference; reference=reference->next) {
244     if (strcmp(reference->name, ref)==0) {
245       // found 
246       if (reference->resolved) {
247         // already resolved
248         return 0;
249       }
250       if (resolved) {
251         reference->resolved=1;
252         return 1;
253       }
254     }
255   }
256   return 0;
257 }
258
259 void addToDefs(char *def, int address, char absolute) {
260   struct SYMBOL *symbol;
261
262   // no duplicates allowed
263   if ((symbol=findSymbolByName(def))) {
264     fprintf (stderr, "*** %s:%d duplicate symbol %s first defined in "
265              "module %s:%d\n", 
266              currentModule->name, currentLine, def, 
267              symbol->module->name, symbol->lineno);
268     fatalErrors++;
269   }
270
271   symbol=calloc(1, sizeof(struct SYMBOL));
272   symbol->name=strdup(def);
273   symbol->module=currentModule;
274   symbol->lineno=currentLine;
275   symbol->segment=currentSegment;
276   symbol->absolute=absolute;
277   symbol->address=currentModule->offset[currentSegment->id]+address;
278   if (!symbols) {
279     symbols=symbol;
280   } else {
281     symbols->last->next=symbol;
282   }
283   symbols->last=symbol;
284   currentSegment->hasSymbols++;
285 }
286
287 void syntaxError (char *err) {
288   fprintf (stderr, "*** %s:%d error while parsing '%s'\n", 
289            currentModule->name, currentLine, err);
290   fatalErrors++;
291 }
292
293 void readModule(char *module, int isLib) {
294   double hisVersion;
295   char line[132];
296   FILE *relModule;
297   char moduleName[PATH_MAX];
298   int segments, globals;
299
300   currentLine=1;
301
302   if ((relModule=fopen(module, "r"))==NULL) {
303     perror (module);
304     exit (1);
305   }
306
307   // first we need to check if this is a valid file
308   if (sscanf(fgets(line, 132, relModule), 
309              "SDCCXA rel, version %lf", &hisVersion)!=1) {
310     fprintf (stderr, "*** %s is not a valid input file\n", module);
311     exit (1);
312   }
313   if (hisVersion!=version) {
314     fprintf (stderr, "*** WARNING: version conflict; "
315              "we(%1.1f) != %s(%1.1f)\n", 
316              version, module, hisVersion);
317   }
318   currentLine++;
319   
320   // H 7 areas 168 global symbols
321   if (sscanf(fgets(line, 132, relModule),
322              "H %d areas %d global symbols",
323              &segments, &globals)!=2) {
324     syntaxError(line);
325   }
326   currentLine++;
327
328   // M module
329   if (sscanf(fgets(line, 132, relModule),
330              "M %s", moduleName)!=1) {
331     syntaxError(line);
332   }
333
334   // add this to the known modules with current offsets
335   addToModules(module, isLib);
336
337   currentLine++;
338
339   // now for the ASTR tags
340   while (fgets(line, 132, relModule)) {
341     switch (line[0]) 
342       {
343       case 'A': {
344         char segment[32];
345         int size, flags;
346         if (sscanf(line, "A %[^ ] size %d flags %d",
347                    segment, &size, &flags)!=3) {
348           syntaxError(line);
349         }
350         // do we know this segment?
351         if (!(currentSegment=findSegmentByName(segment))) {
352           fprintf (stderr, "*** %s:%d unknown area: %s\n", module,
353                    currentLine, segment);
354           exit (1);
355         }
356         // double check repeated 'A' records
357         if (currentModule->size[currentSegment->id]) {
358           if (currentModule->size[currentSegment->id] != size) {
359             fprintf (stderr, "*** %s:%d error %s size %d != %d\n",
360                      module, currentLine,
361                      currentSegment->name,
362                      currentModule->size[currentSegment->id], 
363                      size);
364             fatalErrors++;
365           } else {
366             // pleased to meet you again
367           }
368         } else {
369           currentModule->size[currentSegment->id]=size;
370           currentModule->offset[currentSegment->id]+=currentSegment->_size;
371           currentSegment->_size += size;
372         }
373         // never mind about the flags for now
374         break;
375       }
376       case 'S': {
377         char symbol[132];
378         char refdef[132];
379         unsigned int address;
380         if (sscanf(line, "S %[^ ] %s", symbol, refdef)!=2) {
381           fprintf (stderr, "*** %s:%d syntax error near \"%s\"\n",
382                    module, currentLine, line);
383           exit (1);
384         }
385         if (strncmp(refdef, "Ref", 3)==0) {
386           // we don't need them
387         } else if (strncmp(refdef, "Def", 3)==0) {
388           sscanf (refdef, "Def%04x", &address);
389           addToDefs(symbol, address, 0);
390         } else if (strncmp(refdef, "Abs", 3)==0) {
391           sscanf (refdef, "Abs%04x", &address);
392           addToDefs(symbol, address, 1);
393         } else {
394           fprintf (stderr, "%s:%d found invalid symbol definition \"%s\"\n", 
395                    module, currentLine, line);
396           exit (1);
397         }
398         break;
399       }
400       case 'T': {
401         unsigned int address;
402         unsigned int byte;
403         char *tline=NULL;
404         if (currentSegment->id!=CSEG && 
405             currentSegment->id!=GSINIT &&
406             //currentSegment->id!=GSFINAL &&
407             currentSegment->id!=XINIT) {
408           fprintf (stderr, "%s:%d cannot emit bytes in %s\n",
409                    module, currentLine, currentSegment->name);
410           exit (1);
411         }
412         if (sscanf(strtok(&line[2], " "), "%04x", &address)!=1) {
413           fprintf (stderr, "%s:%d error in T record\n", module, currentLine);
414           fatalErrors++;
415         }
416         address+=currentSegment->current;
417         for ( ;
418               (tline=strtok(NULL, " \t\n")) && 
419                 (sscanf(tline, "%02x", &byte)==1);
420               ) {
421           currentSegment->image[address++]=byte;
422           currentSegment->current++;
423         }
424         break;
425       }
426       case 'R': {
427         unsigned address, from;
428         char symbol[132];
429         char how[32];
430         sscanf (line, "R %x %[^ ] %[^ ] %x", &address, how, symbol, &from);
431         addToRefs (symbol, address, how, from);
432         break;
433       }
434       default:
435         fprintf (stderr, "%s:%d unknown record \"%s\"\n",
436                  module, currentLine, line);
437         fatalErrors++;
438         break;
439       }
440     currentLine++;
441   }
442   // that's all for now, thanks for watching */
443   fclose (relModule);
444 }
445
446 void writeModule(char *outFileName) {
447   FILE *fOut;
448
449   if ((fOut=fopen(outFileName, "w"))==NULL) {
450     perror (outFileName);
451   }
452   // oops, forgot something :) */
453   fclose (fOut);
454 }
455
456 int relocate() {
457   struct SYMBOL *symbol;
458   struct REFERENCE *reference;
459   char *from, *to;
460   int length=segments[GSINIT].current +
461     segments[CSEG].current +
462     segments[XINIT].current;
463   int unresolved=0;
464
465   // first check if it will fit
466   if (length > 0xffff) {
467     fprintf (stderr, "error: code segment exceeds 0xffff\n");
468     fatalErrors++;
469   }
470
471   // resolve reverences
472   for (reference=references; reference; reference=reference->next) {
473     if (!reference->resolved && !findSymbolByName(reference->name)) {
474       unresolved++;
475     }
476   }
477   if (unresolved) {
478     // first scan the libraries
479     return unresolved;
480   }
481
482   // GSINIT gets the --code-loc
483   segments[GSINIT].start=segments[CSEG].start;
484   segments[CSEG].start=segments[GSINIT].start+segments[GSINIT]._size;
485   // concat cseg and gsinit
486   from=csegImage;
487   to=&gsinitImage[segments[GSINIT].start+segments[GSINIT]._size];
488   memcpy(to, from, segments[CSEG]._size);
489   segments[XINIT].start=segments[CSEG].start+segments[CSEG]._size;
490   from=xinitImage;
491   to+=segments[CSEG]._size;
492   memcpy(to, from, segments[XINIT]._size);
493 #if 0
494   from=gsfinalImage;
495   to+=segments[XINIT]._size;
496   memcpy(to, from, segments[GSFINAL]._size);
497 #endif
498   segments[XISEG].start=segments[XSEG].start+segments[XINIT]._size;  
499   // now relocate the defined symbols
500   for (symbol=symbols; symbol; symbol=symbol->next) {
501     if (!symbol->absolute) {
502       symbol->address += symbol->segment->start;
503     }
504   }
505   // and the references
506   for (reference=references; reference; reference=reference->next) {
507     if (!(symbol=findSymbolByName(reference->name))) {
508       // this reference isn't defined after all
509       fprintf (stderr, "*** %s:%d undefined symbol %s\n",
510                reference->module->name, reference->lineno,
511                reference->name);
512       fatalErrors++;
513     } else {
514       reference->address += symbol->segment->start;
515       switch (reference->how) 
516         {
517         case REL_FFFF: {
518           int rel16 = symbol->address-reference->pc;
519           if (rel16<-65536 || rel16>65534) {
520             fprintf (stderr, 
521                      "rel16 target for %s is out of range in module %s\n",
522                      reference->name, reference->module->name);
523             fatalErrors++;
524           }
525           gsinitImage[reference->address+1]=(rel16/2)>>8;
526           gsinitImage[reference->address]=rel16/2;
527           break;
528         }
529         case REL_FF: {
530           int rel8 = symbol->address-reference->pc;
531           if (rel8<-256 || rel8>256) {
532             fprintf (stderr,
533                      "rel8 target for %s is out of range in module %s\n",
534                      reference->name, reference->module->name);
535             fatalErrors++;
536           }
537           gsinitImage[reference->address]=rel8/2;
538           break;
539         }
540         case ABS_FFFF:
541           gsinitImage[reference->address+1] = symbol->address>>8;
542           // fall through
543         case ABS_FF:
544           gsinitImage[reference->address] = symbol->address;
545           break;
546         default:
547           fprintf (stderr, "unsupported reference mode %d.\n",
548                    reference->how);
549           fatalErrors++;
550         }
551     }
552   }
553   return 0;
554 }
555
556 void usage (char * progName, int errNo) {
557   fprintf (stderr, "usage: %s lnkCmdFile\n", progName);
558   if (errNo) {
559     exit (errNo);
560   }
561 }
562
563 int scanLibraries(int unresolved) {
564   int resolved=0;
565   int nlp, nlf;
566   char libFiles[PATH_MAX];
567   char libFile[PATH_MAX];
568   char line[132];
569   char symName[132];
570   FILE *lf, *lfs;
571   
572   for (nlp=0; nlp<nlibPaths; nlp++) {
573     for (nlf=0; nlf<nlibFiles; nlf++) {
574       sprintf (libFiles, "%s/%s.lib", libraryPaths[nlp], libraryFiles[nlf]);
575       //fprintf (stderr, "  %s\n", libFiles);
576       if ((lfs=fopen(libFiles,"r"))==NULL) {
577         continue;
578       }
579       while (fgets(line, 132, lfs)) {
580         // remove trailing \n
581         line[strlen(line)-1]='\0';
582         sprintf (libFile, "%s/%s", libraryPaths[nlp], line);
583         //fprintf (stderr, "    %s\n", libFile);
584         if ((lf=fopen(libFile,"r"))==0) {
585           continue;
586         }
587         while (fgets(line, 132, lf)) {
588           if (sscanf(line, "S %[^ ] Def", symName)==1) {
589             if (isUnresolved(symName, 1)) {
590               fprintf (stderr, "%s:%s\n", libFile, symName);
591               readModule(libFile,1);
592               if (resolved++ == unresolved) {
593                 // we are done
594                 return resolved;
595               }
596             }
597           }
598         }
599       }
600     }
601   }
602   return resolved;
603 }
604
605 int main(int argc, char **argv) {
606   FILE *linkCommandsFile;
607   char linkCommandsPath[PATH_MAX];
608   char linkCommand[PATH_MAX];
609   struct MODULE *module;
610   struct SYMBOL *symbol;
611   int s;
612   int unresolved;
613
614   if (argc!=2) {
615     usage(argv[0], 1);
616   }
617
618   // read in the commands
619   sprintf (linkCommandsPath, "%s.lnk", argv[1]);
620   if (!(linkCommandsFile=fopen(linkCommandsPath, "r"))) {
621     perror(linkCommandsPath);
622     exit(1);
623   }
624   while (fgets(linkCommand, PATH_MAX, linkCommandsFile)) {
625     linkCommand[strlen(linkCommand)-1]='\0';
626
627     // skip empty lines
628     if (!*linkCommand) {
629       continue;
630     }
631
632     //puts (linkCommand);
633     if (*linkCommand=='-') {
634       switch (linkCommand[1]) 
635         {
636         case 'm':
637           // probably -muxi, ignore for now
638           break;
639         case 'e':
640           // -e, always in the last line, ignore for now
641           break;
642         case 'b': 
643           {
644             // a segment start address like: "-b XSEG = 0x4000"
645             int s;
646             char *seg=strtok(&linkCommand[3], " \t");
647             for (s=0; s<MAX_SEGMENTS; s++) {
648               if (strcmp(segments[s].name, seg)==0) {
649                 strtok(NULL, " \t"); // skip the '='
650                 if (sscanf(strtok(NULL, " \t"), "%x", 
651                            &segments[s].start)!=1) {
652                   syntaxError(linkCommand);
653                 }
654                 break;
655               }
656             }
657             if (s==MAX_SEGMENTS) {
658               syntaxError(linkCommand);
659             }
660           }
661           break;
662         case 'k':
663           // a lib path like: "-k /usr/local/share/sdcc/lib/xa51"; one/line
664           libraryPaths[nlibPaths++]=strdup(&linkCommand[3]);
665           break;
666         case 'l':
667           // a lib file like: "-l libsdcc"; one/line
668           libraryFiles[nlibFiles++]=strdup(&linkCommand[3]);
669           break;
670         default:
671           syntaxError(linkCommand);
672         }
673     } else {
674       // not a switch, must be an inputfile; one/line
675       readModule(linkCommand, 0);
676       // the first one defines the output name
677       if (!outFileName[0]) {
678         strncpy(outFileName, linkCommand,
679                 strlen(linkCommand)-4);
680         sprintf(mapFileName, "%s.map", outFileName);
681         strcat(outFileName, ".hex");
682         if ((mapOut=fopen(mapFileName, "w"))==NULL) {
683           perror(mapFileName);
684         }
685       }
686     }
687   }
688
689   // add the segment symbols
690   currentSegment=findSegmentByName("XINIT");
691   addToDefs("s_XINIT", segments[XINIT].start, 0);
692   addToDefs("l_XINIT", segments[XINIT]._size, 0);
693   currentSegment=findSegmentByName("XISEG");
694   addToDefs("s_XISEG", segments[XISEG].start, 0);
695   addToDefs("l_XISEG", segments[XISEG]._size, 0);
696
697   // mark the resolved references
698   resolve();
699
700   // now do something :) EXTREMELY SLOW AND INEFFICIENT
701   while ((unresolved=relocate())) {
702     if (!scanLibraries(unresolved)) {
703       struct REFERENCE *reference;
704       for (reference=references; reference; reference=reference->next) {
705         if (!reference->resolved) {
706           fprintf (stderr, "*** unresolved symbol %s in %s:%d\n",
707                    reference->name, reference->module->name,
708                    reference->lineno);
709           fatalErrors++;
710         }
711       }
712       break;
713     }
714   }
715
716   if (unresolved==0) {
717     writeModule(outFileName);
718   }
719
720   // the modules
721   fprintf (mapOut, "Modules:\n");
722   for (module=modules; module; module=module->next) {
723     fprintf (mapOut, "\t%s\n", module->name);
724     for (s=0; s<MAX_SEGMENTS; s++) {
725       if (module->size[s]) {
726         fprintf (mapOut, "\t\t%s:0x%04x-0x%04x\n", segments[s].name,
727                  module->offset[s], module->offset[s]+module->size[s]);
728       }
729     }
730   }
731
732   // the segments
733   fprintf (mapOut, "\nSegments:\n");
734   for (s=1; s<MAX_SEGMENTS; s++) {
735     if (segments[s]._size) {
736       fprintf (mapOut, "\t%s start 0x%04x size 0x%04x %d symbols\n",
737                segments[s].name, segments[s].start,
738                segments[s]._size, 
739                segments[s].hasSymbols);
740     }
741   }
742
743   // the symbols
744   fprintf (mapOut, "\nSymbols:\n");
745   for (symbol=symbols; symbol; symbol=symbol->next) {
746     fprintf (mapOut, "%s\t%s %s0x%04x %s\n", symbol->name, 
747              symbol->segment->name,
748              symbol->absolute ? "= " : "",
749              symbol->address, symbol->module->name);
750   }
751
752   writeModule(outFileName);
753   fclose(mapOut);
754   return fatalErrors? 1 : 0;
755 }
756