/* 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 . */ /* WORK IN PROGRESS: do not watch this if you don't have the legal age in your country to watch this. */ /* 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, not to mention word allignment. No hash or qsort yet. The relocatable format looks like the known one, BUT ISN'T. The only things that are handled now are: "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 [Ref0000 | DefXXXX | AbsXXXX]" Def's are supposed to be defined in their own area/segment "A size %d flags %d" switch to another segment. this can happen multiple times and should be equal. flags is ignored for now "T xxxx 0" "R xxxx " 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_rasm, which will only process files generated by the xa51 sdcc port. */ #include #include #include #include #include "xa_version.h" enum { // these are all concatenated into the code image GSINIT=1, CSEG, XINIT, // 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 int fatalErrors=0; unsigned char gsinitImage[CODESIZE]; unsigned char csegImage[CODESIZE]; unsigned char xinitImage[CODESIZE]; unsigned char gsfinalImage[CODESIZE]; struct SEGMENT { short id; char *name; int hasSymbols; int _size; int start; int current; unsigned char *image; } segments[MAX_SEGMENTS]={ {0, "???", 0, 0, 0, 0, NULL}, {GSINIT, "GSINIT", 0, 0, 0, 0, gsinitImage}, {CSEG, "CSEG", 0, 0, 0, 0, csegImage}, {XINIT, "XINIT", 0, 0, 0, 0, xinitImage}, {GSFINAL, "GSFINAL", 0, 0, 0, 0, gsfinalImage}, {BSEG, "BSEG", 0, 0, 0, 0, NULL}, {DSEG, "DSEG", 0, 0, 0, 0, NULL}, {XSEG, "XSEG", 0, 0, 0, 0, NULL}, {XISEG, "XISEG", 0, 0, 0, 0, NULL}, }; struct MODULE { char *name; int offset[MAX_SEGMENTS]; int size[MAX_SEGMENTS]; int isLib; struct MODULE *next; struct MODULE *last; } *modules=NULL; struct SYMBOL { char *name; struct MODULE *module; int lineno; struct SEGMENT *segment; char absolute; int address; struct SYMBOL *next; struct SYMBOL *last; } *symbols=NULL; 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 *libraryFiles[128]; int nlibFiles=0; static char outFileName[PATH_MAX]={'\0'}; static char mapFileName[PATH_MAX]={'\0'}; FILE *mapOut; struct SEGMENT *currentSegment; struct MODULE *currentModule; int currentLine; int howToReference(char *how) { int r; for (r=1; rnext) { 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; } void addToModules (char *name, int isLib) { struct MODULE *module; int s; module=calloc(1, sizeof(struct MODULE)); module->name=strdup(name); for (s=0; soffset[s]=(segments[s]._size+1)&0xfffffe; } module->isLib=isLib; if (!modules) { modules=module; } else { modules->last->next=module; } currentModule=modules->last=module; } 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; } } } 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; // 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; } else { symbols->last->next=symbol; } symbols->last=symbol; currentSegment->hasSymbols++; } void syntaxError (char *err) { fprintf (stderr, "*** %s:%d error while parsing '%s'\n", currentModule->name, currentLine, err); fatalErrors++; } void readModule(char *module, int isLib) { double hisVersion; char line[132]; FILE *relModule; char moduleName[PATH_MAX]; int segments, globals; currentLine=1; if ((relModule=fopen(module, "r"))==NULL) { perror (module); exit (1); } // 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); exit (1); } if (hisVersion!=version) { fprintf (stderr, "*** WARNING: version conflict; " "we(%1.1f) != %s(%1.1f)\n", version, module, hisVersion); } currentLine++; // H 7 areas 168 global symbols if (sscanf(fgets(line, 132, relModule), "H %d areas %d global symbols", &segments, &globals)!=2) { syntaxError(line); } currentLine++; // M module if (sscanf(fgets(line, 132, relModule), "M %s", moduleName)!=1) { syntaxError(line); } // add this to the known modules with current offsets addToModules(module, isLib); currentLine++; // now for the ASTR tags while (fgets(line, 132, relModule)) { switch (line[0]) { case 'A': { char segment[32]; int size, flags; if (sscanf(line, "A %[^ ] size %d flags %d", segment, &size, &flags)!=3) { syntaxError(line); } // do we know this segment? 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 size %d != %d\n", module, currentLine, currentSegment->name, currentModule->size[currentSegment->id], size); fatalErrors++; } } else { currentSegment->_size += size; currentModule->size[currentSegment->id] = size; } // never mind about the flags for now break; } case 'S': { char symbol[132]; char refdef[132]; unsigned int address; if (sscanf(line, "S %[^ ] %s", symbol, refdef)!=2) { fprintf (stderr, "*** %s:%d syntax error near \"%s\"\n", module, currentLine, line); exit (1); } if (strncmp(refdef, "Ref", 3)==0) { // we don't need them } else if (strncmp(refdef, "Def", 3)==0) { sscanf (refdef, "Def%04x", &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); exit (1); } break; } case 'T': { unsigned int address; unsigned int byte; char *tline=NULL; if (currentSegment->id!=CSEG && currentSegment->id!=GSINIT && currentSegment->id!=XINIT) { fprintf (stderr, "%s:%d cannot emit bytes in %s\n", module, currentLine, currentSegment->name); exit (1); } if (sscanf(strtok(&line[2], " "), "%04x", &address)!=1) { fprintf (stderr, "%s:%d error in T record\n", module, currentLine); fatalErrors++; } address+=currentModule->offset[currentSegment->id]; //address+=currentSegment->current; for ( ; (tline=strtok(NULL, " \t\n")) && (sscanf(tline, "%02x", &byte)==1); ) { currentSegment->image[address++]=byte; currentSegment->current++; } break; } 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); fatalErrors++; break; } currentLine++; } fclose (relModule); } 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); } int relocate() { struct SYMBOL *symbol; 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"); fatalErrors++; } // 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) { 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) { fprintf (stderr, "usage: %s lnkCmdFile\n", progName); if (errNo) { exit (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; nlpnext) { 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 (mapOut, "\t%s\n", module->name); for (s=0; ssize[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]); } } } // the segments fprintf (mapOut, "\nSegments:\n"); for (s=1; snext) { 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; }