rln/rln.c

3532 lines
90 KiB
C

//
// RLN - Renamed Linker for the Atari Jaguar console system
// Copyright (C) 199x, Allan K. Pratt, 2014-2021 Reboot & Friends
//
#include "rln.h"
//#include <assert.h>
unsigned errflag = 0; // Error flag, goes TRUE on error
unsigned waitflag = 0; // Wait for any keypress flag
unsigned versflag = 0; // Version banner has been shown flag
unsigned aflag = 0; // Absolute linking flag
unsigned bflag = 0; // Don't remove mulitply def locals flag
unsigned cflag = 0; // COF executable
unsigned dflag = 0; // Wait for key after link flag
unsigned gflag = 0; // Source level debug include flag
unsigned lflag = 0; // Add local symbols to output flag
unsigned mflag = 0; // Produce symbol load map flag
unsigned oflag = 0; // Output filename specified
unsigned rflag = 0; // Segment alignment size flag
unsigned sflag = 0; // Output only global symbols
unsigned vflag = 0; // Verbose flag
unsigned wflag = 0; // Show warnings flag
unsigned zflag = 0; // Suppress banner flag
unsigned pflag = 0, uflag = 0; // Unimplemented flags (partial link, don't abort on unresolved symbols)
unsigned hd = 0; // Index of next file handle to fill
unsigned secalign = 7; // Section Alignment (8=phrase)
unsigned tbase = 0; // TEXT base address
unsigned dbase = 0; // DATA base address
unsigned bbase = 0; // BSS base address
unsigned textoffset = 0; // COF TEXT segment offset
unsigned dataoffset = 0; // COF DATA segment offset
unsigned bssoffset = 0; // COF BSS segment offset
unsigned displaybanner = 1; // Display version banner
unsigned symoffset = 0; // Symbol table offset in output file
int noheaderflag = 0; // No header flag for ABS files
int hflags; // Value of the arg to -h option
int ttype, dtype, btype; // Type flag: 0, -1, -2, -3, -4
int tval, dval, bval; // Values of these abs bases
int hflag[NHANDLES]; // True for include files
int handle[NHANDLES]; // Open file handles
int textsize, datasize, bsssize; // Cumulative segment sizes
char libdir[FARGSIZE * 3]; // Library directory to search
char ofile[FARGSIZE]; // Output file name (.o)
char * name[NHANDLES]; // Associated file names
char * cmdlnexec = NULL; // Executable name - pointer to ARGV[0]
char * hsym1[SYMLEN]; // First symbol for include files
char * hsym2[SYMLEN]; // Second symbol for include files
struct OFILE * plist = NULL; // Object image list pointer
struct OFILE * plast; // Last object image list pointer
struct OFILE * olist = NULL; // Pointer to first object file in list
struct OFILE * olast; // Pointer to last object file in list
char * arPtr[512];
uint32_t arIndex = 0;
struct HREC * htable[NBUCKETS]; // Hash table
struct HREC * unresolved = NULL; // Pointer to unresolved hash list
struct SYMREC * ost; // Output symbol table
char * oststr = NULL; // Output string table
char * oststr_ptr = NULL; // Output string table; current pointer
char * oststr_end = NULL; // Output string table; end pointer
int ost_index = 0; // Index of next free ost entry
int ost_size = 0; // Size of ost
uint8_t nullStr[1] = "\x00"; // Empty string
struct HREC * arSymbol = NULL; // Pointer to AR symbol table
// Function prototypes
struct HREC * LookupHREC(char *);
char * PathTail(char *);
void ShowHelp(void);
void ShowVersion(void);
int OSTLookup(char * sym);
int OSTAdd(char * name, int type, long value);
int ProcessFiles(void);
int doargs(int argc, char * argv[]);
//
// Get a long word from memory
//
static inline uint32_t GetLong(uint8_t * src)
{
return (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3];
}
//
// Put a long word into memory
//
static inline void PutLong(uint8_t * dest, uint32_t val)
{
*dest++ = (uint8_t)(val >> 24);
*dest++ = (uint8_t)(val >> 16);
*dest++ = (uint8_t)(val >> 8);
*dest = (uint8_t)val;
}
//
// Get a word from memory
//
static inline uint16_t GetWord(uint8_t * src)
{
return (src[0] << 8) | src[1];
}
//
// Put a word into memory
//
static inline void PutWord(uint8_t * dest, uint16_t val)
{
*dest++ = (uint8_t)(val >> 8);
*dest = (uint8_t)val;
}
//
// Find passed in file's length in bytes
// N.B.: This also resets the file's pointer to the start of the file
//
long FileSize(int fd)
{
long size = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
return size;
}
//
// For this object file, add symbols to the output symbol table after
// relocating them. Returns TRUE if OSTLookup returns an error (-1).
//
int DoSymbols(struct OFILE * ofile)
{
int type;
long value;
int index;
char *string;
int j;
struct HREC * hptr;
uint32_t tsoSave, dsoSave, bsoSave;
// Point to first symbol record in the object file
char * symptr = (ofile->o_image + 32
+ ofile->o_header.tsize
+ ofile->o_header.dsize
+ ofile->o_header.absrel.reloc.tsize
+ ofile->o_header.absrel.reloc.dsize);
// Point to end of symbol record in the object file
char * symend = symptr + ofile->o_header.ssize;
uint32_t tsegoffset = ofile->segBase[TEXT];
uint32_t dsegoffset = ofile->segBase[DATA];
uint32_t bsegoffset = ofile->segBase[BSS];
// Save segment vars, so we can restore them if needed
tsoSave = tsegoffset, dsoSave = dsegoffset, bsoSave = bsegoffset;
// Process each record in the object's symbol table
for(; symptr!=symend; symptr+=12)
{
index = GetLong(symptr + 0); // Obtain symbol string index
type = GetLong(symptr + 4); // Obtain symbol type
value = GetLong(symptr + 8); // Obtain symbol value
string = index ? symend + index : "";
// Global/External symbols have a pre-processing stage
// N.B.: This destroys the t/d/bsegoffset discovered above. So if a
// local symbol follows a global/exported one, it gets wrong
// info! [Should be FIXED now]
if (type & 0x01000000)
{
// Obtain the string table index for the relocation symbol, look
// for it in the globals hash table to obtain information on that
// symbol.
hptr = LookupHREC(string);
if (hptr == NULL)
{
// Try to find it in the OST
int ostIndex = OSTLookup(string);
if (ostIndex == -1)
{
printf("DoSymbols(): Symbol not found in hash table: '%s' (%s)\n", string, ofile->o_name);
return 1;
}
if (vflag > 1)
printf("DoSymbols(): Skipping symbol '%s' (%s) found in OST...\n", string, ofile->o_name);
// If the symbol is not in any .a or .o units, it must be one
// of the injected ones (_TEXT_E, _DATA_E, or _BSS_E), so skip
// it [or maybe not? In verbose mode, we see nothing...!]
continue;
}
tsegoffset = hptr->h_ofile->segBase[TEXT];
dsegoffset = hptr->h_ofile->segBase[DATA];
bsegoffset = hptr->h_ofile->segBase[BSS];
// Update type with global type
type = hptr->h_type;
// Remove global flag if absolute
if (type == (T_GLBL | T_ABS))
type = T_ABS;
// If the global/external has a value then update that value in
// accordance with the segment sizes of the object file it
// originates from
if (hptr->h_value)
{
switch (hptr->h_type & 0x0E000000)
{
case T_ABS:
case T_TEXT:
value = hptr->h_value;
break;
case T_DATA:
value = hptr->h_value - hptr->h_ofile->o_header.tsize;
break;
case T_BSS:
value = hptr->h_value
- (hptr->h_ofile->o_header.tsize
+ hptr->h_ofile->o_header.dsize);
break;
default:
if (vflag > 1)
printf("DoSymbols: No adjustment made for symbol: %s (%s) = %X\n", string, ofile->o_name, hptr->h_value);
}
}
}
// If *not* a global/external, use the info from passed in object
else
tsegoffset = tsoSave, dsegoffset = dsoSave, bsegoffset = bsoSave;
// Process and update the value dependent on whether the symbol is a
// debug symbol or not
// N.B.: Debug symbols are currently not supported
if (type & 0xF0000000)
{
// DEBUG SYMBOL
switch (type & 0xFF000000)
{
case 0x64000000: // Primary source file path and/or name
case 0x84000000: // Included source file path and/or name
case 0x44000000: // Text line number
value = tbase + tsegoffset + value;
break;
case 0x46000000: // Data line number (Not used by GCC/rmac)
value = dbase + dsegoffset + value;
break;
case 0x48000000: // BSS line number (Not used by GCC/rmac)
value = bbase + bsegoffset + value;
default:
// All other debug symbols don't need to be relocated
// XXX Not true, but matches ALN behavior.
break;
}
PutLong(symptr + 8, value);
}
else
{
// NON-DEBUG SYMBOL
// Now make modifications to the symbol value, local or global,
// based on the segment sizes of the object file currently being
// processed.
switch (type & T_SEG)
{
case T_ABS:
break;
case T_TEXT:
value = tbase + tsegoffset + value;
PutLong(symptr + 8, value);
break;
case T_DATA:
if (type & T_GLBL)
value = dbase + dsegoffset + value;
else
value = dbase + dsegoffset + (value
- ofile->o_header.tsize);
PutLong(symptr + 8, value);
break;
case T_BSS:
if (type & T_GLBL)
value = bbase + bsegoffset + value;
else
value = bbase + bsegoffset
+ (value - (ofile->o_header.tsize
+ ofile->o_header.dsize));
PutLong(symptr + 8, value);
break;
default:
break;
}
}
// Add to output symbol table if global/extern, or local flag is set
if (isglobal(type) || lflag)
{
if (vflag > 1)
printf("DoSymbols: Adding symbol: %s (%s) to OST...\n", string, ofile->o_name);
index = OSTAdd(index ? string : NULL, type, value);
if (index == -1)
{
printf("DoSymbols(): Failed to add symbol '%s' to OST!\n", string);
return 1;
}
}
}
return 0;
}
//
// Free up hash memory
//
void FreeHashes(void)
{
int i;
for(i=0; i<NBUCKETS; i++)
{
struct HREC * hptr = htable[i];
while (hptr)
{
struct HREC * htemp = hptr->h_next;
free(hptr);
hptr = htemp;
}
}
}
//
// Add all global and external symbols to the output symbol table
// [This is confusing--is it adding globals or locals? common == local!
// but then again, we see this in the header:
// #define T_COMMON (T_GLOBAL | T_EXTERN) but that could be just bullshit.]
//
// Common symbols have a different number in the "value" field of the symbol
// table (!0) than purely external symbols do (0). So you have to look at the
// type (T_GLBL) *and* the value to determine if it's a common symbol.
//
long DoCommon(void)
{
struct HREC * hptr;
int i;
for(i=0; i<NBUCKETS; i++)
{
for(hptr=htable[i]; hptr!=NULL; hptr=hptr->h_next)
{
//NO! if (iscommon(hptr->h_type))
if (isglobal(hptr->h_type))// || isextern(hptr->h_type))
{
// Skip if in *.a file... (does nothing)
//if (hptr->h_ofile->isArchiveFile)
// continue;
//Is this true? Couldn't an absolute be exported???
if (hptr->h_type == (T_GLBL | T_ABS))
hptr->h_type = T_ABS; // Absolutes *can't* be externals
if (OSTAdd(hptr->h_sym, hptr->h_type, hptr->h_value) == -1)
return -1;
}
}
}
return 0;
}
//
// Add a symbol's name, type, and value to the OST.
// Returns the index of the symbol in OST, or -1 for error.
//
int OSTAdd(char * name, int type, long value)
{
int ost_offset_p = 0, ost_offset_e; // OST table offsets for position calcs
int ostresult; // OST index result
int slen; // String length, including terminator
// If this is a debug symbol and the include debug symbol flag (-g) is not
// set then do nothing
if ((type & 0xF0000000) && !gflag)
{
// Do nothing
return 0;
}
if (!name || !name[0])
slen = 0;
else
slen = strlen(name) + 1;
// Get symbol index in OST, if any (-1 if not found)
ostresult = slen ? OSTLookup(name) : -1;
// If the symbol is in the output symbol table and the bflag is set
// (don't remove multiply defined locals) and this is not an
// external/global symbol, or the gflag (output debug symbols) is
// set and this a debug symbol, *** OR *** the symbol is not in the
// output symbol table then add it.
if ((ostresult != -1) && !(bflag && !(type & 0x01000000))
&& !(gflag && (type & 0xF0000000)))
{
return ostresult;
}
// If the OST has not been initialised, or more space is needed, then
// allocate it.
if ((ost_index + 1) > ost_size)
{
if (ost_size == 0)
ost_size = OST_SIZE_INIT;
ost_size *= 2;
ost = realloc(ost, ost_size * sizeof(ost[0]));
if (ost == NULL)
{
printf("OST memory allocation error.\n");
return -1;
}
}
if (slen)
{
ost_offset_p = (oststr_ptr - oststr);
ost_offset_e = (oststr_end - oststr);
// If the OST data has been exhausted, allocate another chunk.
if (((oststr_ptr + slen + 4) > oststr_end))
{
// string length + terminating NULL + uint32_t (terminal long)
if ((oststr_ptr + (slen + 1 + 4)) > oststr_end)
{
oststr = realloc(oststr, ost_offset_e + OST_BLOCK);
if (oststr == NULL)
{
printf("OSTSTR memory reallocation error.\n");
return -1;
}
oststr_ptr = oststr + ost_offset_p;
oststr_end = (oststr + ost_offset_e) + OST_BLOCK;
// On the first alloc, reserve space for the string table
// size field.
if (ost_offset_e == 0)
oststr_ptr += 4;
}
}
strcpy(oststr_ptr, name); // Put symbol name in string table
oststr_ptr += slen;
oststr_ptr[-1] = '\0'; // Add null terminating character
PutLong(oststr_ptr, 0x00000000); // Null terminating long
PutLong(oststr, (oststr_ptr - oststr)); // Update size of string table
}
ostresult = ost_index++;
ost[ostresult].s_idx = ost_offset_p;
ost[ostresult].s_type = type;
ost[ostresult].s_value = value;
if (vflag > 1)
printf("OSTAdd: (%s), type=$%08X, val=$%08lX\n",
slen ? name : "", type, value);
return ost_index;
}
//
// Return the index of a symbol in the output symbol table
// N.B.: This is a 1-based index! (though there's no real reason for it to be)
//
int OSTLookup(char * sym)
{
int i;
for(i=0; i<ost_index; i++)
{
if (ost[i].s_idx && (strcmp(oststr + ost[i].s_idx, sym) == 0))
return i + 1;
}
return -1;
}
//
// Add unresolved externs to the output symbol table
// N.B.: Only adds unresolved symbols *if* they're not already in the OST
//
int DoUnresolved(void)
{
struct HREC * hptr = unresolved;
// Add to OST while unresolved list is valid
while (hptr != NULL)
{
if (OSTAdd(hptr->h_sym, T_GLBL, 0L) == -1)
return 1;
if (vflag > 1)
printf("DoUnresolved(): '%s' (%s:$%08X) in OST\n", hptr->h_sym, hptr->h_ofile->o_name, hptr->h_type);
struct HREC * htemp = hptr->h_next;
free(hptr);
hptr = htemp;
}
unresolved = NULL;
return 0;
}
//
// Update object file TEXT and DATA segments based on relocation records. Take
// in an OFILE header and flag (T_TEXT, T_DATA) to process. Return (0) is
// successful or non-zero (1) if failed.
//
int RelocateSegment(struct OFILE * ofile, int flag)
{
char * symtab; // Start of symbol table
char * symbols; // Start of symbols
char * sptr; // Start of segment data
char * rptr; // Start of segment relocation records
unsigned symidx; // Offset to symbol
unsigned addr; // Relocation address
unsigned rflg; // Relocation flags
unsigned olddata; // Old segment data at reloc address
unsigned newdata = 0; // New segment data at reloc address
unsigned pad; // Temporary to calculate phrase padding
int i; // Iterator
char sym[SYMLEN]; // String for symbol name/hash search
int ssidx; // Segment size table index
unsigned glblreloc; // Global relocation flag
unsigned absreloc; // Absolute relocation flag
unsigned relreloc; // Relative relocation flag
unsigned wordreloc; // Relocate word only flag
unsigned opreloc; // Relocate OP data address only flag
unsigned swcond; // Switch statement condition
unsigned relocsize; // Relocation record size
unsigned saveBits; // OP data leftover bits save
unsigned saveBits2;
// If there is no TEXT relocation data for the selected object file segment
// then update the COF TEXT segment offset allowing for the phrase padding
if ((flag == T_TEXT) && !ofile->o_header.absrel.reloc.tsize)
{
// SCPCD : we should not increment the textoffset before the end of processing the object file, else data section will point to wrong textoffset
return 0;
}
// If there is no DATA relocation data for the selected object file segment
// then update the COF DATA and BSS segment offsets allowing for the phrase
// padding
if ((flag == T_DATA) && !ofile->o_header.absrel.reloc.dsize)
{
// SCPCD : the T_DATA is the last section of the file, we can now increment the textoffset, dataoffset and bssoffset
// TEXT segment size plus padding
pad = ((ofile->o_header.tsize + secalign) & ~secalign);
textoffset += (ofile->o_header.tsize + (pad - ofile->o_header.tsize));
if (vflag > 1)
printf("RelocateSegment(%s, TEXT) : No relocation data\n", ofile->o_name);
// DATA segment size plus padding
pad = ((ofile->o_header.dsize + secalign) & ~secalign);
dataoffset += (ofile->o_header.dsize + (pad - ofile->o_header.dsize));
// BSS segment size plus padding
pad = ((ofile->o_header.bsize + secalign) & ~secalign);
bssoffset += (ofile->o_header.bsize + (pad - ofile->o_header.bsize));
if (vflag > 1)
printf("RelocateSegment(%s, DATA) : No relocation data\n", ofile->o_name);
return 0;
}
if (vflag > 1)
printf("RelocateSegment(%s, %s) : Processing Relocation Data\n",
ofile->o_name, flag == T_DATA ? "DATA" : "TEXT");
// Obtain pointer to start of symbol table
symtab = (ofile->o_image + 32 + ofile->o_header.tsize
+ ofile->o_header.dsize
+ ofile->o_header.absrel.reloc.tsize
+ ofile->o_header.absrel.reloc.dsize);
// Obtain pointer to start of symbols
symbols = symtab + ofile->o_header.ssize;
// Obtain pointer to start of TEXT segment
sptr = ofile->o_image + 32;
// Obtain pointer to start of TEXT relocation records
rptr = sptr + (ofile->o_header.tsize + ofile->o_header.dsize);
relocsize = ofile->o_header.absrel.reloc.tsize;
if (vflag)
printf("RELOCSIZE :: %d Records = %d\n", relocsize, relocsize / 8);
// Update pointers if DATA relocation records are being processed
if (flag == T_DATA)
{
sptr += ofile->o_header.tsize; // Start of DATA segment
rptr += ofile->o_header.absrel.reloc.tsize; // Start of DATA relocation records
relocsize = ofile->o_header.absrel.reloc.dsize;
}
// Process each relocation record for the TEXT segment
for(i=0; i<(int)relocsize; i+=8)
{
// Obtain both the relocation address and the relocation flags from the
// object file image
addr = GetLong(rptr);
rflg = GetLong(rptr + 4);
glblreloc = (rflg & BSDREL_GLOBAL ? 1 : 0);
absreloc = (rflg & BSDREL_ABS ? 1 : 0);
relreloc = (rflg & BSDREL_PCREL ? 1 : 0);
wordreloc = (rflg & BSDREL_WORD ? 1 : 0);
opreloc = (rflg & BSDREL_OP ? 1 : 0);
// Additional processing required for global relocations
if (glblreloc)
{
// Obtain the string table index for the relocation symbol, look
// for it in the globals hash table to obtain information on that
// symbol. For the hash calculation to work correctly it must be
// placed in a 'clean' string before looking it up.
symidx = GetLong(symtab + (BSDREL_SYMIDX(rflg) * 12));
memset(sym, 0, SYMLEN);
strcpy(sym, symbols + symidx);
olddata = newdata = 0; // Initialise old and new segment data
ssidx = OSTLookup(sym);
newdata = ost[ssidx - 1].s_value;
}
// Obtain the existing long word (or word) segment data and flip words
// if the relocation flags indicate it relates to a RISC MOVEI
// instruction
olddata = (wordreloc ? GetWord(sptr + addr) : GetLong(sptr + addr));
// If it's a OP QUAD relocation, get the data out of the DATA bits.
// Note that because we can't afford to lose the bottom 3 bits of the
// relocation record, we lose 3 off the top--which means the maximum
// this can store is $1FFFF8 (vs. $FFFFF8).
if (opreloc)
{
saveBits2 = (GetLong(sptr + addr + 8) & 0xE0000000) >> 8; // Upper 3 of data addr
saveBits = olddata & 0x7FF;
olddata = (olddata & 0xFFFFF800) >> 11;
olddata |= saveBits2; // Restore upper 3 bits of data addr
}
if (rflg & BSDREL_MOVEI)
olddata = _SWAPWORD(olddata);
// Process record dependant on segment it relates to; TEXT, DATA or
// BSS. Construct a new relocated segment long word based on the
// required segment base address, the segment data offset in the
// resulting COF file and the offsets from the incoming object file.
swcond = (rflg & BSDREL_SEGMASK);
if (!glblreloc)
{
switch (swcond)
{
case BSDREL_SEG_ABS:
break;
case BSDREL_SEG_TEXT:
// SCPCD : the symbol point to a text segment, we should use the textoffset
newdata = tbase + textoffset + olddata;
break;
case BSDREL_SEG_DATA:
newdata = dbase + dataoffset
+ (olddata - ofile->o_header.tsize);
break;
case BSDREL_SEG_BSS:
newdata = bbase + bssoffset
+ (olddata - (ofile->o_header.tsize
+ ofile->o_header.dsize));
break;
}
}
else
{
if (!relreloc)
newdata += olddata;
}
// Set absolute (long) or relative (word) address of symbol
if (absreloc)
{
// Flip the new long word segment data if the relocation record
// indicated a RISC MOVEI instruction and place the resulting data
// back in the COF segment
if (rflg & BSDREL_MOVEI)
newdata = _SWAPWORD(newdata);
if (wordreloc)
PutWord(sptr + addr, newdata);
else if (opreloc)
{
if (vflag > 1)
printf("OP reloc: oldata=$%X, newdata=$%X\n", olddata, newdata);
newdata = ((newdata & 0x00FFFFF8) << 8) | saveBits;
PutLong(sptr + addr, newdata);
// Strip out extraneous data hitchhikers from 2nd phrase...
newdata = GetLong(sptr + addr + 8) & 0x007FFFFF;
PutLong(sptr + addr + 8, newdata);
}
else
PutLong(sptr + addr, newdata);
}
else if (relreloc)
{
PutWord(sptr + addr, newdata - tbase - addr - ofile->o_tbase);
}
// Shamus: Let's output some info to aid in debugging this crap
if (vflag > 1)
{
char ssiString[128];
ssiString[0] = 0;
if (glblreloc)
sprintf(ssiString, " [ssi:%i]", ssidx);
printf("RelocateSegment($%08X): %s, $%08X: $%08X => $%08X%s\n", rflg, (glblreloc ? sym : "(LOCAL)"), addr, olddata, GetLong(sptr + addr), ssiString);
}
rptr += 8; // Point to the next relocation record
}
// Update the COF segment offset allowing for the phrase padding.
// SCPCD : we should not increment the textoffset before the end of processing the object file, else data section will point to wrong textoffset
if (flag == T_DATA)
{
// TEXT segment plus padding
pad = ((ofile->o_header.tsize + secalign) & ~secalign);
textoffset += (ofile->o_header.tsize + (pad - ofile->o_header.tsize));
// DATA segment plus padding
pad = ((ofile->o_header.dsize + secalign) & ~secalign);
dataoffset += (ofile->o_header.dsize + (pad - ofile->o_header.dsize));
// BSS segment plus padding
pad = ((ofile->o_header.bsize + secalign) & ~secalign);
bssoffset += (ofile->o_header.bsize + (pad - ofile->o_header.bsize));
}
// Return value, should always be zero
return 0;
}
//
// Add a path character to the end of string 's' if it doesn't already end with
// one. The last occurrance of '/' or '\' in the string is assumed to be the
// path character.
//
// This is fucking shit. We know what the path delimiter is, its FUCKING
// DEFINED IN THE FUCKING HEADER. FOR FUCKS SAKE. AND YES, HOPE TO GOD THERE'S
// ENOUGH SPACE IN THE PASSED IN BUFFER TO HOLD THE EXTRA CHARACTER!
//
void AppendPathDelimiter(char * s)
{
#if 0
// And hope to God that there's enough space in the buffer...
char pathchar = 0;
while (*s)
{
if (*s == '/' || *s == '\\')
pathchar = *s;
s++;
}
s--;
if (*s == pathchar)
return;
*++s = pathchar;
*++s = 0;
#else
int length = strlen(s);
if (s[length - 1] != PATH_DELIMITER)
{
s[length] = PATH_DELIMITER;
s[length + 1] = 0; // BUFFER OVERFLOW!!!! FFFFFFFFUUUUUUUUUUUU
}
#endif
}
//
// Try to open "name", "name.o", "${libdir}name", "${libdir}name.o". Return the
// handle of the file successfully opened. p_name is updated to point to a
// malloc()'ed string which is the name which actually got opened. p_name will
// return unchanged if the file can't be found.
//
int TryOpenFile(char ** p_name)
{
char * name = *p_name;
// Note that libdir will be an empty string if there is none specified
char * tmpbuf = malloc(strlen(name) + strlen(libdir) + 3);
if (tmpbuf == NULL)
{
printf("TryOpenFile() : out of memory\n");
return -1;
}
strcpy(tmpbuf, name);
int hasdot = (strrchr(tmpbuf, '.') > strrchr(tmpbuf, PATH_DELIMITER));
// Try to open file as passed first
int fd = open(tmpbuf, _OPEN_FLAGS);
if (fd >= 0)
goto ok;
if (!hasdot)
{
// Try to open file with '.o' added
strcat(tmpbuf, ".o");
fd = open(tmpbuf, _OPEN_FLAGS);
if (fd >= 0)
goto ok;
}
// Try the libdir only if the name isn't already anchored
// Shamus: WTH, this makes no sense... Why the ':'? Is this a Macintosh??
// if (*name != '/' && *name != '\\' && !strchr(name, ':'))
if ((*name != PATH_DELIMITER) && (strchr(name, ':') == NULL))
{
strcpy(tmpbuf, libdir);
// Add a trailing path char if there isn't one already
AppendPathDelimiter(tmpbuf);
strcat(tmpbuf, name);
if ((fd = open(tmpbuf, _OPEN_FLAGS)) >= 0)
goto ok;
if (!hasdot)
{
strcat(tmpbuf, ".o");
if ((fd = open(tmpbuf, _OPEN_FLAGS)) >= 0)
goto ok;
}
}
// Couldn't open file at all
return -1;
// There are worse things... :-P
ok:
tmpbuf = realloc(tmpbuf, strlen(tmpbuf) + 1);
if (tmpbuf == NULL)
{
printf("TryOpenFile() : out of memory\n");
return -1;
}
*p_name = tmpbuf;
return fd;
}
//
// What it says on the tin
//
void WriteARName(struct OFILE * p)
{
int flag = *(p->o_arname);
printf("%s%s%s", (flag ? (char *)(p->o_arname) : ""), (flag ? ":" : ""), p->o_name);
}
//
// Collect file names and handles in a buffer so there is less disk activity.
// Call DoFile with flag FALSE for normal object files and archives.
// Call it with flag TRUE and a symbol name for include files (-i).
//
int DoFile(char * fname, int incFlag, char * sym)
{
// Verbose information
if (vflag)
{
printf("DoFile() : `%s' %s", fname, incFlag ? "INCLUDE" : "NORMAL");
if (incFlag)
printf(" symbol %s", sym);
printf("\n");
}
// Reached maximum file handles
if (hd == NHANDLES)
{
if (ProcessFiles())
return 1;
}
// Attempt to open input file
int fd = TryOpenFile(&fname);
if (fd < 0)
{
printf("Cannot find input module %s\n", fname);
return 1;
}
// The file is open; save its info in the handle and name arrays
handle[hd] = fd;
name[hd] = fname; // This is the name from TryOpenFile()
hflag[hd] = incFlag;
// Include files
if (incFlag)
{
int temp = strlen(sym); // Get symbol length
// 100 chars is max length of a symbol
if (temp > 99)
{
sym[99] = '\0';
temp = 99;
}
// Malloc enough space for two symbols, then build the second one.
// Second one may be one character longer than first
if ((hsym1[hd] = malloc((long)temp + 1)) == NULL
|| (hsym2[hd] = malloc((long)temp + 2)) == NULL)
{
printf("DoFile() : out of memory for include-file symbols\n");
return 1;
}
strcpy(hsym1[hd], sym);
strcpy(hsym2[hd], sym);
if (temp == 99)
{
if (sym[99] == 'x')
{
printf("Last char of %s is already 'x': choose another name\n", sym);
return 1;
}
hsym2[hd][99] = 'x';
}
else
{
hsym2[hd][temp] = 'x';
hsym2[hd][temp+1] = '\0';
}
}
// Increment next handle index
hd++;
// No problems
return 0;
}
//
// Pad TEXT or DATA segment to the requested boundary
//
int PadSegment(FILE * fd, long segsize, int value)
{
int i;
char padarray[32];
char * padptr;
// Determine the number of padding bytes that are needed
long padsize = (segsize + secalign) & ~secalign;
padsize = padsize - segsize;
// Fill pad array if padding is required
if (padsize)
{
padptr = padarray;
for(i=0; i<16; i++)
{
PutWord(padptr, value);
padptr += 2;
}
symoffset += padsize;
// Write padding bytes
if (fwrite(padarray, padsize, 1, fd) != 1)
return 1;
}
return 0;
}
//
// Write the output file
//
int WriteOutputFile(struct OHEADER * header)
{
unsigned osize; // Object segment size
struct OFILE * otemp; // Object file pointer
int i, j; // Iterators
char himage[0x168]; // Header image (COF = 0xA8)
uint32_t tsoff, dsoff, bsoff; // Segment offset values
short abstype; // ABS symbol type
char symbol[14]; // raw symbol record
symoffset = 0; // Initialise symbol offset
// Add correct output extension if none
if (strchr(ofile, '.') == NULL)
{
if (aflag && cflag)
strcat(ofile, ".cof"); // COF files (a type of abs)
else if (aflag && !cflag)
strcat(ofile, ".abs"); // ABS files
else
strcat(ofile, ".o"); // Object files (partial linking, etc)
}
FILE * fd = fopen(ofile, "wb"); // Attempt to open output file
if (!fd)
{
printf("Can't open output file %s\n", ofile);
return 1;
}
// Build the output file header
// Absolute (COF) header
if (cflag)
{
tsoff = dsoff = bsoff = 0xA8; // Initialises segment offsets
// Process each object file segment size to obtain a cumulative segment
// size for both the TEXT and DATA segments
for(otemp=olist; otemp!=NULL; otemp=otemp->o_next)
{
dsoff += otemp->segSize[TEXT];
bsoff += otemp->segSize[TEXT] + otemp->segSize[DATA];
}
// Currently this only builds a COF absolute file. Conditionals and
// additional code will need to be added for ABS and partial linking.
// Build the COF_HDR
PutWord(himage + 0, 0x0150 ); // Magic Number (0x0150)
PutWord(himage + 2, 0x0003 ); // Sections Number (3)
PutLong(himage + 4, 0x00000000 ); // Date (0L)
PutLong(himage + 8, dsoff + header->dsize); // Offset to Symbols Section
PutLong(himage + 12, ost_index); // Number of Symbols
PutWord(himage + 16, 0x001C ); // Size of RUN_HDR (0x1C)
PutWord(himage + 18, 0x0003 ); // Executable Flags (3)
// Build the RUN_HDR
PutLong(himage + 20, 0x00000107 ); // Magic/vstamp
PutLong(himage + 24, header->tsize ); // TEXT size in bytes
PutLong(himage + 28, header->dsize ); // DATA size in bytes
PutLong(himage + 32, header->bsize ); // BSS size in bytes
PutLong(himage + 36, tbase ); // Start of executable, normally @TEXT
PutLong(himage + 40, tbase ); // @TEXT
PutLong(himage + 44, dbase ); // @DATA
// Build the TEXT SEC_HDR
PutLong(himage + 48, 0x2E746578 );
PutLong(himage + 52, 0x74000000 ); // ".text"
PutLong(himage + 56, tbase ); // TEXT START
PutLong(himage + 60, tbase ); // TEXT START
PutLong(himage + 64, header->tsize ); // TEXT size in bytes
PutLong(himage + 68, tsoff ); // Offset to section data in file
PutLong(himage + 72, 0x00000000 ); // Offset to section reloc in file (0L)
PutLong(himage + 76, 0x00000000 ); // Offset to debug lines structures (0L)
PutLong(himage + 80, 0x00000000 ); // Nreloc/nlnno (0L)
PutLong(himage + 84, 0x00000020 ); // SEC_FLAGS: STYP_TEXT
// Build the DATA SEC_HDR
PutLong(himage + 88, 0x2E646174 );
PutLong(himage + 92, 0x61000000 ); // ".data"
PutLong(himage + 96, dbase ); // DATA START
PutLong(himage + 100, dbase ); // DATA START
PutLong(himage + 104, header->dsize ); // DATA size in bytes
PutLong(himage + 108, dsoff ); // Offset to section data in file
PutLong(himage + 112, 0x00000000 ); // Offset to section reloc in file (0L)
PutLong(himage + 116, 0x00000000 ); // Offset to debugging lines structures (0L)
PutLong(himage + 120, 0x00000000 ); // Nreloc/nlnno (0L)
PutLong(himage + 124, 0x00000040 ); // SEC_FLAGS: STYP_DATA
// Build the BSS SEC_HDR
PutLong(himage + 128, 0x2E627373 );
PutLong(himage + 132, 0x00000000 ); // ".bss"
PutLong(himage + 136, bbase ); // BSS START
PutLong(himage + 140, bbase ); // BSS START
PutLong(himage + 144, header->bsize ); // BSS size in bytes
PutLong(himage + 148, bsoff ); // Offset to section data in file
PutLong(himage + 152, 0x00000000 ); // Offset to section reloc in file (0L)
PutLong(himage + 156, 0x00000000 ); // Offset to debugging lines structures (0L)
PutLong(himage + 160, 0x00000000 ); // Nreloc/nlnno (0L)
PutLong(himage + 164, 0x00000080 ); // SEC_FLAGS: STYP_BSS
symoffset = 168; // Update symbol offset
}
// Absolute (ABS) header
else
{
// Build the ABS header
PutWord(himage + 0, 0x601B ); // Magic Number (0x601B)
PutLong(himage + 2, header->tsize ); // TEXT segment size
PutLong(himage + 6, header->dsize ); // DATA segment size
PutLong(himage + 10, header->bsize ); // BSS segment size
PutLong(himage + 14, ost_index * 14 ); // Symbol table size (?)
PutLong(himage + 18, 0x00000000 ); //
PutLong(himage + 22, tbase ); // TEXT base address
PutWord(himage + 26, 0xFFFF ); // Flags (?)
PutLong(himage + 28, dbase ); // DATA base address
PutLong(himage + 32, bbase ); // BSS base address
symoffset = 36; // Update symbol offset
}
// Write the header, but not if noheaderflag
if (!noheaderflag)
{
// Absolute (ABS) header
if (!cflag)
{
if (fwrite(himage, 36, 1, fd) != 1)
goto werror;
}
// Absolute (COF) header
else
{
if (fwrite(himage, 168, 1, fd) != 1)
goto werror;
}
}
// Write the TEXT segment of each object file
for(otemp=olist; otemp!=NULL; otemp=otemp->o_next)
{
osize = otemp->o_header.tsize;
// Write only if segment has size
if (osize)
{
if (vflag > 1)
printf("Writing TEXT Segment of %s\n", otemp->o_name);
if (fwrite(otemp->o_image + 32, osize, 1, fd) != 1)
goto werror;
// Pad to required alignment boundary
if (PadSegment(fd, osize, 0x0000))
goto werror;
symoffset += osize;
}
}
// Write the DATA segment of each object file
for(otemp=olist; otemp!=NULL; otemp=otemp->o_next)
{
osize = otemp->o_header.dsize;
// Write only if the segment has size
if (osize)
{
if (vflag > 1)
printf("Writing DATA Segment of %s\n", otemp->o_name);
if (fwrite((otemp->o_image + 32 + otemp->o_header.tsize), osize, 1, fd) != 1)
goto werror;
// Pad to required alignment boundary
if (PadSegment(fd, osize, 0))
goto werror;
symoffset += osize;
}
}
//wha? if (!noheaderflag)
// This isn't quite right, but it's closer...
// (the l and s flags tell the linker to output local & global symbols
// in the symbol table, so it seems there's some other thing that's a
// default set somewhere. Not 100% sure. Setting -s kills -l, BTW...)
if (lflag || sflag)
{
// Write the symbols table and string table
// Absolute (COF) symbol/string table
if (cflag)
{
if (header->ssize)
{
for (i = 0; i < ost_index; i++)
{
PutLong(symbol, ost[i].s_idx);
PutLong(symbol + 4, ost[i].s_type);
PutLong(symbol + 8, ost[i].s_value);
if (fwrite(symbol, 12, 1, fd) != 1)
goto werror;
}
if (fwrite(oststr, (oststr_ptr - oststr), 1, fd) != 1)
goto werror;
}
}
// Absolute (ABS) symbol/string table
else
{
// The symbol and string table have been created as part of the
// DoSymbols() function and the output symbol and string tables are
// in COF format. For an ABS file we need to process through this
// to create the 14 character long combined symbol and string
// table. Format of symbol table in ABS: AAAAAAAATTVVVV, where
// (A)=STRING, (T)=TYPE & (V)=VALUE
for(i=0; i<ost_index; i++)
{
memset(symbol, 0, 14); // Initialise symbol record
abstype = 0; // Initialise ABS symbol type
// Skip debug symbols
if (ost[i].s_type & 0xF0000000)
continue;
// Get symbol string (maximum 8 chars)
strncpy(symbol, oststr + ost[i].s_idx, 8);
// Modify to ABS symbol type
switch (ost[i].s_type)
{
case 0x02000000: abstype = (short)ABST_DEFINED; break;
case 0x04000000: abstype = (short)ABST_DEFINED | ABST_TEXT; break;
case 0x05000000: abstype = (short)ABST_DEFINED | ABST_GLOBAL | ABST_TEXT; break;
case 0x06000000: abstype = (short)ABST_DEFINED | ABST_DATA; break;
case 0x07000000: abstype = (short)ABST_DEFINED | ABST_GLOBAL | ABST_DATA; break;
case 0x08000000: abstype = (short)ABST_DEFINED | ABST_BSS; break;
case 0x09000000: abstype = (short)ABST_DEFINED | ABST_GLOBAL | ABST_BSS; break;
default:
printf("warning (WriteOutputFile): ABS, cannot determine symbol type ($%08X) [%s]\n", ost[i].s_type, symbol);
// type = 0;
break;
}
PutWord(symbol + 8, abstype); // Write back new ABS type
PutLong(symbol + 10, ost[i].s_value); // Write back value
// Write symbol record
if (fwrite(symbol, 14, 1, fd) != 1)
goto werror;
}
}
}
if (fclose(fd))
{
printf("Close error on output file %s\n", ofile);
return 1;
}
return 0;
werror:
printf("Write error on output file %s\n", ofile);
fclose(fd); // Try to close output file anyway
return 1;
}
//
// Display the symbol load map
//
int ShowSymbolLoadMap(struct OHEADER * header)
{
unsigned i, o; // Inner and outer loop iterators
unsigned c; // Column number
unsigned index; // Symbol string index
unsigned type; // Symbol type
unsigned value; // Symbol value
char * symbol; // Symbol string value
if (ost_index == 0)
return 0; // Return if no symbols to map
printf("LOAD MAP\n\n");
// Outer loop for each of the symbol areas to map out;
// 0 = NON-RELOCATABLE SYMBOLS
// 1 = TEXT-SEGMENT RELOCATABLE SYMBOLS
// 2 = DATA-SEGMENT RELOCATABLE SYMBOLS
// 3 = BSS-SEGMENT RELOCATABLE SYMBOLS
for(o=0; o<4; o++)
{
// Display the correct map header for the symbols being processed
switch (o)
{
case 0: printf("NON-RELOCATABLE SYMBOLS\n\n"); break;
case 1: printf("TEXT-SEGMENT RELOCATABLE SYMBOLS\n\n"); break;
case 2: printf("DATA-SEGMENT RELOCATABLE SYMBOLS\n\n"); break;
case 3: printf("BSS-SEGMENT RELOCATABLE SYMBOLS\n\n"); break;
}
c = 0; // Initialise column number
// Inner loop to process each record in the symbol table
for(i=0; i<(unsigned)ost_index; i++)
{
index = ost[i].s_idx; // Get symbol string index
type = ost[i].s_type; // Get symbol type
value = ost[i].s_value; // Get symbol value
symbol = index ? oststr + index : ""; // Get symbol string
// Display only three columns
if (c == 3)
{
printf("\n");
c = 0;
}
// If local symbols not included and the type is local then go to
// next symbol record
if (!lflag & !(type & 0x01000000))
continue;
// Output each symbol to the display, dependant on type
switch (o)
{
case 0:
// Non-relocatable symbols
if (type == 0x02000000 || type == 0x03000000)
{
printf("%-8s %c %08X ", symbol, (type & 0x01000000) ? 'G' : 'L', value);
c++;
}
break;
case 1:
// TEXT segment relocatable symbols
if (type == 0x04000000 || type == 0x05000000)
{
printf("%-8s %c %08X ", symbol, (type & 0x01000000) ? 'G' : 'L', value);
c++;
}
break;
case 2:
// DATA segment relocatble symbols
if (type == 0x06000000 || type == 0x07000000)
{
printf("%-8s %c %08X ", symbol, (type & 0x01000000) ? 'G' : 'L', value);
c++;
}
break;
case 3:
// BSS segment relocatable symbols
if (type == 0x08000000 || type == 0x09000000)
{
printf("%-8s %c %08X ", symbol, (type & 0x01000000) ? 'G' : 'L', value);
c++;
}
break;
}
}
printf("\n\n");
}
return 0;
}
//
// Stuff the (long) value of a string into the value argument. RETURNS TRUE if
// the string doesn't parse. Parses only as a hexadecimal string.
//
int GetHexValue(char * string, int * value)
{
*value = 0;
while (isxdigit(*string))
{
if (isdigit(*string))
{
*value = (*value << 4) + (*string++ - '0');
}
else
{
if (isupper(*string))
*string = tolower(*string);
*value = (*value << 4) + ((*string++ - 'a') + 10);
}
}
if (*string != '\0')
{
printf("Invalid hexadecimal value");
return 1;
}
return 0;
}
//
// Create one big .o file from the images already in memory, returning a
// pointer to an OHEADER. Note that the oheader is just the header for the
// output (plus some other information). The text, data, and fixups are all
// still in the ofile images hanging off the global 'olist'.
//
struct OHEADER * MakeOutputObject()
{
unsigned tptr, dptr, bptr; // Bases in runtime model
int ret = 0; // Return value
struct OHEADER * header; // Output header pointer
// Initialize cumulative segment sizes
textsize = datasize = bsssize = 0;
// For each object file, accumulate the sizes of the segments but remove
// those object files which are unused
struct OFILE * oprev = NULL; // Init previous obj file list ptr
struct OFILE * otemp = olist; // Set temp pointer to object file list
while (otemp != NULL)
{
// If the object is unused, discard it...
if ((otemp->o_flags & O_USED) == 0)
{
if (wflag)
{
printf("Unused object file ");
WriteARName(otemp);
printf(" discarded.\n");
}
// Drop the entry from the linked list
if (oprev == NULL)
olist = otemp->o_next;
else
oprev->o_next = otemp->o_next;
// Free the object entry if it's not an archive file
if (!otemp->isArchiveFile)
free(otemp->o_image);
}
else
{
// Save accumulated addresses in the object
otemp->segBase[TEXT] = textsize;
otemp->segBase[DATA] = datasize;
otemp->segBase[BSS] = bsssize;
// Increment total of segment sizes ensuring requested alignment
textsize += (otemp->o_header.tsize + secalign) & ~secalign;
datasize += (otemp->o_header.dsize + secalign) & ~secalign;
bsssize += (otemp->o_header.bsize + secalign) & ~secalign;
oprev = otemp;
}
// Go to next object file list pointer
otemp = otemp->o_next;
}
// Update base addresses and inject the symbols _TEXT_E, _DATA_E and _BSS_E
// into the OST
tbase = tval;
if (!dval)
{
// DATA follows TEXT
dbase = tval + textsize;
if (!bval)
// BSS follows DATA
bbase = tval + textsize + datasize;
else
// BSS is independent of DATA
bbase = bval;
}
else
{
// DATA is independent of TEXT
dbase = dval;
if (!bval)
{
if (btype == -2)
// BSS follows TEXT
bbase = tval + textsize;
else
// BSS follows DATA
bbase = dval + datasize;
}
else
{
// BSS is independent of DATA
bbase = bval;
}
}
// Inject segment end labels, for C compilers that expect this shite
OSTAdd("_TEXT_E", 0x05000000, tbase + textsize);
OSTAdd("_DATA_E", 0x07000000, dbase + datasize);
OSTAdd("_BSS_E", 0x09000000, bbase + bsssize);
// Place each unresolved symbol in the output symbol table
// N.B.: It only gets here to do this if user passes in -u flag
// [Only used here, once]
if (DoUnresolved())
return NULL;
// Initialise base addresses
tptr = dptr = bptr = 0;
// For each file, relocate its symbols and add them to the output symbol
// table
otemp = olist;
oprev = NULL;
while (otemp != NULL)
{
otemp->o_tbase = tptr;
otemp->o_dbase = dptr;
otemp->o_bbase = bptr;
tptr += (otemp->o_header.tsize + secalign) & ~secalign;
dptr += (otemp->o_header.dsize + secalign) & ~secalign;
bptr += (otemp->o_header.bsize + secalign) & ~secalign;
// For each symbol, (conditionally) add it to the ost
// For ARCHIVE markers, this adds the symbol for the file & returns
// (Shamus: N.B. it does no such thing ATM)
// [Only used here, once]
if (DoSymbols(otemp))
return NULL;
oprev = otemp;
otemp = otemp->o_next;
}
// Places all the externs, globals etc into the output symbol table
if (DoCommon() == -1)
return NULL;
// Create a new output file header
header = new_oheader();
if (header == NULL)
{
printf("MakeOutputObject: out of memory!\n");
return NULL;
}
// Fill in the output header. Does not match the actual output but values
// used as reference
header->magic = 0x0150; // COF magic number
header->tsize = textsize; // TEXT segment size
header->dsize = datasize; // DATA segment size
header->bsize = bsssize; // BSS segment size
header->ssize = ost_index * 12; // Symbol table size
header->ostbase = NULL; // Output symbol table base address
// For each object file, relocate its TEXT and DATA segments. OR the result
// into ret so all files get moved (and errors reported) before returning
// with the error condition
for(otemp=olist; otemp!=NULL; otemp=otemp->o_next)
{
ret |= RelocateSegment(otemp, T_TEXT); // TEXT segment relocations
ret |= RelocateSegment(otemp, T_DATA); // DATA segment relocations
}
// Done with global symbol hash tables
FreeHashes();
return (ret ? (struct OHEADER *)NULL : header);
}
//
// Add symbol to hash list
//
int AddSymbolToHashList(struct HREC ** hptr, char * sym, struct OFILE * ofile,
long value, int type)
{
struct HREC * htemp = new_hrec();
if (htemp == NULL)
{
printf("Out of memory\n");
return 1;
}
// Shamus: Moar testing...
if (vflag > 1)
{
printf("AddSymbolToHashList(): hptr=$%08X, sym=\"%s\", ofile=$%08X, value=$%X, type=$%X\n", hptr, sym, ofile, value, type);
}
// Populate hash record
memset(htemp->h_sym, 0, SYMLEN);
strcpy(htemp->h_sym, sym);
htemp->h_ofile = ofile;
htemp->h_value = value;
htemp->h_type = type;
// Add new hash to the front of the list (hence the ** for hptr)
htemp->h_next = *hptr;
*hptr = htemp;
return 0;
}
//
// Add symbol to the unresolved symbols hash table (really, it's a linked list)
//
int AddUnresolvedSymbol(char * sym, struct OFILE * ofile)
{
if (vflag > 1)
printf("AddUnresolvedSymbol(%s, %s)\n", sym, ofile->o_name);
return AddSymbolToHashList(&unresolved, sym, ofile, 0L, 0);
}
//
// Remove the HREC from the unresolved symbol list, and pass back a pointer
// to the spot where the HREC was.
//
struct HREC * RemoveUnresolvedSymbol(struct HREC * hrec)
{
struct HREC * ptr = unresolved;
struct HREC * previous = NULL;
while ((ptr != hrec) && (ptr != NULL))
{
previous = ptr;
ptr = ptr->h_next;
}
// Not found...!
if (ptr == NULL)
return NULL;
struct HREC * next = ptr->h_next;
// Remove the head if nothing previous, otherwise, remove what we found
if (previous == NULL)
unresolved = next;
else
previous->h_next = next;
free(ptr);
return next;
}
//
// Add symbol to the unresolved symbols hash table
//
int AddARSymbol(char * sym, struct OFILE * ofile)
{
if (vflag > 1)
printf("AddARSymbol(%s, %s)\n", sym, ofile->o_name);
return AddSymbolToHashList(&arSymbol, sym, ofile, 0L, 0);
}
//
// Generate hash value from the 1st 15 characters of the symbol modulo the
// number of buckets in the hash.
//
int GetHash(char * s)
{
// For this to be consistent, the symbol MUST be zeroed out beforehand!
// N.B.: strncpy() pads zeroes for us, if the symbol is less than 15 chars.
char c[15];
strncpy(c, s, 15);
int i = (c[0] + c[1] + c[2] + c[3] + c[4] + c[5] + c[6] + c[7] + c[8]
+ c[9] + c[10] + c[11] + c[12] + c[13] + c[14]) % NBUCKETS;
return i;
}
//
// Lookup a symbol in the hash table.
// Returns either a pointer to the HREC or NULL if not found.
//
struct HREC * LookupHREC(char * symbol)
{
struct HREC * hptr = htable[GetHash(symbol)];
while (hptr != NULL)
{
//This is utter failure...
// if (symcmp(symbol, hptr->h_sym)) <-- left here for giggles :D - LinkoVitch
// Return hash record pointer if found
if (strcmp(symbol, hptr->h_sym) == 0)
return hptr;
hptr = hptr->h_next;
}
return NULL;
}
//
// Lookup a symbol in the AR symbol table.
// Returns either a pointer to the HREC or NULL if not found.
//
struct HREC * LookupARHREC(char * symbol)
{
struct HREC * hptr = arSymbol;
while (hptr != NULL)
{
// Return hash record pointer if found
if (strcmp(symbol, hptr->h_sym) == 0)
return hptr;
hptr = hptr->h_next;
}
return NULL;
}
//
// Add the imported symbols from this file to unresolved, and the global and
// common (???) symbols to the exported hash table.
//
// Change old-style commons (type == T_EXTERN, value != 0) to new-style ones
// (type == (T_GLOBAL | T_EXTERN)). [??? O_o]
// [N.B.: Whoever wrote the above didn't know what the fuck they were talking
// about. Commons (globals) are exactly what they are calling 'old
// style'. Also note, that there is no "T_GLOBAL" or "T_EXTERN" symbols
// defined anywhere in the code.]
//
int AddSymbols(struct OFILE * Ofile)
{
struct HREC * hptr; // Hash record pointer
if (vflag > 1)
{
printf("AddSymbols: for file %s\n", Ofile->o_name);
printf(" t_bbase = $%X\n", Ofile->o_tbase);
printf(" d_bbase = $%X\n", Ofile->o_dbase);
printf(" o_bbase = $%X\n", Ofile->o_bbase);
printf(" tsize = $%X\n", Ofile->o_header.tsize);
printf(" dsize = $%X\n", Ofile->o_header.dsize);
printf(" bsize = $%X\n", Ofile->o_header.bsize);
printf(" reloc.tsize = $%X\n", Ofile->o_header.absrel.reloc.tsize);
printf(" reloc.dsize = $%X\n", Ofile->o_header.absrel.reloc.dsize);
}
// Get base pointer, start of sym fixups
char * ptr = Ofile->o_image + 32
+ Ofile->o_header.tsize
+ Ofile->o_header.dsize
+ Ofile->o_header.absrel.reloc.tsize
+ Ofile->o_header.absrel.reloc.dsize;
char * sfix = ptr; // Set symbol fixup pointer
char * sstr = sfix + Ofile->o_header.ssize; // Set symbol table pointer
long nsymbols = Ofile->o_header.ssize / 12; // Obtain number of symbols
while (nsymbols)
{
long index = GetLong(sfix); // Get symbol string index
long type = GetLong(sfix + 4); // Get symbol type
long value = GetLong(sfix + 8); // Get symbol value
if ((Ofile->isArchiveFile) && !(Ofile->o_flags & O_USED))
{
if ((type & T_GLBL) && (type & (T_SEG | T_ABS)))
if (AddARSymbol(sstr + index, Ofile))
return 1;
}
else if (type == T_GLBL)
{
// Global symbol that may or may not be in the current unit
hptr = LookupHREC(sstr + index);
if (hptr != NULL)
hptr->h_ofile->o_flags |= O_USED; // Mark .o file as used
// Otherwise, *maybe* add to unresolved list
else
{
// Check to see if this is a common symbol; if so, add it to
// the hash list...
if (value != 0)
{
// Actually, we need to convert this to a BSS symbol,
// increase the size of the BSS segment for this object, &
// add it to the hash list
uint32_t bssLocation = Ofile->o_header.tsize + Ofile->o_header.dsize + Ofile->o_header.bsize;
Ofile->o_header.bsize += value;
Ofile->segSize[BSS] += value;
type |= T_BSS;
value = bssLocation;
PutLong(sfix + 4, type);
PutLong(sfix + 8, value);
if (vflag > 1)
printf("AddSymbols: Resetting common label to BSS label\n");
if (AddSymbolToHashList(&htable[GetHash(sstr + index)],
sstr + index, Ofile, value, type))
return 1; // Error if addition failed
}
// Make sure it's not a built-in external...
else if ((strcmp(sstr + index, "_TEXT_E") != 0)
&& (strcmp(sstr + index, "_DATA_E") != 0)
&& (strcmp(sstr + index, "_BSS_E") != 0))
{
if (AddUnresolvedSymbol(sstr + index, Ofile))
return 1; // Error if addition failed
}
}
}
else if ((type & T_GLBL) && (type & (T_SEG | T_ABS)))
{
hptr = LookupHREC(sstr + index);
// Symbol isn't in the table, so try to add it:
if (hptr == NULL)
{
if (AddSymbolToHashList(&htable[GetHash(sstr + index)],
sstr + index, Ofile, value, type))
return 1;
}
else
{
// Symbol already exists, decide what to do about it
// [N.B.: This isn't a check for a common symbol...
// BEWARE OF BAD INTERPRETATIONS!!]
if (iscommon(hptr->h_type))
{
// Mismatch: common came first; warn and keep the global
if (wflag)
{
printf("Warning: %s: global from ", sstr + index);
WriteARName(Ofile);
printf(" used, common from ");
WriteARName(hptr->h_ofile);
printf(" discarded.\n");
}
hptr->h_ofile = Ofile;
hptr->h_type = type;
hptr->h_value = value;
}
else
{
// Global exported by another ofile; warn and make this one
// extern
if (wflag)
{
printf("Duplicate symbol %s: ", sstr + index);
WriteARName(hptr->h_ofile);
printf(" used, ");
WriteARName(Ofile);
printf(" discarded\n");
}
// Set the global in this unit to pure external
// (is this a good idea? what if the other one is a ref to
// this one???)
PutLong(sfix + 4, T_GLBL);
}
}
}
sfix += 12; // Increment symbol fixup pointer
nsymbols--; // Decrement num of symbols to process
}
// Success loading symbols
return 0;
}
//
// Process object file for symbols
//
int DoItem(struct OFILE * obj)
{
// Allocate memory for object record ptr
struct OFILE * Ofile = new_ofile();
if (Ofile == NULL)
{
printf("Out of memory while processing %s\n", obj->o_name);
return 1;
}
// Starting after all pathnames, etc., copy .o file name to Ofile
char * temp = PathTail(obj->o_name);
// Check filename length
if (strlen(temp) > FNLEN - 1)
{
printf("File name too long: %s\n", temp);
return 1;
}
// Check archive name length
if (strlen(obj->o_arname) > (FNLEN - 1))
{
printf("Archive name too long: %s\n", obj->o_arname);
return 1;
}
strcpy(Ofile->o_name, temp); // Store filename
strcpy(Ofile->o_arname, obj->o_arname); // Store archive name
// Initialise object record information
Ofile->o_next = NULL;
Ofile->o_tbase = 0;
Ofile->o_dbase = 0;
Ofile->o_bbase = 0;
Ofile->o_flags = obj->o_flags;
Ofile->o_image = obj->o_image;
Ofile->isArchiveFile = obj->isArchiveFile;
Ofile->segSize[TEXT] = obj->segSize[TEXT];
Ofile->segSize[DATA] = obj->segSize[DATA];
Ofile->segSize[BSS] = obj->segSize[BSS];
char * ptr = obj->o_image;
Ofile->o_header.magic = GetLong(ptr);
Ofile->o_header.tsize = GetLong(ptr + 4);
Ofile->o_header.dsize = GetLong(ptr + 8);
Ofile->o_header.bsize = GetLong(ptr + 12);
Ofile->o_header.ssize = GetLong(ptr + 16);
Ofile->o_header.absrel.reloc.tsize = GetLong(ptr + 24);
Ofile->o_header.absrel.reloc.dsize = GetLong(ptr + 28);
// Round BSS off to alignment boundary (??? isn't this already done ???)
Ofile->o_header.bsize = (Ofile->o_header.bsize + secalign) & ~secalign;
if ((Ofile->o_header.dsize & 7) && wflag)
{
printf("Warning: data segment size of ");
WriteARName(Ofile);
printf(" is not a phrase multiple\n");
}
// Check for odd segment sizes
if ((Ofile->o_header.tsize & 1) || (Ofile->o_header.dsize & 1)
|| (Ofile->o_header.bsize & 1))
{
printf("Error: odd-sized segment in ");
WriteARName(Ofile);
printf("; link aborted.\n");
return 1;
}
if (AddSymbols(Ofile))
return 1;
// Add this file to the olist
if (olist == NULL)
olist = Ofile;
else
olast->o_next = Ofile;
olast = Ofile;
return 0;
}
//
// Handle items in processing list.
//
// After loading all objects, archives & include files, we now go and process
// each item on the processing list (plist). Once this is done, we go through
// any unresolved symbols left and see if they have shown up.
//
int ProcessLists(void)
{
// Process object file list first (adds symbols from each unit & creates
// the olist)
while (plist != NULL)
{
if (DoItem(plist))
return 1;
struct OFILE * ptemp = plist;
plist = plist->o_next;
free(ptemp);
}
struct HREC * uptr;
// Process the unresolved symbols list. This may involve pulling in symbols
// from any included .a units. Such units are lazy linked by default; we
// generally don't want everything they provide, just what's referenced.
for(uptr=unresolved; uptr!=NULL; )
{
if (vflag > 1)
printf("LookupHREC(%s) => ", uptr->h_sym);
struct HREC * htemp = LookupHREC(uptr->h_sym);
if (htemp != NULL)
{
// Found it in the symbol table!
if (vflag > 1)
printf("%s in %s (=$%06X)\n", (isglobal(htemp->h_type) ? "global" : "common"), htemp->h_ofile->o_name, htemp->h_value);
// Mark the .o unit that the symbol is in as seen & remove from the
// unresolved list
htemp->h_ofile->o_flags |= O_USED;
uptr = RemoveUnresolvedSymbol(uptr);
}
else
{
if (vflag > 1)
printf("NULL\n");
// Check to see if the unresolved symbol is on the AR symbol list.
htemp = LookupARHREC(uptr->h_sym);
// If the unresolved symbol is in a .o unit that is unused, we can
// drop it; same if the unresolved symbol is in the exported AR
// symbol list. Otherwise, go to the next unresolved symbol.
if (!(uptr->h_ofile->o_flags & O_USED) || (htemp != NULL))
uptr = RemoveUnresolvedSymbol(uptr);
else
uptr = uptr->h_next;
// Now that we've possibly deleted the symbol from unresolved list
// that was also in the AR list, we add the symbols from this .o
// unit to the symbol table, mark the .o unit as used, and restart
// scanning the unresolved list as there is a good possibility that
// the symbols in the unit we're adding has unresolved symbols as
// well.
if (htemp != NULL)
{
htemp->h_ofile->o_flags |= O_USED;
AddSymbols(htemp->h_ofile);
uptr = unresolved;
}
}
}
// Show files used if the user requests it.
if (vflag > 1)
{
printf("Files used:\n");
struct OFILE * filePtr = olist;
while (filePtr != NULL)
{
if (filePtr->o_flags & O_USED)
{
printf(" %s%s%s\n", filePtr->o_name, (filePtr->isArchiveFile ? ":" : ""), (filePtr->isArchiveFile ? filePtr->o_arname : nullStr));
}
filePtr = filePtr->o_next;
}
}
return 0;
}
//
// Extract filename from path
//
char * PathTail(char * name)
{
// Find last occurance of PATH_DELIMETER
char * temp = strrchr(name, PATH_DELIMITER);
// Return what was passed in if path delimiter was not found
if (temp == NULL)
return name;
return temp + 1;
}
//
// Add input file to processing list
//
int AddToProcessingList(char * ptr, char * fname, char * arname, uint8_t arFile, uint32_t tSize, uint32_t dSize, uint32_t bSize)
{
if (plist == NULL)
{
// First time object record allocation
plist = new_ofile();
plast = plist;
}
else
{
// Next object record allocation
plast->o_next = new_ofile();
plast = plast->o_next;
}
if (plast == NULL)
{
printf("Out of memory.\n"); // Error if memory allocation fails
return 1;
}
// Discard paths from filenames...
fname = PathTail(fname);
arname = PathTail(arname);
// Check for filename length errors...
if (strlen(fname) > (FNLEN - 1))
{
printf("File name too long: %s (sorry!)\n", fname);
return 1;
}
if (strlen(arname) > (FNLEN - 1))
{
printf("AR file name too long: %s (sorry!)\n", arname);
return 1;
}
strcpy(plast->o_name, fname); // Store filename sans path
strcpy(plast->o_arname, arname); // Store archive name sans path
plast->o_image = ptr; // Store data pointer
plast->o_flags = (arFile ? 0 : O_USED); // File is used if NOT in archive
plast->o_next = NULL; // Initialise next record pointer
plast->isArchiveFile = arFile; // Shamus: Temp until can sort it out
plast->segSize[TEXT] = tSize;
plast->segSize[DATA] = dSize;
plast->segSize[BSS] = bSize;
return 0; // Return without errors
}
//
// Process in binary include files and add them to the processing list. This
// routine takes in the binary file and creates an 'object' file in memory.
// Sym1/Sym2 point to the start and end of data.
//
// Image size for include files is:
// Header ....... 32 bytes
// Data ......... dsize
// Sym fixups ... 2 * 12 bytes
// Symbol size .. 4 bytes (Value to include symbols and terminating null)
// Symbols ...... (strlen(sym1) + 1) + (strlen(sym2) + 1)
// Terminate .... 4 bytes (0x00000000)
//
int LoadInclude(char * fname, int handle, char * sym1, char * sym2, int segment)
{
char * ptr, * sptr;
int i;
unsigned symtype = 0;
uint32_t tSize = 0, dSize = 0, bSize = 0;
long fsize = FileSize(handle); // Get size of include file
long dsize = (fsize + secalign) & ~secalign; // Align size to boundary
int sym1len = strlen(sym1) + 1; // Get sym1 length + null termination
int sym2len = strlen(sym2) + 1; // Get sym2 length + null termination
long size = 32 + dsize + 24 + 4 + sym1len + sym2len + 4;
// Use calloc so the header & fixups initialize to zero
// Allocate object image memory
if ((ptr = calloc(size, 1)) == NULL)
{
printf("Out of memory while including %s\n", fname);
close(handle);
return 1;
}
// Read in binary data
if (read(handle, ptr + 32, fsize) != fsize)
{
printf("File read error on %s\n", fname);
close(handle);
free(ptr);
return 1;
}
close(handle);
// Build this image's dummy header
PutLong(ptr, 0x00000107); // Magic number
if (segment)
{
PutLong(ptr+4, dsize); // Text size
PutLong(ptr+8, 0L); // Data size
symtype = 0x05000000;
tSize = dsize;
}
else
{
PutLong(ptr+4, 0L); // Text size
PutLong(ptr+8, dsize); // Data size
symtype = 0x07000000;
dSize = dsize;
}
PutLong(ptr+12, 0L); // BSS size
PutLong(ptr+16, 24); // Symbol table size
PutLong(ptr+20, 0L); // Entry point
PutLong(ptr+24, 0L); // TEXT relocation size
PutLong(ptr+28, 0L); // DATA relocation size
sptr = ptr + 32 + dsize; // Set sptr to symbol table location
PutLong(sptr, 4L); // String offset of symbol1
PutLong(sptr+4, symtype); // Symbol type
PutLong(sptr+8, 0x00000000); // Symbol has no value (START)
PutLong(sptr+12, 4L + (sym2len - 1)); // String offset of symbol2
PutLong(sptr+16, symtype); // Symbol type
PutLong(sptr+20, dsize); // Symbol is data size (END)
sptr = ptr + 32 + dsize + 24; // Set sptr to symbol table size loc
PutLong(sptr, sym1len + 4L); // Size of symbol table
sptr = ptr + 32 + dsize + 24 + 4; // Set sptr to symbol table location
for(i=0; i<(sym1len-1); i++) // Write symbol1 to string table
sptr[i] = *sym1++;
sptr += (sym1len - 1); // Step past symbol string
*sptr = '\0'; // Terminate symbol string
sptr += 1; // Step past termination
for(i=0; i<(sym2len-1); i++) // Write symbol2 to string table
sptr[i] = *sym2++;
sptr += (sym2len - 1); // Step past symbol string
*sptr = '\0'; // Terminate symbol string
sptr += 1; // Step past termination
PutLong(sptr, 0L); // Terminating long for object file
return AddToProcessingList(ptr, fname, nullStr, 0, tSize, dSize, bSize);
}
//
// Takes a file name, gets in its image, puts it on plist. The image may
// already be in memory: If so, the ptr arg is non-null. If so, the file is
// already closed. Note that the file is already open (from DoFile()). RETURNS
// a pointer to the OFILE structure for this file, so you can diddle its flags
// (DoFile sets O_USED for files on the command line).
//
int LoadObject(char * fname, int fd, char * ptr)
{
uint32_t tSize = 0, dSize = 0, bSize = 0;
if (ptr == NULL)
{
long size = FileSize(fd);
// Allocate memory for file data
ptr = malloc(size);
if (ptr == NULL)
{
printf("Out of memory while processing %s\n", fname);
close(fd);
return 1;
}
// Read in file data
if (read(fd, ptr, size) != size)
{
printf("File read error on %s\n", fname);
close(fd);
free(ptr);
return 1;
}
tSize = (GetLong(ptr + 4) + secalign) & ~secalign;
dSize = (GetLong(ptr + 8) + secalign) & ~secalign;
bSize = (GetLong(ptr + 12) + secalign) & ~secalign;
close(fd);
}
// Now add this image to the list of pending ofiles (plist)
return AddToProcessingList(ptr, fname, nullStr, 0, tSize, dSize, bSize);
}
uint32_t SymTypeAlcToAout(uint32_t alcType)
{
uint32_t type = T_UNDF;
// Symbol type mappings here are derived from comparing Alcyon and BSD
// object files generated by MADMAC using the "size" utility from jag_utils
// (https://github.com/cubanismo/jag_utils) and the old PC/DOS Atari SDK.
//
// In BSD/a.out:
//
// 1) text | global text == text relocatable symbol in this file
// 2) data | global data == data relocatable symbol in this file
// 3) BSS | global BSS == bss relocatable symbol in this file
// 4) ABS | global ABS == non-relocatable symbol in this file
// 4) <none> | global <none> == undefined global symbol (extern)
//
// In DRI/Alcyon:
//
// 1) Everything seems to be marked defined.
// 2) There is an explicit "external" bit. It appears to be mutually
// exclusive with the "global" bit, at least in MADMAC's output.
// 3) There are separate "equated" and "equated reg" type bits that
// both represent ABS/non-relocatable values.
if ((alcType & ALCSYM_EQUATED) ||
(alcType & ALCSYM_EQUATED_REG))
type |= T_ABS;
else if (alcType & ALCSYM_TEXT)
type |= T_TEXT;
else if (alcType & ALCSYM_DATA)
type |= T_DATA;
else if (alcType & ALCSYM_BSS)
type |= T_BSS;
if ((alcType & ALCSYM_GLOBAL) ||
(alcType & ALCSYM_EXTERN))
type |= T_GLBL;
return type;
}
int LoadAlcyon(char * fname, int fd)
{
char *ptr, *sptr, *aout, *saout, *traout, *strPtr;
char *trelptr, *drelptr, *relend;
struct ALCHEADER hdr;
struct ALCSYM *alcSyms;
long size = FileSize(fd);
size_t symStrLen;
size_t strOff;
uint32_t numSyms, numTRel, numDRel, i, j;
// Validate the file is at least large enough to contain a valid header
if (size < 0x1c)
{
printf("Alcyon object file %s too small to contain header\n", fname);
return 1;
}
// Allocate memory for file data
ptr = malloc(size);
if (ptr == NULL)
{
printf("Out of memory while processing %s\n", fname);
close(fd);
return 1;
}
// Read in file data
if (read(fd, ptr, size) != size)
{
printf("File read error on %s\n", fname);
close(fd);
free(ptr);
return 1;
}
close(fd);
hdr.magic = GetWord(ptr);
hdr.tsize = GetLong(ptr + 2);
hdr.dsize = GetLong(ptr + 6);
hdr.bsize = GetLong(ptr + 10);
hdr.ssize = GetLong(ptr + 14);
// Construct a BSD-style/aout object file in memory from the Alcyon data
numSyms = hdr.ssize / 14;
alcSyms = calloc(numSyms, sizeof(*alcSyms));
if (alcSyms == NULL)
{
printf("Out of memory while processing %s\n", fname);
free(ptr);
return 1;
}
sptr = ptr + 0x1c + hdr.tsize + hdr.dsize;
trelptr = sptr + hdr.ssize;
drelptr = trelptr + hdr.tsize;
relend = drelptr + hdr.dsize;
if (relend - ptr > size)
{
printf("Alcyon object file %s truncated: Header wants %ldB, file is %ldB\n",
fname, relend - ptr, size);
return 1;
}
for (i = 0, symStrLen = 0; i < numSyms; i++)
{
memcpy(alcSyms[i].name, sptr, 8);
alcSyms[i].type = GetWord(sptr + 8);
alcSyms[i].value = GetLong(sptr + 10);
symStrLen += strnlen((char *)alcSyms[i].name, 8) + 1;
sptr += 14;
}
for (i = 0, numTRel = 0; trelptr + i < drelptr; i += 2)
{
uint16_t rel = GetWord(trelptr + i);
if ((rel != ALCREL_ABS) &&
(rel != ALCREL_LONG))
numTRel++;
}
for (i = 0, numDRel = 0; drelptr + i < relend; i += 2)
{
uint16_t rel = GetWord(drelptr + i);
if ((rel != ALCREL_ABS) &&
(rel != ALCREL_LONG))
numDRel++;
}
aout = malloc(32 + /* header */
hdr.tsize +
hdr.dsize +
numTRel * 8 + /* Text section relocations */
numDRel * 8 + /* Data section relocations */
numSyms * 12 + /* symbol table */
4 + symStrLen + /* string table size + strings */
4 /* NULL-terminator for file */);
if (aout == NULL)
{
printf("Out of memory while processing %s\n", fname);
free(alcSyms);
free(ptr);
return 1;
}
// Construct the BSD/a.out header.
PutLong(aout, 0x00000107); // Magic number
PutLong(aout+4, hdr.tsize); // Text size
PutLong(aout+8, hdr.dsize); // Data size
PutLong(aout+12, hdr.bsize); // BSS size
PutLong(aout+16, numSyms * 12); // Symbol table size
PutLong(aout+20, 0L); // Entry point
PutLong(aout+24, numTRel * 8); // TEXT relocation size
PutLong(aout+28, numDRel * 8); // DATA relocation size
// Copy the raw text and data segments
memcpy(aout + 32, ptr + 0x1c, hdr.tsize);
memcpy(aout + 32 + hdr.tsize, ptr + 0x1c + hdr.tsize, hdr.dsize);
// Set traout to the start of the relocation tables
traout = aout + 32 + hdr.tsize + hdr.dsize;
// Set saout to symbol table location
saout = traout + numTRel * 8 + numDRel * 8 ;
// Convert the text and data relocations to a.out format
for (i = 0; trelptr + i < relend; i += 2)
{
uint16_t rel = GetWord(trelptr + i);
uint16_t relFlags = rel & 7;
uint32_t aoutflags = BSDREL_ABS;
uint32_t valoffset = 0;
char *const valaddr = aout + 32 + i;
const uint32_t reladdr = (trelptr + i >= drelptr) ? i - hdr.tsize : i;
if (relFlags == ALCREL_LONG)
{
i += 2;
rel = GetWord(trelptr + i);
relFlags = rel & 7;
}
else
{
aoutflags |= BSDREL_WORD;
}
if (relFlags == ALCREL_ABS)
continue;
switch (relFlags) {
case ALCREL_EXTPCREL:
aoutflags &= ~BSDREL_ABS;
aoutflags |= BSDREL_PCREL;
/* Fall through */
case ALCREL_EXTABS:
aoutflags |= BSDREL_GLOBAL;
aoutflags |= (ALCREL_SYMIDX(rel) << BSDREL_SYMIDX_SHIFT);
break;
case ALCREL_TEXT:
aoutflags |= BSDREL_SEG_TEXT;
break;
case ALCREL_DATA:
aoutflags |= BSDREL_SEG_DATA;
valoffset = hdr.tsize;
break;
case ALCREL_BSS:
aoutflags |= BSDREL_SEG_BSS;
valoffset = hdr.tsize + hdr.dsize;
break;
default:
printf("Invalid Alcyon relocation flags: 0x%02x\n", relFlags);
free(alcSyms);
free(ptr);
free(aout);
return 1;
}
if (valoffset != 0)
{
if (aoutflags & BSDREL_WORD)
{
valoffset += GetWord(valaddr);
PutWord(valaddr, (uint16_t)valoffset);
}
else
{
valoffset += GetLong(valaddr);
PutLong(valaddr, valoffset);
}
}
PutLong(traout, reladdr);
PutLong(traout+4, aoutflags);
traout += 8;
}
// Done with the Alcyon data.
free(ptr);
ptr = NULL;
sptr = NULL;
// Set strPtr to string table location and write string table size
strPtr = saout + numSyms * 12;
PutLong(strPtr, 4 + symStrLen);
for (i = 0, strOff = 4; i < numSyms; i++)
{
PutLong(saout, strOff); // String offset of symbol
PutLong(saout+4, SymTypeAlcToAout(alcSyms[i].type)); // Symbol type
PutLong(saout+8, alcSyms[i].value); // Symbol value
saout += 12;
for (j = 0; j < 8 && alcSyms[i].name[j] != '\0'; j++)
*(strPtr + strOff + j) = alcSyms[i].name[j];
strOff += j; // Step past string
*(strPtr + strOff) = '\0'; // Terminate symbol string
strOff++; // Step past termination
}
PutLong(strPtr + strOff, 0L); // Terminating long for object file
// Done with the Alcyon symbol table.
free(alcSyms);
// Now add this image to the list of pending ofiles (plist)
return AddToProcessingList(aout, fname, nullStr, 0, hdr.tsize, hdr.dsize, hdr.bsize);
}
//
// What it says on the tin: check for a .o suffix on the passed in string
//
uint8_t HasDotOSuffix(char * s)
{
char * temp = strrchr(s, '.');
if ((temp == NULL) || (strncmp(temp, ".o", 2) != 0))
return 0;
return 1;
}
//
// Process an ar archive file (*.a)
//
int LoadArchive(char * fname, int fd)
{
// Read in the archive file to memory and process
long size = FileSize(fd);
char * ptr = malloc(size);
char * endPtr = ptr + size;
char * longFilenames = NULL;
if (ptr == NULL)
{
printf("Out of memory while processing %s\n", fname);
close(fd);
return 1;
}
if (read(fd, ptr, size) != size)
{
printf("File read error on %s\n", fname);
close(fd);
free(ptr);
return 1;
}
close(fd);
// Save the pointer for later...
arPtr[arIndex++] = ptr;
char objName[FNLEN];
char objSize[11];
int i;
//printf("\nProcessing AR file \"%s\"...\n", fname);
ptr += 8;
// Loop through all objects in the archive and process them
do
{
memset(objName, 0, 17);
objSize[10] = 0;
for(i=0; i<16; i++)
{
// if ((ptr[i] == '/') || (ptr[i] == ' '))
if ((ptr[i] == ' ') && (i != 0))
{
objName[i] = 0;
break;
}
objName[i] = ptr[i];
}
for(i=0; i<10; i++)
{
if (ptr[48 + i] == ' ')
{
objSize[i] = 0;
break;
}
objSize[i] = ptr[48 + i];
}
// Check to see if a long filename was requested
// N.B.: " " is for GNU archives, and "/" is for BSD archives
if ((objName[0] == 0x20) || (objName[0] == '/'))
{
uint32_t fnSize = atoi(objName + 1);
if (longFilenames != NULL)
{
i = 0;
char * currentFilename = longFilenames + fnSize;
while (*currentFilename != 0x0A)
objName[i++] = *currentFilename++;
objName[i] = 0;
}
}
if ((strncmp(objName, "ARFILENAMES/", 12) == 0) || (strncmp(objName, "//", 2) == 0))
{
longFilenames = ptr + 60;
}
else if (HasDotOSuffix(objName))
{
// Strip off any trailing forward slash at end of object name
int lastChar = strlen(objName) - 1;
if (objName[lastChar] == '/')
objName[lastChar] = 0;
//printf("Processing object \"%s\" (size == %i, obj_index == %i)...\n", objName, atoi(objSize), obj_index);
uint32_t tSize = (GetLong(ptr + 60 + 4) + secalign) & ~secalign;
uint32_t dSize = (GetLong(ptr + 60 + 8) + secalign) & ~secalign;
uint32_t bSize = (GetLong(ptr + 60 + 12) + secalign) & ~secalign;
if (AddToProcessingList(ptr + 60, objName, fname, 1, tSize, dSize, bSize))
return 1;
}
uint32_t size = atoi(objSize);
size += (size & 0x01 ? 1 : 0);
ptr += 60 + size;
}
while (ptr < endPtr);
return 0;
}
//
// Process files (*.o, *.a) passed in on the command line
//
int ProcessFiles(void)
{
int i;
char magic[8]; // Magic header number (4 bytes for *.o, 8 for *.a)
// Process all file handles
for(i=0; i<(int)hd; i++)
{
// Verbose mode information
if (vflag == 1)
printf("Read file %s%s\n", name[i], (hflag[i] ? " (include)" : ""));
if (!hflag[i])
{
// Attempt to read file magic number (OBJECT/ARCHIVE FILES)
if (read(handle[i], magic, 8) != 8)
{
printf("Error reading file %s\n", name[i]);
close(handle[i]);
return 1;
}
lseek(handle[i], 0L, 0); // Reset to start of input file
// Look for RMAC/MAC/GCC (a.out) object files
if ((GetLong(magic) & 0xFFFF) == 0x0107)
{
// Process input object file
if (LoadObject(name[i], handle[i], 0L))
return 1;
}
// Look for DRI Alcyon C (and old MAC) object files
else if (GetWord(magic) == 0x601A)
{
// Process Alcyon object file.
if (LoadAlcyon(name[i], handle[i]))
return 1;
}
// Otherwise, look for an object archive file
else if (strncmp(magic, "!<arch>\x0A", 8) == 0)
{
if (LoadArchive(name[i], handle[i]))
return 1;
}
else
{
// Close file and error
printf("%s is not a supported object or archive file\n", name[i]);
printf("Magic == [%02X][%02X][%02X][%02X]\n", magic[0], magic[1], magic[2], magic[3]);
close(handle[i]);
return 1;
}
}
else
{
// INCLUDE FILES
// If hflag[i] is 1, include this in the data segment; if 2, put it
// in text segment
if (LoadInclude(name[i], handle[i], hsym1[i], hsym2[i], hflag[i] - 1))
return 1;
}
}
// Free include, symbol & object handles
for(i=0; i<(int)hd; i++)
{
free(name[i]);
if (hflag[i])
{
free(hsym1[i]);
free(hsym2[i]);
}
}
// Reset next handle indicator
hd = 0;
return 0;
}
//
// Load newargv with pointers to arguments found in the buffer
//
int parse(char * buf, char * newargv[])
{
int i = 1;
if (vflag)
printf("begin parsing\n");
while (1)
{
while (*buf && strchr(",\t\n\r\14 ", *buf))
buf++;
/* test for eof */
if (*buf == '\0' || *buf == 26)
{
if (i == 0)
{
printf("No commands in command file\n");
return -1;
}
else
{
return i;
}
}
/* test for comment */
if (*buf == '#')
{
/* found a comment; skip to next \n and start over */
while (*buf && *buf != '\n')
buf++;
continue;
}
if (i == MAXARGS)
{
printf("Too many arguments in command file\n");
return -1;
}
newargv[i] = buf;
while (!strchr(",\t\n\r\14 ", *buf))
{
if (*buf == '\0' || *buf == 26)
{
printf("Finished parsing %d args\n", i);
return i;
}
buf++;
}
*buf++ = '\0';
if (vflag)
printf("argv[%d] = \"%s\"\n", i, newargv[i]);
i++;
}
}
//
// Process in a link command file
//
int docmdfile(char * fname)
{
int fd; // File descriptor
unsigned size; // Command file size
char * ptr; // Pointer
int newargc; // New argument count
char * (*newargv)[]; // New argument value array
// Verbose information
if (vflag > 1)
printf("docmdfile(%s)\n", fname);
// Allocate memory for new argument values
newargv = malloc((long)(sizeof(char *) * MAXARGS));
if (!newargv)
{
printf("Out of memory.\n");
return 1;
}
// Attempt to open and read in the command file
if (fname)
{
if ((fd = open(fname, _OPEN_FLAGS)) < 0)
{
printf("Cannot open command file %s.\n", fname);
return 1;
}
size = FileSize(fd);
if ((ptr = malloc(size + 1)) == NULL)
{
printf("Out of memory.\n");
close(fd);
return 1;
}
if (read(fd, ptr, size) != (int)size)
{
printf("Read error on command file %s.\n", fname);
close(fd);
return 1;
}
*(ptr + size) = 0; // Null terminate the buffer
close(fd);
}
else
{
printf("No command filename specified\n");
return 1;
}
// Parse the command file
if ((newargc = parse(ptr, *newargv)) == -1)
{
return 1;
}
// Process the inputted flags
if (doargs(newargc, *newargv))
{
printf("docmdfile: doargs returns TRUE\n");
return 1;
}
free(ptr);
free(newargv);
return 0;
}
//
// Take an argument list and parse the command line
//
int doargs(int argc, char * argv[])
{
int i = 1; // Iterator
int c; // Command line character
char * ifile, * isym; // File name and symbol name for -i
// Parse through option switches & files
while (i < argc)
{
// Process command line switches
if (argv[i][0] == '-')
{
if (!argv[i][1])
{
printf("Illegal option argument: %s\n\n", argv[i]);
ShowHelp();
return 1;
}
c = argv[i++][1]; // Get next character in command line
// Process command line switch
switch (c)
{
case '?': // Display usage information
case 'h':
case 'H':
ShowVersion();
ShowHelp();
return 1;
case 'a':
case 'A': // Set absolute linking on
if (aflag)
warn('a', 1);
if (i + 2 >= argc)
{
printf("Not enough arguments to -a\n");
return 1;
}
aflag = 1; // Set abs link flag
// Segment order is TEXT, DATA, BSS
// Text segment can be 'r' or a value
if ((*argv[i] == 'r' || *argv[i] == 'R') && !argv[i][1])
{
ttype = -1; // TEXT segment is relocatable
}
else if ((*argv[i] == 'x' || *argv[i] == 'X'))
{
printf("Error in text-segment address: cannot be contiguous\n");
return 1;
}
else if (GetHexValue(argv[i], &tval))
{
printf("Error in text-segment address: %s is not 'r' or an address.", argv[i]);
return 1;
}
i++;
// Data segment can be 'r', 'x' or a value
if ((*argv[i] == 'r' || *argv[i] == 'R') && !argv[i][1])
{
dtype = -1; // DATA segment is relocatable
}
else if ((*argv[i] == 'x' || *argv[i] == 'X'))
{
dtype = -2; // DATA follows TEXT
}
else if (GetHexValue(argv[i], &dval))
{
printf("Error in data-segment address: %s is not 'r', 'x' or an address.", argv[i]);
return 1;
}
i++;
// BSS segment can be 'r', 'x' or a value
if ((*argv[i] == 'r' || *argv[i] == 'R') && !argv[i][1])
{
btype = -1; // BSS segment is relocatable
}
else if ((*argv[i] == 'x' || *argv[i] == 'X'))
{
switch (argv[i][1])
{
case 'd': case 'D': case '\0':
btype = -3; // BSS follows DATA
break;
case 't': case 'T':
btype = -2; // BSS follows TEXT
if (btype == dtype)
{
printf("Error in bss-segment address: data-segment also follows text\n");
return 1;
}
break;
default:
btype = -4; // Error
break;
}
}
else if (GetHexValue(argv[i], &bval))
{
btype = -4;
return 1;
}
if (btype == -4)
{
printf("Error in bss-segment address: %s is not 'r', 'x[td]', or an address.", argv[i]);
return 1;
}
i++;
break;
case 'b':
case 'B': // Don't remove muliply defined locals
if (bflag)
warn('b', 1);
bflag = 1;
break;
case 'c':
case 'C': // Process a command file
if (i == argc)
{
printf("Not enough arguments to -c\n");
return 1;
}
if (docmdfile(argv[i++]))
{
return 1;
}
break;
case 'd':
case 'D': // Wait for "return" before exiting
if (dflag)
warn('d', 0);
dflag = 1;
waitflag = 1;
break;
case 'e':
case 'E': // Output COFF (absolute only)
cflag = 1;
if (noheaderflag)
printf("Warning: -e overridden by -n, output will be headerless\n");
break;
case 'g':
case 'G': // Output source level debugging
if (gflag) warn('g', 1);
gflag = 1;
break;
case 'i':
case 'I': // Include binary file
if (i + 2 > argc)
{
printf("Not enough arguments to -i\n");
return 1;
}
ifile = argv[i++];
isym = argv[i++];
// handle -ii (No truncation)
if ((argv[i-3][2] == 'i') || (argv[i-3][2] == 'I'))
{
if (!cflag)
printf("warning: (-ii) COFF format output not specified\n");
}
// handle -i (Truncation)
else
{
if (strlen(isym) > 8)
isym[8] = '\0';
}
// Place include files in the DATA segment only
if (DoFile(ifile, DSTSEG_D, isym))
return 1;
break;
case 'l':
case 'L': // Add local symbols
if (lflag)
warn('l', 1);
lflag = 1;
break;
case 'm':
case 'M': // Produce load symbol map
if (mflag)
warn('m', 1);
mflag = 1;
break;
case 'n':
case 'N': // Output no header to .abs file
if (noheaderflag)
warn('n', 1);
if (cflag)
printf("Warning: -e overridden by -n, output will be headerless\n");
noheaderflag = 1;
break;
case 'o':
case 'O': // Specify an output file
if (oflag)
warn('o', 1);
oflag = 1;
if (i >= argc)
{
printf("No output filename following -o switch\n");
return 1;
}
if (strlen(argv[i]) > FARGSIZE - 5)
{
printf("Output file name too long (sorry!)\n");
return 1;
}
strcpy(ofile, argv[i++]);
break;
case 'r':
case 'R': // Section alignment size
if (rflag)
warn('r', 1);
rflag = 1;
switch (argv[i-1][2])
{
case 'w': case 'W': secalign = 1; break; // Word alignment
case 'l': case 'L': secalign = 3; break; // Long alignment
case 'p': case 'P': secalign = 7; break; // Phrase alignment
case 'd': case 'D': secalign = 15; break; // Double phrase alignment
case 'q': case 'Q': secalign = 31; break; // Quad phrase alignment
default: secalign = 7; break; // Default phrase alignment
}
break;
case 's':
case 'S': // Output only global symbols
if (sflag)
warn('s', 1);
sflag = 1;
break;
case 'u':
case 'U': // Undefined symbols
uflag++;
break;
case 'v':
case 'V': // Verbose information
if (!vflag && !versflag)
{
ShowVersion();
}
vflag++;
break;
case 'w':
case 'W': // Show warnings flag
if (wflag)
warn('w', 1);
wflag = 1;
break;
case 'y':
case 'Y':
if (i >= argc)
{
printf("No directory filename following -y switch\n");
return 1;
}
if (strlen(argv[i]) > FARGSIZE * 3)
{
printf("Directory file name too long (sorry!)\n");
return 1;
}
strcpy(libdir, argv[i++]);
break;
case 'z':
case 'Z': // Suppress banner flag
if (zflag)
warn('z', 1);
zflag = 1;
break;
default:
printf("unknown option argument `%c'\n", c);
return 1;
}
}
else
{
// Not a switch, then process as a file
if (DoFile(argv[i++], 0, NULL))
return 1;
}
}
if (!oflag && vflag)
{
strcpy(ofile, "output");
printf("Output file is %s[.ext]\n", ofile);
}
if (oflag && vflag)
printf("Output file is %s\n", ofile);
if (sflag)
lflag = 0;
// No problems encountered
return 0;
}
//
// Display version information
//
void ShowVersion(void)
{
if (displaybanner)// && vflag)
{
printf(
" _\n"
" _ __| |_ ___\n"
"| '__| | '_ \\\n"
"| | | | | | |\n"
"|_| |_|_| |_|\n"
"\nRenamed Linker for Atari Jaguar\n"
"Copyright (c) 199x Allan K. Pratt, 2014-2021 Reboot & Friends\n"
"V%i.%i.%i %s (%s)\n\n", MAJOR, MINOR, PATCH, __DATE__, PLATFORM);
}
}
//
// Display command line help
//
void ShowHelp(void)
{
printf("Usage:\n");
printf(" %s [-options] file(s)\n", cmdlnexec);
printf("\n");
printf("Options:\n");
printf(" -? or -h display usage information\n");
printf(" -a <text> <data> <bss> output absolute file (default: ABS)\n");
printf(" hex value: segment address\n");
printf(" r: relocatable segment\n");
printf(" x[dt]: contiguous segment\n");
printf(" for contiguous bss:\n");
printf(" d(default): follows data segment\n");
printf(" t: follows text segment\n");
printf(" -b don't remove multiply defined local labels\n");
printf(" -c <fname> add contents of <fname> to command line\n");
printf(" -d wait for key after link\n");
printf(" -e output COF absolute file\n");
printf(" -g output source-level debugging\n");
printf(" -i <fname> <label> incbin <fname> and set <label> (trunc to 8 chars)\n");
printf(" -ii <fname> <label> incbin <fname> and set <label> (no truncation)\n");
printf(" -l add local symbols\n");
printf(" -m produce load symbols map\n");
printf(" -n output no file header to absolute file (overrides -e)\n");
printf(" -o <fname> set output name\n");
printf(" -r<size> section alignment size\n");
printf(" w: word (2 bytes)\n");
printf(" l: long (4 bytes)\n");
printf(" p: phrase (8 bytes, default alignment)\n");
printf(" d: double phrase (16 bytes)\n");
printf(" q: quad phrase (32 bytes)\n");
printf(" -s output only global symbols (supresses -l)\n");
printf(" -u allow unresolved symbols (experimental)\n");
printf(" -v set verbose mode\n");
printf(" -w show linker warnings\n");
printf(" -y <fname> set include path (also set by RLNPATH)\n");
printf(" -z suppress banner\n");
printf("\n");
}
//
// Application exit
//
void ExitLinker(void)
{
char tempbuf[128];
// Display link status if verbose mode
if (vflag)
printf("Link %s.\n", errflag ? "aborted" : "complete");
// Wait for return key if requested
if (waitflag)
{
printf("\nPress the [RETURN] key to continue. ");
char * c = fgets(tempbuf, 128, stdin);
}
exit(errflag);
}
int main(int argc, char * argv[])
{
cmdlnexec = argv[0]; // Obtain executable name
char * s = getenv("RLNPATH"); // Attempt to obtain env variable
if (s)
strcpy(libdir, s); // Store it if found
// Initialize some vars
tval = dval = bval = 0;
ttype = dtype = btype = 0;
// Parse the command line
if (doargs(argc, argv))
{
errflag = 1;
ExitLinker();
}
// Check to see if include paths actually exist
if (strlen(libdir) > 0)
{
DIR * test = opendir(libdir);
if (test == NULL)
{
printf("Invalid include path: %s\n", libdir);
errflag = 1;
ExitLinker();
}
closedir(test);
}
if (!zflag && !vflag)
{
ShowVersion(); // Display version information
versflag = 1; // We've dumped the version banner
}
// Load in specified files/objects and add to processing list
if (ProcessFiles())
{
errflag = 1;
ExitLinker();
}
// Work in items in processing list & deal with unresolved list
if (ProcessLists())
{
errflag = 1;
ExitLinker();
}
// Check that there is something to link
if (olist == NULL)
{
ShowHelp();
ExitLinker();
}
// Report unresolved externals
if (unresolved != NULL)
{
printf("UNRESOLVED SYMBOLS\n");
// Don't list them if two -u's or more
if (uflag < 2)
{
struct HREC * utemp = unresolved;
while (utemp != NULL)
{
printf("\t%s (", utemp->h_sym);
WriteARName(utemp->h_ofile);
printf(")\n");
utemp = utemp->h_next;
}
}
if (!uflag)
{
errflag = 1;
ExitLinker();
}
}
// Make one output object from input objects
struct OHEADER * header = MakeOutputObject();
if (header == NULL)
{
errflag = 1;
ExitLinker();
}
// Partial linking
if (pflag)
{
printf("TO DO: Partial linking\n");
errflag = 1;
}
// Relocatable linking
else if (!aflag)
{
printf("TO DO: Relocatable linking\n");
errflag = 1;
}
// Absolute linking
else
{
if (vflag)
printf("Absolute linking (%s)\n", (cflag ? "COF" : "ABS"));
if (vflag > 1)
printf("Header magic is 0x%04X\n", (unsigned int)header->magic);
if (WriteOutputFile(header))
errflag = 1;
}
// Display the loaded symbols map
if (mflag)
if (ShowSymbolLoadMap(header))
errflag = 1;
// Display segment size summary
if (vflag)
{
printf("\n");
printf("+---------+----------+----------+----------+\n");
printf("| Segment | TEXT | DATA | BSS |\n");
printf("| Sizes |----------+----------+----------|\n");
printf("| (Hex) | %8X | %8X | %8X |\n", (unsigned int)header->tsize, (unsigned int)header->dsize, (unsigned int)header->bsize);
printf("+---------+----------+----------+----------+\n\n");
}
free(header);
ExitLinker();
}