X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=as%2Fxa51%2Fxa_link.c;h=a6823d246765f8272f394fb5266747832df3bc99;hb=31264d72ca126a7ad645192145c94daf4d72e2a7;hp=64d89e5dd8dc5300c9d47922a203ffba74ded051;hpb=2578c513da0fcaed975fd43cfd36a7458b7895a5;p=fw%2Fsdcc diff --git a/as/xa51/xa_link.c b/as/xa51/xa_link.c index 64d89e5d..a6823d24 100644 --- a/as/xa51/xa_link.c +++ b/as/xa51/xa_link.c @@ -1,112 +1,864 @@ +/* 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. + 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 + "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 [Ref000 | DefXXXX]" Def's are supposed to be defined in - their own area/segment + "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 - "T xx xx bb bb ..." where x is the address within the current segment - and bb are the bytes - "R xx " the relocation tag. xx is the offset within the - previous "T .." line. could be something like REL_FF, REL_FFFF, - ABS_F0FF. and symbol is the previous defined symbol it refers to - - Thats all for now. + 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" -static char outFileName[PATH_MAX]; +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]; -void baseName(char *name, char*base) { - int i, first, last; +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}, - // find the last path seperator in name - for (first=strlen(name)-1; - (name[first]!='/' && name[first]!='\\') && first; - first--); - if (name[first]=='/' || name[first]=='\\') { - first++; + {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; + } } - base[i]='\0'; + return 0; } - -void readModule(char *module) { - double thisVersion; + +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); } - printf ("ReadModule: %s\n", module); + // first we need to check if this is a valid file - if (sscanf(line, "SDCCXA rel, version %lf", &thisVersion)!=1) { - fprintf (stderr, "%s is not a valid input file\n", module); + 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 (thisVersion!=version) { - fprintf (stderr, "version conflict; we: %f != module: %f\n", - version, thisVersion); + 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 *module) { - if (!outFileName[0]) { - sprintf (outFileName, "%s.bin", module); +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); } - printf ("WriteModule: %s\n", outFileName); + 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 f.rel [f1.rel [f2.rel [...]]]\n", progName); + 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; } - +