Add Alcyon C object file support

Support ingesting the DRI Alcyon C object file
format in addition to a.out object files. The load
file routine simply translates the Alcyon object
file into an a.out file before passing it on to
the existing processing workflow.

The motivation here was to enable linking of the
binary-only cinepak decompression GPU routines
provided in the developer files. ALN can handle
these files just fine, and now RLN can too.

However, that is a very simple object files To
fully exercise the relocation table translation
code, contrived assembly files with all types of
relocations were assembled as Alcyon C object
files using MADMAC and BSD/a.out object files
using RMAC (Note RMAC had to be used to generate
the a.out files because MADMAC generates invalid
a.out relocations for certain relocation types
that it handles fine in Alcyon format), and then
both object files were linked as COFF executables
using RLN. The resulting COFF files were verified
to be identical.
This commit is contained in:
James Jones 2020-04-18 02:45:06 -07:00 committed by Shamus Hammons
parent f63e6b2da1
commit 304cdee3e0
2 changed files with 389 additions and 13 deletions

307
rln.c
View File

@ -671,11 +671,11 @@ int RelocateSegment(struct OFILE * ofile, int flag)
// object file image
addr = GetLong(rptr);
rflg = GetLong(rptr + 4);
glblreloc = (rflg & 0x00000010 ? 1 : 0);// Set global relocation flag
absreloc = (rflg & 0x00000040 ? 1 : 0); // Set absolute relocation flag
relreloc = (rflg & 0x000000A0 ? 1 : 0); // Set relative relocation flag
wordreloc = (rflg & 0x00000002 ? 1 : 0); // Set word relocation flag
opreloc = (rflg & 0x00000004 ? 1 : 0); // Set OP relocation flag
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)
@ -684,7 +684,7 @@ int RelocateSegment(struct OFILE * ofile, int flag)
// 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 + ((rflg >> 8) * 12));
symidx = GetLong(symtab + (BSDREL_SYMIDX(rflg) * 12));
memset(sym, 0, SYMLEN);
strcpy(sym, symbols + symidx);
olddata = newdata = 0; // Initialise old and new segment data
@ -709,32 +709,32 @@ int RelocateSegment(struct OFILE * ofile, int flag)
olddata |= saveBits2; // Restore upper 3 bits of data addr
}
if (rflg & 0x01)
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 & 0xFFFFFF00);
swcond = (rflg & BSDREL_SEGMASK);
if (!glblreloc)
{
switch (swcond)
{
case 0x00000200: // Absolute Value
case BSDREL_SEG_ABS:
break;
case 0x00000400: // TEXT segment relocation record
case BSDREL_SEG_TEXT:
// SCPCD : the symbol point to a text segment, we should use the textoffset
newdata = tbase + textoffset + olddata;
break;
case 0x00000600: // DATA segment relocation record
case BSDREL_SEG_DATA:
newdata = dbase + dataoffset
+ (olddata - ofile->o_header.tsize);
break;
case 0x00000800: // BSS segment relocation record
case BSDREL_SEG_BSS:
newdata = bbase + bssoffset
+ (olddata - (ofile->o_header.tsize
+ ofile->o_header.dsize));
@ -754,7 +754,7 @@ int RelocateSegment(struct OFILE * ofile, int flag)
// 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 & 0x01)
if (rflg & BSDREL_MOVEI)
newdata = _SWAPWORD(newdata);
if (wordreloc)
@ -2375,6 +2375,280 @@ int LoadObject(char * fname, int fd, char * ptr)
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
@ -2539,6 +2813,13 @@ int ProcessFiles(void)
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)
{

95
rln.h
View File

@ -83,6 +83,86 @@
#include <stdint.h>
#include <dirent.h>
// Alcyon object file header structures.
//
// Same as an Atari ST/GEMDOS/TOS executable file header.
//
// References:
// http://cd.textfiles.com/ataricompendium/BOOK/HTML/CHAP2.HTM#processes
// https://mikro.naprvyraz.sk/docs/GEM/GEMDOS.TXT
// MADMAC source from Landon Dyer (See below for URL)
//
// Note the above disagree on the last entry in the header layout: In practice
// the files MADMAC produces and that ALN consumes use the header layout from
// the Atari Compendium page.
struct ALCHEADER
{
uint16_t magic; // $601A
uint32_t tsize; // text segment size
uint32_t dsize; // data segment size
uint32_t bsize; // BSS segment size
uint32_t ssize; // symbol table size
uint32_t reserved0; // unused
uint32_t reserved1; // unused
uint16_t absflag; // Always '0' (relocatable) for obj files
};
// Alcyon/DRI symbol type bits - From size.c:show_dri_symbol_type() in
// https://github.com/cubanismo/jag_utils, as well as
// the GEMDOS Reference Manual, 4/4/86, "Executable Files" section, in the
// table "Values For Symbol Types," available various places, e.g.,
// https://mikro.naprvyraz.sk/docs/GEM/GEMDOS.TXT
#define ALCSYM_DEFINED 0x8000
#define ALCSYM_EQUATED 0x4000
#define ALCSYM_GLOBAL 0x2000
#define ALCSYM_EQUATED_REG 0x1000
#define ALCSYM_EXTERN 0x0800
#define ALCSYM_DATA 0x0400
#define ALCSYM_TEXT 0x0200
#define ALCSYM_BSS 0x0100
// Alcyon/DRI symbol relocation flags - Derived from mark.c in the
// original MADMAC sources released by Landon Dyer on his blog:
//
// https://dadhacker-125488.ingress-alpha.ewp.live/
//
// At this link from his September 2nd, 2008 post:
//
// http://www.dadhacker.com/Downloads/madmac.zip
//
// Downloaded from the Internet Archive Wayback Machine November 2015
// snapshot of the above URL, here:
//
// https://web.archive.org/web/20151120225539/http://www.dadhacker.com/Downloads/madmac.zip
//
// Another valuable source of information on the Alcyon relocation data,
// and the Alcyon/DRI C object file format in general is the Sozobon 2.0
// source code:
//
// https://sourceforge.net/projects/sozobon/files/
//
// Sozobon is a reimplementation of the original Atari Alcyon C compiler
// suite that can generate and link Alcyon/DRI object files compatible
// with the original. Take a look at the jas/cpy.c file for an alternate
// implementation of Alcyon/DRI relocation section data generation than
// the one in MADMAC, as well as an alternate Alcyon/DRI relocation
// data processing implementation in ld/rel.c.
#define ALCREL_ABS 0x0000 // No relocation at this location
#define ALCREL_DATA 0x0001 // Local address from data segment
#define ALCREL_TEXT 0x0002 // Local address from text segment
#define ALCREL_BSS 0x0003 // Local address from BSS segment
#define ALCREL_EXTABS 0x0004 // External fixup: Absolute address
#define ALCREL_LONG 0x0005 // Relocation type is in next word
#define ALCREL_EXTPCREL 0x0006 // External fixup: PC relative address
#define ALCREL_SYMIDX(rval) ((rval) >> 3) // 0-based index of ext fixup symbol
struct ALCSYM
{
uint8_t name[8]; // fixed-size, padded with zeros. NOT NUL-terminated!
uint16_t type; // symbol type mask, from ALCSYM_* flags above.
uint32_t value; // value
};
struct OHEADER
{
uint32_t magic; // $0107 for .o, $601B for .abs
@ -111,6 +191,21 @@ struct OHEADER
#define new_oheader() (struct OHEADER *)malloc(sizeof(struct OHEADER))
// BSD/a.out object relocation flags.
#define BSDREL_MOVEI 0x00000001 // Word-swapped (I.e., JRISC movei) long
#define BSDREL_WORD 0x00000002
#define BSDREL_OP 0x00000004 // Object Processor relocation
#define BSDREL_GLOBAL 0x00000010 // AKA external reference
#define BSDREL_ABS 0x00000040
#define BSDREL_PCREL 0x000000A0 // Note this implies BSDREL_GLOBAL
#define BSDREL_SYMIDX_SHIFT 8 // Bits to shift
#define BSDREL_SYMIDX(_rf) ((_rf) >> BSDREL_SYMIDX_SHIFT)
#define BSDREL_SEGMASK 0xFFFFFF00
#define BSDREL_SEG_ABS 0x00000200
#define BSDREL_SEG_TEXT 0x00000400
#define BSDREL_SEG_DATA 0x00000600
#define BSDREL_SEG_BSS 0x00000800
struct ARHEADER
{
uint8_t a_fname[14];