7c376f156bd35e9e3b64abaadf6e7984e88bce2c
[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. 
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]" Def's are supposed to be defined in
26      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 xx xx bb bb ..." where xx xx is the address within the current segment
30      and bb are the bytes
31    "R xx <how> <symbol>" the relocation info. xx is the offset within the
32      previous "T .." line. <how> could be something like REL_FF, REL_FFFF, 
33      ABS_F0FF. symbol is the (previous) defined symbol it refers to
34
35    So, this is not a standalone linker. It will only link files generated
36    by xa_asm, which will only process files generated by the xa51 sdcc
37    port.
38 */
39
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <limits.h>
43 #include <string.h>
44
45 #include "xa_version.h"
46
47 enum {
48   // these are all concatenated into the code image
49   GSINIT=1,
50   CSEG,
51   XINIT,
52   //GSFINAL, // do we need this?
53
54   // these are only for storage
55   BSEG,
56   DSEG,
57   XSEG,
58   XISEG,
59   MAX_SEGMENTS
60 };
61
62 #define CODESIZE 0x10000
63
64 char codeImage[CODESIZE];
65 char gsinitImage[CODESIZE];
66 char xinitImage[CODESIZE];
67
68 struct SEGMENT {
69   short id;
70   char *name;
71   int hasSymbols;
72   int _size;
73   int start;
74   int current;
75   unsigned char *image;
76 } segments[MAX_SEGMENTS]={
77   {0,       "????",    0, 0, 0, 0, NULL},
78
79   {GSINIT,  "GSINIT",  0, 0, 0, 0, gsinitImage},
80   {CSEG,    "CSEG",    0, 0, 0, 0, codeImage},
81   {XINIT,   "XINIT",   0, 0, 0, 0, xinitImage},
82   //{GSFINAL, "GSFINAL", 0, 0, 0, 0, NULL},
83
84   {BSEG,    "BSEG",    0, 0, 0, 0, NULL},
85   {DSEG,    "DSEG",    0, 0, 0, 0, NULL},
86   {XSEG,    "XSEG",    0, 0, 0, 0, NULL},
87   {XISEG,   "XISEG",   0, 0, 0, 0, NULL},
88 };
89
90 struct MODULE {
91   char *name;
92   int offset[MAX_SEGMENTS];
93   int size[MAX_SEGMENTS];
94   struct MODULE *next;
95   struct MODULE *last;
96 } *modules=NULL;
97
98
99 struct SYMBOL {
100   char *name;
101   struct MODULE *module;
102   struct SEGMENT *segment;
103   int address;
104   struct SYMBOL *next;
105   struct SYMBOL *last;
106 } *symbols=NULL;
107
108 char *libPaths[128];
109 int nlibPaths=0;
110 char *libFiles[128];
111 int nlibFiles=0;
112
113 static char outFileName[PATH_MAX];
114
115 struct SEGMENT *currentSegment;
116 struct MODULE *currentModule;
117
118 struct SEGMENT *findSegment(char *segment) {
119   int i;
120   for (i=1; i<MAX_SEGMENTS; i++) {
121     if (strcmp(segments[i].name, segment)==0) {
122       return &segments[i];
123     }
124   }
125   return 0;
126 }
127
128 void addToModules (char *name) {
129   struct MODULE *module;
130   int s;
131
132   //fprintf (stderr, "addToModules: %s\n", name);
133
134   module=calloc(1, sizeof(struct MODULE));
135   module->name=strdup(name);
136   for (s=0; s<MAX_SEGMENTS; s++) {
137     module->offset[s]=segments[s].current;
138   }
139   if (!modules) {
140     modules=module;
141   } else {
142     modules->last->next=module;
143   }
144   currentModule=modules->last=module;
145 }
146
147 void addToRefs(char *ref) {
148   //fprintf (stderr, "addToRefs: %s\n", ref);
149 }
150
151 void addToDefs(char *def, int address) {
152   struct SYMBOL *symbol;
153   /* fprintf (stderr, "addToDefs: %s %s 0x%04x + 0x%04x\n", 
154      currentSegment->name, def, 
155      currentModule->offset[currentSegment->id],
156      address); */
157   symbol=calloc(1, sizeof(struct SYMBOL));
158   symbol->name=strdup(def);
159   symbol->module=currentModule;
160   symbol->segment=currentSegment;
161   symbol->address=currentModule->offset[currentSegment->id]+address;
162   if (!symbols) {
163     symbols=symbol;
164   } else {
165     symbols->last->next=symbol;
166   }
167   symbols->last=symbol;
168   currentSegment->hasSymbols++;
169 }
170
171 void syntaxError (char *err) {
172   fprintf (stderr, "error while parsing '%s'\n", err);
173   exit(1);
174 }
175
176 void baseName(char *name, char*base) {
177   int i, first, last;
178
179   // find the last path seperator in name
180   for (first=strlen(name)-1; 
181        (name[first]!='/' && name[first]!='\\') && first;
182        first--);
183   if (name[first]=='/' || name[first]=='\\') {
184     first++;
185   }
186
187   // find the last ext seperator in name
188   for (last=strlen(name)-1; 
189        (name[last]!='.' && last);
190        last--);
191   if (!last) {
192     last=strlen(name);
193   }
194
195   fprintf (stderr, "baseName: %s %d %d\n", name, first, last);
196   // fill the base with the baseName
197   for (i=first; i<last; i++) {
198     base[i-first]=name[i];
199   }
200   base[i]='\0';
201 }
202   
203 void readModule(char *module) {
204   double hisVersion;
205   char line[132];
206   FILE *relModule;
207   char moduleName[PATH_MAX];
208   int segments, globals;
209   int currentLine=1;
210
211   if ((relModule=fopen(module, "r"))==NULL) {
212     perror (module);
213     exit (1);
214   }
215
216   //fprintf (stderr, "ReadModule: %s\n", module);
217
218   // first we need to check if this is a valid file
219   if (sscanf(fgets(line, 132, relModule), 
220              "SDCCXA rel, version %lf", &hisVersion)!=1) {
221     fprintf (stderr, "%s is not a valid input file\n", module);
222     exit (1);
223   }
224   if (hisVersion!=version) {
225     fprintf (stderr, "WARNING: version conflict; "
226              "we(%1.1f) != %s(%1.1f)\n", 
227              version, module, hisVersion);
228   }
229   currentLine++;
230   
231   // H 7 areas 168 global symbols
232   if (sscanf(fgets(line, 132, relModule),
233              "H %d areas %d global symbols",
234              &segments, &globals)!=2) {
235     syntaxError(line);
236   }
237   currentLine++;
238
239   // M module
240   if (sscanf(fgets(line, 132, relModule),
241              "M %s", moduleName)!=1) {
242     syntaxError(line);
243   }
244
245   // add this to the known modules with current offsets
246   addToModules(module);
247
248   fprintf (stderr, "module %s has %d segment%s and %d globals\n",
249            moduleName, segments, segments==1?"":"s", globals);
250   currentLine++;
251
252   // now for the ASTR tags
253   while (fgets(line, 132, relModule)) {
254     switch (line[0]) 
255       {
256       case 'A': {
257         char segment[32];
258         int size, flags;
259         if (sscanf(line, "A %[^ ] size %d flags %d",
260                    segment, &size, &flags)!=3) {
261           fprintf (stderr, "%s:%d error in A record line\n", 
262                    module, currentLine);
263           exit (1);
264         }
265         // do we know this segment?
266         if (!(currentSegment=findSegment(segment))) {
267           fprintf (stderr, "%s:%d unknown area: %s\n", module,
268                    currentLine, segment);
269           exit (1);
270         }
271         if (currentModule->size[currentSegment->id]) {
272           if (currentModule->size[currentSegment->id] != size) {
273             fprintf (stderr, "%s:%d error %s %d %d\n",
274                      module, currentLine,
275                      currentSegment->name,
276                      currentModule->size[currentSegment->id], 
277                      size);
278           }
279         } else {
280           currentModule->size[currentSegment->id]=size;
281           currentModule->offset[currentSegment->id]+=currentSegment->_size;
282           currentSegment->_size += size;
283         }
284         //fprintf (stderr, "Area: %s size: %d\n", segment, size);
285         // never mind about the flags for now
286         break;
287       }
288       case 'S': {
289         char symbol[132];
290         char refdef[132];
291         unsigned int address;
292         if (sscanf(line, "S %[^ ] %s", symbol, refdef)!=2) {
293           fprintf (stderr, "%s:%d syntax error near \"%s\"\n",
294                    module, currentLine, line);
295           exit (1);
296         }
297         if (strncmp(refdef, "Ref", 3)==0) {
298           addToRefs(symbol);
299         } else if (strncmp(refdef, "Def", 3)==0) {
300           sscanf (refdef, "Def%04x", &address);
301           addToDefs(symbol, address);
302         } else {
303           fprintf (stderr, "%s:%d found invalid symbol definition \"%s\"\n", 
304                    module, currentLine, line);
305           exit (1);
306         }
307         break;
308       }
309       case 'T': {
310         unsigned int address;
311         unsigned int byte;
312         char *tline=NULL;
313         if (currentSegment->id!=CSEG && 
314             currentSegment->id!=GSINIT &&
315             //currentSegment->id!=GSFINAL &&
316             currentSegment->id!=XINIT) {
317           fprintf (stderr, "%s:%d cannot emit bytes in %s\n",
318                    module, currentLine, currentSegment->name);
319           exit (1);
320         }
321         if (sscanf(strtok(&line[2], " "), "%04x", &address)!=1) {
322           fprintf (stderr, "%s:%d error in T record\n", module, currentLine);
323           exit (1);
324         }
325         //fprintf (stderr, "%04x:", address);
326         address+=currentSegment->current;
327         for ( ;
328               (tline=strtok(NULL, " \t\n")) && 
329                 (sscanf(tline, "%02x", &byte)==1);
330               ) {
331           //fprintf (stderr, " %02x", byte);
332           currentSegment->image[address++]=byte;
333           currentSegment->current++;
334         }
335         //fprintf (stderr, "\n");
336         break;
337       }
338       case 'R':
339         //fprintf (stderr, "%s", line);
340         break;
341       default:
342         /* fprintf (stderr, "%s:%d unknown record \"%s\"\n",
343            module, currentLine, line); */
344         break;
345       }
346     currentLine++;
347   }
348   // that's all for now, thanks for watching */
349   fclose (relModule);
350 }
351
352 void writeModule() {
353   fprintf (stderr, "WriteModule: %s\n", outFileName);
354   // oops, forgot something :) */
355 }
356
357 void relocate() {
358   struct SYMBOL *symbol;
359   int length=segments[GSINIT].current +
360     segments[CSEG].current +
361     segments[XINIT].current;
362
363   // first check if it will fit
364   if (length > 0xffff) {
365     fprintf (stderr, "error: code segment exceeds 0xffff\n");
366     exit(1);
367   }
368   fprintf (stderr, "relocate: total code size: 0x%04x\n", length);
369
370   // GSINIT gets the --code-loc
371   segments[GSINIT].start=segments[CSEG].start;
372   segments[CSEG].start=segments[GSINIT].start+segments[GSINIT]._size;
373   segments[XINIT].start=segments[CSEG].start+segments[CSEG]._size;
374   segments[XISEG].start=segments[XSEG].start+segments[XINIT]._size;  
375   // now relocate the defined symbols
376   for (symbol=symbols; symbol; symbol=symbol->next) {
377     symbol->address += symbol->segment->start;
378   }
379 }
380
381 void usage (char * progName, int errNo) {
382   fprintf (stderr, "usage: %s lnkCmdFile\n", progName);
383   if (errNo) {
384     exit (errNo);
385   }
386 }
387
388 int main(int argc, char **argv) {
389   FILE *linkCommandsFile;
390   char linkCommandsPath[PATH_MAX];
391   char linkCommand[PATH_MAX];
392   struct MODULE *module;
393   struct SYMBOL *symbol;
394   int s;
395
396   if (argc!=2) {
397     usage(argv[0], 1);
398   }
399
400   memset(codeImage, 0xff, CODESIZE);
401
402   // read in the commands
403   sprintf (linkCommandsPath, "%s.lnk", argv[1]);
404   if (!(linkCommandsFile=fopen(linkCommandsPath, "r"))) {
405     perror(linkCommandsPath);
406     exit(1);
407   }
408   while (fgets(linkCommand, PATH_MAX, linkCommandsFile)) {
409     linkCommand[strlen(linkCommand)-1]='\0';
410
411     // skip empty lines
412     if (!*linkCommand) {
413       continue;
414     }
415
416     //puts (linkCommand);
417     if (*linkCommand=='-') {
418       switch (linkCommand[1]) 
419         {
420         case 'm':
421           // probably -muxi, ignore for now
422           break;
423         case 'e':
424           // -e, always in the last line, ignore for now
425           break;
426         case 'b': 
427           {
428             // a segment start address like: "-b XSEG = 0x4000"
429             int s;
430             char *seg=strtok(&linkCommand[3], " \t");
431             for (s=0; s<MAX_SEGMENTS; s++) {
432               if (strcmp(segments[s].name, seg)==0) {
433                 strtok(NULL, " \t"); // skip the '='
434                 if (sscanf(strtok(NULL, " \t"), "%x", 
435                            &segments[s].start)!=1) {
436                   syntaxError(linkCommand);
437                 }
438                 /* fprintf (stderr, "%s starts at 0x%04x\n", segments[s].name,
439                    segments[s].start); */
440                 break;
441               }
442             }
443             if (s==MAX_SEGMENTS) {
444               syntaxError(linkCommand);
445             }
446           }
447           break;
448         case 'k':
449           // a lib path like: "-k /usr/local/share/sdcc/lib/xa51"; one/line
450           libPaths[nlibPaths++]=strdup(&linkCommand[3]);
451           break;
452         case 'l':
453           // a lib file like: "-l libsdcc"; one/line
454           libFiles[nlibFiles++]=strdup(&linkCommand[3]);
455           break;
456         default:
457           syntaxError(linkCommand);
458         }
459     } else {
460       // not a switch, must be an inputfile; one/line
461       readModule(linkCommand);
462     }
463   }
464
465   relocate();
466
467   // the modules
468   for (module=modules; module; module=module->next) {
469     fprintf (stderr, "%s: ", module->name);
470     for (s=0; s<MAX_SEGMENTS; s++) {
471       if (module->size[s]) {
472         fprintf (stderr, "%s:0x%04x-0x%04x ", segments[s].name,
473                  module->offset[s], module->offset[s]+module->size[s]);
474       }
475     }
476     fprintf (stderr, "\n");
477   }
478
479   // the symbols
480   for (symbol=symbols; symbol; symbol=symbol->next) {
481     fprintf (stderr, "%s %s 0x%04x %s\n", symbol->name, symbol->segment->name,
482              symbol->address, symbol->module->name);
483   }
484
485   // the segments
486   for (s=1; s<MAX_SEGMENTS; s++) {
487     if (segments[s]._size) {
488       fprintf (stderr, "%s start 0x%04x size 0x%04x %d symbols\n",
489                segments[s].name, segments[s].start,
490                segments[s]._size, 
491                segments[s].hasSymbols);
492     }
493   }
494
495   writeModule();
496   return 0;
497 }
498