macross/slinky/link.c

460 lines
14 KiB
C

/*
* Copyright (c) 1987 Fujitsu
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/*
link.c -- Primary routines for the Slinky linker.
Chip Morningstar -- Lucasfilm Ltd.
9-March-1985
*/
#include "slinkyTypes.h"
#include "slinkyGlobals.h"
#include "link.h"
#include "debugPrint.h"
#include "errorStuff.h"
#include "instantiate.h"
#include "poke.h"
#include "read.h"
#include "relocate.h"
#include "write.h"
/*#define readWord(f,fn) ((getc(f) & 0xFF) | ((getc(f) & 0xFF)<<8))*/
/*
'internalizeOneObjectFile' does just that: it reads an object file into
the master data structure. The object file format is:
0xFFFF -- A "magic number" to signal the
start of an object file.
ABSOLUTE code segments -- In the form of a 2-byte start
address, followed by a 2-byte end
address, followed by the requisite
number of code bytes.
0xFFFF -- A partition.
RELOCATABLE code segments -- In the same format as the ABSOLUTE
code segments.
0xFFFF -- Another partition.
space reservation blocks -- each in the form of a 2-byte start
address followed by a 2-byte
length.
0xFFFF -- Yet another partition.
number of reference items -- A 4-byte count.
that many reference items -- Each a 'referenceType' in binary
form.
number of symbol entries -- A 4-byte count.
number of bytes of strings -- A 4-byte count of the sum total of
string space used by all of the
following symbol entries.
that many symbol entries -- Each a 'symbolType' in binary form
followed by a string with the
symbol name.
**new**
number of expression entries -- A 4-byte count.
that many expression entries -- Each a 4-byte length indicator
followed by the indicated number of
bytes representing a compacted
parse tree of an expression.
number of function entries -- A 4-byte count.
that many function entries -- Each a 1 byte argument count,
followed by that many symbol
numbers, followed by a compacted
parse tree of the function body
itself represented in the same
manner as expressions (i.e.,
length + bytes).
**end of new stuff**
end-of-file
Potential gotchas:
1) A length 1 code segment signals the program entry point and is not
really code (i.e., start address==end address==entry point).
2) A length 0 code segment encodes a constraint instruction. The 2-byte
word following gives the multiple to constrain by.
3) A length -1 code segment encodes an aligment instruction. The 2-byte
word following gives the multiple to align to.
4) It is assumed that Macross will output all code segments of a given mode
in strictly ascending order of start address.
*/
bool
internalizeOneObjectFile(objectFileListType *objectFile)
{
FILE *objectFildes;
int magic;
int mode;
addressType startAddress;
addressType endAddress;
currentFileName = objectFile->name;
if ((objectFildes = fopen(objectFile->name, "r")) == NULL) {
error(CANT_OPEN_OBJECT_FILE_ERROR, objectFile->name);
perror("Unix says");
return(FALSE);
}
if (verbose)
printf("internalizing %s:\n", objectFile->name);
if ((magic = readWord(objectFildes, objectFile->name)) != 0xFFFF) {
error(BAD_OBJECT_FILE_ERROR, objectFile->name);
return(FALSE);
}
mode = MODE_ABSOLUTE;
for (;;) {
startAddress = readWord(objectFildes, objectFile->name);
if (startAddress == 0xFFFF) {
if (mode == MODE_ABSOLUTE) {
mode = MODE_RELOCATABLE;
continue;
} else {
break;
}
}
endAddress = readWord(objectFildes, objectFile->name);
readCode(startAddress, endAddress, mode, objectFile,
objectFildes);
}
readReservations(objectFile, objectFildes);
readReferences(objectFile, objectFildes);
readSymbols(objectFile, objectFildes);
readExpressions(objectFile, objectFildes);
readFunctions(objectFile, objectFildes);
instantiateExpressionAndSymbolPointers(objectFile);
if (readExpressionEntryPoint) {
pc = entryPointExpression;
putSymbolPointersIntoExpression();
readExpressionEntryPoint = FALSE;
haveExpressionEntryPoint = TRUE;
}
qsort(objectFile->symbolTable, objectFile->symbolCount,
sizeof(symbolType *), compareSymbolValues);
fclose(objectFildes);
return(TRUE);
}
#define toLowerCase(c) (('A'<=(c)&&(c)<='Z')?((c)-'A'+'a'):(c));
bool
strcmplc(char *s1, char *s2)
{
register char c1;
register char c2;
int result;
do {
c1 = *s1++;
c1 = toLowerCase(c1);
c2 = *s2++;
c2 = toLowerCase(c2);
/* if result != 0, they differ */
if (result = c1 - c2) {
return(result); /* c1<c2==neg, c1>c2==pos */
} else if (!c1) { /* if they're null, we're done */
return(0);
}
} while (TRUE);
}
bool
compareSymbols(symbolType **symbol1, symbolType **symbol2)
{
bool result;
result = strcmplc((*symbol1)->symbolName, (*symbol2)->symbolName);
if (result == 0 && (((*symbol1)->symbolClass & ~SYMBOL_EXTERNAL) &&
((*symbol2)->symbolClass & ~SYMBOL_EXTERNAL))) {
error(MULTIPLY_DEFINED_SYMBOL_ERROR, (*symbol1)->symbolName);
(*symbol2)->symbolClass &= ~SYMBOL_ABSOLUTE;
(*symbol2)->symbolClass &= ~SYMBOL_RELOCATABLE;
}
return(result);
}
void
buildGlobalSymbolTable(objectFileListType *inputFileList)
{
int symbolCount;
symbolType **symbol;
symbolType **globalSymbolPtr;
globalSymbolTable = typeAllocBlock(symbolType *, globalSymbolCount);
globalSymbolPtr = globalSymbolTable;
for (; inputFileList != NULL; inputFileList = inputFileList->
nextObjectFile) {
if (inputFileList->name != NULL) {
for (symbol = inputFileList->symbolTable,
symbolCount = inputFileList->
symbolCount; symbolCount>0; symbol++,
symbolCount--) {
if (((*symbol)->symbolClass &
SYMBOL_EXTERNAL) &&
((*symbol)->symbolClass &
~SYMBOL_EXTERNAL)) {
*globalSymbolPtr++ = (*symbol);
}
}
}
}
qsort(globalSymbolTable, globalSymbolCount, sizeof(symbolType *),
compareSymbols);
if (debug) {
printGlobalSymbols();
}
}
bool
readem(void)
{
objectFileListType *inputFileList;
if (objectFileList == NULL) {
error(NO_INPUT_FILES_ERROR);
return(FALSE);
}
inputFileList = objectFileList;
while (inputFileList != NULL) {
if (inputFileList->name != NULL)
if (!internalizeOneObjectFile(inputFileList))
return(FALSE);
inputFileList = inputFileList->nextObjectFile;
}
buildGlobalSymbolTable(objectFileList);
return(TRUE);
}
codeSegmentHeaderType *
locateConflictingSegment(codeSegmentHeaderType *codeSegment)
{
segmentListType *segmentPtr;
int segmentListOffset;
int segmentListOffsetStart;
int segmentListOffsetEnd;
addressType start;
addressType end;
start = codeSegment->segmentStartAddress;
end = codeSegment->segmentEndAddress;
/* segmentListOffsetStart = (start / CODE_REGION_SIZE) - 1;
segmentListOffsetEnd = segmentListOffsetStart + 2;
if (segmentListOffsetStart < 0)*/
segmentListOffsetStart = 0;
/* if (segmentListOffsetEnd >= CODE_REGIONS_IN_ADDRESS_SPACE)*/
segmentListOffsetEnd = CODE_REGIONS_IN_ADDRESS_SPACE - 1;
for (segmentListOffset = segmentListOffsetStart;
segmentListOffset <= segmentListOffsetEnd;
segmentListOffset++) {
segmentPtr = generatedLoadImage[segmentListOffset];
while (segmentPtr != NULL) {
if ((segmentPtr->thisSegment->segmentStartAddress<=start &&
start<=segmentPtr->thisSegment->segmentEndAddress) ||
(segmentPtr->thisSegment->segmentStartAddress<=end &&
end<=segmentPtr->thisSegment->segmentEndAddress))
return(segmentPtr->thisSegment);
else
segmentPtr = segmentPtr->nextSegment;
}
}
printf("Hey! segment list overrun in locateConflictingSegment\n");
return(NULL);
}
void
reserveSegment(addressType start, addressType end)
{
freeSegmentEntryType *freeSegmentPtr;
freeSegmentEntryType *previousSegmentPtr;
freeSegmentEntryType *newSegmentPtr;
previousSegmentPtr = NULL;
freeSegmentPtr = freeSegmentList;
while (freeSegmentPtr != NULL && start > freeSegmentPtr->
segmentEndAddress) {
previousSegmentPtr = freeSegmentPtr;
freeSegmentPtr = freeSegmentPtr->nextFreeSegment;
}
if (end < freeSegmentPtr->segmentStartAddress) {
/* return; */
} else if (start <= freeSegmentPtr->segmentStartAddress && end <
freeSegmentPtr->segmentEndAddress) {
freeSegmentPtr->segmentStartAddress = end + 1;
} else if (end >= freeSegmentPtr->segmentEndAddress) {
if (start > freeSegmentPtr->segmentStartAddress)
freeSegmentPtr->segmentEndAddress = start - 1;
if (previousSegmentPtr == NULL) {
while (end >= freeSegmentPtr->segmentEndAddress) {
freeSegmentList = freeSegmentPtr->
nextFreeSegment;
free(freeSegmentPtr);
freeSegmentPtr = freeSegmentList;
}
} else {
while (end >= freeSegmentPtr->segmentEndAddress) {
previousSegmentPtr->nextFreeSegment =
freeSegmentPtr->nextFreeSegment;
free(freeSegmentPtr);
freeSegmentPtr = previousSegmentPtr->
nextFreeSegment;
}
}
if (end >= freeSegmentPtr->segmentStartAddress) {
freeSegmentPtr->segmentStartAddress = end + 1;
}
} else {
newSegmentPtr = typeAlloc(freeSegmentEntryType);
newSegmentPtr->nextFreeSegment = freeSegmentPtr->
nextFreeSegment;
freeSegmentPtr->nextFreeSegment = newSegmentPtr;
newSegmentPtr->segmentEndAddress = freeSegmentPtr->
segmentEndAddress;
freeSegmentPtr->segmentEndAddress = start - 1;
newSegmentPtr->segmentStartAddress = end + 1;
}
}
codeSegmentHeaderType *
allocateAbsolute(codeSegmentHeaderType *codeSegment)
{
freeSegmentEntryType *freeSegmentPtr;
freeSegmentEntryType *previousSegmentPtr;
freeSegmentEntryType *newSegmentPtr;
addressType start;
addressType end;
previousSegmentPtr = NULL;
freeSegmentPtr = freeSegmentList;
start = codeSegment->segmentStartAddress;
end = codeSegment->segmentEndAddress;
while (freeSegmentPtr != NULL && start > freeSegmentPtr->
segmentEndAddress) {
previousSegmentPtr = freeSegmentPtr;
freeSegmentPtr = freeSegmentPtr->nextFreeSegment;
}
if (freeSegmentPtr->segmentEndAddress < end || start<freeSegmentPtr->
segmentStartAddress)
return(locateConflictingSegment(codeSegment));
if (freeSegmentPtr->segmentStartAddress == start || freeSegmentPtr->
segmentEndAddress == end) {
if (freeSegmentPtr->segmentStartAddress == start)
freeSegmentPtr->segmentStartAddress = end + 1;
if (freeSegmentPtr->segmentEndAddress == end)
freeSegmentPtr->segmentEndAddress = start - 1;
if (freeSegmentPtr->segmentEndAddress < freeSegmentPtr->
segmentStartAddress) {
if (previousSegmentPtr == NULL)
freeSegmentList = freeSegmentPtr->
nextFreeSegment;
else
previousSegmentPtr->nextFreeSegment =
freeSegmentPtr->nextFreeSegment;
free(freeSegmentPtr);
}
} else {
newSegmentPtr = typeAlloc(freeSegmentEntryType);
newSegmentPtr->nextFreeSegment = freeSegmentPtr->
nextFreeSegment;
freeSegmentPtr->nextFreeSegment = newSegmentPtr;
newSegmentPtr->segmentEndAddress = freeSegmentPtr->
segmentEndAddress;
freeSegmentPtr->segmentEndAddress = start - 1;
newSegmentPtr->segmentStartAddress = end + 1;
}
return(NULL);
}
void
reserveReservations(void)
{
while (reservationList != NULL) {
reserveSegment(reservationList->startAddress,
reservationList->startAddress + reservationList->
blockSize - 1);
reservationList = reservationList->nextReservation;
}
}
void
installSegment(codeSegmentHeaderType *codeSegment)
{
segmentListType *previousSegment;
segmentListType *installSegmentList;
segmentListType *newSegmentListEntry;
int regionNumber;
int endRegion;
regionNumber = regionOf(codeSegment->segmentStartAddress);
previousSegment = NULL;
installSegmentList = generatedLoadImage[regionNumber];
while (installSegmentList != NULL && installSegmentList->thisSegment->
segmentStartAddress < codeSegment->segmentStartAddress) {
previousSegment = installSegmentList;
installSegmentList = installSegmentList->nextSegment;
}
newSegmentListEntry = typeAlloc(segmentListType);
newSegmentListEntry->thisSegment = codeSegment;
newSegmentListEntry->nextSegment = installSegmentList;
if (previousSegment == NULL) {
generatedLoadImage[regionNumber] = newSegmentListEntry;
} else {
previousSegment->nextSegment = newSegmentListEntry;
}
if (regionNumber < (endRegion=regionOf(codeSegment->segmentEndAddress))) {
for (regionNumber++; regionNumber <= endRegion; regionNumber++) {
newSegmentListEntry = typeAlloc(segmentListType);
newSegmentListEntry->thisSegment = codeSegment;
newSegmentListEntry->nextSegment=generatedLoadImage[regionNumber];
generatedLoadImage[regionNumber] = newSegmentListEntry;
}
}
}
void
installAbsoluteCodeSegment(codeSegmentHeaderType *codeSegment)
{
codeSegmentHeaderType *conflictingSegment;
if ((conflictingSegment = allocateAbsolute(codeSegment)) != NULL) {
error(OVERLAPPING_ABSOLUTE_CODE_SEGMENTS_ERROR, conflictingSegment->
fileName, conflictingSegment->segmentStartAddress,
conflictingSegment->segmentEndAddress, codeSegment->fileName,
codeSegment->segmentStartAddress, codeSegment->segmentEndAddress);
} else {
installSegment(codeSegment);
}
}
void
linkem(void)
{
if (!readem())
return;
relocatem();
valuem();
pokem();
writem();
}